epoch-tui 0.1.6 → 0.1.8
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 +88 -0
- package/dist/index.js +210 -57
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Epoch
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Epoch is a modern, terminal-based task logger and time tracker built with TypeScript, React, and Ink. It features a three-pane layout designed for efficiency and keyboard-centric workflows, allowing you to manage nested tasks and track your time without ever leaving the terminal.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Three-Pane Layout**: Seamlessly switch between Calendar, Tasks, and Timeline views.
|
|
10
|
+
- **Calendar View**: Navigate through months and days to visualize task distribution and select specific dates.
|
|
11
|
+
- **Infinite Nesting**: Break down complex projects with support for infinitely nested subtasks.
|
|
12
|
+
- **Activity Timeline**: Automatically logs every action (start, complete, delegate) with precise timestamps.
|
|
13
|
+
- **Task States**: Track tasks through 'todo', 'completed', 'delegated', and 'delayed' states.
|
|
14
|
+
- **Extensible Themes**: Comes with built-in dark and light themes (plus 20+ community themes like Catppuccin, Nord, Dracula).
|
|
15
|
+
- **Local Persistence**: Data is saved locally in JSON format for easy backup, portability, and privacy.
|
|
16
|
+
|
|
17
|
+
## Installation & Running
|
|
18
|
+
|
|
19
|
+
### Prerequisites
|
|
20
|
+
- Node.js (v16+ recommended)
|
|
21
|
+
- pnpm
|
|
22
|
+
|
|
23
|
+
### Setup
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Install dependencies
|
|
27
|
+
pnpm install
|
|
28
|
+
|
|
29
|
+
# Run in development mode (with hot reload)
|
|
30
|
+
pnpm dev
|
|
31
|
+
|
|
32
|
+
# Build and start production version
|
|
33
|
+
pnpm build
|
|
34
|
+
pnpm start
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Keyboard Shortcuts
|
|
38
|
+
|
|
39
|
+
Epoch is designed to be used entirely without a mouse.
|
|
40
|
+
|
|
41
|
+
### Global Navigation
|
|
42
|
+
- `1` / `2` / `3`: Switch directly to Calendar / Tasks / Timeline panes
|
|
43
|
+
- `Tab` / `Shift+Tab`: Cycle through panes
|
|
44
|
+
- `?`: Toggle help dialog
|
|
45
|
+
- `q`: Quit application
|
|
46
|
+
|
|
47
|
+
### Calendar Pane
|
|
48
|
+
- `j` / `k` (or `↓` / `↑`): Navigate weeks
|
|
49
|
+
- `h` / `l` (or `←` / `→`): Navigate days
|
|
50
|
+
- `n` / `p`: Next / Previous month
|
|
51
|
+
- `Enter`: Select date
|
|
52
|
+
|
|
53
|
+
### Tasks Pane
|
|
54
|
+
- `a`: Add new task
|
|
55
|
+
- `e`: Edit task title
|
|
56
|
+
- `d`: Delete task
|
|
57
|
+
- `Space`: Toggle completion status
|
|
58
|
+
- `s`: Start task (sets start time)
|
|
59
|
+
- `D`: Mark as delegated
|
|
60
|
+
- `x`: Mark as delayed/cancelled
|
|
61
|
+
- `Tab`: Indent task (convert to subtask)
|
|
62
|
+
- `Shift+Tab`: Unindent task
|
|
63
|
+
- `Enter`: Expand/Collapse subtasks
|
|
64
|
+
|
|
65
|
+
### Timeline Pane
|
|
66
|
+
- `j` / `k`: Scroll through activity history
|
|
67
|
+
- `t`: Toggle theme (Dark/Light)
|
|
68
|
+
|
|
69
|
+
## Data Storage
|
|
70
|
+
|
|
71
|
+
Your data is stored locally in a human-readable JSON file. This allows for easy backups or manual editing if necessary.
|
|
72
|
+
|
|
73
|
+
- **macOS**: `~/Library/Application Support/epoch/data.json`
|
|
74
|
+
- **Linux**: `~/.local/share/epoch/data.json` (or `$XDG_DATA_HOME`)
|
|
75
|
+
- **Windows**: `%APPDATA%\epoch\data.json`
|
|
76
|
+
|
|
77
|
+
## Tech Stack
|
|
78
|
+
|
|
79
|
+
- **UI Framework**: React + Ink
|
|
80
|
+
- **Language**: TypeScript
|
|
81
|
+
- **State Management**: React Context
|
|
82
|
+
- **Date Handling**: date-fns
|
|
83
|
+
- **Persistence**: File System (JSON)
|
|
84
|
+
|
|
85
|
+
## Author
|
|
86
|
+
|
|
87
|
+
Created by [Akshat Dubey](mailto:akshatdubey0808@gmail.com).
|
|
88
|
+
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/App.tsx
|
|
7
|
-
import { Box as
|
|
7
|
+
import { Box as Box18, Text as Text15 } from "ink";
|
|
8
8
|
|
|
9
9
|
// src/contexts/ThemeContext.tsx
|
|
10
10
|
import { createContext as createContext2, useContext as useContext2, useState as useState2, useEffect as useEffect2 } from "react";
|
|
@@ -1769,6 +1769,30 @@ var useUndo = () => {
|
|
|
1769
1769
|
return context;
|
|
1770
1770
|
};
|
|
1771
1771
|
|
|
1772
|
+
// src/utils/version.ts
|
|
1773
|
+
var CURRENT_VERSION = "0.1.8";
|
|
1774
|
+
var PACKAGE_NAME = "epoch-tui";
|
|
1775
|
+
async function checkForUpdate() {
|
|
1776
|
+
try {
|
|
1777
|
+
const res = await fetch(
|
|
1778
|
+
`https://registry.npmjs.org/${PACKAGE_NAME}/latest`
|
|
1779
|
+
);
|
|
1780
|
+
const data = await res.json();
|
|
1781
|
+
const latestVersion = data.version;
|
|
1782
|
+
return {
|
|
1783
|
+
hasUpdate: latestVersion !== CURRENT_VERSION,
|
|
1784
|
+
currentVersion: CURRENT_VERSION,
|
|
1785
|
+
latestVersion
|
|
1786
|
+
};
|
|
1787
|
+
} catch {
|
|
1788
|
+
return {
|
|
1789
|
+
hasUpdate: false,
|
|
1790
|
+
currentVersion: CURRENT_VERSION,
|
|
1791
|
+
latestVersion: CURRENT_VERSION
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1772
1796
|
// src/contexts/AppContext.tsx
|
|
1773
1797
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1774
1798
|
var AppContext = createContext4(void 0);
|
|
@@ -1802,6 +1826,8 @@ var AppProvider = ({ children }) => {
|
|
|
1802
1826
|
const [exitConfirmation, setExitConfirmation] = useState4(false);
|
|
1803
1827
|
const [showThemeDialog, setShowThemeDialog] = useState4(false);
|
|
1804
1828
|
const [showClearTimelineDialog, setShowClearTimelineDialog] = useState4(false);
|
|
1829
|
+
const [showUpdateDialog, setShowUpdateDialog] = useState4(false);
|
|
1830
|
+
const [updateInfo, setUpdateInfo] = useState4(null);
|
|
1805
1831
|
const clearTimelineForDate = useCallback3((dateStr) => {
|
|
1806
1832
|
pushUndoAction("TIMELINE_CLEAR", tasks, timeline);
|
|
1807
1833
|
setTimeline((prev) => {
|
|
@@ -1823,6 +1849,21 @@ var AppProvider = ({ children }) => {
|
|
|
1823
1849
|
setTimeline(action.previousTimeline);
|
|
1824
1850
|
}
|
|
1825
1851
|
}, [undo]);
|
|
1852
|
+
const skipVersion = useCallback3(
|
|
1853
|
+
(version) => {
|
|
1854
|
+
if (data) {
|
|
1855
|
+
save({
|
|
1856
|
+
...data,
|
|
1857
|
+
settings: {
|
|
1858
|
+
...data.settings,
|
|
1859
|
+
skippedVersion: version
|
|
1860
|
+
}
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
setShowUpdateDialog(false);
|
|
1864
|
+
},
|
|
1865
|
+
[data, save]
|
|
1866
|
+
);
|
|
1826
1867
|
const initialLoadDone = useRef2(false);
|
|
1827
1868
|
const dataRef = useRef2(data);
|
|
1828
1869
|
const saveRef = useRef2(save);
|
|
@@ -1835,6 +1876,15 @@ var AppProvider = ({ children }) => {
|
|
|
1835
1876
|
setTasks(data.tasks);
|
|
1836
1877
|
setTimeline(data.timeline);
|
|
1837
1878
|
initialLoadDone.current = true;
|
|
1879
|
+
checkForUpdate().then((info) => {
|
|
1880
|
+
if (info.hasUpdate) {
|
|
1881
|
+
const skippedVersion = data.settings?.skippedVersion;
|
|
1882
|
+
if (skippedVersion !== info.latestVersion) {
|
|
1883
|
+
setUpdateInfo(info);
|
|
1884
|
+
setShowUpdateDialog(true);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1838
1888
|
}
|
|
1839
1889
|
}, [data]);
|
|
1840
1890
|
useEffect3(() => {
|
|
@@ -1873,11 +1923,15 @@ var AppProvider = ({ children }) => {
|
|
|
1873
1923
|
showClearTimelineDialog,
|
|
1874
1924
|
setShowClearTimelineDialog,
|
|
1875
1925
|
clearTimelineForDate,
|
|
1876
|
-
isModalOpen: showHelp || showThemeDialog || showOverview || showClearTimelineDialog,
|
|
1926
|
+
isModalOpen: showHelp || showThemeDialog || showOverview || showClearTimelineDialog || showUpdateDialog,
|
|
1877
1927
|
saveNow,
|
|
1878
1928
|
pushUndoableAction,
|
|
1879
1929
|
performUndo,
|
|
1880
|
-
canUndo
|
|
1930
|
+
canUndo,
|
|
1931
|
+
showUpdateDialog,
|
|
1932
|
+
setShowUpdateDialog,
|
|
1933
|
+
updateInfo,
|
|
1934
|
+
skipVersion
|
|
1881
1935
|
},
|
|
1882
1936
|
children
|
|
1883
1937
|
}
|
|
@@ -2827,9 +2881,9 @@ var TasksPane = () => {
|
|
|
2827
2881
|
const visibleRows = useMemo(() => {
|
|
2828
2882
|
return Math.max(5, terminalHeight - 11);
|
|
2829
2883
|
}, [terminalHeight]);
|
|
2830
|
-
const
|
|
2831
|
-
|
|
2832
|
-
);
|
|
2884
|
+
const selectedDateObj = new Date(selectedDate.year, selectedDate.month, selectedDate.day);
|
|
2885
|
+
const dateStr = getDateString(selectedDateObj);
|
|
2886
|
+
const isSelectedDateToday = isToday(selectedDateObj);
|
|
2833
2887
|
const dayTasks = useMemo(() => tasks[dateStr] || [], [tasks, dateStr]);
|
|
2834
2888
|
const stats = taskService.getTaskStats(tasks, dateStr);
|
|
2835
2889
|
const isFocused = activePane === "tasks" && !isModalOpen;
|
|
@@ -2951,7 +3005,7 @@ var TasksPane = () => {
|
|
|
2951
3005
|
eventTypeToRemove[previousState]
|
|
2952
3006
|
);
|
|
2953
3007
|
setTimeline(updatedTimeline);
|
|
2954
|
-
} else {
|
|
3008
|
+
} else if (isSelectedDateToday) {
|
|
2955
3009
|
const eventTypeMap = {
|
|
2956
3010
|
todo: "started" /* STARTED */,
|
|
2957
3011
|
// shouldn't happen
|
|
@@ -3145,14 +3199,16 @@ var TasksPane = () => {
|
|
|
3145
3199
|
} else {
|
|
3146
3200
|
const updated = taskService.startTask(tasks, selectedTaskId);
|
|
3147
3201
|
setTasks(updated);
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3202
|
+
if (isSelectedDateToday) {
|
|
3203
|
+
const event = timelineService.createEvent(
|
|
3204
|
+
selectedTaskId,
|
|
3205
|
+
selectedTask.title,
|
|
3206
|
+
"started" /* STARTED */,
|
|
3207
|
+
/* @__PURE__ */ new Date()
|
|
3208
|
+
);
|
|
3209
|
+
const updatedTimeline = timelineService.addEvent(timeline, event);
|
|
3210
|
+
setTimeline(updatedTimeline);
|
|
3211
|
+
}
|
|
3156
3212
|
}
|
|
3157
3213
|
} catch (err) {
|
|
3158
3214
|
console.error("Error toggling task start:", err);
|
|
@@ -3850,15 +3906,97 @@ var ClearTimelineDialog = () => {
|
|
|
3850
3906
|
) });
|
|
3851
3907
|
};
|
|
3852
3908
|
|
|
3853
|
-
// src/components/
|
|
3854
|
-
import
|
|
3909
|
+
// src/components/common/UpdateDialog.tsx
|
|
3910
|
+
import { useState as useState10 } from "react";
|
|
3855
3911
|
import { Box as Box16, Text as Text13, useInput as useInput8 } from "ink";
|
|
3856
|
-
import { startOfMonth as startOfMonth2, endOfMonth as endOfMonth2, eachDayOfInterval as eachDayOfInterval2 } from "date-fns";
|
|
3857
3912
|
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3913
|
+
var UpdateDialog = ({
|
|
3914
|
+
currentVersion,
|
|
3915
|
+
latestVersion,
|
|
3916
|
+
onDismiss,
|
|
3917
|
+
onSkipVersion
|
|
3918
|
+
}) => {
|
|
3919
|
+
const { theme } = useTheme();
|
|
3920
|
+
const [selectedOption, setSelectedOption] = useState10(0);
|
|
3921
|
+
const options = [
|
|
3922
|
+
{ label: "Dismiss", action: onDismiss },
|
|
3923
|
+
{ label: "Skip this version", action: () => onSkipVersion(latestVersion) }
|
|
3924
|
+
];
|
|
3925
|
+
useInput8((input, key) => {
|
|
3926
|
+
if (key.escape) {
|
|
3927
|
+
onDismiss();
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
if (key.upArrow || input === "k") {
|
|
3931
|
+
setSelectedOption((prev) => prev > 0 ? prev - 1 : prev);
|
|
3932
|
+
return;
|
|
3933
|
+
}
|
|
3934
|
+
if (key.downArrow || input === "j") {
|
|
3935
|
+
setSelectedOption((prev) => prev < options.length - 1 ? prev + 1 : prev);
|
|
3936
|
+
return;
|
|
3937
|
+
}
|
|
3938
|
+
if (key.return) {
|
|
3939
|
+
options[selectedOption].action();
|
|
3940
|
+
return;
|
|
3941
|
+
}
|
|
3942
|
+
});
|
|
3943
|
+
return /* @__PURE__ */ jsx22(Modal, { children: /* @__PURE__ */ jsxs15(
|
|
3944
|
+
Box16,
|
|
3945
|
+
{
|
|
3946
|
+
flexDirection: "column",
|
|
3947
|
+
borderStyle: "double",
|
|
3948
|
+
borderColor: theme.colors.helpDialogBorder,
|
|
3949
|
+
paddingX: 2,
|
|
3950
|
+
paddingY: 1,
|
|
3951
|
+
width: 50,
|
|
3952
|
+
backgroundColor: theme.colors.modalBackground || theme.colors.background,
|
|
3953
|
+
children: [
|
|
3954
|
+
/* @__PURE__ */ jsx22(Text13, { bold: true, color: theme.colors.calendarHeader, children: "Update Available" }),
|
|
3955
|
+
/* @__PURE__ */ jsxs15(Box16, { marginTop: 1, flexDirection: "column", children: [
|
|
3956
|
+
/* @__PURE__ */ jsx22(Text13, { color: theme.colors.foreground, children: "A new version of Epoch is available!" }),
|
|
3957
|
+
/* @__PURE__ */ jsxs15(Box16, { marginTop: 1, children: [
|
|
3958
|
+
/* @__PURE__ */ jsxs15(Text13, { color: theme.colors.foreground, dimColor: true, children: [
|
|
3959
|
+
"Current:",
|
|
3960
|
+
" "
|
|
3961
|
+
] }),
|
|
3962
|
+
/* @__PURE__ */ jsx22(Text13, { color: theme.colors.taskDelayed, children: currentVersion }),
|
|
3963
|
+
/* @__PURE__ */ jsx22(Text13, { color: theme.colors.foreground, dimColor: true, children: " \u2192 " }),
|
|
3964
|
+
/* @__PURE__ */ jsx22(Text13, { color: theme.colors.taskCompleted, bold: true, children: latestVersion })
|
|
3965
|
+
] })
|
|
3966
|
+
] }),
|
|
3967
|
+
/* @__PURE__ */ jsxs15(Box16, { marginTop: 1, flexDirection: "column", children: [
|
|
3968
|
+
/* @__PURE__ */ jsx22(Text13, { color: theme.colors.foreground, dimColor: true, children: "To update, run:" }),
|
|
3969
|
+
/* @__PURE__ */ jsx22(Box16, { marginTop: 0, paddingLeft: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.timelineEventStarted, children: "npm install -g epoch-tui@latest" }) })
|
|
3970
|
+
] }),
|
|
3971
|
+
/* @__PURE__ */ jsx22(Box16, { marginTop: 1, flexDirection: "column", children: options.map((option, idx) => {
|
|
3972
|
+
const isSelected = idx === selectedOption;
|
|
3973
|
+
return /* @__PURE__ */ jsx22(Box16, { children: /* @__PURE__ */ jsxs15(
|
|
3974
|
+
Text13,
|
|
3975
|
+
{
|
|
3976
|
+
color: isSelected ? theme.colors.focusIndicator : theme.colors.foreground,
|
|
3977
|
+
bold: isSelected,
|
|
3978
|
+
children: [
|
|
3979
|
+
isSelected ? "\u279C " : " ",
|
|
3980
|
+
option.label
|
|
3981
|
+
]
|
|
3982
|
+
}
|
|
3983
|
+
) }, option.label);
|
|
3984
|
+
}) }),
|
|
3985
|
+
/* @__PURE__ */ jsx22(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "\u2191/\u2193 to navigate \u2022 Enter to select \u2022 Esc to dismiss" }) })
|
|
3986
|
+
]
|
|
3987
|
+
}
|
|
3988
|
+
) });
|
|
3989
|
+
};
|
|
3990
|
+
|
|
3991
|
+
// src/components/overview/OverviewScreen.tsx
|
|
3992
|
+
import React11, { useMemo as useMemo4 } from "react";
|
|
3993
|
+
import { Box as Box17, Text as Text14, useInput as useInput9 } from "ink";
|
|
3994
|
+
import { startOfMonth as startOfMonth2, endOfMonth as endOfMonth2, eachDayOfInterval as eachDayOfInterval2 } from "date-fns";
|
|
3995
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3858
3996
|
var OverviewScreen = () => {
|
|
3859
3997
|
const { theme } = useTheme();
|
|
3860
3998
|
const { tasks, overviewMonth, setOverviewMonth, setShowOverview } = useApp();
|
|
3861
|
-
const [scrollOffset, setScrollOffset] =
|
|
3999
|
+
const [scrollOffset, setScrollOffset] = React11.useState(0);
|
|
3862
4000
|
const { height: terminalHeight } = useTerminalSize();
|
|
3863
4001
|
const visibleRows = useMemo4(() => {
|
|
3864
4002
|
return Math.max(2, Math.floor((terminalHeight - 9) / 3));
|
|
@@ -3894,10 +4032,10 @@ var OverviewScreen = () => {
|
|
|
3894
4032
|
setOverviewMonth({ ...overviewMonth, month: newMonth });
|
|
3895
4033
|
}
|
|
3896
4034
|
};
|
|
3897
|
-
|
|
4035
|
+
React11.useEffect(() => {
|
|
3898
4036
|
setScrollOffset(0);
|
|
3899
4037
|
}, [overviewMonth]);
|
|
3900
|
-
|
|
4038
|
+
useInput9((input, key) => {
|
|
3901
4039
|
if (key.escape) {
|
|
3902
4040
|
setShowOverview(false);
|
|
3903
4041
|
return;
|
|
@@ -3933,21 +4071,21 @@ var OverviewScreen = () => {
|
|
|
3933
4071
|
);
|
|
3934
4072
|
const canScrollUp = scrollOffset > 0;
|
|
3935
4073
|
const canScrollDown = scrollOffset + visibleRows < rows;
|
|
3936
|
-
return /* @__PURE__ */
|
|
3937
|
-
/* @__PURE__ */
|
|
3938
|
-
/* @__PURE__ */
|
|
3939
|
-
/* @__PURE__ */
|
|
4074
|
+
return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", padding: 1, width: "100%", height: "100%", children: [
|
|
4075
|
+
/* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginBottom: 1, children: [
|
|
4076
|
+
/* @__PURE__ */ jsx23(Text14, { bold: true, color: theme.colors.focusIndicator, children: "Overview" }),
|
|
4077
|
+
/* @__PURE__ */ jsx23(Text14, { color: theme.colors.foreground, children: monthName })
|
|
3940
4078
|
] }),
|
|
3941
|
-
/* @__PURE__ */
|
|
3942
|
-
canScrollUp && /* @__PURE__ */
|
|
4079
|
+
/* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", flexGrow: 1, children: [
|
|
4080
|
+
canScrollUp && /* @__PURE__ */ jsx23(Box17, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx23(Text14, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
|
|
3943
4081
|
visibleRowData.map((_, index) => {
|
|
3944
4082
|
const rowIndex = scrollOffset + index;
|
|
3945
|
-
return /* @__PURE__ */
|
|
4083
|
+
return /* @__PURE__ */ jsx23(Box17, { flexDirection: "row", marginBottom: 1, children: Array.from({ length: columns }).map((_2, colIndex) => {
|
|
3946
4084
|
const dateIndex = rowIndex * columns + colIndex;
|
|
3947
4085
|
const date = monthDates[dateIndex];
|
|
3948
4086
|
if (!date) {
|
|
3949
|
-
return /* @__PURE__ */
|
|
3950
|
-
|
|
4087
|
+
return /* @__PURE__ */ jsx23(
|
|
4088
|
+
Box17,
|
|
3951
4089
|
{
|
|
3952
4090
|
flexDirection: "column",
|
|
3953
4091
|
flexGrow: 1,
|
|
@@ -3967,17 +4105,17 @@ var OverviewScreen = () => {
|
|
|
3967
4105
|
}
|
|
3968
4106
|
};
|
|
3969
4107
|
traverse(dayTasks, 0);
|
|
3970
|
-
return /* @__PURE__ */
|
|
3971
|
-
|
|
4108
|
+
return /* @__PURE__ */ jsxs16(
|
|
4109
|
+
Box17,
|
|
3972
4110
|
{
|
|
3973
4111
|
flexDirection: "column",
|
|
3974
4112
|
flexGrow: 1,
|
|
3975
4113
|
flexBasis: 0,
|
|
3976
4114
|
marginRight: colIndex === columns - 1 ? 0 : 2,
|
|
3977
4115
|
children: [
|
|
3978
|
-
/* @__PURE__ */
|
|
3979
|
-
/* @__PURE__ */
|
|
3980
|
-
flatTasksWithDepth.length === 0 ? /* @__PURE__ */
|
|
4116
|
+
/* @__PURE__ */ jsx23(Text14, { bold: true, color: theme.colors.calendarSelected, children: formatDate(date, "do MMM") }),
|
|
4117
|
+
/* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
|
|
4118
|
+
flatTasksWithDepth.length === 0 ? /* @__PURE__ */ jsx23(Text14, { dimColor: true, color: theme.colors.keyboardHint, children: "No tasks" }) : flatTasksWithDepth.slice(0, 10).map(({ task, depth }) => /* @__PURE__ */ jsx23(
|
|
3981
4119
|
TaskItem,
|
|
3982
4120
|
{
|
|
3983
4121
|
task,
|
|
@@ -3986,7 +4124,7 @@ var OverviewScreen = () => {
|
|
|
3986
4124
|
},
|
|
3987
4125
|
task.id
|
|
3988
4126
|
)),
|
|
3989
|
-
flatTasksWithDepth.length > 10 && /* @__PURE__ */
|
|
4127
|
+
flatTasksWithDepth.length > 10 && /* @__PURE__ */ jsxs16(Text14, { dimColor: true, color: theme.colors.keyboardHint, children: [
|
|
3990
4128
|
"+",
|
|
3991
4129
|
flatTasksWithDepth.length - 10,
|
|
3992
4130
|
" more..."
|
|
@@ -3998,22 +4136,22 @@ var OverviewScreen = () => {
|
|
|
3998
4136
|
);
|
|
3999
4137
|
}) }, rowIndex);
|
|
4000
4138
|
}),
|
|
4001
|
-
canScrollDown && /* @__PURE__ */
|
|
4139
|
+
canScrollDown && /* @__PURE__ */ jsx23(Box17, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx23(Text14, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
|
|
4002
4140
|
] }),
|
|
4003
|
-
/* @__PURE__ */
|
|
4141
|
+
/* @__PURE__ */ jsx23(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text14, { color: theme.colors.keyboardHint, dimColor: true, children: "n/p or \u2190/\u2192: month | j/k or \u2193/\u2191: scroll | Esc: close | Shift+;: toggle" }) })
|
|
4004
4142
|
] });
|
|
4005
4143
|
};
|
|
4006
4144
|
var TaskItem = ({ task, theme, depth }) => {
|
|
4007
4145
|
const checkbox = getCheckbox2(task.state);
|
|
4008
4146
|
const color = getStateColor2(task.state, theme);
|
|
4009
|
-
return /* @__PURE__ */
|
|
4010
|
-
/* @__PURE__ */
|
|
4147
|
+
return /* @__PURE__ */ jsxs16(Box17, { children: [
|
|
4148
|
+
/* @__PURE__ */ jsxs16(Text14, { color, children: [
|
|
4011
4149
|
checkbox,
|
|
4012
4150
|
" "
|
|
4013
4151
|
] }),
|
|
4014
|
-
depth > 0 && /* @__PURE__ */
|
|
4015
|
-
/* @__PURE__ */
|
|
4016
|
-
|
|
4152
|
+
depth > 0 && /* @__PURE__ */ jsx23(Text14, { children: " ".repeat(depth) }),
|
|
4153
|
+
/* @__PURE__ */ jsx23(
|
|
4154
|
+
Text14,
|
|
4017
4155
|
{
|
|
4018
4156
|
color,
|
|
4019
4157
|
strikethrough: task.state === "completed",
|
|
@@ -4046,7 +4184,7 @@ function getStateColor2(state, theme) {
|
|
|
4046
4184
|
}
|
|
4047
4185
|
|
|
4048
4186
|
// src/App.tsx
|
|
4049
|
-
import { jsx as
|
|
4187
|
+
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4050
4188
|
var AppContent = () => {
|
|
4051
4189
|
const {
|
|
4052
4190
|
showHelp,
|
|
@@ -4054,32 +4192,47 @@ var AppContent = () => {
|
|
|
4054
4192
|
exitConfirmation,
|
|
4055
4193
|
activePane,
|
|
4056
4194
|
showThemeDialog,
|
|
4057
|
-
showClearTimelineDialog
|
|
4195
|
+
showClearTimelineDialog,
|
|
4196
|
+
showUpdateDialog,
|
|
4197
|
+
setShowUpdateDialog,
|
|
4198
|
+
updateInfo,
|
|
4199
|
+
skipVersion
|
|
4058
4200
|
} = useApp();
|
|
4059
4201
|
const { theme } = useTheme();
|
|
4060
4202
|
useKeyboardNav();
|
|
4061
4203
|
const { width, height } = useTerminalSize();
|
|
4204
|
+
if (showUpdateDialog && updateInfo) {
|
|
4205
|
+
return /* @__PURE__ */ jsx24(
|
|
4206
|
+
UpdateDialog,
|
|
4207
|
+
{
|
|
4208
|
+
currentVersion: updateInfo.currentVersion,
|
|
4209
|
+
latestVersion: updateInfo.latestVersion,
|
|
4210
|
+
onDismiss: () => setShowUpdateDialog(false),
|
|
4211
|
+
onSkipVersion: skipVersion
|
|
4212
|
+
}
|
|
4213
|
+
);
|
|
4214
|
+
}
|
|
4062
4215
|
if (showThemeDialog) {
|
|
4063
|
-
return /* @__PURE__ */
|
|
4216
|
+
return /* @__PURE__ */ jsx24(ThemeDialog, {});
|
|
4064
4217
|
}
|
|
4065
4218
|
if (showHelp) {
|
|
4066
|
-
return /* @__PURE__ */
|
|
4219
|
+
return /* @__PURE__ */ jsx24(HelpDialog, {});
|
|
4067
4220
|
}
|
|
4068
4221
|
if (showClearTimelineDialog) {
|
|
4069
|
-
return /* @__PURE__ */
|
|
4222
|
+
return /* @__PURE__ */ jsx24(ClearTimelineDialog, {});
|
|
4070
4223
|
}
|
|
4071
|
-
return /* @__PURE__ */
|
|
4072
|
-
showOverview ? /* @__PURE__ */
|
|
4224
|
+
return /* @__PURE__ */ jsx24(FullscreenBackground, { backgroundColor: theme.colors.background || "black", children: /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", width, height, padding: 1, backgroundColor: theme.colors.background, children: [
|
|
4225
|
+
showOverview ? /* @__PURE__ */ jsx24(OverviewScreen, {}) : /* @__PURE__ */ jsx24(
|
|
4073
4226
|
ThreeColumnLayout,
|
|
4074
4227
|
{
|
|
4075
|
-
leftPane: /* @__PURE__ */
|
|
4076
|
-
centerPane: /* @__PURE__ */
|
|
4077
|
-
rightPane: /* @__PURE__ */
|
|
4228
|
+
leftPane: /* @__PURE__ */ jsx24(CalendarPane, {}),
|
|
4229
|
+
centerPane: /* @__PURE__ */ jsx24(TasksPane, {}),
|
|
4230
|
+
rightPane: /* @__PURE__ */ jsx24(TimelinePane, {}),
|
|
4078
4231
|
activePane,
|
|
4079
4232
|
height: height - 2
|
|
4080
4233
|
}
|
|
4081
4234
|
),
|
|
4082
|
-
exitConfirmation && /* @__PURE__ */
|
|
4235
|
+
exitConfirmation && /* @__PURE__ */ jsx24(Box18, { width: "100%", justifyContent: "center", paddingY: 1, children: /* @__PURE__ */ jsxs17(Text15, { backgroundColor: "red", color: "white", bold: true, children: [
|
|
4083
4236
|
" ",
|
|
4084
4237
|
"Press Ctrl+C again to exit Epoch",
|
|
4085
4238
|
" "
|
|
@@ -4087,12 +4240,12 @@ var AppContent = () => {
|
|
|
4087
4240
|
] }) });
|
|
4088
4241
|
};
|
|
4089
4242
|
var App = () => {
|
|
4090
|
-
return /* @__PURE__ */
|
|
4243
|
+
return /* @__PURE__ */ jsx24(StorageProvider, { children: /* @__PURE__ */ jsx24(ThemeProvider, { initialTheme: "dark", children: /* @__PURE__ */ jsx24(UndoProvider, { children: /* @__PURE__ */ jsx24(AppProvider, { children: /* @__PURE__ */ jsx24(AppContent, {}) }) }) }) });
|
|
4091
4244
|
};
|
|
4092
4245
|
var App_default = App;
|
|
4093
4246
|
|
|
4094
4247
|
// src/index.tsx
|
|
4095
|
-
import { jsx as
|
|
4248
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
4096
4249
|
console.clear();
|
|
4097
|
-
var app = render(/* @__PURE__ */
|
|
4250
|
+
var app = render(/* @__PURE__ */ jsx25(App_default, {}), { exitOnCtrlC: false });
|
|
4098
4251
|
global.__inkApp = app;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epoch-tui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "A TUI app for daily task logging and time tracking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dev": "tsx src/index.tsx",
|
|
15
15
|
"build": "tsup src/index.tsx --format esm --clean",
|
|
16
16
|
"start": "node dist/index.js",
|
|
17
|
-
"
|
|
17
|
+
"deploy": "node scripts/deploy.js"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"tui",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"time-tracking",
|
|
23
23
|
"cli"
|
|
24
24
|
],
|
|
25
|
-
"author": "",
|
|
25
|
+
"author": "Akshat Dubey <akshatdubey0808@gmail.com>",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@inkjs/ui": "^2.0.0",
|