@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
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// packages/studio/src/devbar-state.ts
|
|
2
|
+
// Which row the on-site bar shows, and the motion numbers the stylesheet shares.
|
|
3
|
+
|
|
4
|
+
export type DevbarMode = "preview" | "live";
|
|
5
|
+
|
|
6
|
+
export type DevbarStep = {
|
|
7
|
+
label: string;
|
|
8
|
+
state: "done" | "active" | "pending";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type DevbarActivity =
|
|
12
|
+
| { type: "editing"; field: string }
|
|
13
|
+
| { type: "publishing"; steps: readonly DevbarStep[]; progress: number }
|
|
14
|
+
| { type: "published"; sha: string; when: string };
|
|
15
|
+
|
|
16
|
+
export type DevbarRow = "idle" | "changes" | "editing" | "publishing" | "published";
|
|
17
|
+
|
|
18
|
+
export type DevbarChrome = {
|
|
19
|
+
mode: DevbarMode;
|
|
20
|
+
row: DevbarRow;
|
|
21
|
+
modeLabel: string;
|
|
22
|
+
announce: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const DEVBAR_EASE_OUT = "cubic-bezier(0.23, 1, 0.32, 1)";
|
|
26
|
+
export const DEVBAR_EASE_IN = "cubic-bezier(0.55, 0, 1, 0.45)";
|
|
27
|
+
export const DEVBAR_EASE_DRAWER = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
28
|
+
|
|
29
|
+
/** Entering the violet flood is slower than leaving it. */
|
|
30
|
+
export const DEVBAR_FLOOD_ENTER_MS = 550;
|
|
31
|
+
export const DEVBAR_FLOOD_EXIT_MS = 300;
|
|
32
|
+
|
|
33
|
+
/** The viewport frame draws out slower than it retracts. */
|
|
34
|
+
export const DEVBAR_FRAME_ENTER_MS = 750;
|
|
35
|
+
export const DEVBAR_FRAME_EXIT_MS = 360;
|
|
36
|
+
|
|
37
|
+
const MODE_KEY = "backmatter.devbar.mode";
|
|
38
|
+
|
|
39
|
+
export function devbarChrome(input: {
|
|
40
|
+
siteMode: DevbarMode;
|
|
41
|
+
changeCount: number;
|
|
42
|
+
activity?: DevbarActivity | null;
|
|
43
|
+
}): DevbarChrome {
|
|
44
|
+
const activity = input.activity ?? null;
|
|
45
|
+
const row: DevbarRow = activity
|
|
46
|
+
? activity.type
|
|
47
|
+
: input.siteMode === "preview" && input.changeCount > 0
|
|
48
|
+
? "changes"
|
|
49
|
+
: "idle";
|
|
50
|
+
const mode: DevbarMode = row === "published" ? "live" : input.siteMode;
|
|
51
|
+
const modeLabel = mode === "preview" ? "Exit preview" : "Enter preview";
|
|
52
|
+
const announce = mode === "preview" ? "Preview on, drafts visible" : "Live site";
|
|
53
|
+
return { mode, row, modeLabel, announce };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function originWithin(
|
|
57
|
+
container: { left: number; top: number },
|
|
58
|
+
target: { left: number; top: number; width: number; height: number },
|
|
59
|
+
) {
|
|
60
|
+
return {
|
|
61
|
+
x: target.left + target.width / 2 - container.left,
|
|
62
|
+
y: target.top + target.height / 2 - container.top,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function shouldAnimateWidth(before: number, after: number) {
|
|
67
|
+
return Math.abs(after - before) > 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function prefersReducedMotion() {
|
|
71
|
+
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function readStoredMode(): DevbarMode | null {
|
|
75
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
76
|
+
try {
|
|
77
|
+
const value = sessionStorage.getItem(MODE_KEY);
|
|
78
|
+
return value === "live" || value === "preview" ? value : null;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type SiteThemePreference = "light" | "dark" | "system";
|
|
85
|
+
|
|
86
|
+
export const HIGHLIGHT_BLOCKS_KEY = "backmatter.devbar.highlight";
|
|
87
|
+
export const PREVIEW_WITH_AGENT_KEY = "backmatter.devbar.agent-preview";
|
|
88
|
+
const THEME_KEY = "backmatter-theme";
|
|
89
|
+
|
|
90
|
+
type StorageLike = { getItem(key: string): string | null; setItem(key: string, value: string): void };
|
|
91
|
+
|
|
92
|
+
export function readSiteTheme(storage: StorageLike | null): SiteThemePreference {
|
|
93
|
+
const value = storage?.getItem(THEME_KEY);
|
|
94
|
+
if (value === "light" || value === "dark" || value === "system") return value;
|
|
95
|
+
return "system";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function rememberSiteTheme(storage: StorageLike | null, preference: SiteThemePreference) {
|
|
99
|
+
storage?.setItem(THEME_KEY, preference);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resolveSiteTheme(preference: SiteThemePreference, prefersDark: boolean): "light" | "dark" {
|
|
103
|
+
if (preference === "system") return prefersDark ? "dark" : "light";
|
|
104
|
+
return preference;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function readStoredFlag(storage: StorageLike | null, key: string, fallback: boolean) {
|
|
108
|
+
const value = storage?.getItem(key);
|
|
109
|
+
if (value === "1") return true;
|
|
110
|
+
if (value === "0") return false;
|
|
111
|
+
return fallback;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function rememberStoredFlag(storage: StorageLike | null, key: string, on: boolean) {
|
|
115
|
+
storage?.setItem(key, on ? "1" : "0");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function rememberMode(mode: DevbarMode) {
|
|
119
|
+
if (typeof sessionStorage === "undefined") return;
|
|
120
|
+
try {
|
|
121
|
+
sessionStorage.setItem(MODE_KEY, mode);
|
|
122
|
+
} catch {
|
|
123
|
+
/* private mode */
|
|
124
|
+
}
|
|
125
|
+
}
|
package/src/index.css
CHANGED
|
@@ -1,131 +1,3 @@
|
|
|
1
|
-
/* packages/studio/src/index.css */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*::after {
|
|
5
|
-
box-sizing: border-box;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
html,
|
|
9
|
-
body,
|
|
10
|
-
#root {
|
|
11
|
-
margin: 0;
|
|
12
|
-
min-height: 100dvh;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
html {
|
|
16
|
-
background: #f7f7f8;
|
|
17
|
-
color-scheme: light;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
html[data-theme="dark"] {
|
|
21
|
-
background: #18181b;
|
|
22
|
-
color-scheme: dark;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
body {
|
|
26
|
-
-webkit-font-smoothing: antialiased;
|
|
27
|
-
font-family:
|
|
28
|
-
"Geist Sans",
|
|
29
|
-
Inter,
|
|
30
|
-
ui-sans-serif,
|
|
31
|
-
system-ui,
|
|
32
|
-
sans-serif;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
button,
|
|
36
|
-
input,
|
|
37
|
-
textarea,
|
|
38
|
-
select {
|
|
39
|
-
/* Don't use `font: inherit` — it overrides StyleX sizes in @layer. */
|
|
40
|
-
font-family: inherit;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
button,
|
|
44
|
-
[role="button"] {
|
|
45
|
-
cursor: default;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
button:disabled,
|
|
49
|
-
button[aria-disabled="true"] {
|
|
50
|
-
cursor: not-allowed;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
[data-drag-handle] {
|
|
54
|
-
cursor: grab;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
[data-drag-handle][data-dragging] {
|
|
58
|
-
cursor: grabbing;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/* StyleX colors live in @layer priorityN, which the Vite config orders after
|
|
62
|
-
`reset`. An unlayered color on `a` would beat them, so a primary link
|
|
63
|
-
button inherits dark text on a dark fill and the label disappears. */
|
|
64
|
-
@layer reset {
|
|
65
|
-
a {
|
|
66
|
-
color: inherit;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
a {
|
|
71
|
-
text-decoration: none;
|
|
72
|
-
cursor: default;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
a[href^="http"],
|
|
76
|
-
a[href^="//"],
|
|
77
|
-
a[target="_blank"] {
|
|
78
|
-
cursor: pointer;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
:focus-visible {
|
|
82
|
-
outline: 2px solid #18181b;
|
|
83
|
-
outline-offset: 2px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
html[data-theme="dark"] :focus-visible {
|
|
87
|
-
outline-color: #fafafa;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* Controls whose container already indicates focus (e.g. the chat composer). */
|
|
91
|
-
[data-no-ring]:focus-visible {
|
|
92
|
-
outline: none;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/* Prose UI reads these on the rendered body. Match the app fonts. */
|
|
96
|
-
.prose-ui {
|
|
97
|
-
--p-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
98
|
-
--p-font-family-heading: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
99
|
-
--p-font-family-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
|
|
100
|
-
monospace;
|
|
101
|
-
--p-body-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
102
|
-
--p-h1-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
103
|
-
--p-h2-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
104
|
-
--p-h3-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
105
|
-
--p-h4-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
106
|
-
--p-h5-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
107
|
-
--p-h6-font-family: "Geist Sans", Inter, ui-sans-serif, system-ui, sans-serif;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.prose-ui img {
|
|
111
|
-
max-width: 100%;
|
|
112
|
-
height: auto;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.prose-ui > :first-child {
|
|
116
|
-
margin-top: 0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.prose-ui > :last-child {
|
|
120
|
-
margin-bottom: 0;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.prose-ui [data-body-block] :is(p, h1, h2, h3, h4, h5, h6, ul, ol, blockquote) {
|
|
124
|
-
margin-top: 0;
|
|
125
|
-
margin-bottom: 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.prose-ui [data-body-block] :focus,
|
|
129
|
-
.prose-ui [data-body-block] :focus-visible {
|
|
130
|
-
outline: none;
|
|
131
|
-
}
|
|
1
|
+
/* packages/studio/src/index.css — full studio stylesheet entry (dashboard + SiteStudio). */
|
|
2
|
+
@import "./shell-resets.css";
|
|
3
|
+
@import "./studio-prose-ui.css";
|
|
@@ -1,43 +1,76 @@
|
|
|
1
1
|
// packages/studio/src/pages/CliAuthorizePage.tsx
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { useSearchParams } from "react-router";
|
|
4
|
+
import * as stylex from "@stylexjs/stylex";
|
|
4
5
|
import { approveCliLogin } from "../api";
|
|
6
|
+
import { colors, radius, space, type } from "../styles/tokens.stylex";
|
|
5
7
|
import { Button } from "../ui/Button";
|
|
6
|
-
import { Stack } from "../ui/Page";
|
|
7
8
|
import { Text } from "../ui/Text";
|
|
8
9
|
|
|
9
|
-
export function CliAuthorizePage() {
|
|
10
|
+
export function CliAuthorizePage({ email }: { email: string }) {
|
|
10
11
|
const [params] = useSearchParams();
|
|
11
12
|
const id = params.get("id") ?? "";
|
|
12
13
|
const [status, setStatus] = useState<"idle" | "done" | "error">("idle");
|
|
13
14
|
const [error, setError] = useState<string | null>(null);
|
|
14
15
|
|
|
15
16
|
return (
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
17
|
+
<main {...stylex.props(styles.screen)}>
|
|
18
|
+
<div {...stylex.props(styles.card)}>
|
|
19
|
+
<Text as="h1" size="xl" weight="medium">
|
|
20
|
+
Allow the CLI
|
|
21
|
+
</Text>
|
|
22
|
+
<Text tone="muted">
|
|
23
|
+
This signs the CLI in as {email}. It can act in any workspace you belong to. It does not pick one.
|
|
24
|
+
</Text>
|
|
25
|
+
{status === "done" ? <Text>You can return to the terminal.</Text> : null}
|
|
26
|
+
{error ? <Text tone="danger">{error}</Text> : null}
|
|
27
|
+
{status === "done" ? null : (
|
|
28
|
+
<Button
|
|
29
|
+
disabled={!id}
|
|
30
|
+
onClick={() => {
|
|
31
|
+
setError(null);
|
|
32
|
+
void approveCliLogin(id)
|
|
33
|
+
.then(() => setStatus("done"))
|
|
34
|
+
.catch((err: unknown) => {
|
|
35
|
+
setStatus("error");
|
|
36
|
+
setError(err instanceof Error ? err.message : "Could not approve");
|
|
37
|
+
});
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
Allow
|
|
41
|
+
</Button>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
</main>
|
|
42
45
|
);
|
|
43
46
|
}
|
|
47
|
+
|
|
48
|
+
const styles = stylex.create({
|
|
49
|
+
screen: {
|
|
50
|
+
display: "flex",
|
|
51
|
+
boxSizing: "border-box",
|
|
52
|
+
width: "100%",
|
|
53
|
+
minHeight: "100dvh",
|
|
54
|
+
alignItems: "center",
|
|
55
|
+
justifyContent: "center",
|
|
56
|
+
padding: space.xxl,
|
|
57
|
+
backgroundColor: colors.bgApp,
|
|
58
|
+
color: colors.text,
|
|
59
|
+
},
|
|
60
|
+
card: {
|
|
61
|
+
display: "flex",
|
|
62
|
+
width: "100%",
|
|
63
|
+
maxWidth: "320px",
|
|
64
|
+
flexDirection: "column",
|
|
65
|
+
alignItems: "flex-start",
|
|
66
|
+
gap: space.lg,
|
|
67
|
+
boxSizing: "border-box",
|
|
68
|
+
borderWidth: 1,
|
|
69
|
+
borderStyle: "solid",
|
|
70
|
+
borderColor: colors.border,
|
|
71
|
+
borderRadius: radius.md,
|
|
72
|
+
backgroundColor: colors.bgSurface,
|
|
73
|
+
padding: space.xxl,
|
|
74
|
+
fontSize: type.sm,
|
|
75
|
+
},
|
|
76
|
+
});
|