feat: publish modal, settings tab, plugin entry

Co-Authored-By: Claude
This commit is contained in:
2026-06-19 02:16:48 +00:00
parent 62ae955044
commit 53c2550097
3 changed files with 257 additions and 0 deletions

112
src/PublishModal.ts Normal file
View File

@@ -0,0 +1,112 @@
import { App, Modal, Setting } from "obsidian";
import { Pair, parseNote, resolveFrontmatter } from "./frontmatter";
import { Strategy } from "./images";
import { JekyllPublishSettings } from "./settings";
import { deriveDate, deriveSlug } from "./slug";
export interface ModalResult {
frontmatterPairs: Pair[];
slug: string;
date: string;
strategy: Strategy;
commitMessage: string;
}
export class PublishModal extends Modal {
private result: ModalResult;
private mergeDoc = true;
private custom: Pair[] = [];
constructor(
app: App,
private settings: JekyllPublishSettings,
private noteText: string,
private filename: string,
private onSubmit: (r: ModalResult) => void
) {
super(app);
const { frontmatter } = parseNote(noteText);
const title = typeof frontmatter.title === "string" ? frontmatter.title : "";
this.result = {
frontmatterPairs: [],
slug: deriveSlug({ title, filename }),
date: deriveDate({ frontmatterDate: frontmatter.date, now: new Date() }),
strategy: settings.defaultImageStrategy,
commitMessage: settings.commitMessageTemplate.replace("{{title}}", title || filename),
};
}
private recompute() {
const { frontmatter } = parseNote(this.noteText);
this.result.frontmatterPairs = resolveFrontmatter(
this.settings.presetFrontmatter,
frontmatter,
this.custom,
this.mergeDoc
);
}
onOpen() {
this.recompute();
const { contentEl } = this;
contentEl.createEl("h2", { text: "Publish to Jekyll" });
new Setting(contentEl).setName("Slug").addText((t) =>
t.setValue(this.result.slug).onChange((v) => (this.result.slug = v))
);
new Setting(contentEl).setName("Date").addText((t) =>
t.setValue(this.result.date).onChange((v) => (this.result.date = v))
);
new Setting(contentEl).setName("Image strategy").addDropdown((d) =>
d.addOption("flat-slug", "Flat, renamed to slug")
.addOption("per-post-folder", "Per-post subfolder")
.setValue(this.result.strategy)
.onChange((v) => (this.result.strategy = v as Strategy))
);
new Setting(contentEl).setName("Merge document frontmatter").addToggle((t) =>
t.setValue(this.mergeDoc).onChange((v) => { this.mergeDoc = v; this.renderFrontmatter(); })
);
this.fmEl = contentEl.createDiv();
this.renderFrontmatter();
new Setting(contentEl).addButton((b) =>
b.setButtonText("Add property").onClick(() => {
this.custom.push({ key: "", value: "" });
this.renderFrontmatter();
})
);
new Setting(contentEl).setName("Commit message").addText((t) =>
t.setValue(this.result.commitMessage).onChange((v) => (this.result.commitMessage = v))
);
new Setting(contentEl).addButton((b) =>
b.setButtonText("Publish").setCta().onClick(() => {
this.recompute();
this.close();
this.onSubmit(this.result);
})
);
}
private fmEl!: HTMLElement;
private renderFrontmatter() {
this.recompute();
this.fmEl.empty();
this.fmEl.createEl("h3", { text: "Frontmatter (resolved)" });
for (const p of this.result.frontmatterPairs) {
this.fmEl.createDiv({ cls: "jekyll-publish-row", text: `${p.key}: ${p.value}` });
}
if (this.custom.length) {
this.fmEl.createEl("h4", { text: "Custom properties" });
this.custom.forEach((row, i) => {
const div = this.fmEl.createDiv({ cls: "jekyll-publish-row" });
const k = div.createEl("input", { value: row.key, placeholder: "key" });
const v = div.createEl("input", { value: row.value, placeholder: "value" });
k.oninput = () => { this.custom[i].key = k.value; this.recompute(); };
v.oninput = () => { this.custom[i].value = v.value; this.recompute(); };
});
}
}
onClose() { this.contentEl.empty(); }
}