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.
Files changed (3) hide show
  1. package/README.md +88 -0
  2. package/dist/index.js +210 -57
  3. package/package.json +3 -3
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Epoch
2
+
3
+ ![Epoch Preview](./public/epoch.png)
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 Box17, Text as Text14 } from "ink";
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 dateStr = getDateString(
2831
- new Date(selectedDate.year, selectedDate.month, selectedDate.day)
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
- const event = timelineService.createEvent(
3149
- selectedTaskId,
3150
- selectedTask.title,
3151
- "started" /* STARTED */,
3152
- /* @__PURE__ */ new Date()
3153
- );
3154
- const updatedTimeline = timelineService.addEvent(timeline, event);
3155
- setTimeline(updatedTimeline);
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/overview/OverviewScreen.tsx
3854
- import React10, { useMemo as useMemo4 } from "react";
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] = React10.useState(0);
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
- React10.useEffect(() => {
4035
+ React11.useEffect(() => {
3898
4036
  setScrollOffset(0);
3899
4037
  }, [overviewMonth]);
3900
- useInput8((input, key) => {
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__ */ jsxs15(Box16, { flexDirection: "column", padding: 1, width: "100%", height: "100%", children: [
3937
- /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
3938
- /* @__PURE__ */ jsx22(Text13, { bold: true, color: theme.colors.focusIndicator, children: "Overview" }),
3939
- /* @__PURE__ */ jsx22(Text13, { color: theme.colors.foreground, children: monthName })
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__ */ jsxs15(Box16, { flexDirection: "column", flexGrow: 1, children: [
3942
- canScrollUp && /* @__PURE__ */ jsx22(Box16, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
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__ */ jsx22(Box16, { flexDirection: "row", marginBottom: 1, children: Array.from({ length: columns }).map((_2, colIndex) => {
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__ */ jsx22(
3950
- Box16,
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__ */ jsxs15(
3971
- Box16,
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__ */ jsx22(Text13, { bold: true, color: theme.colors.calendarSelected, children: formatDate(date, "do MMM") }),
3979
- /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
3980
- flatTasksWithDepth.length === 0 ? /* @__PURE__ */ jsx22(Text13, { dimColor: true, color: theme.colors.keyboardHint, children: "No tasks" }) : flatTasksWithDepth.slice(0, 10).map(({ task, depth }) => /* @__PURE__ */ jsx22(
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__ */ jsxs15(Text13, { dimColor: true, color: theme.colors.keyboardHint, children: [
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__ */ jsx22(Box16, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
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__ */ jsx22(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "n/p or \u2190/\u2192: month | j/k or \u2193/\u2191: scroll | Esc: close | Shift+;: toggle" }) })
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__ */ jsxs15(Box16, { children: [
4010
- /* @__PURE__ */ jsxs15(Text13, { color, children: [
4147
+ return /* @__PURE__ */ jsxs16(Box17, { children: [
4148
+ /* @__PURE__ */ jsxs16(Text14, { color, children: [
4011
4149
  checkbox,
4012
4150
  " "
4013
4151
  ] }),
4014
- depth > 0 && /* @__PURE__ */ jsx22(Text13, { children: " ".repeat(depth) }),
4015
- /* @__PURE__ */ jsx22(
4016
- Text13,
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 jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
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__ */ jsx23(ThemeDialog, {});
4216
+ return /* @__PURE__ */ jsx24(ThemeDialog, {});
4064
4217
  }
4065
4218
  if (showHelp) {
4066
- return /* @__PURE__ */ jsx23(HelpDialog, {});
4219
+ return /* @__PURE__ */ jsx24(HelpDialog, {});
4067
4220
  }
4068
4221
  if (showClearTimelineDialog) {
4069
- return /* @__PURE__ */ jsx23(ClearTimelineDialog, {});
4222
+ return /* @__PURE__ */ jsx24(ClearTimelineDialog, {});
4070
4223
  }
4071
- return /* @__PURE__ */ jsx23(FullscreenBackground, { backgroundColor: theme.colors.background || "black", children: /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", width, height, padding: 1, backgroundColor: theme.colors.background, children: [
4072
- showOverview ? /* @__PURE__ */ jsx23(OverviewScreen, {}) : /* @__PURE__ */ jsx23(
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__ */ jsx23(CalendarPane, {}),
4076
- centerPane: /* @__PURE__ */ jsx23(TasksPane, {}),
4077
- rightPane: /* @__PURE__ */ jsx23(TimelinePane, {}),
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__ */ jsx23(Box17, { width: "100%", justifyContent: "center", paddingY: 1, children: /* @__PURE__ */ jsxs16(Text14, { backgroundColor: "red", color: "white", bold: true, children: [
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__ */ jsx23(StorageProvider, { children: /* @__PURE__ */ jsx23(ThemeProvider, { initialTheme: "dark", children: /* @__PURE__ */ jsx23(UndoProvider, { children: /* @__PURE__ */ jsx23(AppProvider, { children: /* @__PURE__ */ jsx23(AppContent, {}) }) }) }) });
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 jsx24 } from "react/jsx-runtime";
4248
+ import { jsx as jsx25 } from "react/jsx-runtime";
4096
4249
  console.clear();
4097
- var app = render(/* @__PURE__ */ jsx24(App_default, {}), { exitOnCtrlC: false });
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.6",
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
- "prepublishOnly": "pnpm run build"
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",