Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions .github/workflows/wiki-page-count-badge.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
name: Update Wiki Badge Count

on:
workflow_run:
workflows: ["Sync GitHub Wiki"]
types: [completed]
push:
branches: [main]
paths:
- "wiki/**/*.md"
- ".github/workflows/wiki-page-count-badge.yml"
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
update-wiki-badge:
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -54,16 +58,22 @@ jobs:
print("README badge already up to date")
PY

- name: Commit and push badge update
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

git add README.md
if git diff --cached --quiet; then
echo "No README badge changes to commit."
exit 0
fi
- name: Create pull request for badge update
id: cpr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "chore(readme): update wiki badge page count to ${{ steps.count.outputs.count }}"
title: "chore(readme): update wiki badge page count to ${{ steps.count.outputs.count }}"
body: "Automated update of the wiki page count badge in README.md."
labels: "automation"
branch: "automation/wiki-page-count-badge"
delete-branch: true

git commit -m "chore(readme): update wiki badge page count to ${{ steps.count.outputs.count }}"
git push
- name: Enable auto-merge for badge PR
if: steps.cpr.outputs.pull-request-number != ''
uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Faster, smoother studying designed specifically for mobile workflows.
![Sprout Banner Five - Data Analysis](branding/Banner%20Five%20-%20Data%20Analysis.png)
![Sprout Banner Six - Anki Compatibility](branding/Banner%20Six%20-%20Anki%20Compatibility.png)
![Sprout Banner Seven - Card Creation](branding/Banner%20Seven%20-%20Card%20Creation.png)
![Sprout Banner Eight - Reminders & Gatekeeper](branding/Banner%20Eight%20-%20Reminders%20%26%20Gatekeeper.png)

## License

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified branding/Banner Five - Data Analysis.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed branding/Banner Graphics.png
Binary file not shown.
Binary file modified branding/Banner Seven - Card Creation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
146 changes: 73 additions & 73 deletions dist/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/styles.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/reminders/gatekeeper-modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,10 @@ export class GatekeeperModal extends Modal {

private renderBypassWarning(contentEl: HTMLElement) {
const wrap = contentEl.createDiv({ cls: "bc sprout-gatekeeper-warning-wrap" });
wrap.createDiv({ cls: "bc sprout-gatekeeper-warning-text", text: "You are cheating..." });
wrap.createDiv({ cls: "bc sprout-gatekeeper-warning-text", text: "Bypass this round?" });
wrap.createDiv({
cls: "bc text-muted-foreground text-sm sprout-gatekeeper-warning-subtext",
text: "Bypassing means this gatekeeper round is not completed.",
text: "You've got cards due today — continuing may weaken your long-term retention!",
});

const actions = wrap.createDiv({ cls: "bc flex items-center justify-center gap-2" });
Expand Down
39 changes: 7 additions & 32 deletions src/settings/sprout-settings-tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1080,19 +1080,17 @@

new Setting(wrapper)
.setName("Cloze mode")
.setDesc("Standard shows normal cloze blanks. Typed replaces blanks with a text input for active recall.")
.setDesc("Choose how cloze cards are answered: Standard shows normal blanks, while Typed uses an input box for active recall.")

Check failure on line 1083 in src/settings/sprout-settings-tab.ts

View workflow job for this annotation

GitHub Actions / build-test-lint

Use sentence case for UI text
.then((s) => {
this._addSimpleSelect(s.controlEl, {
options: [
{
value: "standard",
label: "Standard",
description: "Classic cloze flow with highlighted deletions and reveal styling.",
},
{
value: "typed",
label: "Typed",
description: "Replaces blanks with a text input for active recall before reveal.",
},
],
value: cardsSettings.clozeMode ?? "standard",
Expand Down Expand Up @@ -1197,19 +1195,17 @@

new Setting(wrapper)
.setName("Reveal mode")
.setDesc("For hide-all image occlusion cards, choose what is unmasked on reveal: reveal all unmasks every mask, while reveal group unmasks only the answer group. Hide-group cards are unaffected because other groups are never hidden.")
.setDesc("For hide-all image occlusion cards, choose reveal behavior: Reveal group unmasks only the answer group, while Reveal all unmasks every group. Hide-group cards are unaffected.")

Check failure on line 1198 in src/settings/sprout-settings-tab.ts

View workflow job for this annotation

GitHub Actions / build-test-lint

Use sentence case for UI text
.then((s) => {
this._addSimpleSelect(s.controlEl, {
options: [
{
value: "group",
label: "Reveal group",
description: "On reveal, only the answer group is unmasked; other groups stay masked (Hide all only).",
},
{
value: "all",
label: "Reveal all",
description: "On reveal, all masks are unmasked (Hide all only).",
},
],
value: this.plugin.settings.imageOcclusion?.revealMode || "group",
Expand Down Expand Up @@ -2203,19 +2199,17 @@

new Setting(wrapper)
.setName("Grading buttons")
.setDesc("Choose two buttons (again, good) or four buttons (again, hard, good, easy).")
.setDesc("Choose your grading layout: Two buttons shows Again + Good; Four buttons shows Again + Hard + Good + Easy.")

Check failure on line 2202 in src/settings/sprout-settings-tab.ts

View workflow job for this annotation

GitHub Actions / build-test-lint

Use sentence case for UI text
.then((s) => {
this._addSimpleSelect(s.controlEl, {
options: [
{
value: "two",
label: "Two buttons",
description: "Again + Good only. Faster flow with simpler grading.",
},
{
value: "four",
label: "Four buttons",
description: "Again + Hard + Good + Easy. More control over intervals.",
},
],
value: this.plugin.settings.study.fourButtonMode ? "four" : "two",
Expand Down Expand Up @@ -2279,24 +2273,21 @@

new Setting(wrapper)
.setName("Sibling card management")
.setDesc("Choose how sibling cards from the same note are managed during study sessions.")
.setDesc("Choose how sibling cards are handled: Standard queueing allows normal order, Disperse siblings spaces them apart, and Bury siblings shows one today and postpones the rest.")

Check failure on line 2276 in src/settings/sprout-settings-tab.ts

View workflow job for this annotation

GitHub Actions / build-test-lint

Use sentence case for UI text
.then((s) => {
this._addSimpleSelect(s.controlEl, {
options: [
{
value: "standard",
label: "Standard queueing",
description: "Uses normal FSRS ordering. Siblings can appear close together, especially early in a queue.",
},
{
value: "disperse",
label: "Disperse siblings",
description: "Spreads sibling cards out across the queue so they are less likely to appear back-to-back.",
},
{
value: "bury",
label: "Bury siblings",
description: "Shows one sibling card today and buries the rest until the next day.",
},
],
separatorAfterIndex: 0,
Expand Down Expand Up @@ -2501,20 +2492,18 @@
gatekeeperScopeSetting = new Setting(wrapper)
.setName("Gatekeeper scope")
.setDesc(
"Choose what gatekeeper blocks. Current tab blocks only the active tab; full workspace blocks the entire workspace.",
"Choose what gatekeeper blocks: Full workspace blocks the whole workspace; Current tab blocks only the active tab.",

Check failure on line 2495 in src/settings/sprout-settings-tab.ts

View workflow job for this annotation

GitHub Actions / build-test-lint

Use sentence case for UI text
)
.then((s) => {
this._addSimpleSelect(s.controlEl, {
options: [
{
value: "workspace",
label: "Full workspace",
description: "Blocks the entire workspace until gatekeeper is completed or bypassed.",
},
{
value: "current-tab",
label: "Current tab",
description: "Blocks only the current tab and allows switching to other tabs.",
},
],
value: this.plugin.settings.reminders.gatekeeperScope ?? "workspace",
Expand Down Expand Up @@ -2688,10 +2677,10 @@

new Setting(wrapper)
.setName("Preset")
.setDesc("Apply a recommended configuration, or choose custom to keep your current values.")
.setDesc("Apply a scheduling preset to learning steps, relearning steps, and retention target, or choose Custom to keep current values.")

Check failure on line 2680 in src/settings/sprout-settings-tab.ts

View workflow job for this annotation

GitHub Actions / build-test-lint

Use sentence case for UI text
.then((s) => {
presetHandle = this._addSimpleSelect(s.controlEl, {
options: presets.map((p) => ({ value: p.key, label: p.label, description: p.desc })),
options: presets.map((p) => ({ value: p.key, label: p.label })),
separatorAfterIndex: 0,
value: detectPresetKey(),

Expand Down Expand Up @@ -3432,13 +3421,6 @@
txt.textContent = opt.label;
textWrap.appendChild(txt);

if (opt.description) {
const desc = document.createElement("span");
desc.className = "sprout-ss-item-desc";
desc.textContent = opt.description;
textWrap.appendChild(desc);
}

item.appendChild(textWrap);

const activate = () => {
Expand Down Expand Up @@ -3694,13 +3676,6 @@
txt.textContent = opt.label;
textWrap.appendChild(txt);

if (opt.description) {
const desc = document.createElement("span");
desc.className = "sprout-ss-item-desc";
desc.textContent = opt.description;
textWrap.appendChild(desc);
}

item.appendChild(textWrap);

const activate = () => {
Expand Down
79 changes: 79 additions & 0 deletions src/settings/sprout-settings-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,83 @@ export class SproutSettingsView extends ItemView {
return getGuidePageIcon(pageKey);
}

private _normalizeGuideLinkTarget(raw: string): string {
if (!raw) return "";
let value = raw.trim();
if (!value) return "";

value = value.replace(/^obsidian:\/\/open\?[^#]*file=/i, "");
value = value.replace(/^[./]+/, "");
value = value.split("#")[0]?.split("?")[0] ?? "";
if (!value) return "";

try {
value = decodeURIComponent(value);
} catch {
// keep raw value
}

value = value.replace(/\\/g, "/");
const pieces = value.split("/").filter(Boolean);
value = pieces.length ? pieces[pieces.length - 1] : value;
value = value.replace(/\.md$/i, "").trim();

return value
.toLowerCase()
.replace(/[_\s]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}

private _wireGuideContentLinks(body: HTMLElement, pages: GuidePage[], sourcePath: string) {
const keyByNormalized = new Map<string, string>();

const addAlias = (alias: string, pageKey: string) => {
const normalized = this._normalizeGuideLinkTarget(alias);
if (!normalized) return;
if (!keyByNormalized.has(normalized)) keyByNormalized.set(normalized, pageKey);
};

for (const page of pages) {
addAlias(page.key, page.key);
addAlias(page.label, page.key);
addAlias(this._getGuidePageDisplayLabel(page.key), page.key);
addAlias(`${page.key}.md`, page.key);
}

body.addEventListener("click", (ev) => {
const target = ev.target as HTMLElement | null;
const link = target?.closest("a") as HTMLAnchorElement | null;
if (!link) return;

const dataHref = link.getAttribute("data-href") ?? "";
const hrefAttr = link.getAttribute("href") ?? "";
const rawTarget = dataHref || hrefAttr;
if (!rawTarget) return;

if (/^(https?:|mailto:|tel:)/i.test(rawTarget)) return;

const normalized = this._normalizeGuideLinkTarget(rawTarget);
if (!normalized) return;

const matchedKey = keyByNormalized.get(normalized);
if (matchedKey) {
ev.preventDefault();
ev.stopPropagation();
this._activeGuidePage = matchedKey;
this._renderActiveTabContent();
return;
}

const fallbackTarget = rawTarget.replace(/^\[\[|\]\]$/g, "");
if (!fallbackTarget || fallbackTarget.startsWith("#")) return;

ev.preventDefault();
ev.stopPropagation();
void this.app.workspace.openLinkText(fallbackTarget, sourcePath || "", false);
});
}

private _ensureReleaseComponent(): Component {
if (!this._releaseComponent) this._releaseComponent = new Component();
return this._releaseComponent;
Expand Down Expand Up @@ -768,6 +845,8 @@ export class SproutSettingsView extends ItemView {
this._ensureReleaseComponent(),
);

this._wireGuideContentLinks(body, pages, selected.sourcePath);

/* Enhance About page when shown in the Guide tab */
if (selected.key === "Support-Sprout") {
this._enhanceAboutPage(body);
Expand Down
2 changes: 1 addition & 1 deletion src/styles/modals.css
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@

.sprout .sprout-gatekeeper-warning-subtext {
max-width: 420px;
line-height: 1.4;
line-height: 1.75;
}

.sprout .sprout-gatekeeper-modal.sprout-gatekeeper-warning-mode > .sprout-gatekeeper-top-row,
Expand Down
Loading
Loading