🎨 Use 1.13.0 Settings API

This commit is contained in:
Erin Schnabel 2026-06-03 10:14:16 -04:00
parent 990113e1e6
commit f279ae3297
No known key found for this signature in database
6 changed files with 189 additions and 154 deletions

View file

@ -2,7 +2,7 @@
"id": "deck-notes",
"name": "Deck Notes",
"version": "0.7.6",
"minAppVersion": "1.12.0",
"minAppVersion": "1.13.0",
"description": "Define card decks for activities, strategies, or any content you want to rotate through. Embed cards in your notes or browse them in a modal. Filter by tag hierarchies to narrow your selection.",
"author": "ebullient",
"authorUrl": "https://github.com/ebullient",

View file

@ -2,7 +2,7 @@
"id": "deck-notes",
"name": "Deck Notes",
"version": "0.7.6",
"minAppVersion": "1.12.0",
"minAppVersion": "1.13.0",
"description": "Define card decks for activities, strategies, or any content you want to rotate through. Embed cards in your notes or browse them in a modal. Filter by tag hierarchies to narrow your selection.",
"author": "ebullient",
"authorUrl": "https://github.com/ebullient",

23
package-lock.json generated
View file

@ -15,7 +15,7 @@
"auto-changelog": "^2.5.0",
"esbuild": "^0.28.0",
"eslint-plugin-obsidianmd": "^0.3.0",
"obsidian": "^1.12.3",
"obsidian": "^1.13.0",
"tslib": "^2.8.1",
"typescript": "6.0.2"
}
@ -2432,6 +2432,21 @@
"undici-types": "~5.26.4"
}
},
"node_modules/eslint-plugin-obsidianmd/node_modules/obsidian": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz",
"integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/codemirror": "5.60.8",
"moment": "2.29.4"
},
"peerDependencies": {
"@codemirror/state": "6.5.0",
"@codemirror/view": "6.38.6"
}
},
"node_modules/eslint-plugin-obsidianmd/node_modules/typescript": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
@ -3992,9 +4007,9 @@
}
},
"node_modules/obsidian": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz",
"integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.13.0.tgz",
"integrity": "sha512-PHw5+SAPlJ0S3leFvJ0wgFg63Z3DavxL6+d1ll+8toXR2ZlYKc1rMWqdUv9LgUbTwPQUyY6yfhOMMivampRRiQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View file

@ -34,7 +34,7 @@
"auto-changelog": "^2.5.0",
"esbuild": "^0.28.0",
"eslint-plugin-obsidianmd": "^0.3.0",
"obsidian": "^1.12.3",
"obsidian": "^1.13.0",
"tslib": "^2.8.1",
"typescript": "6.0.2"
},

View file

@ -1,10 +1,15 @@
import { type App, PluginSettingTab, Setting } from "obsidian";
import type { DeckNotesSettings } from "./@types/settings";
import {
type App,
Modal,
PluginSettingTab,
Setting,
type SettingDefinitionItem,
type Setting as SettingType,
} from "obsidian";
import type DeckNotesPlugin from "./dn-Plugin";
export class DeckNotesSettingsTab extends PluginSettingTab {
plugin: DeckNotesPlugin;
newSettings!: DeckNotesSettings;
constructor(app: App, plugin: DeckNotesPlugin) {
super(app, plugin);
@ -12,155 +17,171 @@ export class DeckNotesSettingsTab extends PluginSettingTab {
this.icon = "gallery-thumbnails";
}
async save() {
this.plugin.settings = this.newSettings;
async setControlValue(key: string, value: unknown): Promise<void> {
(this.plugin.settings as unknown as Record<string, unknown>)[key] =
value;
await this.plugin.saveSettings();
}
private cloneSettings(): DeckNotesSettings {
return JSON.parse(
JSON.stringify(this.plugin.settings),
) as DeckNotesSettings;
}
getSettingDefinitions(): SettingDefinitionItem[] {
return [
{
name: "Usage",
render: (setting: SettingType) => {
setting.descEl.appendChild(
createFragment((f) => {
const p = f.createEl("p");
p.appendText(
"Cards are created from markdown files with ",
);
p.createEl("code", { text: "##" });
p.appendText(" headings.");
reset() {
this.newSettings = this.cloneSettings();
this.display();
}
const ul = f.createEl("ul");
display(): void {
if (!this.newSettings) {
this.newSettings = this.cloneSettings();
}
const li1 = ul.createEl("li");
li1.appendText("Use ");
li1.createEl("code", { text: "---" });
li1.appendText(
" (horizontal rule) to mark the end of card content.",
);
const { containerEl } = this;
containerEl.empty();
new Setting(this.containerEl)
.setName("Save settings")
.setClass("decknotes-save-reset")
.addButton((button) =>
button
.setIcon("reset")
.setTooltip("Reset to previously saved values.")
.onClick(() => {
this.reset();
}),
)
.addButton((button) => {
button
.setIcon("save")
.setCta()
.setTooltip("Save all changes")
.onClick(async () => {
await this.save();
});
});
new Setting(containerEl)
.setName("Card paths")
.setDesc(
"Paths to folders containing card decks; " +
"one path relative to vault root per line.",
)
.addTextArea((text) =>
text
.setPlaceholder("journal/coping\nactivities/morning")
.setValue(this.newSettings.cardPaths.join("\n"))
.onChange((value) => {
this.newSettings.cardPaths = value
.split("\n")
.map((p) => p.trim())
.filter((p) => p.length > 0);
}),
)
.then((setting) => {
setting.controlEl
.querySelector("textarea")
?.setAttribute("rows", "4");
});
new Setting(containerEl)
.setName("Default deck tag")
.setDesc(
"Optional tag to narrow the selection of cards " +
"available for 'Show Card' command; for example, " +
"'activities' or 'activities/morning'.",
)
.addText((text) =>
text
.setPlaceholder("Activities")
.setValue(this.newSettings.defaultDeckTag)
.onChange((value) => {
this.newSettings.defaultDeckTag = value.trim();
}),
);
new Setting(containerEl)
.setName("Selection mode")
.setDesc(
"Cards can be selected at random or based on " +
"when they were last viewed.",
)
.addDropdown((dropdown) =>
dropdown
.addOption("random", "Random")
.addOption("least-recent", "Least recently viewed")
.setValue(this.newSettings.selectionMode)
.onChange((value) => {
this.newSettings.selectionMode = value as
| "random"
| "least-recent";
}),
);
new Setting(containerEl)
.setName("Callout type")
.setDesc(
"Callout type for embedded cards; " +
"for example, note, tip, or warning.",
)
.addText((text) =>
text
.setPlaceholder("example")
.setValue(this.newSettings.calloutType)
.onChange((value) => {
this.newSettings.calloutType = value.trim();
}),
);
new Setting(containerEl).setHeading().setName("Usage");
containerEl.createEl("p", {
text:
"Cards are created from markdown files with " +
"H2 headings (##). Each heading becomes one card.",
});
containerEl.createEl("p", {
text:
"Use --- (horizontal rule) to mark the end of " +
"card content. Anything after --- will be ignored.",
});
containerEl.createEl("p", {
text:
"Tag cards with #flashcards/deck-name; for example, " +
"#flashcards/activities/morning. Tags can be in " +
"frontmatter or inline before each H2. Lines starting " +
"with #flashcards are stripped from display.",
});
const div = this.containerEl.createDiv("deck-cards-coffee");
div.createEl("a", {
href: "https://www.buymeacoffee.com/ebullient",
}).createEl("img", {
attr: {
src: "https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=☕&slug=ebullient&button_colour=8e6787&font_colour=ebebeb&font_family=Inter&outline_colour=392a37&coffee_colour=ecc986",
const li2 = ul.createEl("li");
li2.appendText("Use ");
li2.createEl("code", {
text: "#flashcards",
});
li2.appendText(
" tags to identify decks. For example, ",
);
li2.createEl("code", {
text: "#flashcards/activities",
});
li2.appendText(" or ");
li2.createEl("code", {
text: "#flashcards/meditation",
});
li2.appendText(
". Tags can be added to the whole file (in frontmatter properties), or defined inline before each ",
);
li2.createEl("code", { text: "##" });
li2.appendText(". See the ");
li2.createEl("a", {
text: "README",
href: "https://github.com/ebullient/obsidian-deck-notes#card-format",
attr: { target: "_blank" },
});
li2.appendText(" for examples.");
}),
);
},
},
});
}
/** Save on exit */
hide(): void {
void this.save();
{
name: "Default deck tag",
desc:
"Optional tag to narrow the selection of cards available " +
"for 'Show Card' command; for example, 'activities' or " +
"'activities/morning'.",
control: {
type: "text",
key: "defaultDeckTag",
placeholder: "activities",
},
},
{
name: "Selection mode",
desc: "Cards can be selected at random or based on when they were last viewed.",
control: {
type: "dropdown",
key: "selectionMode",
options: {
random: "Random",
"least-recent": "Least recently viewed",
},
},
},
{
name: "Callout type",
desc: "Callout type for embedded cards; for example, note, tip, or warning.",
control: {
type: "text",
key: "calloutType",
placeholder: "example",
},
},
{
type: "list",
heading: "Card paths",
desc: "Paths to folders containing card decks, relative to vault root.",
emptyState: "No card paths configured.",
addItem: {
name: "Add path",
action: () =>
new AddCardPathModal(this.app, (path) => {
this.plugin.settings.cardPaths.push(path);
void this.plugin.saveSettings();
this.update();
}).open(),
},
onDelete: async (idx: number) => {
this.plugin.settings.cardPaths.splice(idx, 1);
await this.plugin.saveSettings();
this.update();
},
items: this.plugin.settings.cardPaths.map((path) => ({
name: path,
})),
},
{
name: "",
render: (setting: SettingType) => {
setting.descEl.addClass("deck-cards-coffee");
setting.descEl
.createEl("a", {
href: "https://www.buymeacoffee.com/ebullient",
})
.createEl("img", {
attr: {
src: "https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=☕&slug=ebullient&button_colour=8e6787&font_colour=ebebeb&font_family=Inter&outline_colour=392a37&coffee_colour=ecc986",
},
});
},
},
];
}
}
class AddCardPathModal extends Modal {
private onSubmit: (path: string) => void;
constructor(app: App, onSubmit: (path: string) => void) {
super(app);
this.onSubmit = onSubmit;
}
onOpen(): void {
const { contentEl } = this;
contentEl.createEl("h2", { text: "Add card path" });
let input = "";
new Setting(contentEl).setName("Path").addText((t) =>
t.setPlaceholder("journal/coping").onChange((v) => {
input = v.trim();
}),
);
new Setting(contentEl).addButton((b) =>
b
.setButtonText("Add")
.setCta()
.onClick(() => {
if (input) {
this.onSubmit(input);
this.close();
}
}),
);
}
onClose(): void {
this.contentEl.empty();
}
}

View file

@ -1,7 +1,6 @@
.deck-cards-coffee {
text-align: center;
img {
margin: auto;
height: 30px;
}
}