mirror of
https://github.com/youfoundjk/TeXcore.git
synced 2026-07-22 07:33:31 +00:00
feat(settings): redesign settings tab with categories, search, i18n, and what's new modal
- Redesigned the settings tab to support structured categories (General, PDF Export, TikZJax, Custom Notes, and What's New) - Implemented a search-based filter in settings that dynamically hides/shows items and highlights matching terms - Added i18n support with `en.json` and a type-safe `t()` utility to handle localized sentence-case labels - Integrated a startup check and "What's New" modal to display the latest updates on first launch or version updates - Declared global `activeDocument` and `activeWindow` properties on the Window interface to ensure strict TypeScript type-safety - Fixed all ESLint issues including deprecated `display()` calls, manual HTML headings, sentence case warnings, and unused variables - Added documentation links support and a feedback footer at the bottom of the settings tab
This commit is contained in:
parent
3dc3333ab6
commit
7c08b356d4
13 changed files with 995 additions and 47 deletions
7
src/declarations.d.ts
vendored
7
src/declarations.d.ts
vendored
|
|
@ -60,3 +60,10 @@ declare module 'obsidian' {
|
|||
export type LeafArgs =
|
||||
| [newLeaf?: PaneType | boolean]
|
||||
| [newLeaf?: 'split', direction?: SplitDirection];
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
activeDocument: Document;
|
||||
activeWindow: Window;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
src/i18n/en.json
Normal file
33
src/i18n/en.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"settings": {
|
||||
"title": "ObsiTeXcore settings",
|
||||
"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.",
|
||||
"hotkeys": "Custom Notes",
|
||||
"hotkeysDesc": "Configure keyboard shortcuts to quickly open specific notes.",
|
||||
"changelog": "What's new",
|
||||
"changelogDesc": "View recent changes and new features."
|
||||
},
|
||||
"header": {
|
||||
"whatsNew": "What's new"
|
||||
},
|
||||
"footer": {
|
||||
"question": "Do you like what you see?",
|
||||
"supportOnKofi": "☕ Buy me a coffee",
|
||||
"suggestFeature": "💡 Suggest a new feature",
|
||||
"reportBug": "🐛 Raise an issue"
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
"whatsNew": {
|
||||
"title": "What's new in ObsiTeXcore",
|
||||
"openSettings": "Open settings",
|
||||
"supportKofi": "Support development / ko-fi"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/i18n/t.ts
Normal file
23
src/i18n/t.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import en from './en.json';
|
||||
|
||||
export function t(key: string, vars?: Record<string, string>): string {
|
||||
const parts = key.split('.');
|
||||
let current: unknown = en;
|
||||
for (const part of parts) {
|
||||
if (current && typeof current === 'object' && part in current) {
|
||||
current = (current as Record<string, unknown>)[part];
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
if (typeof current !== 'string') {
|
||||
return key;
|
||||
}
|
||||
let result: string = current;
|
||||
if (vars) {
|
||||
for (const [k, v] of Object.entries(vars)) {
|
||||
result = result.replace(new RegExp(`\\{${k}\\}`, 'g'), v);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
14
src/main.ts
14
src/main.ts
|
|
@ -59,6 +59,20 @@ export default class LatexReferencer extends Plugin {
|
|||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// Check version and show What's New modal if upgraded/first install
|
||||
const releaseVersion = this.manifest.version;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
if (
|
||||
this.settings.currentVersion === null ||
|
||||
this.settings.currentVersion !== releaseVersion
|
||||
) {
|
||||
const { WhatsNewModal } = await import('./ui/modals/WhatsNewModal');
|
||||
new WhatsNewModal(this.app, this.manifest.id).open();
|
||||
this.settings.currentVersion = releaseVersion;
|
||||
await this.saveSettings();
|
||||
}
|
||||
});
|
||||
|
||||
this.internalProviders.push(new LatexLinkProvider(this));
|
||||
|
||||
// Snippets
|
||||
|
|
|
|||
50
src/settings/changelogData.ts
Normal file
50
src/settings/changelogData.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @file changelogData.ts
|
||||
* @brief Version changelog data for ObsiTeXcore.
|
||||
*/
|
||||
|
||||
export interface ChangelogItem {
|
||||
version: string;
|
||||
date: string;
|
||||
changes: {
|
||||
type: 'new' | 'improvement' | 'fix';
|
||||
description: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const changelogData: ChangelogItem[] = [
|
||||
{
|
||||
version: '0.0.1',
|
||||
date: '2026-06-04',
|
||||
changes: [
|
||||
{
|
||||
type: 'new',
|
||||
description: 'Equation Numbering: Automatic theorem environments and equation referencing.'
|
||||
},
|
||||
{
|
||||
type: 'new',
|
||||
description:
|
||||
'LaTeX Autocomplete: Fast suggestions and search for equations/notes in your vault.'
|
||||
},
|
||||
{
|
||||
type: 'new',
|
||||
description:
|
||||
'PDF Export: Custom print templates (headers, footers) with background graphics support.'
|
||||
},
|
||||
{
|
||||
type: 'new',
|
||||
description: 'TikZJax Rendering: Adapt diagrams dynamically to light and dark themes.'
|
||||
},
|
||||
{
|
||||
type: 'new',
|
||||
description:
|
||||
'Custom Notes: Assign custom keyboard shortcuts to instantly open preferred notes.'
|
||||
},
|
||||
{
|
||||
type: 'new',
|
||||
description:
|
||||
'Quick Preview: Inline equation and reference previewing directly in autocomplete lists.'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
60
src/settings/docsLinks.ts
Normal file
60
src/settings/docsLinks.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* @file docsLinks.ts
|
||||
* @brief Shared helpers for adding documentation links to settings descriptions.
|
||||
*/
|
||||
|
||||
import {
|
||||
createLinksFragment,
|
||||
createMarkdownLinksFragment,
|
||||
LinkItem,
|
||||
linkItemsToSegments
|
||||
} from './linkTextFragments';
|
||||
|
||||
export interface DocsLink {
|
||||
text: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
const DOCS_ROOT = 'https://youfoundjk.github.io/ObsiTeXcore/';
|
||||
|
||||
export function toDocsUrl(path: string): string {
|
||||
return `${DOCS_ROOT}${path.replace(/^\/+/, '')}`;
|
||||
}
|
||||
|
||||
export function createDocsLinksFragment(
|
||||
links: DocsLink[],
|
||||
prefix = 'Learn more'
|
||||
): DocumentFragment {
|
||||
const doc = window.activeDocument ?? window.activeWindow?.document ?? window.document;
|
||||
if (!doc) {
|
||||
return new DocumentFragment();
|
||||
}
|
||||
if (links.length === 0) {
|
||||
return doc.createDocumentFragment();
|
||||
}
|
||||
|
||||
const fragment = doc.createDocumentFragment();
|
||||
fragment.append(prefix, ' ');
|
||||
|
||||
const linkItems: LinkItem[] = links.map(link => ({
|
||||
text: link.text,
|
||||
href: toDocsUrl(link.path)
|
||||
}));
|
||||
|
||||
fragment.append(createLinksFragment(linkItemsToSegments(linkItems), { betweenLinksText: ' | ' }));
|
||||
return fragment;
|
||||
}
|
||||
|
||||
export function createDescWithDocs(description: string, links: DocsLink[]): DocumentFragment {
|
||||
const doc = window.activeDocument ?? window.activeWindow?.document ?? window.document;
|
||||
if (!doc) {
|
||||
return createMarkdownLinksFragment(description);
|
||||
}
|
||||
const fragment = doc.createDocumentFragment();
|
||||
fragment.append(createMarkdownLinksFragment(description));
|
||||
if (links.length > 0) {
|
||||
fragment.append(' ');
|
||||
fragment.append(createDocsLinksFragment(links));
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
82
src/settings/linkTextFragments.ts
Normal file
82
src/settings/linkTextFragments.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* @file linkTextFragments.ts
|
||||
* @brief Shared helpers to render text with links as DocumentFragment segments.
|
||||
*/
|
||||
|
||||
export type LinkTextSegment =
|
||||
| { kind: 'text'; text: string }
|
||||
| { kind: 'link'; text: string; href: string };
|
||||
|
||||
export interface LinkItem {
|
||||
text: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const MARKDOWN_LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
|
||||
|
||||
export function parseMarkdownLinks(text: string): LinkTextSegment[] {
|
||||
const segments: LinkTextSegment[] = [];
|
||||
let lastIndex = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) {
|
||||
const [fullMatch, linkText, href] = match;
|
||||
|
||||
if (match.index > lastIndex) {
|
||||
segments.push({ kind: 'text', text: text.substring(lastIndex, match.index) });
|
||||
}
|
||||
|
||||
segments.push({ kind: 'link', text: linkText, href });
|
||||
lastIndex = match.index + fullMatch.length;
|
||||
}
|
||||
|
||||
if (lastIndex < text.length) {
|
||||
segments.push({ kind: 'text', text: text.substring(lastIndex) });
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
export function createMarkdownLinksFragment(text: string): DocumentFragment {
|
||||
return createLinksFragment(parseMarkdownLinks(text));
|
||||
}
|
||||
|
||||
export function createLinksFragment(
|
||||
segments: LinkTextSegment[],
|
||||
options?: { betweenLinksText?: string }
|
||||
): DocumentFragment {
|
||||
const doc = window.activeDocument ?? window.activeWindow?.document ?? window.document;
|
||||
if (!doc) {
|
||||
return new DocumentFragment();
|
||||
}
|
||||
const fragment = doc.createDocumentFragment();
|
||||
const betweenLinksText = options?.betweenLinksText;
|
||||
let previousSegmentWasLink = false;
|
||||
|
||||
segments.forEach(segment => {
|
||||
if (betweenLinksText && previousSegmentWasLink && segment.kind === 'link') {
|
||||
fragment.append(betweenLinksText);
|
||||
}
|
||||
|
||||
if (segment.kind === 'text') {
|
||||
fragment.append(segment.text);
|
||||
previousSegmentWasLink = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const linkEl = doc.createElement('a');
|
||||
linkEl.textContent = segment.text;
|
||||
linkEl.href = segment.href;
|
||||
linkEl.target = '_blank';
|
||||
linkEl.rel = 'noopener';
|
||||
fragment.append(linkEl);
|
||||
|
||||
previousSegmentWasLink = true;
|
||||
});
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
export function linkItemsToSegments(items: LinkItem[]): LinkTextSegment[] {
|
||||
return items.map(item => ({ kind: 'link', text: item.text, href: item.href }));
|
||||
}
|
||||
|
|
@ -76,6 +76,9 @@ export interface PluginSettings {
|
|||
// TikZJax Settings
|
||||
enableTikzjax: boolean;
|
||||
invertColorsInDarkMode: boolean;
|
||||
|
||||
// Version tracking
|
||||
currentVersion: string | null;
|
||||
}
|
||||
|
||||
export interface CustomNoteHotkey {
|
||||
|
|
@ -87,6 +90,7 @@ export interface CustomNoteHotkey {
|
|||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: Required<PluginSettings> = {
|
||||
currentVersion: null,
|
||||
// Numbering
|
||||
numberOnlyReferencedEquations: true,
|
||||
eqNumberPrefix: '',
|
||||
|
|
|
|||
|
|
@ -1,10 +1,54 @@
|
|||
import { App, PluginSettingTab, Setting, TextAreaComponent } from 'obsidian';
|
||||
import { App, PluginSettingTab, Setting, TextAreaComponent, setIcon } from 'obsidian';
|
||||
import LatexReferencer from 'main';
|
||||
import { NUMBER_STYLES } from './settings';
|
||||
import { NoteSuggestModal } from '../ui/custom-notes/modal';
|
||||
import { setCssProps } from 'utils/obsidian';
|
||||
import { createDescWithDocs } from './docsLinks';
|
||||
import { changelogData } from './changelogData';
|
||||
import { t } from '../i18n/t';
|
||||
|
||||
type SettingsTabId = 'general' | 'pdf' | 'tikz' | 'hotkeys' | 'changelog';
|
||||
|
||||
interface TabCategory {
|
||||
id: SettingsTabId;
|
||||
labelKey: string;
|
||||
descKey: string;
|
||||
}
|
||||
|
||||
const TABS: TabCategory[] = [
|
||||
{
|
||||
id: 'general',
|
||||
labelKey: 'settings.tab.general',
|
||||
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: 'hotkeys',
|
||||
labelKey: 'settings.tab.hotkeys',
|
||||
descKey: 'settings.tab.hotkeysDesc'
|
||||
},
|
||||
{
|
||||
id: 'changelog',
|
||||
labelKey: 'settings.tab.changelog',
|
||||
descKey: 'settings.tab.changelogDesc'
|
||||
}
|
||||
];
|
||||
|
||||
export class MathSettingTab extends PluginSettingTab {
|
||||
private activeTab: SettingsTabId = 'general';
|
||||
private searchQuery = '';
|
||||
private searchExpanded = false;
|
||||
private searchDebounceId: number | null = null;
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
public plugin: LatexReferencer
|
||||
|
|
@ -13,14 +57,199 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
display() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
const shellEl = containerEl.createDiv('obsitexcore-settings-shell');
|
||||
|
||||
// Header
|
||||
const headerEl = shellEl.createDiv('obsitexcore-settings-header');
|
||||
new Setting(headerEl).setName(t('settings.title')).setHeading();
|
||||
|
||||
// Tabs & Search row
|
||||
const tabsRowEl = shellEl.createDiv('obsitexcore-settings-tabs-row');
|
||||
const tabsEl = tabsRowEl.createDiv('obsitexcore-settings-tabs');
|
||||
|
||||
TABS.forEach(tab => {
|
||||
const isActive = tab.id === this.activeTab;
|
||||
const button = tabsEl.createEl('button', {
|
||||
cls: `full-calendar-settings-tab${isActive ? ' is-active' : ''}`,
|
||||
text: t(tab.labelKey)
|
||||
});
|
||||
button.type = 'button';
|
||||
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
||||
button.addEventListener('click', () => {
|
||||
if (this.activeTab === tab.id) return;
|
||||
this.activeTab = tab.id;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
|
||||
// Search wrap
|
||||
const searchWrapEl = tabsRowEl.createDiv('obsitexcore-settings-search-wrap');
|
||||
|
||||
const searchButtonEl = searchWrapEl.createEl('button', {
|
||||
cls: 'clickable-icon obsitexcore-settings-search-trigger'
|
||||
});
|
||||
searchButtonEl.type = 'button';
|
||||
searchButtonEl.ariaLabel = 'Search settings';
|
||||
setIcon(searchButtonEl, 'search');
|
||||
|
||||
const inputWrapEl = searchWrapEl.createDiv('obsitexcore-settings-search-input-wrap');
|
||||
setCssProps(inputWrapEl, {
|
||||
position: 'relative',
|
||||
width: this.searchExpanded || this.searchQuery ? '170px' : '0px',
|
||||
overflow: 'hidden',
|
||||
transition: 'width 140ms ease'
|
||||
});
|
||||
|
||||
const searchInputEl = inputWrapEl.createEl('input', {
|
||||
cls: 'obsitexcore-settings-search-input'
|
||||
});
|
||||
searchInputEl.type = 'text';
|
||||
searchInputEl.placeholder = 'Search settings...';
|
||||
searchInputEl.value = this.searchQuery;
|
||||
|
||||
const clearButtonEl = inputWrapEl.createEl('button', {
|
||||
cls: 'clickable-icon obsitexcore-settings-search-clear'
|
||||
});
|
||||
clearButtonEl.type = 'button';
|
||||
clearButtonEl.ariaLabel = 'Clear search';
|
||||
setCssProps(clearButtonEl, {
|
||||
position: 'absolute',
|
||||
right: '6px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
display: this.searchQuery ? 'inline-flex' : 'none'
|
||||
});
|
||||
setIcon(clearButtonEl, 'x');
|
||||
|
||||
const renderSearchResults = () => {
|
||||
this.renderSettingsContent(contentEl);
|
||||
setCssProps(clearButtonEl, { display: this.searchQuery ? 'inline-flex' : 'none' });
|
||||
setCssProps(searchButtonEl, {
|
||||
display: this.searchExpanded || !!this.searchQuery ? 'none' : 'inline-flex'
|
||||
});
|
||||
searchButtonEl.toggleClass('is-active', this.searchExpanded || !!this.searchQuery);
|
||||
inputWrapEl.toggleClass('is-active-query', !!this.searchQuery);
|
||||
};
|
||||
|
||||
searchButtonEl.addEventListener('click', () => {
|
||||
this.searchExpanded = true;
|
||||
setCssProps(inputWrapEl, { width: '170px' });
|
||||
setCssProps(searchButtonEl, { display: 'none' });
|
||||
searchInputEl.focus();
|
||||
searchButtonEl.toggleClass('is-active', true);
|
||||
});
|
||||
|
||||
searchInputEl.addEventListener('blur', () => {
|
||||
if (this.searchQuery) return;
|
||||
this.searchExpanded = false;
|
||||
setCssProps(inputWrapEl, { width: '0px' });
|
||||
setCssProps(searchButtonEl, { display: 'inline-flex' });
|
||||
searchButtonEl.toggleClass('is-active', false);
|
||||
});
|
||||
|
||||
searchInputEl.addEventListener('input', () => {
|
||||
this.searchQuery = searchInputEl.value;
|
||||
if (this.searchDebounceId !== null) {
|
||||
window.clearTimeout(this.searchDebounceId);
|
||||
}
|
||||
this.searchDebounceId = window.setTimeout(renderSearchResults, 80);
|
||||
});
|
||||
|
||||
clearButtonEl.addEventListener('mousedown', evt => {
|
||||
evt.preventDefault();
|
||||
this.searchQuery = '';
|
||||
searchInputEl.value = '';
|
||||
renderSearchResults();
|
||||
searchInputEl.focus();
|
||||
});
|
||||
|
||||
// Content Panel
|
||||
const contentEl = shellEl.createDiv('obsitexcore-settings-content');
|
||||
this.renderSettingsContent(contentEl);
|
||||
|
||||
// Footer
|
||||
this.renderFooter(shellEl);
|
||||
}
|
||||
|
||||
private renderSettingsContent(containerEl: HTMLElement): void {
|
||||
containerEl.empty();
|
||||
const query = this.searchQuery.trim();
|
||||
|
||||
if (!query) {
|
||||
const activeTab = TABS.find(t => t.id === this.activeTab);
|
||||
if (activeTab) {
|
||||
const introEl = containerEl.createDiv('obsitexcore-settings-category-intro');
|
||||
introEl.createEl('p', { text: t(activeTab.descKey) });
|
||||
}
|
||||
const panelEl = containerEl.createDiv('obsitexcore-settings-panel');
|
||||
this.renderCategoryContent(this.activeTab, panelEl);
|
||||
return;
|
||||
}
|
||||
|
||||
let hasAnyMatches = false;
|
||||
for (const tab of TABS) {
|
||||
if (tab.id === 'changelog') continue; // Skip changelog in search filters
|
||||
|
||||
const sectionEl = containerEl.createDiv('obsitexcore-settings-search-section');
|
||||
const introEl = sectionEl.createDiv('obsitexcore-settings-category-intro');
|
||||
new Setting(introEl).setName(t(tab.labelKey)).setHeading();
|
||||
introEl.createEl('p', { text: t(tab.descKey) });
|
||||
|
||||
const panelEl = sectionEl.createDiv('obsitexcore-settings-panel');
|
||||
this.renderCategoryContent(tab.id, panelEl);
|
||||
|
||||
const sectionHasMatches = this.applySearchFilter(panelEl, query);
|
||||
if (!sectionHasMatches) {
|
||||
sectionEl.remove();
|
||||
} else {
|
||||
hasAnyMatches = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasAnyMatches) {
|
||||
const emptyEl = containerEl.createDiv('obsitexcore-settings-search-empty');
|
||||
emptyEl.createEl('p', { text: `No settings match "${query}".` });
|
||||
}
|
||||
}
|
||||
|
||||
private renderCategoryContent(tabId: SettingsTabId, panelEl: HTMLElement): void {
|
||||
switch (tabId) {
|
||||
case 'general':
|
||||
this.renderGeneral(panelEl);
|
||||
break;
|
||||
case 'pdf':
|
||||
this.renderPdf(panelEl);
|
||||
break;
|
||||
case 'tikz':
|
||||
this.renderTikz(panelEl);
|
||||
break;
|
||||
case 'hotkeys':
|
||||
this.renderHotkeys(panelEl);
|
||||
break;
|
||||
case 'changelog':
|
||||
this.renderChangelog(panelEl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private renderGeneral(containerEl: HTMLElement): void {
|
||||
new Setting(containerEl).setName('Equation numbering & referencing').setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Number only referenced equations')
|
||||
.setDesc('If turned on, only equations that are referenced somewhere will be numbered.')
|
||||
.setDesc(
|
||||
createDescWithDocs(
|
||||
'If turned on, only equations that are referenced somewhere will be numbered.',
|
||||
[{ text: 'Learn more', path: 'features/equations/' }]
|
||||
)
|
||||
)
|
||||
.addToggle(toggle =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.numberOnlyReferencedEquations)
|
||||
|
|
@ -90,12 +319,19 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
|
||||
new Setting(containerEl).setName('Autocomplete & search').setHeading();
|
||||
|
||||
new Setting(containerEl).setName('Enable autocompletion').addToggle(toggle =>
|
||||
toggle.setValue(this.plugin.settings.enableSuggest).onChange(async value => {
|
||||
this.plugin.settings.enableSuggest = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
new Setting(containerEl)
|
||||
.setName('Enable autocompletion')
|
||||
.setDesc(
|
||||
createDescWithDocs('Enable auto-suggestions for equations and theorems as you type.', [
|
||||
{ text: 'Learn more', path: 'features/search/' }
|
||||
])
|
||||
)
|
||||
.addToggle(toggle =>
|
||||
toggle.setValue(this.plugin.settings.enableSuggest).onChange(async value => {
|
||||
this.plugin.settings.enableSuggest = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl).setName('Trigger for autocompletion').addText(text =>
|
||||
text.setValue(this.plugin.settings.triggerSuggest).onChange(async value => {
|
||||
|
|
@ -111,34 +347,51 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl).setName('Zotero cleanup').setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Directories to search')
|
||||
.setDesc(
|
||||
"Comma-separated list of directories to search recursively for Zotero annotations (e.g. 'Zotero,notes/readings')."
|
||||
)
|
||||
.addTextArea(textArea => {
|
||||
textArea.setValue(this.plugin.settings.zoteroCleanDirectories).onChange(async value => {
|
||||
this.plugin.settings.zoteroCleanDirectories = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
textArea.inputEl.setAttr('rows', 3);
|
||||
});
|
||||
}
|
||||
|
||||
private renderPdf(containerEl: HTMLElement): void {
|
||||
new Setting(containerEl).setName('Pdf export').setHeading();
|
||||
|
||||
new Setting(containerEl).setName('Add file name as title').addToggle(toggle =>
|
||||
toggle
|
||||
.setTooltip('Add file name as title')
|
||||
.setValue(this.plugin.settings.showTitle)
|
||||
.onChange(async value => {
|
||||
new Setting(containerEl)
|
||||
.setName('Add file name as title')
|
||||
.setDesc(
|
||||
createDescWithDocs('Add the current file name as the heading/title in exported PDFs.', [
|
||||
{ text: 'Learn more', path: 'features/pdf-export/' }
|
||||
])
|
||||
)
|
||||
.addToggle(toggle =>
|
||||
toggle.setValue(this.plugin.settings.showTitle).onChange(async value => {
|
||||
this.plugin.settings.showTitle = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
);
|
||||
|
||||
new Setting(containerEl).setName('Display headers').addToggle(toggle =>
|
||||
toggle
|
||||
.setTooltip('Display header')
|
||||
.setValue(this.plugin.settings.displayHeader)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.displayHeader = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
toggle.setValue(this.plugin.settings.displayHeader).onChange(async value => {
|
||||
this.plugin.settings.displayHeader = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl).setName('Display footer').addToggle(toggle =>
|
||||
toggle
|
||||
.setTooltip('Display footer')
|
||||
.setValue(this.plugin.settings.displayFooter)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.displayFooter = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
toggle.setValue(this.plugin.settings.displayFooter).onChange(async value => {
|
||||
this.plugin.settings.displayFooter = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
|
|
@ -263,7 +516,9 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private renderTikz(containerEl: HTMLElement): void {
|
||||
new Setting(containerEl).setName('TikZJax rendering').setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
|
|
@ -289,22 +544,9 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
new Setting(containerEl).setName('Zotero cleanup').setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Directories to search')
|
||||
.setDesc(
|
||||
"Comma-separated list of directories to search recursively for Zotero annotations (e.g. 'Zotero,notes/readings')."
|
||||
)
|
||||
.addTextArea(textArea => {
|
||||
textArea.setValue(this.plugin.settings.zoteroCleanDirectories).onChange(async value => {
|
||||
this.plugin.settings.zoteroCleanDirectories = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
textArea.inputEl.setAttr('rows', 3);
|
||||
});
|
||||
|
||||
private renderHotkeys(containerEl: HTMLElement): void {
|
||||
new Setting(containerEl).setName('Custom note hotkeys').setHeading();
|
||||
containerEl.createEl('p', {
|
||||
text: "Configure hotkeys to quickly open specific notes in your vault. You can define optional default hotkeys here, and further customize or rebind them within Obsidian's global 'hotkeys' settings.",
|
||||
|
|
@ -313,7 +555,11 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
|
||||
new Setting(containerEl)
|
||||
.setName('Add new hotkey mapping')
|
||||
.setDesc('Create a new shortcut command to open a specific note.')
|
||||
.setDesc(
|
||||
createDescWithDocs('Create a new shortcut command to open a specific note.', [
|
||||
{ text: 'Learn more', path: 'features/snippets/' }
|
||||
])
|
||||
)
|
||||
.addButton(btn =>
|
||||
btn.onClick(async () => {
|
||||
if (!this.plugin.settings.customNoteHotkeys) {
|
||||
|
|
@ -328,7 +574,7 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
});
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
(this as unknown as { display(): void }).display();
|
||||
this.render();
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -367,7 +613,7 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
hotkeys.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
(this as unknown as { display(): void }).display();
|
||||
this.render();
|
||||
})();
|
||||
});
|
||||
|
||||
|
|
@ -504,4 +750,144 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
private renderChangelog(containerEl: HTMLElement): void {
|
||||
new Setting(containerEl).setName(t('settings.header.whatsNew')).setHeading();
|
||||
const changelogList = containerEl.createDiv('obsitexcore-changelog-view-list');
|
||||
|
||||
changelogData.forEach(version => {
|
||||
const verContainer = changelogList.createDiv('obsitexcore-changelog-version-container');
|
||||
new Setting(verContainer)
|
||||
.setName(`Version ${version.version} (${version.date})`)
|
||||
.setHeading();
|
||||
|
||||
version.changes.forEach(change => {
|
||||
const colonIndex = change.description.indexOf(':');
|
||||
let title: string;
|
||||
let desc = change.description;
|
||||
if (colonIndex !== -1) {
|
||||
title = change.description.substring(0, colonIndex).trim();
|
||||
desc = change.description.substring(colonIndex + 1).trim();
|
||||
} else {
|
||||
title = change.type.charAt(0).toUpperCase() + change.type.slice(1);
|
||||
}
|
||||
|
||||
let emoji = '✨';
|
||||
if (change.type === 'new') {
|
||||
emoji = '🚀';
|
||||
} else if (change.type === 'fix') {
|
||||
emoji = '🛠️';
|
||||
}
|
||||
|
||||
const itemEl = verContainer.createDiv(`full-calendar-change-item full-calendar-change-type-${change.type}`);
|
||||
itemEl.createDiv({
|
||||
cls: 'full-calendar-change-icon',
|
||||
text: emoji
|
||||
});
|
||||
const contentWrap = itemEl.createDiv('change-content');
|
||||
contentWrap.createDiv({
|
||||
cls: 'full-calendar-change-title',
|
||||
text: title
|
||||
});
|
||||
contentWrap.createDiv({
|
||||
cls: 'full-calendar-change-description',
|
||||
text: desc
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private renderFooter(containerEl: HTMLElement): void {
|
||||
const footerEl = containerEl.createDiv({ cls: 'full-calendar-settings-footer' });
|
||||
footerEl.createEl('p', {
|
||||
text: t('settings.footer.question'),
|
||||
cls: 'full-calendar-settings-footer-text'
|
||||
});
|
||||
|
||||
const linksContainer = footerEl.createDiv({ cls: 'full-calendar-settings-footer-links' });
|
||||
|
||||
linksContainer.createEl('a', {
|
||||
text: t('settings.footer.supportOnKofi'),
|
||||
attr: {
|
||||
href: 'https://youfoundjk.github.io/ObsiTeXcore/donation/ko-fi'
|
||||
},
|
||||
cls: 'full-calendar-settings-footer-link'
|
||||
});
|
||||
linksContainer.createEl('a', {
|
||||
text: t('settings.footer.suggestFeature'),
|
||||
attr: {
|
||||
href: 'https://github.com/YouFoundJK/ObsiTeXcore/discussions'
|
||||
},
|
||||
cls: 'full-calendar-settings-footer-link'
|
||||
});
|
||||
linksContainer.createEl('a', {
|
||||
text: t('settings.footer.reportBug'),
|
||||
attr: {
|
||||
href: 'https://github.com/YouFoundJK/ObsiTeXcore/issues'
|
||||
},
|
||||
cls: 'full-calendar-settings-footer-link'
|
||||
});
|
||||
}
|
||||
|
||||
private applySearchFilter(containerEl: HTMLElement, query: string): boolean {
|
||||
const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
const settingEls = Array.from(containerEl.querySelectorAll<HTMLElement>('.setting-item'));
|
||||
|
||||
let visibleCount = 0;
|
||||
settingEls.forEach(settingEl => {
|
||||
const titleEl = settingEl.querySelector<HTMLElement>('.setting-item-name');
|
||||
const descriptionEl = settingEl.querySelector<HTMLElement>('.setting-item-description');
|
||||
const title = titleEl?.textContent ?? '';
|
||||
const description = descriptionEl?.textContent ?? '';
|
||||
const haystack = `${title} ${description}`.toLowerCase();
|
||||
|
||||
const isMatch = tokens.every(token => haystack.includes(token));
|
||||
setCssProps(settingEl, { display: isMatch ? '' : 'none' });
|
||||
if (isMatch) {
|
||||
this.highlightSearchTokens(titleEl, tokens);
|
||||
this.highlightSearchTokens(descriptionEl, tokens);
|
||||
visibleCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return visibleCount > 0;
|
||||
}
|
||||
|
||||
private highlightSearchTokens(el: HTMLElement | null, tokens: string[]): void {
|
||||
if (!el) return;
|
||||
const rawText = el.textContent ?? '';
|
||||
if (!rawText || tokens.length === 0) return;
|
||||
|
||||
const escapedTokens = tokens
|
||||
.filter(Boolean)
|
||||
.map(token => token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
||||
if (escapedTokens.length === 0) return;
|
||||
|
||||
const regex = new RegExp(`(${escapedTokens.join('|')})`, 'gi');
|
||||
const doc = window.activeDocument ?? window.document;
|
||||
const fragment = doc.createDocumentFragment();
|
||||
let lastIndex = 0;
|
||||
|
||||
for (const match of rawText.matchAll(regex)) {
|
||||
const matchText = match[0];
|
||||
const matchIndex = match.index ?? -1;
|
||||
if (matchIndex < 0) continue;
|
||||
|
||||
if (matchIndex > lastIndex) {
|
||||
fragment.append(rawText.slice(lastIndex, matchIndex));
|
||||
}
|
||||
|
||||
const markEl = doc.createElement('mark');
|
||||
markEl.textContent = matchText;
|
||||
fragment.append(markEl);
|
||||
lastIndex = matchIndex + matchText.length;
|
||||
}
|
||||
|
||||
if (lastIndex < rawText.length) {
|
||||
fragment.append(rawText.slice(lastIndex));
|
||||
}
|
||||
|
||||
el.empty();
|
||||
el.append(fragment);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,4 +353,188 @@
|
|||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
/* Ensure wrapper takes full width */
|
||||
}
|
||||
|
||||
/* ====== Settings Redesign (Tabs, Search, Footer, and Changelog) ====== */
|
||||
|
||||
.obsitexcore-settings-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--size-4-3);
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-header {
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-tabs-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
padding-bottom: 8px;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-tabs {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-tab {
|
||||
padding: 6px 12px;
|
||||
background-color: var(--background-secondary-alt);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
font-weight: 500;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-tab:hover {
|
||||
color: var(--text-normal);
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.obsitexcore-settings-tab.is-active {
|
||||
color: var(--text-on-accent);
|
||||
background-color: var(--interactive-accent);
|
||||
border-color: var(--interactive-accent-hover);
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-trigger {
|
||||
padding: 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius-s);
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-input-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-input {
|
||||
width: 100%;
|
||||
padding: 6px 28px 6px 10px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-clear {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-clear:hover {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.obsitexcore-settings-category-intro {
|
||||
font-size: 0.95em;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-section {
|
||||
margin-bottom: 25px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-m);
|
||||
padding: 15px;
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.obsitexcore-settings-search-empty {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ====== Settings Footer (Exact styles from full-calendar) ====== */
|
||||
.full-calendar-settings-footer {
|
||||
margin-top: 1em;
|
||||
padding: var(--size-4-4);
|
||||
text-align: center;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: var(--radius-m);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.full-calendar-settings-footer-text {
|
||||
font-size: var(--font-ui-medium);
|
||||
color: var(--text-normal);
|
||||
margin-top: var(--size-8-4);
|
||||
margin-bottom: var(--size-4-2);
|
||||
}
|
||||
|
||||
.full-calendar-settings-footer-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--size-4-4);
|
||||
}
|
||||
|
||||
.full-calendar-settings-footer-link {
|
||||
font-size: var(--font-ui-medium);
|
||||
font-weight: 600;
|
||||
color: var(--interactive-accent);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.full-calendar-settings-footer-link:hover {
|
||||
color: var(--interactive-accent-hover);
|
||||
}
|
||||
|
||||
/* ====== Changelog/What's New Card Styles (Exact styles from full-calendar) ====== */
|
||||
.full-calendar-change-item {
|
||||
display: flex;
|
||||
gap: var(--size-4-3);
|
||||
padding: var(--size-4-4);
|
||||
border-radius: var(--radius-m);
|
||||
margin-bottom: var(--size-4-2);
|
||||
background-color: var(--background-secondary);
|
||||
border-left: 3px solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.full-calendar-change-type-new {
|
||||
border-color: var(--color-green);
|
||||
}
|
||||
|
||||
.full-calendar-change-type-improvement {
|
||||
border-color: var(--color-blue);
|
||||
}
|
||||
|
||||
.full-calendar-change-type-fix {
|
||||
border-color: var(--color-orange);
|
||||
}
|
||||
|
||||
.full-calendar-change-icon {
|
||||
font-size: var(--font-ui-large);
|
||||
}
|
||||
|
||||
.full-calendar-change-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: var(--size-4-1);
|
||||
}
|
||||
|
||||
.full-calendar-change-description {
|
||||
font-size: var(--font-ui-small);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
104
src/ui/modals/WhatsNewModal.ts
Normal file
104
src/ui/modals/WhatsNewModal.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import { App, Modal, ButtonComponent } from 'obsidian';
|
||||
import { changelogData } from '../../settings/changelogData';
|
||||
import { t } from '../../i18n/t';
|
||||
|
||||
type SettingsManager = {
|
||||
open: () => void;
|
||||
openTabById: (id: string) => void;
|
||||
};
|
||||
|
||||
type AppWithSettings = App & { setting: SettingsManager };
|
||||
|
||||
export class WhatsNewModal extends Modal {
|
||||
constructor(
|
||||
app: App,
|
||||
private manifestId: string
|
||||
) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
contentEl.addClass('obsitexcore-whats-new-modal');
|
||||
|
||||
// Header
|
||||
const headerRow = contentEl.createDiv('obsitexcore-whats-new-header-row');
|
||||
headerRow.createEl('h2', { text: t('modals.whatsNew.title') });
|
||||
|
||||
const seeAllButtonWrap = headerRow.createDiv('obsitexcore-whats-new-header-actions');
|
||||
new ButtonComponent(seeAllButtonWrap)
|
||||
.setButtonText(t('modals.whatsNew.openSettings'))
|
||||
.onClick(() => {
|
||||
this.close();
|
||||
const settingsManager = (this.app as AppWithSettings).setting;
|
||||
if (settingsManager && typeof settingsManager.open === 'function') {
|
||||
settingsManager.open();
|
||||
settingsManager.openTabById(this.manifestId);
|
||||
}
|
||||
});
|
||||
|
||||
// Content container
|
||||
const bodyEl = contentEl.createDiv('obsitexcore-whats-new-body');
|
||||
const latestVersion = changelogData[0];
|
||||
|
||||
const versionHeader = bodyEl.createDiv('obsitexcore-whats-new-version-header');
|
||||
versionHeader.createEl('h3', {
|
||||
text: `Version ${latestVersion.version} (${latestVersion.date})`
|
||||
});
|
||||
|
||||
const changeList = bodyEl.createDiv('obsitexcore-whats-new-list');
|
||||
latestVersion.changes.forEach(change => {
|
||||
const colonIndex = change.description.indexOf(':');
|
||||
let title: string;
|
||||
let desc = change.description;
|
||||
if (colonIndex !== -1) {
|
||||
title = change.description.substring(0, colonIndex).trim();
|
||||
desc = change.description.substring(colonIndex + 1).trim();
|
||||
} else {
|
||||
title = change.type.charAt(0).toUpperCase() + change.type.slice(1);
|
||||
}
|
||||
|
||||
let emoji = '✨';
|
||||
if (change.type === 'new') {
|
||||
emoji = '🚀';
|
||||
} else if (change.type === 'fix') {
|
||||
emoji = '🛠️';
|
||||
}
|
||||
|
||||
const itemEl = changeList.createDiv(`full-calendar-change-item full-calendar-change-type-${change.type}`);
|
||||
itemEl.createDiv({
|
||||
cls: 'full-calendar-change-icon',
|
||||
text: emoji
|
||||
});
|
||||
const contentWrap = itemEl.createDiv('change-content');
|
||||
contentWrap.createDiv({
|
||||
cls: 'full-calendar-change-title',
|
||||
text: title
|
||||
});
|
||||
contentWrap.createDiv({
|
||||
cls: 'full-calendar-change-description',
|
||||
text: desc
|
||||
});
|
||||
});
|
||||
|
||||
// Donation Footer
|
||||
const donationFooter = contentEl.createDiv('obsitexcore-whats-new-donation-footer');
|
||||
donationFooter.createEl('p', {
|
||||
text: 'ObsiTeXcore is built to bring LaTeX-like equation indexing and referencing workflows to Obsidian. If this plugin provides value to you, please consider supporting the development!',
|
||||
cls: 'obsitexcore-whats-new-donation-message'
|
||||
});
|
||||
|
||||
const donationActions = donationFooter.createDiv('obsitexcore-whats-new-donation-actions');
|
||||
new ButtonComponent(donationActions)
|
||||
.setButtonText(t('modals.whatsNew.supportKofi'))
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
window.open('https://youfoundjk.github.io/ObsiTeXcore/donation/ko-fi', '_blank');
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ describe('MockVault API tests', () => {
|
|||
}
|
||||
|
||||
// create another folder
|
||||
const folder2 = await vault.createFolder('folder2');
|
||||
await vault.createFolder('folder2');
|
||||
|
||||
// rename folder
|
||||
const folder1 = vault.getAbstractFileByPath('folder1');
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": ["DOM", "ES5", "ES6", "ES7", "ES2021.String", "DOM.Iterable", "es2022"]
|
||||
},
|
||||
"include": ["**/*.d.ts", "**/*.ts"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue