diff --git a/ext/css/settings.css b/ext/css/settings.css
index 7e6c4bc158..c70e8f9b74 100644
--- a/ext/css/settings.css
+++ b/ext/css/settings.css
@@ -754,6 +754,12 @@ select.short-height {
color: #8b0000;
}
+/* Recommended settings modal */
+/* #recommended-settings-body {
+ display: grid;
+ grid-template-columns: auto 1fr;
+} */
+
/* Advanced settings */
.settings-group.advanced-only>.settings-item::after,
diff --git a/ext/data/default-options-overrides.json b/ext/data/default-options-overrides.json
deleted file mode 100644
index 23e5d9a9b7..0000000000
--- a/ext/data/default-options-overrides.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "es": [
- {
- "path": "scanning.scanResolution",
- "value": "word"
- },
- {
- "path": "foo.bar",
- "value": "baz"
- }
- ]
-}
diff --git a/ext/data/recommended-settings.json b/ext/data/recommended-settings.json
new file mode 100644
index 0000000000..d3610364af
--- /dev/null
+++ b/ext/data/recommended-settings.json
@@ -0,0 +1,30 @@
+{
+ "en": [
+ {
+ "action": "set",
+ "path": "scanning.scanResolution",
+ "value": "word",
+ "description": "English uses spaces to separate words so scanning by word (instead of character) is recommended."
+ },
+ {
+ "action": "set",
+ "path": "translation.searchResolution",
+ "value": "word",
+ "description": "English uses spaces to separate words so looking up whole words in the dictionary is recommended."
+ }
+ ],
+ "es": [
+ {
+ "action": "set",
+ "path": "scanning.scanResolution",
+ "value": "word",
+ "description": "Spanish uses spaces to separate words so scanning by word (instead of character) is recommended."
+ },
+ {
+ "action": "set",
+ "path": "translation.searchResolution",
+ "value": "word",
+ "description": "Spanish uses spaces to separate words so looking up whole words in the dictionary is recommended."
+ }
+ ]
+}
diff --git a/ext/data/schemas/default-options-overrides-schema.json b/ext/data/schemas/recommended-settings-schema.json
similarity index 100%
rename from ext/data/schemas/default-options-overrides-schema.json
rename to ext/data/schemas/recommended-settings-schema.json
diff --git a/ext/js/pages/settings/languages-controller.js b/ext/js/pages/settings/languages-controller.js
index ee80c74986..aa917737b9 100755
--- a/ext/js/pages/settings/languages-controller.js
+++ b/ext/js/pages/settings/languages-controller.js
@@ -35,17 +35,11 @@ export class LanguagesController {
async prepare() {
this._languages = await this._settingsController.application.api.getLanguageSummaries();
this._languages.sort((a, b) => a.name.localeCompare(b.name, 'en'));
- const languageSelect = this._fillSelect(this._languages);
- languageSelect.addEventListener(
- /** @type {string} */ ('settingChanged'),
- /** @type {EventListener} */ (this._onLanguageSelectChanged.bind(this)),
- false,
- );
+ this._fillSelect(this._languages);
}
/**
* @param {import('language').LanguageSummary[]} languages
- * @returns {Element}
*/
_fillSelect(languages) {
const selectElement = querySelectorNotNull(document, '#language-select');
@@ -55,30 +49,5 @@ export class LanguagesController {
option.text = `${name} (${iso})`;
selectElement.appendChild(option);
}
- return selectElement;
- }
-
- /**
- * @param {import('dom-data-binder').SettingChangedEvent} settingChangedEvent
- */
- _onLanguageSelectChanged(settingChangedEvent) {
- // Check if there are language setting overrides and ask the user if they want to apply them
- const setLanguage = settingChangedEvent.detail.value;
- if (typeof setLanguage !== 'string') { return; }
-
- const languageSettingOverrides = this._settingsController.getLanguageSettingOverrides(setLanguage);
- if (this._lastSelectedLanguage !== '' && this._lastSelectedLanguage !== setLanguage && typeof languageSettingOverrides !== 'undefined') {
- const userFriendlyLanguageName = this._languages.find((language) => language.iso === setLanguage)?.name;
- // eslint-disable-next-line no-alert
- const yes = confirm(
- 'We found default settings that work best for ' + userFriendlyLanguageName + ':\n\n' +
- languageSettingOverrides.map(({path, value}) => `${path} = "${value}"`).join('\n') + '\n\n' +
- 'Apply the default settings?',
- );
- if (yes) {
- void this._settingsController.applyLanguageSettingOverrides(setLanguage);
- }
- }
- this._lastSelectedLanguage = setLanguage;
}
}
diff --git a/ext/js/pages/settings/recommended-settings-controller.js b/ext/js/pages/settings/recommended-settings-controller.js
new file mode 100644
index 0000000000..d28ddd37bd
--- /dev/null
+++ b/ext/js/pages/settings/recommended-settings-controller.js
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 Yomitan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see ${path}
-> ${value}
`;
+
+ // Render description
+ const descriptionElement = querySelectorNotNull(template, '.settings-item-description');
+ if (description !== 'undefined') {
+ descriptionElement.textContent = description;
+ }
+
+ // Render checkbox
+ const checkbox /** @type {HTMLInputElement} */ = querySelectorNotNull(template, 'input[type="checkbox"]');
+ checkbox.value = path;
+
+ settingsList.append(template);
+ }
+ this._recommendedSettingsModal.hidden = false;
+ }
+ }
+
+ /**
+ * @param {MouseEvent} e
+ */
+ _onApplyButtonClicked(e) {
+ e.preventDefault();
+ const enabledCheckboxes = querySelectorNotNull(document, '#recommended-settings-list').querySelectorAll('input[type="checkbox"]:checked');
+ if (enabledCheckboxes.length > 0) {
+ const recommendedSettings = this._settingsController.getRecommendedSettings(this._languageSelect.value);
+ for (const checkbox of enabledCheckboxes) {
+ this._settingsController.setSetting(path, value);
+ }
+ }
+ this._recommendedSettingsModal.hidden = true;
+ }
+}
diff --git a/ext/js/pages/settings/settings-controller.js b/ext/js/pages/settings/settings-controller.js
index 4762f41b0a..c54e899212 100644
--- a/ext/js/pages/settings/settings-controller.js
+++ b/ext/js/pages/settings/settings-controller.js
@@ -46,8 +46,8 @@ export class SettingsController extends EventDispatcher {
this._pageExitPreventionEventListeners = new EventListenerCollection();
/** @type {HtmlTemplateCollection} */
this._templates = new HtmlTemplateCollection();
- /** @type {import('settings-controller').LanguageSettingOverrides} */
- this._languageSettingOverrides = {};
+ /** @type {import('settings-controller').RecommendedSettingsByLanguage} */
+ this._recommendedSettingsByLanguage = {};
}
/** @type {import('../../application.js').Application} */
@@ -78,7 +78,7 @@ export class SettingsController extends EventDispatcher {
/** */
async prepare() {
await this._templates.loadFromFiles(['/templates-settings.html']);
- this._languageSettingOverrides = await fetchJson('/data/default-options-overrides.json');
+ this._recommendedSettingsByLanguage = await fetchJson('/data/recommended-settings.json');
this._application.on('optionsUpdated', this._onOptionsUpdated.bind(this));
if (this._canObservePermissionsChanges()) {
chrome.permissions.onAdded.addListener(this._onPermissionsChanged.bind(this));
@@ -190,8 +190,8 @@ export class SettingsController extends EventDispatcher {
* @param {string} language
*/
async applyLanguageSettingOverrides(language) {
- /** @type {import('settings-controller').SettingOverride[]} */
- const settingOverrides = this._languageSettingOverrides[language];
+ /** @type {import('settings-controller').RecommendedSetting[]} */
+ const settingOverrides = this._recommendedSettingsByLanguage[language];
if (typeof settingOverrides === 'undefined') { return; }
/** @type {import('settings-modifications').Modification[]} */
const modifications = settingOverrides.map(({path, value}) => ({action: 'set', path, value}));
@@ -201,10 +201,10 @@ export class SettingsController extends EventDispatcher {
/**
* @param {string} language
- * @returns {import('settings-controller').SettingOverride[]}
+ * @returns {import('settings-controller').RecommendedSetting[]}
*/
- getLanguageSettingOverrides(language) {
- return this._languageSettingOverrides[language];
+ getRecommendedSettings(language) {
+ return this._recommendedSettingsByLanguage[language];
}
/**
diff --git a/ext/js/pages/settings/settings-main.js b/ext/js/pages/settings/settings-main.js
index ebb51276c3..5e1146eb53 100644
--- a/ext/js/pages/settings/settings-main.js
+++ b/ext/js/pages/settings/settings-main.js
@@ -40,6 +40,7 @@ import {PersistentStorageController} from './persistent-storage-controller.js';
import {PopupPreviewController} from './popup-preview-controller.js';
import {PopupWindowController} from './popup-window-controller.js';
import {ProfileController} from './profile-controller.js';
+import {RecommendedSettingsController} from './recommended-settings-controller.js';
import {ScanInputsController} from './scan-inputs-controller.js';
import {ScanInputsSimpleController} from './scan-inputs-simple-controller.js';
import {SecondarySearchDictionaryController} from './secondary-search-dictionary-controller.js';
@@ -174,6 +175,8 @@ await Application.main(true, async (application) => {
const sortFrequencyDictionaryController = new SortFrequencyDictionaryController(settingsController);
preparePromises.push(sortFrequencyDictionaryController.prepare());
+ const recommendedSettingsController = new RecommendedSettingsController(settingsController);
+ preparePromises.push(recommendedSettingsController.prepare());
await Promise.all(preparePromises);
diff --git a/ext/settings.html b/ext/settings.html
index 168fefac38..1053a272c9 100644
--- a/ext/settings.html
+++ b/ext/settings.html
@@ -3851,5 +3851,31 @@