diff --git a/package.json b/package.json index 982a034..2d88bc8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "obsitexcore", "version": "0.0.1", - "description": "An Obsidian.md plugin that provides a powerful indexing & referencing system for theorems & equations in your vault. Bring LaTeX-like workflow into Obsidian with theorem environments, automatic equation numbering, and more.", + "description": "Automatic equation numbering, Tex Diagrams rendering, and more for Obsidian.md", "main": "main.js", "packageManager": "pnpm@11.1.2+sha512.415a1cc25974731e75455c1468371be74c5aa5fb7621b50d4056d222451609f11412f23fd602e6169f1e060466641f798597e1be961a10688836a67b16569499", "scripts": { diff --git a/src/i18n/en.json b/src/i18n/en.json index aee7098..7b77490 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1,13 +1,12 @@ { "settings": { "title": "ObsiTeXcore settings", + "description": "Switch between focused setting groups to keep configuration lighter and easier to scan.", "tab": { "general": "General", - "generalDesc": "Configure equation numbering, referencing, autocomplete, and Zotero clean up settings.", - "pdf": "PDF Export", - "pdfDesc": "Configure layout, templates, and rendering limits for PDF exports.", - "tikz": "TikZJax", - "tikzDesc": "Configure TikZ rendering options and dark mode behavior.", + "generalDesc": "Configure equation numbering, referencing, and autocomplete settings.", + "integrations": "Integrations", + "integrationsDesc": "Configure integrations such as pdf exports, Tikzjax rendering, and Zotero clean up.", "hotkeys": "Custom Notes", "hotkeysDesc": "Configure keyboard shortcuts to quickly open specific notes.", "changelog": "What's new", diff --git a/src/main.ts b/src/main.ts index e255aba..869d46c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -90,15 +90,7 @@ export default class LatexReferencer extends Plugin { this.addSettingTab(new MathSettingTab(this.app, this)); // Commands - this.addCommand({ - id: 'remove-duplicate-zotero-annotations', - name: 'Remove duplicate Zotero annotations', - editorCallback: (editor: Editor, ctx: MarkdownView | MarkdownFileInfo) => { - if (ctx instanceof MarkdownView) { - void processZoteroCleanup(this, ctx); - } - } - }); + this.registerZoteroCommand(); this.addCommand({ id: 'fix-callout-equations', @@ -348,6 +340,34 @@ export default class LatexReferencer extends Plugin { this.register(uninstaller); } + registerZoteroCommand() { + const commandId = 'remove-duplicate-zotero-annotations'; + const fullCommandId = `${this.manifest.id}:${commandId}`; + const appCommands = this.app.commands; + + // 1. Unregister if it exists + if (appCommands && typeof appCommands.removeCommand === 'function') { + try { + appCommands.removeCommand(fullCommandId); + } catch { + // Safe to ignore + } + } + + // 2. Register if enabled + if (this.settings.enableZoteroCleanup) { + this.addCommand({ + id: commandId, + name: 'Remove duplicate Zotero annotations', + editorCallback: (editor: Editor, ctx: MarkdownView | MarkdownFileInfo) => { + if (ctx instanceof MarkdownView) { + void processZoteroCleanup(this, ctx); + } + } + }); + } + } + async generateToc(root: TFolder | TFile) { const tocPath = root.path === '/' || root.path === '.' ? '_TOC_.md' : `${root.path}/_TOC_.md`; let content = `---\ntoc: true\ntitle: ${root.name || 'Root'}\n---\n`; diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 06f3571..d2c4b4b 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -68,6 +68,7 @@ export interface PluginSettings { concurrency: string; // Zotero Cleanup + enableZoteroCleanup: boolean; zoteroCleanDirectories: string; // Custom Note Hotkeys @@ -146,6 +147,7 @@ export const DEFAULT_SETTINGS: Required = { concurrency: '5', // Zotero Cleanup + enableZoteroCleanup: false, zoteroCleanDirectories: '', // Custom Note Hotkeys diff --git a/src/settings/tab.ts b/src/settings/tab.ts index 72713a4..75fbca5 100644 --- a/src/settings/tab.ts +++ b/src/settings/tab.ts @@ -6,8 +6,9 @@ import { setCssProps } from 'utils/obsidian'; import { createDescWithDocs } from './docsLinks'; import { changelogData } from './changelogData'; import { t } from '../i18n/t'; +import { SettingsGroupModal } from '../ui/modals/SettingsGroupModal'; -type SettingsTabId = 'general' | 'pdf' | 'tikz' | 'hotkeys' | 'changelog'; +type SettingsTabId = 'general' | 'integrations' | 'hotkeys' | 'changelog'; interface TabCategory { id: SettingsTabId; @@ -22,14 +23,9 @@ const TABS: TabCategory[] = [ descKey: 'settings.tab.generalDesc' }, { - id: 'pdf', - labelKey: 'settings.tab.pdf', - descKey: 'settings.tab.pdfDesc' - }, - { - id: 'tikz', - labelKey: 'settings.tab.tikz', - descKey: 'settings.tab.tikzDesc' + id: 'integrations', + labelKey: 'settings.tab.integrations', + descKey: 'settings.tab.integrationsDesc' }, { id: 'hotkeys', @@ -68,7 +64,10 @@ export class MathSettingTab extends PluginSettingTab { // Header const headerEl = shellEl.createDiv('obsitexcore-settings-header'); - new Setting(headerEl).setName(t('settings.title')).setHeading(); + headerEl.createEl('p', { + text: t('settings.description'), + cls: 'obsitexcore-settings-header-desc' + }); // Tabs & Search row const tabsRowEl = shellEl.createDiv('obsitexcore-settings-tabs-row'); @@ -224,11 +223,8 @@ export class MathSettingTab extends PluginSettingTab { case 'general': this.renderGeneral(panelEl); break; - case 'pdf': - this.renderPdf(panelEl); - break; - case 'tikz': - this.renderTikz(panelEl); + case 'integrations': + this.renderIntegrations(panelEl); break; case 'hotkeys': this.renderHotkeys(panelEl); @@ -240,6 +236,46 @@ export class MathSettingTab extends PluginSettingTab { } private renderGeneral(containerEl: HTMLElement): void { + const isSearching = !!this.searchQuery.trim(); + + if (isSearching) { + this.renderDetailedGeneralSettings(containerEl); + this.renderDetailedAutocompleteSettings(containerEl); + return; + } + + new Setting(containerEl) + .setName('Equation numbering & referencing') + .setDesc( + 'Configure automatic numbering prefixes, suffixes, initial counts, styles, and link formatting.' + ) + .addExtraButton(button => { + button + .setIcon('gear') + .setTooltip('Configure options') + .onClick(() => { + new SettingsGroupModal(this.app, 'Equation Numbering & Referencing', bodyEl => + this.renderDetailedGeneralSettings(bodyEl) + ).open(); + }); + }); + + new Setting(containerEl) + .setName('Autocomplete & search') + .setDesc('Configure triggers, autocompletion options, and rendering behaviors.') + .addExtraButton(button => { + button + .setIcon('gear') + .setTooltip('Configure options') + .onClick(() => { + new SettingsGroupModal(this.app, 'Autocomplete & Search', bodyEl => + this.renderDetailedAutocompleteSettings(bodyEl) + ).open(); + }); + }); + } + + private renderDetailedGeneralSettings(containerEl: HTMLElement): void { new Setting(containerEl).setName('Equation numbering & referencing').setHeading(); new Setting(containerEl) @@ -316,7 +352,9 @@ export class MathSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }) ); + } + private renderDetailedAutocompleteSettings(containerEl: HTMLElement): void { new Setting(containerEl).setName('Autocomplete & search').setHeading(); new Setting(containerEl) @@ -346,9 +384,22 @@ export class MathSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }) ); + } + private renderDetailedZoteroSettings(containerEl: HTMLElement): void { new Setting(containerEl).setName('Zotero cleanup').setHeading(); + new Setting(containerEl) + .setName('Enable Zotero cleanup') + .setDesc('Enable the command to remove duplicate Zotero annotations in your active notes.') + .addToggle(toggle => + toggle.setValue(this.plugin.settings.enableZoteroCleanup).onChange(async value => { + this.plugin.settings.enableZoteroCleanup = value; + await this.plugin.saveSettings(); + this.plugin.registerZoteroCommand(); + }) + ); + new Setting(containerEl) .setName('Directories to search') .setDesc( @@ -363,7 +414,7 @@ export class MathSettingTab extends PluginSettingTab { }); } - private renderPdf(containerEl: HTMLElement): void { + private renderDetailedPdfSettings(containerEl: HTMLElement): void { new Setting(containerEl).setName('Pdf export').setHeading(); new Setting(containerEl) @@ -518,7 +569,7 @@ export class MathSettingTab extends PluginSettingTab { }); } - private renderTikz(containerEl: HTMLElement): void { + private renderDetailedTikzSettings(containerEl: HTMLElement): void { new Setting(containerEl).setName('TikZJax rendering').setHeading(); new Setting(containerEl) @@ -546,6 +597,59 @@ export class MathSettingTab extends PluginSettingTab { ); } + private renderIntegrations(containerEl: HTMLElement): void { + const isSearching = !!this.searchQuery.trim(); + + if (isSearching) { + this.renderDetailedPdfSettings(containerEl); + this.renderDetailedTikzSettings(containerEl); + this.renderDetailedZoteroSettings(containerEl); + return; + } + + new Setting(containerEl) + .setName('Pdf export options') + .setDesc('Configure layout, templates, and rendering limits for pdf exports.') + .addExtraButton(button => { + button + .setIcon('gear') + .setTooltip('Configure options') + .onClick(() => { + new SettingsGroupModal(this.app, 'PDF Export Options', bodyEl => + this.renderDetailedPdfSettings(bodyEl) + ).open(); + }); + }); + + new Setting(containerEl) + .setName('Tikzjax rendering') + .setDesc('Configure TikZ rendering options and dark mode behavior.') + .addExtraButton(button => { + button + .setIcon('gear') + .setTooltip('Configure options') + .onClick(() => { + new SettingsGroupModal(this.app, 'TikZJax Rendering Options', bodyEl => + this.renderDetailedTikzSettings(bodyEl) + ).open(); + }); + }); + + new Setting(containerEl) + .setName('Zotero cleanup') + .setDesc('Configure Zotero search directories and automatic annotations cleanup.') + .addExtraButton(button => { + button + .setIcon('gear') + .setTooltip('Configure options') + .onClick(() => { + new SettingsGroupModal(this.app, 'Zotero Cleanup Options', bodyEl => + this.renderDetailedZoteroSettings(bodyEl) + ).open(); + }); + }); + } + private renderHotkeys(containerEl: HTMLElement): void { new Setting(containerEl).setName('Custom note hotkeys').setHeading(); containerEl.createEl('p', { @@ -779,7 +883,9 @@ export class MathSettingTab extends PluginSettingTab { emoji = '🛠️'; } - const itemEl = verContainer.createDiv(`full-calendar-change-item full-calendar-change-type-${change.type}`); + const itemEl = verContainer.createDiv( + `full-calendar-change-item full-calendar-change-type-${change.type}` + ); itemEl.createDiv({ cls: 'full-calendar-change-icon', text: emoji diff --git a/src/styles/main.css b/src/styles/main.css index edb352d..95ae12f 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -360,13 +360,19 @@ .obsitexcore-settings-shell { display: flex; flex-direction: column; - gap: var(--size-4-3); - margin-top: 15px; + gap: var(--size-4-5); } .obsitexcore-settings-header { - border-bottom: 1px solid var(--background-modifier-border); - padding-bottom: 10px; + display: flex; + flex-direction: column; + gap: var(--size-4-2); +} + +.obsitexcore-settings-header-desc { + margin: 0; + color: var(--text-muted); + font-size: var(--font-ui-medium); } .obsitexcore-settings-tabs-row { @@ -441,11 +447,19 @@ } .obsitexcore-settings-category-intro { - font-size: 0.95em; - color: var(--text-muted); + display: flex; + flex-direction: column; + gap: var(--size-4-1); + padding-bottom: var(--size-4-2); + border-bottom: 1px solid var(--background-modifier-border); margin-bottom: 15px; } +.obsitexcore-settings-category-intro p { + margin: 0; + color: var(--text-muted); +} + .obsitexcore-settings-panel { display: flex; flex-direction: column; @@ -537,4 +551,43 @@ .full-calendar-change-description { font-size: var(--font-ui-small); color: var(--text-muted); +} + +/* ====== Settings Modals (Exact styles from full-calendar) ====== */ +.ofc-settings-modal-wide { + width: min(900px, 88vw); + max-width: 900px; + max-height: min(860px, 88vh); +} + +.ofc-settings-modal-wide .modal-content { + padding: 0; + overflow: hidden; +} + +.ofc-settings-modal-shell { + display: flex; + flex-direction: column; + max-height: min(860px, 88vh); + min-height: min(620px, 82vh); +} + +.ofc-settings-modal-body { + flex: 1 1 auto; + overflow-y: auto; + padding: 18px 22px; +} + +.ofc-settings-modal-footer { + align-items: center; + background-color: var(--background-primary); + border-top: 1px solid var(--background-modifier-border); + display: flex; + flex: 0 0 auto; + gap: 10px; + justify-content: flex-end; + padding: 12px 22px; + position: sticky; + bottom: 0; + z-index: 1; } \ No newline at end of file diff --git a/src/ui/modals/SettingsGroupModal.ts b/src/ui/modals/SettingsGroupModal.ts new file mode 100644 index 0000000..513397e --- /dev/null +++ b/src/ui/modals/SettingsGroupModal.ts @@ -0,0 +1,44 @@ +import { App, Modal, ButtonComponent } from 'obsidian'; + +export class SettingsGroupModal extends Modal { + constructor( + app: App, + private modalTitle: string, + private onRender: (bodyEl: HTMLElement) => void, + private onCloseCallback?: () => void + ) { + super(app); + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + this.modalEl.addClass('ofc-settings-modal-wide'); + + const shellEl = contentEl.createDiv('ofc-settings-modal-shell'); + + // Header + const headerEl = shellEl.createDiv('modal-header'); + headerEl.createEl('h2', { text: this.modalTitle }); + + // Body + const bodyEl = shellEl.createDiv('ofc-settings-modal-body'); + this.onRender(bodyEl); + + // Footer + const footerEl = shellEl.createDiv('ofc-settings-modal-footer'); + new ButtonComponent(footerEl) + .setButtonText('Close') + .setCta() + .onClick(() => { + this.close(); + }); + } + + onClose() { + this.contentEl.empty(); + if (this.onCloseCallback) { + this.onCloseCallback(); + } + } +} diff --git a/src/ui/modals/WhatsNewModal.ts b/src/ui/modals/WhatsNewModal.ts index 87e5c7b..c4ff370 100644 --- a/src/ui/modals/WhatsNewModal.ts +++ b/src/ui/modals/WhatsNewModal.ts @@ -66,7 +66,9 @@ export class WhatsNewModal extends Modal { emoji = '🛠️'; } - const itemEl = changeList.createDiv(`full-calendar-change-item full-calendar-change-type-${change.type}`); + const itemEl = changeList.createDiv( + `full-calendar-change-item full-calendar-change-type-${change.type}` + ); itemEl.createDiv({ cls: 'full-calendar-change-icon', text: emoji diff --git a/versions.json b/versions.json index bfeef45..1ce85c9 100644 --- a/versions.json +++ b/versions.json @@ -1,7 +1,3 @@ { - "0.3.0": "1.3.5", - "0.3.1": "1.3.5", - "0.3.2": "1.3.5", - "2.2.3": "1.3.5", - "2.2.4": "1.3.5" + "0.0.1": "1.3.5" }