@rewrlution/papyrus-cli 0.0.5 → 0.0.7
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 +4 -8
- package/dist/cli.js +0 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/auth/logout.js +2 -0
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/journal/app.d.ts +1 -0
- package/dist/commands/journal/app.js +22 -0
- package/dist/commands/journal/app.js.map +1 -0
- package/dist/commands/journal/edit.js +8 -10
- package/dist/commands/journal/edit.js.map +1 -1
- package/dist/commands/journal/index.js +9 -10
- package/dist/commands/journal/index.js.map +1 -1
- package/dist/commands/journal/show.js +8 -14
- package/dist/commands/journal/show.js.map +1 -1
- package/dist/components/AppLayout.d.ts +32 -0
- package/dist/components/AppLayout.js +34 -0
- package/dist/components/AppLayout.js.map +1 -0
- package/dist/components/Divider.d.ts +7 -0
- package/dist/components/Divider.js +19 -0
- package/dist/components/Divider.js.map +1 -0
- package/dist/components/JournalBrowser.d.ts +14 -0
- package/dist/components/JournalBrowser.js +102 -0
- package/dist/components/JournalBrowser.js.map +1 -0
- package/dist/components/JournalList.d.ts +16 -0
- package/dist/components/JournalList.js +73 -0
- package/dist/components/JournalList.js.map +1 -0
- package/dist/components/JournalViewer.d.ts +5 -23
- package/dist/components/JournalViewer.js +73 -80
- package/dist/components/JournalViewer.js.map +1 -1
- package/dist/components/LoginForm.js +2 -2
- package/dist/components/LoginForm.js.map +1 -1
- package/dist/components/LogoCompact.d.ts +5 -0
- package/dist/components/LogoCompact.js +11 -0
- package/dist/components/LogoCompact.js.map +1 -0
- package/dist/components/RegisterForm.js +2 -2
- package/dist/components/RegisterForm.js.map +1 -1
- package/dist/components/StatusMessage.d.ts +2 -1
- package/dist/components/StatusMessage.js +3 -3
- package/dist/components/StatusMessage.js.map +1 -1
- package/dist/lib/auth/require-auth.js +4 -5
- package/dist/lib/auth/require-auth.js.map +1 -1
- package/dist/utils/alternate-screen.d.ts +46 -0
- package/dist/utils/alternate-screen.js +60 -0
- package/dist/utils/alternate-screen.js.map +1 -0
- package/dist/utils/date.js +3 -4
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/editor.js +0 -1
- package/dist/utils/editor.js.map +1 -1
- package/dist/utils/journal-preview.d.ts +15 -0
- package/dist/utils/journal-preview.js +34 -0
- package/dist/utils/journal-preview.js.map +1 -0
- package/dist/utils/journal-preview.test.d.ts +1 -0
- package/dist/utils/journal-preview.test.js +62 -0
- package/dist/utils/journal-preview.test.js.map +1 -0
- package/dist/utils/messages.d.ts +43 -0
- package/dist/utils/messages.js +72 -0
- package/dist/utils/messages.js.map +1 -0
- package/dist/utils/text.d.ts +25 -0
- package/dist/utils/text.js +51 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/text.test.d.ts +1 -0
- package/dist/utils/text.test.js +47 -0
- package/dist/utils/text.test.js.map +1 -0
- package/package.json +3 -2
- package/dist/commands/journal/list.d.ts +0 -1
- package/dist/commands/journal/list.js +0 -20
- package/dist/commands/journal/list.js.map +0 -1
- package/dist/components/Browser.d.ts +0 -29
- package/dist/components/Browser.js +0 -124
- package/dist/components/Browser.js.map +0 -1
- package/dist/components/BrowserFooter.d.ts +0 -6
- package/dist/components/BrowserFooter.js +0 -6
- package/dist/components/BrowserFooter.js.map +0 -1
- package/dist/components/BrowserHeader.d.ts +0 -6
- package/dist/components/BrowserHeader.js +0 -6
- package/dist/components/BrowserHeader.js.map +0 -1
- package/dist/components/JournalListView.d.ts +0 -10
- package/dist/components/JournalListView.js +0 -40
- package/dist/components/JournalListView.js.map +0 -1
package/README.md
CHANGED
|
@@ -61,9 +61,7 @@ This opens your default editor. Write your thoughts, save, and close.
|
|
|
61
61
|
### 4. Browse Your Journals
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
papyrus
|
|
65
|
-
# Or use the short alias
|
|
66
|
-
papyrus ls
|
|
64
|
+
papyrus app
|
|
67
65
|
```
|
|
68
66
|
|
|
69
67
|
Use arrow keys or `j/k` to navigate, press `Enter` to read an entry.
|
|
@@ -147,14 +145,12 @@ papyrus show -d yesterday
|
|
|
147
145
|
- `0` - Jump to start of line
|
|
148
146
|
- `q` or `Esc` - Quit
|
|
149
147
|
|
|
150
|
-
#### `papyrus
|
|
148
|
+
#### `papyrus app`
|
|
151
149
|
|
|
152
|
-
|
|
150
|
+
Launch the Papyrus TUI to browse and read journal entries interactively.
|
|
153
151
|
|
|
154
152
|
```bash
|
|
155
|
-
papyrus
|
|
156
|
-
# Or
|
|
157
|
-
papyrus ls
|
|
153
|
+
papyrus app
|
|
158
154
|
```
|
|
159
155
|
|
|
160
156
|
**Features:**
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import { Command } from 'commander';
|
|
4
|
-
import { render } from 'ink';
|
|
5
3
|
import { registerJournalCommands, registerAuthCommands, } from './commands/index.js';
|
|
6
|
-
import { Logo } from './components/Logo.js';
|
|
7
|
-
render(_jsx(Logo, {}));
|
|
8
4
|
const program = new Command();
|
|
9
5
|
program
|
|
10
6
|
.name('papyrus')
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,iCAAiC,CAAC;KAC9C,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAEjC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAC7C,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAE/C,MAAM,UAAU,MAAM;IACpB,GAAG,CAAC,MAAM,EAAE,CAAC;IACb,GAAG,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function launchApp(): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { render } from 'ink';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { JournalBrowser } from '../../components/JournalBrowser.js';
|
|
5
|
+
import { journalStore } from '../../lib/storage/index.js';
|
|
6
|
+
import { withAlternateScreen } from '../../utils/alternate-screen.js';
|
|
7
|
+
import * as msg from '../../utils/messages.js';
|
|
8
|
+
export async function launchApp() {
|
|
9
|
+
try {
|
|
10
|
+
// Load all journal entries
|
|
11
|
+
const journals = journalStore.list();
|
|
12
|
+
// Render the journal browser in alternate screen
|
|
13
|
+
await withAlternateScreen(async () => {
|
|
14
|
+
const { waitUntilExit } = render(React.createElement(JournalBrowser, { journals }));
|
|
15
|
+
await waitUntilExit();
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
msg.error(`Failed to launch Papyrus app: ${error.message}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../src/commands/journal/app.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,CAAC;QACH,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;QAErC,iDAAiD;QACjD,MAAM,mBAAmB,CAAC,KAAK,IAAI,EAAE;YACnC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAC9B,KAAK,CAAC,aAAa,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC,CAClD,CAAC;YACF,MAAM,aAAa,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,iCAAiC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { journalStore } from '../../lib/storage/index.js';
|
|
3
3
|
import { formatDate, parseDate } from '../../utils/date.js';
|
|
4
4
|
import { openInEditor } from '../../utils/editor.js';
|
|
5
|
+
import * as msg from '../../utils/messages.js';
|
|
5
6
|
import { JOURNAL_TEMPATE, stripTemplateComments, } from '../../utils/template.js';
|
|
6
7
|
export function editJournalEntry(options) {
|
|
7
8
|
const { date: inputDate, createIfMissing } = options;
|
|
@@ -15,17 +16,15 @@ export function editJournalEntry(options) {
|
|
|
15
16
|
if (!entry) {
|
|
16
17
|
if (!createIfMissing) {
|
|
17
18
|
// amend: fail if entry doesn't exist
|
|
18
|
-
|
|
19
|
-
`💡 Use 'papyrus add -d ${date}' to create a new entry.\n`);
|
|
20
|
-
process.exit(1);
|
|
19
|
+
msg.error(`No journal entry found for ${date}`, `Use 'papyrus add -d ${date}' to create a new entry`);
|
|
21
20
|
}
|
|
22
21
|
// add: create new entry
|
|
23
22
|
isNew = true;
|
|
24
23
|
entry = '';
|
|
25
|
-
|
|
24
|
+
msg.sparkles(`Creating new entry for ${displayDate}...`);
|
|
26
25
|
}
|
|
27
26
|
else {
|
|
28
|
-
|
|
27
|
+
msg.info(`Loading existing entry for ${displayDate}...`);
|
|
29
28
|
}
|
|
30
29
|
// 3. Append hint comments for user guidance
|
|
31
30
|
const contentWithHints = `${entry}\n${JOURNAL_TEMPATE}`;
|
|
@@ -33,19 +32,18 @@ export function editJournalEntry(options) {
|
|
|
33
32
|
const editedContent = openInEditor(contentWithHints, `papyrus-${date}.md`);
|
|
34
33
|
const finalContent = stripTemplateComments(editedContent);
|
|
35
34
|
if (!finalContent.trim()) {
|
|
36
|
-
|
|
35
|
+
msg.warn('No content written. Entry not saved');
|
|
37
36
|
return;
|
|
38
37
|
}
|
|
39
38
|
journalStore.create(date, finalContent);
|
|
40
39
|
// 5. Show success message with stats
|
|
41
40
|
const words = countWords(finalContent);
|
|
42
41
|
const chars = finalContent.length;
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
msg.success(`Journal entry ${isNew ? 'created' : 'updated'} for ${date}`);
|
|
43
|
+
msg.stats(`Words: ${words} | Characters: ${chars}`);
|
|
45
44
|
}
|
|
46
45
|
catch (error) {
|
|
47
|
-
|
|
48
|
-
process.exit(1);
|
|
46
|
+
msg.error(error.message);
|
|
49
47
|
}
|
|
50
48
|
}
|
|
51
49
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/commands/journal/edit.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EACL,eAAe,EACf,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAOjC,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACnD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IAErD,IAAI,CAAC;QACH,6BAA6B;QAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAErC,uCAAuC;QACvC,IAAI,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,KAAK,GAAG,KAAK,CAAC;QAElB,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,qCAAqC;gBACrC,
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/commands/journal/edit.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAOjC,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACnD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IAErD,IAAI,CAAC;QACH,6BAA6B;QAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAErC,uCAAuC;QACvC,IAAI,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,KAAK,GAAG,KAAK,CAAC;QAElB,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,qCAAqC;gBACrC,GAAG,CAAC,KAAK,CACP,8BAA8B,IAAI,EAAE,EACpC,uBAAuB,IAAI,yBAAyB,CACrD,CAAC;YACJ,CAAC;YAED,wBAAwB;YACxB,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,GAAG,EAAE,CAAC;YACX,GAAG,CAAC,QAAQ,CAAC,0BAA0B,WAAW,KAAK,CAAC,CAAC;QAC3D,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,8BAA8B,WAAW,KAAK,CAAC,CAAC;QAC3D,CAAC;QAED,4CAA4C;QAC5C,MAAM,gBAAgB,GAAG,GAAG,KAAK,KAAK,eAAe,EAAE,CAAC;QAExD,iBAAiB;QACjB,MAAM,aAAa,GAAG,YAAY,CAAC,gBAAgB,EAAE,WAAW,IAAI,KAAK,CAAC,CAAC;QAC3E,MAAM,YAAY,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAC;QAE1D,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;YACzB,GAAG,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QAED,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAExC,qCAAqC;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC;QAElC,GAAG,CAAC,OAAO,CAAC,iBAAiB,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC1E,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,kBAAkB,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI;SACR,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC9C,CAAC"}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { addEntry } from './add.js';
|
|
2
2
|
import { amendEntry } from './amend.js';
|
|
3
|
-
import {
|
|
3
|
+
import { launchApp } from './app.js';
|
|
4
4
|
import { showEntry } from './show.js';
|
|
5
5
|
import { syncEntries } from './sync.js';
|
|
6
6
|
export function registerJournalCommands(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('app')
|
|
9
|
+
.description('Launch the Papyrus TUI to browse and manage journal entries')
|
|
10
|
+
.action(async () => await launchApp());
|
|
11
|
+
program
|
|
12
|
+
.command('sync')
|
|
13
|
+
.description('Sync journals with server (requires authentication)')
|
|
14
|
+
.action(async () => await syncEntries());
|
|
7
15
|
program
|
|
8
16
|
.command('add')
|
|
9
17
|
.description('Create a new journal entry')
|
|
@@ -19,14 +27,5 @@ export function registerJournalCommands(program) {
|
|
|
19
27
|
.description('Display a journal entry')
|
|
20
28
|
.option('-d, --date <date>', 'Date of the entry to show (default: today)', 'today')
|
|
21
29
|
.action(async (options) => await showEntry(options));
|
|
22
|
-
program
|
|
23
|
-
.command('list')
|
|
24
|
-
.alias('ls')
|
|
25
|
-
.description('List all local journal entries')
|
|
26
|
-
.action(async () => await listEntries());
|
|
27
|
-
program
|
|
28
|
-
.command('sync')
|
|
29
|
-
.description('Sync journals with server (requires authentication)')
|
|
30
|
-
.action(async () => await syncEntries());
|
|
31
30
|
}
|
|
32
31
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/journal/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/journal/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,6DAA6D,CAAC;SAC1E,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC;IAEzC,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,qDAAqD,CAAC;SAClE,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,WAAW,EAAE,CAAC,CAAC;IAE3C,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,mBAAmB,EAAE,qCAAqC,EAAE,OAAO,CAAC;SAC3E,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAEtD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CACL,mBAAmB,EACnB,6CAA6C,EAC7C,OAAO,CACR;SACA,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAExD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,yBAAyB,CAAC;SACtC,MAAM,CACL,mBAAmB,EACnB,4CAA4C,EAC5C,OAAO,CACR;SACA,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { render } from 'ink';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { JournalViewer } from '../../components/JournalViewer.js';
|
|
5
2
|
import { journalStore } from '../../lib/storage/index.js';
|
|
6
3
|
import { formatDate, parseDate } from '../../utils/date.js';
|
|
4
|
+
import * as msg from '../../utils/messages.js';
|
|
7
5
|
export async function showEntry(options) {
|
|
8
6
|
const dateInput = options.date;
|
|
9
7
|
try {
|
|
@@ -12,24 +10,20 @@ export async function showEntry(options) {
|
|
|
12
10
|
const displayDate = formatDate(date);
|
|
13
11
|
// 2. Check if entry exists
|
|
14
12
|
if (!journalStore.exists(date)) {
|
|
15
|
-
|
|
16
|
-
console.error(`💡 Run 'papyrus add -d ${date}' to create one.\n`);
|
|
17
|
-
process.exit(1);
|
|
13
|
+
msg.error(`No journal entry found for ${displayDate}`, `Run 'papyrus add -d ${date}' to create one`);
|
|
18
14
|
}
|
|
19
15
|
// 3. Load the entry
|
|
20
16
|
const content = journalStore.load(date);
|
|
21
17
|
if (!content) {
|
|
22
|
-
|
|
23
|
-
process.exit(1);
|
|
18
|
+
msg.error(`Failed to load journal entry for ${date}`);
|
|
24
19
|
}
|
|
25
|
-
// 4.
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
// 4. Output the journal content
|
|
21
|
+
console.log(`\n# ${displayDate}\n`);
|
|
22
|
+
console.log(content);
|
|
23
|
+
console.log('');
|
|
28
24
|
}
|
|
29
25
|
catch (error) {
|
|
30
|
-
|
|
31
|
-
console.error('💡 Use formats like: today, yesterday, tomorrow, or YYYYMMDD (e.g., 20260104)\n');
|
|
32
|
-
process.exit(1);
|
|
26
|
+
msg.error(error.message, 'Use formats like: today, yesterday, tomorrow, or YYYYMMDD (e.g., 20260104)');
|
|
33
27
|
}
|
|
34
28
|
}
|
|
35
29
|
//# sourceMappingURL=show.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"show.js","sourceRoot":"","sources":["../../../src/commands/journal/show.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"show.js","sourceRoot":"","sources":["../../../src/commands/journal/show.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAoB;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/B,IAAI,CAAC;QACH,6BAA6B;QAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAErC,2BAA2B;QAC3B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,KAAK,CACP,8BAA8B,WAAW,EAAE,EAC3C,uBAAuB,IAAI,iBAAiB,CAC7C,CAAC;QACJ,CAAC;QAED,oBAAoB;QACpB,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,gCAAgC;QAChC,OAAO,CAAC,GAAG,CAAC,OAAO,WAAW,IAAI,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CACP,KAAK,CAAC,OAAO,EACb,4EAA4E,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface AppLayoutProps {
|
|
3
|
+
headerContent: React.ReactNode;
|
|
4
|
+
footerContent: React.ReactNode;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* App layout with sticky header and footer.
|
|
9
|
+
*
|
|
10
|
+
* Provides a consistent frame for all screens:
|
|
11
|
+
* ┌────────────────────────────────┐
|
|
12
|
+
* │ Logo │
|
|
13
|
+
* │ ──────────────────────────── │
|
|
14
|
+
* │ [Custom Header Content] │ ← Passed as headerContent prop
|
|
15
|
+
* │ ──────────────────────────── │
|
|
16
|
+
* ├────────────────────────────────┤
|
|
17
|
+
* │ │
|
|
18
|
+
* │ [Children Content] │ ← Passed as children
|
|
19
|
+
* │ │
|
|
20
|
+
* ├────────────────────────────────┤
|
|
21
|
+
* │ ──────────────────────────── │
|
|
22
|
+
* │ [Custom Footer Content] │ ← Passed as footerContent prop
|
|
23
|
+
* └────────────────────────────────┘
|
|
24
|
+
*
|
|
25
|
+
* Benefits:
|
|
26
|
+
* - Reusable across all screens (list, viewer, etc.)
|
|
27
|
+
* - Consistent layout structure
|
|
28
|
+
* - Sticky header/footer with flexbox
|
|
29
|
+
* - No duplicate layout code
|
|
30
|
+
*/
|
|
31
|
+
export declare const AppLayout: React.FC<AppLayoutProps>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, useStdout } from 'ink';
|
|
3
|
+
import { Divider } from './Divider.js';
|
|
4
|
+
import { LogoCompact } from './LogoCompact.js';
|
|
5
|
+
/**
|
|
6
|
+
* App layout with sticky header and footer.
|
|
7
|
+
*
|
|
8
|
+
* Provides a consistent frame for all screens:
|
|
9
|
+
* ┌────────────────────────────────┐
|
|
10
|
+
* │ Logo │
|
|
11
|
+
* │ ──────────────────────────── │
|
|
12
|
+
* │ [Custom Header Content] │ ← Passed as headerContent prop
|
|
13
|
+
* │ ──────────────────────────── │
|
|
14
|
+
* ├────────────────────────────────┤
|
|
15
|
+
* │ │
|
|
16
|
+
* │ [Children Content] │ ← Passed as children
|
|
17
|
+
* │ │
|
|
18
|
+
* ├────────────────────────────────┤
|
|
19
|
+
* │ ──────────────────────────── │
|
|
20
|
+
* │ [Custom Footer Content] │ ← Passed as footerContent prop
|
|
21
|
+
* └────────────────────────────────┘
|
|
22
|
+
*
|
|
23
|
+
* Benefits:
|
|
24
|
+
* - Reusable across all screens (list, viewer, etc.)
|
|
25
|
+
* - Consistent layout structure
|
|
26
|
+
* - Sticky header/footer with flexbox
|
|
27
|
+
* - No duplicate layout code
|
|
28
|
+
*/
|
|
29
|
+
export const AppLayout = ({ headerContent, footerContent, children, }) => {
|
|
30
|
+
const { stdout } = useStdout();
|
|
31
|
+
const terminalHeight = stdout?.rows || 24;
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", height: terminalHeight - 2, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [_jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(LogoCompact, {}), _jsx(Divider, {}), headerContent, _jsx(Divider, {})] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, children: children }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(Divider, {}), footerContent] })] }));
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=AppLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppLayout.js","sourceRoot":"","sources":["../../src/components/AppLayout.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,SAAS,GAA6B,CAAC,EAClD,aAAa,EACb,aAAa,EACb,QAAQ,GACT,EAAE,EAAE;IACH,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,cAAc,GAAG,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;IAE1C,OAAO,CACL,MAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,MAAM,EAAE,cAAc,GAAG,CAAC,EAC1B,WAAW,EAAC,OAAO,EACnB,WAAW,EAAC,MAAM,EAClB,QAAQ,EAAE,CAAC,aAGX,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,aACvC,KAAC,WAAW,KAAG,EACf,KAAC,OAAO,KAAG,EACV,aAAa,EACd,KAAC,OAAO,KAAG,IACP,EAGN,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,YACnD,QAAQ,GACL,EAGN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,aACvC,KAAC,OAAO,KAAG,EACV,aAAa,IACV,IACF,CACP,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Horizontal divider for separating sections within a unified box
|
|
6
|
+
* Uses box-drawing characters for clean visual separation
|
|
7
|
+
* Automatically adjusts to terminal width
|
|
8
|
+
*/
|
|
9
|
+
export const Divider = () => {
|
|
10
|
+
const dividerLine = useMemo(() => {
|
|
11
|
+
const terminalWidth = process.stdout.columns || 120;
|
|
12
|
+
// Account for box border (2 chars) and padding (2 chars)
|
|
13
|
+
const availableWidth = terminalWidth - 4;
|
|
14
|
+
// Create divider line that fits exactly
|
|
15
|
+
return '─'.repeat(Math.max(0, availableWidth));
|
|
16
|
+
}, []);
|
|
17
|
+
return _jsx(Text, { dimColor: true, children: dividerLine });
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=Divider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Divider.js","sourceRoot":"","sources":["../../src/components/Divider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAc,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAa,GAAG,EAAE;IACpC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;QAC/B,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,GAAG,CAAC;QACpD,yDAAyD;QACzD,MAAM,cAAc,GAAG,aAAa,GAAG,CAAC,CAAC;QACzC,wCAAwC;QACxC,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;IACjD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,KAAC,IAAI,IAAC,QAAQ,kBAAE,WAAW,GAAQ,CAAC;AAC7C,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type JournalFileInfo } from '../lib/storage/journal-storage.js';
|
|
3
|
+
interface JournalBrowserProps {
|
|
4
|
+
journals: JournalFileInfo[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Journal browser - shows all journals with navigation.
|
|
8
|
+
*
|
|
9
|
+
* Uses AppLayout for consistent layout structure.
|
|
10
|
+
* Handles list navigation and state management.
|
|
11
|
+
* Can switch to viewer mode when opening a journal.
|
|
12
|
+
*/
|
|
13
|
+
export declare const JournalBrowser: React.FC<JournalBrowserProps>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput, useApp, useStdout } from 'ink';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { journalStore } from '../lib/storage/index.js';
|
|
5
|
+
import { getTodayDate } from '../utils/date.js';
|
|
6
|
+
import { AppLayout } from './AppLayout.js';
|
|
7
|
+
import { JournalList } from './JournalList.js';
|
|
8
|
+
import { JournalViewer } from './JournalViewer.js';
|
|
9
|
+
/**
|
|
10
|
+
* Journal browser - shows all journals with navigation.
|
|
11
|
+
*
|
|
12
|
+
* Uses AppLayout for consistent layout structure.
|
|
13
|
+
* Handles list navigation and state management.
|
|
14
|
+
* Can switch to viewer mode when opening a journal.
|
|
15
|
+
*/
|
|
16
|
+
export const JournalBrowser = ({ journals }) => {
|
|
17
|
+
const { exit } = useApp();
|
|
18
|
+
const { stdout } = useStdout();
|
|
19
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
20
|
+
const [viewMode, setViewMode] = useState('list');
|
|
21
|
+
const [viewerContent, setViewerContent] = useState('');
|
|
22
|
+
const [viewerDate, setViewerDate] = useState('');
|
|
23
|
+
const today = getTodayDate();
|
|
24
|
+
const terminalHeight = stdout?.rows || 24;
|
|
25
|
+
// Calculate available height for journal list
|
|
26
|
+
// Terminal height - header (logo 1 + divider 1 + title 1 + divider 1 = 4)
|
|
27
|
+
// - footer (divider 1 + shortcuts 1 = 2)
|
|
28
|
+
// - borders (2)
|
|
29
|
+
// - content margins (2)
|
|
30
|
+
const availableHeight = Math.max(5, terminalHeight - 10);
|
|
31
|
+
// Navigation handlers
|
|
32
|
+
const handleNavigateUp = () => {
|
|
33
|
+
setSelectedIndex((current) => {
|
|
34
|
+
const next = current - 1;
|
|
35
|
+
// Circular navigation: wrap to bottom if at top
|
|
36
|
+
return next < 0 ? journals.length - 1 : next;
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const handleNavigateDown = () => {
|
|
40
|
+
setSelectedIndex((current) => {
|
|
41
|
+
const next = current + 1;
|
|
42
|
+
// Circular navigation: wrap to top if at bottom
|
|
43
|
+
return next >= journals.length ? 0 : next;
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const handleOpenJournal = () => {
|
|
47
|
+
const journal = journals[selectedIndex];
|
|
48
|
+
if (journal) {
|
|
49
|
+
try {
|
|
50
|
+
const content = journalStore.load(journal.date);
|
|
51
|
+
if (content) {
|
|
52
|
+
setViewerDate(journal.date);
|
|
53
|
+
setViewerContent(content);
|
|
54
|
+
setViewMode('viewer');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error(`Failed to load journal: ${error}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const handleBackToList = () => {
|
|
63
|
+
setViewMode('list');
|
|
64
|
+
setViewerContent('');
|
|
65
|
+
setViewerDate('');
|
|
66
|
+
};
|
|
67
|
+
// Keyboard input handlers (only for list view)
|
|
68
|
+
useInput((input, key) => {
|
|
69
|
+
// Only handle keyboard in list view
|
|
70
|
+
if (viewMode !== 'list')
|
|
71
|
+
return;
|
|
72
|
+
// Navigate up (↑ or k)
|
|
73
|
+
if (key.upArrow || input === 'k') {
|
|
74
|
+
handleNavigateUp();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Navigate down (↓ or j)
|
|
78
|
+
if (key.downArrow || input === 'j') {
|
|
79
|
+
handleNavigateDown();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Open journal (Enter)
|
|
83
|
+
if (key.return) {
|
|
84
|
+
handleOpenJournal();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Quit (q or Escape)
|
|
88
|
+
if (input === 'q' || key.escape) {
|
|
89
|
+
exit();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// Viewer mode - show journal content
|
|
94
|
+
if (viewMode === 'viewer' && viewerDate && viewerContent) {
|
|
95
|
+
return (_jsx(JournalViewer, { date: viewerDate, content: viewerContent, onExit: handleBackToList }));
|
|
96
|
+
}
|
|
97
|
+
// List mode - show journal list
|
|
98
|
+
const headerContent = (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: "Browse Your Journals" }), _jsxs(Text, { dimColor: true, children: [" (", journals.length, " entries)"] })] }));
|
|
99
|
+
const footerContent = (_jsx(Text, { dimColor: true, children: "\u2191\u2193/jk Navigate \u2022 Enter Read \u2022 q Quit" }));
|
|
100
|
+
return (_jsx(AppLayout, { headerContent: headerContent, footerContent: footerContent, children: _jsx(JournalList, { journals: journals, selectedIndex: selectedIndex, todayDate: today, availableHeight: availableHeight }) }));
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=JournalBrowser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JournalBrowser.js","sourceRoot":"","sources":["../../src/components/JournalBrowser.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type JournalFileInfo } from '../lib/storage/journal-storage.js';
|
|
3
|
+
interface JournalListProps {
|
|
4
|
+
journals: JournalFileInfo[];
|
|
5
|
+
selectedIndex: number;
|
|
6
|
+
todayDate: string;
|
|
7
|
+
availableHeight: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Journal list view with viewport-based rendering.
|
|
11
|
+
*
|
|
12
|
+
* Only renders journals that fit in the available height, centered around
|
|
13
|
+
* the selected item. Shows date and content preview for each journal.
|
|
14
|
+
*/
|
|
15
|
+
export declare const JournalList: React.FC<JournalListProps>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useStdout } from 'ink';
|
|
3
|
+
import { journalStore } from '../lib/storage/index.js';
|
|
4
|
+
import { formatDate } from '../utils/date.js';
|
|
5
|
+
import { extractPreview } from '../utils/journal-preview.js';
|
|
6
|
+
import { truncateToWidth, padToWidth } from '../utils/text.js';
|
|
7
|
+
// Column widths for alignment
|
|
8
|
+
const SELECTION_INDICATOR_WIDTH = 2; // "> " or " "
|
|
9
|
+
const DATE_COLUMN_WIDTH = 27; // "December 31, 2025 ●" = 20 chars + padding
|
|
10
|
+
const BORDER_AND_PADDING = 4; // Box borders and padding
|
|
11
|
+
/**
|
|
12
|
+
* Journal list view with viewport-based rendering.
|
|
13
|
+
*
|
|
14
|
+
* Only renders journals that fit in the available height, centered around
|
|
15
|
+
* the selected item. Shows date and content preview for each journal.
|
|
16
|
+
*/
|
|
17
|
+
export const JournalList = ({ journals, selectedIndex, todayDate, availableHeight, }) => {
|
|
18
|
+
const { stdout } = useStdout();
|
|
19
|
+
const terminalWidth = stdout?.columns || 120;
|
|
20
|
+
// Calculate which journals to show (viewport around selection)
|
|
21
|
+
const getVisibleJournals = () => {
|
|
22
|
+
const maxItems = Math.max(3, availableHeight); // At least show 3 items
|
|
23
|
+
if (journals.length <= maxItems) {
|
|
24
|
+
// All journals fit, show everything
|
|
25
|
+
return { visible: journals, startIndex: 0 };
|
|
26
|
+
}
|
|
27
|
+
// Center the selection in the viewport
|
|
28
|
+
const halfWindow = Math.floor(maxItems / 2);
|
|
29
|
+
let start = selectedIndex - halfWindow;
|
|
30
|
+
// Keep viewport within bounds
|
|
31
|
+
if (start < 0) {
|
|
32
|
+
start = 0;
|
|
33
|
+
}
|
|
34
|
+
else if (start + maxItems > journals.length) {
|
|
35
|
+
start = journals.length - maxItems;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
visible: journals.slice(start, start + maxItems),
|
|
39
|
+
startIndex: start,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
const { visible, startIndex } = getVisibleJournals();
|
|
43
|
+
// Calculate available width for preview
|
|
44
|
+
const availableWidthForPreview = terminalWidth -
|
|
45
|
+
SELECTION_INDICATOR_WIDTH -
|
|
46
|
+
DATE_COLUMN_WIDTH -
|
|
47
|
+
BORDER_AND_PADDING;
|
|
48
|
+
return (_jsx(Box, { flexDirection: "column", children: visible.map((journal, viewportIndex) => {
|
|
49
|
+
const actualIndex = startIndex + viewportIndex;
|
|
50
|
+
const isSelected = actualIndex === selectedIndex;
|
|
51
|
+
const isToday = journal.date === todayDate;
|
|
52
|
+
const formattedDate = formatDate(journal.date);
|
|
53
|
+
// Load journal content and extract preview
|
|
54
|
+
let preview = '';
|
|
55
|
+
try {
|
|
56
|
+
const content = journalStore.load(journal.date);
|
|
57
|
+
if (content) {
|
|
58
|
+
preview = extractPreview(content);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
preview = '(error loading)';
|
|
63
|
+
}
|
|
64
|
+
// Format date with today indicator as one unit
|
|
65
|
+
const dateWithIndicator = isToday
|
|
66
|
+
? `${formattedDate} ●`
|
|
67
|
+
: formattedDate;
|
|
68
|
+
const paddedDate = padToWidth(dateWithIndicator, DATE_COLUMN_WIDTH);
|
|
69
|
+
const truncatedPreview = truncateToWidth(preview, availableWidthForPreview);
|
|
70
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? 'cyan' : 'white', children: isSelected ? '> ' : ' ' }), _jsx(Text, { color: isSelected ? 'cyan' : 'white', children: paddedDate }), _jsx(Text, { dimColor: true, children: truncatedPreview })] }, journal.date));
|
|
71
|
+
}) }));
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=JournalList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JournalList.js","sourceRoot":"","sources":["../../src/components/JournalList.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,32 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
interface JournalViewerProps {
|
|
2
3
|
date: string;
|
|
3
4
|
content: string;
|
|
4
5
|
onExit?: () => void;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Journal viewer - displays full journal content with scrolling.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* - Horizontal panning for long lines (no text wrapping)
|
|
12
|
-
* - Line numbers for easy reference
|
|
13
|
-
* - Sticky header with date and position indicator
|
|
14
|
-
* - Sticky footer with keyboard shortcuts
|
|
15
|
-
* - Multiple navigation methods (arrows, vim keys, page up/down)
|
|
16
|
-
*
|
|
17
|
-
* Architecture:
|
|
18
|
-
* 1. Split content into lines (memoized for performance)
|
|
19
|
-
* 2. Calculate viewport dimensions based on terminal size
|
|
20
|
-
* 3. Track scroll position (vertical and horizontal)
|
|
21
|
-
* 4. Only render visible slice of content (virtual scrolling)
|
|
22
|
-
* 5. Update scroll position on keyboard input
|
|
23
|
-
*
|
|
24
|
-
* Key Design Decisions:
|
|
25
|
-
* - Each content line = 1 display row (no wrapping, use horizontal panning instead)
|
|
26
|
-
* - Line numbers always correspond to content lines (1:1 mapping)
|
|
27
|
-
* - Progress calculated from last visible line (shows 100% when viewing all content)
|
|
28
|
-
* - Auto-reset horizontal position when moving vertically (better UX)
|
|
29
|
-
* - React keys use line numbers (unique, stable identifiers)
|
|
10
|
+
* Uses AppLayout for consistent layout structure.
|
|
11
|
+
* Handles viewer-specific navigation and scrolling.
|
|
30
12
|
*/
|
|
31
|
-
export declare const JournalViewer:
|
|
13
|
+
export declare const JournalViewer: React.FC<JournalViewerProps>;
|
|
32
14
|
export {};
|