@backmatter/studio 0.0.8 → 0.0.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/chunks/UiKitApp-BxvTwsko.js +2410 -0
- package/dist/chunks/WorkspaceRoutes-BKKEkAq7.js +90126 -0
- package/dist/chunks/{dashboard-bNp6AbKE.js → dashboard-8rjkfEXl.js} +380 -382
- package/dist/chunks/{index-DoDE-hwB.js → index-laWAz5_p.js} +111 -112
- package/dist/dashboard.js +2 -2
- package/dist/index.js +287 -146
- package/dist/site-widget.css +1615 -0
- package/dist/site-widget.js +30903 -628
- package/dist/studio.css +268 -67
- package/package.json +4 -4
- package/src/App.tsx +12 -29
- package/src/api.ts +55 -0
- package/src/chat/ChangeCard.tsx +2 -0
- package/src/chat/ChatMessage.tsx +73 -62
- package/src/chat/ChatPanel.tsx +110 -15
- package/src/chat/DocListCard.tsx +2 -0
- package/src/chat/LiveStatus.tsx +78 -8
- package/src/chat/Reveal.tsx +34 -0
- package/src/chat/Shimmer.tsx +65 -23
- package/src/chat/chat-helpers.ts +35 -0
- package/src/chat/motion.ts +73 -0
- package/src/chat/useStickToBottom.ts +129 -14
- package/src/chrome/ContentExplorerSidebar.tsx +23 -8
- package/src/chrome/content-tree-visibility.ts +55 -0
- package/src/devbar-state.ts +125 -0
- package/src/index.css +3 -131
- package/src/pages/CliAuthorizePage.tsx +61 -28
- package/src/pages/MediaGalleryPage.tsx +74 -11
- package/src/pages/MediaLightbox.tsx +93 -1
- package/src/pages/SettingsUsagePage.tsx +23 -1
- package/src/pages/media-grid.ts +13 -0
- package/src/pages/ui-kit/UiKitApp.tsx +164 -0
- package/src/pages/ui-kit/buttons.tsx +30 -0
- package/src/pages/ui-kit/catalog.tsx +41 -0
- package/src/pages/ui-kit/colors.tsx +95 -0
- package/src/pages/ui-kit/devbar.tsx +160 -0
- package/src/pages/ui-kit/feedback.tsx +39 -0
- package/src/pages/ui-kit/fields.tsx +47 -0
- package/src/pages/ui-kit/lists.tsx +19 -0
- package/src/pages/ui-kit/overlays.tsx +39 -0
- package/src/pages/ui-kit/path.ts +9 -0
- package/src/pages/ui-kit/sections.tsx +32 -0
- package/src/pages/ui-kit/surfaces.tsx +121 -0
- package/src/pages/ui-kit/topic.tsx +112 -0
- package/src/pages/ui-kit/topics.ts +71 -0
- package/src/pages/ui-kit/type.tsx +55 -0
- package/src/preview-frame-radius.ts +51 -0
- package/src/preview-frame.tsx +108 -49
- package/src/shell-resets.css +155 -0
- package/src/site-bar.tsx +1810 -425
- package/src/site-dashboard-href.ts +21 -0
- package/src/site-devbar-overlay-inject.ts +15 -0
- package/src/site-devbar-overlay.css +217 -0
- package/src/site-pointer.ts +1 -1
- package/src/site-publish-menu.tsx +33 -0
- package/src/site-widget-styles.ts +7 -0
- package/src/site-widget.tsx +61 -21
- package/src/studio-prose-ui.css +39 -0
- package/src/theme/ThemeProvider.tsx +4 -1
- package/src/workspace-path.ts +10 -6
- package/dist/chunks/EmbedPublishPage-CtIlaeGO.js +0 -149
- package/dist/chunks/UiKitPage-DcnSQ-Hf.js +0 -173
- package/dist/chunks/WorkspaceRoutes-DVpUOaMV.js +0 -59550
- package/dist/chunks/status-bar-action-C0fsAWvD.js +0 -30618
- package/src/pages/UiKitPage.tsx +0 -331
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/devbar.tsx
|
|
2
|
+
// Specimens of the host bar. This file stays free of StyleX so the markup
|
|
3
|
+
// test can render it under bun, which does not compile StyleX.
|
|
4
|
+
import { useState, type CSSProperties } from "react";
|
|
5
|
+
import type { DevbarActivity } from "../../devbar-state";
|
|
6
|
+
import { SiteBar } from "../../site-bar";
|
|
7
|
+
|
|
8
|
+
const noop = () => {};
|
|
9
|
+
|
|
10
|
+
const stageStyle: CSSProperties = {
|
|
11
|
+
position: "relative",
|
|
12
|
+
height: 96,
|
|
13
|
+
overflow: "hidden",
|
|
14
|
+
transform: "translateZ(0)",
|
|
15
|
+
["--bm-devbar-right" as string]: "auto",
|
|
16
|
+
["--bm-devbar-left" as string]: "12px",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const sampleDoc = {
|
|
20
|
+
locale: "en",
|
|
21
|
+
type: "page",
|
|
22
|
+
slug: "home",
|
|
23
|
+
workspaceId: "ws_sample",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function DevBarTopic() {
|
|
27
|
+
const [play, setPlay] = useState(0);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 40 }}>
|
|
31
|
+
<style>{`
|
|
32
|
+
.ui-kit-devbar-label {
|
|
33
|
+
margin: 0 0 8px;
|
|
34
|
+
font-size: 14px;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
letter-spacing: -0.01em;
|
|
37
|
+
line-height: 1.3;
|
|
38
|
+
}
|
|
39
|
+
.ui-kit-devbar-stage {
|
|
40
|
+
width: 100%;
|
|
41
|
+
}
|
|
42
|
+
.ui-kit-devbar-reset {
|
|
43
|
+
align-self: flex-start;
|
|
44
|
+
height: 28px;
|
|
45
|
+
padding: 0 10px;
|
|
46
|
+
border: 1px solid currentColor;
|
|
47
|
+
border-radius: 8px;
|
|
48
|
+
background: transparent;
|
|
49
|
+
color: inherit;
|
|
50
|
+
font: 500 13px/1 ui-sans-serif, system-ui, sans-serif;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
}
|
|
53
|
+
.ui-kit-devbar-reset:focus-visible {
|
|
54
|
+
outline: 2px solid currentColor;
|
|
55
|
+
outline-offset: 2px;
|
|
56
|
+
}
|
|
57
|
+
`}</style>
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
className="ui-kit-devbar-reset"
|
|
61
|
+
onClick={() => setPlay((next) => next + 1)}
|
|
62
|
+
>
|
|
63
|
+
Reset
|
|
64
|
+
</button>
|
|
65
|
+
<Bar play={play} heading="Preview" siteMode="preview" />
|
|
66
|
+
<Bar play={play} heading="Live" siteMode="live" />
|
|
67
|
+
<Bar play={play} heading="Branch" siteMode="preview" branch="feat/sale" doc />
|
|
68
|
+
<Bar play={play} heading="Exiting" siteMode="preview" exiting />
|
|
69
|
+
<Bar play={play} heading="Agent" siteMode="preview" doc chatOpen />
|
|
70
|
+
<Bar play={play} heading="Publish" siteMode="live" doc changeCount={3} />
|
|
71
|
+
<Bar
|
|
72
|
+
play={play}
|
|
73
|
+
heading="Changes"
|
|
74
|
+
siteMode="preview"
|
|
75
|
+
doc
|
|
76
|
+
changeCount={3}
|
|
77
|
+
lineDiff={{ added: 14, removed: 2 }}
|
|
78
|
+
/>
|
|
79
|
+
<Bar
|
|
80
|
+
play={play}
|
|
81
|
+
heading="Editing"
|
|
82
|
+
siteMode="preview"
|
|
83
|
+
doc
|
|
84
|
+
activity={{ type: "editing", field: "hero.title" }}
|
|
85
|
+
/>
|
|
86
|
+
<Bar
|
|
87
|
+
play={play}
|
|
88
|
+
heading="Publishing"
|
|
89
|
+
siteMode="preview"
|
|
90
|
+
doc
|
|
91
|
+
activity={{
|
|
92
|
+
type: "publishing",
|
|
93
|
+
progress: 0.45,
|
|
94
|
+
steps: [
|
|
95
|
+
{ label: "Commit", state: "done" },
|
|
96
|
+
{ label: "PR #42", state: "done" },
|
|
97
|
+
{ label: "Deploying 0:14", state: "active" },
|
|
98
|
+
],
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
<Bar
|
|
102
|
+
play={play}
|
|
103
|
+
heading="Published"
|
|
104
|
+
siteMode="live"
|
|
105
|
+
doc
|
|
106
|
+
activity={{ type: "published", sha: "3a9f21", when: "just now" }}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function Bar({
|
|
113
|
+
play,
|
|
114
|
+
heading,
|
|
115
|
+
siteMode,
|
|
116
|
+
branch,
|
|
117
|
+
exiting = false,
|
|
118
|
+
doc = false,
|
|
119
|
+
chatOpen = false,
|
|
120
|
+
changeCount = 0,
|
|
121
|
+
lineDiff,
|
|
122
|
+
activity = null,
|
|
123
|
+
}: {
|
|
124
|
+
play: number;
|
|
125
|
+
heading: string;
|
|
126
|
+
siteMode: "preview" | "live";
|
|
127
|
+
branch?: string;
|
|
128
|
+
exiting?: boolean;
|
|
129
|
+
doc?: boolean;
|
|
130
|
+
chatOpen?: boolean;
|
|
131
|
+
changeCount?: number;
|
|
132
|
+
lineDiff?: { added: number; removed: number };
|
|
133
|
+
activity?: DevbarActivity | null;
|
|
134
|
+
}) {
|
|
135
|
+
return (
|
|
136
|
+
<section style={{ display: "flex", flexDirection: "column" }}>
|
|
137
|
+
<h2 className="ui-kit-devbar-label">{heading}</h2>
|
|
138
|
+
<div className="ui-kit-devbar-stage" style={stageStyle}>
|
|
139
|
+
<SiteBar
|
|
140
|
+
key={play}
|
|
141
|
+
label="page/home"
|
|
142
|
+
branch={branch}
|
|
143
|
+
doc={doc ? sampleDoc : null}
|
|
144
|
+
siteMode={siteMode}
|
|
145
|
+
exiting={exiting}
|
|
146
|
+
chatOpen={chatOpen}
|
|
147
|
+
publishOpen={false}
|
|
148
|
+
changeCount={changeCount}
|
|
149
|
+
lineDiff={lineDiff}
|
|
150
|
+
activity={activity}
|
|
151
|
+
entrance="play"
|
|
152
|
+
onExit={noop}
|
|
153
|
+
onEnterPreview={noop}
|
|
154
|
+
onToggleChat={noop}
|
|
155
|
+
onTogglePublish={noop}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
</section>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/feedback.tsx
|
|
2
|
+
import { StatusBar, StatusItem } from "../../chrome/StatusBar";
|
|
3
|
+
import { Text } from "../../ui/Text";
|
|
4
|
+
import { Columns, Specimen, Topic } from "./topic";
|
|
5
|
+
import { UI_KIT_TOPICS } from "./topics";
|
|
6
|
+
|
|
7
|
+
const topic = UI_KIT_TOPICS.find((item) => item.slug === "feedback")!;
|
|
8
|
+
|
|
9
|
+
export function FeedbackTopic() {
|
|
10
|
+
return (
|
|
11
|
+
<Topic title={topic.title} lede={topic.lede}>
|
|
12
|
+
<Columns>
|
|
13
|
+
<Specimen label="StatusBar" block>
|
|
14
|
+
<StatusBar>
|
|
15
|
+
<StatusItem>main</StatusItem>
|
|
16
|
+
<StatusItem>2 uncommitted</StatusItem>
|
|
17
|
+
</StatusBar>
|
|
18
|
+
</Specimen>
|
|
19
|
+
<Specimen label="StatusBar" block>
|
|
20
|
+
<StatusBar>
|
|
21
|
+
<StatusItem>feat/sale</StatusItem>
|
|
22
|
+
<StatusItem>Unsaved</StatusItem>
|
|
23
|
+
</StatusBar>
|
|
24
|
+
</Specimen>
|
|
25
|
+
<Specimen label="danger" block>
|
|
26
|
+
<StatusBar>
|
|
27
|
+
<StatusItem tone="danger">Save failed</StatusItem>
|
|
28
|
+
</StatusBar>
|
|
29
|
+
</Specimen>
|
|
30
|
+
<Specimen label="success">
|
|
31
|
+
<Text tone="success">Success</Text>
|
|
32
|
+
</Specimen>
|
|
33
|
+
<Specimen label="danger">
|
|
34
|
+
<Text tone="danger">Danger</Text>
|
|
35
|
+
</Specimen>
|
|
36
|
+
</Columns>
|
|
37
|
+
</Topic>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/fields.tsx
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Field } from "../../ui/Field";
|
|
4
|
+
import { Input, Select, Textarea } from "../../ui/Input";
|
|
5
|
+
import { Columns, Specimen, Topic } from "./topic";
|
|
6
|
+
import { UI_KIT_TOPICS } from "./topics";
|
|
7
|
+
|
|
8
|
+
const topic = UI_KIT_TOPICS.find((item) => item.slug === "fields")!;
|
|
9
|
+
|
|
10
|
+
export function FieldsTopic() {
|
|
11
|
+
const [locale, setLocale] = useState("en");
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Topic title={topic.title} lede={topic.lede}>
|
|
15
|
+
<Columns>
|
|
16
|
+
<Specimen label="Input" block>
|
|
17
|
+
<Field label="Text" hint="Shown under the field.">
|
|
18
|
+
<Input defaultValue="Ada Lovelace" autoComplete="off" spellCheck={false} />
|
|
19
|
+
</Field>
|
|
20
|
+
</Specimen>
|
|
21
|
+
<Specimen label="disabled" block>
|
|
22
|
+
<Field label="Disabled">
|
|
23
|
+
<Input defaultValue="Locked" disabled />
|
|
24
|
+
</Field>
|
|
25
|
+
</Specimen>
|
|
26
|
+
<Specimen label="Select" block>
|
|
27
|
+
<Field label="Select">
|
|
28
|
+
<Select
|
|
29
|
+
aria-label="Locale"
|
|
30
|
+
value={locale}
|
|
31
|
+
onValueChange={(next) => setLocale(next ?? "en")}
|
|
32
|
+
options={[
|
|
33
|
+
{ value: "en", label: "English (en)" },
|
|
34
|
+
{ value: "da", label: "Dansk (da)" },
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
</Field>
|
|
38
|
+
</Specimen>
|
|
39
|
+
<Specimen label="Textarea" block>
|
|
40
|
+
<Field label="Body">
|
|
41
|
+
<Textarea mono rows={4} defaultValue={"---\ntitle: Hello\n---\n\nParagraph."} />
|
|
42
|
+
</Field>
|
|
43
|
+
</Specimen>
|
|
44
|
+
</Columns>
|
|
45
|
+
</Topic>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/lists.tsx
|
|
2
|
+
import { List, ListRow } from "../../ui/ListRow";
|
|
3
|
+
import { Specimen, Topic } from "./topic";
|
|
4
|
+
import { UI_KIT_TOPICS } from "./topics";
|
|
5
|
+
|
|
6
|
+
const topic = UI_KIT_TOPICS.find((item) => item.slug === "lists")!;
|
|
7
|
+
|
|
8
|
+
export function ListsTopic() {
|
|
9
|
+
return (
|
|
10
|
+
<Topic title={topic.title} lede={topic.lede}>
|
|
11
|
+
<Specimen label="List" block>
|
|
12
|
+
<List>
|
|
13
|
+
<ListRow to="/__ui/lists" title="hello" meta="en" />
|
|
14
|
+
<ListRow to="/__ui/lists" title="about" meta="da" />
|
|
15
|
+
</List>
|
|
16
|
+
</Specimen>
|
|
17
|
+
</Topic>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/overlays.tsx
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button } from "../../ui/Button";
|
|
4
|
+
import { ConfirmDialog } from "../../ui/ConfirmDialog";
|
|
5
|
+
import { Tooltip, TooltipProvider } from "../../ui/Tooltip";
|
|
6
|
+
import { Columns, Specimen, Topic } from "./topic";
|
|
7
|
+
import { UI_KIT_TOPICS } from "./topics";
|
|
8
|
+
|
|
9
|
+
const topic = UI_KIT_TOPICS.find((item) => item.slug === "overlays")!;
|
|
10
|
+
|
|
11
|
+
export function OverlaysTopic() {
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<TooltipProvider>
|
|
16
|
+
<Topic title={topic.title} lede={topic.lede}>
|
|
17
|
+
<Columns>
|
|
18
|
+
<Specimen label="ConfirmDialog">
|
|
19
|
+
<Button onClick={() => setOpen(true)}>Open dialog</Button>
|
|
20
|
+
<ConfirmDialog
|
|
21
|
+
open={open}
|
|
22
|
+
onOpenChange={setOpen}
|
|
23
|
+
title="Delete this sample?"
|
|
24
|
+
confirmLabel="Delete"
|
|
25
|
+
cancelLabel="Cancel"
|
|
26
|
+
destructive
|
|
27
|
+
onConfirm={() => setOpen(false)}
|
|
28
|
+
/>
|
|
29
|
+
</Specimen>
|
|
30
|
+
<Specimen label="Tooltip">
|
|
31
|
+
<Tooltip label="Sample tooltip">
|
|
32
|
+
<Button variant="secondary">Hover</Button>
|
|
33
|
+
</Tooltip>
|
|
34
|
+
</Specimen>
|
|
35
|
+
</Columns>
|
|
36
|
+
</Topic>
|
|
37
|
+
</TooltipProvider>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/path.ts
|
|
2
|
+
import { isStudioDev } from "../../studio-config";
|
|
3
|
+
|
|
4
|
+
/** Dev-only catalog: `/__ui` or `/__ui/` plus one segment. */
|
|
5
|
+
export function isUiKitPath(pathname: string) {
|
|
6
|
+
if (!isStudioDev()) return false;
|
|
7
|
+
const path = pathname.replace(/\/$/, "") || "/";
|
|
8
|
+
return path === "/__ui" || /^\/__ui\/[^/]+$/.test(path);
|
|
9
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/sections.tsx
|
|
2
|
+
import { BackLink } from "../../ui/BackLink";
|
|
3
|
+
import { Button } from "../../ui/Button";
|
|
4
|
+
import { PageHeader, Panel, Stack } from "../../ui/Page";
|
|
5
|
+
import { Text } from "../../ui/Text";
|
|
6
|
+
import { Specimen, Topic } from "./topic";
|
|
7
|
+
import { UI_KIT_TOPICS } from "./topics";
|
|
8
|
+
|
|
9
|
+
const topic = UI_KIT_TOPICS.find((item) => item.slug === "sections")!;
|
|
10
|
+
|
|
11
|
+
export function SectionsTopic() {
|
|
12
|
+
return (
|
|
13
|
+
<Topic title={topic.title} lede={topic.lede}>
|
|
14
|
+
<Specimen label="PageHeader" block>
|
|
15
|
+
<PageHeader
|
|
16
|
+
kicker={<BackLink to="/__ui/sections">Sections</BackLink>}
|
|
17
|
+
title="Documents"
|
|
18
|
+
description="Sample page header with action."
|
|
19
|
+
action={<Button>New</Button>}
|
|
20
|
+
/>
|
|
21
|
+
</Specimen>
|
|
22
|
+
<Specimen label="Panel" block>
|
|
23
|
+
<Panel>
|
|
24
|
+
<Stack>
|
|
25
|
+
<Text>First line</Text>
|
|
26
|
+
<Text>Second line</Text>
|
|
27
|
+
</Stack>
|
|
28
|
+
</Panel>
|
|
29
|
+
</Specimen>
|
|
30
|
+
</Topic>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/surfaces.tsx
|
|
2
|
+
import * as stylex from "@stylexjs/stylex";
|
|
3
|
+
import { colors, radius, space } from "../../styles/tokens.stylex";
|
|
4
|
+
import { Text } from "../../ui/Text";
|
|
5
|
+
import { Specimen, Topic } from "./topic";
|
|
6
|
+
import { UI_KIT_TOPICS } from "./topics";
|
|
7
|
+
|
|
8
|
+
const topic = UI_KIT_TOPICS.find((item) => item.slug === "surfaces")!;
|
|
9
|
+
|
|
10
|
+
export function SurfacesTopic() {
|
|
11
|
+
return (
|
|
12
|
+
<Topic title={topic.title} lede={topic.lede}>
|
|
13
|
+
<Specimen label="Space">
|
|
14
|
+
<div {...stylex.props(styles.scale)}>
|
|
15
|
+
<Scale label="xs" bar={styles.xs} />
|
|
16
|
+
<Scale label="sm" bar={styles.sm} />
|
|
17
|
+
<Scale label="md" bar={styles.md} />
|
|
18
|
+
<Scale label="lg" bar={styles.lg} />
|
|
19
|
+
<Scale label="xl" bar={styles.xl} />
|
|
20
|
+
<Scale label="xxl" bar={styles.xxl} />
|
|
21
|
+
</div>
|
|
22
|
+
</Specimen>
|
|
23
|
+
<Specimen label="Radius">
|
|
24
|
+
<div {...stylex.props(styles.scale)}>
|
|
25
|
+
<Scale label="sm" box={styles.radiusSm} />
|
|
26
|
+
<Scale label="md" box={styles.radiusMd} />
|
|
27
|
+
</div>
|
|
28
|
+
</Specimen>
|
|
29
|
+
<Specimen label="Backgrounds">
|
|
30
|
+
<div {...stylex.props(styles.role, styles.app)}>
|
|
31
|
+
<Text size="xs" tone="muted" mono>
|
|
32
|
+
bgApp
|
|
33
|
+
</Text>
|
|
34
|
+
<div {...stylex.props(styles.role, styles.sidebar)}>
|
|
35
|
+
<Text size="xs" tone="muted" mono>
|
|
36
|
+
bgSidebar
|
|
37
|
+
</Text>
|
|
38
|
+
<div {...stylex.props(styles.role, styles.panel)}>
|
|
39
|
+
<Text size="xs" tone="muted" mono>
|
|
40
|
+
bgPanel
|
|
41
|
+
</Text>
|
|
42
|
+
<div {...stylex.props(styles.role, styles.surface)}>
|
|
43
|
+
<Text size="xs" mono>
|
|
44
|
+
bgSurface
|
|
45
|
+
</Text>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</Specimen>
|
|
51
|
+
</Topic>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Scale({
|
|
56
|
+
label,
|
|
57
|
+
bar,
|
|
58
|
+
box,
|
|
59
|
+
}: {
|
|
60
|
+
label: string;
|
|
61
|
+
bar?: stylex.StyleXStyles;
|
|
62
|
+
box?: stylex.StyleXStyles;
|
|
63
|
+
}) {
|
|
64
|
+
return (
|
|
65
|
+
<div {...stylex.props(styles.tick)}>
|
|
66
|
+
<div {...stylex.props(bar ? styles.bar : styles.radiusBox, bar, box)} />
|
|
67
|
+
<Text size="xs" tone="muted" mono>
|
|
68
|
+
{label}
|
|
69
|
+
</Text>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const styles = stylex.create({
|
|
75
|
+
bar: {
|
|
76
|
+
height: "16px",
|
|
77
|
+
backgroundColor: colors.accent,
|
|
78
|
+
borderRadius: radius.sm,
|
|
79
|
+
},
|
|
80
|
+
scale: {
|
|
81
|
+
display: "flex",
|
|
82
|
+
flexWrap: "wrap",
|
|
83
|
+
alignItems: "flex-end",
|
|
84
|
+
gap: space.xl,
|
|
85
|
+
},
|
|
86
|
+
tick: {
|
|
87
|
+
display: "flex",
|
|
88
|
+
flexDirection: "column",
|
|
89
|
+
alignItems: "flex-start",
|
|
90
|
+
gap: space.sm,
|
|
91
|
+
},
|
|
92
|
+
xs: { width: space.xs },
|
|
93
|
+
sm: { width: space.sm },
|
|
94
|
+
md: { width: space.md },
|
|
95
|
+
lg: { width: space.lg },
|
|
96
|
+
xl: { width: space.xl },
|
|
97
|
+
xxl: { width: space.xxl },
|
|
98
|
+
radiusBox: {
|
|
99
|
+
width: "64px",
|
|
100
|
+
height: "40px",
|
|
101
|
+
backgroundColor: colors.bgSurface,
|
|
102
|
+
borderWidth: 1,
|
|
103
|
+
borderStyle: "solid",
|
|
104
|
+
borderColor: colors.border,
|
|
105
|
+
},
|
|
106
|
+
radiusSm: { borderRadius: radius.sm },
|
|
107
|
+
radiusMd: { borderRadius: radius.md },
|
|
108
|
+
role: {
|
|
109
|
+
padding: space.lg,
|
|
110
|
+
borderRadius: radius.md,
|
|
111
|
+
},
|
|
112
|
+
app: { backgroundColor: colors.bgApp },
|
|
113
|
+
sidebar: { backgroundColor: colors.bgSidebar },
|
|
114
|
+
panel: { backgroundColor: colors.bgPanel },
|
|
115
|
+
surface: {
|
|
116
|
+
backgroundColor: colors.bgSurface,
|
|
117
|
+
borderWidth: 1,
|
|
118
|
+
borderStyle: "solid",
|
|
119
|
+
borderColor: colors.border,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/topic.tsx
|
|
2
|
+
import * as stylex from "@stylexjs/stylex";
|
|
3
|
+
import { colors, space, type } from "../../styles/tokens.stylex";
|
|
4
|
+
import { Text } from "../../ui/Text";
|
|
5
|
+
|
|
6
|
+
export function Topic({
|
|
7
|
+
title,
|
|
8
|
+
lede,
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
title: string;
|
|
12
|
+
lede: string;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<div {...stylex.props(styles.topic)}>
|
|
17
|
+
<header {...stylex.props(styles.intro)}>
|
|
18
|
+
<Text as="h1" size="display" weight="semibold">
|
|
19
|
+
{title}
|
|
20
|
+
</Text>
|
|
21
|
+
<Text tone="muted" size="sm">
|
|
22
|
+
{lede}
|
|
23
|
+
</Text>
|
|
24
|
+
</header>
|
|
25
|
+
<div {...stylex.props(styles.body)}>{children}</div>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Two aligned columns. Sections share a top edge across the row. */
|
|
31
|
+
export function Columns({ children }: { children: React.ReactNode }) {
|
|
32
|
+
return <div {...stylex.props(styles.columns)}>{children}</div>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function Row({ children }: { children: React.ReactNode }) {
|
|
36
|
+
return <div {...stylex.props(styles.row)}>{children}</div>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function Specimen({
|
|
40
|
+
label,
|
|
41
|
+
children,
|
|
42
|
+
}: {
|
|
43
|
+
label: string;
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
/** Kept so older call sites still compile. Sections always stack. */
|
|
46
|
+
block?: boolean;
|
|
47
|
+
}) {
|
|
48
|
+
return (
|
|
49
|
+
<section {...stylex.props(styles.specimen)}>
|
|
50
|
+
<h2 {...stylex.props(styles.heading)}>{label}</h2>
|
|
51
|
+
<div {...stylex.props(styles.example)}>{children}</div>
|
|
52
|
+
</section>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const styles = stylex.create({
|
|
57
|
+
topic: {
|
|
58
|
+
display: "flex",
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
gap: "40px",
|
|
61
|
+
maxWidth: "920px",
|
|
62
|
+
},
|
|
63
|
+
intro: {
|
|
64
|
+
display: "flex",
|
|
65
|
+
flexDirection: "column",
|
|
66
|
+
gap: space.md,
|
|
67
|
+
maxWidth: "62ch",
|
|
68
|
+
},
|
|
69
|
+
body: {
|
|
70
|
+
display: "flex",
|
|
71
|
+
flexDirection: "column",
|
|
72
|
+
gap: "40px",
|
|
73
|
+
},
|
|
74
|
+
columns: {
|
|
75
|
+
display: "grid",
|
|
76
|
+
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
77
|
+
columnGap: "48px",
|
|
78
|
+
rowGap: "40px",
|
|
79
|
+
alignItems: "start",
|
|
80
|
+
"@media (max-width: 800px)": {
|
|
81
|
+
gridTemplateColumns: "1fr",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
specimen: {
|
|
85
|
+
display: "flex",
|
|
86
|
+
flexDirection: "column",
|
|
87
|
+
gap: space.md,
|
|
88
|
+
minWidth: 0,
|
|
89
|
+
},
|
|
90
|
+
heading: {
|
|
91
|
+
margin: 0,
|
|
92
|
+
fontSize: type.lg,
|
|
93
|
+
fontWeight: 600,
|
|
94
|
+
letterSpacing: "-0.01em",
|
|
95
|
+
lineHeight: 1.3,
|
|
96
|
+
color: colors.text,
|
|
97
|
+
},
|
|
98
|
+
example: {
|
|
99
|
+
display: "flex",
|
|
100
|
+
flexDirection: "column",
|
|
101
|
+
alignItems: "stretch",
|
|
102
|
+
gap: space.sm,
|
|
103
|
+
width: "100%",
|
|
104
|
+
minWidth: 0,
|
|
105
|
+
},
|
|
106
|
+
row: {
|
|
107
|
+
display: "flex",
|
|
108
|
+
flexWrap: "wrap",
|
|
109
|
+
alignItems: "center",
|
|
110
|
+
gap: space.sm,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// packages/studio/src/pages/ui-kit/topics.ts
|
|
2
|
+
|
|
3
|
+
export const UI_KIT_TOPICS = [
|
|
4
|
+
{
|
|
5
|
+
slug: "colors",
|
|
6
|
+
title: "Colors",
|
|
7
|
+
group: "Foundations",
|
|
8
|
+
lede: "Color tokens paint surfaces, text, and status, and follow the active theme.",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
slug: "type",
|
|
12
|
+
title: "Type",
|
|
13
|
+
group: "Foundations",
|
|
14
|
+
lede: "Text sizes and tones for titles, body copy, and metadata.",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
slug: "surfaces",
|
|
18
|
+
title: "Surfaces",
|
|
19
|
+
group: "Foundations",
|
|
20
|
+
lede: "Spacing, radii, and the background roles stacked the way a page stacks them.",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
slug: "sections",
|
|
24
|
+
title: "Sections",
|
|
25
|
+
group: "Foundations",
|
|
26
|
+
lede: "Page header, panel, and stack are how a settings page is built.",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
slug: "buttons",
|
|
30
|
+
title: "Buttons",
|
|
31
|
+
group: "Components",
|
|
32
|
+
lede: "Variants for a primary action, a secondary action, a quiet action, and danger.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
slug: "fields",
|
|
36
|
+
title: "Fields",
|
|
37
|
+
group: "Components",
|
|
38
|
+
lede: "Text, select, and multiline fields, including a hint and a disabled control.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
slug: "lists",
|
|
42
|
+
title: "Lists",
|
|
43
|
+
group: "Components",
|
|
44
|
+
lede: "A list of rows that link to a document.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
slug: "overlays",
|
|
48
|
+
title: "Overlays",
|
|
49
|
+
group: "Components",
|
|
50
|
+
lede: "A confirm dialog and a tooltip.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
slug: "feedback",
|
|
54
|
+
title: "Feedback",
|
|
55
|
+
group: "Components",
|
|
56
|
+
lede: "Status bars, and the success and danger text tones.",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
slug: "devbar",
|
|
60
|
+
title: "DevBar",
|
|
61
|
+
group: "Host",
|
|
62
|
+
lede: "The host bar a site mounts. These specimens do not call the preview API.",
|
|
63
|
+
},
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
export type UiKitTopicSlug = (typeof UI_KIT_TOPICS)[number]["slug"];
|
|
67
|
+
|
|
68
|
+
export function resolveUiKitTopic(slug: string | undefined): UiKitTopicSlug {
|
|
69
|
+
const match = UI_KIT_TOPICS.find((topic) => topic.slug === slug);
|
|
70
|
+
return match ? match.slug : "colors";
|
|
71
|
+
}
|