From f90e51b5469dd2c8d84cdfc247b83bd3cf419882 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 7 Dec 2024 20:54:56 +0400 Subject: [PATCH 01/10] Added icon for the properties to differentiate between props & tags --- manifest.json | 3 +++ src/lib/components/SearchWrapper.js | 4 ++++ src/styles/content/header.scss | 9 +++++++++ 3 files changed, 16 insertions(+) create mode 100644 src/styles/content/header.scss diff --git a/manifest.json b/manifest.json index 0ec3a7d..d547d94 100644 --- a/manifest.json +++ b/manifest.json @@ -39,6 +39,9 @@ ], "js": [ "src/content/header.js" + ], + "css": [ + "src/styles/content/header.scss" ] }, { diff --git a/src/lib/components/SearchWrapper.js b/src/lib/components/SearchWrapper.js index 43720db..276747b 100644 --- a/src/lib/components/SearchWrapper.js +++ b/src/lib/components/SearchWrapper.js @@ -289,6 +289,10 @@ export class SearchWrapper extends BaseComponent { suggestionItem.dataset.value = suggestedTerm; suggestionItem.innerText = suggestedTerm; + const propertyIcon = document.createElement('i'); + propertyIcon.classList.add('fa', 'fa-info-circle'); + suggestionItem.insertAdjacentElement('afterbegin', propertyIcon); + suggestionItem.addEventListener('mouseover', () => { SearchWrapper.#findAndResetSelectedSuggestion(suggestionItem); suggestionItem.classList.add('autocomplete__item--selected'); diff --git a/src/styles/content/header.scss b/src/styles/content/header.scss new file mode 100644 index 0000000..22b76e0 --- /dev/null +++ b/src/styles/content/header.scss @@ -0,0 +1,9 @@ +.autocomplete { + &__item { + &--property { + i { + margin-right: .5em; + } + } + } +} From 6775a2175a540521e3e3046edcbb3cd9ecc121d9 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 19:55:45 +0400 Subject: [PATCH 02/10] Moving settings classes to TypeScript --- .../components/ImageShowFullscreenButton.js | 2 +- src/lib/components/MaintenancePopup.js | 2 +- src/lib/components/SearchWrapper.js | 2 +- src/lib/components/TagDropdownWrapper.js | 2 +- ...heableSettings.js => CacheableSettings.ts} | 40 ++++++------ .../extension/settings/MaintenanceSettings.js | 63 ------------------- .../extension/settings/MaintenanceSettings.ts | 39 ++++++++++++ src/lib/extension/settings/MiscSettings.js | 39 ------------ src/lib/extension/settings/MiscSettings.ts | 19 ++++++ src/lib/extension/settings/SearchSettings.js | 42 ------------- src/lib/extension/settings/SearchSettings.ts | 28 +++++++++ src/stores/maintenance-profiles-store.js | 2 +- src/stores/misc-preferences.js | 4 +- src/stores/search-preferences.js | 6 +- 14 files changed, 117 insertions(+), 173 deletions(-) rename src/lib/extension/base/{CacheableSettings.js => CacheableSettings.ts} (58%) delete mode 100644 src/lib/extension/settings/MaintenanceSettings.js create mode 100644 src/lib/extension/settings/MaintenanceSettings.ts delete mode 100644 src/lib/extension/settings/MiscSettings.js create mode 100644 src/lib/extension/settings/MiscSettings.ts delete mode 100644 src/lib/extension/settings/SearchSettings.js create mode 100644 src/lib/extension/settings/SearchSettings.ts diff --git a/src/lib/components/ImageShowFullscreenButton.js b/src/lib/components/ImageShowFullscreenButton.js index 7a2fc09..f0cd8c7 100644 --- a/src/lib/components/ImageShowFullscreenButton.js +++ b/src/lib/components/ImageShowFullscreenButton.js @@ -1,6 +1,6 @@ import {BaseComponent} from "$lib/components/base/BaseComponent.js"; import {getComponent} from "$lib/components/base/ComponentUtils.js"; -import MiscSettings from "$lib/extension/settings/MiscSettings.js"; +import MiscSettings from "$lib/extension/settings/MiscSettings.ts"; import {FullscreenViewer} from "$lib/components/FullscreenViewer.js"; export class ImageShowFullscreenButton extends BaseComponent { diff --git a/src/lib/components/MaintenancePopup.js b/src/lib/components/MaintenancePopup.js index 7abb794..0267304 100644 --- a/src/lib/components/MaintenancePopup.js +++ b/src/lib/components/MaintenancePopup.js @@ -1,4 +1,4 @@ -import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.js"; +import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.ts"; import MaintenanceProfile from "$entities/MaintenanceProfile.ts"; import {BaseComponent} from "$lib/components/base/BaseComponent.js"; import {getComponent} from "$lib/components/base/ComponentUtils.js"; diff --git a/src/lib/components/SearchWrapper.js b/src/lib/components/SearchWrapper.js index 43720db..d674851 100644 --- a/src/lib/components/SearchWrapper.js +++ b/src/lib/components/SearchWrapper.js @@ -1,6 +1,6 @@ import {BaseComponent} from "$lib/components/base/BaseComponent.js"; import {QueryLexer, QuotedTermToken, TermToken, Token} from "$lib/booru/search/QueryLexer.js"; -import SearchSettings from "$lib/extension/settings/SearchSettings.js"; +import SearchSettings from "$lib/extension/settings/SearchSettings.ts"; export class SearchWrapper extends BaseComponent { /** @type {HTMLInputElement|null} */ diff --git a/src/lib/components/TagDropdownWrapper.js b/src/lib/components/TagDropdownWrapper.js index 0eb5e3c..78961a3 100644 --- a/src/lib/components/TagDropdownWrapper.js +++ b/src/lib/components/TagDropdownWrapper.js @@ -1,6 +1,6 @@ import {BaseComponent} from "$lib/components/base/BaseComponent.js"; import MaintenanceProfile from "$entities/MaintenanceProfile.ts"; -import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.js"; +import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.ts"; import {getComponent} from "$lib/components/base/ComponentUtils.js"; const isTagEditorProcessedKey = Symbol(); diff --git a/src/lib/extension/base/CacheableSettings.js b/src/lib/extension/base/CacheableSettings.ts similarity index 58% rename from src/lib/extension/base/CacheableSettings.js rename to src/lib/extension/base/CacheableSettings.ts index 6627297..d5bb6e7 100644 --- a/src/lib/extension/base/CacheableSettings.js +++ b/src/lib/extension/base/CacheableSettings.ts @@ -1,20 +1,20 @@ import ConfigurationController from "$lib/extension/ConfigurationController.js"; -export default class CacheableSettings { - /** @type {ConfigurationController} */ - #controller; - /** @type {Map} */ - #cachedValues = new Map(); - /** @type {function[]} */ - #disposables = []; +export default class CacheableSettings { + #controller: ConfigurationController; + #cachedValues: Map = new Map(); + #disposables: Function[] = []; - constructor(settingsNamespace) { + constructor(settingsNamespace: string) { this.#controller = new ConfigurationController(settingsNamespace); this.#disposables.push( this.#controller.subscribeToChanges(settings => { for (const key of Object.keys(settings)) { - this.#cachedValues.set(key, settings[key]); + this.#cachedValues.set( + key as keyof Fields, + settings[key] + ); } }) ); @@ -27,12 +27,12 @@ export default class CacheableSettings { * @return {Promise} * @protected */ - async _resolveSetting(settingName, defaultValue) { + protected async _resolveSetting(settingName: Key, defaultValue: Fields[Key]): Promise { if (this.#cachedValues.has(settingName)) { return this.#cachedValues.get(settingName); } - const settingValue = await this.#controller.readSetting(settingName, defaultValue); + const settingValue = await this.#controller.readSetting(settingName as string, defaultValue); this.#cachedValues.set(settingName, settingValue); @@ -40,13 +40,12 @@ export default class CacheableSettings { } /** - * @param {string} settingName Name of the setting to write. - * @param {*} value Value to pass. - * @param {boolean} [force=false] Ignore the cache and force the update. - * @return {Promise} + * @param settingName Name of the setting to write. + * @param value Value to pass. + * @param force Ignore the cache and force the update. * @protected */ - async _writeSetting(settingName, value, force = false) { + async _writeSetting(settingName: Key, value: Fields[Key], force: boolean = false): Promise { if ( !force && this.#cachedValues.has(settingName) @@ -55,7 +54,10 @@ export default class CacheableSettings { return; } - return this.#controller.writeSetting(settingName, value); + return this.#controller.writeSetting( + settingName as string, + value + ); } /** @@ -63,8 +65,8 @@ export default class CacheableSettings { * @param {function(Object): void} callback Callback which will receive list of settings. * @return {function(): void} Unsubscribe function. */ - subscribe(callback) { - const unsubscribeCallback = this.#controller.subscribeToChanges(callback); + subscribe(callback: (settings: Partial) => void): () => void { + const unsubscribeCallback = this.#controller.subscribeToChanges(callback as (fields: Record) => void); this.#disposables.push(unsubscribeCallback); diff --git a/src/lib/extension/settings/MaintenanceSettings.js b/src/lib/extension/settings/MaintenanceSettings.js deleted file mode 100644 index ad0da60..0000000 --- a/src/lib/extension/settings/MaintenanceSettings.js +++ /dev/null @@ -1,63 +0,0 @@ -import ConfigurationController from "$lib/extension/ConfigurationController.js"; -import MaintenanceProfile from "$entities/MaintenanceProfile.ts"; -import CacheableSettings from "$lib/extension/base/CacheableSettings.js"; - -export default class MaintenanceSettings extends CacheableSettings { - constructor() { - super("maintenance"); - } - - /** - * Set the active maintenance profile. - * - * @return {Promise} - */ - async resolveActiveProfileId() { - return this._resolveSetting("activeProfile", null); - } - - /** - * Get the active maintenance profile if it is set. - * - * @return {Promise} - */ - async resolveActiveProfileAsObject() { - const resolvedProfileId = await this.resolveActiveProfileId(); - - return (await MaintenanceProfile.readAll()) - .find(profile => profile.id === resolvedProfileId) || null; - } - - /** - * Set the active maintenance profile. - * - * @param {string|null} profileId ID of the profile to set as active. If `null`, the active profile will be considered - * unset. - * - * @return {Promise} - */ - async setActiveProfileId(profileId) { - await this._writeSetting("activeProfile", profileId); - } - - /** - * Subscribe to the changes in the maintenance-related settings. - * - * @param {function(MaintenanceSettingsObject): void} callback Callback to call when the settings change. The new - * settings are passed as an argument. - * - * @return {function(): void} Unsubscribe function. - */ - subscribe(callback) { - return super.subscribe(settings => { - callback({ - activeProfile: settings.activeProfile || null, - }); - }); - } -} - -/** - * @typedef {Object} MaintenanceSettingsObject - * @property {string|null} activeProfile - */ diff --git a/src/lib/extension/settings/MaintenanceSettings.ts b/src/lib/extension/settings/MaintenanceSettings.ts new file mode 100644 index 0000000..94b9b85 --- /dev/null +++ b/src/lib/extension/settings/MaintenanceSettings.ts @@ -0,0 +1,39 @@ +import MaintenanceProfile from "$entities/MaintenanceProfile.ts"; +import CacheableSettings from "$lib/extension/base/CacheableSettings.ts"; + +interface MaintenanceSettingsFields { + activeProfile: string | null; +} + +export default class MaintenanceSettings extends CacheableSettings { + constructor() { + super("maintenance"); + } + + /** + * Set the active maintenance profile. + */ + async resolveActiveProfileId() { + return this._resolveSetting("activeProfile", null); + } + + /** + * Get the active maintenance profile if it is set. + */ + async resolveActiveProfileAsObject(): Promise { + const resolvedProfileId = await this.resolveActiveProfileId(); + + return (await MaintenanceProfile.readAll()) + .find(profile => profile.id === resolvedProfileId) || null; + } + + /** + * Set the active maintenance profile. + * + * @param profileId ID of the profile to set as active. If `null`, the active profile will be considered + * unset. + */ + async setActiveProfileId(profileId: string | null): Promise { + await this._writeSetting("activeProfile", profileId); + } +} diff --git a/src/lib/extension/settings/MiscSettings.js b/src/lib/extension/settings/MiscSettings.js deleted file mode 100644 index fd49f18..0000000 --- a/src/lib/extension/settings/MiscSettings.js +++ /dev/null @@ -1,39 +0,0 @@ -import CacheableSettings from "$lib/extension/base/CacheableSettings.js"; - -export default class MiscSettings extends CacheableSettings { - constructor() { - super("misc"); - } - - /** - * @return {Promise} - */ - async resolveFullscreenViewerEnabled() { - return this._resolveSetting("fullscreenViewer", true); - } - - /** - * @param {boolean} isEnabled - * @return {Promise} - */ - async setFullscreenViewerEnabled(isEnabled) { - return this._writeSetting("fullscreenViewer", isEnabled); - } - - /** - * @param {function(MiscSettingsObject): void} callback - * @return {function(): void} - */ - subscribe(callback) { - return super.subscribe(settings => { - callback({ - fullscreenViewer: settings.fullscreenViewer ?? true, - }) - }); - } -} - -/** - * @typedef {Object} MiscSettingsObject - * @property {boolean} fullscreenViewer - */ diff --git a/src/lib/extension/settings/MiscSettings.ts b/src/lib/extension/settings/MiscSettings.ts new file mode 100644 index 0000000..94ea7d2 --- /dev/null +++ b/src/lib/extension/settings/MiscSettings.ts @@ -0,0 +1,19 @@ +import CacheableSettings from "$lib/extension/base/CacheableSettings.ts"; + +interface MiscSettingsFields { + fullscreenViewer: boolean; +} + +export default class MiscSettings extends CacheableSettings { + constructor() { + super("misc"); + } + + async resolveFullscreenViewerEnabled() { + return this._resolveSetting("fullscreenViewer", true); + } + + async setFullscreenViewerEnabled(isEnabled: boolean) { + return this._writeSetting("fullscreenViewer", isEnabled); + } +} diff --git a/src/lib/extension/settings/SearchSettings.js b/src/lib/extension/settings/SearchSettings.js deleted file mode 100644 index e5de971..0000000 --- a/src/lib/extension/settings/SearchSettings.js +++ /dev/null @@ -1,42 +0,0 @@ -import CacheableSettings from "$lib/extension/base/CacheableSettings.js"; - -export default class SearchSettings extends CacheableSettings { - constructor() { - super("search"); - } - - async resolvePropertiesSuggestionsEnabled() { - return this._resolveSetting("suggestProperties", false); - } - - async resolvePropertiesSuggestionsPosition() { - return this._resolveSetting("suggestPropertiesPosition", "start"); - } - - async setPropertiesSuggestions(isEnabled) { - return this._writeSetting("suggestProperties", isEnabled); - } - - async setPropertiesSuggestionsPosition(position) { - return this._writeSetting("suggestPropertiesPosition", position); - } - - /** - * @param {function(SearchSettingsObject): void} callback - * @return {function(): void} - */ - subscribe(callback) { - return super.subscribe(rawSettings => { - callback({ - suggestProperties: rawSettings.suggestProperties ?? false, - suggestPropertiesPosition: rawSettings.suggestPropertiesPosition ?? "start", - }); - }); - } -} - -/** - * @typedef {Object} SearchSettingsObject - * @property {boolean} suggestProperties - * @property {"start"|"end"} suggestPropertiesPosition - */ diff --git a/src/lib/extension/settings/SearchSettings.ts b/src/lib/extension/settings/SearchSettings.ts new file mode 100644 index 0000000..13a7bce --- /dev/null +++ b/src/lib/extension/settings/SearchSettings.ts @@ -0,0 +1,28 @@ +import CacheableSettings from "$lib/extension/base/CacheableSettings.ts"; + +interface SearchSettingsFields { + suggestProperties: boolean; + suggestPropertiesPosition: "start" | "end"; +} + +export default class SearchSettings extends CacheableSettings { + constructor() { + super("search"); + } + + async resolvePropertiesSuggestionsEnabled() { + return this._resolveSetting("suggestProperties", false); + } + + async resolvePropertiesSuggestionsPosition() { + return this._resolveSetting("suggestPropertiesPosition", "start"); + } + + async setPropertiesSuggestions(isEnabled: boolean) { + return this._writeSetting("suggestProperties", isEnabled); + } + + async setPropertiesSuggestionsPosition(position: "start" | "end") { + return this._writeSetting("suggestPropertiesPosition", position); + } +} diff --git a/src/stores/maintenance-profiles-store.js b/src/stores/maintenance-profiles-store.js index 95e1a70..db0ce85 100644 --- a/src/stores/maintenance-profiles-store.js +++ b/src/stores/maintenance-profiles-store.js @@ -1,6 +1,6 @@ import {writable} from "svelte/store"; import MaintenanceProfile from "$entities/MaintenanceProfile.ts"; -import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.js"; +import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.ts"; /** * Store for working with maintenance profiles in the Svelte popup. diff --git a/src/stores/misc-preferences.js b/src/stores/misc-preferences.js index 22eb309..53c7c04 100644 --- a/src/stores/misc-preferences.js +++ b/src/stores/misc-preferences.js @@ -1,5 +1,5 @@ import {writable} from "svelte/store"; -import MiscSettings from "$lib/extension/settings/MiscSettings.js"; +import MiscSettings from "$lib/extension/settings/MiscSettings.ts"; export const fullScreenViewerEnabled = writable(true); @@ -13,6 +13,6 @@ Promise.allSettled([ }); miscSettings.subscribe(settings => { - fullScreenViewerEnabled.set(settings.fullscreenViewer); + fullScreenViewerEnabled.set(Boolean(settings.fullscreenViewer)); }); }); diff --git a/src/stores/search-preferences.js b/src/stores/search-preferences.js index 5d01486..6d35192 100644 --- a/src/stores/search-preferences.js +++ b/src/stores/search-preferences.js @@ -1,5 +1,5 @@ import {writable} from "svelte/store"; -import SearchSettings from "$lib/extension/settings/SearchSettings.js"; +import SearchSettings from "$lib/extension/settings/SearchSettings.ts"; export const searchPropertiesSuggestionsEnabled = writable(false); @@ -23,7 +23,7 @@ Promise.allSettled([ }); searchSettings.subscribe(settings => { - searchPropertiesSuggestionsEnabled.set(settings.suggestProperties); - searchPropertiesSuggestionsPosition.set(settings.suggestPropertiesPosition); + searchPropertiesSuggestionsEnabled.set(Boolean(settings.suggestProperties)); + searchPropertiesSuggestionsPosition.set(settings.suggestPropertiesPosition || 'start'); }); }) From 3123ce1c0f9262eb687cbf2bb842ea0e6476598b Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 20:18:33 +0400 Subject: [PATCH 03/10] Added list of blacklisted tags from Furbooru --- src/config/tags.ts | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/config/tags.ts diff --git a/src/config/tags.ts b/src/config/tags.ts new file mode 100644 index 0000000..6cfea79 --- /dev/null +++ b/src/config/tags.ts @@ -0,0 +1,66 @@ +export const tagsBlacklist: string[] = [ + "anthro art", + "anthro artist", + "anthro cute", + "anthro furry", + "anthro nsfw", + "anthro oc", + "anthroart", + "anthroartist", + "anthrofurry", + "anthronsfw", + "anthrooc", + "art", + "artist", + "artwork", + "cringe", + "cringeworthy", + "cute art", + "cute artwork", + "cute furry", + "downvotes galore", + "drama in comments", + "drama in the comments", + "fandom", + "furries", + "furry anthro", + "furry art", + "furry artist", + "furry artwork", + "furry character", + "furry community", + "furry cute", + "furry fandom", + "furry nsfw", + "furry oc", + "furryanthro", + "furryart", + "furryartist", + "furryartwork", + "furrynsfw", + "furryoc", + "image", + "no tag", + "not tagged", + "notag", + "notags", + "nsfw anthro", + "nsfw art", + "nsfw artist", + "nsfw artwork", + "nsfw", + "nsfwanthro", + "nsfwart", + "nsfwartist", + "nsfwartwork", + "paywall", + "rcf community", + "sfw", + "solo oc", + "tag me", + "tag needed", + "tag your shit", + "tagme", + "upvotes galore", + "wall of faves" +]; From ca3c4f6618efca5ef8c79085c28435d52f8722b0 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 21:25:36 +0400 Subject: [PATCH 04/10] Added `$config` alias for config directory --- .vite/lib/content-scripts.js | 1 + svelte.config.js | 1 + 2 files changed, 2 insertions(+) diff --git a/.vite/lib/content-scripts.js b/.vite/lib/content-scripts.js index aad275e..55f886c 100644 --- a/.vite/lib/content-scripts.js +++ b/.vite/lib/content-scripts.js @@ -48,6 +48,7 @@ function wrapScriptIntoIIFE() { */ function makeAliases(rootDir) { return { + "$config": path.resolve(rootDir, 'src/config'), "$lib": path.resolve(rootDir, 'src/lib'), "$entities": path.resolve(rootDir, 'src/lib/extension/entities'), "$styles": path.resolve(rootDir, 'src/styles'), diff --git a/svelte.config.js b/svelte.config.js index 5532039..e32e94c 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -14,6 +14,7 @@ const config = { name: Date.now().toString(36) }, alias: { + "$config": "./src/config", "$components": "./src/components", "$styles": "./src/styles", "$stores": "./src/stores", From d1e22eaa0c2c9ed70f10c570aa3396a8eace6280 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 21:48:35 +0400 Subject: [PATCH 05/10] Added flag for auto-removing invalid tags --- .../extension/settings/MaintenanceSettings.ts | 9 +++++++++ src/stores/maintenance-preferences.ts | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/stores/maintenance-preferences.ts diff --git a/src/lib/extension/settings/MaintenanceSettings.ts b/src/lib/extension/settings/MaintenanceSettings.ts index 94b9b85..c8aeadd 100644 --- a/src/lib/extension/settings/MaintenanceSettings.ts +++ b/src/lib/extension/settings/MaintenanceSettings.ts @@ -3,6 +3,7 @@ import CacheableSettings from "$lib/extension/base/CacheableSettings.ts"; interface MaintenanceSettingsFields { activeProfile: string | null; + stripBlacklistedTags: boolean; } export default class MaintenanceSettings extends CacheableSettings { @@ -27,6 +28,10 @@ export default class MaintenanceSettings extends CacheableSettings profile.id === resolvedProfileId) || null; } + async resolveStripBlacklistedTags() { + return this._resolveSetting('stripBlacklistedTags', false); + } + /** * Set the active maintenance profile. * @@ -36,4 +41,8 @@ export default class MaintenanceSettings extends CacheableSettings { await this._writeSetting("activeProfile", profileId); } + + async setStripBlacklistedTags(isEnabled: boolean) { + await this._writeSetting('stripBlacklistedTags', isEnabled); + } } diff --git a/src/stores/maintenance-preferences.ts b/src/stores/maintenance-preferences.ts new file mode 100644 index 0000000..05954a0 --- /dev/null +++ b/src/stores/maintenance-preferences.ts @@ -0,0 +1,18 @@ +import {writable} from "svelte/store"; +import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings.ts"; + +export const stripBlacklistedTagsEnabled = writable(true); + +const maintenanceSettings = new MaintenanceSettings(); + +Promise + .all([ + maintenanceSettings.resolveStripBlacklistedTags().then(v => stripBlacklistedTagsEnabled.set(v ?? true)) + ]) + .then(() => { + maintenanceSettings.subscribe(settings => { + stripBlacklistedTagsEnabled.set(typeof settings.stripBlacklistedTags === 'boolean' ? settings.stripBlacklistedTags : true); + }); + + stripBlacklistedTagsEnabled.subscribe(v => maintenanceSettings.setStripBlacklistedTags(v)); + }); From 90562f3878a6c0693e6174c21878d0f98ca8d114 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 21:59:47 +0400 Subject: [PATCH 06/10] Added settings menu for tags with auto-remove option --- src/routes/preferences/+page.svelte | 1 + src/routes/preferences/tags/+page.svelte | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/routes/preferences/tags/+page.svelte diff --git a/src/routes/preferences/+page.svelte b/src/routes/preferences/+page.svelte index bc1d369..f4f4ea3 100644 --- a/src/routes/preferences/+page.svelte +++ b/src/routes/preferences/+page.svelte @@ -6,6 +6,7 @@ Back
+ Tagging Search Misc & Tools
diff --git a/src/routes/preferences/tags/+page.svelte b/src/routes/preferences/tags/+page.svelte new file mode 100644 index 0000000..811329a --- /dev/null +++ b/src/routes/preferences/tags/+page.svelte @@ -0,0 +1,20 @@ + + + + Back +
+
+ + + + Automatically remove black-listed tags from the images + + + From fa8ff3b718a7d70928e5eac66c56bc13aceda5be Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 22:00:35 +0400 Subject: [PATCH 07/10] Auto-removing or simply revealing tags when detected --- src/lib/components/MaintenancePopup.js | 85 +++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/src/lib/components/MaintenancePopup.js b/src/lib/components/MaintenancePopup.js index 0267304..ea3ab0f 100644 --- a/src/lib/components/MaintenancePopup.js +++ b/src/lib/components/MaintenancePopup.js @@ -3,6 +3,16 @@ import MaintenanceProfile from "$entities/MaintenanceProfile.ts"; import {BaseComponent} from "$lib/components/base/BaseComponent.js"; import {getComponent} from "$lib/components/base/ComponentUtils.js"; import ScrapedAPI from "$lib/booru/scraped/ScrapedAPI.js"; +import {tagsBlacklist} from "$config/tags.ts"; + +class BlackListedTagsEncounteredError extends Error { + /** + * @param {string} tagName + */ + constructor(tagName) { + super(`This tag is blacklisted and prevents submission: ${tagName}`); + } +} export class MaintenancePopup extends BaseComponent { /** @type {HTMLElement} */ @@ -11,6 +21,9 @@ export class MaintenancePopup extends BaseComponent { /** @type {HTMLElement[]} */ #tagsList = []; + /** @type {Map} */ + #suggestedInvalidTags = new Map(); + /** @type {MaintenanceProfile|null} */ #activeProfile = null; @@ -89,11 +102,16 @@ export class MaintenancePopup extends BaseComponent { /** @type {string[]} */ const activeProfileTagsList = this.#activeProfile?.settings.tags || []; - for (let tagElement of this.#tagsList) { + for (const tagElement of this.#tagsList) { + tagElement.remove(); + } + + for (const tagElement of this.#suggestedInvalidTags.values()) { tagElement.remove(); } this.#tagsList = new Array(activeProfileTagsList.length); + this.#suggestedInvalidTags.clear(); const currentPostTags = this.#mediaBoxTools.mediaBox.tagsAndAliases; @@ -109,6 +127,12 @@ export class MaintenancePopup extends BaseComponent { tagElement.classList.toggle('is-present', isPresent); tagElement.classList.toggle('is-missing', !isPresent); tagElement.classList.toggle('is-aliased', isPresent && currentPostTags.get(tagName) !== tagName); + + // Just to prevent duplication, we need to include this tag to the map of suggested invalid tags + if (tagsBlacklist.includes(tagName)) { + MaintenancePopup.#markTagAsInvalid(tagElement); + this.#suggestedInvalidTags.set(tagName, tagElement); + } }); } @@ -188,6 +212,8 @@ export class MaintenancePopup extends BaseComponent { let maybeTagsAndAliasesAfterUpdate; + const shouldAutoRemove = await MaintenancePopup.#maintenanceSettings.resolveStripBlacklistedTags(); + try { maybeTagsAndAliasesAfterUpdate = await MaintenancePopup.#scrapedAPI.updateImageTags( this.#mediaBoxTools.mediaBox.imageId, @@ -200,11 +226,27 @@ export class MaintenancePopup extends BaseComponent { tagsList.add(tagName); } + if (shouldAutoRemove) { + for (let tagName of tagsBlacklist) { + tagsList.delete(tagName); + } + } else { + for (let tagName of tagsList) { + if (tagsBlacklist.includes(tagName)) { + throw new BlackListedTagsEncounteredError(tagName); + } + } + } + return tagsList; } ); } catch (e) { - console.warn('Tags submission failed:', e); + if (e instanceof BlackListedTagsEncounteredError) { + this.#revealInvalidTags(); + } else { + console.warn('Tags submission failed:', e); + } MaintenancePopup.#notifyAboutPendingSubmission(false); this.emit('maintenance-state-change', 'failed'); @@ -228,6 +270,36 @@ export class MaintenancePopup extends BaseComponent { this.#isSubmitting = false; } + #revealInvalidTags() { + const tagsAndAliases = this.#mediaBoxTools.mediaBox.tagsAndAliases; + + if (!tagsAndAliases) { + return; + } + + const firstTagInList = this.#tagsList[0]; + + for (let tagName of tagsBlacklist) { + if (tagsAndAliases.has(tagName)) { + if (this.#suggestedInvalidTags.has(tagName)) { + continue; + } + + const tagElement = MaintenancePopup.#buildTagElement(tagName); + MaintenancePopup.#markTagAsInvalid(tagElement); + tagElement.classList.add('is-present'); + + this.#suggestedInvalidTags.set(tagName, tagElement); + + if (firstTagInList && firstTagInList.isConnected) { + this.#tagsListElement.insertBefore(tagElement, firstTagInList); + } else { + this.#tagsListElement.appendChild(tagElement); + } + } + } + } + /** * @return {boolean} */ @@ -248,6 +320,15 @@ export class MaintenancePopup extends BaseComponent { return tagElement; } + /** + * Marks the tag with red color. + * @param {HTMLElement} tagElement Element to mark. + */ + static #markTagAsInvalid(tagElement) { + tagElement.dataset.tagCategory = 'error'; + tagElement.setAttribute('data-tag-category', 'error'); + } + /** * Controller with maintenance settings. * @type {MaintenanceSettings} From 71039ee65772d09fa953d0e21412a71ff4254d56 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sun, 8 Dec 2024 22:57:49 +0400 Subject: [PATCH 08/10] Adding default theme colors for categories --- src/styles/colors.scss | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/styles/colors.scss b/src/styles/colors.scss index b6beafa..37e5039 100644 --- a/src/styles/colors.scss +++ b/src/styles/colors.scss @@ -25,6 +25,27 @@ $tag-background: #1b3c21; $tag-count-background: #2d6236; $tag-text: #4aa158; +$tag-rating-text: #418dd9; +$tag-rating-background: darken($tag-rating-text, 35%); +$tag-spoiler-text: #d49b39; +$tag-spoiler-background: darken($tag-spoiler-text, 34%); +$tag-origin-text: #6f66d6; +$tag-origin-background: darken($tag-origin-text, 40%); +$tag-oc-text: #b157b7; +$tag-oc-background: darken($tag-oc-text, 33%); +$tag-error-text: #d45460; +$tag-error-background: desaturate(darken($tag-error-text, 38%), 6%); +$tag-character-text: #4aaabf; +$tag-character-background: darken($tag-character-text, 33%); +$tag-content-official-text: #b9b541; +$tag-content-official-background: desaturate(darken($tag-content-official-text, 29%),2%); +$tag-content-fanmade-text: #cc8eb5; +$tag-content-fanmade-background: darken($tag-content-fanmade-text, 40%); +$tag-species-text: #b16b50; +$tag-species-background: darken($tag-species-text, 35%); +$tag-body-type-text: #b8b8b8; +$tag-body-type-background: desaturate(darken($tag-body-type-text, 35%), 10%); + $input-background: #26232d; $input-border: #5c5a61; From c4f00c4905791e78ff0f7b39a5d870454044419a Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sat, 14 Dec 2024 22:04:31 +0400 Subject: [PATCH 09/10] Fixed hover colors for error-tags --- src/styles/content/listing.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/styles/content/listing.scss b/src/styles/content/listing.scss index 54b4496..37804f5 100644 --- a/src/styles/content/listing.scss +++ b/src/styles/content/listing.scss @@ -70,6 +70,11 @@ color: colors.$tag-background; } + &[data-tag-category=error]:hover { + background: colors.$tag-error-text; + color: colors.$tag-error-background; + } + &.is-missing:not(.is-added), &.is-present.is-removed { opacity: 0.5; From f58c4aa8187ad3d25bbb218906e6743931d8ba03 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 16 Dec 2024 16:41:05 +0400 Subject: [PATCH 10/10] Bumped version to 0.3.4 --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manifest.json b/manifest.json index d547d94..d60403a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "Furbooru Tagging Assistant", "description": "Experimental extension with a set of tools to make the tagging faster and easier. Made specifically for Furbooru.", - "version": "0.3.3", + "version": "0.3.4", "browser_specific_settings": { "gecko": { "id": "furbooru-tagging-assistant@thecore.city" diff --git a/package-lock.json b/package-lock.json index 50cf5af..a1890af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "furbooru-tagging-assistant", - "version": "0.3.3", + "version": "0.3.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "furbooru-tagging-assistant", - "version": "0.3.3", + "version": "0.3.4", "dependencies": { "@fortawesome/fontawesome-free": "^6.7.1", "lz-string": "^1.5.0" diff --git a/package.json b/package.json index 0817b99..01e162d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "furbooru-tagging-assistant", - "version": "0.3.3", + "version": "0.3.4", "private": true, "scripts": { "build": "npm run build:popup && npm run build:extension",