mirror of
https://github.com/guicattani/obsidian-countdown-to.git
synced 2026-07-22 05:42:39 +00:00
Improve naming, formatting and README
This commit is contained in:
parent
1163079592
commit
cdd94926d4
7 changed files with 279 additions and 152 deletions
168
README.md
168
README.md
|
|
@ -1,94 +1,106 @@
|
|||
# Obsidian Sample Plugin
|
||||
|
||||
This is a sample plugin for Obsidian (https://obsidian.md).
|
||||
|
||||
This project uses TypeScript to provide type checking and documentation.
|
||||
The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does.
|
||||
# CountdownTo
|
||||
|
||||
This sample plugin demonstrates some of the basic functionality the plugin API can do.
|
||||
- Adds a ribbon icon, which shows a Notice when clicked.
|
||||
- Adds a command "Open Sample Modal" which opens a Modal.
|
||||
- Adds a plugin setting tab to the settings page.
|
||||
- Registers a global click event and output 'click' to the console.
|
||||
- Registers a global interval which logs 'setInterval' to the console.
|
||||

|
||||
|
||||
## First time developing plugins?
|
||||
Track time until important deadlines, events, or milestones with visual progress indicators.
|
||||
|
||||
Quick starting guide for new plugin devs:
|
||||
## Features
|
||||
|
||||
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
|
||||
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
|
||||
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
|
||||
- Install NodeJS, then run `npm i` in the command line under your repo folder.
|
||||
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
|
||||
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
|
||||
- Reload Obsidian to load the new version of your plugin.
|
||||
- Enable plugin in settings window.
|
||||
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
|
||||
- Create visual progress bars that count down to specific dates
|
||||
- Different progress bar types: `Line`, `Circle`, `SemiCircle`, and `Square`
|
||||
- Customizable appearance with color options
|
||||
- Flexible date and time formatting using Luxon
|
||||
- Custom info text with placeholders for dates, percentages, and durations
|
||||
- Progress can be shown as forward progress or as a countdown (days until)
|
||||
- Configurable completion messages
|
||||
|
||||
## Releasing new releases
|
||||
## Manual Installation
|
||||
|
||||
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
|
||||
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
|
||||
- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
|
||||
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release.
|
||||
- Publish the release.
|
||||
1. Download the latest release from the [GitHub releases page](https://github.com/guicattani/countdown-to/releases)
|
||||
2. Extract the files to your Obsidian vault's plugins folder: `<vault>/.obsidian/plugins/countdown-to/`
|
||||
3. Reload Obsidian
|
||||
4. Enable the plugin in Obsidian settings under Community Plugins
|
||||
|
||||
> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`.
|
||||
> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json`
|
||||
## How to Use
|
||||
|
||||
## Adding your plugin to the community plugin list
|
||||
Create a countdown progress bar by adding a code block with the `progressbar` language identifier:
|
||||
|
||||
- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines).
|
||||
- Publish an initial version.
|
||||
- Make sure you have a `README.md` file in the root of your repo.
|
||||
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
|
||||
|
||||
## How to use
|
||||
|
||||
- Clone this repo.
|
||||
- Make sure your NodeJS is at least v16 (`node --version`).
|
||||
- `npm i` or `yarn` to install dependencies.
|
||||
- `npm run dev` to start compilation in watch mode.
|
||||
|
||||
## Manually installing the plugin
|
||||
|
||||
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
|
||||
|
||||
## Improve code quality with eslint (optional)
|
||||
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
|
||||
- To use eslint with this project, make sure to install eslint from terminal:
|
||||
- `npm install -g eslint`
|
||||
- To use eslint to analyze this project use this command:
|
||||
- `eslint main.ts`
|
||||
- eslint will then create a report with suggestions for code improvement by file and line number.
|
||||
- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder:
|
||||
- `eslint .\src\`
|
||||
|
||||
## Funding URL
|
||||
|
||||
You can include funding URLs where people who use your plugin can financially support it.
|
||||
|
||||
The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"fundingUrl": "https://buymeacoffee.com"
|
||||
}
|
||||
````markdown
|
||||
```progressbar
|
||||
title: Project Deadline
|
||||
startDate: 2025-03-12
|
||||
endDate: 2025-04-11
|
||||
type: Circle
|
||||
color: #ff5722
|
||||
trailColor: #f5f5f5
|
||||
progressType: countdown
|
||||
infoFormat: {percent}% complete - {remaining} until {end(LLL d, yyyy)}
|
||||
```
|
||||
````
|
||||
|
||||
If you have multiple URLs, you can also do:
|
||||
### Required Parameters
|
||||
|
||||
```json
|
||||
{
|
||||
"fundingUrl": {
|
||||
"Buy Me a Coffee": "https://buymeacoffee.com",
|
||||
"GitHub Sponsor": "https://github.com/sponsors",
|
||||
"Patreon": "https://www.patreon.com/"
|
||||
}
|
||||
}
|
||||
```
|
||||
- `endDate`: The target date to count down to (ISO format: YYYY-MM-DDTHH:MM:SS)
|
||||
- `startDate`: Start date for the progress calculation (ISO format: YYYY-MM-DDTHH:MM:SS)
|
||||
|
||||
## API Documentation
|
||||
### Optional Parameters
|
||||
|
||||
See https://github.com/obsidianmd/obsidian-api
|
||||
- `title`: Title of the countdown (show above the progress bar)
|
||||
- `type`: Progress bar type - `line`, `circle`, `semicircle`, or `square` (defaults to `line`)
|
||||
- `color`: Color for the progress bar (HEX format)
|
||||
- `trailColor`: Color for the incomplete part of the progress bar (HEX format)
|
||||
- `progressType`: `progress` (default) or `countdown`
|
||||
- `onCompleteText`: Text to display when the countdown is complete
|
||||
- `infoFormat`: Custom format for the information text
|
||||
|
||||
## Formatting Options
|
||||
|
||||
The `infoFormat` parameter supports various placeholders and Luxon formatting options:
|
||||
|
||||
### Basic Placeholders
|
||||
|
||||
- `{percent}` - Percentage of completion
|
||||
- `{start}` - Start date (ISO format)
|
||||
- `{end}` - End date (ISO format)
|
||||
- `{current}` - Current date (ISO format)
|
||||
- `{remaining}` - Remaining time in human-readable format
|
||||
- `{elapsed}` - Elapsed time in human-readable format
|
||||
- `{total}` - Total duration in human-readable format
|
||||
|
||||
### Luxon Formatting
|
||||
|
||||
You can format dates and durations using [Luxon's](https://moment.github.io/luxon/#/formatting?id=table-of-tokens) formatting syntax:
|
||||
|
||||
- `{start(format)}` - Format start date
|
||||
- `{end(format)}` - Format end date
|
||||
- `{current(format)}` - Format current date
|
||||
- `{remaining(format)}` - Format remaining duration
|
||||
- `{elapsed(format)}` - Format elapsed duration
|
||||
- `{total(format)}` - Format total duration
|
||||
|
||||
Examples:
|
||||
- `{end(LLL d, yyyy)}` displays the end date as "Apr 11, 2025"
|
||||
- `{end(EEEE, MMMM d, yyyy)}` displays as "Thursday, April 11, 2025"
|
||||
|
||||
See the [Luxon formatting reference](https://moment.github.io/luxon/#/formatting?id=table-of-tokens) for all available format tokens.
|
||||
|
||||
## Configuration
|
||||
|
||||
You can configure default settings for all progress bars in the plugin settings:
|
||||
|
||||
1. Open Obsidian Settings
|
||||
2. Navigate to the "Countdown To" plugin settings
|
||||
3. Customize the default bar type, colors, progress type, and info format
|
||||
|
||||
All settings can be overridden in individual progress bar code blocks.
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter any issues or have feature requests, please [open an issue](https://github.com/guicattani/countdown-to/issues) on GitHub.
|
||||
|
||||
## Credits
|
||||
|
||||
- Uses [progressbar.js](https://kimmobrunfeldt.github.io/progressbar.js/) for rendering progress bars
|
||||
- Uses [Luxon](https://moment.github.io/luxon/) for date and time handling
|
||||
|
|
|
|||
221
main.ts
221
main.ts
|
|
@ -1,20 +1,23 @@
|
|||
import { App, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
||||
import { App, Plugin, PluginSettingTab, Setting, Modal } from 'obsidian';
|
||||
import { DateTime, Duration, Interval } from 'luxon';
|
||||
import * as ProgressBar from 'progressbar.js';
|
||||
|
||||
interface ProgressBarSettings {
|
||||
defaultBarColor: string;
|
||||
defaultBarBackgroundColor: string;
|
||||
defaultTrailColor: string;
|
||||
defaultBarType: string;
|
||||
defaultProgressType: string;
|
||||
defaultOnCompleteText: string;
|
||||
defaultProgressType: string;
|
||||
defaultOnCompleteText: string;
|
||||
defaultInfoFormat: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: ProgressBarSettings = {
|
||||
defaultBarColor: '#4CAF50',
|
||||
defaultBarBackgroundColor: '#e0e0e0',
|
||||
defaultTrailColor: '#e0e0e0',
|
||||
defaultBarType: 'Line',
|
||||
defaultProgressType: 'forward',
|
||||
defaultOnCompleteText: 'Completed!',
|
||||
defaultProgressType: 'forward',
|
||||
defaultOnCompleteText: '{title} is done!',
|
||||
defaultInfoFormat: '{percent}% - {remaining} remaining',
|
||||
}
|
||||
|
||||
export default class ProgressBarPlugin extends Plugin {
|
||||
|
|
@ -41,14 +44,16 @@ export default class ProgressBarPlugin extends Plugin {
|
|||
renderProgressBar(source: string, el: HTMLElement) {
|
||||
try {
|
||||
const params = this.parseProgressBarParams(source);
|
||||
const containerEl = el.createDiv({ cls: 'progress-bar-container' });
|
||||
const containerEl = el.createDiv({ cls: 'countdown-to-container' });
|
||||
|
||||
const startDate = params.startDate ? new Date(params.startDate) : new Date();
|
||||
const endDate = new Date(params.endDate);
|
||||
const currentDate = new Date();
|
||||
const startDate = params.startDate ?
|
||||
DateTime.fromISO(params.startDate) :
|
||||
DateTime.now();
|
||||
const endDate = DateTime.fromISO(params.endDate);
|
||||
const currentDate = DateTime.now();
|
||||
|
||||
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
||||
containerEl.setText('Invalid date format. Please use YYYY-MM-DD format.');
|
||||
if (!startDate.isValid || !endDate.isValid) {
|
||||
containerEl.setText('Invalid date format. Please use ISO format (YYYY-MM-DDTHH:MM:SS).');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -57,37 +62,64 @@ export default class ProgressBarPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
const totalDuration = endDate.getTime() - startDate.getTime();
|
||||
const elapsedDuration = currentDate.getTime() - startDate.getTime();
|
||||
let progress = Math.min(Math.max(elapsedDuration / totalDuration, 0), 1);
|
||||
const totalInterval = Interval.fromDateTimes(startDate, endDate);
|
||||
const elapsedInterval = Interval.fromDateTimes(startDate, currentDate);
|
||||
|
||||
const barColor = params.color || this.settings.defaultBarColor;
|
||||
const barBgColor = params.backgroundColor || this.settings.defaultBarBackgroundColor;
|
||||
const barType = params.type || this.settings.defaultBarType;
|
||||
const progressType = params.progressType || this.settings.defaultProgressType;
|
||||
const onCompleteText = params.onCompleteText || this.settings.defaultOnCompleteText;
|
||||
const totalMillis = totalInterval.length();
|
||||
const elapsedMillis = Math.min(elapsedInterval.length(), totalMillis);
|
||||
|
||||
let progress = Math.min(Math.max(elapsedMillis / totalMillis, 0), 1);
|
||||
|
||||
const barColor = params.color || this.settings.defaultBarColor;
|
||||
const trailColor = params.trailColor || this.settings.defaultTrailColor;
|
||||
const barType = params.type || this.settings.defaultBarType;
|
||||
const progressType = params.progressType || this.settings.defaultProgressType;
|
||||
const onCompleteText = params.onCompleteText || this.settings.defaultOnCompleteText;
|
||||
const infoFormat = params.infoFormat || this.settings.defaultInfoFormat;
|
||||
|
||||
const progressBarEl = containerEl.createDiv({ cls: 'progress-bar-element' });
|
||||
progressBarEl.addClass(`progress-bar-${barType.toLowerCase()}`);
|
||||
|
||||
const infoEl = containerEl.createDiv({ cls: 'progress-bar-info' });
|
||||
const remainingTime = this.formatRemainingTime(endDate, currentDate);
|
||||
|
||||
if (progress >= 1) {
|
||||
infoEl.setText(onCompleteText);
|
||||
infoEl.setText(
|
||||
onCompleteText.replace(/{title}/g, params.title || '')
|
||||
);
|
||||
} else {
|
||||
if (progressType.toLowerCase() === 'countdown') {
|
||||
infoEl.setText(`${remainingTime} until completion`);
|
||||
} else {
|
||||
infoEl.setText(`${Math.round(progress * 100)}% - ${remainingTime} remaining`);
|
||||
}
|
||||
let infoText = infoFormat;
|
||||
|
||||
const remainingInterval = Interval.fromDateTimes(currentDate, endDate);
|
||||
const remainingDuration = remainingInterval.toDuration(['days', 'hours', 'minutes', 'seconds']);
|
||||
|
||||
const elapsedDuration = elapsedInterval.toDuration(['days', 'hours', 'minutes', 'seconds']);
|
||||
|
||||
const totalDuration = totalInterval.toDuration(['days', 'hours', 'minutes', 'seconds']);
|
||||
infoText = infoText
|
||||
.replace(/{start\((.*?)\)}/g, (_match: string, format: string) => startDate.toFormat(format))
|
||||
.replace(/{end\((.*?)\)}/g, (_match: string, format: string) => endDate.toFormat(format))
|
||||
.replace(/{current\((.*?)\)}/g, (_match: string, format: string) => currentDate.toFormat(format))
|
||||
.replace(/{remaining\((.*?)\)}/g, (_match: string, format: string) => remainingDuration.toFormat(format))
|
||||
.replace(/{elapsed\((.*?)\)}/g, (_match: string, format: string) => elapsedDuration.toFormat(format))
|
||||
.replace(/{total\((.*?)\)}/g, (_match: string, format: string) => totalDuration.toFormat(format));
|
||||
|
||||
infoText = infoText
|
||||
.replace(/{percent}/g, Math.round(progress * 100).toString())
|
||||
.replace(/{start}/g, startDate.toISODate() || '')
|
||||
.replace(/{end}/g, endDate.toISODate() || '')
|
||||
.replace(/{current}/g, currentDate.toISODate() || '')
|
||||
.replace(/{remaining}/g, this.formatDuration(remainingDuration))
|
||||
.replace(/{elapsed}/g, this.formatDuration(elapsedDuration))
|
||||
.replace(/{total}/g, this.formatDuration(totalDuration));
|
||||
|
||||
infoEl.setText(infoText);
|
||||
}
|
||||
|
||||
let bar;
|
||||
const commonOptions = {
|
||||
strokeWidth: 4,
|
||||
color: barColor,
|
||||
trailColor: barBgColor,
|
||||
trailColor: trailColor,
|
||||
trailWidth: 1,
|
||||
};
|
||||
|
||||
|
|
@ -119,14 +151,12 @@ export default class ProgressBarPlugin extends Plugin {
|
|||
break;
|
||||
}
|
||||
|
||||
// Animation support still needs some work
|
||||
if (progressType.toLowerCase() === 'countdown') {
|
||||
bar.set(1.0 - progress);
|
||||
} else {
|
||||
bar.set(progress);
|
||||
}
|
||||
if (progressType.toLowerCase() === 'countdown') {
|
||||
bar.set(1.0 - progress);
|
||||
} else {
|
||||
bar.set(progress);
|
||||
}
|
||||
|
||||
// Add title if provided
|
||||
if (params.title) {
|
||||
const titleEl = containerEl.createDiv({ cls: 'progress-bar-title' });
|
||||
titleEl.setText(params.title);
|
||||
|
|
@ -149,7 +179,6 @@ export default class ProgressBarPlugin extends Plugin {
|
|||
}
|
||||
});
|
||||
|
||||
// Ensure required parameters
|
||||
if (!params.endDate) {
|
||||
throw new Error('End date is required');
|
||||
}
|
||||
|
|
@ -157,17 +186,11 @@ export default class ProgressBarPlugin extends Plugin {
|
|||
return params;
|
||||
}
|
||||
|
||||
formatRemainingTime(endDate: Date, currentDate: Date) {
|
||||
const remainingMs = endDate.getTime() - currentDate.getTime();
|
||||
|
||||
if (remainingMs <= 0) {
|
||||
return 'Completed';
|
||||
}
|
||||
|
||||
const seconds = Math.floor(remainingMs / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
formatDuration(duration: Duration): string {
|
||||
const days = Math.ceil(duration.as('days'));
|
||||
const hours = Math.ceil(duration.as('hours') % 24);
|
||||
const minutes = Math.ceil(duration.as('minutes') % 60);
|
||||
const seconds = Math.ceil(duration.as('seconds') % 60);
|
||||
|
||||
if (days > 0) {
|
||||
return `${days} day${days > 1 ? 's' : ''}`;
|
||||
|
|
@ -194,7 +217,8 @@ class ProgressBarSettingTab extends PluginSettingTab {
|
|||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', {text: 'Progress Bar Settings'});
|
||||
containerEl.createEl('h2', {text: 'Countdown To Settings'});
|
||||
containerEl.createEl('a', {href: 'https://github.com/guicattani/countdown-to?tab=readme-ov-file#how-to-use', text: 'How to use'});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Default bar type')
|
||||
|
|
@ -211,10 +235,10 @@ class ProgressBarSettingTab extends PluginSettingTab {
|
|||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Progress type')
|
||||
.setDesc('Type of progress bar to display (forward or countdown)')
|
||||
.setName('Default progress type')
|
||||
.setDesc('Count as progress or as a countdown')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('Forward', 'Forward')
|
||||
.addOption('Progress', 'Progress')
|
||||
.addOption('Countdown', 'Countdown')
|
||||
.setValue(this.plugin.settings.defaultProgressType)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -222,6 +246,25 @@ class ProgressBarSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Default info format')
|
||||
.setDesc('Default format for the info text. Uses Luxon formatting (See format help button for a quick reference).')
|
||||
.addTextArea(text => text
|
||||
.setPlaceholder('{percent}% - {remaining} remaining')
|
||||
.setValue(this.plugin.settings.defaultInfoFormat)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.defaultInfoFormat = value;
|
||||
await this.plugin.saveSettings();
|
||||
}))
|
||||
.addExtraButton(button => {
|
||||
button
|
||||
.setIcon('help')
|
||||
.setTooltip('Show format help')
|
||||
.onClick(() => {
|
||||
new LuxonFormatHelpModal(this.plugin.app).open();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Default bar color')
|
||||
.setDesc('Default color for the progress bar')
|
||||
|
|
@ -235,9 +278,9 @@ class ProgressBarSettingTab extends PluginSettingTab {
|
|||
|
||||
new Setting(containerEl)
|
||||
.setName('Default on complete text')
|
||||
.setDesc('Default text to display when the progress bar is complete')
|
||||
.setDesc('Default text to display when the progress is complete. Use {title} to display the title of the progress bar.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('Completed!')
|
||||
.setPlaceholder('{title} is done!')
|
||||
.setValue(this.plugin.settings.defaultOnCompleteText)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.defaultOnCompleteText = value;
|
||||
|
|
@ -245,14 +288,76 @@ class ProgressBarSettingTab extends PluginSettingTab {
|
|||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Default background color')
|
||||
.setDesc('Default background color for the progress bar')
|
||||
.setName('Default trail color')
|
||||
.setDesc('Default trail color for the progress bar (the incomplete part)')
|
||||
.addText(text => text
|
||||
.setPlaceholder('#e0e0e0')
|
||||
.setValue(this.plugin.settings.defaultBarBackgroundColor)
|
||||
.setValue(this.plugin.settings.defaultTrailColor)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.defaultBarBackgroundColor = value;
|
||||
this.plugin.settings.defaultTrailColor = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
containerEl.createEl('i', {text: 'All settings can be overridden in the markdown code block.'});
|
||||
}
|
||||
}
|
||||
|
||||
class LuxonFormatHelpModal extends Modal {
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
|
||||
contentEl.createEl('h2', {text: 'Info Format Help'});
|
||||
|
||||
contentEl.createEl('h3', {text: 'Placeholders'});
|
||||
const placeholdersList = contentEl.createEl('ul');
|
||||
placeholdersList.createEl('li', {text: '{percent} - Percentage of completion'});
|
||||
placeholdersList.createEl('li', {text: '{start} - Start date (ISO format)'});
|
||||
placeholdersList.createEl('li', {text: '{end} - End date (ISO format)'});
|
||||
placeholdersList.createEl('li', {text: '{current} - Current date (ISO format)'});
|
||||
placeholdersList.createEl('li', {text: '{remaining} - Remaining time'});
|
||||
placeholdersList.createEl('li', {text: '{elapsed} - Elapsed time'});
|
||||
placeholdersList.createEl('li', {text: '{total} - Total duration'});
|
||||
|
||||
contentEl.createEl('h3', {text: 'Formatting'});
|
||||
contentEl.createEl('p', {text: 'You can use Luxon formatting for dates:'});
|
||||
const luxonList = contentEl.createEl('ul');
|
||||
luxonList.createEl('li', {text: '{start(format)} - Format start date'});
|
||||
luxonList.createEl('li', {text: '{end(format)} - Format end date'});
|
||||
luxonList.createEl('li', {text: '{current(format)} - Format current date'});
|
||||
luxonList.createEl('li', {text: '{remaining(format)} - Format remaining time'});
|
||||
luxonList.createEl('li', {text: '{elapsed(format)} - Format elapsed time'});
|
||||
luxonList.createEl('li', {text: '{total(format)} - Format total duration'});
|
||||
contentEl.createEl('a', {href: 'https://moment.github.io/luxon/#/formatting?id=table-of-tokens', text: 'Luxon Formatting Reference'});
|
||||
|
||||
contentEl.createEl('h3', {text: 'Examples'});
|
||||
const examplesList = contentEl.createEl('ul');
|
||||
examplesList.createEl('li', {text: '{percent}% complete - {remaining} left'});
|
||||
examplesList.createEl('li', {text: 'Started on {start(LLL d)}, ends on {end(LLL d, yyyy)}'});
|
||||
examplesList.createEl('li', {text: '{elapsed} elapsed out of {total} total'});
|
||||
|
||||
contentEl.createEl('h3', {text: 'Common Luxon Formats'});
|
||||
const formatsTable = contentEl.createEl('table');
|
||||
const headerRow = formatsTable.createEl('tr');
|
||||
headerRow.createEl('th', {text: 'Format'});
|
||||
headerRow.createEl('th', {text: 'Output'});
|
||||
|
||||
const addFormatRow = (format: string, output: string) => {
|
||||
const row = formatsTable.createEl('tr');
|
||||
row.createEl('td', {text: format});
|
||||
row.createEl('td', {text: output});
|
||||
};
|
||||
|
||||
addFormatRow('yyyy-MM-dd', '2025-04-11');
|
||||
addFormatRow('LLL d, yyyy', 'Apr 11, 2025');
|
||||
addFormatRow('EEEE, MMMM d, yyyy', 'Thursday, April 11, 2025');
|
||||
addFormatRow('d MMMM yyyy', '11 April 2025');
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
{
|
||||
"id": "obsidian-progress-bar",
|
||||
"name": "Progress Bar",
|
||||
"id": "obsidian-countdown-to",
|
||||
"name": "Countdown To",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Create countdown progress bars in your notes",
|
||||
"author": "Gui Cattani",
|
||||
"authorUrl": "https://github.com/guicattani",
|
||||
"fundingUrl": "https://obsidian.md/pricing",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
|
|
|
|||
22
package-lock.json
generated
22
package-lock.json
generated
|
|
@ -1,17 +1,19 @@
|
|||
{
|
||||
"name": "obsidian-sample-plugin",
|
||||
"name": "obsidian-progress-bar",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-sample-plugin",
|
||||
"name": "obsidian-progress-bar",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"luxon": "^3.5.0",
|
||||
"progressbar.js": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/progressbar.js": "^1.1.7",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
|
|
@ -599,6 +601,13 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
|
||||
"integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.18.125",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.125.tgz",
|
||||
|
|
@ -1824,6 +1833,15 @@
|
|||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
|
||||
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "obsidian-progress-bar",
|
||||
"name": "obsidian-countdown-to",
|
||||
"version": "1.0.0",
|
||||
"description": "Create countdown progress bars in your notes",
|
||||
"main": "main.js",
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
"author": "Gui Cattani",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/progressbar.js": "^1.1.7",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"luxon": "^3.5.0",
|
||||
"progressbar.js": "^1.1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
11
styles.css
11
styles.css
|
|
@ -1,13 +1,4 @@
|
|||
/*
|
||||
|
||||
This CSS file will be included with your plugin, and
|
||||
available in the app when your plugin is enabled.
|
||||
|
||||
If your plugin does not need CSS, delete this file.
|
||||
|
||||
*/
|
||||
|
||||
.progress-bar-container {
|
||||
.countdown-to-container {
|
||||
position: relative;
|
||||
margin: 20px 0;
|
||||
padding: 10px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue