Initial commit: Pinned Daily Notes plugin for Obsidian

- Adds ribbon icon and command to open today's daily note in a pinned tab
- Automatically updates existing pinned daily note tab to today's note
- Respects user's Daily Notes plugin settings and folder structure (Journal/Daily/YYYY/MM/YYYY-MM-DD)
- Clean, focused implementation with no automatic tab positioning
This commit is contained in:
Jeremy Neiman 2025-03-10 19:56:17 -04:00
commit bc16100975
7 changed files with 254 additions and 0 deletions

20
.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
# Dependencies
node_modules/
package-lock.json
# Build output
main.js
*.js.map
data.json
# IDE
.idea/
.vscode/
*.iml
# OS
.DS_Store
Thumbs.db
# Obsidian
.obsidian/

44
README.md Normal file
View file

@ -0,0 +1,44 @@
# Pinned Daily Notes for Obsidian
A plugin for Obsidian that helps you manage your daily notes with pinned tabs. It provides a dedicated button and command to open today's daily note in a pinned tab.
## Features
- Adds a ribbon icon (calendar+) to open today's daily note in a pinned tab
- Adds a command palette action "Open today's daily note"
- When clicking the button or running the command:
- If a pinned daily note tab exists: Updates it to today's note and focuses it
- If no pinned daily note exists: Creates a new pinned tab with today's note
- Works with any daily note folder structure and format
- Respects your Daily Notes plugin settings
## How to Use
1. Install the plugin
2. Click the calendar+ icon in the ribbon (left sidebar) or use the command palette to run "Open today's daily note"
3. The first time you use it, a new pinned tab will be created
4. Move this tab wherever you want it to stay
5. From now on, that tab will be updated to show today's note whenever you use the plugin
## Configuration
No configuration needed! The plugin uses your existing Daily Notes settings for:
- Note folder location
- Date format
- Other Daily Notes preferences
## Installation
1. Open Obsidian Settings
2. Go to Community Plugins and disable Safe Mode
3. Click Browse and search for "Pinned Daily Notes"
4. Install the plugin
5. Enable the plugin in your list of installed plugins
## Support
If you encounter any issues or have suggestions, please file them on [GitHub](https://github.com/docmarionum1/obsidian-pinned-daily-notes/issues).
## License
MIT

47
esbuild.config.mjs Normal file
View file

@ -0,0 +1,47 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
*/
`;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

87
main.ts Normal file
View file

@ -0,0 +1,87 @@
import { App, Plugin, TFile, moment } from 'obsidian';
export default class PinDailyNotePlugin extends Plugin {
pinnedLeafId: string | null = null;
async onload() {
// Pin today's note on startup
this.app.workspace.onLayoutReady(() => this.pinTodayNote());
// Register event for when daily notes plugin creates a new note
this.registerEvent(
this.app.workspace.on('file-open', (file) => {
if (file && this.isDailyNote(file)) {
this.pinDailyNote(file);
}
})
);
// Add ribbon icon to manually pin today's note
this.addRibbonIcon('pin', 'Pin Today\'s Note', () => {
this.pinTodayNote();
});
}
getDailyNoteSettings() {
// @ts-ignore
const dailyNotesPlugin = this.app.internalPlugins.plugins['daily-notes'];
// Explicitly check boolean status
const isEnabled = dailyNotesPlugin?.enabled === true;
if (!isEnabled) {
return null;
}
return dailyNotesPlugin.instance.options;
}
isDailyNote(file: TFile): boolean {
const dailyNotePath = this.getTodayNotePath();
return dailyNotePath ? file.path === dailyNotePath : false;
}
getTodayNotePath(): string | null {
const settings = this.getDailyNoteSettings();
if (!settings) {
console.log('Daily Notes plugin is not enabled');
return null;
}
const folder = settings.folder?.trim() || '';
const format = settings.format?.trim() || 'YYYY-MM-DD';
const filename = moment().format(format);
return folder
? `${folder}/${filename}.md`
: `${filename}.md`;
}
async pinTodayNote() {
const dailyNotePath = this.getTodayNotePath();
if (!dailyNotePath) {
return;
}
const file = this.app.vault.getAbstractFileByPath(dailyNotePath);
if (file instanceof TFile) {
await this.pinDailyNote(file);
}
}
async pinDailyNote(file: TFile) {
let targetLeaf = this.pinnedLeafId
? this.app.workspace.getLeafById(this.pinnedLeafId)
: this.app.workspace.getLeaf(true);
if (!targetLeaf) {
targetLeaf = this.app.workspace.getLeaf(true);
}
// Set the leaf to be pinned
await targetLeaf.setPinned(true);
// Store the leaf id
this.pinnedLeafId = targetLeaf.id;
// Open the file in the pinned leaf
await targetLeaf.openFile(file);
}
}

9
manifest.json Normal file
View file

@ -0,0 +1,9 @@
{
"id": "obsidian-pinned-daily-notes",
"name": "Pinned Daily Notes",
"version": "1.0.0",
"minAppVersion": "0.15.0",
"description": "Dynamically update a pinned tab with today's daily note",
"author": "Jeremy Neiman",
"isDesktopOnly": false
}

24
package.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "obsidian-pin-daily-note",
"version": "1.0.0",
"description": "Automatically pins today's daily note to the first tab",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": ["obsidian", "plugin", "daily-notes"],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "^5.2.0",
"@typescript-eslint/parser": "^5.2.0",
"builtin-modules": "^3.2.0",
"esbuild": "0.13.12",
"obsidian": "^1.1.1",
"tslib": "2.3.1",
"typescript": "4.4.4"
}
}

23
tsconfig.json Normal file
View file

@ -0,0 +1,23 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": false,
"moduleResolution": "node",
"importHelpers": true,
"skipLibCheck": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
]
},
"include": [
"**/*.ts"
]
}