gitwrit 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/bin/gitwrit.js +5 -0
- package/package.json +1 -1
- package/src/commands/config.js +34 -7
- package/src/commands/demo.js +81 -0
- package/src/commands/help.js +2 -0
- package/src/commands/start.js +2 -2
- package/src/commands/status.js +2 -2
- package/src/ui.js +9 -0
package/README.md
CHANGED
|
@@ -161,13 +161,19 @@ If that works, restart gitwrit with `gitwrit restart`. If it fails, fix your Git
|
|
|
161
161
|
|
|
162
162
|
---
|
|
163
163
|
|
|
164
|
-
## Why not
|
|
164
|
+
## Why not cloud-based writing tools?
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
Most cloud-based writing and note-taking tools store your files on their
|
|
167
|
+
infrastructure. gitwrit does not. Your writing lives in a plain Git repository
|
|
168
|
+
that you control, pushed wherever you want—GitHub, GitLab, a private server,
|
|
169
|
+
anywhere that accepts a Git remote.
|
|
167
170
|
|
|
168
|
-
Just to be clear, there is absolutely nothing wrong with using
|
|
171
|
+
Just to be clear, there is absolutely nothing wrong with using cloud-based
|
|
172
|
+
tools. Many people use them every day for good reason!
|
|
169
173
|
|
|
170
|
-
But
|
|
174
|
+
But gitwrit might be especially useful when cloud storage isn't an option—for
|
|
175
|
+
proprietary documentation like internal specs, research notes, model cards,
|
|
176
|
+
experiment logs, etc. For when putting files into a third-party cloud is not an option, or when you want to keep everything essential to your codebase all in one place.
|
|
171
177
|
|
|
172
178
|
---
|
|
173
179
|
|
package/bin/gitwrit.js
CHANGED
|
@@ -14,6 +14,7 @@ import { add } from '../src/commands/add.js';
|
|
|
14
14
|
import { remove } from '../src/commands/remove.js';
|
|
15
15
|
import { logs } from '../src/commands/logs.js';
|
|
16
16
|
import { help } from '../src/commands/help.js';
|
|
17
|
+
import { demo } from '../src/commands/demo.js';
|
|
17
18
|
import { checkForUpdate, currentVersion } from '../src/updater.js';
|
|
18
19
|
import chalk from 'chalk';
|
|
19
20
|
import { TEAL, PINK } from '../src/ui.js';
|
|
@@ -74,6 +75,10 @@ program.command('help')
|
|
|
74
75
|
.description('List all available commands')
|
|
75
76
|
.action(help);
|
|
76
77
|
|
|
78
|
+
program.command('demo')
|
|
79
|
+
.description('Preview gitwrit\'s UI with placeholder data')
|
|
80
|
+
.action(demo);
|
|
81
|
+
|
|
77
82
|
// ── hidden daemon command ─────────────────────────────────────────────────────
|
|
78
83
|
program.command('__daemon', { hidden: true })
|
|
79
84
|
.action(() => import('../src/daemon.js'));
|
package/package.json
CHANGED
package/src/commands/config.js
CHANGED
|
@@ -43,9 +43,11 @@ async function editField(key, currentValue) {
|
|
|
43
43
|
{ name: '5 seconds', value: 5000 },
|
|
44
44
|
{ name: '10 seconds', value: 10000 },
|
|
45
45
|
{ name: 'Custom', value: 'custom' },
|
|
46
|
+
{ name: 'Exit', value: 'exit' },
|
|
46
47
|
],
|
|
47
48
|
default: currentValue,
|
|
48
49
|
});
|
|
50
|
+
if (choice === 'exit') return currentValue; // no change
|
|
49
51
|
if (choice === 'custom') {
|
|
50
52
|
const raw = await input({ message: 'Seconds to wait:' });
|
|
51
53
|
return parseFloat(raw) * 1000;
|
|
@@ -61,9 +63,11 @@ async function editField(key, currentValue) {
|
|
|
61
63
|
{ name: 'Every 10 minutes', value: 600000 },
|
|
62
64
|
{ name: 'Every 30 minutes', value: 1800000 },
|
|
63
65
|
{ name: 'Custom', value: 'custom' },
|
|
66
|
+
{ name: 'Exit', value: 'exit' },
|
|
64
67
|
],
|
|
65
68
|
default: currentValue,
|
|
66
69
|
});
|
|
70
|
+
if (choice === 'exit') return currentValue; // no change
|
|
67
71
|
if (choice === 'custom') {
|
|
68
72
|
const raw = await input({ message: 'Minutes between pushes:' });
|
|
69
73
|
return parseFloat(raw) * 60000;
|
|
@@ -72,14 +76,17 @@ async function editField(key, currentValue) {
|
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
if (key === 'branchMode') {
|
|
75
|
-
|
|
79
|
+
const choice = await select({
|
|
76
80
|
message: 'Default branch mode?',
|
|
77
81
|
choices: [
|
|
78
82
|
{ name: "Current branch — commit to whatever branch you're on", value: 'current' },
|
|
79
83
|
{ name: 'Autogenerated — create a fresh named branch each session', value: 'autogenerated' },
|
|
84
|
+
{ name: 'Exit', value: 'exit' },
|
|
80
85
|
],
|
|
81
86
|
default: currentValue,
|
|
82
87
|
});
|
|
88
|
+
if (choice === 'exit') return currentValue; // no change
|
|
89
|
+
return choice;
|
|
83
90
|
}
|
|
84
91
|
}
|
|
85
92
|
|
|
@@ -112,10 +119,11 @@ async function configureGlobal() {
|
|
|
112
119
|
choices: [
|
|
113
120
|
...FIELDS.map(f => ({ name: f.label, value: f.key })),
|
|
114
121
|
{ name: 'Done', value: null },
|
|
122
|
+
{ name: 'Exit', value: 'exit' },
|
|
115
123
|
],
|
|
116
124
|
});
|
|
117
125
|
|
|
118
|
-
if (!field) return;
|
|
126
|
+
if (!field || field === 'exit') return;
|
|
119
127
|
|
|
120
128
|
const newValue = await editField(field, config[field]);
|
|
121
129
|
config[field] = newValue;
|
|
@@ -167,10 +175,13 @@ async function configureLocal(dir) {
|
|
|
167
175
|
{ name: 'Edit an override', value: 'edit' },
|
|
168
176
|
{ name: 'Remove an override', value: 'remove' },
|
|
169
177
|
{ name: 'Add another override', value: 'add' },
|
|
178
|
+
{ name: 'Exit', value: 'exit' },
|
|
170
179
|
],
|
|
171
180
|
})
|
|
172
181
|
: 'add';
|
|
173
182
|
|
|
183
|
+
if (action === 'exit') return;
|
|
184
|
+
|
|
174
185
|
if (action === 'edit' || action === 'add') {
|
|
175
186
|
const overriddenKeys = Object.keys(local);
|
|
176
187
|
const choices = action === 'edit'
|
|
@@ -184,9 +195,14 @@ async function configureLocal(dir) {
|
|
|
184
195
|
|
|
185
196
|
const field = await select({
|
|
186
197
|
message: 'Which setting?',
|
|
187
|
-
choices:
|
|
198
|
+
choices: [
|
|
199
|
+
...choices.map(f => ({ name: f.label, value: f.key })),
|
|
200
|
+
{ name: 'Exit', value: 'exit' },
|
|
201
|
+
],
|
|
188
202
|
});
|
|
189
203
|
|
|
204
|
+
if (field === 'exit') return;
|
|
205
|
+
|
|
190
206
|
const currentValue = local[field] ?? global[field];
|
|
191
207
|
const newValue = await editField(field, currentValue);
|
|
192
208
|
local[field] = newValue;
|
|
@@ -199,11 +215,16 @@ async function configureLocal(dir) {
|
|
|
199
215
|
const overriddenKeys = Object.keys(local);
|
|
200
216
|
const field = await select({
|
|
201
217
|
message: 'Which override would you like to remove?',
|
|
202
|
-
choices:
|
|
203
|
-
|
|
204
|
-
|
|
218
|
+
choices: [
|
|
219
|
+
...FIELDS
|
|
220
|
+
.filter(f => overriddenKeys.includes(f.key))
|
|
221
|
+
.map(f => ({ name: f.label, value: f.key })),
|
|
222
|
+
{ name: 'Exit', value: 'exit' },
|
|
223
|
+
],
|
|
205
224
|
});
|
|
206
225
|
|
|
226
|
+
if (field === 'exit') return;
|
|
227
|
+
|
|
207
228
|
delete local[field];
|
|
208
229
|
await saveLocalConfig(dir, local);
|
|
209
230
|
print.gap();
|
|
@@ -235,9 +256,15 @@ export async function config() {
|
|
|
235
256
|
choices: [
|
|
236
257
|
{ name: 'Global defaults', value: 'global' },
|
|
237
258
|
{ name: `This directory (${dir})`, value: 'local' },
|
|
259
|
+
{ name: 'Exit', value: 'exit' },
|
|
238
260
|
],
|
|
239
261
|
});
|
|
240
262
|
|
|
263
|
+
if (scope === 'exit') {
|
|
264
|
+
print.gap();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
241
268
|
print.gap();
|
|
242
269
|
|
|
243
270
|
if (scope === 'global') {
|
|
@@ -247,4 +274,4 @@ export async function config() {
|
|
|
247
274
|
}
|
|
248
275
|
|
|
249
276
|
print.gap();
|
|
250
|
-
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import boxen from 'boxen';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { print, TEAL, PINK } from '../ui.js';
|
|
4
|
+
import { renderBanner } from '../banner.js';
|
|
5
|
+
|
|
6
|
+
// ─── demo ─────────────────────────────────────────────────────────────────────
|
|
7
|
+
//
|
|
8
|
+
// renders gitwrit's UI with placeholder data — no real paths, no real usernames.
|
|
9
|
+
// useful for screenshots, documentation, and promo material.
|
|
10
|
+
//
|
|
11
|
+
// shows three screens in sequence:
|
|
12
|
+
// 1. gitwrit start output
|
|
13
|
+
// 2. gitwrit status output
|
|
14
|
+
// 3. gitwrit stop output
|
|
15
|
+
|
|
16
|
+
function boxRow(label, value) {
|
|
17
|
+
return chalk.dim(` ${label.padEnd(13)}`) + chalk.white(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const BOX_OPTS = {
|
|
21
|
+
padding: { top: 0, bottom: 0, left: 1, right: 2 },
|
|
22
|
+
borderStyle: 'round',
|
|
23
|
+
borderColor: TEAL,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function demo() {
|
|
27
|
+
// ── start ──────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
renderBanner();
|
|
30
|
+
|
|
31
|
+
print.good('gitwrit is running.');
|
|
32
|
+
print.gap();
|
|
33
|
+
print.row('Watching', '~/notes, ~/projects/docs');
|
|
34
|
+
print.row('Files', '.md, .mdx');
|
|
35
|
+
print.row('Branch', 'Current branch');
|
|
36
|
+
print.row('Committing', '3s after last save');
|
|
37
|
+
print.row('Pushing', 'Every 5 min');
|
|
38
|
+
print.gap();
|
|
39
|
+
print.hint('Type all you want, we\'ve got you.');
|
|
40
|
+
print.gap();
|
|
41
|
+
|
|
42
|
+
// ── status ─────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
const statusLines = [
|
|
45
|
+
'',
|
|
46
|
+
chalk.hex(TEAL).bold(' gitwrit is running.'),
|
|
47
|
+
'',
|
|
48
|
+
boxRow('Uptime', '42m'),
|
|
49
|
+
boxRow('Watching', '~/notes, ~/projects/docs'),
|
|
50
|
+
boxRow('Branch', 'Current branch'),
|
|
51
|
+
boxRow('Commits', '12 this session'),
|
|
52
|
+
boxRow('Last commit', '26s ago (ideas.md)'),
|
|
53
|
+
boxRow('Last push', '2m ago'),
|
|
54
|
+
'',
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
console.log(boxen(statusLines.join('\n'), BOX_OPTS));
|
|
58
|
+
print.gap();
|
|
59
|
+
|
|
60
|
+
// ── stop ───────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
const stopLines = [
|
|
63
|
+
chalk.hex(TEAL).bold(' gitwrit stopped.'),
|
|
64
|
+
'',
|
|
65
|
+
chalk.dim(' Committed ') + chalk.white('12 times this session'),
|
|
66
|
+
chalk.dim(' Last push ') + chalk.white('2m ago'),
|
|
67
|
+
'',
|
|
68
|
+
chalk.dim(' Your work is safe. See you next time.'),
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
console.log(
|
|
72
|
+
boxen(stopLines.join('\n'), {
|
|
73
|
+
padding: { top: 0, bottom: 0, left: 1, right: 2 },
|
|
74
|
+
borderStyle: 'round',
|
|
75
|
+
borderColor: TEAL,
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
print.gap();
|
|
79
|
+
print.hint('This is a demo. Run gitwrit start to begin a real session.');
|
|
80
|
+
print.gap();
|
|
81
|
+
}
|
package/src/commands/help.js
CHANGED
|
@@ -17,6 +17,7 @@ const commands = [
|
|
|
17
17
|
{ name: 'remove', desc: 'Remove a directory from your watch list' },
|
|
18
18
|
{ name: '', desc: '' },
|
|
19
19
|
{ name: 'help', desc: 'List all available commands' },
|
|
20
|
+
{ name: 'demo', desc: 'Preview gitwrit\'s UI with placeholder data' },
|
|
20
21
|
];
|
|
21
22
|
|
|
22
23
|
const tips = [
|
|
@@ -25,6 +26,7 @@ const tips = [
|
|
|
25
26
|
'gitwrit commits to whatever branch you\'re on — switch branches freely.',
|
|
26
27
|
'Branch mode "autogenerated" names your session branch automatically.',
|
|
27
28
|
'gitwrit requires a configured Git remote to push your work offsite.',
|
|
29
|
+
'Run gitwrit demo to preview the UI with placeholder data — great for screenshots.',
|
|
28
30
|
];
|
|
29
31
|
|
|
30
32
|
export function help() {
|
package/src/commands/start.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import ora from 'ora';
|
|
6
6
|
|
|
7
|
-
import { print } from '../ui.js';
|
|
7
|
+
import { print, displayPath } from '../ui.js';
|
|
8
8
|
import { renderBanner } from '../banner.js';
|
|
9
9
|
import { PID_FILE, GITWRIT_DIR } from '../paths.js';
|
|
10
10
|
import { globalConfigExists, loadGlobalConfig } from '../settings.js';
|
|
@@ -103,7 +103,7 @@ export async function start() {
|
|
|
103
103
|
spinner.succeed(wasUnclean ? 'Picked up where you left off.' : 'gitwrit is running.');
|
|
104
104
|
|
|
105
105
|
print.gap();
|
|
106
|
-
print.row('Watching', config.watch.map(w => w.path).join(', '));
|
|
106
|
+
print.row('Watching', config.watch.map(w => displayPath(w.path)).join(', '));
|
|
107
107
|
print.row('Files', config.fileTypes.join(', '));
|
|
108
108
|
|
|
109
109
|
if (sessionBranch) {
|
package/src/commands/status.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'fs/promises';
|
|
2
2
|
import boxen from 'boxen';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { print, timeAgo, TEAL, PINK } from '../ui.js';
|
|
4
|
+
import { print, timeAgo, TEAL, PINK, displayPath } from '../ui.js';
|
|
5
5
|
import { PID_FILE } from '../paths.js';
|
|
6
6
|
import { readState, STATE } from '../state.js';
|
|
7
7
|
import { loadGlobalConfig, globalConfigExists } from '../settings.js';
|
|
@@ -105,7 +105,7 @@ export async function status() {
|
|
|
105
105
|
|
|
106
106
|
lines.push('');
|
|
107
107
|
lines.push(row('Uptime', formatUptime(state.startedAt)));
|
|
108
|
-
lines.push(row('Watching', config.watch.map(w => w.path).join(', ')));
|
|
108
|
+
lines.push(row('Watching', config.watch.map(w => displayPath(w.path)).join(', ')));
|
|
109
109
|
|
|
110
110
|
if (state.sessionBranch) {
|
|
111
111
|
lines.push(row('Branch', state.sessionBranch) + chalk.hex(PINK).dim(' (autogenerated)'));
|
package/src/ui.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { homedir } from 'os';
|
|
2
3
|
|
|
3
4
|
// ─── palette ──────────────────────────────────────────────────────────────────
|
|
4
5
|
|
|
@@ -59,6 +60,14 @@ export function timeAgo(date) {
|
|
|
59
60
|
return `${Math.floor(seconds / 86400)}d ago`;
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
// ─── path display ─────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
// replaces the full home directory path with ~ for cleaner, safer output.
|
|
66
|
+
// e.g. /Users/terrancebiddle/Documents/notes → ~/Documents/notes
|
|
67
|
+
export function displayPath(p) {
|
|
68
|
+
return p.replace(homedir(), '~');
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
// ─── ms formatting ────────────────────────────────────────────────────────────
|
|
63
72
|
|
|
64
73
|
export function formatMs(ms) {
|