mirror of
https://github.com/ebullient/obsidian-deck-notes.git
synced 2026-07-22 06:40:43 +00:00
🎨 Simpilify tracking last seen
This commit is contained in:
parent
3921ebf361
commit
c7df00f53d
7 changed files with 55 additions and 31 deletions
|
|
@ -63,6 +63,11 @@ npm run format # Format code
|
|||
|
||||
- **Error handling**: `try/catch` with user-friendly `Notice` messages
|
||||
- **Async**: Use `async/await` consistently
|
||||
- **UI text (enforced by `obsidianmd/ui/sentence-case`)**:
|
||||
- Use sentence case (capitalize first word only, except proper nouns)
|
||||
- Setting descriptions must be complete sentences ending with periods
|
||||
- Use semicolons to separate related clauses in a single sentence
|
||||
- Avoid parenthetical notes; use semicolons instead
|
||||
|
||||
## Development Patterns
|
||||
|
||||
|
|
|
|||
5
src/@types/settings.d.ts
vendored
5
src/@types/settings.d.ts
vendored
|
|
@ -9,10 +9,9 @@ export interface Card {
|
|||
|
||||
export interface DeckNotesSettings {
|
||||
cardPaths: string[]; // ["Journal/Coping", "Activities"]
|
||||
defaultDeckTag: string; // Default tag for deck selection (e.g., "activities")
|
||||
trackViews: boolean; // Enable view tracking
|
||||
defaultDeckTag: string; // Default tag for deck selection
|
||||
selectionMode: "random" | "least-recent";
|
||||
calloutType: string; // Callout type for embedded cards (e.g., "readaloud")
|
||||
calloutType: string; // Callout type for embedded cards
|
||||
}
|
||||
|
||||
export interface DeckNotesData {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export class DeckNotesApi {
|
|||
|
||||
/**
|
||||
* Get all unique deck tags discovered in cards
|
||||
* @returns Array of normalized tag strings (e.g., ["activities", "activities/morning"])
|
||||
* @returns Array of normalized tag strings; for example,
|
||||
* ["activities", "activities/morning"]
|
||||
*/
|
||||
getTags(): string[] {
|
||||
const tagsSet = new Set<string>();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import type { DeckNotesSettings } from "./@types/settings";
|
|||
export const DEFAULT_SETTINGS: DeckNotesSettings = {
|
||||
cardPaths: [],
|
||||
defaultDeckTag: "",
|
||||
trackViews: true,
|
||||
selectionMode: "least-recent",
|
||||
calloutType: "tip",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export class CardModal extends Modal {
|
|||
|
||||
private showNextCard() {
|
||||
// Record view for current card
|
||||
if (this.card && this.plugin.settings.trackViews) {
|
||||
if (this.card) {
|
||||
this.plugin.recordView(this.card.key);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default class DeckNotesPlugin extends Plugin {
|
|||
cachedCards: Card[] = [];
|
||||
cardParser!: CardParser;
|
||||
api!: DeckNotesApi;
|
||||
saveViewsTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
async onload() {
|
||||
console.debug("Loading Deck Notes plugin", `v${this.manifest.version}`);
|
||||
|
|
@ -21,7 +22,8 @@ export default class DeckNotesPlugin extends Plugin {
|
|||
|
||||
this.addSettingTab(new DeckNotesSettingsTab(this.app, this));
|
||||
|
||||
// Defer initial card scan, API, and command registration to avoid blocking startup
|
||||
// Defer initial card scan, API, and command registration
|
||||
// to avoid blocking startup
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
await this.scanCards();
|
||||
|
||||
|
|
@ -65,6 +67,12 @@ export default class DeckNotesPlugin extends Plugin {
|
|||
onunload() {
|
||||
console.debug("Unloading Deck Notes plugin");
|
||||
|
||||
// Flush any pending view saves
|
||||
if (this.saveViewsTimer) {
|
||||
clearTimeout(this.saveViewsTimer);
|
||||
void this.saveViews();
|
||||
}
|
||||
|
||||
// Clear API reference
|
||||
if (window.deckNotes) {
|
||||
window.deckNotes.api = undefined;
|
||||
|
|
@ -151,7 +159,19 @@ export default class DeckNotesPlugin extends Plugin {
|
|||
|
||||
recordView(cardKey: string) {
|
||||
this.data.cardViews[cardKey] = Date.now();
|
||||
void this.saveData({
|
||||
|
||||
// Debounce saves: only write to disk after 2s of no card views
|
||||
if (this.saveViewsTimer) {
|
||||
clearTimeout(this.saveViewsTimer);
|
||||
}
|
||||
this.saveViewsTimer = setTimeout(() => {
|
||||
void this.saveViews();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
async saveViews() {
|
||||
this.saveViewsTimer = null;
|
||||
await this.saveData({
|
||||
...this.settings,
|
||||
cardViews: this.data.cardViews,
|
||||
});
|
||||
|
|
@ -188,9 +208,7 @@ export default class DeckNotesPlugin extends Plugin {
|
|||
].join("\n");
|
||||
|
||||
// Record as viewed
|
||||
if (this.settings.trackViews) {
|
||||
this.recordView(card.key);
|
||||
}
|
||||
this.recordView(card.key);
|
||||
|
||||
return embedText;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ export class DeckNotesSettingsTab extends PluginSettingTab {
|
|||
new Setting(containerEl)
|
||||
.setName("Card paths")
|
||||
.setDesc(
|
||||
"Paths to folders containing card files (one per line, relative to vault root)",
|
||||
"Paths to folders containing card files; " +
|
||||
"one path relative to vault root per line.",
|
||||
)
|
||||
.addTextArea((text) =>
|
||||
text
|
||||
|
|
@ -87,7 +88,9 @@ export class DeckNotesSettingsTab extends PluginSettingTab {
|
|||
new Setting(containerEl)
|
||||
.setName("Default deck tag")
|
||||
.setDesc(
|
||||
"Default tag for 'Show Random Activity Card' command (e.g., 'activities' or 'activities/morning'). Leave empty for all cards.",
|
||||
"Optional tag to narrow the selection of cards " +
|
||||
"available for 'Show Card' command; for example, " +
|
||||
"'activities' or 'activities/morning'.",
|
||||
)
|
||||
.addText((text) =>
|
||||
text
|
||||
|
|
@ -98,22 +101,12 @@ export class DeckNotesSettingsTab extends PluginSettingTab {
|
|||
}),
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Track views")
|
||||
.setDesc(
|
||||
"Track when cards were last viewed (enables least-recent selection)",
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.newSettings.trackViews)
|
||||
.onChange((value) => {
|
||||
this.newSettings.trackViews = value;
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Selection mode")
|
||||
.setDesc("How to select cards to display")
|
||||
.setDesc(
|
||||
"Cards can be selected at random or based on " +
|
||||
"when they were last viewed.",
|
||||
)
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("random", "Random")
|
||||
|
|
@ -129,7 +122,8 @@ export class DeckNotesSettingsTab extends PluginSettingTab {
|
|||
new Setting(containerEl)
|
||||
.setName("Callout type")
|
||||
.setDesc(
|
||||
"Callout type for embedded cards (e.g., note, tip, warning)",
|
||||
"Callout type for embedded cards; " +
|
||||
"for example, note, tip, or warning.",
|
||||
)
|
||||
.addText((text) =>
|
||||
text
|
||||
|
|
@ -143,13 +137,21 @@ export class DeckNotesSettingsTab extends PluginSettingTab {
|
|||
new Setting(containerEl).setHeading().setName("Usage");
|
||||
|
||||
containerEl.createEl("p", {
|
||||
text: "Cards are created from markdown files with H2 headings (##). Each heading becomes one card.",
|
||||
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.",
|
||||
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 (e.g., #flashcards/activities/morning). Tags can be in frontmatter or inline before each H2. Lines starting with #flashcards are stripped from display.",
|
||||
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.",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue