@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.
Files changed (61) hide show
  1. package/dist/chunks/{UiKitApp-BDsrXN12.js → UiKitApp-_EDqWID3.js} +2 -2
  2. package/dist/chunks/{WorkspaceRoutes-BTZ1Ah4F.js → WorkspaceRoutes-B2PrFgpz.js} +28979 -27418
  3. package/dist/chunks/{dashboard-FT7dm5wi.js → dashboard-BnlnMnnr.js} +55 -54
  4. package/dist/chunks/{index-DHJvh5Uy.js → index-D_BHF_8q.js} +1 -1
  5. package/dist/dashboard.js +2 -2
  6. package/dist/index.js +2 -2
  7. package/dist/site-widget.css +4 -0
  8. package/dist/site-widget.js +8304 -8029
  9. package/dist/studio.css +186 -12
  10. package/package.json +2 -2
  11. package/src/WorkspaceRoutes.tsx +5 -2
  12. package/src/api.ts +63 -7
  13. package/src/chat/ChangeCard.tsx +8 -2
  14. package/src/chat/ChatMessage.tsx +171 -102
  15. package/src/chat/ChatPanel.tsx +54 -34
  16. package/src/chat/ChatProvider.tsx +116 -20
  17. package/src/chat/Composer.tsx +48 -7
  18. package/src/chat/PointerChip.tsx +9 -5
  19. package/src/chat/ThreadHistory.tsx +3 -3
  20. package/src/chat/chat-context.ts +67 -3
  21. package/src/chat/chat-helpers.ts +12 -0
  22. package/src/chat/chat-state.ts +11 -0
  23. package/src/chrome/AppShell.tsx +16 -5
  24. package/src/chrome/CommandPalette.tsx +12 -0
  25. package/src/chrome/ContentExplorerSidebar.tsx +23 -3
  26. package/src/chrome/UserMenu.tsx +34 -3
  27. package/src/chrome/WorkspaceStatusBar.tsx +209 -27
  28. package/src/chrome/content-api-url.ts +0 -78
  29. package/src/chrome/content-tree-visibility.ts +13 -3
  30. package/src/chrome/explorer-icons.tsx +5 -0
  31. package/src/code-session-entry.ts +9 -0
  32. package/src/components/WorkspaceSwitcher.tsx +2 -2
  33. package/src/element-origin.ts +149 -0
  34. package/src/host-pointer-bootstrap.ts +11 -0
  35. package/src/host-pointer.ts +147 -0
  36. package/src/install-preview-pointer.ts +19 -3
  37. package/src/pages/CodeSessionFiles.tsx +221 -0
  38. package/src/pages/CodeSessionPage.tsx +1188 -0
  39. package/src/pages/DocEditorPage.tsx +33 -15
  40. package/src/pages/PreviewSize.tsx +181 -0
  41. package/src/pages/SettingsGeneralPage.tsx +67 -2
  42. package/src/pages/WorkspaceHomePage.tsx +313 -0
  43. package/src/pages/WorkspaceLayout.tsx +10 -1
  44. package/src/pages/code-session-boot.ts +43 -0
  45. package/src/pages/code-session-context.tsx +51 -0
  46. package/src/pages/code-session-files.ts +42 -0
  47. package/src/pages/code-session-frame.ts +52 -0
  48. package/src/pages/code-session-routes.ts +42 -0
  49. package/src/pages/home-rows.ts +140 -0
  50. package/src/pages/preview-size.ts +33 -0
  51. package/src/pages/session-preview.ts +109 -0
  52. package/src/pages/use-session-preview.ts +70 -0
  53. package/src/replica/store.ts +11 -0
  54. package/src/replica/types.ts +6 -0
  55. package/src/site-pointer.ts +24 -5
  56. package/src/studio-config.ts +1 -1
  57. package/src/ui/ListRow.tsx +12 -3
  58. package/src/ui/Page.tsx +12 -1
  59. package/src/workspace-path.ts +35 -3
  60. package/src/chrome/ContentApiPopover.tsx +0 -215
  61. package/src/pages/WorkspaceHomeRedirect.tsx +0 -17
package/dist/studio.css CHANGED
@@ -27,6 +27,10 @@
27
27
  padding: 0;
28
28
  }
29
29
 
30
+ .x1i3ajwb {
31
+ padding: 2px;
32
+ }
33
+
30
34
  .xk7greg {
31
35
  padding: var(--x1ft7ks7);
32
36
  }
@@ -35,6 +39,10 @@
35
39
  padding: var(--x1lzhbes);
36
40
  }
37
41
 
42
+ .x12u58kn {
43
+ padding: var(--x1o7ezzp);
44
+ }
45
+
38
46
  .xue2yr4 {
39
47
  padding: var(--x1uiwg42);
40
48
  }
@@ -149,10 +157,18 @@
149
157
  border-width: 0;
150
158
  }
151
159
 
160
+ .xvndefy {
161
+ border-width: 1.5px;
162
+ }
163
+
152
164
  .xmkeg23 {
153
165
  border-width: 1px;
154
166
  }
155
167
 
168
+ .xdh2fpr {
169
+ border-width: 2px;
170
+ }
171
+
156
172
  .x9r2w2z {
157
173
  border-width: 3px;
158
174
  }
@@ -209,6 +225,10 @@
209
225
  gap: var(--xvc7yhn);
210
226
  }
211
227
 
228
+ .x1osaytk {
229
+ grid-column: 1 / -1;
230
+ }
231
+
212
232
  .x1xohano {
213
233
  inset-block: -5px;
214
234
  }
@@ -245,10 +265,6 @@
245
265
  margin-inline: auto;
246
266
  }
247
267
 
248
- .x1nmszcn {
249
- margin-inline: var(--x1ft7ks7);
250
- }
251
-
252
268
  .x1agxhcx {
253
269
  margin-inline: var(--x1o7ezzp);
254
270
  }
@@ -289,10 +305,18 @@
289
305
  padding-block: 2px;
290
306
  }
291
307
 
308
+ .xf4egzd {
309
+ padding-block: 3px;
310
+ }
311
+
292
312
  .x62rkhy {
293
313
  padding-block: 48px;
294
314
  }
295
315
 
316
+ .x12ulsxz {
317
+ padding-block: 4px;
318
+ }
319
+
296
320
  .x1t0vds8 {
297
321
  padding-block: 6px;
298
322
  }
@@ -481,6 +505,10 @@
481
505
  align-items: stretch;
482
506
  }
483
507
 
508
+ .xpvyfi4 {
509
+ align-self: flex-end;
510
+ }
511
+
484
512
  .xqcrz7y {
485
513
  align-self: flex-start;
486
514
  }
@@ -501,6 +529,10 @@
501
529
  animation-duration: .7s;
502
530
  }
503
531
 
532
+ .xemfg65 {
533
+ animation-duration: .8s;
534
+ }
535
+
504
536
  .x1m9vv7p {
505
537
  animation-duration: 1.2s;
506
538
  }
@@ -525,6 +557,10 @@
525
557
  animation-name: x1tbvpe3-B;
526
558
  }
527
559
 
560
+ .x1aerksh {
561
+ animation-name: x1wc8ddo-B;
562
+ }
563
+
528
564
  .xt3aoqv {
529
565
  animation-name: x1ymhlvw-B;
530
566
  }
@@ -617,6 +653,10 @@
617
653
  background-color: #fafafa;
618
654
  }
619
655
 
656
+ .x83z2og {
657
+ background-color: #fff;
658
+ }
659
+
620
660
  .xtwfq29 {
621
661
  background-color: currentColor;
622
662
  }
@@ -773,6 +813,10 @@
773
813
  box-shadow: 0 16px 48px #18181b33;
774
814
  }
775
815
 
816
+ .x1dej7w9 {
817
+ box-shadow: 0 1px 2px #00000014;
818
+ }
819
+
776
820
  .xtpupu6 {
777
821
  box-shadow: 0 1px 2px #18181b33;
778
822
  }
@@ -921,6 +965,14 @@
921
965
  display: inline-flex;
922
966
  }
923
967
 
968
+ .x1t1x2f9 {
969
+ flex-basis: 0%;
970
+ }
971
+
972
+ .xdl72j9 {
973
+ flex-basis: auto;
974
+ }
975
+
924
976
  .xdt5ytf {
925
977
  flex-direction: column;
926
978
  }
@@ -933,6 +985,10 @@
933
985
  flex-grow: 0;
934
986
  }
935
987
 
988
+ .x1iyjqo2 {
989
+ flex-grow: 1;
990
+ }
991
+
936
992
  .x2lah0s {
937
993
  flex-shrink: 0;
938
994
  }
@@ -965,10 +1021,22 @@
965
1021
  font-size: .92em;
966
1022
  }
967
1023
 
1024
+ .x1j6dyjg {
1025
+ font-size: 11px;
1026
+ }
1027
+
1028
+ .xfifm61 {
1029
+ font-size: 12px;
1030
+ }
1031
+
968
1032
  .xneq65o {
969
1033
  font-size: 28px;
970
1034
  }
971
1035
 
1036
+ .x1s3cmhv {
1037
+ font-size: 32px;
1038
+ }
1039
+
972
1040
  .xdzlmae {
973
1041
  font-size: 8px;
974
1042
  }
@@ -1029,6 +1097,10 @@
1029
1097
  grid-template-columns: 1fr 1fr;
1030
1098
  }
1031
1099
 
1100
+ .x19ohwaf {
1101
+ grid-template-columns: 1fr minmax(0, 520px) 1fr;
1102
+ }
1103
+
1032
1104
  .x1d5a2s6 {
1033
1105
  grid-template-columns: 7rem 1fr;
1034
1106
  }
@@ -1061,6 +1133,14 @@
1061
1133
  justify-content: space-between;
1062
1134
  }
1063
1135
 
1136
+ .x1qab1bc {
1137
+ justify-self: end;
1138
+ }
1139
+
1140
+ .x1lqcxt8 {
1141
+ justify-self: start;
1142
+ }
1143
+
1064
1144
  .xjat59b {
1065
1145
  letter-spacing: -.01em;
1066
1146
  }
@@ -1117,6 +1197,10 @@
1117
1197
  line-height: 1.55;
1118
1198
  }
1119
1199
 
1200
+ .x1dbl2gt {
1201
+ line-height: 1.6;
1202
+ }
1203
+
1120
1204
  .xo5v014 {
1121
1205
  line-height: 1;
1122
1206
  }
@@ -1195,6 +1279,10 @@
1195
1279
  outline-color: var(--x1nzi8h4);
1196
1280
  }
1197
1281
 
1282
+ .x1e6zrab {
1283
+ outline-color: var(--xz3qvk5);
1284
+ }
1285
+
1198
1286
  .x1ewu8gn {
1199
1287
  outline-offset: 3px;
1200
1288
  }
@@ -1203,6 +1291,10 @@
1203
1291
  outline-style: solid;
1204
1292
  }
1205
1293
 
1294
+ .x1qgsegg {
1295
+ outline-width: 1px;
1296
+ }
1297
+
1206
1298
  .x1de99jn {
1207
1299
  outline-width: 2px;
1208
1300
  }
@@ -1255,6 +1347,10 @@
1255
1347
  row-gap: 40px;
1256
1348
  }
1257
1349
 
1350
+ .xeq4nuv {
1351
+ tab-size: 2;
1352
+ }
1353
+
1258
1354
  .x2b8uid {
1259
1355
  text-align: center;
1260
1356
  }
@@ -1542,6 +1638,14 @@
1542
1638
  outline-width: 2px;
1543
1639
  }
1544
1640
 
1641
+ .x1ke7wyk:disabled {
1642
+ color: var(--xw4dauw);
1643
+ }
1644
+
1645
+ .x14xq8pj:disabled {
1646
+ color: var(--xz3qvk5);
1647
+ }
1648
+
1545
1649
  .xtgyt42:disabled {
1546
1650
  cursor: default;
1547
1651
  }
@@ -1550,6 +1654,10 @@
1550
1654
  cursor: not-allowed;
1551
1655
  }
1552
1656
 
1657
+ .x1bq4at4:disabled {
1658
+ opacity: .3;
1659
+ }
1660
+
1553
1661
  .x2wzlov:disabled {
1554
1662
  opacity: .35;
1555
1663
  }
@@ -1605,6 +1713,10 @@
1605
1713
  animation-duration: 0s;
1606
1714
  }
1607
1715
 
1716
+ .xy1mfq8.xy1mfq8 {
1717
+ animation-duration: 2.4s;
1718
+ }
1719
+
1608
1720
  .x1aquc0h.x1aquc0h {
1609
1721
  animation-name: none;
1610
1722
  }
@@ -1657,6 +1769,14 @@
1657
1769
  }
1658
1770
 
1659
1771
  @media (hover: hover) {
1772
+ .x1avvw9k.x1avvw9k:hover:not(:disabled) {
1773
+ background-color: var(--xeg52a);
1774
+ }
1775
+
1776
+ .x1caf6yr.x1caf6yr:hover:not(:disabled) {
1777
+ color: var(--x5y0tk9);
1778
+ }
1779
+
1660
1780
  .x1e7vmwq.x1e7vmwq:hover [data-folder-action] {
1661
1781
  opacity: 1;
1662
1782
  }
@@ -1762,6 +1882,10 @@
1762
1882
  border-right-width: 1px;
1763
1883
  }
1764
1884
 
1885
+ .x17ycamp {
1886
+ border-top-color: var(--x11n7hvk);
1887
+ }
1888
+
1765
1889
  .xmzxa0n {
1766
1890
  border-top-color: var(--xz3qvk5);
1767
1891
  }
@@ -2030,6 +2154,10 @@
2030
2154
  max-height: 180px;
2031
2155
  }
2032
2156
 
2157
+ .x1hkcv85 {
2158
+ max-height: 200px;
2159
+ }
2160
+
2033
2161
  .x1983irj {
2034
2162
  max-height: 220px;
2035
2163
  }
@@ -2042,6 +2170,10 @@
2042
2170
  max-height: 280px;
2043
2171
  }
2044
2172
 
2173
+ .x1h9acoj {
2174
+ max-height: min(320px,var(--available-height));
2175
+ }
2176
+
2045
2177
  .x10todyo {
2046
2178
  max-height: min(52vh, 440px);
2047
2179
  }
@@ -2078,6 +2210,10 @@
2078
2210
  max-width: 220px;
2079
2211
  }
2080
2212
 
2213
+ .x16em4ew {
2214
+ max-width: 240px;
2215
+ }
2216
+
2081
2217
  .x14lruf5 {
2082
2218
  max-width: 260px;
2083
2219
  }
@@ -2098,6 +2234,10 @@
2098
2234
  max-width: 440px;
2099
2235
  }
2100
2236
 
2237
+ .x1pfbilc {
2238
+ max-width: 460px;
2239
+ }
2240
+
2101
2241
  .xh9uqaf {
2102
2242
  max-width: 60%;
2103
2243
  }
@@ -2122,6 +2262,10 @@
2122
2262
  max-width: 920px;
2123
2263
  }
2124
2264
 
2265
+ .xyzno7u {
2266
+ max-width: 960px;
2267
+ }
2268
+
2125
2269
  .x2lwn1j {
2126
2270
  min-height: 0;
2127
2271
  }
@@ -2174,6 +2318,10 @@
2174
2318
  min-height: 44px;
2175
2319
  }
2176
2320
 
2321
+ .x1ujl9rh {
2322
+ min-height: 84px;
2323
+ }
2324
+
2177
2325
  .x562ti3 {
2178
2326
  min-height: 8rem;
2179
2327
  }
@@ -2210,6 +2358,10 @@
2210
2358
  min-width: 200px;
2211
2359
  }
2212
2360
 
2361
+ .x12rczxh {
2362
+ min-width: 220px;
2363
+ }
2364
+
2213
2365
  .x1f4i7yj {
2214
2366
  min-width: 252px;
2215
2367
  }
@@ -2222,10 +2374,6 @@
2222
2374
  min-width: 48px;
2223
2375
  }
2224
2376
 
2225
- .x9hgts1 {
2226
- min-width: 52px;
2227
- }
2228
-
2229
2377
  .xzhy1st {
2230
2378
  min-width: 8rem;
2231
2379
  }
@@ -2246,6 +2394,10 @@
2246
2394
  overflow-y: auto;
2247
2395
  }
2248
2396
 
2397
+ .x10wlt62 {
2398
+ overflow-y: hidden;
2399
+ }
2400
+
2249
2401
  .x18d9i69 {
2250
2402
  padding-bottom: 0;
2251
2403
  }
@@ -2254,6 +2406,10 @@
2254
2406
  padding-bottom: 2px;
2255
2407
  }
2256
2408
 
2409
+ .xq1608w {
2410
+ padding-bottom: 36px;
2411
+ }
2412
+
2257
2413
  .x2f2w3t {
2258
2414
  padding-bottom: calc(var(--x1uiwg42) + 12px);
2259
2415
  }
@@ -2374,6 +2530,10 @@
2374
2530
  right: var(--x1lzhbes);
2375
2531
  }
2376
2532
 
2533
+ .x1gki5z1 {
2534
+ right: var(--x1o7ezzp);
2535
+ }
2536
+
2377
2537
  .x13vifvy {
2378
2538
  top: 0;
2379
2539
  }
@@ -2458,6 +2618,10 @@
2458
2618
  width: 24px;
2459
2619
  }
2460
2620
 
2621
+ .x1hfn5x7 {
2622
+ width: 260px;
2623
+ }
2624
+
2461
2625
  .x23j0i4 {
2462
2626
  width: 26px;
2463
2627
  }
@@ -2482,12 +2646,12 @@
2482
2646
  width: 32px;
2483
2647
  }
2484
2648
 
2485
- .x14qfxbe {
2486
- width: 36px;
2649
+ .x1cvmir6 {
2650
+ width: 360px;
2487
2651
  }
2488
2652
 
2489
- .xwl9fdd {
2490
- width: 380px;
2653
+ .x14qfxbe {
2654
+ width: 36px;
2491
2655
  }
2492
2656
 
2493
2657
  .x1q22l7i {
@@ -2772,6 +2936,16 @@
2772
2936
  }
2773
2937
  }
2774
2938
 
2939
+ @keyframes x1wc8ddo-B {
2940
+ from {
2941
+ transform: rotate(0);
2942
+ }
2943
+
2944
+ to {
2945
+ transform: rotate(360deg);
2946
+ }
2947
+ }
2948
+
2775
2949
  @keyframes x1n620qs-B {
2776
2950
  0%, 100% {
2777
2951
  transform: translateX(.001%);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backmatter/studio",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
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.28",
59
+ "@backmatter/core": "^0.0.33",
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",
@@ -3,7 +3,7 @@
3
3
  // a site Studio mounts the same tree under its base path.
4
4
  import { Route } from "react-router";
5
5
  import { WorkspaceLayout } from "./pages/WorkspaceLayout";
6
- import { WorkspaceHomeRedirect } from "./pages/WorkspaceHomeRedirect";
6
+ import { WorkspaceHomePage } from "./pages/WorkspaceHomePage";
7
7
  import { TypeDocsPage } from "./pages/TypeDocsPage";
8
8
  import { SettingsGeneralPage } from "./pages/SettingsGeneralPage";
9
9
  import { SettingsUsagePage } from "./pages/SettingsUsagePage";
@@ -15,6 +15,7 @@ import { SettingsUsersPage } from "./pages/SettingsUsersPage";
15
15
  import { GithubConnectPage } from "./pages/GithubConnectPage";
16
16
  import { DocEditorPage } from "./pages/DocEditorPage";
17
17
  import { MediaGalleryPage } from "./pages/MediaGalleryPage";
18
+ import CodeSessionPage from "./pages/CodeSessionPage";
18
19
 
19
20
  /**
20
21
  * Returns `<Route>` elements. Spread the result inside a `<Routes>`; react-router
@@ -23,8 +24,10 @@ import { MediaGalleryPage } from "./pages/MediaGalleryPage";
23
24
  export function workspaceRoutes() {
24
25
  return (
25
26
  <Route path="/:workspaceSlug" element={<WorkspaceLayout />}>
26
- <Route index element={<WorkspaceHomeRedirect />} />
27
+ <Route index element={<WorkspaceHomePage />} />
28
+ <Route path="content" element={<TypeDocsPage />} />
27
29
  <Route path="content/:type" element={<TypeDocsPage />} />
30
+ <Route path="code" element={<CodeSessionPage />} />
28
31
  <Route element={<SettingsLayout />}>
29
32
  <Route path="settings" element={<SettingsIndexRedirect />} />
30
33
  <Route path="settings/general" element={<SettingsGeneralPage />} />
package/src/api.ts CHANGED
@@ -23,6 +23,7 @@ async function api<T>(path: string, init?: RequestInit): Promise<T> {
23
23
  const body = (await res.json().catch(() => ({}))) as {
24
24
  error?: string;
25
25
  details?: string[];
26
+ paths?: string[];
26
27
  };
27
28
  if (res.status === 401 || res.status === 426) reportUnauthorized(res.status, body.error);
28
29
  const code = body.error ?? `request failed: ${res.status}`;
@@ -33,7 +34,7 @@ async function api<T>(path: string, init?: RequestInit): Promise<T> {
33
34
  const err = new Error(`${code}${detailSuffix}`) as ApiError;
34
35
  err.status = res.status;
35
36
  err.code = body.error;
36
- err.details = body.details;
37
+ err.details = body.details ?? body.paths;
37
38
  throw err;
38
39
  }
39
40
  return res.json() as Promise<T>;
@@ -371,6 +372,8 @@ export type SchemaPayload = {
371
372
  singleton?: boolean;
372
373
  indexSlug?: string;
373
374
  format?: "mdx" | "page";
375
+ external?: boolean;
376
+ template?: string;
374
377
  fields: Record<string, SchemaField>;
375
378
  }>;
376
379
  blocks?: BlockSnapshot[];
@@ -400,10 +403,21 @@ export type GitChange = {
400
403
  type: string;
401
404
  slug: string;
402
405
  status: ChangeStatus;
406
+ /** Working-tree edit time. Present on git status; absent when derived from the document list. */
407
+ updatedAt?: string;
403
408
  };
404
409
 
405
410
  export type GitHead = { sha: string; updatedAt: string };
406
411
 
412
+ /** A commit on the branch, newest first. `cms` means Studio wrote it. */
413
+ export type RecentCommit = {
414
+ sha: string;
415
+ message: string;
416
+ author: string;
417
+ committedAt: string;
418
+ cms: boolean;
419
+ };
420
+
407
421
  /** `git status` for the current user on their checked-out branch. */
408
422
  export type GitState = {
409
423
  defaultBranch: string;
@@ -413,6 +427,10 @@ export type GitState = {
413
427
  githubError?: "installation_missing" | "github_list_failed" | null;
414
428
  head: GitHead | null;
415
429
  changes: GitChange[];
430
+ /** Last git status list, including deletions and edit times. */
431
+ fetchedChanges?: GitChange[];
432
+ /** Latest commits on the branch, once git status has been fetched. */
433
+ recentCommits?: RecentCommit[];
416
434
  };
417
435
 
418
436
  export function countConflicts(state: Pick<GitState, "changes"> | null | undefined) {
@@ -466,6 +484,32 @@ export function commitWorkspace(workspaceId: string, message?: string) {
466
484
  });
467
485
  }
468
486
 
487
+ export type CodeSessionEditList = {
488
+ files: Array<{ path: string; deleted: boolean }>;
489
+ skipped: Array<{ path: string; reason: string }>;
490
+ foldPending: boolean;
491
+ };
492
+
493
+ export function fetchCodeSessionEdits(workspaceId: string, sessionId: string) {
494
+ return api<CodeSessionEditList>(
495
+ `/workspaces/${workspaceId}/code-sessions/${sessionId}/edits`,
496
+ );
497
+ }
498
+
499
+ export function commitCodeSessionEdits(
500
+ workspaceId: string,
501
+ sessionId: string,
502
+ message?: string,
503
+ ) {
504
+ return api<{ ok: true; commitSha: string; foldErrors: string[] }>(
505
+ `/workspaces/${workspaceId}/code-sessions/${sessionId}/commit`,
506
+ {
507
+ method: "POST",
508
+ body: JSON.stringify(message?.trim() ? { message: message.trim() } : {}),
509
+ },
510
+ );
511
+ }
512
+
469
513
  /** Default commit message the server uses when none is given (mirrored for the placeholder). */
470
514
  export function defaultCommitMessage(changes: GitChange[]) {
471
515
  if (changes.length === 1) {
@@ -701,6 +745,8 @@ export type ChatThread = {
701
745
  workspaceId: string;
702
746
  userId: string;
703
747
  branch: string;
748
+ /** Null for an editor thread; a code session id for a session thread. */
749
+ codeSessionId?: string | null;
704
750
  title: string;
705
751
  createdAt: string;
706
752
  updatedAt: string;
@@ -728,10 +774,11 @@ export type ChatChangeSummary = {
728
774
  workspaceId: string;
729
775
  userId: string;
730
776
  branch: string;
731
- resourceKind: "content";
777
+ resourceKind: "content" | "code";
778
+ path: string | null;
732
779
  type: string;
733
780
  slug: string;
734
- op: "write" | "create";
781
+ op: "write" | "create" | "delete";
735
782
  beforeSource: ContentSource | null;
736
783
  diff: ChatChangeDiff | null;
737
784
  status: ChatChangeStatus;
@@ -740,14 +787,23 @@ export type ChatChangeSummary = {
740
787
  summary?: string;
741
788
  };
742
789
 
743
- export function fetchThreads(workspaceId: string) {
744
- return api<{ threads: ChatThread[] }>(`/workspaces/${workspaceId}/ai/threads`);
790
+ export function fetchThreads(workspaceId: string, codeSessionId?: string) {
791
+ const query = codeSessionId
792
+ ? `?codeSessionId=${encodeURIComponent(codeSessionId)}`
793
+ : "";
794
+ return api<{ threads: ChatThread[] }>(`/workspaces/${workspaceId}/ai/threads${query}`);
745
795
  }
746
796
 
747
- export function createThread(workspaceId: string, title?: string) {
797
+ export function createThread(
798
+ workspaceId: string,
799
+ opts?: { title?: string; codeSessionId?: string },
800
+ ) {
801
+ const body: { title?: string; codeSessionId?: string } = {};
802
+ if (opts?.title) body.title = opts.title;
803
+ if (opts?.codeSessionId) body.codeSessionId = opts.codeSessionId;
748
804
  return api<{ thread: ChatThread }>(`/workspaces/${workspaceId}/ai/threads`, {
749
805
  method: "POST",
750
- body: JSON.stringify(title ? { title } : {}),
806
+ body: JSON.stringify(body),
751
807
  });
752
808
  }
753
809
 
@@ -28,8 +28,14 @@ export function ChangeCard({
28
28
  }) {
29
29
  const [expanded, setExpanded] = useState(false);
30
30
  const nav = useWorkspaceNav();
31
- const label = `${change.type}/${change.slug}`;
32
- const to = nav ? contentDocPath(nav.workspaceSlug, change.type, change.slug) : "#";
31
+ const label =
32
+ change.resourceKind === "code"
33
+ ? (change.path ?? "")
34
+ : `${change.type}/${change.slug}`;
35
+ const to =
36
+ change.resourceKind === "code" || !nav
37
+ ? "#"
38
+ : contentDocPath(nav.workspaceSlug, change.type, change.slug);
33
39
  const status = change.status;
34
40
 
35
41
  return (