@backmatter/studio 0.0.15 → 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-BDsrXN12.js → UiKitApp-_EDqWID3.js} +2 -2
- package/dist/chunks/{WorkspaceRoutes-BTZ1Ah4F.js → WorkspaceRoutes-B2PrFgpz.js} +28979 -27418
- package/dist/chunks/{dashboard-FT7dm5wi.js → dashboard-BnlnMnnr.js} +55 -54
- package/dist/chunks/{index-DHJvh5Uy.js → index-D_BHF_8q.js} +1 -1
- package/dist/dashboard.js +2 -2
- package/dist/index.js +2 -2
- package/dist/site-widget.css +4 -0
- package/dist/site-widget.js +8304 -8029
- package/dist/studio.css +186 -12
- package/package.json +2 -2
- package/src/WorkspaceRoutes.tsx +5 -2
- package/src/api.ts +63 -7
- package/src/chat/ChangeCard.tsx +8 -2
- package/src/chat/ChatMessage.tsx +171 -102
- package/src/chat/ChatPanel.tsx +54 -34
- package/src/chat/ChatProvider.tsx +116 -20
- package/src/chat/Composer.tsx +48 -7
- package/src/chat/PointerChip.tsx +9 -5
- package/src/chat/ThreadHistory.tsx +3 -3
- package/src/chat/chat-context.ts +67 -3
- package/src/chat/chat-helpers.ts +12 -0
- package/src/chat/chat-state.ts +11 -0
- package/src/chrome/AppShell.tsx +16 -5
- package/src/chrome/CommandPalette.tsx +12 -0
- package/src/chrome/ContentExplorerSidebar.tsx +23 -3
- package/src/chrome/UserMenu.tsx +34 -3
- package/src/chrome/WorkspaceStatusBar.tsx +209 -27
- 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/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/use-session-preview.ts +70 -0
- package/src/replica/store.ts +11 -0
- package/src/replica/types.ts +6 -0
- package/src/site-pointer.ts +24 -5
- 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/site-pointer.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// packages/studio/src/site-pointer.ts
|
|
2
2
|
// Hit-test a click on the host page and draw the armed pointer outline.
|
|
3
3
|
|
|
4
|
+
import { readElementOrigin, type ElementOrigin } from "./element-origin";
|
|
5
|
+
|
|
4
6
|
export const POINTER_QUOTE_MAX = 240;
|
|
5
7
|
|
|
6
8
|
export type PointerGrab = {
|
|
@@ -8,6 +10,14 @@ export type PointerGrab = {
|
|
|
8
10
|
blockType: string | null;
|
|
9
11
|
blockIndex: number | null;
|
|
10
12
|
quote: string;
|
|
13
|
+
} & ElementOrigin;
|
|
14
|
+
|
|
15
|
+
const EMPTY_ORIGIN: ElementOrigin = {
|
|
16
|
+
component: null,
|
|
17
|
+
file: null,
|
|
18
|
+
line: null,
|
|
19
|
+
column: null,
|
|
20
|
+
stack: [],
|
|
11
21
|
};
|
|
12
22
|
|
|
13
23
|
function quoteOf(target: Element): string {
|
|
@@ -29,9 +39,10 @@ export function grabAt(target: Element | null, bar: Element | null): PointerGrab
|
|
|
29
39
|
|
|
30
40
|
const marker = target.closest("[data-backmatter-block]");
|
|
31
41
|
const quote = quoteOf(target);
|
|
42
|
+
const origin = readElementOrigin(target) ?? EMPTY_ORIGIN;
|
|
32
43
|
if (!marker) {
|
|
33
|
-
if (!quote) return null;
|
|
34
|
-
return { blockId: null, blockType: null, blockIndex: null, quote };
|
|
44
|
+
if (!quote && !origin.component && !origin.file) return null;
|
|
45
|
+
return { blockId: null, blockType: null, blockIndex: null, quote, ...origin };
|
|
35
46
|
}
|
|
36
47
|
const indexRaw = marker.getAttribute("data-backmatter-index");
|
|
37
48
|
const blockIndex = indexRaw !== null && /^\d+$/.test(indexRaw) ? Number(indexRaw) : null;
|
|
@@ -40,6 +51,7 @@ export function grabAt(target: Element | null, bar: Element | null): PointerGrab
|
|
|
40
51
|
blockType: presentAttr(marker, "data-backmatter-type"),
|
|
41
52
|
blockIndex,
|
|
42
53
|
quote,
|
|
54
|
+
...origin,
|
|
43
55
|
};
|
|
44
56
|
}
|
|
45
57
|
|
|
@@ -104,10 +116,13 @@ export function installSitePointer(
|
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
function onClick(event: MouseEvent) {
|
|
107
|
-
if (!armed
|
|
119
|
+
if (!armed) return;
|
|
120
|
+
const raw = event.target instanceof Element ? event.target : null;
|
|
121
|
+
// The outline sits on the element. A click on it belongs to the element under it.
|
|
122
|
+
const target = raw?.closest("[data-backmatter-pointer]") ? current : raw;
|
|
123
|
+
if (!target || ignored(target)) return;
|
|
108
124
|
event.preventDefault();
|
|
109
125
|
event.stopPropagation();
|
|
110
|
-
const target = event.target instanceof Element ? event.target : null;
|
|
111
126
|
const grab = grabAt(target, options.bar());
|
|
112
127
|
if (grab) options.onGrab(grab, isOpenModifier(event));
|
|
113
128
|
}
|
|
@@ -174,6 +189,7 @@ function createPointerOutline(doc: Document): HTMLDivElement {
|
|
|
174
189
|
label.style.background = "#18181b";
|
|
175
190
|
label.style.color = "#ffffff";
|
|
176
191
|
label.style.font = "11px/1.4 ui-sans-serif, system-ui, sans-serif";
|
|
192
|
+
label.style.pointerEvents = "none";
|
|
177
193
|
label.style.display = "none";
|
|
178
194
|
el.appendChild(label);
|
|
179
195
|
doc.body.appendChild(el);
|
|
@@ -216,7 +232,10 @@ function positionPointerOutline(outline: HTMLDivElement, target: Element) {
|
|
|
216
232
|
outline.style.width = `${rect.width}px`;
|
|
217
233
|
outline.style.height = `${rect.height}px`;
|
|
218
234
|
const label = outline.firstElementChild as HTMLElement | null;
|
|
219
|
-
const name =
|
|
235
|
+
const name =
|
|
236
|
+
target.closest("[data-backmatter-block]")?.getAttribute("data-backmatter-type") ||
|
|
237
|
+
readElementOrigin(target)?.component ||
|
|
238
|
+
"";
|
|
220
239
|
if (!label) return;
|
|
221
240
|
if (!name) {
|
|
222
241
|
label.textContent = "";
|
package/src/studio-config.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type StudioConfig = {
|
|
|
14
14
|
dev: boolean;
|
|
15
15
|
/**
|
|
16
16
|
* The CMS API origin as the outside world reaches it, for URLs the Studio
|
|
17
|
-
* shows rather than fetches (the Content API
|
|
17
|
+
* shows rather than fetches (the Content API URL in General settings). A site Studio fetches
|
|
18
18
|
* through a relative proxy, so its host passes the real origin here.
|
|
19
19
|
* Defaults to `baseUrl`.
|
|
20
20
|
*/
|
package/src/ui/ListRow.tsx
CHANGED
|
@@ -11,13 +11,13 @@ export function ListRow({
|
|
|
11
11
|
meta,
|
|
12
12
|
trailing,
|
|
13
13
|
}: {
|
|
14
|
-
to
|
|
14
|
+
to?: string;
|
|
15
15
|
title: string;
|
|
16
16
|
meta?: string;
|
|
17
17
|
trailing?: string;
|
|
18
18
|
}) {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const body = (
|
|
20
|
+
<>
|
|
21
21
|
<span {...stylex.props(styles.body)}>
|
|
22
22
|
<span {...stylex.props(styles.title)}>{title}</span>
|
|
23
23
|
{meta ? <span {...stylex.props(styles.meta)}>{meta}</span> : null}
|
|
@@ -25,6 +25,12 @@ export function ListRow({
|
|
|
25
25
|
{trailing ? (
|
|
26
26
|
<span {...stylex.props(styles.trailing)}>{trailing}</span>
|
|
27
27
|
) : null}
|
|
28
|
+
</>
|
|
29
|
+
);
|
|
30
|
+
if (!to) return <div {...stylex.props(styles.row, styles.static)}>{body}</div>;
|
|
31
|
+
return (
|
|
32
|
+
<Link to={to} {...stylex.props(styles.row)}>
|
|
33
|
+
{body}
|
|
28
34
|
</Link>
|
|
29
35
|
);
|
|
30
36
|
}
|
|
@@ -66,6 +72,9 @@ const styles = stylex.create({
|
|
|
66
72
|
},
|
|
67
73
|
},
|
|
68
74
|
},
|
|
75
|
+
static: {
|
|
76
|
+
cursor: "default",
|
|
77
|
+
},
|
|
69
78
|
body: {
|
|
70
79
|
display: "flex",
|
|
71
80
|
minWidth: 0,
|
package/src/ui/Page.tsx
CHANGED
|
@@ -6,10 +6,12 @@ export function PageFrame({
|
|
|
6
6
|
children,
|
|
7
7
|
flush,
|
|
8
8
|
centered,
|
|
9
|
+
wide,
|
|
9
10
|
}: {
|
|
10
11
|
children: React.ReactNode;
|
|
11
12
|
flush?: boolean;
|
|
12
13
|
centered?: boolean;
|
|
14
|
+
wide?: boolean;
|
|
13
15
|
}) {
|
|
14
16
|
return (
|
|
15
17
|
<div
|
|
@@ -22,7 +24,13 @@ export function PageFrame({
|
|
|
22
24
|
{flush ? (
|
|
23
25
|
children
|
|
24
26
|
) : (
|
|
25
|
-
<div
|
|
27
|
+
<div
|
|
28
|
+
{...stylex.props(
|
|
29
|
+
styles.inner,
|
|
30
|
+
centered && styles.innerCentered,
|
|
31
|
+
wide && styles.innerWide,
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
26
34
|
{children}
|
|
27
35
|
</div>
|
|
28
36
|
)}
|
|
@@ -108,6 +116,9 @@ const styles = stylex.create({
|
|
|
108
116
|
innerCentered: {
|
|
109
117
|
maxWidth: "440px",
|
|
110
118
|
},
|
|
119
|
+
innerWide: {
|
|
120
|
+
maxWidth: "960px",
|
|
121
|
+
},
|
|
111
122
|
header: {
|
|
112
123
|
display: "flex",
|
|
113
124
|
alignItems: "flex-start",
|
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
|
-
}
|