diff --git a/README.md b/README.md index 0feeed4..4610c6b 100644 --- a/README.md +++ b/README.md @@ -1,202 +1,59 @@ -# obsidian-calendar-plugin +# Day One Calendar for Obsidian -This plugin for [Obsidian](https://obsidian.md/) creates a simple Calendar view for visualizing and navigating between your daily notes. +Calendar with scrolling functionality and other rich features, stylized after the [Day One journaling app](https://dayoneapp.com/). -![screenshot-full](https://raw.githubusercontent.com/liamcain/obsidian-calendar-plugin/master/images/screenshot-full.png) - -## Usage - -After enabling the plugin in the settings menu, you should see the calendar view appear in the right sidebar. - -The plugin reads your Daily Note settings to know your date format, your daily note template location, and the location for new daily notes it creates. - -## Features - -- Go to any **daily note**. -- Create new daily notes for days that don't have one. (This is helpful for when you need to backfill old notes or if you're planning ahead for future notes! This will use your current **daily note** template!) -- Visualize your writing. Each day includes a meter to approximate how much you've written that day. -- Use **Weekly notes** for an added organization layer! They work just like daily notes, but have their own customization options. - -## Settings - -- **Start week on [default: locale]**: Configure the Calendar view to show Sunday or Monday as the first day of the week. Choosing 'locale' will set the start day to be whatever is the default for your chosen locale (`Settings > About > Language`) -- **Words per Dot [default: 250]**: Starting in version 1.3, dots reflect the word count of your files. By default, each dot represents 250 words, you can change that value to whatever you want. Set this to `0` to disable the word count entirely. **Note:** There is a max of 5 dots so that the view doesn't get too big! -- **Confirm before creating new note [default: on]**: If you don't like that a modal prompts you before creating a new daily note, you can turn it off. -- **Show Week Number [default: off]**: Enable this to add a new column to the calendar view showing the [Week Number](https://en.wikipedia.org/wiki/Week#Week_numbering). Clicking on these cells will open your **weekly note**. - -## Customization - -The following CSS Variables can be overridden in your `obsidian.css` file. - -```css -/* obsidian-calendar-plugin */ -/* https://github.com/liamcain/obsidian-calendar-plugin */ - -#calendar-container { - --color-background-heading: transparent; - --color-background-day: transparent; - --color-background-weeknum: transparent; - --color-background-weekend: transparent; - - --color-dot: var(--text-muted); - --color-arrow: var(--text-muted); - --color-button: var(--text-muted); - - --color-text-title: var(--text-normal); - --color-text-heading: var(--text-muted); - --color-text-day: var(--text-normal); - --color-text-today: var(--interactive-accent); - --color-text-weeknum: var(--text-muted); -} -``` - -In addition to the CSS Variables, there are some classes you can override for further customization. For example, if you don't like how bright the title is, you can override it with: - -```css -#calendar-container .year { - color: var(--text-normal); -} -``` - -> **Note:** It's especially important when overriding the classes to prefix them with `#calendar-container` to avoid any unexpected changes within Obsidian! - -### Caution to Theme Creators - -If you use "Inspect Element" on the calendar, you will notice that the CSS classes are quite illegible. For example: `.task.svelte-1lgyrog.svelte-1lgyrog`. What's going on here? The classes that begin with `svelte-` are autogenerated and are used to avoid the calendar styles affecting any other elements in the app. That being said: **ignore them!** Those CSS classes are likely to change from release to release, and your overrides _will_ break. Just target the human-readable part of the class names. So to override `task.svelte-1lgyrog.svelte-1lgyrog`, you should use `#calendar-container .task { ... }` - -## Compatibility - -`obsidian-calendar-plugin` currently requires Obsidian v0.9.11 or above to work properly. +Built off the classic [Calendar](https://github.com/liamcain/obsidian-calendar-plugin) plugin. ## Installation -You can install the plugin via the Community Plugins tab within Obsidian. Just search for "Calendar." - -## FAQ - -### What do the dots mean? - -Each solid dot represents 250 words in your daily note. So 4 dots means you've written a thousands words for that day! If you want to change that threshold, you can set a different value for "Words Per Dot" in the Calendar settings. - -The hollow dots, on the other hand, mean that the day has incomplete tasks in it. (**Note:** There will only ever be 1 hollow dot on a particular day, regardless of the number of remaining tasks) - -### How do I change the styling of the Calendar? - -By default, the calendar should seamlessly match your theme, but if you'd like to further customize it, you can! In your `obsidian.css` file (inside your vault) you can configure the styling to your heart's content. +This plugin is not yet an obsidian community plugin. Install manually: -### Can I add week numbers to the calendar? +### Bun -In the settings, you can enable "Show Week Numbers" to add a "week number" column to the calendar. Click on the week number to open a "weekly note". +Install [Bun](https://bun.sh/docs/installation). -### How do I hide the calendar plugin without disabling the plugin? +```bash -Just like other sidebar views (e.g. Backlinks, Outline), the calendar view can be closed by right-clicking on the view icon. +bun create / -![how-to-close](./images/how-to-close.png) +bun run build -### I accidentally closed the calendar. How do I reopen it? - -If you close the calendar widget (right-clicking on the panel nav and clicking close), you can always reopen the view from the Command Palette. Just search for `Calendar: Open view`. - -![how-to-reopen](./images/how-to-reopen.png) - -### How do I have the calendar start on Monday? - -From the Settings menu, you can toggle "Start week on Monday". - -### How do I include "unformatted" words in my weekly note filenames? - -If you want the weekly note format to include a word (e.g. "Week 21 of Year 2020") you can do so by surrounding the words with `[]` brackets. This tells [moment](https://momentjs.com/docs/#/displaying/format/) to ignore the words. So for the example above, you would set your format to `[Week] ww [of Year] gggg`. - -### I don't like showing the week numbers but I still want to use weekly notes. Can I still use them? - -You can open the current weekly note from the command palette by searching `Calendar: Open weekly Note`. This will open the weekly note for the current week. - -To configure the `format`, `folder`, and `template`, you will temporarily need to toggle on "Show weekly numbers" in the settings, but if you toggle it back off, your settings will persist. - -## Protips - -### Embed your entire week in a weekly note - -If you add the following snippet to your weekly note template, you can a seamless view of your week in a single click. - -```md -## Week at a Glance - -![[{{sunday:gggg-MM-DD}}]] -![[{{monday:gggg-MM-DD}}]] -![[{{tuesday:gggg-MM-DD}}]] -![[{{wednesday:gggg-MM-DD}}]] -![[{{thursday:gggg-MM-DD}}]] -![[{{friday:gggg-MM-DD}}]] -![[{{saturday:gggg-MM-DD}}]] ``` -### Hover Preview - -Just like the Obsidian's graph and internal links, the calendar supports page previews for your daily notes. Just hover over a cell while holding down `Ctrl/Cmd` on your keyboard! - -### The calendar can be moved (and pinned!) anywhere - -Just because the calendar appears in the right sidebar doesn't mean it has to stay there. Feel free to drag it to the left sidebar, or (if you have the screen real estate for it) into the main content area. If you move it out of the sidebar, the view can even be pinned; great for more advanced tile layouts! -![how-to-pin](./images/how-to-pin.png) - -### Open daily notes in a new split - -If you `Ctrl/Command`-Click on a note in your calendar, it will open daily note in a new split. Useful if you want to open a bunch of daily notes in a row (especially if you have the **Sliding Panes** plugin enabled!) - -### Reveal open note on calendar - -If you open a note from a different month, you might want to see it on the calendar view. To do so, you can run the command `Calendar: Reveal open note` from the command palette. - -### Add custom styling for weekends - -If you want to style weekends to be distinguishable from weekdays, you can set the `var(--color-background-weekend)` to be any color you want. - -![how-to-weekend](./images/how-to-weekend.png) - -### Weekly Notes (deprecated) - -#### Weekly notes have a new home +## Usage -The weekly note functionality has been split out into its [very own plugin](https://github.com/liamcain/obsidian-periodic-notes/). In the future, the functionality will be removed from the Calendar plugin; so if you're currently using weekly notes, I encourage you to make the switch. Don't worry, the behavior is functionally identical and will still integrate with the calendar view! +After installing and enabling this plugin, the calendar view will be on the right side panel. -This split was inspired by the [One Thing Well](https://en.wikipedia.org/wiki/Unix_philosophy) philosophy. Plugins should be as modular. Some users might want weekly notes and have no use for a calendar view. And vice versa. +If you close it, you can open the calendar at any time via the Command Palette. -If you are currently using weekly notes within the Calendar plugin, the new Periodic Notes plugin will migrate your settings for you automatically. +This plugin is _not_ built to be compatible with any other calendar or journaling plugins. +I recommend you disable or uninstall any before testing this plugin. -### Usage -You can open **weekly notes** in 2 ways: searching `Calendar: open weekly note` in the command palette or by clicking on the week number. Weekly notes can be configured from the Calendar settings. There are 3 settings: +### Daily Note Name and Location -- **Folder:** The folder that your weekly notes go into. It can be the same or different from your daily notes. By default they are placed in your vault root. -- **Template:** Configure a template for weekly notes. Weekly notes have slightly different template tags than daily notes. See here for the list of supported [weekly note template tags](#template-tags). +The name for Daily Notes is specified in the settings for your Daily Note. +I recommend changing the default values and taking advantage of folders. +For reference, my date format looks like this: `YYYY/MMMM/MMM-D-YYYY`. -> Note: The path here won't autocomplete for you, you'll need to enter the full path. -- **Format:** The date format for the weekly note filename. Defaults to `"gggg-[W]ww`. If you use `DD` in the week format, this will refer to first day of the week (Sunday or Monday, depending on your settings). +## Customization -#### Template Tags +You can overwrite anything in `styles.css` in your `obsidian.css` file. -| Tag | Description | -| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `sunday`, `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday` | Because weekly tags refer to main days, you can refer to individual days like this `{{sunday:gggg-MM-DD}}` to automatically insert the date for that particular day. Note, you must specify the date format! | -| `title` | Works the same as the daily note `{{title}}`. It will insert the title of the note | -| `date`, `time` | Works the same as the daily note `{{date}}` and `{{time}}`. It will insert the date and time of the first day of the week. Useful for creating a heading (e.g. `# # {{date:gggg [Week] ww}}`). | +```css +/* day-one-calendar-plugin */ -## See it in action +#calendar-container { + --color-background-day: var(--interactive-normal); +} +``` -- [Nick Milo provides a nice plugin walkthrough](https://www.youtube.com/watch?v=X61wRmfZU8Y&t=1099s) -- [Santi Younger demos how Calendar + Periodic Notes can be used for weekly review](https://www.youtube.com/watch?v=T9y8JABS9_Q) -- [Filipe Donadio uses the calendar to plan his day](https://www.youtube.com/watch?v=hxf3_dXIcqc) -## Say Thanks 🙏 +> **Note:** It's especially important when overriding the classes to prefix them with `#calendar-container` to avoid any unexpected changes within Obsidian! -If you like this plugin and would like to buy me a coffee, you can! -[BuyMeACoffee](https://www.buymeacoffee.com/liamcain) -Like my work and want to see more like it? You can sponsor me. +## To Do -[![GitHub Sponsors](https://img.shields.io/github/sponsors/liamcain?style=social)](https://github.com/sponsors/liamcain) diff --git a/manifest.json b/manifest.json index 0c15497..9776f14 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { - "id": "calendar", - "name": "Better Calendar", - "description": "Calendar view of your daily notes", - "version": "2.0", + "id": "day-one-calendar", + "name": "Day One Calendar", + "description": "Calendar with scrolling functionality and other rich features, stylized after the Day One journaling app.", + "version": "1.0", "author": "Varun Neal Srivastava", "authorUrl": "https://github.com/varunneal/", "isDesktopOnly": true, diff --git a/package.json b/package.json index 4d4441e..7da379a 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "better-calendar", - "version": "1.5.10", - "description": "Calendar view of your daily notes", - "author": "varun", + "name": "day-one-calendar", + "version": "1.0.0", + "description": "Calendar with scrolling functionality and other rich features, stylized after the Day One journaling app.", + "author": "varun neal srivastava", "main": "main.js", "license": "MIT", "scripts": { diff --git a/src/.DS_Store b/src/.DS_Store index 173db9f..a0b2331 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/components/ContextPanel.svelte b/src/components/ContextPanel.svelte new file mode 100644 index 0000000..2cc2e16 --- /dev/null +++ b/src/components/ContextPanel.svelte @@ -0,0 +1,140 @@ + + + +{#if $selectedDate} +
+
+ {#if currentNote} +
+
{currentNote.basename}
+ {#if content} +
{content}
+ {/if} +
+ {:else} +
No entry on this day. Click to create.
+ {/if} +
+ +
+{/if} + + diff --git a/src/components/Header.svelte b/src/components/Header.svelte index f443790..2f51856 100644 --- a/src/components/Header.svelte +++ b/src/components/Header.svelte @@ -30,26 +30,24 @@ font-size: 1.8em; font-weight: 600; } - .today-button { - padding: 0.25rem 0.6rem; /* Reduced padding */ + padding: 0.25rem 0.6rem; border-radius: 15%; font-size: 1.0em; font-weight: 200; - /*border: 1px solid orange;*/ - /*color: var(--text-on-accent);*/ - border: 2px solid transparent; + border: none; cursor: pointer; - /*background-color: var(--background-secondary);*/ + border: 1px solid transparent; + transition: all 0.05s ease; + background: var(--background-secondary); } .today-button.active { - /*background-color: var(--interactive-accent);*/ background: var(--interactive-normal); - border: 2px solid white; - opacity: 0.8; /* Maintains some of that glassy feel */ - font-weight: 300; /* Slightly bolder than normal state */ + border: 1px solid var(--color-base-70); } .today-button:hover { background-color: var(--interactive-hover); + + transition: all 0.2s ease; } diff --git a/src/components/OnThisDay.svelte b/src/components/OnThisDay.svelte index e40471a..e49ffa7 100644 --- a/src/components/OnThisDay.svelte +++ b/src/components/OnThisDay.svelte @@ -17,43 +17,47 @@ name: string; } - // We'll now get historical notes based on selectedDate - $: historicalNotes = getHistoricalNotes($selectedDate); - - function getHistoricalNotes(date: Moment | null): HistoricalNote[] { - if (!date) return []; - - const notes: HistoricalNote[] = []; - const currentYear = date.year(); - const month = date.month(); - const day = date.date(); - - // todo: make less derpy - for (let year = currentYear - 60; year <= currentYear + 60; year++) { - const historicalDate = window.moment().year(year).month(month).date(day); - const file = getDailyNote(historicalDate, $dailyNotes); - if (file) { - notes.push({ - date: historicalDate, - name: file.basename, - exists: true - }); - } - } - return notes; + // derived store to force reactivity + $: noteKey = $selectedDate ? + `${$selectedDate.format('YYYY-MM-DD')}-${Object.keys($dailyNotes).length}` : + null; + + $: historicalNotes = noteKey ? getHistoricalNotes($selectedDate) : []; + + function getHistoricalNotes(selectedDate: Moment | null): HistoricalNote[] { + if (!selectedDate) return []; + + const month = selectedDate.month(); + const day = selectedDate.date(); + + // Get all dates from dailyNotes that match the month and day + return Object.entries($dailyNotes) + .map(([dateStr, file]): HistoricalNote | null => { + const date = moment(dateStr, "YYYY-MM-DD"); + + // Check if this note's month and day match our selected date + if (date.month() === month && date.date() === day) { + return { + date: date, + name: file.basename, + exists: true + }; + } + return null; + }) + .filter((note): note is HistoricalNote => note !== null) + .sort((a, b) => a.date.valueOf() - b.date.valueOf()); } - - // Debug logging for store changes - $: console.log('selectedDate in historical notes:', $selectedDate ? $selectedDate.format('YYYY-MM-DD') : 'none'); + {#if historicalNotes && historicalNotes.length > 0}

On this day ({historicalNotes.length} {historicalNotes.length === 1 ? 'entry' : 'entries'})

    - {#each historicalNotes as note} + {#each historicalNotes as note (note.date.valueOf())}
  • - // import VirtualScroll from "../VirtualList" import VirtualScroll from './VirtualScroll.svelte' import { fade } from 'svelte/transition'; - import type { Moment } from "moment"; import moment from 'moment'; - import MonthCalendar from "./MonthCalendar.svelte"; import { ICalendarSource } from "../types"; import { configureGlobalMomentLocale } from "../localization"; import { onDestroy, onMount } from "svelte"; import type { ISettings } from "src/settings"; - import { activeFile, dailyNotes, settings, weeklyNotes } from "../ui/stores"; + import { dailyNotes, settings } from "../ui/stores"; - // Props export let sources: ICalendarSource[]; export let onHoverDay: (date: Moment, targetEl: EventTarget) => boolean; export let onClickDay: (date: Moment, isMetaPressed: boolean) => boolean; export let onFileMenuDay: (date: Moment, event: MouseEvent) => boolean; - // State let today: Moment; let displayedMonths: Moment[] = []; let currentMonthIndex: number; - let virtualScroll: VirtualScroll; // Reference to hold the component instance + let virtualScroll: VirtualScroll; let isLoading = false; let visibleIndex = 0; export let visibleMonth; + // Track position of the loader + let loadingPosition: "top" | "bottom" = "top"; $: today = getToday($settings); @@ -40,55 +37,65 @@ virtualScroll.scrollToIndex(currentMonthIndex); } - function getToday(settings: ISettings) { configureGlobalMomentLocale(settings.localeOverride, settings.weekStart); dailyNotes.reindex(); - weeklyNotes.reindex(); return window.moment(); } - function generateInitialMonths(): { months: Moment[], i: number } { const months: Moment[] = []; const currentYear = today.year(); const currentMonth: number = today.month(); - const numYears = 2; + const numYears = 1; - let month; - for (month = 0; month < numYears * 12; month++) { + for (let month = 0; month < numYears * 12; month++) { const year = currentYear - numYears + 1 + Math.floor(month / 12); months.push(moment().year(year).month(month % 12).startOf('month')); } const i = (numYears - 1) * 12 + currentMonth; - return { months, i}; + return { months, i }; } - - async function handleScrollTop() { + loadingPosition = 'top'; isLoading = true; - console.log("handle scroll top called"); - - // Wait for 1 second - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise(resolve => setTimeout(resolve, 250)); const yearsToLoad = 1; let currFirstYear = displayedMonths[0].year(); const months: Moment[] = []; - let month; - for (month = 0; month < 12 * yearsToLoad; month++) { + for (let month = 0; month < 12 * yearsToLoad; month++) { const year = currFirstYear - yearsToLoad + Math.floor(month / 12); months.push(moment().year(year).month(month % 12).startOf('month')); } + displayedMonths = [...months, ...displayedMonths]; currentMonthIndex += yearsToLoad * 12; + await tick(); + virtualScroll.scrollToIndex(12, 'auto'); isLoading = false; } + async function handleScrollBottom() { + loadingPosition = 'bottom'; + isLoading = true; + await new Promise(resolve => setTimeout(resolve, 250)); + + const yearsToLoad = 1; + const lastMonth = displayedMonths[displayedMonths.length - 1]; + const months: Moment[] = []; + + for (let i = 1; i <= 12 * yearsToLoad; i++) { + months.push(moment(lastMonth).add(i, 'months').startOf('month')); + } + + displayedMonths = [...displayedMonths, ...months]; + isLoading = false; + } $: monthsData = displayedMonths.map((month) => ({ key: month.format('YYYY-MM'), @@ -99,23 +106,15 @@ ? displayedMonths[visibleIndex] : window.moment(); - - onMount(() => { - let {months, i} = generateInitialMonths(); + let { months, i } = generateInitialMonths(); displayedMonths = months; currentMonthIndex = i; virtualScroll.scrollToIndex(currentMonthIndex); }); - // 1 minute heartbeat to keep today reflecting the current day let heartbeat = setInterval(() => { today = window.moment(); - - // todo: change this to monthkt logic for updating display months index lol idk - // if (!today.isSame(displayedMonths[currentMonthIndex], 'month')) { - // displayedMonths = [today.clone(), ...displayedMonths]; - // } }, 1000 * 60); onDestroy(() => { @@ -125,17 +124,12 @@ export interface ScrollingCalendar { scrollToToday(): number; } - -
    - {#if isLoading} -
    + {#if isLoading && loadingPosition === 'top'} +
    {/if} @@ -148,6 +142,7 @@ keeps={18} start={currentMonthIndex} on:scrolltop={handleScrollTop} + on:scrollbottom={handleScrollBottom} bind:visibleIndex={visibleIndex} > @@ -163,4 +158,10 @@ /> + + {#if isLoading && loadingPosition === 'bottom'} +
    +
    +
    + {/if}
    diff --git a/src/components/VirtualScroll.svelte b/src/components/VirtualScroll.svelte index c79a3e2..3c5cadc 100644 --- a/src/components/VirtualScroll.svelte +++ b/src/components/VirtualScroll.svelte @@ -31,13 +31,18 @@ if (!container) return; const scrollTop = container.scrollTop; + const scrollHeight = container.scrollHeight; + const clientHeight = container.clientHeight; - // Dispatch scrolltop event when scrolled to top if (scrollTop === 0) { dispatch('scrolltop'); } - // Calculate visible range + if (scrollTop + clientHeight >= scrollHeight) { + dispatch('scrollbottom'); + } + + let startIndex = Math.floor(scrollTop / height) - buffer; startIndex = Math.max(0, startIndex); @@ -58,20 +63,21 @@ /** * Public method to scroll to a specific index * @param {number} index - Index to scroll to + * @param {string} behavior - Scroll behavior */ - export function scrollToIndex(index) { + export function scrollToIndex(index, behavior='smooth') { if (!container) return; container.scrollTo({ top: index * height, - behavior: 'smooth' + behavior: behavior }); } export interface VirtualScrollMethods { visibleIndex: number; - scrollToIndex(index: number): void; + scrollToIndex(index: number, behavior?: string): void; } onMount(() => { diff --git a/src/main.ts b/src/main.ts index 51a4ec1..3b34524 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,8 +42,8 @@ export default class CalendarPlugin extends Plugin { ); this.addCommand({ - id: "show-calendar-view", - name: "Open view", + id: "open-calendar-view", + name: "Open calendar", checkCallback: (checking: boolean) => { if (checking) { return ( @@ -54,16 +54,16 @@ export default class CalendarPlugin extends Plugin { }, }); - this.addCommand({ - id: "open-weekly-note", - name: "Open Weekly Note", - checkCallback: (checking) => { - if (checking) { - return !appHasPeriodicNotesPluginLoaded(); - } - this.view.openOrCreateWeeklyNote(window.moment(), false); - }, - }); + // this.addCommand({ + // id: "open-weekly-note", + // name: "Open Weekly Note", + // checkCallback: (checking) => { + // if (checking) { + // return !appHasPeriodicNotesPluginLoaded(); + // } + // this.view.openOrCreateWeeklyNote(window.moment(), false); + // }, + // }); this.addCommand({ id: "reveal-active-note", diff --git a/src/settings.ts b/src/settings.ts index ffdc91f..4975bbc 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -10,6 +10,7 @@ export interface ISettings { wordsPerDot: number; weekStart: IWeekStartOption; shouldConfirmBeforeCreate: boolean; + openInNewTab: boolean; // Weekly Note settings showWeeklyNote: boolean; @@ -31,7 +32,8 @@ const weekdays = [ ]; export const defaultSettings = Object.freeze({ - shouldConfirmBeforeCreate: true, + shouldConfirmBeforeCreate: false, + openInNewTab: true, weekStart: "locale" as IWeekStartOption, wordsPerDot: DEFAULT_WORDS_PER_DOT, @@ -79,25 +81,26 @@ export class CalendarSettingsTab extends PluginSettingTab { }); // this.addDotThresholdSetting(); // this.addWeekStartSetting(); + this.addOpenInNewTabSetting(); this.addConfirmCreateSetting(); // this.addShowWeeklyNoteSetting(); - if ( - this.plugin.options.showWeeklyNote && - !appHasPeriodicNotesPluginLoaded() - ) { - this.containerEl.createEl("h3", { - text: "Weekly Note Settings", - }); - this.containerEl.createEl("p", { - cls: "setting-item-description", - text: - "Note: Weekly Note settings are moving. You are encouraged to install the 'Periodic Notes' plugin to keep the functionality in the future.", - }); - this.addWeeklyNoteFormatSetting(); - this.addWeeklyNoteTemplateSetting(); - this.addWeeklyNoteFolderSetting(); - } + // if ( + // this.plugin.options.showWeeklyNote && + // !appHasPeriodicNotesPluginLoaded() + // ) { + // this.containerEl.createEl("h3", { + // text: "Weekly Note Settings", + // }); + // this.containerEl.createEl("p", { + // cls: "setting-item-description", + // text: + // "Note: Weekly Note settings are moving. You are encouraged to install the 'Periodic Notes' plugin to keep the functionality in the future.", + // }); + // this.addWeeklyNoteFormatSetting(); + // this.addWeeklyNoteTemplateSetting(); + // this.addWeeklyNoteFolderSetting(); + // } this.containerEl.createEl("h3", { text: "Advanced Settings", @@ -147,6 +150,21 @@ export class CalendarSettingsTab extends PluginSettingTab { }); } + + addOpenInNewTabSetting(): void { + new Setting(this.containerEl) + .setName("Open note in new tab.") + .addToggle((toggle) => { + toggle.setValue(this.plugin.options.openInNewTab); + toggle.onChange(async (value) => { + this.plugin.writeOptions(() => ({ + openInNewTab: value, + })); + }); + }); + } + + addConfirmCreateSetting(): void { new Setting(this.containerEl) .setName("Confirm before creating new note") diff --git a/src/ui/Main.svelte b/src/ui/Main.svelte index f87b6a8..803467b 100644 --- a/src/ui/Main.svelte +++ b/src/ui/Main.svelte @@ -5,8 +5,8 @@ import Header from "../components/Header.svelte"; import OnThisDay from "../components/OnThisDay.svelte"; import { ICalendarSource } from "../types"; - import { activeFile, selectedDate } from "./stores"; import ContextPanel from "../components/ContextPanel.svelte"; + import type { TFile } from "obsidian"; // Props to be passed from View @@ -18,19 +18,15 @@ }; export let openOrCreateNote: (date: Moment, isMetaPressed: boolean) => boolean; + export let loadNoteContent: (note: TFile | null) => Promise; - - $: console.log('selectedDate changed:', $selectedDate); export let onInit: (calendar: ScrollingCalendar) => void; let calendarComponent: ScrollingCalendar; - // todo: change to moment, will allow for active control export let visibleMonth: Moment = window.moment(); - $: console.log("visible month is", visibleMonth.month(), visibleMonth.year()); - let scrollToToday: () => void; @@ -63,8 +59,9 @@ {...calendarActions} />
    - - - +
diff --git a/src/ui/sources/streak.ts b/src/ui/sources/streak.ts index 4e36f7e..5317409 100644 --- a/src/ui/sources/streak.ts +++ b/src/ui/sources/streak.ts @@ -1,10 +1,10 @@ import type { Moment } from "moment"; import type { TFile } from "obsidian"; import type { ICalendarSource, IDayMetadata } from "../../types"; -import { getDailyNote, getWeeklyNote } from "obsidian-daily-notes-interface"; +import { getDailyNote } from "obsidian-daily-notes-interface"; import { get } from "svelte/store"; -import { dailyNotes, weeklyNotes } from "../stores"; +import { dailyNotes } from "../stores"; import { classList } from "../utils"; const getStreakClasses = (file: TFile): string[] => { @@ -22,11 +22,4 @@ export const streakSource: ICalendarSource = { }; }, - getWeeklyMetadata: async (date: Moment): Promise => { - const file = getWeeklyNote(date, get(weeklyNotes)); - return { - classes: getStreakClasses(file), - dots: [], - }; - }, }; diff --git a/src/ui/sources/tags.ts b/src/ui/sources/tags.ts index 7bc5c5e..b95b9c6 100644 --- a/src/ui/sources/tags.ts +++ b/src/ui/sources/tags.ts @@ -6,7 +6,7 @@ import { get } from "svelte/store"; import { partition } from "src/ui/utils"; -import { dailyNotes, weeklyNotes } from "../stores"; +import { dailyNotes } from "../stores"; function getNoteTags(note: TFile | null): string[] { if (!note) { @@ -55,11 +55,5 @@ export const customTagsSource: ICalendarSource = { dots: [], }; }, - getWeeklyMetadata: async (date: Moment): Promise => { - const file = getWeeklyNote(date, get(weeklyNotes)); - return { - dataAttributes: getFormattedTagAttributes(file), - dots: [], - }; - }, + }; diff --git a/src/ui/sources/tasks.ts b/src/ui/sources/tasks.ts index 7e70bf6..9089cef 100644 --- a/src/ui/sources/tasks.ts +++ b/src/ui/sources/tasks.ts @@ -4,7 +4,7 @@ import type { ICalendarSource, IDayMetadata, IDot } from "../../types"; import { getDailyNote, getWeeklyNote } from "obsidian-daily-notes-interface"; import { get } from "svelte/store"; -import { dailyNotes, weeklyNotes } from "../stores"; +import { dailyNotes } from "../stores"; export async function getNumberOfRemainingTasks(note: TFile): Promise { if (!note) { @@ -44,12 +44,4 @@ export const tasksSource: ICalendarSource = { }; }, - getWeeklyMetadata: async (date: Moment): Promise => { - const file = getWeeklyNote(date, get(weeklyNotes)); - const dots = await getDotsForDailyNote(file); - - return { - dots, - }; - }, }; diff --git a/src/ui/sources/wordCount.ts b/src/ui/sources/wordCount.ts index e3fcc78..8b56423 100644 --- a/src/ui/sources/wordCount.ts +++ b/src/ui/sources/wordCount.ts @@ -6,7 +6,7 @@ import { get } from "svelte/store"; import { DEFAULT_WORDS_PER_DOT } from "src/constants"; -import { dailyNotes, settings, weeklyNotes } from "../stores"; +import { dailyNotes, settings } from "../stores"; import { clamp, getWordCount } from "../utils"; const NUM_MAX_DOTS = 5; @@ -50,12 +50,4 @@ export const wordCountSource: ICalendarSource = { }; }, - getWeeklyMetadata: async (date: Moment): Promise => { - const file = getWeeklyNote(date, get(weeklyNotes)); - const dots = await getDotsForDailyNote(file); - - return { - dots, - }; - }, }; diff --git a/src/ui/stores.ts b/src/ui/stores.ts index 9721587..520d971 100644 --- a/src/ui/stores.ts +++ b/src/ui/stores.ts @@ -3,7 +3,7 @@ import { getAllDailyNotes, getAllWeeklyNotes, } from "obsidian-daily-notes-interface"; -import { writable } from "svelte/store"; +import { get, writable } from "svelte/store"; import { defaultSettings, ISettings } from "src/settings"; @@ -54,18 +54,24 @@ function createWeeklyNotesStore() { }; } -function createSelectedFileStore() { - const store = writable(null); +function createActiveFileStore() { + const store = writable<{ uid: string | null; content: string }>({ + uid: null, + content: "", + }); return { - setFile: (file: TFile) => { + setFile: (file: TFile, content = "") => { const id = getDateUIDFromFile(file); - store.set(id); + store.set({uid: id, content}); }, + setContent: (content: string) => + store.update((state) => ({ ...state, content })), ...store, }; } + function createSelectedDateStore() { const store = writable(null); return { @@ -78,8 +84,10 @@ function createSelectedDateStore() { }; } + + + export const selectedDate = createSelectedDateStore(); export const settings = writable(defaultSettings); export const dailyNotes = createDailyNotesStore(); -export const weeklyNotes = createWeeklyNotesStore(); -export const activeFile = createSelectedFileStore(); +export const activeFile = createActiveFileStore(); diff --git a/src/view.ts b/src/view.ts index b47904d..c28594e 100644 --- a/src/view.ts +++ b/src/view.ts @@ -3,7 +3,6 @@ import { getDailyNote, getDailyNoteSettings, getDateFromFile, - getWeeklyNote, getWeeklyNoteSettings, } from "obsidian-daily-notes-interface"; import { FileView, TFile, ItemView, WorkspaceLeaf, MarkdownView } from "obsidian"; @@ -11,20 +10,20 @@ import { get } from "svelte/store"; import { TRIGGER_ON_OPEN, VIEW_TYPE_CALENDAR } from "src/constants"; import { tryToCreateDailyNote } from "src/io/dailyNotes"; -import { tryToCreateWeeklyNote } from "src/io/weeklyNotes"; import type { ISettings } from "src/settings"; import Main from "./ui/Main.svelte" import type ScrollingCalendar from "./components/ScrollingCalendar.svelte"; import { showFileMenu } from "./ui/fileMenu"; -import { activeFile, dailyNotes, weeklyNotes, settings, selectedDate} from "./ui/stores"; +import { activeFile, dailyNotes, settings, selectedDate} from "./ui/stores"; import { customTagsSource, streakSource, tasksSource, wordCountSource, } from "./ui/sources"; +import { getDateUIDFromFile } from "./ui/utils"; export default class CalendarView extends ItemView { private main: Main; private calendar: ScrollingCalendar; @@ -36,11 +35,13 @@ export default class CalendarView extends ItemView { // Group related bindings together for better readability // Calendar interactions - this.openOrCreateDailyNote = this.openOrCreateDailyNote.bind(this); this.onHoverDay = this.onHoverDay.bind(this); this.onContextMenuDay = this.onContextMenuDay.bind(this); this.onClickDay = this.onClickDay.bind(this); + this.openOrCreateDailyNote = this.openOrCreateDailyNote.bind(this); + this.loadNoteContent = this.loadNoteContent.bind(this); + // File system events this.onNoteSettingsUpdate = this.onNoteSettingsUpdate.bind(this); this.onFileCreated = this.onFileCreated.bind(this); @@ -123,6 +124,7 @@ export default class CalendarView extends ItemView { sources, }, openOrCreateNote: this.openOrCreateDailyNote, + loadNoteContent: this.loadNoteContent, onInit: (calendarComponent: ScrollingCalendar) => { this.calendar = calendarComponent; }, @@ -153,24 +155,24 @@ export default class CalendarView extends ItemView { ); } - onHoverWeek( - date: Moment, - targetEl: EventTarget, - isMetaPressed: boolean - ): void { - if (!isMetaPressed) { - return; - } - const note = getWeeklyNote(date, get(weeklyNotes)); - const { format } = getWeeklyNoteSettings(); - this.app.workspace.trigger( - "link-hover", - this, - targetEl, - date.format(format), - note?.path - ); - } + // onHoverWeek( + // date: Moment, + // targetEl: EventTarget, + // isMetaPressed: boolean + // ): void { + // if (!isMetaPressed) { + // return; + // } + // const note = getWeeklyNote(date, get(weeklyNotes)); + // const { format } = getWeeklyNoteSettings(); + // this.app.workspace.trigger( + // "link-hover", + // this, + // targetEl, + // date.format(format), + // note?.path + // ); + // } private onContextMenuDay(date: Moment, event: MouseEvent): void { const note = getDailyNote(date, get(dailyNotes)); @@ -184,21 +186,21 @@ export default class CalendarView extends ItemView { }); } - private onContextMenuWeek(date: Moment, event: MouseEvent): void { - const note = getWeeklyNote(date, get(weeklyNotes)); - if (!note) { - // If no file exists for a given day, show nothing. - return; - } - showFileMenu(this.app, note, { - x: event.pageX, - y: event.pageY, - }); - } + // private onContextMenuWeek(date: Moment, event: MouseEvent): void { + // const note = getWeeklyNote(date, get(weeklyNotes)); + // if (!note) { + // // If no file exists for a given day, show nothing. + // return; + // } + // showFileMenu(this.app, note, { + // x: event.pageX, + // y: event.pageY, + // }); + // } private onNoteSettingsUpdate(): void { dailyNotes.reindex(); - weeklyNotes.reindex(); + // weeklyNotes.reindex(); this.updateActiveFile(); } @@ -207,10 +209,10 @@ export default class CalendarView extends ItemView { dailyNotes.reindex(); this.updateActiveFile(); } - if (getDateFromFile(file, "week")) { - weeklyNotes.reindex(); - this.updateActiveFile(); - } + // if (getDateFromFile(file, "week")) { + // weeklyNotes.reindex(); + // this.updateActiveFile(); + // } } private async onFileModified(file: TFile): Promise { @@ -218,6 +220,11 @@ export default class CalendarView extends ItemView { if (date && this.calendar) { this.calendar.tick(); } + + if (get(activeFile).uid === getDateUIDFromFile(file)) { + const content = await this.app.vault.cachedRead(file); + activeFile.setContent(content); + } } private onFileCreated(file: TFile): void { @@ -226,10 +233,10 @@ export default class CalendarView extends ItemView { dailyNotes.reindex(); this.calendar.tick(); } - if (getDateFromFile(file, "week")) { - weeklyNotes.reindex(); - this.calendar.tick(); - } + // if (getDateFromFile(file, "week")) { + // weeklyNotes.reindex(); + // this.calendar.tick(); + // } } } @@ -243,7 +250,8 @@ export default class CalendarView extends ItemView { const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); const file = markdownView?.file || null; - activeFile.setFile(file); + this.app.vault.cachedRead(file).then( c => activeFile.setFile(file, c)); + if (this.calendar) { this.calendar.tick(); } @@ -271,24 +279,37 @@ export default class CalendarView extends ItemView { } } - async openOrCreateWeeklyNote( - date: Moment, - inNewSplit: boolean - ): Promise { - const { workspace } = this.app; - const startOfWeek = date.clone().startOf("week"); - const existingFile = getWeeklyNote(date, get(weeklyNotes)); - - if (!existingFile) { - tryToCreateWeeklyNote(startOfWeek, inNewSplit, this.settings, (file) => { - activeFile.setFile(file); - }); - return; + // async openOrCreateWeeklyNote( + // date: Moment, + // inNewSplit: boolean + // ): Promise { + // const { workspace } = this.app; + // const startOfWeek = date.clone().startOf("week"); + // const existingFile = getWeeklyNote(date, get(weeklyNotes)); + // + // if (!existingFile) { + // tryToCreateWeeklyNote(startOfWeek, inNewSplit, this.settings, (file) => { + // activeFile.setFile(file); + // }); + // return; + // } + // + // const leaf = await workspace.getLeaf(inNewSplit); + // await leaf.openFile(existingFile); + // activeFile.setFile(existingFile); + // } + + + async loadNoteContent(note: TFile | null): Promise { + if (!note) { + return ''; + } + try { + return await this.app.vault.cachedRead(note); + } catch (error) { + console.error('Failed to load note content:', error); + return ''; } - - const leaf = await workspace.getLeaf(inNewSplit); - await leaf.openFile(existingFile); - activeFile.setFile(existingFile); } async openOrCreateDailyNote( @@ -302,6 +323,8 @@ export default class CalendarView extends ItemView { activeFile.setFile(dailyNote); }); return; + } else if (getDateUIDFromFile(existingFile) == get(activeFile).uid) { + return; } const viewState = (this.app.vault as any).getConfig("defaultViewMode"); const leaf = await workspace.getLeaf(inNewSplit); @@ -309,6 +332,7 @@ export default class CalendarView extends ItemView { active: true, state: { mode: viewState } }); - activeFile.setFile(existingFile); + const content = await this.app.vault.cachedRead(existingFile); + activeFile.setFile(existingFile, content); } } diff --git a/styles.css b/styles.css index d21fa20..9c02002 100644 --- a/styles.css +++ b/styles.css @@ -1,3 +1,14 @@ +body { + --hue-60: calc(var(--accent-h) + 60); + --hue-ternary-one: calc(var(--accent-h) + 120); + --hue-opposite: calc(var(--accent-h) + 180); + --hue-ternary-two: calc(var(--accent-h) + 240); + --hue-300: calc(var(--accent-h) + 300); + + --accent: var(--interactive-accent); + --accent-light: var(--interactive-accent-hover); +} + .settings-banner { background-color: var(--background-secondary-alt); border-radius: 4px; @@ -10,13 +21,7 @@ margin: 0; } -body { - --hue-60: calc(var(--accent-h) + 60); - --hue-ternary-one: calc(var(--accent-h) + 120); - --hue-opposite: calc(var(--accent-h) + 180); - --hue-ternary-two: calc(var(--accent-h) + 240); - --hue-300: calc(var(--accent-h) + 300); -} + .month-container { --color-background-heading: transparent; @@ -121,10 +126,17 @@ th { z-index: 10; } + +.loading-bar.bottom { + top: auto; + bottom: 0; +} + + .loading-bar-inner { height: 100%; background: var(--interactive-accent); - animation: loading 0.5s ease-in-out; + animation: loading 0.25s ease-in-out; } @keyframes loading { @@ -149,7 +161,7 @@ th { } .calendar-section { - height: 65%; + /*height: %;*/ min-height: 300px; border-bottom: 2px solid var(--background-modifier-border); } diff --git a/versions.json b/versions.json deleted file mode 100644 index 0203722..0000000 --- a/versions.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "1.5.9": "0.9.11", - "1.5.10": "0.11.0" -}