@backmatter/studio 0.0.7 → 0.0.9
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-b8qFsmA-.js +2401 -0
- package/dist/chunks/WorkspaceRoutes-BKKEkAq7.js +90126 -0
- package/dist/chunks/{dashboard-DCHRI0WD.js → dashboard-BNUcbYU0.js} +380 -382
- package/dist/chunks/{index-BS0-Bvbk.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 +30896 -630
- package/dist/studio.css +303 -98
- 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/AppShell.tsx +47 -10
- package/src/chrome/AppSidebar.tsx +9 -34
- package/src/chrome/ContentExplorerSidebar.tsx +149 -96
- package/src/chrome/UserMenu.tsx +3 -1
- package/src/chrome/content-tree-visibility.ts +55 -0
- package/src/chrome/explorer-icons.tsx +2 -4
- 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 +144 -85
- package/src/pages/MediaLightbox.tsx +195 -96
- package/src/pages/SelectRepositoriesLink.tsx +28 -4
- package/src/pages/SettingsGeneralPage.tsx +25 -28
- package/src/pages/SettingsGithubPage.tsx +413 -124
- package/src/pages/SettingsUsagePage.tsx +23 -1
- package/src/pages/media-grid.ts +14 -1
- 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 +93 -49
- package/src/shell-resets.css +155 -0
- package/src/site-bar.tsx +1791 -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 +18 -1
- package/dist/chunks/EmbedPublishPage-88VsmOhi.js +0 -149
- package/dist/chunks/UiKitPage-X4hkFL2d.js +0 -173
- package/dist/chunks/WorkspaceRoutes-DXzdWqN3.js +0 -59523
- package/dist/chunks/status-bar-action-wj1KZ51m.js +0 -30612
- package/src/pages/UiKitPage.tsx +0 -331
|
@@ -9,16 +9,15 @@ import {
|
|
|
9
9
|
unbindGithub,
|
|
10
10
|
} from "../api";
|
|
11
11
|
import { Button, ButtonLink } from "../ui/Button";
|
|
12
|
-
import { Field } from "../ui/Field";
|
|
13
12
|
import { Input } from "../ui/Input";
|
|
14
13
|
import { ConfirmDialog } from "../ui/ConfirmDialog";
|
|
15
|
-
import { PageHeader
|
|
14
|
+
import { PageHeader } from "../ui/Page";
|
|
16
15
|
import { Text } from "../ui/Text";
|
|
17
16
|
import * as stylex from "@stylexjs/stylex";
|
|
18
17
|
import { colors, space, type } from "../styles/tokens.stylex";
|
|
19
18
|
import type { WorkspaceOutletContext } from "../workspace/types";
|
|
20
19
|
import { githubConnectPath } from "../workspace-path";
|
|
21
|
-
import {
|
|
20
|
+
import { GithubRepositoryAccess } from "./SelectRepositoriesLink";
|
|
22
21
|
|
|
23
22
|
export function SettingsGithubPage() {
|
|
24
23
|
const { workspace, reload } = useOutletContext<WorkspaceOutletContext>();
|
|
@@ -34,6 +33,7 @@ export function SettingsGithubPage() {
|
|
|
34
33
|
const [directory, setDirectory] = useState(workspace.githubDirectory ?? "");
|
|
35
34
|
const [directoryError, setDirectoryError] = useState<string | null>(null);
|
|
36
35
|
const [savingDirectory, setSavingDirectory] = useState(false);
|
|
36
|
+
const directoryDirty = directory !== (workspace.githubDirectory ?? "");
|
|
37
37
|
|
|
38
38
|
useEffect(() => {
|
|
39
39
|
setDirectory(workspace.githubDirectory ?? "");
|
|
@@ -119,121 +119,36 @@ export function SettingsGithubPage() {
|
|
|
119
119
|
<Text tone="danger">{error}</Text>
|
|
120
120
|
</div>
|
|
121
121
|
) : null}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
<Input
|
|
153
|
-
value={directory}
|
|
154
|
-
onChange={(e) => setDirectory(e.target.value)}
|
|
155
|
-
/>
|
|
156
|
-
</Field>
|
|
157
|
-
{directoryError ? (
|
|
158
|
-
<Text tone="danger">{directoryError}</Text>
|
|
159
|
-
) : null}
|
|
160
|
-
<div>
|
|
161
|
-
<Button type="submit" disabled={savingDirectory}>
|
|
162
|
-
{savingDirectory ? "Saving…" : "Save"}
|
|
163
|
-
</Button>
|
|
164
|
-
</div>
|
|
165
|
-
</Stack>
|
|
166
|
-
</form>
|
|
167
|
-
) : null}
|
|
168
|
-
{canEdit ? (
|
|
169
|
-
<div {...stylex.props(styles.actions)}>
|
|
170
|
-
<ButtonLink
|
|
171
|
-
variant="secondary"
|
|
172
|
-
to={githubConnectPath(workspace.slug)}
|
|
173
|
-
>
|
|
174
|
-
Change repository
|
|
175
|
-
</ButtonLink>
|
|
176
|
-
<SelectRepositoriesLink workspaceId={workspace.id} />
|
|
177
|
-
<Button
|
|
178
|
-
variant="danger"
|
|
179
|
-
disabled={disconnecting}
|
|
180
|
-
onClick={() => setConfirmDisconnect(true)}
|
|
181
|
-
>
|
|
182
|
-
Disconnect
|
|
183
|
-
</Button>
|
|
184
|
-
</div>
|
|
185
|
-
) : null}
|
|
186
|
-
</Stack>
|
|
187
|
-
) : (
|
|
188
|
-
<Stack>
|
|
189
|
-
<Text tone="muted" size="sm">
|
|
190
|
-
{workspace.githubInstallationId
|
|
191
|
-
? "The list only includes repositories this GitHub App can access. Select repositories on GitHub to grant more. Saving there does not return to Backmatter."
|
|
192
|
-
: "Connect finds the backmatter CMS installation on your GitHub account and opens the repository list here. Saving on GitHub does not return to Backmatter."}
|
|
193
|
-
</Text>
|
|
194
|
-
{canEdit ? (
|
|
195
|
-
<div {...stylex.props(styles.actions)}>
|
|
196
|
-
{workspace.githubInstallationId ? (
|
|
197
|
-
<ButtonLink to={githubConnectPath(workspace.slug)}>
|
|
198
|
-
Choose repository
|
|
199
|
-
</ButtonLink>
|
|
200
|
-
) : choices ? (
|
|
201
|
-
choices.map((choice) => (
|
|
202
|
-
<Button
|
|
203
|
-
key={choice.id}
|
|
204
|
-
disabled={connecting}
|
|
205
|
-
onClick={() => void onConnect(choice.id)}
|
|
206
|
-
>
|
|
207
|
-
{choice.account ?? choice.id}
|
|
208
|
-
</Button>
|
|
209
|
-
))
|
|
210
|
-
) : (
|
|
211
|
-
<Button disabled={connecting} onClick={() => void onConnect()}>
|
|
212
|
-
{connecting ? "Connecting…" : "Connect GitHub"}
|
|
213
|
-
</Button>
|
|
214
|
-
)}
|
|
215
|
-
{workspace.githubInstallationId ? (
|
|
216
|
-
<SelectRepositoriesLink workspaceId={workspace.id} />
|
|
217
|
-
) : null}
|
|
218
|
-
{installUrl ? (
|
|
219
|
-
<ButtonLink href={installUrl} newTab={false} variant="secondary">
|
|
220
|
-
Install on GitHub
|
|
221
|
-
</ButtonLink>
|
|
222
|
-
) : null}
|
|
223
|
-
{workspace.githubInstallationId ? (
|
|
224
|
-
<Button
|
|
225
|
-
variant="danger"
|
|
226
|
-
disabled={disconnecting}
|
|
227
|
-
onClick={() => setConfirmDisconnect(true)}
|
|
228
|
-
>
|
|
229
|
-
Disconnect
|
|
230
|
-
</Button>
|
|
231
|
-
) : null}
|
|
232
|
-
</div>
|
|
233
|
-
) : null}
|
|
234
|
-
</Stack>
|
|
235
|
-
)}
|
|
236
|
-
</Panel>
|
|
122
|
+
{connected ? (
|
|
123
|
+
<ConnectedGithub
|
|
124
|
+
repo={workspace.githubRepoFullName ?? ""}
|
|
125
|
+
branch={workspace.githubDefaultBranch ?? ""}
|
|
126
|
+
directory={directory}
|
|
127
|
+
directoryDirty={directoryDirty}
|
|
128
|
+
directoryError={directoryError}
|
|
129
|
+
savingDirectory={savingDirectory}
|
|
130
|
+
canEdit={canEdit}
|
|
131
|
+
changeTo={githubConnectPath(workspace.slug)}
|
|
132
|
+
workspaceId={workspace.id}
|
|
133
|
+
disconnecting={disconnecting}
|
|
134
|
+
onDirectoryChange={setDirectory}
|
|
135
|
+
onSaveDirectory={(e) => void onSaveDirectory(e)}
|
|
136
|
+
onDisconnect={() => setConfirmDisconnect(true)}
|
|
137
|
+
/>
|
|
138
|
+
) : (
|
|
139
|
+
<DisconnectedGithub
|
|
140
|
+
canEdit={canEdit}
|
|
141
|
+
connecting={connecting}
|
|
142
|
+
choices={choices}
|
|
143
|
+
installUrl={installUrl}
|
|
144
|
+
hasInstallation={Boolean(workspace.githubInstallationId)}
|
|
145
|
+
chooseTo={githubConnectPath(workspace.slug)}
|
|
146
|
+
workspaceId={workspace.id}
|
|
147
|
+
disconnecting={disconnecting}
|
|
148
|
+
onConnect={(id) => void onConnect(id)}
|
|
149
|
+
onDisconnect={() => setConfirmDisconnect(true)}
|
|
150
|
+
/>
|
|
151
|
+
)}
|
|
237
152
|
<ConfirmDialog
|
|
238
153
|
open={confirmDisconnect}
|
|
239
154
|
onOpenChange={(open) => {
|
|
@@ -254,28 +169,402 @@ export function SettingsGithubPage() {
|
|
|
254
169
|
);
|
|
255
170
|
}
|
|
256
171
|
|
|
172
|
+
function ConnectedGithub({
|
|
173
|
+
repo,
|
|
174
|
+
branch,
|
|
175
|
+
directory,
|
|
176
|
+
directoryDirty,
|
|
177
|
+
directoryError,
|
|
178
|
+
savingDirectory,
|
|
179
|
+
canEdit,
|
|
180
|
+
changeTo,
|
|
181
|
+
workspaceId,
|
|
182
|
+
disconnecting,
|
|
183
|
+
onDirectoryChange,
|
|
184
|
+
onSaveDirectory,
|
|
185
|
+
onDisconnect,
|
|
186
|
+
}: {
|
|
187
|
+
repo: string;
|
|
188
|
+
branch: string;
|
|
189
|
+
directory: string;
|
|
190
|
+
directoryDirty: boolean;
|
|
191
|
+
directoryError: string | null;
|
|
192
|
+
savingDirectory: boolean;
|
|
193
|
+
canEdit: boolean;
|
|
194
|
+
changeTo: string;
|
|
195
|
+
workspaceId: string;
|
|
196
|
+
disconnecting: boolean;
|
|
197
|
+
onDirectoryChange: (value: string) => void;
|
|
198
|
+
onSaveDirectory: (e: React.FormEvent) => void;
|
|
199
|
+
onDisconnect: () => void;
|
|
200
|
+
}) {
|
|
201
|
+
return (
|
|
202
|
+
<div {...stylex.props(styles.groups)}>
|
|
203
|
+
<SettingsGroup title="Connection" titleId="github-connection">
|
|
204
|
+
<SettingsRow
|
|
205
|
+
label="Repository"
|
|
206
|
+
control={
|
|
207
|
+
<>
|
|
208
|
+
<span {...stylex.props(styles.value)}>{repo}</span>
|
|
209
|
+
{canEdit ? (
|
|
210
|
+
<ButtonLink variant="secondary" to={changeTo}>
|
|
211
|
+
Change
|
|
212
|
+
</ButtonLink>
|
|
213
|
+
) : null}
|
|
214
|
+
</>
|
|
215
|
+
}
|
|
216
|
+
/>
|
|
217
|
+
<SettingsRow
|
|
218
|
+
label="Branch"
|
|
219
|
+
control={<span {...stylex.props(styles.value)}>{branch}</span>}
|
|
220
|
+
/>
|
|
221
|
+
<SettingsRow
|
|
222
|
+
label="Directory"
|
|
223
|
+
htmlFor={canEdit ? "github-directory" : undefined}
|
|
224
|
+
description={
|
|
225
|
+
<>
|
|
226
|
+
Folder inside the repository. Empty means the repository root.
|
|
227
|
+
{directoryError ? <Text tone="danger">{directoryError}</Text> : null}
|
|
228
|
+
</>
|
|
229
|
+
}
|
|
230
|
+
control={
|
|
231
|
+
canEdit ? (
|
|
232
|
+
<form onSubmit={onSaveDirectory} {...stylex.props(styles.directoryForm)}>
|
|
233
|
+
<div {...stylex.props(styles.directoryField)}>
|
|
234
|
+
<Input
|
|
235
|
+
id="github-directory"
|
|
236
|
+
value={directory}
|
|
237
|
+
spellCheck={false}
|
|
238
|
+
autoCapitalize="off"
|
|
239
|
+
autoCorrect="off"
|
|
240
|
+
aria-invalid={directoryError ? true : undefined}
|
|
241
|
+
onChange={(e) => onDirectoryChange(e.target.value)}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
{directoryDirty ? (
|
|
245
|
+
<Button type="submit" disabled={savingDirectory}>
|
|
246
|
+
{savingDirectory ? "Saving…" : "Save"}
|
|
247
|
+
</Button>
|
|
248
|
+
) : null}
|
|
249
|
+
</form>
|
|
250
|
+
) : (
|
|
251
|
+
<span {...stylex.props(styles.value)}>
|
|
252
|
+
{directory || "Repository root"}
|
|
253
|
+
</span>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
/>
|
|
257
|
+
</SettingsGroup>
|
|
258
|
+
{canEdit ? (
|
|
259
|
+
<SettingsGroup title="Access" titleId="github-access">
|
|
260
|
+
<SettingsRow
|
|
261
|
+
label="Repository access"
|
|
262
|
+
description="Repositories the app can see on GitHub. Saving there does not return here."
|
|
263
|
+
control={
|
|
264
|
+
<GithubRepositoryAccess workspaceId={workspaceId}>
|
|
265
|
+
{(url) => (
|
|
266
|
+
<ButtonLink href={url} variant="secondary">
|
|
267
|
+
Manage on GitHub
|
|
268
|
+
</ButtonLink>
|
|
269
|
+
)}
|
|
270
|
+
</GithubRepositoryAccess>
|
|
271
|
+
}
|
|
272
|
+
/>
|
|
273
|
+
</SettingsGroup>
|
|
274
|
+
) : null}
|
|
275
|
+
{canEdit ? (
|
|
276
|
+
<SettingsGroup separated>
|
|
277
|
+
<SettingsRow
|
|
278
|
+
label="Disconnect"
|
|
279
|
+
description="Stops commits and imports. Content already here stays."
|
|
280
|
+
control={
|
|
281
|
+
<Button variant="danger" disabled={disconnecting} onClick={onDisconnect}>
|
|
282
|
+
Disconnect
|
|
283
|
+
</Button>
|
|
284
|
+
}
|
|
285
|
+
/>
|
|
286
|
+
</SettingsGroup>
|
|
287
|
+
) : null}
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function DisconnectedGithub({
|
|
293
|
+
canEdit,
|
|
294
|
+
connecting,
|
|
295
|
+
choices,
|
|
296
|
+
installUrl,
|
|
297
|
+
hasInstallation,
|
|
298
|
+
chooseTo,
|
|
299
|
+
workspaceId,
|
|
300
|
+
disconnecting,
|
|
301
|
+
onConnect,
|
|
302
|
+
onDisconnect,
|
|
303
|
+
}: {
|
|
304
|
+
canEdit: boolean;
|
|
305
|
+
connecting: boolean;
|
|
306
|
+
choices: GithubInstallationChoice[] | null;
|
|
307
|
+
installUrl: string | null;
|
|
308
|
+
hasInstallation: boolean;
|
|
309
|
+
chooseTo: string;
|
|
310
|
+
workspaceId: string;
|
|
311
|
+
disconnecting: boolean;
|
|
312
|
+
onConnect: (installationId?: string) => void;
|
|
313
|
+
onDisconnect: () => void;
|
|
314
|
+
}) {
|
|
315
|
+
if (choices) {
|
|
316
|
+
return (
|
|
317
|
+
<SettingsGroup
|
|
318
|
+
title="GitHub accounts"
|
|
319
|
+
titleId="github-accounts"
|
|
320
|
+
description="Connect finds the backmatter CMS installation on your GitHub account and opens the repository list here. Saving on GitHub does not return to Backmatter."
|
|
321
|
+
>
|
|
322
|
+
{choices.map((choice) => (
|
|
323
|
+
<SettingsRow
|
|
324
|
+
key={choice.id}
|
|
325
|
+
label={choice.account ?? choice.id}
|
|
326
|
+
control={
|
|
327
|
+
canEdit ? (
|
|
328
|
+
<Button
|
|
329
|
+
variant="secondary"
|
|
330
|
+
disabled={connecting}
|
|
331
|
+
onClick={() => onConnect(choice.id)}
|
|
332
|
+
>
|
|
333
|
+
Connect
|
|
334
|
+
</Button>
|
|
335
|
+
) : null
|
|
336
|
+
}
|
|
337
|
+
/>
|
|
338
|
+
))}
|
|
339
|
+
</SettingsGroup>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (hasInstallation) {
|
|
344
|
+
return (
|
|
345
|
+
<div {...stylex.props(styles.groups)}>
|
|
346
|
+
<SettingsGroup title="Connection" titleId="github-connection">
|
|
347
|
+
<SettingsRow
|
|
348
|
+
label="Repository"
|
|
349
|
+
description="The list only includes repositories this GitHub App can access."
|
|
350
|
+
control={
|
|
351
|
+
canEdit ? (
|
|
352
|
+
<ButtonLink to={chooseTo}>Choose repository</ButtonLink>
|
|
353
|
+
) : null
|
|
354
|
+
}
|
|
355
|
+
/>
|
|
356
|
+
</SettingsGroup>
|
|
357
|
+
{canEdit ? (
|
|
358
|
+
<SettingsGroup title="Access" titleId="github-access">
|
|
359
|
+
<SettingsRow
|
|
360
|
+
label="Repository access"
|
|
361
|
+
description="Grant more repositories on GitHub. Saving there does not return here."
|
|
362
|
+
control={
|
|
363
|
+
<GithubRepositoryAccess workspaceId={workspaceId}>
|
|
364
|
+
{(url) => (
|
|
365
|
+
<ButtonLink href={url} variant="secondary">
|
|
366
|
+
Manage on GitHub
|
|
367
|
+
</ButtonLink>
|
|
368
|
+
)}
|
|
369
|
+
</GithubRepositoryAccess>
|
|
370
|
+
}
|
|
371
|
+
/>
|
|
372
|
+
</SettingsGroup>
|
|
373
|
+
) : null}
|
|
374
|
+
{canEdit ? (
|
|
375
|
+
<SettingsGroup separated>
|
|
376
|
+
<SettingsRow
|
|
377
|
+
label="Disconnect"
|
|
378
|
+
description="Removes the GitHub App from this workspace. You can connect again later."
|
|
379
|
+
control={
|
|
380
|
+
<Button
|
|
381
|
+
variant="danger"
|
|
382
|
+
disabled={disconnecting}
|
|
383
|
+
onClick={onDisconnect}
|
|
384
|
+
>
|
|
385
|
+
Disconnect
|
|
386
|
+
</Button>
|
|
387
|
+
}
|
|
388
|
+
/>
|
|
389
|
+
</SettingsGroup>
|
|
390
|
+
) : null}
|
|
391
|
+
</div>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return (
|
|
396
|
+
<SettingsGroup>
|
|
397
|
+
<SettingsRow
|
|
398
|
+
label="Not connected"
|
|
399
|
+
description="Connect finds the backmatter CMS installation on your GitHub account and opens the repository list here. Saving on GitHub does not return to Backmatter."
|
|
400
|
+
control={
|
|
401
|
+
canEdit ? (
|
|
402
|
+
installUrl ? (
|
|
403
|
+
<ButtonLink href={installUrl} newTab={false}>
|
|
404
|
+
Install on GitHub
|
|
405
|
+
</ButtonLink>
|
|
406
|
+
) : (
|
|
407
|
+
<Button disabled={connecting} onClick={() => onConnect()}>
|
|
408
|
+
{connecting ? "Connecting…" : "Connect GitHub"}
|
|
409
|
+
</Button>
|
|
410
|
+
)
|
|
411
|
+
) : null
|
|
412
|
+
}
|
|
413
|
+
/>
|
|
414
|
+
</SettingsGroup>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function SettingsGroup({
|
|
419
|
+
title,
|
|
420
|
+
titleId,
|
|
421
|
+
description,
|
|
422
|
+
separated,
|
|
423
|
+
children,
|
|
424
|
+
}: {
|
|
425
|
+
title?: string;
|
|
426
|
+
titleId?: string;
|
|
427
|
+
description?: string;
|
|
428
|
+
separated?: boolean;
|
|
429
|
+
children: React.ReactNode;
|
|
430
|
+
}) {
|
|
431
|
+
return (
|
|
432
|
+
<section
|
|
433
|
+
aria-labelledby={title ? titleId : undefined}
|
|
434
|
+
{...stylex.props(styles.group, separated && styles.groupSeparated)}
|
|
435
|
+
>
|
|
436
|
+
{title && titleId ? (
|
|
437
|
+
<h2 id={titleId} {...stylex.props(styles.groupTitle)}>
|
|
438
|
+
{title}
|
|
439
|
+
</h2>
|
|
440
|
+
) : null}
|
|
441
|
+
{description ? (
|
|
442
|
+
<p {...stylex.props(styles.groupDescription)}>{description}</p>
|
|
443
|
+
) : null}
|
|
444
|
+
<div {...stylex.props(styles.rows)}>{children}</div>
|
|
445
|
+
</section>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function SettingsRow({
|
|
450
|
+
label,
|
|
451
|
+
description,
|
|
452
|
+
htmlFor,
|
|
453
|
+
control,
|
|
454
|
+
}: {
|
|
455
|
+
label: string;
|
|
456
|
+
description?: React.ReactNode;
|
|
457
|
+
htmlFor?: string;
|
|
458
|
+
control?: React.ReactNode;
|
|
459
|
+
}) {
|
|
460
|
+
return (
|
|
461
|
+
<div {...stylex.props(styles.row)}>
|
|
462
|
+
<div {...stylex.props(styles.rowText)}>
|
|
463
|
+
{htmlFor ? (
|
|
464
|
+
<label htmlFor={htmlFor} {...stylex.props(styles.rowLabel)}>
|
|
465
|
+
{label}
|
|
466
|
+
</label>
|
|
467
|
+
) : (
|
|
468
|
+
<div {...stylex.props(styles.rowLabel)}>{label}</div>
|
|
469
|
+
)}
|
|
470
|
+
{description ? (
|
|
471
|
+
<div {...stylex.props(styles.rowDescription)}>{description}</div>
|
|
472
|
+
) : null}
|
|
473
|
+
</div>
|
|
474
|
+
{control ? <div {...stylex.props(styles.rowControl)}>{control}</div> : null}
|
|
475
|
+
</div>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
257
479
|
const styles = stylex.create({
|
|
258
|
-
|
|
480
|
+
groups: {
|
|
481
|
+
display: "flex",
|
|
482
|
+
flexDirection: "column",
|
|
483
|
+
gap: space.xxl,
|
|
484
|
+
},
|
|
485
|
+
group: {
|
|
486
|
+
display: "flex",
|
|
487
|
+
flexDirection: "column",
|
|
488
|
+
gap: space.xs,
|
|
489
|
+
},
|
|
490
|
+
groupSeparated: {
|
|
491
|
+
paddingTop: space.lg,
|
|
492
|
+
borderTopWidth: 1,
|
|
493
|
+
borderTopStyle: "solid",
|
|
494
|
+
borderTopColor: colors.border,
|
|
495
|
+
},
|
|
496
|
+
groupTitle: {
|
|
259
497
|
margin: 0,
|
|
498
|
+
fontSize: type.sm,
|
|
499
|
+
fontWeight: 500,
|
|
500
|
+
color: colors.textMuted,
|
|
501
|
+
},
|
|
502
|
+
groupDescription: {
|
|
503
|
+
margin: 0,
|
|
504
|
+
color: colors.textMuted,
|
|
505
|
+
fontSize: type.sm,
|
|
506
|
+
lineHeight: 1.45,
|
|
507
|
+
},
|
|
508
|
+
rows: {
|
|
260
509
|
display: "flex",
|
|
261
510
|
flexDirection: "column",
|
|
511
|
+
},
|
|
512
|
+
row: {
|
|
513
|
+
display: "flex",
|
|
514
|
+
alignItems: "center",
|
|
515
|
+
justifyContent: "space-between",
|
|
516
|
+
flexWrap: "wrap",
|
|
262
517
|
gap: space.lg,
|
|
518
|
+
minHeight: "44px",
|
|
519
|
+
paddingBlock: space.lg,
|
|
520
|
+
borderBottomWidth: 1,
|
|
521
|
+
borderBottomStyle: "solid",
|
|
522
|
+
borderBottomColor: colors.border,
|
|
523
|
+
":last-child": {
|
|
524
|
+
borderBottomWidth: 0,
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
rowText: {
|
|
528
|
+
flex: "1 1 16rem",
|
|
529
|
+
minWidth: 0,
|
|
530
|
+
display: "flex",
|
|
531
|
+
flexDirection: "column",
|
|
532
|
+
gap: space.xs,
|
|
533
|
+
},
|
|
534
|
+
rowLabel: {
|
|
263
535
|
fontSize: type.md,
|
|
536
|
+
fontWeight: 500,
|
|
537
|
+
color: colors.text,
|
|
264
538
|
},
|
|
265
|
-
|
|
539
|
+
rowDescription: {
|
|
540
|
+
display: "flex",
|
|
541
|
+
flexDirection: "column",
|
|
542
|
+
gap: space.xs,
|
|
266
543
|
color: colors.textMuted,
|
|
267
544
|
fontSize: type.sm,
|
|
545
|
+
lineHeight: 1.45,
|
|
268
546
|
},
|
|
269
|
-
|
|
270
|
-
|
|
547
|
+
rowControl: {
|
|
548
|
+
display: "flex",
|
|
549
|
+
alignItems: "center",
|
|
550
|
+
justifyContent: "flex-end",
|
|
551
|
+
flexShrink: 0,
|
|
552
|
+
gap: space.sm,
|
|
553
|
+
marginLeft: "auto",
|
|
554
|
+
},
|
|
555
|
+
value: {
|
|
556
|
+
fontSize: type.md,
|
|
271
557
|
fontWeight: 500,
|
|
558
|
+
color: colors.text,
|
|
272
559
|
},
|
|
273
|
-
|
|
560
|
+
directoryForm: {
|
|
274
561
|
display: "flex",
|
|
275
|
-
flexWrap: "wrap",
|
|
276
562
|
alignItems: "center",
|
|
277
563
|
gap: space.sm,
|
|
278
564
|
},
|
|
565
|
+
directoryField: {
|
|
566
|
+
width: "240px",
|
|
567
|
+
},
|
|
279
568
|
error: {
|
|
280
569
|
marginBottom: space.lg,
|
|
281
570
|
},
|
|
@@ -16,13 +16,18 @@ const SURFACES: { key: keyof WorkspaceUsage["dataRequests"]; label: string }[] =
|
|
|
16
16
|
{ key: "ai", label: "Assistant" },
|
|
17
17
|
];
|
|
18
18
|
|
|
19
|
+
const IMAGES: { key: "transformations" | "cachedServes" | "uncachedServes"; label: string }[] = [
|
|
20
|
+
{ key: "transformations", label: "Transformations" },
|
|
21
|
+
{ key: "cachedServes", label: "Cached serves" },
|
|
22
|
+
{ key: "uncachedServes", label: "Uncached serves" },
|
|
23
|
+
];
|
|
24
|
+
|
|
19
25
|
const WORK: { key: keyof WorkspaceUsage["work"]; label: string }[] = [
|
|
20
26
|
{ key: "commits", label: "Commits" },
|
|
21
27
|
{ key: "commitFiles", label: "Files committed" },
|
|
22
28
|
{ key: "imports", label: "Imports" },
|
|
23
29
|
{ key: "importFiles", label: "Files imported" },
|
|
24
30
|
{ key: "saves", label: "Saves" },
|
|
25
|
-
{ key: "mediaTransforms", label: "Image transforms" },
|
|
26
31
|
];
|
|
27
32
|
|
|
28
33
|
function formatBytes(value: number): string {
|
|
@@ -128,6 +133,23 @@ export function SettingsUsagePage() {
|
|
|
128
133
|
</div>
|
|
129
134
|
</dl>
|
|
130
135
|
</Panel>
|
|
136
|
+
<Panel>
|
|
137
|
+
<Text as="h2" size="lg" weight="medium">
|
|
138
|
+
Images
|
|
139
|
+
</Text>
|
|
140
|
+
<dl {...stylex.props(styles.list)}>
|
|
141
|
+
{IMAGES.map((item) => (
|
|
142
|
+
<div key={item.key} {...stylex.props(styles.row)}>
|
|
143
|
+
<dt {...stylex.props(styles.label)}>{item.label}</dt>
|
|
144
|
+
<dd {...stylex.props(styles.value)}>{formatCount(usage.images[item.key])}</dd>
|
|
145
|
+
</div>
|
|
146
|
+
))}
|
|
147
|
+
<div {...stylex.props(styles.row)}>
|
|
148
|
+
<dt {...stylex.props(styles.label)}>Transfer</dt>
|
|
149
|
+
<dd {...stylex.props(styles.value)}>{formatBytes(usage.images.bytes)}</dd>
|
|
150
|
+
</div>
|
|
151
|
+
</dl>
|
|
152
|
+
</Panel>
|
|
131
153
|
<Panel>
|
|
132
154
|
<Text as="h2" size="lg" weight="medium">
|
|
133
155
|
AI
|
package/src/pages/media-grid.ts
CHANGED
|
@@ -8,6 +8,8 @@ export type CatalogMedia = {
|
|
|
8
8
|
thumbUrl: string;
|
|
9
9
|
width: number | null;
|
|
10
10
|
height: number | null;
|
|
11
|
+
alt?: string;
|
|
12
|
+
caption?: string;
|
|
11
13
|
contentType?: string;
|
|
12
14
|
byteSize?: number;
|
|
13
15
|
createdAt?: string;
|
|
@@ -128,6 +130,17 @@ export function receivePage(
|
|
|
128
130
|
};
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
/** The saved alt text and caption replace that image wherever the grid is holding it. */
|
|
134
|
+
export function describeMedia(state: MediaGridState, media: CatalogMedia): MediaGridState {
|
|
135
|
+
return {
|
|
136
|
+
...state,
|
|
137
|
+
pending: state.pending.map((item) =>
|
|
138
|
+
item.media?.id === media.id ? { ...item, media } : item,
|
|
139
|
+
),
|
|
140
|
+
loaded: state.loaded.map((item) => (item.id === media.id ? media : item)),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
131
144
|
/** Drop a hidden row from the grid. A just-finished upload leaves its cell too. */
|
|
132
145
|
export function removeMedia(state: MediaGridState, id: string): MediaGridState {
|
|
133
146
|
const pending = state.pending.filter((item) => item.media?.id !== id);
|
|
@@ -142,7 +155,7 @@ export function removeMedia(state: MediaGridState, id: string): MediaGridState {
|
|
|
142
155
|
};
|
|
143
156
|
}
|
|
144
157
|
|
|
145
|
-
/**
|
|
158
|
+
/** Uploads plus catalog rows, including rows that have not been fetched yet. */
|
|
146
159
|
export function gridLength(state: MediaGridState): number {
|
|
147
160
|
const held = state.pending.filter((item) => item.media).length;
|
|
148
161
|
const catalog = Math.max(state.total - held, state.loaded.length);
|