mirror of
https://github.com/playmean/obsidian-event-highlight-plugin.git
synced 2026-07-22 05:44:13 +00:00
feat: initial commit
This commit is contained in:
commit
83f813c7c5
13 changed files with 3415 additions and 0 deletions
3
.eslintignore
Normal file
3
.eslintignore
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
|
||||
main.js
|
||||
21
.eslintrc
Normal file
21
.eslintrc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": { "node": true },
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
},
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
},
|
||||
}
|
||||
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# vscode
|
||||
.vscode
|
||||
|
||||
# Intellij
|
||||
*.iml
|
||||
.idea
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
|
||||
# Don't include the compiled main.js file in the repo.
|
||||
# They should be uploaded to GitHub releases instead.
|
||||
main.js
|
||||
|
||||
# Exclude sourcemaps
|
||||
*.map
|
||||
|
||||
# obsidian
|
||||
data.json
|
||||
|
||||
# Exclude macOS Finder (System Explorer) View States
|
||||
.DS_Store
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
tag-version-prefix=""
|
||||
13
.prettierrc.yml
Normal file
13
.prettierrc.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
tabWidth: 4
|
||||
semi: true
|
||||
singleQuote: true
|
||||
printWidth: 90
|
||||
importOrder:
|
||||
[
|
||||
"^[a-z]",
|
||||
'^\.{2}/',
|
||||
'^\./',
|
||||
'^~(.*)$',
|
||||
]
|
||||
importOrderSeparation: true
|
||||
importOrderSortSpecifiers: true
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Aleksey Korolev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
50
esbuild.config.mjs
Normal file
50
esbuild.config.mjs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import esbuild from 'esbuild';
|
||||
import process from 'process';
|
||||
import builtins from 'builtin-modules';
|
||||
|
||||
const banner = `/*
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
`;
|
||||
|
||||
const prod = process.argv[2] === 'production';
|
||||
|
||||
const context = await esbuild.context({
|
||||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ['main.ts'],
|
||||
bundle: true,
|
||||
external: [
|
||||
'obsidian',
|
||||
'electron',
|
||||
'@codemirror/autocomplete',
|
||||
'@codemirror/collab',
|
||||
'@codemirror/commands',
|
||||
'@codemirror/language',
|
||||
'@codemirror/lint',
|
||||
'@codemirror/search',
|
||||
'@codemirror/state',
|
||||
'@codemirror/view',
|
||||
'@lezer/common',
|
||||
'@lezer/highlight',
|
||||
'@lezer/lr',
|
||||
...builtins,
|
||||
],
|
||||
format: 'cjs',
|
||||
target: 'es2018',
|
||||
logLevel: 'info',
|
||||
sourcemap: prod ? false : 'inline',
|
||||
treeShaking: true,
|
||||
outfile: 'main.js',
|
||||
minify: prod,
|
||||
});
|
||||
|
||||
if (prod) {
|
||||
await context.rebuild();
|
||||
|
||||
process.exit(0);
|
||||
} else {
|
||||
await context.watch();
|
||||
}
|
||||
136
main.ts
Normal file
136
main.ts
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
import { Plugin, moment, setIcon, setTooltip } from 'obsidian';
|
||||
|
||||
export default class EventHighlightPlugin extends Plugin {
|
||||
private attributeName = 'data-datey';
|
||||
|
||||
private renderElement(el: Element, source: string) {
|
||||
const parsedDate = moment(source, 'YYYY-MM-DD', true);
|
||||
const parsedDateTime = moment(source, 'YYYY-MM-DD HH:mm', true);
|
||||
|
||||
const isDateTime = parsedDateTime.isValid();
|
||||
|
||||
const workFormat = isDateTime ? 'DD.MM.YYYY HH:mm' : 'DD.MM.YYYY';
|
||||
const workStamp = isDateTime ? parsedDateTime : parsedDate;
|
||||
const workGranularity = isDateTime ? 'hour' : 'day';
|
||||
const workMinimalGranularity = isDateTime ? 'minute' : 'day';
|
||||
|
||||
const now = moment();
|
||||
|
||||
const isAfter = now.isSameOrAfter(
|
||||
moment(workStamp).add(1, 'hour'),
|
||||
workMinimalGranularity,
|
||||
);
|
||||
const isActual = !isAfter && now.isSameOrAfter(workStamp, workMinimalGranularity);
|
||||
const isUpcomming =
|
||||
!isActual &&
|
||||
now.isSame(
|
||||
isDateTime ? workStamp : moment(workStamp).subtract(1, 'day'),
|
||||
'day',
|
||||
);
|
||||
const isBefore = !isUpcomming && now.isBefore(workStamp, workGranularity);
|
||||
|
||||
el.innerHTML = '';
|
||||
|
||||
if (isAfter) {
|
||||
el.removeAttribute(this.attributeName);
|
||||
} else {
|
||||
el.setAttribute(this.attributeName, source);
|
||||
}
|
||||
|
||||
const dateSpan = el.createEl('div');
|
||||
const iconSpan = dateSpan.createEl('div');
|
||||
|
||||
const roundingDefault = moment.relativeTimeRounding();
|
||||
|
||||
moment.relativeTimeRounding(Math.floor);
|
||||
|
||||
moment.relativeTimeThreshold('m', 60);
|
||||
moment.relativeTimeThreshold('h', 24);
|
||||
moment.relativeTimeThreshold('d', 7);
|
||||
moment.relativeTimeThreshold('w', 4);
|
||||
moment.relativeTimeThreshold('M', 12);
|
||||
|
||||
const workFormatted = workStamp.format(workFormat);
|
||||
const workFromNow = workStamp.fromNow();
|
||||
|
||||
moment.relativeTimeRounding(roundingDefault);
|
||||
|
||||
dateSpan.createEl('div', {
|
||||
text: isAfter ? workFormatted : workFromNow,
|
||||
});
|
||||
|
||||
iconSpan.style.display = 'inline-flex';
|
||||
iconSpan.style.alignItems = 'center';
|
||||
|
||||
dateSpan.style.display = 'inline-flex';
|
||||
dateSpan.style.alignItems = 'center';
|
||||
dateSpan.style.gap = '4px';
|
||||
dateSpan.style.borderRadius = '4px';
|
||||
dateSpan.style.padding = '2px 4px';
|
||||
dateSpan.style.userSelect = 'none';
|
||||
dateSpan.style.cursor = 'default';
|
||||
|
||||
switch (true) {
|
||||
case isAfter:
|
||||
dateSpan.style.backgroundColor = '#404040';
|
||||
|
||||
setIcon(iconSpan, 'calendar-check-2');
|
||||
setTooltip(dateSpan, `Past event`);
|
||||
|
||||
break;
|
||||
case isBefore:
|
||||
dateSpan.style.backgroundColor = 'green';
|
||||
|
||||
setIcon(iconSpan, 'calendar');
|
||||
setTooltip(dateSpan, `Event soon (${workFormatted})`);
|
||||
|
||||
break;
|
||||
case isUpcomming:
|
||||
dateSpan.style.backgroundColor = '#e0a500';
|
||||
dateSpan.style.color = '#333333';
|
||||
|
||||
setIcon(iconSpan, 'calendar-clock');
|
||||
setTooltip(dateSpan, `Upcoming event (${workFormatted})`);
|
||||
|
||||
break;
|
||||
case isActual:
|
||||
dateSpan.style.backgroundColor = 'purple';
|
||||
dateSpan.style.fontWeight = 'bold';
|
||||
|
||||
setIcon(iconSpan, 'clock');
|
||||
setTooltip(dateSpan, `Event started (${workFormatted})`);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private updateAllPage() {
|
||||
const elements = document.querySelectorAll(`[${this.attributeName}]`);
|
||||
|
||||
elements.forEach((el) => {
|
||||
const source = el.getAttribute(this.attributeName);
|
||||
|
||||
if (!source) return;
|
||||
|
||||
this.renderElement(el, source);
|
||||
});
|
||||
}
|
||||
|
||||
async onload() {
|
||||
this.registerMarkdownCodeBlockProcessor('datey', (source, el, ctx) => {
|
||||
this.renderElement(el, source.trim());
|
||||
});
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on('active-leaf-change', () => {
|
||||
this.updateAllPage();
|
||||
}),
|
||||
);
|
||||
|
||||
this.registerInterval(
|
||||
window.setInterval(() => {
|
||||
this.updateAllPage();
|
||||
}, 10_000),
|
||||
);
|
||||
}
|
||||
}
|
||||
11
manifest.json
Normal file
11
manifest.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "event-highlight",
|
||||
"name": "Event Highlight",
|
||||
"description": "Render colored bars with relative event dates",
|
||||
"version": "0.0.0",
|
||||
"author": "playmean",
|
||||
"authorUrl": "https://playmean.ru",
|
||||
"fundingUrl": "https://playmean.ru/donate",
|
||||
"minAppVersion": "0.15.0",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
3080
package-lock.json
generated
Normal file
3080
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
26
package.json
Normal file
26
package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "obsidian-event-highlight-plugin",
|
||||
"version": "0.0.0",
|
||||
"description": "Render colored bars with relative event dates",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||
"release": "npm run build && ./release.sh"
|
||||
},
|
||||
"author": "playmean",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
||||
"@typescript-eslint/parser": "^5.29.0",
|
||||
"builtin-modules": "^3.3.0",
|
||||
"esbuild": "^0.17.3",
|
||||
"obsidian": "latest",
|
||||
"prettier": "^3.3.3",
|
||||
"semverity": "^0.3.0",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
7
release.sh
Executable file
7
release.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
VERSION=`npx semverity bump`
|
||||
|
||||
npx semverity patch --files package.json:version package-lock.json:version,packages..version manifest.json:version --commit bump $VERSION
|
||||
|
||||
git tag $VERSION && git push origin $VERSION
|
||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
"ES6",
|
||||
"ES7"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue