add customizable model options for summarize

This commit is contained in:
Guido Kraemer 2026-01-03 22:18:00 +01:00
parent c3e27f0356
commit 3d2c2b9320
4 changed files with 270 additions and 35 deletions

View file

@ -1003,6 +1003,10 @@
"message": "Extract all relevant details required to generate a calendar event from the following text. The extracted information should include:\n- Event Title\n- Start Date and Time (including timezone, if specified)\n- End Date and Time (including timezone, if specified)\n- Full day (if mentioned)\n- Attendees\nEnsure the data is formatted clearly and consistently so that it can be directly used for creating a calendar event.\nIf there are relative time references, consider that the date and time of the email are \"{%mail_datetime%}\". Calculate the start date and time based on this reference. If the calculated start date and time are earlier than \"{%current_datetime%}\", recalculate the start date and time using \"{%current_datetime%}\" as the base.\nIf the duration is not specified, set it to one hour.\nThese are the attendees: {%author%}, {%recipients%}, {%cc_list%}. If present, exclude my address: {%account_email_address%}.\nIf you're not able to get one or more of the required information, please respond with an empty string.\nGenerate a response in JSON format only. Do not include any additional text or explanations; provide only the JSON. Here is the format to be used:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Calendar event summary here\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHere's the text:\"{%selected_text%}\"",
"description": ""
},
"Summarize_prompt_prefs_title": {
"message": "Add Summarization Options",
"description": ""
},
"prompt_summarize": {
"message": "Summarize this email or these emails",
"description": ""
@ -1215,6 +1219,10 @@
"message": "Summarize mail",
"description": ""
},
"prefs_OptionText_summarize_use_specific_integration_Info": {
"message": "If checked, the Model and API specified below will be used for summarizing email(s), regardless the one choosen in the ThunderAI options page.",
"description": ""
},
"prefs_OptionText_summarize_Info": {
"message": "If checked, adds an option to context menu to summarize mail.",
"description": ""

View file

@ -16,6 +16,49 @@
font-style: italic;
}
#account_selector_container{
display: none;
}
#account_selector_checkboxes{
padding: 10px;
border: 1px solid gray;
width: 24em;
margin-bottom: 10px;
margin-top: 10px;
}
.specific_integration{
background-color: #dfeaff;
}
table {
border-collapse: separate;
border-spacing: 0;
}
.group td {
border-top: none;
border-bottom: none;
}
.specific_integration_sub td:first-child {
border-left: 10px solid #dfeaff;
}
.specific_integration_sub td:last-child {
border-right: 10px solid #dfeaff;
}
#connection_ui_end td{
height: 6px;
background-color: #dfeaff;
}
#connection_ui_end{
display: none;
}
.btn_div {
width: 100%;
display: flex;
@ -30,6 +73,11 @@ table#miczPrefs {
border: 1px solid #ccc;
}
.addtags_table_title{
width: 90%;
margin: auto;
}
.section_title {
font-weight: bold;
}
@ -90,27 +138,21 @@ table#miczPrefs tr:last-child td {
color: red;
}
label:has(input[type="checkbox"]) {
cursor: pointer;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #1c1b22;
color: rgb(251, 251, 254);
}
a:link {
color: #409eff;
}
a:visited {
color: #409eff;
}
a:hover {
color: #66b1ff;
}
a:active {
color: #66b1ff;
}
a:active {
color: #66b1ff;
}
a:link { color: #409eff; }
a:visited { color: #409eff; }
a:hover { color: #66b1ff; }
a:active { color: #66b1ff; }
a:active { color: #66b1ff; }
.autocomplete-list {
background-color: #2e2f36;
@ -124,4 +166,24 @@ table#miczPrefs tr:last-child td {
.autocomplete-list li.active {
background-color: #4c4e58;
}
.summarize_auto{
background-color: #415c35;
}
.specific_integration{
background-color: #2E3A4F;
}
.specific_integration_sub td:first-child {
border-left: 10px solid #2E3A4F;
}
.specific_integration_sub td:last-child {
border-right: 10px solid #2E3A4F;
}
#connection_ui_end td{
background-color: #2E3A4F;
}
}

View file

@ -4,13 +4,29 @@
<meta charset="UTF-8" />
<title>ThunderAI - __MSG_Summarize_PageTitle__</title>
<link rel="stylesheet" href="mzta-summarize.css" />
<link rel="stylesheet" href="../_lib/connection-ui.css">
<link rel="icon" href="../../images/icon-16px.png" />
</head>
<body>
<div>
<h1 class="page_title">__MSG_Summarize_PageTitle__</h1>
<p>__MSG_Summarize_info_default__</p>
</div>
<div>
<h1 class="page_title">__MSG_Summarize_PageTitle__</h1>
<p>__MSG_Summarize_info_default__</p>
</div>
<div class="summarize_table_title section_title">__MSG_Summarize_prompt_prefs_title__</div>
<table id="miczPrefs">
<tr class="summarize_tr specific_integration">
<td><span class="opt_title">__MSG_prefs_OptionText_use_specific_integration__</span>
</td>
<td>
<label>
<input type="checkbox" id="summarize_use_specific_integration" name="summarize_use_specific_integration" class="option-input" />
__MSG_prefs_OptionText_summarize_use_specific_integration_Info__
</label>
</td>
</tr>
<tr id="connection_ui_anchor"></tr>
<tr id="connection_ui_end"><td colspan="2"></td></tr>
</table>
<div id="summarize_prompt_container">
<!--title and description-->
<span class="section_title">

View file

@ -16,29 +16,71 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { prefs_default, integration_options_config } from '../../options/mzta-options-default.js';
import { taLogger } from "../../js/mzta-logger.js";
import { getSpecialPrompts, setSpecialPrompts } from "../../js/mzta-prompts.js";
import { getPlaceholders } from "../../js/mzta-placeholders.js";
import {
getSpecialPrompts,
setSpecialPrompts
} from "../../js/mzta-prompts.js";
import {
getPlaceholders,
mapPlaceholderToSuggestion
} from "../../js/mzta-placeholders.js";
import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
import {
getAccountsList,
normalizeStringList,
isAPIKeyValue
} from "../../js/mzta-utils.js";
import {
initializeSpecificIntegrationUI
} from "../_lib/connection-ui.js";
let autocompleteSuggestions = [];
let taLog = new taLogger("mzta-summarize-page", true);
document.addEventListener("DOMContentLoaded", async () => {
i18n.updateDocument();
const summarize_textarea = document.getElementById("summarize_prompt_text");
const summarize_save_btn = document.getElementById("btn_save_prompt");
const summarize_reset_btn = document.getElementById("btn_reset_prompt");
const summarize_textarea_email_template = document.getElementById("summarize_email_template_text");
const summarize_reset_email_template_btn = document.getElementById("btn_reset_email_template");
const summarize_save_email_template_btn = document.getElementById("btn_save_email_template");
let specialPrompts = await getSpecialPrompts();
let summarize_prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize');
let summarize_email_template = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template');
if (summarize_prompt && summarize_prompt.api_type && summarize_prompt.api_type !== '') {
let update_prefs = {};
update_prefs['summarize_connection_type'] = summarize_prompt.api_type;
let integration = summarize_prompt.api_type.replace('_api', '');
if (integration_options_config && integration_options_config[integration]) {
for (const key of Object.keys(integration_options_config[integration])) {
if (summarize_prompt[key] !== undefined) {
update_prefs[`summarize_${integration}_${key}`] = summarize_prompt[key];
}
}
}
await browser.storage.sync.set(update_prefs);
}
await initializeSpecificIntegrationUI({
prefix: 'summarize',
promptId: 'prompt_summarize',
taLog: taLog,
restoreOptionsCallback: restoreOptions
});
i18n.updateDocument();
document.querySelectorAll(".option-input").forEach(element => {
element.addEventListener("change", saveOptions);
});
let prefs_summarize = await browser.storage.sync.get({ summarize_enabled_accounts: [], connection_type: 'chatgpt_web' });
let summarize_textarea = document.getElementById("summarize_prompt_text");
let summarize_save_btn = document.getElementById("btn_save_prompt");
let summarize_reset_btn = document.getElementById("btn_reset_prompt");
let summarize_textarea_email_template = document.getElementById("summarize_email_template_text");
let summarize_reset_email_template_btn = document.getElementById("btn_reset_email_template");
let summarize_save_email_template_btn = document.getElementById("btn_save_email_template");
summarize_textarea.addEventListener("input", (event) => {
summarize_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_full_text'));
@ -84,16 +126,123 @@ document.addEventListener("DOMContentLoaded", async () => {
if(summarize_email_template === 'prompt_summarize_email_template'){
summarize_email_template.text = browser.i18n.getMessage(summarize_email_template.text);
}
summarize_textarea_email_template.value = summarize_email_template.text;
summarize_reset_email_template_btn.disabled = (summarize_email_template.value === browser.i18n.getMessage("prompt_summarize_email_template"));
summarize_textarea.value = summarize_prompt.text;
summarize_reset_btn.disabled = (summarize_textarea.value === browser.i18n.getMessage("prompt_summarize_full_text"));
autocompleteSuggestions = (await getPlaceholders(true))
.filter((p) => p.id !== "additional_text")
.map((p) => ({ command: `{%${p.id}%}`, type: p.type }));
.map(mapPlaceholderToSuggestion);
textareaAutocomplete(summarize_textarea, autocompleteSuggestions, 1);
textareaAutocomplete(summarize_textarea_email_template, autocompleteSuggestions, 1);
});
// Methods to manage options, derived from: /options/mzta-options.js
function saveOptions(e) {
e.preventDefault();
let options = {};
let element = e.target;
// console.log(">>>>>>>>>> Saving option: " + element.id + " = " + element.value);
switch (element.type) {
case 'checkbox':
options[element.id] = element.checked;
break;
case 'number':
options[element.id] = element.valueAsNumber;
break;
case 'text':
case 'password':
options[element.id] = element.value.trim();
break;
case 'select-one':
// console.log(">>>>>>>>>> Saving option [select-one]: " + element.id + " = " + element.value);
options[element.id] = element.value;
break;
case 'textarea':
if(element.id === 'add_tags_auto_uselist_list') {
element.value = normalizeStringList(element.value, 1);
}
options[element.id] = normalizeStringList(element.value);
break;
default:
console.error("[ThunderAI] Unhandled input type:", element.type);
}
browser.storage.sync.set(options);
}
async function restoreOptions() {
function setCurrentChoice(result) {
document.querySelectorAll(".option-input").forEach(element => {
taLog.log("Options restoring " + element.id + " = " + (isAPIKeyValue(element.id) ? "****************" : result[element.id]));
switch (element.type) {
case 'checkbox':
element.checked = result[element.id] || false;
break;
case 'number':
let default_number_value = 0;
if(element.id == 'chatgpt_win_height') default_number_value = prefs_default.chatgpt_win_height;
if(element.id == 'chatgpt_win_width') default_number_value = prefs_default.chatgpt_win_width;
element.value = result[element.id] ?? default_number_value;
break;
case 'text':
case 'textarea':
case 'password':
let default_text_value = '';
if(element.id == 'default_chatgpt_lang') default_text_value = prefs_default.default_chatgpt_lang;
if(element.id === 'add_tags_auto_uselist_list') {
result[element.id] = normalizeStringList(result[element.id], 1);
}
element.value = result[element.id] || default_text_value;
break;
default:
if (element.tagName === 'SELECT') {
let default_select_value = '';
const restoreValue = result[element.id] || default_select_value;
// Check if option exists
let optionExists = Array.from(element.options).some(opt => opt.value === restoreValue);
// If it doesn't exist and restoreValue is not empty, create it
if (!optionExists && restoreValue !== '') {
let newOption = new Option(restoreValue, restoreValue);
element.add(newOption);
}
// Set value
element.value = restoreValue;
if (element.value === '') {
element.selectedIndex = -1;
}
}else{
console.error("[ThunderAI] Unhandled input type:", element.type);
}
}
});
}
let getting = await browser.storage.sync.get(prefs_default);
let specialPrompts = await getSpecialPrompts();
let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_add_tags');
if (addtags_prompt) {
if (addtags_prompt.api_type && addtags_prompt.api_type !== '') {
getting['add_tags_connection_type'] = addtags_prompt.api_type;
} else {
getting['add_tags_connection_type'] = getting['connection_type'];
}
for (const [integration, options] of Object.entries(integration_options_config)) {
for (const key of Object.keys(options)) {
const propName = `${integration}_${key}`;
if (addtags_prompt[propName] !== undefined && addtags_prompt[propName] !== '') {
getting[`add_tags_${propName}`] = addtags_prompt[propName];
} else {
getting[`add_tags_${propName}`] = getting[propName];
}
}
}
}
setCurrentChoice(getting);
}