@backmatter/studio 0.0.14 → 0.0.15
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-BDsrXN12.js} +2 -2
- package/dist/chunks/{WorkspaceRoutes-Dt7JvEWT.js → WorkspaceRoutes-BTZ1Ah4F.js} +3304 -3322
- package/dist/chunks/{dashboard-BuY2aGkO.js → dashboard-FT7dm5wi.js} +2 -2
- package/dist/chunks/{index-NWDqEZYW.js → index-DHJvh5Uy.js} +1 -1
- package/dist/dashboard.js +2 -2
- package/dist/index.js +51 -52
- package/dist/site-widget.js +4132 -4144
- package/package.json +1 -1
- package/src/api.ts +1 -4
- package/src/chat/ChangeCard.tsx +3 -15
- package/src/chat/ChatMessage.tsx +1 -1
- package/src/chat/ChatProvider.tsx +4 -4
- package/src/chat/DocListCard.tsx +2 -16
- package/src/chat/MentionPopover.tsx +8 -9
- package/src/chat/PointerChip.tsx +1 -1
- package/src/chat/chat-context.ts +7 -7
- package/src/chat/chat-helpers.ts +3 -8
- package/src/chrome/CommandPalette.tsx +2 -10
- package/src/chrome/WorkspaceStatusBar.tsx +6 -23
- package/src/pages/EmbedChatPage.tsx +3 -3
- package/src/pages/EmbedPublishPage.tsx +2 -3
- package/src/pages/type-docs.ts +3 -6
- package/src/replica/store.ts +2 -10
- package/src/replica/types.ts +1 -2
- package/src/site-widget.tsx +2 -3
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -451,7 +451,6 @@ export type CommitResult = {
|
|
|
451
451
|
message: string;
|
|
452
452
|
branch: string;
|
|
453
453
|
files: Array<{
|
|
454
|
-
locale: string;
|
|
455
454
|
type: string;
|
|
456
455
|
slug: string;
|
|
457
456
|
githubPath: string;
|
|
@@ -588,7 +587,7 @@ export function fetchDocNav(
|
|
|
588
587
|
q?: string;
|
|
589
588
|
limit?: number;
|
|
590
589
|
offset?: number;
|
|
591
|
-
current?: {
|
|
590
|
+
current?: { type: string; slug: string };
|
|
592
591
|
} = {},
|
|
593
592
|
) {
|
|
594
593
|
const qs = new URLSearchParams({ nav: "1" });
|
|
@@ -713,7 +712,6 @@ export type ChatChangeDiff = {
|
|
|
713
712
|
added: number;
|
|
714
713
|
removed: number;
|
|
715
714
|
renamedFrom?: string;
|
|
716
|
-
renamedFromLocale?: string;
|
|
717
715
|
hunks: Array<{
|
|
718
716
|
oldStart: number;
|
|
719
717
|
oldLines: number;
|
|
@@ -731,7 +729,6 @@ export type ChatChangeSummary = {
|
|
|
731
729
|
userId: string;
|
|
732
730
|
branch: string;
|
|
733
731
|
resourceKind: "content";
|
|
734
|
-
locale: string;
|
|
735
732
|
type: string;
|
|
736
733
|
slug: string;
|
|
737
734
|
op: "write" | "create";
|
package/src/chat/ChangeCard.tsx
CHANGED
|
@@ -28,18 +28,8 @@ export function ChangeCard({
|
|
|
28
28
|
}) {
|
|
29
29
|
const [expanded, setExpanded] = useState(false);
|
|
30
30
|
const nav = useWorkspaceNav();
|
|
31
|
-
const label = change.
|
|
32
|
-
|
|
33
|
-
: `${change.type}/${change.slug}`;
|
|
34
|
-
const to = nav
|
|
35
|
-
? contentDocPath(
|
|
36
|
-
nav.workspaceSlug,
|
|
37
|
-
change.type,
|
|
38
|
-
change.slug,
|
|
39
|
-
change.locale,
|
|
40
|
-
nav.defaultLocale,
|
|
41
|
-
)
|
|
42
|
-
: "#";
|
|
31
|
+
const label = `${change.type}/${change.slug}`;
|
|
32
|
+
const to = nav ? contentDocPath(nav.workspaceSlug, change.type, change.slug) : "#";
|
|
43
33
|
const status = change.status;
|
|
44
34
|
|
|
45
35
|
return (
|
|
@@ -71,9 +61,7 @@ export function ChangeCard({
|
|
|
71
61
|
)}
|
|
72
62
|
{change.diff?.renamedFrom ? (
|
|
73
63
|
<span {...stylex.props(styles.badge)}>
|
|
74
|
-
{change.diff.
|
|
75
|
-
? `${change.diff.renamedFromLocale}/${change.diff.renamedFrom} → ${change.locale}/${change.slug}`
|
|
76
|
-
: `${change.diff.renamedFrom} → ${change.slug}`}
|
|
64
|
+
{`${change.diff.renamedFrom} → ${change.slug}`}
|
|
77
65
|
</span>
|
|
78
66
|
) : change.op === "create" ? (
|
|
79
67
|
<span {...stylex.props(styles.badge)}>new</span>
|
package/src/chat/ChatMessage.tsx
CHANGED
|
@@ -34,7 +34,7 @@ type ToolPart = Extract<UIMessage["parts"][number], { toolCallId: string }>;
|
|
|
34
34
|
type ListDocsOutput = { docs: DocCardItem[]; total?: number };
|
|
35
35
|
type SearchDocsOutput = {
|
|
36
36
|
matches: Array<{
|
|
37
|
-
docRef: {
|
|
37
|
+
docRef: { type: string; slug: string };
|
|
38
38
|
source?: string;
|
|
39
39
|
excerpt?: string;
|
|
40
40
|
title?: string | null;
|
|
@@ -163,7 +163,7 @@ function publishTreePatches(workspaceId: string, patches: TreeStatusPatch[]) {
|
|
|
163
163
|
// A published file stays on screen; head-changed reloads it once. A file
|
|
164
164
|
// that disappeared still needs the open editor to drop it.
|
|
165
165
|
if (patch.absent || patch.status !== "clean") {
|
|
166
|
-
notifyDocChanged({
|
|
166
|
+
notifyDocChanged({ type: patch.type, slug: patch.slug });
|
|
167
167
|
}
|
|
168
168
|
if (patch.status === "clean") published = true;
|
|
169
169
|
}
|
|
@@ -175,8 +175,8 @@ function publishTreePatches(workspaceId: string, patches: TreeStatusPatch[]) {
|
|
|
175
175
|
|
|
176
176
|
function refFromInput(input: unknown): DocRef | null {
|
|
177
177
|
const ref = (input as { docRef?: Partial<DocRef> } | undefined)?.docRef;
|
|
178
|
-
if (!ref || !ref.
|
|
179
|
-
return {
|
|
178
|
+
if (!ref || !ref.type || !ref.slug) return null;
|
|
179
|
+
return { type: ref.type, slug: ref.slug };
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
export function ChatProvider({
|
|
@@ -657,7 +657,7 @@ function LiveSession({
|
|
|
657
657
|
const applyChangeResult = useCallback((change: ChatChangeSummary, notify: boolean) => {
|
|
658
658
|
setChanges((prev) => ({ ...prev, [change.id]: { ...prev[change.id], ...change } }));
|
|
659
659
|
if (notify) {
|
|
660
|
-
notifyDocChanged({
|
|
660
|
+
notifyDocChanged({ type: change.type, slug: change.slug });
|
|
661
661
|
notifyTreeChanged();
|
|
662
662
|
}
|
|
663
663
|
}, []);
|
package/src/chat/DocListCard.tsx
CHANGED
|
@@ -14,7 +14,6 @@ import { ChevronDownIcon, FileIcon } from "./icons";
|
|
|
14
14
|
const hover = "@media (hover: hover)";
|
|
15
15
|
|
|
16
16
|
export type DocCardItem = {
|
|
17
|
-
locale: string;
|
|
18
17
|
type: string;
|
|
19
18
|
slug: string;
|
|
20
19
|
title?: string | null;
|
|
@@ -57,7 +56,6 @@ export function DocListCard({
|
|
|
57
56
|
|
|
58
57
|
const groups = groupByType(items);
|
|
59
58
|
const multiType = groups.length > 1;
|
|
60
|
-
const multiLocale = new Set(items.map((d) => d.locale)).size > 1;
|
|
61
59
|
const hidden = expanded ? 0 : Math.max(0, items.length - COLLAPSED_ROWS);
|
|
62
60
|
const truncated = total !== undefined && total > items.length ? total - items.length : 0;
|
|
63
61
|
|
|
@@ -78,10 +76,9 @@ export function DocListCard({
|
|
|
78
76
|
) : null}
|
|
79
77
|
{shown.map((doc) => (
|
|
80
78
|
<DocRow
|
|
81
|
-
key={`${doc.
|
|
79
|
+
key={`${doc.type}/${doc.slug}`}
|
|
82
80
|
doc={doc}
|
|
83
81
|
workspaceId={workspaceId}
|
|
84
|
-
showLocale={multiLocale}
|
|
85
82
|
/>
|
|
86
83
|
))}
|
|
87
84
|
</div>
|
|
@@ -109,22 +106,12 @@ export function DocListCard({
|
|
|
109
106
|
function DocRow({
|
|
110
107
|
doc,
|
|
111
108
|
workspaceId,
|
|
112
|
-
showLocale,
|
|
113
109
|
}: {
|
|
114
110
|
doc: DocCardItem;
|
|
115
111
|
workspaceId: string;
|
|
116
|
-
showLocale: boolean;
|
|
117
112
|
}) {
|
|
118
113
|
const nav = useWorkspaceNav();
|
|
119
|
-
const to = nav
|
|
120
|
-
? contentDocPath(
|
|
121
|
-
nav.workspaceSlug,
|
|
122
|
-
doc.type,
|
|
123
|
-
doc.slug,
|
|
124
|
-
doc.locale,
|
|
125
|
-
nav.defaultLocale,
|
|
126
|
-
)
|
|
127
|
-
: "#";
|
|
114
|
+
const to = nav ? contentDocPath(nav.workspaceSlug, doc.type, doc.slug) : "#";
|
|
128
115
|
const title = doc.title?.trim() || doc.slug;
|
|
129
116
|
const status = docCardBadge(doc);
|
|
130
117
|
const secondary = doc.path ?? `${doc.type}/${doc.slug}`;
|
|
@@ -136,7 +123,6 @@ function DocRow({
|
|
|
136
123
|
<FileIcon size={13} />
|
|
137
124
|
</span>
|
|
138
125
|
<span {...stylex.props(styles.title)}>{title}</span>
|
|
139
|
-
{showLocale ? <span {...stylex.props(styles.locale)}>{doc.locale}</span> : null}
|
|
140
126
|
{status ? (
|
|
141
127
|
<span
|
|
142
128
|
{...stylex.props(
|
|
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState, type RefObject } from "react";
|
|
|
3
3
|
import { Popover } from "@base-ui/react/popover";
|
|
4
4
|
import * as stylex from "@stylexjs/stylex";
|
|
5
5
|
import { fetchDocs } from "../api";
|
|
6
|
-
import type
|
|
6
|
+
import { docKey, type DocRef } from "../chrome/EditorFocusContext";
|
|
7
7
|
import { colors, type } from "../styles/tokens.stylex";
|
|
8
8
|
import { cx, popup, popupClassName } from "../ui/popup";
|
|
9
9
|
import { FileIcon } from "./icons";
|
|
@@ -52,9 +52,9 @@ export function MentionPopover({
|
|
|
52
52
|
}, [workspaceId]);
|
|
53
53
|
|
|
54
54
|
const items = useMemo(() => {
|
|
55
|
-
const excluded = new Set(exclude.map((r) =>
|
|
55
|
+
const excluded = new Set(exclude.map((r) => docKey(r)));
|
|
56
56
|
return filterDocs(
|
|
57
|
-
docs.filter((d) => !excluded.has(
|
|
57
|
+
docs.filter((d) => !excluded.has(docKey(d))),
|
|
58
58
|
query,
|
|
59
59
|
);
|
|
60
60
|
}, [docs, query, exclude]);
|
|
@@ -69,7 +69,7 @@ export function MentionPopover({
|
|
|
69
69
|
else if (key === "ArrowUp") setActive((i) => Math.max(i - 1, 0));
|
|
70
70
|
else if (key === "Enter" || key === "Tab") {
|
|
71
71
|
const item = items[active];
|
|
72
|
-
if (item) onSelect({
|
|
72
|
+
if (item) onSelect({ type: item.type, slug: item.slug });
|
|
73
73
|
else onClose();
|
|
74
74
|
} else if (key === "Escape") onClose();
|
|
75
75
|
};
|
|
@@ -79,7 +79,7 @@ export function MentionPopover({
|
|
|
79
79
|
}, [items, active, onSelect, onClose, keyHandlerRef]);
|
|
80
80
|
|
|
81
81
|
const activeId = items[active]
|
|
82
|
-
? `mention-${items[active].
|
|
82
|
+
? `mention-${items[active].type}-${items[active].slug}`
|
|
83
83
|
: undefined;
|
|
84
84
|
|
|
85
85
|
return (
|
|
@@ -113,13 +113,13 @@ export function MentionPopover({
|
|
|
113
113
|
) : (
|
|
114
114
|
items.map((item, i) => (
|
|
115
115
|
<div
|
|
116
|
-
key={
|
|
117
|
-
id={`mention-${item.
|
|
116
|
+
key={docKey(item)}
|
|
117
|
+
id={`mention-${item.type}-${item.slug}`}
|
|
118
118
|
role="option"
|
|
119
119
|
aria-selected={i === active}
|
|
120
120
|
onMouseEnter={() => setActive(i)}
|
|
121
121
|
onMouseDown={(e) => e.preventDefault()}
|
|
122
|
-
onClick={() => onSelect({
|
|
122
|
+
onClick={() => onSelect({ type: item.type, slug: item.slug })}
|
|
123
123
|
{...stylex.props(popup.item, styles.item, i === active && popup.itemHighlighted)}
|
|
124
124
|
>
|
|
125
125
|
<FileIcon size={12} />
|
|
@@ -127,7 +127,6 @@ export function MentionPopover({
|
|
|
127
127
|
{item.type}/{item.slug}
|
|
128
128
|
</span>
|
|
129
129
|
<span {...stylex.props(popup.itemMeta)}>
|
|
130
|
-
{item.locale}
|
|
131
130
|
{item.status === "added"
|
|
132
131
|
? " · new"
|
|
133
132
|
: item.status !== "clean"
|
package/src/chat/PointerChip.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import { CloseIcon, PointerIcon } from "./icons";
|
|
|
11
11
|
const hover = "@media (hover: hover)";
|
|
12
12
|
|
|
13
13
|
export function pointerKey(pointer: ChatPointer): string {
|
|
14
|
-
return `${pointer.
|
|
14
|
+
return `${pointer.type}/${pointer.slug}/${pointer.blockId ?? ""}/${pointer.quote}`;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function pointerLabel(pointer: ChatPointer): string {
|
package/src/chat/chat-context.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// packages/studio/src/chat/chat-context.ts
|
|
2
2
|
import {
|
|
3
|
+
docKey,
|
|
3
4
|
sameDocRef,
|
|
4
5
|
type DocRef,
|
|
5
6
|
type EditorFocus,
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
|
|
8
9
|
/** An element the user pointed at on the site. The agent edits that block, or the quote. */
|
|
9
10
|
export type ChatPointer = {
|
|
10
|
-
locale: string;
|
|
11
11
|
type: string;
|
|
12
12
|
slug: string;
|
|
13
13
|
blockId: string | null;
|
|
@@ -28,7 +28,6 @@ export type ChatRequestContext = {
|
|
|
28
28
|
|
|
29
29
|
function samePointer(a: ChatPointer, b: ChatPointer): boolean {
|
|
30
30
|
return (
|
|
31
|
-
a.locale === b.locale &&
|
|
32
31
|
a.type === b.type &&
|
|
33
32
|
a.slug === b.slug &&
|
|
34
33
|
a.blockId === b.blockId &&
|
|
@@ -61,13 +60,13 @@ export function removePointer(list: ChatPointer[], next: ChatPointer): ChatPoint
|
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
export function chatPointerFromGrab(
|
|
64
|
-
doc: {
|
|
63
|
+
doc: { type?: unknown; slug?: unknown },
|
|
65
64
|
grab: unknown,
|
|
66
65
|
): ChatPointer | null {
|
|
67
|
-
if (!doc || typeof doc.
|
|
66
|
+
if (!doc || typeof doc.type !== "string" || typeof doc.slug !== "string") {
|
|
68
67
|
return null;
|
|
69
68
|
}
|
|
70
|
-
if (!doc.
|
|
69
|
+
if (!doc.type || !doc.slug) return null;
|
|
71
70
|
if (!grab || typeof grab !== "object") return null;
|
|
72
71
|
const value = grab as Record<string, unknown>;
|
|
73
72
|
if (value.blockId !== null && typeof value.blockId !== "string") return null;
|
|
@@ -75,7 +74,6 @@ export function chatPointerFromGrab(
|
|
|
75
74
|
if (value.blockIndex !== null && typeof value.blockIndex !== "number") return null;
|
|
76
75
|
if (typeof value.quote !== "string") return null;
|
|
77
76
|
return {
|
|
78
|
-
locale: doc.locale,
|
|
79
77
|
type: doc.type,
|
|
80
78
|
slug: doc.slug,
|
|
81
79
|
blockId: value.blockId,
|
|
@@ -100,7 +98,7 @@ export function buildChatContext(input: {
|
|
|
100
98
|
for (const m of input.mentions) {
|
|
101
99
|
if (sameDocRef(m, focusDoc)) continue;
|
|
102
100
|
if (mentions.some((x) => sameDocRef(x, m))) continue;
|
|
103
|
-
mentions.push({
|
|
101
|
+
mentions.push({ type: m.type, slug: m.slug });
|
|
104
102
|
}
|
|
105
103
|
const pointers: ChatPointer[] = [];
|
|
106
104
|
for (const pointer of input.pointers) {
|
|
@@ -118,3 +116,5 @@ export function addMention(list: DocRef[], ref: DocRef): DocRef[] {
|
|
|
118
116
|
export function removeMention(list: DocRef[], ref: DocRef): DocRef[] {
|
|
119
117
|
return list.filter((x) => !sameDocRef(x, ref));
|
|
120
118
|
}
|
|
119
|
+
|
|
120
|
+
export { docKey };
|
package/src/chat/chat-helpers.ts
CHANGED
|
@@ -39,7 +39,6 @@ const TREE_STATUSES = new Set<DocStatus>(["clean", "modified", "added", "conflic
|
|
|
39
39
|
|
|
40
40
|
/** Statuses a tool result wants applied to the local doc list immediately. */
|
|
41
41
|
export type TreeStatusPatch = {
|
|
42
|
-
locale: string;
|
|
43
42
|
type: string;
|
|
44
43
|
slug: string;
|
|
45
44
|
status: DocStatus;
|
|
@@ -55,7 +54,6 @@ export function treeStatusPatches(output: unknown): TreeStatusPatch[] {
|
|
|
55
54
|
if (!row || typeof row !== "object") continue;
|
|
56
55
|
const item = row as Partial<TreeStatusPatch>;
|
|
57
56
|
if (
|
|
58
|
-
typeof item.locale !== "string" ||
|
|
59
57
|
typeof item.type !== "string" ||
|
|
60
58
|
!item.type ||
|
|
61
59
|
typeof item.slug !== "string" ||
|
|
@@ -66,7 +64,6 @@ export function treeStatusPatches(output: unknown): TreeStatusPatch[] {
|
|
|
66
64
|
continue;
|
|
67
65
|
}
|
|
68
66
|
patches.push({
|
|
69
|
-
locale: item.locale,
|
|
70
67
|
type: item.type,
|
|
71
68
|
slug: item.slug,
|
|
72
69
|
status: item.status as DocStatus,
|
|
@@ -82,7 +79,6 @@ export function detailForTreePatch(
|
|
|
82
79
|
patch: TreeStatusPatch,
|
|
83
80
|
): {
|
|
84
81
|
workspaceId: string;
|
|
85
|
-
locale: string;
|
|
86
82
|
type: string;
|
|
87
83
|
slug: string;
|
|
88
84
|
status?: TreeStatusPatch["status"];
|
|
@@ -91,7 +87,6 @@ export function detailForTreePatch(
|
|
|
91
87
|
const gone = patch.absent === true || patch.status === "deleted";
|
|
92
88
|
return {
|
|
93
89
|
workspaceId,
|
|
94
|
-
locale: patch.locale,
|
|
95
90
|
type: patch.type,
|
|
96
91
|
slug: patch.slug,
|
|
97
92
|
...(gone ? { remove: true } : { status: patch.status }),
|
|
@@ -277,7 +272,7 @@ export function changeIdsInMessage(message: UIMessage): string[] {
|
|
|
277
272
|
|
|
278
273
|
export type ChangeStackItem = Pick<
|
|
279
274
|
ChatChangeSummary,
|
|
280
|
-
"id" | "
|
|
275
|
+
"id" | "type" | "slug" | "status" | "createdAt"
|
|
281
276
|
>;
|
|
282
277
|
|
|
283
278
|
function isLaterChange(a: ChangeStackItem, b: ChangeStackItem) {
|
|
@@ -295,10 +290,10 @@ export function presentedChangeStatus(
|
|
|
295
290
|
all: ChangeStackItem[],
|
|
296
291
|
): ChatChangeStatus {
|
|
297
292
|
if (change.status !== "applied") return change.status;
|
|
298
|
-
const key = `${change.
|
|
293
|
+
const key = `${change.type}/${change.slug}`;
|
|
299
294
|
const covered = all.some((other) => {
|
|
300
295
|
if (other.id === change.id) return false;
|
|
301
|
-
if (`${other.
|
|
296
|
+
if (`${other.type}/${other.slug}` !== key) return false;
|
|
302
297
|
if (other.status !== "applied" && other.status !== "kept") return false;
|
|
303
298
|
return isLaterChange(other, change);
|
|
304
299
|
});
|
|
@@ -357,7 +357,7 @@ export function CommandPalette() {
|
|
|
357
357
|
}
|
|
358
358
|
for (const doc of docs) {
|
|
359
359
|
list.push({
|
|
360
|
-
id: `doc-${doc.
|
|
360
|
+
id: `doc-${doc.type}-${doc.slug}`,
|
|
361
361
|
group: "Documents",
|
|
362
362
|
icon: "document",
|
|
363
363
|
label: `${doc.type}/${doc.slug}`,
|
|
@@ -370,15 +370,7 @@ export function CommandPalette() {
|
|
|
370
370
|
: doc.status === "modified"
|
|
371
371
|
? "Modified"
|
|
372
372
|
: doc.status,
|
|
373
|
-
run: go(
|
|
374
|
-
contentDocPath(
|
|
375
|
-
workspaceNav.workspaceSlug,
|
|
376
|
-
doc.type,
|
|
377
|
-
doc.slug,
|
|
378
|
-
doc.locale,
|
|
379
|
-
workspaceNav.defaultLocale,
|
|
380
|
-
),
|
|
381
|
-
),
|
|
373
|
+
run: go(contentDocPath(workspaceNav.workspaceSlug, doc.type, doc.slug)),
|
|
382
374
|
});
|
|
383
375
|
}
|
|
384
376
|
}
|
|
@@ -56,12 +56,11 @@ export const STATUS_LABEL: Record<ChangeStatus, string> = {
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
function changeKey(doc: GitChange) {
|
|
59
|
-
return `${doc.
|
|
59
|
+
return `${doc.type}/${doc.slug}`;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function ConflictResolvePanel({
|
|
63
63
|
workspaceSlug,
|
|
64
|
-
defaultLocale,
|
|
65
64
|
conflicts,
|
|
66
65
|
resolvingKey,
|
|
67
66
|
error,
|
|
@@ -69,7 +68,6 @@ function ConflictResolvePanel({
|
|
|
69
68
|
onResolve,
|
|
70
69
|
}: {
|
|
71
70
|
workspaceSlug: string | null;
|
|
72
|
-
defaultLocale: string;
|
|
73
71
|
conflicts: GitChange[];
|
|
74
72
|
resolvingKey: string | null;
|
|
75
73
|
error: string | null;
|
|
@@ -88,13 +86,7 @@ function ConflictResolvePanel({
|
|
|
88
86
|
{conflicts.map((doc) => {
|
|
89
87
|
const key = changeKey(doc);
|
|
90
88
|
const href = workspaceSlug
|
|
91
|
-
? contentDocPath(
|
|
92
|
-
workspaceSlug,
|
|
93
|
-
doc.type,
|
|
94
|
-
doc.slug,
|
|
95
|
-
doc.locale,
|
|
96
|
-
defaultLocale,
|
|
97
|
-
)
|
|
89
|
+
? contentDocPath(workspaceSlug, doc.type, doc.slug)
|
|
98
90
|
: null;
|
|
99
91
|
return (
|
|
100
92
|
<li key={key} {...stylex.props(styles.conflictRow)}>
|
|
@@ -107,7 +99,6 @@ function ConflictResolvePanel({
|
|
|
107
99
|
<span {...stylex.props(styles.changeName)}>
|
|
108
100
|
{doc.type}/{doc.slug}
|
|
109
101
|
</span>
|
|
110
|
-
<span {...stylex.props(popup.itemMeta)}>{doc.locale}</span>
|
|
111
102
|
</Link>
|
|
112
103
|
) : (
|
|
113
104
|
<span {...stylex.props(styles.changeName)}>
|
|
@@ -293,10 +284,9 @@ export function WorkspaceStatusBar({ workspaceId }: { workspaceId: string }) {
|
|
|
293
284
|
setResolvingKey(key);
|
|
294
285
|
setError(null);
|
|
295
286
|
try {
|
|
296
|
-
await resolveDoc(workspaceId, doc.
|
|
287
|
+
await resolveDoc(workspaceId, doc.type, doc.slug, strategy);
|
|
297
288
|
notifyTreeChanged({
|
|
298
289
|
workspaceId,
|
|
299
|
-
locale: doc.locale,
|
|
300
290
|
type: doc.type,
|
|
301
291
|
slug: doc.slug,
|
|
302
292
|
status: strategy === "theirs" ? "clean" : "modified",
|
|
@@ -341,13 +331,7 @@ export function WorkspaceStatusBar({ workspaceId }: { workspaceId: string }) {
|
|
|
341
331
|
const slug = ref.slice(slash + 1);
|
|
342
332
|
const doc = changes.find((change) => change.type === type && change.slug === slug);
|
|
343
333
|
if (!doc) return null;
|
|
344
|
-
return contentDocPath(
|
|
345
|
-
workspaceNav.workspaceSlug,
|
|
346
|
-
doc.type,
|
|
347
|
-
doc.slug,
|
|
348
|
-
doc.locale,
|
|
349
|
-
workspaceNav.defaultLocale,
|
|
350
|
-
);
|
|
334
|
+
return contentDocPath(workspaceNav.workspaceSlug, doc.type, doc.slug);
|
|
351
335
|
}
|
|
352
336
|
|
|
353
337
|
const commitLabel = committing
|
|
@@ -444,7 +428,7 @@ export function WorkspaceStatusBar({ workspaceId }: { workspaceId: string }) {
|
|
|
444
428
|
<div {...stylex.props(styles.changes)}>
|
|
445
429
|
<p {...stylex.props(popup.groupLabel)}>Uncommitted</p>
|
|
446
430
|
{changes.map((doc) => (
|
|
447
|
-
<ChangeRow key={
|
|
431
|
+
<ChangeRow key={changeKey(doc)} doc={doc} />
|
|
448
432
|
))}
|
|
449
433
|
</div>
|
|
450
434
|
</>
|
|
@@ -566,7 +550,6 @@ export function WorkspaceStatusBar({ workspaceId }: { workspaceId: string }) {
|
|
|
566
550
|
{action === "resolve" ? (
|
|
567
551
|
<ConflictResolvePanel
|
|
568
552
|
workspaceSlug={workspaceNav?.workspaceSlug ?? null}
|
|
569
|
-
defaultLocale={workspaceNav?.defaultLocale ?? "en"}
|
|
570
553
|
conflicts={conflicts}
|
|
571
554
|
resolvingKey={resolvingKey}
|
|
572
555
|
error={error}
|
|
@@ -598,7 +581,7 @@ export function WorkspaceStatusBar({ workspaceId }: { workspaceId: string }) {
|
|
|
598
581
|
) : null}
|
|
599
582
|
<ul {...stylex.props(styles.commitFiles)}>
|
|
600
583
|
{changes.map((doc) => (
|
|
601
|
-
<li key={
|
|
584
|
+
<li key={changeKey(doc)}>
|
|
602
585
|
<ChangeRow
|
|
603
586
|
doc={doc}
|
|
604
587
|
invalid={
|
|
@@ -52,9 +52,9 @@ function FocusBridge() {
|
|
|
52
52
|
const data = event.data as { type?: string; doc?: Partial<DocRef> };
|
|
53
53
|
if (data?.type !== "backmatter:focus") return;
|
|
54
54
|
const doc = data.doc;
|
|
55
|
-
if (!doc?.
|
|
55
|
+
if (!doc?.type || !doc.slug) return;
|
|
56
56
|
setFocus({
|
|
57
|
-
docRef: {
|
|
57
|
+
docRef: { type: doc.type, slug: doc.slug },
|
|
58
58
|
frontmatter: {},
|
|
59
59
|
body: "",
|
|
60
60
|
selection: null,
|
|
@@ -89,7 +89,7 @@ function PointerBridge() {
|
|
|
89
89
|
const data = event.data as { type?: string; grab?: unknown; doc?: unknown };
|
|
90
90
|
if (data?.type !== "backmatter:pointer-grab") return;
|
|
91
91
|
const pointer = chatPointerFromGrab(
|
|
92
|
-
(data.doc ?? {}) as {
|
|
92
|
+
(data.doc ?? {}) as { type?: unknown; slug?: unknown },
|
|
93
93
|
data.grab,
|
|
94
94
|
);
|
|
95
95
|
if (pointer) addPointer(pointer);
|
|
@@ -39,7 +39,7 @@ function parentOrigin(): string {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function changeKey(doc: GitChange) {
|
|
42
|
-
return `${doc.
|
|
42
|
+
return `${doc.type}/${doc.slug}`;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const STATUS_LABEL: Record<ChangeStatus, string> = {
|
|
@@ -129,10 +129,9 @@ export function EmbedPublishPage({
|
|
|
129
129
|
setResolvingKey(key);
|
|
130
130
|
setError(null);
|
|
131
131
|
try {
|
|
132
|
-
await resolveDoc(workspaceId, doc.
|
|
132
|
+
await resolveDoc(workspaceId, doc.type, doc.slug, strategy);
|
|
133
133
|
notifyTreeChanged({
|
|
134
134
|
workspaceId,
|
|
135
|
-
locale: doc.locale,
|
|
136
135
|
type: doc.type,
|
|
137
136
|
slug: doc.slug,
|
|
138
137
|
status: strategy === "theirs" ? "clean" : "modified",
|
package/src/pages/type-docs.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// packages/studio/src/pages/type-docs.ts
|
|
2
2
|
|
|
3
|
-
/** Slug order
|
|
4
|
-
export function compareTypeDocs(
|
|
5
|
-
a
|
|
6
|
-
b: { slug: string; locale?: string },
|
|
7
|
-
) {
|
|
8
|
-
return a.slug.localeCompare(b.slug) || (a.locale ?? "").localeCompare(b.locale ?? "");
|
|
3
|
+
/** Slug order for type index lists. */
|
|
4
|
+
export function compareTypeDocs(a: { slug: string }, b: { slug: string }) {
|
|
5
|
+
return a.slug.localeCompare(b.slug);
|
|
9
6
|
}
|
package/src/replica/store.ts
CHANGED
|
@@ -14,25 +14,19 @@ import type {
|
|
|
14
14
|
} from "./types";
|
|
15
15
|
|
|
16
16
|
function docKey(ref: DocRef) {
|
|
17
|
-
return `${ref.
|
|
17
|
+
return `${ref.type}/${ref.slug}`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const OVERLAY_STATUSES = new Set(["modified", "added", "conflict", "deleted"]);
|
|
21
21
|
|
|
22
22
|
function validOverlay(row: ReplicaHit["overlay"][number]) {
|
|
23
|
-
return Boolean(
|
|
24
|
-
typeof row.locale === "string" &&
|
|
25
|
-
row.type &&
|
|
26
|
-
row.slug &&
|
|
27
|
-
OVERLAY_STATUSES.has(row.status),
|
|
28
|
-
);
|
|
23
|
+
return Boolean(row.type && row.slug && OVERLAY_STATUSES.has(row.status));
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
function changesFromDocs(docs: DocListItem[]) {
|
|
32
27
|
return docs
|
|
33
28
|
.filter((doc): doc is DocListItem & { status: ChangeStatus } => doc.status !== "clean")
|
|
34
29
|
.map((doc) => ({
|
|
35
|
-
locale: doc.locale,
|
|
36
30
|
type: doc.type,
|
|
37
31
|
slug: doc.slug,
|
|
38
32
|
status: doc.status,
|
|
@@ -308,7 +302,6 @@ export function createReplicaStore(
|
|
|
308
302
|
pending.delete(docKey(doc));
|
|
309
303
|
if (over.status === "deleted") continue;
|
|
310
304
|
docs.push({
|
|
311
|
-
locale: over.locale,
|
|
312
305
|
type: over.type,
|
|
313
306
|
slug: over.slug,
|
|
314
307
|
status: over.status,
|
|
@@ -325,7 +318,6 @@ export function createReplicaStore(
|
|
|
325
318
|
for (const over of pending.values()) {
|
|
326
319
|
if (over.status === "deleted") continue;
|
|
327
320
|
docs.push({
|
|
328
|
-
locale: over.locale,
|
|
329
321
|
type: over.type,
|
|
330
322
|
slug: over.slug,
|
|
331
323
|
status: over.status,
|
package/src/replica/types.ts
CHANGED
|
@@ -29,7 +29,6 @@ export type ReplicaSnapshot = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export type ReplicaOverlayItem = {
|
|
32
|
-
locale: string;
|
|
33
32
|
type: string;
|
|
34
33
|
slug: string;
|
|
35
34
|
status: Exclude<DocStatus, "clean">;
|
|
@@ -77,7 +76,7 @@ export type ReplicaRecord = {
|
|
|
77
76
|
checkouts: Record<string, ReplicaCheckout>;
|
|
78
77
|
};
|
|
79
78
|
|
|
80
|
-
export type DocRef = {
|
|
79
|
+
export type DocRef = { type: string; slug: string };
|
|
81
80
|
|
|
82
81
|
export type ReplicaPersistence = {
|
|
83
82
|
load(userId: string): Promise<ReplicaRecord | null>;
|
package/src/site-widget.tsx
CHANGED
|
@@ -27,7 +27,6 @@ import { applyThemeClass } from "./theme/apply-theme-class";
|
|
|
27
27
|
import { TooltipProvider } from "./ui/Tooltip";
|
|
28
28
|
|
|
29
29
|
export type SiteWidgetDoc = {
|
|
30
|
-
locale: string;
|
|
31
30
|
type: string;
|
|
32
31
|
slug: string;
|
|
33
32
|
workspaceId: string;
|
|
@@ -200,12 +199,12 @@ function DocFocus({ doc }: { doc: SiteWidgetDoc }) {
|
|
|
200
199
|
const { setFocus } = useEditorFocus();
|
|
201
200
|
useEffect(() => {
|
|
202
201
|
setFocus({
|
|
203
|
-
docRef: {
|
|
202
|
+
docRef: { type: doc.type, slug: doc.slug },
|
|
204
203
|
frontmatter: {},
|
|
205
204
|
body: "",
|
|
206
205
|
selection: null,
|
|
207
206
|
});
|
|
208
|
-
}, [doc.
|
|
207
|
+
}, [doc.type, doc.slug, setFocus]);
|
|
209
208
|
return null;
|
|
210
209
|
}
|
|
211
210
|
|