@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backmatter/studio",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
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": {
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?: { locale: string; type: string; slug: string };
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";
@@ -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.locale
32
- ? `${change.locale}/${change.type}/${change.slug}`
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.renamedFromLocale
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>
@@ -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: { locale: string; type: string; slug: string };
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({ locale: patch.locale, type: patch.type, slug: patch.slug });
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.locale || !ref.type || !ref.slug) return null;
179
- return { locale: ref.locale, type: ref.type, slug: ref.slug };
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({ locale: change.locale, type: change.type, slug: change.slug });
660
+ notifyDocChanged({ type: change.type, slug: change.slug });
661
661
  notifyTreeChanged();
662
662
  }
663
663
  }, []);
@@ -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.locale}/${doc.type}/${doc.slug}`}
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 { DocRef } from "../chrome/EditorFocusContext";
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) => `${r.locale}/${r.type}/${r.slug}`));
55
+ const excluded = new Set(exclude.map((r) => docKey(r)));
56
56
  return filterDocs(
57
- docs.filter((d) => !excluded.has(`${d.locale}/${d.type}/${d.slug}`)),
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({ locale: item.locale, type: item.type, slug: item.slug });
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].locale}-${items[active].type}-${items[active].slug}`
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={`${item.locale}/${item.type}/${item.slug}`}
117
- id={`mention-${item.locale}-${item.type}-${item.slug}`}
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({ locale: item.locale, type: item.type, slug: item.slug })}
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"
@@ -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.locale}/${pointer.type}/${pointer.slug}/${pointer.blockId ?? ""}/${pointer.quote}`;
14
+ return `${pointer.type}/${pointer.slug}/${pointer.blockId ?? ""}/${pointer.quote}`;
15
15
  }
16
16
 
17
17
  function pointerLabel(pointer: ChatPointer): string {
@@ -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: { locale?: unknown; type?: unknown; slug?: unknown },
63
+ doc: { type?: unknown; slug?: unknown },
65
64
  grab: unknown,
66
65
  ): ChatPointer | null {
67
- if (!doc || typeof doc.locale !== "string" || typeof doc.type !== "string" || typeof doc.slug !== "string") {
66
+ if (!doc || typeof doc.type !== "string" || typeof doc.slug !== "string") {
68
67
  return null;
69
68
  }
70
- if (!doc.locale || !doc.type || !doc.slug) return null;
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({ locale: m.locale, type: m.type, slug: m.slug });
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 };
@@ -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" | "locale" | "type" | "slug" | "status" | "createdAt"
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.locale}/${change.type}/${change.slug}`;
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.locale}/${other.type}/${other.slug}` !== key) return false;
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.locale}-${doc.type}-${doc.slug}`,
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.locale}/${doc.type}/${doc.slug}`;
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.locale, doc.type, doc.slug, strategy);
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={`${doc.locale}/${doc.type}/${doc.slug}`} doc={doc} />
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={`${doc.locale}/${doc.type}/${doc.slug}`}>
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?.locale || !doc.type || !doc.slug) return;
55
+ if (!doc?.type || !doc.slug) return;
56
56
  setFocus({
57
- docRef: { locale: doc.locale, type: doc.type, slug: doc.slug },
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 { locale?: unknown; type?: unknown; slug?: unknown },
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.locale}/${doc.type}/${doc.slug}`;
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.locale, doc.type, doc.slug, strategy);
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",
@@ -1,9 +1,6 @@
1
1
  // packages/studio/src/pages/type-docs.ts
2
2
 
3
- /** Slug order. A missing locale is not a language — data models omit it. */
4
- export function compareTypeDocs(
5
- a: { slug: string; locale?: string },
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
  }
@@ -14,25 +14,19 @@ import type {
14
14
  } from "./types";
15
15
 
16
16
  function docKey(ref: DocRef) {
17
- return `${ref.locale}/${ref.type}/${ref.slug}`;
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,
@@ -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 = { locale: string; type: string; slug: string };
79
+ export type DocRef = { type: string; slug: string };
81
80
 
82
81
  export type ReplicaPersistence = {
83
82
  load(userId: string): Promise<ReplicaRecord | null>;
@@ -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: { locale: doc.locale, type: doc.type, slug: doc.slug },
202
+ docRef: { type: doc.type, slug: doc.slug },
204
203
  frontmatter: {},
205
204
  body: "",
206
205
  selection: null,
207
206
  });
208
- }, [doc.locale, doc.type, doc.slug, setFocus]);
207
+ }, [doc.type, doc.slug, setFocus]);
209
208
  return null;
210
209
  }
211
210