feat: add open-after-save toggle, fix footnotes to [^N] syntax, rename Clip→Save in UI

This commit is contained in:
@gapmiss 2026-06-19 20:48:43 -05:00
parent 8dacb96863
commit d2c9ab36e9
3 changed files with 34 additions and 13 deletions

View file

@ -1,4 +1,4 @@
import { normalizePath, Notice, Plugin } from 'obsidian';
import { normalizePath, Notice, Plugin, TFile } from 'obsidian';
import { type SubstackClipperSettings, DEFAULT_SETTINGS } from './types';
import type { ParsedArticle, DownloadResult } from './types';
import { SettingsTab } from './settings';
@ -18,10 +18,14 @@ export default class SubstackClipperPlugin extends Plugin {
this.addCommand({
id: 'clip-post',
name: 'Clip substack post',
name: 'Save substack post',
callback: () => {
new ClipModal(this.app, (url) => {
void this.clipPost(url);
new ClipModal(this.app, this.settings.openAfterClip, (url, openAfterClip) => {
if (openAfterClip !== this.settings.openAfterClip) {
this.settings.openAfterClip = openAfterClip;
void this.saveSettings();
}
void this.clipPost(url, openAfterClip);
}).open();
},
});
@ -35,8 +39,8 @@ export default class SubstackClipperPlugin extends Plugin {
await this.saveData(this.settings);
}
private async clipPost(url: string): Promise<void> {
const notice = new Notice('Clipping...', 0);
private async clipPost(url: string, openAfterClip: boolean): Promise<void> {
const notice = new Notice('Saving...', 0);
try {
const { username, slug, domain } = parseUrl(url);
@ -189,12 +193,19 @@ export default class SubstackClipperPlugin extends Plugin {
await this.writeFile(htmlPath, article.html);
}
if (openAfterClip) {
const file = this.app.vault.getAbstractFileByPath(notePath);
if (file instanceof TFile) {
await this.app.workspace.getLeaf(false).openFile(file);
}
}
notice.hide();
new Notice(`Clipped: ${article.title}`);
new Notice(`Saved: ${article.title}`);
} catch (e) {
notice.hide();
new Notice(`Clip failed: ${e instanceof Error ? e.message : String(e)}`);
new Notice(`Save failed: ${e instanceof Error ? e.message : String(e)}`);
}
}

View file

@ -2,17 +2,19 @@ import { App, Modal, Notice, Setting } from 'obsidian';
export class ClipModal extends Modal {
private url = '';
private onSubmit: (url: string) => void;
private openAfterClip: boolean;
private onSubmit: (url: string, openAfterClip: boolean) => void;
constructor(app: App, onSubmit: (url: string) => void) {
constructor(app: App, openAfterClip: boolean, onSubmit: (url: string, openAfterClip: boolean) => void) {
super(app);
this.openAfterClip = openAfterClip;
this.onSubmit = onSubmit;
}
onOpen(): void {
const { contentEl } = this;
contentEl.addClass('substack-clipper-modal');
contentEl.createEl('h2', { text: 'Clip substack post' });
contentEl.createEl('h2', { text: 'Save substack post' });
new Setting(contentEl)
.setName('Post URL')
@ -29,9 +31,15 @@ export class ClipModal extends Modal {
text.inputEl.setCssStyles({ width: '100%' });
});
new Setting(contentEl)
.setName('Open after saving')
.addToggle(toggle => toggle
.setValue(this.openAfterClip)
.onChange(value => { this.openAfterClip = value; }));
new Setting(contentEl)
.addButton(btn => btn
.setButtonText('Clip')
.setButtonText('Save')
.setCta()
.onClick(() => { this.submit(); }));
}
@ -42,7 +50,7 @@ export class ClipModal extends Modal {
return;
}
this.close();
this.onSubmit(this.url);
this.onSubmit(this.url, this.openAfterClip);
}
onClose(): void {

View file

@ -32,6 +32,7 @@ export interface SubstackClipperSettings {
saveRawJson: boolean;
saveRawHtml: boolean;
commentSort: 'most_recent_first' | 'oldest_first' | 'best_first';
openAfterClip: boolean;
}
export const DEFAULT_SETTINGS: SubstackClipperSettings = {
@ -42,6 +43,7 @@ export const DEFAULT_SETTINGS: SubstackClipperSettings = {
saveRawJson: false,
saveRawHtml: false,
commentSort: 'most_recent_first',
openAfterClip: false,
};
export interface DownloadResult {