Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
197aa2e
Add time formatting for edit duration CAMS; Fix some linted issues
rupel190 Aug 27, 2024
1de468a
Formatting for edited_time works; Has unresolved debugging todos
rupel190 Aug 28, 2024
393d5d6
First iteration of debounced time tracking core refactoring. Editing …
rupel190 Aug 29, 2024
203d4c9
backup intermediate state changing edit updates
rupel190 Aug 29, 2024
5844f43
backup state with intermediate saving in development
rupel190 Aug 30, 2024
329b816
backup intermittent saving before simplifying solution
rupel190 Sep 2, 2024
5a02473
proof of concept for updating with cams'
rupel190 Sep 2, 2024
d9e5a51
clean up poc before adding boms
rupel190 Sep 2, 2024
a966782
add boms to new update mechanism
rupel190 Sep 2, 2024
c400f85
Update settings to reflect timeout values in slider+textbox combination
rupel190 Sep 2, 2024
cd20582
Overhaul complete settings menu including styling; Add ability to set…
rupel190 Sep 5, 2024
29ffa67
backup, usage of settings in progress, complications because of dynam…
rupel190 Sep 5, 2024
a5bbdcb
backup, timeouts still use stale reference. Most probable fix to put …
rupel190 Sep 5, 2024
790a8dd
Timeout setting works, might be simplified
rupel190 Sep 5, 2024
d4b96e3
Refactor, format, remove unused variables
rupel190 Sep 5, 2024
ca0200a
Refactor frontmatter updating including realtime date format validati…
rupel190 Sep 8, 2024
73b0496
Refactor frontmatter update in progress
rupel190 Sep 9, 2024
cb89b1a
BOMS modified date works, rest in progress
rupel190 Sep 9, 2024
5ae09e7
backup before going even deeper into cams/boms refactoring
rupel190 Sep 9, 2024
b5ae28b
BOMS working, CAMS in progress
rupel190 Sep 9, 2024
b964cbd
CAMS doesnt handle frontmatter creation and only works on modified. B…
rupel190 Sep 9, 2024
25b224c
CAMS standalone frontmatter handling
rupel190 Sep 10, 2024
d07dbae
Formatting, commenting
rupel190 Sep 10, 2024
29b048d
Update manifest (authors, ...)
rupel190 Sep 11, 2024
6cec4ee
Update README for v2.0.0 fork changes
rupel190 Feb 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 41 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,42 @@
# Time Things
# Time Things (Fork)

Show clock in status bar. Sync modified file property with frontmatter.
> Fork of [Time Things](https://github.com/DynamicPlayerSector/timethings) by Nick Winters with enhanced editing time tracking.

This plugin uses moment.js. It's a time manipulation library that is already included in Obsidian.
Track total editing time and modification timestamps in frontmatter. Show clock and typing indicator in the status bar.

This plugin uses moment.js, a time manipulation library already included in Obsidian.

![Obsidian_vH8xXX5e7Z](https://github.com/DynamicPlayerSector/timethings/assets/65742767/67edb231-1149-4896-a0f1-6cfa2aec3d93)

## Fork Changes

### Typing Indicator
- Visual indicator in the status bar showing whether you're actively editing
- Customizable active/inactive icons (default: ✏🔵 / ✋🔴)

### Configurable Editing Timeout
- Slider + textbox combination for precise control over editing timeout
- Defines how long after you stop typing before the plugin stops tracking
- Works with both CAMS (custom) and BOMS (Obsidian API) modes

### Date Refresh Threshold
- Set a minimum active typing duration before the modification timestamp updates
- Prevents minor edits from updating the "last modified" date

### Real-time Format Validation
- Date format fields now validate in real-time as you type
- Invalid formats are highlighted immediately

### Customizable Edit Duration Format
- Format how the editing duration is displayed in the status bar

---

## Clock

![image](https://github.com/DynamicPlayerSector/timethings/assets/65742767/c2b4c4e0-002b-43ea-8b94-6860d6f7c703)

- Option to change the date format. Recommended: `HH:MM:ss` and `hh:mm A`.
- Option to change update interval.
- Option to use UTC timezone.

## Modified frontmatter key
Expand All @@ -27,25 +52,21 @@ This plugin uses moment.js. It's a time manipulation library that is already inc

## About custom frontmatter handling solution

Custom frontmatter handling solution is disabled by default since Obsidian's straightforward frontmatter API is much more stable and robust. However, advanced users may enable it if they wish. Don't forget to regularly back up your vault.

### Reasons to enable custom frontmatter handling solution
Custom frontmatter handling solution (CAMS) is disabled by default since Obsidian's frontmatter API (BOMS) is more stable. However, advanced users may enable CAMS for a smoother experience. Don't forget to regularly back up your vault.

- It updates the value instantly
- It only touches one line, which means it never makes your cursor jump, or a message "A file has been modified" popup
- It doesn't reformat your frontmatter to fit any standard
### Reasons to enable CAMS

### Reasons to leave custom frontmatter handling solution disabled
- Updates values instantly
- Only touches one line, which means it never makes your cursor jump or shows "A file has been modified" popup
- Doesn't reformat your frontmatter to fit any standard
- Allows for shorter editing timeout (as low as 1 second)

- You are using nested keys in the Time Things settings. Using custom frontmatter handling solution with a nested key may result in the wrong key being updated. This only happens if it comes before the needed key in the frontmatter and has a similar path. For example `x.y.z` will update `x.y.g.z` instead if it meets it first and if it has a value of a format specified in the settings.
- You are not using templates populated with needed variables to create notes. Using custom frontmatter handling solution works best with templates since it doesn't create a key for you if it doesn't already exist. Also it doesn't update null values or the values of the format different from the one specified in the settings.
### Reasons to use BOMS (default)

I may improve it further in the future, but for that I feel like I'd have to write a full-blown YAML parser from scratch. For now it covers my own wishes completely and even has some room for limited flexibility, so I will focus on other aspects of the plugin.
- More stable with nested keys
- Works better without templates (can create keys if they don't exist)
- Less prone to edge cases

## What's next
## Credits

- [ ] Ignore files in specified folders
- [ ] Ingore files with specified frontmatter keys (and their values)
- [ ] Pick a timezone for all things globally
- [ ] Pick a timezone for clock and frontmatter seperately
- [x] Track time spent editing a note
Original plugin by [Nick Winters](https://github.com/DynamicPlayerSector)
10 changes: 5 additions & 5 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"id": "timethings",
"name": "Time Things",
"version": "1.3.0",
"version": "2.0.0",
"minAppVersion": "0.15.0",
"description": "Show clock in the corner. Track total editing time of a note and the last time it was modified.",
"author": "Nick Winters",
"authorUrl": "https://github.com/plasmabit",
"fundingUrl": "https://www.patreon.com/nickwinters",
"description": "Track total editing time of a note and the last time it was modified. Show clock and/or editing indicator in the corner",
"author": "Nick Winters, Rupel Rupelsson",
"authorUrl": "https://github.com/plasmabit, https://github.com/rupel190",
"fundingUrl": "https://www.patreon.com/nickwinters, https://ko-fi.com/rupel190",
"isDesktopOnly": true
}
73 changes: 52 additions & 21 deletions src/CAMS.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { Editor } from "obsidian";

export function isLineIndented(line: string): boolean {
return /^[\s\t]/.test(line);
}

export function getLine(editor: Editor, fieldPath: string): number | undefined {
const frontmatterLine = frontmatterEndLine(editor);
const keys = fieldPath.split(".");
Expand All @@ -25,13 +21,10 @@ export function getLine(editor: Editor, fieldPath: string): number | undefined {
if (currentFieldName === key) {
emergingPath.push(currentFieldName);
const targetPath = fieldPath.split(".");
const targetPathShrink = targetPath.slice(
0,
emergingPath.length,
);
const targetPathShrink = targetPath.slice(0, emergingPath.length);
if (
(targetPathShrink.join(".") === emergingPath.join(".")) ===
false
(targetPathShrink.join(".") === emergingPath.join("."))
=== false
) {
emergingPath.pop();
startLine = i + 1;
Expand Down Expand Up @@ -63,10 +56,31 @@ export function getLine(editor: Editor, fieldPath: string): number | undefined {
return undefined;
}

export function isFrontmatterPresent(editor: Editor): boolean {
export function setLine(editor: Editor, fieldPath: string, fieldValue: string,) {
// Check for frontmatter
if(!isFrontmatterPresent(editor)) {
// Create empty frontmatter
editor.setLine(0, "---\n---\n");
}
// Check for path
if(!fieldPathPresent(editor, fieldPath)) {
appendNewLine(editor, fieldPath, fieldValue);
} else {
overrideCurrentLine(editor, fieldPath, fieldValue);
}
}

//#region private
function isLineIndented(line: string): boolean {
return /^[\s\t]/.test(line);
}


function isFrontmatterPresent(editor: Editor): boolean {
if (editor.getLine(0) !== "---") {
return false;
}

for (let i = 1; i <= editor.lastLine(); i++) {
if (editor.getLine(i) === "---") {
return true;
Expand All @@ -75,7 +89,7 @@ export function isFrontmatterPresent(editor: Editor): boolean {
return false;
}

export function frontmatterEndLine(editor: Editor): number | undefined {
function frontmatterEndLine(editor: Editor): number | undefined {
if (isFrontmatterPresent(editor)) {
for (let i = 1; i <= editor.lastLine(); i++) {
if (editor.getLine(i) === "---") {
Expand All @@ -86,14 +100,31 @@ export function frontmatterEndLine(editor: Editor): number | undefined {
return undefined; // # End line not found
}

function fieldPathPresent(editor: Editor, fieldPath: string): boolean {
const pathValue = getLine(editor, fieldPath)
if(pathValue) {
return true;
}
return false;
}

export function setValue(editor: Editor, fieldPath: string, fieldValue: string,) {
// The thing with this function is that it uses the format from settings to check against. I can make it as an argument that can be passed, or better yet, eradicate the check from the function to make it more atomic and place it somewhere else in the main code.
const fieldLine = getLine(editor, fieldPath);
if (fieldLine === undefined) {
return;
}
const initialLine = editor.getLine(fieldLine).split(":", 1);
const newLine = initialLine[0] + ": " + fieldValue;
editor.setLine(fieldLine, newLine);
function appendNewLine(editor: Editor, fieldPath: string, fieldValue: string) {
const endLine = frontmatterEndLine(editor);
if(!endLine) {
console.log("No frontmatter endline found!");
return;
}
editor.setLine(endLine, fieldPath + ": " + fieldValue + "\n---");
}

function overrideCurrentLine(editor: Editor, fieldPath: string, fieldValue: string) {
const currentValue = getLine(editor, fieldPath);
if (currentValue === undefined) {
console.log("Value not found!");
return;
}
const initialLine = editor.getLine(currentValue).split(":", 1);
const newLine = initialLine[0] + ": " + fieldValue;
editor.setLine(currentValue, newLine);
}
//#endregion
4 changes: 2 additions & 2 deletions src/gates.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface FilterList {

export function isFileMatchFilter(file: TFile, filter: FilterList,): boolean {
// Check if file matches paths
if (isStringInList(file.parent.path, filter.folders)) {
if (file.parent && isStringInList(file.parent.path, filter.folders)) {
return true;
}
// Check if file matches tags
Expand All @@ -27,7 +27,7 @@ export function isStringInList(path: string, list: string[]): boolean {
export async function isTagPresentInFile(file: TFile, tag: string,) {
await this.app.fileManager.processFrontMatter(
file as TFile,
(frontmatter) => {
(frontmatter: any) => {
const updateKeyValue = BOMS.getValue(frontmatter, "tags");
if (updateKeyValue.includes(tag))
{
Expand Down
Loading