diff --git a/README.md b/README.md index c773152..edc53aa 100644 --- a/README.md +++ b/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. +![CountdownTo](./screenshot.png) -## 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: `/.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 diff --git a/main.ts b/main.ts index 45a1aa1..7b7ef13 100644 --- a/main.ts +++ b/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(); } } \ No newline at end of file diff --git a/manifest.json b/manifest.json index 99489eb..3368d16 100644 --- a/manifest.json +++ b/manifest.json @@ -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 } diff --git a/package-lock.json b/package-lock.json index 06f1fef..5d06a04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b08fbc2..3061856 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..17aec56 Binary files /dev/null and b/screenshot.png differ diff --git a/styles.css b/styles.css index 039fa1d..892e3e8 100644 --- a/styles.css +++ b/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;