Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 38 additions & 24 deletions src/components/ai-edition/LeftPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { toast } from "sonner";
import { useScopedT } from "@/contexts/I18nContext";
import { type AxcutAsset, ensureDocument } from "@/lib/ai-edition/schema";
import type { AxcutAsset } from "@/lib/ai-edition/schema";
import {
applyAgentDocumentIfCurrent,
runAgentTurn,
} from "@/lib/ai-edition/store/agentDocumentApply";
import { useProjectStore } from "@/lib/ai-edition/store/projectStore";
import {
useAssetTranscriptions,
Expand Down Expand Up @@ -869,16 +873,6 @@ function ChatStripPanel() {
scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
});

// Apply a document returned by the agent (tool batch or undo). setDocument
// pushes the previous doc to the local undo stack (Cmd+Z also works), then
// saveDocument persists it to disk.
const applyAgentDocument = useCallback(async (doc: unknown) => {
const parsed = ensureDocument(doc);
const store = useProjectStore.getState();
store.setDocument(parsed);
await store.saveDocument(parsed);
}, []);

const send = async (overrideText?: string) => {
const text = (overrideText ?? input).trim();
if (!projectId || !text || busy) return;
Expand Down Expand Up @@ -928,21 +922,39 @@ function ChatStripPanel() {
thinkingRunSessionRef.current = sessionId;
// Send the current document snapshot so the agent can run edit tools
// against it (P1). Falls back to text-only chat when no doc is open.
const documentSnapshot = useProjectStore.getState().document ?? undefined;
const result = await nativeBridgeClient.aiEdition.chatRun(
projectId,
sessionId,
text,
documentSnapshot,
// `runAgentTurn` reads the document AND the revision it is at from one snapshot
// before the turn starts, so a manual edit landing while the agent works is
// detectable when its answer comes back.
const { result, applyDocument } = await runAgentTurn((documentSnapshot) =>
nativeBridgeClient.aiEdition.chatRun(projectId, sessionId, text, documentSnapshot),
);
const assistant = result.assistantMessage;
if (result.success && assistant) {
if (result.document) {
try {
await applyAgentDocument(result.document);
} catch (err) {
toast.error(t("chat.applyEditsFailed"), {
description: err instanceof Error ? err.message : String(err),
const applyEdits = async (options?: { ignoreConflict?: boolean }) => {
try {
return await applyDocument(options);
} catch (err) {
toast.error(t("chat.applyEditsFailed"), {
description: err instanceof Error ? err.message : String(err),
});
return "conflict" as const;
}
};
if ((await applyEdits()) === "conflict") {
// The turn is not lost, it is just not automatically applied: the document
// is still in hand and the assistant's reply is about to be rendered as if
// the edits had landed. The thing that usually moves `revision` here is a
// background transcription finishing, not the user -- so dropping the whole
// turn on the floor and blaming "the project changed" costs them a minute
// of waiting and their tokens for something they never did. Let them take
// it. No auto-dismiss: it is the only way back to this document.
toast.warning(t("chat.agentEditConflict"), {
duration: Number.POSITIVE_INFINITY,
action: {
label: t("chat.applyAnyway"),
onClick: () => void applyEdits({ ignoreConflict: true }),
},
});
}
}
Expand Down Expand Up @@ -1019,7 +1031,9 @@ function ChatStripPanel() {
return;
}
const doc = (result as { document?: unknown }).document;
if (doc) await applyAgentDocument(doc);
// No `expectedRevision`: a rewind REPLACES whatever is live, which is what the
// confirmation dialog now says out loud -- including any manual edit made since.
if (doc) await applyAgentDocumentIfCurrent(doc);
setMessages(
result.messages.map((m) => ({
id: m.id,
Expand All @@ -1040,7 +1054,7 @@ function ChatStripPanel() {
setRewindFor(null);
}
},
[projectId, activeSessionId, applyAgentDocument, t],
[projectId, activeSessionId, t],
);

useEffect(() => {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/ar/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "تعذّر اختيار النموذج",
"providerSettings": "إعدادات المزوّد…",
"applyEditsFailed": "تعذّر تطبيق تعديلات الوكيل",
"agentEditConflict": "لم تُطبَّق تعديلات الوكيل لأن المشروع تغيّر أثناء عمله.",
"applyAnyway": "تطبيق على أي حال",
"chatFailed": "فشلت المحادثة",
"rewindFailed": "فشلت إعادة الضبط",
"rewoundSuccess": "تمت إعادة الضبط إلى بداية تلك الرسالة",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "إرسال (Enter)",
"send": "إرسال",
"rewindConfirmTitle": "إعادة الضبط هنا؟",
"rewindConfirmBody": "سيتم التراجع عن تعديلات الوكيل والأدوار اللاحقة بعد هذه النقطة. سيُستعاد المشروع والمحادثة وحالة الوكيل.",
"rewindConfirmBody": "سيتم التراجع عن تعديلات الوكيل والأدوار اللاحقة بعد هذه النقطة. سيُستعاد المشروع والمحادثة وحالة الوكيل. وسيتم استبدال أي تعديلات أجريتها منذ ذلك الحين أيضًا.",
"rewindConfirm": "إعادة الضبط",
"configureModel": "تهيئة نموذج الذكاء الاصطناعي",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/en/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Could not select model",
"providerSettings": "Provider settings…",
"applyEditsFailed": "Could not apply the agent's edits",
"agentEditConflict": "Agent edits were not applied because the project changed while it was working.",
"applyAnyway": "Apply anyway",
"chatFailed": "Chat failed",
"rewindFailed": "Rewind failed",
"rewoundSuccess": "Rewound to the start of that message",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Send (Enter)",
"send": "Send",
"rewindConfirmTitle": "Rewind here?",
"rewindConfirmBody": "The agent's edits and follow-up turns after this point get rolled back. Project, conversation, and agent state will be restored.",
"rewindConfirmBody": "The agent's edits and follow-up turns after this point get rolled back. Project, conversation, and agent state will be restored. Any edits you made since then are replaced too.",
"rewindConfirm": "Rewind",
"configureModel": "Configure AI Model",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/es/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "No se pudo seleccionar el modelo",
"providerSettings": "Configuración del proveedor…",
"applyEditsFailed": "No se pudieron aplicar las ediciones del agente",
"agentEditConflict": "Las ediciones del agente no se aplicaron porque el proyecto cambió mientras trabajaba.",
"applyAnyway": "Aplicar de todos modos",
"chatFailed": "Error en el chat",
"rewindFailed": "Error al rebobinar",
"rewoundSuccess": "Rebobinado al inicio de ese mensaje",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Enviar (Intro)",
"send": "Enviar",
"rewindConfirmTitle": "¿Rebobinar aquí?",
"rewindConfirmBody": "Las ediciones del agente y los turnos posteriores a este punto se revertirán. Se restaurará el proyecto, la conversación y el estado del agente.",
"rewindConfirmBody": "Las ediciones del agente y los turnos posteriores a este punto se revertirán. Se restaurará el proyecto, la conversación y el estado del agente. Las ediciones que hayas hecho desde entonces también se reemplazarán.",
"rewindConfirm": "Rebobinar",
"configureModel": "Configurar modelo de IA",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/fr/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Impossible de sélectionner le modèle",
"providerSettings": "Réglages du fournisseur…",
"applyEditsFailed": "Impossible d'appliquer les modifications de l'agent",
"agentEditConflict": "Les modifications de l'agent n'ont pas été appliquées, car le projet a changé pendant son travail.",
"applyAnyway": "Appliquer quand même",
"chatFailed": "Échec du chat",
"rewindFailed": "Échec du retour en arrière",
"rewoundSuccess": "Retour au début de ce message effectué",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Envoyer (Entrée)",
"send": "Envoyer",
"rewindConfirmTitle": "Revenir ici ?",
"rewindConfirmBody": "Les modifications de l'agent et les échanges suivants après ce point seront annulés. Le projet, la conversation et l'état de l'agent seront restaurés.",
"rewindConfirmBody": "Les modifications de l'agent et les échanges suivants après ce point seront annulés. Le projet, la conversation et l'état de l'agent seront restaurés. Les modifications que vous avez faites depuis seront elles aussi remplacées.",
"rewindConfirm": "Revenir en arrière",
"configureModel": "Configurer le modèle d'IA",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/it/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Impossibile selezionare il modello",
"providerSettings": "Impostazioni provider…",
"applyEditsFailed": "Impossibile applicare le modifiche dell'agente",
"agentEditConflict": "Le modifiche dell'agente non sono state applicate perché il progetto è cambiato durante l'elaborazione.",
"applyAnyway": "Applica comunque",
"chatFailed": "Chat non riuscita",
"rewindFailed": "Riavvolgimento non riuscito",
"rewoundSuccess": "Riavvolto all'inizio di quel messaggio",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Invia (Invio)",
"send": "Invia",
"rewindConfirmTitle": "Riavvolgere qui?",
"rewindConfirmBody": "Le modifiche dell'agente e i turni successivi a questo punto verranno annullati. Il progetto, la conversazione e lo stato dell'agente verranno ripristinati.",
"rewindConfirmBody": "Le modifiche dell'agente e i turni successivi a questo punto verranno annullati. Il progetto, la conversazione e lo stato dell'agente verranno ripristinati. Anche le modifiche che hai fatto da allora verranno sostituite.",
"rewindConfirm": "Riavvolgi",
"configureModel": "Configura modello IA",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/ja-JP/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "モデルを選択できませんでした",
"providerSettings": "プロバイダー設定…",
"applyEditsFailed": "エージェントの編集を適用できませんでした",
"agentEditConflict": "エージェントの処理中にプロジェクトが変更されたため、編集は適用されませんでした。",
"applyAnyway": "それでも適用",
"chatFailed": "チャットに失敗しました",
"rewindFailed": "巻き戻しに失敗しました",
"rewoundSuccess": "そのメッセージの先頭まで巻き戻しました",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "送信(Enter)",
"send": "送信",
"rewindConfirmTitle": "ここまで巻き戻しますか?",
"rewindConfirmBody": "この時点以降のエージェントの編集とやり取りは元に戻されます。プロジェクト、会話、エージェントの状態が復元されます。",
"rewindConfirmBody": "この時点以降のエージェントの編集とやり取りは元に戻されます。プロジェクト、会話、エージェントの状態が復元されます。それ以降にあなたが行った編集も置き換えられます。",
"rewindConfirm": "巻き戻す",
"configureModel": "AIモデルを設定",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/ko-KR/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "모델을 선택할 수 없습니다",
"providerSettings": "제공업체 설정…",
"applyEditsFailed": "에이전트의 편집을 적용할 수 없습니다",
"agentEditConflict": "에이전트가 작업하는 동안 프로젝트가 변경되어 편집 내용이 적용되지 않았습니다.",
"applyAnyway": "그래도 적용",
"chatFailed": "채팅 실패",
"rewindFailed": "되감기 실패",
"rewoundSuccess": "해당 메시지 시작 지점으로 되감았습니다",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "보내기 (Enter)",
"send": "보내기",
"rewindConfirmTitle": "여기로 되감을까요?",
"rewindConfirmBody": "이 시점 이후의 에이전트 편집과 이후 대화 턴이 롤백됩니다. 프로젝트, 대화, 에이전트 상태가 복원됩니다.",
"rewindConfirmBody": "이 시점 이후의 에이전트 편집과 이후 대화 턴이 롤백됩니다. 프로젝트, 대화, 에이전트 상태가 복원됩니다. 그 이후에 직접 한 편집도 함께 대체됩니다.",
"rewindConfirm": "되감기",
"configureModel": "AI 모델 구성",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/pt-BR/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Não foi possível selecionar o modelo",
"providerSettings": "Configurações do provedor…",
"applyEditsFailed": "Não foi possível aplicar as edições do agente",
"agentEditConflict": "As edições do agente não foram aplicadas porque o projeto mudou durante o processamento.",
"applyAnyway": "Aplicar mesmo assim",
"chatFailed": "Falha no chat",
"rewindFailed": "Falha ao rebobinar",
"rewoundSuccess": "Rebobinado até o início dessa mensagem",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Enviar (Enter)",
"send": "Enviar",
"rewindConfirmTitle": "Rebobinar até aqui?",
"rewindConfirmBody": "As edições do agente e as interações seguintes a partir deste ponto serão desfeitas. O projeto, a conversa e o estado do agente serão restaurados.",
"rewindConfirmBody": "As edições do agente e as interações seguintes a partir deste ponto serão desfeitas. O projeto, a conversa e o estado do agente serão restaurados. As edições que você fez desde então também serão substituídas.",
"rewindConfirm": "Rebobinar",
"configureModel": "Configurar modelo de IA",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/ru/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Не удалось выбрать модель",
"providerSettings": "Настройки провайдера…",
"applyEditsFailed": "Не удалось применить правки агента",
"agentEditConflict": "Правки агента не применены, потому что проект изменился во время его работы.",
"applyAnyway": "Всё равно применить",
"chatFailed": "Ошибка чата",
"rewindFailed": "Ошибка отката",
"rewoundSuccess": "Откат к началу этого сообщения выполнен",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Отправить (Enter)",
"send": "Отправить",
"rewindConfirmTitle": "Откатить сюда?",
"rewindConfirmBody": "Правки агента и последующие сообщения после этой точки будут отменены. Проект, беседа и состояние агента будут восстановлены.",
"rewindConfirmBody": "Правки агента и последующие сообщения после этой точки будут отменены. Проект, беседа и состояние агента будут восстановлены. Правки, сделанные вами с тех пор, тоже будут заменены.",
"rewindConfirm": "Откатить",
"configureModel": "Настроить модель ИИ",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/tr/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Model seçilemedi",
"providerSettings": "Sağlayıcı ayarları…",
"applyEditsFailed": "Aracının düzenlemeleri uygulanamadı",
"agentEditConflict": "Aracı çalışırken proje değiştiği için düzenlemeleri uygulanmadı.",
"applyAnyway": "Yine de uygula",
"chatFailed": "Sohbet başarısız oldu",
"rewindFailed": "Geri sarma başarısız oldu",
"rewoundSuccess": "O mesajın başına geri sarıldı",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Gönder (Enter)",
"send": "Gönder",
"rewindConfirmTitle": "Buraya geri sarılsın mı?",
"rewindConfirmBody": "Bu noktadan sonraki aracı düzenlemeleri ve takip eden turlar geri alınacak. Proje, konuşma ve aracı durumu geri yüklenecek.",
"rewindConfirmBody": "Bu noktadan sonraki aracı düzenlemeleri ve takip eden turlar geri alınacak. Proje, konuşma ve aracı durumu geri yüklenecek. O zamandan beri yaptığınız düzenlemeler de değiştirilir.",
"rewindConfirm": "Geri sar",
"configureModel": "Yapay zeka modelini yapılandır",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/vi/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "Không thể chọn mô hình",
"providerSettings": "Cài đặt nhà cung cấp…",
"applyEditsFailed": "Không thể áp dụng chỉnh sửa của tác nhân",
"agentEditConflict": "Các chỉnh sửa của tác nhân không được áp dụng vì dự án đã thay đổi trong lúc xử lý.",
"applyAnyway": "Vẫn áp dụng",
"chatFailed": "Trò chuyện thất bại",
"rewindFailed": "Tua lại thất bại",
"rewoundSuccess": "Đã tua lại đến đầu tin nhắn đó",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "Gửi (Enter)",
"send": "Gửi",
"rewindConfirmTitle": "Tua lại đến đây?",
"rewindConfirmBody": "Các chỉnh sửa của tác nhân và các lượt tiếp theo sau điểm này sẽ được khôi phục lại. Dự án, cuộc trò chuyện và trạng thái tác nhân sẽ được khôi phục.",
"rewindConfirmBody": "Các chỉnh sửa của tác nhân và các lượt tiếp theo sau điểm này sẽ được khôi phục lại. Dự án, cuộc trò chuyện và trạng thái tác nhân sẽ được khôi phục. Các chỉnh sửa bạn đã thực hiện từ lúc đó cũng sẽ bị thay thế.",
"rewindConfirm": "Tua lại",
"configureModel": "Cấu hình mô hình AI",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/zh-CN/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "无法选择模型",
"providerSettings": "提供方设置…",
"applyEditsFailed": "无法应用代理的编辑",
"agentEditConflict": "代理工作期间项目已更改,因此未应用代理的编辑。",
"applyAnyway": "仍然应用",
"chatFailed": "聊天失败",
"rewindFailed": "回退失败",
"rewoundSuccess": "已回退到该消息的开头",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "发送(回车)",
"send": "发送",
"rewindConfirmTitle": "要回退到这里吗?",
"rewindConfirmBody": "此时间点之后代理的编辑和后续对话轮次将被回滚。项目、对话和代理状态将被恢复。",
"rewindConfirmBody": "此时间点之后代理的编辑和后续对话轮次将被回滚。项目、对话和代理状态将被恢复。你在那之后所做的编辑也会被替换。",
"rewindConfirm": "回退",
"configureModel": "配置 AI 模型",
"historyDialog": {
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/zh-TW/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
"selectModelFailed": "無法選擇模型",
"providerSettings": "提供者設定…",
"applyEditsFailed": "無法套用代理的編輯",
"agentEditConflict": "代理執行期間專案已變更,因此未套用代理的編輯。",
"applyAnyway": "仍然套用",
"chatFailed": "聊天失敗",
"rewindFailed": "倒轉失敗",
"rewoundSuccess": "已倒轉至該訊息的開頭",
Expand Down Expand Up @@ -283,7 +285,7 @@
"sendTitle": "傳送(Enter)",
"send": "傳送",
"rewindConfirmTitle": "要倒轉到這裡嗎?",
"rewindConfirmBody": "此時間點之後代理的編輯與後續對話回合將被回復。專案、對話與代理狀態將被還原。",
"rewindConfirmBody": "此時間點之後代理的編輯與後續對話回合將被回復。專案、對話與代理狀態將被還原。你在那之後所做的編輯也會被取代。",
"rewindConfirm": "倒轉",
"configureModel": "設定 AI 模型",
"historyDialog": {
Expand Down
Loading
Loading