@backmatter/studio 0.0.12 → 0.0.14
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-ZZXQt3zy.js → UiKitApp-fYeVEf4-.js} +323 -321
- package/dist/chunks/{WorkspaceRoutes-CCDPtLla.js → WorkspaceRoutes-Dt7JvEWT.js} +33082 -33099
- package/dist/chunks/{dashboard-Dw0zfTXg.js → dashboard-BuY2aGkO.js} +2 -2
- package/dist/chunks/{index-DhZGCAru.js → index-NWDqEZYW.js} +1 -1
- package/dist/dashboard.js +2 -2
- package/dist/index.js +2 -2
- package/dist/site-widget.js +8491 -9824
- package/package.json +2 -2
- package/src/api.ts +8 -23
- package/src/chat/ChangeCard.tsx +6 -2
- package/src/chat/ChatMessage.tsx +23 -18
- package/src/chat/ChatProvider.tsx +13 -11
- package/src/chat/chat-helpers.ts +56 -8
- package/src/chrome/ContentExplorerSidebar.tsx +51 -88
- package/src/chrome/EditorFocusContext.tsx +3 -3
- package/src/chrome/EditorStatusContext.tsx +0 -3
- package/src/chrome/WorkspaceNavContext.tsx +0 -2
- package/src/chrome/content-api-url.ts +3 -12
- package/src/chrome/content-repo-path.ts +8 -25
- package/src/chrome/content-type-groups.ts +0 -3
- package/src/chrome/folder-create-context.ts +15 -39
- package/src/pages/DocEditorPage.tsx +17 -33
- package/src/pages/LegacyWorkspaceRedirect.tsx +2 -9
- package/src/pages/WorkspaceLayout.tsx +0 -5
- package/src/pages/preview-url.ts +6 -20
- package/src/preview-frame.tsx +10 -4
- package/src/replica/store.ts +1 -1
- package/src/site-bar.tsx +3 -6
- package/src/site-dashboard-href.ts +1 -3
- package/src/workspace-path.ts +3 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backmatter/studio",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "The Backmatter editing surface: document editor, AI chat, publish. Mounted on your site or hosted on the dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@ai-sdk/react": "^4.0.100",
|
|
59
|
-
"@backmatter/core": "^0.0.
|
|
59
|
+
"@backmatter/core": "^0.0.28",
|
|
60
60
|
"@base-ui/react": "^1.6.0",
|
|
61
61
|
"@central-icons-react/round-outlined-radius-2-stroke-1.5": "1.2.15",
|
|
62
62
|
"@dnd-kit/abstract": "^0.5.0",
|
package/src/api.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// packages/studio/src/api.ts
|
|
2
|
-
import { encodeDocLocale } from "@backmatter/core";
|
|
3
2
|
import { replicaQuery, type ReplicaCache } from "./replica/cache";
|
|
4
3
|
import type { ReplicaBody } from "./replica/types";
|
|
5
4
|
import { apiCredentials, apiHeaders, apiUrl, reportUnauthorized } from "./studio-config";
|
|
@@ -160,7 +159,6 @@ export type WorkspaceDetail = {
|
|
|
160
159
|
githubDefaultBranch: string | null;
|
|
161
160
|
contentRoot: string | null;
|
|
162
161
|
githubDirectory: string | null;
|
|
163
|
-
locales: string[];
|
|
164
162
|
siteUrls: string[];
|
|
165
163
|
/** Last `@backmatter/studio` version each site reported, keyed by origin. */
|
|
166
164
|
studioVersions?: Record<string, { version: string; seenAt: string }>;
|
|
@@ -399,7 +397,6 @@ export type ChangeStatus = "modified" | "added" | "conflict" | "deleted";
|
|
|
399
397
|
export type DocStatus = "clean" | ChangeStatus;
|
|
400
398
|
|
|
401
399
|
export type GitChange = {
|
|
402
|
-
locale: string;
|
|
403
400
|
type: string;
|
|
404
401
|
slug: string;
|
|
405
402
|
status: ChangeStatus;
|
|
@@ -474,7 +471,7 @@ export function commitWorkspace(workspaceId: string, message?: string) {
|
|
|
474
471
|
export function defaultCommitMessage(changes: GitChange[]) {
|
|
475
472
|
if (changes.length === 1) {
|
|
476
473
|
const f = changes[0]!;
|
|
477
|
-
return `backmatter: update ${f.type}/${f.slug}
|
|
474
|
+
return `backmatter: update ${f.type}/${f.slug}`;
|
|
478
475
|
}
|
|
479
476
|
return `backmatter: update ${changes.length} files`;
|
|
480
477
|
}
|
|
@@ -486,7 +483,6 @@ export function notifyWorkspaceChanged() {
|
|
|
486
483
|
|
|
487
484
|
export type TreeChangeDetail = {
|
|
488
485
|
workspaceId: string;
|
|
489
|
-
locale: string;
|
|
490
486
|
type: string;
|
|
491
487
|
slug: string;
|
|
492
488
|
status?: DocStatus;
|
|
@@ -512,12 +508,11 @@ export function notifyHeadChanged() {
|
|
|
512
508
|
*/
|
|
513
509
|
export function publishCommittedFiles(
|
|
514
510
|
workspaceId: string,
|
|
515
|
-
files: Array<{
|
|
511
|
+
files: Array<{ type: string; slug: string; githubSha: string }>,
|
|
516
512
|
) {
|
|
517
513
|
for (const file of files) {
|
|
518
514
|
notifyTreeChanged({
|
|
519
515
|
workspaceId,
|
|
520
|
-
locale: file.locale,
|
|
521
516
|
type: file.type,
|
|
522
517
|
slug: file.slug,
|
|
523
518
|
...(file.githubSha ? { status: "clean" } : { remove: true }),
|
|
@@ -561,22 +556,18 @@ export async function importContent(workspaceId: string) {
|
|
|
561
556
|
export const pullFromGithub = importContent;
|
|
562
557
|
|
|
563
558
|
export type DocListItem = {
|
|
564
|
-
locale: string;
|
|
565
559
|
type: string;
|
|
566
560
|
slug: string;
|
|
567
561
|
status: DocStatus;
|
|
568
562
|
};
|
|
569
563
|
|
|
570
|
-
export { encodeDocLocale };
|
|
571
|
-
|
|
572
564
|
export function docApiPath(
|
|
573
565
|
workspaceId: string,
|
|
574
|
-
locale: string,
|
|
575
566
|
type: string,
|
|
576
567
|
slug: string,
|
|
577
568
|
suffix = "",
|
|
578
569
|
) {
|
|
579
|
-
return `/workspaces/${workspaceId}/docs/${encodeURIComponent(
|
|
570
|
+
return `/workspaces/${workspaceId}/docs/${encodeURIComponent(type)}/${encodeURIComponent(slug)}${suffix}`;
|
|
580
571
|
}
|
|
581
572
|
|
|
582
573
|
export function fetchDocs(workspaceId: string) {
|
|
@@ -605,7 +596,6 @@ export function fetchDocNav(
|
|
|
605
596
|
if (input.limit !== undefined) qs.set("limit", String(input.limit));
|
|
606
597
|
if (input.offset !== undefined) qs.set("offset", String(input.offset));
|
|
607
598
|
if (input.current) {
|
|
608
|
-
qs.set("locale", input.current.locale);
|
|
609
599
|
qs.set("type", input.current.type);
|
|
610
600
|
qs.set("slug", input.current.slug);
|
|
611
601
|
}
|
|
@@ -614,13 +604,11 @@ export function fetchDocNav(
|
|
|
614
604
|
|
|
615
605
|
export function fetchDoc(
|
|
616
606
|
workspaceId: string,
|
|
617
|
-
locale: string,
|
|
618
607
|
type: string,
|
|
619
608
|
slug: string,
|
|
620
609
|
) {
|
|
621
610
|
return api<{
|
|
622
611
|
doc: {
|
|
623
|
-
locale: string;
|
|
624
612
|
type: string;
|
|
625
613
|
slug: string;
|
|
626
614
|
branch: string;
|
|
@@ -643,7 +631,7 @@ export function fetchDoc(
|
|
|
643
631
|
} | null;
|
|
644
632
|
parseError?: string | null;
|
|
645
633
|
};
|
|
646
|
-
}>(docApiPath(workspaceId,
|
|
634
|
+
}>(docApiPath(workspaceId, type, slug));
|
|
647
635
|
}
|
|
648
636
|
|
|
649
637
|
/** `head` = committed on the branch; `tree` = my uncommitted working copy. */
|
|
@@ -652,14 +640,12 @@ export type ContentSource = "head" | "tree";
|
|
|
652
640
|
/** Save into my working tree (autosave). */
|
|
653
641
|
export function saveDoc(
|
|
654
642
|
workspaceId: string,
|
|
655
|
-
locale: string,
|
|
656
643
|
type: string,
|
|
657
644
|
slug: string,
|
|
658
645
|
input: { frontmatter: Record<string, unknown>; body: string },
|
|
659
646
|
) {
|
|
660
647
|
return api<{
|
|
661
648
|
doc: {
|
|
662
|
-
locale: string;
|
|
663
649
|
type: string;
|
|
664
650
|
slug: string;
|
|
665
651
|
source: "tree";
|
|
@@ -667,7 +653,7 @@ export function saveDoc(
|
|
|
667
653
|
headSha: string | null;
|
|
668
654
|
raw: string;
|
|
669
655
|
};
|
|
670
|
-
}>(docApiPath(workspaceId,
|
|
656
|
+
}>(docApiPath(workspaceId, type, slug), {
|
|
671
657
|
method: "PUT",
|
|
672
658
|
body: JSON.stringify(input),
|
|
673
659
|
});
|
|
@@ -676,12 +662,11 @@ export function saveDoc(
|
|
|
676
662
|
/** Drop my uncommitted copy; the doc reads from HEAD again. */
|
|
677
663
|
export function revertDoc(
|
|
678
664
|
workspaceId: string,
|
|
679
|
-
locale: string,
|
|
680
665
|
type: string,
|
|
681
666
|
slug: string,
|
|
682
667
|
) {
|
|
683
668
|
return api<{ ok: true }>(
|
|
684
|
-
docApiPath(workspaceId,
|
|
669
|
+
docApiPath(workspaceId, type, slug, "/tree"),
|
|
685
670
|
{ method: "DELETE" },
|
|
686
671
|
);
|
|
687
672
|
}
|
|
@@ -689,13 +674,12 @@ export function revertDoc(
|
|
|
689
674
|
/** Resolve a conflict: keep mine (rebase onto the new HEAD) or take theirs (revert). */
|
|
690
675
|
export function resolveDoc(
|
|
691
676
|
workspaceId: string,
|
|
692
|
-
locale: string,
|
|
693
677
|
type: string,
|
|
694
678
|
slug: string,
|
|
695
679
|
strategy: "mine" | "theirs",
|
|
696
680
|
) {
|
|
697
681
|
return api<{ ok: true; status: DocStatus; headSha: string | null }>(
|
|
698
|
-
docApiPath(workspaceId,
|
|
682
|
+
docApiPath(workspaceId, type, slug, "/resolve"),
|
|
699
683
|
{ method: "POST", body: JSON.stringify({ strategy }) },
|
|
700
684
|
);
|
|
701
685
|
}
|
|
@@ -729,6 +713,7 @@ export type ChatChangeDiff = {
|
|
|
729
713
|
added: number;
|
|
730
714
|
removed: number;
|
|
731
715
|
renamedFrom?: string;
|
|
716
|
+
renamedFromLocale?: string;
|
|
732
717
|
hunks: Array<{
|
|
733
718
|
oldStart: number;
|
|
734
719
|
oldLines: number;
|
package/src/chat/ChangeCard.tsx
CHANGED
|
@@ -28,7 +28,9 @@ export function ChangeCard({
|
|
|
28
28
|
}) {
|
|
29
29
|
const [expanded, setExpanded] = useState(false);
|
|
30
30
|
const nav = useWorkspaceNav();
|
|
31
|
-
const label =
|
|
31
|
+
const label = change.locale
|
|
32
|
+
? `${change.locale}/${change.type}/${change.slug}`
|
|
33
|
+
: `${change.type}/${change.slug}`;
|
|
32
34
|
const to = nav
|
|
33
35
|
? contentDocPath(
|
|
34
36
|
nav.workspaceSlug,
|
|
@@ -69,7 +71,9 @@ export function ChangeCard({
|
|
|
69
71
|
)}
|
|
70
72
|
{change.diff?.renamedFrom ? (
|
|
71
73
|
<span {...stylex.props(styles.badge)}>
|
|
72
|
-
{change.diff.
|
|
74
|
+
{change.diff.renamedFromLocale
|
|
75
|
+
? `${change.diff.renamedFromLocale}/${change.diff.renamedFrom} → ${change.locale}/${change.slug}`
|
|
76
|
+
: `${change.diff.renamedFrom} → ${change.slug}`}
|
|
73
77
|
</span>
|
|
74
78
|
) : change.op === "create" ? (
|
|
75
79
|
<span {...stylex.props(styles.badge)}>new</span>
|
package/src/chat/ChatMessage.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
stackChanges,
|
|
20
20
|
toolLabel,
|
|
21
21
|
trimPartialPlaceholder,
|
|
22
|
+
changesInToolOutput,
|
|
22
23
|
type WriteToolOutput,
|
|
23
24
|
} from "./chat-helpers";
|
|
24
25
|
import { CheckIcon, EyeIcon, FileIcon, SearchIcon, UndoIcon } from "./icons";
|
|
@@ -256,13 +257,7 @@ export function ChatMessage({
|
|
|
256
257
|
const failed =
|
|
257
258
|
tp.state === "output-error" ||
|
|
258
259
|
(out !== null && (write || commit) && !out.ok);
|
|
259
|
-
const
|
|
260
|
-
const change = outChange
|
|
261
|
-
? {
|
|
262
|
-
...(session.changes[outChange.id] ?? outChange),
|
|
263
|
-
summary: session.changes[outChange.id]?.summary ?? outChange.summary,
|
|
264
|
-
}
|
|
265
|
-
: null;
|
|
260
|
+
const written = write && out && out.ok ? changesInToolOutput(out) : [];
|
|
266
261
|
// Doc lists placed inline via [[docs]] are not repeated here. While
|
|
267
262
|
// streaming, the placeholder usually arrives a moment after the tool
|
|
268
263
|
// result, so hold the fallback back until the stream has ended;
|
|
@@ -271,7 +266,14 @@ export function ChatMessage({
|
|
|
271
266
|
tp.state === "output-available" && !inlined.has(i) && !streaming
|
|
272
267
|
? docListFromTool(name, tp.output)
|
|
273
268
|
: null;
|
|
274
|
-
const
|
|
269
|
+
const cards = written.map((raw) => {
|
|
270
|
+
const change = {
|
|
271
|
+
...(session.changes[raw.id] ?? raw),
|
|
272
|
+
summary: session.changes[raw.id]?.summary ?? raw.summary,
|
|
273
|
+
};
|
|
274
|
+
const viewStatus = presentedChangeStatus(change, stack);
|
|
275
|
+
return { change, viewStatus };
|
|
276
|
+
});
|
|
275
277
|
return (
|
|
276
278
|
<Reveal key={tp.toolCallId} active={streaming} origin="start">
|
|
277
279
|
<div {...stylex.props(styles.toolBlock)}>
|
|
@@ -294,16 +296,19 @@ export function ChatMessage({
|
|
|
294
296
|
emptyLabel={name === "searchDocs" ? "No matches" : "No documents"}
|
|
295
297
|
/>
|
|
296
298
|
) : null}
|
|
297
|
-
{change
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
299
|
+
{cards.map(({ change, viewStatus }) =>
|
|
300
|
+
viewStatus ? (
|
|
301
|
+
<ChangeCard
|
|
302
|
+
key={change.id}
|
|
303
|
+
change={{ ...change, status: viewStatus }}
|
|
304
|
+
workspaceId={workspaceId}
|
|
305
|
+
busy={busyId === change.id}
|
|
306
|
+
canAct={canAct && !streaming}
|
|
307
|
+
onKeep={() => void act(change.id, () => session.keep(change.id))}
|
|
308
|
+
onUndo={() => void act(change.id, () => session.undo(change.id))}
|
|
309
|
+
/>
|
|
310
|
+
) : null,
|
|
311
|
+
)}
|
|
307
312
|
</div>
|
|
308
313
|
</Reveal>
|
|
309
314
|
);
|
|
@@ -56,9 +56,10 @@ import {
|
|
|
56
56
|
import {
|
|
57
57
|
changeIdsInMessage,
|
|
58
58
|
isWriteTool,
|
|
59
|
+
changesInToolOutput,
|
|
60
|
+
detailForTreePatch,
|
|
59
61
|
treeStatusPatches,
|
|
60
62
|
type TreeStatusPatch,
|
|
61
|
-
type WriteToolOutput,
|
|
62
63
|
} from "./chat-helpers";
|
|
63
64
|
import {
|
|
64
65
|
initialPanelState,
|
|
@@ -158,13 +159,7 @@ const modelStorageKey = (ws: string) => `backmatter-chat-model:${ws}`;
|
|
|
158
159
|
function publishTreePatches(workspaceId: string, patches: TreeStatusPatch[]) {
|
|
159
160
|
let published = false;
|
|
160
161
|
for (const patch of patches) {
|
|
161
|
-
notifyTreeChanged(
|
|
162
|
-
workspaceId,
|
|
163
|
-
locale: patch.locale,
|
|
164
|
-
type: patch.type,
|
|
165
|
-
slug: patch.slug,
|
|
166
|
-
...(patch.absent ? { remove: true } : { status: patch.status }),
|
|
167
|
-
});
|
|
162
|
+
notifyTreeChanged(detailForTreePatch(workspaceId, patch));
|
|
168
163
|
// A published file stays on screen; head-changed reloads it once. A file
|
|
169
164
|
// that disappeared still needs the open editor to drop it.
|
|
170
165
|
if (patch.absent || patch.status !== "clean") {
|
|
@@ -173,6 +168,9 @@ function publishTreePatches(workspaceId: string, patches: TreeStatusPatch[]) {
|
|
|
173
168
|
if (patch.status === "clean") published = true;
|
|
174
169
|
}
|
|
175
170
|
if (published) notifyHeadChanged();
|
|
171
|
+
// The per-file patch updates the tree immediately. A refetch picks up a
|
|
172
|
+
// file the patch missed, and drops one the server no longer lists.
|
|
173
|
+
notifyTreeChanged();
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
function refFromInput(input: unknown): DocRef | null {
|
|
@@ -636,9 +634,13 @@ function LiveSession({
|
|
|
636
634
|
seen.add(part.toolCallId);
|
|
637
635
|
const patches = treeStatusPatches(part.output);
|
|
638
636
|
if (patches.length > 0) publishTreePatches(workspaceId, patches);
|
|
639
|
-
const
|
|
640
|
-
if (
|
|
641
|
-
setChanges((prev) =>
|
|
637
|
+
const found = write ? changesInToolOutput(part.output) : [];
|
|
638
|
+
if (found.length > 0) {
|
|
639
|
+
setChanges((prev) => {
|
|
640
|
+
const next = { ...prev };
|
|
641
|
+
for (const change of found) next[change.id] = change;
|
|
642
|
+
return next;
|
|
643
|
+
});
|
|
642
644
|
if (patches.length === 0) {
|
|
643
645
|
if (ref) notifyDocChanged(ref);
|
|
644
646
|
notifyTreeChanged();
|
package/src/chat/chat-helpers.ts
CHANGED
|
@@ -7,11 +7,28 @@ import type { ChatChangeSummary, ChatThread, DocStatus, ChatChangeStatus } from
|
|
|
7
7
|
import type { DocRef } from "../chrome/EditorFocusContext";
|
|
8
8
|
|
|
9
9
|
export type WriteToolOutput =
|
|
10
|
-
| { ok: true; change
|
|
10
|
+
| { ok: true; change?: ChatChangeSummary; changes?: ChatChangeSummary[] }
|
|
11
11
|
| { ok: false; error: string; message: string };
|
|
12
12
|
|
|
13
13
|
export function isWriteTool(name: string) {
|
|
14
|
-
return
|
|
14
|
+
return (
|
|
15
|
+
name === "writeDoc" ||
|
|
16
|
+
name === "createDoc" ||
|
|
17
|
+
name === "renameDoc" ||
|
|
18
|
+
name === "deleteDoc" ||
|
|
19
|
+
name === "deleteFolder" ||
|
|
20
|
+
name === "moveFolder"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** One write tool may record several files, as a folder move or delete does. */
|
|
25
|
+
export function changesInToolOutput(output: unknown): ChatChangeSummary[] {
|
|
26
|
+
if (!output || typeof output !== "object") return [];
|
|
27
|
+
const body = output as { ok?: boolean; change?: ChatChangeSummary; changes?: ChatChangeSummary[] };
|
|
28
|
+
if (body.ok === false) return [];
|
|
29
|
+
const many = Array.isArray(body.changes) ? body.changes.filter((change) => change?.id) : [];
|
|
30
|
+
if (many.length > 0) return many;
|
|
31
|
+
return body.change?.id ? [body.change] : [];
|
|
15
32
|
}
|
|
16
33
|
|
|
17
34
|
export function isCommitTool(name: string) {
|
|
@@ -39,7 +56,6 @@ export function treeStatusPatches(output: unknown): TreeStatusPatch[] {
|
|
|
39
56
|
const item = row as Partial<TreeStatusPatch>;
|
|
40
57
|
if (
|
|
41
58
|
typeof item.locale !== "string" ||
|
|
42
|
-
!item.locale ||
|
|
43
59
|
typeof item.type !== "string" ||
|
|
44
60
|
!item.type ||
|
|
45
61
|
typeof item.slug !== "string" ||
|
|
@@ -60,6 +76,28 @@ export function treeStatusPatches(output: unknown): TreeStatusPatch[] {
|
|
|
60
76
|
return patches;
|
|
61
77
|
}
|
|
62
78
|
|
|
79
|
+
/** A deleted or absent row leaves the content tree. Other rows update status. */
|
|
80
|
+
export function detailForTreePatch(
|
|
81
|
+
workspaceId: string,
|
|
82
|
+
patch: TreeStatusPatch,
|
|
83
|
+
): {
|
|
84
|
+
workspaceId: string;
|
|
85
|
+
locale: string;
|
|
86
|
+
type: string;
|
|
87
|
+
slug: string;
|
|
88
|
+
status?: TreeStatusPatch["status"];
|
|
89
|
+
remove?: boolean;
|
|
90
|
+
} {
|
|
91
|
+
const gone = patch.absent === true || patch.status === "deleted";
|
|
92
|
+
return {
|
|
93
|
+
workspaceId,
|
|
94
|
+
locale: patch.locale,
|
|
95
|
+
type: patch.type,
|
|
96
|
+
slug: patch.slug,
|
|
97
|
+
...(gone ? { remove: true } : { status: patch.status }),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
63
101
|
export function toolLabel(name: string, input: unknown, output: unknown, state: string): string {
|
|
64
102
|
const ref = (input as { docRef?: { type?: string; slug?: string } } | undefined)?.docRef;
|
|
65
103
|
const doc = ref?.type && ref.slug ? `${ref.type}/${ref.slug}` : "";
|
|
@@ -84,6 +122,17 @@ export function toolLabel(name: string, input: unknown, output: unknown, state:
|
|
|
84
122
|
return running ? `Creating ${doc}…` : `Created ${doc}`;
|
|
85
123
|
case "renameDoc":
|
|
86
124
|
return running ? `Renaming ${doc}…` : `Renamed ${doc}`;
|
|
125
|
+
case "deleteDoc":
|
|
126
|
+
return running ? `Deleting ${doc}…` : `Deleted ${doc}`;
|
|
127
|
+
case "deleteFolder": {
|
|
128
|
+
const path = (input as { path?: string } | undefined)?.path ?? "folder";
|
|
129
|
+
return running ? `Deleting ${path}…` : `Deleted ${path}`;
|
|
130
|
+
}
|
|
131
|
+
case "moveFolder": {
|
|
132
|
+
const from = (input as { from?: string } | undefined)?.from ?? "folder";
|
|
133
|
+
const to = (input as { to?: string } | undefined)?.to ?? "";
|
|
134
|
+
return running ? `Moving ${from}…` : `Moved ${from} to ${to}`;
|
|
135
|
+
}
|
|
87
136
|
case "commit": {
|
|
88
137
|
if (running) return "Publishing…";
|
|
89
138
|
const published = output as { ok?: boolean; files?: unknown[] } | undefined;
|
|
@@ -221,8 +270,7 @@ export function changeIdsInMessage(message: UIMessage): string[] {
|
|
|
221
270
|
if (!isToolUIPart(part)) continue;
|
|
222
271
|
if (!isWriteTool(getToolName(part))) continue;
|
|
223
272
|
if (part.state !== "output-available") continue;
|
|
224
|
-
const
|
|
225
|
-
if (out?.ok) ids.push(out.change.id);
|
|
273
|
+
for (const change of changesInToolOutput(part.output)) ids.push(change.id);
|
|
226
274
|
}
|
|
227
275
|
return ids;
|
|
228
276
|
}
|
|
@@ -267,9 +315,9 @@ export function stackChanges(
|
|
|
267
315
|
for (const part of message.parts) {
|
|
268
316
|
if (!isToolUIPart(part) || part.state !== "output-available") continue;
|
|
269
317
|
if (!isWriteTool(getToolName(part))) continue;
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
318
|
+
for (const change of changesInToolOutput(part.output)) {
|
|
319
|
+
byId.set(change.id, change);
|
|
320
|
+
}
|
|
273
321
|
}
|
|
274
322
|
}
|
|
275
323
|
for (const change of Object.values(overrides)) {
|