From 8e079cc5179b77cd5c60025fb0c7b594ecdf286c Mon Sep 17 00:00:00 2001 From: Darren Sadr Date: Sun, 24 Aug 2025 22:45:57 -0700 Subject: [PATCH 1/3] initial custom interval commit --- src/algorithms/base/isrs-algorithm.ts | 1 + src/algorithms/custom/srs-algorithm-custom.ts | 101 ++++++++++++++++++ src/gui/settings.tsx | 100 +++++++++++++++++ src/lang/locale/en.ts | 7 ++ src/main.ts | 17 ++- src/settings.ts | 21 ++++ 6 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 src/algorithms/custom/srs-algorithm-custom.ts diff --git a/src/algorithms/base/isrs-algorithm.ts b/src/algorithms/base/isrs-algorithm.ts index 4d0339b0..927d8182 100644 --- a/src/algorithms/base/isrs-algorithm.ts +++ b/src/algorithms/base/isrs-algorithm.ts @@ -7,6 +7,7 @@ import { INoteEaseList } from "src/note-ease-list"; export enum Algorithm { SM_2_OSR = "SM-2-OSR", + CUSTOM_INTERVALS = "Custom-Intervals", } export interface ISrsAlgorithm { diff --git a/src/algorithms/custom/srs-algorithm-custom.ts b/src/algorithms/custom/srs-algorithm-custom.ts new file mode 100644 index 00000000..52e2acf6 --- /dev/null +++ b/src/algorithms/custom/srs-algorithm-custom.ts @@ -0,0 +1,101 @@ +import { ISrsAlgorithm } from "src/algorithms/base/isrs-algorithm"; +import { RepItemScheduleInfo } from "src/algorithms/base/rep-item-schedule-info"; +import { ReviewResponse } from "src/algorithms/base/repetition-item"; +import { OsrNoteGraph } from "src/algorithms/osr/osr-note-graph"; +import { RepItemScheduleInfoOsr } from "src/algorithms/osr/rep-item-schedule-info-osr"; +// TICKS_PER_DAY not needed for custom intervals +// import { TICKS_PER_DAY } from "src/constants"; +import { DueDateHistogram } from "src/due-date-histogram"; +import { Note } from "src/note"; +import { INoteEaseList } from "src/note-ease-list"; +import { SRSettings } from "src/settings"; +import { globalDateProvider } from "src/utils/dates"; + +export class SrsAlgorithmCustom implements ISrsAlgorithm { + private settings: SRSettings; + + constructor(settings: SRSettings) { + this.settings = settings; + } + + noteOnLoadedNote(_path: string, _note: Note, _noteEase: number): void { + // For custom intervals, we don't need to track note ease like SM-2 + // This is a no-op for the custom intervals algorithm + } + + noteCalcNewSchedule( + notePath: string, + osrNoteGraph: OsrNoteGraph, + response: ReviewResponse, + _dueDateNoteHistogram: DueDateHistogram, + ): RepItemScheduleInfo { + const interval = this.getCustomInterval(response); + const dueDate = globalDateProvider.today.add(interval, "d"); + + return new RepItemScheduleInfoOsr(dueDate, interval, 250); // Use default ease for custom intervals + } + + noteCalcUpdatedSchedule( + notePath: string, + noteSchedule: RepItemScheduleInfo, + response: ReviewResponse, + _dueDateNoteHistogram: DueDateHistogram, + ): RepItemScheduleInfo { + const interval = this.getCustomInterval(response); + const dueDate = globalDateProvider.today.add(interval, "d"); + + return new RepItemScheduleInfoOsr(dueDate, interval, 250); // Use default ease for custom intervals + } + + noteStats(): INoteEaseList { + // Return empty note stats for custom intervals + return { + dict: {}, + hasEaseForPath: () => false, + getEaseByPath: () => 250, + baseEase: () => 250, + }; + } + + cardGetResetSchedule(): RepItemScheduleInfo { + // Reset to new card state + return null; + } + + cardGetNewSchedule( + response: ReviewResponse, + _notePath: string, + _dueDateFlashcardHistogram: DueDateHistogram, + ): RepItemScheduleInfo { + const interval = this.getCustomInterval(response); + const dueDate = globalDateProvider.today.add(interval, "d"); + + return new RepItemScheduleInfoOsr(dueDate, interval, 250); // Use default ease for custom intervals + } + + cardCalcUpdatedSchedule( + response: ReviewResponse, + _schedule: RepItemScheduleInfo, + _dueDateFlashcardHistogram: DueDateHistogram, + ): RepItemScheduleInfo { + const interval = this.getCustomInterval(response); + const dueDate = globalDateProvider.today.add(interval, "d"); + + return new RepItemScheduleInfoOsr(dueDate, interval, 250); // Use default ease for custom intervals + } + + private getCustomInterval(response: ReviewResponse): number { + switch (response) { + case ReviewResponse.Easy: + return this.settings.customIntervalEasy; + case ReviewResponse.Good: + return this.settings.customIntervalGood; + case ReviewResponse.Hard: + return this.settings.customIntervalHard; + case ReviewResponse.Reset: + return 0; // Reset means new card + default: + return this.settings.customIntervalGood; // Default to Good interval + } + } +} diff --git a/src/gui/settings.tsx b/src/gui/settings.tsx index 20dc2092..3b02b841 100644 --- a/src/gui/settings.tsx +++ b/src/gui/settings.tsx @@ -833,14 +833,114 @@ export class SRSettingTab extends PluginSettingTab { dropdown .addOptions({ "SM-2-OSR": t("SM2_OSR_VARIANT"), + "Custom-Intervals": t("CUSTOM_INTERVALS"), }) .setValue(this.plugin.data.settings.algorithm) .onChange(async (value) => { this.plugin.data.settings.algorithm = value; await this.plugin.savePluginData(); + // Reinitialize algorithm instance with new setting + this.plugin.setupDataStoreAndAlgorithmInstances(this.plugin.data.settings); + // Refresh the settings display to show/hide custom interval settings + this.display(); }), ); + // Custom interval settings (only show when Custom Intervals algorithm is selected) + if (this.plugin.data.settings.algorithm === "Custom-Intervals") { + containerEl.createEl("h3", { text: t("CUSTOM_INTERVALS") }); + + new Setting(containerEl) + .setName(t("CUSTOM_INTERVAL_EASY")) + .setDesc(t("CUSTOM_INTERVAL_EASY_DESC")) + .addText((text) => + text + .setValue(this.plugin.data.settings.customIntervalEasy.toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseInt(value); + if (!isNaN(numValue) && numValue > 0) { + this.plugin.data.settings.customIntervalEasy = numValue; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); + } + }); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.customIntervalEasy = + DEFAULT_SETTINGS.customIntervalEasy; + await this.plugin.savePluginData(); + this.display(); + }); + }); + + new Setting(containerEl) + .setName(t("CUSTOM_INTERVAL_GOOD")) + .setDesc(t("CUSTOM_INTERVAL_GOOD_DESC")) + .addText((text) => + text + .setValue(this.plugin.data.settings.customIntervalGood.toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseInt(value); + if (!isNaN(numValue) && numValue > 0) { + this.plugin.data.settings.customIntervalGood = numValue; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); + } + }); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.customIntervalGood = + DEFAULT_SETTINGS.customIntervalGood; + await this.plugin.savePluginData(); + this.display(); + }); + }); + + new Setting(containerEl) + .setName(t("CUSTOM_INTERVAL_HARD")) + .setDesc(t("CUSTOM_INTERVAL_HARD_DESC")) + .addText((text) => + text + .setValue(this.plugin.data.settings.customIntervalHard.toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseInt(value); + if (!isNaN(numValue) && numValue > 0) { + this.plugin.data.settings.customIntervalHard = numValue; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); + } + }); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.customIntervalHard = + DEFAULT_SETTINGS.customIntervalHard; + await this.plugin.savePluginData(); + this.display(); + }); + }); + } + new Setting(containerEl) .setName(t("BASE_EASE")) .setDesc(t("BASE_EASE_DESC")) diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 5f9f0aed..9834ad4f 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -167,6 +167,7 @@ export default { CHECK_ALGORITHM_WIKI: 'For more information, check the algorithm details.', SM2_OSR_VARIANT: "OSR's variant of SM-2", + CUSTOM_INTERVALS: "Custom Intervals", BASE_EASE: "Base ease", BASE_EASE_DESC: "minimum = 130, preferrably approximately 250.", BASE_EASE_MIN_WARNING: "The base ease must be at least 130.", @@ -176,6 +177,12 @@ export default { EASY_BONUS_DESC: "The easy bonus allows you to set the difference in intervals between answering Good and Easy on a flashcard/note (minimum = 100%).", EASY_BONUS_MIN_WARNING: "The easy bonus must be at least 100.", + CUSTOM_INTERVAL_EASY: "Easy Interval (days)", + CUSTOM_INTERVAL_EASY_DESC: "Number of days before the next review when answering Easy.", + CUSTOM_INTERVAL_GOOD: "Good Interval (days)", + CUSTOM_INTERVAL_GOOD_DESC: "Number of days before the next review when answering Good.", + CUSTOM_INTERVAL_HARD: "Hard Interval (days)", + CUSTOM_INTERVAL_HARD_DESC: "Number of days before the next review when answering Hard.", LOAD_BALANCE: "Enable load balancer", LOAD_BALANCE_DESC: `Slightly tweaks the interval so that the number of reviews per day is more consistent. It's like Anki's fuzz but instead of being random, it picks the day with the least amount of reviews. diff --git a/src/main.ts b/src/main.ts index c92c9152..c71cdaa4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,9 @@ import { Menu, Notice, Plugin, TAbstractFile, TFile, WorkspaceLeaf } from "obsidian"; +import { Algorithm } from "src/algorithms/base/isrs-algorithm"; import { ReviewResponse } from "src/algorithms/base/repetition-item"; import { SrsAlgorithm } from "src/algorithms/base/srs-algorithm"; +import { SrsAlgorithmCustom } from "src/algorithms/custom/srs-algorithm-custom"; import { ObsidianVaultNoteLinkInfoFinder } from "src/algorithms/osr/obsidian-vault-notelink-info-finder"; import { SrsAlgorithmOsr } from "src/algorithms/osr/srs-algorithm-osr"; import { OsrAppCore } from "src/core"; @@ -523,9 +525,20 @@ export default class SRPlugin extends Plugin { } setupDataStoreAndAlgorithmInstances(settings: SRSettings) { - // For now we can hardcode as we only support the one data store and one algorithm + // Set up data store (only one supported for now) DataStore.instance = new StoreInNotes(settings); - SrsAlgorithm.instance = new SrsAlgorithmOsr(settings); + + // Select algorithm based on settings + switch (settings.algorithm) { + case Algorithm.CUSTOM_INTERVALS: + SrsAlgorithm.instance = new SrsAlgorithmCustom(settings); + break; + case Algorithm.SM_2_OSR: + default: + SrsAlgorithm.instance = new SrsAlgorithmOsr(settings); + break; + } + DataStoreAlgorithm.instance = new DataStoreInNoteAlgorithmOsr(settings); } async savePluginData(): Promise { diff --git a/src/settings.ts b/src/settings.ts index d2c8a3da..473c6008 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -56,6 +56,11 @@ export interface SRSettings { maximumInterval: number; maxLinkFactor: number; + // custom intervals (for notes and flashcards) + customIntervalEasy: number; + customIntervalGood: number; + customIntervalHard: number; + // storage dataStore: string; cardCommentOnSameLine: boolean; @@ -116,6 +121,11 @@ export const DEFAULT_SETTINGS: SRSettings = { maximumInterval: 36525, maxLinkFactor: 1.0, + // custom intervals (for notes and flashcards) - in days + customIntervalEasy: 4, + customIntervalGood: 2, + customIntervalHard: 1, + // storage dataStore: DataStoreName.NOTES, cardCommentOnSameLine: false, @@ -152,6 +162,17 @@ export function upgradeSettings(settings: SRSettings) { if (settings.convertCurlyBracketsToClozes) settings.clozePatterns.push("{{[123;;]answer[;;hint]}}"); } + + // Add custom interval settings if they don't exist (for existing users) + if (settings.customIntervalEasy == null) { + settings.customIntervalEasy = DEFAULT_SETTINGS.customIntervalEasy; + } + if (settings.customIntervalGood == null) { + settings.customIntervalGood = DEFAULT_SETTINGS.customIntervalGood; + } + if (settings.customIntervalHard == null) { + settings.customIntervalHard = DEFAULT_SETTINGS.customIntervalHard; + } } export class SettingsUtil { From 755528664c43f1d046ad693626344b2a4e01b005 Mon Sep 17 00:00:00 2001 From: Darren Sadr Date: Sun, 24 Aug 2025 23:06:18 -0700 Subject: [PATCH 2/3] fix settings --- src/gui/settings.tsx | 324 ++++++++++++++++++++++--------------------- 1 file changed, 166 insertions(+), 158 deletions(-) diff --git a/src/gui/settings.tsx b/src/gui/settings.tsx index 3b02b841..9d5de3d2 100644 --- a/src/gui/settings.tsx +++ b/src/gui/settings.tsx @@ -848,8 +848,6 @@ export class SRSettingTab extends PluginSettingTab { // Custom interval settings (only show when Custom Intervals algorithm is selected) if (this.plugin.data.settings.algorithm === "Custom-Intervals") { - containerEl.createEl("h3", { text: t("CUSTOM_INTERVALS") }); - new Setting(containerEl) .setName(t("CUSTOM_INTERVAL_EASY")) .setDesc(t("CUSTOM_INTERVAL_EASY_DESC")) @@ -941,177 +939,187 @@ export class SRSettingTab extends PluginSettingTab { }); } - new Setting(containerEl) - .setName(t("BASE_EASE")) - .setDesc(t("BASE_EASE_DESC")) - .addText((text) => - text.setValue(this.plugin.data.settings.baseEase.toString()).onChange((value) => { - applySettingsUpdate(async () => { - const numValue: number = Number.parseInt(value); - if (!isNaN(numValue)) { - if (numValue < 130) { - new Notice(t("BASE_EASE_MIN_WARNING")); - text.setValue(this.plugin.data.settings.baseEase.toString()); - return; - } - - this.plugin.data.settings.baseEase = numValue; + // SM-2 OSR specific settings (only show when SM-2 OSR algorithm is selected) + if (this.plugin.data.settings.algorithm === "SM-2-OSR") { + new Setting(containerEl) + .setName(t("BASE_EASE")) + .setDesc(t("BASE_EASE_DESC")) + .addText((text) => + text + .setValue(this.plugin.data.settings.baseEase.toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseInt(value); + if (!isNaN(numValue)) { + if (numValue < 130) { + new Notice(t("BASE_EASE_MIN_WARNING")); + text.setValue( + this.plugin.data.settings.baseEase.toString(), + ); + return; + } + + this.plugin.data.settings.baseEase = numValue; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); + } + }); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.baseEase = DEFAULT_SETTINGS.baseEase; await this.plugin.savePluginData(); - } else { - new Notice(t("VALID_NUMBER_WARNING")); - } - }); - }), - ) - .addExtraButton((button) => { - button - .setIcon("reset") - .setTooltip(t("RESET_DEFAULT")) - .onClick(async () => { - this.plugin.data.settings.baseEase = DEFAULT_SETTINGS.baseEase; - await this.plugin.savePluginData(); - this.display(); - }); - }); + this.display(); + }); + }); - new Setting(containerEl) - .setName(t("LAPSE_INTERVAL_CHANGE")) - .setDesc(t("LAPSE_INTERVAL_CHANGE_DESC")) - .addSlider((slider) => - slider - .setLimits(1, 99, 1) - .setValue(this.plugin.data.settings.lapsesIntervalChange * 100) - .setDynamicTooltip() - .onChange(async (value: number) => { - this.plugin.data.settings.lapsesIntervalChange = value / 100; - await this.plugin.savePluginData(); - }), - ) - .addExtraButton((button) => { - button - .setIcon("reset") - .setTooltip(t("RESET_DEFAULT")) - .onClick(async () => { - this.plugin.data.settings.lapsesIntervalChange = - DEFAULT_SETTINGS.lapsesIntervalChange; - await this.plugin.savePluginData(); + new Setting(containerEl) + .setName(t("LAPSE_INTERVAL_CHANGE")) + .setDesc(t("LAPSE_INTERVAL_CHANGE_DESC")) + .addSlider((slider) => + slider + .setLimits(1, 99, 1) + .setValue(this.plugin.data.settings.lapsesIntervalChange * 100) + .setDynamicTooltip() + .onChange(async (value: number) => { + this.plugin.data.settings.lapsesIntervalChange = value / 100; + await this.plugin.savePluginData(); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.lapsesIntervalChange = + DEFAULT_SETTINGS.lapsesIntervalChange; + await this.plugin.savePluginData(); - this.display(); - }); - }); + this.display(); + }); + }); - new Setting(containerEl) - .setName(t("EASY_BONUS")) - .setDesc(t("EASY_BONUS_DESC")) - .addText((text) => - text - .setValue((this.plugin.data.settings.easyBonus * 100).toString()) - .onChange((value) => { - applySettingsUpdate(async () => { - const numValue: number = Number.parseInt(value) / 100; - if (!isNaN(numValue)) { - if (numValue < 1.0) { - new Notice(t("EASY_BONUS_MIN_WARNING")); - text.setValue( - (this.plugin.data.settings.easyBonus * 100).toString(), - ); - return; + new Setting(containerEl) + .setName(t("EASY_BONUS")) + .setDesc(t("EASY_BONUS_DESC")) + .addText((text) => + text + .setValue((this.plugin.data.settings.easyBonus * 100).toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseInt(value) / 100; + if (!isNaN(numValue)) { + if (numValue < 1.0) { + new Notice(t("EASY_BONUS_MIN_WARNING")); + text.setValue( + (this.plugin.data.settings.easyBonus * 100).toString(), + ); + return; + } + + this.plugin.data.settings.easyBonus = numValue; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); } + }); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.easyBonus = DEFAULT_SETTINGS.easyBonus; + await this.plugin.savePluginData(); - this.plugin.data.settings.easyBonus = numValue; - await this.plugin.savePluginData(); - } else { - new Notice(t("VALID_NUMBER_WARNING")); - } + this.display(); }); - }), - ) - .addExtraButton((button) => { - button - .setIcon("reset") - .setTooltip(t("RESET_DEFAULT")) - .onClick(async () => { - this.plugin.data.settings.easyBonus = DEFAULT_SETTINGS.easyBonus; - await this.plugin.savePluginData(); - - this.display(); - }); - }); + }); - new Setting(containerEl) - .setName(t("LOAD_BALANCE")) - .setDesc(t("LOAD_BALANCE_DESC")) - .addToggle((toggle) => - toggle.setValue(this.plugin.data.settings.loadBalance).onChange(async (value) => { - this.plugin.data.settings.loadBalance = value; - await this.plugin.savePluginData(); - }), - ); + new Setting(containerEl) + .setName(t("LOAD_BALANCE")) + .setDesc(t("LOAD_BALANCE_DESC")) + .addToggle((toggle) => + toggle + .setValue(this.plugin.data.settings.loadBalance) + .onChange(async (value) => { + this.plugin.data.settings.loadBalance = value; + await this.plugin.savePluginData(); + }), + ); - new Setting(containerEl) - .setName(t("MAX_INTERVAL")) - .setDesc(t("MAX_INTERVAL_DESC")) - .addText((text) => - text - .setValue(this.plugin.data.settings.maximumInterval.toString()) - .onChange((value) => { - applySettingsUpdate(async () => { - const numValue: number = Number.parseInt(value); - if (!isNaN(numValue)) { - if (numValue < 1) { - new Notice(t("MAX_INTERVAL_MIN_WARNING")); - text.setValue( - this.plugin.data.settings.maximumInterval.toString(), - ); - return; + new Setting(containerEl) + .setName(t("MAX_INTERVAL")) + .setDesc(t("MAX_INTERVAL_DESC")) + .addText((text) => + text + .setValue(this.plugin.data.settings.maximumInterval.toString()) + .onChange((value) => { + applySettingsUpdate(async () => { + const numValue: number = Number.parseInt(value); + if (!isNaN(numValue)) { + if (numValue < 1) { + new Notice(t("MAX_INTERVAL_MIN_WARNING")); + text.setValue( + this.plugin.data.settings.maximumInterval.toString(), + ); + return; + } + + this.plugin.data.settings.maximumInterval = numValue; + await this.plugin.savePluginData(); + } else { + new Notice(t("VALID_NUMBER_WARNING")); } + }); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.maximumInterval = + DEFAULT_SETTINGS.maximumInterval; + await this.plugin.savePluginData(); - this.plugin.data.settings.maximumInterval = numValue; - await this.plugin.savePluginData(); - } else { - new Notice(t("VALID_NUMBER_WARNING")); - } + this.display(); }); - }), - ) - .addExtraButton((button) => { - button - .setIcon("reset") - .setTooltip(t("RESET_DEFAULT")) - .onClick(async () => { - this.plugin.data.settings.maximumInterval = - DEFAULT_SETTINGS.maximumInterval; - await this.plugin.savePluginData(); - - this.display(); - }); - }); + }); - new Setting(containerEl) - .setName(t("MAX_LINK_CONTRIB")) - .setDesc(t("MAX_LINK_CONTRIB_DESC")) - .addSlider((slider) => - slider - .setLimits(0, 100, 1) - .setValue(this.plugin.data.settings.maxLinkFactor * 100) - .setDynamicTooltip() - .onChange(async (value: number) => { - this.plugin.data.settings.maxLinkFactor = value / 100; - await this.plugin.savePluginData(); - }), - ) - .addExtraButton((button) => { - button - .setIcon("reset") - .setTooltip(t("RESET_DEFAULT")) - .onClick(async () => { - this.plugin.data.settings.maxLinkFactor = DEFAULT_SETTINGS.maxLinkFactor; - await this.plugin.savePluginData(); + new Setting(containerEl) + .setName(t("MAX_LINK_CONTRIB")) + .setDesc(t("MAX_LINK_CONTRIB_DESC")) + .addSlider((slider) => + slider + .setLimits(0, 100, 1) + .setValue(this.plugin.data.settings.maxLinkFactor * 100) + .setDynamicTooltip() + .onChange(async (value: number) => { + this.plugin.data.settings.maxLinkFactor = value / 100; + await this.plugin.savePluginData(); + }), + ) + .addExtraButton((button) => { + button + .setIcon("reset") + .setTooltip(t("RESET_DEFAULT")) + .onClick(async () => { + this.plugin.data.settings.maxLinkFactor = + DEFAULT_SETTINGS.maxLinkFactor; + await this.plugin.savePluginData(); - this.display(); - }); - }); + this.display(); + }); + }); + } containerEl.createEl("h3", { text: t("GROUP_DATA_STORAGE") }); new Setting(containerEl) From eb2250f68685bd90f70879bad9ba081693af1d0e Mon Sep 17 00:00:00 2001 From: Darren Sadr Date: Sun, 24 Aug 2025 23:30:38 -0700 Subject: [PATCH 3/3] remove sr-ease and sr-interval from custom intervals --- .../data-store-in-note-algorithm-osr.ts | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/data-store-algorithm/data-store-in-note-algorithm-osr.ts b/src/data-store-algorithm/data-store-in-note-algorithm-osr.ts index 6fe4b67b..29ca6bbc 100644 --- a/src/data-store-algorithm/data-store-in-note-algorithm-osr.ts +++ b/src/data-store-algorithm/data-store-in-note-algorithm-osr.ts @@ -1,6 +1,7 @@ import { Moment } from "moment"; import moment from "moment"; +import { Algorithm } from "src/algorithms/base/isrs-algorithm"; import { RepItemScheduleInfo } from "src/algorithms/base/rep-item-schedule-info"; import { RepItemScheduleInfoOsr } from "src/algorithms/osr/rep-item-schedule-info-osr"; import { Card } from "src/card"; @@ -32,15 +33,22 @@ export class DataStoreInNoteAlgorithmOsr implements IDataStoreAlgorithm { let result: RepItemScheduleInfo = null; const frontmatter: Map = await note.getFrontmatter(); - if ( - frontmatter && - frontmatter.has("sr-due") && - frontmatter.has("sr-interval") && - frontmatter.has("sr-ease") - ) { + if (frontmatter && frontmatter.has("sr-due")) { const dueDate: Moment = moment(frontmatter.get("sr-due"), ALLOWED_DATE_FORMATS); - const interval: number = parseFloat(frontmatter.get("sr-interval")); - const ease: number = parseFloat(frontmatter.get("sr-ease")); + + // For Custom Intervals, interval might not be stored, so use a default value + // The actual interval will be determined by the algorithm when scheduling + let interval: number = 1; // Default interval + if (frontmatter.has("sr-interval")) { + interval = parseFloat(frontmatter.get("sr-interval")); + } + + // For Custom Intervals, ease might not be stored, so use default value + let ease: number = 250; // Default ease + if (frontmatter.has("sr-ease")) { + ease = parseFloat(frontmatter.get("sr-ease")); + } + result = new RepItemScheduleInfoOsr(dueDate, interval, ease); } return result; @@ -54,13 +62,18 @@ export class DataStoreInNoteAlgorithmOsr implements IDataStoreAlgorithm { const interval: number = schedInfo.interval; const ease: number = schedInfo.latestEase; + // Determine what to include based on algorithm + const includeSM2Data = this.settings.algorithm === Algorithm.SM_2_OSR; + const intervalString = includeSM2Data ? `sr-interval: ${interval}\n` : ""; + const easeString = includeSM2Data ? `sr-ease: ${ease}\n` : ""; + // check if scheduling info exists if (SCHEDULING_INFO_REGEX.test(fileText)) { const schedulingInfo = SCHEDULING_INFO_REGEX.exec(fileText); fileText = fileText.replace( SCHEDULING_INFO_REGEX, `---\n${schedulingInfo[1]}sr-due: ${dueString}\n` + - `sr-interval: ${interval}\nsr-ease: ${ease}\n` + + `${intervalString}${easeString}` + `${schedulingInfo[5]}---`, ); } else if (YAML_FRONT_MATTER_REGEX.test(fileText)) { @@ -69,12 +82,11 @@ export class DataStoreInNoteAlgorithmOsr implements IDataStoreAlgorithm { fileText = fileText.replace( YAML_FRONT_MATTER_REGEX, `---\n${existingYaml[1]}sr-due: ${dueString}\n` + - `sr-interval: ${interval}\nsr-ease: ${ease}\n---`, + `${intervalString}${easeString}---`, ); } else { fileText = - `---\nsr-due: ${dueString}\nsr-interval: ${interval}\n` + - `sr-ease: ${ease}\n---\n\n${fileText}`; + `---\nsr-due: ${dueString}\n` + `${intervalString}${easeString}---\n\n${fileText}`; } await note.write(fileText);