feat: add advanced context actions and optional translation

This commit is contained in:
golem
2026-10-05 15:38:16 -06:00
parent 33c4dad00f
commit 5308932bf1
34 changed files with 1579 additions and 159 deletions
+45
View File
@@ -0,0 +1,45 @@
import { renderPrompt, ACTION_GROUPS } from './actions.js';
import { normalizeLanguage } from './settings.js';
import { serializePrompt, MAX_URL_CHARS } from './build-url.js';
const IDS = new Set([...Object.values(ACTION_GROUPS).flat(), 'translate']);
const error = message => ({ kind: 'error', message });
export function prepareAction(request) {
if (!IDS.has(request.actionId)) return error('This action is no longer available. Please try again.');
const source = String(request.source ?? '').trim();
if (!source && request.actionId !== 'ask') return error('There is no text or URL to send. Please select it again.');
const isUrl = request.actionId.includes('-');
if (isUrl) {
try {
const parsed = new URL(source);
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password) throw new Error();
} catch {
return error('This action supports only HTTP or HTTPS links without embedded login credentials.');
}
}
let language = normalizeLanguage(request.language);
if (request.actionId === 'translate') {
if (!language && request.settings.translationMode === 'preferred') language = normalizeLanguage(request.settings.translationLanguage);
if (!language) return { kind: 'language' };
}
const resolved = { ...request, language };
const serialize = text => serializePrompt(request.settings, renderPrompt(resolved, text));
const url = serialize(source);
if (url.length <= MAX_URL_CHARS) return { kind: 'ready', url };
if (isUrl) return error('This URL is too long to send intact. No part of it has been sent.');
const marker = '\n[Selected text shortened]';
if (serialize(marker).length >= MAX_URL_CHARS) return error('The prompt instructions are too long. Shorten your prompt template and try again.');
const points = Array.from(source);
let low = 0, high = points.length;
while (low < high) {
const mid = Math.ceil((low + high) / 2);
if (serialize(points.slice(0, mid).join('') + marker).length <= MAX_URL_CHARS) low = mid;
else high = mid - 1;
}
let kept = points.slice(0, low).join('');
const boundary = kept.search(/\s+\S*$/u);
if (boundary > kept.length * 0.9) kept = kept.slice(0, boundary);
if (!kept.trim()) return error('The prompt leaves no room for the selected text. Shorten the prompt template and try again.');
return { kind: 'shortened', url: serialize(kept + marker) };
}