@alpaca-editor/core 1.0.4174 → 1.0.4177

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 (58) hide show
  1. package/dist/agents-view/AgentsView.js +0 -20
  2. package/dist/agents-view/AgentsView.js.map +1 -1
  3. package/dist/editor/QuickItemSwitcher.js +5 -1
  4. package/dist/editor/QuickItemSwitcher.js.map +1 -1
  5. package/dist/editor/ai/AgentTerminal.js +47 -83
  6. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  7. package/dist/editor/ai/Agents.js +7 -5
  8. package/dist/editor/ai/Agents.js.map +1 -1
  9. package/dist/editor/ai/AiResponseMessage.js +1 -1
  10. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  11. package/dist/editor/ai/ContextInfoBar.js +30 -64
  12. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  13. package/dist/editor/ai/useAgentStatus.js +81 -2
  14. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  15. package/dist/editor/client/EditorShell.js +44 -7
  16. package/dist/editor/client/EditorShell.js.map +1 -1
  17. package/dist/editor/client/operations.js +30 -3
  18. package/dist/editor/client/operations.js.map +1 -1
  19. package/dist/editor/control-center/WebSocketMessages.js +0 -1
  20. package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
  21. package/dist/editor/page-viewer/PageViewerFrame.js +14 -3
  22. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  23. package/dist/editor/reviews/commentAi.js +43 -32
  24. package/dist/editor/reviews/commentAi.js.map +1 -1
  25. package/dist/editor/services/agentService.d.ts +8 -4
  26. package/dist/editor/services/agentService.js +30 -53
  27. package/dist/editor/services/agentService.js.map +1 -1
  28. package/dist/editor/services/aiService.d.ts +19 -1
  29. package/dist/editor/services/aiService.js +78 -2
  30. package/dist/editor/services/aiService.js.map +1 -1
  31. package/dist/page-wizard/steps/ContentStep.js +35 -57
  32. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  33. package/dist/page-wizard/steps/MetaDataStep.js +14 -23
  34. package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
  35. package/dist/page-wizard/steps/SelectStep.js +12 -42
  36. package/dist/page-wizard/steps/SelectStep.js.map +1 -1
  37. package/dist/revision.d.ts +2 -2
  38. package/dist/revision.js +2 -2
  39. package/dist/styles.css +0 -8
  40. package/package.json +1 -1
  41. package/src/agents-view/AgentsView.tsx +1 -21
  42. package/src/editor/QuickItemSwitcher.tsx +23 -19
  43. package/src/editor/ai/AgentTerminal.tsx +83 -142
  44. package/src/editor/ai/Agents.tsx +9 -6
  45. package/src/editor/ai/AiResponseMessage.tsx +5 -1
  46. package/src/editor/ai/ContextInfoBar.tsx +34 -75
  47. package/src/editor/ai/useAgentStatus.ts +82 -4
  48. package/src/editor/client/EditorShell.tsx +62 -9
  49. package/src/editor/client/operations.ts +42 -4
  50. package/src/editor/control-center/WebSocketMessages.tsx +5 -1
  51. package/src/editor/page-viewer/PageViewerFrame.tsx +15 -2
  52. package/src/editor/reviews/commentAi.ts +48 -36
  53. package/src/editor/services/agentService.ts +38 -55
  54. package/src/editor/services/aiService.ts +110 -3
  55. package/src/page-wizard/steps/ContentStep.tsx +51 -81
  56. package/src/page-wizard/steps/MetaDataStep.tsx +52 -60
  57. package/src/page-wizard/steps/SelectStep.tsx +13 -51
  58. package/src/revision.ts +2 -2
@@ -42,7 +42,7 @@ export function ContextInfoBar({
42
42
 
43
43
  const removeContextKey = useCallback(
44
44
  async (
45
- key: "items" | "componentIds" | "components" | "field" | "comment",
45
+ key: "items" | "components" | "field" | "comment",
46
46
  index?: number,
47
47
  ) => {
48
48
  if (!agent?.id) return;
@@ -54,7 +54,7 @@ export function ContextInfoBar({
54
54
  next.items.splice(index, 1);
55
55
  if (next.items.length === 0) delete next.items;
56
56
  } else if (
57
- (key === "componentIds" || key === "components") &&
57
+ key === "components" &&
58
58
  typeof index === "number" &&
59
59
  next.components
60
60
  ) {
@@ -137,7 +137,7 @@ export function ContextInfoBar({
137
137
  const current = agentMetadata || {};
138
138
  const currentComponents = current.components || [];
139
139
 
140
- const existingIds = new Set(currentComponents.map(c => c.componentId));
140
+ const existingIds = new Set(currentComponents.map((c) => c.componentId));
141
141
  const newComponentIds = editContext.selection.filter(
142
142
  (id) => !existingIds.has(id),
143
143
  );
@@ -145,16 +145,18 @@ export function ContextInfoBar({
145
145
  if (newComponentIds.length === 0) return;
146
146
 
147
147
  // Get page item descriptor from edit context
148
- const pageItem = editContext.currentItemDescriptor ? {
149
- id: editContext.currentItemDescriptor.id,
150
- language: editContext.currentItemDescriptor.language,
151
- version: editContext.currentItemDescriptor.version,
152
- name: editContext.contentEditorItem?.name,
153
- path: undefined,
154
- } : undefined;
148
+ const pageItem = editContext.currentItemDescriptor
149
+ ? {
150
+ id: editContext.currentItemDescriptor.id,
151
+ language: editContext.currentItemDescriptor.language,
152
+ version: editContext.currentItemDescriptor.version,
153
+ name: editContext.contentEditorItem?.name,
154
+ path: undefined,
155
+ }
156
+ : undefined;
155
157
 
156
158
  // Build new components with page info
157
- const newComponents = newComponentIds.map(componentId => ({
159
+ const newComponents = newComponentIds.map((componentId) => ({
158
160
  componentId,
159
161
  pageItem: pageItem!,
160
162
  }));
@@ -185,21 +187,23 @@ export function ContextInfoBar({
185
187
  const current = agentMetadata || {};
186
188
  const currentComponents = current.components || [];
187
189
 
188
- const existingIds = new Set(currentComponents.map(c => c.componentId));
190
+ const existingIds = new Set(currentComponents.map((c) => c.componentId));
189
191
  const newComponentIds = ids.filter((id) => !!id && !existingIds.has(id));
190
192
  if (newComponentIds.length === 0) return;
191
193
 
192
194
  // Get page item descriptor from edit context
193
- const pageItem = editContext?.currentItemDescriptor ? {
194
- id: editContext.currentItemDescriptor.id,
195
- language: editContext.currentItemDescriptor.language,
196
- version: editContext.currentItemDescriptor.version,
197
- name: editContext.contentEditorItem?.name,
198
- path: undefined,
199
- } : undefined;
195
+ const pageItem = editContext?.currentItemDescriptor
196
+ ? {
197
+ id: editContext.currentItemDescriptor.id,
198
+ language: editContext.currentItemDescriptor.language,
199
+ version: editContext.currentItemDescriptor.version,
200
+ name: editContext.contentEditorItem?.name,
201
+ path: undefined,
202
+ }
203
+ : undefined;
200
204
 
201
205
  // Build new components with page info
202
- const newComponents = newComponentIds.map(componentId => ({
206
+ const newComponents = newComponentIds.map((componentId) => ({
203
207
  componentId,
204
208
  pageItem: pageItem!,
205
209
  }));
@@ -351,7 +355,7 @@ export function ContextInfoBar({
351
355
  const ctx = agentMetadata;
352
356
 
353
357
  const hasContext =
354
- ctx && (ctx.items?.length || ctx.components?.length || ctx.componentIds?.length || ctx.field);
358
+ ctx && (ctx.items?.length || ctx.components?.length || ctx.field);
355
359
 
356
360
  const currentPageAlreadyAdded =
357
361
  editContext?.currentItemDescriptor &&
@@ -365,8 +369,7 @@ export function ContextInfoBar({
365
369
  const selectedComponentsAlreadyAdded =
366
370
  !!editContext?.selection?.length &&
367
371
  !!editContext.selection?.every((selectedId) =>
368
- ctx?.components?.some(c => c.componentId === selectedId) ||
369
- ctx?.componentIds?.includes(selectedId),
372
+ ctx?.components?.some((c) => c.componentId === selectedId),
370
373
  );
371
374
 
372
375
  const canAddPage =
@@ -404,10 +407,7 @@ export function ContextInfoBar({
404
407
 
405
408
  if (ctx?.items && ctx.items.length > 0) {
406
409
  ctx.items.forEach((page, index) => {
407
- const pageLabelName =
408
- page.name ||
409
- resolvedPageName ||
410
- undefined;
410
+ const pageLabelName = page.name || resolvedPageName || undefined;
411
411
  const pageLabel = `${pageLabelName || page.path || page.id} (${page.language}/${page.version})`;
412
412
  chips.push(
413
413
  <Chip
@@ -432,12 +432,12 @@ export function ContextInfoBar({
432
432
  compLabel = (comp?.name as string) || componentId;
433
433
  } catch {}
434
434
  }
435
-
435
+
436
436
  // Include page info in the label
437
- const pageInfo = component.pageItem
437
+ const pageInfo = component.pageItem
438
438
  ? ` on ${component.pageItem.name || component.pageItem.path || component.pageItem.id}`
439
- : '';
440
-
439
+ : "";
440
+
441
441
  chips.push(
442
442
  <Chip
443
443
  key={`component-${index}`}
@@ -447,34 +447,10 @@ export function ContextInfoBar({
447
447
  />,
448
448
  );
449
449
  });
450
- } else if (ctx?.componentIds && ctx.componentIds.length > 0) {
451
- // Legacy support for componentIds without page info
452
- ctx.componentIds.forEach((componentId, index) => {
453
- let compLabel: string = componentId as string;
454
- if (index === 0 && resolvedComponentName) {
455
- compLabel = resolvedComponentName as string;
456
- } else if (editContext?.page) {
457
- try {
458
- const comp = getComponentById(
459
- componentId as string,
460
- editContext.page,
461
- );
462
- compLabel = (comp?.name as string) || (componentId as string);
463
- } catch {}
464
- }
465
- chips.push(
466
- <Chip
467
- key={`component-${index}`}
468
- icon={<Puzzle className="h-3 w-3 text-gray-500" strokeWidth={1} />}
469
- label={`Component: ${compLabel}`}
470
- onRemove={() => removeContextKey("componentIds", index)}
471
- />,
472
- );
473
- });
474
450
  }
475
451
 
476
452
  if (ctx?.field) {
477
- const fieldLabel = `${ctx.field.name || resolvedFieldName || ctx.field.fieldId}`;
453
+ const fieldLabel = `${ctx.field.fieldName || resolvedFieldName || ctx.field.fieldId}`;
478
454
  chips.push(
479
455
  <Chip
480
456
  key="field"
@@ -489,10 +465,7 @@ export function ContextInfoBar({
489
465
  const summaryParts: string[] = [];
490
466
  if (ctx?.items && ctx.items.length > 0) {
491
467
  const first = ctx.items[0]!;
492
- const pageLabelName =
493
- first.name ||
494
- resolvedPageName ||
495
- undefined;
468
+ const pageLabelName = first.name || resolvedPageName || undefined;
496
469
  const pageText = pageLabelName || first.path || first.id;
497
470
  const more = ctx.items.length > 1 ? ` (+${ctx.items.length - 1})` : "";
498
471
  summaryParts.push(`Item: ${pageText}${more}`);
@@ -511,24 +484,10 @@ export function ContextInfoBar({
511
484
  const more =
512
485
  ctx.components.length > 1 ? ` (+${ctx.components.length - 1})` : "";
513
486
  summaryParts.push(`Component: ${compLabel}${more}`);
514
- } else if (ctx?.componentIds && ctx.componentIds.length > 0) {
515
- // Legacy support for componentIds
516
- const firstId = ctx.componentIds[0]!;
517
- let compLabel: string = firstId as string;
518
- if (resolvedComponentName) compLabel = resolvedComponentName as string;
519
- else if (editContext?.page) {
520
- try {
521
- const comp = getComponentById(firstId as string, editContext.page);
522
- compLabel = (comp?.name as string) || (firstId as string);
523
- } catch {}
524
- }
525
- const more =
526
- ctx.componentIds.length > 1 ? ` (+${ctx.componentIds.length - 1})` : "";
527
- summaryParts.push(`Component: ${compLabel}${more}`);
528
487
  }
529
488
  if (ctx?.field) {
530
489
  summaryParts.push(
531
- `Field: ${ctx.field.name || resolvedFieldName || ctx.field.fieldId}`,
490
+ `Field: ${ctx.field.fieldName || resolvedFieldName || ctx.field.fieldId}`,
532
491
  );
533
492
  }
534
493
  if ((ctx as any)?.comment?.selectedText) {
@@ -53,10 +53,7 @@ export function useAgentStatus(): AgentStatusSummary {
53
53
 
54
54
  // Subscribe to WebSocket messages for agent status updates
55
55
  const handleMessage = (message: { type: string; payload: any }) => {
56
- if (
57
- message.type === "agent:status:changed" ||
58
- message.type === "agent:run:start"
59
- ) {
56
+ if (message.type === "agent:run:start") {
60
57
  // Update agent status in real-time from WebSocket message
61
58
  const { agentId, status, name, agentName } = message.payload || {};
62
59
  if (!agentId) return;
@@ -111,6 +108,87 @@ export function useAgentStatus(): AgentStatusSummary {
111
108
 
112
109
  return prevAgents;
113
110
  });
111
+ } else if (message.type === "agent:run:status") {
112
+ // Unified status updates with data.state
113
+ const { agentId, data } = message.payload || {};
114
+ if (!agentId) return;
115
+
116
+ const rawState = (data?.state as string | undefined) || undefined;
117
+ const mappedStatus = ((): Agent["status"] | undefined => {
118
+ if (!rawState) return undefined;
119
+ switch (rawState) {
120
+ case "Running":
121
+ return "running" as const;
122
+ case "WaitingForApproval":
123
+ return "waitingForApproval" as const;
124
+ case "Completed":
125
+ return "completed" as const;
126
+ case "Closed":
127
+ return "closed" as const;
128
+ case "Error":
129
+ return "error" as const;
130
+ case "Idle":
131
+ return "idle" as const;
132
+ case "CostLimitReached":
133
+ return "costLimitReached" as const;
134
+ case "Queued":
135
+ return undefined; // do not show queued in badge
136
+ default:
137
+ // allow already-lowercased strings
138
+ return rawState as any;
139
+ }
140
+ })();
141
+
142
+ if (!mappedStatus) return;
143
+
144
+ setAgents((prevAgents) => {
145
+ const existingIndex = prevAgents.findIndex((a) => a.id === agentId);
146
+ const isTerminal =
147
+ mappedStatus === "completed" ||
148
+ mappedStatus === "error" ||
149
+ mappedStatus === "closed";
150
+
151
+ if (existingIndex !== -1) {
152
+ if (isTerminal) {
153
+ // Remove terminal states from active list
154
+ return prevAgents.filter((a) => a.id !== agentId);
155
+ }
156
+ const updated = [...prevAgents];
157
+ updated[existingIndex] = {
158
+ ...updated[existingIndex]!,
159
+ status: mappedStatus,
160
+ updatedDate: new Date().toISOString(),
161
+ };
162
+ return updated;
163
+ }
164
+
165
+ // Add new if active state
166
+ if (
167
+ mappedStatus === "running" ||
168
+ mappedStatus === "waitingForApproval" ||
169
+ mappedStatus === "costLimitReached" ||
170
+ mappedStatus === "idle"
171
+ ) {
172
+ const newAgent: Agent = {
173
+ id: agentId,
174
+ name: (data?.name as string) || "Agent",
175
+ userId: "",
176
+ status: mappedStatus,
177
+ updatedDate: new Date().toISOString(),
178
+ };
179
+ return [...prevAgents, newAgent];
180
+ }
181
+
182
+ return prevAgents;
183
+ });
184
+ } else if (message.type === "agent:run:complete") {
185
+ const { agentId } = message.payload || {};
186
+ if (!agentId) return;
187
+ setAgents((prevAgents) => prevAgents.filter((a) => a.id !== agentId));
188
+ } else if (message.type === "agent:run:error") {
189
+ const { agentId } = message.payload || {};
190
+ if (!agentId) return;
191
+ setAgents((prevAgents) => prevAgents.filter((a) => a.id !== agentId));
114
192
  } else if (message.type === "agent:run:closed") {
115
193
  // Remove agent from list when it's closed
116
194
  const { agentId } = message.payload || {};
@@ -1229,7 +1229,11 @@ export function EditorShell({
1229
1229
  const loadItem = useCallback(
1230
1230
  async (
1231
1231
  itemToLoad: ItemDescriptor | string,
1232
- options?: { addToBrowseHistory?: boolean; skipViewChange?: boolean },
1232
+ options?: {
1233
+ addToBrowseHistory?: boolean;
1234
+ skipViewChange?: boolean;
1235
+ skipNavigationHistory?: boolean;
1236
+ },
1233
1237
  ): Promise<FullItem | undefined> => {
1234
1238
  if (typeof itemToLoad === "string")
1235
1239
  itemToLoad = {
@@ -1271,8 +1275,10 @@ export function EditorShell({
1271
1275
  options?.addToBrowseHistory === undefined
1272
1276
  ) {
1273
1277
  addToBrowseHistory(item);
1274
- // Also add to navigation history with current view
1275
- addNavigationEntry(viewName, item);
1278
+ // Also add to navigation history with current view (unless skipped)
1279
+ if (!options?.skipNavigationHistory) {
1280
+ addNavigationEntry(viewName, item);
1281
+ }
1276
1282
  }
1277
1283
 
1278
1284
  return item;
@@ -1483,7 +1489,7 @@ export function EditorShell({
1483
1489
 
1484
1490
  const switchView = (
1485
1491
  viewName: string,
1486
- options?: { skipConfirmation?: boolean },
1492
+ options?: { skipConfirmation?: boolean; skipNavigationHistory?: boolean },
1487
1493
  ) => {
1488
1494
  async function switchView() {
1489
1495
  if (currentView?.beforeClose && !options?.skipConfirmation) {
@@ -1494,8 +1500,10 @@ export function EditorShell({
1494
1500
  // Track previous view name before switching
1495
1501
  setPreviousViewName(editContext.viewName);
1496
1502
 
1497
- // Add to navigation history with current item
1498
- addNavigationEntry(viewName, contentEditorItem);
1503
+ // Add to navigation history with current item (unless explicitly skipped)
1504
+ if (!options?.skipNavigationHistory) {
1505
+ addNavigationEntry(viewName, contentEditorItem);
1506
+ }
1499
1507
 
1500
1508
  if (typeof document.startViewTransition === "function") {
1501
1509
  document.startViewTransition(() => {
@@ -1572,6 +1580,20 @@ export function EditorShell({
1572
1580
  field: FieldDescriptor | undefined,
1573
1581
  requestLock: boolean,
1574
1582
  ): Promise<boolean> => {
1583
+ console.log("[setFocusedFieldWithLock] Called with:", {
1584
+ field: field
1585
+ ? {
1586
+ fieldId: field.fieldId,
1587
+ itemId: field.item?.id,
1588
+ language: field.item?.language,
1589
+ version: field.item?.version,
1590
+ }
1591
+ : undefined,
1592
+ requestLock,
1593
+ ignoreBlur,
1594
+ timestamp: new Date().toISOString(),
1595
+ });
1596
+
1575
1597
  if (field) {
1576
1598
  setIgnoreBlur(true);
1577
1599
  setTimeout(() => {
@@ -1579,11 +1601,26 @@ export function EditorShell({
1579
1601
  }, 20);
1580
1602
  setFocusedField({ ...field });
1581
1603
  if (requestLock) {
1582
- return (await operations.ensureLock(field)) || false;
1604
+ console.log(
1605
+ "[setFocusedFieldWithLock] Calling operations.ensureLock for field:",
1606
+ field.fieldId,
1607
+ );
1608
+ const lockResult = (await operations.ensureLock(field)) || false;
1609
+ console.log(
1610
+ "[setFocusedFieldWithLock] ensureLock result:",
1611
+ lockResult,
1612
+ );
1613
+ return lockResult;
1583
1614
  }
1584
1615
  } else {
1616
+ console.log(
1617
+ "[setFocusedFieldWithLock] Clearing focused field, ignoreBlur:",
1618
+ ignoreBlur,
1619
+ );
1585
1620
  if (!ignoreBlur) {
1586
1621
  setFocusedField(undefined);
1622
+ console.log("[setFocusedFieldWithLock] Clearing lockedField state");
1623
+ setLockedField(undefined); // Clear client-side lock state
1587
1624
  releaseFieldLocks(sessionId);
1588
1625
  }
1589
1626
  }
@@ -1684,7 +1721,12 @@ export function EditorShell({
1684
1721
 
1685
1722
  // First switch view
1686
1723
  if (selectedEntry.viewName !== viewName) {
1687
- switchView(selectedEntry.viewName, { skipConfirmation: true });
1724
+ // Avoid inserting a duplicate navigation entry; we'll explicitly move the
1725
+ // selected one to the front below
1726
+ switchView(selectedEntry.viewName, {
1727
+ skipConfirmation: true,
1728
+ skipNavigationHistory: true,
1729
+ });
1688
1730
  }
1689
1731
 
1690
1732
  // Then load item if it exists and is different from current
@@ -1706,7 +1748,12 @@ export function EditorShell({
1706
1748
  language: selectedEntry.item.language,
1707
1749
  version: selectedEntry.item.version,
1708
1750
  },
1709
- { addToBrowseHistory: false, skipViewChange: true },
1751
+ {
1752
+ addToBrowseHistory: false,
1753
+ skipViewChange: true,
1754
+ // Avoid inserting an extra nav entry; we move the selected one below
1755
+ skipNavigationHistory: true,
1756
+ },
1710
1757
  );
1711
1758
  } else {
1712
1759
  console.log(`[QuickSwitcher] Same item, skipping reload`);
@@ -2400,6 +2447,8 @@ export function EditorShell({
2400
2447
  isRefreshing,
2401
2448
  activeSessions,
2402
2449
  unlockField: async () => {
2450
+ console.log("[EditContext.unlockField] Clearing lockedField state");
2451
+ setLockedField(undefined); // Clear client-side lock state
2403
2452
  await releaseFieldLocks(sessionId);
2404
2453
  },
2405
2454
  isCommandDisabled: <T extends CommandData>({
@@ -2705,6 +2754,10 @@ export function EditorShell({
2705
2754
  focusedField,
2706
2755
  setFocusedField: setFocusedFieldWithLock,
2707
2756
  unlockField: async () => {
2757
+ console.log(
2758
+ "[FieldsEditContext.unlockField] Clearing lockedField state",
2759
+ );
2760
+ setLockedField(undefined); // Clear client-side lock state
2708
2761
  await releaseFieldLocks(sessionId);
2709
2762
  },
2710
2763
  inlineEditingFieldElement,
@@ -241,27 +241,65 @@ export function getOperationsContext(
241
241
 
242
242
  const ensureLock = useCallback(
243
243
  async (field: FieldDescriptor): Promise<boolean> => {
244
- if (!field || !field.item) return false;
245
- if (
244
+ console.log("[ensureLock] Called with field:", {
245
+ fieldId: field?.fieldId,
246
+ itemId: field?.item?.id,
247
+ language: field?.item?.language,
248
+ version: field?.item?.version,
249
+ currentLockedField: state.lockedField
250
+ ? {
251
+ fieldId: state.lockedField.fieldId,
252
+ itemId: state.lockedField.item.id,
253
+ language: state.lockedField.item.language,
254
+ version: state.lockedField.item.version,
255
+ }
256
+ : null,
257
+ timestamp: new Date().toISOString(),
258
+ });
259
+
260
+ if (!field || !field.item) {
261
+ console.log(
262
+ "[ensureLock] Early return: field or field.item is missing",
263
+ );
264
+ return false;
265
+ }
266
+
267
+ const isAlreadyLocked =
246
268
  state.lockedField?.fieldId === field.fieldId &&
247
269
  state.lockedField?.item.id === field.item.id &&
248
270
  state.lockedField?.item.language === field.item.language &&
249
- state.lockedField?.item.version === field.item.version
250
- )
271
+ state.lockedField?.item.version === field.item.version;
272
+
273
+ if (isAlreadyLocked) {
274
+ console.log(
275
+ "[ensureLock] Field is already locked, returning true without lockField call",
276
+ );
251
277
  return true;
278
+ }
279
+
280
+ console.log(
281
+ "[ensureLock] Field is NOT locked, calling lockField service",
282
+ );
252
283
  const result = await lockField(
253
284
  field.item,
254
285
  field.fieldId,
255
286
  state.sessionId,
256
287
  );
288
+ console.log("[ensureLock] lockField service returned:", {
289
+ resultType: result.type,
290
+ success: result.type === "success" ? result.data?.success : undefined,
291
+ });
292
+
257
293
  if (result.type == "error") {
258
294
  console.log("error locking field", result);
259
295
  }
260
296
  if (handleErrorResult(result, ui, state)) return false;
261
297
  if (result.type == "success" && result.data.success) {
262
298
  state.setLockedField(field);
299
+ console.log("[ensureLock] Successfully locked field, returning true");
263
300
  return true;
264
301
  }
302
+ console.log("[ensureLock] Failed to lock field, returning false");
265
303
  return false;
266
304
  },
267
305
  [state, ui],
@@ -54,7 +54,7 @@ export function WebSocketMessages() {
54
54
  "suggested-edit-updated": "bg-indigo-100 text-indigo-800",
55
55
  "suggested-edit-deleted": "bg-red-100 text-red-800",
56
56
  "update-quota": "bg-gray-100 text-gray-800",
57
- "agent:status:changed": "bg-violet-100 text-violet-800",
57
+
58
58
  "agent:run:closed": "bg-red-100 text-red-800",
59
59
  "agent:run:start": "bg-green-100 text-green-800",
60
60
  "agent:name:updated": "bg-blue-100 text-blue-800",
@@ -158,3 +158,7 @@ export function WebSocketMessages() {
158
158
  </div>
159
159
  );
160
160
  }
161
+
162
+
163
+
164
+
@@ -608,6 +608,14 @@ export function PageViewerFrame({
608
608
 
609
609
  blockBlurEventRef.current = Date.now() + 500;
610
610
 
611
+ console.log("[PageViewerFrame] Attempting to focus field:", {
612
+ fieldId: fieldElement.getAttribute("data-fieldid"),
613
+ itemId: fieldElement.getAttribute("data-itemid"),
614
+ mode: editContextRef.current?.mode,
615
+ currentlyFocusedField: fieldsContextRef.current?.focusedField,
616
+ timestamp: new Date().toISOString(),
617
+ });
618
+
611
619
  const hasLock =
612
620
  editContextRef.current?.mode === "suggestions" ||
613
621
  (await fieldsContextRef.current?.setFocusedField(
@@ -615,6 +623,11 @@ export function PageViewerFrame({
615
623
  true,
616
624
  ));
617
625
 
626
+ console.log("[PageViewerFrame] Focus field result:", {
627
+ hasLock,
628
+ fieldId: fieldElement.getAttribute("data-fieldid"),
629
+ });
630
+
618
631
  if (
619
632
  hasLock &&
620
633
  fieldsContextRef.current?.inlineEditingFieldElement !== fieldElement
@@ -629,8 +642,8 @@ export function PageViewerFrame({
629
642
  fieldsContextRef.current?.setFocusedField(undefined, false);
630
643
  fieldsContextRef.current?.setInlineEditingFieldElement(undefined);
631
644
  // Release field locks when unfocusing field
632
- if (editContextRef.current?.sessionId) {
633
- releaseFieldLocks(editContextRef.current.sessionId);
645
+ if (editContextRef.current?.unlockField) {
646
+ editContextRef.current.unlockField(undefined as any);
634
647
  }
635
648
  }
636
649
  }
@@ -52,49 +52,61 @@ export function openAiAgentForComment(
52
52
  editContext.setShowAgentsPanel(true);
53
53
 
54
54
  const initialMetadata: AgentMetadata = {
55
+ items: [
56
+ {
57
+ id:
58
+ (comment as any).mainItemId ||
59
+ editContext.currentItemDescriptor?.id!,
60
+ language: comment.language,
61
+ version: comment.version,
62
+ name: editContext.contentEditorItem?.name,
63
+ },
64
+ ],
65
+ components:
66
+ editContext.selection && editContext.selection.length
67
+ ? editContext.selection.map((componentId) => ({
68
+ componentId,
69
+ pageItem: {
70
+ id:
71
+ (comment as any).mainItemId ||
72
+ editContext.currentItemDescriptor?.id!,
73
+ language: comment.language,
74
+ version: comment.version,
75
+ name: editContext.contentEditorItem?.name,
76
+ },
77
+ }))
78
+ : undefined,
79
+ field:
80
+ comment.fieldId && comment.itemId
81
+ ? {
82
+ fieldId: comment.fieldId,
83
+ fieldName: comment.fieldName,
84
+ item: {
85
+ id: comment.itemId,
86
+ language: comment.language,
87
+ version: comment.version,
88
+ name: comment.itemName,
89
+ },
90
+ }
91
+ : undefined,
92
+ comment: {
93
+ id: comment.id,
94
+ text: comment.text,
95
+ fieldName: comment.fieldName,
96
+ itemName: comment.itemName,
97
+ author: comment.author,
98
+ selectedText: selectedText,
99
+ rangeStart: comment.rangeStart,
100
+ rangeEnd: comment.rangeEnd,
101
+ },
55
102
  additionalData: {
56
103
  initialPrompt,
57
- context: {
58
- pages: [
59
- {
60
- id:
61
- (comment as any).mainItemId ||
62
- editContext.currentItemDescriptor?.id!,
63
- language: comment.language,
64
- version: comment.version,
65
- name: editContext.contentEditorItem?.name,
66
- } as any,
67
- ],
68
- componentIds:
69
- editContext.selection && editContext.selection.length
70
- ? editContext.selection
71
- : undefined,
72
- field:
73
- comment.fieldId && comment.itemId
74
- ? {
75
- fieldId: comment.fieldId,
76
- itemId: comment.itemId,
77
- name: comment.fieldName,
78
- }
79
- : undefined,
80
- comment: {
81
- id: comment.id,
82
- text: comment.text,
83
- fieldName: comment.fieldName,
84
- itemName: comment.itemName,
85
- author: comment.author,
86
- selectedText: selectedText,
87
- rangeStart: comment.rangeStart,
88
- rangeEnd: comment.rangeEnd,
89
- },
90
- },
91
- // Seed profile from editor settings if available
92
104
  profileId:
93
105
  editContext.editorSettings?.commentResolveProfileId || undefined,
94
106
  profileName:
95
107
  editContext.editorSettings?.commentResolveProfileName || undefined,
96
108
  },
97
- } as any;
109
+ };
98
110
 
99
111
  // Ask Agents UI to open a new terminal seeded with this metadata
100
112
  try {