@backmatter/studio 0.0.14 → 0.0.16
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-fYeVEf4-.js → UiKitApp-_EDqWID3.js} +2 -2
- package/dist/chunks/{WorkspaceRoutes-Dt7JvEWT.js → WorkspaceRoutes-B2PrFgpz.js} +29019 -27476
- package/dist/chunks/{dashboard-BuY2aGkO.js → dashboard-BnlnMnnr.js} +55 -54
- package/dist/chunks/{index-NWDqEZYW.js → index-D_BHF_8q.js} +1 -1
- package/dist/dashboard.js +2 -2
- package/dist/index.js +51 -52
- package/dist/site-widget.css +4 -0
- package/dist/site-widget.js +9419 -9156
- package/dist/studio.css +186 -12
- package/package.json +2 -2
- package/src/WorkspaceRoutes.tsx +5 -2
- package/src/api.ts +64 -11
- package/src/chat/ChangeCard.tsx +9 -15
- package/src/chat/ChatMessage.tsx +172 -103
- package/src/chat/ChatPanel.tsx +54 -34
- package/src/chat/ChatProvider.tsx +120 -24
- package/src/chat/Composer.tsx +48 -7
- package/src/chat/DocListCard.tsx +2 -16
- package/src/chat/MentionPopover.tsx +8 -9
- package/src/chat/PointerChip.tsx +9 -5
- package/src/chat/ThreadHistory.tsx +3 -3
- package/src/chat/chat-context.ts +74 -10
- package/src/chat/chat-helpers.ts +15 -8
- package/src/chat/chat-state.ts +11 -0
- package/src/chrome/AppShell.tsx +16 -5
- package/src/chrome/CommandPalette.tsx +14 -10
- package/src/chrome/ContentExplorerSidebar.tsx +23 -3
- package/src/chrome/UserMenu.tsx +34 -3
- package/src/chrome/WorkspaceStatusBar.tsx +215 -50
- package/src/chrome/content-api-url.ts +0 -78
- package/src/chrome/content-tree-visibility.ts +13 -3
- package/src/chrome/explorer-icons.tsx +5 -0
- package/src/code-session-entry.ts +9 -0
- package/src/components/WorkspaceSwitcher.tsx +2 -2
- package/src/element-origin.ts +149 -0
- package/src/host-pointer-bootstrap.ts +11 -0
- package/src/host-pointer.ts +147 -0
- package/src/install-preview-pointer.ts +19 -3
- package/src/pages/CodeSessionFiles.tsx +221 -0
- package/src/pages/CodeSessionPage.tsx +1188 -0
- package/src/pages/DocEditorPage.tsx +33 -15
- package/src/pages/EmbedChatPage.tsx +3 -3
- package/src/pages/EmbedPublishPage.tsx +2 -3
- package/src/pages/PreviewSize.tsx +181 -0
- package/src/pages/SettingsGeneralPage.tsx +67 -2
- package/src/pages/WorkspaceHomePage.tsx +313 -0
- package/src/pages/WorkspaceLayout.tsx +10 -1
- package/src/pages/code-session-boot.ts +43 -0
- package/src/pages/code-session-context.tsx +51 -0
- package/src/pages/code-session-files.ts +42 -0
- package/src/pages/code-session-frame.ts +52 -0
- package/src/pages/code-session-routes.ts +42 -0
- package/src/pages/home-rows.ts +140 -0
- package/src/pages/preview-size.ts +33 -0
- package/src/pages/session-preview.ts +109 -0
- package/src/pages/type-docs.ts +3 -6
- package/src/pages/use-session-preview.ts +70 -0
- package/src/replica/store.ts +13 -10
- package/src/replica/types.ts +7 -2
- package/src/site-pointer.ts +24 -5
- package/src/site-widget.tsx +2 -3
- package/src/studio-config.ts +1 -1
- package/src/ui/ListRow.tsx +12 -3
- package/src/ui/Page.tsx +12 -1
- package/src/workspace-path.ts +35 -3
- package/src/chrome/ContentApiPopover.tsx +0 -215
- package/src/pages/WorkspaceHomeRedirect.tsx +0 -17
package/src/workspace-path.ts
CHANGED
|
@@ -3,6 +3,14 @@ export function workspaceHomePath(slug: string) {
|
|
|
3
3
|
return `/${slug}`;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
/** The workspace index. Content, media, and settings are deeper routes. */
|
|
7
|
+
export function isWorkspaceHomePath(pathname: string) {
|
|
8
|
+
const slug = cmsWorkspaceSlug(pathname);
|
|
9
|
+
if (!slug) return false;
|
|
10
|
+
const path = pathname.split("?")[0]?.replace(/\/$/, "") || "/";
|
|
11
|
+
return path === `/${slug}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
export function contentTypePath(slug: string, type: string) {
|
|
7
15
|
return `/${slug}/content/${type}`;
|
|
8
16
|
}
|
|
@@ -15,6 +23,10 @@ export function mediaPath(slug: string) {
|
|
|
15
23
|
return `/${slug}/media`;
|
|
16
24
|
}
|
|
17
25
|
|
|
26
|
+
export function codeSessionPath(slug: string) {
|
|
27
|
+
return `/${slug}/code`;
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
export function settingsPath(slug: string, page: string) {
|
|
19
31
|
return `/${slug}/settings/${page}`;
|
|
20
32
|
}
|
|
@@ -71,19 +83,33 @@ export function isWorkspaceMediaPath(pathname: string) {
|
|
|
71
83
|
return /\/media\/?$/.test(pathname);
|
|
72
84
|
}
|
|
73
85
|
|
|
86
|
+
export function isWorkspaceCodePath(pathname: string) {
|
|
87
|
+
return /\/code\/?$/.test(pathname);
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
/** The content file tree belongs on content routes only. */
|
|
75
91
|
export function isWorkspaceContentPath(pathname: string) {
|
|
76
92
|
return /\/content(?:\/|$)/.test(pathname);
|
|
77
93
|
}
|
|
78
94
|
|
|
95
|
+
/** The content index. The file tree lives here; the workspace home does not. */
|
|
96
|
+
export function contentIndexPath(slug: string) {
|
|
97
|
+
return `/${slug}/content`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function isWorkspaceContentIndexPath(pathname: string) {
|
|
101
|
+
const path = pathname.split("?")[0]?.replace(/\/$/, "") || "/";
|
|
102
|
+
return /\/content$/.test(path);
|
|
103
|
+
}
|
|
104
|
+
|
|
79
105
|
/**
|
|
80
106
|
* Where the content rail icon navigates.
|
|
81
107
|
* On a content route it stays put: a document toggles the file tree, and the
|
|
82
|
-
*
|
|
83
|
-
* Anywhere else (settings, media, connect):
|
|
108
|
+
* content index is already the file tree.
|
|
109
|
+
* Anywhere else (home, settings, media, connect): `/content`.
|
|
84
110
|
*/
|
|
85
111
|
export function contentRailHref(pathname: string, slug: string): string | null {
|
|
86
|
-
if (!isWorkspaceContentPath(pathname)) return
|
|
112
|
+
if (!isWorkspaceContentPath(pathname)) return contentIndexPath(slug);
|
|
87
113
|
return null;
|
|
88
114
|
}
|
|
89
115
|
|
|
@@ -101,6 +127,12 @@ export function isOrgPickerPath(pathname: string) {
|
|
|
101
127
|
return path === "/invite";
|
|
102
128
|
}
|
|
103
129
|
|
|
130
|
+
/** The workspace list and create form. The app sidebar repeats this list. */
|
|
131
|
+
export function isWorkspaceListPath(pathname: string) {
|
|
132
|
+
const path = pathname.split("?")[0]?.replace(/\/$/, "") || "/";
|
|
133
|
+
return path === "/workspaces" || path === "/workspaces/new";
|
|
134
|
+
}
|
|
135
|
+
|
|
104
136
|
/** CLI approval is a full-screen form, not a workspace. */
|
|
105
137
|
export function isCliAuthorizePath(pathname: string) {
|
|
106
138
|
const path = pathname.replace(/\/$/, "") || "/";
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
// packages/studio/src/chrome/ContentApiPopover.tsx
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { Popover } from "@base-ui/react/popover";
|
|
4
|
-
import * as stylex from "@stylexjs/stylex";
|
|
5
|
-
import { publicApiUrl } from "../studio-config";
|
|
6
|
-
import { colors, radius, space, type } from "../styles/tokens.stylex";
|
|
7
|
-
import { cx, popup, popupClassName } from "../ui/popup";
|
|
8
|
-
import { contentApiLinks } from "./content-api-url";
|
|
9
|
-
import { ApiIcon } from "./icons";
|
|
10
|
-
|
|
11
|
-
const hover = "@media (hover: hover)";
|
|
12
|
-
|
|
13
|
-
function CopyUrl({ label, href }: { label: string; href: string }) {
|
|
14
|
-
const [copied, setCopied] = useState(false);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (!copied) return;
|
|
18
|
-
const id = window.setTimeout(() => setCopied(false), 1500);
|
|
19
|
-
return () => window.clearTimeout(id);
|
|
20
|
-
}, [copied]);
|
|
21
|
-
|
|
22
|
-
async function copy() {
|
|
23
|
-
try {
|
|
24
|
-
await navigator.clipboard.writeText(href);
|
|
25
|
-
setCopied(true);
|
|
26
|
-
} catch {
|
|
27
|
-
setCopied(false);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<div {...stylex.props(styles.row)}>
|
|
33
|
-
<p {...stylex.props(popup.groupLabel)}>{label}</p>
|
|
34
|
-
<div {...stylex.props(styles.urlRow)}>
|
|
35
|
-
<a
|
|
36
|
-
href={href}
|
|
37
|
-
target="_blank"
|
|
38
|
-
rel="noreferrer"
|
|
39
|
-
{...stylex.props(styles.href)}
|
|
40
|
-
>
|
|
41
|
-
{href}
|
|
42
|
-
</a>
|
|
43
|
-
<button
|
|
44
|
-
type="button"
|
|
45
|
-
onClick={() => void copy()}
|
|
46
|
-
aria-label={copied ? "Copied" : `Copy ${label} URL`}
|
|
47
|
-
{...stylex.props(styles.copy)}
|
|
48
|
-
>
|
|
49
|
-
{copied ? "Copied" : "Copy"}
|
|
50
|
-
</button>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function ContentApiPopover({
|
|
57
|
-
workspaceId,
|
|
58
|
-
branch,
|
|
59
|
-
defaultBranch,
|
|
60
|
-
pathname,
|
|
61
|
-
search,
|
|
62
|
-
}: {
|
|
63
|
-
workspaceId: string;
|
|
64
|
-
branch?: string | null;
|
|
65
|
-
defaultBranch?: string | null;
|
|
66
|
-
pathname: string;
|
|
67
|
-
search?: string;
|
|
68
|
-
}) {
|
|
69
|
-
const links = contentApiLinks({
|
|
70
|
-
apiUrl: publicApiUrl(),
|
|
71
|
-
workspaceId,
|
|
72
|
-
branch,
|
|
73
|
-
defaultBranch,
|
|
74
|
-
pathname,
|
|
75
|
-
search,
|
|
76
|
-
});
|
|
77
|
-
const locked = Boolean(branch && defaultBranch && branch !== defaultBranch);
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<Popover.Root>
|
|
81
|
-
<Popover.Trigger aria-label="Content API" className={cx(styles.trigger)}>
|
|
82
|
-
<ApiIcon />
|
|
83
|
-
<span>API</span>
|
|
84
|
-
</Popover.Trigger>
|
|
85
|
-
<Popover.Portal>
|
|
86
|
-
<Popover.Positioner
|
|
87
|
-
side="bottom"
|
|
88
|
-
align="start"
|
|
89
|
-
sideOffset={6}
|
|
90
|
-
className={cx(popup.positioner)}
|
|
91
|
-
>
|
|
92
|
-
<Popover.Popup
|
|
93
|
-
aria-label="Content API"
|
|
94
|
-
className={(state) => popupClassName(state, styles.popup)}
|
|
95
|
-
>
|
|
96
|
-
<p {...stylex.props(popup.groupLabel)}>Content API</p>
|
|
97
|
-
<p {...stylex.props(styles.hint)}>
|
|
98
|
-
{locked
|
|
99
|
-
? "This branch needs the content token from Settings → General."
|
|
100
|
-
: "Default branch is public — no token. URLs use the workspace id."}
|
|
101
|
-
</p>
|
|
102
|
-
{links.map((link) => (
|
|
103
|
-
<CopyUrl key={link.href} label={link.label} href={link.href} />
|
|
104
|
-
))}
|
|
105
|
-
</Popover.Popup>
|
|
106
|
-
</Popover.Positioner>
|
|
107
|
-
</Popover.Portal>
|
|
108
|
-
</Popover.Root>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const styles = stylex.create({
|
|
113
|
-
trigger: {
|
|
114
|
-
display: "inline-flex",
|
|
115
|
-
alignItems: "center",
|
|
116
|
-
gap: space.xs,
|
|
117
|
-
minHeight: "24px",
|
|
118
|
-
paddingInline: space.xs,
|
|
119
|
-
borderWidth: 0,
|
|
120
|
-
backgroundColor: "transparent",
|
|
121
|
-
color: colors.textMuted,
|
|
122
|
-
fontSize: type.xs,
|
|
123
|
-
fontFamily: type.mono,
|
|
124
|
-
cursor: "pointer",
|
|
125
|
-
touchAction: "manipulation",
|
|
126
|
-
":focus-visible": {
|
|
127
|
-
outlineWidth: 2,
|
|
128
|
-
outlineStyle: "solid",
|
|
129
|
-
outlineColor: colors.focus,
|
|
130
|
-
outlineOffset: "-2px",
|
|
131
|
-
},
|
|
132
|
-
[hover]: {
|
|
133
|
-
":hover": {
|
|
134
|
-
color: colors.text,
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
popup: {
|
|
139
|
-
width: "380px",
|
|
140
|
-
paddingBottom: space.md,
|
|
141
|
-
overflow: "hidden",
|
|
142
|
-
},
|
|
143
|
-
hint: {
|
|
144
|
-
margin: 0,
|
|
145
|
-
paddingInline: space.lg,
|
|
146
|
-
paddingBottom: space.sm,
|
|
147
|
-
fontFamily: type.mono,
|
|
148
|
-
fontSize: type.xs,
|
|
149
|
-
lineHeight: 1.45,
|
|
150
|
-
color: colors.textMuted,
|
|
151
|
-
},
|
|
152
|
-
row: {
|
|
153
|
-
display: "flex",
|
|
154
|
-
flexDirection: "column",
|
|
155
|
-
gap: space.xs,
|
|
156
|
-
paddingBottom: space.sm,
|
|
157
|
-
},
|
|
158
|
-
urlRow: {
|
|
159
|
-
display: "flex",
|
|
160
|
-
alignItems: "center",
|
|
161
|
-
gap: space.sm,
|
|
162
|
-
minWidth: 0,
|
|
163
|
-
marginInline: space.lg,
|
|
164
|
-
},
|
|
165
|
-
href: {
|
|
166
|
-
minWidth: 0,
|
|
167
|
-
flex: 1,
|
|
168
|
-
overflow: "hidden",
|
|
169
|
-
textOverflow: "ellipsis",
|
|
170
|
-
whiteSpace: "nowrap",
|
|
171
|
-
color: colors.text,
|
|
172
|
-
fontFamily: type.mono,
|
|
173
|
-
fontSize: type.xs,
|
|
174
|
-
textDecoration: "none",
|
|
175
|
-
[hover]: {
|
|
176
|
-
":hover": {
|
|
177
|
-
textDecoration: "underline",
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
":focus-visible": {
|
|
181
|
-
outlineWidth: 2,
|
|
182
|
-
outlineStyle: "solid",
|
|
183
|
-
outlineColor: colors.focus,
|
|
184
|
-
outlineOffset: 2,
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
copy: {
|
|
188
|
-
flexShrink: 0,
|
|
189
|
-
minWidth: "52px",
|
|
190
|
-
minHeight: "24px",
|
|
191
|
-
paddingInline: space.sm,
|
|
192
|
-
borderWidth: 1,
|
|
193
|
-
borderStyle: "solid",
|
|
194
|
-
borderColor: colors.border,
|
|
195
|
-
borderRadius: radius.md,
|
|
196
|
-
backgroundColor: colors.bgSurface,
|
|
197
|
-
color: colors.textMuted,
|
|
198
|
-
fontSize: type.xs,
|
|
199
|
-
fontFamily: type.sans,
|
|
200
|
-
cursor: "pointer",
|
|
201
|
-
touchAction: "manipulation",
|
|
202
|
-
[hover]: {
|
|
203
|
-
":hover": {
|
|
204
|
-
color: colors.text,
|
|
205
|
-
backgroundColor: colors.bgHover,
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
":focus-visible": {
|
|
209
|
-
outlineWidth: 2,
|
|
210
|
-
outlineStyle: "solid",
|
|
211
|
-
outlineColor: colors.focus,
|
|
212
|
-
outlineOffset: 1,
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// packages/studio/src/pages/WorkspaceHomeRedirect.tsx
|
|
2
|
-
import { Navigate, useOutletContext } from "react-router";
|
|
3
|
-
import type { WorkspaceOutletContext } from "../workspace/types";
|
|
4
|
-
import { contentTypePath, settingsPath } from "../workspace-path";
|
|
5
|
-
|
|
6
|
-
export function WorkspaceHomeRedirect() {
|
|
7
|
-
const { workspace, models } = useOutletContext<WorkspaceOutletContext>();
|
|
8
|
-
const first = models[0]?.type;
|
|
9
|
-
if (first) {
|
|
10
|
-
return (
|
|
11
|
-
<Navigate to={contentTypePath(workspace.slug, first)} replace />
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
return (
|
|
15
|
-
<Navigate to={settingsPath(workspace.slug, "schema")} replace />
|
|
16
|
-
);
|
|
17
|
-
}
|