From d46be3cce56a802a1b142bf90ae0ab89e73a2477 Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Mon, 17 Nov 2025 21:33:15 -0500 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20rename=20to=20Deck=20Notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 8 +-- CONTRIBUTING.md | 14 ++--- README.md | 53 ++++++++++++------- manifest-beta.json | 10 ++++ manifest.json | 6 +-- package-lock.json | 16 +++--- package.json | 4 +- src/@types/settings.d.ts | 4 +- src/@types/window-type.d.ts | 6 +-- src/{flashcards-Api.ts => dn-Api.ts} | 8 +-- ...shcards-CardParser.ts => dn-CardParser.ts} | 0 ...lashcards-Constants.ts => dn-Constants.ts} | 4 +- src/{flashcards-Modal.ts => dn-Modal.ts} | 6 +-- src/{flashcards-Plugin.ts => dn-Plugin.ts} | 42 +++++++-------- ...cards-SettingsTab.ts => dn-SettingsTab.ts} | 18 +++---- src/main.ts | 4 +- 16 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 manifest-beta.json rename src/{flashcards-Api.ts => dn-Api.ts} (89%) rename src/{flashcards-CardParser.ts => dn-CardParser.ts} (100%) rename src/{flashcards-Constants.ts => dn-Constants.ts} (50%) rename src/{flashcards-Modal.ts => dn-Modal.ts} (96%) rename src/{flashcards-Plugin.ts => dn-Plugin.ts} (83%) rename src/{flashcards-SettingsTab.ts => dn-SettingsTab.ts} (90%) diff --git a/CLAUDE.md b/CLAUDE.md index 5ec5b5f..489152b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,10 +31,10 @@ When working with this codebase: **Core modules to understand:** -- `flashcards-Plugin.ts` - Main plugin, scanning, filtering -- `flashcards-CardParser.ts` - Parsing and tag extraction -- `flashcards-Modal.ts` - Display modal -- `flashcards-Api.ts` - External API +- `dn-Plugin.ts` - Main plugin, scanning, filtering +- `dn-CardParser.ts` - Parsing and tag extraction +- `dn-Modal.ts` - Display modal +- `dn-Api.ts` - External API - `@types/settings.d.ts` - Interfaces ## Key Development Principles diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe4d4ec..3ca1aba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to Simple Flashcards +# Contributing to Deck Notes -This is an Obsidian plugin for simple flashcard creation and review using tag-based deck organization. See [README.md](README.md) for user-facing features and usage. +This is an Obsidian plugin for tag-based card decks. Define cards for activities, strategies, or any content you want to rotate through. See [README.md](README.md) for user-facing features and usage. ## Development Commands @@ -16,11 +16,11 @@ npm run format # Format code ### Core Files -- **`flashcards-Plugin.ts`** - Main plugin class, card scanning, deck filtering -- **`flashcards-CardParser.ts`** - Parses markdown files into cards, extracts tags -- **`flashcards-Modal.ts`** - Card display modal with deck switching -- **`flashcards-Api.ts`** - JavaScript API for external access -- **`flashcards-SettingsTab.ts`** - Settings UI +- **`dn-Plugin.ts`** - Main plugin class, card scanning, deck filtering +- **`dn-CardParser.ts`** - Parses markdown files into cards, extracts tags +- **`dn-Modal.ts`** - Card display modal with deck switching +- **`dn-Api.ts`** - JavaScript API for external access +- **`dn-SettingsTab.ts`** - Settings UI - **`@types/settings.d.ts`** - TypeScript interfaces ### Key Concepts diff --git a/README.md b/README.md index df06c5c..9291359 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Simple Flashcards +# Deck Notes -A simple activity card plugin for Obsidian. Display random cards from your markdown files for meditations, prompts, exercises, or any repeatable activities. +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. ## Features @@ -15,20 +15,22 @@ A simple activity card plugin for Obsidian. Display random cards from your markd ### Manual Installation 1. Download the latest release from the [Releases page](https://github.com/ebullient/obsidian-flashcards/releases) -2. Extract the files into your vault's plugins folder: `/.obsidian/plugins/simple-flashcards/` +2. Extract the files into your vault's plugins folder: `/.obsidian/plugins/deck-notes/` 3. Reload Obsidian 4. Enable the plugin in Settings → Community Plugins ### Development Installation 1. Clone this repository into your vault's plugins folder: + ```bash cd /.obsidian/plugins - git clone https://github.com/ebullient/obsidian-flashcards.git simple-flashcards - cd simple-flashcards + git clone https://github.com/ebullient/obsidian-flashcards.git deck-notes + cd deck-notes ``` 2. Install dependencies and build: + ```bash npm install npm run build @@ -95,42 +97,51 @@ Repeat 4 times. ## Configuration -Open Settings → Simple Flashcards to configure: +Open Settings → Deck Notes to configure: ### Card Paths + List of folders or files to scan for cards (one per line, relative to vault root). This defines which files are scanned, not which cards are displayed. Example: -``` + +```txt Activities/Exercises Daily/Prompts Resources/Meditations.md ``` ### Default Deck Tag + The default tag used by "Show Random Activity Card" command (e.g., `activities` or `activities/morning`). Leave empty to select from all cards with any `#flashcards/*` tag. ### Track Views + Enable tracking of when cards were last viewed. Required for "Least Recently Viewed" selection mode. ### Selection Mode + - **Random** - Pure random selection - **Least Recently Viewed** - Prioritizes cards you haven't seen recently ## Commands ### Show Card + Opens a modal displaying a card from your default deck (or all cards if no default is set). **Modal Controls:** + - **Switch Deck** - Cycle through discovered deck tags (e.g., `activities`, `activities/morning`) - **Next Card** - View another card (records the current card as viewed) - **Done** - Close the modal ### Embed Card + Inserts a card as a collapsible callout at the cursor position. ### Refresh Cards + Rescans all configured paths to pick up new or modified cards. ## Usage Tips @@ -140,12 +151,14 @@ Rescans all configured paths to pick up new or modified cards. Cards are organized into decks using hierarchical `#flashcards/*` tags. Tags support hierarchy, so selecting `activities` will show cards tagged with `activities`, `activities/morning`, `activities/creative`, etc. **Tag inheritance:** + - Tags in frontmatter apply to all cards in the file - Tags before each H2 heading apply only to that card - Cards can have multiple tags for multiple deck membership **Example structure:** -``` + +```txt Activities/stretches.md → All cards get #flashcards/activities from frontmatter Card 1: Also tagged #flashcards/activities/morning Card 2: Also tagged #flashcards/activities/evening @@ -158,6 +171,7 @@ Journal/prompts.md → All cards get #flashcards/creative from frontmatter ### View Tracking With "Track Views" enabled and "Least Recently Viewed" selection mode, the plugin ensures you see all cards before repeating. Perfect for: + - Daily meditation rotations - Exercise variety - Prompt cycling @@ -165,6 +179,7 @@ With "Track Views" enabled and "Least Recently Viewed" selection mode, the plugi ### Tags for Deck Organization Use hierarchical `#flashcards/*` tags to organize cards: + - Add tags in frontmatter: `tags: [flashcards/activities]` (applies to all cards in file) - Add inline tags before H2 headings: `#flashcards/activities/morning` (applies to single card) - Lines starting with `#flashcards` are automatically stripped from card display @@ -172,20 +187,20 @@ Use hierarchical `#flashcards/*` tags to organize cards: ### API Access -The plugin exposes a JavaScript API at `window.simpleFlashcards.api`: +The plugin exposes a JavaScript API at `window.deckNotes.api`: ```javascript // Get a random card embed -window.simpleFlashcards.api.embedCard('activities') +window.deckNotes.api.embedCard('activities') // Get all discovered deck tags -window.simpleFlashcards.api.getTags() +window.deckNotes.api.getTags() // Select cards by hash -window.simpleFlashcards.api.selectCardsByHash(['card-hash-1', 'card-hash-2']) +window.deckNotes.api.selectCardsByHash(['card-hash-1', 'card-hash-2']) // Get random card from hash list -window.simpleFlashcards.api.selectCardByHash(['card-hash-1', 'card-hash-2']) +window.deckNotes.api.selectCardByHash(['card-hash-1', 'card-hash-2']) ``` ## Development @@ -202,15 +217,15 @@ npm run format # Format code ### Project Structure -``` +```txt src/ ├── @types/ │ └── settings.d.ts # TypeScript interfaces - ├── flashcards-Plugin.ts # Main plugin class - ├── flashcards-CardParser.ts # Parse files into cards - ├── flashcards-Modal.ts # Card display modal - ├── flashcards-SettingsTab.ts # Settings UI - ├── flashcards-Constants.ts # Default settings + ├── dn-Plugin.ts # Main plugin class + ├── dn-CardParser.ts # Parse files into cards + ├── dn-Modal.ts # Card display modal + ├── dn-SettingsTab.ts # Settings UI + ├── dn-Constants.ts # Default settings └── main.ts # Entry point ``` diff --git a/manifest-beta.json b/manifest-beta.json new file mode 100644 index 0000000..4cd1270 --- /dev/null +++ b/manifest-beta.json @@ -0,0 +1,10 @@ +{ + "id": "deck-notes", + "name": "Deck Notes", + "version": "0.1.0", + "minAppVersion": "1.9.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", + "isDesktopOnly": false +} diff --git a/manifest.json b/manifest.json index 01dac30..4cd1270 100644 --- a/manifest.json +++ b/manifest.json @@ -1,9 +1,9 @@ { - "id": "simple-flashcards", - "name": "Simple Flashcards", + "id": "deck-notes", + "name": "Deck Notes", "version": "0.1.0", "minAppVersion": "1.9.0", - "description": "A simple flashcard plugin for Obsidian", + "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", "isDesktopOnly": false diff --git a/package-lock.json b/package-lock.json index 4fd5e50..39e4090 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "simple-flashcards", + "name": "deck-notes", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "simple-flashcards", + "name": "deck-notes", "version": "0.1.0", "license": "MIT", "devDependencies": { @@ -654,8 +654,7 @@ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/codemirror": { "version": "5.60.8", @@ -743,8 +742,7 @@ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/esbuild": { "version": "0.25.10", @@ -950,8 +948,7 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tr46": { "version": "0.0.3", @@ -1007,8 +1004,7 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/webidl-conversions": { "version": "3.0.1", diff --git a/package.json b/package.json index 4bac577..019f6cf 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "simple-flashcards", + "name": "deck-notes", "version": "0.1.0", "private": true, - "description": "Simple flashcard plugin for Obsidian (https://obsidian.md)", + "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.", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", diff --git a/src/@types/settings.d.ts b/src/@types/settings.d.ts index df51466..704662b 100644 --- a/src/@types/settings.d.ts +++ b/src/@types/settings.d.ts @@ -7,7 +7,7 @@ export interface Card { tags: string[]; // Normalized deck tags (without #flashcards/ prefix) } -export interface SimpleFlashcardsSettings { +export interface DeckNotesSettings { cardPaths: string[]; // ["Journal/Coping", "Activities"] defaultDeckTag: string; // Default tag for deck selection (e.g., "activities") trackViews: boolean; // Enable view tracking @@ -15,6 +15,6 @@ export interface SimpleFlashcardsSettings { calloutType: string; // Callout type for embedded cards (e.g., "readaloud") } -export interface SimpleFlashcardsData { +export interface DeckNotesData { cardViews: Record; // "file.md#heading" → timestamp } diff --git a/src/@types/window-type.d.ts b/src/@types/window-type.d.ts index fba9d16..007ba19 100644 --- a/src/@types/window-type.d.ts +++ b/src/@types/window-type.d.ts @@ -1,9 +1,9 @@ -import type { SimpleFlashcardsApi } from "../flashcards-Api"; +import type { DeckNotesApi } from "../dn-Api"; declare global { interface Window { - simpleFlashcards?: { - api?: SimpleFlashcardsApi; + deckNotes?: { + api?: DeckNotesApi; }; } } diff --git a/src/flashcards-Api.ts b/src/dn-Api.ts similarity index 89% rename from src/flashcards-Api.ts rename to src/dn-Api.ts index 8cc7cb3..e35162e 100644 --- a/src/flashcards-Api.ts +++ b/src/dn-Api.ts @@ -1,10 +1,10 @@ import type { Card } from "./@types/settings"; -import type SimpleFlashcardsPlugin from "./flashcards-Plugin"; +import type DeckNotesPlugin from "./dn-Plugin"; -export class SimpleFlashcardsApi { - plugin: SimpleFlashcardsPlugin; +export class DeckNotesApi { + plugin: DeckNotesPlugin; - constructor(plugin: SimpleFlashcardsPlugin) { + constructor(plugin: DeckNotesPlugin) { this.plugin = plugin; } diff --git a/src/flashcards-CardParser.ts b/src/dn-CardParser.ts similarity index 100% rename from src/flashcards-CardParser.ts rename to src/dn-CardParser.ts diff --git a/src/flashcards-Constants.ts b/src/dn-Constants.ts similarity index 50% rename from src/flashcards-Constants.ts rename to src/dn-Constants.ts index 942e8b1..6ec6ead 100644 --- a/src/flashcards-Constants.ts +++ b/src/dn-Constants.ts @@ -1,6 +1,6 @@ -import type { SimpleFlashcardsSettings } from "./@types/settings"; +import type { DeckNotesSettings } from "./@types/settings"; -export const DEFAULT_SETTINGS: SimpleFlashcardsSettings = { +export const DEFAULT_SETTINGS: DeckNotesSettings = { cardPaths: [], defaultDeckTag: "", trackViews: true, diff --git a/src/flashcards-Modal.ts b/src/dn-Modal.ts similarity index 96% rename from src/flashcards-Modal.ts rename to src/dn-Modal.ts index 4fd0a3c..bf6b06d 100644 --- a/src/flashcards-Modal.ts +++ b/src/dn-Modal.ts @@ -1,15 +1,15 @@ import { type App, ButtonComponent, MarkdownRenderer, Modal } from "obsidian"; import type { Card } from "./@types/settings"; -import type SimpleFlashcardsPlugin from "./flashcards-Plugin"; +import type DeckNotesPlugin from "./dn-Plugin"; export class FlashcardModal extends Modal { - plugin: SimpleFlashcardsPlugin; + plugin: DeckNotesPlugin; card: Card | null; deckTag: string | undefined; constructor( app: App, - plugin: SimpleFlashcardsPlugin, + plugin: DeckNotesPlugin, card: Card | null, deckTag?: string, ) { diff --git a/src/flashcards-Plugin.ts b/src/dn-Plugin.ts similarity index 83% rename from src/flashcards-Plugin.ts rename to src/dn-Plugin.ts index e65a2fd..7edf1fe 100644 --- a/src/flashcards-Plugin.ts +++ b/src/dn-Plugin.ts @@ -1,40 +1,36 @@ import { type Editor, Notice, Plugin, TFile, TFolder } from "obsidian"; -import type { - Card, - SimpleFlashcardsData, - SimpleFlashcardsSettings, -} from "./@types/settings"; -import { SimpleFlashcardsApi } from "./flashcards-Api"; -import { CardParser } from "./flashcards-CardParser"; -import { DEFAULT_SETTINGS } from "./flashcards-Constants"; -import { FlashcardModal } from "./flashcards-Modal"; -import { SimpleFlashcardsSettingsTab } from "./flashcards-SettingsTab"; +import type { Card, DeckNotesData, DeckNotesSettings } from "./@types/settings"; +import { DeckNotesApi } from "./dn-Api"; +import { CardParser } from "./dn-CardParser"; +import { DEFAULT_SETTINGS } from "./dn-Constants"; +import { FlashcardModal } from "./dn-Modal"; +import { DeckNotesSettingsTab } from "./dn-SettingsTab"; -export default class SimpleFlashcardsPlugin extends Plugin { - settings!: SimpleFlashcardsSettings; - data!: SimpleFlashcardsData; +export default class DeckNotesPlugin extends Plugin { + settings!: DeckNotesSettings; + data!: DeckNotesData; cachedCards: Card[] = []; cardParser!: CardParser; - api!: SimpleFlashcardsApi; + api!: DeckNotesApi; async onload() { - console.log("Loading Simple Flashcards plugin"); + console.log("Loading Deck Notes plugin"); await this.loadSettings(); this.cardParser = new CardParser(this.app); - this.addSettingTab(new SimpleFlashcardsSettingsTab(this.app, this)); + this.addSettingTab(new DeckNotesSettingsTab(this.app, this)); // Defer initial card scan, API, and command registration to avoid blocking startup this.app.workspace.onLayoutReady(async () => { await this.scanCards(); // Initialize API - this.api = new SimpleFlashcardsApi(this); - if (!window.simpleFlashcards) { - window.simpleFlashcards = {}; + this.api = new DeckNotesApi(this); + if (!window.deckNotes) { + window.deckNotes = {}; } - window.simpleFlashcards.api = this.api; + window.deckNotes.api = this.api; // Command: Show Random Card this.addCommand({ @@ -67,11 +63,11 @@ export default class SimpleFlashcardsPlugin extends Plugin { } onunload() { - console.log("Unloading Simple Flashcards plugin"); + console.log("Unloading Deck Notes plugin"); // Clear API reference - if (window.simpleFlashcards) { - window.simpleFlashcards.api = undefined; + if (window.deckNotes) { + window.deckNotes.api = undefined; } } diff --git a/src/flashcards-SettingsTab.ts b/src/dn-SettingsTab.ts similarity index 90% rename from src/flashcards-SettingsTab.ts rename to src/dn-SettingsTab.ts index 6c01fcc..83b881f 100644 --- a/src/flashcards-SettingsTab.ts +++ b/src/dn-SettingsTab.ts @@ -1,12 +1,12 @@ import { type App, PluginSettingTab, Setting } from "obsidian"; -import type { SimpleFlashcardsSettings } from "./@types/settings"; -import type SimpleFlashcardsPlugin from "./flashcards-Plugin"; +import type { DeckNotesSettings } from "./@types/settings"; +import type DeckNotesPlugin from "./dn-Plugin"; -export class SimpleFlashcardsSettingsTab extends PluginSettingTab { - plugin: SimpleFlashcardsPlugin; - newSettings!: SimpleFlashcardsSettings; +export class DeckNotesSettingsTab extends PluginSettingTab { + plugin: DeckNotesPlugin; + newSettings!: DeckNotesSettings; - constructor(app: App, plugin: SimpleFlashcardsPlugin) { + constructor(app: App, plugin: DeckNotesPlugin) { super(app, plugin); this.plugin = plugin; } @@ -16,7 +16,7 @@ export class SimpleFlashcardsSettingsTab extends PluginSettingTab { await this.plugin.saveSettings(); } - private cloneSettings(): SimpleFlashcardsSettings { + private cloneSettings(): DeckNotesSettings { return JSON.parse(JSON.stringify(this.plugin.settings)); } @@ -33,11 +33,11 @@ export class SimpleFlashcardsSettingsTab extends PluginSettingTab { const { containerEl } = this; containerEl.empty(); - new Setting(containerEl).setHeading().setName("Simple Flashcards"); + new Setting(containerEl).setHeading().setName("Deck Notes"); new Setting(containerEl) .setName("Save Settings") - .setClass("flashcards-save-reset") + .setClass("decknotes-save-reset") .addButton((button) => button .setButtonText("Reset") diff --git a/src/main.ts b/src/main.ts index db9fc7b..e79a667 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,3 @@ -import SimpleFlashcardsPlugin from "./flashcards-Plugin"; +import DeckNotesPlugin from "./dn-Plugin"; -export default SimpleFlashcardsPlugin; +export default DeckNotesPlugin;