mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-08 11:56:59 +00:00
Write email replies into open composer
This commit is contained in:
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user