Remove npm usage and reference static Bulma
This commit is contained in:
parent
5007a96ba8
commit
37ac69a82a
3 changed files with 157 additions and 242 deletions
|
@ -49,9 +49,11 @@ Sortana is implemented entirely with standard WebExtension scripts—no custom e
|
|||
|
||||
1. Ensure PowerShell is available (for Windows) or adapt the script for other
|
||||
environments.
|
||||
2. Run `powershell ./build-xpi.ps1` from the repository root. The script reads
|
||||
the version from `manifest.json` and creates an XPI in the `release` folder.
|
||||
3. Install the generated XPI in Thunderbird via the Add-ons Manager. During
|
||||
2. Ensure the Bulma stylesheet (v1.0.4) is saved as `options/bulma.css`. You can
|
||||
download it from <https://github.com/jgthms/bulma/blob/1.0.4/css/bulma.css>.
|
||||
3. Run `powershell ./build-xpi.ps1` from the repository root. The script reads
|
||||
the version from `manifest.json` and creates an XPI in the `release` folder.
|
||||
4. Install the generated XPI in Thunderbird via the Add-ons Manager. During
|
||||
development you can also load the directory as a temporary add-on.
|
||||
|
||||
## Usage
|
||||
|
|
|
@ -4,248 +4,148 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>AI Filter Options</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f9f9f9;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: #ffffff;
|
||||
padding: 20px 30px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
header img {
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: 30px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
border-color: #007acc;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
background: #007acc;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#rules-container {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.rule {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.rule-actions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.action-row {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s, transform 0.2s;
|
||||
}
|
||||
|
||||
button#save {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button#save:hover {
|
||||
background-color: #005fa3;
|
||||
}
|
||||
|
||||
button#reset-system {
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button#reset-system:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
main {
|
||||
margin: 20px 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="bulma.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="../resources/img/full-logo.png" alt="AI Filter Logo">
|
||||
</header>
|
||||
<section class="section">
|
||||
<div class="container" id="options-container">
|
||||
<figure class="has-text-centered mb-4">
|
||||
<img src="../resources/img/full-logo.png" alt="AI Filter Logo" style="max-height:40px;">
|
||||
</figure>
|
||||
|
||||
<nav style="display:flex; gap:10px; justify-content:center; margin-top:10px;">
|
||||
<button type="button" data-tab="settings" class="tab-button">Settings</button>
|
||||
<button type="button" data-tab="rules" class="tab-button">Rules</button>
|
||||
</nav>
|
||||
<div class="level mb-4">
|
||||
<div class="level-left">
|
||||
<div class="tabs" id="main-tabs">
|
||||
<ul>
|
||||
<li class="is-active" data-tab="settings"><a>Settings</a></li>
|
||||
<li data-tab="rules"><a>Rules</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<button class="button is-primary" id="save" disabled>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div id="settings-tab" class="tab">
|
||||
<div class="form-group">
|
||||
<label for="endpoint">Endpoint:</label>
|
||||
<input type="text" id="endpoint" placeholder="https://api.example.com">
|
||||
</div>
|
||||
<div id="settings-tab" class="tab-content">
|
||||
<div class="field">
|
||||
<label class="label" for="endpoint">Endpoint</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" id="endpoint" placeholder="https://api.example.com">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="template">Prompt template:</label>
|
||||
<select id="template">
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="template">Prompt template</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="template"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="custom-template-container" class="form-group" style="display:none">
|
||||
<label>Custom template</label>
|
||||
<textarea id="custom-template" rows="6" cols="60" placeholder="Enter your custom template here..."></textarea>
|
||||
<p class="placeholder-text">Placeholders: {{system}}, {{email}}, {{query}}</p>
|
||||
</div>
|
||||
<div id="custom-template-container" class="field is-hidden">
|
||||
<label class="label">Custom template</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" id="custom-template" rows="6" placeholder="Enter your custom template here..."></textarea>
|
||||
</div>
|
||||
<p class="help">Placeholders: {{system}}, {{email}}, {{query}}</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="system-instructions">System instructions:</label>
|
||||
<textarea id="system-instructions" rows="4" cols="60" placeholder="Enter system instructions..."></textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="system-instructions">System instructions</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" id="system-instructions" rows="4" placeholder="Enter system instructions..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="reset-system">Reset to default</button>
|
||||
<button id="toggle-advanced" type="button">Advanced</button>
|
||||
<button id="save">Save</button>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button class="button is-danger" id="reset-system">Reset to default</button>
|
||||
<button class="button" id="toggle-advanced" type="button">Advanced</button>
|
||||
</div>
|
||||
|
||||
<div id="advanced-options" style="display:none">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="debug-logging"> Enable debug logging
|
||||
</label>
|
||||
<div id="advanced-options" class="mt-4 is-hidden">
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="debug-logging"> Enable debug logging
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="max_tokens">Max tokens</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" id="max_tokens">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="temperature">Temperature</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="temperature">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="top_p">Top P</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="top_p">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="seed">Seed</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" id="seed">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="repetition_penalty">Repetition penalty</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="repetition_penalty">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="top_k">Top K</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" id="top_k">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="min_p">Min P</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="min_p">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="presence_penalty">Presence penalty</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="presence_penalty">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="frequency_penalty">Frequency penalty</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="frequency_penalty">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="typical_p">Typical P</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="typical_p">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="tfs">TFS</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" step="0.01" id="tfs">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="max_tokens">Max tokens:</label>
|
||||
<input type="number" id="max_tokens">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="temperature">Temperature:</label>
|
||||
<input type="number" step="0.01" id="temperature">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="top_p">Top P:</label>
|
||||
<input type="number" step="0.01" id="top_p">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="seed">Seed:</label>
|
||||
<input type="number" id="seed">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="repetition_penalty">Repetition penalty:</label>
|
||||
<input type="number" step="0.01" id="repetition_penalty">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="top_k">Top K:</label>
|
||||
<input type="number" id="top_k">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="min_p">Min P:</label>
|
||||
<input type="number" step="0.01" id="min_p">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="presence_penalty">Presence penalty:</label>
|
||||
<input type="number" step="0.01" id="presence_penalty">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="frequency_penalty">Frequency penalty:</label>
|
||||
<input type="number" step="0.01" id="frequency_penalty">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="typical_p">Typical P:</label>
|
||||
<input type="number" step="0.01" id="typical_p">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tfs">TFS:</label>
|
||||
<input type="number" step="0.01" id="tfs">
|
||||
|
||||
<div id="rules-tab" class="tab-content is-hidden">
|
||||
<h2 class="title is-4">Classification Rules</h2>
|
||||
<div id="rules-container"></div>
|
||||
<button class="button is-link" id="add-rule" type="button">Add Rule</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="rules-tab" class="tab" style="display:none">
|
||||
<h2>Classification Rules</h2>
|
||||
<div id="rules-container"></div>
|
||||
<button id="add-rule" type="button">Add Rule</button>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
</section>
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,16 +11,24 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
'debugLogging',
|
||||
'aiRules'
|
||||
]);
|
||||
const tabButtons = document.querySelectorAll('.tab-button');
|
||||
const tabs = document.querySelectorAll('.tab');
|
||||
const tabButtons = document.querySelectorAll('#main-tabs li');
|
||||
const tabs = document.querySelectorAll('.tab-content');
|
||||
tabButtons.forEach(btn => btn.addEventListener('click', () => {
|
||||
tabButtons.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
tabButtons.forEach(b => b.classList.remove('is-active'));
|
||||
btn.classList.add('is-active');
|
||||
tabs.forEach(tab => {
|
||||
tab.style.display = tab.id === `${btn.dataset.tab}-tab` ? 'block' : 'none';
|
||||
tab.classList.toggle('is-hidden', tab.id !== `${btn.dataset.tab}-tab`);
|
||||
});
|
||||
}));
|
||||
tabButtons[0]?.click();
|
||||
|
||||
const saveBtn = document.getElementById('save');
|
||||
let initialized = false;
|
||||
function markDirty() {
|
||||
if (initialized) saveBtn.disabled = false;
|
||||
}
|
||||
document.addEventListener('input', markDirty, true);
|
||||
document.addEventListener('change', markDirty, true);
|
||||
logger.setDebug(defaults.debugLogging === true);
|
||||
const DEFAULT_AI_PARAMS = {
|
||||
max_tokens: 4096,
|
||||
|
@ -57,7 +65,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
customTemplate.value = defaults.customTemplate || '';
|
||||
|
||||
function updateVisibility() {
|
||||
customBox.style.display = templateSelect.value === 'custom' ? 'block' : 'none';
|
||||
customBox.classList.toggle('is-hidden', templateSelect.value !== 'custom');
|
||||
}
|
||||
templateSelect.addEventListener('change', updateVisibility);
|
||||
updateVisibility();
|
||||
|
@ -65,7 +73,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
const advancedBox = document.getElementById('advanced-options');
|
||||
const advancedBtn = document.getElementById('toggle-advanced');
|
||||
advancedBtn.addEventListener('click', () => {
|
||||
advancedBox.style.display = advancedBox.style.display === 'none' ? 'block' : 'none';
|
||||
advancedBox.classList.toggle('is-hidden');
|
||||
});
|
||||
|
||||
const debugToggle = document.getElementById('debug-logging');
|
||||
|
@ -109,7 +117,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
|
||||
function createActionRow(action = {type: 'tag'}) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'action-row';
|
||||
row.className = 'action-row field is-grouped';
|
||||
|
||||
const typeSelect = document.createElement('select');
|
||||
['tag','move','junk'].forEach(t => {
|
||||
|
@ -162,6 +170,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
const removeBtn = document.createElement('button');
|
||||
removeBtn.textContent = 'Remove';
|
||||
removeBtn.type = 'button';
|
||||
removeBtn.className = 'button is-small is-danger is-light';
|
||||
removeBtn.addEventListener('click', () => row.remove());
|
||||
|
||||
row.appendChild(typeSelect);
|
||||
|
@ -175,7 +184,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
rulesContainer.innerHTML = '';
|
||||
for (const rule of rules) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'rule';
|
||||
div.className = 'rule box';
|
||||
|
||||
const critInput = document.createElement('input');
|
||||
critInput.type = 'text';
|
||||
|
@ -193,11 +202,13 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
const addAction = document.createElement('button');
|
||||
addAction.textContent = 'Add Action';
|
||||
addAction.type = 'button';
|
||||
addAction.className = 'button is-small';
|
||||
addAction.addEventListener('click', () => actionsContainer.appendChild(createActionRow()));
|
||||
|
||||
const delBtn = document.createElement('button');
|
||||
delBtn.textContent = 'Delete Rule';
|
||||
delBtn.type = 'button';
|
||||
delBtn.className = 'button is-small is-danger';
|
||||
delBtn.addEventListener('click', () => div.remove());
|
||||
|
||||
div.appendChild(critInput);
|
||||
|
@ -238,6 +249,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
if (r.moveTo) actions.push({ type: 'move', folder: r.moveTo });
|
||||
return { criterion: r.criterion, actions };
|
||||
}));
|
||||
initialized = true;
|
||||
|
||||
document.getElementById('save').addEventListener('click', async () => {
|
||||
const endpoint = document.getElementById('endpoint').value;
|
||||
|
@ -277,5 +289,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
} catch (e) {
|
||||
logger.aiLog('[options] failed to apply config', {level: 'error'}, e);
|
||||
}
|
||||
saveBtn.disabled = true;
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue