Skip to content

Commit a8a908f

Browse files
committed
imp: theming + minor refactor
1 parent 145ae4b commit a8a908f

File tree

14 files changed

+179
-61
lines changed

14 files changed

+179
-61
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
**11.8.0**
44

55
```
6-
ADD: options: Theming (part 1)
6+
ADD: options: Theming
77
88
FIX: exportfile xml: Include ids for records without reference
99
FIX: hex2rgb: Correctly handle hex values
1010
FIX: Command Assistant: Colors (issue #142)
1111
FIX: Recordset Table: Display the contents of fields of type Object (issue #143)
1212
FIX: Recordset Table: Correct processing of binary type fields
13+
FIX: 'get_content': Use own implementation
1314
```
1415

1516
**11.7.2**

scripts/build.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function createZipArchive() {
2323
zip.addLocalFolder('./src/img', './src/img');
2424
zip.addLocalFolder('./dist', './dist');
2525
zip.addLocalFolder('./_locales', './_locales');
26+
zip.addLocalFolder('./themes', './themes');
2627
zip.addLocalFile('manifest.json');
2728
zip.addLocalFile('README.md');
2829
zip.writeZip(outputFile);

src/css/terminal.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
--terminal-screen-font: 'Lucida Console', Monaco, monospace;
88
--terminal-screen-height: 40vh;
99
--terminal-screen-height-maximized: 100vh;
10+
--terminal-screen-opacity: 0.93;
1011
--terminal-font-size: medium;
1112
--terminal-font-size-ca: small;
1213
--terminal-color-primary: #007bff;
@@ -52,7 +53,7 @@
5253
caret-color: var(--terminal-color-white);
5354
color: var(--terminal-color-light);
5455
height: 100%;
55-
opacity: 0.93;
56+
opacity: var(--terminal-screen-opacity);
5657
overflow-y: scroll;
5758
padding: 0;
5859
resize: none;

src/html/options.html

+8-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<div class="optsection">
1717
<h4 id="title_behaviour">Behaviour</h4>
18-
<p></p>
18+
<br />
1919
<input type="checkbox" id="pinned" name="pinned" />
2020
<label for="pinned">Pinned</label><br />
2121
<input type="checkbox" id="maximized" name="maximized" />
@@ -34,7 +34,10 @@ <h4 id="title_behaviour">Behaviour</h4>
3434

3535
<div class="optsection">
3636
<h4 id="title_theme">Theme</h4>
37-
<p></p>
37+
<label for="theme_preset">Load Preset:</label>
38+
<select id="theme_preset" name="theme_preset">
39+
<option value=""></option></select
40+
><br /><br />
3841
<label for="opacity">Opacity:</label>
3942
<input type="range" id="opacity" name="opacity" min="0" max="100" /><br />
4043
<label for="fontsize">Font size (General):</label>
@@ -166,7 +169,7 @@ <h4 id="title_theme">Theme</h4>
166169

167170
<div class="optsection">
168171
<h4 id="title_shortcuts">Shortcuts</h4>
169-
<p></p>
172+
<br />
170173
<table class="fullwidth" id="shorcut_table">
171174
<thead>
172175
<tr>
@@ -186,7 +189,7 @@ <h4 id="title_shortcuts">Shortcuts</h4>
186189

187190
<div class="optsection">
188191
<h4 id="title_command_assistant">Command Assistant</h4>
189-
<p></p>
192+
<br />
190193
<input type="checkbox" id="cmd_assistant_dyn_options_disabled" name="cmd_assistant_dyn_options_disabled" />
191194
<label for="cmd_assistant_dyn_options_disabled">Disable dynamic options</label><br />
192195
<label for="cmd_assistant_match_mode">Match mode:</label>
@@ -219,7 +222,7 @@ <h4 id="title_terminal_context">Terminal Context</h4>
219222

220223
<div class="optsection">
221224
<h4 id="title_developer_zone">Extension Developer Zone</h4>
222-
<p></p>
225+
<br />
223226
<input type="checkbox" id="devmode_tests" name="devmode_tests" />
224227
<label for="devmode_tests">Use OdooTerminalTests</label><br />
225228
<input type="checkbox" id="devmode_ignore_comp_checks" name="devmode_ignore_comp_checks" />

src/js/common/constants.mjs

+10-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export const SETTING_TYPES: {
6666
colors_domain: 'manual',
6767
};
6868

69-
export const SETTING_NAMES: Array<string> = Array.from(Object.keys(SETTING_TYPES));
69+
export const SETTING_NAMES: $ReadOnlyArray<string> = Array.from(Object.keys(SETTING_TYPES));
7070

7171
export type ExtensionSettings = {
7272
init_cmds: string,
@@ -132,7 +132,7 @@ export const SETTING_DEFAULTS: ExtensionSettings = {
132132
colors_domain: {},
133133
};
134134

135-
export const IGNORED_KEYS: Array<string> = ['Control', 'Meta', 'Shift', 'Alt', 'Escape'];
135+
export const IGNORED_KEYS: $ReadOnlyArray<string> = ['Control', 'Meta', 'Shift', 'Alt', 'Escape'];
136136

137137
export const VERSION_COLOR = {
138138
normal: '#71639e',
@@ -141,7 +141,7 @@ export const VERSION_COLOR = {
141141
enterprise: '#c0c18a',
142142
}
143143

144-
export const COMPATIBLE_VERSIONS: Array<string> = [
144+
export const COMPATIBLE_VERSIONS: $ReadOnlyArray<string> = [
145145
'11.',
146146
'saas~11',
147147
'12.',
@@ -159,3 +159,10 @@ export const COMPATIBLE_VERSIONS: Array<string> = [
159159
'18.0',
160160
'saas~18.0',
161161
];
162+
163+
export const THEMES: $ReadOnlyArray<[string, string]> = [
164+
['dark', 'Dark'],
165+
['light', 'Light'],
166+
['odoo', 'Odoo'],
167+
['matrix', 'Matrix'],
168+
]

src/js/page/terminal/templates/screen_assistant_panel_arg_option_item.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ export default function (option: CMDAssistantOption, index: number, selected_opt
1111
strname = `<strong>${strname}</strong>`;
1212
}
1313
return `<li class="nav-item"><a class="nav-link p-1 px-2 ${
14-
option.is_default ? 'text-warning' : ''
15-
} ${option.is_required ? 'text-warning' : ''} ${
14+
option.is_default ? 'text-info' : ''
15+
} ${option.is_required ? 'text-info' : ''} ${
1616
index === selected_option_index ? 'bg-dark active' : ''
1717
}" data-string="${strval}" style="padding:0.25em" href="#">${strname}</a></li>`;
1818
}

src/js/page/terminal/terminal.mjs

+3-3
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,11 @@ export default class Terminal {
206206
}
207207

208208
#applyTheme() {
209+
const opacity = new String(this.#config.opacity * 0.01).toString();
210+
document.documentElement?.style.setProperty('--terminal-screen-ocapity', opacity);
211+
document.documentElement?.style.setProperty('--terminal-screen-font', this.#config.fontfamily);
209212
document.documentElement?.style.setProperty('--terminal-font-size', this.#config.fontsize);
210213
document.documentElement?.style.setProperty('--terminal-font-size-ca', this.#config.fontsize_ca);
211-
document.documentElement?.style.setProperty('--terminal-screen-font', this.#config.fontfamily);
212214
document.documentElement?.style.setProperty('--terminal-color-primary', this.#config.color_primary);
213215
document.documentElement?.style.setProperty('--terminal-color-secondary', this.#config.color_secondary);
214216
document.documentElement?.style.setProperty('--terminal-color-success', this.#config.color_success);
@@ -250,7 +252,6 @@ export default class Terminal {
250252
this.#onInput();
251253
},
252254
});
253-
this.screen.applyStyle('opacity', new String(this.#config.opacity).toString());
254255
this.onStart();
255256
}
256257

@@ -489,7 +490,6 @@ export default class Terminal {
489490

490491
#applySettings(config: TerminalOptions) {
491492
Object.assign(this.#config, config);
492-
this.#config.opacity *= 0.01;
493493
this.#config.term_context = this.#config.term_context || {};
494494
this.userContext = Object.assign({}, this.#config.term_context, this.userContext);
495495
}

src/js/page/terminal/utils/file2base64.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default function <T>(this: T): Promise<string> {
2727

2828
reader.onerror = reject;
2929
reader.onabort = reject;
30-
reader.onload = (readerEvent) => {
30+
reader.onload = readerEvent => {
3131
// $FlowFixMe
3232
resolve(btoa(readerEvent.target.result));
3333
};

src/js/private/options.mjs

+85-44
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,34 @@ import '@css/options.css';
77
import processKeybind from '@common/utils/process_keybind';
88
import {ubrowser} from '@shared/constants';
99
import {getStorageSync, setStorageSync} from '@shared/storage';
10-
import {IGNORED_KEYS, SETTING_DEFAULTS, SETTING_NAMES, SETTING_TYPES} from '../common/constants.mjs';
10+
import {IGNORED_KEYS, SETTING_DEFAULTS, SETTING_NAMES, SETTING_TYPES, THEMES} from '../common/constants.mjs';
1111

1212
// $FlowFixMe
13-
export type EventCallback = (ev: any) => void;
13+
export type EventCallback = (ev: any) => Promise<void> | void;
1414

1515
let unique_counter: number = 1;
1616
let shortcuts_defs: {[string]: string} = {};
1717
let color_domain_defs: {[string]: string} = {};
1818

19+
async function loadThemeValues(theme: string): Promise<{[string]: mixed}> {
20+
return new Promise((resolve, reject) => {
21+
ubrowser.runtime.getPackageDirectoryEntry((root) => {
22+
root.getFile(`themes/${theme}.json`, {}, (fileEntry) => {
23+
fileEntry.file((file) => {
24+
const reader = new FileReader();
25+
reader.onerror = reject;
26+
reader.onabort = reject;
27+
reader.onloadend = readerEvent => {
28+
// $FlowFixMe
29+
resolve(JSON.parse(readerEvent.target.result));
30+
};
31+
reader.readAsText(file);
32+
}, reject);
33+
}, reject);
34+
});
35+
});
36+
}
37+
1938
function onClickShortcutRemove(e: MouseEvent) {
2039
if (e.target instanceof HTMLElement) {
2140
const el_target = e.target;
@@ -114,8 +133,10 @@ function saveOptions() {
114133
}
115134
const target = document.getElementById(name);
116135
if (target instanceof HTMLInputElement) {
117-
if (type === 'edit' || type === 'int' || type === 'option' || type === 'color') {
136+
if (type === 'edit' || type === 'option' || type === 'color') {
118137
data[name] = target.value;
138+
} else if (type === 'int') {
139+
data[name] = Number(target.value);
119140
} else if (type === 'check') {
120141
data[name] = target.checked;
121142
} else if (type === 'json') {
@@ -151,46 +172,46 @@ function saveOptions() {
151172
setStorageSync(data);
152173
}
153174
154-
function applyInputValues() {
155-
getStorageSync(SETTING_NAMES).then(result => {
156-
const cmd_names = Object.keys(result);
157-
for (const name of cmd_names) {
158-
// $FlowFixMe
159-
const type = SETTING_TYPES[name];
160-
if (type === 'manual') {
161-
continue;
162-
}
163-
const elm = document.getElementById(name);
164-
if (elm) {
165-
if (elm instanceof HTMLInputElement) {
166-
if (type === 'edit' || type === 'color') {
167-
elm.value = result[name] || '';
168-
} else if (type === 'check') {
169-
elm.checked = result[name] || false;
170-
} else if (type === 'int') {
171-
elm.value = result[name] || 0;
172-
} else if (type === 'json') {
173-
elm.value = JSON.stringify(result[name] || {}, null, 4);
174-
}
175-
} else if (elm instanceof HTMLTextAreaElement) {
176-
if (type === 'edit') {
177-
elm.value = result[name] || '';
178-
} else if (type === 'json') {
179-
elm.value = JSON.stringify(result[name] || {}, null, 4);
180-
}
181-
} else if (elm instanceof HTMLSelectElement) {
182-
if (type === 'option') {
183-
// $FlowIgnore
184-
elm.value = result[name] || SETTING_DEFAULTS[name];
185-
}
175+
function applyInputValues(values: {[string]: mixed}) {
176+
const cmd_names = Object.keys(values);
177+
for (const name of cmd_names) {
178+
// $FlowFixMe
179+
const type = SETTING_TYPES[name];
180+
if (type === 'manual') {
181+
continue;
182+
}
183+
const elm = document.getElementById(name);
184+
if (elm) {
185+
if (elm instanceof HTMLInputElement) {
186+
if ((type === 'edit' || type === 'color') && typeof values[name] === 'string') {
187+
elm.value = values[name] || '';
188+
} else if (type === 'check' && typeof values[name] === 'boolean') {
189+
elm.checked = values[name] || false;
190+
} else if (type === 'int' && (typeof values[name] === 'number' || typeof values[name] === 'string')) {
191+
elm.value = new String(values[name] || 0).toString();
192+
} else if (type === 'json' && typeof values[name] === 'object') {
193+
elm.value = JSON.stringify(values[name] || {}, null, 4);
194+
}
195+
} else if (elm instanceof HTMLTextAreaElement) {
196+
if (type === 'edit' && typeof values[name] === 'string') {
197+
elm.value = values[name] || '';
198+
} else if (type === 'json' && typeof values[name] === 'object') {
199+
elm.value = JSON.stringify(values[name] || {}, null, 4);
200+
}
201+
} else if (elm instanceof HTMLSelectElement) {
202+
if (type === 'option') {
203+
// $FlowIgnore
204+
elm.value = values[name] || SETTING_DEFAULTS[name];
186205
}
187206
}
188207
}
189-
shortcuts_defs = result.shortcuts || {};
190-
color_domain_defs = result.colors_domain || {};
191-
renderShortcutTable();
192-
renderColorDomainTable();
193-
});
208+
}
209+
// $FlowFixMe
210+
shortcuts_defs = values.shortcuts || {};
211+
// $FlowFixMe
212+
color_domain_defs = values.colors_domain || {};
213+
renderShortcutTable();
214+
renderColorDomainTable();
194215
}
195216
196217
function onSubmitForm(e: Event) {
@@ -263,9 +284,20 @@ function onClickColorDomainAdd() {
263284
}
264285
}
265286

266-
function onClickResetSettings() {
287+
async function onClickResetSettings() {
267288
setStorageSync(SETTING_DEFAULTS);
268-
applyInputValues();
289+
applyInputValues(await getStorageSync(SETTING_NAMES));
290+
}
291+
292+
async function onChangeThemePreset(ev: Event) {
293+
// $FlowFixMe
294+
const theme_preset = ev.target.value;
295+
try {
296+
const theme_values = await loadThemeValues(theme_preset);
297+
applyInputValues(theme_values);
298+
} catch (e) {
299+
console.error('Failed to load theme values:', e);
300+
}
269301
}
270302

271303
function _apply_i18n(selector: string, ikey: string) {
@@ -338,15 +370,24 @@ function _add_event_listener(selector: string, event_type: string, callback: Eve
338370
}
339371
}
340372

341-
function onDOMLoaded() {
342-
applyInputValues();
373+
async function onDOMLoaded() {
374+
const config_values = await getStorageSync(SETTING_NAMES);
375+
applyInputValues(config_values);
376+
343377
_add_event_listener('#form_options', 'submit', onSubmitForm);
344378
_add_event_listener('#shortcut_keybind', 'keydown', onKeyDownShortcut);
345379
_add_event_listener('#shortcut_keybind', 'keyup', onKeyUpShortcut);
346380
_add_event_listener('#add_shortcut', 'click', onClickShortcutAdd);
347381
_add_event_listener('#add_color_domain', 'click', onClickColorDomainAdd);
348382
_add_event_listener('.reset_settings', 'click', onClickResetSettings);
383+
_add_event_listener('#theme_preset', 'change', onChangeThemePreset);
349384
i18n();
385+
for (const theme of THEMES) {
386+
const option = document.createElement('option');
387+
option.value = theme[0];
388+
option.textContent = theme[1];
389+
document.querySelector('#theme_preset')?.appendChild(option);
390+
}
350391
}
351392

352393
document.addEventListener('DOMContentLoaded', onDOMLoaded);

src/js/shared/storage.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import {ubrowser} from './constants.mjs';
66

77
// $FlowFixMe
8-
export function getStorageSync(keys: Array<string>): Promise<Object> {
8+
export function getStorageSync(keys: $ReadOnlyArray<string>): Promise<Object> {
99
return new Promise((resolve, reject) => {
1010
ubrowser.storage.sync.get(keys, items => {
1111
if (ubrowser.runtime?.lastError) {

themes/dark.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"opacity": 93,
3+
"fontsize": "medium",
4+
"fontsize_ca": "small",
5+
"fontfamily": "'Lucida Console', Monaco, monospace",
6+
"color_primary": "#007bff",
7+
"color_secondary": "#677886",
8+
"color_success": "#28a745",
9+
"color_danger": "#dc3545",
10+
"color_warning": "#ffc107",
11+
"color_info": "#17a2b8",
12+
"color_light": "#f8f9fa",
13+
"color_dark": "#343a40",
14+
"color_muted": "#6c757d",
15+
"color_white": "#ffffff"
16+
}

0 commit comments

Comments
 (0)