mirror of
https://github.com/youfoundjk/TeXcore.git
synced 2026-07-22 07:33:31 +00:00
feat: redesign settings UI, add Integrations tab, and support dynamic Zotero cleanup command
- Renamed TikZJax tab to Integrations and consolidated PDF Export, TikZJax, and Zotero cleanup under it - Grouped PDF Export, TikZJax, and Zotero cleanup configurations inside modular gear-button modals using SettingsGroupModal - Added an "Enable Zotero cleanup" toggle to dynamically register/unregister the `remove-duplicate-zotero-annotations` command - Restructured headers, line separators, and vertical spacing in CSS to match the full-calendar design - Updated translation keys in en.json and corrected casing across settings descriptions - Verified that all lint checks, type check compilations, and Jest tests pass with zero errors
This commit is contained in:
parent
7c08b356d4
commit
2cf270523a
9 changed files with 267 additions and 45 deletions
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
38
src/main.ts
38
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`;
|
||||
|
|
|
|||
|
|
@ -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<PluginSettings> = {
|
|||
concurrency: '5',
|
||||
|
||||
// Zotero Cleanup
|
||||
enableZoteroCleanup: false,
|
||||
zoteroCleanDirectories: '',
|
||||
|
||||
// Custom Note Hotkeys
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
44
src/ui/modals/SettingsGroupModal.ts
Normal file
44
src/ui/modals/SettingsGroupModal.ts
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue