From 345b0c26a397e3280af4b938f049aca70792d794 Mon Sep 17 00:00:00 2001 From: YazanAmmar <193124783+YazanAmmar@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:09:41 +0300 Subject: [PATCH] chore: apply prettier + normalize line endings --- .gitattributes | 1 + .prettierrc | 7 + src/commands/index.ts | 60 +- src/constants.ts | 374 ++--- src/i18n/locales/ar.ts | 932 ++++++------ src/i18n/locales/en.ts | 948 ++++++------ src/i18n/locales/fa.ts | 940 ++++++------ src/i18n/locales/fr.ts | 861 ++++++----- src/i18n/strings.ts | 30 +- src/i18n/types.ts | 16 +- src/main.ts | 668 ++++----- src/styles/_components.scss | 26 +- src/styles/_modals.scss | 30 +- src/styles/_settings.scss | 10 +- src/styles/main.scss | 8 +- src/types.ts | 6 +- src/ui/components/color-pickers.ts | 320 ++--- src/ui/components/import-export.ts | 62 +- src/ui/components/like-plugin-card.ts | 124 +- src/ui/components/options-section.ts | 163 +-- src/ui/components/profile-manager.ts | 158 +- src/ui/components/snippets-ui.ts | 164 +-- src/ui/modals.ts | 1902 +++++++++++-------------- src/ui/settingsTab.ts | 457 +++--- src/utils.ts | 98 +- 25 files changed, 3765 insertions(+), 4600 deletions(-) create mode 100644 .gitattributes create mode 100644 .prettierrc diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..2214f5e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "printWidth": 100, + "endOfLine": "lf" +} diff --git a/src/commands/index.ts b/src/commands/index.ts index f174b4a..9a80b2c 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,6 +1,6 @@ -import { Notice, App } from "obsidian"; -import { t } from "../i18n/strings"; -import type ColorMaster from "../main"; +import { Notice, App } from 'obsidian'; +import { t } from '../i18n/strings'; +import type ColorMaster from '../main'; interface AppWithSetting extends App { setting: { @@ -16,45 +16,43 @@ interface AppWithSetting extends App { export function registerCommands(plugin: ColorMaster) { // Toggle plugin on/off plugin.addCommand({ - id: "toggle", - name: t("commands.enableDisable"), + id: 'toggle', + name: t('commands.enableDisable'), callback: async () => { plugin.settings.pluginEnabled = !plugin.settings.pluginEnabled; await plugin.saveSettings(); new Notice( - plugin.settings.pluginEnabled - ? t("notices.pluginEnabled") - : t("notices.pluginDisabled"), + plugin.settings.pluginEnabled ? t('notices.pluginEnabled') : t('notices.pluginDisabled'), ); }, }); // Select next profile plugin.addCommand({ - id: "profile-next", - name: t("commands.cycleNext"), + id: 'profile-next', + name: t('commands.cycleNext'), callback: async () => { const names = Object.keys(plugin.settings.profiles || {}); if (names.length === 0) { - new Notice(t("notices.noProfilesFound")); + new Notice(t('notices.noProfilesFound')); return; } const idx = names.indexOf(plugin.settings.activeProfile); const next = names[(idx + 1) % names.length]; plugin.settings.activeProfile = next; await plugin.saveSettings(); - new Notice(t("notices.activeProfileSwitched", next)); + new Notice(t('notices.activeProfileSwitched', next)); }, }); // Select previous profile plugin.addCommand({ - id: "profile-prev", - name: t("commands.cyclePrevious"), + id: 'profile-prev', + name: t('commands.cyclePrevious'), callback: async () => { const names = Object.keys(plugin.settings.profiles || {}); if (names.length === 0) { - new Notice(t("notices.noProfilesFound")); + new Notice(t('notices.noProfilesFound')); return; } const currentIndex = names.indexOf(plugin.settings.activeProfile); @@ -63,14 +61,14 @@ export function registerCommands(plugin: ColorMaster) { plugin.settings.activeProfile = previousProfile; await plugin.saveSettings(); - new Notice(t("notices.activeProfileSwitched", previousProfile)); + new Notice(t('notices.activeProfileSwitched', previousProfile)); }, }); // Open plugin settings plugin.addCommand({ - id: "open-settings", - name: t("commands.openSettings"), + id: 'open-settings', + name: t('commands.openSettings'), callback: () => { const app = plugin.app as AppWithSetting; app.setting.open(); @@ -80,30 +78,30 @@ export function registerCommands(plugin: ColorMaster) { // Toggle active profile theme plugin.addCommand({ - id: "toggle-theme", - name: t("commands.toggleTheme"), + id: 'toggle-theme', + name: t('commands.toggleTheme'), callback: async () => { const activeProfileName = plugin.settings.activeProfile; const activeProfile = plugin.settings.profiles[activeProfileName]; if (!activeProfile) { - new Notice(t("notices.profileNotFound")); + new Notice(t('notices.profileNotFound')); return; } - const currentTheme = activeProfile.themeType || "auto"; - let nextTheme: "auto" | "dark" | "light"; + const currentTheme = activeProfile.themeType || 'auto'; + let nextTheme: 'auto' | 'dark' | 'light'; let noticeMessage: string; - if (currentTheme === "light") { - nextTheme = "dark"; - noticeMessage = t("notices.themeSwitchedDark"); - } else if (currentTheme === "dark") { - nextTheme = "auto"; - noticeMessage = t("notices.themeSwitchedAuto"); + if (currentTheme === 'light') { + nextTheme = 'dark'; + noticeMessage = t('notices.themeSwitchedDark'); + } else if (currentTheme === 'dark') { + nextTheme = 'auto'; + noticeMessage = t('notices.themeSwitchedAuto'); } else { - nextTheme = "light"; - noticeMessage = t("notices.themeSwitchedLight"); + nextTheme = 'light'; + noticeMessage = t('notices.themeSwitchedLight'); } activeProfile.themeType = nextTheme; diff --git a/src/constants.ts b/src/constants.ts index 14f8aaf..edad476 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,90 +1,90 @@ -import { PluginSettings } from "./types"; +import { PluginSettings } from './types'; export const DEFAULT_VARS = { - "Plugin Integrations": { - "--iconize-icon-color": "#00aaff", + 'Plugin Integrations': { + '--iconize-icon-color': '#00aaff', }, Backgrounds: { - "--background-primary": "#1e1e1e", - "--background-primary-alt": "#252525", - "--background-secondary": "#2a2a2a", - "--background-secondary-alt": "#303030", - "--background-modifier-border": "#444444", - "--background-modifier-border-hover": "#555555", - "--background-modifier-border-focus": "#007acc", - "--background-modifier-flair": "#3a3a3a", - "--background-modifier-hover": "#3a3a3a", - "--background-modifier-active": "#4a4a4a", + '--background-primary': '#1e1e1e', + '--background-primary-alt': '#252525', + '--background-secondary': '#2a2a2a', + '--background-secondary-alt': '#303030', + '--background-modifier-border': '#444444', + '--background-modifier-border-hover': '#555555', + '--background-modifier-border-focus': '#007acc', + '--background-modifier-flair': '#3a3a3a', + '--background-modifier-hover': '#3a3a3a', + '--background-modifier-active': '#4a4a4a', }, Text: { - "--text-normal": "#e0e0e0", - "--text-muted": "#999999", - "--text-faint": "#666666", - "--text-on-accent": "#ffffff", - "--text-accent": "#00aaff", - "--text-accent-hover": "#33bbff", - "--text-selection": "#007acc66", - "--h1-color": "#e0e0e0", - "--h2-color": "#d0d0d0", - "--h3-color": "#c0c0c0", - "--h4-color": "#b0b0b0", - "--h5-color": "#a0a0a0", - "--h6-color": "#909090", + '--text-normal': '#e0e0e0', + '--text-muted': '#999999', + '--text-faint': '#666666', + '--text-on-accent': '#ffffff', + '--text-accent': '#00aaff', + '--text-accent-hover': '#33bbff', + '--text-selection': '#007acc66', + '--h1-color': '#e0e0e0', + '--h2-color': '#d0d0d0', + '--h3-color': '#c0c0c0', + '--h4-color': '#b0b0b0', + '--h5-color': '#a0a0a0', + '--h6-color': '#909090', }, Markdown: { - "--hr-color": "#444444", - "--blockquote-border-color": "#007acc", - "--blockquote-color": "#d0d0d0", - "--blockquote-bg": "transparent", - "--tag-color": "#33bbff", - "--tag-color-hover": "#33bbff", - "--tag-bg": "#1e1e1e", - "--checklist-done-color": "#33bbff", - "--code-normal": "#e0e0e0", - "--code-background": "#2d2d2d", - "--text-highlight-bg": "#2c6887", + '--hr-color': '#444444', + '--blockquote-border-color': '#007acc', + '--blockquote-color': '#d0d0d0', + '--blockquote-bg': 'transparent', + '--tag-color': '#33bbff', + '--tag-color-hover': '#33bbff', + '--tag-bg': '#1e1e1e', + '--checklist-done-color': '#33bbff', + '--code-normal': '#e0e0e0', + '--code-background': '#2d2d2d', + '--text-highlight-bg': '#2c6887', }, Notices: { - "--cm-notice-text-default": "#e0e0e0", - "--cm-notice-bg-default": "#2a2a2a", + '--cm-notice-text-default': '#e0e0e0', + '--cm-notice-bg-default': '#2a2a2a', }, - "Interactive Elements": { - "--interactive-normal": "#5a5a5a", - "--interactive-hover": "#6a6a6a", - "--interactive-accent": "#007acc", - "--interactive-accent-hover": "#3399dd", - "--interactive-success": "#28a745", - "--interactive-error": "#dc3545", - "--interactive-warning": "#ffc107", + 'Interactive Elements': { + '--interactive-normal': '#5a5a5a', + '--interactive-hover': '#6a6a6a', + '--interactive-accent': '#007acc', + '--interactive-accent-hover': '#3399dd', + '--interactive-success': '#28a745', + '--interactive-error': '#dc3545', + '--interactive-warning': '#ffc107', }, - "UI Elements": { - "--titlebar-background": "#252525", - "--titlebar-background-focused": "#303030", - "--titlebar-text-color": "#e0e0e0", - "--sidebar-background": "#252525", - "--sidebar-border-color": "#333333", - "--header-background": "#2a2a2a", - "--header-border-color": "#333333", - "--vault-name-color": "#e0e0e0", + 'UI Elements': { + '--titlebar-background': '#252525', + '--titlebar-background-focused': '#303030', + '--titlebar-text-color': '#e0e0e0', + '--sidebar-background': '#252525', + '--sidebar-border-color': '#333333', + '--header-background': '#2a2a2a', + '--header-border-color': '#333333', + '--vault-name-color': '#e0e0e0', }, - "Graph View": { - "--graph-line": "#444444", - "--graph-node": "#999999", - "--graph-text": "#e0e0e0", - "--graph-node-unresolved": "#dc3545", - "--graph-node-focused": "#ffffff", - "--graph-node-tag": "#86efac", - "--graph-node-attachment": "#60a5fa", + 'Graph View': { + '--graph-line': '#444444', + '--graph-node': '#999999', + '--graph-text': '#e0e0e0', + '--graph-node-unresolved': '#dc3545', + '--graph-node-focused': '#ffffff', + '--graph-node-tag': '#86efac', + '--graph-node-attachment': '#60a5fa', }, Misc: { - "--scrollbar-thumb-bg": "#444444", - "--scrollbar-bg": "#2a2a2a", - "--divider-color": "#444444", + '--scrollbar-thumb-bg': '#444444', + '--scrollbar-bg': '#2a2a2a', + '--divider-color': '#444444', }, }; export const DEFAULT_PROFILE = { vars: {}, - themeType: "auto", + themeType: 'auto', snippets: [], convertImagesToJpg: false, jpgQuality: 85, @@ -92,55 +92,55 @@ export const DEFAULT_PROFILE = { const OLED_MATRIX_PROFILE = { vars: { - "--iconize-icon-color": "#00ff00", - "--background-primary": "#000000", - "--background-primary-alt": "#0a0a0a", - "--background-secondary": "#050505", - "--background-secondary-alt": "#101010", - "--background-modifier-border": "#223322", - "--background-modifier-border-hover": "#44ff44", - "--background-modifier-border-focus": "#00ff00", - "--background-modifier-flair": "#111111", - "--background-modifier-hover": "#1a1a1a", - "--background-modifier-active": "#002a00", - "--text-normal": "#f0f0f0", - "--text-muted": "#666666", - "--text-faint": "#444444", - "--text-on-accent": "#000000", - "--text-accent": "#00ff00", - "--text-accent-hover": "#88ff88", - "--text-selection": "#00ff004d", - "--text-highlight-bg": "#00640080", - "--interactive-normal": "#1c1c1c", - "--interactive-hover": "#2c2c2c", - "--interactive-accent": "#008a00", - "--interactive-accent-hover": "#00ff00", - "--interactive-success": "#28a745", - "--interactive-error": "#dc3545", - "--interactive-warning": "#ffc107", - "--titlebar-background": "#000000", - "--titlebar-background-focused": "#000000", - "--titlebar-text-color": "#f0f0f0", - "--sidebar-background": "#000000", - "--sidebar-border-color": "#002a00", - "--header-background": "#050505", - "--header-border-color": "#223322", - "--vault-name-color": "#00ff00", - "--scrollbar-thumb-bg": "#222222", - "--scrollbar-bg": "#000000", - "--divider-color": "#223322", - "--checklist-done-color": "#00ff00", - "--graph-line": "#000000", - "--graph-node": "#00ff00", - "--graph-text": "#ffffff", - "--graph-node-unresolved": "#00ff00", - "--graph-node-focused": "#000000", - "--graph-node-tag": "#000000", - "--graph-node-attachment": "#54ee20", - "--blockquote-color": "#54ee20", - "--tag-color": "#0bd62d", + '--iconize-icon-color': '#00ff00', + '--background-primary': '#000000', + '--background-primary-alt': '#0a0a0a', + '--background-secondary': '#050505', + '--background-secondary-alt': '#101010', + '--background-modifier-border': '#223322', + '--background-modifier-border-hover': '#44ff44', + '--background-modifier-border-focus': '#00ff00', + '--background-modifier-flair': '#111111', + '--background-modifier-hover': '#1a1a1a', + '--background-modifier-active': '#002a00', + '--text-normal': '#f0f0f0', + '--text-muted': '#666666', + '--text-faint': '#444444', + '--text-on-accent': '#000000', + '--text-accent': '#00ff00', + '--text-accent-hover': '#88ff88', + '--text-selection': '#00ff004d', + '--text-highlight-bg': '#00640080', + '--interactive-normal': '#1c1c1c', + '--interactive-hover': '#2c2c2c', + '--interactive-accent': '#008a00', + '--interactive-accent-hover': '#00ff00', + '--interactive-success': '#28a745', + '--interactive-error': '#dc3545', + '--interactive-warning': '#ffc107', + '--titlebar-background': '#000000', + '--titlebar-background-focused': '#000000', + '--titlebar-text-color': '#f0f0f0', + '--sidebar-background': '#000000', + '--sidebar-border-color': '#002a00', + '--header-background': '#050505', + '--header-border-color': '#223322', + '--vault-name-color': '#00ff00', + '--scrollbar-thumb-bg': '#222222', + '--scrollbar-bg': '#000000', + '--divider-color': '#223322', + '--checklist-done-color': '#00ff00', + '--graph-line': '#000000', + '--graph-node': '#00ff00', + '--graph-text': '#ffffff', + '--graph-node-unresolved': '#00ff00', + '--graph-node-focused': '#000000', + '--graph-node-tag': '#000000', + '--graph-node-attachment': '#54ee20', + '--blockquote-color': '#54ee20', + '--tag-color': '#0bd62d', }, - themeType: "dark", + themeType: 'dark', snippets: [], convertImagesToJpg: false, jpgQuality: 85, @@ -148,53 +148,53 @@ const OLED_MATRIX_PROFILE = { const CITRUS_ZEST_PROFILE = { vars: { - "--iconize-icon-color": "#FF8C00", - "--background-primary": "#F5F5F5", - "--background-primary-alt": "#f2eded", - "--background-secondary": "#f2eded", - "--background-secondary-alt": "#F0F0F0", - "--background-modifier-border": "#E0E0E0", - "--background-modifier-border-hover": "#FF8C00", - "--background-modifier-border-focus": "#F57C00", - "--background-modifier-flair": "#FFFFFF", - "--background-modifier-hover": "#EEEEEE", - "--background-modifier-active": "#E0E0E0", - "--text-normal": "#3c3434", - "--text-muted": "#ff7300", - "--text-faint": "#ff7b00", - "--text-on-accent": "#FFFFFF", - "--text-accent": "#F57C00", - "--text-accent-hover": "#FF9800", - "--text-selection": "#f27602", - "--text-highlight-bg": "#E0E0E0", - "--interactive-normal": "#F5F5F5", - "--interactive-hover": "#EEEEEE", - "--interactive-accent": "#FF8C00", - "--interactive-accent-hover": "#FFA726", - "--interactive-success": "#4CAF50", - "--interactive-error": "#F44336", - "--interactive-warning": "#FFC107", - "--titlebar-background": "#FFFFFF", - "--titlebar-background-focused": "#F7F7F7", - "--titlebar-text-color": "#e0e0e0", - "--sidebar-background": "#FFFFFF", - "--sidebar-border-color": "#E0E0E0", - "--header-background": "#FDFDFD", - "--header-border-color": "#E0E0E0", - "--vault-name-color": "#F57C00", - "--scrollbar-thumb-bg": "#BDBDBD", - "--scrollbar-bg": "#F5F5F5", - "--divider-color": "#E0E0E0", - "--checklist-done-color": "#757575", - "--graph-line": "#F57C00", - "--graph-node": "#999999", - "--graph-text": "#e0e0e0", - "--graph-node-unresolved": "#F57C00", - "--graph-node-focused": "#ffffff", - "--graph-node-tag": "#F57C00", - "--graph-node-attachment": "#F57C00", + '--iconize-icon-color': '#FF8C00', + '--background-primary': '#F5F5F5', + '--background-primary-alt': '#f2eded', + '--background-secondary': '#f2eded', + '--background-secondary-alt': '#F0F0F0', + '--background-modifier-border': '#E0E0E0', + '--background-modifier-border-hover': '#FF8C00', + '--background-modifier-border-focus': '#F57C00', + '--background-modifier-flair': '#FFFFFF', + '--background-modifier-hover': '#EEEEEE', + '--background-modifier-active': '#E0E0E0', + '--text-normal': '#3c3434', + '--text-muted': '#ff7300', + '--text-faint': '#ff7b00', + '--text-on-accent': '#FFFFFF', + '--text-accent': '#F57C00', + '--text-accent-hover': '#FF9800', + '--text-selection': '#f27602', + '--text-highlight-bg': '#E0E0E0', + '--interactive-normal': '#F5F5F5', + '--interactive-hover': '#EEEEEE', + '--interactive-accent': '#FF8C00', + '--interactive-accent-hover': '#FFA726', + '--interactive-success': '#4CAF50', + '--interactive-error': '#F44336', + '--interactive-warning': '#FFC107', + '--titlebar-background': '#FFFFFF', + '--titlebar-background-focused': '#F7F7F7', + '--titlebar-text-color': '#e0e0e0', + '--sidebar-background': '#FFFFFF', + '--sidebar-border-color': '#E0E0E0', + '--header-background': '#FDFDFD', + '--header-border-color': '#E0E0E0', + '--vault-name-color': '#F57C00', + '--scrollbar-thumb-bg': '#BDBDBD', + '--scrollbar-bg': '#F5F5F5', + '--divider-color': '#E0E0E0', + '--checklist-done-color': '#757575', + '--graph-line': '#F57C00', + '--graph-node': '#999999', + '--graph-text': '#e0e0e0', + '--graph-node-unresolved': '#F57C00', + '--graph-node-focused': '#ffffff', + '--graph-node-tag': '#F57C00', + '--graph-node-attachment': '#F57C00', }, - themeType: "light", + themeType: 'light', snippets: [], convertImagesToJpg: false, jpgQuality: 85, @@ -202,14 +202,14 @@ const CITRUS_ZEST_PROFILE = { export const BUILT_IN_PROFILES_VARS = { Default: DEFAULT_PROFILE.vars, - "OLED Matrix": OLED_MATRIX_PROFILE.vars, - "Citrus Zest": CITRUS_ZEST_PROFILE.vars, + 'OLED Matrix': OLED_MATRIX_PROFILE.vars, + 'Citrus Zest': CITRUS_ZEST_PROFILE.vars, }; export const BUILT_IN_PROFILES_DATA = { Default: DEFAULT_PROFILE, - "OLED Matrix": OLED_MATRIX_PROFILE, - "Citrus Zest": CITRUS_ZEST_PROFILE, + 'OLED Matrix': OLED_MATRIX_PROFILE, + 'Citrus Zest': CITRUS_ZEST_PROFILE, }; export const DEFAULT_SETTINGS: PluginSettings = { @@ -220,12 +220,12 @@ export const DEFAULT_SETTINGS: PluginSettings = { deleteSettings: false, }, pluginEnabled: true, - language: "auto", + language: 'auto', useRtlLayout: true, overrideIconizeColors: true, cleanupInterval: 5, colorUpdateFPS: 10, - activeProfile: "Default", + activeProfile: 'Default', profiles: JSON.parse(JSON.stringify(BUILT_IN_PROFILES_DATA)), globalSnippets: [], pinnedSnapshots: {}, @@ -233,25 +233,25 @@ export const DEFAULT_SETTINGS: PluginSettings = { // Maps text color variables to their common backgrounds for contrast checking. export const TEXT_TO_BG_MAP = { - "--text-normal": "--background-primary", - "--text-muted": "--background-primary", - "--text-faint": "--background-primary", - "--text-accent": "--background-primary", - "--text-accent-hover": "--background-primary", - "--h1-color": "--background-primary", - "--h2-color": "--background-primary", - "--h3-color": "--background-primary", - "--h4-color": "--background-primary", - "--h5-color": "--background-primary", - "--h6-color": "--background-primary", - "--blockquote-color": "--background-primary", - "--text-on-accent": "--interactive-accent", - "--vault-name-color": "--sidebar-background", - "--titlebar-text-color": "--titlebar-background", - "--graph-text": "--graph-node", - "--checklist-done-color": "--background-primary", - "--text-highlight-bg": "--text-normal", - "--tag-color": "--tag-bg", + '--text-normal': '--background-primary', + '--text-muted': '--background-primary', + '--text-faint': '--background-primary', + '--text-accent': '--background-primary', + '--text-accent-hover': '--background-primary', + '--h1-color': '--background-primary', + '--h2-color': '--background-primary', + '--h3-color': '--background-primary', + '--h4-color': '--background-primary', + '--h5-color': '--background-primary', + '--h6-color': '--background-primary', + '--blockquote-color': '--background-primary', + '--text-on-accent': '--interactive-accent', + '--vault-name-color': '--sidebar-background', + '--titlebar-text-color': '--titlebar-background', + '--graph-text': '--graph-node', + '--checklist-done-color': '--background-primary', + '--text-highlight-bg': '--text-normal', + '--tag-color': '--tag-bg', // "--iconize-icon-color": "--background-secondary", // "--cm-notice-text-default": "--cm-notice-bg-default", }; diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 59f88cb..1d60728 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -1,638 +1,588 @@ export default { plugin: { - name: "متحكم الألوان - v1.2.0", - ribbonTooltip: "إعدادات Color Master", + name: 'متحكم الألوان - v1.2.0', + ribbonTooltip: 'إعدادات Color Master', }, buttons: { - new: "جديد", - delete: "حذف", - selectOption: "اختر خياراً واحداً عالأقل", - create: "إنشاء", - reset: "إعادة تعيين", - update: "تحديث", - apply: "تطبيق", - cancel: "إلغاء", - reload: "إعادة تحميل", - exportFile: "تصدير ملف", - copyJson: "نسخ JSON", - importCss: "استيراد / لصق (.css)", - importJson: "استيراد / لصق (.json)", - chooseFile: "اختر ملف...", - import: "استيراد", - select: "اختيار", - browse: "استعراض...", - deleteAnyway: "حذف على أي حال", - restore: "استعادة", + new: 'جديد', + delete: 'حذف', + selectOption: 'اختر خياراً واحداً عالأقل', + create: 'إنشاء', + reset: 'إعادة تعيين', + update: 'تحديث', + apply: 'تطبيق', + cancel: 'إلغاء', + reload: 'إعادة تحميل', + exportFile: 'تصدير ملف', + copyJson: 'نسخ JSON', + importCss: 'استيراد / لصق (.css)', + importJson: 'استيراد / لصق (.json)', + chooseFile: 'اختر ملف...', + import: 'استيراد', + select: 'اختيار', + browse: 'استعراض...', + deleteAnyway: 'حذف على أي حال', + restore: 'استعادة', }, settings: { - enablePlugin: "تفعيل متحكم الألوان", + enablePlugin: 'تفعيل متحكم الألوان', enablePluginDesc: - "أطفئ هذا الخيار لتعطيل جميع الألوان المخصصة مؤقتاً والعودة إلى ثيم Obsidian النشط.", - language: "اللغة", - languageDesc: "اختر لغة واجهة الإضافة.", - languageSettingsModalTitle: "إعدادات اللغة", - rtlLayoutName: "تفعيل تنسيق اليمين لليسار (RTL)", + 'أطفئ هذا الخيار لتعطيل جميع الألوان المخصصة مؤقتاً والعودة إلى ثيم Obsidian النشط.', + language: 'اللغة', + languageDesc: 'اختر لغة واجهة الإضافة.', + languageSettingsModalTitle: 'إعدادات اللغة', + rtlLayoutName: 'تفعيل تنسيق اليمين لليسار (RTL)', rtlLayoutDesc: - "عند تفعيله، يتم قلب واجهة الإضافة لتتناسب مع اللغات التي تكتب من اليمين لليسار.", - searchPlaceholder: "ابحث عن المتغيرات (بالاسم أو القيمة)...", - regexPlaceholder: "أدخل التعبير النمطي واضغط Enter...", - allSections: "كل الأقسام", - clear: "تنظيف", - ariaCase: "بحث حساس لحالة الأحرف", - ariaRegex: "استخدام التعابير النمطية (Regex)", + 'عند تفعيله، يتم قلب واجهة الإضافة لتتناسب مع اللغات التي تكتب من اليمين لليسار.', + searchPlaceholder: 'ابحث عن المتغيرات (بالاسم أو القيمة)...', + regexPlaceholder: 'أدخل التعبير النمطي واضغط Enter...', + allSections: 'كل الأقسام', + clear: 'تنظيف', + ariaCase: 'بحث حساس لحالة الأحرف', + ariaRegex: 'استخدام التعابير النمطية (Regex)', themeWarningTooltip: (currentTheme: string) => `الثيم "${currentTheme}" مطبّق حالياً، وقد يتعارض مع مظهر الملف الشخصي. للحصول على أفضل النتائج، ننصح بالتبديل إلى الثيم الافتراضي لأوبسيديان، أو يمكنك استيراد ثيم "${currentTheme}" كملف شخصي جديد لتعديله مباشرةً.`, }, profileManager: { - heading: "إدارة الملفّات الشخصيّة", - activeProfile: "الملف الشخصي النشط", - activeProfileDesc: "تنقل بين الملفّات الشخصيّة أو أنشئ واحد جديد.", - themeType: "نوع ثيم الملف الشخصي", - themeTypeDesc: - "حدد ما إذا كانت هذا الملف الشخصي سيفرض ثيماً معيناً (غامق/فاتح) عند تفعيله.", - themeAuto: "الثيم الافتراضي لأوبسيديان", - themeDark: "فرض الوضع الغامق", - themeLight: "فرض الوضع الفاتح", - tooltipThemeAuto: "الثيم: تلقائي (يتبع أوبسيديان) (اضغط للتبديل للفاتح)", - tooltipThemeDark: "الثيم: فرض الوضع الغامق (اضغط للتبديل للتلقائي)", - tooltipThemeLight: "الثيم: فرض الوضع الفاتح (اضغط للتبديل للغامق)", - tooltipExport: "تصدير الملف الشخصي الحالي كملف JSON", - tooltipCopyJson: "نسخ JSON للملف الشخصي الحالي إلى الحافظة", + heading: 'إدارة الملفّات الشخصيّة', + activeProfile: 'الملف الشخصي النشط', + activeProfileDesc: 'تنقل بين الملفّات الشخصيّة أو أنشئ واحد جديد.', + themeType: 'نوع ثيم الملف الشخصي', + themeTypeDesc: 'حدد ما إذا كانت هذا الملف الشخصي سيفرض ثيماً معيناً (غامق/فاتح) عند تفعيله.', + themeAuto: 'الثيم الافتراضي لأوبسيديان', + themeDark: 'فرض الوضع الغامق', + themeLight: 'فرض الوضع الفاتح', + tooltipThemeAuto: 'الثيم: تلقائي (يتبع أوبسيديان) (اضغط للتبديل للفاتح)', + tooltipThemeDark: 'الثيم: فرض الوضع الغامق (اضغط للتبديل للتلقائي)', + tooltipThemeLight: 'الثيم: فرض الوضع الفاتح (اضغط للتبديل للغامق)', + tooltipExport: 'تصدير الملف الشخصي الحالي كملف JSON', + tooltipCopyJson: 'نسخ JSON للملف الشخصي الحالي إلى الحافظة', }, options: { - heading: "إعدادات متقدمة", - liveUpdateName: "معدل التحديث المباشر (إطار بالثانية)", + heading: 'إعدادات متقدمة', + liveUpdateName: 'معدل التحديث المباشر (إطار بالثانية)', liveUpdateDesc: - "يحدد عدد مرات تحديث معاينة الألوان في الثانية أثناء السحب (0 = تعطيل المعاينة). القيم المنخفضة تحسن الأداء.", - iconizeModalTitle: "إعدادات تكامل Iconize", - overrideIconizeName: "تجاوز ألوان إضافة Iconize", + 'يحدد عدد مرات تحديث معاينة الألوان في الثانية أثناء السحب (0 = تعطيل المعاينة). القيم المنخفضة تحسن الأداء.', + iconizeModalTitle: 'إعدادات تكامل Iconize', + overrideIconizeName: 'تجاوز ألوان إضافة Iconize', overrideIconizeDesc: - "اسمح لـ Color Master بالتحكم في كل ألوان أيقونات Iconize. لأفضل النتائج، قم بتعطيل إعدادات الألوان في إضافة Iconize نفسها.", - cleanupIntervalName: "فترة التنظيف", + 'اسمح لـ Color Master بالتحكم في كل ألوان أيقونات Iconize. لأفضل النتائج، قم بتعطيل إعدادات الألوان في إضافة Iconize نفسها.', + cleanupIntervalName: 'فترة التنظيف', cleanupIntervalDesc: - "تحدد عدد المرات (بالثواني) التي تتحقق فيها الإضافة من إلغاء تثبيت إضافة Iconize لتنظيف أيقوناتها.", - addCustomVarName: "إضافة متغير مخصص", + 'تحدد عدد المرات (بالثواني) التي تتحقق فيها الإضافة من إلغاء تثبيت إضافة Iconize لتنظيف أيقوناتها.', + addCustomVarName: 'إضافة متغير مخصص', addCustomVarDesc: "أضف متغير CSS جديد غير موجود بالقائمة الافتراضية. يجب أن يبدأ الاسم بـ '--'.", - addNewVarButton: "إضافة متغير جديد...", - resetPluginName: "إعادة تعيين الإضافة", + addNewVarButton: 'إضافة متغير جديد...', + resetPluginName: 'إعادة تعيين الإضافة', resetPluginDesc: - "سيؤدي هذا إلى حذف جميع الملفات الشخصية، والمقتطفات، والإعدادات، والخلفيات، وإعادة ضبط الإضافة إلى حالتها الأصلية. يتطلب هذا الإجراء إعادة تحميل التطبيق، ولا يمكن التراجع عنه.", - resetPluginButton: "إعادة تعيين كل البيانات...", - backgroundName: "تعيين خلفية مخصصة", - backgroundDesc: "إدارة صورة أو فيديو الخلفية لهذا الملف الشخصي.", - backgroundModalSettingsTitle: "إعدادات الخلفية", - backgroundEnableName: "تفعيل الخلفية", - backgroundEnableDesc: - "التحكم بظهور أو إخفاء الخلفية المخصصة لهذا الملف الشخصي.", - convertImagesName: "تحويل الصور إلى JPG", + 'سيؤدي هذا إلى حذف جميع الملفات الشخصية، والمقتطفات، والإعدادات، والخلفيات، وإعادة ضبط الإضافة إلى حالتها الأصلية. يتطلب هذا الإجراء إعادة تحميل التطبيق، ولا يمكن التراجع عنه.', + resetPluginButton: 'إعادة تعيين كل البيانات...', + backgroundName: 'تعيين خلفية مخصصة', + backgroundDesc: 'إدارة صورة أو فيديو الخلفية لهذا الملف الشخصي.', + backgroundModalSettingsTitle: 'إعدادات الخلفية', + backgroundEnableName: 'تفعيل الخلفية', + backgroundEnableDesc: 'التحكم بظهور أو إخفاء الخلفية المخصصة لهذا الملف الشخصي.', + convertImagesName: 'تحويل الصور إلى JPG', convertImagesDesc: - "تحويل صور PNG أو WEBP أو BMP تلقائياً إلى JPG عند الرفع لتقليل حجم الملف وتحسين أداء التحميل. (ملاحظة: ستفقد الصور شفافيتها)", - jpgQualityName: "جودة JPG", - jpgQualityDesc: - "حدد جودة الضغط (من 1 إلى 100). القيم الأقل تعني ملفات أصغر ولكن بجودة أدنى.", - badgeNotInstalled: "غير مُثبّت", - videoOpacityName: "شفافية الفيديو", - videoOpacityDesc: "التحكم بشفافية خلفية الفيديو لتحسين قابلية القراءة.", - videoMuteName: "كتم الفيديو", - videoMuteDesc: "كتم صوت خلفية الفيديو. موصى به بشدة.", - settingType: "نوع الإعدادات", - settingTypeImage: "صورة", - settingTypeVideo: "فيديو", + 'تحويل صور PNG أو WEBP أو BMP تلقائياً إلى JPG عند الرفع لتقليل حجم الملف وتحسين أداء التحميل. (ملاحظة: ستفقد الصور شفافيتها)', + jpgQualityName: 'جودة JPG', + jpgQualityDesc: 'حدد جودة الضغط (من 1 إلى 100). القيم الأقل تعني ملفات أصغر ولكن بجودة أدنى.', + badgeNotInstalled: 'غير مُثبّت', + videoOpacityName: 'شفافية الفيديو', + videoOpacityDesc: 'التحكم بشفافية خلفية الفيديو لتحسين قابلية القراءة.', + videoMuteName: 'كتم الفيديو', + videoMuteDesc: 'كتم صوت خلفية الفيديو. موصى به بشدة.', + settingType: 'نوع الإعدادات', + settingTypeImage: 'صورة', + settingTypeVideo: 'فيديو', }, snippets: { - heading: "قصاصات CSS", - createButton: "إنشاء قصاصة جديدة", - noSnippetsDesc: "لم يتم إنشاء أي قصاصات CSS لهذا الملف الشخصي بعد.", - global: "قصاصة عامّة", - globalName: "حفظ كقصاصة عامة", - globalDesc: "القصاصة العامة يتم تطبيقها على جميع ملفاتك الشخصية.", + heading: 'قصاصات CSS', + createButton: 'إنشاء قصاصة جديدة', + noSnippetsDesc: 'لم يتم إنشاء أي قصاصات CSS لهذا الملف الشخصي بعد.', + global: 'قصاصة عامّة', + globalName: 'حفظ كقصاصة عامة', + globalDesc: 'القصاصة العامة يتم تطبيقها على جميع ملفاتك الشخصية.', }, categories: { - pluginintegrations: "تكامل الإضافات", - backgrounds: "الخلفيات", - text: "النصوص", - interactive: "العناصر التفاعلية", - ui: "عناصر الواجهة", - misc: "متنوع", - graph: "عرض الرسم البياني", - markdown: "عناصر الماركدوان", - notices: "الإشعارات", - custom: "المتغيرات المخصصة", - customDesc: "متغير مضاف من قبل المستخدم.", - helpTextPre: "لم تجد المتغير الذي تبحث عنه؟ ", - helpTextLink: "تصفح قائمة متغيرات CSS الرسمية في Obsidian.", + pluginintegrations: 'تكامل الإضافات', + backgrounds: 'الخلفيات', + text: 'النصوص', + interactive: 'العناصر التفاعلية', + ui: 'عناصر الواجهة', + misc: 'متنوع', + graph: 'عرض الرسم البياني', + markdown: 'عناصر الماركدوان', + notices: 'الإشعارات', + custom: 'المتغيرات المخصصة', + customDesc: 'متغير مضاف من قبل المستخدم.', + helpTextPre: 'لم تجد المتغير الذي تبحث عنه؟ ', + helpTextLink: 'تصفح قائمة متغيرات CSS الرسمية في Obsidian.', }, modals: { newProfile: { - title: "إنشاء ملف شخصي جديد", - nameLabel: "اسم الملف الشخصي", - namePlaceholder: "أدخل اسم الملف الشخصي...", + title: 'إنشاء ملف شخصي جديد', + nameLabel: 'اسم الملف الشخصي', + namePlaceholder: 'أدخل اسم الملف الشخصي...', }, deleteProfile: { - title: "حذف الملف الشخصي", + title: 'حذف الملف الشخصي', confirmation: (name: string) => `هل أنت متأكد من رغبتك في حذف الملف الشخصي "${name}"؟ لا يمكن التراجع عن هذا الإجراء.`, }, jsonImport: { - title: "لصق أو استيراد ملف JSON للملف شخصي", - desc1: "يمكنك لصق بيانات الملف الشخصي (JSON) في الصندوق أدناه.", + title: 'لصق أو استيراد ملف JSON للملف شخصي', + desc1: 'يمكنك لصق بيانات الملف الشخصي (JSON) في الصندوق أدناه.', placeholder: '{ "name": "...", "profile": { ... } }', - settingName: "استيراد من ملف", - settingDesc: - "أو، اختر ملف الملف الشخصي (.json) من جهاز الكمبيوتر الخاص بك.", - replaceActiveButton: "استبدال النشط", - createNewButton: "إنشاء جديدة", + settingName: 'استيراد من ملف', + settingDesc: 'أو، اختر ملف الملف الشخصي (.json) من جهاز الكمبيوتر الخاص بك.', + replaceActiveButton: 'استبدال النشط', + createNewButton: 'إنشاء جديدة', }, cssImport: { - title: "استيراد / لصق CSS وإنشاء ملف شخصي", - titleEdit: "تعديل الملف الشخصي", - note: "ملاحظة : كود CSS الذي تلصقه قد يؤثر على واجهة البرنامج، استخدم فقط الأكواد الموثوقة.", - importFromFile: "استيراد من ملف", - importFromFileDesc: "أو، اختر ملف (.css) من جهاز الكمبيوتر الخاص بك.", - importFromTheme: "استيراد من ثيم مثبّت", - importFromThemeDesc: "قم بتحميل CSS مباشرة من أحد الثيمات المثبتة لديك.", - noThemes: "لا يوجد ثيمات مثبتة", + title: 'استيراد / لصق CSS وإنشاء ملف شخصي', + titleEdit: 'تعديل الملف الشخصي', + note: 'ملاحظة : كود CSS الذي تلصقه قد يؤثر على واجهة البرنامج، استخدم فقط الأكواد الموثوقة.', + importFromFile: 'استيراد من ملف', + importFromFileDesc: 'أو، اختر ملف (.css) من جهاز الكمبيوتر الخاص بك.', + importFromTheme: 'استيراد من ثيم مثبّت', + importFromThemeDesc: 'قم بتحميل CSS مباشرة من أحد الثيمات المثبتة لديك.', + noThemes: 'لا يوجد ثيمات مثبتة', }, snippetEditor: { - title: "إنشاء قصاصة CSS جديدة", - titleEdit: "تعديل قصاصة CSS", - nameLabel: "اسم القصاصة", - namePlaceholder: "أدخل اسم القصاصة...", - importFromSnippet: "استيراد من قصاصة مثبتة", - importFromSnippetDesc: - "قم بتحميل CSS مباشرة من إحدى قصاصات Obsidian المثبتة لديك.", - noSnippets: "لا يوجد قصاصات مثبتة", - cssPlaceholder: "الصق كود CSS الخاص بك هنا...", + title: 'إنشاء قصاصة CSS جديدة', + titleEdit: 'تعديل قصاصة CSS', + nameLabel: 'اسم القصاصة', + namePlaceholder: 'أدخل اسم القصاصة...', + importFromSnippet: 'استيراد من قصاصة مثبتة', + importFromSnippetDesc: 'قم بتحميل CSS مباشرة من إحدى قصاصات Obsidian المثبتة لديك.', + noSnippets: 'لا يوجد قصاصات مثبتة', + cssPlaceholder: 'الصق كود CSS الخاص بك هنا...', }, confirmation: { - resetProfileTitle: "تأكيد استرجاع الملف الشخصي", + resetProfileTitle: 'تأكيد استرجاع الملف الشخصي', resetProfileDesc: - "هل أنت متأكد من رغبتك في استرجاع هذا الملف الشخصي لآخر لقطة تم تثبيتها؟ سيتم الكتابة فوق الألوان الحالية ولا يمكن التراجع عن هذا الإجراء.", + 'هل أنت متأكد من رغبتك في استرجاع هذا الملف الشخصي لآخر لقطة تم تثبيتها؟ سيتم الكتابة فوق الألوان الحالية ولا يمكن التراجع عن هذا الإجراء.', deleteSnippetTitle: (name: string) => `حذف القصاصة: ${name}`, deleteSnippetDesc: - "هل أنت متأكد من رغبتك في حذف هذه القصاصة؟ لا يمكن التراجع عن هذا الإجراء.", - resetPluginTitle: "هل أنت متأكد؟", + 'هل أنت متأكد من رغبتك في حذف هذه القصاصة؟ لا يمكن التراجع عن هذا الإجراء.', + resetPluginTitle: 'هل أنت متأكد؟', resetPluginDesc: - "سيتم حذف جميع بيانات Color Master بشكل دائم (الملفات الشخصية، القصاصات، الإعدادات، والخلفيات). هذا الإجراء لا يمكن التراجع عنه.", - deleteGlobalBgTitle: "تأكيد حذف الخلفية", + 'سيتم حذف جميع بيانات Color Master بشكل دائم (الملفات الشخصية، القصاصات، الإعدادات، والخلفيات). هذا الإجراء لا يمكن التراجع عنه.', + deleteGlobalBgTitle: 'تأكيد حذف الخلفية', deleteGlobalBgDesc: - "هل أنت متأكد من رغبتك في حذف هذه الخلفية بشكل دائم؟ البروفايلات التالية تستخدمها حالياً وسيتم إزالتها منها:", - deleteBackgroundTitle: "حذف الخلفية؟", - deleteBackgroundDesc: (name: string) => - `هل أنت متأكد من رغبتك في حذف '${name}' بشكل دائم؟`, - deleteLangTitle: "حذف اللغة", + 'هل أنت متأكد من رغبتك في حذف هذه الخلفية بشكل دائم؟ البروفايلات التالية تستخدمها حالياً وسيتم إزالتها منها:', + deleteBackgroundTitle: 'حذف الخلفية؟', + deleteBackgroundDesc: (name: string) => `هل أنت متأكد من رغبتك في حذف '${name}' بشكل دائم؟`, + deleteLangTitle: 'حذف اللغة', deleteLangDesc: (name: string) => `هل أنت متأكد من حذف حزمة اللغة "${name}"؟ لا يمكن التراجع عن هذا الإجراء.`, - restoreLangTitle: "استعادة اللغة؟", + restoreLangTitle: 'استعادة اللغة؟', restoreLangDesc: (name: string) => `هل أنت متأكد من حذف التعديلات المخصصة للغة ${name} والعودة للافتراضي؟`, }, noticeRules: { - titleText: "قواعد لون النص المتقدمة", - titleBg: "قواعد لون الخلفية المتقدمة", - desc: "أنشئ قواعد ذات أولوية لتلوين الإشعارات بناءً على محتواها. سيتم تطبيق أول قاعدة مطابقة من الأعلى إلى الأسفل.", - addNewRule: "إضافة قاعدة جديدة", - keywordPlaceholder: "اكتب كلمة ثم اضغط مسافة...", - useRegex: "Regex", - highlightOnly: "تلوين الكلمة فقط", + titleText: 'قواعد لون النص المتقدمة', + titleBg: 'قواعد لون الخلفية المتقدمة', + desc: 'أنشئ قواعد ذات أولوية لتلوين الإشعارات بناءً على محتواها. سيتم تطبيق أول قاعدة مطابقة من الأعلى إلى الأسفل.', + addNewRule: 'إضافة قاعدة جديدة', + keywordPlaceholder: 'اكتب كلمة ثم اضغط مسافة...', + useRegex: 'Regex', + highlightOnly: 'تلوين الكلمة فقط', }, duplicateProfile: { - title: "اسم الملف الشخصي مكرر", + title: 'اسم الملف الشخصي مكرر', descParts: [`الملف الشخصي "`, `" موجود بالفعل. الرجاء اختيار اسم مختلف.`], - placeholder: "أدخل اسم الملف الشخصي الجديد...", + placeholder: 'أدخل اسم الملف الشخصي الجديد...', }, customVar: { - title: "إضافة متغير CSS مخصص جديد", - desc: "حدد متغير CSS جديد (مثال: --my-color: #f00). سيتم إضافة هذا المتغير إلى ملفك الشخصي النشط.", - displayName: "اسم العرض", + title: 'إضافة متغير CSS مخصص جديد', + desc: 'حدد متغير CSS جديد (مثال: --my-color: #f00). سيتم إضافة هذا المتغير إلى ملفك الشخصي النشط.', + displayName: 'اسم العرض', displayNameDesc: "اسم ودي للمتغير الخاص بك (مثال: 'لون أساسي مخصص لي').", - displayNamePlaceholder: "مثال: لوني الأساسي", - varName: "اسم المتغير", - varNameDesc: - "الاسم الفعلي لمتغير CSS. يجب أن يبدأ بـ '--' (مثال: '--my-primary-color').", - varNamePlaceholder: "مثال: --my-primary-color", - varValue: "قيمة المتغير", - varType: "نوع المتغير", - varTypeDesc: "اختر نوع البيانات التي يحملها هذا المتغير.", + displayNamePlaceholder: 'مثال: لوني الأساسي', + varName: 'اسم المتغير', + varNameDesc: "الاسم الفعلي لمتغير CSS. يجب أن يبدأ بـ '--' (مثال: '--my-primary-color').", + varNamePlaceholder: 'مثال: --my-primary-color', + varValue: 'قيمة المتغير', + varType: 'نوع المتغير', + varTypeDesc: 'اختر نوع البيانات التي يحملها هذا المتغير.', types: { - color: "لون", - size: "حجم (مثل px, em)", - text: "نص", - number: "رقم", + color: 'لون', + size: 'حجم (مثل px, em)', + text: 'نص', + number: 'رقم', }, varValueDesc: "قيمة متغير CSS (مثال: 'red', '#ff0000', 'rgb(255,0,0)').", - varValuePlaceholder: "مثال: #FF0000 أو أحمر", - description: "الوصف (اختياري)", - descriptionDesc: "وصف موجز لما يتحكم فيه هذا المتغير.", - descriptionPlaceholder: "مثال: اللون الأساسي للعناوين", - addVarButton: "إضافة", - textValuePlaceholder: "أدخل القيمة النصية...", + varValuePlaceholder: 'مثال: #FF0000 أو أحمر', + description: 'الوصف (اختياري)', + descriptionDesc: 'وصف موجز لما يتحكم فيه هذا المتغير.', + descriptionPlaceholder: 'مثال: اللون الأساسي للعناوين', + addVarButton: 'إضافة', + textValuePlaceholder: 'أدخل القيمة النصية...', }, addBackground: { - title: "إضافة خلفية جديدة (صورة/فيديو)", - importFromFile: "استيراد من ملف", - importFromFileDesc: "استيراد ملف صورة أو فيديو من جهاز الكمبيوتر.", - pasteBoxPlaceholder: "اسحب وأفلت / أو الصق خلفية / رابط هنا (Ctrl+V)", - dropToAdd: "أفلتها لإضافة الخلفية...", - processing: "جاري المعالجة", + title: 'إضافة خلفية جديدة (صورة/فيديو)', + importFromFile: 'استيراد من ملف', + importFromFileDesc: 'استيراد ملف صورة أو فيديو من جهاز الكمبيوتر.', + pasteBoxPlaceholder: 'اسحب وأفلت / أو الصق خلفية / رابط هنا (Ctrl+V)', + dropToAdd: 'أفلتها لإضافة الخلفية...', + processing: 'جاري المعالجة', }, fileConflict: { - title: "الملف موجود", + title: 'الملف موجود', desc: (name: string) => `يوجد ملف باسم '${name}' موجود مسبقاً في مجلد الخلفيات. ماذا تريد أن تفعل؟`, - replaceButton: "استبدال الملف", - keepButton: "الاحتفاظ بكليهما (إعادة تسمية)", + replaceButton: 'استبدال الملف', + keepButton: 'الاحتفاظ بكليهما (إعادة تسمية)', }, backgroundBrowser: { - title: "استعراض الخلفيات", - noImages: "لا توجد صور أو فيديوهات في مجلد الخلفيات.", + title: 'استعراض الخلفيات', + noImages: 'لا توجد صور أو فيديوهات في مجلد الخلفيات.', }, advancedReset: { - title: "إعادة تعيين متقدمة للبيانات", - desc: "اختر أجزاء البيانات التي تريد حذفها نهائياً. هذا الإجراء لا يمكن التراجع عنه.", - profilesLabel: "الملفات الشخصية واللقطات", + title: 'إعادة تعيين متقدمة للبيانات', + desc: 'اختر أجزاء البيانات التي تريد حذفها نهائياً. هذا الإجراء لا يمكن التراجع عنه.', + profilesLabel: 'الملفات الشخصية واللقطات', profilesDesc: - "حذف جميع الملفات الشخصية المخصصة واللقطات المثبتة. (يحافظ على الملفات المدمجة).", - snippetsLabel: "القصاصات العامة والخاصة", - snippetsDesc: - "حذف جميع قصاصات CSS، سواء العامة أو الموجودة داخل الملفات الشخصية.", - backgroundsLabel: "مجلد الخلفيات", + 'حذف جميع الملفات الشخصية المخصصة واللقطات المثبتة. (يحافظ على الملفات المدمجة).', + snippetsLabel: 'القصاصات العامة والخاصة', + snippetsDesc: 'حذف جميع قصاصات CSS، سواء العامة أو الموجودة داخل الملفات الشخصية.', + backgroundsLabel: 'مجلد الخلفيات', backgroundsDesc: - "حذف مجلد الخلفيات داخل مجلد إعدادات Obsidian (configDir/backgrounds) بالكامل مع جميع الوسائط بداخله.", - settingsLabel: "إعدادات الإضافة", - settingsDesc: - "إعادة تعيين اللغة، معدل التحديث، والتفضيلات الأخرى للوضع الافتراضي.", - languagesLabel: "اللغات المخصصة", - languagesDesc: "حذف جميع اللغات المخصصة التي أنشأها المستخدم.", + 'حذف مجلد الخلفيات داخل مجلد إعدادات Obsidian (configDir/backgrounds) بالكامل مع جميع الوسائط بداخله.', + settingsLabel: 'إعدادات الإضافة', + settingsDesc: 'إعادة تعيين اللغة، معدل التحديث، والتفضيلات الأخرى للوضع الافتراضي.', + languagesLabel: 'اللغات المخصصة', + languagesDesc: 'حذف جميع اللغات المخصصة التي أنشأها المستخدم.', }, addLang: { - title: "إضافة لغة جديدة", - desc: "إنشاء حزمة لغة مخصصة جديدة. ستتمكن من تعديل الترجمات بعد الإنشاء.", - nameLabel: "اسم اللغة (الأصلي)", - nameDesc: "اسم اللغة بخطها الأصلي.", - namePlaceholder: "مثال: العَرَبيَّةُ", - codeLabel: "رمز اللغة", + title: 'إضافة لغة جديدة', + desc: 'إنشاء حزمة لغة مخصصة جديدة. ستتمكن من تعديل الترجمات بعد الإنشاء.', + nameLabel: 'اسم اللغة (الأصلي)', + nameDesc: 'اسم اللغة بخطها الأصلي.', + namePlaceholder: 'مثال: العَرَبيَّةُ', + codeLabel: 'رمز اللغة', codeDesc: "رمز ISO 639 فريد (مثل: 'en', 'ar', 'zh').", - codePlaceholder: "مثال: ar", - rtlLabel: "لغة RTL", + codePlaceholder: 'مثال: ar', + rtlLabel: 'لغة RTL', rtlDesc: - "قم بتفعيل هذا الخيار إذا كانت اللغة تكتب من اليمين إلى اليسار (مثل العربيّة، الأردية).", + 'قم بتفعيل هذا الخيار إذا كانت اللغة تكتب من اليمين إلى اليسار (مثل العربيّة، الأردية).', }, translator: { title: (langName: string) => `تعديل ترجمات: ${langName}`, - searchPlaceholder: "ابحث عن مفاتيح الترجمة...", - copyJson: "نسخ JSON", - pasteJson: "لصق JSON", - exportFile: "تصدير ملف", - importFile: "استيراد ملف", - showMissing: "عرض المفقودة فقط", - showAll: "عرض الكل", - showMore: "عرض المزيد", - showLess: "عرض أقل", - noMatches: "لا توجد ترجمات تطابق بحثك.", - dynamicValue: "قيمة ديناميكيّة", + searchPlaceholder: 'ابحث عن مفاتيح الترجمة...', + copyJson: 'نسخ JSON', + pasteJson: 'لصق JSON', + exportFile: 'تصدير ملف', + importFile: 'استيراد ملف', + showMissing: 'عرض المفقودة فقط', + showAll: 'عرض الكل', + showMore: 'عرض المزيد', + showLess: 'عرض أقل', + noMatches: 'لا توجد ترجمات تطابق بحثك.', + dynamicValue: 'قيمة ديناميكيّة', }, langInfo: { - title: "حول اللغات المخصصة", + title: 'حول اللغات المخصصة', desc: `تتيح لك هذه الميزة إنشاء ترجماتك الخاصة للإضافة.\n\n**لماذا أضفنا هذه الميزة؟**\n\nبصراحة، أنا كمطور وحيد أعمل على هذه الإضافة، ومن الصعب جداً إضافة ميزات جديدة *وترجمتها* بشكل فوري لأكثر من 20 لغة. هذه الميزة هي دعوة لك للمساعدة وجعل الإضافة أفضل للجميع.\n\n**كيف يمكنك المساعدة:**\n\n1. قم بإنشاء لغة جديدة (مثلاً "الإسبانية").\n2. انقر على أيقونة "تعديل" (القلم).\n3. يمكنك استخدام "نسخ JSON" من لغة موجودة (مثل الإنجليزية) وطلب ترجمتها من ChatGPT.\n4. استخدم "لصق JSON" أو "استيراد ملف" لتحميل ترجمتك.\n5. عندما تكون راضياً، يمكنك إرسال ملف JSON إلي (عبر Pull Request على GitHub) وسأقوم بدمجه بكل سرور ليصبح **لغة أساسية** جديدة ليستفيد منها الجميع.\n\nتتيح لك هذه الميزة أيضاً إصلاح أي أخطاء إملائية أو تحسين الترجمات في اللغات الأساسية لاستخدامك الخاص.`, }, }, notices: { - pluginEnabled: "تم تفعيل متحكم الألوان", - pluginDisabled: "تم تعطيل متحكم الألوان", - profilePinned: "تم تثبيت ألوان الملف الشخصي بنجاح!", - profileReset: "تمت إعادة تعيين الملف الشخصي إلى اللقطة المثبتة.", - noPinnedSnapshot: "لا توجد لقطة مثبتة لهذا الملف الشخصي.", - profileNotFound: "تعذر العثور على الملف الشخصي النشط.", - noProfilesFound: "لا توجد أيّة ملفات شخصيّة.", + pluginEnabled: 'تم تفعيل متحكم الألوان', + pluginDisabled: 'تم تعطيل متحكم الألوان', + profilePinned: 'تم تثبيت ألوان الملف الشخصي بنجاح!', + profileReset: 'تمت إعادة تعيين الملف الشخصي إلى اللقطة المثبتة.', + noPinnedSnapshot: 'لا توجد لقطة مثبتة لهذا الملف الشخصي.', + profileNotFound: 'تعذر العثور على الملف الشخصي النشط.', + noProfilesFound: 'لا توجد أيّة ملفات شخصيّة.', activeProfileSwitched: (name: string) => `الملف الشخصي النشط: ${name}`, - graphColorsApplied: "تم تطبيق ألوان الرسم البياني!", - invalidJson: "ملف JSON غير صالح.", - jsonMustHaveName: - "يجب أن يحتوي ملف JSON المستورد على خاصية 'name' لإنشاء ملف شخصي جديد.", - profileCreatedSuccess: (name: string) => - `تم إنشاء الملف الشخصي "${name}" بنجاح.`, + graphColorsApplied: 'تم تطبيق ألوان الرسم البياني!', + invalidJson: 'ملف JSON غير صالح.', + jsonMustHaveName: "يجب أن يحتوي ملف JSON المستورد على خاصية 'name' لإنشاء ملف شخصي جديد.", + profileCreatedSuccess: (name: string) => `تم إنشاء الملف الشخصي "${name}" بنجاح.`, profileImportedSuccess: `تم استيراد الملف الشخصي بنجاح.`, - noActiveProfileToCopy: "لا يوجد ملف شخصي نشط لنسخه", - noActiveProfileToExport: "لا يوجد ملف شخصي نشط لتصديره.", - snippetCssCopied: "تم نسخ CSS القصاصة إلى الحافظة!", - snippetEmpty: "هذه القصاصة فارغة.", - cssContentEmpty: "لا يمكن أن يكون محتوى CSS فارغاً.", + noActiveProfileToCopy: 'لا يوجد ملف شخصي نشط لنسخه', + noActiveProfileToExport: 'لا يوجد ملف شخصي نشط لتصديره.', + snippetCssCopied: 'تم نسخ CSS القصاصة إلى الحافظة!', + snippetEmpty: 'هذه القصاصة فارغة.', + cssContentEmpty: 'لا يمكن أن يكون محتوى CSS فارغاً.', snippetNameExists: (name: string) => `اسم القصاصة "${name}" موجود بالفعل.`, - profileNameExists: (name: string) => - `اسم الملف الشخصي "${name}" موجود بالفعل.`, + profileNameExists: (name: string) => `اسم الملف الشخصي "${name}" موجود بالفعل.`, profileUpdated: (name: string) => `تم تحديث الملف الشخصي "${name}".`, snippetUpdated: (name: string) => `تم تحديث القصاصة "${name}".`, snippetCreated: (name: string) => `تم إنشاء القصاصة "${name}" بنجاح!`, - snippetDeleted: "تم حذف القصاصة بنجاح.", - snippetScopeMove: "استخدم نافذة التعديل لنقل القصاصة بين الأقسام.", - profileCreatedFromCss: (name: string) => - `تم إنشاء الملف الشخصي "${name}" بنجاح!`, - noColorHistory: "لا يوجد سجل ألوان لاستعادته.", + snippetDeleted: 'تم حذف القصاصة بنجاح.', + snippetScopeMove: 'استخدم نافذة التعديل لنقل القصاصة بين الأقسام.', + profileCreatedFromCss: (name: string) => `تم إنشاء الملف الشخصي "${name}" بنجاح!`, + noColorHistory: 'لا يوجد سجل ألوان لاستعادته.', colorRestored: (color: string) => `تمت الاستعادة: ${color}`, - textboxEmpty: - "صندوق النص فارغ. الصق بعض بيانات JSON أو استورد ملفاً أولاً.", - fileLoaded: (fileName: string) => - `تم تحميل الملف "${fileName}" في منطقة النص.`, - exportSuccess: "تم تصدير الملف الشخصي بنجاح!", - jsonCopied: "تم نسخ JSON للملف الشخصي إلى الحافظة بنجاح!", - resetSuccess: - "تم حذف بيانات Color Master. يرجى إعادة تحميل Obsidian لتطبيق التغييرات.", - fpsUpdated: (value: number) => - `تم تعيين معدل التحديث المباشر إلى: ${value}`, - invalidProfileObject: "لا يبدو أن ملف JSON هو كائن ملف شخصي صالح.", + textboxEmpty: 'صندوق النص فارغ. الصق بعض بيانات JSON أو استورد ملفاً أولاً.', + fileLoaded: (fileName: string) => `تم تحميل الملف "${fileName}" في منطقة النص.`, + exportSuccess: 'تم تصدير الملف الشخصي بنجاح!', + jsonCopied: 'تم نسخ JSON للملف الشخصي إلى الحافظة بنجاح!', + resetSuccess: 'تم حذف بيانات Color Master. يرجى إعادة تحميل Obsidian لتطبيق التغييرات.', + fpsUpdated: (value: number) => `تم تعيين معدل التحديث المباشر إلى: ${value}`, + invalidProfileObject: 'لا يبدو أن ملف JSON هو كائن ملف شخصي صالح.', profileCreated: (name: string) => `تم إنشاء الملف الشخصي "${name}" بنجاح!`, - settingsSaved: "تم تطبيق الإعدادات بنجاح!", - testSentence: (word: string) => - `هكذا سيبدو لون إشعار يحتوي على كلمة "${word}"`, - varNameEmpty: "اسم المتغير لا يمكن أن يكون فارغاً.", + settingsSaved: 'تم تطبيق الإعدادات بنجاح!', + testSentence: (word: string) => `هكذا سيبدو لون إشعار يحتوي على كلمة "${word}"`, + varNameEmpty: 'اسم المتغير لا يمكن أن يكون فارغاً.', varNameFormat: "يجب أن يبدأ اسم المتغير بـ '--'.", varExists: (name: string) => `المتغير "${name}" موجود بالفعل.`, varAdded: (name: string) => `تمت إضافة المتغير "${name}" بنجاح.`, - iconizeNotFound: - "إضافة Iconize غير موجودة. يرجى تثبيتها وتفعيلها لاستخدام هذه الميزة.", + iconizeNotFound: 'إضافة Iconize غير موجودة. يرجى تثبيتها وتفعيلها لاستخدام هذه الميزة.', themeCssLoaded: (theme: string) => `تم تحميل CSS بنجاح من ثيم "${theme}".`, themeReadFailed: (theme: string) => `تعذّر قراءة ملف الثيم "${theme}". قد يكون الملف محمياً أو مفقوداً.`, - snippetLoaded: (snippet: string) => - `تم تحميل CSS بنجاح من قصاصة "${snippet}".`, - snippetReadFailed: (snippet: string) => - `تعذّرت قراءة ملف القصاصة "${snippet}".`, - themeSwitchedLight: "تم التبديل إلى الوضع الفاتح", - themeSwitchedDark: "تم التبديل إلى الوضع الغامق", - themeSwitchedAuto: "تم التبديل إلى الوضع التلقائي", - bgSet: "تم تعيين الخلفية بنجاح.", - bgRemoved: "تمت إزالة الخلفية.", - backgroundLoadError: "فشل تحميل الخلفية.", - noBgToRemove: "لا توجد خلفية نشطة لهذا الملف الشخصي لإزالتها.", - bgDeleted: "تم حذف الخلفية وملفها بنجاح.", - backgroundUrlLoadError: "فشل تحميل الخلفية من الرابط.", - backgroundPasteError: "المحتوى الذي تم لصقه ليس ملف خلفية أو رابط صالح.", + snippetLoaded: (snippet: string) => `تم تحميل CSS بنجاح من قصاصة "${snippet}".`, + snippetReadFailed: (snippet: string) => `تعذّرت قراءة ملف القصاصة "${snippet}".`, + themeSwitchedLight: 'تم التبديل إلى الوضع الفاتح', + themeSwitchedDark: 'تم التبديل إلى الوضع الغامق', + themeSwitchedAuto: 'تم التبديل إلى الوضع التلقائي', + bgSet: 'تم تعيين الخلفية بنجاح.', + bgRemoved: 'تمت إزالة الخلفية.', + backgroundLoadError: 'فشل تحميل الخلفية.', + noBgToRemove: 'لا توجد خلفية نشطة لهذا الملف الشخصي لإزالتها.', + bgDeleted: 'تم حذف الخلفية وملفها بنجاح.', + backgroundUrlLoadError: 'فشل تحميل الخلفية من الرابط.', + backgroundPasteError: 'المحتوى الذي تم لصقه ليس ملف خلفية أو رابط صالح.', downloadingFromUrl: (url: string) => `جاري التحميل من ${url}...`, - pastedBase64Image: "تم لصق صورة Base64", + pastedBase64Image: 'تم لصق صورة Base64', pastedImage: (name: string) => `تم لصق الصورة "${name}"`, - invalidFilename: "اسم الملف غير صالح.", + invalidFilename: 'اسم الملف غير صالح.', filenameExists: (name: string) => `اسم الملف "${name}" موجود مسبقاً.`, renameSuccess: (name: string) => `تمت إعادة التسمية إلى "${name}"`, - renameError: "خطأ أثناء إعادة تسمية الملف.", - profileDeleted: "تم حذف الملف الشخصي بنجاح.", + renameError: 'خطأ أثناء إعادة تسمية الملف.', + profileDeleted: 'تم حذف الملف الشخصي بنجاح.', jpgQualitySet: (value: number) => `تم ضبط جودة JPG على ${value}%`, - cannotDeleteLastProfile: "لا يمكنك حذف آخر ملف شخصي.", - noKeywordsToTest: "لا تحتوي هذه القاعدة على كلمات مفتاحية لاختبارها.", - langNameEmpty: "اسم اللغة لا يمكن أن يكون فارغاً.", - langCodeEmpty: "رمز اللغة لا يمكن أن يكون فارغاً.", - langCodeCore: (code: string) => - `الرمز "${code}" محجوز للغة أساسية. الرجاء اختيار رمز آخر.`, - langCodeExists: (code: string) => - `لغة مخصصة بالرمز "${code}" موجودة مسبقاً.`, - langNameExists: (name: string) => - `لغة مخصصة بالاسم "${name}" موجودة مسبقاً.`, - langNameCore: (name: string) => - `الاسم "${name}" محجوز للغة أساسية. الرجاء اختيار اسم آخر.`, + cannotDeleteLastProfile: 'لا يمكنك حذف آخر ملف شخصي.', + noKeywordsToTest: 'لا تحتوي هذه القاعدة على كلمات مفتاحية لاختبارها.', + langNameEmpty: 'اسم اللغة لا يمكن أن يكون فارغاً.', + langCodeEmpty: 'رمز اللغة لا يمكن أن يكون فارغاً.', + langCodeCore: (code: string) => `الرمز "${code}" محجوز للغة أساسية. الرجاء اختيار رمز آخر.`, + langCodeExists: (code: string) => `لغة مخصصة بالرمز "${code}" موجودة مسبقاً.`, + langNameExists: (name: string) => `لغة مخصصة بالاسم "${name}" موجودة مسبقاً.`, + langNameCore: (name: string) => `الاسم "${name}" محجوز للغة أساسية. الرجاء اختيار اسم آخر.`, langCreated: (name: string) => `تم إنشاء اللغة بنجاح: ${name}`, langSaved: (name: string) => `تم حفظ الترجمات بنجاح لـ: ${name}`, langExported: (code: string) => `تم تصدير ${code}.json بنجاح`, langImported: (name: string) => `تم استيراد الترجمات بنجاح من ${name}`, - langCopiedJson: "تم نسخ ترجمات JSON إلى الحافظة.", + langCopiedJson: 'تم نسخ ترجمات JSON إلى الحافظة.', langPastedJson: (count: number) => `تم تطبيق ${count} ترجمة بنجاح.`, langDeleted: (name: string) => `تم حذف اللغة: ${name}`, - langRestored: "تمت استعادة اللغة للوضع الافتراضي.", - deleteBackgroundsError: (message: string) => - `فشل حذف مجلد الخلفيات: ${message}`, - snippetsLocked: "تم قفل القصاصات (تم تعطيل السحب).", - snippetsUnlocked: "تم فتح قفل القصاصات.", + langRestored: 'تمت استعادة اللغة للوضع الافتراضي.', + deleteBackgroundsError: (message: string) => `فشل حذف مجلد الخلفيات: ${message}`, + snippetsLocked: 'تم قفل القصاصات (تم تعطيل السحب).', + snippetsUnlocked: 'تم فتح قفل القصاصات.', }, tooltips: { - editCssProfile: "تعديل تشكيلة CSS", - pinSnapshot: "تثبيت الألوان الحالية كلقطة", - pinSnapshotDate: (date: string) => - `تم تثبيت الألوان بتاريخ ${date}. انقر لإعادة التثبيت.`, - resetToPinned: "إعادة التعيين إلى الألوان المثبتة", - editSnippet: "تعديل القصاصة", - copySnippetCss: "نسخ كود CSS إلى الحافظة", - deleteSnippet: "حذف القصاصة", - setTransparent: "تعيين إلى شفاف", - undoChange: "تراجع عن آخر تغيير", - dragReorder: "اسحب لإعادة الترتيب", - testRule: "تجربة القاعدة بكلمة عشوائية", - deleteCustomVar: "حذف المتغير المخصص", - iconizeSettings: "إعدادات Iconize", - themeLight: "الثيم: فرض الوضع الفاتح (اضغط للتبديل للغامق)", - themeDark: "الثيم: فرض الوضع الغامق (اضغط للتبديل للتلقائي)", - themeAuto: "الثيم: تلقائي (يتبع أوبسيديان) (اضغط للتبديل للفاتح)", - addBg: "إضافة خلفية", - removeBg: "إزالة الخلفية", - bgSettings: "إعدادات الخلفية", - browseBg: "استعراض الخلفيات المخزنة", + editCssProfile: 'تعديل تشكيلة CSS', + pinSnapshot: 'تثبيت الألوان الحالية كلقطة', + pinSnapshotDate: (date: string) => `تم تثبيت الألوان بتاريخ ${date}. انقر لإعادة التثبيت.`, + resetToPinned: 'إعادة التعيين إلى الألوان المثبتة', + editSnippet: 'تعديل القصاصة', + copySnippetCss: 'نسخ كود CSS إلى الحافظة', + deleteSnippet: 'حذف القصاصة', + setTransparent: 'تعيين إلى شفاف', + undoChange: 'تراجع عن آخر تغيير', + dragReorder: 'اسحب لإعادة الترتيب', + testRule: 'تجربة القاعدة بكلمة عشوائية', + deleteCustomVar: 'حذف المتغير المخصص', + iconizeSettings: 'إعدادات Iconize', + themeLight: 'الثيم: فرض الوضع الفاتح (اضغط للتبديل للغامق)', + themeDark: 'الثيم: فرض الوضع الغامق (اضغط للتبديل للتلقائي)', + themeAuto: 'الثيم: تلقائي (يتبع أوبسيديان) (اضغط للتبديل للفاتح)', + addBg: 'إضافة خلفية', + removeBg: 'إزالة الخلفية', + bgSettings: 'إعدادات الخلفية', + browseBg: 'استعراض الخلفيات المخزنة', iconizeNotInstalled: "الإضافة غير مُثبتة أو مُعطلة. الرجاء تثبيت وتفعيل 'Iconize' لاستخدام هذه الميزة.", - editLang: "تعديل اللغة المحددة", - langMenu: "خيارات اللغة", - langInfo: "لماذا أقوم بإنشاء/تعديل اللغات؟", - restoreDefaultLang: "استعادة الترجمات الافتراضية", - lockSnippets: "قفل السحب", - unlockSnippets: "فتح السحب", + editLang: 'تعديل اللغة المحددة', + langMenu: 'خيارات اللغة', + langInfo: 'لماذا أقوم بإنشاء/تعديل اللغات؟', + restoreDefaultLang: 'استعادة الترجمات الافتراضية', + lockSnippets: 'قفل السحب', + unlockSnippets: 'فتح السحب', }, commands: { - toggleTheme: "تبديل ثيم الملف الشخصي النشط", - enableDisable: "تفعيل وتعطيل", - cycleNext: "الانتقال للبروفايل التالي", - cyclePrevious: "الانتقال للبروفايل السابق", - openSettings: "فتح نافذة الإعدادات", + toggleTheme: 'تبديل ثيم الملف الشخصي النشط', + enableDisable: 'تفعيل وتعطيل', + cycleNext: 'الانتقال للبروفايل التالي', + cyclePrevious: 'الانتقال للبروفايل السابق', + openSettings: 'فتح نافذة الإعدادات', }, likeCard: { - profilesStat: (p: number, s: number) => - `ملفّات شخصيّة: ${p} & قصاصات: ${s}`, - colorsStat: "ألوان قابلة للتخصيص", - integrationsStat: "تكامل الإضافات", - daysStat: "أيام الاستخدام", - starButton: "أضف ★ على GitHub", - issueButton: "أبلغ عن مشكلة", - syncButton: "مزامنة خزنتك", - telegramButton: "تيليجرام", + profilesStat: (p: number, s: number) => `ملفّات شخصيّة: ${p} & قصاصات: ${s}`, + colorsStat: 'ألوان قابلة للتخصيص', + integrationsStat: 'تكامل الإضافات', + daysStat: 'أيام الاستخدام', + starButton: 'أضف ★ على GitHub', + issueButton: 'أبلغ عن مشكلة', + syncButton: 'مزامنة خزنتك', + telegramButton: 'تيليجرام', }, colors: { names: { // Iconize - "--iconize-icon-color": "لون أيقونات Iconize", + '--iconize-icon-color': 'لون أيقونات Iconize', // Backgrounds - "--background-primary": "الخلفية الأساسية", - "--background-primary-alt": "الخلفية الأساسية (بديل)", - "--background-secondary": "الخلفية الثانوية", - "--background-secondary-alt": "الخلفية الثانوية (بديل)", - "--background-modifier-border": "الإطار", - "--background-modifier-border-hover": "الإطار (عند التمرير)", - "--background-modifier-border-focus": "الإطار (عند التحديد)", - "--background-modifier-flair": "خلفية العناصر الخاصة", - "--background-modifier-hover": "خلفية (عند التمرير)", - "--background-modifier-active": "خلفية (عند النقر)", + '--background-primary': 'الخلفية الأساسية', + '--background-primary-alt': 'الخلفية الأساسية (بديل)', + '--background-secondary': 'الخلفية الثانوية', + '--background-secondary-alt': 'الخلفية الثانوية (بديل)', + '--background-modifier-border': 'الإطار', + '--background-modifier-border-hover': 'الإطار (عند التمرير)', + '--background-modifier-border-focus': 'الإطار (عند التحديد)', + '--background-modifier-flair': 'خلفية العناصر الخاصة', + '--background-modifier-hover': 'خلفية (عند التمرير)', + '--background-modifier-active': 'خلفية (عند النقر)', // Text - "--text-normal": "النص العادي", - "--text-muted": "النص الباهت", - "--text-faint": "النص الخافت", - "--text-on-accent": "النص فوق اللون المميز", - "--text-accent": "النص المميز", - "--text-accent-hover": "النص المميز (عند التمرير)", - "--text-selection": "تحديد النص", - "--checklist-done-color": "عنصر منجز", - "--tag-color": "نص الوسم (Tag)", - "--tag-color-hover": "نص الوسم (عند التمرير)", - "--tag-bg": "خلفية الوسم (Tag)", + '--text-normal': 'النص العادي', + '--text-muted': 'النص الباهت', + '--text-faint': 'النص الخافت', + '--text-on-accent': 'النص فوق اللون المميز', + '--text-accent': 'النص المميز', + '--text-accent-hover': 'النص المميز (عند التمرير)', + '--text-selection': 'تحديد النص', + '--checklist-done-color': 'عنصر منجز', + '--tag-color': 'نص الوسم (Tag)', + '--tag-color-hover': 'نص الوسم (عند التمرير)', + '--tag-bg': 'خلفية الوسم (Tag)', // Headings - "--h1-color": "لون H1", - "--h2-color": "لون H2", - "--h3-color": "لون H3", - "--h4-color": "لون H4", - "--h5-color": "لون H5", - "--h6-color": "لون H6", + '--h1-color': 'لون H1', + '--h2-color': 'لون H2', + '--h3-color': 'لون H3', + '--h4-color': 'لون H4', + '--h5-color': 'لون H5', + '--h6-color': 'لون H6', // Markdown - "--hr-color": "الخط الفاصل", - "--blockquote-border-color": "إطار الاقتباس", - "--blockquote-color": "نص الاقتباس", - "--blockquote-bg": "خلفية الاقتباس", - "--code-normal": "نص الكود المضمن", - "--code-background": "خلفية الكود المضمن", - "--text-highlight-bg": "خلفية النص المظلل", + '--hr-color': 'الخط الفاصل', + '--blockquote-border-color': 'إطار الاقتباس', + '--blockquote-color': 'نص الاقتباس', + '--blockquote-bg': 'خلفية الاقتباس', + '--code-normal': 'نص الكود المضمن', + '--code-background': 'خلفية الكود المضمن', + '--text-highlight-bg': 'خلفية النص المظلل', // Interactive Elements - "--interactive-normal": "عنصر تفاعلي", - "--interactive-hover": "عنصر تفاعلي (عند التمرير)", - "--interactive-accent": "عنصر تفاعلي مميز", - "--interactive-accent-hover": "عنصر تفاعلي مميز (عند التمرير)", - "--interactive-success": "لون النجاح", - "--interactive-error": "لون الخطأ", - "--interactive-warning": "لون التحذير", + '--interactive-normal': 'عنصر تفاعلي', + '--interactive-hover': 'عنصر تفاعلي (عند التمرير)', + '--interactive-accent': 'عنصر تفاعلي مميز', + '--interactive-accent-hover': 'عنصر تفاعلي مميز (عند التمرير)', + '--interactive-success': 'لون النجاح', + '--interactive-error': 'لون الخطأ', + '--interactive-warning': 'لون التحذير', // UI Elements - "--titlebar-background": "خلفية شريط العنوان", - "--titlebar-background-focused": "خلفية شريط العنوان (محدد)", - "--titlebar-text-color": "نص شريط العنوان", - "--sidebar-background": "خلفية الشريط الجانبي", - "--sidebar-border-color": "إطار الشريط الجانبي", - "--header-background": "خلفية الترويسة", - "--header-border-color": "إطار الترويسة", - "--vault-name-color": "اسم الخزنة (Vault)", + '--titlebar-background': 'خلفية شريط العنوان', + '--titlebar-background-focused': 'خلفية شريط العنوان (محدد)', + '--titlebar-text-color': 'نص شريط العنوان', + '--sidebar-background': 'خلفية الشريط الجانبي', + '--sidebar-border-color': 'إطار الشريط الجانبي', + '--header-background': 'خلفية الترويسة', + '--header-border-color': 'إطار الترويسة', + '--vault-name-color': 'اسم الخزنة (Vault)', // Notices - "--cm-notice-text-default": "لون نص الإشعار الافتراضي", - "--cm-notice-bg-default": "لون خلفية الإشعار الافتراضي", + '--cm-notice-text-default': 'لون نص الإشعار الافتراضي', + '--cm-notice-bg-default': 'لون خلفية الإشعار الافتراضي', // Graph View - "--graph-line": "خط الرسم البياني", - "--graph-node": "عقدة الرسم البياني", - "--graph-text": "نص الرسم البياني", - "--graph-node-unresolved": "عقدة (غير موجودة)", - "--graph-node-focused": "عقدة (محددة)", - "--graph-node-tag": "عقدة وسم (Tag)", - "--graph-node-attachment": "عقدة مرفق", + '--graph-line': 'خط الرسم البياني', + '--graph-node': 'عقدة الرسم البياني', + '--graph-text': 'نص الرسم البياني', + '--graph-node-unresolved': 'عقدة (غير موجودة)', + '--graph-node-focused': 'عقدة (محددة)', + '--graph-node-tag': 'عقدة وسم (Tag)', + '--graph-node-attachment': 'عقدة مرفق', // Misc - "--scrollbar-thumb-bg": "مقبض شريط التمرير", - "--scrollbar-bg": "خلفية شريط التمرير", - "--divider-color": "الفاصل", + '--scrollbar-thumb-bg': 'مقبض شريط التمرير', + '--scrollbar-bg': 'خلفية شريط التمرير', + '--divider-color': 'الفاصل', }, descriptions: { // Iconize - "--iconize-icon-color": - "يحدد لون جميع الأيقونات المضافة بواسطة إضافة Iconize. هذا الخيار سيتجاوز إعدادات الألوان الخاصة بالإضافة.", + '--iconize-icon-color': + 'يحدد لون جميع الأيقونات المضافة بواسطة إضافة Iconize. هذا الخيار سيتجاوز إعدادات الألوان الخاصة بالإضافة.', // Backgrounds - "--background-primary": - "لون الخلفية الأساسي للتطبيق بالكامل، خصوصاً للمحرر وصفحات الملاحظات.", - "--background-primary-alt": - "لون خلفية بديل، يستخدم غالباً للسطر النشط في المحرر.", - "--background-secondary": - "خلفية ثانوية، تستخدم عادةً للأشرطة الجانبية واللوحات الأخرى.", - "--background-secondary-alt": - "خلفية ثانوية بديلة، تستخدم لعناصر مثل الملف النشط في مستكشف الملفات.", - "--background-modifier-border": - "لون الإطارات (Borders) على مختلف عناصر الواجهة كالأزرار وحقول الإدخال.", - "--background-modifier-border-hover": - "لون الإطار عند مرور مؤشر الفأرة فوق العنصر.", - "--background-modifier-border-focus": - "لون الإطار عندما يكون العنصر محدداً، مثل حقل نصي نشط.", - "--background-modifier-flair": + '--background-primary': + 'لون الخلفية الأساسي للتطبيق بالكامل، خصوصاً للمحرر وصفحات الملاحظات.', + '--background-primary-alt': 'لون خلفية بديل، يستخدم غالباً للسطر النشط في المحرر.', + '--background-secondary': 'خلفية ثانوية، تستخدم عادةً للأشرطة الجانبية واللوحات الأخرى.', + '--background-secondary-alt': + 'خلفية ثانوية بديلة، تستخدم لعناصر مثل الملف النشط في مستكشف الملفات.', + '--background-modifier-border': + 'لون الإطارات (Borders) على مختلف عناصر الواجهة كالأزرار وحقول الإدخال.', + '--background-modifier-border-hover': 'لون الإطار عند مرور مؤشر الفأرة فوق العنصر.', + '--background-modifier-border-focus': 'لون الإطار عندما يكون العنصر محدداً، مثل حقل نصي نشط.', + '--background-modifier-flair': "لون خلفية لعناصر واجهة خاصة، مثل حالة 'المزامنة' أو 'الفهرسة'.", - "--background-modifier-hover": - "لون خلفية العناصر عند مرور مؤشر الفأرة فوقها (مثل عناصر القوائم).", - "--background-modifier-active": - "لون خلفية العنصر عند النقر عليه أو عندما يكون محدداً ونشطاً.", + '--background-modifier-hover': + 'لون خلفية العناصر عند مرور مؤشر الفأرة فوقها (مثل عناصر القوائم).', + '--background-modifier-active': + 'لون خلفية العنصر عند النقر عليه أو عندما يكون محدداً ونشطاً.', // Text - "--text-normal": - "لون النص الافتراضي لجميع الملاحظات ومعظم عناصر الواجهة.", - "--text-muted": - "لون نص باهت قليلاً، يستخدم للمعلومات الأقل أهمية مثل بيانات الملف.", - "--text-faint": - "أكثر لون نص باهت، يستخدم لنصوص الواجهة الخفية جداً أو العناصر المعطلة.", - "--text-on-accent": - "لون النص الذي يظهر فوق الخلفيات الملونة (Accent)، مثل نص على زر أساسي.", - "--text-accent": - "اللون المميز (Accent) الأساسي للنصوص، يستخدم للروابط وعناصر الواجهة الهامة.", - "--text-accent-hover": - "لون النص المميز (مثل الروابط) عند مرور مؤشر الفأرة فوقه.", - "--text-selection": "لون خلفية النص الذي تحدده بمؤشر الفأرة.", - "--checklist-done-color": - "لون علامة الصح والنص لمهمة منجزة في قائمة المهام.", - "--tag-color": "يحدد لون نص التاغات (#tags).", - "--tag-color-hover": "يحدد لون نص التاغات عند تمرير الماوس فوقها.", - "--tag-bg": "يحدد لون خلفية التاغات، مما يسمح بإنشاء شكل 'الحبة'.", + '--text-normal': 'لون النص الافتراضي لجميع الملاحظات ومعظم عناصر الواجهة.', + '--text-muted': 'لون نص باهت قليلاً، يستخدم للمعلومات الأقل أهمية مثل بيانات الملف.', + '--text-faint': 'أكثر لون نص باهت، يستخدم لنصوص الواجهة الخفية جداً أو العناصر المعطلة.', + '--text-on-accent': 'لون النص الذي يظهر فوق الخلفيات الملونة (Accent)، مثل نص على زر أساسي.', + '--text-accent': + 'اللون المميز (Accent) الأساسي للنصوص، يستخدم للروابط وعناصر الواجهة الهامة.', + '--text-accent-hover': 'لون النص المميز (مثل الروابط) عند مرور مؤشر الفأرة فوقه.', + '--text-selection': 'لون خلفية النص الذي تحدده بمؤشر الفأرة.', + '--checklist-done-color': 'لون علامة الصح والنص لمهمة منجزة في قائمة المهام.', + '--tag-color': 'يحدد لون نص التاغات (#tags).', + '--tag-color-hover': 'يحدد لون نص التاغات عند تمرير الماوس فوقها.', + '--tag-bg': "يحدد لون خلفية التاغات، مما يسمح بإنشاء شكل 'الحبة'.", // Headings - "--h1-color": "لون نصوص العناوين من نوع H1.", - "--h2-color": "لون نصوص العناوين من نوع H2.", - "--h3-color": "لون نصوص العناوين من نوع H3.", - "--h4-color": "لون نصوص العناوين من نوع H4.", - "--h5-color": "لون نصوص العناوين من نوع H5.", - "--h6-color": "لون نصوص العناوين من نوع H6.", + '--h1-color': 'لون نصوص العناوين من نوع H1.', + '--h2-color': 'لون نصوص العناوين من نوع H2.', + '--h3-color': 'لون نصوص العناوين من نوع H3.', + '--h4-color': 'لون نصوص العناوين من نوع H4.', + '--h5-color': 'لون نصوص العناوين من نوع H5.', + '--h6-color': 'لون نصوص العناوين من نوع H6.', // Markdown - "--hr-color": "لون الخط الفاصل الأفقي الذي يتم إنشاؤه باستخدام `---`.", - "--blockquote-border-color": - "لون الإطار العمودي الذي يظهر على يسار نص الاقتباس.", - "--blockquote-color": "لون النص داخل فقرة الاقتباس.", - "--blockquote-bg": "يحدد لون خلفية عناصر الاقتباس (>) في النص.", - "--code-normal": - "يحدد لون النص داخل الكود المضمن (بين علامات الاقتباس المعكوسة).", - "--code-background": "يحدد لون خلفية الكود المضمن.", - "--text-highlight-bg": "يحدد لون خلفية النص المظلل (==بهذا الشكل==).", + '--hr-color': 'لون الخط الفاصل الأفقي الذي يتم إنشاؤه باستخدام `---`.', + '--blockquote-border-color': 'لون الإطار العمودي الذي يظهر على يسار نص الاقتباس.', + '--blockquote-color': 'لون النص داخل فقرة الاقتباس.', + '--blockquote-bg': 'يحدد لون خلفية عناصر الاقتباس (>) في النص.', + '--code-normal': 'يحدد لون النص داخل الكود المضمن (بين علامات الاقتباس المعكوسة).', + '--code-background': 'يحدد لون خلفية الكود المضمن.', + '--text-highlight-bg': 'يحدد لون خلفية النص المظلل (==بهذا الشكل==).', // Interactive Elements - "--interactive-normal": "لون خلفية العناصر التفاعلية مثل الأزرار.", - "--interactive-hover": - "لون خلفية العناصر التفاعلية عند مرور الفأرة فوقها.", - "--interactive-accent": - "اللون المميز للعناصر التفاعلية الهامة (مثل زر 'إنشاء').", - "--interactive-accent-hover": - "اللون المميز للعناصر التفاعلية الهامة عند مرور الفأرة فوقها.", - "--interactive-success": "لون يدل على عملية ناجحة (مثل الأخضر).", - "--interactive-error": "لون يدل على حدوث خطأ (مثل الأحمر).", - "--interactive-warning": "لون يدل على وجود تحذير (مثل الأصفر).", + '--interactive-normal': 'لون خلفية العناصر التفاعلية مثل الأزرار.', + '--interactive-hover': 'لون خلفية العناصر التفاعلية عند مرور الفأرة فوقها.', + '--interactive-accent': "اللون المميز للعناصر التفاعلية الهامة (مثل زر 'إنشاء').", + '--interactive-accent-hover': 'اللون المميز للعناصر التفاعلية الهامة عند مرور الفأرة فوقها.', + '--interactive-success': 'لون يدل على عملية ناجحة (مثل الأخضر).', + '--interactive-error': 'لون يدل على حدوث خطأ (مثل الأحمر).', + '--interactive-warning': 'لون يدل على وجود تحذير (مثل الأصفر).', // UI Elements - "--titlebar-background": "لون خلفية شريط العنوان للنافذة الرئيسية.", - "--titlebar-background-focused": - "لون خلفية شريط العنوان عندما تكون النافذة نشطة.", - "--titlebar-text-color": "لون النص في شريط العنوان.", - "--sidebar-background": "يستهدف بشكل خاص خلفية الأشرطة الجانبية.", - "--sidebar-border-color": "لون الخط الفاصل بجانب الأشرطة الجانبية.", - "--header-background": - "خلفية العناوين داخل اللوحات (مثل عنوان الملاحظة).", - "--header-border-color": "لون الخط الفاصل تحت عناوين اللوحات.", - "--vault-name-color": - "لون اسم القبو (Vault) الخاص بك في الزاوية العلوية.", - "--cm-notice-text-default": - "يحدد لون النص الافتراضي لكل الإشعارات، ما لم يتم تجاوزه بقاعدة مخصصة.", - "--cm-notice-bg-default": - "يحدد لون الخلفية الافتراضي لكل الإشعارات، ما لم يتم تجاوزه بقاعدة مخصصة.", + '--titlebar-background': 'لون خلفية شريط العنوان للنافذة الرئيسية.', + '--titlebar-background-focused': 'لون خلفية شريط العنوان عندما تكون النافذة نشطة.', + '--titlebar-text-color': 'لون النص في شريط العنوان.', + '--sidebar-background': 'يستهدف بشكل خاص خلفية الأشرطة الجانبية.', + '--sidebar-border-color': 'لون الخط الفاصل بجانب الأشرطة الجانبية.', + '--header-background': 'خلفية العناوين داخل اللوحات (مثل عنوان الملاحظة).', + '--header-border-color': 'لون الخط الفاصل تحت عناوين اللوحات.', + '--vault-name-color': 'لون اسم القبو (Vault) الخاص بك في الزاوية العلوية.', + '--cm-notice-text-default': + 'يحدد لون النص الافتراضي لكل الإشعارات، ما لم يتم تجاوزه بقاعدة مخصصة.', + '--cm-notice-bg-default': + 'يحدد لون الخلفية الافتراضي لكل الإشعارات، ما لم يتم تجاوزه بقاعدة مخصصة.', // Graph View - "--graph-line": "لون الخطوط الواصلة بين الملاحظات في عرض الرسم البياني.", - "--graph-node": "لون النقاط الدائرية للملاحظات الموجودة.", - "--graph-text": - "لون النصوص (أسماء الملاحظات) على النقاط في الرسم البياني.", - "--graph-node-unresolved": - "لون النقاط الخاصة بالملاحظات التي لم يتم إنشاؤها بعد (روابط غير موجودة).", - "--graph-node-focused": - "لون العقدة التي عليها التركيز (عندما تمرر الفأرة عليها أو تحددها).", - "--graph-node-tag": - "لون العقد التي تمثل الوسوم (Tags) إذا كانت ظاهرة في الرسم البياني.", - "--graph-node-attachment": - "لون العقد التي تمثل المرفقات (مثل الصور أو الملفات الأخرى) في الرسم البياني.", + '--graph-line': 'لون الخطوط الواصلة بين الملاحظات في عرض الرسم البياني.', + '--graph-node': 'لون النقاط الدائرية للملاحظات الموجودة.', + '--graph-text': 'لون النصوص (أسماء الملاحظات) على النقاط في الرسم البياني.', + '--graph-node-unresolved': + 'لون النقاط الخاصة بالملاحظات التي لم يتم إنشاؤها بعد (روابط غير موجودة).', + '--graph-node-focused': 'لون العقدة التي عليها التركيز (عندما تمرر الفأرة عليها أو تحددها).', + '--graph-node-tag': 'لون العقد التي تمثل الوسوم (Tags) إذا كانت ظاهرة في الرسم البياني.', + '--graph-node-attachment': + 'لون العقد التي تمثل المرفقات (مثل الصور أو الملفات الأخرى) في الرسم البياني.', // Misc - "--scrollbar-thumb-bg": "لون الجزء القابل للسحب من شريط التمرير.", - "--scrollbar-bg": "لون مسار شريط التمرير (الخلفية).", - "--divider-color": - "لون الخطوط الفاصلة في واجهة المستخدم، مثل الخطوط بين الإعدادات.", + '--scrollbar-thumb-bg': 'لون الجزء القابل للسحب من شريط التمرير.', + '--scrollbar-bg': 'لون مسار شريط التمرير (الخلفية).', + '--divider-color': 'لون الخطوط الفاصلة في واجهة المستخدم، مثل الخطوط بين الإعدادات.', }, }, }; diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index ed85037..aef8e5c 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -1,663 +1,603 @@ export default { plugin: { - name: "Color Master - v1.2.0", - ribbonTooltip: "Color Master Settings", + name: 'Color Master - v1.2.0', + ribbonTooltip: 'Color Master Settings', }, buttons: { - new: "New", - delete: "Delete", - selectOption: "Select an option", - create: "Create", - reset: "Reset", - update: "Update", - apply: "Apply", - cancel: "Cancel", - reload: "Reload", - exportFile: "Export File", - copyJson: "Copy JSON", - importCss: "Import / Paste (.css)", - importJson: "Import / Paste (.json)", - chooseFile: "Choose File...", - import: "Import", - select: "Select", - browse: "Browse...", - deleteAnyway: "Delete Anyway", - restore: "Restore", + new: 'New', + delete: 'Delete', + selectOption: 'Select an option', + create: 'Create', + reset: 'Reset', + update: 'Update', + apply: 'Apply', + cancel: 'Cancel', + reload: 'Reload', + exportFile: 'Export File', + copyJson: 'Copy JSON', + importCss: 'Import / Paste (.css)', + importJson: 'Import / Paste (.json)', + chooseFile: 'Choose File...', + import: 'Import', + select: 'Select', + browse: 'Browse...', + deleteAnyway: 'Delete Anyway', + restore: 'Restore', }, settings: { - enablePlugin: "Enable Color Master", + enablePlugin: 'Enable Color Master', enablePluginDesc: - "Turn this off to temporarily disable all custom colors and revert to your active Obsidian theme.", - language: "Language", - languageDesc: "Set the interface language for the plugin.", - languageSettingsModalTitle: "Language Settings", - rtlLayoutName: "Enable Right-to-Left (RTL) Layout", + 'Turn this off to temporarily disable all custom colors and revert to your active Obsidian theme.', + language: 'Language', + languageDesc: 'Set the interface language for the plugin.', + languageSettingsModalTitle: 'Language Settings', + rtlLayoutName: 'Enable Right-to-Left (RTL) Layout', rtlLayoutDesc: "When enabled, the plugin's interface is flipped to properly support languages written from right to left.", - searchPlaceholder: "Search variables (name or value)...", - regexPlaceholder: "Enter Regex and press Enter...", - allSections: "All Sections", - clear: "Clear", - ariaCase: "Case-sensitive search", - ariaRegex: "Use regular expression", + searchPlaceholder: 'Search variables (name or value)...', + regexPlaceholder: 'Enter Regex and press Enter...', + allSections: 'All Sections', + clear: 'Clear', + ariaCase: 'Case-sensitive search', + ariaRegex: 'Use regular expression', themeWarningTooltip: (currentTheme: string) => `The community theme "${currentTheme}" is active, which may interfere with the profile's appearance. For best results, switch to Obsidian's default theme, or import "${currentTheme}" as a new CSS profile to customize it directly.`, }, profileManager: { - heading: "Profile Manager", - activeProfile: "Active Profile", - activeProfileDesc: "Manage and switch between color profiles.", - themeType: "Profile Theme Type", + heading: 'Profile Manager', + activeProfile: 'Active Profile', + activeProfileDesc: 'Manage and switch between color profiles.', + themeType: 'Profile Theme Type', themeTypeDesc: - "Set whether this profile should force a specific theme (Dark/Light) when activated.", + 'Set whether this profile should force a specific theme (Dark/Light) when activated.', themeAuto: "Obsidian's Default Theme", - themeDark: "Force Dark Mode", - themeLight: "Force Light Mode", - tooltipThemeAuto: - "Theme: Auto (Follows Obsidian) (Click to switch to Light)", - tooltipThemeDark: "Theme: Force Dark Mode (Click to switch to Auto)", - tooltipThemeLight: "Theme: Force Light Mode (Click to switch to Dark)", - tooltipExport: "Export current profile as JSON file", - tooltipCopyJson: "Copy current profile JSON to clipboard", + themeDark: 'Force Dark Mode', + themeLight: 'Force Light Mode', + tooltipThemeAuto: 'Theme: Auto (Follows Obsidian) (Click to switch to Light)', + tooltipThemeDark: 'Theme: Force Dark Mode (Click to switch to Auto)', + tooltipThemeLight: 'Theme: Force Light Mode (Click to switch to Dark)', + tooltipExport: 'Export current profile as JSON file', + tooltipCopyJson: 'Copy current profile JSON to clipboard', }, options: { - heading: "Advanced Settings", - liveUpdateName: "Live Update FPS", + heading: 'Advanced Settings', + liveUpdateName: 'Live Update FPS', liveUpdateDesc: - "Sets how many times per second the UI previews color changes while dragging (0 = disable live preview). Lower values can improve performance.", - iconizeModalTitle: "Iconize Integration Settings", - overrideIconizeName: "Override Iconize Plugin Colors", + 'Sets how many times per second the UI previews color changes while dragging (0 = disable live preview). Lower values can improve performance.', + iconizeModalTitle: 'Iconize Integration Settings', + overrideIconizeName: 'Override Iconize Plugin Colors', overrideIconizeDesc: - "Let Color Master control all icon colors from the Iconize plugin. For best results, disable the color settings within Iconize itself.", - cleanupIntervalName: "Cleanup Interval", + 'Let Color Master control all icon colors from the Iconize plugin. For best results, disable the color settings within Iconize itself.', + cleanupIntervalName: 'Cleanup Interval', cleanupIntervalDesc: - "Sets how often (in seconds) the plugin checks for uninstalled Iconize plugin to clean up its icons.", - addCustomVarName: "Add Custom Variable", + 'Sets how often (in seconds) the plugin checks for uninstalled Iconize plugin to clean up its icons.', + addCustomVarName: 'Add Custom Variable', addCustomVarDesc: "Add a new CSS variable that isn't in the default list. The name must start with '--'.", - addNewVarButton: "Add New Variable...", - resetPluginName: "Reset Plugin Settings", + addNewVarButton: 'Add New Variable...', + resetPluginName: 'Reset Plugin Settings', resetPluginDesc: - "This will delete all profiles, snippets, settings, and backgrounds, resetting the plugin to its original state. This action requires an app reload and cannot be undone.", - resetPluginButton: "Reset All Data...", - backgroundName: "Set Custom Background", - backgroundDesc: "Manage the background image or video for this profile.", + 'This will delete all profiles, snippets, settings, and backgrounds, resetting the plugin to its original state. This action requires an app reload and cannot be undone.', + resetPluginButton: 'Reset All Data...', + backgroundName: 'Set Custom Background', + backgroundDesc: 'Manage the background image or video for this profile.', - backgroundModalSettingsTitle: "Background Settings", - backgroundEnableName: "Enable Background", - backgroundEnableDesc: - "Toggle the visibility of the custom background for this profile.", - convertImagesName: "Convert images to JPG", + backgroundModalSettingsTitle: 'Background Settings', + backgroundEnableName: 'Enable Background', + backgroundEnableDesc: 'Toggle the visibility of the custom background for this profile.', + convertImagesName: 'Convert images to JPG', convertImagesDesc: - "Automatically convert PNG, WEBP, or BMP images to JPG on upload to reduce file size and improve loading performance. (Note: Transparency will be lost)", - jpgQualityName: "JPG Quality", + 'Automatically convert PNG, WEBP, or BMP images to JPG on upload to reduce file size and improve loading performance. (Note: Transparency will be lost)', + jpgQualityName: 'JPG Quality', jpgQualityDesc: - "Set the compression quality (1-100). Lower values mean smaller files but lower quality.", - badgeNotInstalled: "Not Installed", - videoOpacityName: "Video Opacity", - videoOpacityDesc: - "Controls the transparency of the video background for better readability.", - videoMuteName: "Mute Video", - videoMuteDesc: "Mutes the video background. Highly recommended.", - settingType: "Setting Type", - settingTypeImage: "Image", - settingTypeVideo: "Video", + 'Set the compression quality (1-100). Lower values mean smaller files but lower quality.', + badgeNotInstalled: 'Not Installed', + videoOpacityName: 'Video Opacity', + videoOpacityDesc: 'Controls the transparency of the video background for better readability.', + videoMuteName: 'Mute Video', + videoMuteDesc: 'Mutes the video background. Highly recommended.', + settingType: 'Setting Type', + settingTypeImage: 'Image', + settingTypeVideo: 'Video', }, snippets: { - heading: "CSS Snippets", - createButton: "Create New Snippet", - noSnippetsDesc: "No CSS snippets created for this profile yet.", - global: "Global Snippet", - globalName: "Save as Global Snippet", - globalDesc: "A global snippet is applied to all of your profiles.", + heading: 'CSS Snippets', + createButton: 'Create New Snippet', + noSnippetsDesc: 'No CSS snippets created for this profile yet.', + global: 'Global Snippet', + globalName: 'Save as Global Snippet', + globalDesc: 'A global snippet is applied to all of your profiles.', }, categories: { - pluginintegrations: "Plugin Integrations", - backgrounds: "Backgrounds", - text: "Text", - interactive: "Interactive Elements", - ui: "UI Elements", - misc: "Misc", - graph: "Graph View", - markdown: "Markdown", - notices: "Notices", - custom: "Custom Variables", - customDesc: "Variable added by the user.", + pluginintegrations: 'Plugin Integrations', + backgrounds: 'Backgrounds', + text: 'Text', + interactive: 'Interactive Elements', + ui: 'UI Elements', + misc: 'Misc', + graph: 'Graph View', + markdown: 'Markdown', + notices: 'Notices', + custom: 'Custom Variables', + customDesc: 'Variable added by the user.', helpTextPre: "Can't find the variable you're looking for? ", - helpTextLink: "Browse the official list of Obsidian CSS variables.", + helpTextLink: 'Browse the official list of Obsidian CSS variables.', }, modals: { newProfile: { - title: "Create New Profile", - nameLabel: "Profile Name", - namePlaceholder: "Enter profile name...", + title: 'Create New Profile', + nameLabel: 'Profile Name', + namePlaceholder: 'Enter profile name...', }, deleteProfile: { - title: "Delete Profile", + title: 'Delete Profile', confirmation: (name: string) => `Are you sure you want to delete the profile "${name}"? This action cannot be undone.`, }, jsonImport: { - title: "Paste or Import Profile JSON", - desc1: "You can paste a profile JSON in the box below.", + title: 'Paste or Import Profile JSON', + desc1: 'You can paste a profile JSON in the box below.', placeholder: '{ "name": "...", "profile": { ... } }', - settingName: "Import from File", - settingDesc: "Or, select a (.json) profile file from your computer.", - replaceActiveButton: "Replace Active", - createNewButton: "Create New", + settingName: 'Import from File', + settingDesc: 'Or, select a (.json) profile file from your computer.', + replaceActiveButton: 'Replace Active', + createNewButton: 'Create New', }, cssImport: { - title: "Import / Paste CSS & Create Profile", - titleEdit: "Edit CSS Profile", - note: "Note : Pasted CSS can affect UI, proceed only with trusted CSS.", - importFromFile: "Import from File", - importFromFileDesc: "Or, select a (.css) file from your computer.", - importFromTheme: "Import from installed theme", - importFromThemeDesc: - "Quickly load the CSS from one of your installed community themes.", - noThemes: "No community themes installed", + title: 'Import / Paste CSS & Create Profile', + titleEdit: 'Edit CSS Profile', + note: 'Note : Pasted CSS can affect UI, proceed only with trusted CSS.', + importFromFile: 'Import from File', + importFromFileDesc: 'Or, select a (.css) file from your computer.', + importFromTheme: 'Import from installed theme', + importFromThemeDesc: 'Quickly load the CSS from one of your installed community themes.', + noThemes: 'No community themes installed', }, snippetEditor: { - title: "Create New CSS Snippet", - titleEdit: "Edit CSS Snippet", - nameLabel: "Snippet Name", - namePlaceholder: "Enter snippet name...", - importFromSnippet: "Import from installed snippet", - importFromSnippetDesc: - "Quickly load the CSS from one of your enabled Obsidian snippets.", - noSnippets: "No snippets installed", - cssPlaceholder: "Paste your CSS here...", + title: 'Create New CSS Snippet', + titleEdit: 'Edit CSS Snippet', + nameLabel: 'Snippet Name', + namePlaceholder: 'Enter snippet name...', + importFromSnippet: 'Import from installed snippet', + importFromSnippetDesc: 'Quickly load the CSS from one of your enabled Obsidian snippets.', + noSnippets: 'No snippets installed', + cssPlaceholder: 'Paste your CSS here...', }, confirmation: { - resetProfileTitle: "Reset Profile Confirmation", + resetProfileTitle: 'Reset Profile Confirmation', resetProfileDesc: - "Are you sure you want to reset this profile to the last pinned snapshot? This will overwrite your current colors and cannot be undone.", + 'Are you sure you want to reset this profile to the last pinned snapshot? This will overwrite your current colors and cannot be undone.', deleteSnippetTitle: (name: string) => `Delete Snippet: ${name}`, deleteSnippetDesc: - "Are you sure you want to delete this snippet? This action cannot be undone.", - resetPluginTitle: "Are you sure?", + 'Are you sure you want to delete this snippet? This action cannot be undone.', + resetPluginTitle: 'Are you sure?', resetPluginDesc: - "This will permanently delete all your Color Master data (profiles, snippets, settings, and backgrounds). This is irreversible.", - deleteGlobalBgTitle: "Confirm Background Deletion", + 'This will permanently delete all your Color Master data (profiles, snippets, settings, and backgrounds). This is irreversible.', + deleteGlobalBgTitle: 'Confirm Background Deletion', deleteGlobalBgDesc: - "Are you sure you want to permanently delete this background? The following profiles are using it and will be reset:", - deleteBackgroundTitle: "Delete Background?", + 'Are you sure you want to permanently delete this background? The following profiles are using it and will be reset:', + deleteBackgroundTitle: 'Delete Background?', deleteBackgroundDesc: (name: string) => `Are you sure you want to permanently delete '${name}'?`, - deleteLangTitle: "Delete Language", + deleteLangTitle: 'Delete Language', deleteLangDesc: (name: string) => `Are you sure you want to delete the language pack "${name}"? This action cannot be undone.`, - restoreLangTitle: "Restore Language?", + restoreLangTitle: 'Restore Language?', restoreLangDesc: (name: string) => `Are you sure you want to delete custom changes for ${name} and restore defaults?`, }, noticeRules: { - titleText: "Advanced Text Color Rules", - titleBg: "Advanced Background Color Rules", - desc: "Create prioritized rules to color notices based on their content. The first matching rule from top to bottom will be applied.", - addNewRule: "Add New Rule", - keywordPlaceholder: "Type a keyword and press Space...", - useRegex: "Regex", - highlightOnly: "Highlight keyword only", + titleText: 'Advanced Text Color Rules', + titleBg: 'Advanced Background Color Rules', + desc: 'Create prioritized rules to color notices based on their content. The first matching rule from top to bottom will be applied.', + addNewRule: 'Add New Rule', + keywordPlaceholder: 'Type a keyword and press Space...', + useRegex: 'Regex', + highlightOnly: 'Highlight keyword only', }, duplicateProfile: { - title: "Duplicate Profile Name", - descParts: [ - `The profile name "`, - `" already exists. Please choose a different name.`, - ], - placeholder: "Enter new profile name...", + title: 'Duplicate Profile Name', + descParts: [`The profile name "`, `" already exists. Please choose a different name.`], + placeholder: 'Enter new profile name...', }, customVar: { - title: "Add New Custom CSS Variable", - desc: "Define a new CSS variable (e.g., --my-color: #f00). This variable will be added to your active profile.", - displayName: "Display Name", - displayNameDesc: - "A friendly name for your variable (e.g., 'My Custom Primary Color').", - displayNamePlaceholder: "e.g., My Primary Color", - varName: "Variable Name", + title: 'Add New Custom CSS Variable', + desc: 'Define a new CSS variable (e.g., --my-color: #f00). This variable will be added to your active profile.', + displayName: 'Display Name', + displayNameDesc: "A friendly name for your variable (e.g., 'My Custom Primary Color').", + displayNamePlaceholder: 'e.g., My Primary Color', + varName: 'Variable Name', varNameDesc: "The actual CSS variable name. Must start with '--' (e.g., '--my-primary-color').", - varNamePlaceholder: "e.g., --my-primary-color", - varValue: "Variable Value", - varType: "Variable Type", - varTypeDesc: "Select the type of data this variable holds.", + varNamePlaceholder: 'e.g., --my-primary-color', + varValue: 'Variable Value', + varType: 'Variable Type', + varTypeDesc: 'Select the type of data this variable holds.', types: { - color: "Color", - size: "Size (e.g., px, em)", - text: "Text", - number: "Number", + color: 'Color', + size: 'Size (e.g., px, em)', + text: 'Text', + number: 'Number', }, - varValueDesc: - "The value of the CSS variable (e.g., 'red', '#ff0000', 'rgb(255,0,0)').", - varValuePlaceholder: "e.g., #FF0000 or red", - description: "Description (Optional)", - descriptionDesc: "A brief description of what this variable controls.", - descriptionPlaceholder: "e.g., Main color for headings", - addVarButton: "Add", - textValuePlaceholder: "Enter text value...", + varValueDesc: "The value of the CSS variable (e.g., 'red', '#ff0000', 'rgb(255,0,0)').", + varValuePlaceholder: 'e.g., #FF0000 or red', + description: 'Description (Optional)', + descriptionDesc: 'A brief description of what this variable controls.', + descriptionPlaceholder: 'e.g., Main color for headings', + addVarButton: 'Add', + textValuePlaceholder: 'Enter text value...', }, addBackground: { - title: "Add New Background (Image/Video)", - importFromFile: "Import from File", - importFromFileDesc: - "Import an image file from your computer to use as a background.", - pasteBoxPlaceholder: - "Drag & Drop / Paste a background or URL here (Ctrl+V)", - dropToAdd: "Drop to add background...", - processing: "Processing", + title: 'Add New Background (Image/Video)', + importFromFile: 'Import from File', + importFromFileDesc: 'Import an image file from your computer to use as a background.', + pasteBoxPlaceholder: 'Drag & Drop / Paste a background or URL here (Ctrl+V)', + dropToAdd: 'Drop to add background...', + processing: 'Processing', }, fileConflict: { - title: "File Exists", + title: 'File Exists', desc: (name: string) => `A file named '${name}' already exists in your backgrounds folder. What would you like to do?`, - replaceButton: "Replace File", - keepButton: "Keep Both (Rename)", + replaceButton: 'Replace File', + keepButton: 'Keep Both (Rename)', }, backgroundBrowser: { - title: "Stored Backgrounds", - noImages: "No images or videos found in your backgrounds folder.", + title: 'Stored Backgrounds', + noImages: 'No images or videos found in your backgrounds folder.', }, advancedReset: { - title: "Advanced Data Reset", - desc: "Select the data components you want to permanently delete. This is irreversible.", - profilesLabel: "Profiles & Snapshots", - profilesDesc: - "Deletes all custom profiles and pinned snapshots. (Keeps built-in profiles).", - snippetsLabel: "Global & Profile Snippets", - snippetsDesc: - "Deletes all CSS snippets, both global and inside all profiles.", - backgroundsLabel: "Backgrounds Folder", + title: 'Advanced Data Reset', + desc: 'Select the data components you want to permanently delete. This is irreversible.', + profilesLabel: 'Profiles & Snapshots', + profilesDesc: 'Deletes all custom profiles and pinned snapshots. (Keeps built-in profiles).', + snippetsLabel: 'Global & Profile Snippets', + snippetsDesc: 'Deletes all CSS snippets, both global and inside all profiles.', + backgroundsLabel: 'Backgrounds Folder', backgroundsDesc: "Deletes the entire backgrounds folder inside Obsidian's configuration directory (configDir/backgrounds), including all media.", - settingsLabel: "Plugin Settings", - settingsDesc: - "Resets language, FPS, layout, and other options to default.", - languagesLabel: "Custom Languages", - languagesDesc: "Deletes all custom languages created by the user.", + settingsLabel: 'Plugin Settings', + settingsDesc: 'Resets language, FPS, layout, and other options to default.', + languagesLabel: 'Custom Languages', + languagesDesc: 'Deletes all custom languages created by the user.', }, addLang: { - title: "Add New Language", - desc: "Create a new custom language pack. You will be able to edit the translations after creation.", - nameLabel: "Language Name (Native)", - nameDesc: "The name of the language in its own script.", - namePlaceholder: "e.g., English", - codeLabel: "Language Code", + title: 'Add New Language', + desc: 'Create a new custom language pack. You will be able to edit the translations after creation.', + nameLabel: 'Language Name (Native)', + nameDesc: 'The name of the language in its own script.', + namePlaceholder: 'e.g., English', + codeLabel: 'Language Code', codeDesc: "A unique ISO 639 code (e.g., 'en', 'ar', 'zh').", - codePlaceholder: "e.g., en", - rtlLabel: "RTL Language", - rtlDesc: - "Enable this if the language is written Right-to-Left (e.g., Arabic, Urdu).", + codePlaceholder: 'e.g., en', + rtlLabel: 'RTL Language', + rtlDesc: 'Enable this if the language is written Right-to-Left (e.g., Arabic, Urdu).', }, translator: { title: (langName: string) => `Edit Translations: ${langName}`, - searchPlaceholder: "Search translation keys...", - copyJson: "Copy JSON", - pasteJson: "Paste JSON", - exportFile: "Export File", - importFile: "Import File", - showMissing: "Show Missing Only", - showAll: "Show All", - showMore: "Show More", - showLess: "Show Less", - noMatches: "No translations match your search.", - dynamicValue: "Dynamic Value", + searchPlaceholder: 'Search translation keys...', + copyJson: 'Copy JSON', + pasteJson: 'Paste JSON', + exportFile: 'Export File', + importFile: 'Import File', + showMissing: 'Show Missing Only', + showAll: 'Show All', + showMore: 'Show More', + showLess: 'Show Less', + noMatches: 'No translations match your search.', + dynamicValue: 'Dynamic Value', }, langInfo: { - title: "About Custom Languages", + title: 'About Custom Languages', desc: `This feature allows you to create your own translations for the plugin.\n\n**Why?**\n\nI'm currently the sole developer, and it's difficult to add new features *and* translate them into 20+ languages. This feature was added so you can help!\n\n**How you can help:**\n\n1. Create a new language (e.g., "Spanish").\n2. Click the "Edit" (pencil) icon.\n3. You can use "Copy JSON" on an existing language (like English) and ask ChatGPT to translate it.\n4. Use "Paste JSON" or "Import File" to load your translation.\n5. Once you're happy, you can send me the JSON file (via a GitHub Pull Request) and I will happily merge it as a new **core language** for everyone to use.\n\nIt also lets you fix any typos or improve translations in the core languages for your own use.`, }, }, notices: { - pluginEnabled: "Color Master Enabled", - pluginDisabled: "Color Master Disabled", - profilePinned: "Profile colors pinned successfully!", - profileReset: "Profile has been reset to the pinned snapshot.", - noPinnedSnapshot: "No pinned snapshot found for this profile.", - profileNotFound: "Active profile could not be found.", - noProfilesFound: "No profiles found.", + pluginEnabled: 'Color Master Enabled', + pluginDisabled: 'Color Master Disabled', + profilePinned: 'Profile colors pinned successfully!', + profileReset: 'Profile has been reset to the pinned snapshot.', + noPinnedSnapshot: 'No pinned snapshot found for this profile.', + profileNotFound: 'Active profile could not be found.', + noProfilesFound: 'No profiles found.', activeProfileSwitched: (name: string) => `Active profile: ${name}`, - graphColorsApplied: "Graph colors applied!", - invalidJson: "Invalid JSON.", - jsonMustHaveName: - "The imported JSON must have a 'name' property to create a new profile.", - profileCreatedSuccess: (name: string) => - `Profile "${name}" was created successfully.`, + graphColorsApplied: 'Graph colors applied!', + invalidJson: 'Invalid JSON.', + jsonMustHaveName: "The imported JSON must have a 'name' property to create a new profile.", + profileCreatedSuccess: (name: string) => `Profile "${name}" was created successfully.`, profileImportedSuccess: `Profile imported successfully.`, - noActiveProfileToCopy: "No active profile to copy.", - noActiveProfileToExport: "No active profile to export.", - snippetCssCopied: "Snippet CSS copied to clipboard!", - snippetEmpty: "This snippet is empty.", - cssContentEmpty: "CSS content cannot be empty.", - snippetNameExists: (name: string) => - `Snippet name "${name}" already exists.`, - profileNameExists: (name: string) => - `Profile name "${name}" already exists.`, + noActiveProfileToCopy: 'No active profile to copy.', + noActiveProfileToExport: 'No active profile to export.', + snippetCssCopied: 'Snippet CSS copied to clipboard!', + snippetEmpty: 'This snippet is empty.', + cssContentEmpty: 'CSS content cannot be empty.', + snippetNameExists: (name: string) => `Snippet name "${name}" already exists.`, + profileNameExists: (name: string) => `Profile name "${name}" already exists.`, profileUpdated: (name: string) => `Profile "${name}" updated.`, snippetUpdated: (name: string) => `Snippet "${name}" updated.`, - snippetCreated: (name: string) => - `Snippet "${name}" has been created successfully!`, - snippetDeleted: "Snippet deleted successfully.", - snippetScopeMove: "Use the edit modal to move a snippet between scopes.", - profileCreatedFromCss: (name: string) => - `Profile "${name}" has been created successfully!`, - noColorHistory: "No color history to restore.", + snippetCreated: (name: string) => `Snippet "${name}" has been created successfully!`, + snippetDeleted: 'Snippet deleted successfully.', + snippetScopeMove: 'Use the edit modal to move a snippet between scopes.', + profileCreatedFromCss: (name: string) => `Profile "${name}" has been created successfully!`, + noColorHistory: 'No color history to restore.', colorRestored: (color: string) => `Restored: ${color}`, - textboxEmpty: - "The text box is empty. Paste some JSON or import a file first.", - fileLoaded: (fileName: string) => - `File "${fileName}" loaded into the text area.`, - exportSuccess: "Profile exported successfully!", - jsonCopied: "Profile JSON copied to clipboard.", + textboxEmpty: 'The text box is empty. Paste some JSON or import a file first.', + fileLoaded: (fileName: string) => `File "${fileName}" loaded into the text area.`, + exportSuccess: 'Profile exported successfully!', + jsonCopied: 'Profile JSON copied to clipboard.', resetSuccess: - "Color Master data has been deleted. Please reload Obsidian to apply the changes.", + 'Color Master data has been deleted. Please reload Obsidian to apply the changes.', fpsUpdated: (value: number) => `Live Update FPS set to: ${value}`, - invalidProfileObject: "JSON does not appear to be a valid profile object.", + invalidProfileObject: 'JSON does not appear to be a valid profile object.', profileCreated: (name: string) => `Profile "${name}" created successfully!`, - settingsSaved: "Settings applied successfully!", - testSentence: (word: string) => - `Notice color for "${word}" looks like this:`, - varNameEmpty: "Variable name cannot be empty.", + settingsSaved: 'Settings applied successfully!', + testSentence: (word: string) => `Notice color for "${word}" looks like this:`, + varNameEmpty: 'Variable name cannot be empty.', varNameFormat: "Variable name must start with '--'.", varExists: (name: string) => `Variable "${name}" already exists.`, varAdded: (name: string) => `Variable "${name}" added successfully.`, - iconizeNotFound: - "Iconize plugin not found. Please install and enable it to use this feature.", - themeCssLoaded: (theme: string) => - `Successfully loaded CSS from "${theme}" theme.`, + iconizeNotFound: 'Iconize plugin not found. Please install and enable it to use this feature.', + themeCssLoaded: (theme: string) => `Successfully loaded CSS from "${theme}" theme.`, themeReadFailed: (theme: string) => `Could not read the theme file for "${theme}". It might be protected or missing.`, - snippetLoaded: (snippet: string) => - `Successfully loaded CSS from "${snippet}" snippet.`, - snippetReadFailed: (snippet: string) => - `Could not read the snippet file for "${snippet}".`, - themeSwitchedLight: "Switched to Light Mode", - themeSwitchedDark: "Switched to Dark Mode", - themeSwitchedAuto: "Switched to Auto Mode", - bgSet: "Background has been set successfully.", - bgRemoved: "Background has been removed.", - backgroundLoadError: "Failed to load the background.", - noBgToRemove: "There is no active background for this profile to remove.", - bgDeleted: "Background media and file have been deleted.", - backgroundUrlLoadError: "Failed to download background from URL.", - backgroundPasteError: - "Pasted content is not a valid background file or URL.", + snippetLoaded: (snippet: string) => `Successfully loaded CSS from "${snippet}" snippet.`, + snippetReadFailed: (snippet: string) => `Could not read the snippet file for "${snippet}".`, + themeSwitchedLight: 'Switched to Light Mode', + themeSwitchedDark: 'Switched to Dark Mode', + themeSwitchedAuto: 'Switched to Auto Mode', + bgSet: 'Background has been set successfully.', + bgRemoved: 'Background has been removed.', + backgroundLoadError: 'Failed to load the background.', + noBgToRemove: 'There is no active background for this profile to remove.', + bgDeleted: 'Background media and file have been deleted.', + backgroundUrlLoadError: 'Failed to download background from URL.', + backgroundPasteError: 'Pasted content is not a valid background file or URL.', downloadingFromUrl: (url: string) => `Downloading from ${url}...`, - pastedBase64Image: "Pasted Base64 image", + pastedBase64Image: 'Pasted Base64 image', pastedImage: (name: string) => `Pasted image "${name}"`, - invalidFilename: "Invalid file name.", + invalidFilename: 'Invalid file name.', filenameExists: (name: string) => `File name "${name}" already exists.`, renameSuccess: (name: string) => `Renamed to "${name}"`, - renameError: "Error renaming file.", - profileDeleted: "Profile deleted successfully.", + renameError: 'Error renaming file.', + profileDeleted: 'Profile deleted successfully.', jpgQualitySet: (value: number) => `JPG Quality set to ${value}%`, - cannotDeleteLastProfile: "You cannot delete the last profile.", - noKeywordsToTest: "This rule has no keywords to test.", - langNameEmpty: "Language name cannot be empty.", - langCodeEmpty: "Language code cannot be empty.", + cannotDeleteLastProfile: 'You cannot delete the last profile.', + noKeywordsToTest: 'This rule has no keywords to test.', + langNameEmpty: 'Language name cannot be empty.', + langCodeEmpty: 'Language code cannot be empty.', langCodeCore: (code: string) => `The code "${code}" is reserved for a core language. Please choose another.`, - langCodeExists: (code: string) => - `A custom language with the code "${code}" already exists.`, - langNameExists: (name: string) => - `A custom language with the name "${name}" already exists.`, + langCodeExists: (code: string) => `A custom language with the code "${code}" already exists.`, + langNameExists: (name: string) => `A custom language with the name "${name}" already exists.`, langNameCore: (name: string) => `The name "${name}" is reserved for a core language. Please choose another.`, langCreated: (name: string) => `Successfully created language: ${name}`, langSaved: (name: string) => `Successfully saved translations for: ${name}`, langExported: (code: string) => `Successfully exported ${code}.json`, - langImported: (name: string) => - `Successfully imported translations from ${name}`, - langCopiedJson: "Copied translations JSON to clipboard.", - langPastedJson: (count: number) => - `Successfully applied ${count} translations.`, + langImported: (name: string) => `Successfully imported translations from ${name}`, + langCopiedJson: 'Copied translations JSON to clipboard.', + langPastedJson: (count: number) => `Successfully applied ${count} translations.`, langDeleted: (name: string) => `Language deleted: ${name}`, - langRestored: "Language restored to defaults.", - deleteBackgroundsError: (message: string) => - `Failed to delete backgrounds folder: ${message}`, - snippetsLocked: "Snippets locked (Dragging disabled).", - snippetsUnlocked: "Snippets unlocked.", + langRestored: 'Language restored to defaults.', + deleteBackgroundsError: (message: string) => `Failed to delete backgrounds folder: ${message}`, + snippetsLocked: 'Snippets locked (Dragging disabled).', + snippetsUnlocked: 'Snippets unlocked.', }, tooltips: { - editCssProfile: "Edit CSS Profile", - pinSnapshot: "Pin current colors as a snapshot", - pinSnapshotDate: (date: string) => - `Colors pinned on ${date}. Click to re-pin.`, - resetToPinned: "Reset to pinned colors", - editSnippet: "Edit Snippet", - copySnippetCss: "Copy CSS to clipboard", - deleteSnippet: "Delete Snippet", - setTransparent: "Set to transparent", - undoChange: "Undo last change", - dragReorder: "Drag to reorder", - testRule: "Test this rule with a random keyword", - deleteCustomVar: "Delete Custom Variable", - iconizeSettings: "Iconize Settings", - themeLight: "Theme: Force Light Mode (Click to switch to Dark)", - themeDark: "Theme: Force Dark Mode (Click to switch to Auto)", - themeAuto: "Theme: Auto (Follows Obsidian) (Click to switch to Light)", - addBg: "Add background media", - removeBg: "Remove background media", - bgSettings: "Background media settings", - browseBg: "Browse stored background media", + editCssProfile: 'Edit CSS Profile', + pinSnapshot: 'Pin current colors as a snapshot', + pinSnapshotDate: (date: string) => `Colors pinned on ${date}. Click to re-pin.`, + resetToPinned: 'Reset to pinned colors', + editSnippet: 'Edit Snippet', + copySnippetCss: 'Copy CSS to clipboard', + deleteSnippet: 'Delete Snippet', + setTransparent: 'Set to transparent', + undoChange: 'Undo last change', + dragReorder: 'Drag to reorder', + testRule: 'Test this rule with a random keyword', + deleteCustomVar: 'Delete Custom Variable', + iconizeSettings: 'Iconize Settings', + themeLight: 'Theme: Force Light Mode (Click to switch to Dark)', + themeDark: 'Theme: Force Dark Mode (Click to switch to Auto)', + themeAuto: 'Theme: Auto (Follows Obsidian) (Click to switch to Light)', + addBg: 'Add background media', + removeBg: 'Remove background media', + bgSettings: 'Background media settings', + browseBg: 'Browse stored background media', iconizeNotInstalled: "Plugin not installed or disabled. Please install and enable 'Iconize' to use this feature.", - editLang: "Edit selected language", - langMenu: "Language options", - langInfo: "Why create/edit languages?", - restoreDefaultLang: "Restore default translations", - lockSnippets: "Lock Dragging", - unlockSnippets: "Unlock Dragging", + editLang: 'Edit selected language', + langMenu: 'Language options', + langInfo: 'Why create/edit languages?', + restoreDefaultLang: 'Restore default translations', + lockSnippets: 'Lock Dragging', + unlockSnippets: 'Unlock Dragging', }, commands: { - toggleTheme: "Cycle active profile theme", - enableDisable: "Enable & Disable", - cycleNext: "Cycle to next profile", - cyclePrevious: "Cycle to previous profile", - openSettings: "Open settings tab", + toggleTheme: 'Cycle active profile theme', + enableDisable: 'Enable & Disable', + cycleNext: 'Cycle to next profile', + cyclePrevious: 'Cycle to previous profile', + openSettings: 'Open settings tab', }, likeCard: { profilesStat: (p: number, s: number) => `Profiles: ${p} & Snippets: ${s}`, - colorsStat: "Customizable Colors", - integrationsStat: "Plugin Integrations", - daysStat: "Days of Use", - starButton: "Star on GitHub", - issueButton: "Report an Issue", - syncButton: "Sync Your Vault", - telegramButton: "Telegram", + colorsStat: 'Customizable Colors', + integrationsStat: 'Plugin Integrations', + daysStat: 'Days of Use', + starButton: 'Star on GitHub', + issueButton: 'Report an Issue', + syncButton: 'Sync Your Vault', + telegramButton: 'Telegram', }, colors: { names: { // Iconize - "--iconize-icon-color": "Iconize Icon Color", + '--iconize-icon-color': 'Iconize Icon Color', // Backgrounds - "--background-primary": "Background Primary", - "--background-primary-alt": "Background Primary Alt", - "--background-secondary": "Background Secondary", - "--background-secondary-alt": "Background Secondary Alt", - "--background-modifier-border": "Border", - "--background-modifier-border-hover": "Border (Hover)", - "--background-modifier-border-focus": "Border (Focus)", - "--background-modifier-flair": "Flair Background", - "--background-modifier-hover": "Hover Background", - "--background-modifier-active": "Active Background", + '--background-primary': 'Background Primary', + '--background-primary-alt': 'Background Primary Alt', + '--background-secondary': 'Background Secondary', + '--background-secondary-alt': 'Background Secondary Alt', + '--background-modifier-border': 'Border', + '--background-modifier-border-hover': 'Border (Hover)', + '--background-modifier-border-focus': 'Border (Focus)', + '--background-modifier-flair': 'Flair Background', + '--background-modifier-hover': 'Hover Background', + '--background-modifier-active': 'Active Background', // Text - "--text-normal": "Normal Text", - "--text-muted": "Muted Text", - "--text-faint": "Faint Text", - "--text-on-accent": "Text on Accent", - "--text-accent": "Accent Text", - "--text-accent-hover": "Accent Text (Hover)", - "--text-selection": "Text Selection", - "--checklist-done-color": "Checklist Done", - "--tag-color": "Tag Text", - "--tag-color-hover": "Tag Text (Hover)", - "--tag-bg": "Tag Background", + '--text-normal': 'Normal Text', + '--text-muted': 'Muted Text', + '--text-faint': 'Faint Text', + '--text-on-accent': 'Text on Accent', + '--text-accent': 'Accent Text', + '--text-accent-hover': 'Accent Text (Hover)', + '--text-selection': 'Text Selection', + '--checklist-done-color': 'Checklist Done', + '--tag-color': 'Tag Text', + '--tag-color-hover': 'Tag Text (Hover)', + '--tag-bg': 'Tag Background', // Headings - "--h1-color": "H1 Color", - "--h2-color": "H2 Color", - "--h3-color": "H3 Color", - "--h4-color": "H4 Color", - "--h5-color": "H5 Color", - "--h6-color": "H6 Color", + '--h1-color': 'H1 Color', + '--h2-color': 'H2 Color', + '--h3-color': 'H3 Color', + '--h4-color': 'H4 Color', + '--h5-color': 'H5 Color', + '--h6-color': 'H6 Color', // Markdown - "--hr-color": "Horizontal Rule", - "--blockquote-border-color": "Blockquote Border", - "--blockquote-color": "Blockquote Text", - "--blockquote-bg": "Blockquote Background", - "--code-normal": "Inline code text", - "--code-background": "Inline code background", - "--text-highlight-bg": "Highlighted text background", + '--hr-color': 'Horizontal Rule', + '--blockquote-border-color': 'Blockquote Border', + '--blockquote-color': 'Blockquote Text', + '--blockquote-bg': 'Blockquote Background', + '--code-normal': 'Inline code text', + '--code-background': 'Inline code background', + '--text-highlight-bg': 'Highlighted text background', // Interactive Elements - "--interactive-normal": "Interactive Normal", - "--interactive-hover": "Interactive (Hover)", - "--interactive-accent": "Interactive Accent", - "--interactive-accent-hover": "Interactive Accent (Hover)", - "--interactive-success": "Success Color", - "--interactive-error": "Error Color", - "--interactive-warning": "Warning Color", + '--interactive-normal': 'Interactive Normal', + '--interactive-hover': 'Interactive (Hover)', + '--interactive-accent': 'Interactive Accent', + '--interactive-accent-hover': 'Interactive Accent (Hover)', + '--interactive-success': 'Success Color', + '--interactive-error': 'Error Color', + '--interactive-warning': 'Warning Color', // UI Elements - "--titlebar-background": "Titlebar Background", - "--titlebar-background-focused": "Titlebar Background (Focused)", - "--titlebar-text-color": "Titlebar Text", - "--sidebar-background": "Sidebar Background", - "--sidebar-border-color": "Sidebar Border", - "--header-background": "Header Background", - "--header-border-color": "Header Border", - "--vault-name-color": "Vault Name", + '--titlebar-background': 'Titlebar Background', + '--titlebar-background-focused': 'Titlebar Background (Focused)', + '--titlebar-text-color': 'Titlebar Text', + '--sidebar-background': 'Sidebar Background', + '--sidebar-border-color': 'Sidebar Border', + '--header-background': 'Header Background', + '--header-border-color': 'Header Border', + '--vault-name-color': 'Vault Name', // Notices - "--cm-notice-text-default": "Default Notice Text", - "--cm-notice-bg-default": "Default Notice Background", + '--cm-notice-text-default': 'Default Notice Text', + '--cm-notice-bg-default': 'Default Notice Background', // Graph View - "--graph-line": "Graph Line", - "--graph-node": "Graph Node", - "--graph-text": "Graph Text", - "--graph-node-unresolved": "Graph Unresolved Node", - "--graph-node-focused": "Graph Focused Node", - "--graph-node-tag": "Graph Tag Node", - "--graph-node-attachment": "Graph Attachment Node", + '--graph-line': 'Graph Line', + '--graph-node': 'Graph Node', + '--graph-text': 'Graph Text', + '--graph-node-unresolved': 'Graph Unresolved Node', + '--graph-node-focused': 'Graph Focused Node', + '--graph-node-tag': 'Graph Tag Node', + '--graph-node-attachment': 'Graph Attachment Node', // Misc - "--scrollbar-thumb-bg": "Scrollbar Thumb", - "--scrollbar-bg": "Scrollbar Background", - "--divider-color": "Divider", + '--scrollbar-thumb-bg': 'Scrollbar Thumb', + '--scrollbar-bg': 'Scrollbar Background', + '--divider-color': 'Divider', }, descriptions: { // Iconize - "--iconize-icon-color": + '--iconize-icon-color': "Sets the color for all icons added by the Iconize plugin. This will override Iconize's own color settings.", // Backgrounds - "--background-primary": - "Main background color for the entire app, especially for editor and note panes.", - "--background-primary-alt": - "An alternate background color, often used for the active line in the editor.", - "--background-secondary": - "Secondary background, typically used for sidebars and other UI panels.", - "--background-secondary-alt": + '--background-primary': + 'Main background color for the entire app, especially for editor and note panes.', + '--background-primary-alt': + 'An alternate background color, often used for the active line in the editor.', + '--background-secondary': + 'Secondary background, typically used for sidebars and other UI panels.', + '--background-secondary-alt': "An alternate secondary background, used for the file explorer's active file.", - "--background-modifier-border": - "The color of borders on various UI elements like buttons and inputs.", - "--background-modifier-border-hover": - "The border color when you hover over an element.", - "--background-modifier-border-focus": - "The border color for a focused element, like a selected text field.", - "--background-modifier-flair": + '--background-modifier-border': + 'The color of borders on various UI elements like buttons and inputs.', + '--background-modifier-border-hover': 'The border color when you hover over an element.', + '--background-modifier-border-focus': + 'The border color for a focused element, like a selected text field.', + '--background-modifier-flair': "Background color for special UI elements, like the 'Syncing' or 'Indexing' status.", - "--background-modifier-hover": - "The background color of elements when you hover over them (e.g., list items).", - "--background-modifier-active": + '--background-modifier-hover': + 'The background color of elements when you hover over them (e.g., list items).', + '--background-modifier-active': "The background color of an element when it's actively being clicked or is selected.", // Text - "--text-normal": - "The default text color for all notes and most of the UI.", - "--text-muted": - "A slightly faded text color, used for less important information like file metadata.", - "--text-faint": - "The most faded text color, for very subtle UI text or disabled elements.", - "--text-on-accent": - "Text color that appears on top of accented backgrounds (like on a primary button).", - "--text-accent": - "The primary accent color for text, used for links and highlighted UI elements.", - "--text-accent-hover": - "The color of accent text (like links) when you hover over it.", - "--text-selection": - "The background color of text that you have selected with your cursor.", - "--checklist-done-color": - "The color of the checkmark and text for a completed to-do item.", - "--tag-color": "Sets the text color of #tags.", - "--tag-color-hover": - "Sets the text color of #tags when hovering over them.", - "--tag-bg": - "Sets the background color of #tags, allowing for a 'pill' shape.", + '--text-normal': 'The default text color for all notes and most of the UI.', + '--text-muted': + 'A slightly faded text color, used for less important information like file metadata.', + '--text-faint': 'The most faded text color, for very subtle UI text or disabled elements.', + '--text-on-accent': + 'Text color that appears on top of accented backgrounds (like on a primary button).', + '--text-accent': + 'The primary accent color for text, used for links and highlighted UI elements.', + '--text-accent-hover': 'The color of accent text (like links) when you hover over it.', + '--text-selection': 'The background color of text that you have selected with your cursor.', + '--checklist-done-color': 'The color of the checkmark and text for a completed to-do item.', + '--tag-color': 'Sets the text color of #tags.', + '--tag-color-hover': 'Sets the text color of #tags when hovering over them.', + '--tag-bg': "Sets the background color of #tags, allowing for a 'pill' shape.", // Headings - "--h1-color": "The color of H1 heading text.", - "--h2-color": "The color of H2 heading text.", - "--h3-color": "The color of H3 heading text.", - "--h4-color": "The color of H4 heading text.", - "--h5-color": "The color of H5 heading text.", - "--h6-color": "The color of H6 heading text.", + '--h1-color': 'The color of H1 heading text.', + '--h2-color': 'The color of H2 heading text.', + '--h3-color': 'The color of H3 heading text.', + '--h4-color': 'The color of H4 heading text.', + '--h5-color': 'The color of H5 heading text.', + '--h6-color': 'The color of H6 heading text.', // Markdown - "--hr-color": "The color of the horizontal rule line created with `---`.", - "--blockquote-border-color": - "The color of the vertical border on the left side of a blockquote.", - "--blockquote-color": - "The text color for content inside of a blockquote.", - "--blockquote-bg": - "Sets the background color of blockquote elements (>).", - "--code-normal": - "Sets the text color inside inline code (between backticks).", - "--code-background": "Sets the background color for inline code blocks.", - "--text-highlight-bg": - "Sets the background color for highlighted text (==like this==).", + '--hr-color': 'The color of the horizontal rule line created with `---`.', + '--blockquote-border-color': + 'The color of the vertical border on the left side of a blockquote.', + '--blockquote-color': 'The text color for content inside of a blockquote.', + '--blockquote-bg': 'Sets the background color of blockquote elements (>).', + '--code-normal': 'Sets the text color inside inline code (between backticks).', + '--code-background': 'Sets the background color for inline code blocks.', + '--text-highlight-bg': 'Sets the background color for highlighted text (==like this==).', // Interactive Elements - "--interactive-normal": - "The background color for interactive elements like buttons.", - "--interactive-hover": - "The background color for interactive elements when hovered.", - "--interactive-accent": + '--interactive-normal': 'The background color for interactive elements like buttons.', + '--interactive-hover': 'The background color for interactive elements when hovered.', + '--interactive-accent': "The accent color for important interactive elements (e.g., the 'Create' button).", - "--interactive-accent-hover": - "The accent color for important interactive elements when hovered.", - "--interactive-success": - "Color indicating a successful operation (e.g., green).", - "--interactive-error": "Color indicating an error (e.g., red).", - "--interactive-warning": "Color indicating a warning (e.g., yellow).", + '--interactive-accent-hover': + 'The accent color for important interactive elements when hovered.', + '--interactive-success': 'Color indicating a successful operation (e.g., green).', + '--interactive-error': 'Color indicating an error (e.g., red).', + '--interactive-warning': 'Color indicating a warning (e.g., yellow).', // UI Elements - "--titlebar-background": - "The background color of the main window's title bar.", - "--titlebar-background-focused": - "The title bar background color when the window is active.", - "--titlebar-text-color": "The text color in the title bar.", - "--sidebar-background": - "Specifically targets the background of the sidebars.", - "--sidebar-border-color": "The color of the border next to the sidebars.", - "--header-background": - "The background for headers within panes (e.g., note title header).", - "--header-border-color": "The border color below pane headers.", - "--vault-name-color": - "The color of your vault's name in the top-left corner.", - "--cm-notice-text-default": - "Sets the default text color for all notices, unless overridden by a rule.", - "--cm-notice-bg-default": - "Sets the default background color for all notices, unless overridden by a rule.", + '--titlebar-background': "The background color of the main window's title bar.", + '--titlebar-background-focused': 'The title bar background color when the window is active.', + '--titlebar-text-color': 'The text color in the title bar.', + '--sidebar-background': 'Specifically targets the background of the sidebars.', + '--sidebar-border-color': 'The color of the border next to the sidebars.', + '--header-background': 'The background for headers within panes (e.g., note title header).', + '--header-border-color': 'The border color below pane headers.', + '--vault-name-color': "The color of your vault's name in the top-left corner.", + '--cm-notice-text-default': + 'Sets the default text color for all notices, unless overridden by a rule.', + '--cm-notice-bg-default': + 'Sets the default background color for all notices, unless overridden by a rule.', // Graph View - "--graph-line": - "The color of the connection lines between notes in the Graph View.", - "--graph-node": "The color of the circular nodes for existing notes.", - "--graph-text": "The color of the text labels on the graph nodes.", - "--graph-node-unresolved": - "The color of nodes for notes that do not exist yet (unresolved links).", - "--graph-node-focused": - "Color of the node that is focused or hovered (highlighted node).", - "--graph-node-tag": - "Color of nodes representing tags when tags are shown in the graph.", - "--graph-node-attachment": - "Color of nodes representing attachments (e.g., image or other linked files).", + '--graph-line': 'The color of the connection lines between notes in the Graph View.', + '--graph-node': 'The color of the circular nodes for existing notes.', + '--graph-text': 'The color of the text labels on the graph nodes.', + '--graph-node-unresolved': + 'The color of nodes for notes that do not exist yet (unresolved links).', + '--graph-node-focused': 'Color of the node that is focused or hovered (highlighted node).', + '--graph-node-tag': 'Color of nodes representing tags when tags are shown in the graph.', + '--graph-node-attachment': + 'Color of nodes representing attachments (e.g., image or other linked files).', // Misc - "--scrollbar-thumb-bg": - "The color of the draggable part of the scrollbar.", - "--scrollbar-bg": "The color of the scrollbar track (the background).", - "--divider-color": - "The color for general UI separator lines, like the borders between settings.", + '--scrollbar-thumb-bg': 'The color of the draggable part of the scrollbar.', + '--scrollbar-bg': 'The color of the scrollbar track (the background).', + '--divider-color': + 'The color for general UI separator lines, like the borders between settings.', }, }, }; diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts index 4f98e74..e84be72 100644 --- a/src/i18n/locales/fa.ts +++ b/src/i18n/locales/fa.ts @@ -1,648 +1,600 @@ export default { plugin: { - name: "استاد رنگ - v1.2.0", - ribbonTooltip: "تنظیمات استاد رنگ", + name: 'استاد رنگ - v1.2.0', + ribbonTooltip: 'تنظیمات استاد رنگ', }, buttons: { - new: "جدید", - delete: "حذف", - selectOption: "حداقل یک گزینه را انتخاب کنید", - create: "ایجاد", - reset: "بازنشانی", - update: "به‌روزرسانی", - apply: "اعمال", - cancel: "لغو", - reload: "بارگذاری مجدد", - exportFile: "خروجی گرفتن از فایل", - copyJson: "کپی کردن JSON", - importCss: "وارد کردن / چسباندن (.css)", - importJson: "وارد کردن / چسباندن (.json)", - chooseFile: "انتخاب فایل...", - import: "وارد کردن", - select: "انتخاب", - browse: "مرور...", - deleteAnyway: "در هر صورت حذف کن", - restore: "بازگردانی", + new: 'جدید', + delete: 'حذف', + selectOption: 'حداقل یک گزینه را انتخاب کنید', + create: 'ایجاد', + reset: 'بازنشانی', + update: 'به‌روزرسانی', + apply: 'اعمال', + cancel: 'لغو', + reload: 'بارگذاری مجدد', + exportFile: 'خروجی گرفتن از فایل', + copyJson: 'کپی کردن JSON', + importCss: 'وارد کردن / چسباندن (.css)', + importJson: 'وارد کردن / چسباندن (.json)', + chooseFile: 'انتخاب فایل...', + import: 'وارد کردن', + select: 'انتخاب', + browse: 'مرور...', + deleteAnyway: 'در هر صورت حذف کن', + restore: 'بازگردانی', }, settings: { - enablePlugin: "فعال کردن استاد رنگ", + enablePlugin: 'فعال کردن استاد رنگ', enablePluginDesc: - "این گزینه را برای غیرفعال کردن موقت تمام رنگ‌های سفارشی و بازگشت به تم فعال Obsidian خود خاموش کنید.", - language: "زبان", - languageDesc: "زبان رابط کاربری افزونه را تنظیم کنید.", - languageSettingsModalTitle: "تنظیمات زبان", - rtlLayoutName: "فعال کردن چیدمان راست به چپ (RTL)", + 'این گزینه را برای غیرفعال کردن موقت تمام رنگ‌های سفارشی و بازگشت به تم فعال Obsidian خود خاموش کنید.', + language: 'زبان', + languageDesc: 'زبان رابط کاربری افزونه را تنظیم کنید.', + languageSettingsModalTitle: 'تنظیمات زبان', + rtlLayoutName: 'فعال کردن چیدمان راست به چپ (RTL)', rtlLayoutDesc: - "هنگامی که فعال باشد، رابط کاربری افزونه برای پشتیبانی صحیح از زبان‌های راست به چپ برعکس می‌شود.", - searchPlaceholder: "جستجوی متغیرها (نام یا مقدار)...", - regexPlaceholder: "عبارت منظم را وارد کنید و Enter را بزنید...", - allSections: "همه بخش‌ها", - clear: "پاک کردن", - ariaCase: "جستجوی حساس به حروف بزرگ و کوچک", - ariaRegex: "استفاده از عبارت منظم", + 'هنگامی که فعال باشد، رابط کاربری افزونه برای پشتیبانی صحیح از زبان‌های راست به چپ برعکس می‌شود.', + searchPlaceholder: 'جستجوی متغیرها (نام یا مقدار)...', + regexPlaceholder: 'عبارت منظم را وارد کنید و Enter را بزنید...', + allSections: 'همه بخش‌ها', + clear: 'پاک کردن', + ariaCase: 'جستجوی حساس به حروف بزرگ و کوچک', + ariaRegex: 'استفاده از عبارت منظم', themeWarningTooltip: (currentTheme: string) => `تم انجمن "${currentTheme}" فعال است که ممکن است با ظاهر پروفایل تداخل داشته باشد. برای بهترین نتیجه، به تم پیش‌فرض Obsidian بروید یا تم "${currentTheme}" را به عنوان یک پروفایل CSS جدید وارد کرده و مستقیماً آن را ویرایش کنید.`, }, profileManager: { - heading: "مدیریت پروفایل", - activeProfile: "پروفایل فعال", - activeProfileDesc: "مدیریت و جابجایی بین پروفایل‌های رنگ.", - themeType: "نوع تم پروفایل", + heading: 'مدیریت پروفایل', + activeProfile: 'پروفایل فعال', + activeProfileDesc: 'مدیریت و جابجایی بین پروفایل‌های رنگ.', + themeType: 'نوع تم پروفایل', themeTypeDesc: - "تنظیم کنید که آیا این پروفایل باید هنگام فعال شدن یک تم خاص (تاریک/روشن) را اعمال کند.", - themeAuto: "تم پیش‌فرض Obsidian", - themeDark: "اعمال حالت تاریک", - themeLight: "اعمال حالت روشن", - tooltipThemeAuto: - "تم: خودکار (مطابق با Obsidian) (برای تغییر به روشن کلیک کنید)", - tooltipThemeDark: "تم: اعمال حالت تاریک (برای تغییر به خودکار کلیک کنید)", - tooltipThemeLight: "تم: اعمال حالت روشن (برای تغییر به تاریک کلیک کنید)", - tooltipExport: "صادر کردن نمایه (پروفایل) فعلی به صورت فایل JSON", - tooltipCopyJson: "کپی کردن JSON نمایه فعلی در کلیپ‌بورد", + 'تنظیم کنید که آیا این پروفایل باید هنگام فعال شدن یک تم خاص (تاریک/روشن) را اعمال کند.', + themeAuto: 'تم پیش‌فرض Obsidian', + themeDark: 'اعمال حالت تاریک', + themeLight: 'اعمال حالت روشن', + tooltipThemeAuto: 'تم: خودکار (مطابق با Obsidian) (برای تغییر به روشن کلیک کنید)', + tooltipThemeDark: 'تم: اعمال حالت تاریک (برای تغییر به خودکار کلیک کنید)', + tooltipThemeLight: 'تم: اعمال حالت روشن (برای تغییر به تاریک کلیک کنید)', + tooltipExport: 'صادر کردن نمایه (پروفایل) فعلی به صورت فایل JSON', + tooltipCopyJson: 'کپی کردن JSON نمایه فعلی در کلیپ‌بورد', }, options: { - heading: "تنظیمات پیشرفته", - liveUpdateName: "فریم در ثانیه به‌روزرسانی زنده", + heading: 'تنظیمات پیشرفته', + liveUpdateName: 'فریم در ثانیه به‌روزرسانی زنده', liveUpdateDesc: - "تعداد دفعاتی که رابط کاربری در هر ثانیه پیش‌نمایش تغییرات رنگ را هنگام کشیدن نشان می‌دهد تنظیم می‌کند (0 = غیرفعال کردن پیش‌نمایش زنده). مقادیر کمتر می‌تواند عملکرد را بهبود بخشد.", - iconizeModalTitle: "تنظیمات ادغام Iconize", - overrideIconizeName: "نادیده گرفتن رنگ‌های افزونه Iconize", + 'تعداد دفعاتی که رابط کاربری در هر ثانیه پیش‌نمایش تغییرات رنگ را هنگام کشیدن نشان می‌دهد تنظیم می‌کند (0 = غیرفعال کردن پیش‌نمایش زنده). مقادیر کمتر می‌تواند عملکرد را بهبود بخشد.', + iconizeModalTitle: 'تنظیمات ادغام Iconize', + overrideIconizeName: 'نادیده گرفتن رنگ‌های افزونه Iconize', overrideIconizeDesc: - "اجازه دهید استاد رنگ تمام رنگ‌های آیکون‌های افزونه Iconize را کنترل کند. برای بهترین نتایج، تنظیمات رنگ را در خود Iconize غیرفعال کنید.", - cleanupIntervalName: "فاصله زمانی پاکسازی", + 'اجازه دهید استاد رنگ تمام رنگ‌های آیکون‌های افزونه Iconize را کنترل کند. برای بهترین نتایج، تنظیمات رنگ را در خود Iconize غیرفعال کنید.', + cleanupIntervalName: 'فاصله زمانی پاکسازی', cleanupIntervalDesc: - "هر چند ثانیه یکبار افزونه برای پاکسازی آیکون‌های افزونه حذف شده Iconize بررسی می‌کند.", - addCustomVarName: "افزودن متغیر سفارشی", + 'هر چند ثانیه یکبار افزونه برای پاکسازی آیکون‌های افزونه حذف شده Iconize بررسی می‌کند.', + addCustomVarName: 'افزودن متغیر سفارشی', addCustomVarDesc: "یک متغیر CSS جدید که در لیست پیش‌فرض نیست اضافه کنید. نام باید با '--' شروع شود.", - addNewVarButton: "افزودن متغیر جدید...", - resetPluginName: "بازنشانی تنظیمات افزونه", + addNewVarButton: 'افزودن متغیر جدید...', + resetPluginName: 'بازنشانی تنظیمات افزونه', resetPluginDesc: - "این کار تمام پروفایل‌ها، قطعه کدها، تنظیمات و پس‌زمینه‌ها را حذف می‌کند و افزونه را به حالت اولیه‌اش بازنشانی می‌کند. این عمل نیاز به بارگذاری مجدد برنامه دارد و قابل بازگشت نیست.", - resetPluginButton: "بازنشانی تمام داده‌ها...", - backgroundName: "تنظیم پس‌زمینه سفارشی", - backgroundDesc: "مدیریت تصویر یا ویدئو پس‌زمینه برای این پروفایل.", - backgroundModalSettingsTitle: "تنظیمات پس‌زمینه", - backgroundEnableName: "فعال کردن پس‌زمینه", - backgroundEnableDesc: - "نمایش یا پنهان کردن پس‌زمینه سفارشی برای این پروفایل را تغییر دهید.", - convertImagesName: "تبدیل تصاویر به JPG", + 'این کار تمام پروفایل‌ها، قطعه کدها، تنظیمات و پس‌زمینه‌ها را حذف می‌کند و افزونه را به حالت اولیه‌اش بازنشانی می‌کند. این عمل نیاز به بارگذاری مجدد برنامه دارد و قابل بازگشت نیست.', + resetPluginButton: 'بازنشانی تمام داده‌ها...', + backgroundName: 'تنظیم پس‌زمینه سفارشی', + backgroundDesc: 'مدیریت تصویر یا ویدئو پس‌زمینه برای این پروفایل.', + backgroundModalSettingsTitle: 'تنظیمات پس‌زمینه', + backgroundEnableName: 'فعال کردن پس‌زمینه', + backgroundEnableDesc: 'نمایش یا پنهان کردن پس‌زمینه سفارشی برای این پروفایل را تغییر دهید.', + convertImagesName: 'تبدیل تصاویر به JPG', convertImagesDesc: - "تبدیل خودکار تصاویر PNG، WEBP، یا BMP به JPG هنگام بارگذاری برای کاهش حجم فایل و بهبود عملکرد بارگیری. (توجه: شفافیت از دست خواهد رفت)", - jpgQualityName: "کیفیت JPG", + 'تبدیل خودکار تصاویر PNG، WEBP، یا BMP به JPG هنگام بارگذاری برای کاهش حجم فایل و بهبود عملکرد بارگیری. (توجه: شفافیت از دست خواهد رفت)', + jpgQualityName: 'کیفیت JPG', jpgQualityDesc: - "کیفيت فشرده‌سازی (از 1 تا 100) را تنظیم کنید. مقادیر پایین‌تر به معنای فایل‌های کوچک‌تر اما کیفیت پایین‌تر است.", - badgeNotInstalled: "نصب نشده", - videoOpacityName: "شفافیت ویدئو", - videoOpacityDesc: "کنترل شفافیت پس‌زمینه ویدئو برای خوانایی بهتر.", - videoMuteName: "بی‌صدا کردن ویدئو", - videoMuteDesc: "بی‌صدا کردن پس‌زمینه ویدئو. به شدت توصیه می‌شود.", - settingType: "نوع تنظیمات", - settingTypeImage: "تصویر", - settingTypeVideo: "ویدئو", + 'کیفيت فشرده‌سازی (از 1 تا 100) را تنظیم کنید. مقادیر پایین‌تر به معنای فایل‌های کوچک‌تر اما کیفیت پایین‌تر است.', + badgeNotInstalled: 'نصب نشده', + videoOpacityName: 'شفافیت ویدئو', + videoOpacityDesc: 'کنترل شفافیت پس‌زمینه ویدئو برای خوانایی بهتر.', + videoMuteName: 'بی‌صدا کردن ویدئو', + videoMuteDesc: 'بی‌صدا کردن پس‌زمینه ویدئو. به شدت توصیه می‌شود.', + settingType: 'نوع تنظیمات', + settingTypeImage: 'تصویر', + settingTypeVideo: 'ویدئو', }, snippets: { - heading: "قطعه کدهای CSS", - createButton: "ایجاد قطعه کد جدید", - noSnippetsDesc: "هنوز هیچ قطعه کد CSS برای این پروفایل ایجاد نشده است.", - global: "اسنیپت جهانی", - globalName: "ذخیره به عنوان قطعه کد سراسری", - globalDesc: "یک قطعه کد سراسری بر روی تمام پروفایل‌های شما اعمال می‌شود.", + heading: 'قطعه کدهای CSS', + createButton: 'ایجاد قطعه کد جدید', + noSnippetsDesc: 'هنوز هیچ قطعه کد CSS برای این پروفایل ایجاد نشده است.', + global: 'اسنیپت جهانی', + globalName: 'ذخیره به عنوان قطعه کد سراسری', + globalDesc: 'یک قطعه کد سراسری بر روی تمام پروفایل‌های شما اعمال می‌شود.', }, categories: { - pluginintegrations: "ادغام با افزونه‌ها", - backgrounds: "پس‌زمینه‌ها", - text: "متن", - interactive: "عناصر تعاملی", - ui: "عناصر رابط کاربری", - misc: "متفرقه", - graph: "نمای گراف", - markdown: "مارک‌داون", - notices: "اعلان‌ها", - custom: "متغیرهای سفارشی", - customDesc: "متغیر اضافه شده توسط کاربر.", - helpTextPre: "متغیری که به دنبال آن هستید را پیدا نمی‌کنید؟ ", - helpTextLink: "لیست رسمی متغیرهای CSS Obsidian را مرور کنید.", + pluginintegrations: 'ادغام با افزونه‌ها', + backgrounds: 'پس‌زمینه‌ها', + text: 'متن', + interactive: 'عناصر تعاملی', + ui: 'عناصر رابط کاربری', + misc: 'متفرقه', + graph: 'نمای گراف', + markdown: 'مارک‌داون', + notices: 'اعلان‌ها', + custom: 'متغیرهای سفارشی', + customDesc: 'متغیر اضافه شده توسط کاربر.', + helpTextPre: 'متغیری که به دنبال آن هستید را پیدا نمی‌کنید؟ ', + helpTextLink: 'لیست رسمی متغیرهای CSS Obsidian را مرور کنید.', }, modals: { newProfile: { - title: "ایجاد پروفایل جدید", - nameLabel: "نام پروفایل", - namePlaceholder: "نام پروفایل را وارد کنید...", + title: 'ایجاد پروفایل جدید', + nameLabel: 'نام پروفایل', + namePlaceholder: 'نام پروفایل را وارد کنید...', }, deleteProfile: { - title: "حذف پروفایل", + title: 'حذف پروفایل', confirmation: (name: string) => `آیا مطمئن هستید که می‌خواهید پروفایل "${name}" را حذف کنید؟ این عمل قابل بازگشت نیست.`, }, jsonImport: { - title: "چسباندن یا وارد کردن JSON پروفایل", - desc1: "می‌توانید یک JSON پروفایل را در کادر زیر بچسبانید.", + title: 'چسباندن یا وارد کردن JSON پروفایل', + desc1: 'می‌توانید یک JSON پروفایل را در کادر زیر بچسبانید.', placeholder: '{ "name": "...", "profile": { ... } }', - settingName: "وارد کردن از فایل", - settingDesc: - "یا، یک فایل پروفایل (.json) را از کامپیوتر خود انتخاب کنید.", - replaceActiveButton: "جایگزینی فعال", - createNewButton: "ایجاد جدید", + settingName: 'وارد کردن از فایل', + settingDesc: 'یا، یک فایل پروفایل (.json) را از کامپیوتر خود انتخاب کنید.', + replaceActiveButton: 'جایگزینی فعال', + createNewButton: 'ایجاد جدید', }, cssImport: { - title: "وارد کردن / چسباندن CSS و ایجاد پروفایل", - titleEdit: "ویرایش پروفایل CSS", - note: "توجه: CSS چسبانده شده می‌تواند بر رابط کاربری تأثیر بگذارد، فقط با CSS معتبر ادامه دهید.", - importFromFile: "وارد کردن از فایل", - importFromFileDesc: "یا، یک فایل (.css) را از کامپیوتر خود انتخاب کنید.", - importFromTheme: "وارد کردن از تم نصب شده", - importFromThemeDesc: - "به سرعت CSS را از یکی از تم‌های نصب شده خود بارگیری کنید.", - noThemes: "هیچ تمی نصب نشده است", + title: 'وارد کردن / چسباندن CSS و ایجاد پروفایل', + titleEdit: 'ویرایش پروفایل CSS', + note: 'توجه: CSS چسبانده شده می‌تواند بر رابط کاربری تأثیر بگذارد، فقط با CSS معتبر ادامه دهید.', + importFromFile: 'وارد کردن از فایل', + importFromFileDesc: 'یا، یک فایل (.css) را از کامپیوتر خود انتخاب کنید.', + importFromTheme: 'وارد کردن از تم نصب شده', + importFromThemeDesc: 'به سرعت CSS را از یکی از تم‌های نصب شده خود بارگیری کنید.', + noThemes: 'هیچ تمی نصب نشده است', }, snippetEditor: { - title: "ایجاد قطعه کد CSS جدید", - titleEdit: "ویرایش قطعه کد CSS", - nameLabel: "نام قطعه کد", - namePlaceholder: "نام قطعه کد را وارد کنید...", - importFromSnippet: "وارد کردن از قطعه کد نصب شده", + title: 'ایجاد قطعه کد CSS جدید', + titleEdit: 'ویرایش قطعه کد CSS', + nameLabel: 'نام قطعه کد', + namePlaceholder: 'نام قطعه کد را وارد کنید...', + importFromSnippet: 'وارد کردن از قطعه کد نصب شده', importFromSnippetDesc: - "به سرعت CSS را از یکی از قطعه کدهای نصب شده Obsidian خود بارگیری کنید.", - noSnippets: "هیچ قطعه کدی نصب نشده است", - cssPlaceholder: "کد CSS خود را اینجا جایگذاری کنید...", + 'به سرعت CSS را از یکی از قطعه کدهای نصب شده Obsidian خود بارگیری کنید.', + noSnippets: 'هیچ قطعه کدی نصب نشده است', + cssPlaceholder: 'کد CSS خود را اینجا جایگذاری کنید...', }, confirmation: { - resetProfileTitle: "تأیید بازنشانی پروفایل", + resetProfileTitle: 'تأیید بازنشانی پروفایل', resetProfileDesc: - "آیا مطمئن هستید که می‌خواهید این پروفایل را به آخرین عکس فوری پین شده بازنشانی کنید؟ این کار رنگ‌های فعلی شما را بازنویسی می‌کند و قابل بازگشت نیست.", + 'آیا مطمئن هستید که می‌خواهید این پروفایل را به آخرین عکس فوری پین شده بازنشانی کنید؟ این کار رنگ‌های فعلی شما را بازنویسی می‌کند و قابل بازگشت نیست.', deleteSnippetTitle: (name: string) => `حذف قطعه کد: ${name}`, deleteSnippetDesc: - "آیا مطمئن هستید که می‌خواهید این قطعه کد را حذف کنید؟ این عمل قابل بازگشت نیست.", - resetPluginTitle: "آیا مطمئن هستید؟", + 'آیا مطمئن هستید که می‌خواهید این قطعه کد را حذف کنید؟ این عمل قابل بازگشت نیست.', + resetPluginTitle: 'آیا مطمئن هستید؟', resetPluginDesc: - "این کار تمام داده‌های Color Master شما (پروفایل‌ها، قطعه کدها، تنظیمات و پس‌زمینه‌ها) را برای همیشه حذف می‌کند. این غیرقابل برگشت است.", - deleteGlobalBgTitle: "تأیید حذف پس‌زمینه", + 'این کار تمام داده‌های Color Master شما (پروفایل‌ها، قطعه کدها، تنظیمات و پس‌زمینه‌ها) را برای همیشه حذف می‌کند. این غیرقابل برگشت است.', + deleteGlobalBgTitle: 'تأیید حذف پس‌زمینه', deleteGlobalBgDesc: - "آیا مطمئن هستید که می‌خواهید این پس‌زمینه را برای همیشه حذف کنید؟ پروفایل‌های زیر از آن استفاده می‌کنند و بازنشانی خواهند شد:", - deleteBackgroundTitle: "حذف پس‌زمینه؟", + 'آیا مطمئن هستید که می‌خواهید این پس‌زمینه را برای همیشه حذف کنید؟ پروفایل‌های زیر از آن استفاده می‌کنند و بازنشانی خواهند شد:', + deleteBackgroundTitle: 'حذف پس‌زمینه؟', deleteBackgroundDesc: (name: string) => `آیا مطمئن هستید که می‌خواهید '${name}' را برای همیشه حذف کنید؟`, - deleteLangTitle: "حذف زبان", + deleteLangTitle: 'حذف زبان', deleteLangDesc: (name: string) => `آیا مطمئن هستید که می‌خواهید بسته زبان "${name}" را حذف کنید؟ این عمل قابل بازگشت نیست.`, - restoreLangTitle: "بازگردانی زبان؟", + restoreLangTitle: 'بازگردانی زبان؟', restoreLangDesc: (name: string) => `آیا مطمئن هستید که می‌خواهید تغییرات سفارشی ${name} را حذف کرده و پیش‌فرض‌ها را بازگردانید؟`, }, noticeRules: { - titleText: "قوانین پیشرفته رنگ متن", - titleBg: "قوانین پیشرفته رنگ پس‌زمینه", - desc: "قوانین اولویت‌بندی شده برای رنگ‌آمیزی اعلان‌ها بر اساس محتوای آنها ایجاد کنید. اولین قانون منطبق از بالا به پایین اعمال خواهد شد.", - addNewRule: "افزودن قانون جدید", - keywordPlaceholder: "یک کلمه کلیدی تایپ کنید و فاصله را فشار دهید...", - useRegex: "عبارت منظم", - highlightOnly: "فقط کلمه کلیدی را هایلایت کن", + titleText: 'قوانین پیشرفته رنگ متن', + titleBg: 'قوانین پیشرفته رنگ پس‌زمینه', + desc: 'قوانین اولویت‌بندی شده برای رنگ‌آمیزی اعلان‌ها بر اساس محتوای آنها ایجاد کنید. اولین قانون منطبق از بالا به پایین اعمال خواهد شد.', + addNewRule: 'افزودن قانون جدید', + keywordPlaceholder: 'یک کلمه کلیدی تایپ کنید و فاصله را فشار دهید...', + useRegex: 'عبارت منظم', + highlightOnly: 'فقط کلمه کلیدی را هایلایت کن', }, duplicateProfile: { - title: "نام پروفایل تکراری", - descParts: [ - `نام پروفایل "`, - `" از قبل وجود دارد. لطفاً نام دیگری انتخاب کنید.`, - ], - placeholder: "نام پروفایل جدید را وارد کنید...", + title: 'نام پروفایل تکراری', + descParts: [`نام پروفایل "`, `" از قبل وجود دارد. لطفاً نام دیگری انتخاب کنید.`], + placeholder: 'نام پروفایل جدید را وارد کنید...', }, customVar: { - title: "افزودن متغیر جدید CSS سفارشی", - desc: "یک متغیر CSS جدید تعریف کنید (مانند --my-color: #f00). این متغیر به پروفایل فعال شما اضافه خواهد شد.", - displayName: "نام نمایشی", - displayNameDesc: - "یک نام دوستانه برای متغیر شما (مانند 'رنگ اصلی سفارشی من').", - displayNamePlaceholder: "مثال: رنگ اصلی من", - varName: "نام متغیر", - varNameDesc: - "نام واقعی متغیر CSS. باید با '--' شروع شود (مانند '--my-primary-color').", - varNamePlaceholder: "مثال: --my-primary-color", - varValue: "مقدار متغیر", - varType: "نوع متغیر", - varTypeDesc: "نوع داده‌ای که این متغیر نگهداری می‌کند را انتخاب کنید.", + title: 'افزودن متغیر جدید CSS سفارشی', + desc: 'یک متغیر CSS جدید تعریف کنید (مانند --my-color: #f00). این متغیر به پروفایل فعال شما اضافه خواهد شد.', + displayName: 'نام نمایشی', + displayNameDesc: "یک نام دوستانه برای متغیر شما (مانند 'رنگ اصلی سفارشی من').", + displayNamePlaceholder: 'مثال: رنگ اصلی من', + varName: 'نام متغیر', + varNameDesc: "نام واقعی متغیر CSS. باید با '--' شروع شود (مانند '--my-primary-color').", + varNamePlaceholder: 'مثال: --my-primary-color', + varValue: 'مقدار متغیر', + varType: 'نوع متغیر', + varTypeDesc: 'نوع داده‌ای که این متغیر نگهداری می‌کند را انتخاب کنید.', types: { - color: "رنگ", - size: "اندازه (مانند px, em)", - text: "متن", - number: "عدد", + color: 'رنگ', + size: 'اندازه (مانند px, em)', + text: 'متن', + number: 'عدد', }, varValueDesc: "مقدار متغیر CSS (مانند 'red', '#ff0000', 'rgb(255,0,0)').", - varValuePlaceholder: "مثال: #FF0000 یا قرمز", - description: "توضیحات (اختیاری)", - descriptionDesc: "توضیح مختصری در مورد آنچه این متغیر کنترل می‌کند.", - descriptionPlaceholder: "مثال: رنگ اصلی برای عناوین", - addVarButton: "افزودن", - textValuePlaceholder: "مقدار متنی را وارد کنید...", + varValuePlaceholder: 'مثال: #FF0000 یا قرمز', + description: 'توضیحات (اختیاری)', + descriptionDesc: 'توضیح مختصری در مورد آنچه این متغیر کنترل می‌کند.', + descriptionPlaceholder: 'مثال: رنگ اصلی برای عناوین', + addVarButton: 'افزودن', + textValuePlaceholder: 'مقدار متنی را وارد کنید...', }, addBackground: { - title: "افزودن پس‌زمینه جدید (تصویر/ویدئو)", - importFromFile: "وارد کردن از فایل", - importFromFileDesc: "وارد کردن یک فایل تصویر یا ویدئو از کامپیوتر شما.", - pasteBoxPlaceholder: - "پس‌زمینه یا URL را بکشید و رها کنید / یا اینجا بچسبانید (Ctrl+V)", - dropToAdd: "برای افزودن پس‌زمینه رها کنید...", - processing: "در حال پردازش", + title: 'افزودن پس‌زمینه جدید (تصویر/ویدئو)', + importFromFile: 'وارد کردن از فایل', + importFromFileDesc: 'وارد کردن یک فایل تصویر یا ویدئو از کامپیوتر شما.', + pasteBoxPlaceholder: 'پس‌زمینه یا URL را بکشید و رها کنید / یا اینجا بچسبانید (Ctrl+V)', + dropToAdd: 'برای افزودن پس‌زمینه رها کنید...', + processing: 'در حال پردازش', }, fileConflict: { - title: "فایل موجود است", + title: 'فایل موجود است', desc: (name: string) => `فایلی با نام '${name}' قبلاً در پوشه پس‌زمینه‌های شما وجود دارد. چه کاری می‌خواهید انجام دهید؟`, - replaceButton: "جایگزینی فایل", - keepButton: "نگه داشتن هر دو (تغییر نام)", + replaceButton: 'جایگزینی فایل', + keepButton: 'نگه داشتن هر دو (تغییر نام)', }, backgroundBrowser: { - title: "مرورگر پس‌زمینه‌ها", - noImages: "هیچ تصویر یا ویدئویی در پوشه پس‌زمینه‌های شما یافت نشد.", + title: 'مرورگر پس‌زمینه‌ها', + noImages: 'هیچ تصویر یا ویدئویی در پوشه پس‌زمینه‌های شما یافت نشد.', }, advancedReset: { - title: "بازنشانی پیشرفته داده‌ها", - desc: "اجزای داده‌ای را که می‌خواهید برای همیشه حذف شوند، انتخاب کنید. این عمل غیرقابل بازگشت است.", - profilesLabel: "پروفایل‌ها و اسنپ‌شات‌ها", + title: 'بازنشانی پیشرفته داده‌ها', + desc: 'اجزای داده‌ای را که می‌خواهید برای همیشه حذف شوند، انتخاب کنید. این عمل غیرقابل بازگشت است.', + profilesLabel: 'پروفایل‌ها و اسنپ‌شات‌ها', profilesDesc: - "حذف تمام پروفایل‌های سفارشی و اسنپ‌شات‌های پین‌شده. (پروفایل‌های داخلی حفظ می‌شوند).", - snippetsLabel: "قطعه کدهای سراسری و پروفایل", - snippetsDesc: "حذف تمام قطعه کدهای CSS، چه سراسری و چه داخلی پروفایل‌ها.", - backgroundsLabel: "پوشه پس‌زمینه‌ها", + 'حذف تمام پروفایل‌های سفارشی و اسنپ‌شات‌های پین‌شده. (پروفایل‌های داخلی حفظ می‌شوند).', + snippetsLabel: 'قطعه کدهای سراسری و پروفایل', + snippetsDesc: 'حذف تمام قطعه کدهای CSS، چه سراسری و چه داخلی پروفایل‌ها.', + backgroundsLabel: 'پوشه پس‌زمینه‌ها', backgroundsDesc: - "پوشهٔ پس‌زمینه‌ها را در پوشهٔ تنظیمات Obsidian (configDir/backgrounds) به‌طور کامل همراه با تمامی رسانه‌ها حذف می‌کند.", - settingsLabel: "تنظیمات افزونه", - settingsDesc: - "بازنشانی زبان، FPS، چیدمان و سایر گزینه‌ها به حالت پیش‌فرض.", - languagesLabel: "زبان‌های سفارشی", - languagesDesc: "حذف تمام زبان‌های سفارشی ایجاد شده توسط کاربر.", + 'پوشهٔ پس‌زمینه‌ها را در پوشهٔ تنظیمات Obsidian (configDir/backgrounds) به‌طور کامل همراه با تمامی رسانه‌ها حذف می‌کند.', + settingsLabel: 'تنظیمات افزونه', + settingsDesc: 'بازنشانی زبان، FPS، چیدمان و سایر گزینه‌ها به حالت پیش‌فرض.', + languagesLabel: 'زبان‌های سفارشی', + languagesDesc: 'حذف تمام زبان‌های سفارشی ایجاد شده توسط کاربر.', }, addLang: { - title: "افزودن زبان جدید", - desc: "یک بسته زبان سفارشی جدید ایجاد کنید. پس از ایجاد می‌توانید ترجمه‌ها را ویرایش کنید.", - nameLabel: "نام زبان (بومی)", - nameDesc: "نام زبان به خط خود زبان.", - namePlaceholder: "مثال: فارسی", - codeLabel: "کد زبان", + title: 'افزودن زبان جدید', + desc: 'یک بسته زبان سفارشی جدید ایجاد کنید. پس از ایجاد می‌توانید ترجمه‌ها را ویرایش کنید.', + nameLabel: 'نام زبان (بومی)', + nameDesc: 'نام زبان به خط خود زبان.', + namePlaceholder: 'مثال: فارسی', + codeLabel: 'کد زبان', codeDesc: "یک کد ISO 639 منحصر به فرد (مثال: 'en', 'ar', 'zh').", - codePlaceholder: "مثال: fa", - rtlLabel: "زبان RTL", - rtlDesc: - "این گزینه را فعال کنید اگر زبان از راست به چپ نوشته می‌شود (مانند عربی، اردو).", + codePlaceholder: 'مثال: fa', + rtlLabel: 'زبان RTL', + rtlDesc: 'این گزینه را فعال کنید اگر زبان از راست به چپ نوشته می‌شود (مانند عربی، اردو).', }, translator: { title: (langName: string) => `ویرایش ترجمه‌ها: ${langName}`, - searchPlaceholder: "جستجوی کلیدهای ترجمه...", - copyJson: "کپی JSON", - pasteJson: "چسباندن JSON", - exportFile: "خروجی گرفتن", - importFile: "وارد کردن", - showMissing: "فقط موارد ترجمه نشده", - showAll: "نمایش همه", - showMore: "نمایش بیشتر", - showLess: "نمایش کمتر", - noMatches: "هیچ ترجمه‌ای با جستجوی شما مطابقت ندارد.", - dynamicValue: "ارزش دینامیکی", + searchPlaceholder: 'جستجوی کلیدهای ترجمه...', + copyJson: 'کپی JSON', + pasteJson: 'چسباندن JSON', + exportFile: 'خروجی گرفتن', + importFile: 'وارد کردن', + showMissing: 'فقط موارد ترجمه نشده', + showAll: 'نمایش همه', + showMore: 'نمایش بیشتر', + showLess: 'نمایش کمتر', + noMatches: 'هیچ ترجمه‌ای با جستجوی شما مطابقت ندارد.', + dynamicValue: 'ارزش دینامیکی', }, langInfo: { - title: "درباره زبان‌های سفارشی", + title: 'درباره زبان‌های سفارشی', desc: `این ویژگی به شما امکان می‌دهد ترجمه‌های خود را برای افزونه ایجاد کنید.\n\n**چرا این ویژگی اضافه شد؟**\n\nصادقانه بگویم، من به عنوان تنها توسعه‌دهنده این افزونه، برایم دشوار است که همزمان با افزودن ویژگی‌های جدید، آن‌ها را به بیش از 20 زبان ترجمه کنم. این ویژگی دعوتی از شما برای کمک و بهتر کردن افزونه برای همگان است.\n\n**چگونه می‌توانید کمک کنید:**\n\n1. یک زبان جدید ایجاد کنید (مثلاً "اسپانیایی").\n2. روی نماد "ویرایش" (مداد) کلیک کنید.\n3. می‌توانید از "کپی JSON" یک زبان موجود (مانند انگلیسی) استفاده کنید و از ChatGPT بخواهید آن را ترجمه کند.\n4. از "چسباندن JSON" یا "وارد کردن فایل" برای بارگذاری ترجمه خود استفاده کنید.\n5. پس از رضایت، فایل JSON را برای من ارسال کنید (از طریق Pull Request در GitHub) تا با خوشحالی آن را به عنوان یک **زبان اصلی** جدید ادغام کنم.\n\nاین ویژگی همچنین به شما امکان می‌دهد اشتباهات تایپی را اصلاح کرده یا ترجمه‌های زبان‌های اصلی را برای استفاده شخصی خود بهبود بخشید.`, }, }, notices: { - pluginEnabled: "استاد رنگ فعال شد", - pluginDisabled: "استاد رنگ غیرفعال شد", - profilePinned: "رنگ‌های پروفایل با موفقیت پین شدند!", - profileReset: "پروفایل به عکس فوری پین شده بازنشانی شد.", - noPinnedSnapshot: "هیچ عکس فوری پین شده‌ای برای این پروفایل یافت نشد.", - profileNotFound: "پروفایل فعال یافت نشد.", - noProfilesFound: "هیچ پروفایلی یافت نشد.", + pluginEnabled: 'استاد رنگ فعال شد', + pluginDisabled: 'استاد رنگ غیرفعال شد', + profilePinned: 'رنگ‌های پروفایل با موفقیت پین شدند!', + profileReset: 'پروفایل به عکس فوری پین شده بازنشانی شد.', + noPinnedSnapshot: 'هیچ عکس فوری پین شده‌ای برای این پروفایل یافت نشد.', + profileNotFound: 'پروفایل فعال یافت نشد.', + noProfilesFound: 'هیچ پروفایلی یافت نشد.', activeProfileSwitched: (name: string) => `پروفایل فعال: ${name}`, - graphColorsApplied: "رنگ‌های گراف اعمال شد!", - invalidJson: "JSON نامعتبر است.", - jsonMustHaveName: - "JSON وارد شده برای ایجاد پروفایل جدید باید دارای ویژگی 'name' باشد.", - profileCreatedSuccess: (name: string) => - `پروفایل "${name}" با موفقیت ایجاد شد.`, + graphColorsApplied: 'رنگ‌های گراف اعمال شد!', + invalidJson: 'JSON نامعتبر است.', + jsonMustHaveName: "JSON وارد شده برای ایجاد پروفایل جدید باید دارای ویژگی 'name' باشد.", + profileCreatedSuccess: (name: string) => `پروفایل "${name}" با موفقیت ایجاد شد.`, profileImportedSuccess: `پروفایل با موفقیت وارد شد.`, - noActiveProfileToCopy: "هیچ پروفایل فعالی برای کپی وجود ندارد.", - noActiveProfileToExport: "هیچ پروفایل فعالی برای خروجی گرفتن وجود ندارد.", - snippetCssCopied: "CSS قطعه کد در کلیپ‌بورد کپی شد!", - snippetEmpty: "این قطعه کد خالی است.", - cssContentEmpty: "محتوای CSS نمی‌تواند خالی باشد.", - snippetNameExists: (name: string) => - `نام قطعه کد "${name}" از قبل وجود دارد.`, - profileNameExists: (name: string) => - `نام پروفایل "${name}" از قبل وجود دارد.`, + noActiveProfileToCopy: 'هیچ پروفایل فعالی برای کپی وجود ندارد.', + noActiveProfileToExport: 'هیچ پروفایل فعالی برای خروجی گرفتن وجود ندارد.', + snippetCssCopied: 'CSS قطعه کد در کلیپ‌بورد کپی شد!', + snippetEmpty: 'این قطعه کد خالی است.', + cssContentEmpty: 'محتوای CSS نمی‌تواند خالی باشد.', + snippetNameExists: (name: string) => `نام قطعه کد "${name}" از قبل وجود دارد.`, + profileNameExists: (name: string) => `نام پروفایل "${name}" از قبل وجود دارد.`, profileUpdated: (name: string) => `پروفایل "${name}" به‌روزرسانی شد.`, snippetUpdated: (name: string) => `قطعه کد "${name}" به‌روزرسانی شد.`, snippetCreated: (name: string) => `قطعه کد "${name}" با موفقیت ایجاد شد!`, - snippetDeleted: "قطعه کد با موفقیت حذف شد.", - snippetScopeMove: - "برای جابجایی قطعه کد بین بخش‌ها از پنجره ویرایش استفاده کنید.", - profileCreatedFromCss: (name: string) => - `پروفایل "${name}" با موفقیت ایجاد شد!`, - noColorHistory: "هیچ تاریخچه رنگی برای بازیابی وجود ندارد.", + snippetDeleted: 'قطعه کد با موفقیت حذف شد.', + snippetScopeMove: 'برای جابجایی قطعه کد بین بخش‌ها از پنجره ویرایش استفاده کنید.', + profileCreatedFromCss: (name: string) => `پروفایل "${name}" با موفقیت ایجاد شد!`, + noColorHistory: 'هیچ تاریخچه رنگی برای بازیابی وجود ندارد.', colorRestored: (color: string) => `بازیابی شد: ${color}`, - textboxEmpty: - "کادر متنی خالی است. ابتدا مقداری JSON بچسبانید یا یک فایل وارد کنید.", - fileLoaded: (fileName: string) => - `فایل "${fileName}" در ناحیه متنی بارگذاری شد.`, - exportSuccess: "پروفایل با موفقیت خروجی گرفته شد!", - jsonCopied: "JSON نمایه با موفقیت در کلیپ‌بورد کپی شد!", + textboxEmpty: 'کادر متنی خالی است. ابتدا مقداری JSON بچسبانید یا یک فایل وارد کنید.', + fileLoaded: (fileName: string) => `فایل "${fileName}" در ناحیه متنی بارگذاری شد.`, + exportSuccess: 'پروفایل با موفقیت خروجی گرفته شد!', + jsonCopied: 'JSON نمایه با موفقیت در کلیپ‌بورد کپی شد!', resetSuccess: - "داده‌های استاد رنگ حذف شد. لطفاً برای اعمال تغییرات Obsidian را مجدداً بارگذاری کنید.", - fpsUpdated: (value: number) => - `فریم در ثانیه به‌روزرسانی زنده روی ${value} تنظیم شد`, - invalidProfileObject: "JSON به نظر نمی‌رسد یک شی پروفایل معتبر باشد.", + 'داده‌های استاد رنگ حذف شد. لطفاً برای اعمال تغییرات Obsidian را مجدداً بارگذاری کنید.', + fpsUpdated: (value: number) => `فریم در ثانیه به‌روزرسانی زنده روی ${value} تنظیم شد`, + invalidProfileObject: 'JSON به نظر نمی‌رسد یک شی پروفایل معتبر باشد.', profileCreated: (name: string) => `پروفایل "${name}" با موفقیت ایجاد شد!`, - settingsSaved: "تنظیمات با موفقیت اعمال شد!", + settingsSaved: 'تنظیمات با موفقیت اعمال شد!', testSentence: (word: string) => `رنگ اعلان برای "${word}" به این شکل است:`, - varNameEmpty: "نام متغیر نمی‌تواند خالی باشد.", + varNameEmpty: 'نام متغیر نمی‌تواند خالی باشد.', varNameFormat: "نام متغیر باید با '--' شروع شود.", varExists: (name: string) => `متغیر "${name}" از قبل وجود دارد.`, varAdded: (name: string) => `متغیر "${name}" با موفقیت اضافه شد.`, iconizeNotFound: - "افزونه Iconize یافت نشد. لطفاً برای استفاده از این ویژگی آن را نصب و فعال کنید.", - themeCssLoaded: (theme: string) => - `CSS با موفقیت از تم "${theme}" بارگیری شد.`, + 'افزونه Iconize یافت نشد. لطفاً برای استفاده از این ویژگی آن را نصب و فعال کنید.', + themeCssLoaded: (theme: string) => `CSS با موفقیت از تم "${theme}" بارگیری شد.`, themeReadFailed: (theme: string) => `فایل تم "${theme}" خوانده نشد. ممکن است فایل محافظت شده یا موجود نباشد.`, - snippetLoaded: (snippet: string) => - `CSS با موفقیت از قطعه کد "${snippet}" بارگیری شد.`, - snippetReadFailed: (snippet: string) => - `فایل قطعه کد "${snippet}" خوانده نشد.`, - themeSwitchedLight: "به حالت روشن تغییر کرد", - themeSwitchedDark: "به حالت تاریک تغییر کرد", - themeSwitchedAuto: "به حالت خودکار تغییر کرد", - bgSet: "پس‌زمینه با موفقیت تنظیم شد.", - bgRemoved: "پس‌زمینه حذف شد.", - backgroundLoadError: "بارگیری پس‌زمینه ناموفق بود.", - noBgToRemove: "هیچ پس‌زمینه فعالی برای حذف در این نمایه وجود ندارد.", - bgDeleted: "پس‌زمینه و فایل آن حذف شدند.", - backgroundUrlLoadError: "دانلود پس‌زمینه از URL ناموفق بود.", - backgroundPasteError: - "محتوای چسبانده شده یک فایل پس‌زمینه یا URL معتبر نیست.", + snippetLoaded: (snippet: string) => `CSS با موفقیت از قطعه کد "${snippet}" بارگیری شد.`, + snippetReadFailed: (snippet: string) => `فایل قطعه کد "${snippet}" خوانده نشد.`, + themeSwitchedLight: 'به حالت روشن تغییر کرد', + themeSwitchedDark: 'به حالت تاریک تغییر کرد', + themeSwitchedAuto: 'به حالت خودکار تغییر کرد', + bgSet: 'پس‌زمینه با موفقیت تنظیم شد.', + bgRemoved: 'پس‌زمینه حذف شد.', + backgroundLoadError: 'بارگیری پس‌زمینه ناموفق بود.', + noBgToRemove: 'هیچ پس‌زمینه فعالی برای حذف در این نمایه وجود ندارد.', + bgDeleted: 'پس‌زمینه و فایل آن حذف شدند.', + backgroundUrlLoadError: 'دانلود پس‌زمینه از URL ناموفق بود.', + backgroundPasteError: 'محتوای چسبانده شده یک فایل پس‌زمینه یا URL معتبر نیست.', downloadingFromUrl: (url: string) => `در حال دانلود از ${url}...`, - pastedBase64Image: "تصویر Base64 چسبانده شد", + pastedBase64Image: 'تصویر Base64 چسبانده شد', pastedImage: (name: string) => `تصویر "${name}" چسبانده شد`, - invalidFilename: "نام فایل نامعتبر است.", + invalidFilename: 'نام فایل نامعتبر است.', filenameExists: (name: string) => `نام فایل "${name}" از قبل وجود دارد.`, renameSuccess: (name: string) => `تغییر نام به "${name}"`, - renameError: "خطا در تغییر نام فایل.", - profileDeleted: "پروفایل با موفقیت حذف شد.", + renameError: 'خطا در تغییر نام فایل.', + profileDeleted: 'پروفایل با موفقیت حذف شد.', jpgQualitySet: (value: number) => `کیفیت JPG روی ${value}% تنظیم شد`, - cannotDeleteLastProfile: "شما نمی توانید آخرین پروفایل را حذف کنید.", - noKeywordsToTest: "این قانون هیچ کلمه کلیدی برای آزمایش ندارد.", - langNameEmpty: "نام زبان نمی‌تواند خالی باشد.", - langCodeEmpty: "کد زبان نمی‌تواند خالی باشد.", + cannotDeleteLastProfile: 'شما نمی توانید آخرین پروفایل را حذف کنید.', + noKeywordsToTest: 'این قانون هیچ کلمه کلیدی برای آزمایش ندارد.', + langNameEmpty: 'نام زبان نمی‌تواند خالی باشد.', + langCodeEmpty: 'کد زبان نمی‌تواند خالی باشد.', langCodeCore: (code: string) => `کد "${code}" برای یک زبان اصلی رزرو شده است. لطفاً کد دیگری انتخاب کنید.`, - langCodeExists: (code: string) => - `یک زبان سفارشی با کد "${code}" از قبل وجود دارد.`, - langNameExists: (name: string) => - `یک زبان سفارشی با نام "${name}" از قبل وجود دارد.`, + langCodeExists: (code: string) => `یک زبان سفارشی با کد "${code}" از قبل وجود دارد.`, + langNameExists: (name: string) => `یک زبان سفارشی با نام "${name}" از قبل وجود دارد.`, langNameCore: (name: string) => `نام "${name}" برای یک زبان اصلی رزرو شده است. لطفاً نام دیگری انتخاب کنید.`, langCreated: (name: string) => `زبان با موفقیت ایجاد شد: ${name}`, langSaved: (name: string) => `ترجمه‌ها با موفقیت ذخیره شد برای: ${name}`, langExported: (code: string) => `${code}.json با موفقیت صادر شد`, langImported: (name: string) => `ترجمه‌ها با موفقیت از ${name} وارد شد`, - langCopiedJson: "ترجمه‌های JSON در کلیپ‌بورد کپی شدند.", + langCopiedJson: 'ترجمه‌های JSON در کلیپ‌بورد کپی شدند.', langPastedJson: (count: number) => `${count} ترجمه با موفقیت اعمال شد.`, langDeleted: (name: string) => `زبان حذف شد: ${name}`, - langRestored: "زبان به حالت پیش‌فرض بازگردانده شد.", - deleteBackgroundsError: (message: string) => - `حذف پوشه پس‌زمینه‌ها ناموفق بود: ${message}`, - snippetsLocked: "قطعه کدها قفل شدند (کشیدن غیرفعال شد).", - snippetsUnlocked: "قفل قطعه کدها باز شد.", + langRestored: 'زبان به حالت پیش‌فرض بازگردانده شد.', + deleteBackgroundsError: (message: string) => `حذف پوشه پس‌زمینه‌ها ناموفق بود: ${message}`, + snippetsLocked: 'قطعه کدها قفل شدند (کشیدن غیرفعال شد).', + snippetsUnlocked: 'قفل قطعه کدها باز شد.', }, tooltips: { - editCssProfile: "ویرایش پروفایل CSS", - pinSnapshot: "پین کردن رنگ‌های فعلی به عنوان یک عکس فوری", - pinSnapshotDate: (date: string) => - `رنگ‌ها در تاریخ ${date} پین شدند. برای پین مجدد کلیک کنید.`, - resetToPinned: "بازنشانی به رنگ‌های پین شده", - editSnippet: "ویرایش قطعه کد", - copySnippetCss: "کپی کردن CSS در کلیپ‌بورد", - deleteSnippet: "حذف قطعه کد", - setTransparent: "تنظیم به شفاف", - undoChange: "لغو آخرین تغییر", - dragReorder: "برای ترتیب مجدد بکشید", - testRule: "این قانون را با یک کلمه کلیدی تصادفی آزمایش کنید", - deleteCustomVar: "حذف متغیر سفارشی", - iconizeSettings: "تنظیمات Iconize", - themeLight: "تم: اعمال حالت روشن (برای تغییر به تاریک کلیک کنید)", - themeDark: "تم: اعمال حالت تاریک (برای تغییر به خودکار کلیک کنید)", - themeAuto: "تم: خودکار (مطابق با Obsidian) (برای تغییر به روشن کلیک کنید)", - addBg: "اضافه کردن پس‌زمینه", - removeBg: "حذف پس‌زمینه", - bgSettings: "تنظیمات پس‌زمینه", - browseBg: "مرور پس‌زمینه‌های ذخیره شده", + editCssProfile: 'ویرایش پروفایل CSS', + pinSnapshot: 'پین کردن رنگ‌های فعلی به عنوان یک عکس فوری', + pinSnapshotDate: (date: string) => `رنگ‌ها در تاریخ ${date} پین شدند. برای پین مجدد کلیک کنید.`, + resetToPinned: 'بازنشانی به رنگ‌های پین شده', + editSnippet: 'ویرایش قطعه کد', + copySnippetCss: 'کپی کردن CSS در کلیپ‌بورد', + deleteSnippet: 'حذف قطعه کد', + setTransparent: 'تنظیم به شفاف', + undoChange: 'لغو آخرین تغییر', + dragReorder: 'برای ترتیب مجدد بکشید', + testRule: 'این قانون را با یک کلمه کلیدی تصادفی آزمایش کنید', + deleteCustomVar: 'حذف متغیر سفارشی', + iconizeSettings: 'تنظیمات Iconize', + themeLight: 'تم: اعمال حالت روشن (برای تغییر به تاریک کلیک کنید)', + themeDark: 'تم: اعمال حالت تاریک (برای تغییر به خودکار کلیک کنید)', + themeAuto: 'تم: خودکار (مطابق با Obsidian) (برای تغییر به روشن کلیک کنید)', + addBg: 'اضافه کردن پس‌زمینه', + removeBg: 'حذف پس‌زمینه', + bgSettings: 'تنظیمات پس‌زمینه', + browseBg: 'مرور پس‌زمینه‌های ذخیره شده', iconizeNotInstalled: "افزونه نصب نشده یا غیرفعال است. لطفاً 'Iconize' را نصب و فعال کنید تا از این ویژگی استفاده کنید.", - editLang: "ویرایش زبان انتخاب شده", - langMenu: "گزینه‌های زبان", - langInfo: "چرا زبان‌ها را ایجاد/ویرایش کنیم؟", - restoreDefaultLang: "بازگردانی ترجمه‌های پیش‌فرض", - lockSnippets: "قفل کردن کشیدن", - unlockSnippets: "باز کردن کشیدن", + editLang: 'ویرایش زبان انتخاب شده', + langMenu: 'گزینه‌های زبان', + langInfo: 'چرا زبان‌ها را ایجاد/ویرایش کنیم؟', + restoreDefaultLang: 'بازگردانی ترجمه‌های پیش‌فرض', + lockSnippets: 'قفل کردن کشیدن', + unlockSnippets: 'باز کردن کشیدن', }, commands: { - toggleTheme: "تغییر تم پروفایل فعال", - enableDisable: "فعال/غیرفعال کردن", - cycleNext: "رفتن به پروفایل بعدی", - cyclePrevious: "رفتن به پروفایل قبلی", - openSettings: "باز کردن تنظیمات", + toggleTheme: 'تغییر تم پروفایل فعال', + enableDisable: 'فعال/غیرفعال کردن', + cycleNext: 'رفتن به پروفایل بعدی', + cyclePrevious: 'رفتن به پروفایل قبلی', + openSettings: 'باز کردن تنظیمات', }, likeCard: { - profilesStat: (p: number, s: number) => - `پروفایل‌ها: ${p} و قطعه کدها: ${s}`, - colorsStat: "رنگ‌های قابل تنظیم", - integrationsStat: "ادغام با افزونه‌ها", - daysStat: "روزهای استفاده", - starButton: "ستاره در گیت‌هاب", - issueButton: "گزارش مشکل", - syncButton: "همگام‌سازی صندوق شما", - telegramButton: "تلگرام", + profilesStat: (p: number, s: number) => `پروفایل‌ها: ${p} و قطعه کدها: ${s}`, + colorsStat: 'رنگ‌های قابل تنظیم', + integrationsStat: 'ادغام با افزونه‌ها', + daysStat: 'روزهای استفاده', + starButton: 'ستاره در گیت‌هاب', + issueButton: 'گزارش مشکل', + syncButton: 'همگام‌سازی صندوق شما', + telegramButton: 'تلگرام', }, colors: { names: { // Iconize - "--iconize-icon-color": "رنگ آیکون Iconize", + '--iconize-icon-color': 'رنگ آیکون Iconize', // Backgrounds - "--background-primary": "پس‌زمینه اصلی", - "--background-primary-alt": "پس‌زمینه اصلی (جایگزین)", - "--background-secondary": "پس‌زمینه ثانویه", - "--background-secondary-alt": "پس‌زمینه ثانویه (جایگزین)", - "--background-modifier-border": "کادر", - "--background-modifier-border-hover": "کادر (هاور)", - "--background-modifier-border-focus": "کادر (فوکوس)", - "--background-modifier-flair": "پس‌زمینه Flair", - "--background-modifier-hover": "پس‌زمینه (هاور)", - "--background-modifier-active": "پس‌زمینه (فعال)", + '--background-primary': 'پس‌زمینه اصلی', + '--background-primary-alt': 'پس‌زمینه اصلی (جایگزین)', + '--background-secondary': 'پس‌زمینه ثانویه', + '--background-secondary-alt': 'پس‌زمینه ثانویه (جایگزین)', + '--background-modifier-border': 'کادر', + '--background-modifier-border-hover': 'کادر (هاور)', + '--background-modifier-border-focus': 'کادر (فوکوس)', + '--background-modifier-flair': 'پس‌زمینه Flair', + '--background-modifier-hover': 'پس‌زمینه (هاور)', + '--background-modifier-active': 'پس‌زمینه (فعال)', // Text - "--text-normal": "متن عادی", - "--text-muted": "متن کم‌رنگ", - "--text-faint": "متن بسیار کم‌رنگ", - "--text-on-accent": "متن روی رنگ تأکیدی", - "--text-accent": "متن تأکیدی", - "--text-accent-hover": "متن تأکیدی (هاور)", - "--text-selection": "انتخاب متن", - "--checklist-done-color": "چک‌لیست انجام‌شده", - "--tag-color": "متن تگ", - "--tag-color-hover": "متن تگ (هاور)", - "--tag-bg": "پس‌زمینه تگ", + '--text-normal': 'متن عادی', + '--text-muted': 'متن کم‌رنگ', + '--text-faint': 'متن بسیار کم‌رنگ', + '--text-on-accent': 'متن روی رنگ تأکیدی', + '--text-accent': 'متن تأکیدی', + '--text-accent-hover': 'متن تأکیدی (هاور)', + '--text-selection': 'انتخاب متن', + '--checklist-done-color': 'چک‌لیست انجام‌شده', + '--tag-color': 'متن تگ', + '--tag-color-hover': 'متن تگ (هاور)', + '--tag-bg': 'پس‌زمینه تگ', // Headings - "--h1-color": "رنگ H1", - "--h2-color": "رنگ H2", - "--h3-color": "رنگ H3", - "--h4-color": "رنگ H4", - "--h5-color": "رنگ H5", - "--h6-color": "رنگ H6", + '--h1-color': 'رنگ H1', + '--h2-color': 'رنگ H2', + '--h3-color': 'رنگ H3', + '--h4-color': 'رنگ H4', + '--h5-color': 'رنگ H5', + '--h6-color': 'رنگ H6', // Markdown - "--hr-color": "خط افقی", - "--blockquote-border-color": "کادر نقل‌قول", - "--blockquote-color": "متن نقل‌قول", - "--blockquote-bg": "پس‌زمینه نقل‌قول", - "--code-normal": "متن کد درون‌خطی", - "--code-background": "پس‌زمینه کد درون‌خطی", - "--text-highlight-bg": "پس‌زمینه متن هایلایت‌شده", + '--hr-color': 'خط افقی', + '--blockquote-border-color': 'کادر نقل‌قول', + '--blockquote-color': 'متن نقل‌قول', + '--blockquote-bg': 'پس‌زمینه نقل‌قول', + '--code-normal': 'متن کد درون‌خطی', + '--code-background': 'پس‌زمینه کد درون‌خطی', + '--text-highlight-bg': 'پس‌زمینه متن هایلایت‌شده', // Interactive Elements - "--interactive-normal": "تعاملی عادی", - "--interactive-hover": "تعاملی (هاور)", - "--interactive-accent": "تعاملی تأکیدی", - "--interactive-accent-hover": "تعاملی تأکیدی (هاور)", - "--interactive-success": "رنگ موفقیت", - "--interactive-error": "رنگ خطا", - "--interactive-warning": "رنگ هشدار", + '--interactive-normal': 'تعاملی عادی', + '--interactive-hover': 'تعاملی (هاور)', + '--interactive-accent': 'تعاملی تأکیدی', + '--interactive-accent-hover': 'تعاملی تأکیدی (هاور)', + '--interactive-success': 'رنگ موفقیت', + '--interactive-error': 'رنگ خطا', + '--interactive-warning': 'رنگ هشدار', // UI Elements - "--titlebar-background": "پس‌زمینه نوار عنوان", - "--titlebar-background-focused": "پس‌زمینه نوار عنوان (فوکوس)", - "--titlebar-text-color": "متن نوار عنوان", - "--sidebar-background": "پس‌زمینه نوار کناری", - "--sidebar-border-color": "کادر نوار کناری", - "--header-background": "پس‌زمینه سربرگ", - "--header-border-color": "کادر سربرگ", - "--vault-name-color": "نام والت", + '--titlebar-background': 'پس‌زمینه نوار عنوان', + '--titlebar-background-focused': 'پس‌زمینه نوار عنوان (فوکوس)', + '--titlebar-text-color': 'متن نوار عنوان', + '--sidebar-background': 'پس‌زمینه نوار کناری', + '--sidebar-border-color': 'کادر نوار کناری', + '--header-background': 'پس‌زمینه سربرگ', + '--header-border-color': 'کادر سربرگ', + '--vault-name-color': 'نام والت', // Notices - "--cm-notice-text-default": "متن پیش‌فرض اعلان", - "--cm-notice-bg-default": "پس‌زمینه پیش‌فرض اعلان", + '--cm-notice-text-default': 'متن پیش‌فرض اعلان', + '--cm-notice-bg-default': 'پس‌زمینه پیش‌فرض اعلان', // Graph View - "--graph-line": "خط گراف", - "--graph-node": "گره گراف", - "--graph-text": "متن گراف", - "--graph-node-unresolved": "گره حل‌نشده", - "--graph-node-focused": "گره در فوکوس", - "--graph-node-tag": "گره تگ", - "--graph-node-attachment": "گره پیوست", + '--graph-line': 'خط گراف', + '--graph-node': 'گره گراف', + '--graph-text': 'متن گراف', + '--graph-node-unresolved': 'گره حل‌نشده', + '--graph-node-focused': 'گره در فوکوس', + '--graph-node-tag': 'گره تگ', + '--graph-node-attachment': 'گره پیوست', // Misc - "--scrollbar-thumb-bg": "شستی اسکرول‌بار", - "--scrollbar-bg": "پس‌زمینه اسکرول‌بار", - "--divider-color": "جداکننده", + '--scrollbar-thumb-bg': 'شستی اسکرول‌بار', + '--scrollbar-bg': 'پس‌زمینه اسکرول‌بار', + '--divider-color': 'جداکننده', }, descriptions: { // Iconize - "--iconize-icon-color": - "رنگ تمام آیکون‌های اضافه‌شده توسط افزونه Iconize را تنظیم می‌کند. این تنظیمات رنگ خود Iconize را نادیده می‌گیرد.", + '--iconize-icon-color': + 'رنگ تمام آیکون‌های اضافه‌شده توسط افزونه Iconize را تنظیم می‌کند. این تنظیمات رنگ خود Iconize را نادیده می‌گیرد.', // Backgrounds - "--background-primary": - "رنگ پس‌زمینه اصلی برای کل برنامه، به‌ویژه برای ویرایشگر و پنل‌های یادداشت.", - "--background-primary-alt": - "یک رنگ پس‌زمینه جایگزین، که اغلب برای خط فعال در ویرایشگر استفاده می‌شود.", - "--background-secondary": - "پس‌زمینه ثانویه، معمولاً برای نوارهای کناری و سایر پنل‌های رابط کاربری استفاده می‌شود.", - "--background-secondary-alt": - "یک پس‌زمینه ثانویه جایگزین، که برای فایل فعال در کاوشگر فایل استفاده می‌شود.", - "--background-modifier-border": - "رنگ کادرها در عناصر مختلف رابط کاربری مانند دکمه‌ها و ورودی‌ها.", - "--background-modifier-border-hover": - "رنگ کادر هنگام بردن ماوس روی یک عنصر.", - "--background-modifier-border-focus": - "رنگ کادر برای یک عنصر در حالت فوکوس، مانند یک فیلد متنی انتخاب‌شده.", - "--background-modifier-flair": + '--background-primary': + 'رنگ پس‌زمینه اصلی برای کل برنامه، به‌ویژه برای ویرایشگر و پنل‌های یادداشت.', + '--background-primary-alt': + 'یک رنگ پس‌زمینه جایگزین، که اغلب برای خط فعال در ویرایشگر استفاده می‌شود.', + '--background-secondary': + 'پس‌زمینه ثانویه، معمولاً برای نوارهای کناری و سایر پنل‌های رابط کاربری استفاده می‌شود.', + '--background-secondary-alt': + 'یک پس‌زمینه ثانویه جایگزین، که برای فایل فعال در کاوشگر فایل استفاده می‌شود.', + '--background-modifier-border': + 'رنگ کادرها در عناصر مختلف رابط کاربری مانند دکمه‌ها و ورودی‌ها.', + '--background-modifier-border-hover': 'رنگ کادر هنگام بردن ماوس روی یک عنصر.', + '--background-modifier-border-focus': + 'رنگ کادر برای یک عنصر در حالت فوکوس، مانند یک فیلد متنی انتخاب‌شده.', + '--background-modifier-flair': "رنگ پس‌زمینه برای عناصر ویژه رابط کاربری، مانند وضعیت 'در حال همگام‌سازی' یا 'در حال نمایه‌سازی'.", - "--background-modifier-hover": - "رنگ پس‌زمینه عناصر هنگام بردن ماوس روی آنها (مثلاً، موارد لیست).", - "--background-modifier-active": - "رنگ پس‌زمینه یک عنصر هنگامی که فعالانه کلیک می‌شود یا انتخاب شده است.", + '--background-modifier-hover': + 'رنگ پس‌زمینه عناصر هنگام بردن ماوس روی آنها (مثلاً، موارد لیست).', + '--background-modifier-active': + 'رنگ پس‌زمینه یک عنصر هنگامی که فعالانه کلیک می‌شود یا انتخاب شده است.', // Text - "--text-normal": - "رنگ متن پیش‌فرض برای تمام یادداشت‌ها و بیشتر بخش‌های رابط کاربری.", - "--text-muted": - "یک رنگ متن کمی کم‌رنگ‌تر، برای اطلاعات کم‌اهمیت‌تر مانند فراداده فایل استفاده می‌شود.", - "--text-faint": - "کم‌رنگ‌ترین رنگ متن، برای متن‌های بسیار ظریف رابط کاربری یا عناصر غیرفعال.", - "--text-on-accent": - "رنگ متنی که روی پس‌زمینه‌های تأکیدی ظاهر می‌شود (مانند روی یک دکمه اصلی).", - "--text-accent": - "رنگ تأکیدی اصلی برای متن، برای پیوندها و عناصر برجسته رابط کاربری استفاده می‌شود.", - "--text-accent-hover": - "رنگ متن تأکیدی (مانند پیوندها) هنگام بردن ماوس روی آن.", - "--text-selection": - "رنگ پس‌زمینه متنی که با مکان‌نمای خود انتخاب کرده‌اید.", - "--checklist-done-color": - "رنگ علامت تیک و متن برای یک مورد انجام‌شده در فهرست کارها.", - "--tag-color": "رنگ متن #تگ‌ها را تنظیم می‌کند.", - "--tag-color-hover": "رنگ متن #تگ‌ها را هنگام هاور کردن تنظیم می‌کند.", - "--tag-bg": - "رنگ پس‌زمینه #تگ‌ها را تنظیم می‌کند، که امکان ایجاد شکل 'قرص' را فراهم می‌کند.", + '--text-normal': 'رنگ متن پیش‌فرض برای تمام یادداشت‌ها و بیشتر بخش‌های رابط کاربری.', + '--text-muted': + 'یک رنگ متن کمی کم‌رنگ‌تر، برای اطلاعات کم‌اهمیت‌تر مانند فراداده فایل استفاده می‌شود.', + '--text-faint': 'کم‌رنگ‌ترین رنگ متن، برای متن‌های بسیار ظریف رابط کاربری یا عناصر غیرفعال.', + '--text-on-accent': + 'رنگ متنی که روی پس‌زمینه‌های تأکیدی ظاهر می‌شود (مانند روی یک دکمه اصلی).', + '--text-accent': + 'رنگ تأکیدی اصلی برای متن، برای پیوندها و عناصر برجسته رابط کاربری استفاده می‌شود.', + '--text-accent-hover': 'رنگ متن تأکیدی (مانند پیوندها) هنگام بردن ماوس روی آن.', + '--text-selection': 'رنگ پس‌زمینه متنی که با مکان‌نمای خود انتخاب کرده‌اید.', + '--checklist-done-color': 'رنگ علامت تیک و متن برای یک مورد انجام‌شده در فهرست کارها.', + '--tag-color': 'رنگ متن #تگ‌ها را تنظیم می‌کند.', + '--tag-color-hover': 'رنگ متن #تگ‌ها را هنگام هاور کردن تنظیم می‌کند.', + '--tag-bg': "رنگ پس‌زمینه #تگ‌ها را تنظیم می‌کند، که امکان ایجاد شکل 'قرص' را فراهم می‌کند.", // Headings - "--h1-color": "رنگ متن عنوان‌های H1.", - "--h2-color": "رنگ متن عنوان‌های H2.", - "--h3-color": "رنگ متن عنوان‌های H3.", - "--h4-color": "رنگ متن عنوان‌های H4.", - "--h5-color": "رنگ متن عنوان‌های H5.", - "--h6-color": "رنگ متن عنوان‌های H6.", + '--h1-color': 'رنگ متن عنوان‌های H1.', + '--h2-color': 'رنگ متن عنوان‌های H2.', + '--h3-color': 'رنگ متن عنوان‌های H3.', + '--h4-color': 'رنگ متن عنوان‌های H4.', + '--h5-color': 'رنگ متن عنوان‌های H5.', + '--h6-color': 'رنگ متن عنوان‌های H6.', // Markdown - "--hr-color": "رنگ خط افقی که با `---` ایجاد می‌شود.", - "--blockquote-border-color": "رنگ کادر عمودی در سمت چپ یک نقل‌قول.", - "--blockquote-color": "رنگ متن برای محتوای داخل یک نقل‌قول.", - "--blockquote-bg": "رنگ پس‌زمینه عناصر نقل‌قول (>) را تنظیم می‌کند.", - "--code-normal": - "رنگ متن داخل کد درون‌خطی (بین بک‌تیک‌ها) را تنظیم می‌کند.", - "--code-background": - "رنگ پس‌زمینه برای بلوک‌های کد درون‌خطی را تنظیم می‌کند.", - "--text-highlight-bg": - "رنگ پس‌زمینه برای متن هایلایت‌شده (==مانند این==) را تنظیم می‌کند.", + '--hr-color': 'رنگ خط افقی که با `---` ایجاد می‌شود.', + '--blockquote-border-color': 'رنگ کادر عمودی در سمت چپ یک نقل‌قول.', + '--blockquote-color': 'رنگ متن برای محتوای داخل یک نقل‌قول.', + '--blockquote-bg': 'رنگ پس‌زمینه عناصر نقل‌قول (>) را تنظیم می‌کند.', + '--code-normal': 'رنگ متن داخل کد درون‌خطی (بین بک‌تیک‌ها) را تنظیم می‌کند.', + '--code-background': 'رنگ پس‌زمینه برای بلوک‌های کد درون‌خطی را تنظیم می‌کند.', + '--text-highlight-bg': 'رنگ پس‌زمینه برای متن هایلایت‌شده (==مانند این==) را تنظیم می‌کند.', // Interactive Elements - "--interactive-normal": "رنگ پس‌زمینه برای عناصر تعاملی مانند دکمه‌ها.", - "--interactive-hover": "رنگ پس‌زمینه برای عناصر تعاملی هنگام هاور.", - "--interactive-accent": - "رنگ تأکیدی برای عناصر تعاملی مهم (مثلاً، دکمه 'ایجاد').", - "--interactive-accent-hover": - "رنگ تأکیدی برای عناصر تعاملی مهم هنگام هاور.", - "--interactive-success": "رنگ نشان‌دهنده یک عملیات موفق (مثلاً، سبز).", - "--interactive-error": "رنگ نشان‌دهنده یک خطا (مثلاً، قرمز).", - "--interactive-warning": "رنگ نشان‌دهنده یک هشدار (مثلاً، زرد).", + '--interactive-normal': 'رنگ پس‌زمینه برای عناصر تعاملی مانند دکمه‌ها.', + '--interactive-hover': 'رنگ پس‌زمینه برای عناصر تعاملی هنگام هاور.', + '--interactive-accent': "رنگ تأکیدی برای عناصر تعاملی مهم (مثلاً، دکمه 'ایجاد').", + '--interactive-accent-hover': 'رنگ تأکیدی برای عناصر تعاملی مهم هنگام هاور.', + '--interactive-success': 'رنگ نشان‌دهنده یک عملیات موفق (مثلاً، سبز).', + '--interactive-error': 'رنگ نشان‌دهنده یک خطا (مثلاً، قرمز).', + '--interactive-warning': 'رنگ نشان‌دهنده یک هشدار (مثلاً، زرد).', // UI Elements - "--titlebar-background": "رنگ پس‌زمینه نوار عنوان پنجره اصلی.", - "--titlebar-background-focused": - "رنگ پس‌زمینه نوار عنوان زمانی که پنجره فعال است.", - "--titlebar-text-color": "رنگ متن در نوار عنوان.", - "--sidebar-background": - "به‌طور خاص پس‌زمینه نوارهای کناری را هدف قرار می‌دهد.", - "--sidebar-border-color": "رنگ کادر کنار نوارهای کناری.", - "--header-background": - "پس‌زمینه برای سربرگ‌ها در داخل پنل‌ها (مثلاً، سربرگ عنوان یادداشت).", - "--header-border-color": "رنگ کادر زیر سربرگ‌های پنل.", - "--vault-name-color": "رنگ نام والت شما در گوشه بالا سمت چپ.", - "--cm-notice-text-default": - "رنگ متن پیش‌فرض برای تمام اعلان‌ها را تنظیم می‌کند، مگر اینکه توسط یک قانون بازنویسی شود.", - "--cm-notice-bg-default": - "رنگ پس‌زمینه پیش‌فرض برای تمام اعلان‌ها را تنظیم می‌کند، مگر اینکه توسط یک قانون بازنویسی شود.", + '--titlebar-background': 'رنگ پس‌زمینه نوار عنوان پنجره اصلی.', + '--titlebar-background-focused': 'رنگ پس‌زمینه نوار عنوان زمانی که پنجره فعال است.', + '--titlebar-text-color': 'رنگ متن در نوار عنوان.', + '--sidebar-background': 'به‌طور خاص پس‌زمینه نوارهای کناری را هدف قرار می‌دهد.', + '--sidebar-border-color': 'رنگ کادر کنار نوارهای کناری.', + '--header-background': 'پس‌زمینه برای سربرگ‌ها در داخل پنل‌ها (مثلاً، سربرگ عنوان یادداشت).', + '--header-border-color': 'رنگ کادر زیر سربرگ‌های پنل.', + '--vault-name-color': 'رنگ نام والت شما در گوشه بالا سمت چپ.', + '--cm-notice-text-default': + 'رنگ متن پیش‌فرض برای تمام اعلان‌ها را تنظیم می‌کند، مگر اینکه توسط یک قانون بازنویسی شود.', + '--cm-notice-bg-default': + 'رنگ پس‌زمینه پیش‌فرض برای تمام اعلان‌ها را تنظیم می‌کند، مگر اینکه توسط یک قانون بازنویسی شود.', // Graph View - "--graph-line": "رنگ خطوط اتصال بین یادداشت‌ها در نمای گراف.", - "--graph-node": "رنگ گره‌های دایره‌ای برای یادداشت‌های موجود.", - "--graph-text": "رنگ برچسب‌های متنی روی گره‌های گراف.", - "--graph-node-unresolved": - "رنگ گره‌ها برای یادداشت‌هایی که هنوز وجود ندارند (پیوندهای حل‌نشده).", - "--graph-node-focused": - "رنگ گره‌ای که در حالت فوکوس یا هاور است (گره برجسته‌شده).", - "--graph-node-tag": - "رنگ گره‌های نماینده تگ‌ها زمانی که تگ‌ها در گراف نمایش داده می‌شوند.", - "--graph-node-attachment": - "رنگ گره‌های نماینده پیوست‌ها (مثلاً، تصویر یا فایل‌های پیوند داده شده دیگر).", + '--graph-line': 'رنگ خطوط اتصال بین یادداشت‌ها در نمای گراف.', + '--graph-node': 'رنگ گره‌های دایره‌ای برای یادداشت‌های موجود.', + '--graph-text': 'رنگ برچسب‌های متنی روی گره‌های گراف.', + '--graph-node-unresolved': + 'رنگ گره‌ها برای یادداشت‌هایی که هنوز وجود ندارند (پیوندهای حل‌نشده).', + '--graph-node-focused': 'رنگ گره‌ای که در حالت فوکوس یا هاور است (گره برجسته‌شده).', + '--graph-node-tag': 'رنگ گره‌های نماینده تگ‌ها زمانی که تگ‌ها در گراف نمایش داده می‌شوند.', + '--graph-node-attachment': + 'رنگ گره‌های نماینده پیوست‌ها (مثلاً، تصویر یا فایل‌های پیوند داده شده دیگر).', // Misc - "--scrollbar-thumb-bg": "رنگ قسمت قابل کشیدن اسکرول‌بار.", - "--scrollbar-bg": "رنگ مسیر اسکرول‌بار (پس‌زمینه).", - "--divider-color": - "رنگ برای خطوط جداکننده عمومی رابط کاربری، مانند کادرهای بین تنظیمات.", + '--scrollbar-thumb-bg': 'رنگ قسمت قابل کشیدن اسکرول‌بار.', + '--scrollbar-bg': 'رنگ مسیر اسکرول‌بار (پس‌زمینه).', + '--divider-color': 'رنگ برای خطوط جداکننده عمومی رابط کاربری، مانند کادرهای بین تنظیمات.', }, }, }; diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 704b197..c5227b9 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -1,185 +1,179 @@ export default { plugin: { - name: "Maître des Couleurs - v1.2.0", - ribbonTooltip: "Paramètres de Color Master", + name: 'Maître des Couleurs - v1.2.0', + ribbonTooltip: 'Paramètres de Color Master', }, buttons: { - new: "Nouveau", - delete: "Supprimer", - selectOption: "Sélectionnez une option", - create: "Créer", - reset: "Réinitialiser", - update: "Mettre à jour", - apply: "Appliquer", - cancel: "Annuler", - reload: "Recharger", - exportFile: "Exporter le fichier", - copyJson: "Copier JSON", - importCss: "Importer / Coller (.css)", - importJson: "Importer / Coller (.json)", - chooseFile: "Choisir un fichier...", - import: "Importer", - select: "Sélectionner", - browse: "Parcourir...", - deleteAnyway: "Supprimer quand même", - restore: "Restaurer", + new: 'Nouveau', + delete: 'Supprimer', + selectOption: 'Sélectionnez une option', + create: 'Créer', + reset: 'Réinitialiser', + update: 'Mettre à jour', + apply: 'Appliquer', + cancel: 'Annuler', + reload: 'Recharger', + exportFile: 'Exporter le fichier', + copyJson: 'Copier JSON', + importCss: 'Importer / Coller (.css)', + importJson: 'Importer / Coller (.json)', + chooseFile: 'Choisir un fichier...', + import: 'Importer', + select: 'Sélectionner', + browse: 'Parcourir...', + deleteAnyway: 'Supprimer quand même', + restore: 'Restaurer', }, settings: { - enablePlugin: "Activer Maître des Couleurs", + enablePlugin: 'Activer Maître des Couleurs', enablePluginDesc: - "Désactivez cette option pour désactiver temporairement toutes les couleurs personnalisées et revenir à votre thème Obsidian actif.", - language: "Langue", + 'Désactivez cette option pour désactiver temporairement toutes les couleurs personnalisées et revenir à votre thème Obsidian actif.', + language: 'Langue', languageDesc: "Définissez la langue de l'interface pour le plugin.", - languageSettingsModalTitle: "Paramètres de langue", - rtlLayoutName: "Activer la disposition de droite à gauche (RTL)", + languageSettingsModalTitle: 'Paramètres de langue', + rtlLayoutName: 'Activer la disposition de droite à gauche (RTL)', rtlLayoutDesc: "Lorsque cette option est activée, l'interface du plugin est inversée pour prendre en charge correctement les langues écrites de droite à gauche.", - searchPlaceholder: "Rechercher des variables (nom ou valeur)...", - regexPlaceholder: "Entrez Regex et appuyez sur Entrée...", - allSections: "Toutes les sections", - clear: "Effacer", - ariaCase: "Recherche sensible à la casse", - ariaRegex: "Utiliser une expression régulière", + searchPlaceholder: 'Rechercher des variables (nom ou valeur)...', + regexPlaceholder: 'Entrez Regex et appuyez sur Entrée...', + allSections: 'Toutes les sections', + clear: 'Effacer', + ariaCase: 'Recherche sensible à la casse', + ariaRegex: 'Utiliser une expression régulière', themeWarningTooltip: (currentTheme: string) => `Le thème communautaire "${currentTheme}" est actif, ce qui peut interférer avec l'apparence du profil. Pour de meilleurs résultats, passez au thème par défaut d'Obsidian, ou importez "${currentTheme}" comme un nouveau profil CSS pour le personnaliser directement.`, }, profileManager: { - heading: "Gestionnaire de Profils", - activeProfile: "Profil Actif", - activeProfileDesc: "Gérez et basculez entre les profils de couleurs.", - themeType: "Type de thème du profil", + heading: 'Gestionnaire de Profils', + activeProfile: 'Profil Actif', + activeProfileDesc: 'Gérez et basculez entre les profils de couleurs.', + themeType: 'Type de thème du profil', themeTypeDesc: - "Définissez si ce profil doit forcer un thème spécifique (Sombre/Clair) lors de son activation.", + 'Définissez si ce profil doit forcer un thème spécifique (Sombre/Clair) lors de son activation.', themeAuto: "Thème par défaut d'Obsidian", - themeDark: "Forcer le mode sombre", - themeLight: "Forcer le mode clair", - tooltipThemeAuto: - "Thème : Auto (Suit Obsidian) (Cliquer pour passer au Clair)", - tooltipThemeDark: - "Thème : Forcer le mode Sombre (Cliquer pour passer en Auto)", - tooltipThemeLight: - "Thème : Forcer le mode Clair (Cliquer pour passer au Sombre)", - tooltipExport: "Exporter le profil actuel en tant que fichier JSON", - tooltipCopyJson: "Copier le JSON du profil actuel dans le presse-papiers", + themeDark: 'Forcer le mode sombre', + themeLight: 'Forcer le mode clair', + tooltipThemeAuto: 'Thème : Auto (Suit Obsidian) (Cliquer pour passer au Clair)', + tooltipThemeDark: 'Thème : Forcer le mode Sombre (Cliquer pour passer en Auto)', + tooltipThemeLight: 'Thème : Forcer le mode Clair (Cliquer pour passer au Sombre)', + tooltipExport: 'Exporter le profil actuel en tant que fichier JSON', + tooltipCopyJson: 'Copier le JSON du profil actuel dans le presse-papiers', }, options: { - heading: "Paramètres avancés", - liveUpdateName: "FPS de la mise à jour en direct", + heading: 'Paramètres avancés', + liveUpdateName: 'FPS de la mise à jour en direct', liveUpdateDesc: "Définit le nombre de fois par seconde où l'interface prévisualise les changements de couleur lors du glissement (0 = désactiver l'aperçu en direct). Des valeurs plus basses peuvent améliorer les performances.", iconizeModalTitle: "Paramètres d'intégration d'Iconize", - overrideIconizeName: "Outrepasser les couleurs du plugin Iconize", + overrideIconizeName: 'Outrepasser les couleurs du plugin Iconize', overrideIconizeDesc: "Laissez Maître des Couleurs contrôler toutes les couleurs d'icônes du plugin Iconize. Pour de meilleurs résultats, désactivez les paramètres de couleur dans Iconize lui-même.", - cleanupIntervalName: "Intervalle de nettoyage", + cleanupIntervalName: 'Intervalle de nettoyage', cleanupIntervalDesc: - "Définit la fréquence (en secondes) à laquelle le plugin recherche un plugin Iconize désinstallé pour nettoyer ses icônes.", - addCustomVarName: "Ajouter une variable personnalisée", + 'Définit la fréquence (en secondes) à laquelle le plugin recherche un plugin Iconize désinstallé pour nettoyer ses icônes.', + addCustomVarName: 'Ajouter une variable personnalisée', addCustomVarDesc: "Ajoutez une nouvelle variable CSS qui n'est pas dans la liste par défaut. Le nom doit commencer par '--'.", - addNewVarButton: "Ajouter une nouvelle variable...", - resetPluginName: "Réinitialiser les paramètres du plugin", + addNewVarButton: 'Ajouter une nouvelle variable...', + resetPluginName: 'Réinitialiser les paramètres du plugin', resetPluginDesc: "Cette action supprimera tous les profils, extraits, paramètres et arrière-plans, réinitialisant ainsi l'extension à son état d'origine. Cette action nécessite un rechargement de l'application et est irréversible.", - resetPluginButton: "Réinitialiser toutes les données...", - backgroundName: "Définir un arrière-plan personnalisé", + resetPluginButton: 'Réinitialiser toutes les données...', + backgroundName: 'Définir un arrière-plan personnalisé', backgroundDesc: "Gérer l'image ou la vidéo d'arrière-plan pour ce profil.", backgroundModalSettingsTitle: "Paramètres d'arrière-plan", backgroundEnableName: "Activer l'arrière-plan", backgroundEnableDesc: "Activer/désactiver la visibilité de l'arrière-plan personnalisé pour ce profil.", - convertImagesName: "Convertir les images en JPG", + convertImagesName: 'Convertir les images en JPG', convertImagesDesc: - "Convertir automatiquement les images PNG, WEBP ou BMP en JPG lors du téléversement pour réduire la taille du fichier et améliorer les performances de chargement. (Note : La transparence sera perdue)", - jpgQualityName: "Qualité JPG", + 'Convertir automatiquement les images PNG, WEBP ou BMP en JPG lors du téléversement pour réduire la taille du fichier et améliorer les performances de chargement. (Note : La transparence sera perdue)', + jpgQualityName: 'Qualité JPG', jpgQualityDesc: - "Définissez la qualité de compression (1-100). Des valeurs plus basses signifient des fichiers plus petits mais une qualité inférieure.", - badgeNotInstalled: "Non installé", - videoOpacityName: "Opacité de la vidéo", + 'Définissez la qualité de compression (1-100). Des valeurs plus basses signifient des fichiers plus petits mais une qualité inférieure.', + badgeNotInstalled: 'Non installé', + videoOpacityName: 'Opacité de la vidéo', videoOpacityDesc: "Contrôle la transparence de la vidéo d'arrière-plan pour une meilleure lisibilité.", - videoMuteName: "Désactiver le son", - videoMuteDesc: - "Désactive le son de la vidéo d'arrière-plan. Fortement recommandé.", - settingType: "Type de paramètre", - settingTypeImage: "Image", - settingTypeVideo: "Vidéo", + videoMuteName: 'Désactiver le son', + videoMuteDesc: "Désactive le son de la vidéo d'arrière-plan. Fortement recommandé.", + settingType: 'Type de paramètre', + settingTypeImage: 'Image', + settingTypeVideo: 'Vidéo', }, snippets: { - heading: "Extraits CSS", - createButton: "Créer un nouvel extrait", + heading: 'Extraits CSS', + createButton: 'Créer un nouvel extrait', noSnippetsDesc: "Aucun extrait CSS n'a encore été créé pour ce profil.", - global: "Extrait global", - globalName: "Enregistrer comme extrait global", - globalDesc: "Un extrait global est appliqué à tous vos profils.", + global: 'Extrait global', + globalName: 'Enregistrer comme extrait global', + globalDesc: 'Un extrait global est appliqué à tous vos profils.', }, categories: { - pluginintegrations: "Intégrations de plugins", - backgrounds: "Arrière-plans", - text: "Texte", - interactive: "Éléments Interactifs", + pluginintegrations: 'Intégrations de plugins', + backgrounds: 'Arrière-plans', + text: 'Texte', + interactive: 'Éléments Interactifs', ui: "Éléments d'Interface", - misc: "Divers", - graph: "Vue Graphique", - markdown: "Markdown", - notices: "Notifications", - custom: "Variables personnalisées", + misc: 'Divers', + graph: 'Vue Graphique', + markdown: 'Markdown', + notices: 'Notifications', + custom: 'Variables personnalisées', customDesc: "Variable ajoutée par l'utilisateur.", - helpTextPre: "Vous ne trouvez pas la variable que vous cherchez ? ", + helpTextPre: 'Vous ne trouvez pas la variable que vous cherchez ? ', helpTextLink: "Parcourez la liste officielle des variables CSS d'Obsidian.", }, modals: { newProfile: { - title: "Créer un nouveau profil", - nameLabel: "Nom du profil", - namePlaceholder: "Entrez le nom du profil...", + title: 'Créer un nouveau profil', + nameLabel: 'Nom du profil', + namePlaceholder: 'Entrez le nom du profil...', }, deleteProfile: { - title: "Supprimer le profil", + title: 'Supprimer le profil', confirmation: (name: string) => `Êtes-vous sûr de vouloir supprimer le profil "${name}" ? Cette action est irréversible.`, }, jsonImport: { - title: "Coller ou Importer le JSON du Profil", - desc1: "Vous pouvez coller un JSON de profil dans la case ci-dessous.", + title: 'Coller ou Importer le JSON du Profil', + desc1: 'Vous pouvez coller un JSON de profil dans la case ci-dessous.', placeholder: '{ "name": "...", "profile": { ... } }', - settingName: "Importer depuis un fichier", - settingDesc: - "Ou, sélectionnez un fichier de profil (.json) depuis votre ordinateur.", + settingName: 'Importer depuis un fichier', + settingDesc: 'Ou, sélectionnez un fichier de profil (.json) depuis votre ordinateur.', replaceActiveButton: "Remplacer l'actuel", - createNewButton: "Créer un nouveau", + createNewButton: 'Créer un nouveau', }, cssImport: { - title: "Importer / Coller du CSS et créer un profil", - titleEdit: "Modifier le profil CSS", + title: 'Importer / Coller du CSS et créer un profil', + titleEdit: 'Modifier le profil CSS', note: "Remarque : Le CSS collé peut affecter l'interface utilisateur, n'utilisez que du CSS de confiance.", - importFromFile: "Importer depuis un fichier", - importFromFileDesc: - "Ou, sélectionnez un fichier (.css) depuis votre ordinateur.", - importFromTheme: "Importer depuis un thème installé", + importFromFile: 'Importer depuis un fichier', + importFromFileDesc: 'Ou, sélectionnez un fichier (.css) depuis votre ordinateur.', + importFromTheme: 'Importer depuis un thème installé', importFromThemeDesc: "Chargez rapidement le CSS de l'un de vos thèmes communautaires installés.", - noThemes: "Aucun thème installé", + noThemes: 'Aucun thème installé', }, snippetEditor: { - title: "Créer un nouvel extrait CSS", + title: 'Créer un nouvel extrait CSS', titleEdit: "Modifier l'extrait CSS", nameLabel: "Nom de l'extrait", namePlaceholder: "Entrez le nom de l'extrait...", - importFromSnippet: "Importer depuis un extrait installé", + importFromSnippet: 'Importer depuis un extrait installé', importFromSnippetDesc: "Chargez rapidement le CSS de l'un de vos extraits Obsidian installés.", - noSnippets: "Aucun extrait installé", - cssPlaceholder: "Collez votre code CSS ici...", + noSnippets: 'Aucun extrait installé', + cssPlaceholder: 'Collez votre code CSS ici...', }, confirmation: { - resetProfileTitle: "Confirmation de la réinitialisation du profil", + resetProfileTitle: 'Confirmation de la réinitialisation du profil', resetProfileDesc: - "Êtes-vous sûr de vouloir réinitialiser ce profil à la dernière capture épinglée ? Cela écrasera vos couleurs actuelles et ne pourra pas être annulé.", + 'Êtes-vous sûr de vouloir réinitialiser ce profil à la dernière capture épinglée ? Cela écrasera vos couleurs actuelles et ne pourra pas être annulé.', deleteSnippetTitle: (name: string) => `Supprimer l'extrait : ${name}`, deleteSnippetDesc: - "Êtes-vous sûr de vouloir supprimer cet extrait ? Cette action est irréversible.", - resetPluginTitle: "Êtes-vous sûr ?", + 'Êtes-vous sûr de vouloir supprimer cet extrait ? Cette action est irréversible.', + resetPluginTitle: 'Êtes-vous sûr ?', resetPluginDesc: "Cela supprimera définitivement toutes vos données Color Master (profils, extraits, paramètres et arrière-plans). C'est irréversible.", deleteGlobalBgTitle: "Confirmer la suppression de l'arrière-plan", @@ -188,229 +182,198 @@ export default { deleteBackgroundTitle: "Supprimer l'arrière-plan ?", deleteBackgroundDesc: (name: string) => `Êtes-vous sûr de vouloir supprimer définitivement '${name}' ?`, - deleteLangTitle: "Supprimer la langue", + deleteLangTitle: 'Supprimer la langue', deleteLangDesc: (name: string) => `Êtes-vous sûr de vouloir supprimer le pack de langue "${name}" ? Cette action est irréversible.`, - restoreLangTitle: "Restaurer la langue ?", + restoreLangTitle: 'Restaurer la langue ?', restoreLangDesc: (name: string) => `Êtes-vous sûr de vouloir supprimer les modifications personnalisées pour ${name} et restaurer les valeurs par défaut ?`, }, noticeRules: { - titleText: "Règles avancées pour la couleur du texte", - titleBg: "Règles avancées pour la couleur de fond", - desc: "Créez des règles priorisées pour colorer les notifications en fonction de leur contenu. La première règle correspondante de haut en bas sera appliquée.", - addNewRule: "Ajouter une nouvelle règle", - keywordPlaceholder: "Tapez un mot-clé et appuyez sur Espace...", - useRegex: "Regex", - highlightOnly: "Surligner le mot-clé", + titleText: 'Règles avancées pour la couleur du texte', + titleBg: 'Règles avancées pour la couleur de fond', + desc: 'Créez des règles priorisées pour colorer les notifications en fonction de leur contenu. La première règle correspondante de haut en bas sera appliquée.', + addNewRule: 'Ajouter une nouvelle règle', + keywordPlaceholder: 'Tapez un mot-clé et appuyez sur Espace...', + useRegex: 'Regex', + highlightOnly: 'Surligner le mot-clé', }, duplicateProfile: { - title: "Nom de profil en double", - descParts: [ - `Le nom de profil "`, - `" existe déjà. Veuillez choisir un nom différent.`, - ], - placeholder: "Entrez un nouveau nom de profil...", + title: 'Nom de profil en double', + descParts: [`Le nom de profil "`, `" existe déjà. Veuillez choisir un nom différent.`], + placeholder: 'Entrez un nouveau nom de profil...', }, customVar: { - title: "Ajouter une nouvelle variable CSS personnalisée", - desc: "Définissez une nouvelle variable CSS (ex: --my-color: #f00). Cette variable sera ajoutée à votre profil actif.", + title: 'Ajouter une nouvelle variable CSS personnalisée', + desc: 'Définissez une nouvelle variable CSS (ex: --my-color: #f00). Cette variable sera ajoutée à votre profil actif.', displayName: "Nom d'affichage", displayNameDesc: "Un nom convivial pour votre variable (ex: 'Ma couleur primaire personnalisée').", - displayNamePlaceholder: "Ex: Ma couleur primaire", - varName: "Nom de la variable", + displayNamePlaceholder: 'Ex: Ma couleur primaire', + varName: 'Nom de la variable', varNameDesc: "Le nom réel de la variable CSS. Doit commencer par '--' (ex: '--ma-couleur-primaire').", - varNamePlaceholder: "Ex: --ma-couleur-primaire", - varValue: "Valeur de la variable", - varType: "Type de variable", - varTypeDesc: - "Sélectionnez le type de données que cette variable contient.", + varNamePlaceholder: 'Ex: --ma-couleur-primaire', + varValue: 'Valeur de la variable', + varType: 'Type de variable', + varTypeDesc: 'Sélectionnez le type de données que cette variable contient.', types: { - color: "Couleur", - size: "Taille (ex: px, em)", - text: "Texte", - number: "Nombre", + color: 'Couleur', + size: 'Taille (ex: px, em)', + text: 'Texte', + number: 'Nombre', }, - varValueDesc: - "La valeur de la variable CSS (ex: 'red', '#ff0000', 'rgb(255,0,0)').", - varValuePlaceholder: "Ex: #FF0000 ou rouge", - description: "Description (Optionnel)", - descriptionDesc: - "Une brève description de ce que cette variable contrôle.", - descriptionPlaceholder: "Ex: Couleur principale des titres", - addVarButton: "Ajouter", - textValuePlaceholder: "Entrez la valeur du texte...", + varValueDesc: "La valeur de la variable CSS (ex: 'red', '#ff0000', 'rgb(255,0,0)').", + varValuePlaceholder: 'Ex: #FF0000 ou rouge', + description: 'Description (Optionnel)', + descriptionDesc: 'Une brève description de ce que cette variable contrôle.', + descriptionPlaceholder: 'Ex: Couleur principale des titres', + addVarButton: 'Ajouter', + textValuePlaceholder: 'Entrez la valeur du texte...', }, addBackground: { - title: "Ajouter un nouvel arrière-plan (Image/Vidéo)", - importFromFile: "Importer depuis un fichier", - importFromFileDesc: - "Importer un fichier image ou vidéo depuis votre ordinateur.", - pasteBoxPlaceholder: - "Glissez-déposez / Collez un arrière-plan ou URL ici (Ctrl+V)", + title: 'Ajouter un nouvel arrière-plan (Image/Vidéo)', + importFromFile: 'Importer depuis un fichier', + importFromFileDesc: 'Importer un fichier image ou vidéo depuis votre ordinateur.', + pasteBoxPlaceholder: 'Glissez-déposez / Collez un arrière-plan ou URL ici (Ctrl+V)', dropToAdd: "Relâchez pour ajouter l'arrière-plan...", - processing: "Traitement", + processing: 'Traitement', }, fileConflict: { - title: "Le fichier existe", + title: 'Le fichier existe', desc: (name: string) => `Un fichier nommé '${name}' existe déjà dans votre dossier d'arrière-plans. Que souhaitez-vous faire ?`, - replaceButton: "Remplacer le fichier", - keepButton: "Garder les deux (Renommer)", + replaceButton: 'Remplacer le fichier', + keepButton: 'Garder les deux (Renommer)', }, backgroundBrowser: { - title: "Arrière-plans stockés", - noImages: - "Aucune image ou vidéo trouvée dans votre dossier d'arrière-plans.", + title: 'Arrière-plans stockés', + noImages: "Aucune image ou vidéo trouvée dans votre dossier d'arrière-plans.", }, advancedReset: { - title: "Réinitialisation avancée des données", + title: 'Réinitialisation avancée des données', desc: "Sélectionnez les composants de données que vous souhaitez supprimer définitivement. C'est irréversible.", - profilesLabel: "Profils et Captures", + profilesLabel: 'Profils et Captures', profilesDesc: - "Supprime tous les profils personnalisés et les captures épinglées. (Conserve les profils intégrés).", - snippetsLabel: "Extraits globaux et de profil", - snippetsDesc: - "Supprime tous les extraits CSS, globaux et internes à tous les profils.", - backgroundsLabel: "Dossier des arrière-plans", + 'Supprime tous les profils personnalisés et les captures épinglées. (Conserve les profils intégrés).', + snippetsLabel: 'Extraits globaux et de profil', + snippetsDesc: 'Supprime tous les extraits CSS, globaux et internes à tous les profils.', + backgroundsLabel: 'Dossier des arrière-plans', backgroundsDesc: "Supprime entièrement le dossier des arrière-plans dans le répertoire de configuration d'Obsidian (configDir/backgrounds), ainsi que tous les médias qu'il contient.", - settingsLabel: "Paramètres du plugin", + settingsLabel: 'Paramètres du plugin', settingsDesc: - "Réinitialise la langue, les FPS, la disposition et les autres options par défaut.", - languagesLabel: "Langues personnalisées", - languagesDesc: - "Supprime toutes les langues personnalisées créées par l'utilisateur.", + 'Réinitialise la langue, les FPS, la disposition et les autres options par défaut.', + languagesLabel: 'Langues personnalisées', + languagesDesc: "Supprime toutes les langues personnalisées créées par l'utilisateur.", }, addLang: { - title: "Ajouter une nouvelle langue", - desc: "Créez un nouveau pack de langue personnalisé. Vous pourrez modifier les traductions après la création.", - nameLabel: "Nom de la langue (Natif)", - nameDesc: "Le nom de la langue dans sa propre écriture.", - namePlaceholder: "ex: Français", - codeLabel: "Code de la langue", + title: 'Ajouter une nouvelle langue', + desc: 'Créez un nouveau pack de langue personnalisé. Vous pourrez modifier les traductions après la création.', + nameLabel: 'Nom de la langue (Natif)', + nameDesc: 'Le nom de la langue dans sa propre écriture.', + namePlaceholder: 'ex: Français', + codeLabel: 'Code de la langue', codeDesc: "Un code ISO 639 unique (ex: 'en', 'ar', 'zh').", - codePlaceholder: "ex: fr", - rtlLabel: "Langue RTL", - rtlDesc: - "Activez ceci si la langue s'écrit de droite à gauche (par ex. arabe, ourdou).", + codePlaceholder: 'ex: fr', + rtlLabel: 'Langue RTL', + rtlDesc: "Activez ceci si la langue s'écrit de droite à gauche (par ex. arabe, ourdou).", }, translator: { title: (langName: string) => `Modifier les traductions : ${langName}`, - searchPlaceholder: "Rechercher des clés de traduction...", - copyJson: "Copier JSON", - pasteJson: "Coller JSON", - exportFile: "Exporter", - importFile: "Importer", - showMissing: "Afficher manquants", - showAll: "Afficher tout", - showMore: "Afficher plus", - showLess: "Afficher moins", - noMatches: "Aucune traduction ne correspond à votre recherche.", - dynamicValue: "Valeur dynamique", + searchPlaceholder: 'Rechercher des clés de traduction...', + copyJson: 'Copier JSON', + pasteJson: 'Coller JSON', + exportFile: 'Exporter', + importFile: 'Importer', + showMissing: 'Afficher manquants', + showAll: 'Afficher tout', + showMore: 'Afficher plus', + showLess: 'Afficher moins', + noMatches: 'Aucune traduction ne correspond à votre recherche.', + dynamicValue: 'Valeur dynamique', }, langInfo: { - title: "À propos des langues personnalisées", + title: 'À propos des langues personnalisées', desc: `Cette fonctionnalité vous permet de créer vos propres traductions pour le plugin.\n\n**Pourquoi cette fonctionnalité ?**\n\nHonnêtement, en tant que seul développeur sur ce plugin, il m'est difficile d'ajouter de nouvelles fonctionnalités *et* de les traduire instantanément dans plus de 20 langues. Cette fonctionnalité est une invitation à aider et à améliorer le plugin pour tout le monde.\n\n**Comment pouvez-vous aider :**\n\n1. Créez une new langue (par ex. "Espagnol").\n2. Cliquez sur l'icône "Modifier" (crayon).\n3. Vous pouvez utiliser "Copier JSON" d'une langue existante (comme l'anglais) et demander à ChatGPT de la traduire.\n4. Utilisez "Coller JSON" ou "Importer un fichier" pour charger votre traduction.\n5. Une fois satisfait, envoyez-moi le fichier JSON (via une Pull Request GitHub) et je l'intégrerai volontiers comme nouvelle **langue de base**.\n\nCela vous permet également de corriger les fautes de frappe ou d'améliorer les traductions des langues de base pour votre propre usage.`, }, }, notices: { - pluginEnabled: "Maître des Couleurs activé", - pluginDisabled: "Maître des Couleurs désactivé", - profilePinned: "Couleurs du profil épinglées avec succès !", - profileReset: "Le profil a été réinitialisé à la capture épinglée.", - noPinnedSnapshot: "Aucune capture épinglée trouvée pour ce profil.", + pluginEnabled: 'Maître des Couleurs activé', + pluginDisabled: 'Maître des Couleurs désactivé', + profilePinned: 'Couleurs du profil épinglées avec succès !', + profileReset: 'Le profil a été réinitialisé à la capture épinglée.', + noPinnedSnapshot: 'Aucune capture épinglée trouvée pour ce profil.', profileNotFound: "Le profil actif n'a pas pu être trouvé.", - noProfilesFound: "Aucun profil trouvé.", + noProfilesFound: 'Aucun profil trouvé.', activeProfileSwitched: (name: string) => `Profil actif : ${name}`, - graphColorsApplied: "Couleurs du graphique appliquées !", - invalidJson: "JSON invalide.", + graphColorsApplied: 'Couleurs du graphique appliquées !', + invalidJson: 'JSON invalide.', jsonMustHaveName: "Le JSON importé doit avoir une propriété 'name' pour créer un nouveau profil.", - profileCreatedSuccess: (name: string) => - `Le profil "${name}" a été créé avec succès.`, + profileCreatedSuccess: (name: string) => `Le profil "${name}" a été créé avec succès.`, profileImportedSuccess: `Profil importé avec succès.`, - noActiveProfileToCopy: "Aucun profil actif à copier.", - noActiveProfileToExport: "Aucun profil actif à exporter.", + noActiveProfileToCopy: 'Aucun profil actif à copier.', + noActiveProfileToExport: 'Aucun profil actif à exporter.', snippetCssCopied: "CSS de l'extrait copié dans le presse-papiers !", - snippetEmpty: "Cet extrait est vide.", - cssContentEmpty: "Le contenu CSS ne peut pas être vide.", - snippetNameExists: (name: string) => - `Le nom d'extrait "${name}" existe déjà.`, - profileNameExists: (name: string) => - `Le nom de profil "${name}" existe déjà.`, + snippetEmpty: 'Cet extrait est vide.', + cssContentEmpty: 'Le contenu CSS ne peut pas être vide.', + snippetNameExists: (name: string) => `Le nom d'extrait "${name}" existe déjà.`, + profileNameExists: (name: string) => `Le nom de profil "${name}" existe déjà.`, profileUpdated: (name: string) => `Profil "${name}" mis à jour.`, snippetUpdated: (name: string) => `Extrait "${name}" mis à jour.`, - snippetCreated: (name: string) => - `L'extrait "${name}" a été créé avec succès !`, - snippetDeleted: "Extrait supprimé avec succès.", - snippetScopeMove: - "Utilisez la fenêtre de modification pour déplacer un extrait.", - profileCreatedFromCss: (name: string) => - `Le profil "${name}" a été créé avec succès !`, - noColorHistory: "Aucun historique de couleur à restaurer.", + snippetCreated: (name: string) => `L'extrait "${name}" a été créé avec succès !`, + snippetDeleted: 'Extrait supprimé avec succès.', + snippetScopeMove: 'Utilisez la fenêtre de modification pour déplacer un extrait.', + profileCreatedFromCss: (name: string) => `Le profil "${name}" a été créé avec succès !`, + noColorHistory: 'Aucun historique de couleur à restaurer.', colorRestored: (color: string) => `Restauré : ${color}`, - textboxEmpty: - "La zone de texte est vide. Collez du JSON ou importez d'abord un fichier.", - fileLoaded: (fileName: string) => - `Fichier "${fileName}" chargé dans la zone de texte.`, - exportSuccess: "Profil exporté avec succès !", - jsonCopied: "JSON du profil copié dans le presse-papiers avec succès !", + textboxEmpty: "La zone de texte est vide. Collez du JSON ou importez d'abord un fichier.", + fileLoaded: (fileName: string) => `Fichier "${fileName}" chargé dans la zone de texte.`, + exportSuccess: 'Profil exporté avec succès !', + jsonCopied: 'JSON du profil copié dans le presse-papiers avec succès !', resetSuccess: - "Les données de Color Master ont été supprimées. Veuillez recharger Obsidian pour appliquer les changements.", - fpsUpdated: (value: number) => - `FPS de la mise à jour en direct défini sur : ${value}`, - invalidProfileObject: - "Le JSON ne semble pas être un objet de profil valide.", - profileCreated: (name: string) => - `Le profil "${name}" a été créé avec succès !`, - settingsSaved: "Paramètres appliqués avec succès !", - testSentence: (word: string) => - `La couleur de notification pour "${word}" ressemble à ceci :`, - varNameEmpty: "Le nom de la variable ne peut pas être vide.", + 'Les données de Color Master ont été supprimées. Veuillez recharger Obsidian pour appliquer les changements.', + fpsUpdated: (value: number) => `FPS de la mise à jour en direct défini sur : ${value}`, + invalidProfileObject: 'Le JSON ne semble pas être un objet de profil valide.', + profileCreated: (name: string) => `Le profil "${name}" a été créé avec succès !`, + settingsSaved: 'Paramètres appliqués avec succès !', + testSentence: (word: string) => `La couleur de notification pour "${word}" ressemble à ceci :`, + varNameEmpty: 'Le nom de la variable ne peut pas être vide.', varNameFormat: "Le nom de la variable doit commencer par '--'.", varExists: (name: string) => `La variable "${name}" existe déjà.`, - varAdded: (name: string) => - `La variable "${name}" a été ajoutée avec succès.`, + varAdded: (name: string) => `La variable "${name}" a été ajoutée avec succès.`, iconizeNotFound: "Plugin Iconize non trouvé. Veuillez l'installer et l'activer pour utiliser cette fonctionnalité.", - themeCssLoaded: (theme: string) => - `CSS du thème "${theme}" chargé avec succès.`, + themeCssLoaded: (theme: string) => `CSS du thème "${theme}" chargé avec succès.`, themeReadFailed: (theme: string) => `Impossible de lire le fichier du thème "${theme}". Il est peut-être protégé ou manquant.`, - snippetLoaded: (snippet: string) => - `CSS de l'extrait "${snippet}" chargé avec succès.`, + snippetLoaded: (snippet: string) => `CSS de l'extrait "${snippet}" chargé avec succès.`, snippetReadFailed: (snippet: string) => `Impossible de lire le fichier de l'extrait "${snippet}".`, - themeSwitchedLight: "Passage en mode Clair", - themeSwitchedDark: "Passage en mode Sombre", - themeSwitchedAuto: "Passage en mode Auto", + themeSwitchedLight: 'Passage en mode Clair', + themeSwitchedDark: 'Passage en mode Sombre', + themeSwitchedAuto: 'Passage en mode Auto', bgSet: "L'arrière-plan a été défini avec succès.", bgRemoved: "L'arrière-plan a été supprimé.", backgroundLoadError: "Échec du chargement de l'arrière-plan.", - noBgToRemove: - "Il n'y a pas d'arrière-plan actif à supprimer for ce profil.", + noBgToRemove: "Il n'y a pas d'arrière-plan actif à supprimer for ce profil.", bgDeleted: "L'arrière-plan et son fichier ont été supprimés.", - backgroundUrlLoadError: - "Échec du téléchargement de l'arrière-plan depuis l'URL.", - backgroundPasteError: - "Le contenu collé n'est pas un fichier d'arrière-plan ou une URL valide.", + backgroundUrlLoadError: "Échec du téléchargement de l'arrière-plan depuis l'URL.", + backgroundPasteError: "Le contenu collé n'est pas un fichier d'arrière-plan ou une URL valide.", downloadingFromUrl: (url: string) => `Téléchargement depuis ${url}...`, - pastedBase64Image: "Image Base64 collée", + pastedBase64Image: 'Image Base64 collée', pastedImage: (name: string) => `Image "${name}" collée`, - invalidFilename: "Nom de fichier invalide.", - filenameExists: (name: string) => - `Le nom de fichier "${name}" existe déjà.`, + invalidFilename: 'Nom de fichier invalide.', + filenameExists: (name: string) => `Le nom de fichier "${name}" existe déjà.`, renameSuccess: (name: string) => `Renommé en "${name}"`, - renameError: "Erreur lors du changement de nom du fichier.", - profileDeleted: "Profil supprimé avec succès.", + renameError: 'Erreur lors du changement de nom du fichier.', + profileDeleted: 'Profil supprimé avec succès.', jpgQualitySet: (value: number) => `Qualité JPG réglée sur ${value}%`, - cannotDeleteLastProfile: "Vous ne pouvez pas supprimer le dernier profil.", + cannotDeleteLastProfile: 'Vous ne pouvez pas supprimer le dernier profil.', noKeywordsToTest: "Cette règle n'a pas de mots-clés à tester.", - langNameEmpty: "Le nom de la langue ne peut pas être vide.", - langCodeEmpty: "Le code de la langue ne peut pas être vide.", + langNameEmpty: 'Le nom de la langue ne peut pas être vide.', + langCodeEmpty: 'Le code de la langue ne peut pas être vide.', langCodeCore: (code: string) => `Le code "${code}" est réservé à une langue de base. Veuillez en choisir un autre.`, langCodeExists: (code: string) => @@ -420,269 +383,241 @@ export default { langNameCore: (name: string) => `Le nom "${name}" est réservé à une langue de base. Veuillez en choisir un autre.`, langCreated: (name: string) => `Langue créée avec succès : ${name}`, - langSaved: (name: string) => - `Traductions enregistrées avec succès pour : ${name}`, + langSaved: (name: string) => `Traductions enregistrées avec succès pour : ${name}`, langExported: (code: string) => `${code}.json exporté avec succès`, - langImported: (name: string) => - `Traductions importées avec succès depuis ${name}`, - langCopiedJson: "Traductions JSON copiées dans le presse-papiers.", - langPastedJson: (count: number) => - `${count} traductions appliquées avec succès.`, + langImported: (name: string) => `Traductions importées avec succès depuis ${name}`, + langCopiedJson: 'Traductions JSON copiées dans le presse-papiers.', + langPastedJson: (count: number) => `${count} traductions appliquées avec succès.`, langDeleted: (name: string) => `Langue supprimée : ${name}`, - langRestored: "Langue restaurée aux valeurs par défaut.", + langRestored: 'Langue restaurée aux valeurs par défaut.', deleteBackgroundsError: (message: string) => `Échec de la suppression du dossier des arrière-plans : ${message}`, - snippetsLocked: "Extraits verrouillés (Glissement désactivé).", - snippetsUnlocked: "Extraits déverrouillés.", + snippetsLocked: 'Extraits verrouillés (Glissement désactivé).', + snippetsUnlocked: 'Extraits déverrouillés.', }, tooltips: { - editCssProfile: "Modifier le profil CSS", - pinSnapshot: "Épingler les couleurs actuelles comme une capture", - pinSnapshotDate: (date: string) => - `Couleurs épinglées le ${date}. Cliquez pour ré-épingler.`, - resetToPinned: "Réinitialiser aux couleurs épinglées", + editCssProfile: 'Modifier le profil CSS', + pinSnapshot: 'Épingler les couleurs actuelles comme une capture', + pinSnapshotDate: (date: string) => `Couleurs épinglées le ${date}. Cliquez pour ré-épingler.`, + resetToPinned: 'Réinitialiser aux couleurs épinglées', editSnippet: "Modifier l'extrait", - copySnippetCss: "Copier le CSS dans le presse-papiers", + copySnippetCss: 'Copier le CSS dans le presse-papiers', deleteSnippet: "Supprimer l'extrait", - setTransparent: "Rendre transparent", - undoChange: "Annuler la dernière modification", - dragReorder: "Glisser pour réorganiser", - testRule: "Tester cette règle avec un mot-clé aléatoire", - deleteCustomVar: "Supprimer la variable personnalisée", + setTransparent: 'Rendre transparent', + undoChange: 'Annuler la dernière modification', + dragReorder: 'Glisser pour réorganiser', + testRule: 'Tester cette règle avec un mot-clé aléatoire', + deleteCustomVar: 'Supprimer la variable personnalisée', iconizeSettings: "Paramètres d'Iconize", - themeLight: "Thème : Forcer le mode Clair (Cliquer pour passer au Sombre)", - themeDark: "Thème : Forcer le mode Sombre (Cliquer pour passer en Auto)", - themeAuto: "Thème : Auto (Suit Obsidian) (Cliquer pour passer au Clair)", - addBg: "Ajouter un arrière-plan", + themeLight: 'Thème : Forcer le mode Clair (Cliquer pour passer au Sombre)', + themeDark: 'Thème : Forcer le mode Sombre (Cliquer pour passer en Auto)', + themeAuto: 'Thème : Auto (Suit Obsidian) (Cliquer pour passer au Clair)', + addBg: 'Ajouter un arrière-plan', removeBg: "Supprimer l'arrière-plan", bgSettings: "Paramètres d'arrière-plan", - browseBg: "Parcourir les arrière-plans stockés", + browseBg: 'Parcourir les arrière-plans stockés', iconizeNotInstalled: "Plugin non installé ou désactivé. Veuillez installer et activer 'Iconize' pour utiliser cette fonctionnalité.", - editLang: "Modifier la langue sélectionnée", - langMenu: "Options de langue", - langInfo: "Pourquoi créer/modifier des langues ?", - restoreDefaultLang: "Restaurer les traductions par défaut", - lockSnippets: "Verrouiller le glissement", - unlockSnippets: "Déverrouiller le glissement", + editLang: 'Modifier la langue sélectionnée', + langMenu: 'Options de langue', + langInfo: 'Pourquoi créer/modifier des langues ?', + restoreDefaultLang: 'Restaurer les traductions par défaut', + lockSnippets: 'Verrouiller le glissement', + unlockSnippets: 'Déverrouiller le glissement', }, commands: { - toggleTheme: "Changer le thème du profil actif", - enableDisable: "Activer & Désactiver", - cycleNext: "Profil suivant", - cyclePrevious: "Profil précédent", - openSettings: "Ouvrir les paramètres", + toggleTheme: 'Changer le thème du profil actif', + enableDisable: 'Activer & Désactiver', + cycleNext: 'Profil suivant', + cyclePrevious: 'Profil précédent', + openSettings: 'Ouvrir les paramètres', }, likeCard: { profilesStat: (p: number, s: number) => `Profils: ${p} & Extraits: ${s}`, - colorsStat: "Couleurs personnalisables", - integrationsStat: "Intégrations de plugins", + colorsStat: 'Couleurs personnalisables', + integrationsStat: 'Intégrations de plugins', daysStat: "Jours d'utilisation", - starButton: "Étoile sur GitHub", - issueButton: "Signaler un problème", - syncButton: "Synchronisez votre coffre", - telegramButton: "Telegram", + starButton: 'Étoile sur GitHub', + issueButton: 'Signaler un problème', + syncButton: 'Synchronisez votre coffre', + telegramButton: 'Telegram', }, colors: { names: { // Iconize - "--iconize-icon-color": "Couleur des icônes Iconize", + '--iconize-icon-color': 'Couleur des icônes Iconize', // Backgrounds - "--background-primary": "Arrière-plan principal", - "--background-primary-alt": "Arrière-plan principal (Alt)", - "--background-secondary": "Arrière-plan secondaire", - "--background-secondary-alt": "Arrière-plan secondaire (Alt)", - "--background-modifier-border": "Bordure", - "--background-modifier-border-hover": "Bordure (Survol)", - "--background-modifier-border-focus": "Bordure (Focus)", - "--background-modifier-flair": "Arrière-plan Flair", - "--background-modifier-hover": "Arrière-plan (Survol)", - "--background-modifier-active": "Arrière-plan (Actif)", + '--background-primary': 'Arrière-plan principal', + '--background-primary-alt': 'Arrière-plan principal (Alt)', + '--background-secondary': 'Arrière-plan secondaire', + '--background-secondary-alt': 'Arrière-plan secondaire (Alt)', + '--background-modifier-border': 'Bordure', + '--background-modifier-border-hover': 'Bordure (Survol)', + '--background-modifier-border-focus': 'Bordure (Focus)', + '--background-modifier-flair': 'Arrière-plan Flair', + '--background-modifier-hover': 'Arrière-plan (Survol)', + '--background-modifier-active': 'Arrière-plan (Actif)', // Text - "--text-normal": "Texte normal", - "--text-muted": "Texte estompé", - "--text-faint": "Texte faible", - "--text-on-accent": "Texte sur accent", - "--text-accent": "Texte accentué", - "--text-accent-hover": "Texte accentué (Survol)", - "--text-selection": "Sélection de texte", - "--checklist-done-color": "Tâche cochée", - "--tag-color": "Texte des tags", - "--tag-color-hover": "Texte des tags (Survol)", - "--tag-bg": "Fond des tags", + '--text-normal': 'Texte normal', + '--text-muted': 'Texte estompé', + '--text-faint': 'Texte faible', + '--text-on-accent': 'Texte sur accent', + '--text-accent': 'Texte accentué', + '--text-accent-hover': 'Texte accentué (Survol)', + '--text-selection': 'Sélection de texte', + '--checklist-done-color': 'Tâche cochée', + '--tag-color': 'Texte des tags', + '--tag-color-hover': 'Texte des tags (Survol)', + '--tag-bg': 'Fond des tags', // Headings - "--h1-color": "Couleur H1", - "--h2-color": "Couleur H2", - "--h3-color": "Couleur H3", - "--h4-color": "Couleur H4", - "--h5-color": "Couleur H5", - "--h6-color": "Couleur H6", + '--h1-color': 'Couleur H1', + '--h2-color': 'Couleur H2', + '--h3-color': 'Couleur H3', + '--h4-color': 'Couleur H4', + '--h5-color': 'Couleur H5', + '--h6-color': 'Couleur H6', // Markdown - "--hr-color": "Ligne horizontale", - "--blockquote-border-color": "Bordure de citation", - "--blockquote-color": "Texte de citation", - "--blockquote-bg": "Fond de citation", - "--code-normal": "Texte de code en ligne", - "--code-background": "Fond de code en ligne", - "--text-highlight-bg": "Fond de texte surligné", + '--hr-color': 'Ligne horizontale', + '--blockquote-border-color': 'Bordure de citation', + '--blockquote-color': 'Texte de citation', + '--blockquote-bg': 'Fond de citation', + '--code-normal': 'Texte de code en ligne', + '--code-background': 'Fond de code en ligne', + '--text-highlight-bg': 'Fond de texte surligné', // Interactive Elements - "--interactive-normal": "Interactif Normal", - "--interactive-hover": "Interactif (Survol)", - "--interactive-accent": "Interactif Accent", - "--interactive-accent-hover": "Interactif Accent (Survol)", - "--interactive-success": "Couleur de succès", - "--interactive-error": "Couleur d'erreur", - "--interactive-warning": "Couleur d'avertissement", + '--interactive-normal': 'Interactif Normal', + '--interactive-hover': 'Interactif (Survol)', + '--interactive-accent': 'Interactif Accent', + '--interactive-accent-hover': 'Interactif Accent (Survol)', + '--interactive-success': 'Couleur de succès', + '--interactive-error': "Couleur d'erreur", + '--interactive-warning': "Couleur d'avertissement", // UI Elements - "--titlebar-background": "Fond de la barre de titre", - "--titlebar-background-focused": "Fond de la barre de titre (Focus)", - "--titlebar-text-color": "Texte de la barre de titre", - "--sidebar-background": "Fond de la barre latérale", - "--sidebar-border-color": "Bordure de la barre latérale", - "--header-background": "Fond de l'en-tête", - "--header-border-color": "Bordure de l'en-tête", - "--vault-name-color": "Nom du coffre", + '--titlebar-background': 'Fond de la barre de titre', + '--titlebar-background-focused': 'Fond de la barre de titre (Focus)', + '--titlebar-text-color': 'Texte de la barre de titre', + '--sidebar-background': 'Fond de la barre latérale', + '--sidebar-border-color': 'Bordure de la barre latérale', + '--header-background': "Fond de l'en-tête", + '--header-border-color': "Bordure de l'en-tête", + '--vault-name-color': 'Nom du coffre', // Notices - "--cm-notice-text-default": "Texte de notification par défaut", - "--cm-notice-bg-default": "Fond de notification par défaut", + '--cm-notice-text-default': 'Texte de notification par défaut', + '--cm-notice-bg-default': 'Fond de notification par défaut', // Graph View - "--graph-line": "Ligne du graphique", - "--graph-node": "Nœud du graphique", - "--graph-text": "Texte du graphique", - "--graph-node-unresolved": "Nœud non résolu", - "--graph-node-focused": "Nœud focalisé", - "--graph-node-tag": "Nœud de tag", - "--graph-node-attachment": "Nœud de pièce jointe", + '--graph-line': 'Ligne du graphique', + '--graph-node': 'Nœud du graphique', + '--graph-text': 'Texte du graphique', + '--graph-node-unresolved': 'Nœud non résolu', + '--graph-node-focused': 'Nœud focalisé', + '--graph-node-tag': 'Nœud de tag', + '--graph-node-attachment': 'Nœud de pièce jointe', // Misc - "--scrollbar-thumb-bg": "Curseur de la barre de défilement", - "--scrollbar-bg": "Fond de la barre de défilement", - "--divider-color": "Séparateur", + '--scrollbar-thumb-bg': 'Curseur de la barre de défilement', + '--scrollbar-bg': 'Fond de la barre de défilement', + '--divider-color': 'Séparateur', }, descriptions: { // Iconize - "--iconize-icon-color": + '--iconize-icon-color': "Définit la couleur de toutes les icônes ajoutées par le plugin Iconize. Cela remplacera les propres paramètres de couleur d'Iconize.", // Backgrounds - "--background-primary": + '--background-primary': "Couleur de fond principale pour toute l'application, en particulier pour les volets d'édition et de notes.", - "--background-primary-alt": + '--background-primary-alt': "Une couleur de fond alternative, souvent utilisée pour la ligne active dans l'éditeur.", - "--background-secondary": + '--background-secondary': "Arrière-plan secondaire, généralement utilisé pour les barres latérales et autres panneaux d'interface.", - "--background-secondary-alt": + '--background-secondary-alt': "Un arrière-plan secondaire alternatif, utilisé pour le fichier actif de l'explorateur de fichiers.", - "--background-modifier-border": + '--background-modifier-border': "La couleur des bordures sur divers éléments de l'interface comme les boutons et les entrées.", - "--background-modifier-border-hover": - "La couleur de la bordure lorsque vous survolez un élément.", - "--background-modifier-border-focus": - "La couleur de la bordure pour un élément focalisé, comme un champ de texte sélectionné.", - "--background-modifier-flair": + '--background-modifier-border-hover': + 'La couleur de la bordure lorsque vous survolez un élément.', + '--background-modifier-border-focus': + 'La couleur de la bordure pour un élément focalisé, comme un champ de texte sélectionné.', + '--background-modifier-flair': "Couleur de fond pour les éléments spéciaux de l'interface, comme l'état 'Synchronisation' ou 'Indexation'.", - "--background-modifier-hover": - "La couleur de fond des éléments lorsque vous les survolez (par exemple, les éléments de liste).", - "--background-modifier-active": + '--background-modifier-hover': + 'La couleur de fond des éléments lorsque vous les survolez (par exemple, les éléments de liste).', + '--background-modifier-active': "La couleur de fond d'un élément lorsqu'il est activement cliqué ou sélectionné.", // Text - "--text-normal": + '--text-normal': "La couleur de texte par défaut pour toutes les notes et la plupart de l'interface.", - "--text-muted": - "Une couleur de texte légèrement estompée, utilisée pour des informations moins importantes comme les métadonnées de fichier.", - "--text-faint": + '--text-muted': + 'Une couleur de texte légèrement estompée, utilisée pour des informations moins importantes comme les métadonnées de fichier.', + '--text-faint': "La couleur de texte la plus estompée, pour un texte d'interface très subtil ou des éléments désactivés.", - "--text-on-accent": - "Couleur de texte qui apparaît sur des fonds accentués (comme sur un bouton principal).", - "--text-accent": + '--text-on-accent': + 'Couleur de texte qui apparaît sur des fonds accentués (comme sur un bouton principal).', + '--text-accent': "La couleur d'accentuation principale pour le texte, utilisée pour les liens et les éléments d'interface mis en évidence.", - "--text-accent-hover": - "La couleur du texte accentué (comme les liens) lorsque vous le survolez.", - "--text-selection": - "La couleur de fond du texte que vous avez sélectionné avec votre curseur.", - "--checklist-done-color": - "La couleur de la coche et du texte pour une tâche terminée.", - "--tag-color": "Définit la couleur du texte des #tags.", - "--tag-color-hover": - "Définit la couleur du texte des #tags lors du survol.", - "--tag-bg": - "Définit la couleur de fond des #tags, permettant une forme de 'pilule'.", + '--text-accent-hover': + 'La couleur du texte accentué (comme les liens) lorsque vous le survolez.', + '--text-selection': + 'La couleur de fond du texte que vous avez sélectionné avec votre curseur.', + '--checklist-done-color': 'La couleur de la coche et du texte pour une tâche terminée.', + '--tag-color': 'Définit la couleur du texte des #tags.', + '--tag-color-hover': 'Définit la couleur du texte des #tags lors du survol.', + '--tag-bg': "Définit la couleur de fond des #tags, permettant une forme de 'pilule'.", // Headings - "--h1-color": "La couleur du texte des titres H1.", - "--h2-color": "La couleur du texte des titres H2.", - "--h3-color": "La couleur du texte des titres H3.", - "--h4-color": "La couleur du texte des titres H4.", - "--h5-color": "La couleur du texte des titres H5.", - "--h6-color": "La couleur du texte des titres H6.", + '--h1-color': 'La couleur du texte des titres H1.', + '--h2-color': 'La couleur du texte des titres H2.', + '--h3-color': 'La couleur du texte des titres H3.', + '--h4-color': 'La couleur du texte des titres H4.', + '--h5-color': 'La couleur du texte des titres H5.', + '--h6-color': 'La couleur du texte des titres H6.', // Markdown - "--hr-color": - "La couleur de la ligne de séparation horizontale créée avec `---`.", - "--blockquote-border-color": + '--hr-color': 'La couleur de la ligne de séparation horizontale créée avec `---`.', + '--blockquote-border-color': "La couleur de la bordure verticale sur le côté gauche d'une citation.", - "--blockquote-color": - "La couleur du texte pour le contenu à l'intérieur d'une citation.", - "--blockquote-bg": - "Définit la couleur de fond des éléments de citation (>).", - "--code-normal": + '--blockquote-color': "La couleur du texte pour le contenu à l'intérieur d'une citation.", + '--blockquote-bg': 'Définit la couleur de fond des éléments de citation (>).', + '--code-normal': "Définit la couleur du texte à l'intérieur du code en ligne (entre apostrophes inverses).", - "--code-background": - "Définit la couleur de fond pour les blocs de code en ligne.", - "--text-highlight-bg": - "Définit la couleur de fond pour le texte surligné (==comme ceci==).", + '--code-background': 'Définit la couleur de fond pour les blocs de code en ligne.', + '--text-highlight-bg': 'Définit la couleur de fond pour le texte surligné (==comme ceci==).', // Interactive Elements - "--interactive-normal": - "La couleur de fond pour les éléments interactifs comme les boutons.", - "--interactive-hover": - "La couleur de fond pour les éléments interactifs lors du survol.", - "--interactive-accent": + '--interactive-normal': 'La couleur de fond pour les éléments interactifs comme les boutons.', + '--interactive-hover': 'La couleur de fond pour les éléments interactifs lors du survol.', + '--interactive-accent': "La couleur d'accentuation pour les éléments interactifs importants (par exemple, le bouton 'Créer').", - "--interactive-accent-hover": + '--interactive-accent-hover': "La couleur d'accentuation pour les éléments interactifs importants lors du survol.", - "--interactive-success": - "Couleur indiquant une opération réussie (par exemple, vert).", - "--interactive-error": - "Couleur indiquant une erreur (par exemple, rouge).", - "--interactive-warning": - "Couleur indiquant un avertissement (par exemple, jaune).", + '--interactive-success': 'Couleur indiquant une opération réussie (par exemple, vert).', + '--interactive-error': 'Couleur indiquant une erreur (par exemple, rouge).', + '--interactive-warning': 'Couleur indiquant un avertissement (par exemple, jaune).', // UI Elements - "--titlebar-background": - "La couleur de fond de la barre de titre de la fenêtre principale.", - "--titlebar-background-focused": - "La couleur de fond de la barre de titre lorsque la fenêtre est active.", - "--titlebar-text-color": "La couleur du texte dans la barre de titre.", - "--sidebar-background": - "Cible spécifiquement l'arrière-plan des barres latérales.", - "--sidebar-border-color": - "La couleur de la bordure à côté des barres latérales.", - "--header-background": + '--titlebar-background': 'La couleur de fond de la barre de titre de la fenêtre principale.', + '--titlebar-background-focused': + 'La couleur de fond de la barre de titre lorsque la fenêtre est active.', + '--titlebar-text-color': 'La couleur du texte dans la barre de titre.', + '--sidebar-background': "Cible spécifiquement l'arrière-plan des barres latérales.", + '--sidebar-border-color': 'La couleur de la bordure à côté des barres latérales.', + '--header-background': "L'arrière-plan pour les en-têtes dans les volets (par exemple, l'en-tête du titre de la note).", - "--header-border-color": - "La couleur de la bordure sous les en-têtes de volet.", - "--vault-name-color": - "La couleur du nom de votre coffre dans le coin supérieur gauche.", - "--cm-notice-text-default": - "Définit la couleur de texte par défaut pour toutes les notifications, sauf si elle est remplacée par une règle.", - "--cm-notice-bg-default": - "Définit la couleur de fond par défaut pour toutes les notifications, sauf si elle est remplacée par une règle.", + '--header-border-color': 'La couleur de la bordure sous les en-têtes de volet.', + '--vault-name-color': 'La couleur du nom de votre coffre dans le coin supérieur gauche.', + '--cm-notice-text-default': + 'Définit la couleur de texte par défaut pour toutes les notifications, sauf si elle est remplacée par une règle.', + '--cm-notice-bg-default': + 'Définit la couleur de fond par défaut pour toutes les notifications, sauf si elle est remplacée par une règle.', // Graph View - "--graph-line": - "La couleur des lignes de connexion entre les notes dans la vue graphique.", - "--graph-node": - "La couleur des nœuds circulaires pour les notes existantes.", - "--graph-text": - "La couleur des étiquettes de texte sur les nœuds du graphique.", - "--graph-node-unresolved": + '--graph-line': 'La couleur des lignes de connexion entre les notes dans la vue graphique.', + '--graph-node': 'La couleur des nœuds circulaires pour les notes existantes.', + '--graph-text': 'La couleur des étiquettes de texte sur les nœuds du graphique.', + '--graph-node-unresolved': "La couleur des nœuds pour les notes qui n'existent pas encore (liens non résolus).", - "--graph-node-focused": - "Couleur du nœud qui est focalisé ou survolé (nœud en surbrillance).", - "--graph-node-tag": - "Couleur des nœuds représentant les tags lorsque les tags sont affichés dans le graphique.", - "--graph-node-attachment": - "Couleur des nœuds représentant les pièces jointes (par exemple, image ou autres fichiers liés).", + '--graph-node-focused': 'Couleur du nœud qui est focalisé ou survolé (nœud en surbrillance).', + '--graph-node-tag': + 'Couleur des nœuds représentant les tags lorsque les tags sont affichés dans le graphique.', + '--graph-node-attachment': + 'Couleur des nœuds représentant les pièces jointes (par exemple, image ou autres fichiers liés).', // Misc - "--scrollbar-thumb-bg": - "La couleur de la partie déplaçable de la barre de défilement.", - "--scrollbar-bg": - "La couleur de la piste de la barre de défilement (l'arrière-plan).", - "--divider-color": + '--scrollbar-thumb-bg': 'La couleur de la partie déplaçable de la barre de défilement.', + '--scrollbar-bg': "La couleur de la piste de la barre de défilement (l'arrière-plan).", + '--divider-color': "La couleur pour les lignes de séparation générales de l'interface, comme les bordures entre les paramètres.", }, }, diff --git a/src/i18n/strings.ts b/src/i18n/strings.ts index b97166a..7f820a3 100644 --- a/src/i18n/strings.ts +++ b/src/i18n/strings.ts @@ -1,12 +1,12 @@ -import type ColorMaster from "../main"; -import type { PluginSettings } from "../types"; -import type { LocaleStrings } from "./types"; -import { DEFAULT_LOCALE, LocaleCode } from "./types"; -import type { LocaleFunc } from "./types"; -import arStrings from "./locales/ar"; -import enStrings from "./locales/en"; -import faStrings from "./locales/fa"; -import frStrings from "./locales/fr"; +import type ColorMaster from '../main'; +import type { PluginSettings } from '../types'; +import type { LocaleStrings } from './types'; +import { DEFAULT_LOCALE, LocaleCode } from './types'; +import type { LocaleFunc } from './types'; +import arStrings from './locales/ar'; +import enStrings from './locales/en'; +import faStrings from './locales/fa'; +import frStrings from './locales/fr'; let T: ColorMaster; @@ -55,9 +55,7 @@ export function loadLanguage(settings: PluginSettings) { console.debug(`Color Master: Loading custom language "${langCode}"`); ACTIVE_STRINGS = customLang.translations; } else { - console.debug( - `Color Master: Language "${langCode}" not found, using default.`, - ); + console.debug(`Color Master: Language "${langCode}" not found, using default.`); ACTIVE_STRINGS = FALLBACK_STRINGS; } } @@ -82,7 +80,7 @@ export const t = (key: string, ...args: (string | number)[]): string => { } // Handle dynamic strings with arguments - if (typeof string === "function") { + if (typeof string === 'function') { return string(...args); } @@ -92,7 +90,7 @@ export const t = (key: string, ...args: (string | number)[]): string => { // Recursively flattens nested objects into dot-notation keys export function flattenStrings( obj: Record, - parentKey = "", + parentKey = '', result: Record = {}, ): Record { for (const key in obj) { @@ -101,10 +99,10 @@ export function flattenStrings( const value = obj[key]; if ( - typeof value === "object" && + typeof value === 'object' && value !== null && !Array.isArray(value) && - typeof value !== "function" + typeof value !== 'function' ) { flattenStrings(value, newKey, result); } else if (!Array.isArray(value)) { diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 5af1983..d1e37ff 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -1,20 +1,18 @@ -export type LocaleCode = "en" | "ar" | "fa" | "fr"; +export type LocaleCode = 'en' | 'ar' | 'fa' | 'fr'; export type LocaleFunc = (...args: (string | number)[]) => string; -export type LocalizedValue = - | string - | ((...args: (string | number)[]) => string); +export type LocalizedValue = string | ((...args: (string | number)[]) => string); /** * A dictionary of core language names to display in the list. */ export const CORE_LANGUAGES: Record = { - en: "English", - ar: "العَرَبيَّةُ", - fa: "فارسی", - fr: "Français", + en: 'English', + ar: 'العَرَبيَّةُ', + fa: 'فارسی', + fr: 'Français', }; -export const DEFAULT_LOCALE: LocaleCode = "en"; +export const DEFAULT_LOCALE: LocaleCode = 'en'; export interface LocaleStrings { plugin: { diff --git a/src/main.ts b/src/main.ts index 816365e..d4d2dee 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,24 +5,20 @@ * Description: Provides a comprehensive UI to control all Obsidian CSS color variables directly, * removing the need for Force Mode and expanding customization options. */ -import { ButtonComponent, moment, Notice, Plugin, requestUrl } from "obsidian"; -import { registerCommands } from "./commands"; -import { - DEFAULT_SETTINGS, - DEFAULT_VARS, - BUILT_IN_PROFILES_VARS, -} from "./constants"; -import { initializeT, t } from "./i18n/strings"; -import { PluginSettings } from "./types"; -import { ColorMasterSettingTab } from "./ui/settingsTab"; +import { ButtonComponent, moment, Notice, Plugin, requestUrl } from 'obsidian'; +import { registerCommands } from './commands'; +import { DEFAULT_SETTINGS, DEFAULT_VARS, BUILT_IN_PROFILES_VARS } from './constants'; +import { initializeT, t } from './i18n/strings'; +import { PluginSettings } from './types'; +import { ColorMasterSettingTab } from './ui/settingsTab'; import { flattenVars, findNextAvailablePath, maybeConvertToJpg, isIconizeEnabled, convertColorToHex, -} from "./utils"; -import { FileConflictModal } from "./ui/modals"; +} from './utils'; +import { FileConflictModal } from './ui/modals'; let T: ColorMaster; @@ -33,12 +29,12 @@ export default class ColorMaster extends Plugin { pendingVarUpdates: Record = {}; settingTabInstance: ColorMasterSettingTab | null = null; liveNoticeRules: unknown[] | null = null; - liveNoticeRuleType: "text" | "background" | null = null; + liveNoticeRuleType: 'text' | 'background' | null = null; iconizeObserver: MutationObserver; noticeObserver: MutationObserver; cssHistory: Record = {}; cachedThemeDefaults: Record | null = null; - lastCachedThemeMode: "dark" | "light" | null = null; + lastCachedThemeMode: 'dark' | 'light' | null = null; startColorUpdateLoop() { this.stopColorUpdateLoop(); @@ -53,7 +49,7 @@ export default class ColorMaster extends Plugin { if (pendingKeys.length === 0) return; // Optimization: Check specifically for Iconize var to avoid expensive DOM updates later - const iconizeUpdateNeeded = pendingKeys.includes("--iconize-icon-color"); + const iconizeUpdateNeeded = pendingKeys.includes('--iconize-icon-color'); for (const varName of pendingKeys) { const value = this.pendingVarUpdates[varName] ?? null; @@ -63,13 +59,13 @@ export default class ColorMaster extends Plugin { this.pendingVarUpdates = {}; // Notify Obsidian components (e.g., Graph View) to repaint - this.app.workspace.trigger("css-change"); + this.app.workspace.trigger('css-change'); if (iconizeUpdateNeeded) { try { this.forceIconizeColors(); } catch (e) { - console.warn("forceIconizeColors failed in update loop", e); + console.warn('forceIconizeColors failed in update loop', e); } } }, intervalMs); @@ -91,12 +87,12 @@ export default class ColorMaster extends Plugin { // Removes background image CSS variable and body classes. _clearBackgroundMedia() { - document.body.setCssProps({ "--cm-background-image": null }); - document.body.classList.remove("cm-workspace-background-active"); - document.body.classList.remove("cm-settings-background-active"); + document.body.setCssProps({ '--cm-background-image': null }); + document.body.classList.remove('cm-workspace-background-active'); + document.body.classList.remove('cm-settings-background-active'); // Delete any old video - const oldVideo = document.getElementById("cm-background-video"); + const oldVideo = document.getElementById('cm-background-video'); if (oldVideo) oldVideo.remove(); } @@ -106,28 +102,26 @@ export default class ColorMaster extends Plugin { const activeProfileName = this.settings.activeProfile; const profileOriginalVars = - BUILT_IN_PROFILES_VARS[ - activeProfileName as keyof typeof BUILT_IN_PROFILES_VARS - ]; + BUILT_IN_PROFILES_VARS[activeProfileName as keyof typeof BUILT_IN_PROFILES_VARS]; const baseVars = profileOriginalVars || BUILT_IN_PROFILES_VARS.Default; const varsToMakeTransparent = [ - "--background-primary", - "--background-secondary", - "--background-modifier-border", - "--titlebar-background-focused", - "--background-modifier-hover", + '--background-primary', + '--background-secondary', + '--background-modifier-border', + '--titlebar-background-focused', + '--background-modifier-hover', ]; let settingsChanged = false; for (const varName of varsToMakeTransparent) { - if (profile.vars[varName] !== "transparent") { + if (profile.vars[varName] !== 'transparent') { const defaultValue = baseVars[varName as keyof typeof baseVars]; if (!profile.vars[varName] || profile.vars[varName] === defaultValue) { - profile.vars[varName] = "transparent"; + profile.vars[varName] = 'transparent'; settingsChanged = true; if (this.settings.colorUpdateFPS > 0) { - this.pendingVarUpdates[varName] = "transparent"; + this.pendingVarUpdates[varName] = 'transparent'; } } } @@ -135,10 +129,7 @@ export default class ColorMaster extends Plugin { if (settingsChanged) { await this.saveData(this.settings); - if ( - this.settingTabInstance && - this.settingTabInstance.containerEl.offsetHeight > 0 - ) { + if (this.settingTabInstance && this.settingTabInstance.containerEl.offsetHeight > 0) { this.settingTabInstance.display(); } } @@ -153,11 +144,7 @@ export default class ColorMaster extends Plugin { this._clearBackgroundMedia(); // 2. Exit if the add-on is off, there is no profile, or the background is off - if ( - !profile || - profile.backgroundEnabled === false || - !profile.backgroundPath - ) { + if (!profile || profile.backgroundEnabled === false || !profile.backgroundPath) { if (profile && profile.backgroundEnabled === false) { await this._restoreDefaultBackgroundVars(); } @@ -169,22 +156,20 @@ export default class ColorMaster extends Plugin { const path = profile.backgroundPath; const type = profile.backgroundType; - if (type === "image") { + if (type === 'image') { const imageUrl = this.app.vault.adapter.getResourcePath(path); document.body.setCssProps({ - "--cm-background-image": `url("${imageUrl}")`, + '--cm-background-image': `url("${imageUrl}")`, }); - document.body.classList.add("cm-workspace-background-active"); - } else if (type === "video") { + document.body.classList.add('cm-workspace-background-active'); + } else if (type === 'video') { const videoUrl = this.app.vault.adapter.getResourcePath(path); - let videoEl = document.querySelector( - "#cm-background-video", - ); + let videoEl = document.querySelector('#cm-background-video'); if (!videoEl) { - videoEl = document.createElement("video"); - videoEl.id = "cm-background-video"; + videoEl = document.createElement('video'); + videoEl.id = 'cm-background-video'; document.body.appendChild(videoEl); } @@ -198,7 +183,7 @@ export default class ColorMaster extends Plugin { }); videoEl.load(); - document.body.classList.add("cm-workspace-background-active"); + document.body.classList.add('cm-workspace-background-active'); } } @@ -212,24 +197,22 @@ export default class ColorMaster extends Plugin { const activeProfileName = this.settings.activeProfile; const profileOriginalVars = - BUILT_IN_PROFILES_VARS[ - activeProfileName as keyof typeof BUILT_IN_PROFILES_VARS - ]; + BUILT_IN_PROFILES_VARS[activeProfileName as keyof typeof BUILT_IN_PROFILES_VARS]; const baseVars = profileOriginalVars || BUILT_IN_PROFILES_VARS.Default; // List of variables to check and potentially restore const varsToRestore = [ - "--background-primary", - "--background-secondary", - "--background-modifier-border", - "--titlebar-background-focused", - "--background-modifier-hover", + '--background-primary', + '--background-secondary', + '--background-modifier-border', + '--titlebar-background-focused', + '--background-modifier-hover', ]; let settingsRestored = false; for (const varName of varsToRestore) { // Only restore if currently 'transparent' - if (profile.vars[varName] === "transparent") { + if (profile.vars[varName] === 'transparent') { const defaultValue = baseVars[varName as keyof typeof baseVars]; if (defaultValue) { profile.vars[varName] = defaultValue; @@ -246,10 +229,7 @@ export default class ColorMaster extends Plugin { if (settingsRestored) { await this.saveData(this.settings); // Persist // Refresh settings UI if visible - if ( - this.settingTabInstance && - this.settingTabInstance.containerEl.offsetHeight > 0 - ) { + if (this.settingTabInstance && this.settingTabInstance.containerEl.offsetHeight > 0) { this.settingTabInstance.display(); } if (this.settings.colorUpdateFPS === 0) { @@ -271,15 +251,10 @@ export default class ColorMaster extends Plugin { if (!(await this.app.vault.adapter.exists(backgroundsPath))) { // Create the folder if it's missing await this.app.vault.adapter.mkdir(backgroundsPath); - console.debug( - `Color Master: Created global backgrounds folder at ${backgroundsPath}`, - ); + console.debug(`Color Master: Created global backgrounds folder at ${backgroundsPath}`); } } catch (error) { - console.error( - "Color Master: Failed to create backgrounds folder on startup.", - error, - ); + console.error('Color Master: Failed to create backgrounds folder on startup.', error); } } @@ -288,11 +263,11 @@ export default class ColorMaster extends Plugin { if (!pathToDelete) return; const varsToRestore = [ - "--background-primary", - "--background-secondary", - "--background-modifier-border", - "--titlebar-background-focused", - "--background-modifier-hover", + '--background-primary', + '--background-secondary', + '--background-modifier-border', + '--titlebar-background-focused', + '--background-modifier-hover', ]; let settingsChanged = false; @@ -303,21 +278,19 @@ export default class ColorMaster extends Plugin { if (profile.backgroundPath === pathToDelete) { // Clear the path - profile.backgroundPath = ""; + profile.backgroundPath = ''; profile.backgroundType = undefined; // Disable background automatically to prevent issues profile.backgroundEnabled = false; // Restore colors for THIS profile (Active or Inactive) const profileOriginalVars = - BUILT_IN_PROFILES_VARS[ - profileName as keyof typeof BUILT_IN_PROFILES_VARS - ] || BUILT_IN_PROFILES_VARS.Default; + BUILT_IN_PROFILES_VARS[profileName as keyof typeof BUILT_IN_PROFILES_VARS] || + BUILT_IN_PROFILES_VARS.Default; for (const varName of varsToRestore) { - if (profile.vars[varName] === "transparent") { - const defaultValue = - profileOriginalVars[varName as keyof typeof profileOriginalVars]; + if (profile.vars[varName] === 'transparent') { + const defaultValue = profileOriginalVars[varName as keyof typeof profileOriginalVars]; if (defaultValue) { profile.vars[varName] = defaultValue; @@ -338,10 +311,7 @@ export default class ColorMaster extends Plugin { await this.app.vault.adapter.remove(pathToDelete); } } catch (error) { - console.warn( - `Color Master: Could not delete background file '${pathToDelete}'.`, - error, - ); + console.warn(`Color Master: Could not delete background file '${pathToDelete}'.`, error); } if (settingsChanged) { @@ -350,7 +320,7 @@ export default class ColorMaster extends Plugin { // If the active profile was affected, we need to apply changes immediately // We use pending updates to make it smooth if live preview is on const activeProfile = this.settings.profiles[this.settings.activeProfile]; - if (activeProfile.backgroundPath === "") { + if (activeProfile.backgroundPath === '') { // Re-apply pending updates for the active profile just in case for (const varName of varsToRestore) { const val = activeProfile.vars[varName]; @@ -370,17 +340,20 @@ export default class ColorMaster extends Plugin { async setBackgroundMedia( arrayBuffer: ArrayBuffer, fileName: string, - conflictChoice: "replace" | "keep" | "prompt" = "prompt", + conflictChoice: 'replace' | 'keep' | 'prompt' = 'prompt', ) { const activeProfile = this.settings.profiles?.[this.settings.activeProfile]; if (!activeProfile) return; - const { arrayBuffer: finalArrayBuffer, fileName: finalFileName } = - await maybeConvertToJpg(activeProfile, arrayBuffer, fileName); + const { arrayBuffer: finalArrayBuffer, fileName: finalFileName } = await maybeConvertToJpg( + activeProfile, + arrayBuffer, + fileName, + ); - const fileExt = finalFileName.split(".").pop()?.toLowerCase(); - const mediaType: "image" | "video" = - fileExt === "mp4" || fileExt === "webm" ? "video" : "image"; + const fileExt = finalFileName.split('.').pop()?.toLowerCase(); + const mediaType: 'image' | 'video' = + fileExt === 'mp4' || fileExt === 'webm' ? 'video' : 'image'; const backgroundsPath = `${this.app.vault.configDir}/backgrounds`; @@ -395,35 +368,22 @@ export default class ColorMaster extends Plugin { const fileExists = await this.app.vault.adapter.exists(targetPath); // 2. Resolve conflicts if file exists - if (fileExists && conflictChoice === "prompt") { - new FileConflictModal( - this.app, - this, - finalArrayBuffer, - finalFileName, - (choice) => { - // run async logic safely - void (async () => { - await this.setBackgroundMedia( - finalArrayBuffer, - finalFileName, - choice, - ); - })().catch((err) => { - console.error("Failed to set background media:", err); - }); - }, - ).open(); + if (fileExists && conflictChoice === 'prompt') { + new FileConflictModal(this.app, this, finalArrayBuffer, finalFileName, (choice) => { + // run async logic safely + void (async () => { + await this.setBackgroundMedia(finalArrayBuffer, finalFileName, choice); + })().catch((err) => { + console.error('Failed to set background media:', err); + }); + }).open(); return; } - if (fileExists && conflictChoice === "keep") { - targetPath = await findNextAvailablePath( - this.app.vault.adapter, - targetPath, - ); + if (fileExists && conflictChoice === 'keep') { + targetPath = await findNextAvailablePath(this.app.vault.adapter, targetPath); } - if (fileExists && conflictChoice === "replace") { + if (fileExists && conflictChoice === 'replace') { await this.app.vault.adapter.remove(targetPath); } @@ -438,13 +398,13 @@ export default class ColorMaster extends Plugin { // 5. Save settings (this will trigger applyBackgroundMedia) await this.saveSettings(); - new Notice(t("notices.bgSet")); + new Notice(t('notices.bgSet')); // 6. Cleanup previous file if replacing original name and paths differ if ( oldImagePath && oldImagePath !== targetPath && - conflictChoice === "replace" // Only delete if 'replace' + conflictChoice === 'replace' // Only delete if 'replace' ) { if (await this.app.vault.adapter.exists(oldImagePath)) { await this.app.vault.adapter.remove(oldImagePath); @@ -456,8 +416,8 @@ export default class ColorMaster extends Plugin { this.settingTabInstance.display(); } } catch (error) { - new Notice(t("notices.backgroundLoadError")); - console.error("Color Master: Error setting background media:", error); + new Notice(t('notices.backgroundLoadError')); + console.error('Color Master: Error setting background media:', error); } } @@ -472,18 +432,18 @@ export default class ColorMaster extends Plugin { } // eslint-disable-next-line obsidianmd/no-forbidden-elements - const el = document.createElement("style"); + const el = document.createElement('style'); el.id = `cm-custom-css-for-profile`; el.textContent = profile.customCss; document.head.appendChild(el); } catch (e) { - console.warn("applyCustomCssForProfile failed", e); + console.warn('applyCustomCssForProfile failed', e); } } removeInjectedCustomCss() { try { - const oldStyle = document.getElementById("cm-custom-css-for-profile"); + const oldStyle = document.getElementById('cm-custom-css-for-profile'); if (oldStyle) oldStyle.remove(); } catch (e) { console.warn(e); @@ -496,36 +456,29 @@ export default class ColorMaster extends Plugin { if (!activeProfile) return; const globalSnippets = this.settings.globalSnippets || []; - const profileSnippets = Array.isArray(activeProfile.snippets) - ? activeProfile.snippets - : []; + const profileSnippets = Array.isArray(activeProfile.snippets) ? activeProfile.snippets : []; const allSnippets = [...globalSnippets, ...profileSnippets].filter(Boolean); const enabledCss = allSnippets .filter((s) => s.enabled && s.css) .map((s) => { - const upgradedCss = s.css.replace( - /\bbody\s*(?=\{)/g, - "body.theme-dark, body.theme-light", - ); - return `/* Snippet: ${s.name} ${ - s.isGlobal ? "(Global)" : "" - } */\n${upgradedCss}`; + const upgradedCss = s.css.replace(/\bbody\s*(?=\{)/g, 'body.theme-dark, body.theme-light'); + return `/* Snippet: ${s.name} ${s.isGlobal ? '(Global)' : ''} */\n${upgradedCss}`; }) - .join("\n\n"); + .join('\n\n'); if (enabledCss) { // eslint-disable-next-line obsidianmd/no-forbidden-elements - const el = document.createElement("style"); - el.id = "cm-css-snippets"; + const el = document.createElement('style'); + el.id = 'cm-css-snippets'; el.textContent = enabledCss; document.head.appendChild(el); } } removeCssSnippets() { - const el = document.getElementById("cm-css-snippets"); + const el = document.getElementById('cm-css-snippets'); if (el) el.remove(); } @@ -536,8 +489,8 @@ export default class ColorMaster extends Plugin { const keys = Object.keys(pending); if (keys.length === 0) { // If nothing is pending, still trigger a repaint for safety - this.app.workspace.trigger("css-change"); - window.dispatchEvent(new Event("resize")); + this.app.workspace.trigger('css-change'); + window.dispatchEvent(new Event('resize')); return; } @@ -551,13 +504,13 @@ export default class ColorMaster extends Plugin { this.pendingVarUpdates = {}; // Notify Obsidian and other components to update - this.app.workspace.trigger("css-change"); + this.app.workspace.trigger('css-change'); this.forceIconizeColors(); // Force graph view to repaint by dispatching a resize event - window.dispatchEvent(new Event("resize")); + window.dispatchEvent(new Event('resize')); } catch (e) { - console.error("Color Master: applyPendingNow failed", e); + console.error('Color Master: applyPendingNow failed', e); } } @@ -566,17 +519,15 @@ export default class ColorMaster extends Plugin { this.settings.pinnedSnapshots = this.settings.pinnedSnapshots || {}; const profile = this.settings.profiles?.[profileName]; if (!profile) { - new Notice(t("notices.profileNotFound")); + new Notice(t('notices.profileNotFound')); return; } const snapshotData = { vars: JSON.parse(JSON.stringify(profile.vars || {})), - customCss: profile.customCss || "", + customCss: profile.customCss || '', snippets: JSON.parse(JSON.stringify(profile.snippets || {})), - noticeRules: JSON.parse( - JSON.stringify(profile.noticeRules || { text: [], background: [] }), - ), + noticeRules: JSON.parse(JSON.stringify(profile.noticeRules || { text: [], background: [] })), }; this.settings.pinnedSnapshots[profileName] = { @@ -590,21 +541,19 @@ export default class ColorMaster extends Plugin { if (!profileName) profileName = this.settings.activeProfile; const snap = this.settings.pinnedSnapshots?.[profileName]; if (!snap || !snap.vars) { - new Notice(t("notices.noPinnedSnapshot")); + new Notice(t('notices.noPinnedSnapshot')); return; } const activeProfile = this.settings.profiles[profileName]; if (!activeProfile) { - new Notice(t("notices.profileNotFound")); + new Notice(t('notices.profileNotFound')); return; } activeProfile.vars = JSON.parse(JSON.stringify(snap.vars)); - activeProfile.customCss = snap.customCss || ""; - activeProfile.snippets = snap.snippets - ? JSON.parse(JSON.stringify(snap.snippets)) - : {}; + activeProfile.customCss = snap.customCss || ''; + activeProfile.snippets = snap.snippets ? JSON.parse(JSON.stringify(snap.snippets)) : {}; activeProfile.noticeRules = snap.noticeRules ? JSON.parse(JSON.stringify(snap.noticeRules)) : { text: [], background: [] }; @@ -643,18 +592,18 @@ export default class ColorMaster extends Plugin { await this._ensureBackgroundsFolderExists(); this.liveNoticeRules = null; this.liveNoticeRuleType = null; - if (this.settings.language === "auto") { + if (this.settings.language === 'auto') { const obsidianLang = moment.locale(); // Determine the language based on Obsidian's current locale - if (obsidianLang === "ar") { - this.settings.language = "ar"; - } else if (obsidianLang === "fa") { - this.settings.language = "fa"; - } else if (obsidianLang === "fr") { - this.settings.language = "fr"; + if (obsidianLang === 'ar') { + this.settings.language = 'ar'; + } else if (obsidianLang === 'fa') { + this.settings.language = 'fa'; + } else if (obsidianLang === 'fr') { + this.settings.language = 'fr'; } else { - this.settings.language = "en"; // The default + this.settings.language = 'en'; // The default } await this.saveSettings(); } @@ -664,15 +613,11 @@ export default class ColorMaster extends Plugin { registerCommands(this); // Add a ribbon icon to the left gutter - this.addRibbonIcon( - "paint-bucket", - t("plugin.ribbonTooltip"), - (_evt: MouseEvent) => { - // Open the settings tab when the icon is clicked - (this.app as unknown).setting.open(); - (this.app as unknown).setting.openTabById(this.manifest.id); - }, - ); + this.addRibbonIcon('paint-bucket', t('plugin.ribbonTooltip'), (_evt: MouseEvent) => { + // Open the settings tab when the icon is clicked + (this.app as unknown).setting.open(); + (this.app as unknown).setting.openTabById(this.manifest.id); + }); // Store a reference to the settings tab and add it this.settingTabInstance = new ColorMasterSettingTab(this.app, this); @@ -680,15 +625,12 @@ export default class ColorMaster extends Plugin { this.app.workspace.onLayoutReady(() => { void this.applyStyles(); - setTimeout(() => this.app.workspace.trigger("css-change"), 100); + setTimeout(() => this.app.workspace.trigger('css-change'), 100); // Start the update engine this.startColorUpdateLoop(); this.iconizeObserver = new MutationObserver(() => { - if ( - this.settings.pluginEnabled && - this.settings.overrideIconizeColors - ) { + if (this.settings.pluginEnabled && this.settings.overrideIconizeColors) { this.forceIconizeColors(); } }); @@ -699,11 +641,11 @@ export default class ColorMaster extends Plugin { mutation.addedNodes.forEach((node) => { if (node.nodeType !== 1) return; - if ((node as Element).matches(".notice, .toast")) { + if ((node as Element).matches('.notice, .toast')) { this.processNotice(node as HTMLElement); } (node as Element) - .querySelectorAll(".notice, .toast") + .querySelectorAll('.notice, .toast') .forEach(this.processNotice.bind(this)); }); }); @@ -723,10 +665,10 @@ export default class ColorMaster extends Plugin { if (this.iconizeObserver) this.iconizeObserver.disconnect(); if (this.settings) { - this.settings.lastSearchQuery = ""; + this.settings.lastSearchQuery = ''; this.settings.lastScrollPosition = 0; this.saveData(this.settings).catch((err) => { - console.error("Color Master: Failed to save settings on unload.", err); + console.error('Color Master: Failed to save settings on unload.', err); }); } @@ -734,7 +676,7 @@ export default class ColorMaster extends Plugin { this.removeInjectedCustomCss(); this.stopColorUpdateLoop(); this._clearBackgroundMedia(); - console.debug("Color Master unloaded."); + console.debug('Color Master unloaded.'); } public enableObservers(): void { @@ -747,9 +689,9 @@ export default class ColorMaster extends Plugin { childList: true, subtree: true, }); - console.debug("Color Master Observers: Enabled"); + console.debug('Color Master Observers: Enabled'); } catch (e) { - console.error("Color Master: Failed to enable observers", e); + console.error('Color Master: Failed to enable observers', e); } } @@ -757,14 +699,14 @@ export default class ColorMaster extends Plugin { try { this.iconizeObserver.disconnect(); this.noticeObserver.disconnect(); - console.debug("Color Master Observers: Disabled"); + console.debug('Color Master Observers: Disabled'); } catch (e) { - console.error("Color Master: Failed to disable observers", e); + console.error('Color Master: Failed to disable observers', e); } } async refreshOpenGraphViews() { - const graphLeaves = this.app.workspace.getLeavesOfType("graph"); + const graphLeaves = this.app.workspace.getLeavesOfType('graph'); if (graphLeaves.length === 0) { return; } @@ -778,7 +720,7 @@ export default class ColorMaster extends Plugin { await leaf.setViewState({ ...currentState, - type: "graph", + type: 'graph', state: { ...currentState.state }, }); } @@ -788,35 +730,28 @@ export default class ColorMaster extends Plugin { // read the *computed* CSS var first (this covers live-preview via pendingVarUpdates) let computedIconizeColor = null; try { - const cssVal = getComputedStyle(document.body).getPropertyValue( - "--iconize-icon-color", - ); + const cssVal = getComputedStyle(document.body).getPropertyValue('--iconize-icon-color'); if (cssVal) computedIconizeColor = cssVal.trim(); } catch (e) { - console.warn( - "Color Master: failed to read computed --iconize-icon-color", - e, - ); + console.warn('Color Master: failed to read computed --iconize-icon-color', e); computedIconizeColor = null; } // fallback to stored profile value if computed is empty const storedIconizeColor = - this.settings.profiles?.[this.settings.activeProfile]?.vars?.[ - "--iconize-icon-color" - ]; + this.settings.profiles?.[this.settings.activeProfile]?.vars?.['--iconize-icon-color']; const iconizeColor = this.settings.overrideIconizeColors ? computedIconizeColor || storedIconizeColor || null : null; // Iterate over elements that Iconize marks (keep the original safe logic) - document.querySelectorAll(".iconize-icon").forEach((iconNode) => { - const svg = iconNode.querySelector("svg"); + document.querySelectorAll('.iconize-icon').forEach((iconNode) => { + const svg = iconNode.querySelector('svg'); if (!svg) return; - [svg, ...svg.querySelectorAll("*")].forEach((el: unknown) => { - if (typeof el.hasAttribute !== "function") return; + [svg, ...svg.querySelectorAll('*')].forEach((el: unknown) => { + if (typeof el.hasAttribute !== 'function') return; if (!iconizeColor) { // remove inline overrides to let theme/defaults show @@ -827,19 +762,15 @@ export default class ColorMaster extends Plugin { return; } - const originalFill = el.getAttribute("fill"); - const originalStroke = el.getAttribute("stroke"); + const originalFill = el.getAttribute('fill'); + const originalStroke = el.getAttribute('stroke'); // apply with !important so plugin/theme inline styles are overridden - if ( - originalFill && - originalFill !== "none" && - !originalFill.startsWith("url(") - ) { + if (originalFill && originalFill !== 'none' && !originalFill.startsWith('url(')) { el.setCssProps({ fill: iconizeColor }); } - if (originalStroke && originalStroke !== "none") { + if (originalStroke && originalStroke !== 'none') { el.setCssProps({ stroke: iconizeColor }); } }); @@ -857,15 +788,13 @@ export default class ColorMaster extends Plugin { // If not installed, check if the override setting is still on. let settingsChanged = false; if (this.settings.overrideIconizeColors === true) { - console.debug( - "Color Master: Iconize plugin not found. Disabling override setting.", - ); + console.debug('Color Master: Iconize plugin not found. Disabling override setting.'); this.settings.overrideIconizeColors = false; settingsChanged = true; } // Find all elements with the .iconize-icon class and check if they have content. - const orphanedIcons = document.querySelectorAll(".iconize-icon"); + const orphanedIcons = document.querySelectorAll('.iconize-icon'); if (orphanedIcons.length > 0) { console.debug( @@ -876,10 +805,7 @@ export default class ColorMaster extends Plugin { if (settingsChanged) { void this.saveSettings().catch((err) => { - console.error( - "Failed to save settings after removing Iconize leftovers:", - err, - ); + console.error('Failed to save settings after removing Iconize leftovers:', err); }); } } @@ -894,7 +820,7 @@ export default class ColorMaster extends Plugin { const profile = this.settings.profiles[this.settings.activeProfile]; if (!profile) { - console.error("Color Master: Active profile not found!"); + console.error('Color Master: Active profile not found!'); return; } @@ -902,14 +828,14 @@ export default class ColorMaster extends Plugin { if (profileVars.length > 0) { const cssString = `body.theme-dark, body.theme-light { ${profileVars - .map(([key, value]) => (value ? `${key}: ${value};` : "")) + .map(([key, value]) => (value ? `${key}: ${value};` : '')) .filter(Boolean) - .join("\n ")} + .join('\n ')} }`; // eslint-disable-next-line obsidianmd/no-forbidden-elements - const styleEl = document.createElement("style"); - styleEl.id = "cm-profile-variables"; + const styleEl = document.createElement('style'); + styleEl.id = 'cm-profile-variables'; styleEl.textContent = cssString; document.head.appendChild(styleEl); } @@ -917,30 +843,25 @@ export default class ColorMaster extends Plugin { this.forceIconizeColors(); setTimeout(() => this.forceIconizeColors(), 100); - const themeType = profile.themeType || "auto"; + const themeType = profile.themeType || 'auto'; // Handle theme toggle (Dark/Light/Auto) - if (themeType === "dark") { - document.body.classList.remove("theme-light"); - document.body.classList.add("theme-dark"); - } else if (themeType === "light") { - document.body.classList.remove("theme-dark"); - document.body.classList.add("theme-light"); + if (themeType === 'dark') { + document.body.classList.remove('theme-light'); + document.body.classList.add('theme-dark'); + } else if (themeType === 'light') { + document.body.classList.remove('theme-dark'); + document.body.classList.add('theme-light'); } else { - const currentConfig = (this.app.vault as unknown).getConfig("theme"); - const isSystemDark = window.matchMedia( - "(prefers-color-scheme: dark)", - ).matches; + const currentConfig = (this.app.vault as unknown).getConfig('theme'); + const isSystemDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - if ( - currentConfig === "obsidian" || - (currentConfig === "system" && isSystemDark) - ) { - document.body.classList.remove("theme-light"); - document.body.classList.add("theme-dark"); + if (currentConfig === 'obsidian' || (currentConfig === 'system' && isSystemDark)) { + document.body.classList.remove('theme-light'); + document.body.classList.add('theme-dark'); } else { - document.body.classList.remove("theme-dark"); - document.body.classList.add("theme-light"); + document.body.classList.remove('theme-dark'); + document.body.classList.add('theme-light'); } } this.applyCustomCssForProfile(this.settings.activeProfile); @@ -949,23 +870,23 @@ export default class ColorMaster extends Plugin { // Add/Remove body class for RTL notices const langCode = this.settings.language; const customLang = this.settings.customLanguages?.[langCode]; - const isCoreRtlLang = langCode === "ar" || langCode === "fa"; + const isCoreRtlLang = langCode === 'ar' || langCode === 'fa'; const isCustomRtlLang = customLang?.isRtl === true; const isRtlEnabled = this.settings.useRtlLayout; const isRTL = (isCoreRtlLang || isCustomRtlLang) && isRtlEnabled; if (isRTL) { - document.body.classList.add("color-master-rtl"); + document.body.classList.add('color-master-rtl'); } else { - document.body.classList.remove("color-master-rtl"); + document.body.classList.remove('color-master-rtl'); } await this.applyBackgroundMedia(); - this.app.workspace.trigger("css-change"); - window.dispatchEvent(new Event("resize")); + this.app.workspace.trigger('css-change'); + window.dispatchEvent(new Event('resize')); setTimeout(() => { - this.app.workspace.trigger("css-change"); - const graphLeaves = this.app.workspace.getLeavesOfType("graph"); + this.app.workspace.trigger('css-change'); + const graphLeaves = this.app.workspace.getLeavesOfType('graph'); graphLeaves.forEach((leaf) => { if (leaf.view && (leaf.view as unknown).reload) { (leaf.view as unknown).reload(); @@ -975,7 +896,7 @@ export default class ColorMaster extends Plugin { } clearStyles() { - const profileStyleEl = document.getElementById("cm-profile-variables"); + const profileStyleEl = document.getElementById('cm-profile-variables'); if (profileStyleEl) { profileStyleEl.remove(); } @@ -994,12 +915,12 @@ export default class ColorMaster extends Plugin { document.body.setCssProps({ [key]: null }); }); - document.querySelectorAll(".iconize-icon").forEach((iconNode) => { - const svg = iconNode.querySelector("svg"); + document.querySelectorAll('.iconize-icon').forEach((iconNode) => { + const svg = iconNode.querySelector('svg'); if (!svg) return; - [svg, ...svg.querySelectorAll("*")].forEach((el: unknown) => { - if (typeof el.hasAttribute !== "function") return; + [svg, ...svg.querySelectorAll('*')].forEach((el: unknown) => { + if (typeof el.hasAttribute !== 'function') return; el.setCssProps({ fill: null, @@ -1008,13 +929,13 @@ export default class ColorMaster extends Plugin { }); }); - const styleId = "color-master-overrides"; + const styleId = 'color-master-overrides'; const overrideStyleEl = document.getElementById(styleId); if (overrideStyleEl) { overrideStyleEl.remove(); } - this.app.workspace.trigger("css-change"); - document.body.classList.remove("color-master-rtl"); + this.app.workspace.trigger('css-change'); + document.body.classList.remove('color-master-rtl'); this._clearBackgroundMedia(); } @@ -1022,19 +943,12 @@ export default class ColorMaster extends Plugin { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); let migrationNeeded = false; - if ( - this.settings.noticeRules && - Object.keys(this.settings.profiles || {}).length > 0 - ) { - console.debug( - "Color Master: Detected old global notice rules. Starting migration...", - ); + if (this.settings.noticeRules && Object.keys(this.settings.profiles || {}).length > 0) { + console.debug('Color Master: Detected old global notice rules. Starting migration...'); for (const profileName in this.settings.profiles) { const profile = this.settings.profiles[profileName]; if (!profile.noticeRules) { - profile.noticeRules = JSON.parse( - JSON.stringify(this.settings.noticeRules), - ); + profile.noticeRules = JSON.parse(JSON.stringify(this.settings.noticeRules)); } } delete (this.settings as unknown).noticeRules; @@ -1051,7 +965,7 @@ export default class ColorMaster extends Plugin { if (migrationNeeded) { console.debug( - "Color Master: Notice rules migration complete. Saving new settings structure.", + 'Color Master: Notice rules migration complete. Saving new settings structure.', ); await this.saveData(this.settings); } @@ -1060,11 +974,11 @@ export default class ColorMaster extends Plugin { this.settings.installDate = new Date().toISOString(); // Check the current Obsidian theme to set a smart default profile. - const isDarkMode = document.body.classList.contains("theme-dark"); + const isDarkMode = document.body.classList.contains('theme-dark'); if (isDarkMode) { - this.settings.activeProfile = "Default"; + this.settings.activeProfile = 'Default'; } else { - this.settings.activeProfile = "Citrus Zest"; + this.settings.activeProfile = 'Citrus Zest'; } await this.saveData(this.settings); @@ -1080,14 +994,14 @@ export default class ColorMaster extends Plugin { profile && profile.snippets && !Array.isArray(profile.snippets) && - typeof profile.snippets === "object" + typeof profile.snippets === 'object' ) { snippetsMigrationNeeded = true; const snippetsArray = Object.entries(profile.snippets).map( ([name, data]: [string, unknown]) => ({ id: `snippet-${Date.now()}-${Math.random()}`, name: name, - css: data.css || "", + css: data.css || '', enabled: data.enabled !== false, }), ); @@ -1097,7 +1011,7 @@ export default class ColorMaster extends Plugin { if (snippetsMigrationNeeded) { console.debug( - "Color Master: The clipping data structure is being migrated to the new format (array).", + 'Color Master: The clipping data structure is being migrated to the new format (array).', ); await this.saveData(this.settings); } @@ -1106,28 +1020,18 @@ export default class ColorMaster extends Plugin { for (const profileName in this.settings.profiles) { const profile = this.settings.profiles[profileName]; // If background exists but enabled status is not set, default it to true - if ( - profile && - profile.backgroundPath && - typeof profile.backgroundEnabled === "undefined" - ) { + if (profile && profile.backgroundPath && typeof profile.backgroundEnabled === 'undefined') { profile.backgroundEnabled = true; profileMigrationNeeded = true; } // If background doesn't exist, ensure enabled is false or undefined - else if ( - profile && - !profile.backgroundPath && - profile.backgroundEnabled === true - ) { + else if (profile && !profile.backgroundPath && profile.backgroundEnabled === true) { profile.backgroundEnabled = false; profileMigrationNeeded = true; } } if (profileMigrationNeeded) { - console.debug( - "Color Master: Setting default backgroundEnabled status for profiles.", - ); + console.debug('Color Master: Setting default backgroundEnabled status for profiles.'); await this.saveData(this.settings); // Save changes if migration happened } } @@ -1136,7 +1040,7 @@ export default class ColorMaster extends Plugin { await this.saveData(this.settings); await this.applyStyles(); await this.refreshOpenGraphViews(); - this.app.workspace.trigger("css-change"); + this.app.workspace.trigger('css-change'); } async resetPluginData(options: { @@ -1149,9 +1053,7 @@ export default class ColorMaster extends Plugin { const oldInstallDate = this.settings.installDate; // Deep clone defaults to ensure a clean slate before merging preserved data - const newSettings = JSON.parse( - JSON.stringify(DEFAULT_SETTINGS), - ) as PluginSettings; + const newSettings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS)) as PluginSettings; // 1. Preserve Settings (if not deleting) if (!options.deleteSettings) { @@ -1196,20 +1098,20 @@ export default class ColorMaster extends Plugin { // Reset specific settings if requested if (options.deleteSettings) { newSettings.advancedResetOptions = this.settings.advancedResetOptions; - newSettings.language = "auto"; + newSettings.language = 'auto'; } this.settings = newSettings; await this.saveData(this.settings); - console.debug("Color Master: Selective data reset complete.", options); + console.debug('Color Master: Selective data reset complete.', options); // Handle File System operations (Backgrounds) if (options.deleteBackgrounds) { const backgroundsPath = `${this.app.vault.configDir}/backgrounds`; try { if (await this.app.vault.adapter.exists(backgroundsPath)) { - console.debug("Color Master: Deleting backgrounds folder..."); + console.debug('Color Master: Deleting backgrounds folder...'); await this.app.vault.adapter.rmdir(backgroundsPath, true); } @@ -1225,38 +1127,36 @@ export default class ColorMaster extends Plugin { await this.saveData(this.settings); } } catch (folderError) { - console.error( - `Color Master: Error deleting backgrounds: ${folderError.message}`, - ); - new Notice(t("notices.deleteBackgroundsError", folderError.message)); + console.error(`Color Master: Error deleting backgrounds: ${folderError.message}`); + new Notice(t('notices.deleteBackgroundsError', folderError.message)); } } // UI Feedback & Reload - const notice = new Notice(t("notices.resetSuccess"), 15000); + const notice = new Notice(t('notices.resetSuccess'), 15000); const buttonContainer = notice.messageEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); new ButtonComponent(buttonContainer) - .setButtonText(t("buttons.reload")) + .setButtonText(t('buttons.reload')) .setCta() .onClick(() => { - (this.app as unknown).commands.executeCommandById("app:reload"); + (this.app as unknown).commands.executeCommandById('app:reload'); }); } processNotice(el: HTMLElement) { // Prevent processing the same element multiple times - if (!el || !el.classList || el.dataset.cmProcessed === "true") return; + if (!el || !el.classList || el.dataset.cmProcessed === 'true') return; // Extract text content (handle test keywords if present) - let noticeText = (el.textContent || "").toLowerCase(); - const testKeywordEl = el.querySelector(".cm-test-keyword"); + let noticeText = (el.textContent || '').toLowerCase(); + const testKeywordEl = el.querySelector('.cm-test-keyword'); if (testKeywordEl) { - noticeText = (testKeywordEl.textContent || "").toLowerCase(); + noticeText = (testKeywordEl.textContent || '').toLowerCase(); } - el.dataset.cmProcessed = "true"; + el.dataset.cmProcessed = 'true'; try { const settings = this.settings; @@ -1268,11 +1168,11 @@ export default class ColorMaster extends Plugin { // --- 1. Background Rules (Base Style) --- const bgRules = - liveRuleType === "background" && liveRules + liveRuleType === 'background' && liveRules ? liveRules : activeProfile?.noticeRules?.background || []; - let finalBgColor = activeProfile.vars["--cm-notice-bg-default"]; + let finalBgColor = activeProfile.vars['--cm-notice-bg-default']; for (const rule of bgRules) { if (!rule.keywords?.trim()) continue; @@ -1281,24 +1181,22 @@ export default class ColorMaster extends Plugin { if (rule.isRegex) { try { - if (new RegExp(keywords, "i").test(noticeText)) match = true; + if (new RegExp(keywords, 'i').test(noticeText)) match = true; } catch { // ignore invalid regex } } else { // Optimize: Only process if keyword array isn't empty const keywordArray = keywords - .split(",") + .split(',') .map((k: string) => k.trim()) .filter(Boolean); if (keywordArray.length > 0) { const escaped = keywordArray.map((k: string) => - k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), + k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), ); // Enforce whole-word matching to avoid partial false positives - if ( - new RegExp(`\\b(${escaped.join("|")})\\b`, "i").test(noticeText) - ) { + if (new RegExp(`\\b(${escaped.join('|')})\\b`, 'i').test(noticeText)) { match = true; } } @@ -1316,11 +1214,9 @@ export default class ColorMaster extends Plugin { // --- 2. Text Rules (Split: Base Color vs. DOM Highlights) --- const textRules = - liveRuleType === "text" && liveRules - ? liveRules - : activeProfile?.noticeRules?.text || []; + liveRuleType === 'text' && liveRules ? liveRules : activeProfile?.noticeRules?.text || []; - let finalTextColor = activeProfile.vars["--cm-notice-text-default"]; + let finalTextColor = activeProfile.vars['--cm-notice-text-default']; const highlightRules: unknown[] = []; const fullColorRules: unknown[] = []; @@ -1341,22 +1237,20 @@ export default class ColorMaster extends Plugin { if (rule.isRegex) { try { - if (new RegExp(keywords, "i").test(noticeText)) match = true; + if (new RegExp(keywords, 'i').test(noticeText)) match = true; } catch { // ignore invalid regex } } else { const keywordArray = keywords - .split(",") + .split(',') .map((k: string) => k.trim()) .filter(Boolean); if (keywordArray.length > 0) { const escaped = keywordArray.map((k: string) => - k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), + k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), ); - if ( - new RegExp(`\\b(${escaped.join("|")})\\b`, "i").test(noticeText) - ) { + if (new RegExp(`\\b(${escaped.join('|')})\\b`, 'i').test(noticeText)) { match = true; } } @@ -1378,9 +1272,7 @@ export default class ColorMaster extends Plugin { // Use TreeWalker to safely traverse text nodes without breaking nested HTML const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { - return node.parentElement?.classList.contains( - "cm-keyword-highlight", - ) + return node.parentElement?.classList.contains('cm-keyword-highlight') ? NodeFilter.FILTER_REJECT // Skip already highlighted : NodeFilter.FILTER_ACCEPT; }, @@ -1396,8 +1288,7 @@ export default class ColorMaster extends Plugin { if (!parent) continue; // Collect all matches from all highlight rules - const allMatches: { start: number; end: number; color: string }[] = - []; + const allMatches: { start: number; end: number; color: string }[] = []; for (const rule of highlightRules) { if (!rule.keywords?.trim()) continue; @@ -1405,7 +1296,7 @@ export default class ColorMaster extends Plugin { if (rule.isRegex) { try { - const regex = new RegExp(rule.keywords, "gi"); + const regex = new RegExp(rule.keywords, 'gi'); let match; while ((match = regex.exec(nodeContent)) !== null) { if (match[0].length === 0) break; @@ -1416,21 +1307,18 @@ export default class ColorMaster extends Plugin { }); } } catch (e) { - console.warn("Color Master: Invalid Regex in notice rule", e); + console.warn('Color Master: Invalid Regex in notice rule', e); } } else { const keywords = rule.keywords - .split(",") + .split(',') .map((k: string) => k.trim()) .filter(Boolean); if (keywords.length > 0) { const escaped = keywords.map((k: string) => - k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), - ); - const ruleRegex = new RegExp( - `\\b(${escaped.join("|")})\\b`, - "gi", + k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), ); + const ruleRegex = new RegExp(`\\b(${escaped.join('|')})\\b`, 'gi'); let match; while ((match = ruleRegex.exec(nodeContent)) !== null) { allMatches.push({ @@ -1466,13 +1354,11 @@ export default class ColorMaster extends Plugin { for (const match of uniqueMatches) { if (match.start > lastIndex) { fragments.appendChild( - document.createTextNode( - nodeContent.substring(lastIndex, match.start), - ), + document.createTextNode(nodeContent.substring(lastIndex, match.start)), ); } - const span = document.createElement("span"); - span.className = "cm-keyword-highlight"; + const span = document.createElement('span'); + span.className = 'cm-keyword-highlight'; span.setCssProps({ color: match.color }); span.textContent = nodeContent.substring(match.start, match.end); fragments.appendChild(span); @@ -1480,9 +1366,7 @@ export default class ColorMaster extends Plugin { } if (lastIndex < nodeContent.length) { - fragments.appendChild( - document.createTextNode(nodeContent.substring(lastIndex)), - ); + fragments.appendChild(document.createTextNode(nodeContent.substring(lastIndex))); } parent.replaceChild(fragments, node); @@ -1491,16 +1375,16 @@ export default class ColorMaster extends Plugin { this.updateNoticeStyles(); } catch (e) { - console.warn("Color Master: processNotice failed", e); + console.warn('Color Master: processNotice failed', e); } } updateNoticeStyles() { - const styleId = "cm-dynamic-notice-styles"; + const styleId = 'cm-dynamic-notice-styles'; let styleEl = document.getElementById(styleId) as HTMLStyleElement | null; const notices = document.querySelectorAll( - "[data-cm-notice-bg], [data-cm-notice-text]", + '[data-cm-notice-bg], [data-cm-notice-text]', ); if (notices.length === 0) { if (styleEl) styleEl.remove(); @@ -1509,7 +1393,7 @@ export default class ColorMaster extends Plugin { if (!styleEl) { // eslint-disable-next-line obsidianmd/no-forbidden-elements - styleEl = document.createElement("style"); + styleEl = document.createElement('style'); styleEl.id = styleId; document.head.appendChild(styleEl); } @@ -1529,11 +1413,11 @@ export default class ColorMaster extends Plugin { if (textColor) { rule += ` color: ${textColor} !important;`; } - rule += " }"; + rule += ' }'; cssRules.push(rule); }); - styleEl.textContent = cssRules.join("\n"); + styleEl.textContent = cssRules.join('\n'); } /** @@ -1589,7 +1473,7 @@ export default class ColorMaster extends Plugin { // Downloads an image from a URL and sets it as the background. async setBackgroundMediaFromUrl(url: string) { if (!url) { - new Notice(t("notices.backgroundUrlLoadError")); + new Notice(t('notices.backgroundUrlLoadError')); return; } try { @@ -1597,30 +1481,29 @@ export default class ColorMaster extends Plugin { const arrayBuffer = response.arrayBuffer; // Extract filename, remove query params - let fileName = url.split("/").pop(); + let fileName = url.split('/').pop(); // Clean up potential query parameters if (fileName) { - fileName = fileName.split("?")[0]; // Get image data as ArrayBuffer + fileName = fileName.split('?')[0]; // Get image data as ArrayBuffer } // Generate fallback name if needed - if (!fileName || fileName.indexOf(".") === -1 || fileName.length > 50) { - const extension = - response.headers["content-type"]?.split("/")[1] || "png"; + if (!fileName || fileName.indexOf('.') === -1 || fileName.length > 50) { + const extension = response.headers['content-type']?.split('/')[1] || 'png'; fileName = `image-${Date.now()}.${extension}`; } // Pass to main function - await this.setBackgroundMedia(arrayBuffer, fileName, "prompt"); + await this.setBackgroundMedia(arrayBuffer, fileName, 'prompt'); } catch (error) { - new Notice(t("notices.backgroundUrlLoadError")); - console.error("Color Master: Error fetching image from URL:", error); + new Notice(t('notices.backgroundUrlLoadError')); + console.error('Color Master: Error fetching image from URL:', error); } } // Sets an existing media file as the active background. - async selectBackgroundMedia(newPath: string, mediaType: "image" | "video") { + async selectBackgroundMedia(newPath: string, mediaType: 'image' | 'video') { const activeProfile = this.settings.profiles?.[this.settings.activeProfile]; if (!activeProfile) return; @@ -1632,45 +1515,38 @@ export default class ColorMaster extends Plugin { // Save settings (this will trigger applyBackgroundMedia to show it) await this.saveSettings(); - new Notice(t("notices.bgSet")); + new Notice(t('notices.bgSet')); } // Renames a background file and updates ALL profiles using it. - async renameBackgroundMedia( - oldPath: string, - newFullName: string, - ): Promise { + async renameBackgroundMedia(oldPath: string, newFullName: string): Promise { const adapter = this.app.vault.adapter; // Validate name - if ( - !newFullName || - newFullName.includes("/") || - newFullName.includes("\\") - ) { - new Notice(t("notices.invalidFilename")); + if (!newFullName || newFullName.includes('/') || newFullName.includes('\\')) { + new Notice(t('notices.invalidFilename')); return false; } - const pathParts = oldPath.split("/"); - const originalFileName = pathParts.pop() || ""; - const folderPath = pathParts.join("/"); + const pathParts = oldPath.split('/'); + const originalFileName = pathParts.pop() || ''; + const folderPath = pathParts.join('/'); const newPath = `${folderPath}/${newFullName}`; const oldExtMatch = originalFileName.match(/\.([a-zA-Z0-9]+)$/); - const oldExt = oldExtMatch ? oldExtMatch[0] : ""; + const oldExt = oldExtMatch ? oldExtMatch[0] : ''; if (oldExt && !newFullName.toLowerCase().endsWith(oldExt.toLowerCase())) { console.warn( `Color Master: Rename blocked. Attempted to change extension from "${oldExt}" in "${newFullName}".`, ); - new Notice(t("notices.invalidFilename") + " (Extension mismatch)"); + new Notice(t('notices.invalidFilename') + ' (Extension mismatch)'); return false; } // Prevent overwriting *different* existing file if (await adapter.exists(newPath)) { if (oldPath.toLowerCase() !== newPath.toLowerCase()) { - new Notice(t("notices.filenameExists", newFullName)); + new Notice(t('notices.filenameExists', newFullName)); return false; } } @@ -1693,11 +1569,11 @@ export default class ColorMaster extends Plugin { await this.saveSettings(); } - new Notice(t("notices.renameSuccess", newFullName)); + new Notice(t('notices.renameSuccess', newFullName)); return newPath; } catch (error) { - new Notice(t("notices.renameError")); - console.error("Color Master: Error renaming background image:", error); + new Notice(t('notices.renameError')); + console.error('Color Master: Error renaming background image:', error); return false; } } @@ -1707,7 +1583,7 @@ export default class ColorMaster extends Plugin { * Essential for determining base theme defaults. */ async captureCurrentComputedVars(): Promise> { - console.debug("Color Master: Capturing current computed styles..."); + console.debug('Color Master: Capturing current computed styles...'); // 1. Flush plugin overrides and await repaint to expose base theme this.clearStyles(); @@ -1725,7 +1601,7 @@ export default class ColorMaster extends Plugin { for (const varName of allVarKeys) { const computedValue = bodyStyles.getPropertyValue(varName).trim(); - const defaultValue = flatDefaultVars[varName] || ""; + const defaultValue = flatDefaultVars[varName] || ''; if (computedValue) { if (isColorRegex.test(defaultValue)) { @@ -1733,10 +1609,7 @@ export default class ColorMaster extends Plugin { try { capturedVars[varName] = convertColorToHex(computedValue); } catch (e) { - console.warn( - `Failed to convert color ${varName}: ${computedValue}`, - e, - ); + console.warn(`Failed to convert color ${varName}: ${computedValue}`, e); capturedVars[varName] = computedValue; } } else { @@ -1745,9 +1618,7 @@ export default class ColorMaster extends Plugin { } } - console.debug( - `Color Master: Captured ${Object.keys(capturedVars).length} variables.`, - ); + console.debug(`Color Master: Captured ${Object.keys(capturedVars).length} variables.`); // 2. Restore plugin state void this.applyStyles(); @@ -1761,20 +1632,13 @@ export default class ColorMaster extends Plugin { * Prevents UI flickering by caching defaults until theme mode (dark/light) changes. */ async getThemeDefaults(): Promise> { - const currentThemeMode = document.body.classList.contains("theme-dark") - ? "dark" - : "light"; + const currentThemeMode = document.body.classList.contains('theme-dark') ? 'dark' : 'light'; - if ( - this.cachedThemeDefaults && - this.lastCachedThemeMode === currentThemeMode - ) { + if (this.cachedThemeDefaults && this.lastCachedThemeMode === currentThemeMode) { return this.cachedThemeDefaults; } - console.debug( - "Color Master: Cache miss or theme change. Refreshing defaults...", - ); + console.debug('Color Master: Cache miss or theme change. Refreshing defaults...'); const newDefaults = await this.captureCurrentComputedVars(); diff --git a/src/styles/_components.scss b/src/styles/_components.scss index 3063de3..fa282fb 100644 --- a/src/styles/_components.scss +++ b/src/styles/_components.scss @@ -94,20 +94,13 @@ background: none; &::before { - content: ""; + content: ''; position: absolute; top: 0; left: 0; width: 400%; height: 100%; - background: linear-gradient( - 115deg, - #33ff00, - #ffcc00, - #8000ff, - #00b7ff, - #00ff66 - ); + background: linear-gradient(115deg, #33ff00, #ffcc00, #8000ff, #00b7ff, #00ff66); background-size: 25% 100%; animation: cm-border-animation 2s linear infinite; } @@ -129,20 +122,13 @@ width: 100%; &::before { - content: ""; + content: ''; position: absolute; top: 0; left: 0; width: 400%; height: 100%; - background: linear-gradient( - 115deg, - #33ff00, - #ffcc00, - #8000ff, - #00b7ff, - #00ff66 - ); + background: linear-gradient(115deg, #33ff00, #ffcc00, #8000ff, #00b7ff, #00ff66); background-size: 25% 100%; animation: cm-border-animation 1.5s linear infinite; animation-play-state: running; @@ -411,7 +397,7 @@ } } -.color-master-settings-tab[dir="ltr"] .cm-flyout-menu-buttons { +.color-master-settings-tab[dir='ltr'] .cm-flyout-menu-buttons { transform: translateX(15px); &.is-open { opacity: 1; @@ -420,7 +406,7 @@ } } -.color-master-settings-tab[dir="rtl"] .cm-flyout-menu-buttons { +.color-master-settings-tab[dir='rtl'] .cm-flyout-menu-buttons { transform: translateX(-15px); &.is-open { opacity: 1; diff --git a/src/styles/_modals.scss b/src/styles/_modals.scss index e6a66af..7600042 100644 --- a/src/styles/_modals.scss +++ b/src/styles/_modals.scss @@ -131,26 +131,10 @@ outline: none; /* Active Border Pattern */ background-image: - linear-gradient( - to right, - var(--interactive-accent) 50%, - transparent 50% - ), - linear-gradient( - to bottom, - var(--interactive-accent) 50%, - transparent 50% - ), - linear-gradient( - to right, - var(--interactive-accent) 50%, - transparent 50% - ), - linear-gradient( - to bottom, - var(--interactive-accent) 50%, - transparent 50% - ); + linear-gradient(to right, var(--interactive-accent) 50%, transparent 50%), + linear-gradient(to bottom, var(--interactive-accent) 50%, transparent 50%), + linear-gradient(to right, var(--interactive-accent) 50%, transparent 50%), + linear-gradient(to bottom, var(--interactive-accent) 50%, transparent 50%); } &:focus, @@ -484,7 +468,7 @@ } /* Transparent swatch fix */ - input[type="color"].is-transparent { + input[type='color'].is-transparent { background-color: transparent; &::-webkit-color-swatch { background-image: @@ -635,7 +619,7 @@ } /* RTL Override */ - [dir="rtl"] & { + [dir='rtl'] & { .setting-item-control { order: 1; justify-content: flex-start; @@ -809,7 +793,7 @@ .setting-item-control { flex: 1 1 50%; padding-left: 10px; - input[type="text"], + input[type='text'], textarea { width: 100%; } diff --git a/src/styles/_settings.scss b/src/styles/_settings.scss index 1070a96..039831b 100644 --- a/src/styles/_settings.scss +++ b/src/styles/_settings.scss @@ -6,7 +6,7 @@ .color-master-settings-tab { /* --- LTR/RTL Localization Overrides --- */ - &[dir="ltr"] { + &[dir='ltr'] { direction: ltr !important; .setting-item { @@ -52,7 +52,7 @@ } } - &[dir="rtl"] { + &[dir='rtl'] { .cm-search-input-icon { left: auto; right: 10px; @@ -370,7 +370,7 @@ } /* Color Swatch Styling */ - .setting-item-control input[type="color"] { + .setting-item-control input[type='color'] { appearance: none; -webkit-appearance: none; width: 28px; @@ -553,7 +553,7 @@ } } - &[dir="rtl"] { + &[dir='rtl'] { .setting-item-control select, .cm-search-small { padding-right: 7px !important; @@ -567,7 +567,7 @@ } /* Range Slider Styling */ - input[type="range"] { + input[type='range'] { -webkit-appearance: none; appearance: none; background: transparent; diff --git a/src/styles/main.scss b/src/styles/main.scss index 2d4f7ab..e691568 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -1,4 +1,4 @@ -@use "base"; -@use "components"; -@use "modals"; -@use "settings"; +@use 'base'; +@use 'components'; +@use 'modals'; +@use 'settings'; diff --git a/src/types.ts b/src/types.ts index 5ac7690..6ea0b2e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -export type CustomVarType = "color" | "size" | "text" | "number"; +export type CustomVarType = 'color' | 'size' | 'text' | 'number'; export interface Snippet { id: string; @@ -18,7 +18,7 @@ export interface NoticeRule { export interface Profile { vars: { [key: string]: string }; - themeType: "auto" | "dark" | "light"; + themeType: 'auto' | 'dark' | 'light'; snippets: Snippet[]; isCssProfile?: boolean; customCss?: string; @@ -35,7 +35,7 @@ export interface Profile { }; }; backgroundPath?: string; - backgroundType?: "image" | "video"; + backgroundType?: 'image' | 'video'; videoOpacity?: number; videoMuted?: boolean; backgroundEnabled?: boolean; diff --git a/src/ui/components/color-pickers.ts b/src/ui/components/color-pickers.ts index f99b927..9fe5ba9 100644 --- a/src/ui/components/color-pickers.ts +++ b/src/ui/components/color-pickers.ts @@ -1,14 +1,14 @@ -import { Notice, Setting } from "obsidian"; -import { DEFAULT_VARS, TEXT_TO_BG_MAP } from "../../constants"; -import { t } from "../../i18n/strings"; +import { Notice, Setting } from 'obsidian'; +import { DEFAULT_VARS, TEXT_TO_BG_MAP } from '../../constants'; +import { t } from '../../i18n/strings'; import { flattenVars, getAccessibilityRating, getContrastRatio, isIconizeEnabled, -} from "../../utils"; -import { IconizeSettingsModal, NoticeRulesModal } from "../modals"; -import type { ColorMasterSettingTab } from "../settingsTab"; +} from '../../utils'; +import { IconizeSettingsModal, NoticeRulesModal } from '../modals'; +import type { ColorMasterSettingTab } from '../settingsTab'; export function drawColorPickers( containerEl: HTMLElement, @@ -23,90 +23,79 @@ export function drawColorPickers( let categoryKey: string; const lowerCategory = category.toLowerCase(); - if (lowerCategory === "interactive elements") { - categoryKey = "interactive"; - } else if (lowerCategory === "ui elements") { - categoryKey = "ui"; - } else if (lowerCategory === "graph view") { - categoryKey = "graph"; - } else if (lowerCategory === "plugin integrations") { - categoryKey = "pluginintegrations"; + if (lowerCategory === 'interactive elements') { + categoryKey = 'interactive'; + } else if (lowerCategory === 'ui elements') { + categoryKey = 'ui'; + } else if (lowerCategory === 'graph view') { + categoryKey = 'graph'; + } else if (lowerCategory === 'plugin integrations') { + categoryKey = 'pluginintegrations'; } else { - categoryKey = lowerCategory.replace(/ /g, ""); + categoryKey = lowerCategory.replace(/ /g, ''); } const headingText = t(`categories.${categoryKey}`) || category; - const categoryContainerClasses = ["cm-category-container"]; + const categoryContainerClasses = ['cm-category-container']; const categoryContainer = containerEl.createDiv({ - cls: categoryContainerClasses.join(" "), + cls: categoryContainerClasses.join(' '), }); categoryContainer.dataset.category = category; - categoryContainer.createEl("h3", { text: headingText }); + categoryContainer.createEl('h3', { text: headingText }); for (const [varName, originalDefaultValue] of Object.entries(vars)) { - const defaultValue = themeDefaults[varName] || ""; + const defaultValue = themeDefaults[varName] || ''; - const description = t(`colors.descriptions.${varName}`) || ""; + const description = t(`colors.descriptions.${varName}`) || ''; const setting = new Setting(containerEl) .setName(t(`colors.names.${varName}`) || varName) .setDesc(description); - if ( - categoryKey === "pluginintegrations" && - varName === "--iconize-icon-color" - ) { + if (categoryKey === 'pluginintegrations' && varName === '--iconize-icon-color') { if (!isIconizeEnabled(plugin.app)) { const badgeEl = setting.nameEl.createSpan({ - text: t("options.badgeNotInstalled"), - cls: "cm-not-installed-badge", + text: t('options.badgeNotInstalled'), + cls: 'cm-not-installed-badge', }); - badgeEl.setAttr("aria-label", t("tooltips.iconizeNotInstalled")); + badgeEl.setAttr('aria-label', t('tooltips.iconizeNotInstalled')); } } - if (varName === "--iconize-icon-color") { + if (varName === '--iconize-icon-color') { setting.addExtraButton((button) => { button - .setIcon("settings") - .setTooltip(t("tooltips.iconizeSettings")) + .setIcon('settings') + .setTooltip(t('tooltips.iconizeSettings')) .onClick(() => { new IconizeSettingsModal(settingTab.app, plugin).open(); }); }); } - if (category === "Notices") { + if (category === 'Notices') { setting.addExtraButton((button) => { - button.setIcon("settings").onClick(() => { - const ruleType = varName.includes("-text-") ? "text" : "background"; - new NoticeRulesModal( - settingTab.app, - plugin, - settingTab, - ruleType, - ).open(); + button.setIcon('settings').onClick(() => { + const ruleType = varName.includes('-text-') ? 'text' : 'background'; + new NoticeRulesModal(settingTab.app, plugin, settingTab, ruleType).open(); }); }); } - setting.settingEl.classList.add("cm-var-row"); + setting.settingEl.classList.add('cm-var-row'); setting.settingEl.dataset.var = varName; setting.settingEl.dataset.category = category; - setting.nameEl.classList.add("cm-var-name"); + setting.nameEl.classList.add('cm-var-name'); - const bgVarForTextColor = - TEXT_TO_BG_MAP[varName as keyof typeof TEXT_TO_BG_MAP]; + const bgVarForTextColor = TEXT_TO_BG_MAP[varName as keyof typeof TEXT_TO_BG_MAP]; if (bgVarForTextColor) { let textColor = activeProfileVars[varName] || defaultValue; let bgColor = - activeProfileVars[bgVarForTextColor] || - themeDefaults[bgVarForTextColor] || - "#ffffff"; + activeProfileVars[bgVarForTextColor] || themeDefaults[bgVarForTextColor] || '#ffffff'; - if (varName === "--text-highlight-bg") { + if (varName === '--text-highlight-bg') { [textColor, bgColor] = [bgColor, textColor]; } @@ -120,31 +109,24 @@ export function drawColorPickers( checkerEl.setText(`${rating.text} (${rating.score})`); } - const colorPicker = setting.controlEl.createEl("input", { - type: "color", + const colorPicker = setting.controlEl.createEl('input', { + type: 'color', }); - const textInput = setting.controlEl.createEl("input", { - type: "text", - cls: "color-master-text-input", + const textInput = setting.controlEl.createEl('input', { + type: 'text', + cls: 'color-master-text-input', }); - const isModified = Object.prototype.hasOwnProperty.call( - activeProfileVars, - varName, - ); + const isModified = Object.prototype.hasOwnProperty.call(activeProfileVars, varName); - const initialValue = isModified - ? activeProfileVars[varName] - : defaultValue; + const initialValue = isModified ? activeProfileVars[varName] : defaultValue; - setting.settingEl.classList.add( - isModified ? "is-modified" : "is-pristine", - ); + setting.settingEl.classList.add(isModified ? 'is-modified' : 'is-pristine'); textInput.value = initialValue; settingTab.updateColorPickerAppearance(textInput, colorPicker); - colorPicker.addEventListener("input", (e) => { + colorPicker.addEventListener('input', (e) => { const newColor = (e.target as HTMLInputElement).value; textInput.value = newColor; if (plugin.settings.colorUpdateFPS > 0) { @@ -159,10 +141,7 @@ export function drawColorPickers( // 2. Get profile and old color (for history) const profile = plugin.settings.profiles[plugin.settings.activeProfile]; - const oldColor = Object.prototype.hasOwnProperty.call( - activeProfileVars, - varName, - ) + const oldColor = Object.prototype.hasOwnProperty.call(activeProfileVars, varName) ? activeProfileVars[varName] : defaultValue; @@ -175,24 +154,20 @@ export function drawColorPickers( } // 4. Decide UI state (Pristine/Modified) and update data - if ( - newColor.trim() === "" || - newColor.toLowerCase() === defaultValue.toLowerCase() - ) { - setting.settingEl.classList.remove("is-modified"); - setting.settingEl.classList.add("is-pristine"); + if (newColor.trim() === '' || newColor.toLowerCase() === defaultValue.toLowerCase()) { + setting.settingEl.classList.remove('is-modified'); + setting.settingEl.classList.add('is-pristine'); delete activeProfileVars[varName]; } else { // If it's a new and custom color - setting.settingEl.classList.remove("is-pristine"); - setting.settingEl.classList.add("is-modified"); + setting.settingEl.classList.remove('is-pristine'); + setting.settingEl.classList.add('is-modified'); activeProfileVars[varName] = newColor; } // 5. Apply changes and save const valueToApply = - newColor.trim() === "" || - newColor.toLowerCase() === defaultValue.toLowerCase() - ? "" + newColor.trim() === '' || newColor.toLowerCase() === defaultValue.toLowerCase() + ? '' : newColor; if (plugin.settings.colorUpdateFPS === 0) { @@ -205,23 +180,23 @@ export function drawColorPickers( plugin.pendingVarUpdates[varName] = valueToApply; void plugin.saveSettings(); settingTab.updateAccessibilityCheckers(); - setTimeout(() => settingTab.app.workspace.trigger("css-change"), 50); + setTimeout(() => settingTab.app.workspace.trigger('css-change'), 50); }; - colorPicker.addEventListener("change", (e) => { + colorPicker.addEventListener('change', (e) => { handleFinalChange((e.target as HTMLInputElement).value); }); - textInput.addEventListener("change", (e) => { + textInput.addEventListener('change', (e) => { handleFinalChange((e.target as HTMLInputElement).value); }); setting.addExtraButton((button) => { button - .setIcon("eraser") - .setTooltip(t("tooltips.setTransparent")) + .setIcon('eraser') + .setTooltip(t('tooltips.setTransparent')) .onClick(() => { - const newColor = "transparent"; + const newColor = 'transparent'; textInput.value = newColor; handleFinalChange(newColor); settingTab.updateColorPickerAppearance(textInput, colorPicker); @@ -230,15 +205,13 @@ export function drawColorPickers( setting.addExtraButton((button) => { button - .setIcon("reset") - .setTooltip(t("tooltips.undoChange")) + .setIcon('reset') + .setTooltip(t('tooltips.undoChange')) .onClick(async () => { - const profile = - plugin.settings.profiles[plugin.settings.activeProfile]; + const profile = plugin.settings.profiles[plugin.settings.activeProfile]; const history = profile.history?.[varName]; - const themeDefaultValue = - themeDefaults[varName] || originalDefaultValue || "#ffffff"; + const themeDefaultValue = themeDefaults[varName] || originalDefaultValue || '#ffffff'; if (history && history.length > 0) { const restoredColor = history.shift(); @@ -248,20 +221,20 @@ export function drawColorPickers( textInput.value = safeRestoredColor; settingTab.updateColorPickerAppearance(textInput, colorPicker); - if (safeRestoredColor.trim() === "") { - setting.settingEl.classList.remove("is-modified"); - setting.settingEl.classList.add("is-pristine"); + if (safeRestoredColor.trim() === '') { + setting.settingEl.classList.remove('is-modified'); + setting.settingEl.classList.add('is-pristine'); delete activeProfileVars[varName]; } else { - setting.settingEl.classList.remove("is-pristine"); - setting.settingEl.classList.add("is-modified"); + setting.settingEl.classList.remove('is-pristine'); + setting.settingEl.classList.add('is-modified'); } await plugin.saveSettings(); settingTab.updateAccessibilityCheckers(); - new Notice(t("notices.colorRestored", safeRestoredColor)); + new Notice(t('notices.colorRestored', safeRestoredColor)); } else { - new Notice(t("notices.noColorHistory")); + new Notice(t('notices.noColorHistory')); } }); }); @@ -279,49 +252,43 @@ export function drawColorPickers( if (Object.keys(customVars).length > 0) { const customVarsContainer = containerEl.createDiv({ - cls: "cm-category-container", + cls: 'cm-category-container', }); - customVarsContainer.dataset.category = "Custom"; + customVarsContainer.dataset.category = 'Custom'; - customVarsContainer.createEl("h3", { - text: t("categories.custom"), - cls: "cm-section-heading", + customVarsContainer.createEl('h3', { + text: t('categories.custom'), + cls: 'cm-section-heading', }); - const activeProfile = - plugin.settings.profiles[plugin.settings.activeProfile]; + const activeProfile = plugin.settings.profiles[plugin.settings.activeProfile]; for (const [varName, varValue] of Object.entries(customVars)) { // Get metadata, falling back to defaults if not set const meta = activeProfile.customVarMetadata?.[varName]; - const varType = meta?.type || "color"; + const varType = meta?.type || 'color'; const displayName = meta?.name || varName; - const displayDesc = meta?.desc || t("categories.customDesc"); + const displayDesc = meta?.desc || t('categories.customDesc'); - const isModified = varValue !== ""; + const isModified = varValue !== ''; - const setting = new Setting(customVarsContainer) - .setName(displayName) - .setDesc(displayDesc); + const setting = new Setting(customVarsContainer).setName(displayName).setDesc(displayDesc); - setting.settingEl.classList.add( - "cm-var-row", - isModified ? "is-modified" : "is-pristine", - ); + setting.settingEl.classList.add('cm-var-row', isModified ? 'is-modified' : 'is-pristine'); setting.settingEl.dataset.var = varName; - setting.settingEl.dataset.category = "Custom"; - setting.nameEl.classList.add("cm-var-name"); + setting.settingEl.dataset.category = 'Custom'; + setting.nameEl.classList.add('cm-var-name'); const handleCustomVarChange = (newValue: string) => { activeProfile.vars[varName] = newValue; - if (newValue === "") { - setting.settingEl.classList.remove("is-modified"); - setting.settingEl.classList.add("is-pristine"); + if (newValue === '') { + setting.settingEl.classList.remove('is-modified'); + setting.settingEl.classList.add('is-pristine'); } else { - setting.settingEl.classList.remove("is-pristine"); - setting.settingEl.classList.add("is-modified"); + setting.settingEl.classList.remove('is-pristine'); + setting.settingEl.classList.add('is-modified'); } if (plugin.settings.colorUpdateFPS > 0) { @@ -340,19 +307,19 @@ export function drawColorPickers( }; switch (varType) { - case "color": { - const colorPicker = setting.controlEl.createEl("input", { - type: "color", + case 'color': { + const colorPicker = setting.controlEl.createEl('input', { + type: 'color', }); - const textInput = setting.controlEl.createEl("input", { - type: "text", - cls: "color-master-text-input", + const textInput = setting.controlEl.createEl('input', { + type: 'text', + cls: 'color-master-text-input', }); textInput.value = varValue; settingTab.updateColorPickerAppearance(textInput, colorPicker); - colorPicker.addEventListener("input", (e) => { + colorPicker.addEventListener('input', (e) => { const newColor = (e.target as HTMLInputElement).value; textInput.value = newColor; if (plugin.settings.colorUpdateFPS > 0) { @@ -360,12 +327,12 @@ export function drawColorPickers( } }); - colorPicker.addEventListener("change", (e) => { + colorPicker.addEventListener('change', (e) => { handleCustomVarChange((e.target as HTMLInputElement).value); settingTab.updateColorPickerAppearance(textInput, colorPicker); }); - textInput.addEventListener("change", (e) => { + textInput.addEventListener('change', (e) => { const newColor = (e.target as HTMLInputElement).value; handleCustomVarChange(newColor); settingTab.updateColorPickerAppearance(textInput, colorPicker); @@ -373,10 +340,10 @@ export function drawColorPickers( setting.addExtraButton((button) => { button - .setIcon("eraser") - .setTooltip(t("tooltips.setTransparent")) + .setIcon('eraser') + .setTooltip(t('tooltips.setTransparent')) .onClick(() => { - const newColor = "transparent"; + const newColor = 'transparent'; textInput.value = newColor; handleCustomVarChange(newColor); settingTab.updateColorPickerAppearance(textInput, colorPicker); @@ -385,83 +352,80 @@ export function drawColorPickers( break; } - case "size": { - const sizeMatch = varValue.match( - /(-?\d*\.?\d+)\s*(px|em|rem|%|vw|vh|auto)?/, - ); + case 'size': { + const sizeMatch = varValue.match(/(-?\d*\.?\d+)\s*(px|em|rem|%|vw|vh|auto)?/); - let numValue = sizeMatch?.[1] || "10"; - let unitValue = sizeMatch?.[2] || "px"; + let numValue = sizeMatch?.[1] || '10'; + let unitValue = sizeMatch?.[2] || 'px'; - if (varValue === "auto") { - numValue = ""; - unitValue = "auto"; + if (varValue === 'auto') { + numValue = ''; + unitValue = 'auto'; } - const sizeInput = setting.controlEl.createEl("input", { - type: "number", - cls: "color-master-text-input", + const sizeInput = setting.controlEl.createEl('input', { + type: 'number', + cls: 'color-master-text-input', }); sizeInput.value = numValue; - sizeInput.setCssProps({ width: "80px" }); + sizeInput.setCssProps({ width: '80px' }); - const unitDropdown = setting.controlEl.createEl("select", { - cls: "dropdown cm-search-small", + const unitDropdown = setting.controlEl.createEl('select', { + cls: 'dropdown cm-search-small', }); - const units = ["px", "em", "rem", "%", "vw", "vh", "auto"]; - if (!units.includes(unitValue)) unitValue = "px"; + const units = ['px', 'em', 'rem', '%', 'vw', 'vh', 'auto']; + if (!units.includes(unitValue)) unitValue = 'px'; units.forEach((unit) => { - unitDropdown.createEl("option", { text: unit, value: unit }); + unitDropdown.createEl('option', { text: unit, value: unit }); }); unitDropdown.value = unitValue; const updateSize = () => { - const newNum = sizeInput.value || "0"; + const newNum = sizeInput.value || '0'; const newUnit = unitDropdown.value; - const newValue = - newUnit === "auto" ? "auto" : `${newNum}${newUnit}`; - sizeInput.disabled = newUnit === "auto"; + const newValue = newUnit === 'auto' ? 'auto' : `${newNum}${newUnit}`; + sizeInput.disabled = newUnit === 'auto'; handleCustomVarChange(newValue); }; - sizeInput.addEventListener("change", updateSize); - unitDropdown.addEventListener("change", updateSize); + sizeInput.addEventListener('change', updateSize); + unitDropdown.addEventListener('change', updateSize); - sizeInput.disabled = unitValue === "auto"; + sizeInput.disabled = unitValue === 'auto'; break; } - case "text": { - setting.controlEl.classList.add("cm-full-width-control"); + case 'text': { + setting.controlEl.classList.add('cm-full-width-control'); - const textInputArea = setting.controlEl.createEl("input", { - type: "text", - cls: "cm-wide-text-input", + const textInputArea = setting.controlEl.createEl('input', { + type: 'text', + cls: 'cm-wide-text-input', }); textInputArea.value = varValue; - textInputArea.placeholder = "Enter text value..."; + textInputArea.placeholder = 'Enter text value...'; - textInputArea.addEventListener("change", (e) => { + textInputArea.addEventListener('change', (e) => { handleCustomVarChange((e.target as HTMLInputElement).value); }); break; } - case "number": { - const numInput = setting.controlEl.createEl("input", { - type: "number", - cls: "color-master-text-input", + case 'number': { + const numInput = setting.controlEl.createEl('input', { + type: 'number', + cls: 'color-master-text-input', }); numInput.value = varValue; - numInput.setCssProps({ width: "100px" }); + numInput.setCssProps({ width: '100px' }); - numInput.addEventListener("change", (e) => { + numInput.addEventListener('change', (e) => { handleCustomVarChange((e.target as HTMLInputElement).value); }); @@ -469,15 +433,15 @@ export function drawColorPickers( } default: { - setting.controlEl.classList.add("cm-full-width-control"); + setting.controlEl.classList.add('cm-full-width-control'); - const defaultInput = setting.controlEl.createEl("input", { - type: "text", - cls: "cm-wide-text-input", + const defaultInput = setting.controlEl.createEl('input', { + type: 'text', + cls: 'cm-wide-text-input', }); defaultInput.value = varValue; - defaultInput.addEventListener("change", (e) => { + defaultInput.addEventListener('change', (e) => { handleCustomVarChange((e.target as HTMLInputElement).value); }); @@ -487,8 +451,8 @@ export function drawColorPickers( setting.addExtraButton((button) => { button - .setIcon("trash") - .setTooltip(t("tooltips.deleteCustomVar")) + .setIcon('trash') + .setTooltip(t('tooltips.deleteCustomVar')) .onClick(async () => { delete activeProfile.vars[varName]; // Remove the property from the DOM diff --git a/src/ui/components/import-export.ts b/src/ui/components/import-export.ts index e01a445..434ed47 100644 --- a/src/ui/components/import-export.ts +++ b/src/ui/components/import-export.ts @@ -1,60 +1,48 @@ -import { t } from "../../i18n/strings"; -import { PasteCssModal, ProfileJsonImportModal } from "../modals"; -import type { ColorMasterSettingTab } from "../settingsTab"; +import { t } from '../../i18n/strings'; +import { PasteCssModal, ProfileJsonImportModal } from '../modals'; +import type { ColorMasterSettingTab } from '../settingsTab'; -export function drawImportExport( - containerEl: HTMLElement, - settingTab: ColorMasterSettingTab, -) { - const actionsEl = containerEl.createDiv("cm-profile-actions"); +export function drawImportExport(containerEl: HTMLElement, settingTab: ColorMasterSettingTab) { + const actionsEl = containerEl.createDiv('cm-profile-actions'); // --- 1. Output Actions (Export / Copy) --- actionsEl - .createEl("button", { - text: t("buttons.exportFile"), - cls: "cm-profile-action-btn", + .createEl('button', { + text: t('buttons.exportFile'), + cls: 'cm-profile-action-btn', }) - .addEventListener("click", () => settingTab._exportProfileToFile()); + .addEventListener('click', () => settingTab._exportProfileToFile()); actionsEl - .createEl("button", { - text: t("buttons.copyJson"), - cls: "cm-profile-action-btn", + .createEl('button', { + text: t('buttons.copyJson'), + cls: 'cm-profile-action-btn', }) - .addEventListener("click", () => { + .addEventListener('click', () => { void settingTab._copyProfileToClipboard().catch((err) => { - console.error("Failed to copy profile to clipboard:", err); + console.error('Failed to copy profile to clipboard:', err); }); }); // Spacer to push input actions to the right - actionsEl.createDiv({ cls: "cm-profile-action-spacer" }); + actionsEl.createDiv({ cls: 'cm-profile-action-spacer' }); // --- 2. Input Actions (Import CSS / JSON) --- - const pasteCssBtn = actionsEl.createEl("button", { - text: t("buttons.importCss"), - cls: "cm-profile-action-btn cm-paste-css-btn", + const pasteCssBtn = actionsEl.createEl('button', { + text: t('buttons.importCss'), + cls: 'cm-profile-action-btn cm-paste-css-btn', }); - pasteCssBtn.addEventListener("click", () => { - new PasteCssModal( - settingTab.app, - settingTab.plugin, - settingTab, - null, - ).open(); + pasteCssBtn.addEventListener('click', () => { + new PasteCssModal(settingTab.app, settingTab.plugin, settingTab, null).open(); }); actionsEl - .createEl("button", { - text: t("buttons.importJson"), - cls: "cm-profile-action-btn mod-cta", + .createEl('button', { + text: t('buttons.importJson'), + cls: 'cm-profile-action-btn mod-cta', }) - .addEventListener("click", () => - new ProfileJsonImportModal( - settingTab.app, - settingTab.plugin, - settingTab, - ).open(), + .addEventListener('click', () => + new ProfileJsonImportModal(settingTab.app, settingTab.plugin, settingTab).open(), ); } diff --git a/src/ui/components/like-plugin-card.ts b/src/ui/components/like-plugin-card.ts index 857cd2d..1da2537 100644 --- a/src/ui/components/like-plugin-card.ts +++ b/src/ui/components/like-plugin-card.ts @@ -1,25 +1,20 @@ -import { DEFAULT_VARS } from "../../constants"; -import { t } from "../../i18n/strings"; -import { flattenVars } from "../../utils"; -import type { ColorMasterSettingTab } from "../settingsTab"; +import { DEFAULT_VARS } from '../../constants'; +import { t } from '../../i18n/strings'; +import { flattenVars } from '../../utils'; +import type { ColorMasterSettingTab } from '../settingsTab'; -function createStatBar( - parentEl: HTMLElement, - label: string, - value: number, - max: number, -) { - const skillBox = parentEl.createDiv("cm-stat-box"); - const header = skillBox.createDiv("cm-stat-header"); - header.createEl("span", { cls: "title", text: label }); - header.createEl("span", { cls: "value", text: String(value) }); - const skillBar = skillBox.createDiv("skill-bar"); +function createStatBar(parentEl: HTMLElement, label: string, value: number, max: number) { + const skillBox = parentEl.createDiv('cm-stat-box'); + const header = skillBox.createDiv('cm-stat-header'); + header.createEl('span', { cls: 'title', text: label }); + header.createEl('span', { cls: 'value', text: String(value) }); + const skillBar = skillBox.createDiv('skill-bar'); const percentage = Math.min(100, Math.round((value / max) * 100)); - const skillPer = skillBar.createEl("span", { - cls: "skill-per cm-skill-gradient", + const skillPer = skillBar.createEl('span', { + cls: 'skill-per cm-skill-gradient', }); skillPer.setCssProps({ - "--skill-percentage": `${percentage}%`, + '--skill-percentage': `${percentage}%`, }); } @@ -31,9 +26,7 @@ function calcSnippetsCount(settingTab: ColorMasterSettingTab): number { const settings = settingTab.plugin.settings; const profiles = settings.profiles || {}; - let totalSnippets = settings.globalSnippets - ? settings.globalSnippets.length - : 0; + let totalSnippets = settings.globalSnippets ? settings.globalSnippets.length : 0; for (const profileName in profiles) { const profile = profiles[profileName]; @@ -47,9 +40,7 @@ function calcSnippetsCount(settingTab: ColorMasterSettingTab): number { function calcVarsCount(settingTab: ColorMasterSettingTab): number { const allVars = new Set(Object.keys(flattenVars(DEFAULT_VARS))); const activeProfile = - settingTab.plugin.settings.profiles[ - settingTab.plugin.settings.activeProfile - ]; + settingTab.plugin.settings.profiles[settingTab.plugin.settings.activeProfile]; if (activeProfile && activeProfile.vars) { Object.keys(activeProfile.vars).forEach((varName) => allVars.add(varName)); } @@ -58,21 +49,18 @@ function calcVarsCount(settingTab: ColorMasterSettingTab): number { function calcPluginIntegrations(): number { try { - if (DEFAULT_VARS && DEFAULT_VARS["Plugin Integrations"]) { - return Object.keys(DEFAULT_VARS["Plugin Integrations"]).length; + if (DEFAULT_VARS && DEFAULT_VARS['Plugin Integrations']) { + return Object.keys(DEFAULT_VARS['Plugin Integrations']).length; } } catch (e) { - console.error("Color Master: Failed to calculate plugin integrations.", e); + console.error('Color Master: Failed to calculate plugin integrations.', e); } return 0; } -export function drawLikePluginCard( - containerEl: HTMLElement, - settingTab: ColorMasterSettingTab, -) { - const likeCardEl = containerEl.createDiv("cm-like-card"); - const bannerContainer = likeCardEl.createDiv("cm-banner-container"); +export function drawLikePluginCard(containerEl: HTMLElement, settingTab: ColorMasterSettingTab) { + const likeCardEl = containerEl.createDiv('cm-like-card'); + const bannerContainer = likeCardEl.createDiv('cm-banner-container'); const bannerSvgString = ` @@ -100,78 +88,64 @@ export function drawLikePluginCard( `; const parser = new DOMParser(); - const svgDoc = parser.parseFromString(bannerSvgString, "image/svg+xml"); + const svgDoc = parser.parseFromString(bannerSvgString, 'image/svg+xml'); const svgElement = svgDoc.documentElement; bannerContainer.appendChild(svgElement); - const contentWrapper = likeCardEl.createDiv("cm-content-wrapper"); + const contentWrapper = likeCardEl.createDiv('cm-content-wrapper'); - const statsContainer = contentWrapper.createDiv("cm-like-stats"); + const statsContainer = contentWrapper.createDiv('cm-like-stats'); const profilesCount = calcProfilesCount(settingTab); const snippetsCount = calcSnippetsCount(settingTab); - const sinceInstalled = - settingTab.plugin.settings.installDate || new Date().toISOString(); + const sinceInstalled = settingTab.plugin.settings.installDate || new Date().toISOString(); const days = Math.max( 1, - Math.floor( - (Date.now() - new Date(sinceInstalled).getTime()) / (1000 * 60 * 60 * 24), - ), + Math.floor((Date.now() - new Date(sinceInstalled).getTime()) / (1000 * 60 * 60 * 24)), ); createStatBar( statsContainer, - t("likeCard.profilesStat", profilesCount, snippetsCount), + t('likeCard.profilesStat', profilesCount, snippetsCount), profilesCount + snippetsCount, 50, ); createStatBar( statsContainer, - t("likeCard.colorsStat"), + t('likeCard.colorsStat'), calcVarsCount(settingTab), calcVarsCount(settingTab), ); - createStatBar( - statsContainer, - t("likeCard.integrationsStat"), - calcPluginIntegrations(), - 5, - ); - createStatBar(statsContainer, t("likeCard.daysStat"), days, 365); + createStatBar(statsContainer, t('likeCard.integrationsStat'), calcPluginIntegrations(), 5); + createStatBar(statsContainer, t('likeCard.daysStat'), days, 365); - const actions = contentWrapper.createDiv("cm-like-actions"); - const starButtonWrapper = actions.createDiv({ cls: "codepen-button" }); - starButtonWrapper.createEl("span", { text: t("likeCard.starButton") }); - starButtonWrapper.addEventListener("click", () => { - window.open( - "https://github.com/YazanAmmar/obsidian-color-master", - "_blank", - ); + const actions = contentWrapper.createDiv('cm-like-actions'); + const starButtonWrapper = actions.createDiv({ cls: 'codepen-button' }); + starButtonWrapper.createEl('span', { text: t('likeCard.starButton') }); + starButtonWrapper.addEventListener('click', () => { + window.open('https://github.com/YazanAmmar/obsidian-color-master', '_blank'); }); - const reportButtonWrapper = actions.createDiv({ cls: "codepen-button" }); - reportButtonWrapper.createEl("span", { text: t("likeCard.issueButton") }); - reportButtonWrapper.addEventListener("click", () => { - window.open( - "https://github.com/YazanAmmar/obsidian-color-master/issues", - "_blank", - ); + const reportButtonWrapper = actions.createDiv({ cls: 'codepen-button' }); + reportButtonWrapper.createEl('span', { text: t('likeCard.issueButton') }); + reportButtonWrapper.addEventListener('click', () => { + window.open('https://github.com/YazanAmmar/obsidian-color-master/issues', '_blank'); }); - const syncPromoContainer = actions.createDiv({ cls: "cm-promo-container" }); + const syncPromoContainer = actions.createDiv({ cls: 'cm-promo-container' }); const syncButtonWrapper = syncPromoContainer.createDiv({ - cls: "codepen-button", + cls: 'codepen-button', }); - syncButtonWrapper.createEl("span", { text: t("likeCard.syncButton") }); - syncButtonWrapper.addEventListener("click", () => { - window.open("https://github.com/YazanAmmar/SyncEveryThing", "_blank"); + syncButtonWrapper.createEl('span', { text: t('likeCard.syncButton') }); + syncButtonWrapper.addEventListener('click', () => { + window.open('https://github.com/YazanAmmar/SyncEveryThing', '_blank'); }); - const myGithubButtonWrapper = actions.createDiv({ cls: "codepen-button" }); - myGithubButtonWrapper.createEl("span", { - text: t("likeCard.telegramButton"), + const myGithubButtonWrapper = actions.createDiv({ cls: 'codepen-button' }); + myGithubButtonWrapper.createEl('span', { + text: t('likeCard.telegramButton'), }); - myGithubButtonWrapper.addEventListener("click", () => { - window.open("https://t.me/ObsidianColorMaster", "_blank"); + myGithubButtonWrapper.addEventListener('click', () => { + window.open('https://t.me/ObsidianColorMaster', '_blank'); }); } diff --git a/src/ui/components/options-section.ts b/src/ui/components/options-section.ts index 75cf6a9..2e0e3a9 100644 --- a/src/ui/components/options-section.ts +++ b/src/ui/components/options-section.ts @@ -1,6 +1,6 @@ -import { Setting, Notice, setIcon } from "obsidian"; -import { t } from "../../i18n/strings"; -import type { ColorMasterSettingTab } from "../settingsTab"; +import { Setting, Notice, setIcon } from 'obsidian'; +import { t } from '../../i18n/strings'; +import type { ColorMasterSettingTab } from '../settingsTab'; import { CustomVariableMetaModal, ConfirmationModal, @@ -8,25 +8,20 @@ import { AddBackgroundModal, ProfileImageBrowserModal, AdvancedResetModal, -} from "../modals"; +} from '../modals'; -export function drawOptionsSection( - containerEl: HTMLElement, - settingTab: ColorMasterSettingTab, -) { +export function drawOptionsSection(containerEl: HTMLElement, settingTab: ColorMasterSettingTab) { const plugin = settingTab.plugin; const activeProfile = plugin.settings.profiles[plugin.settings.activeProfile]; - containerEl.createEl("h3", { text: t("options.heading") }); - containerEl.createEl("hr"); - const advancedSettingsGrid = containerEl.createDiv( - "cm-advanced-settings-grid", - ); + containerEl.createEl('h3', { text: t('options.heading') }); + containerEl.createEl('hr'); + const advancedSettingsGrid = containerEl.createDiv('cm-advanced-settings-grid'); // --- Live Update FPS --- const fpsSetting = new Setting(advancedSettingsGrid) - .setName(t("options.liveUpdateName")) - .setDesc(t("options.liveUpdateDesc")) + .setName(t('options.liveUpdateName')) + .setDesc(t('options.liveUpdateDesc')) .addSlider((slider) => { slider .setLimits(0, 60, 1) @@ -36,90 +31,83 @@ export function drawOptionsSection( plugin.settings.colorUpdateFPS = value; await plugin.saveSettings(); plugin.restartColorUpdateLoop(); - new Notice(t("notices.fpsUpdated", value)); + new Notice(t('notices.fpsUpdated', value)); }); }); fpsSetting.nameEl.appendChild(fpsSetting.controlEl); - fpsSetting.settingEl.classList.add("cm-card-with-header-control"); + fpsSetting.settingEl.classList.add('cm-card-with-header-control'); // --- Add Custom Variable --- const customVarSetting = new Setting(advancedSettingsGrid) - .setName(t("options.addCustomVarName")) - .setDesc(t("options.addCustomVarDesc")) + .setName(t('options.addCustomVarName')) + .setDesc(t('options.addCustomVarDesc')) .addButton((button) => { button - .setButtonText(t("options.addNewVarButton")) - .setClass("cm-add-variable-button") + .setButtonText(t('options.addNewVarButton')) + .setClass('cm-add-variable-button') .onClick(() => { - new CustomVariableMetaModal( - settingTab.app, - plugin, - settingTab, - (result) => { - void (async () => { - if (!activeProfile.customVarMetadata) { - activeProfile.customVarMetadata = {}; - } + new CustomVariableMetaModal(settingTab.app, plugin, settingTab, (result) => { + void (async () => { + if (!activeProfile.customVarMetadata) { + activeProfile.customVarMetadata = {}; + } - activeProfile.vars[result.varName] = result.varValue; - activeProfile.customVarMetadata[result.varName] = { - name: result.displayName, - desc: result.description, - type: result.varType, - }; + activeProfile.vars[result.varName] = result.varValue; + activeProfile.customVarMetadata[result.varName] = { + name: result.displayName, + desc: result.description, + type: result.varType, + }; - await plugin.saveSettings(); - new Notice(t("notices.varAdded", result.varName)); - settingTab.display(); - })().catch((err) => { - console.error("Failed to add custom variable:", err); - }); - }, - ).open(); + await plugin.saveSettings(); + new Notice(t('notices.varAdded', result.varName)); + settingTab.display(); + })().catch((err) => { + console.error('Failed to add custom variable:', err); + }); + }).open(); }); }); customVarSetting.nameEl.appendChild(customVarSetting.controlEl); - customVarSetting.settingEl.classList.add("cm-card-with-header-control"); + customVarSetting.settingEl.classList.add('cm-card-with-header-control'); // --- Reset Plugin Settings --- const resetSetting = new Setting(advancedSettingsGrid) - .setName(t("options.resetPluginName")) - .setDesc(t("options.resetPluginDesc")) + .setName(t('options.resetPluginName')) + .setDesc(t('options.resetPluginDesc')) .addButton((button) => { button - .setButtonText(t("options.resetPluginButton")) + .setButtonText(t('options.resetPluginButton')) .setWarning() .onClick(() => { new AdvancedResetModal(settingTab.app, plugin).open(); }); - setIcon(button.buttonEl, "database-backup"); - button.buttonEl.classList.add("cm-reset-plugin-icon-button"); + setIcon(button.buttonEl, 'database-backup'); + button.buttonEl.classList.add('cm-reset-plugin-icon-button'); }); resetSetting.nameEl.appendChild(resetSetting.controlEl); - resetSetting.settingEl.classList.add("cm-card-with-header-control"); - const resetButtonText = resetSetting.controlEl.querySelector( - ".setting-editor-button", - ); + resetSetting.settingEl.classList.add('cm-card-with-header-control'); + const resetButtonText = resetSetting.controlEl.querySelector('.setting-editor-button'); if (resetButtonText) { - resetButtonText.textContent = ""; + resetButtonText.textContent = ''; } // --- Set Background Image --- const backgroundSetting = new Setting(advancedSettingsGrid) - .setName(t("options.backgroundName")) - .setDesc(t("options.backgroundDesc")); + .setName(t('options.backgroundName')) + .setDesc(t('options.backgroundDesc')); backgroundSetting.nameEl.appendChild(backgroundSetting.controlEl); - backgroundSetting.settingEl.classList.add("cm-card-with-header-control"); + backgroundSetting.settingEl.classList.add('cm-card-with-header-control'); backgroundSetting .addButton((button) => { // Add/Choose Button button - .setIcon("plus") - .setTooltip(t("tooltips.addBg")) - .setClass("cm-control-icon-button") + .setIcon('plus') + .setTooltip(t('tooltips.addBg')) + .setClass('cm-control-icon-button') .onClick(() => { // Pass the settingTab instance to the modal new AddBackgroundModal(settingTab.app, plugin, settingTab).open(); @@ -128,73 +116,64 @@ export function drawOptionsSection( .addButton((button) => { // Browse Button button - .setIcon("package-search") - .setTooltip(t("tooltips.browseBg")) - .setClass("cm-control-icon-button") + .setIcon('package-search') + .setTooltip(t('tooltips.browseBg')) + .setClass('cm-control-icon-button') .onClick(() => { // Directly open the browser modal - new ProfileImageBrowserModal( - settingTab.app, - plugin, - settingTab, - () => {}, - ).open(); + new ProfileImageBrowserModal(settingTab.app, plugin, settingTab, () => {}).open(); }); }) .addButton((button) => { // Remove Button button - .setIcon("trash") - .setTooltip(t("tooltips.removeBg")) - .setClass("cm-control-icon-button") + .setIcon('trash') + .setTooltip(t('tooltips.removeBg')) + .setClass('cm-control-icon-button') .onClick(() => { - const profile = - plugin.settings.profiles[plugin.settings.activeProfile]; + const profile = plugin.settings.profiles[plugin.settings.activeProfile]; const imagePathToDelete = profile?.backgroundPath; if (!imagePathToDelete) { - new Notice(t("notices.noBgToRemove")); + new Notice(t('notices.noBgToRemove')); return; } const profilesUsingImage: string[] = []; for (const profileName in plugin.settings.profiles) { - if ( - plugin.settings.profiles[profileName].backgroundPath === - imagePathToDelete - ) { + if (plugin.settings.profiles[profileName].backgroundPath === imagePathToDelete) { profilesUsingImage.push(profileName); } } const messageFragment = new DocumentFragment(); - messageFragment.append(t("modals.confirmation.deleteGlobalBgDesc")); + messageFragment.append(t('modals.confirmation.deleteGlobalBgDesc')); if (profilesUsingImage.length > 0) { - const profileListEl = messageFragment.createEl("ul", { - cls: "cm-profile-list-modal", + const profileListEl = messageFragment.createEl('ul', { + cls: 'cm-profile-list-modal', }); profilesUsingImage.forEach((name) => { - profileListEl.createEl("li").createEl("strong", { text: name }); + profileListEl.createEl('li').createEl('strong', { text: name }); }); } new ConfirmationModal( settingTab.app, plugin, - t("modals.confirmation.deleteGlobalBgTitle"), + t('modals.confirmation.deleteGlobalBgTitle'), messageFragment, () => { void (async () => { await plugin.removeBackgroundMediaByPath(imagePathToDelete); - new Notice(t("notices.bgDeleted")); + new Notice(t('notices.bgDeleted')); })().catch((err) => { - console.error("Failed to delete background image:", err); + console.error('Failed to delete background image:', err); }); }, { - buttonText: t("buttons.deleteAnyway"), - buttonClass: "mod-warning", + buttonText: t('buttons.deleteAnyway'), + buttonClass: 'mod-warning', }, ).open(); }); @@ -202,9 +181,9 @@ export function drawOptionsSection( .addButton((button) => { // Settings Button button - .setIcon("settings") - .setTooltip(t("tooltips.bgSettings")) - .setClass("cm-control-icon-button") + .setIcon('settings') + .setTooltip(t('tooltips.bgSettings')) + .setClass('cm-control-icon-button') .onClick(() => { new BackgroundImageSettingsModal(settingTab.app, plugin).open(); }); diff --git a/src/ui/components/profile-manager.ts b/src/ui/components/profile-manager.ts index 93f0844..f7a62ae 100644 --- a/src/ui/components/profile-manager.ts +++ b/src/ui/components/profile-manager.ts @@ -1,7 +1,7 @@ -import { App, Notice, setIcon, Setting } from "obsidian"; -import { t } from "../../i18n/strings"; -import { ConfirmationModal, NewProfileModal, PasteCssModal } from "../modals"; -import type { ColorMasterSettingTab } from "../settingsTab"; +import { App, Notice, setIcon, Setting } from 'obsidian'; +import { t } from '../../i18n/strings'; +import { ConfirmationModal, NewProfileModal, PasteCssModal } from '../modals'; +import type { ColorMasterSettingTab } from '../settingsTab'; interface AppWithCustomCss extends App { customCss: { @@ -9,29 +9,26 @@ interface AppWithCustomCss extends App { }; } -export function drawProfileManager( - containerEl: HTMLElement, - settingTab: ColorMasterSettingTab, -) { +export function drawProfileManager(containerEl: HTMLElement, settingTab: ColorMasterSettingTab) { const plugin = settingTab.plugin; - containerEl.createEl("h3", { - text: t("profileManager.heading"), - cls: "cm-section-heading", + containerEl.createEl('h3', { + text: t('profileManager.heading'), + cls: 'cm-section-heading', }); const activeProfileName = plugin.settings.activeProfile; const activeProfile = plugin.settings.profiles[activeProfileName]; const isCssProfile = activeProfile?.isCssProfile; const profileSetting = new Setting(containerEl) - .setName(t("profileManager.activeProfile")) - .setDesc(t("profileManager.activeProfileDesc")) - .setClass("cm-profile-manager-controls") + .setName(t('profileManager.activeProfile')) + .setDesc(t('profileManager.activeProfileDesc')) + .setClass('cm-profile-manager-controls') .addDropdown((dropdown) => { for (const profileName in plugin.settings.profiles) { let displayName = profileName; if (plugin.settings.profiles[profileName]?.isCssProfile) { - displayName += " (CSS)"; + displayName += ' (CSS)'; } dropdown.addOption(profileName, displayName); } @@ -46,33 +43,26 @@ export function drawProfileManager( }) .addButton((button) => { - const activeProfile = - plugin.settings.profiles[plugin.settings.activeProfile]; + const activeProfile = plugin.settings.profiles[plugin.settings.activeProfile]; if (!activeProfile) { button.buttonEl.hide(); return; } - button.buttonEl.classList.add("cm-control-button"); + button.buttonEl.classList.add('cm-control-button'); // Helper function to update the button's appearance - const updateButtonAppearance = (themeType: "auto" | "dark" | "light") => { + const updateButtonAppearance = (themeType: 'auto' | 'dark' | 'light') => { switch (themeType) { - case "light": - button - .setIcon("sun") - .setTooltip(t("profileManager.tooltipThemeLight")); + case 'light': + button.setIcon('sun').setTooltip(t('profileManager.tooltipThemeLight')); break; - case "dark": - button - .setIcon("moon") - .setTooltip(t("profileManager.tooltipThemeDark")); + case 'dark': + button.setIcon('moon').setTooltip(t('profileManager.tooltipThemeDark')); break; - case "auto": + case 'auto': default: - button - .setIcon("sun-moon") - .setTooltip(t("profileManager.tooltipThemeAuto")); + button.setIcon('sun-moon').setTooltip(t('profileManager.tooltipThemeAuto')); break; } }; @@ -81,20 +71,20 @@ export function drawProfileManager( updateButtonAppearance(activeProfile.themeType); // Add click logic (Simplified - No Animation) - button.buttonEl.addEventListener("click", () => { + button.buttonEl.addEventListener('click', () => { void (async () => { const currentType = activeProfile.themeType; - let nextType: "auto" | "dark" | "light"; + let nextType: 'auto' | 'dark' | 'light'; - if (currentType === "light") { - nextType = "dark"; - new Notice(t("notices.themeSwitchedDark")); - } else if (currentType === "dark") { - nextType = "auto"; - new Notice(t("notices.themeSwitchedAuto")); + if (currentType === 'light') { + nextType = 'dark'; + new Notice(t('notices.themeSwitchedDark')); + } else if (currentType === 'dark') { + nextType = 'auto'; + new Notice(t('notices.themeSwitchedAuto')); } else { - nextType = "light"; - new Notice(t("notices.themeSwitchedLight")); + nextType = 'light'; + new Notice(t('notices.themeSwitchedLight')); } activeProfile.themeType = nextType; @@ -102,7 +92,7 @@ export function drawProfileManager( await plugin.saveSettings(); })().catch((err) => { - console.error("Failed to switch theme type:", err); + console.error('Failed to switch theme type:', err); }); }); }) @@ -110,78 +100,73 @@ export function drawProfileManager( .addButton((button) => { if (isCssProfile && activeProfile) { button - .setIcon("pencil") - .setTooltip(t("tooltips.editCssProfile")) + .setIcon('pencil') + .setTooltip(t('tooltips.editCssProfile')) .onClick(() => { const profileData = { name: activeProfileName, - css: activeProfile.customCss || "", + css: activeProfile.customCss || '', isProfile: true, }; - new PasteCssModal( - settingTab.app, - plugin, - settingTab, - profileData, - ).open(); + new PasteCssModal(settingTab.app, plugin, settingTab, profileData).open(); }); } else { button.buttonEl.hide(); } - button.buttonEl.classList.add("cm-control-button"); + button.buttonEl.classList.add('cm-control-button'); }) .addButton((button) => { settingTab.pinBtn = button; button - .setIcon("pin") - .setTooltip(t("tooltips.pinSnapshot")) + .setIcon('pin') + .setTooltip(t('tooltips.pinSnapshot')) .onClick(async () => { await plugin.pinProfileSnapshot(plugin.settings.activeProfile); - new Notice(t("notices.profilePinned")); + new Notice(t('notices.profilePinned')); settingTab._updatePinButtons(); plugin.settings.profiles[plugin.settings.activeProfile].vars = {}; plugin.pendingVarUpdates = {}; settingTab.display(); }); - button.buttonEl.classList.add("cm-control-button"); + button.buttonEl.classList.add('cm-control-button'); }) .addButton((button) => { settingTab.resetPinBtn = button; button - .setIcon("history") - .setTooltip(t("tooltips.resetToPinned")) + .setIcon('history') + .setTooltip(t('tooltips.resetToPinned')) .onClick(() => { const name = plugin.settings.activeProfile; new ConfirmationModal( settingTab.app, plugin, - t("modals.confirmation.resetProfileTitle"), - t("modals.confirmation.resetProfileDesc"), + t('modals.confirmation.resetProfileTitle'), + t('modals.confirmation.resetProfileDesc'), () => { void (async () => { await plugin.resetProfileToPinned(name); - new Notice(t("notices.profileReset")); + new Notice(t('notices.profileReset')); settingTab.display(); })().catch((err) => { - console.error("Failed to reset profile:", err); + console.error('Failed to reset profile:', err); }); }, - { buttonText: t("buttons.reset"), buttonClass: "mod-cta" }, + { buttonText: t('buttons.reset'), buttonClass: 'mod-cta' }, ).open(); }); - button.buttonEl.classList.add("cm-control-button"); + button.buttonEl.classList.add('cm-control-button'); }) .addButton((button) => { - button.setButtonText(t("buttons.new")).onClick(() => { + button.setButtonText(t('buttons.new')).onClick(() => { new NewProfileModal(settingTab.app, plugin, (result) => { void (async () => { if (!result || !result.name) return; if (plugin.settings.profiles[result.name]) { - new Notice(t("notices.profileNameExists", result.name)); + new Notice(t('notices.profileNameExists', result.name)); return; } @@ -190,10 +175,9 @@ export function drawProfileManager( const themeType = result.themeType; let finalThemeType = themeType; - if (themeType === "auto") { - const isCurrentlyDark = - document.body.classList.contains("theme-dark"); - finalThemeType = isCurrentlyDark ? "dark" : "light"; + if (themeType === 'auto') { + const isCurrentlyDark = document.body.classList.contains('theme-dark'); + finalThemeType = isCurrentlyDark ? 'dark' : 'light'; } plugin.settings.profiles[result.name] = { @@ -209,57 +193,49 @@ export function drawProfileManager( await plugin.saveSettings(); settingTab.display(); })().catch((err) => { - console.error("Failed to create new profile:", err); + console.error('Failed to create new profile:', err); }); }).open(); }); - button.buttonEl.classList.add("cm-control-button"); + button.buttonEl.classList.add('cm-control-button'); }) .addButton((button) => { - button.setButtonText(t("buttons.delete")).onClick(() => { + button.setButtonText(t('buttons.delete')).onClick(() => { if (Object.keys(plugin.settings.profiles).length <= 1) { - new Notice(t("notices.cannotDeleteLastProfile")); + new Notice(t('notices.cannotDeleteLastProfile')); return; } - const message = t( - "modals.deleteProfile.confirmation", - plugin.settings.activeProfile, - ); + const message = t('modals.deleteProfile.confirmation', plugin.settings.activeProfile); new ConfirmationModal( settingTab.app, plugin, - t("modals.deleteProfile.title"), + t('modals.deleteProfile.title'), message, () => { plugin.removeInjectedCustomCss(); delete plugin.settings.profiles[plugin.settings.activeProfile]; - plugin.settings.activeProfile = Object.keys( - plugin.settings.profiles, - )[0]; + plugin.settings.activeProfile = Object.keys(plugin.settings.profiles)[0]; plugin.pendingVarUpdates = {}; void plugin.saveSettings(); settingTab.display(); - new Notice(t("notices.profileDeleted")); + new Notice(t('notices.profileDeleted')); }, ).open(); }); - button.buttonEl.classList.add("cm-control-button"); + button.buttonEl.classList.add('cm-control-button'); }); const currentTheme = (plugin.app as AppWithCustomCss).customCss.theme; if (currentTheme) { const iconEl = profileSetting.nameEl.createSpan({ - cls: "cm-theme-warning-icon", + cls: 'cm-theme-warning-icon', }); - setIcon(iconEl, "alert-triangle"); - iconEl.setAttr( - "aria-label", - t("settings.themeWarningTooltip", currentTheme), - ); + setIcon(iconEl, 'alert-triangle'); + iconEl.setAttr('aria-label', t('settings.themeWarningTooltip', currentTheme)); } - profileSetting.settingEl.classList.add("cm-active-profile-controls"); + profileSetting.settingEl.classList.add('cm-active-profile-controls'); settingTab._updatePinButtons(); } diff --git a/src/ui/components/snippets-ui.ts b/src/ui/components/snippets-ui.ts index 1137efc..697c128 100644 --- a/src/ui/components/snippets-ui.ts +++ b/src/ui/components/snippets-ui.ts @@ -1,37 +1,28 @@ -import { - ButtonComponent, - Notice, - Setting, - ToggleComponent, - setIcon, -} from "obsidian"; -import { t } from "../../i18n/strings"; -import type { Snippet } from "../../types"; -import { ConfirmationModal, SnippetCssModal } from "../modals"; -import type { ColorMasterSettingTab } from "../settingsTab"; -import Sortable from "sortablejs"; +import { ButtonComponent, Notice, Setting, ToggleComponent, setIcon } from 'obsidian'; +import { t } from '../../i18n/strings'; +import type { Snippet } from '../../types'; +import { ConfirmationModal, SnippetCssModal } from '../modals'; +import type { ColorMasterSettingTab } from '../settingsTab'; +import Sortable from 'sortablejs'; -function initSnippetDrag( - containerEl: HTMLElement, - settingTab: ColorMasterSettingTab, -) { +function initSnippetDrag(containerEl: HTMLElement, settingTab: ColorMasterSettingTab) { const plugin = settingTab.plugin; if (settingTab.snippetSortable) { settingTab.snippetSortable.destroy(); } if (!Sortable) { - console.warn("Color Master: SortableJS library not found."); + console.warn('Color Master: SortableJS library not found.'); return; } const isLocked = (plugin.settings as unknown).snippetsLocked || false; settingTab.snippetSortable = new Sortable(containerEl, { - handle: ".cm-snippet-drag-handle", + handle: '.cm-snippet-drag-handle', animation: 160, - ghostClass: "cm-snippet-ghost", - dragClass: "cm-snippet-dragged", + ghostClass: 'cm-snippet-ghost', + dragClass: 'cm-snippet-dragged', disabled: isLocked, // Disable dragging via Sortable API onEnd: (evt: unknown) => { @@ -44,14 +35,13 @@ function initSnippetDrag( const globalSnippets = plugin.settings.globalSnippets || []; const profileSnippets = - plugin.settings.profiles[plugin.settings.activeProfile].snippets || - []; + plugin.settings.profiles[plugin.settings.activeProfile].snippets || []; const numGlobal = globalSnippets.length; if (oldIndex < numGlobal) { if (newIndex >= numGlobal) { settingTab.display(); - new Notice(t("notices.snippetScopeMove")); + new Notice(t('notices.snippetScopeMove')); return; } const [movedItem] = globalSnippets.splice(oldIndex, 1); @@ -59,7 +49,7 @@ function initSnippetDrag( } else { if (newIndex < numGlobal) { settingTab.display(); - new Notice(t("notices.snippetScopeMove")); + new Notice(t('notices.snippetScopeMove')); return; } @@ -73,59 +63,48 @@ function initSnippetDrag( await plugin.saveSettings(); settingTab.display(); })().catch((err) => { - console.error("Failed to reorder snippets:", err); + console.error('Failed to reorder snippets:', err); }); }, }); } -export function drawCssSnippetsUI( - containerEl: HTMLElement, - settingTab: ColorMasterSettingTab, -) { +export function drawCssSnippetsUI(containerEl: HTMLElement, settingTab: ColorMasterSettingTab) { const plugin = settingTab.plugin; const headerContainer = containerEl.createDiv({ - cls: "cm-snippets-header", + cls: 'cm-snippets-header', }); const isLocked = (plugin.settings as unknown).snippetsLocked || false; - headerContainer.createEl("h3", { text: t("snippets.heading") }); + headerContainer.createEl('h3', { text: t('snippets.heading') }); - const controlsContainer = new Setting(headerContainer).setClass( - "cm-snippets-add-button", - ); + const controlsContainer = new Setting(headerContainer).setClass('cm-snippets-add-button'); // --- Lock Button --- controlsContainer.addExtraButton((btn) => { btn - .setIcon(isLocked ? "lock" : "lock-open") - .setTooltip( - isLocked ? t("tooltips.unlockSnippets") : t("tooltips.lockSnippets"), - ) + .setIcon(isLocked ? 'lock' : 'lock-open') + .setTooltip(isLocked ? t('tooltips.unlockSnippets') : t('tooltips.lockSnippets')) .onClick(async () => { (plugin.settings as unknown).snippetsLocked = !isLocked; await plugin.saveSettings(); settingTab.display(); - new Notice( - isLocked - ? t("notices.snippetsUnlocked") - : t("notices.snippetsLocked"), - ); + new Notice(isLocked ? t('notices.snippetsUnlocked') : t('notices.snippetsLocked')); }); - btn.extraSettingsEl.classList.add("cm-snippet-lock-btn"); + btn.extraSettingsEl.classList.add('cm-snippet-lock-btn'); if (isLocked) { - btn.extraSettingsEl.classList.add("is-locked"); + btn.extraSettingsEl.classList.add('is-locked'); } }); controlsContainer.addButton((button) => { button - .setButtonText(t("snippets.createButton")) + .setButtonText(t('snippets.createButton')) .setCta() .onClick(() => { new SnippetCssModal(settingTab.app, plugin, settingTab, null).open(); @@ -136,118 +115,109 @@ export function drawCssSnippetsUI( if (!activeProfile) return; const globalSnippets = plugin.settings.globalSnippets || []; - const profileSnippets = Array.isArray(activeProfile.snippets) - ? activeProfile.snippets - : []; + const profileSnippets = Array.isArray(activeProfile.snippets) ? activeProfile.snippets : []; const snippetsContainer = containerEl.createDiv({ - cls: "cm-snippets-list-container", + cls: 'cm-snippets-list-container', }); if (globalSnippets.length === 0 && profileSnippets.length === 0) { - const emptyState = snippetsContainer.createDiv("cm-snippets-empty-state"); - emptyState.setText(t("snippets.noSnippetsDesc")); + const emptyState = snippetsContainer.createDiv('cm-snippets-empty-state'); + emptyState.setText(t('snippets.noSnippetsDesc')); return; } - const renderSnippet = ( - snippet: Snippet, - index: number, - isGlobal: boolean, - ) => { + const renderSnippet = (snippet: Snippet, index: number, isGlobal: boolean) => { const snippetEl = snippetsContainer.createDiv({ - cls: "setting-item cm-snippet-item", + cls: 'setting-item cm-snippet-item', }); const dragContainer = snippetEl.createDiv({ - cls: "cm-snippet-drag-container", + cls: 'cm-snippet-drag-container', }); const handle = dragContainer.createDiv({ - cls: "cm-snippet-drag-handle", + cls: 'cm-snippet-drag-handle', }); - setIcon(handle, "grip-vertical"); + setIcon(handle, 'grip-vertical'); if (isLocked) { - handle.classList.add("is-locked"); + handle.classList.add('is-locked'); } - dragContainer.createEl("span", { - cls: "cm-snippet-divider", - text: "|", + dragContainer.createEl('span', { + cls: 'cm-snippet-divider', + text: '|', }); dragContainer.createDiv({ - cls: "cm-snippet-order-number", + cls: 'cm-snippet-order-number', text: `${index + 1}`, }); - const infoEl = snippetEl.createDiv("setting-item-info"); + const infoEl = snippetEl.createDiv('setting-item-info'); const nameEl = infoEl.createDiv({ - cls: "setting-item-name", + cls: 'setting-item-name', }); nameEl.createSpan({ text: snippet.name }); if (isGlobal) { nameEl.createSpan({ - text: t("snippets.global"), - cls: "cm-snippet-global-badge", + text: t('snippets.global'), + cls: 'cm-snippet-global-badge', }); } - const controlEl = snippetEl.createDiv("setting-item-control"); + const controlEl = snippetEl.createDiv('setting-item-control'); - new ToggleComponent(controlEl) - .setValue(snippet.enabled) - .onChange(async (value) => { - snippet.enabled = value; - await plugin.saveSettings(); - }); + new ToggleComponent(controlEl).setValue(snippet.enabled).onChange(async (value) => { + snippet.enabled = value; + await plugin.saveSettings(); + }); new ButtonComponent(controlEl) - .setIcon("pencil") - .setTooltip(t("tooltips.editSnippet")) + .setIcon('pencil') + .setTooltip(t('tooltips.editSnippet')) .onClick(() => { new SnippetCssModal(settingTab.app, plugin, settingTab, snippet).open(); }); new ButtonComponent(controlEl) - .setIcon("copy") - .setTooltip(t("tooltips.copySnippetCss")) + .setIcon('copy') + .setTooltip(t('tooltips.copySnippetCss')) .onClick(async (evt) => { if (!snippet.css) { - new Notice(t("notices.snippetEmpty")); + new Notice(t('notices.snippetEmpty')); return; } await navigator.clipboard.writeText(snippet.css); - new Notice(t("notices.snippetCssCopied")); + new Notice(t('notices.snippetCssCopied')); const buttonEl = evt.currentTarget as HTMLElement; if (!buttonEl) return; - setIcon(buttonEl, "check"); - buttonEl.classList.add("is-success"); + setIcon(buttonEl, 'check'); + buttonEl.classList.add('is-success'); (buttonEl as unknown).disabled = true; setTimeout(() => { - buttonEl.classList.remove("is-success"); - setIcon(buttonEl, "copy"); + buttonEl.classList.remove('is-success'); + setIcon(buttonEl, 'copy'); (buttonEl as unknown).disabled = false; }, 1000); }); new ButtonComponent(controlEl) - .setIcon("trash") - .setTooltip(t("tooltips.deleteSnippet")) + .setIcon('trash') + .setTooltip(t('tooltips.deleteSnippet')) .onClick(() => { new ConfirmationModal( settingTab.app, plugin, - t("modals.confirmation.deleteSnippetTitle", snippet.name), - t("modals.confirmation.deleteSnippetDesc"), + t('modals.confirmation.deleteSnippetTitle', snippet.name), + t('modals.confirmation.deleteSnippetDesc'), () => { void (async () => { const list = isGlobal ? plugin.settings.globalSnippets - : plugin.settings.profiles[plugin.settings.activeProfile] - .snippets; + : plugin.settings.profiles[plugin.settings.activeProfile].snippets; const snippetIndex = list.findIndex((s) => s.id === snippet.id); if (snippetIndex > -1) { @@ -256,18 +226,16 @@ export function drawCssSnippetsUI( await plugin.saveSettings(); settingTab.display(); - new Notice(t("notices.snippetDeleted")); + new Notice(t('notices.snippetDeleted')); })().catch((err) => { - console.error("Failed to delete snippet:", err); + console.error('Failed to delete snippet:', err); }); }, ).open(); }); }; - globalSnippets.forEach((snippet, index) => - renderSnippet(snippet, index, true), - ); + globalSnippets.forEach((snippet, index) => renderSnippet(snippet, index, true)); profileSnippets.forEach((snippet, index) => renderSnippet(snippet, index + globalSnippets.length, false), diff --git a/src/ui/modals.ts b/src/ui/modals.ts index b9bc065..78f2b86 100644 --- a/src/ui/modals.ts +++ b/src/ui/modals.ts @@ -12,27 +12,15 @@ import { TextComponent, requestUrl, Component, -} from "obsidian"; -import Sortable from "sortablejs"; -import { DEFAULT_VARS, DEFAULT_PROFILE } from "../constants"; -import { - CORE_LOCALES, - flattenStrings, - getFallbackStrings, - loadLanguage, - t, -} from "../i18n/strings"; -import { type LocaleCode, CORE_LANGUAGES, LocalizedValue } from "../i18n/types"; -import type ColorMaster from "../main"; -import type { - CustomTranslation, - CustomVarType, - NoticeRule, - Profile, - Snippet, -} from "../types"; -import { debounce, flattenVars, unflattenStrings } from "../utils"; -import type { ColorMasterSettingTab } from "./settingsTab"; +} from 'obsidian'; +import Sortable from 'sortablejs'; +import { DEFAULT_VARS, DEFAULT_PROFILE } from '../constants'; +import { CORE_LOCALES, flattenStrings, getFallbackStrings, loadLanguage, t } from '../i18n/strings'; +import { type LocaleCode, CORE_LANGUAGES, LocalizedValue } from '../i18n/types'; +import type ColorMaster from '../main'; +import type { CustomTranslation, CustomVarType, NoticeRule, Profile, Snippet } from '../types'; +import { debounce, flattenVars, unflattenStrings } from '../utils'; +import type { ColorMasterSettingTab } from './settingsTab'; /** * A new Base Modal that all Color Master modals should extend from. @@ -44,7 +32,7 @@ class ColorMasterBaseModal extends Modal { constructor(app: App, plugin: ColorMaster) { super(app); this.plugin = plugin; - this.modalEl.classList.add("color-master-modal"); + this.modalEl.classList.add('color-master-modal'); } /** @@ -54,12 +42,12 @@ class ColorMasterBaseModal extends Modal { onOpen() { const langCode = this.plugin.settings.language; const customLang = this.plugin.settings.customLanguages?.[langCode]; - const isCoreRtlLang = langCode === "ar" || langCode === "fa"; + const isCoreRtlLang = langCode === 'ar' || langCode === 'fa'; const isCustomRtlLang = customLang?.isRtl === true; const isRtlEnabled = this.plugin.settings.useRtlLayout; const isRTL = (isCoreRtlLang || isCustomRtlLang) && isRtlEnabled; - this.modalEl.setAttribute("dir", isRTL ? "rtl" : "ltr"); + this.modalEl.setAttribute('dir', isRTL ? 'rtl' : 'ltr'); } } @@ -75,30 +63,30 @@ export class LanguageInfoModal extends ColorMasterBaseModal { super.onOpen(); const { contentEl } = this; contentEl.empty(); - this.modalEl.classList.add("color-master-modal", "cm-info-modal"); + this.modalEl.classList.add('color-master-modal', 'cm-info-modal'); - contentEl.createEl("h3", { text: t("modals.langInfo.title") }); + contentEl.createEl('h3', { text: t('modals.langInfo.title') }); - const infoContainer = contentEl.createDiv({ cls: "cm-info-content" }); + const infoContainer = contentEl.createDiv({ cls: 'cm-info-content' }); const comp = new Component(); void MarkdownRenderer.render( this.app, - t("modals.langInfo.desc"), + t('modals.langInfo.desc'), infoContainer, - "", + '', comp, ).catch((err) => { - console.error("Failed to render markdown:", err); + console.error('Failed to render markdown:', err); }); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); new ButtonComponent(buttonContainer) - .setButtonText(t("buttons.apply")) + .setButtonText(t('buttons.apply')) .setCta() .onClick(() => { this.close(); @@ -115,13 +103,9 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { settingTab: ColorMasterSettingTab; textarea: HTMLTextAreaElement; nameInput: TextComponent; - profileName: string = ""; + profileName: string = ''; - constructor( - app: App, - plugin: ColorMaster, - settingTabInstance: ColorMasterSettingTab, - ) { + constructor(app: App, plugin: ColorMaster, settingTabInstance: ColorMasterSettingTab) { super(app, plugin); this.settingTab = settingTabInstance; } @@ -131,25 +115,21 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("modals.jsonImport.title") }); - new Setting(contentEl) - .setName(t("modals.newProfile.nameLabel")) - .addText((text) => { - this.nameInput = text; - text - .setPlaceholder(t("modals.newProfile.namePlaceholder")) - .onChange((value) => { - this.profileName = value.trim(); - }); + contentEl.createEl('h3', { text: t('modals.jsonImport.title') }); + new Setting(contentEl).setName(t('modals.newProfile.nameLabel')).addText((text) => { + this.nameInput = text; + text.setPlaceholder(t('modals.newProfile.namePlaceholder')).onChange((value) => { + this.profileName = value.trim(); }); - contentEl.createEl("hr"); - - contentEl.createEl("p", { - text: t("modals.jsonImport.desc1"), }); - this.textarea = contentEl.createEl("textarea", { - cls: "cm-search-input cm-import-textarea", - attr: { rows: "20", placeholder: t("modals.jsonImport.placeholder") }, + contentEl.createEl('hr'); + + contentEl.createEl('p', { + text: t('modals.jsonImport.desc1'), + }); + this.textarea = contentEl.createEl('textarea', { + cls: 'cm-search-input cm-import-textarea', + attr: { rows: '20', placeholder: t('modals.jsonImport.placeholder') }, }); // Add an 'input' event listener to parse JSON on-the-fly @@ -161,7 +141,7 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { const parsed = JSON.parse(content); // Check if the parsed object has a 'name' property and it's a string - if (parsed && typeof parsed.name === "string" && parsed.name) { + if (parsed && typeof parsed.name === 'string' && parsed.name) { this.nameInput.setValue(parsed.name); this.profileName = parsed.name; } @@ -171,39 +151,36 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { }, 0); // Attach the debounced function to the textarea's input event - this.textarea.addEventListener("input", debouncedParse); + this.textarea.addEventListener('input', debouncedParse); // File import button new Setting(contentEl) - .setName(t("modals.jsonImport.settingName")) - .setDesc(t("modals.jsonImport.settingDesc")) + .setName(t('modals.jsonImport.settingName')) + .setDesc(t('modals.jsonImport.settingDesc')) .addButton((button) => { - button.setButtonText(t("buttons.chooseFile")).onClick(() => { + button.setButtonText(t('buttons.chooseFile')).onClick(() => { this._handleFileImport(); }); }); // Action buttons (Replace/Create New) - const ctrl = contentEl.createDiv({ cls: "modal-button-container" }); - const replaceBtn = ctrl.createEl("button", { - text: t("modals.jsonImport.replaceActiveButton"), + const ctrl = contentEl.createDiv({ cls: 'modal-button-container' }); + const replaceBtn = ctrl.createEl('button', { + text: t('modals.jsonImport.replaceActiveButton'), }); - const createBtn = ctrl.createEl("button", { - text: t("modals.jsonImport.createNewButton"), - cls: "mod-cta", + const createBtn = ctrl.createEl('button', { + text: t('modals.jsonImport.createNewButton'), + cls: 'mod-cta', }); - replaceBtn.addEventListener( - "click", - () => void this._applyImport("replace"), - ); - createBtn.addEventListener("click", () => void this._applyCreate()); + replaceBtn.addEventListener('click', () => void this._applyImport('replace')); + createBtn.addEventListener('click', () => void this._applyCreate()); } _handleFileImport() { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".json"; + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; input.onchange = () => { void (async () => { @@ -215,17 +192,16 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { try { const parsed = JSON.parse(content); const profileNameFromJson = - parsed.name || - file.name.replace(".profile.json", "").replace(".json", ""); + parsed.name || file.name.replace('.profile.json', '').replace('.json', ''); this.nameInput.setValue(profileNameFromJson); this.profileName = profileNameFromJson; } catch { // ignore json parsing errors } - new Notice(t("notices.fileLoaded", file.name)); + new Notice(t('notices.fileLoaded', file.name)); })().catch((err) => { - console.error("Failed to load profile JSON:", err); + console.error('Failed to load profile JSON:', err); }); }; input.click(); @@ -238,18 +214,18 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { async _applyCreate() { const name = this.profileName; if (!name) { - new Notice(t("notices.varNameEmpty")); + new Notice(t('notices.varNameEmpty')); return; } if (this.plugin.settings.profiles[name]) { - new Notice(t("notices.profileNameExists", name)); + new Notice(t('notices.profileNameExists', name)); return; } const raw = this.textarea.value.trim(); if (!raw) { - new Notice(t("notices.textboxEmpty")); + new Notice(t('notices.textboxEmpty')); return; } @@ -257,7 +233,7 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { try { parsed = JSON.parse(raw); } catch { - new Notice(t("notices.invalidJson")); + new Notice(t('notices.invalidJson')); return; } @@ -273,45 +249,44 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { await this.plugin.saveSettings(); this.settingTab.display(); this.close(); - new Notice(t("notices.profileCreatedSuccess", name)); + new Notice(t('notices.profileCreatedSuccess', name)); } - async _applyImport(mode: "replace") { + async _applyImport(mode: 'replace') { const raw = this.textarea.value.trim(); if (!raw) { - new Notice(t("notices.textboxEmpty")); + new Notice(t('notices.textboxEmpty')); return; } let parsed; try { parsed = JSON.parse(raw); } catch { - new Notice(t("notices.invalidJson")); + new Notice(t('notices.invalidJson')); return; } const profileObj = parsed.profile ? parsed.profile : parsed; - if (typeof profileObj !== "object" || profileObj === null) { + if (typeof profileObj !== 'object' || profileObj === null) { // Added null check for safety - new Notice(t("notices.invalidProfileObject")); + new Notice(t('notices.invalidProfileObject')); return; } const activeName = this.plugin.settings.activeProfile; const activeProfile = this.plugin.settings.profiles[activeName]; if (!activeProfile) { - new Notice(t("notices.profileNotFound")); + new Notice(t('notices.profileNotFound')); return; } - const importedVars = "vars" in profileObj ? profileObj.vars : {}; - const importedSnippets = - "snippets" in profileObj ? profileObj.snippets : []; - const themeType = "themeType" in profileObj ? profileObj.themeType : "auto"; + const importedVars = 'vars' in profileObj ? profileObj.vars : {}; + const importedSnippets = 'snippets' in profileObj ? profileObj.snippets : []; + const themeType = 'themeType' in profileObj ? profileObj.themeType : 'auto'; - if (mode === "replace") { + if (mode === 'replace') { activeProfile.vars = importedVars as { [key: string]: string }; activeProfile.snippets = importedSnippets as Snippet[]; - activeProfile.themeType = themeType as "auto" | "dark" | "light"; + activeProfile.themeType = themeType as 'auto' | 'dark' | 'light'; } await this.plugin.saveSettings(); @@ -324,24 +299,18 @@ export class ProfileJsonImportModal extends ColorMasterBaseModal { this.settingTab.display(); this.close(); - new Notice(t("notices.profileImportedSuccess")); + new Notice(t('notices.profileImportedSuccess')); } } export class NewProfileModal extends ColorMasterBaseModal { - onSubmit: (result: { - name: string; - themeType: "auto" | "dark" | "light"; - }) => void; + onSubmit: (result: { name: string; themeType: 'auto' | 'dark' | 'light' }) => void; plugin: ColorMaster; constructor( app: App, plugin: ColorMaster, - onSubmit: (result: { - name: string; - themeType: "auto" | "dark" | "light"; - }) => void, + onSubmit: (result: { name: string; themeType: 'auto' | 'dark' | 'light' }) => void, ) { super(app, plugin); this.onSubmit = onSubmit; @@ -352,46 +321,42 @@ export class NewProfileModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("modals.newProfile.title") }); + contentEl.createEl('h3', { text: t('modals.newProfile.title') }); - let profileName = ""; - let themeType: "auto" | "dark" | "light" = "auto"; + let profileName = ''; + let themeType: 'auto' | 'dark' | 'light' = 'auto'; - new Setting(contentEl) - .setName(t("modals.newProfile.nameLabel")) - .addText((text) => { - text - .setPlaceholder(t("modals.newProfile.namePlaceholder")) - .onChange((value) => { - profileName = value; - }); + new Setting(contentEl).setName(t('modals.newProfile.nameLabel')).addText((text) => { + text.setPlaceholder(t('modals.newProfile.namePlaceholder')).onChange((value) => { + profileName = value; }); + }); new Setting(contentEl) - .setName(t("profileManager.themeType")) - .setDesc(t("profileManager.themeTypeDesc")) + .setName(t('profileManager.themeType')) + .setDesc(t('profileManager.themeTypeDesc')) .addDropdown((dropdown) => { - dropdown.addOption("auto", t("profileManager.themeAuto")); - dropdown.addOption("dark", t("profileManager.themeDark")); - dropdown.addOption("light", t("profileManager.themeLight")); + dropdown.addOption('auto', t('profileManager.themeAuto')); + dropdown.addOption('dark', t('profileManager.themeDark')); + dropdown.addOption('light', t('profileManager.themeLight')); dropdown.setValue(themeType); - dropdown.onChange((value: "auto" | "dark" | "light") => { + dropdown.onChange((value: 'auto' | 'dark' | 'light') => { themeType = value; }); }); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); - const cancelButton = buttonContainer.createEl("button", { - text: t("buttons.cancel"), + const cancelButton = buttonContainer.createEl('button', { + text: t('buttons.cancel'), }); - cancelButton.addEventListener("click", () => this.close()); + cancelButton.addEventListener('click', () => this.close()); - const createButton = buttonContainer.createEl("button", { - text: t("buttons.create"), - cls: "mod-cta", + const createButton = buttonContainer.createEl('button', { + text: t('buttons.create'), + cls: 'mod-cta', }); const submit = () => { @@ -399,18 +364,18 @@ export class NewProfileModal extends ColorMasterBaseModal { if (trimmedName) { this.onSubmit({ name: trimmedName, themeType: themeType }); this.close(); - new Notice(t("notices.profileCreated", trimmedName)); + new Notice(t('notices.profileCreated', trimmedName)); } else { - new Notice(t("notices.varNameEmpty")); + new Notice(t('notices.varNameEmpty')); } }; - createButton.addEventListener("click", submit); + createButton.addEventListener('click', submit); contentEl .querySelector('input[type="text"]') - ?.addEventListener("keydown", (e: KeyboardEvent) => { - if (e.key === "Enter") { + ?.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.key === 'Enter') { e.preventDefault(); submit(); } @@ -442,8 +407,8 @@ export class ConfirmationModal extends ColorMasterBaseModal { this.title = title; this.message = message; this.onConfirm = onConfirm; - this.confirmButtonText = options.buttonText || t("buttons.delete"); - this.confirmButtonClass = options.buttonClass || "mod-warning"; + this.confirmButtonText = options.buttonText || t('buttons.delete'); + this.confirmButtonClass = options.buttonClass || 'mod-warning'; } onOpen() { @@ -451,23 +416,23 @@ export class ConfirmationModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: this.title }); - contentEl.createEl("p").append(this.message); + contentEl.createEl('h3', { text: this.title }); + contentEl.createEl('p').append(this.message); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); - const cancelButton = buttonContainer.createEl("button", { - text: t("buttons.cancel"), + const cancelButton = buttonContainer.createEl('button', { + text: t('buttons.cancel'), }); - cancelButton.addEventListener("click", () => this.close()); + cancelButton.addEventListener('click', () => this.close()); - const confirmButton = buttonContainer.createEl("button", { + const confirmButton = buttonContainer.createEl('button', { text: this.confirmButtonText, cls: this.confirmButtonClass, }); - confirmButton.addEventListener("click", () => { + confirmButton.addEventListener('click', () => { this.onConfirm(); this.close(); }); @@ -508,13 +473,13 @@ export class PasteCssModal extends ColorMasterBaseModal { this.settingTab = settingTab; this.existingProfileData = existingProfileData; this.isEditing = !!existingProfileData; - this.profileName = existingProfileData ? existingProfileData.name : ""; + this.profileName = existingProfileData ? existingProfileData.name : ''; } _handleFileImport() { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".css"; + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.css'; input.onchange = () => { void (async () => { @@ -522,9 +487,9 @@ export class PasteCssModal extends ColorMasterBaseModal { const file = input.files[0]; const content = await file.text(); this.cssTextarea.value = content; - new Notice(t("notices.fileLoaded", file.name)); + new Notice(t('notices.fileLoaded', file.name)); })().catch((err) => { - console.error("Failed to load CSS file:", err); + console.error('Failed to load CSS file:', err); }); }; input.click(); @@ -536,22 +501,22 @@ export class PasteCssModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - const titleContainer = contentEl.createDiv({ cls: "cm-title-container" }); + const titleContainer = contentEl.createDiv({ cls: 'cm-title-container' }); const titleText = this.isEditing - ? t("modals.cssImport.titleEdit") - : t("modals.cssImport.title"); + ? t('modals.cssImport.titleEdit') + : t('modals.cssImport.title'); - this.modalTitleEl = titleContainer.createEl("h3", { text: titleText }); + this.modalTitleEl = titleContainer.createEl('h3', { text: titleText }); const installedThemes = (this.app as unknown).customCss.themes || {}; const themeNames = Object.keys(installedThemes); - let selectedTheme = themeNames.length > 0 ? themeNames[0] : ""; + let selectedTheme = themeNames.length > 0 ? themeNames[0] : ''; const themeImporterEl = new Setting(contentEl) - .setName(t("modals.cssImport.importFromTheme")) - .setDesc(t("modals.cssImport.importFromThemeDesc")); - themeImporterEl.settingEl.addClass("cm-theme-importer-setting"); + .setName(t('modals.cssImport.importFromTheme')) + .setDesc(t('modals.cssImport.importFromThemeDesc')); + themeImporterEl.settingEl.addClass('cm-theme-importer-setting'); themeImporterEl.addDropdown((dropdown) => { if (themeNames.length > 0) { @@ -562,14 +527,14 @@ export class PasteCssModal extends ColorMasterBaseModal { selectedTheme = value; }); } else { - dropdown.addOption("", t("modals.cssImport.noThemes")); + dropdown.addOption('', t('modals.cssImport.noThemes')); dropdown.setDisabled(true); } }); themeImporterEl.addButton((button) => { button - .setButtonText(t("buttons.import")) + .setButtonText(t('buttons.import')) .setCta() .setDisabled(themeNames.length === 0) .onClick(async () => { @@ -580,53 +545,44 @@ export class PasteCssModal extends ColorMasterBaseModal { this.cssTextarea.value = cssContent; this.nameInput.setValue(selectedTheme); this.profileName = selectedTheme; - new Notice(t("notices.themeCssLoaded", selectedTheme)); + new Notice(t('notices.themeCssLoaded', selectedTheme)); } catch (error) { - new Notice(t("notices.themeReadFailed", selectedTheme)); - console.error( - `Color Master: Failed to read theme CSS at ${themePath}`, - error, - ); + new Notice(t('notices.themeReadFailed', selectedTheme)); + console.error(`Color Master: Failed to read theme CSS at ${themePath}`, error); } }); }); - let nameLabelText = t("modals.newProfile.nameLabel"); - this.nameSetting = new Setting(contentEl) - .setName(nameLabelText) - .addText((text) => { - this.nameInput = text; - let placeholderText = t("modals.newProfile.namePlaceholder"); + let nameLabelText = t('modals.newProfile.nameLabel'); + this.nameSetting = new Setting(contentEl).setName(nameLabelText).addText((text) => { + this.nameInput = text; + let placeholderText = t('modals.newProfile.namePlaceholder'); - text - .setValue( - this.isEditing && this.existingProfileData - ? this.existingProfileData.name - : "", - ) - .setPlaceholder(placeholderText) - .onChange((value) => { - this.profileName = value.trim(); - }); - }); + text + .setValue(this.isEditing && this.existingProfileData ? this.existingProfileData.name : '') + .setPlaceholder(placeholderText) + .onChange((value) => { + this.profileName = value.trim(); + }); + }); - this.cssTextarea = contentEl.createEl("textarea", { - cls: "cm-search-input cm-large-textarea", + this.cssTextarea = contentEl.createEl('textarea', { + cls: 'cm-search-input cm-large-textarea', attr: { - rows: "18", - placeholder: t("modals.snippetEditor.cssPlaceholder"), + rows: '18', + placeholder: t('modals.snippetEditor.cssPlaceholder'), }, }); contentEl.createDiv({ - text: t("modals.cssImport.note"), - cls: "cm-modal-warning-note", + text: t('modals.cssImport.note'), + cls: 'cm-modal-warning-note', }); new Setting(contentEl) - .setName(t("modals.cssImport.importFromFile")) - .setDesc(t("modals.cssImport.importFromFileDesc")) + .setName(t('modals.cssImport.importFromFile')) + .setDesc(t('modals.cssImport.importFromFileDesc')) .addButton((button) => { - button.setButtonText(t("buttons.chooseFile")).onClick(() => { + button.setButtonText(t('buttons.chooseFile')).onClick(() => { this._handleFileImport(); }); }); @@ -637,9 +593,7 @@ export class PasteCssModal extends ColorMasterBaseModal { : null; const initialCss = - this.isEditing && this.existingProfileData - ? this.existingProfileData.css - : ""; + this.isEditing && this.existingProfileData ? this.existingProfileData.css : ''; if (historyId) { const lastState = this.plugin.cssHistory[historyId]?.undoStack.last(); @@ -659,21 +613,21 @@ export class PasteCssModal extends ColorMasterBaseModal { this.plugin.pushCssHistory(id, value); }, 500); - this.cssTextarea.addEventListener("input", () => { + this.cssTextarea.addEventListener('input', () => { if (historyId) { debouncedPushHistory(historyId, this.cssTextarea.value); } }); - this.cssTextarea.addEventListener("keydown", (e) => { + this.cssTextarea.addEventListener('keydown', (e) => { if (historyId && e.ctrlKey) { - if (e.key.toLowerCase() === "z") { + if (e.key.toLowerCase() === 'z') { e.preventDefault(); const prevState = this.plugin.undoCssHistory(historyId); if (prevState !== null) { this.cssTextarea.value = prevState; } - } else if (e.key.toLowerCase() === "y") { + } else if (e.key.toLowerCase() === 'y') { e.preventDefault(); const nextState = this.plugin.redoCssHistory(historyId); if (nextState !== null) { @@ -683,21 +637,21 @@ export class PasteCssModal extends ColorMasterBaseModal { } }); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); buttonContainer - .createEl("button", { text: t("buttons.cancel") }) - .addEventListener("click", () => this.close()); + .createEl('button', { text: t('buttons.cancel') }) + .addEventListener('click', () => this.close()); buttonContainer - .createEl("button", { - text: this.isEditing ? t("buttons.update") : t("buttons.create"), - cls: "mod-cta", + .createEl('button', { + text: this.isEditing ? t('buttons.update') : t('buttons.create'), + cls: 'mod-cta', }) - .addEventListener("click", () => { + .addEventListener('click', () => { void this.handleSave().catch((err) => { - console.error("Failed to save CSS snippet:", err); + console.error('Failed to save CSS snippet:', err); }); }); setTimeout(() => this.nameInput.inputEl.focus(), 0); @@ -705,22 +659,19 @@ export class PasteCssModal extends ColorMasterBaseModal { async handleSave() { if (this.isEditing && !this.existingProfileData) { - console.error( - "Attempted to save in editing mode without an existing snippet.", - ); + console.error('Attempted to save in editing mode without an existing snippet.'); return; } const cssText = this.cssTextarea.value.trim(); - let name = (this.profileName || "").trim(); + let name = (this.profileName || '').trim(); if (!name) { - new Notice(t("notices.varNameEmpty")); + new Notice(t('notices.varNameEmpty')); return; } - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if (!activeProfile) return; const isNameTaken = Object.keys(this.plugin.settings.profiles || {}).some( @@ -728,18 +679,17 @@ export class PasteCssModal extends ColorMasterBaseModal { profileName.toLowerCase() === name.toLowerCase() && (!this.isEditing || (this.existingProfileData && - this.existingProfileData.name.toLowerCase() !== - name.toLowerCase())), + this.existingProfileData.name.toLowerCase() !== name.toLowerCase())), ); if (isNameTaken) { - new Notice(t("notices.profileNameExists", name)); + new Notice(t('notices.profileNameExists', name)); return; } // 1. UI Feedback - const saveBtn = this.contentEl.querySelector(".mod-cta"); + const saveBtn = this.contentEl.querySelector('.mod-cta'); if (saveBtn) { - saveBtn.textContent = t("modals.addBackground.processing") + "..."; + saveBtn.textContent = t('modals.addBackground.processing') + '...'; saveBtn.disabled = true; } @@ -749,10 +699,8 @@ export class PasteCssModal extends ColorMasterBaseModal { // 3. SWITCH TO GHOST PROFILE // Create a temporary blank profile in settings so we can switch to it - const tempProfileName = "__cm_temp_clean_slate__"; - this.plugin.settings.profiles[tempProfileName] = JSON.parse( - JSON.stringify(DEFAULT_PROFILE), - ); + const tempProfileName = '__cm_temp_clean_slate__'; + this.plugin.settings.profiles[tempProfileName] = JSON.parse(JSON.stringify(DEFAULT_PROFILE)); this.plugin.settings.activeProfile = tempProfileName; // Force apply the blank profile (removes all plugin colors instantly) @@ -760,7 +708,7 @@ export class PasteCssModal extends ColorMasterBaseModal { // 4. Disable Obsidian Theme (Back to Default) if (originalObsidianTheme) { - customCss.setTheme(""); + customCss.setTheme(''); } // 5. FORCE REFLOW & WAIT @@ -769,8 +717,8 @@ export class PasteCssModal extends ColorMasterBaseModal { // 6. Inject New CSS (Minimal) // eslint-disable-next-line obsidianmd/no-forbidden-elements - const tempStyleEl = document.createElement("style"); - tempStyleEl.id = "cm-temp-import-style"; + const tempStyleEl = document.createElement('style'); + tempStyleEl.id = 'cm-temp-import-style'; tempStyleEl.textContent = cssText; document.head.appendChild(tempStyleEl); @@ -814,11 +762,11 @@ export class PasteCssModal extends ColorMasterBaseModal { }; this.plugin.settings.activeProfile = name; - new Notice(t("notices.profileUpdated", name)); + new Notice(t('notices.profileUpdated', name)); } else { this.plugin.settings.profiles[name] = { vars: capturedVars, - themeType: "auto", + themeType: 'auto', isCssProfile: true, customCss: cssText, snippets: [], @@ -826,7 +774,7 @@ export class PasteCssModal extends ColorMasterBaseModal { }; this.plugin.settings.activeProfile = name; - new Notice(t("notices.profileCreatedFromCss", name)); + new Notice(t('notices.profileCreatedFromCss', name)); } this.isSaving = true; @@ -886,14 +834,14 @@ export class SnippetCssModal extends ColorMasterBaseModal { this.settingTab = settingTab; this.existingSnippet = existingSnippet; this.isEditing = !!existingSnippet; - this.snippetName = existingSnippet ? existingSnippet.name : ""; + this.snippetName = existingSnippet ? existingSnippet.name : ''; this.isGlobalSnippet = existingSnippet ? !!existingSnippet.isGlobal : false; } _handleFileImport() { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".css"; + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.css'; input.onchange = () => { void (async () => { @@ -901,9 +849,9 @@ export class SnippetCssModal extends ColorMasterBaseModal { const file = input.files[0]; const content = await file.text(); this.cssTextarea.inputEl.value = content; - new Notice(t("notices.fileLoaded", file.name)); + new Notice(t('notices.fileLoaded', file.name)); })().catch((err) => { - console.error("Failed to load CSS file:", err); + console.error('Failed to load CSS file:', err); }); }; input.click(); @@ -915,22 +863,21 @@ export class SnippetCssModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - const titleContainer = contentEl.createDiv({ cls: "cm-title-container" }); + const titleContainer = contentEl.createDiv({ cls: 'cm-title-container' }); let titleText = this.isEditing - ? t("modals.snippetEditor.titleEdit") - : t("modals.snippetEditor.title"); + ? t('modals.snippetEditor.titleEdit') + : t('modals.snippetEditor.title'); - this.modalTitleEl = titleContainer.createEl("h3", { text: titleText }); + this.modalTitleEl = titleContainer.createEl('h3', { text: titleText }); const installedSnippets = (this.app as unknown).customCss.snippets || []; - let selectedSnippet = - installedSnippets.length > 0 ? installedSnippets[0] : ""; + let selectedSnippet = installedSnippets.length > 0 ? installedSnippets[0] : ''; const snippetImporterEl = new Setting(contentEl) - .setName(t("modals.snippetEditor.importFromSnippet")) - .setDesc(t("modals.snippetEditor.importFromSnippetDesc")); - snippetImporterEl.settingEl.addClass("cm-theme-importer-setting"); + .setName(t('modals.snippetEditor.importFromSnippet')) + .setDesc(t('modals.snippetEditor.importFromSnippetDesc')); + snippetImporterEl.settingEl.addClass('cm-theme-importer-setting'); snippetImporterEl.addDropdown((dropdown) => { if (installedSnippets.length > 0) { @@ -941,14 +888,14 @@ export class SnippetCssModal extends ColorMasterBaseModal { selectedSnippet = value; }); } else { - dropdown.addOption("", t("modals.snippetEditor.noSnippets")); + dropdown.addOption('', t('modals.snippetEditor.noSnippets')); dropdown.setDisabled(true); } }); snippetImporterEl.addButton((button) => { button - .setButtonText(t("buttons.import")) + .setButtonText(t('buttons.import')) .setCta() .setDisabled(installedSnippets.length === 0) .onClick(async () => { @@ -959,40 +906,31 @@ export class SnippetCssModal extends ColorMasterBaseModal { this.cssTextarea.setValue(cssContent); this.nameInput.setValue(selectedSnippet); this.snippetName = selectedSnippet; - new Notice(t("notices.snippetLoaded", selectedSnippet)); + new Notice(t('notices.snippetLoaded', selectedSnippet)); } catch (error) { - new Notice(t("notices.snippetReadFailed", selectedSnippet)); - console.error( - `Color Master: Failed to read snippet CSS at ${snippetPath}`, - error, - ); + new Notice(t('notices.snippetReadFailed', selectedSnippet)); + console.error(`Color Master: Failed to read snippet CSS at ${snippetPath}`, error); } }); }); - const nameLabelText = t("modals.snippetEditor.nameLabel"); + const nameLabelText = t('modals.snippetEditor.nameLabel'); - this.nameSetting = new Setting(contentEl) - .setName(nameLabelText) - .addText((text) => { - this.nameInput = text; - let placeholderText = t("modals.snippetEditor.namePlaceholder"); + this.nameSetting = new Setting(contentEl).setName(nameLabelText).addText((text) => { + this.nameInput = text; + let placeholderText = t('modals.snippetEditor.namePlaceholder'); - text - .setValue( - this.isEditing && this.existingSnippet - ? this.existingSnippet.name - : "", - ) - .setPlaceholder(placeholderText) - .onChange((value) => { - this.snippetName = value.trim(); - }); - }); + text + .setValue(this.isEditing && this.existingSnippet ? this.existingSnippet.name : '') + .setPlaceholder(placeholderText) + .onChange((value) => { + this.snippetName = value.trim(); + }); + }); new Setting(contentEl) - .setName(t("snippets.globalName")) - .setDesc(t("snippets.globalDesc")) + .setName(t('snippets.globalName')) + .setDesc(t('snippets.globalDesc')) .addToggle((toggle) => { toggle.setValue(this.isGlobalSnippet).onChange((value) => { this.isGlobalSnippet = value; @@ -1001,31 +939,26 @@ export class SnippetCssModal extends ColorMasterBaseModal { this.cssTextarea = new TextAreaComponent(contentEl); contentEl.createDiv({ - text: t("modals.cssImport.note"), - cls: "cm-modal-warning-note", + text: t('modals.cssImport.note'), + cls: 'cm-modal-warning-note', }); new Setting(contentEl) - .setName(t("modals.cssImport.importFromFile")) - .setDesc(t("modals.cssImport.importFromFileDesc")) + .setName(t('modals.cssImport.importFromFile')) + .setDesc(t('modals.cssImport.importFromFileDesc')) .addButton((button) => { - button.setButtonText(t("buttons.chooseFile")).onClick(() => { + button.setButtonText(t('buttons.chooseFile')).onClick(() => { this._handleFileImport(); }); }); - this.cssTextarea.inputEl.classList.add( - "cm-search-input", - "cm-large-textarea", - ); + this.cssTextarea.inputEl.classList.add('cm-search-input', 'cm-large-textarea'); this.cssTextarea.inputEl.rows = 18; - this.cssTextarea.setPlaceholder(t("modals.snippetEditor.cssPlaceholder")); + this.cssTextarea.setPlaceholder(t('modals.snippetEditor.cssPlaceholder')); - const historyId = - this.isEditing && this.existingSnippet ? this.existingSnippet.id : null; + const historyId = this.isEditing && this.existingSnippet ? this.existingSnippet.id : null; - const initialCss = - this.isEditing && this.existingSnippet ? this.existingSnippet.css : ""; + const initialCss = this.isEditing && this.existingSnippet ? this.existingSnippet.css : ''; if (historyId) { const lastState = this.plugin.cssHistory[historyId]?.undoStack.last(); @@ -1051,15 +984,15 @@ export class SnippetCssModal extends ColorMasterBaseModal { } }); - this.cssTextarea.inputEl.addEventListener("keydown", (e: KeyboardEvent) => { + this.cssTextarea.inputEl.addEventListener('keydown', (e: KeyboardEvent) => { if (historyId && e.ctrlKey) { - if (e.key.toLowerCase() === "z") { + if (e.key.toLowerCase() === 'z') { e.preventDefault(); const prevState = this.plugin.undoCssHistory(historyId); if (prevState !== null) { this.cssTextarea.setValue(prevState); } - } else if (e.key.toLowerCase() === "y") { + } else if (e.key.toLowerCase() === 'y') { e.preventDefault(); const nextState = this.plugin.redoCssHistory(historyId); if (nextState !== null) { @@ -1069,37 +1002,36 @@ export class SnippetCssModal extends ColorMasterBaseModal { } }); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); buttonContainer - .createEl("button", { text: t("buttons.cancel") }) - .addEventListener("click", () => this.close()); + .createEl('button', { text: t('buttons.cancel') }) + .addEventListener('click', () => this.close()); buttonContainer - .createEl("button", { - text: this.isEditing ? t("buttons.update") : t("buttons.create"), - cls: "mod-cta", + .createEl('button', { + text: this.isEditing ? t('buttons.update') : t('buttons.create'), + cls: 'mod-cta', }) - .addEventListener("click", () => this.handleSave()); + .addEventListener('click', () => this.handleSave()); setTimeout(() => this.nameInput.inputEl.focus(), 0); } handleSave() { const cssText = this.cssTextarea.getValue().trim(); - const name = (this.snippetName || "").trim(); + const name = (this.snippetName || '').trim(); if (!name) { - new Notice(t("notices.varNameEmpty")); + new Notice(t('notices.varNameEmpty')); return; } if (!cssText) { - new Notice(t("notices.cssContentEmpty")); + new Notice(t('notices.cssContentEmpty')); return; } - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if (!activeProfile) return; if (!Array.isArray(this.plugin.settings.globalSnippets)) { @@ -1113,12 +1045,11 @@ export class SnippetCssModal extends ColorMasterBaseModal { const isNameTaken = targetList.some( (s) => s.name.toLowerCase() === name.toLowerCase() && - (!this.isEditing || - (this.existingSnippet && this.existingSnippet.id !== s.id)), + (!this.isEditing || (this.existingSnippet && this.existingSnippet.id !== s.id)), ); if (isNameTaken) { - new Notice(t("notices.snippetNameExists", name)); + new Notice(t('notices.snippetNameExists', name)); return; } @@ -1131,9 +1062,7 @@ export class SnippetCssModal extends ColorMasterBaseModal { ? this.plugin.settings.globalSnippets : activeProfile.snippets; - const snippetIndex = originalList.findIndex( - (s) => s.id === this.existingSnippet!.id, - ); + const snippetIndex = originalList.findIndex((s) => s.id === this.existingSnippet!.id); if (snippetIndex > -1) { // First case If the clip type does not change (remains public or private) @@ -1153,7 +1082,7 @@ export class SnippetCssModal extends ColorMasterBaseModal { targetList.push(snippetToMove); } - new Notice(t("notices.snippetUpdated", name)); + new Notice(t('notices.snippetUpdated', name)); } } else { targetList.push({ @@ -1163,7 +1092,7 @@ export class SnippetCssModal extends ColorMasterBaseModal { enabled: true, isGlobal: this.isGlobalSnippet, }); - new Notice(t("notices.snippetCreated", name)); + new Notice(t('notices.snippetCreated', name)); } this.isSaving = true; @@ -1175,15 +1104,14 @@ export class SnippetCssModal extends ColorMasterBaseModal { this.close(); }) .catch((err) => { - console.error("Failed to save settings:", err); + console.error('Failed to save settings:', err); }); } onClose() { // If the save button is not pressed if (!this.isSaving) { - const historyId = - this.isEditing && this.existingSnippet ? this.existingSnippet.id : null; + const historyId = this.isEditing && this.existingSnippet ? this.existingSnippet.id : null; // If there is a temporary date delete it. if (historyId && this.plugin.cssHistory[historyId]) { delete this.plugin.cssHistory[historyId]; @@ -1196,7 +1124,7 @@ export class SnippetCssModal extends ColorMasterBaseModal { export class NoticeRulesModal extends ColorMasterBaseModal { plugin: ColorMaster; settingTab: ColorMasterSettingTab; - ruleType: "text" | "background"; + ruleType: 'text' | 'background'; localRules: NoticeRule[]; newlyAddedRuleId: string | null = null; rulesContainer: HTMLElement; @@ -1206,21 +1134,18 @@ export class NoticeRulesModal extends ColorMasterBaseModal { app: App, plugin: ColorMaster, settingTab: ColorMasterSettingTab, - ruleType: "text" | "background", + ruleType: 'text' | 'background', ) { super(app, plugin); this.settingTab = settingTab; this.ruleType = ruleType; // 'text' or 'background' - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; - this.localRules = JSON.parse( - JSON.stringify(activeProfile?.noticeRules?.[this.ruleType] || []), - ); + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + this.localRules = JSON.parse(JSON.stringify(activeProfile?.noticeRules?.[this.ruleType] || [])); if (this.localRules.length === 0) { this.localRules.push({ id: `rule-${Date.now()}`, - keywords: "", - color: this.ruleType === "text" ? "#ffffff" : "#444444", + keywords: '', + color: this.ruleType === 'text' ? '#ffffff' : '#444444', isRegex: false, highlightOnly: false, }); @@ -1232,96 +1157,91 @@ export class NoticeRulesModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - this.modalEl.classList.add("color-master-modal", "cm-rules-modal"); + this.modalEl.classList.add('color-master-modal', 'cm-rules-modal'); const title = - this.ruleType === "text" - ? t("modals.noticeRules.titleText") - : t("modals.noticeRules.titleBg"); + this.ruleType === 'text' + ? t('modals.noticeRules.titleText') + : t('modals.noticeRules.titleBg'); const headerContainer = contentEl.createDiv({ - cls: "cm-rules-modal-header", + cls: 'cm-rules-modal-header', }); const iconEl = headerContainer.createDiv({ - cls: "cm-rules-modal-header-icon", + cls: 'cm-rules-modal-header-icon', }); - setIcon(iconEl, "bell"); - headerContainer.createEl("h3", { text: title }); + setIcon(iconEl, 'bell'); + headerContainer.createEl('h3', { text: title }); const descAndButtonContainer = contentEl.createDiv({ - cls: "cm-rules-header", + cls: 'cm-rules-header', }); - descAndButtonContainer.createEl("p", { - text: t("modals.noticeRules.desc"), - cls: "cm-rules-modal-desc", + descAndButtonContainer.createEl('p', { + text: t('modals.noticeRules.desc'), + cls: 'cm-rules-modal-desc', }); const buttonSettingContainer = descAndButtonContainer.createDiv(); - const settingEl = new Setting(buttonSettingContainer).addButton( - (button) => { - button - .setButtonText(t("modals.noticeRules.addNewRule")) - .setCta() - .onClick(() => { - const newRule: NoticeRule = { - id: `rule-${Date.now()}`, - keywords: "", - color: this.ruleType === "text" ? "#ffffff" : "#444444", - isRegex: false, - highlightOnly: false, - }; - this.localRules.push(newRule); + const settingEl = new Setting(buttonSettingContainer).addButton((button) => { + button + .setButtonText(t('modals.noticeRules.addNewRule')) + .setCta() + .onClick(() => { + const newRule: NoticeRule = { + id: `rule-${Date.now()}`, + keywords: '', + color: this.ruleType === 'text' ? '#ffffff' : '#444444', + isRegex: false, + highlightOnly: false, + }; + this.localRules.push(newRule); - this.newlyAddedRuleId = newRule.id; + this.newlyAddedRuleId = newRule.id; - this.displayRules(); - }); - }, - ); + this.displayRules(); + }); + }); - settingEl.settingEl.classList.add("cm-rules-add-button-setting"); + settingEl.settingEl.classList.add('cm-rules-add-button-setting'); - this.rulesContainer = contentEl.createDiv("cm-rules-container"); + this.rulesContainer = contentEl.createDiv('cm-rules-container'); this.displayRules(); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); buttonContainer - .createEl("button", { text: t("buttons.cancel") }) - .addEventListener("click", () => this.close()); + .createEl('button', { text: t('buttons.cancel') }) + .addEventListener('click', () => this.close()); buttonContainer - .createEl("button", { text: t("buttons.apply"), cls: "mod-cta" }) - .addEventListener("click", () => { + .createEl('button', { text: t('buttons.apply'), cls: 'mod-cta' }) + .addEventListener('click', () => { void (async () => { const allTagInputs = - this.rulesContainer.querySelectorAll( - ".cm-tag-input-field", - ); + this.rulesContainer.querySelectorAll('.cm-tag-input-field'); allTagInputs.forEach((inputEl, index) => { - const newKeyword = inputEl.value.trim().replace(/,/g, ""); + const newKeyword = inputEl.value.trim().replace(/,/g, ''); if (newKeyword) { const rule = this.localRules[index]; if (rule) { const keywords = - typeof rule.keywords === "string" && rule.keywords + typeof rule.keywords === 'string' && rule.keywords ? rule.keywords - .split(",") + .split(',') .map((k) => k.trim()) .filter(Boolean) : []; if (!keywords.includes(newKeyword)) { - rule.keywords = [...keywords, newKeyword].join(","); + rule.keywords = [...keywords, newKeyword].join(','); } } } }); - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if (!activeProfile.noticeRules) { activeProfile.noticeRules = { text: [], background: [] }; @@ -1333,10 +1253,10 @@ export class NoticeRulesModal extends ColorMasterBaseModal { this.plugin.liveNoticeRules = null; this.plugin.liveNoticeRuleType = null; - new Notice(t("notices.settingsSaved")); + new Notice(t('notices.settingsSaved')); this.close(); })().catch((err) => { - console.error("Failed to save notice rules:", err); + console.error('Failed to save notice rules:', err); }); }); } @@ -1346,103 +1266,97 @@ export class NoticeRulesModal extends ColorMasterBaseModal { container.empty(); this.localRules.forEach((rule, index) => { - const ruleEl = container.createDiv({ cls: "cm-rule-item" }); + const ruleEl = container.createDiv({ cls: 'cm-rule-item' }); ruleEl.dataset.ruleId = rule.id; if (this.newlyAddedRuleId && rule.id === this.newlyAddedRuleId) { - ruleEl.classList.add("newly-added"); + ruleEl.classList.add('newly-added'); this.newlyAddedRuleId = null; } const actionButtonsContainer = ruleEl.createDiv({ - cls: "cm-rule-actions", + cls: 'cm-rule-actions', }); const moveButtons = actionButtonsContainer.createDiv({ - cls: "cm-rule-action-buttons", + cls: 'cm-rule-action-buttons', }); const handleBtn = new ButtonComponent(moveButtons) - .setIcon("grip-vertical") - .setTooltip(t("tooltips.dragReorder")); + .setIcon('grip-vertical') + .setTooltip(t('tooltips.dragReorder')); - handleBtn.buttonEl.classList.add("cm-drag-handle"); - handleBtn.buttonEl.addEventListener("mousedown", (e) => { + handleBtn.buttonEl.classList.add('cm-drag-handle'); + handleBtn.buttonEl.addEventListener('mousedown', (e) => { e.stopPropagation(); }); actionButtonsContainer.createDiv({ - cls: "cm-rule-order-number", + cls: 'cm-rule-order-number', text: `${index + 1}`, }); const tagInputWrapper = ruleEl.createDiv({ - cls: "cm-rule-input-wrapper", + cls: 'cm-rule-input-wrapper', }); this._createTagInput(tagInputWrapper, rule); - const colorContainer = ruleEl.createDiv({ cls: "cm-color-container" }); + const colorContainer = ruleEl.createDiv({ cls: 'cm-color-container' }); - const colorInput = colorContainer.createEl("input", { type: "color" }); + const colorInput = colorContainer.createEl('input', { type: 'color' }); colorInput.value = rule.color; - if (rule.color && rule.color.toLowerCase() === "transparent") { - colorInput.classList.add("is-transparent"); + if (rule.color && rule.color.toLowerCase() === 'transparent') { + colorInput.classList.add('is-transparent'); } - colorInput.addEventListener("input", (evt) => { + colorInput.addEventListener('input', (evt) => { rule.color = (evt.target as HTMLInputElement).value; this.plugin.liveNoticeRules = this.localRules; this.plugin.liveNoticeRuleType = this.ruleType; - colorInput.classList.remove("is-transparent"); + colorInput.classList.remove('is-transparent'); }); new ButtonComponent(colorContainer) - .setIcon("eraser") - .setClass("cm-rule-icon-button") - .setTooltip(t("tooltips.setTransparent")) + .setIcon('eraser') + .setClass('cm-rule-icon-button') + .setTooltip(t('tooltips.setTransparent')) .onClick(() => { - rule.color = "transparent"; - colorInput.classList.add("is-transparent"); + rule.color = 'transparent'; + colorInput.classList.add('is-transparent'); }); const regexBtn = new ButtonComponent(ruleEl) - .setIcon("regex") - .setClass("cm-rule-icon-button") - .setTooltip(t("modals.noticeRules.useRegex")) + .setIcon('regex') + .setClass('cm-rule-icon-button') + .setTooltip(t('modals.noticeRules.useRegex')) .onClick(() => { rule.isRegex = !rule.isRegex; - regexBtn.buttonEl.classList.toggle("is-active", rule.isRegex); + regexBtn.buttonEl.classList.toggle('is-active', rule.isRegex); }); - regexBtn.buttonEl.classList.toggle("is-active", rule.isRegex); + regexBtn.buttonEl.classList.toggle('is-active', rule.isRegex); - if (this.ruleType === "text") { + if (this.ruleType === 'text') { const highlightBtn = new ButtonComponent(ruleEl) - .setIcon("highlighter") - .setClass("cm-rule-icon-button") - .setTooltip(t("modals.noticeRules.highlightOnly")) + .setIcon('highlighter') + .setClass('cm-rule-icon-button') + .setTooltip(t('modals.noticeRules.highlightOnly')) .onClick(() => { (rule as unknown).highlightOnly = !(rule as unknown).highlightOnly; - highlightBtn.buttonEl.classList.toggle( - "is-active", - (rule as unknown).highlightOnly, - ); + highlightBtn.buttonEl.classList.toggle('is-active', (rule as unknown).highlightOnly); }); - highlightBtn.buttonEl.classList.toggle( - "is-active", - (rule as unknown).highlightOnly, - ); + highlightBtn.buttonEl.classList.toggle('is-active', (rule as unknown).highlightOnly); } new ButtonComponent(ruleEl) - .setIcon("bell") - .setClass("cm-rule-icon-button") - .setTooltip(t("tooltips.testRule")) + .setIcon('bell') + .setClass('cm-rule-icon-button') + .setTooltip(t('tooltips.testRule')) .onClick(() => { this._handleTestRule(rule); }); new ButtonComponent(ruleEl) - .setIcon("trash") - .setClass("cm-rule-icon-button") + .setIcon('trash') + .setClass('cm-rule-icon-button') .setWarning() .onClick(() => { this._handleDeleteRule(index); @@ -1458,25 +1372,24 @@ export class NoticeRulesModal extends ColorMasterBaseModal { try { this.sortable.destroy(); } catch (e) { - console.warn("Could not destroy sortable instance", e); + console.warn('Could not destroy sortable instance', e); } this.sortable = null; } if (!Sortable) { - console.warn("Color Master: SortableJS not found, drag & drop disabled."); + console.warn('Color Master: SortableJS not found, drag & drop disabled.'); return; } this.sortable = new Sortable(this.rulesContainer, { - handle: ".cm-drag-handle", + handle: '.cm-drag-handle', animation: 160, - ghostClass: "cm-rule-ghost", - dataIdAttr: "data-rule-id", + ghostClass: 'cm-rule-ghost', + dataIdAttr: 'data-rule-id', onEnd: (evt: Sortable.SortableEvent) => { const { oldIndex, newIndex } = evt; - if (oldIndex === newIndex || oldIndex == null || newIndex == null) - return; + if (oldIndex === newIndex || oldIndex == null || newIndex == null) return; const [moved] = this.localRules.splice(oldIndex, 1); if (moved) { @@ -1494,48 +1407,48 @@ export class NoticeRulesModal extends ColorMasterBaseModal { } _createTagInput(parentEl: HTMLElement, rule: NoticeRule) { - const container = parentEl.createDiv({ cls: "cm-tag-input-container" }); + const container = parentEl.createDiv({ cls: 'cm-tag-input-container' }); const renderTags = () => { container.empty(); const keywords = - typeof rule.keywords === "string" && rule.keywords + typeof rule.keywords === 'string' && rule.keywords ? rule.keywords - .split(",") + .split(',') .map((k) => k.trim()) .filter(Boolean) : []; keywords.forEach((keyword, index) => { - const tagEl = container.createDiv({ cls: "cm-tag-item" }); + const tagEl = container.createDiv({ cls: 'cm-tag-item' }); tagEl.dataset.keyword = keyword; - tagEl.createSpan({ cls: "cm-tag-text", text: keyword }); + tagEl.createSpan({ cls: 'cm-tag-text', text: keyword }); const removeEl = tagEl.createSpan({ - cls: "cm-tag-remove", - text: "×", + cls: 'cm-tag-remove', + text: '×', }); - removeEl.addEventListener("click", (e) => { + removeEl.addEventListener('click', (e) => { e.stopPropagation(); keywords.splice(index, 1); - rule.keywords = keywords.join(","); + rule.keywords = keywords.join(','); renderTags(); }); }); - const inputEl = container.createEl("input", { - type: "text", - cls: "cm-tag-input-field", + const inputEl = container.createEl('input', { + type: 'text', + cls: 'cm-tag-input-field', }); - inputEl.placeholder = t("modals.noticeRules.keywordPlaceholder"); + inputEl.placeholder = t('modals.noticeRules.keywordPlaceholder'); const addKeywordFromInput = () => { - const newKeyword = inputEl.value.trim().replace(/,/g, ""); + const newKeyword = inputEl.value.trim().replace(/,/g, ''); if (!newKeyword) return; const keywords = - typeof rule.keywords === "string" && rule.keywords + typeof rule.keywords === 'string' && rule.keywords ? rule.keywords - .split(",") + .split(',') .map((k) => k.trim()) .filter(Boolean) : []; @@ -1544,44 +1457,42 @@ export class NoticeRulesModal extends ColorMasterBaseModal { const newKeywordLower = newKeyword.toLowerCase(); if (!keywordsLower.includes(newKeywordLower)) { - rule.keywords = [...keywords, newKeyword].join(","); + rule.keywords = [...keywords, newKeyword].join(','); renderTags(); } else { const existingTagEl = container.querySelector( - `.cm-tag-item[data-keyword="${ - keywords[keywordsLower.indexOf(newKeywordLower)] - }"]`, + `.cm-tag-item[data-keyword="${keywords[keywordsLower.indexOf(newKeywordLower)]}"]`, ); if (existingTagEl) { - existingTagEl.classList.add("cm-tag-duplicate-flash"); + existingTagEl.classList.add('cm-tag-duplicate-flash'); setTimeout(() => { - existingTagEl.classList.remove("cm-tag-duplicate-flash"); + existingTagEl.classList.remove('cm-tag-duplicate-flash'); }, 700); } - inputEl.value = ""; + inputEl.value = ''; } }; - inputEl.addEventListener("keydown", (e) => { - if (e.key === "Enter" || e.key === " ") { + inputEl.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); addKeywordFromInput(); - } else if (e.key === "Backspace" && inputEl.value === "") { + } else if (e.key === 'Backspace' && inputEl.value === '') { if (keywords.length > 0) { keywords.pop(); - rule.keywords = keywords.join(","); + rule.keywords = keywords.join(','); renderTags(); } } }); - inputEl.addEventListener("blur", addKeywordFromInput); + inputEl.addEventListener('blur', addKeywordFromInput); inputEl.focus(); }; - container.addEventListener("click", () => { - container.querySelector(".cm-tag-input-field")?.focus(); + container.addEventListener('click', () => { + container.querySelector('.cm-tag-input-field')?.focus(); }); renderTags(); @@ -1590,15 +1501,15 @@ export class NoticeRulesModal extends ColorMasterBaseModal { const ruleEl = this.rulesContainer.children[index]; if (!ruleEl) return; - ruleEl.classList.add("removing"); + ruleEl.classList.add('removing'); setTimeout(() => { if (this.localRules.length === 1) { this.localRules.splice(index, 1); const newRule: NoticeRule = { id: `rule-${Date.now()}`, - keywords: "", - color: this.ruleType === "text" ? "#ffffff" : "#444444", + keywords: '', + color: this.ruleType === 'text' ? '#ffffff' : '#444444', isRegex: false, highlightOnly: false, }; @@ -1611,18 +1522,18 @@ export class NoticeRulesModal extends ColorMasterBaseModal { }, 100); } _handleTestRule(rule: NoticeRule & { _lastTestIndex?: number }) { - const keywordsString = rule.keywords || ""; + const keywordsString = rule.keywords || ''; if (!keywordsString.trim()) { - new Notice(t("notices.noKeywordsToTest")); + new Notice(t('notices.noKeywordsToTest')); return; } const keywordsArray = keywordsString - .split(",") + .split(',') .map((k) => k.trim()) .filter(Boolean); if (keywordsArray.length === 0) { - new Notice(t("notices.noKeywordsToTest")); + new Notice(t('notices.noKeywordsToTest')); return; } if (rule._lastTestIndex === undefined || rule._lastTestIndex === null) { @@ -1635,18 +1546,18 @@ export class NoticeRulesModal extends ColorMasterBaseModal { const sequentialKeyword = keywordsArray[rule._lastTestIndex]; const fragment = new DocumentFragment(); - const text = t("notices.testSentence", sequentialKeyword).split( - new RegExp(`(${sequentialKeyword})`, "i"), + const text = t('notices.testSentence', sequentialKeyword).split( + new RegExp(`(${sequentialKeyword})`, 'i'), ); fragment.append(text[0]); const keywordSpan = fragment.createSpan({ - cls: "cm-test-keyword", + cls: 'cm-test-keyword', text: text[1], }); fragment.append(keywordSpan); - fragment.append(text[2] || ""); + fragment.append(text[2] || ''); new Notice(fragment); } } @@ -1663,12 +1574,12 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { }) => void; // Instance variables - varName: string = ""; - varValue: string = ""; - displayName: string = ""; - description: string = ""; - varType: CustomVarType = "color"; - sizeUnit: string = "px"; + varName: string = ''; + varValue: string = ''; + displayName: string = ''; + description: string = ''; + varType: CustomVarType = 'color'; + sizeUnit: string = 'px'; valueInputContainer: HTMLElement; constructor( @@ -1692,36 +1603,33 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { renderValueInput(container: HTMLElement) { container.empty(); // Clean up the old field const valueSetting = new Setting(container) - .setName(t("modals.customVar.varValue")) - .setDesc(t("modals.customVar.varValueDesc")); + .setName(t('modals.customVar.varValue')) + .setDesc(t('modals.customVar.varValueDesc')); switch (this.varType) { - case "color": { - if ( - this.varValue !== "" && - !this.varValue.match(/^(#|rgb|hsl|transparent|var\(--)/i) - ) { - this.varValue = ""; + case 'color': { + if (this.varValue !== '' && !this.varValue.match(/^(#|rgb|hsl|transparent|var\(--)/i)) { + this.varValue = ''; } - const colorPicker = valueSetting.controlEl.createEl("input", { - type: "color", + const colorPicker = valueSetting.controlEl.createEl('input', { + type: 'color', }); - const textInput = valueSetting.controlEl.createEl("input", { - type: "text", - cls: "color-master-text-input", + const textInput = valueSetting.controlEl.createEl('input', { + type: 'text', + cls: 'color-master-text-input', }); colorPicker.value = this.varValue; textInput.value = this.varValue; - colorPicker.addEventListener("input", (e) => { + colorPicker.addEventListener('input', (e) => { const newColor = (e.target as HTMLInputElement).value; textInput.value = newColor; this.varValue = newColor; }); - textInput.addEventListener("change", (e) => { + textInput.addEventListener('change', (e) => { const newColor = (e.target as HTMLInputElement).value; colorPicker.value = newColor; this.varValue = newColor; @@ -1730,48 +1638,46 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { break; } - case "size": { + case 'size': { // If the current value is not a size, return it to the default if (!this.varValue.match(/^(-?\d+)(\.\d+)?(px|em|rem|%)$/)) { - this.varValue = "10px"; + this.varValue = '10px'; } // Extract the number and unit const sizeMatch = this.varValue.match(/(-?\d+)(\.\d+)?(\D+)/); - let num = sizeMatch - ? (sizeMatch[1] || "") + (sizeMatch[2] || "") - : "10"; + let num = sizeMatch ? (sizeMatch[1] || '') + (sizeMatch[2] || '') : '10'; - let unit = sizeMatch ? sizeMatch[3] || "" : "px"; - if (!["px", "em", "rem", "%"].includes(unit)) unit = "px"; + let unit = sizeMatch ? sizeMatch[3] || '' : 'px'; + if (!['px', 'em', 'rem', '%'].includes(unit)) unit = 'px'; this.sizeUnit = unit; - const sizeInput = valueSetting.controlEl.createEl("input", { - type: "number", - cls: "color-master-text-input", + const sizeInput = valueSetting.controlEl.createEl('input', { + type: 'number', + cls: 'color-master-text-input', }); - sizeInput.setCssProps({ width: "80px" }); + sizeInput.setCssProps({ width: '80px' }); sizeInput.value = num; const unitDropdown = new DropdownComponent(valueSetting.controlEl); // eslint-disable-next-line obsidianmd/ui/sentence-case - unitDropdown.addOption("px", "px"); + unitDropdown.addOption('px', 'px'); // eslint-disable-next-line obsidianmd/ui/sentence-case - unitDropdown.addOption("em", "em"); + unitDropdown.addOption('em', 'em'); // eslint-disable-next-line obsidianmd/ui/sentence-case - unitDropdown.addOption("rem", "rem"); - unitDropdown.addOption("%", "%"); + unitDropdown.addOption('rem', 'rem'); + unitDropdown.addOption('%', '%'); unitDropdown.setValue(this.sizeUnit); const updateSizeValue = () => { - this.varValue = (sizeInput.value || "0") + this.sizeUnit; + this.varValue = (sizeInput.value || '0') + this.sizeUnit; }; - sizeInput.addEventListener("change", updateSizeValue); + sizeInput.addEventListener('change', updateSizeValue); unitDropdown.onChange((newUnit) => { this.sizeUnit = newUnit; @@ -1781,31 +1687,31 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { break; } - case "text": { + case 'text': { // Dump the value if the type is different - if (this.varType !== "text") this.varValue = ""; + if (this.varType !== 'text') this.varValue = ''; valueSetting.addTextArea((text) => { text .setValue(this.varValue) - .setPlaceholder(t("modals.customVar.textValuePlaceholder")) + .setPlaceholder(t('modals.customVar.textValuePlaceholder')) .onChange((value) => { this.varValue = value; }); - text.inputEl.setCssProps({ width: "100%" }); - text.inputEl.classList.add("cm-textarea-size"); + text.inputEl.setCssProps({ width: '100%' }); + text.inputEl.classList.add('cm-textarea-size'); }); break; } - case "number": { + case 'number': { // If the value is NaN, return it to 0 - if (isNaN(parseFloat(this.varValue))) this.varValue = "0"; + if (isNaN(parseFloat(this.varValue))) this.varValue = '0'; valueSetting.addText((text) => { - text.inputEl.type = "number"; + text.inputEl.type = 'number'; text.setValue(this.varValue).onChange((value) => { this.varValue = value; }); @@ -1819,19 +1725,19 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { onOpen() { const { contentEl } = this; contentEl.empty(); - this.modalEl.classList.add("color-master-modal"); + this.modalEl.classList.add('color-master-modal'); super.onOpen(); - contentEl.createEl("h3", { text: t("modals.customVar.title") }); - contentEl.createEl("p", { text: t("modals.customVar.desc") }); + contentEl.createEl('h3', { text: t('modals.customVar.title') }); + contentEl.createEl('p', { text: t('modals.customVar.desc') }); new Setting(contentEl) - .setName(t("modals.customVar.displayName")) - .setDesc(t("modals.customVar.displayNameDesc")) + .setName(t('modals.customVar.displayName')) + .setDesc(t('modals.customVar.displayNameDesc')) .addText((text) => text - .setPlaceholder(t("modals.customVar.displayNamePlaceholder")) + .setPlaceholder(t('modals.customVar.displayNamePlaceholder')) .setValue(this.displayName) .onChange((value) => { this.displayName = value; @@ -1839,18 +1745,18 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { ); new Setting(contentEl) - .setName(t("modals.customVar.varName")) - .setDesc(t("modals.customVar.varNameDesc")) + .setName(t('modals.customVar.varName')) + .setDesc(t('modals.customVar.varNameDesc')) .addText((text) => text - .setPlaceholder(t("modals.customVar.varNamePlaceholder")) + .setPlaceholder(t('modals.customVar.varNamePlaceholder')) .setValue(this.varName) .onChange((value) => { - if (value.length > 0 && !value.startsWith("--")) { - if (value.startsWith("-")) { - this.varName = "-" + value; + if (value.length > 0 && !value.startsWith('--')) { + if (value.startsWith('-')) { + this.varName = '-' + value; } else { - this.varName = "--" + value; + this.varName = '--' + value; } } else { this.varName = value; @@ -1859,46 +1765,46 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { ); new Setting(contentEl) - .setName(t("modals.customVar.varType")) - .setDesc(t("modals.customVar.varTypeDesc")) + .setName(t('modals.customVar.varType')) + .setDesc(t('modals.customVar.varTypeDesc')) .addDropdown((dropdown) => { dropdown - .addOption("color", t("modals.customVar.types.color")) - .addOption("size", t("modals.customVar.types.size")) - .addOption("text", t("modals.customVar.types.text")) - .addOption("number", t("modals.customVar.types.number")) + .addOption('color', t('modals.customVar.types.color')) + .addOption('size', t('modals.customVar.types.size')) + .addOption('text', t('modals.customVar.types.text')) + .addOption('number', t('modals.customVar.types.number')) .setValue(this.varType) .onChange((value: CustomVarType) => { this.varType = value; switch (value) { - case "color": - this.varValue = ""; + case 'color': + this.varValue = ''; break; - case "size": - this.varValue = "10px"; + case 'size': + this.varValue = '10px'; break; - case "number": - this.varValue = "0"; + case 'number': + this.varValue = '0'; break; - case "text": - this.varValue = ""; + case 'text': + this.varValue = ''; break; } this.renderValueInput(this.valueInputContainer); }); }); - this.valueInputContainer = contentEl.createDiv("cm-value-input-container"); + this.valueInputContainer = contentEl.createDiv('cm-value-input-container'); this.renderValueInput(this.valueInputContainer); // Description new Setting(contentEl) - .setName(t("modals.customVar.description")) - .setDesc(t("modals.customVar.descriptionDesc")) + .setName(t('modals.customVar.description')) + .setDesc(t('modals.customVar.descriptionDesc')) .addTextArea((text) => text - .setPlaceholder(t("modals.customVar.descriptionPlaceholder")) + .setPlaceholder(t('modals.customVar.descriptionPlaceholder')) .setValue(this.description) .onChange((value) => { this.description = value; @@ -1907,34 +1813,31 @@ export class CustomVariableMetaModal extends ColorMasterBaseModal { // Buttons new Setting(contentEl) - .setClass("modal-button-container") - .addButton((button) => - button.setButtonText(t("buttons.cancel")).onClick(() => this.close()), - ) + .setClass('modal-button-container') + .addButton((button) => button.setButtonText(t('buttons.cancel')).onClick(() => this.close())) .addButton((button) => button - .setButtonText(t("modals.customVar.addVarButton")) + .setButtonText(t('modals.customVar.addVarButton')) .setCta() .onClick(() => { const trimmedVarName = this.varName.trim(); - if (!trimmedVarName.startsWith("--")) { - new Notice(t("notices.varNameFormat")); + if (!trimmedVarName.startsWith('--')) { + new Notice(t('notices.varNameFormat')); return; } if (!this.displayName.trim()) { - new Notice(t("notices.varNameEmpty")); + new Notice(t('notices.varNameEmpty')); return; } const allDefaultVars = Object.keys(flattenVars(DEFAULT_VARS)); - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; const allProfileVars = Object.keys(activeProfile.vars || {}); const allVarNames = new Set([...allDefaultVars, ...allProfileVars]); if (allVarNames.has(trimmedVarName)) { - new Notice(t("notices.varExists", trimmedVarName)); + new Notice(t('notices.varExists', trimmedVarName)); return; } @@ -1964,26 +1867,24 @@ export class LanguageSettingsModal extends ColorMasterBaseModal { onOpen() { super.onOpen(); - this.modalEl.classList.add("color-master-modal"); + this.modalEl.classList.add('color-master-modal'); const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { - text: t("settings.languageSettingsModalTitle"), + contentEl.createEl('h3', { + text: t('settings.languageSettingsModalTitle'), }); new Setting(contentEl) - .setName(t("settings.rtlLayoutName")) - .setDesc(t("settings.rtlLayoutDesc")) + .setName(t('settings.rtlLayoutName')) + .setDesc(t('settings.rtlLayoutDesc')) .addToggle((toggle) => { - toggle - .setValue(this.plugin.settings.useRtlLayout) - .onChange(async (value) => { - this.plugin.settings.useRtlLayout = value; - await this.plugin.saveSettings(); - // Refresh the main settings tab to reflect the change - this.plugin.settingTabInstance?.display(); - }); + toggle.setValue(this.plugin.settings.useRtlLayout).onChange(async (value) => { + this.plugin.settings.useRtlLayout = value; + await this.plugin.saveSettings(); + // Refresh the main settings tab to reflect the change + this.plugin.settingTabInstance?.display(); + }); }); } @@ -2002,39 +1903,37 @@ export class IconizeSettingsModal extends ColorMasterBaseModal { onOpen() { super.onOpen(); - this.modalEl.classList.add("color-master-modal"); + this.modalEl.classList.add('color-master-modal'); const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("options.iconizeModalTitle") }); + contentEl.createEl('h3', { text: t('options.iconizeModalTitle') }); new Setting(contentEl) - .setName(t("options.overrideIconizeName")) - .setDesc(t("options.overrideIconizeDesc")) + .setName(t('options.overrideIconizeName')) + .setDesc(t('options.overrideIconizeDesc')) .addToggle((toggle) => { - toggle - .setValue(this.plugin.settings.overrideIconizeColors) - .onChange(async (value) => { - if (value) { - const iconizeIDs = ["obsidian-icon-folder", "iconize"]; - const pluginManager = (this.app as unknown).plugins; - const isIconizeInstalled = iconizeIDs.some( - (id: string) => !!pluginManager.getPlugin(id), - ); + toggle.setValue(this.plugin.settings.overrideIconizeColors).onChange(async (value) => { + if (value) { + const iconizeIDs = ['obsidian-icon-folder', 'iconize']; + const pluginManager = (this.app as unknown).plugins; + const isIconizeInstalled = iconizeIDs.some( + (id: string) => !!pluginManager.getPlugin(id), + ); - if (!isIconizeInstalled) { - new Notice(t("notices.iconizeNotFound")); - toggle.setValue(false); - return; - } + if (!isIconizeInstalled) { + new Notice(t('notices.iconizeNotFound')); + toggle.setValue(false); + return; } - this.plugin.settings.overrideIconizeColors = value; - await this.plugin.saveSettings(); - }); + } + this.plugin.settings.overrideIconizeColors = value; + await this.plugin.saveSettings(); + }); }); new Setting(contentEl) - .setName(t("options.cleanupIntervalName")) - .setDesc(t("options.cleanupIntervalDesc")) + .setName(t('options.cleanupIntervalName')) + .setDesc(t('options.cleanupIntervalDesc')) .addSlider((slider) => { slider .setLimits(1, 10, 1) @@ -2063,25 +1962,23 @@ export class BackgroundImageSettingsModal extends ColorMasterBaseModal { onOpen() { super.onOpen(); - this.modalEl.classList.add("color-master-modal"); + this.modalEl.classList.add('color-master-modal'); const { contentEl } = this; - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; contentEl.empty(); - contentEl.createEl("h3", { - text: t("options.backgroundModalSettingsTitle"), + contentEl.createEl('h3', { + text: t('options.backgroundModalSettingsTitle'), }); // --- Enable/Disable Toggle --- new Setting(contentEl) - .setName(t("options.backgroundEnableName")) - .setDesc(t("options.backgroundEnableDesc")) + .setName(t('options.backgroundEnableName')) + .setDesc(t('options.backgroundEnableDesc')) .addToggle((toggle) => { const isCurrentlyEnabled = activeProfile?.backgroundEnabled !== false; toggle.setValue(isCurrentlyEnabled).onChange(async (value) => { - const profile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const profile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if (profile) { profile.backgroundEnabled = value; await this.plugin.saveSettings(); @@ -2090,40 +1987,36 @@ export class BackgroundImageSettingsModal extends ColorMasterBaseModal { }); }); - const settingTypeSetting = new Setting(contentEl).setName( - t("options.settingType"), - ); + const settingTypeSetting = new Setting(contentEl).setName(t('options.settingType')); let imageButton: ButtonComponent; let videoButton: ButtonComponent; - const imageSettingsEl = contentEl.createDiv("cm-settings-group"); - imageSettingsEl.setCssProps({ display: "none" }); + const imageSettingsEl = contentEl.createDiv('cm-settings-group'); + imageSettingsEl.setCssProps({ display: 'none' }); - const videoSettingsEl = contentEl.createDiv("cm-settings-group"); - videoSettingsEl.setCssProps({ display: "none" }); + const videoSettingsEl = contentEl.createDiv('cm-settings-group'); + videoSettingsEl.setCssProps({ display: 'none' }); // --- Fill the Image Settings container --- new Setting(imageSettingsEl) - .setName(t("options.convertImagesName")) - .setDesc(t("options.convertImagesDesc")) + .setName(t('options.convertImagesName')) + .setDesc(t('options.convertImagesDesc')) .addToggle((toggle) => { - toggle - .setValue(activeProfile?.convertImagesToJpg || false) - .onChange(async (value) => { - activeProfile.convertImagesToJpg = value; - if (value && !activeProfile.jpgQuality) { - activeProfile.jpgQuality = 85; - } - await this.plugin.saveSettings(); - this.onOpen(); - }); + toggle.setValue(activeProfile?.convertImagesToJpg || false).onChange(async (value) => { + activeProfile.convertImagesToJpg = value; + if (value && !activeProfile.jpgQuality) { + activeProfile.jpgQuality = 85; + } + await this.plugin.saveSettings(); + this.onOpen(); + }); }); if (activeProfile?.convertImagesToJpg === true) { new Setting(imageSettingsEl) - .setName(t("options.jpgQualityName")) - .setDesc(t("options.jpgQualityDesc")) + .setName(t('options.jpgQualityName')) + .setDesc(t('options.jpgQualityDesc')) .addSlider((slider) => { slider .setLimits(1, 100, 1) @@ -2137,8 +2030,8 @@ export class BackgroundImageSettingsModal extends ColorMasterBaseModal { // --- Fill the video settings container (it is still hidden) --- new Setting(videoSettingsEl) - .setName(t("options.videoOpacityName")) - .setDesc(t("options.videoOpacityDesc")) + .setName(t('options.videoOpacityName')) + .setDesc(t('options.videoOpacityDesc')) .addSlider((slider) => { slider .setLimits(0.1, 1, 0.1) @@ -2151,9 +2044,7 @@ export class BackgroundImageSettingsModal extends ColorMasterBaseModal { slider.sliderEl.oninput = (e) => { const value = parseFloat((e.target as HTMLInputElement).value); - const videoEl = document.querySelector( - "#cm-background-video", - ); + const videoEl = document.querySelector('#cm-background-video'); if (videoEl) { videoEl.setCssProps({ opacity: value.toString() }); } @@ -2161,69 +2052,60 @@ export class BackgroundImageSettingsModal extends ColorMasterBaseModal { }); new Setting(videoSettingsEl) - .setName(t("options.videoMuteName")) - .setDesc(t("options.videoMuteDesc")) + .setName(t('options.videoMuteName')) + .setDesc(t('options.videoMuteDesc')) .addToggle((toggle) => - toggle - .setValue(activeProfile.videoMuted !== false) - .onChange(async (value) => { - activeProfile.videoMuted = value; - await this.plugin.saveSettings(); + toggle.setValue(activeProfile.videoMuted !== false).onChange(async (value) => { + activeProfile.videoMuted = value; + await this.plugin.saveSettings(); - const videoEl = document.querySelector( - "#cm-background-video", - ); - if (videoEl) { - videoEl.muted = value; - } - }), + const videoEl = document.querySelector('#cm-background-video'); + if (videoEl) { + videoEl.muted = value; + } + }), ); // --- Attaching buttons to containers --- - const setActiveButton = (active: "image" | "video") => { - if (active === "image") { + const setActiveButton = (active: 'image' | 'video') => { + if (active === 'image') { imageButton.setCta(); - videoButton.buttonEl.classList.remove("mod-cta"); - imageSettingsEl.setCssProps({ display: "block" }); - videoSettingsEl.setCssProps({ display: "none" }); + videoButton.buttonEl.classList.remove('mod-cta'); + imageSettingsEl.setCssProps({ display: 'block' }); + videoSettingsEl.setCssProps({ display: 'none' }); } else { - imageButton.buttonEl.classList.remove("mod-cta"); + imageButton.buttonEl.classList.remove('mod-cta'); videoButton.setCta(); - imageSettingsEl.setCssProps({ display: "none" }); - videoSettingsEl.setCssProps({ display: "block" }); + imageSettingsEl.setCssProps({ display: 'none' }); + videoSettingsEl.setCssProps({ display: 'block' }); } }; settingTypeSetting.addButton((button) => { imageButton = button; - button - .setButtonText(t("options.settingTypeImage")) - .onClick(() => setActiveButton("image")); + button.setButtonText(t('options.settingTypeImage')).onClick(() => setActiveButton('image')); }); settingTypeSetting.addButton((button) => { videoButton = button; - button - .setButtonText(t("options.settingTypeVideo")) - .onClick(() => setActiveButton("video")); + button.setButtonText(t('options.settingTypeVideo')).onClick(() => setActiveButton('video')); }); - const currentType = activeProfile?.backgroundType || "image"; + const currentType = activeProfile?.backgroundType || 'image'; setActiveButton(currentType); } debouncedSaveSettings = debounce((value: number) => { void (async () => { - const profile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const profile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if (profile) { profile.jpgQuality = value; await this.plugin.saveSettings(); - new Notice(t("notices.jpgQualitySet", value)); + new Notice(t('notices.jpgQualitySet', value)); } })().catch((err) => { - console.error("Failed to save JPG quality:", err); + console.error('Failed to save JPG quality:', err); }); }, 0); @@ -2252,14 +2134,14 @@ export class FileConflictModal extends ColorMasterBaseModal { plugin: ColorMaster; arrayBuffer: ArrayBuffer; // The image data to save fileName: string; // The conflicting filename - onResolve: (choice: "replace" | "keep") => void; // Callback with user's choice + onResolve: (choice: 'replace' | 'keep') => void; // Callback with user's choice constructor( app: App, plugin: ColorMaster, arrayBuffer: ArrayBuffer, fileName: string, - onResolve: (choice: "replace" | "keep") => void, + onResolve: (choice: 'replace' | 'keep') => void, ) { super(app, plugin); this.arrayBuffer = arrayBuffer; @@ -2272,30 +2154,30 @@ export class FileConflictModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("modals.fileConflict.title") }); - contentEl.createEl("p", { - text: t("modals.fileConflict.desc", this.fileName), + contentEl.createEl('h3', { text: t('modals.fileConflict.title') }); + contentEl.createEl('p', { + text: t('modals.fileConflict.desc', this.fileName), }); const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); // Replace Button new ButtonComponent(buttonContainer) - .setButtonText(t("modals.fileConflict.replaceButton")) + .setButtonText(t('modals.fileConflict.replaceButton')) .onClick(() => { - this.onResolve("replace"); + this.onResolve('replace'); this.close(); }); // Keep Both Button new ButtonComponent(buttonContainer) - .setButtonText(t("modals.fileConflict.keepButton")) + .setButtonText(t('modals.fileConflict.keepButton')) .setCta() .onClick(() => { - this.onResolve("keep"); + this.onResolve('keep'); this.close(); }); } @@ -2310,99 +2192,92 @@ export class AddBackgroundModal extends ColorMasterBaseModal { plugin: ColorMaster; settingTab: ColorMasterSettingTab; - constructor( - app: App, - plugin: ColorMaster, - settingTab: ColorMasterSettingTab, - ) { + constructor(app: App, plugin: ColorMaster, settingTab: ColorMasterSettingTab) { super(app, plugin); this.settingTab = settingTab; } // Process pasted image files async handlePastedFile(file: File) { - new Notice(t("notices.pastedImage", file.name)); + new Notice(t('notices.pastedImage', file.name)); await this.processFileWithProgress(file); } // Process pasted URLs (http/https or data URLs) async handlePastedUrl(url: string) { - const pasteBox = this.contentEl.querySelector(".cm-paste-box"); + const pasteBox = this.contentEl.querySelector('.cm-paste-box'); // Handle data URLs directly - if (url.startsWith("data:image")) { + if (url.startsWith('data:image')) { try { if (pasteBox) { - pasteBox.textContent = t("modals.addBackground.processing") + "..."; + pasteBox.textContent = t('modals.addBackground.processing') + '...'; } const response = await requestUrl({ url }); const arrayBuffer = response.arrayBuffer; - const contentType = response.headers["content-type"] || "image/png"; - const fileName = `pasted-image-${Date.now()}.${ - contentType.split("/")[1] - }`; - new Notice(t("notices.pastedBase64Image")); + const contentType = response.headers['content-type'] || 'image/png'; + const fileName = `pasted-image-${Date.now()}.${contentType.split('/')[1]}`; + new Notice(t('notices.pastedBase64Image')); - await this.plugin.setBackgroundMedia(arrayBuffer, fileName, "prompt"); + await this.plugin.setBackgroundMedia(arrayBuffer, fileName, 'prompt'); this.close(); return; // Exit after handling } catch (error) { - new Notice(t("notices.backgroundLoadError")); - console.error("Color Master: Error handling pasted data URL:", error); + new Notice(t('notices.backgroundLoadError')); + console.error('Color Master: Error handling pasted data URL:', error); this.close(); return; // Exit on error } } // Handle regular URLs - if (url.startsWith("http://") || url.startsWith("https://")) { - new Notice(t("notices.downloadingFromUrl", url)); + if (url.startsWith('http://') || url.startsWith('https://')) { + new Notice(t('notices.downloadingFromUrl', url)); if (pasteBox) { // Show processing (no percentage) - pasteBox.textContent = t("modals.addBackground.processing") + "..."; + pasteBox.textContent = t('modals.addBackground.processing') + '...'; } await this.plugin.setBackgroundMediaFromUrl(url); // Use dedicated function this.close(); return; // Exit after handling } // If neither type matched - new Notice(t("notices.pasteError")); + new Notice(t('notices.pasteError')); } // Read file as ArrayBuffer, update progress, and call setBackgroundImage async processFileWithProgress(file: File) { await Promise.resolve(); const reader = new FileReader(); - const pasteBox = this.contentEl.querySelector(".cm-paste-box"); + const pasteBox = this.contentEl.querySelector('.cm-paste-box'); // Update progress text in paste box reader.onprogress = (event) => { if (event.lengthComputable && pasteBox) { const percent = Math.round((event.loaded / event.total) * 100); - pasteBox.textContent = - t("modals.addBackground.processing") + `${percent}%`; + pasteBox.textContent = t('modals.addBackground.processing') + `${percent}%`; } }; // On successful load, pass data to main plugin function reader.onload = async () => { if (pasteBox) { - pasteBox.textContent = t("modals.addBackground.processing") + " 100%"; + pasteBox.textContent = t('modals.addBackground.processing') + ' 100%'; } const arrayBuffer = reader.result as ArrayBuffer; - await this.plugin.setBackgroundMedia(arrayBuffer, file.name, "prompt"); + await this.plugin.setBackgroundMedia(arrayBuffer, file.name, 'prompt'); this.close(); }; // Handle read errors reader.onerror = () => { - new Notice(t("notices.backgroundLoadError")); + new Notice(t('notices.backgroundLoadError')); this.close(); }; // Start reading the file if (pasteBox) { - pasteBox.textContent = t("modals.addBackground.processing") + "0%"; + pasteBox.textContent = t('modals.addBackground.processing') + '0%'; } reader.readAsArrayBuffer(file); } @@ -2412,21 +2287,21 @@ export class AddBackgroundModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("modals.addBackground.title") }); + contentEl.createEl('h3', { text: t('modals.addBackground.title') }); // --- File Import Button --- new Setting(contentEl) - .setName(t("modals.addBackground.importFromFile")) - .setDesc(t("modals.addBackground.importFromFileDesc")) + .setName(t('modals.addBackground.importFromFile')) + .setDesc(t('modals.addBackground.importFromFileDesc')) .addButton((button) => { // Hidden input element to handle file selection - const input = document.createElement("input"); - input.type = "file"; - input.accept = "image/*, video/mp4, video/webm"; + const input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/*, video/mp4, video/webm'; button .setCta() - .setButtonText(t("buttons.chooseFile")) + .setButtonText(t('buttons.chooseFile')) .onClick(() => { input.click(); }); @@ -2439,16 +2314,16 @@ export class AddBackgroundModal extends ColorMasterBaseModal { }; }); - contentEl.createEl("hr"); + contentEl.createEl('hr'); // --- Paste Box (URL or Image via Paste/DragDrop) --- const pasteBox = contentEl.createDiv({ - cls: "cm-paste-box", - text: t("modals.addBackground.pasteBoxPlaceholder"), + cls: 'cm-paste-box', + text: t('modals.addBackground.pasteBoxPlaceholder'), }); - pasteBox.setAttribute("contenteditable", "true"); + pasteBox.setAttribute('contenteditable', 'true'); - pasteBox.addEventListener("paste", (event: ClipboardEvent) => { + pasteBox.addEventListener('paste', (event: ClipboardEvent) => { event.preventDefault(); const clipboardData = event.clipboardData; @@ -2457,54 +2332,54 @@ export class AddBackgroundModal extends ColorMasterBaseModal { // Check for pasted files first if (clipboardData.files && clipboardData.files.length > 0) { const file = clipboardData.files[0]; - if (file.type.startsWith("image/") || file.type.startsWith("video/")) { + if (file.type.startsWith('image/') || file.type.startsWith('video/')) { void this.handlePastedFile(file).catch((err) => { - console.error("Failed to handle pasted file:", err); + console.error('Failed to handle pasted file:', err); }); return; } } // Check for pasted text (URLs) - const pastedText = clipboardData.getData("text/plain"); + const pastedText = clipboardData.getData('text/plain'); if ( pastedText && - (pastedText.startsWith("http://") || - pastedText.startsWith("https://") || - pastedText.startsWith("data:image")) + (pastedText.startsWith('http://') || + pastedText.startsWith('https://') || + pastedText.startsWith('data:image')) ) { void this.handlePastedUrl(pastedText).catch((err) => { - console.error("Failed to handle pasted URL:", err); + console.error('Failed to handle pasted URL:', err); }); return; } // If neither worked - new Notice(t("notices.backgroundPasteError")); + new Notice(t('notices.backgroundPasteError')); }); // --- Drag and Drop Listeners --- - pasteBox.addEventListener("dragover", (event: DragEvent) => { + pasteBox.addEventListener('dragover', (event: DragEvent) => { event.preventDefault(); // Required to allow drop - pasteBox.classList.add("is-over"); - pasteBox.textContent = t("modals.addBackground.dropToAdd"); + pasteBox.classList.add('is-over'); + pasteBox.textContent = t('modals.addBackground.dropToAdd'); }); - pasteBox.addEventListener("dragleave", () => { - pasteBox.classList.remove("is-over"); - pasteBox.textContent = t("modals.addBackground.pasteBoxPlaceholder"); + pasteBox.addEventListener('dragleave', () => { + pasteBox.classList.remove('is-over'); + pasteBox.textContent = t('modals.addBackground.pasteBoxPlaceholder'); }); - pasteBox.addEventListener("drop", (event: DragEvent) => { + pasteBox.addEventListener('drop', (event: DragEvent) => { event.preventDefault(); // Prevent default browser file handling - pasteBox.classList.remove("is-over"); - pasteBox.textContent = t("modals.addBackground.pasteBoxPlaceholder"); + pasteBox.classList.remove('is-over'); + pasteBox.textContent = t('modals.addBackground.pasteBoxPlaceholder'); if (!event.dataTransfer) return; // Check for dropped files first if (event.dataTransfer.files && event.dataTransfer.files.length > 0) { const file = event.dataTransfer.files[0]; - if (file.type.startsWith("image/")) { + if (file.type.startsWith('image/')) { void this.handlePastedFile(file); return; } @@ -2512,14 +2387,13 @@ export class AddBackgroundModal extends ColorMasterBaseModal { // Check for dropped URLs (less common, but possible) const url = - event.dataTransfer.getData("text/uri-list") || - event.dataTransfer.getData("text/plain"); - if (url && (url.startsWith("http") || url.startsWith("data:image"))) { + event.dataTransfer.getData('text/uri-list') || event.dataTransfer.getData('text/plain'); + if (url && (url.startsWith('http') || url.startsWith('data:image'))) { void this.handlePastedUrl(url); return; } // If neither worked - new Notice(t("notices.backgroundPasteError")); + new Notice(t('notices.backgroundPasteError')); }); } @@ -2552,13 +2426,13 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); this.videoPlayers = []; - contentEl.createEl("h3", { text: t("modals.backgroundBrowser.title") }); + contentEl.createEl('h3', { text: t('modals.backgroundBrowser.title') }); - this.galleryEl = contentEl.createDiv({ cls: "cm-image-gallery" }); + this.galleryEl = contentEl.createDiv({ cls: 'cm-image-gallery' }); // run async tasks safely void this._renderImages().catch((err) => { - console.error("Failed to render images:", err); + console.error('Failed to render images:', err); }); } @@ -2570,16 +2444,7 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { async displayImages() { this.galleryEl.empty(); const backgroundsPath = `${this.app.vault.configDir}/backgrounds`; - const mediaExtensions = [ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".webp", - ".svg", - ".mp4", - ".webm", - ]; + const mediaExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.mp4', '.webm']; let files: string[] = []; try { @@ -2588,7 +2453,7 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { files = list.files; } } catch (e) { - console.warn("Color Master: Error listing background folder.", e); + console.warn('Color Master: Error listing background folder.', e); } const mediaFiles = files.filter((path) => @@ -2597,161 +2462,151 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { if (mediaFiles.length === 0) { this.galleryEl.createDiv({ - cls: "cm-image-browser-empty", - text: t("modals.backgroundBrowser.noImages"), + cls: 'cm-image-browser-empty', + text: t('modals.backgroundBrowser.noImages'), }); return; } - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; const activeMediaPath = activeProfile?.backgroundPath; // Optimization: Helper to parse filenames (defined once outside the loop) const splitName = (fullFileName: string) => { - const decoded = decodeURIComponent(fullFileName || ""); - const lastDot = decoded.lastIndexOf("."); + const decoded = decodeURIComponent(fullFileName || ''); + const lastDot = decoded.lastIndexOf('.'); if (lastDot > 0 && lastDot < decoded.length - 1) { return { basename: decoded.substring(0, lastDot), ext: decoded.substring(lastDot), }; } - return { basename: decoded, ext: "" }; + return { basename: decoded, ext: '' }; }; // Optimization: Use DocumentFragment to minimize DOM reflows const fragment = document.createDocumentFragment(); for (const mediaPath of mediaFiles) { - const cardEl = document.createElement("div"); - cardEl.className = "cm-image-card"; - if (mediaPath === activeMediaPath) cardEl.classList.add("is-active"); + const cardEl = document.createElement('div'); + cardEl.className = 'cm-image-card'; + if (mediaPath === activeMediaPath) cardEl.classList.add('is-active'); const mediaUrl = this.app.vault.adapter.getResourcePath(mediaPath); - const fileName = mediaPath.split("/").pop(); + const fileName = mediaPath.split('/').pop(); const isVideo = - mediaPath.toLowerCase().endsWith(".mp4") || - mediaPath.toLowerCase().endsWith(".webm"); + mediaPath.toLowerCase().endsWith('.mp4') || mediaPath.toLowerCase().endsWith('.webm'); // --- Media Preview Section --- const previewContainer = cardEl.createDiv({ - cls: "cm-media-preview-container", + cls: 'cm-media-preview-container', }); if (isVideo) { - const videoEl = previewContainer.createEl("video", { - cls: "cm-image-card-preview", + const videoEl = previewContainer.createEl('video', { + cls: 'cm-image-card-preview', attr: { src: mediaUrl, muted: true, loop: true, playsinline: true, - "data-path": mediaPath, + 'data-path': mediaPath, }, }); this.videoPlayers.push(videoEl); const playOverlay = previewContainer.createDiv({ - cls: "cm-media-play-overlay", + cls: 'cm-media-play-overlay', }); - setIcon(playOverlay, "play"); + setIcon(playOverlay, 'play'); const muteButton = previewContainer.createDiv({ - cls: "cm-media-mute-toggle", + cls: 'cm-media-mute-toggle', }); const updateMuteIcon = () => { - setIcon(muteButton, videoEl.muted ? "volume-x" : "volume-2"); - muteButton.setCssProps({ opacity: "0" }); + setIcon(muteButton, videoEl.muted ? 'volume-x' : 'volume-2'); + muteButton.setCssProps({ opacity: '0' }); }; updateMuteIcon(); // Mute Toggle Logic - muteButton.addEventListener("click", (e) => { + muteButton.addEventListener('click', (e) => { e.stopPropagation(); videoEl.muted = !videoEl.muted; updateMuteIcon(); muteButton.setCssProps({ - opacity: videoEl.muted ? "0.8" : "1", + opacity: videoEl.muted ? '0.8' : '1', }); }); // Video Play/Pause & Exclusive Play Logic - previewContainer.addEventListener("click", () => { + previewContainer.addEventListener('click', () => { // Pause other playing videos for (const player of this.videoPlayers) { if (player !== videoEl && !player.paused) { player.pause(); - const container = player.closest( - ".cm-media-preview-container", - ); + const container = player.closest('.cm-media-preview-container'); if (container) { - const playOverlay = container.querySelector( - ".cm-media-play-overlay", - ); - const muteToggle = container.querySelector( - ".cm-media-mute-toggle", - ); + const playOverlay = container.querySelector('.cm-media-play-overlay'); + const muteToggle = container.querySelector('.cm-media-mute-toggle'); - playOverlay?.setCssProps({ opacity: "1" }); - muteToggle?.setCssProps({ opacity: "0" }); + playOverlay?.setCssProps({ opacity: '1' }); + muteToggle?.setCssProps({ opacity: '0' }); } } } if (videoEl.paused) { void videoEl.play().catch((err) => { - console.error("Failed to play video:", err); + console.error('Failed to play video:', err); }); - playOverlay.setCssProps({ opacity: "0" }); + playOverlay.setCssProps({ opacity: '0' }); muteButton.setCssProps({ - opacity: videoEl.muted ? "0.8" : "1", + opacity: videoEl.muted ? '0.8' : '1', }); } else { - muteButton.setCssProps({ opacity: "0" }); + muteButton.setCssProps({ opacity: '0' }); videoEl.pause(); - playOverlay.setCssProps({ opacity: "1" }); + playOverlay.setCssProps({ opacity: '1' }); } }); } else { - previewContainer.createEl("img", { - cls: "cm-image-card-preview", - attr: { src: mediaUrl, "data-path": mediaPath }, + previewContainer.createEl('img', { + cls: 'cm-image-card-preview', + attr: { src: mediaUrl, 'data-path': mediaPath }, }); } // --- Rename Input Section --- const nameSettingEl = cardEl.createDiv({ - cls: "setting-item cm-image-card-name-input", + cls: 'setting-item cm-image-card-name-input', }); const nameControlEl = nameSettingEl.createDiv({ - cls: "setting-item-control", + cls: 'setting-item-control', }); const nameInputContainer = nameControlEl.createDiv({ - cls: "cm-name-input-container", + cls: 'cm-name-input-container', }); - const nameInput = nameInputContainer.createEl("input", { - type: "text", - cls: "cm-name-input-basename", + const nameInput = nameInputContainer.createEl('input', { + type: 'text', + cls: 'cm-name-input-basename', }); const extensionSpan = nameInputContainer.createSpan({ - cls: "cm-name-input-extension", + cls: 'cm-name-input-extension', }); // State tracking for rename logic let currentImagePath = mediaPath; - let currentFileName = fileName || ""; + let currentFileName = fileName || ''; let { basename, ext } = splitName(currentFileName); nameInput.value = basename; extensionSpan.setText(ext); - nameInput.addEventListener("focus", (e) => - (e.target as HTMLInputElement).select(), - ); + nameInput.addEventListener('focus', (e) => (e.target as HTMLInputElement).select()); const saveName = async () => { const newBasename = nameInput.value.trim(); @@ -2764,30 +2619,21 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { newFullName, ); - if (renameResult && typeof renameResult === "string") { + if (renameResult && typeof renameResult === 'string') { // Update local state upon success currentImagePath = renameResult; - currentFileName = renameResult.split("/").pop() || ""; + currentFileName = renameResult.split('/').pop() || ''; const updatedSplit = splitName(currentFileName); basename = updatedSplit.basename; // Partial UI update to avoid full reload - const imgEl = cardEl.querySelector( - ".cm-image-card-preview", - ); - const selectBtn = cardEl.querySelector( - ".cm-image-card-select-btn", - ); - const deleteBtn = cardEl.querySelector( - ".cm-image-card-delete-btn", - ); + const imgEl = cardEl.querySelector('.cm-image-card-preview'); + const selectBtn = cardEl.querySelector('.cm-image-card-select-btn'); + const deleteBtn = cardEl.querySelector('.cm-image-card-delete-btn'); - if (imgEl) - imgEl.src = this.app.vault.adapter.getResourcePath(renameResult); - if (selectBtn) - selectBtn.onclick = () => this.selectMedia(renameResult); - if (deleteBtn) - deleteBtn.onclick = () => this.deleteMedia(renameResult, cardEl); + if (imgEl) imgEl.src = this.app.vault.adapter.getResourcePath(renameResult); + if (selectBtn) selectBtn.onclick = () => this.selectMedia(renameResult); + if (deleteBtn) deleteBtn.onclick = () => this.deleteMedia(renameResult, cardEl); } else { nameInput.value = currentBase; // Revert on failure } @@ -2796,32 +2642,32 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { } }; - nameInput.addEventListener("keydown", (e) => { - if (e.key === "Enter") { + nameInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { e.preventDefault(); nameInput.blur(); // Triggers focusout } }); - nameInput.addEventListener("focusout", () => { + nameInput.addEventListener('focusout', () => { void saveName().catch((err) => { - console.error("Failed to save snippet name:", err); + console.error('Failed to save snippet name:', err); }); }); // --- Actions (Select / Delete) --- - const controlsEl = cardEl.createDiv({ cls: "cm-image-card-controls" }); + const controlsEl = cardEl.createDiv({ cls: 'cm-image-card-controls' }); const selectButton = new ButtonComponent(controlsEl) - .setButtonText(t("buttons.select")) + .setButtonText(t('buttons.select')) .setCta() .onClick(() => this.selectMedia(currentImagePath)); - selectButton.buttonEl.classList.add("cm-image-card-select-btn"); + selectButton.buttonEl.classList.add('cm-image-card-select-btn'); const deleteButton = new ButtonComponent(controlsEl) - .setIcon("trash") - .setClass("mod-warning") + .setIcon('trash') + .setClass('mod-warning') .onClick(() => this.deleteMedia(currentImagePath, cardEl)); - deleteButton.buttonEl.classList.add("cm-image-card-delete-btn"); + deleteButton.buttonEl.classList.add('cm-image-card-delete-btn'); fragment.appendChild(cardEl); } @@ -2832,9 +2678,9 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { async selectMedia(path: string) { // Infer media type from extension to ensure correct rendering tag (img vs video) - const fileExt = path.split(".").pop()?.toLowerCase(); - const mediaType: "image" | "video" = - fileExt === "mp4" || fileExt === "webm" ? "video" : "image"; + const fileExt = path.split('.').pop()?.toLowerCase(); + const mediaType: 'image' | 'video' = + fileExt === 'mp4' || fileExt === 'webm' ? 'video' : 'image'; await this.plugin.selectBackgroundMedia(path, mediaType); @@ -2852,45 +2698,45 @@ export class ProfileImageBrowserModal extends ColorMasterBaseModal { // Construct warning UI with list of affected profiles const messageFragment = new DocumentFragment(); - messageFragment.append(t("modals.confirmation.deleteGlobalBgDesc")); + messageFragment.append(t('modals.confirmation.deleteGlobalBgDesc')); if (affectedProfiles.length > 0) { - const listEl = messageFragment.createEl("ul", { - cls: "cm-profile-list-modal", + const listEl = messageFragment.createEl('ul', { + cls: 'cm-profile-list-modal', }); affectedProfiles.forEach((name) => { - listEl.createEl("li").createEl("strong", { text: name }); + listEl.createEl('li').createEl('strong', { text: name }); }); } new ConfirmationModal( this.app, this.plugin, - t("modals.confirmation.deleteBackgroundTitle"), + t('modals.confirmation.deleteBackgroundTitle'), messageFragment, () => { void (async () => { // Perform deletion await this.plugin.removeBackgroundMediaByPath(path); - new Notice(t("notices.bgDeleted")); + new Notice(t('notices.bgDeleted')); // Update browser UI cardEl.remove(); if (this.galleryEl.childElementCount === 0) { this.galleryEl.createDiv({ - cls: "cm-image-browser-empty", - text: t("modals.backgroundBrowser.noImages"), + cls: 'cm-image-browser-empty', + text: t('modals.backgroundBrowser.noImages'), }); } // Sync main settings tab this.settingTab.display(); })().catch((err) => { - console.error("Failed to delete background:", err); + console.error('Failed to delete background:', err); }); }, - { buttonText: t("buttons.deleteAnyway"), buttonClass: "mod-warning" }, + { buttonText: t('buttons.deleteAnyway'), buttonClass: 'mod-warning' }, ).open(); } @@ -2938,15 +2784,15 @@ export class AdvancedResetModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("modals.advancedReset.title") }); - contentEl.createEl("p", { text: t("modals.advancedReset.desc") }); - contentEl.addClass("cm-advanced-reset-options"); + contentEl.createEl('h3', { text: t('modals.advancedReset.title') }); + contentEl.createEl('p', { text: t('modals.advancedReset.desc') }); + contentEl.addClass('cm-advanced-reset-options'); // 1. Profiles & Snapshots new Setting(contentEl) - .setName(t("modals.advancedReset.profilesLabel")) + .setName(t('modals.advancedReset.profilesLabel')) .addExtraButton((btn) => { - btn.setIcon("info").setTooltip(t("modals.advancedReset.profilesDesc")); + btn.setIcon('info').setTooltip(t('modals.advancedReset.profilesDesc')); }) .addToggle((toggle) => { toggle.setValue(this.resetOptions.deleteProfiles).onChange((value) => { @@ -2958,9 +2804,9 @@ export class AdvancedResetModal extends ColorMasterBaseModal { // 2. Snippets new Setting(contentEl) - .setName(t("modals.advancedReset.snippetsLabel")) + .setName(t('modals.advancedReset.snippetsLabel')) .addExtraButton((btn) => { - btn.setIcon("info").setTooltip(t("modals.advancedReset.snippetsDesc")); + btn.setIcon('info').setTooltip(t('modals.advancedReset.snippetsDesc')); }) .addToggle((toggle) => { toggle.setValue(this.resetOptions.deleteSnippets).onChange((value) => { @@ -2972,9 +2818,9 @@ export class AdvancedResetModal extends ColorMasterBaseModal { // 3. Plugin Settings new Setting(contentEl) - .setName(t("modals.advancedReset.settingsLabel")) + .setName(t('modals.advancedReset.settingsLabel')) .addExtraButton((btn) => { - btn.setIcon("info").setTooltip(t("modals.advancedReset.settingsDesc")); + btn.setIcon('info').setTooltip(t('modals.advancedReset.settingsDesc')); }) .addToggle((toggle) => { toggle.setValue(this.resetOptions.deleteSettings).onChange((value) => { @@ -2986,56 +2832,50 @@ export class AdvancedResetModal extends ColorMasterBaseModal { // 4. Backgrounds Folder new Setting(contentEl) - .setName(t("modals.advancedReset.backgroundsLabel")) + .setName(t('modals.advancedReset.backgroundsLabel')) .addExtraButton((btn) => { - btn - .setIcon("info") - .setTooltip(t("modals.advancedReset.backgroundsDesc")); + btn.setIcon('info').setTooltip(t('modals.advancedReset.backgroundsDesc')); }) .addToggle((toggle) => { - toggle - .setValue(this.resetOptions.deleteBackgrounds) - .onChange((value) => { - this.resetOptions.deleteBackgrounds = value; - this.validateButton(); - }); + toggle.setValue(this.resetOptions.deleteBackgrounds).onChange((value) => { + this.resetOptions.deleteBackgrounds = value; + this.validateButton(); + }); toggle.toggleEl.blur(); }); // 5. Custom Languages (Handles optional type safely) new Setting(contentEl) - .setName(t("modals.advancedReset.languagesLabel")) + .setName(t('modals.advancedReset.languagesLabel')) .addExtraButton((btn) => { - btn.setIcon("info").setTooltip(t("modals.advancedReset.languagesDesc")); + btn.setIcon('info').setTooltip(t('modals.advancedReset.languagesDesc')); }) .addToggle((toggle) => { - toggle - .setValue(this.resetOptions.deleteLanguages ?? false) - .onChange((value) => { - this.resetOptions.deleteLanguages = value; - this.validateButton(); - }); + toggle.setValue(this.resetOptions.deleteLanguages ?? false).onChange((value) => { + this.resetOptions.deleteLanguages = value; + this.validateButton(); + }); toggle.toggleEl.blur(); }); // --- Action Buttons --- const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); new ButtonComponent(buttonContainer) - .setButtonText(t("buttons.cancel")) + .setButtonText(t('buttons.cancel')) .onClick(() => this.close()); this.deleteButton = new ButtonComponent(buttonContainer) - .setButtonText(t("buttons.delete")) + .setButtonText(t('buttons.delete')) .setWarning() .onClick(() => { if (Object.values(this.resetOptions).every((v) => v === false)) return; this.plugin.settings.advancedResetOptions = this.resetOptions; void this.plugin.resetPluginData(this.resetOptions).catch((err) => { - console.error("Failed to reset plugin data:", err); + console.error('Failed to reset plugin data:', err); }); this.close(); @@ -3045,13 +2885,9 @@ export class AdvancedResetModal extends ColorMasterBaseModal { } validateButton() { - const hasSelection = Object.values(this.resetOptions).some( - (v) => v === true, - ); + const hasSelection = Object.values(this.resetOptions).some((v) => v === true); this.deleteButton.setDisabled(!hasSelection); - this.deleteButton.setButtonText( - hasSelection ? t("buttons.delete") : t("buttons.selectOption"), - ); + this.deleteButton.setButtonText(hasSelection ? t('buttons.delete') : t('buttons.selectOption')); } onClose() { @@ -3064,15 +2900,11 @@ export class AdvancedResetModal extends ColorMasterBaseModal { */ export class AddNewLanguageModal extends ColorMasterBaseModal { settingTab: ColorMasterSettingTab; - langName: string = ""; - langCode: string = ""; + langName: string = ''; + langCode: string = ''; isRtl: boolean = false; - constructor( - app: App, - plugin: ColorMaster, - settingTab: ColorMasterSettingTab, - ) { + constructor(app: App, plugin: ColorMaster, settingTab: ColorMasterSettingTab) { super(app, plugin); this.settingTab = settingTab; } @@ -3082,44 +2914,40 @@ export class AddNewLanguageModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); - contentEl.createEl("h3", { text: t("modals.addLang.title") }); - contentEl.createEl("p", { text: t("modals.addLang.desc") }); + contentEl.createEl('h3', { text: t('modals.addLang.title') }); + contentEl.createEl('p', { text: t('modals.addLang.desc') }); // 1. Language Name Input new Setting(contentEl) - .setName(t("modals.addLang.nameLabel")) - .setDesc(t("modals.addLang.nameDesc")) + .setName(t('modals.addLang.nameLabel')) + .setDesc(t('modals.addLang.nameDesc')) .addText((text) => { - text - .setPlaceholder(t("modals.addLang.namePlaceholder")) - .onChange((value) => { - this.langName = value.trim(); - }); + text.setPlaceholder(t('modals.addLang.namePlaceholder')).onChange((value) => { + this.langName = value.trim(); + }); }); // 2. Language Code Input (Sanitized) new Setting(contentEl) - .setName(t("modals.addLang.codeLabel")) - .setDesc(t("modals.addLang.codeDesc")) + .setName(t('modals.addLang.codeLabel')) + .setDesc(t('modals.addLang.codeDesc')) .addText((text) => { - text - .setPlaceholder(t("modals.addLang.codePlaceholder")) - .onChange((value) => { - // Enforce lowercase alphanumeric format for safe keys - this.langCode = value - .trim() - .toLowerCase() - .replace(/[^a-z0-9_-]/g, ""); - if (text.inputEl.value !== this.langCode) { - text.setValue(this.langCode); - } - }); + text.setPlaceholder(t('modals.addLang.codePlaceholder')).onChange((value) => { + // Enforce lowercase alphanumeric format for safe keys + this.langCode = value + .trim() + .toLowerCase() + .replace(/[^a-z0-9_-]/g, ''); + if (text.inputEl.value !== this.langCode) { + text.setValue(this.langCode); + } + }); }); // 3. RTL Toggle new Setting(contentEl) - .setName(t("modals.addLang.rtlLabel")) - .setDesc(t("modals.addLang.rtlDesc")) + .setName(t('modals.addLang.rtlLabel')) + .setDesc(t('modals.addLang.rtlDesc')) .addToggle((toggle) => { toggle.setValue(this.isRtl).onChange((value) => { this.isRtl = value; @@ -3128,15 +2956,15 @@ export class AddNewLanguageModal extends ColorMasterBaseModal { // 4. Action Buttons const buttonContainer = contentEl.createDiv({ - cls: "modal-button-container", + cls: 'modal-button-container', }); new ButtonComponent(buttonContainer) - .setButtonText(t("buttons.cancel")) + .setButtonText(t('buttons.cancel')) .onClick(() => this.close()); new ButtonComponent(buttonContainer) - .setButtonText(t("buttons.create")) + .setButtonText(t('buttons.create')) .setCta() .onClick(() => this.handleCreate()); } @@ -3144,18 +2972,18 @@ export class AddNewLanguageModal extends ColorMasterBaseModal { async handleCreate() { // --- Validation --- if (!this.langName) { - new Notice(t("notices.langNameEmpty")); + new Notice(t('notices.langNameEmpty')); return; } if (!this.langCode) { - new Notice(t("notices.langCodeEmpty")); + new Notice(t('notices.langCodeEmpty')); return; } // Prevent overriding core languages - const coreCodes = ["en", "ar", "fa", "fr"]; + const coreCodes = ['en', 'ar', 'fa', 'fr']; if (coreCodes.includes(this.langCode)) { - new Notice(t("notices.langCodeCore", this.langCode)); + new Notice(t('notices.langCodeCore', this.langCode)); return; } @@ -3165,7 +2993,7 @@ export class AddNewLanguageModal extends ColorMasterBaseModal { // Check for duplicate codes if (this.plugin.settings.customLanguages[this.langCode]) { - new Notice(t("notices.langCodeExists", this.langCode)); + new Notice(t('notices.langCodeExists', this.langCode)); return; } @@ -3175,16 +3003,14 @@ export class AddNewLanguageModal extends ColorMasterBaseModal { (lang) => lang.languageName.toLowerCase() === this.langName.toLowerCase(), ); if (nameExists) { - new Notice(t("notices.langNameExists", this.langName)); + new Notice(t('notices.langNameExists', this.langName)); return; } // Check for duplicate names (Core) - const coreLangNames = Object.values(CORE_LANGUAGES).map((name) => - name.toLowerCase(), - ); + const coreLangNames = Object.values(CORE_LANGUAGES).map((name) => name.toLowerCase()); if (coreLangNames.includes(this.langName.toLowerCase())) { - new Notice(t("notices.langNameCore", this.langName)); + new Notice(t('notices.langNameCore', this.langName)); return; } @@ -3200,17 +3026,12 @@ export class AddNewLanguageModal extends ColorMasterBaseModal { await this.plugin.saveSettings(); loadLanguage(this.plugin.settings); // Refresh i18n engine - new Notice(t("notices.langCreated", this.langName)); + new Notice(t('notices.langCreated', this.langName)); this.settingTab.display(); // Refresh UI this.close(); // Open translator immediately for convenience - new LanguageTranslatorModal( - this.app, - this.plugin, - this.settingTab, - this.langCode, - ).open(); + new LanguageTranslatorModal(this.app, this.plugin, this.settingTab, this.langCode).open(); } onClose() { @@ -3232,27 +3053,20 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { listContainer: HTMLElement; isCoreLanguage: boolean; isRtl: boolean = false; - searchQuery: string = ""; + searchQuery: string = ''; caseSensitive: boolean = false; filterMissing: boolean = false; searchInput: HTMLInputElement; debouncedRender: () => void; - constructor( - app: App, - plugin: ColorMaster, - settingTab: ColorMasterSettingTab, - langCode: string, - ) { + constructor(app: App, plugin: ColorMaster, settingTab: ColorMasterSettingTab, langCode: string) { super(app, plugin); this.settingTab = settingTab; this.langCode = langCode; this.fallbackStrings = getFallbackStrings(); // Pre-calculate nested structure once to avoid overhead during render loops - this.nestedFallback = unflattenStrings( - this.fallbackStrings as CustomTranslation, - ); + this.nestedFallback = unflattenStrings(this.fallbackStrings as CustomTranslation); const customLangData = this.plugin.settings.customLanguages?.[langCode]; const coreLangNameString = CORE_LANGUAGES[langCode as LocaleCode]; @@ -3266,26 +3080,22 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { const flatCoreLang = flattenStrings(CORE_LOCALES[langCode as LocaleCode]); const baseStrings: CustomTranslation = {}; for (const key in flatCoreLang) { - if (typeof flatCoreLang[key] === "string") - baseStrings[key] = flatCoreLang[key]; + if (typeof flatCoreLang[key] === 'string') baseStrings[key] = flatCoreLang[key]; } // 2. If we have saved overrides, merge them on top of base strings if (customLangData && customLangData.translations) { this.translations = { ...baseStrings, ...customLangData.translations }; - this.isRtl = - customLangData.isRtl ?? (langCode === "ar" || langCode === "fa"); + this.isRtl = customLangData.isRtl ?? (langCode === 'ar' || langCode === 'fa'); } else { // No overrides yet, just show full base strings this.translations = baseStrings; - this.isRtl = langCode === "ar" || langCode === "fa"; + this.isRtl = langCode === 'ar' || langCode === 'fa'; } } else if (customLangData) { // Purely custom language (not core) - load as is this.langName = customLangData.languageName; - this.translations = JSON.parse( - JSON.stringify(customLangData.translations || {}), - ); + this.translations = JSON.parse(JSON.stringify(customLangData.translations || {})); this.isCoreLanguage = false; this.isRtl = customLangData.isRtl || false; } else { @@ -3301,13 +3111,13 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { const { contentEl } = this; contentEl.empty(); this.modalEl.classList.add( - "color-master-modal", - "cm-translator-modal", - "cm-translator-tree-modal", + 'color-master-modal', + 'cm-translator-modal', + 'cm-translator-tree-modal', ); - contentEl.createEl("h3", { - text: t("modals.translator.title", this.langName), + contentEl.createEl('h3', { + text: t('modals.translator.title', this.langName), }); this.debouncedRender = debounce(() => { @@ -3315,35 +3125,32 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { }, 250); this.renderControls(contentEl); - this.listContainer = contentEl.createDiv("cm-translator-list"); + this.listContainer = contentEl.createDiv('cm-translator-list'); this.renderTranslationTree(); const mainControls = new Setting(contentEl) .addButton((button) => { button - .setButtonText(t("buttons.apply")) + .setButtonText(t('buttons.apply')) .setCta() .onClick(() => this.handleSave()); }) .addButton((button) => { - button.setButtonText(t("buttons.cancel")).onClick(() => this.close()); + button.setButtonText(t('buttons.cancel')).onClick(() => this.close()); }); - mainControls.settingEl.classList.add( - "cm-translator-main-controls", - "modal-button-container", - ); + mainControls.settingEl.classList.add('cm-translator-main-controls', 'modal-button-container'); } renderControls(containerEl: HTMLElement) { - const controlsEl = containerEl.createDiv("cm-translator-controls"); + const controlsEl = containerEl.createDiv('cm-translator-controls'); // --- 1. Search Bar (Using Obsidian Component) --- const searchBarContainer = controlsEl.createDiv({ - cls: "cm-search-bar-container", + cls: 'cm-search-bar-container', }); const searchComponent = new SearchComponent(searchBarContainer) - .setPlaceholder(t("modals.translator.searchPlaceholder")) + .setPlaceholder(t('modals.translator.searchPlaceholder')) .setValue(this.searchQuery) .onChange((value) => { this.searchQuery = value; @@ -3352,64 +3159,64 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { this.searchInput = searchComponent.inputEl; - searchBarContainer.addClass("cm-search-input-container"); + searchBarContainer.addClass('cm-search-input-container'); const searchActions = searchBarContainer.createDiv({ - cls: "cm-search-actions", + cls: 'cm-search-actions', }); // Case Sensitivity Toggle - const caseToggle = searchActions.createEl("button", { - cls: "cm-search-action-btn", - text: "Aa", + const caseToggle = searchActions.createEl('button', { + cls: 'cm-search-action-btn', + text: 'Aa', }); - caseToggle.setAttr("aria-label", t("settings.ariaCase")); - caseToggle.classList.toggle("is-active", this.caseSensitive); - caseToggle.addEventListener("click", () => { + caseToggle.setAttr('aria-label', t('settings.ariaCase')); + caseToggle.classList.toggle('is-active', this.caseSensitive); + caseToggle.addEventListener('click', () => { this.caseSensitive = !this.caseSensitive; - caseToggle.classList.toggle("is-active", this.caseSensitive); + caseToggle.classList.toggle('is-active', this.caseSensitive); this.debouncedRender(); }); // Filter Missing Toggle - const missingToggle = searchActions.createEl("button", { - cls: "cm-search-action-btn", + const missingToggle = searchActions.createEl('button', { + cls: 'cm-search-action-btn', }); - setIcon(missingToggle, "filter"); - missingToggle.setAttr("aria-label", t("modals.translator.showMissing")); - missingToggle.classList.toggle("is-active", this.filterMissing); - missingToggle.addEventListener("click", () => { + setIcon(missingToggle, 'filter'); + missingToggle.setAttr('aria-label', t('modals.translator.showMissing')); + missingToggle.classList.toggle('is-active', this.filterMissing); + missingToggle.addEventListener('click', () => { this.filterMissing = !this.filterMissing; - missingToggle.classList.toggle("is-active", this.filterMissing); + missingToggle.classList.toggle('is-active', this.filterMissing); this.debouncedRender(); }); // --- 2. IO Buttons (Import/Export) --- - const ioControls = controlsEl.createDiv("cm-translator-io-controls"); + const ioControls = controlsEl.createDiv('cm-translator-io-controls'); new Setting(ioControls) .addButton((btn) => btn - .setIcon("copy") - .setTooltip(t("modals.translator.copyJson")) + .setIcon('copy') + .setTooltip(t('modals.translator.copyJson')) .onClick(() => this._copyJson()), ) .addButton((btn) => btn - .setIcon("paste") - .setTooltip(t("modals.translator.pasteJson")) + .setIcon('paste') + .setTooltip(t('modals.translator.pasteJson')) .onClick(() => this._pasteJson()), ) .addButton((btn) => btn - .setIcon("download") - .setTooltip(t("modals.translator.importFile")) + .setIcon('download') + .setTooltip(t('modals.translator.importFile')) .onClick(() => this._importLanguageFile()), ) .addButton((btn) => btn - .setIcon("upload") - .setTooltip(t("modals.translator.exportFile")) + .setIcon('upload') + .setTooltip(t('modals.translator.exportFile')) .onClick(() => this._exportLanguageFile()), ); } @@ -3419,22 +3226,20 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { // Use cached structure instead of recalculating const counter = { index: 1 }; - const query = this.caseSensitive - ? this.searchQuery - : this.searchQuery.toLowerCase(); + const query = this.caseSensitive ? this.searchQuery : this.searchQuery.toLowerCase(); const totalRendered = this.renderGroup( this.listContainer, this.nestedFallback, - "", + '', counter, query, ); if (totalRendered === 0 && (this.searchQuery || this.filterMissing)) { - this.listContainer.createEl("p", { - cls: "cm-translator-empty", - text: t("modals.translator.noMatches"), + this.listContainer.createEl('p', { + cls: 'cm-translator-empty', + text: t('modals.translator.noMatches'), }); } } @@ -3456,8 +3261,8 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { const keys = Object.keys(fallbackGroup).sort((a, b) => { const aVal = fallbackGroup[a]; const bVal = fallbackGroup[b]; - const aIsObj = typeof aVal === "object" && aVal !== null; - const bIsObj = typeof bVal === "object" && bVal !== null; + const aIsObj = typeof aVal === 'object' && aVal !== null; + const bIsObj = typeof bVal === 'object' && bVal !== null; if (aIsObj && !bIsObj) return -1; if (!aIsObj && bIsObj) return 1; @@ -3472,41 +3277,36 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { // Prepare search terms once const keyStr = this.caseSensitive ? key : key.toLowerCase(); - let displayFallback = ""; - if (typeof fallbackValue === "string") { + let displayFallback = ''; + if (typeof fallbackValue === 'string') { displayFallback = fallbackValue; - } else if (typeof fallbackValue === "function") { - displayFallback = t("modals.translator.dynamicValue"); + } else if (typeof fallbackValue === 'function') { + displayFallback = t('modals.translator.dynamicValue'); } - const fallbackStr = this.caseSensitive - ? displayFallback - : displayFallback.toLowerCase(); + const fallbackStr = this.caseSensitive ? displayFallback : displayFallback.toLowerCase(); const valStr = - typeof currentValue === "string" + typeof currentValue === 'string' ? this.caseSensitive ? currentValue : currentValue.toLowerCase() - : ""; + : ''; const isMatch = - !query || - keyStr.includes(query) || - fallbackStr.includes(query) || - valStr.includes(query); + !query || keyStr.includes(query) || fallbackStr.includes(query) || valStr.includes(query); // Recursive Case: Group/Folder (Must be object and NOT null) - if (typeof fallbackValue === "object" && fallbackValue !== null) { - const details = container.createEl("details", { - cls: "cm-translator-group", + if (typeof fallbackValue === 'object' && fallbackValue !== null) { + const details = container.createEl('details', { + cls: 'cm-translator-group', }); - const summary = details.createEl("summary", { - cls: "cm-translator-group-title", + const summary = details.createEl('summary', { + cls: 'cm-translator-group-title', }); summary.createSpan({ text: key }); - const groupContainer = details.createDiv("cm-translator-group-content"); + const groupContainer = details.createDiv('cm-translator-group-content'); const childrenCount = this.renderGroup( groupContainer, @@ -3527,10 +3327,7 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { } } // Base Case: Translation Item (String OR Function) - else if ( - typeof fallbackValue === "string" || - typeof fallbackValue === "function" - ) { + else if (typeof fallbackValue === 'string' || typeof fallbackValue === 'function') { const isMissing = !currentValue; const matchesFilter = !this.filterMissing || isMissing; @@ -3538,39 +3335,35 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { itemsRenderedInThisGroup++; const itemEl = container.createDiv({ - cls: "cm-translator-item setting-item", + cls: 'cm-translator-item setting-item', }); itemEl.createSpan({ - cls: "cm-translator-index", + cls: 'cm-translator-index', text: `${counter.index++}.`, }); - const infoEl = itemEl.createDiv("setting-item-info"); - const nameEl = infoEl.createDiv( - "setting-item-name cm-translator-key", - ); + const infoEl = itemEl.createDiv('setting-item-info'); + const nameEl = infoEl.createDiv('setting-item-name cm-translator-key'); const keySpan = nameEl.createSpan(); this.highlightMatch(keySpan, key, query); const descEl = infoEl.createDiv({ - cls: "setting-item-description", + cls: 'setting-item-description', }); const isLongText = displayFallback.length > 100; const isDesc = - newPath.endsWith(".desc") || - newPath.endsWith("Desc") || - newPath.includes("langInfo"); + newPath.endsWith('.desc') || newPath.endsWith('Desc') || newPath.includes('langInfo'); if (isDesc && isLongText) { - const truncatedText = displayFallback.substring(0, 100) + "..."; + const truncatedText = displayFallback.substring(0, 100) + '...'; this.highlightMatch(descEl, truncatedText, query); - const toggleBtn = infoEl.createEl("a", { - cls: "cm-translator-toggle", - text: t("modals.translator.showMore"), + const toggleBtn = infoEl.createEl('a', { + cls: 'cm-translator-toggle', + text: t('modals.translator.showMore'), }); let isExpanded = false; @@ -3581,16 +3374,14 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { this.highlightMatch(descEl, textToShow, query); toggleBtn.setText( - isExpanded - ? t("modals.translator.showLess") - : t("modals.translator.showMore"), + isExpanded ? t('modals.translator.showLess') : t('modals.translator.showMore'), ); }; } else { this.highlightMatch(descEl, displayFallback, query); } - const controlEl = itemEl.createDiv("setting-item-control"); + const controlEl = itemEl.createDiv('setting-item-control'); // Use TextArea for long strings const isMultiLine = this.isLongString(displayFallback); @@ -3599,7 +3390,7 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { : new TextComponent(controlEl); component - .setValue(currentValue || "") + .setValue(currentValue || '') .setPlaceholder(displayFallback) .onChange((value) => { if (value) { @@ -3623,8 +3414,8 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { } try { - const flags = this.caseSensitive ? "g" : "gi"; - const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const flags = this.caseSensitive ? 'g' : 'gi'; + const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(escapedQuery, flags); let lastIndex = 0; @@ -3634,7 +3425,7 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { if (match.index > lastIndex) { element.appendText(text.substring(lastIndex, match.index)); } - element.createSpan({ cls: "cm-search-match", text: match[0] }); + element.createSpan({ cls: 'cm-search-match', text: match[0] }); lastIndex = regex.lastIndex; } @@ -3648,7 +3439,7 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { } isLongString(str: string): boolean { - return str.length > 50 || str.includes("\n"); + return str.length > 50 || str.includes('\n'); } async handleSave() { @@ -3661,10 +3452,7 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { // If it's a Core Language, save ONLY the differences (Deltas) if (this.isCoreLanguage) { const flatCoreLang = flattenStrings( - CORE_LOCALES[this.langCode as LocaleCode] as unknown as Record< - string, - unknown - >, + CORE_LOCALES[this.langCode as LocaleCode] as unknown as Record, ); const diffs: CustomTranslation = {}; @@ -3696,7 +3484,7 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { loadLanguage(this.plugin.settings); } - new Notice(t("notices.langSaved", this.langName)); + new Notice(t('notices.langSaved', this.langName)); this.settingTab.display(); this.close(); } @@ -3704,21 +3492,21 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { _exportLanguageFile() { const nestedData = unflattenStrings(this.translations); const data = JSON.stringify(nestedData, null, 2); - const blob = new Blob([data], { type: "application/json" }); + const blob = new Blob([data], { type: 'application/json' }); const url = URL.createObjectURL(blob); - const a = document.createElement("a"); + const a = document.createElement('a'); a.href = url; a.download = `${this.langCode}.json`; a.click(); URL.revokeObjectURL(url); a.remove(); - new Notice(t("notices.langExported", this.langCode)); + new Notice(t('notices.langExported', this.langCode)); } _importLanguageFile() { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".json"; + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; input.onchange = () => { void (async () => { @@ -3728,23 +3516,21 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { try { const content = await file.text(); const nestedJson = JSON.parse(content); - const importedTranslations = flattenStrings( - nestedJson, - ) as CustomTranslation; + const importedTranslations = flattenStrings(nestedJson) as CustomTranslation; this.translations = { ...this.translations, ...importedTranslations, }; - new Notice(t("notices.langImported", file.name)); + new Notice(t('notices.langImported', file.name)); this.renderTranslationTree(); } catch (e) { - new Notice(t("notices.invalidJson")); - console.error("Failed to import language file:", e); + new Notice(t('notices.invalidJson')); + console.error('Failed to import language file:', e); } })().catch((err) => { - console.error("Unhandled file import error:", err); + console.error('Unhandled file import error:', err); }); }; @@ -3756,10 +3542,10 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { const jsonText = JSON.stringify(nestedData, null, 2); void navigator.clipboard.writeText(jsonText).catch((err) => { - console.error("Failed to copy JSON to clipboard:", err); + console.error('Failed to copy JSON to clipboard:', err); }); - new Notice(t("notices.langCopiedJson")); + new Notice(t('notices.langCopiedJson')); } _pasteJson(): void { @@ -3782,14 +3568,14 @@ export class LanguageTranslatorModal extends ColorMasterBaseModal { } } - new Notice(t("notices.langPastedJson", updateCount)); + new Notice(t('notices.langPastedJson', updateCount)); this.renderTranslationTree(); } catch (e) { - new Notice(t("notices.invalidJson")); - console.error("Failed to paste JSON from clipboard:", e); + new Notice(t('notices.invalidJson')); + console.error('Failed to paste JSON from clipboard:', e); } })().catch((err) => { - console.error("Unhandled paste JSON error:", err); + console.error('Unhandled paste JSON error:', err); }); } diff --git a/src/ui/settingsTab.ts b/src/ui/settingsTab.ts index b928fbb..6c28d93 100644 --- a/src/ui/settingsTab.ts +++ b/src/ui/settingsTab.ts @@ -7,32 +7,27 @@ import { Setting, setIcon, SearchComponent, -} from "obsidian"; -import { DEFAULT_VARS, TEXT_TO_BG_MAP } from "../constants"; -import { t } from "../i18n/strings"; -import type ColorMaster from "../main"; -import { - flattenVars, - getAccessibilityRating, - getContrastRatio, - debounce, -} from "../utils"; -import { drawColorPickers } from "./components/color-pickers"; -import { drawImportExport } from "./components/import-export"; -import { drawLikePluginCard } from "./components/like-plugin-card"; -import { drawOptionsSection } from "./components/options-section"; -import { drawProfileManager } from "./components/profile-manager"; -import { drawCssSnippetsUI } from "./components/snippets-ui"; -import { LanguageSettingsModal } from "./modals"; -import Sortable from "sortablejs"; -import { loadLanguage } from "../i18n/strings"; -import { CORE_LANGUAGES, LocaleCode } from "../i18n/types"; +} from 'obsidian'; +import { DEFAULT_VARS, TEXT_TO_BG_MAP } from '../constants'; +import { t } from '../i18n/strings'; +import type ColorMaster from '../main'; +import { flattenVars, getAccessibilityRating, getContrastRatio, debounce } from '../utils'; +import { drawColorPickers } from './components/color-pickers'; +import { drawImportExport } from './components/import-export'; +import { drawLikePluginCard } from './components/like-plugin-card'; +import { drawOptionsSection } from './components/options-section'; +import { drawProfileManager } from './components/profile-manager'; +import { drawCssSnippetsUI } from './components/snippets-ui'; +import { LanguageSettingsModal } from './modals'; +import Sortable from 'sortablejs'; +import { loadLanguage } from '../i18n/strings'; +import { CORE_LANGUAGES, LocaleCode } from '../i18n/types'; import { AddNewLanguageModal, LanguageTranslatorModal, ConfirmationModal, LanguageInfoModal, -} from "./modals"; +} from './modals'; export class ColorMasterSettingTab extends PluginSettingTab { plugin: ColorMaster; @@ -58,22 +53,16 @@ export class ColorMasterSettingTab extends PluginSettingTab { this.snippetSortable = null; } - updateColorPickerAppearance( - textInput: HTMLInputElement, - colorPicker: HTMLInputElement, - ) { + updateColorPickerAppearance(textInput: HTMLInputElement, colorPicker: HTMLInputElement) { const value = textInput.value.toLowerCase().trim(); - if (value === "transparent" || value === "") { - colorPicker.classList.add("is-transparent"); - colorPicker.value = "#ffffff"; - } else if ( - value.startsWith("#") && - (value.length === 9 || value.length === 5) - ) { - colorPicker.classList.remove("is-transparent"); + if (value === 'transparent' || value === '') { + colorPicker.classList.add('is-transparent'); + colorPicker.value = '#ffffff'; + } else if (value.startsWith('#') && (value.length === 9 || value.length === 5)) { + colorPicker.classList.remove('is-transparent'); - let rgbHex = "#ffffff"; + let rgbHex = '#ffffff'; if (value.length === 9) { rgbHex = value.substring(0, 7); } else if (value.length === 5) { @@ -87,14 +76,14 @@ export class ColorMasterSettingTab extends PluginSettingTab { colorPicker.value = rgbHex; } catch { console.warn(`Color Master: Invalid HEX for color picker: ${rgbHex}`); - colorPicker.value = "#ffffff"; + colorPicker.value = '#ffffff'; } } else { - colorPicker.classList.remove("is-transparent"); + colorPicker.classList.remove('is-transparent'); try { colorPicker.value = value; } catch { - colorPicker.value = "#ffffff"; + colorPicker.value = '#ffffff'; } } } @@ -102,29 +91,24 @@ export class ColorMasterSettingTab extends PluginSettingTab { _clearSearchAndFilters() { if (!this.searchInput || !this.sectionSelect) return; - this.searchInput.value = ""; - this.sectionSelect.value = ""; + this.searchInput.value = ''; + this.sectionSelect.value = ''; - this._searchState.query = ""; - this._searchState.section = ""; + this._searchState.query = ''; + this._searchState.section = ''; // Clear saved settings - this.plugin.settings.lastSearchQuery = ""; - this.plugin.settings.lastSearchSection = ""; + this.plugin.settings.lastSearchQuery = ''; + this.plugin.settings.lastSearchSection = ''; void this.plugin.saveData(this.plugin.settings).catch((err) => { - console.error("Failed to save search input state:", err); + console.error('Failed to save search input state:', err); }); - const filterButton = this.containerEl.querySelector( - 'button[data-cm-action="filter"]', - ); - const filterOptionsContainer = this.containerEl.querySelector( - ".cm-search-filter-options", - ); + const filterButton = this.containerEl.querySelector('button[data-cm-action="filter"]'); + const filterOptionsContainer = this.containerEl.querySelector('.cm-search-filter-options'); - if (filterButton) filterButton.classList.remove("is-active"); - if (filterOptionsContainer) - filterOptionsContainer.classList.add("is-hidden"); + if (filterButton) filterButton.classList.remove('is-active'); + if (filterOptionsContainer) filterOptionsContainer.classList.add('is-hidden'); this._applySearchFilter(); } @@ -132,38 +116,38 @@ export class ColorMasterSettingTab extends PluginSettingTab { initSearchUI(containerEl: HTMLElement) { // Load saved section here this._searchState = { - query: this.plugin.settings.lastSearchQuery || "", + query: this.plugin.settings.lastSearchQuery || '', regex: false, caseSensitive: false, - section: this.plugin.settings.lastSearchSection || "", + section: this.plugin.settings.lastSearchSection || '', }; const searchBarContainer = containerEl.createDiv({ - cls: "cm-search-bar-container", + cls: 'cm-search-bar-container', }); // --- 1. Input field section --- const searchInputContainer = searchBarContainer.createDiv({ - cls: "cm-search-input-container", + cls: 'cm-search-input-container', }); const searchComponent = new SearchComponent(searchInputContainer) - .setPlaceholder(t("settings.searchPlaceholder")) + .setPlaceholder(t('settings.searchPlaceholder')) .setValue(this._searchState.query) .onChange((value) => { if (this._searchState.regex) return; this._searchState.query = value; this.plugin.settings.lastSearchQuery = value; void this.plugin.saveData(this.plugin.settings).catch((err) => { - console.error("Failed to save search/filter reset state:", err); + console.error('Failed to save search/filter reset state:', err); }); debouncedFilter(); }); this.searchInput = searchComponent.inputEl; - this.searchInput.addEventListener("keydown", (e: KeyboardEvent) => { - if (e.key === "Enter" && this._searchState.regex) { + this.searchInput.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.key === 'Enter' && this._searchState.regex) { this._searchState.query = (e.target as HTMLInputElement).value; debouncedFilter(); } @@ -171,27 +155,27 @@ export class ColorMasterSettingTab extends PluginSettingTab { // --- 2. Control buttons section --- const searchActions = searchBarContainer.createDiv({ - cls: "cm-search-actions", + cls: 'cm-search-actions', }); - this.caseToggle = searchActions.createEl("button", { - cls: "cm-search-action-btn", + this.caseToggle = searchActions.createEl('button', { + cls: 'cm-search-action-btn', }); - this.caseToggle.textContent = "Aa"; - this.caseToggle.setAttr("aria-label", t("settings.ariaCase")); + this.caseToggle.textContent = 'Aa'; + this.caseToggle.setAttr('aria-label', t('settings.ariaCase')); - this.regexToggle = searchActions.createEl("button", { - cls: "cm-search-action-btn", + this.regexToggle = searchActions.createEl('button', { + cls: 'cm-search-action-btn', }); - setIcon(this.regexToggle, "regex"); - this.regexToggle.setAttr("aria-label", t("settings.ariaRegex")); + setIcon(this.regexToggle, 'regex'); + this.regexToggle.setAttr('aria-label', t('settings.ariaRegex')); const filterOptionsContainer = searchActions.createDiv({ - cls: "cm-search-filter-options is-hidden", + cls: 'cm-search-filter-options is-hidden', }); const dropdown = new DropdownComponent(filterOptionsContainer) - .addOption("", t("settings.allSections")) + .addOption('', t('settings.allSections')) .onChange(async (value) => { this._searchState.section = value; @@ -199,17 +183,14 @@ export class ColorMasterSettingTab extends PluginSettingTab { this.plugin.settings.lastSearchSection = value; await this.plugin.saveData(this.plugin.settings); - filterOptionsContainer.classList.toggle( - "is-filter-active", - value !== "", - ); + filterOptionsContainer.classList.toggle('is-filter-active', value !== ''); debouncedFilter(); }); // Restore filter state dropdown.setValue(this._searchState.section); if (this._searchState.section) { - filterOptionsContainer.classList.remove("is-hidden"); + filterOptionsContainer.classList.remove('is-hidden'); } try { @@ -217,13 +198,11 @@ export class ColorMasterSettingTab extends PluginSettingTab { let categoryKey: string; const lowerCategory = category.toLowerCase(); - if (lowerCategory === "interactive elements") - categoryKey = "interactive"; - else if (lowerCategory === "ui elements") categoryKey = "ui"; - else if (lowerCategory === "graph view") categoryKey = "graph"; - else if (lowerCategory === "plugin integrations") - categoryKey = "pluginintegrations"; - else categoryKey = lowerCategory.replace(/ /g, ""); + if (lowerCategory === 'interactive elements') categoryKey = 'interactive'; + else if (lowerCategory === 'ui elements') categoryKey = 'ui'; + else if (lowerCategory === 'graph view') categoryKey = 'graph'; + else if (lowerCategory === 'plugin integrations') categoryKey = 'pluginintegrations'; + else categoryKey = lowerCategory.replace(/ /g, ''); const translatedCategory = t(`categories.${categoryKey}`) || category; dropdown.addOption(category, translatedCategory); @@ -232,57 +211,56 @@ export class ColorMasterSettingTab extends PluginSettingTab { // ignore invalid category translation } - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if ( activeProfile?.customVarMetadata && Object.keys(activeProfile.customVarMetadata).length > 0 ) { - dropdown.addOption("Custom", t("categories.custom")); + dropdown.addOption('Custom', t('categories.custom')); } - dropdown.selectEl.classList.add("cm-search-small"); + dropdown.selectEl.classList.add('cm-search-small'); this.sectionSelect = dropdown.selectEl; - const filterButton = searchActions.createEl("button", { - cls: "cm-search-action-btn", + const filterButton = searchActions.createEl('button', { + cls: 'cm-search-action-btn', }); - setIcon(filterButton, "sliders-horizontal"); - filterButton.dataset.cmAction = "filter"; + setIcon(filterButton, 'sliders-horizontal'); + filterButton.dataset.cmAction = 'filter'; // Activate button if section is saved - if (this._searchState.section) filterButton.classList.add("is-active"); + if (this._searchState.section) filterButton.classList.add('is-active'); const debouncedFilter = debounce(() => this._applySearchFilter(), 180); - this.caseToggle.addEventListener("click", () => { + this.caseToggle.addEventListener('click', () => { this._searchState.caseSensitive = !this._searchState.caseSensitive; - this.caseToggle.toggleClass("is-active", this._searchState.caseSensitive); + this.caseToggle.toggleClass('is-active', this._searchState.caseSensitive); debouncedFilter(); }); - this.regexToggle.addEventListener("click", () => { + this.regexToggle.addEventListener('click', () => { this._searchState.regex = !this._searchState.regex; - this.regexToggle.toggleClass("is-active", this._searchState.regex); + this.regexToggle.toggleClass('is-active', this._searchState.regex); if (this._searchState.regex) { - searchComponent.setPlaceholder(t("settings.regexPlaceholder")); + searchComponent.setPlaceholder(t('settings.regexPlaceholder')); } else { - searchComponent.setPlaceholder(t("settings.searchPlaceholder")); + searchComponent.setPlaceholder(t('settings.searchPlaceholder')); } this._searchState.query = this.searchInput.value; debouncedFilter(); }); - filterButton.addEventListener("click", () => { - if (filterButton.classList.contains("is-active")) { + filterButton.addEventListener('click', () => { + if (filterButton.classList.contains('is-active')) { this._clearSearchAndFilters(); } else { - filterOptionsContainer.classList.remove("is-hidden"); - filterButton.classList.add("is-active"); + filterOptionsContainer.classList.remove('is-hidden'); + filterButton.classList.add('is-active'); } }); - this.sectionSelect.addEventListener("change", (e: Event) => { + this.sectionSelect.addEventListener('change', (e: Event) => { this._searchState.section = (e.target as HTMLSelectElement).value; debouncedFilter(); }); @@ -290,16 +268,13 @@ export class ColorMasterSettingTab extends PluginSettingTab { _applySearchFilter() { const s = this._searchState; - const activeProfile = - this.plugin.settings.profiles[this.plugin.settings.activeProfile]; + const activeProfile = this.plugin.settings.profiles[this.plugin.settings.activeProfile]; if (this.staticContentContainer) { - const isSearching = s.query.trim().length > 0 || s.section !== ""; - this.staticContentContainer.toggleClass("cm-hidden", isSearching); + const isSearching = s.query.trim().length > 0 || s.section !== ''; + this.staticContentContainer.toggleClass('cm-hidden', isSearching); } const rows = Array.from( - this.containerEl.querySelectorAll( - ".cm-var-row, .cm-searchable-row", - ), + this.containerEl.querySelectorAll('.cm-var-row, .cm-searchable-row'), ); let qRegex: RegExp | null = null; @@ -307,7 +282,7 @@ export class ColorMasterSettingTab extends PluginSettingTab { if (s.query && s.query.trim()) { if (s.regex) { try { - qRegex = new RegExp(s.query, s.caseSensitive ? "" : "i"); + qRegex = new RegExp(s.query, s.caseSensitive ? '' : 'i'); } catch { qRegex = null; } @@ -315,14 +290,13 @@ export class ColorMasterSettingTab extends PluginSettingTab { } rows.forEach((row) => { - const varName = row.dataset.var || ""; - const snippetName = row.dataset.name || ""; - const textInput = - row.querySelector("input[type='text']"); - const varValue = textInput ? textInput.value.trim() : ""; + const varName = row.dataset.var || ''; + const snippetName = row.dataset.name || ''; + const textInput = row.querySelector("input[type='text']"); + const varValue = textInput ? textInput.value.trim() : ''; - let displayName = ""; - let description = ""; + let displayName = ''; + let description = ''; const customMeta = activeProfile?.customVarMetadata?.[varName]; if (customMeta) { @@ -330,11 +304,11 @@ export class ColorMasterSettingTab extends PluginSettingTab { description = customMeta.desc; } else { displayName = t(`colors.names.${varName}`) || snippetName; - description = t(`colors.descriptions.${varName}`) || ""; + description = t(`colors.descriptions.${varName}`) || ''; } if (s.section && s.section !== row.dataset.category) { - row.classList.add("cm-hidden"); + row.classList.add('cm-hidden'); return; } @@ -351,15 +325,9 @@ export class ColorMasterSettingTab extends PluginSettingTab { } else { const queryLower = s.caseSensitive ? q : q.toLowerCase(); const nameLower = s.caseSensitive ? varName : varName.toLowerCase(); - const valueLower = s.caseSensitive - ? varValue - : varValue.toLowerCase(); - const displayNameLower = s.caseSensitive - ? displayName - : displayName.toLowerCase(); - const descriptionLower = s.caseSensitive - ? description - : description.toLowerCase(); + const valueLower = s.caseSensitive ? varValue : varValue.toLowerCase(); + const displayNameLower = s.caseSensitive ? displayName : displayName.toLowerCase(); + const descriptionLower = s.caseSensitive ? description : description.toLowerCase(); isMatch = nameLower.includes(queryLower) || @@ -369,18 +337,16 @@ export class ColorMasterSettingTab extends PluginSettingTab { } if (!isMatch) { - row.classList.add("cm-hidden"); + row.classList.add('cm-hidden'); return; } } - row.classList.remove("cm-hidden"); + row.classList.remove('cm-hidden'); this._highlightRowMatches(row, s); }); - const headings = this.containerEl.querySelectorAll( - ".cm-category-container", - ); + const headings = this.containerEl.querySelectorAll('.cm-category-container'); headings.forEach((heading) => { const category = heading.dataset.category; const hasVisibleRows = this.containerEl.querySelector( @@ -388,9 +354,9 @@ export class ColorMasterSettingTab extends PluginSettingTab { ); if (hasVisibleRows) { - heading.classList.remove("cm-hidden"); + heading.classList.remove('cm-hidden'); } else { - heading.classList.add("cm-hidden"); + heading.classList.add('cm-hidden'); } }); } @@ -402,7 +368,7 @@ export class ColorMasterSettingTab extends PluginSettingTab { if (!element) return; if (!element.dataset.originalText) { - element.dataset.originalText = element.textContent || ""; + element.dataset.originalText = element.textContent || ''; } const originalText = element.dataset.originalText; @@ -413,13 +379,13 @@ export class ColorMasterSettingTab extends PluginSettingTab { return; } - const flags = state.caseSensitive ? "g" : "gi"; + const flags = state.caseSensitive ? 'g' : 'gi'; let regex: RegExp; try { regex = state.regex ? new RegExp(query, flags) - : new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags); + : new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags); let lastIndex = 0; let match; @@ -428,7 +394,7 @@ export class ColorMasterSettingTab extends PluginSettingTab { if (match.index > lastIndex) { element.appendText(originalText.substring(lastIndex, match.index)); } - element.createSpan({ cls: "cm-search-match", text: match[0] }); + element.createSpan({ cls: 'cm-search-match', text: match[0] }); lastIndex = regex.lastIndex; } @@ -440,8 +406,8 @@ export class ColorMasterSettingTab extends PluginSettingTab { } }; - const nameEl = row.querySelector(".cm-var-name"); - const descEl = row.querySelector(".setting-item-description"); + const nameEl = row.querySelector('.cm-var-name'); + const descEl = row.querySelector('.setting-item-description'); highlightElement(nameEl); highlightElement(descEl); @@ -452,9 +418,7 @@ export class ColorMasterSettingTab extends PluginSettingTab { const snapshot = this.plugin.settings.pinnedSnapshots?.[name]; if (this.resetPinBtn) { - this.resetPinBtn - .setTooltip(t("tooltips.resetToPinned")) - .setDisabled(!snapshot); + this.resetPinBtn.setTooltip(t('tooltips.resetToPinned')).setDisabled(!snapshot); } if (this.pinBtn) { @@ -465,16 +429,15 @@ export class ColorMasterSettingTab extends PluginSettingTab { const day = dateObj.getDate(); const formattedDate = `${year}-${month}-${day}`; - this.pinBtn.setTooltip(t("tooltips.pinSnapshotDate", formattedDate)); + this.pinBtn.setTooltip(t('tooltips.pinSnapshotDate', formattedDate)); } else { - this.pinBtn.setTooltip(t("tooltips.pinSnapshot")); + this.pinBtn.setTooltip(t('tooltips.pinSnapshot')); } } } _getCurrentProfileJson() { - const p = - this.plugin.settings.profiles?.[this.plugin.settings.activeProfile]; + const p = this.plugin.settings.profiles?.[this.plugin.settings.activeProfile]; if (!p) return null; return { name: this.plugin.settings.activeProfile, @@ -486,30 +449,30 @@ export class ColorMasterSettingTab extends PluginSettingTab { async _copyProfileToClipboard() { const payload = this._getCurrentProfileJson(); if (!payload) { - new Notice(t("notices.noActiveProfileToCopy")); + new Notice(t('notices.noActiveProfileToCopy')); return; } await navigator.clipboard.writeText(JSON.stringify(payload, null, 2)); - new Notice(t("notices.jsonCopied")); + new Notice(t('notices.jsonCopied')); } _exportProfileToFile() { const payload = this._getCurrentProfileJson(); if (!payload) { - new Notice(t("notices.noActiveProfileToExport")); + new Notice(t('notices.noActiveProfileToExport')); return; } const blob = new Blob([JSON.stringify(payload, null, 2)], { - type: "application/json", + type: 'application/json', }); const url = URL.createObjectURL(blob); - const a = document.createElement("a"); + const a = document.createElement('a'); a.download = `${this.plugin.settings.activeProfile}.profile.json`; a.href = url; a.click(); URL.revokeObjectURL(url); a.remove(); - new Notice(t("notices.exportSuccess")); + new Notice(t('notices.exportSuccess')); } updateAccessibilityCheckers() { @@ -517,24 +480,19 @@ export class ColorMasterSettingTab extends PluginSettingTab { this.plugin.settings.profiles[this.plugin.settings.activeProfile].vars; const allDefaultVars = flattenVars(DEFAULT_VARS); - const checkerElements = this.containerEl.querySelectorAll( - ".cm-accessibility-checker", - ); + const checkerElements = this.containerEl.querySelectorAll('.cm-accessibility-checker'); checkerElements.forEach((checkerEl) => { const varName = (checkerEl as HTMLElement).dataset.varName; if (!varName) return; - const bgVarForTextColor = - TEXT_TO_BG_MAP[varName as keyof typeof TEXT_TO_BG_MAP]; + const bgVarForTextColor = TEXT_TO_BG_MAP[varName as keyof typeof TEXT_TO_BG_MAP]; if (!bgVarForTextColor) return; let textColor = activeProfileVars[varName] || allDefaultVars[varName]; - let bgColor = - activeProfileVars[bgVarForTextColor] || - allDefaultVars[bgVarForTextColor]; + let bgColor = activeProfileVars[bgVarForTextColor] || allDefaultVars[bgVarForTextColor]; - if (varName === "--text-highlight-bg") { + if (varName === '--text-highlight-bg') { [textColor, bgColor] = [bgColor, textColor]; } @@ -549,7 +507,7 @@ export class ColorMasterSettingTab extends PluginSettingTab { display(): void { // call the async renderer and handle any error void this.renderDisplay().catch((err) => { - console.error("Error in SettingsTab.renderDisplay:", err); + console.error('Error in SettingsTab.renderDisplay:', err); }); } @@ -557,13 +515,13 @@ export class ColorMasterSettingTab extends PluginSettingTab { const themeDefaults = await this.plugin.getThemeDefaults(); const { containerEl } = this; - containerEl.classList.add("color-master-settings-tab"); + containerEl.classList.add('color-master-settings-tab'); containerEl.empty(); - containerEl.classList.add("color-master-hidden"); + containerEl.classList.add('color-master-hidden'); const langCode = this.plugin.settings.language; const customLang = this.plugin.settings.customLanguages?.[langCode]; - const isCoreRtlLang = langCode === "ar" || langCode === "fa"; + const isCoreRtlLang = langCode === 'ar' || langCode === 'fa'; const isCustomRtlLang = customLang?.isRtl === true; const isRtlCapable = isCoreRtlLang || isCustomRtlLang; const isRtlEnabled = this.plugin.settings.useRtlLayout; @@ -571,92 +529,80 @@ export class ColorMasterSettingTab extends PluginSettingTab { const isCustom = !!customLang; const isCore = !!CORE_LANGUAGES[langCode as LocaleCode]; - this.containerEl.setAttribute("dir", isRTL ? "rtl" : "ltr"); + this.containerEl.setAttribute('dir', isRTL ? 'rtl' : 'ltr'); - new Setting(containerEl).setName(t("plugin.name")).setHeading(); + new Setting(containerEl).setName(t('plugin.name')).setHeading(); new Setting(containerEl) - .setName(t("settings.enablePlugin")) - .setDesc(t("settings.enablePluginDesc")) + .setName(t('settings.enablePlugin')) + .setDesc(t('settings.enablePluginDesc')) .addToggle((toggle) => { - toggle - .setValue(this.plugin.settings.pluginEnabled) - .onChange(async (value) => { - this.plugin.settings.pluginEnabled = value; + toggle.setValue(this.plugin.settings.pluginEnabled).onChange(async (value) => { + this.plugin.settings.pluginEnabled = value; - if (value) { - this.plugin.enableObservers(); - } else { - this.plugin.disableObservers(); - } + if (value) { + this.plugin.enableObservers(); + } else { + this.plugin.disableObservers(); + } - await this.plugin.saveSettings(); - this.plugin.restartColorUpdateLoop(); - new Notice( - value ? t("notices.pluginEnabled") : t("notices.pluginDisabled"), - ); - }); + await this.plugin.saveSettings(); + this.plugin.restartColorUpdateLoop(); + new Notice(value ? t('notices.pluginEnabled') : t('notices.pluginDisabled')); + }); }); const languageSetting = new Setting(containerEl) - .setName(t("settings.language")) - .setDesc(t("settings.languageDesc")); + .setName(t('settings.language')) + .setDesc(t('settings.languageDesc')); const langIcon = languageSetting.nameEl.createSpan({ - cls: "cm-setting-icon", + cls: 'cm-setting-icon', }); - setIcon(langIcon, "languages"); + setIcon(langIcon, 'languages'); languageSetting.nameEl.prepend(langIcon); const flyoutContainer = languageSetting.controlEl.createDiv({ - cls: "cm-flyout-menu-container", + cls: 'cm-flyout-menu-container', }); const buttonListEl = flyoutContainer.createDiv({ - cls: "cm-flyout-menu-buttons", + cls: 'cm-flyout-menu-buttons', }); const triggerBtn = new ButtonComponent(flyoutContainer) - .setIcon("settings") - .setTooltip(t("tooltips.langMenu")) - .setClass("cm-flyout-trigger-btn") + .setIcon('settings') + .setTooltip(t('tooltips.langMenu')) + .setClass('cm-flyout-trigger-btn') .onClick(() => { - buttonListEl.toggleClass( - "is-open", - !buttonListEl.classList.contains("is-open"), - ); + buttonListEl.toggleClass('is-open', !buttonListEl.classList.contains('is-open')); }); - triggerBtn.buttonEl.classList.add("cm-control-icon-button"); + triggerBtn.buttonEl.classList.add('cm-control-icon-button'); // Info Button new ButtonComponent(buttonListEl) - .setIcon("info") - .setTooltip(t("tooltips.langInfo")) - .setClass("cm-control-icon-button") + .setIcon('info') + .setTooltip(t('tooltips.langInfo')) + .setClass('cm-control-icon-button') .onClick(() => { new LanguageInfoModal(this.app, this.plugin).open(); }); // Edit Button new ButtonComponent(buttonListEl) - .setIcon("pencil") - .setTooltip(t("tooltips.editLang")) - .setClass("cm-control-icon-button") + .setIcon('pencil') + .setTooltip(t('tooltips.editLang')) + .setClass('cm-control-icon-button') .onClick(() => { const langCode = this.plugin.settings.language; - new LanguageTranslatorModal( - this.app, - this.plugin, - this, - langCode, - ).open(); + new LanguageTranslatorModal(this.app, this.plugin, this, langCode).open(); }); // Add Button new ButtonComponent(buttonListEl) - .setIcon("plus") - .setTooltip(t("modals.addLang.title")) - .setClass("cm-control-icon-button") + .setIcon('plus') + .setTooltip(t('modals.addLang.title')) + .setClass('cm-control-icon-button') .onClick(() => { new AddNewLanguageModal(this.app, this.plugin, this).open(); }); @@ -664,9 +610,9 @@ export class ColorMasterSettingTab extends PluginSettingTab { // RTL Button if (isRtlCapable) { new ButtonComponent(buttonListEl) - .setIcon("settings-2") // - .setTooltip(t("settings.languageSettingsModalTitle")) - .setClass("cm-control-icon-button") + .setIcon('settings-2') // + .setTooltip(t('settings.languageSettingsModalTitle')) + .setClass('cm-control-icon-button') .onClick(() => { new LanguageSettingsModal(this.app, this.plugin).open(); }); @@ -675,18 +621,15 @@ export class ColorMasterSettingTab extends PluginSettingTab { // History/Restore Button for Core Languages that are customized if (isCore && isCustom) { const restoreBtn = new ButtonComponent(buttonListEl) - .setIcon("history") - .setTooltip(t("tooltips.restoreDefaultLang")) - .setClass("mod-warning") + .setIcon('history') + .setTooltip(t('tooltips.restoreDefaultLang')) + .setClass('mod-warning') .onClick(() => { new ConfirmationModal( this.app, this.plugin, - t("modals.confirmation.restoreLangTitle"), - t( - "modals.confirmation.restoreLangDesc", - CORE_LANGUAGES[langCode as LocaleCode], - ), + t('modals.confirmation.restoreLangTitle'), + t('modals.confirmation.restoreLangDesc', CORE_LANGUAGES[langCode as LocaleCode]), () => { void (async () => { if (this.plugin.settings.customLanguages) { @@ -694,47 +637,47 @@ export class ColorMasterSettingTab extends PluginSettingTab { loadLanguage(this.plugin.settings); await this.plugin.saveSettings(); this.display(); - new Notice(t("notices.langRestored")); + new Notice(t('notices.langRestored')); } })().catch((err) => { // Short human comment in English (as you requested) - console.error("Failed to restore language:", err); + console.error('Failed to restore language:', err); }); }, - { buttonText: t("buttons.restore"), buttonClass: "mod-warning" }, + { buttonText: t('buttons.restore'), buttonClass: 'mod-warning' }, ).open(); }); - restoreBtn.buttonEl.classList.add("cm-control-icon-button"); + restoreBtn.buttonEl.classList.add('cm-control-icon-button'); } if (isCustom && !isCore) { const deleteBtn = new ButtonComponent(buttonListEl) - .setIcon("trash") - .setTooltip(t("buttons.delete") + ` (${customLang.languageName})`) - .setClass("mod-warning") + .setIcon('trash') + .setTooltip(t('buttons.delete') + ` (${customLang.languageName})`) + .setClass('mod-warning') .onClick(() => { new ConfirmationModal( this.app, this.plugin, - t("modals.confirmation.deleteLangTitle"), - t("modals.confirmation.deleteLangDesc", customLang.languageName), + t('modals.confirmation.deleteLangTitle'), + t('modals.confirmation.deleteLangDesc', customLang.languageName), () => { void (async () => { if (!this.plugin.settings.customLanguages) return; delete this.plugin.settings.customLanguages[langCode]; - this.plugin.settings.language = "en"; + this.plugin.settings.language = 'en'; loadLanguage(this.plugin.settings); await this.plugin.saveSettings(); this.display(); - new Notice(t("notices.langDeleted", customLang.languageName)); + new Notice(t('notices.langDeleted', customLang.languageName)); })().catch((err) => { - console.error("Failed to delete custom language:", err); + console.error('Failed to delete custom language:', err); }); }, - { buttonText: t("buttons.delete"), buttonClass: "mod-warning" }, + { buttonText: t('buttons.delete'), buttonClass: 'mod-warning' }, ).open(); }); - deleteBtn.buttonEl.classList.add("cm-control-icon-button"); + deleteBtn.buttonEl.classList.add('cm-control-icon-button'); } languageSetting.addDropdown((dropdown) => { @@ -771,15 +714,15 @@ export class ColorMasterSettingTab extends PluginSettingTab { this.initSearchUI(containerEl); this.staticContentContainer = containerEl.createDiv({ - cls: "cm-static-sections", + cls: 'cm-static-sections', }); drawProfileManager(this.staticContentContainer, this); drawImportExport(this.staticContentContainer, this); drawOptionsSection(this.staticContentContainer, this); - this.staticContentContainer.createEl("hr"); + this.staticContentContainer.createEl('hr'); drawCssSnippetsUI(this.staticContentContainer, this); drawColorPickers(this.containerEl, this, themeDefaults); - containerEl.createEl("hr"); + containerEl.createEl('hr'); drawLikePluginCard(containerEl, this); //---Implement automatic search and scrolling--- @@ -791,45 +734,39 @@ export class ColorMasterSettingTab extends PluginSettingTab { setTimeout(() => { // Find the first "visible" line const firstVisibleRow = this.containerEl.querySelector( - ".cm-var-row:not(.cm-hidden)", + '.cm-var-row:not(.cm-hidden)', ); if (firstVisibleRow) { firstVisibleRow.scrollIntoView({ - behavior: "smooth", - block: "center", + behavior: 'smooth', + block: 'center', }); } }, 0); } - const scrollContainer = this.containerEl.closest( - ".vertical-tab-content", - ); + const scrollContainer = this.containerEl.closest('.vertical-tab-content'); if (!scrollContainer) { - console.warn("Color Master: Could not find scroll container."); + console.warn('Color Master: Could not find scroll container.'); } else { const debouncedScrollSave = debounce(() => { this.plugin.settings.lastScrollPosition = scrollContainer.scrollTop; void this.plugin.saveData(this.plugin.settings).catch((err) => { - console.error("Failed to save scroll position:", err); + console.error('Failed to save scroll position:', err); }); }, 200); - this.plugin.registerDomEvent( - scrollContainer, - "scroll", - debouncedScrollSave, - ); + this.plugin.registerDomEvent(scrollContainer, 'scroll', debouncedScrollSave); if (!this._searchState.query && this.plugin.settings.lastScrollPosition) { scrollContainer.scrollTo({ top: this.plugin.settings.lastScrollPosition, - behavior: "auto", + behavior: 'auto', }); } } - containerEl.classList.remove("color-master-hidden"); + containerEl.classList.remove('color-master-hidden'); } } diff --git a/src/utils.ts b/src/utils.ts index cd29cba..c103ab8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ -import { App, DataAdapter } from "obsidian"; -import type { Profile } from "./types"; +import { App, DataAdapter } from 'obsidian'; +import type { Profile } from './types'; // Flattens nested variable objects into a single level map export function flattenVars(varsObject: { [key: string]: unknown }): { @@ -14,7 +14,7 @@ export function flattenVars(varsObject: { [key: string]: unknown }): { // Standard relative luminance calculation export function getLuminance(hex: string): number { - const rgb = parseInt(hex.startsWith("#") ? hex.substring(1) : hex, 16); + const rgb = parseInt(hex.startsWith('#') ? hex.substring(1) : hex, 16); const r = (rgb >> 16) & 0xff; const g = (rgb >> 8) & 0xff; const b = (rgb >> 0) & 0xff; @@ -40,22 +40,19 @@ export function getContrastRatio(hex1: string, hex2: string): number { export function getAccessibilityRating(ratio: number) { const score = ratio.toFixed(2); if (ratio >= 7) { - return { text: "AAA", score, cls: "cm-accessibility-pass" }; + return { text: 'AAA', score, cls: 'cm-accessibility-pass' }; } if (ratio >= 4.5) { - return { text: "AA", score, cls: "cm-accessibility-pass" }; + return { text: 'AA', score, cls: 'cm-accessibility-pass' }; } if (ratio >= 3) { - return { text: "AA Large", score, cls: "cm-accessibility-warn" }; + return { text: 'AA Large', score, cls: 'cm-accessibility-warn' }; } - return { text: "Fail", score, cls: "cm-accessibility-fail" }; + return { text: 'Fail', score, cls: 'cm-accessibility-fail' }; } // Checks if a plugin is both installed AND enabled -export function isPluginEnabled( - app: App, - pluginIds: string | string[], -): boolean { +export function isPluginEnabled(app: App, pluginIds: string | string[]): boolean { const pluginManager = (app as unknown).plugins; const idsToCheck = Array.isArray(pluginIds) ? pluginIds : [pluginIds]; @@ -64,7 +61,7 @@ export function isPluginEnabled( // Helper check for Iconize or Icon Folder export function isIconizeEnabled(app: App): boolean { - const iconizeIDs = ["obsidian-icon-folder", "iconize"]; + const iconizeIDs = ['obsidian-icon-folder', 'iconize']; return isPluginEnabled(app, iconizeIDs); } @@ -78,33 +75,26 @@ export function debounce(fn: (...args: unknown[]) => void, ms = 200) { } // Auto-increments filename if path exists (e.g., file-2.png) -export async function findNextAvailablePath( - adapter: DataAdapter, - path: string, -): Promise { +export async function findNextAvailablePath(adapter: DataAdapter, path: string): Promise { if (!(await adapter.exists(path))) { return path; } - const pathParts = path.split("/"); + const pathParts = path.split('/'); const fullFileName = pathParts.pop(); if (!fullFileName) return path; - const dir = pathParts.join("/"); - const fileNameParts = fullFileName.split("."); - const ext = fileNameParts.length > 1 ? fileNameParts.pop() : ""; - const baseName = fileNameParts.join("."); + const dir = pathParts.join('/'); + const fileNameParts = fullFileName.split('.'); + const ext = fileNameParts.length > 1 ? fileNameParts.pop() : ''; + const baseName = fileNameParts.join('.'); let counter = 2; - let newPath = ext - ? `${dir}/${baseName}-${counter}.${ext}` - : `${dir}/${baseName}-${counter}`; + let newPath = ext ? `${dir}/${baseName}-${counter}.${ext}` : `${dir}/${baseName}-${counter}`; while (await adapter.exists(newPath)) { counter++; - newPath = ext - ? `${dir}/${baseName}-${counter}.${ext}` - : `${dir}/${baseName}-${counter}`; + newPath = ext ? `${dir}/${baseName}-${counter}.${ext}` : `${dir}/${baseName}-${counter}`; } return newPath; } @@ -115,16 +105,11 @@ export async function maybeConvertToJpg( arrayBuffer: ArrayBuffer, fileName: string, ): Promise<{ arrayBuffer: ArrayBuffer; fileName: string }> { - const fileExt = fileName.split(".").pop()?.toLowerCase(); - const isAlreadyJpg = fileExt === "jpg" || fileExt === "jpeg"; - const supportedToConvert = ["png", "webp", "bmp"].includes(fileExt || ""); + const fileExt = fileName.split('.').pop()?.toLowerCase(); + const isAlreadyJpg = fileExt === 'jpg' || fileExt === 'jpeg'; + const supportedToConvert = ['png', 'webp', 'bmp'].includes(fileExt || ''); - if ( - !activeProfile || - !activeProfile.convertImagesToJpg || - isAlreadyJpg || - !supportedToConvert - ) { + if (!activeProfile || !activeProfile.convertImagesToJpg || isAlreadyJpg || !supportedToConvert) { return { arrayBuffer, fileName }; } @@ -136,18 +121,18 @@ export async function maybeConvertToJpg( const image = new Image(); image.onload = () => { - const canvas = document.createElement("canvas"); + const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; - const ctx = canvas.getContext("2d"); + const ctx = canvas.getContext('2d'); if (!ctx) { URL.revokeObjectURL(url); - return reject(new Error("Failed to get canvas context")); + return reject(new Error('Failed to get canvas context')); } // Fill background white since JPG doesn't support alpha - ctx.fillStyle = "#FFFFFF"; + ctx.fillStyle = '#FFFFFF'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.drawImage(image, 0, 0); @@ -158,15 +143,14 @@ export async function maybeConvertToJpg( canvas.toBlob( (jpgBlob) => { if (!jpgBlob) { - reject(new Error("Failed to create JPG blob")); + reject(new Error('Failed to create JPG blob')); return; } jpgBlob .arrayBuffer() .then((newArrayBuffer) => { - const newFileName = - fileName.substring(0, fileName.lastIndexOf(".")) + ".jpg"; + const newFileName = fileName.substring(0, fileName.lastIndexOf('.')) + '.jpg'; console.debug( `Color Master: Conversion complete. New size: ${newArrayBuffer.byteLength} bytes`, @@ -178,14 +162,14 @@ export async function maybeConvertToJpg( reject(err instanceof Error ? err : new Error(String(err))); }); }, - "image/jpeg", + 'image/jpeg', quality, ); }; image.onerror = () => { URL.revokeObjectURL(url); - reject(new Error("Failed to load image for conversion")); + reject(new Error('Failed to load image for conversion')); }; image.src = url; @@ -193,14 +177,12 @@ export async function maybeConvertToJpg( } // Reconstructs nested object from dot-notation keys -export function unflattenStrings( - flatObject: Record, -): Record { +export function unflattenStrings(flatObject: Record): Record { const nestedResult: Record = {}; for (const key in flatObject) { if (Object.prototype.hasOwnProperty.call(flatObject, key)) { - const keys = key.split("."); + const keys = key.split('.'); let currentLevel = nestedResult; for (let i = 0; i < keys.length; i++) { @@ -209,7 +191,7 @@ export function unflattenStrings( if (i === keys.length - 1) { currentLevel[part] = flatObject[key] as unknown; } else { - if (!currentLevel[part] || typeof currentLevel[part] !== "object") { + if (!currentLevel[part] || typeof currentLevel[part] !== 'object') { currentLevel[part] = {}; } currentLevel = currentLevel[part] as Record; @@ -222,19 +204,19 @@ export function unflattenStrings( function componentToHex(c: number): string { const hex = Math.round(c).toString(16); - return hex.length == 1 ? "0" + hex : hex; + return hex.length == 1 ? '0' + hex : hex; } // Normalizes any CSS color string to Hex/HexA format export function convertColorToHex(colorString: string): string { const s = colorString.toLowerCase().trim(); - if (s === "transparent") { - return "#00000000"; + if (s === 'transparent') { + return '#00000000'; } // Expand short hex codes if needed - if (s.startsWith("#")) { + if (s.startsWith('#')) { if (s.length === 4) { const r = s[1]; const g = s[2]; @@ -252,7 +234,7 @@ export function convertColorToHex(colorString: string): string { } // Use the DOM to normalize other formats (rgb, hsl, names) - const d = document.createElement("div"); + const d = document.createElement('div'); d.style.color = s; document.body.appendChild(d); @@ -260,9 +242,7 @@ export function convertColorToHex(colorString: string): string { const computedColor = getComputedStyle(d).color; document.body.removeChild(d); - const match = computedColor.match( - /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/, - ); + const match = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/); if (match) { const r = parseInt(match[1]); @@ -297,7 +277,7 @@ if (!HTMLElement.prototype.setCssProps) { for (const key in props) { const value = props[key]; - if (value === null || value === undefined || value === "") { + if (value === null || value === undefined || value === '') { this.style.removeProperty(key); } else { try {