mirror of
https://github.com/tomasriveral/NoteWrapper.git
synced 2026-10-11 01:12:48 +02:00
Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1c63a1694 | ||
|
|
e01e580162 | ||
|
|
b2d76adb88 | ||
|
|
7403bed29c | ||
|
|
13bacbb6d6 | ||
|
|
886eff21d3 | ||
|
|
bcf30a4576 | ||
|
|
72bf03bc9f | ||
|
|
7ca0bf1561 | ||
|
|
94d659db2c | ||
|
|
e01be6ecea | ||
|
|
713fa27893 | ||
|
|
dd7345c2fa | ||
|
|
dd58a86fce | ||
|
|
19610c1c51 | ||
|
|
39a3cb6bba | ||
|
|
0d5da72b13 | ||
|
|
e78a9ca669 | ||
|
|
c83a504648 | ||
|
|
2e301b0b52 | ||
|
|
c7a9d45a65 | ||
|
|
62da4cfb2e | ||
|
|
d56e7d9e4f | ||
|
|
449a9eaca6 | ||
|
|
edc979dccb |
No files matched your search
+3
-1
@@ -10,7 +10,7 @@ This project aims to remain minimal, terminal-focused, and easy to extend. Contr
|
||||
|
||||
Please use the standardized debugging and error-handling functions defined in [/src/utils.c](./src/utils.c):
|
||||
|
||||
* `debug(...)`
|
||||
* `debug("message", ...)`
|
||||
Used for formatted debugging output (printf-style formatting).
|
||||
|
||||
* `altDebug(...)`
|
||||
@@ -21,6 +21,8 @@ Please use the standardized debugging and error-handling functions defined in [/
|
||||
|
||||
* `"user"`: errors caused by user input or configuration
|
||||
* `"program"`: internal or unexpected program errors
|
||||
* `warn(condition, "message", ...)`
|
||||
Used for formatted warning (printf-style formatting).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ Note: This project fullfills all my usecases for now, so my main source for feat
|
||||
|
||||
[](https://deepwiki.com/tomasriveral/notewrapper)
|
||||
|
||||

|
||||
|
||||
## Why?
|
||||
|
||||
I started journaling and note-taking with **[Obsidian](https://obsidian.md/)** but wanted to use only free software. I also tried **[Logseq](https://logseq.com/)** and **[Joplin](https://github.com/laurent22/joplin)**, but I preferred a terminal-based workflow.
|
||||
@@ -229,17 +231,18 @@ Edit `~/.config/notewrapper/config.json`. If it does not exist, it will be creat
|
||||
* `git.enable`: enable git version control for backups
|
||||
* `git.name` and `git.email` are for the commits signature
|
||||
|
||||
Note:
|
||||
* Directories must end with `/`.
|
||||
* All directories used must exist.
|
||||
* Elements of `directory` and `backup.directory` must match exactly.
|
||||
* Directories set in `directory` but not in `backup.directory` won't be backed up.
|
||||
* Directories set in `backup.directory` but not in `directory` will be ignored.
|
||||
* `journalRegex` must match if the file name ends with `.md` and if it doesn't.
|
||||
* It is recommended to keep `-q` or `--quiet` flag in `rsyncArgs` to avoid interference with `ncurses`.
|
||||
* If `rsync` fails, you will see it inside `ncurses`.
|
||||
* If you want the git backup but without rsync, you can leave `backup.directory` empty or map each directory to itself. When enabled, git will apply to all your vaults.
|
||||
* Each vault gets its own git repository that will be created the first time you select the vault with the option enabled.
|
||||
> [!NOTE]
|
||||
> * The git backup implementation relies a lot on the IO speed of your drive. A low speed one (or a web mounted one) could create blanks between closing a document and the TUI opening.
|
||||
> * Directories must end with `/`.
|
||||
> * All directories used must exist.
|
||||
> * Elements of `directory` and `backup.directory` must match exactly.
|
||||
> * Directories set in `directory` but not in `backup.directory` won't be backed up.
|
||||
> * Directories set in `backup.directory` but not in `directory` will be ignored.
|
||||
> * `journalRegex` must match if the file name ends with `.md` and if it doesn't.
|
||||
> * It is recommended to keep `-q` or `--quiet` flag in `rsyncArgs` to avoid interference with `ncurses`.
|
||||
> * If `rsync` fails, you will see it inside `ncurses`.
|
||||
> * If you want the git backup but without rsync, you can leave `backup.directory` empty or map each directory to itself. When enabled, git will apply to all your vaults.
|
||||
> * Each vault gets its own git repository that will be created the first time you select the vault with the option enabled.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
Require echo
|
||||
|
||||
Set Shell "zsh"
|
||||
Set TypingSpeed 35ms
|
||||
Set FontSize 18
|
||||
Set Width 1800
|
||||
Set Height 900
|
||||
|
||||
Output demo.gif
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Introduction
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
Type "echo 'Welcome to NoteWrapper!'"
|
||||
Sleep 800ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
Type "notewrapper --help"
|
||||
Sleep 500ms
|
||||
Enter
|
||||
Sleep 4000ms
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Create / open a unified journal
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
Type "notewrapper"
|
||||
Sleep 700ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 700ms
|
||||
|
||||
Enter
|
||||
Sleep 1200ms
|
||||
|
||||
Down
|
||||
Sleep 600ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
# Enter the editor
|
||||
Type "i"
|
||||
Sleep 800ms
|
||||
|
||||
Type "NoteWrapper uses standard Markdown files, so you can open your notes in any other application that supports Markdown."
|
||||
Sleep 700ms
|
||||
Enter
|
||||
Sleep 800ms
|
||||
|
||||
Type "This is a unified journal: all your daily entries live in the same file. You can also configure NoteWrapper to use monthly or other time-based entries."
|
||||
Sleep 700ms
|
||||
Enter
|
||||
Sleep 800ms
|
||||
|
||||
Type "And, although you can't see it in this demo, this Markdown file is rendered live in my browser thanks to Vivify!"
|
||||
Sleep 1500ms
|
||||
Sleep 3000ms
|
||||
|
||||
# Leave the editor
|
||||
Escape
|
||||
Sleep 500ms
|
||||
Type ":q!"
|
||||
Sleep 800ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Create / open a divided journal
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 700ms
|
||||
|
||||
Enter
|
||||
Sleep 2000ms
|
||||
|
||||
Enter
|
||||
Sleep 1000ms
|
||||
|
||||
Type "i"
|
||||
Sleep 800ms
|
||||
|
||||
Type "And this is a divided journal."
|
||||
Sleep 500ms
|
||||
Type " It uses one file per entry."
|
||||
Sleep 700ms
|
||||
Enter
|
||||
Sleep 1000ms
|
||||
|
||||
Type "Find the dates and journal divisions a little too cumbersome?"
|
||||
Sleep 700ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
Type "Don't worry: NoteWrapper also works with basic notes."
|
||||
Sleep 800ms
|
||||
|
||||
Type " It distinguishes them using a regular expression, which defaults to `.*journal.*`."
|
||||
Sleep 800ms
|
||||
Enter
|
||||
Sleep 700ms
|
||||
|
||||
Type " The unified or divided journal mode is chosen when you first create the journal."
|
||||
Sleep 1000ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Search
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
Type "Let me show you one last cool feature."
|
||||
Sleep 1000ms
|
||||
|
||||
Type " Have you ever forgotten when or where you wrote something?"
|
||||
Sleep 1000ms
|
||||
Enter
|
||||
Sleep 800ms
|
||||
|
||||
Type "Well ... NoteWrapper was made for you!"
|
||||
Sleep 1500ms
|
||||
|
||||
Escape
|
||||
Sleep 500ms
|
||||
Type ":q!"
|
||||
Sleep 800ms
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
# Navigate to search
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 800ms
|
||||
|
||||
Enter
|
||||
Sleep 1500ms
|
||||
|
||||
Down
|
||||
Sleep 500ms
|
||||
Down
|
||||
Sleep 800ms
|
||||
|
||||
Enter
|
||||
Sleep 2000ms
|
||||
|
||||
# Search all notes with fzf
|
||||
Type "c"
|
||||
Sleep 300ms
|
||||
Type "o"
|
||||
Sleep 300ms
|
||||
Type "o"
|
||||
Sleep 300ms
|
||||
Type "l"
|
||||
Sleep 1500ms
|
||||
|
||||
# Show the search result / preview
|
||||
Up
|
||||
Sleep 2000ms
|
||||
Enter
|
||||
Sleep 1000ms
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Final note
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
Type "i"
|
||||
Sleep 700ms
|
||||
|
||||
Type "Never lose a note."
|
||||
Sleep 1500ms
|
||||
Enter
|
||||
Sleep 1000ms
|
||||
|
||||
Type "P. S. Don't mind the broken fzf preview. Normally, you'll see the context around your search result, but sometimes `sed` breaks when fzf is opened for the first time."
|
||||
Sleep 5000ms
|
||||
Generated
+3
-3
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1786106723,
|
||||
"narHash": "sha256-zDSUbpoeo/9ZmD2+wXnzxoo1+uhL8vxc0b8yuYMKYq0=",
|
||||
"lastModified": 1791048980,
|
||||
"narHash": "sha256-KgItSKML8Xvte0B7/uGnBDsYzSnnKHcOaiUbgWBXLXw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f13ff45afd1bb73e640eaa08a7066dbed07e3238",
|
||||
"rev": "a7868a727837f3c09cee2ce0ca671c76b1589fed",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
+1
-2
@@ -766,7 +766,7 @@ backup_config_end:
|
||||
openEditor(fullPath, editorToOpen, shouldRender, shouldJumpToEnd, shouldDebug);
|
||||
free(fullPath);
|
||||
}
|
||||
openEditor(fullPath, editorToOpen, shouldRender, shouldJumpToEnd, shouldDebug);
|
||||
free(returnNoteSelection);
|
||||
if (gitEnabled) { // After closing a file, we need to update the git repo (if git is enable
|
||||
char *date = getDateAndTime();
|
||||
char *commitMsg = malloc(29);
|
||||
@@ -775,7 +775,6 @@ backup_config_end:
|
||||
free(date);
|
||||
free(commitMsg);
|
||||
}
|
||||
free(fullPath);
|
||||
} else if (strcmp(noteSelected, "Create new note") == 0) {
|
||||
note_creation:
|
||||
noteSelected = createNewNote(notesDirectoryString, vaultSelected, bypassSelectionNote, bypassSelectionNoteValue, journalRegex, shouldDebug);
|
||||
|
||||
+53
-2
@@ -71,6 +71,26 @@ void _error(const int shouldDebug, const int condition, const char *type, const
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
void _warn(const int condition, const char *file, const int line, const char *function, const char *message, ...) { // use for warnings that should always be shown
|
||||
if (condition) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
int h, m, s;
|
||||
getCurrentTime(&h, &m, &s);
|
||||
|
||||
fprintf(stdout, "\e[0;33m[WARNING -- %d:%d:%d] From file %s line %d function %s:\e[0m\n", h, m, s, file, line, function);
|
||||
|
||||
vfprintf(stdout, message, args);
|
||||
|
||||
fprintf(stdout, "\e[0m\n");
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
static void copyDir(const char *source, const char *destination, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug) {
|
||||
debug("Backuping... source: %s and destination: %s", source, destination);
|
||||
@@ -113,6 +133,20 @@ static void ensureDir(const char *path, const int shouldDebug) {
|
||||
}
|
||||
}
|
||||
|
||||
static void slowGitWarning(struct timespec start, int *wasShown) {
|
||||
if (!*wasShown) {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
double elapsed = (double)(now.tv_sec - start.tv_sec) + (double)(now.tv_nsec - start.tv_nsec) / 1e9;
|
||||
|
||||
warn(elapsed > 0.3, "Git operations are taking too much time. You drive might be slow. Please do not stop the program.");
|
||||
if (elapsed > 0.3) {
|
||||
*wasShown = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ensureGitDirectory(const char *path, const char *vault, const char *signatureName, const char *signatureEmail, const int shouldDebug) {
|
||||
git_repository *repo = NULL;
|
||||
char *fullPath = malloc(PATH_MAX);
|
||||
@@ -151,6 +185,10 @@ void ensureGitDirectory(const char *path, const char *vault, const char *signatu
|
||||
}
|
||||
|
||||
void gitBackupUpdate(const char *path, const char *vault, const char *signatureName, const char *signatureEmail, const char *commitMsg, const int shouldDebug) {
|
||||
// on slow drive or WebDav mounted drive this git operations are pretty slow. We'll just show a warning to avoid the user closing the program.
|
||||
struct timespec start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
git_repository *repo = NULL;
|
||||
git_index *index = NULL;
|
||||
git_tree *tree = NULL;
|
||||
@@ -162,6 +200,8 @@ void gitBackupUpdate(const char *path, const char *vault, const char *signatureN
|
||||
git_oid tree_id;
|
||||
git_oid commit_id;
|
||||
|
||||
int slowGitWarningAlreadyShown = 0;
|
||||
|
||||
char *fullPath = malloc(PATH_MAX);
|
||||
|
||||
error(fullPath == NULL, "program", "Failed to allocate path");
|
||||
@@ -172,17 +212,21 @@ void gitBackupUpdate(const char *path, const char *vault, const char *signatureN
|
||||
int return_code = git_repository_open(&repo, fullPath);
|
||||
|
||||
error(return_code, "program", "Failed to open Git repository\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
/* Get the index */
|
||||
return_code = git_repository_index(&index, repo);
|
||||
error(return_code, "program", "Failed to get Git index\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
/* git add . */
|
||||
return_code = git_index_add_all(index, NULL, 0, NULL, NULL);
|
||||
error(return_code, "program", "Failed to add files\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
return_code = git_index_write(index);
|
||||
error(return_code, "program", "Failed to write index\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
/*
|
||||
* Check whether there are changes to commit.
|
||||
@@ -199,16 +243,19 @@ void gitBackupUpdate(const char *path, const char *vault, const char *signatureN
|
||||
|
||||
return_code = git_reference_name_to_id(&head_oid, repo, "HEAD");
|
||||
error(return_code, "program", "Failed to get HEAD\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
return_code = git_commit_lookup(&head_commit, repo, &head_oid);
|
||||
error(return_code, "program", "Failed to lookup HEAD commit\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
return_code = git_commit_tree(&head_tree, head_commit);
|
||||
error(return_code, "program", "Failed to get HEAD tree\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
return_code = git_diff_tree_to_index(&diff, repo, head_tree, index, NULL);
|
||||
|
||||
error(return_code, "program", "Failed to create diff\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
has_changes = git_diff_num_deltas(diff) > 0;
|
||||
}
|
||||
@@ -222,13 +269,16 @@ void gitBackupUpdate(const char *path, const char *vault, const char *signatureN
|
||||
/* index → tree */
|
||||
return_code = git_index_write_tree(&tree_id, index);
|
||||
error(return_code, "program", "Failed to write tree\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
return_code = git_tree_lookup(&tree, repo, &tree_id);
|
||||
error(return_code, "program", "Failed to lookup tree\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
/* Author/committer */
|
||||
return_code = git_signature_now(&signature, signatureName, signatureEmail);
|
||||
error(return_code, "program", "Failed to create signature\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
/* Create commit */
|
||||
if (git_repository_head_unborn(repo)) {
|
||||
@@ -237,6 +287,7 @@ void gitBackupUpdate(const char *path, const char *vault, const char *signatureN
|
||||
return_code = git_commit_create_v(&commit_id, repo, "HEAD", signature, signature, NULL, commitMsg, tree, 1, head_commit);
|
||||
}
|
||||
error(return_code, "program", "Failed to create commit\n%s", git_error_last()->message);
|
||||
slowGitWarning(start, &slowGitWarningAlreadyShown);
|
||||
|
||||
debug("Created commit %s in %s", commitMsg, fullPath);
|
||||
|
||||
@@ -315,7 +366,7 @@ char *getDateAndTime() {
|
||||
struct tm tm_now;
|
||||
localtime_r(&now, &tm_now);
|
||||
|
||||
strftime(return_value, sizeof(return_value), "%Y-%m-%%d %H:%M:%%S", &tm_now);
|
||||
strftime(return_value, 20, "%Y-%m-%d %H:%M:%S", &tm_now);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#define debug(message, ...) _debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||
#define altDebug(message, ...) _altDebug(shouldDebug, message, ##__VA_ARGS__)
|
||||
#define error(condition, type, message, ...) _error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||
#define warn(condition, message, ...) _warn(condition, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||
// you must edit this two values if you want to add suport for an editor
|
||||
extern const char *supportedEditor[]; // array of supported editors
|
||||
extern const int numEditors; // number of supported editors
|
||||
@@ -53,6 +54,8 @@ void _debug(const int d, const char *file, const int line, const char *function,
|
||||
void _altDebug(const int d, const char *message, ...);
|
||||
// formated error.
|
||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...);
|
||||
// formated warn
|
||||
void _warn(const int condition, const char *file, const int line, const char *function, const char *message, ...);
|
||||
// Returns 1 if the string is in the array.
|
||||
// Returns 0 if the string is not in the array.
|
||||
// If you want to only check the first n elements of the array, pass n as len.
|
||||
|
||||
Reference in new issue
Block a user