Write email replies into open composer

This commit is contained in:
pewdiepie-archdaemon
2026-06-30 10:48:08 +00:00
parent e131245c91
commit 7854298eae
6 changed files with 63 additions and 8 deletions
+16
View File
@@ -1055,11 +1055,27 @@ def _turn_targets_active_document(intent: Dict[str, object], last_user: str, act
"""
if active_document is None:
return False
raw_doc = getattr(active_document, "current_content", "") or ""
title_l = (getattr(active_document, "title", "") or "").strip().lower()
is_email_doc = (
getattr(active_document, "language", None) == "email"
or title_l in {"new email", "new mail", "new message"}
or ("To:" in raw_doc[:400] and "Subject:" in raw_doc[:400] and "\n---\n" in raw_doc)
)
if "documents" in (intent.get("domains") or set()):
return True
text = str(last_user or "").strip().lower()
if not text:
return False
if is_email_doc and re.search(
r"\b("
r"email|mail|reply|respond|response|draft|compose|send|"
r"tell them|tell her|tell him|say|write|make it say|"
r"japanese|japan|polite|formal|tone|style"
r")\b",
text,
):
return True
return bool(re.search(
r"\b("
r"document|doc|draft|text|poem|story|essay|outline|letter|paragraph|"
+3 -3
View File
@@ -219,8 +219,8 @@
}, { once: true });
})();
</script>
<link rel="stylesheet" href="/static/style.css?v=20260630tasksactivity">
<link rel="modulepreload" href="/static/app.js?v=20260630tasksactivity">
<link rel="stylesheet" href="/static/style.css?v=20260630emailcomposer">
<link rel="modulepreload" href="/static/app.js?v=20260630emailcomposer">
<link rel="modulepreload" href="/static/js/chat.js">
<link rel="modulepreload" href="/static/js/ui.js">
<link rel="modulepreload" href="/static/js/sessions.js">
@@ -2512,7 +2512,7 @@
<script type="module" src="/static/js/settings.js"></script>
<script type="module" src="/static/js/admin.js"></script>
<script type="module" src="/static/js/assistant.js"></script>
<script type="module" src="/static/app.js?v=20260630tasksactivity"></script> <!-- app.js must be LAST -->
<script type="module" src="/static/app.js?v=20260630emailcomposer"></script> <!-- app.js must be LAST -->
<script type="module" src="/static/js/init.js"></script>
<script type="module" src="/static/js/a11y.js"></script>
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script>
+11 -2
View File
@@ -780,9 +780,15 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
}
// Auto-save document editor content before sending so the AI sees latest text
const activeDocIdForSend = documentModule && typeof documentModule.getCurrentDocId === 'function'
const activeEmailComposerCtx = documentModule && typeof documentModule.getActiveEmailComposerContext === 'function'
? documentModule.getActiveEmailComposerContext()
: null;
let activeDocIdForSend = documentModule && typeof documentModule.getCurrentDocId === 'function'
? documentModule.getCurrentDocId()
: null;
if (!activeDocIdForSend && activeEmailComposerCtx?.docId) {
activeDocIdForSend = activeEmailComposerCtx.docId;
}
if (documentModule && activeDocIdForSend) {
try { await documentModule.saveDocument(); } catch(e) { console.warn('doc auto-save failed', e); }
}
@@ -826,7 +832,10 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
try {
const getEmailCtx = window.__odysseusGetActiveEmailContext;
const emCtx = typeof getEmailCtx === 'function' ? getEmailCtx() : null;
if (emCtx && emCtx.uid) {
if (activeEmailComposerCtx && activeEmailComposerCtx.sourceUid) {
fd.append('active_email_uid', String(activeEmailComposerCtx.sourceUid));
fd.append('active_email_folder', String(activeEmailComposerCtx.sourceFolder || 'INBOX'));
} else if (emCtx && emCtx.uid) {
fd.append('active_email_uid', String(emCtx.uid));
fd.append('active_email_folder', String(emCtx.folder || 'INBOX'));
if (emCtx.account) fd.append('active_email_account', String(emCtx.account));
+14
View File
@@ -7,6 +7,7 @@ import Storage from './storage.js';
import themeModule from './theme.js';
import markdownModule from './markdown.js';
import sessionModule from './sessions.js';
import documentModule from './document.js';
/**
* Handle a ui_control SSE event — AI-driven UI manipulation.
@@ -183,6 +184,19 @@ export function handleUIControl(uiData) {
}
} else if (uiEvent === 'open_email_reply' || uiData.ui_event === 'open_email_reply') {
try {
var existingDocId = documentModule && documentModule.findEmailDocId
? documentModule.findEmailDocId(uiData.uid, uiData.folder || 'INBOX')
: null;
if (existingDocId && documentModule.replaceEmailReplyBody) {
if (documentModule.loadDocument) documentModule.loadDocument(existingDocId);
documentModule.replaceEmailReplyBody(existingDocId, uiData.body || '', { force: true });
if (uiModule && uiModule.showToast) uiModule.showToast('Wrote reply into the open email');
return;
}
} catch (e) {
console.warn('open_email_reply existing draft update failed:', e);
}
import('./emailInbox.js').then(function(mod) {
var fn = mod.openReplyDraft || (mod.default && mod.default.openReplyDraft);
if (fn) fn(uiData.uid, uiData.folder || 'INBOX', uiData.mode || 'reply', uiData.body || '');
+18 -2
View File
@@ -6539,7 +6539,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
}));
}
export async function replaceEmailReplyBody(docId, replyText) {
export async function replaceEmailReplyBody(docId, replyText, { force = false } = {}) {
const doc = docs.get(docId);
if (!doc) return;
const fields = _parseEmailHeader(doc.content || '');
@@ -6550,7 +6550,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
);
const quote = quoteIdx >= 0 ? lines.slice(quoteIdx).join('\n') : '';
const ownText = _emailReplyOwnText(fields.body || '');
if (ownText && !/^(\[AI reply draft will appear here\]|Drafting AI reply)/i.test(ownText)) {
if (!force && ownText && !/^(\[AI reply draft will appear here\]|Drafting AI reply)/i.test(ownText)) {
if (uiModule) uiModule.showToast('AI reply ready, but draft was edited');
return;
}
@@ -10430,6 +10430,21 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
return activeDocId;
}
export function getActiveEmailComposerContext() {
if (!activeDocId) return null;
const doc = docs.get(activeDocId);
if (!doc || doc.language !== 'email') return null;
const fields = _parseEmailHeader(doc.content || '');
return {
docId: activeDocId,
sourceUid: fields.sourceUid || '',
sourceFolder: fields.sourceFolder || 'INBOX',
inReplyTo: fields.inReplyTo || '',
to: fields.to || '',
subject: fields.subject || '',
};
}
/** Find an open email tab by source UID + folder. Returns docId or null. */
export function findEmailDocId(uid, folder) {
if (uid == null) return null;
@@ -10472,6 +10487,7 @@ const documentModule = {
exitDiffMode,
isDiffModeActive,
getCurrentDocId,
getActiveEmailComposerContext,
findEmailDocId,
getSelectionContext,
clearSelection,
+1 -1
View File
@@ -7,7 +7,7 @@
// - Other static assets (images/fonts/libs): cache-first with bg refresh.
// - API / non-GET: never cached.
// Bump CACHE_NAME whenever the precache list or SW logic changes.
const CACHE_NAME = 'odysseus-v329';
const CACHE_NAME = 'odysseus-v330';
// Core shell precached on install so repeat opens are instant without any
// network wait. Keep this list in sync with the <script type="module"> tags