This commit is contained in:
Ell 2023-05-22 20:01:50 +02:00
parent 402b97c799
commit 70ade8ada0
11 changed files with 184 additions and 181 deletions

View file

@ -4,6 +4,6 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
insert_final_newline = true insert_final_newline = true
indent_style = tab indent_style = space
indent_size = 4 indent_size = 4
tab_width = 4 tab_width = 4

View file

@ -9,7 +9,7 @@ To get started tracking your time with Super Simple Time Tracker, open up the no
When switching to live preview or reading mode, you will now see the time tracker you just inserted! Now, simply name the first segment (or leave the box empty if you don't want to name it) and press the **Start** button. Once you're done with the thing you were doing, simply press the **End** button and the time you spent will be saved and displayed to you in the table. When switching to live preview or reading mode, you will now see the time tracker you just inserted! Now, simply name the first segment (or leave the box empty if you don't want to name it) and press the **Start** button. Once you're done with the thing you were doing, simply press the **End** button and the time you spent will be saved and displayed to you in the table.
# 👀 What it does # 👀 What it does
A time tracker is really just a special code block that stores information about the times you pressed the Start and End buttons on. Since time is tracked solely through timestamps, you can switch notes, close Obsidian or even shut down your device completely while the tracker is running! Once you come back, your time tracker will still be running. A time tracker is really just a special code block that stores information about the times you pressed the Start and End buttons on. Since time is tracked solely through timestamps, you can switch notes, close Obsidian or even shut down your device completely while the tracker is running! Once you come back, your time tracker will still be running.
The tracker's information is stored in the code block as JSON data. The names, start times and end times of each segment are stored. They're displayed neatly in the code block in preview or reading mode. The tracker's information is stored in the code block as JSON data. The names, start times and end times of each segment are stored. They're displayed neatly in the code block in preview or reading mode.

View file

@ -11,50 +11,50 @@ if you want to view the source, please visit the github repository of this plugi
const prod = (process.argv[2] === 'production'); const prod = (process.argv[2] === 'production');
esbuild.build({ esbuild.build({
banner: { banner: {
js: banner, js: banner,
}, },
entryPoints: ['src/main.ts'], entryPoints: ['src/main.ts'],
bundle: true, bundle: true,
external: [ external: [
'obsidian', 'obsidian',
'electron', 'electron',
'@codemirror/autocomplete', '@codemirror/autocomplete',
'@codemirror/closebrackets', '@codemirror/closebrackets',
'@codemirror/collab', '@codemirror/collab',
'@codemirror/commands', '@codemirror/commands',
'@codemirror/comment', '@codemirror/comment',
'@codemirror/fold', '@codemirror/fold',
'@codemirror/gutter', '@codemirror/gutter',
'@codemirror/highlight', '@codemirror/highlight',
'@codemirror/history', '@codemirror/history',
'@codemirror/language', '@codemirror/language',
'@codemirror/lint', '@codemirror/lint',
'@codemirror/matchbrackets', '@codemirror/matchbrackets',
'@codemirror/panel', '@codemirror/panel',
'@codemirror/rangeset', '@codemirror/rangeset',
'@codemirror/rectangular-selection', '@codemirror/rectangular-selection',
'@codemirror/search', '@codemirror/search',
'@codemirror/state', '@codemirror/state',
'@codemirror/stream-parser', '@codemirror/stream-parser',
'@codemirror/text', '@codemirror/text',
'@codemirror/tooltip', '@codemirror/tooltip',
'@codemirror/view', '@codemirror/view',
...builtins ...builtins
], ],
plugins: [ plugins: [
copy({ copy({
assets: [{ assets: [{
from: ["./manifest.json", "./main.js", "./styles.css"], from: ["./manifest.json", "./main.js", "./styles.css"],
to: ["./test-vault/.obsidian/plugins/simple-time-tracker/."] to: ["./test-vault/.obsidian/plugins/simple-time-tracker/."]
}] }]
}), }),
], ],
format: 'cjs', format: 'cjs',
watch: !prod, watch: !prod,
target: 'es2016', target: 'es2016',
logLevel: "info", logLevel: "info",
sourcemap: prod ? false : 'inline', sourcemap: prod ? false : 'inline',
treeShaking: true, treeShaking: true,
outfile: 'main.js', outfile: 'main.js',
}).catch(() => process.exit(1)); }).catch(() => process.exit(1));

View file

@ -1,10 +1,10 @@
{ {
"id": "simple-time-tracker", "id": "simple-time-tracker",
"name": "Super Simple Time Tracker", "name": "Super Simple Time Tracker",
"version": "0.1.5", "version": "0.1.5",
"minAppVersion": "0.15.0", "minAppVersion": "0.15.0",
"description": "Multi-purpose time trackers for your notes!", "description": "Multi-purpose time trackers for your notes!",
"author": "Ellpeck", "author": "Ellpeck",
"authorUrl": "https://ellpeck.de", "authorUrl": "https://ellpeck.de",
"isDesktopOnly": false "isDesktopOnly": false
} }

View file

@ -1,24 +1,24 @@
{ {
"name": "simple-time-tracker", "name": "simple-time-tracker",
"version": "0.1.5", "version": "0.1.5",
"description": "Multi-purpose time trackers for your notes!", "description": "Multi-purpose time trackers for your notes!",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"dev": "node esbuild.config.mjs", "dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json" "version": "node version-bump.mjs && git add manifest.json versions.json"
}, },
"keywords": [], "keywords": [],
"author": "Ellpeck", "author": "Ellpeck",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.6", "@types/node": "^16.11.6",
"builtin-modules": "^3.2.0", "builtin-modules": "^3.2.0",
"electron": "^13.6.2", "electron": "^13.6.2",
"esbuild": "0.14.0", "esbuild": "0.14.0",
"esbuild-plugin-copy": "^1.3.0", "esbuild-plugin-copy": "^1.3.0",
"obsidian": "latest", "obsidian": "latest",
"tslib": "2.3.1", "tslib": "2.3.1",
"typescript": "4.4.4" "typescript": "4.4.4"
} }
} }

View file

@ -1,37 +1,37 @@
import { Plugin } from "obsidian"; import { Plugin } from "obsidian";
import { defaultSettings, SimpleTimeTrackerSettings } from "./settings"; import { defaultSettings, SimpleTimeTrackerSettings } from "./settings";
import { SimpleTimeTrackerSettingsTab } from "./settings-tab"; import { SimpleTimeTrackerSettingsTab } from "./settings-tab";
import { displayTracker, loadTracker } from "./tracker"; import { displayTracker, loadTracker, Tracker } from "./tracker";
export default class SimpleTimeTrackerPlugin extends Plugin { export default class SimpleTimeTrackerPlugin extends Plugin {
settings: SimpleTimeTrackerSettings; settings: SimpleTimeTrackerSettings;
async onload(): Promise<void> { async onload(): Promise<void> {
await this.loadSettings(); await this.loadSettings();
this.addSettingTab(new SimpleTimeTrackerSettingsTab(this.app, this)); this.addSettingTab(new SimpleTimeTrackerSettingsTab(this.app, this));
this.registerMarkdownCodeBlockProcessor("simple-time-tracker", (s, e, i) => { this.registerMarkdownCodeBlockProcessor("simple-time-tracker", (s, e, i) => {
let tracker = loadTracker(s); let tracker: Tracker = loadTracker(s);
e.empty(); e.empty();
displayTracker(tracker, e, () => i.getSectionInfo(e), this.settings); displayTracker(tracker, e, () => i.getSectionInfo(e), this.settings);
}); });
this.addCommand({ this.addCommand({
id: `insert`, id: `insert`,
name: `Insert Time Tracker`, name: `Insert Time Tracker`,
editorCallback: (e, _) => { editorCallback: (e, _) => {
e.replaceSelection("```simple-time-tracker\n```\n"); e.replaceSelection("```simple-time-tracker\n```\n");
} }
}); });
} }
async loadSettings() { async loadSettings(): Promise<void> {
this.settings = Object.assign({}, defaultSettings, await this.loadData()); this.settings = Object.assign({}, defaultSettings, await this.loadData());
} }
async saveSettings() { async saveSettings(): Promise<void> {
await this.saveData(this.settings); await this.saveData(this.settings);
} }
} }

View file

@ -13,14 +13,14 @@ export class SimpleTimeTrackerSettingsTab extends PluginSettingTab {
display(): void { display(): void {
this.containerEl.empty(); this.containerEl.empty();
this.containerEl.createEl("h2", { text: "Super Simple Time Tracker Settings" }); this.containerEl.createEl("h2", {text: "Super Simple Time Tracker Settings"});
new Setting(this.containerEl) new Setting(this.containerEl)
.setName("Timestamp Display Format") .setName("Timestamp Display Format")
.setDesc(createFragment(f => { .setDesc(createFragment(f => {
f.createSpan({ text: "The way that timestamps in time tracker tables should be displayed. Uses " }); f.createSpan({text: "The way that timestamps in time tracker tables should be displayed. Uses "});
f.createEl("a", { text: "moment.js", href: "https://momentjs.com/docs/#/parsing/string-format/" }); f.createEl("a", {text: "moment.js", href: "https://momentjs.com/docs/#/parsing/string-format/"});
f.createSpan({ text: " syntax." }); f.createSpan({text: " syntax."});
})) }))
.addText(t => { .addText(t => {
t.setValue(String(this.plugin.settings.timestampFormat)); t.setValue(String(this.plugin.settings.timestampFormat));
@ -42,8 +42,11 @@ export class SimpleTimeTrackerSettingsTab extends PluginSettingTab {
}); });
this.containerEl.createEl("hr"); this.containerEl.createEl("hr");
this.containerEl.createEl("p", { text: "If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!" }); this.containerEl.createEl("p", {text: "If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!"});
this.containerEl.createEl("a", { href: "https://ellpeck.de/support" }) this.containerEl.createEl("a", {href: "https://ellpeck.de/support"})
.createEl("img", { attr: { src: "https://ellpeck.de/res/generalsupport.png" }, cls: "simple-time-tracker-support" }); .createEl("img", {
attr: {src: "https://ellpeck.de/res/generalsupport.png"},
cls: "simple-time-tracker-support"
});
} }
} }

View file

@ -36,7 +36,7 @@ export function loadTracker(json: string): Tracker {
console.log(`Failed to parse Tracker from ${json}`); console.log(`Failed to parse Tracker from ${json}`);
} }
} }
return { entries: [] }; return {entries: []};
} }
export function displayTracker(tracker: Tracker, element: HTMLElement, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings): void { export function displayTracker(tracker: Tracker, element: HTMLElement, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings): void {
@ -61,29 +61,29 @@ export function displayTracker(tracker: Tracker, element: HTMLElement, getSectio
newSegmentNameBox.inputEl.addClass("simple-time-tracker-txt"); newSegmentNameBox.inputEl.addClass("simple-time-tracker-txt");
// add timers // add timers
let timer = element.createDiv({ cls: "simple-time-tracker-timers" }); let timer = element.createDiv({cls: "simple-time-tracker-timers"});
let currentDiv = timer.createEl("div", { cls: "simple-time-tracker-timer" }); let currentDiv = timer.createEl("div", {cls: "simple-time-tracker-timer"});
let current = currentDiv.createEl("span", { cls: "simple-time-tracker-timer-time" }); let current = currentDiv.createEl("span", {cls: "simple-time-tracker-timer-time"});
currentDiv.createEl("span", { text: "Current" }); currentDiv.createEl("span", {text: "Current"});
let totalDiv = timer.createEl("div", { cls: "simple-time-tracker-timer" }); let totalDiv = timer.createEl("div", {cls: "simple-time-tracker-timer"});
let total = totalDiv.createEl("span", { cls: "simple-time-tracker-timer-time", text: "0s" }); let total = totalDiv.createEl("span", {cls: "simple-time-tracker-timer-time", text: "0s"});
totalDiv.createEl("span", { text: "Total" }); totalDiv.createEl("span", {text: "Total"});
if (tracker.entries.length > 0) { if (tracker.entries.length > 0) {
// add table // add table
let table = element.createEl("table", { cls: "simple-time-tracker-table" }); let table = element.createEl("table", {cls: "simple-time-tracker-table"});
table.createEl("tr").append( table.createEl("tr").append(
createEl("th", { text: "Segment" }), createEl("th", {text: "Segment"}),
createEl("th", { text: "Start time" }), createEl("th", {text: "Start time"}),
createEl("th", { text: "End time" }), createEl("th", {text: "End time"}),
createEl("th", { text: "Duration" }), createEl("th", {text: "Duration"}),
createEl("th")); createEl("th"));
for (let entry of tracker.entries) for (let entry of tracker.entries)
addEditableTableRow(tracker, entry, table, newSegmentNameBox, running, getSectionInfo, settings, 0); addEditableTableRow(tracker, entry, table, newSegmentNameBox, running, getSectionInfo, settings, 0);
// add copy buttons // add copy buttons
let buttons = element.createEl("div", { cls: "simple-time-tracker-bottom" }); let buttons = element.createEl("div", {cls: "simple-time-tracker-bottom"});
new ButtonComponent(buttons) new ButtonComponent(buttons)
.setButtonText("Copy as table") .setButtonText("Copy as table")
.onClick(() => navigator.clipboard.writeText(createMarkdownTable(tracker, settings))); .onClick(() => navigator.clipboard.writeText(createMarkdownTable(tracker, settings)));
@ -107,22 +107,22 @@ export function displayTracker(tracker: Tracker, element: HTMLElement, getSectio
function startSubEntry(entry: Entry, name: string) { function startSubEntry(entry: Entry, name: string) {
// if this entry is not split yet, we add its time as a sub-entry instead // if this entry is not split yet, we add its time as a sub-entry instead
if (!entry.subEntries) { if (!entry.subEntries) {
entry.subEntries = [{ ...entry, name: `Part 1` }]; entry.subEntries = [{...entry, name: `Part 1`}];
entry.startTime = null; entry.startTime = null;
entry.endTime = null; entry.endTime = null;
} }
if (!name) if (!name)
name = `Part ${entry.subEntries.length + 1}`; name = `Part ${entry.subEntries.length + 1}`;
entry.subEntries.push({ name: name, startTime: moment().unix(), endTime: null, subEntries: null }); entry.subEntries.push({name: name, startTime: moment().unix(), endTime: null, subEntries: null});
} }
function startNewEntry(tracker: Tracker, name: string): void { function startNewEntry(tracker: Tracker, name: string): void {
if (!name) if (!name)
name = `Segment ${tracker.entries.length + 1}`; name = `Segment ${tracker.entries.length + 1}`;
let entry: Entry = { name: name, startTime: moment().unix(), endTime: null, subEntries: null }; let entry: Entry = {name: name, startTime: moment().unix(), endTime: null, subEntries: null};
tracker.entries.push(entry); tracker.entries.push(entry);
}; }
function endRunningEntry(tracker: Tracker): void { function endRunningEntry(tracker: Tracker): void {
let entry = getRunningEntry(tracker.entries); let entry = getRunningEntry(tracker.entries);
@ -260,14 +260,14 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
let row = table.createEl("tr"); let row = table.createEl("tr");
let name = row.createEl("td"); let name = row.createEl("td");
let namePar = name.createEl("span", { text: entry.name }); let namePar = name.createEl("span", {text: entry.name});
namePar.style.marginLeft = `${indent}em`; namePar.style.marginLeft = `${indent}em`;
let nameBox = new TextComponent(name).setValue(entry.name); let nameBox = new TextComponent(name).setValue(entry.name);
nameBox.inputEl.hidden = true; nameBox.inputEl.hidden = true;
row.createEl("td", { text: entry.startTime ? formatTimestamp(entry.startTime, settings) : "" }); row.createEl("td", {text: entry.startTime ? formatTimestamp(entry.startTime, settings) : ""});
row.createEl("td", { text: entry.endTime ? formatTimestamp(entry.endTime, settings) : "" }); row.createEl("td", {text: entry.endTime ? formatTimestamp(entry.endTime, settings) : ""});
row.createEl("td", { text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry)) : "" }); row.createEl("td", {text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry)) : ""});
let entryButtons = row.createEl("td"); let entryButtons = row.createEl("td");
if (!running) { if (!running) {

View file

@ -1,64 +1,64 @@
.simple-time-tracker-support { .simple-time-tracker-support {
max-width: 50%; max-width: 50%;
width: 400px; width: 400px;
height: auto; height: auto;
} }
.simple-time-tracker-btn, .simple-time-tracker-btn,
.simple-time-tracker-txt { .simple-time-tracker-txt {
display: block; display: block;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.simple-time-tracker-txt { .simple-time-tracker-txt {
text-align: center; text-align: center;
} }
.simple-time-tracker-btn { .simple-time-tracker-btn {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.simple-time-tracker-btn svg { .simple-time-tracker-btn svg {
width: 32px; width: 32px;
height: 32px; height: 32px;
} }
.simple-time-tracker-bottom button { .simple-time-tracker-bottom button {
margin: 10px 5px 10px 5px; margin: 10px 5px 10px 5px;
} }
.simple-time-tracker-timers, .simple-time-tracker-timers,
.simple-time-tracker-bottom { .simple-time-tracker-bottom {
display: flex; display: flex;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
} }
.simple-time-tracker-timers span { .simple-time-tracker-timers span {
display: block; display: block;
} }
.simple-time-tracker-timer { .simple-time-tracker-timer {
margin: 20px; margin: 20px;
} }
.simple-time-tracker-timer-time { .simple-time-tracker-timer-time {
font-size: xx-large; font-size: xx-large;
font-weight: bolder; font-weight: bolder;
} }
.simple-time-tracker-table { .simple-time-tracker-table {
width: 100%; width: 100%;
margin-top: 20px; margin-top: 20px;
} }
.simple-time-tracker-table td, .simple-time-tracker-table td,
.simple-time-tracker-table th { .simple-time-tracker-table th {
border: none; border: none;
} }
.simple-time-tracker-table .clickable-icon { .simple-time-tracker-table .clickable-icon {
display: inline; display: inline;
} }

View file

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

View file

@ -1,9 +1,9 @@
{ {
"0.0.1": "0.15.0", "0.0.1": "0.15.0",
"0.1.0": "0.15.0", "0.1.0": "0.15.0",
"0.1.1": "0.15.0", "0.1.1": "0.15.0",
"0.1.2": "0.15.0", "0.1.2": "0.15.0",
"0.1.3": "0.15.0", "0.1.3": "0.15.0",
"0.1.4": "0.15.0", "0.1.4": "0.15.0",
"0.1.5": "0.15.0" "0.1.5": "0.15.0"
} }