From 32d3212395af24b397cda1303d5cf7b874851a90 Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Sat, 28 Mar 2026 17:27:29 +1300 Subject: [PATCH] feat: status bar items --- src/components/timesheetRowDuration.ts | 5 ++ src/components/timesheetStatusBarItem.ts | 74 ++++++++++++++++++ src/main.ts | 7 ++ src/service/registry.ts | 2 +- src/settings-tab.ts | 21 ++++++ src/settings.ts | 4 + src/styles.css | 13 ++++ src/views/timekeep-status-bar-view.ts | 95 ++++++++++++++++++++++++ test-vault/Obsidian Timekeep.md | 34 ++++++++- 9 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 src/components/timesheetStatusBarItem.ts create mode 100644 src/views/timekeep-status-bar-view.ts diff --git a/src/components/timesheetRowDuration.ts b/src/components/timesheetRowDuration.ts index 0675f0d..da97221 100644 --- a/src/components/timesheetRowDuration.ts +++ b/src/components/timesheetRowDuration.ts @@ -54,6 +54,11 @@ export class TimesheetRowDurationComponent extends Component { } } + onunload(): void { + super.onunload(); + this.#timeEl?.remove(); + } + /** * Updates the current time duration value based * on the current time diff --git a/src/components/timesheetStatusBarItem.ts b/src/components/timesheetStatusBarItem.ts new file mode 100644 index 0000000..2ddde56 --- /dev/null +++ b/src/components/timesheetStatusBarItem.ts @@ -0,0 +1,74 @@ +import { Component } from "obsidian"; + +import { TimeEntry } from "@/timekeep/schema"; + +import { TimesheetRowDurationComponent } from "./timesheetRowDuration"; + +export class TimesheetStatusBarItem extends Component { + /** Parent container element */ + #containerEl: HTMLElement; + + /** The entry this duration belongs to */ + entry: TimeEntry; + + /** The time span element */ + #wrapperEl: HTMLSpanElement | undefined; + + /** Currently tracked background interval for content */ + currentContentInterval: number | undefined; + + /** Component for rendering the real time updating duration */ + duration: TimesheetRowDurationComponent; + + /** Callback to open the file when the content is clicked */ + onOpen: VoidFunction; + /** Callback to stop the timekeep item */ + onStop: VoidFunction; + + constructor( + containerEl: HTMLElement, + entry: TimeEntry, + onOpen: VoidFunction, + onStop: VoidFunction + ) { + super(); + + this.#containerEl = containerEl; + this.entry = entry; + + this.onOpen = onOpen; + this.onStop = onStop; + } + + onload(): void { + super.onload(); + + const entry = this.entry; + const wrapperEl = this.#containerEl.createDiv({ cls: "timekeep-status-item" }); + this.#wrapperEl = wrapperEl; + + // const stopButton = wrapperEl.createEl("button", { + // cls: "timekeep-status-item__button", + // }); + // this.registerDomEvent(stopButton, "click", this.onStop); + + const contentEl = wrapperEl.createDiv({ + cls: "timekeep-status-item__content", + title: "Open File", + }); + + contentEl.createSpan({ + cls: "timekeep-status-item__name", + text: entry.name + ":", + }); + + this.addChild(new TimesheetRowDurationComponent(contentEl, entry)); + + this.registerDomEvent(contentEl, "click", this.onOpen); + } + + onunload(): void { + super.onunload(); + this.#wrapperEl?.remove(); + } +} diff --git a/src/main.ts b/src/main.ts index a72aa2b..f0880a3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,12 +23,14 @@ import { load, replaceTimekeepCodeblock, extractTimekeepCodeblocks } from "@/tim import { stopAllTimekeeps } from "./commands/stopAllTimekeeps"; import { stopFileTimekeeps } from "./commands/stopFileTimekeeps"; +import { TimesheetStatusBarItem } from "./components/timesheetStatusBarItem"; import { CustomOutputFormat } from "./output"; import { TimekeepRegistry } from "./service/registry"; import { Timekeep, TimeEntry } from "./timekeep/schema"; import { TimekeepLocatorModal } from "./views/timekeep-locator-modal"; import { TimekeepMarkdownView } from "./views/timekeep-markdown-view"; import { TimekeepMergerModal } from "./views/timekeep-merger-modal"; +import { TimekeepStatusBarView } from "./views/timekeep-status-bar-view"; export default class TimekeepPlugin extends Plugin { settingsStore: Store; @@ -105,6 +107,11 @@ export default class TimekeepPlugin extends Plugin { this.registry = new TimekeepRegistry(this.app.vault); this.registry.concurrencyLimit = settings.registryConcurrencyLimit; this.registry.onload(); + + if (settings.statusBarEnabled) { + const containerEl = this.addStatusBarItem(); + new TimekeepStatusBarView(containerEl, this.app, this.registry, this.settingsStore); + } }); this.registerMarkdownCodeBlockProcessor( diff --git a/src/service/registry.ts b/src/service/registry.ts index 960116d..879690f 100644 --- a/src/service/registry.ts +++ b/src/service/registry.ts @@ -7,7 +7,7 @@ import { type TimekeepWithPosition, } from "@/timekeep/parser"; -type TimekeepRegistryEntry = { +export type TimekeepRegistryEntry = { file: TFile; timekeeps: TimekeepWithPosition[]; }; diff --git a/src/settings-tab.ts b/src/settings-tab.ts index 4a3fa93..7633173 100644 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -419,5 +419,26 @@ export class TimekeepSettingsTab extends PluginSettingTab { })); }); }); + + // Status bar section + new Setting(this.containerEl) + .setName("Status Bar") + .setDesc( + "Timekeep can show status bar entries for running timers within your vault. This requires that the registry option above is enabled" + ) + .setHeading(); + + new Setting(this.containerEl) + .setName("Enabled") + .setDesc("Whether to enable status bar entries.") + .addToggle((t) => { + t.setValue(settings.statusBarEnabled); + t.onChange((v) => { + this.settingsStore.setState((currentValue) => ({ + ...currentValue, + statusBarEnabled: v, + })); + }); + }); } } diff --git a/src/settings.ts b/src/settings.ts index 9f01e98..52df1c9 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -72,6 +72,8 @@ export interface TimekeepSettings { registryEnabled: boolean; registryConcurrencyLimit: number; + + statusBarEnabled: boolean; } export const defaultSettings: TimekeepSettings = { @@ -98,6 +100,8 @@ export const defaultSettings: TimekeepSettings = { registryEnabled: true, registryConcurrencyLimit: 15, + + statusBarEnabled: true, }; export function legacySettingsCompatibility(settings: TimekeepSettings): void { diff --git a/src/styles.css b/src/styles.css index f89b2d2..f8307ec 100644 --- a/src/styles.css +++ b/src/styles.css @@ -286,3 +286,16 @@ width: 100%; margin-bottom: 1rem; } + +.timekeep-status-item { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.timekeep-status-item__content { + display: flex; + align-items: center; + gap: 0.25rem; + cursor: pointer; +} diff --git a/src/views/timekeep-status-bar-view.ts b/src/views/timekeep-status-bar-view.ts new file mode 100644 index 0000000..bc37ed6 --- /dev/null +++ b/src/views/timekeep-status-bar-view.ts @@ -0,0 +1,95 @@ +import { App, MarkdownView } from "obsidian"; + +import { TimesheetStatusBarItem } from "@/components/timesheetStatusBarItem"; +import { TimekeepRegistry, TimekeepRegistryEntry } from "@/service/registry"; +import { TimekeepSettings } from "@/settings"; +import { Store } from "@/store"; +import { getRunningEntry } from "@/timekeep"; +import { TimekeepWithPosition } from "@/timekeep/parser"; +import { TimeEntry } from "@/timekeep/schema"; + +export class TimekeepStatusBarView { + /** Parent container element */ + #containerEl: HTMLElement; + + /** Access to the app instance */ + app: App; + /** Access to the app registry */ + registry: TimekeepRegistry; + /** Access to the timekeep settings */ + settings: Store; + + /** Currently rendered items */ + items: TimesheetStatusBarItem[] = []; + + constructor( + containerEl: HTMLElement, + app: App, + registry: TimekeepRegistry, + settings: Store + ) { + this.#containerEl = containerEl; + + this.app = app; + this.registry = registry; + this.settings = settings; + + // Render the initial state + this.render(this.registry.entries.getState()); + + // Subscribe to changes to re-render + this.registry.entries.subscribe(() => { + this.render(this.registry.entries.getState()); + }); + } + + render(entries: TimekeepRegistryEntry[]) { + // Unload the current children + for (const item of this.items) { + item.unload(); + } + + // Load the new children + for (const entry of entries) { + for (const timekeep of entry.timekeeps) { + const runningEntry = getRunningEntry(timekeep.timekeep.entries); + if (runningEntry === null) continue; + + const item = new TimesheetStatusBarItem( + this.#containerEl, + runningEntry, + () => { + void this.onOpen(entry, timekeep); + }, + () => { + this.onStop(entry, timekeep, runningEntry); + } + ); + + this.items.push(item); + item.load(); + } + } + } + + onStop(_entry: TimekeepRegistryEntry, _timekeep: TimekeepWithPosition, _timeEntry: TimeEntry) { + // TODO: For future implementation, ...being able to stop entries + } + + async onOpen(entry: TimekeepRegistryEntry, timekeep: TimekeepWithPosition) { + const leaf = this.app.workspace.getLeaf(); + await leaf.openFile(entry.file); + + const view = leaf.view; + + if (view instanceof MarkdownView) { + const editor = view.editor; + + const line = timekeep.startLine; + + // Focus the line we opened to + editor.setCursor({ line: Math.max(line - 1, 0), ch: 0 }); + editor.scrollIntoView({ from: { line, ch: 0 }, to: { line, ch: 0 } }, true); + } + } +} diff --git a/test-vault/Obsidian Timekeep.md b/test-vault/Obsidian Timekeep.md index 66a4d37..01a6689 100644 --- a/test-vault/Obsidian Timekeep.md +++ b/test-vault/Obsidian Timekeep.md @@ -26,11 +26,41 @@ dv.span(totalRunningDuration); ``` ```timekeep -{"entries":[{"name":"Block 4 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-17T01:33:51.000Z","endTime":"2024-09-17T07:50:55.000Z","subEntries":null},{"name":"Part 2","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-24T05:20:54.000Z","endTime":"2024-03-24T05:20:55.000Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T22:33:49.279Z","endTime":"2026-03-25T23:04:57.335Z","subEntries":null}]}]},{"name":"Part 2","startTime":"2024-03-24T05:20:51.498Z","endTime":"2024-03-24T05:20:53.370Z","subEntries":null},{"name":"Part 3","startTime":"2024-03-24T05:20:56.000Z","endTime":"2024-03-24T05:21:30.000Z","subEntries":null}]},{"name":"Block 5","startTime":"2024-03-17T01:33:57.375Z","endTime":"2024-03-17T01:33:58.867Z","subEntries":null},{"name":"Block 8","startTime":"2024-03-17T01:37:33.000Z","endTime":"2024-03-17T06:37:35.000Z","subEntries":null},{"name":"Block 9","startTime":"2024-03-17T01:37:44.470Z","endTime":"2024-03-17T01:37:45.141Z","subEntries":null},{"name":"Block 6","startTime":"2024-03-17T02:00:38.491Z","endTime":"2024-03-17T02:00:39.208Z","subEntries":null},{"name":"Block 7","startTime":"2024-03-17T02:00:39.528Z","endTime":"2024-03-17T02:00:39.823Z","subEntries":null},{"name":"Block 8","startTime":"2024-03-17T02:00:40.118Z","endTime":"2024-03-17T02:00:40.428Z","subEntries":null},{"name":"Block 9","startTime":"2024-03-17T02:00:40.814Z","endTime":"2024-03-17T02:00:41.066Z","subEntries":null},{"name":"Block 10","startTime":"2024-03-17T02:00:41.327Z","endTime":"2024-03-17T02:00:41.507Z","subEntries":null},{"name":"Block 11","startTime":"2024-03-17T02:00:41.783Z","endTime":"2024-03-17T02:00:42.009Z","subEntries":null},{"name":"Block 13","startTime":"2024-03-17T02:57:16.000Z","endTime":"2024-03-17T02:57:16.000Z","subEntries":null},{"name":"Block 14","startTime":"2024-03-17T02:57:19.000Z","endTime":"2024-03-17T02:57:19.000Z","subEntries":null},{"name":"Block 15","startTime":"2024-03-17T02:57:24.000Z","endTime":"2024-03-17T02:57:24.000Z","subEntries":null},{"name":"Block 16 Supper reallly long name that leaves the table bounds","startTime":"2024-03-17T02:57:25.000Z","endTime":"2024-03-17T02:57:25.000Z","subEntries":null},{"name":"Block 17","startTime":"2024-03-17T03:29:53.766Z","endTime":"2024-03-17T03:29:54.925Z","subEntries":null},{"name":"Block 18","startTime":"2024-03-17T03:30:26.368Z","endTime":"2024-03-17T03:30:34.257Z","subEntries":null},{"name":"New Block","startTime":"2024-03-17T03:32:42.451Z","endTime":"2024-03-17T03:33:41.318Z","subEntries":null},{"name":"Block 20","startTime":"2024-03-17T03:37:45.552Z","endTime":"2024-03-17T03:37:47.568Z","subEntries":null},{"name":"Block 21","startTime":"2024-03-17T03:38:14.405Z","endTime":"2024-03-17T03:38:18.039Z","subEntries":null},{"name":"Block 21","startTime":"2024-03-17T05:01:30.553Z","endTime":"2024-03-17T05:01:36.012Z","subEntries":null},{"name":"Block 22","startTime":"2024-03-17T05:01:37.365Z","endTime":"2024-03-17T05:01:40.180Z","subEntries":null},{"name":"Block 23","startTime":"2024-03-24T03:44:16.500Z","endTime":"2024-03-24T03:44:18.168Z","subEntries":null},{"name":"Block 24","startTime":"2024-03-24T03:44:34.125Z","endTime":"2024-03-24T03:44:35.421Z","subEntries":null},{"name":"Block 25","startTime":"2024-03-24T03:44:38.073Z","endTime":"2024-03-24T03:44:50.001Z","subEntries":null},{"name":"Block 26","startTime":"2024-03-24T04:05:50.912Z","endTime":"2024-03-24T04:05:53.243Z","subEntries":null},{"name":"This is my block","startTime":"2024-03-24T04:09:02.146Z","endTime":"2024-03-24T04:09:06.662Z","subEntries":null},{"name":"Block 28","startTime":"2024-03-24T04:15:47.980Z","endTime":"2024-03-24T04:15:48.924Z","subEntries":null},{"name":"Block 29","startTime":"2024-03-24T04:15:49.518Z","endTime":"2024-03-24T04:15:50.561Z","subEntries":null},{"name":"Block 30","startTime":"2024-03-24T04:15:51.654Z","endTime":"2024-03-24T04:15:52.560Z","subEntries":null},{"name":"Block 31","startTime":"2024-03-24T04:17:34.822Z","endTime":"2024-03-24T04:17:35.571Z","subEntries":null},{"name":"Block 32","startTime":"2024-03-24T04:17:36.303Z","endTime":"2024-03-24T04:17:36.936Z","subEntries":null},{"name":"Block 33","startTime":"2024-03-24T04:17:43.413Z","endTime":"2024-03-24T04:17:44.142Z","subEntries":null},{"name":"Block 34","startTime":"2024-03-24T04:17:44.916Z","endTime":"2024-03-24T04:17:45.552Z","subEntries":null},{"name":"Block 35","startTime":"2024-03-24T04:17:52.315Z","endTime":"2024-03-24T04:17:52.971Z","subEntries":null},{"name":"Block 36","startTime":"2024-03-24T04:17:53.590Z","endTime":"2024-03-24T04:17:54.318Z","subEntries":null},{"name":"Block 37","startTime":"2024-03-24T04:17:54.858Z","endTime":"2024-03-24T04:17:56.340Z","subEntries":null},{"name":"Block 38","startTime":"2024-03-24T04:21:19.540Z","endTime":"2024-03-24T04:21:20.803Z","subEntries":null},{"name":"Block 39","startTime":"2024-03-24T04:21:21.876Z","endTime":"2024-03-24T04:21:24.420Z","subEntries":null},{"name":"Block 40","startTime":"2024-03-24T04:22:14.073Z","endTime":"2024-03-24T04:22:14.793Z","subEntries":null},{"name":"Block 41","startTime":"2024-03-24T04:22:15.463Z","endTime":"2024-03-24T04:22:16.016Z","subEntries":null},{"name":"Block 42","startTime":"2024-03-24T04:28:14.807Z","endTime":"2024-03-24T04:28:15.565Z","subEntries":null},{"name":"Block 43","startTime":"2024-03-24T04:28:39.885Z","endTime":"2024-03-24T04:28:42.912Z","subEntries":null},{"name":"Block 44","startTime":"2024-03-24T04:28:44.495Z","endTime":"2024-03-24T04:28:45.736Z","subEntries":null},{"name":"Block 44","startTime":"2024-03-24T04:30:43.184Z","endTime":"2024-03-24T04:30:45.307Z","subEntries":null},{"name":"Block 45","startTime":"2024-03-24T04:30:46.565Z","endTime":"2024-03-24T04:30:47.749Z","subEntries":null},{"name":"Block 46","startTime":"2024-03-24T04:30:48.502Z","endTime":"2024-03-24T04:30:49.762Z","subEntries":null},{"name":"Block 47","startTime":"2024-03-24T04:31:01.261Z","endTime":"2024-03-24T04:31:12.831Z","subEntries":null},{"name":"Block 48","startTime":"2024-03-24T04:31:14.603Z","endTime":"2024-03-24T04:31:16.328Z","subEntries":null},{"name":"Block 49","startTime":"2024-03-24T04:31:17.249Z","endTime":"2024-03-24T04:31:18.115Z","subEntries":null},{"name":"Block 50","startTime":"2024-03-24T04:31:19.266Z","endTime":"2024-03-24T04:31:20.199Z","subEntries":null},{"name":"Block 51","startTime":"2024-03-24T04:33:17.046Z","endTime":"2024-03-24T04:33:17.646Z","subEntries":null},{"name":"Block 52","startTime":"2024-03-24T04:33:18.253Z","endTime":"2024-03-24T04:33:18.969Z","subEntries":null},{"name":"Block 53","startTime":"2024-03-24T04:33:19.684Z","endTime":"2024-03-24T04:33:21.706Z","subEntries":null},{"name":"Block 54","startTime":"2024-03-24T04:33:22.293Z","endTime":"2024-03-24T04:33:22.674Z","subEntries":null},{"name":"Block 55","startTime":"2024-03-24T06:35:53.785Z","endTime":"2024-03-24T06:35:54.322Z","subEntries":null},{"name":"Block 56","startTime":"2024-03-24T06:35:54.693Z","endTime":"2024-03-24T06:35:54.864Z","subEntries":null},{"name":"Block 57","startTime":"2024-03-24T06:35:55.031Z","endTime":"2024-03-24T06:35:55.190Z","subEntries":null},{"name":"Block 58","startTime":"2024-03-24T06:35:55.631Z","endTime":"2024-03-24T06:35:57.836Z","subEntries":null},{"name":"Block 59","startTime":"2024-03-24T06:36:06.400Z","endTime":"2024-03-24T06:36:08.452Z","subEntries":null},{"name":"Block 60","startTime":"2024-03-24T06:36:53.108Z","endTime":"2024-03-24T06:36:53.791Z","subEntries":null},{"name":"Block 61","startTime":"2024-03-24T06:42:04.593Z","endTime":"2024-03-24T06:42:05.961Z","subEntries":null},{"name":"Block 62","startTime":"2024-03-24T06:42:30.789Z","endTime":"2024-03-24T06:42:31.609Z","subEntries":null},{"name":"Block 63","startTime":"2024-05-01T08:17:37.721Z","endTime":"2024-05-01T08:20:49.070Z","subEntries":null},{"name":"Test dwada uidawh dawhiud hawhiduia uhiwdiuhhauiw dhawd ihuawuidh awudhiu ahiwudhiu ahwiudihu awhiudhiu hiud ihuawhid hiuahiud ihwad","startTime":"2024-05-01T08:20:56.478Z","endTime":"2024-05-01T08:21:01.643Z","subEntries":null},{"name":"Test","startTime":"2024-05-01T08:21:03.005Z","endTime":"2024-05-01T08:21:35.567Z","subEntries":null},{"name":"tEWADAWDAWDAWD","startTime":"2024-05-01T08:21:37.838Z","endTime":"2024-05-01T08:22:25.344Z","subEntries":null},{"name":"Tewadwdawd","startTime":"2024-05-01T08:22:27.180Z","endTime":"2024-05-01T08:22:28.683Z","subEntries":null},{"name":"awdawd","startTime":"2024-05-01T08:22:30.186Z","endTime":"2024-05-01T08:22:36.241Z","subEntries":null},{"name":"Block 69","startTime":"2024-05-01T08:22:36.760Z","endTime":"2024-05-01T08:22:37.361Z","subEntries":null},{"name":"Block 70","startTime":"2024-05-01T08:22:37.695Z","endTime":"2024-05-01T08:22:38.428Z","subEntries":null},{"name":"Block 71","startTime":"2024-05-01T08:22:39.077Z","endTime":"2024-05-01T08:22:40.410Z","subEntries":null},{"name":"Block 72","startTime":"2024-05-01T08:22:40.991Z","endTime":"2024-05-01T08:22:41.538Z","subEntries":null},{"name":"Block 73","startTime":"2024-05-01T08:22:41.943Z","endTime":"2024-05-01T08:22:42.729Z","subEntries":null},{"name":"Block 74","startTime":"2024-05-01T08:22:43.160Z","endTime":"2024-05-01T08:22:44.806Z","subEntries":null},{"name":"Block 75","startTime":"2024-05-01T08:22:45.423Z","endTime":"2024-05-01T08:22:46.040Z","subEntries":null},{"name":"Block 76","startTime":"2024-05-01T08:22:49.574Z","endTime":"2024-05-01T08:22:50.712Z","subEntries":null},{"name":"Block 77","startTime":"2024-05-01T08:22:51.298Z","endTime":"2024-05-01T08:23:06.033Z","subEntries":null},{"name":"Block 78","startTime":"2024-05-01T08:23:07.620Z","endTime":"2024-05-01T08:23:08.124Z","subEntries":null},{"name":"Block 79","startTime":"2024-05-01T08:23:09.529Z","endTime":"2024-05-01T08:23:09.984Z","subEntries":null},{"name":"Block 80","startTime":"2024-05-01T08:23:44.681Z","endTime":"2024-05-01T08:23:56.772Z","subEntries":null},{"name":"Block 82","startTime":null,"endTime":null,"collapsed":true,"subEntries":[{"name":"Part 1","startTime":"2024-07-21T07:12:56.433Z","endTime":"2024-07-21T07:13:33.907Z","subEntries":null},{"name":"Part 2","startTime":"2024-07-21T07:24:44.769Z","endTime":"2024-07-21T07:24:48.149Z","subEntries":null},{"name":"Part 3","startTime":"2024-07-21T09:06:54.527Z","endTime":"2024-07-21T09:06:57.360Z","subEntries":null},{"name":"Part 4","startTime":"2024-07-21T09:07:10.820Z","endTime":"2024-07-21T09:07:11.426Z","subEntries":null}]},{"name":"Block 82","startTime":null,"endTime":null,"collapsed":true,"subEntries":[{"name":"Part 1","startTime":"2024-07-21T07:13:34.743Z","endTime":"2024-07-21T07:14:13.095Z","subEntries":null},{"name":"Part 2","startTime":"2024-07-21T07:14:13.562Z","endTime":"2024-07-21T07:14:14.679Z","subEntries":null},{"name":"Part 3","startTime":"2024-07-21T07:14:15.566Z","endTime":"2024-07-21T07:14:15.970Z","subEntries":null},{"name":"Part 4","startTime":"2024-07-21T09:06:52.510Z","endTime":"2024-07-21T09:06:53.351Z","subEntries":null},{"name":"Part 5","startTime":"2024-07-21T09:07:19.089Z","endTime":"2024-07-21T09:07:20.066Z","subEntries":null}]},{"name":"Block 83","startTime":"2024-07-21T09:07:21.923Z","endTime":"2024-07-21T09:07:22.516Z","subEntries":null},{"name":"Block 81","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2025-01-07T02:20:00.000Z","endTime":"2025-01-07T03:20:00.000Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T23:04:57.335Z","endTime":"2026-03-25T23:05:00.051Z","subEntries":null}]},{"name":"Block 85 [[#Test]] ","startTime":"2025-01-07T04:18:19.000Z","endTime":"2025-01-07T03:18:20.000Z","subEntries":null},{"name":"Block 86","startTime":"2026-03-25T20:20:44.814Z","endTime":"2026-03-25T20:53:38.364Z","subEntries":null},{"name":"Block 87","startTime":"2026-03-25T22:20:11.034Z","endTime":"2026-03-25T22:20:13.254Z","subEntries":null},{"name":"Block 88","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2026-03-25T22:20:13.921Z","endTime":"2026-03-25T22:33:49.279Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T23:05:33.696Z","endTime":"2026-03-25T23:05:35.361Z","subEntries":null}]},{"name":"Part 2","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2026-03-25T23:05:12.433Z","endTime":"2026-03-25T23:05:14.427Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T23:05:18.672Z","endTime":"2026-03-25T23:05:22.812Z","subEntries":null}]},{"name":"Part 3","startTime":"2026-03-25T23:09:44.281Z","endTime":"2026-03-25T23:09:46.096Z","subEntries":null}]},{"name":"Part 2","startTime":"2026-03-25T23:05:10.205Z","endTime":"2026-03-25T23:05:11.240Z","subEntries":null},{"name":"Part 3","startTime":"2026-03-25T23:05:15.995Z","endTime":"2026-03-25T23:05:17.046Z","subEntries":null}]}]} +{"entries":[{"name":"Block 4 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-17T01:33:51.000Z","endTime":"2024-09-17T07:50:55.000Z","subEntries":null},{"name":"Part 2","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-24T05:20:54.000Z","endTime":"2024-03-24T05:20:55.000Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T22:33:49.279Z","endTime":"2026-03-25T23:04:57.335Z","subEntries":null}]}]},{"name":"Part 2","startTime":"2024-03-24T05:20:51.498Z","endTime":"2024-03-24T05:20:53.370Z","subEntries":null},{"name":"Part 3","startTime":"2024-03-24T05:20:56.000Z","endTime":"2024-03-24T05:21:30.000Z","subEntries":null}]},{"name":"Block 5","startTime":"2024-03-17T01:33:57.375Z","endTime":"2024-03-17T01:33:58.867Z","subEntries":null},{"name":"Block 8","startTime":"2024-03-17T01:37:33.000Z","endTime":"2024-03-17T06:37:35.000Z","subEntries":null},{"name":"Block 9","startTime":"2024-03-17T01:37:44.470Z","endTime":"2024-03-17T01:37:45.141Z","subEntries":null},{"name":"Block 6","startTime":"2024-03-17T02:00:38.491Z","endTime":"2024-03-17T02:00:39.208Z","subEntries":null},{"name":"Block 7","startTime":"2024-03-17T02:00:39.528Z","endTime":"2024-03-17T02:00:39.823Z","subEntries":null},{"name":"Block 8","startTime":"2024-03-17T02:00:40.118Z","endTime":"2024-03-17T02:00:40.428Z","subEntries":null},{"name":"Block 9","startTime":"2024-03-17T02:00:40.814Z","endTime":"2024-03-17T02:00:41.066Z","subEntries":null},{"name":"Block 10","startTime":"2024-03-17T02:00:41.327Z","endTime":"2024-03-17T02:00:41.507Z","subEntries":null},{"name":"Block 11","startTime":"2024-03-17T02:00:41.783Z","endTime":"2024-03-17T02:00:42.009Z","subEntries":null},{"name":"Block 13","startTime":"2024-03-17T02:57:16.000Z","endTime":"2024-03-17T02:57:16.000Z","subEntries":null},{"name":"Block 14","startTime":"2024-03-17T02:57:19.000Z","endTime":"2024-03-17T02:57:19.000Z","subEntries":null},{"name":"Block 15","startTime":"2024-03-17T02:57:24.000Z","endTime":"2024-03-17T02:57:24.000Z","subEntries":null},{"name":"Block 16 Supper reallly long name that leaves the table bounds","startTime":"2024-03-17T02:57:25.000Z","endTime":"2024-03-17T02:57:25.000Z","subEntries":null},{"name":"Block 17","startTime":"2024-03-17T03:29:53.766Z","endTime":"2024-03-17T03:29:54.925Z","subEntries":null},{"name":"Block 18","startTime":"2024-03-17T03:30:26.368Z","endTime":"2024-03-17T03:30:34.257Z","subEntries":null},{"name":"New Block","startTime":"2024-03-17T03:32:42.451Z","endTime":"2024-03-17T03:33:41.318Z","subEntries":null},{"name":"Block 20","startTime":"2024-03-17T03:37:45.552Z","endTime":"2024-03-17T03:37:47.568Z","subEntries":null},{"name":"Block 21","startTime":"2024-03-17T03:38:14.405Z","endTime":"2024-03-17T03:38:18.039Z","subEntries":null},{"name":"Block 21","startTime":"2024-03-17T05:01:30.553Z","endTime":"2024-03-17T05:01:36.012Z","subEntries":null},{"name":"Block 22","startTime":"2024-03-17T05:01:37.365Z","endTime":"2024-03-17T05:01:40.180Z","subEntries":null},{"name":"Block 23","startTime":"2024-03-24T03:44:16.500Z","endTime":"2024-03-24T03:44:18.168Z","subEntries":null},{"name":"Block 24","startTime":"2024-03-24T03:44:34.125Z","endTime":"2024-03-24T03:44:35.421Z","subEntries":null},{"name":"Block 25","startTime":"2024-03-24T03:44:38.073Z","endTime":"2024-03-24T03:44:50.001Z","subEntries":null},{"name":"Block 26","startTime":"2024-03-24T04:05:50.912Z","endTime":"2024-03-24T04:05:53.243Z","subEntries":null},{"name":"This is my block","startTime":"2024-03-24T04:09:02.146Z","endTime":"2024-03-24T04:09:06.662Z","subEntries":null},{"name":"Block 28","startTime":"2024-03-24T04:15:47.980Z","endTime":"2024-03-24T04:15:48.924Z","subEntries":null},{"name":"Block 29","startTime":"2024-03-24T04:15:49.518Z","endTime":"2024-03-24T04:15:50.561Z","subEntries":null},{"name":"Block 30","startTime":"2024-03-24T04:15:51.654Z","endTime":"2024-03-24T04:15:52.560Z","subEntries":null},{"name":"Block 31","startTime":"2024-03-24T04:17:34.822Z","endTime":"2024-03-24T04:17:35.571Z","subEntries":null},{"name":"Block 32","startTime":"2024-03-24T04:17:36.303Z","endTime":"2024-03-24T04:17:36.936Z","subEntries":null},{"name":"Block 33","startTime":"2024-03-24T04:17:43.413Z","endTime":"2024-03-24T04:17:44.142Z","subEntries":null},{"name":"Block 34","startTime":"2024-03-24T04:17:44.916Z","endTime":"2024-03-24T04:17:45.552Z","subEntries":null},{"name":"Block 35","startTime":"2024-03-24T04:17:52.315Z","endTime":"2024-03-24T04:17:52.971Z","subEntries":null},{"name":"Block 36","startTime":"2024-03-24T04:17:53.590Z","endTime":"2024-03-24T04:17:54.318Z","subEntries":null},{"name":"Block 37","startTime":"2024-03-24T04:17:54.858Z","endTime":"2024-03-24T04:17:56.340Z","subEntries":null},{"name":"Block 38","startTime":"2024-03-24T04:21:19.540Z","endTime":"2024-03-24T04:21:20.803Z","subEntries":null},{"name":"Block 39","startTime":"2024-03-24T04:21:21.876Z","endTime":"2024-03-24T04:21:24.420Z","subEntries":null},{"name":"Block 40","startTime":"2024-03-24T04:22:14.073Z","endTime":"2024-03-24T04:22:14.793Z","subEntries":null},{"name":"Block 41","startTime":"2024-03-24T04:22:15.463Z","endTime":"2024-03-24T04:22:16.016Z","subEntries":null},{"name":"Block 42","startTime":"2024-03-24T04:28:14.807Z","endTime":"2024-03-24T04:28:15.565Z","subEntries":null},{"name":"Block 43","startTime":"2024-03-24T04:28:39.885Z","endTime":"2024-03-24T04:28:42.912Z","subEntries":null},{"name":"Block 44","startTime":"2024-03-24T04:28:44.495Z","endTime":"2024-03-24T04:28:45.736Z","subEntries":null},{"name":"Block 44","startTime":"2024-03-24T04:30:43.184Z","endTime":"2024-03-24T04:30:45.307Z","subEntries":null},{"name":"Block 45","startTime":"2024-03-24T04:30:46.565Z","endTime":"2024-03-24T04:30:47.749Z","subEntries":null},{"name":"Block 46","startTime":"2024-03-24T04:30:48.502Z","endTime":"2024-03-24T04:30:49.762Z","subEntries":null},{"name":"Block 47","startTime":"2024-03-24T04:31:01.261Z","endTime":"2024-03-24T04:31:12.831Z","subEntries":null},{"name":"Block 48","startTime":"2024-03-24T04:31:14.603Z","endTime":"2024-03-24T04:31:16.328Z","subEntries":null},{"name":"Block 49","startTime":"2024-03-24T04:31:17.249Z","endTime":"2024-03-24T04:31:18.115Z","subEntries":null},{"name":"Block 50","startTime":"2024-03-24T04:31:19.266Z","endTime":"2024-03-24T04:31:20.199Z","subEntries":null},{"name":"Block 51","startTime":"2024-03-24T04:33:17.046Z","endTime":"2024-03-24T04:33:17.646Z","subEntries":null},{"name":"Block 52","startTime":"2024-03-24T04:33:18.253Z","endTime":"2024-03-24T04:33:18.969Z","subEntries":null},{"name":"Block 53","startTime":"2024-03-24T04:33:19.684Z","endTime":"2024-03-24T04:33:21.706Z","subEntries":null},{"name":"Block 54","startTime":"2024-03-24T04:33:22.293Z","endTime":"2024-03-24T04:33:22.674Z","subEntries":null},{"name":"Block 55","startTime":"2024-03-24T06:35:53.785Z","endTime":"2024-03-24T06:35:54.322Z","subEntries":null},{"name":"Block 56","startTime":"2024-03-24T06:35:54.693Z","endTime":"2024-03-24T06:35:54.864Z","subEntries":null},{"name":"Block 57","startTime":"2024-03-24T06:35:55.031Z","endTime":"2024-03-24T06:35:55.190Z","subEntries":null},{"name":"Block 58","startTime":"2024-03-24T06:35:55.631Z","endTime":"2024-03-24T06:35:57.836Z","subEntries":null},{"name":"Block 59","startTime":"2024-03-24T06:36:06.400Z","endTime":"2024-03-24T06:36:08.452Z","subEntries":null},{"name":"Block 60","startTime":"2024-03-24T06:36:53.108Z","endTime":"2024-03-24T06:36:53.791Z","subEntries":null},{"name":"Block 61","startTime":"2024-03-24T06:42:04.593Z","endTime":"2024-03-24T06:42:05.961Z","subEntries":null},{"name":"Block 62","startTime":"2024-03-24T06:42:30.789Z","endTime":"2024-03-24T06:42:31.609Z","subEntries":null},{"name":"Block 63","startTime":"2024-05-01T08:17:37.721Z","endTime":"2024-05-01T08:20:49.070Z","subEntries":null},{"name":"Test dwada uidawh dawhiud hawhiduia uhiwdiuhhauiw dhawd ihuawuidh awudhiu ahiwudhiu ahwiudihu awhiudhiu hiud ihuawhid hiuahiud ihwad","startTime":"2024-05-01T08:20:56.478Z","endTime":"2024-05-01T08:21:01.643Z","subEntries":null},{"name":"Test","startTime":"2024-05-01T08:21:03.005Z","endTime":"2024-05-01T08:21:35.567Z","subEntries":null},{"name":"tEWADAWDAWDAWD","startTime":"2024-05-01T08:21:37.838Z","endTime":"2024-05-01T08:22:25.344Z","subEntries":null},{"name":"Tewadwdawd","startTime":"2024-05-01T08:22:27.180Z","endTime":"2024-05-01T08:22:28.683Z","subEntries":null},{"name":"awdawd","startTime":"2024-05-01T08:22:30.186Z","endTime":"2024-05-01T08:22:36.241Z","subEntries":null},{"name":"Block 69","startTime":"2024-05-01T08:22:36.760Z","endTime":"2024-05-01T08:22:37.361Z","subEntries":null},{"name":"Block 70","startTime":"2024-05-01T08:22:37.695Z","endTime":"2024-05-01T08:22:38.428Z","subEntries":null},{"name":"Block 71","startTime":"2024-05-01T08:22:39.077Z","endTime":"2024-05-01T08:22:40.410Z","subEntries":null},{"name":"Block 72","startTime":"2024-05-01T08:22:40.991Z","endTime":"2024-05-01T08:22:41.538Z","subEntries":null},{"name":"Block 73","startTime":"2024-05-01T08:22:41.943Z","endTime":"2024-05-01T08:22:42.729Z","subEntries":null},{"name":"Block 74","startTime":"2024-05-01T08:22:43.160Z","endTime":"2024-05-01T08:22:44.806Z","subEntries":null},{"name":"Block 75","startTime":"2024-05-01T08:22:45.423Z","endTime":"2024-05-01T08:22:46.040Z","subEntries":null},{"name":"Block 76","startTime":"2024-05-01T08:22:49.574Z","endTime":"2024-05-01T08:22:50.712Z","subEntries":null},{"name":"Block 77","startTime":"2024-05-01T08:22:51.298Z","endTime":"2024-05-01T08:23:06.033Z","subEntries":null},{"name":"Block 78","startTime":"2024-05-01T08:23:07.620Z","endTime":"2024-05-01T08:23:08.124Z","subEntries":null},{"name":"Block 79","startTime":"2024-05-01T08:23:09.529Z","endTime":"2024-05-01T08:23:09.984Z","subEntries":null},{"name":"Block 80","startTime":"2024-05-01T08:23:44.681Z","endTime":"2024-05-01T08:23:56.772Z","subEntries":null},{"name":"Block 82","startTime":null,"endTime":null,"collapsed":true,"subEntries":[{"name":"Part 1","startTime":"2024-07-21T07:12:56.433Z","endTime":"2024-07-21T07:13:33.907Z","subEntries":null},{"name":"Part 2","startTime":"2024-07-21T07:24:44.769Z","endTime":"2024-07-21T07:24:48.149Z","subEntries":null},{"name":"Part 3","startTime":"2024-07-21T09:06:54.527Z","endTime":"2024-07-21T09:06:57.360Z","subEntries":null},{"name":"Part 4","startTime":"2024-07-21T09:07:10.820Z","endTime":"2024-07-21T09:07:11.426Z","subEntries":null}]},{"name":"Block 82","startTime":null,"endTime":null,"collapsed":true,"subEntries":[{"name":"Part 1","startTime":"2024-07-21T07:13:34.743Z","endTime":"2024-07-21T07:14:13.095Z","subEntries":null},{"name":"Part 2","startTime":"2024-07-21T07:14:13.562Z","endTime":"2024-07-21T07:14:14.679Z","subEntries":null},{"name":"Part 3","startTime":"2024-07-21T07:14:15.566Z","endTime":"2024-07-21T07:14:15.970Z","subEntries":null},{"name":"Part 4","startTime":"2024-07-21T09:06:52.510Z","endTime":"2024-07-21T09:06:53.351Z","subEntries":null},{"name":"Part 5","startTime":"2024-07-21T09:07:19.089Z","endTime":"2024-07-21T09:07:20.066Z","subEntries":null}]},{"name":"Block 83","startTime":"2024-07-21T09:07:21.923Z","endTime":"2024-07-21T09:07:22.516Z","subEntries":null},{"name":"Block 81","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2025-01-07T02:20:00.000Z","endTime":"2025-01-07T03:20:00.000Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T23:04:57.335Z","endTime":"2026-03-25T23:05:00.051Z","subEntries":null}]},{"name":"Block 85 [[#Test]] ","startTime":"2025-01-07T04:18:19.000Z","endTime":"2025-01-07T03:18:20.000Z","subEntries":null},{"name":"Block 86","startTime":"2026-03-25T20:20:44.814Z","endTime":"2026-03-25T20:53:38.364Z","subEntries":null},{"name":"Block 87","startTime":"2026-03-25T22:20:11.034Z","endTime":"2026-03-25T22:20:13.254Z","subEntries":null},{"name":"Block 88","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2026-03-25T22:20:13.921Z","endTime":"2026-03-25T22:33:49.279Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T23:05:33.696Z","endTime":"2026-03-25T23:05:35.361Z","subEntries":null}]},{"name":"Part 2","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2026-03-25T23:05:12.433Z","endTime":"2026-03-25T23:05:14.427Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-25T23:05:18.672Z","endTime":"2026-03-25T23:05:22.812Z","subEntries":null}]},{"name":"Part 3","startTime":"2026-03-25T23:09:44.281Z","endTime":"2026-03-25T23:09:46.096Z","subEntries":null}]},{"name":"Part 2","startTime":"2026-03-25T23:05:10.205Z","endTime":"2026-03-25T23:05:11.240Z","subEntries":null},{"name":"Part 3","startTime":"2026-03-25T23:05:15.995Z","endTime":"2026-03-25T23:05:17.046Z","subEntries":null}]},{"name":"Block 89","startTime":"2026-03-28T04:16:49.497Z","endTime":"2026-03-28T04:21:38.831Z","subEntries":null}]} ``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ```timekeep -{"entries":[{"name":"Block 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-31T07:27:36.569Z","endTime":"2024-03-31T07:27:37.726Z","subEntries":null},{"name":"Part 2","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-31T07:27:42.846Z","endTime":"2024-03-31T07:27:44.468Z","subEntries":null},{"name":"Part 2","startTime":"2024-03-31T07:27:47.517Z","endTime":"2024-03-31T07:27:48.459Z","subEntries":null}]}]},{"name":"Block 2","startTime":"2024-03-31T07:27:38.418Z","endTime":"2024-03-31T07:27:38.917Z","subEntries":null},{"name":"Block 3","startTime":"2024-03-31T07:27:39.480Z","endTime":"2024-03-31T07:27:39.796Z","subEntries":null},{"name":"Block 4","startTime":"2024-03-31T07:27:40.088Z","endTime":"2024-03-31T07:27:40.331Z","subEntries":null},{"name":"Block 5","startTime":"2024-03-31T07:27:40.548Z","endTime":"2024-03-31T07:27:40.795Z","subEntries":null},{"name":"Block 6","startTime":"2024-03-31T07:27:41.037Z","endTime":"2024-03-31T07:27:41.520Z","subEntries":null}]} +{"entries":[{"name":"Block 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-31T07:27:36.569Z","endTime":"2024-03-31T07:27:37.726Z","subEntries":null},{"name":"Part 2","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-03-31T07:27:42.846Z","endTime":"2024-03-31T07:27:44.468Z","subEntries":null},{"name":"Part 2","startTime":"2024-03-31T07:27:47.517Z","endTime":"2024-03-31T07:27:48.459Z","subEntries":null}]}]},{"name":"Block 2","startTime":"2024-03-31T07:27:38.418Z","endTime":"2024-03-31T07:27:38.917Z","subEntries":null},{"name":"Block 3","startTime":"2024-03-31T07:27:39.480Z","endTime":"2024-03-31T07:27:39.796Z","subEntries":null},{"name":"Block 4","startTime":"2024-03-31T07:27:40.088Z","endTime":"2024-03-31T07:27:40.331Z","subEntries":null},{"name":"Block 5","startTime":"2024-03-31T07:27:40.548Z","endTime":"2024-03-31T07:27:40.795Z","subEntries":null},{"name":"Block 6","startTime":"2024-03-31T07:27:41.037Z","endTime":"2024-03-31T07:27:41.520Z","subEntries":null},{"name":"Block 7","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2026-03-28T04:21:48.393Z","endTime":"2026-03-28T04:22:05.906Z","subEntries":null},{"name":"Part 2","startTime":"2026-03-28T04:27:18.175Z","endTime":"2026-03-28T04:27:19.172Z","subEntries":null}]},{"name":"Block 8","startTime":"2026-03-28T04:22:53.600Z","endTime":"2026-03-28T04:27:17.500Z","subEntries":null}]} ``` # Test