epoch-tui 0.1.3 → 0.1.5

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 (2) hide show
  1. package/dist/index.js +356 -212
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1686,51 +1686,143 @@ var useTheme = () => {
1686
1686
 
1687
1687
  // src/contexts/AppContext.tsx
1688
1688
  import {
1689
- createContext as createContext3,
1690
- useContext as useContext3,
1689
+ createContext as createContext4,
1690
+ useContext as useContext4,
1691
1691
  useEffect as useEffect3,
1692
- useState as useState3,
1692
+ useState as useState4,
1693
1693
  useRef as useRef2,
1694
- useCallback as useCallback2
1694
+ useCallback as useCallback3
1695
1695
  } from "react";
1696
+
1697
+ // src/contexts/UndoContext.tsx
1698
+ import { createContext as createContext3, useContext as useContext3, useState as useState3, useCallback as useCallback2 } from "react";
1696
1699
  import { jsx as jsx3 } from "react/jsx-runtime";
1697
- var AppContext = createContext3(void 0);
1700
+ var UndoContext = createContext3(void 0);
1701
+ var MAX_UNDO_STACK_SIZE = 50;
1702
+ function deepClone(obj) {
1703
+ if (obj === null || typeof obj !== "object") {
1704
+ return obj;
1705
+ }
1706
+ if (obj instanceof Date) {
1707
+ return new Date(obj.getTime());
1708
+ }
1709
+ if (Array.isArray(obj)) {
1710
+ return obj.map((item) => deepClone(item));
1711
+ }
1712
+ const cloned = {};
1713
+ for (const key in obj) {
1714
+ if (obj.hasOwnProperty(key)) {
1715
+ cloned[key] = deepClone(obj[key]);
1716
+ }
1717
+ }
1718
+ return cloned;
1719
+ }
1720
+ var UndoProvider = ({ children }) => {
1721
+ const [undoStack, setUndoStack] = useState3([]);
1722
+ const pushUndoAction = useCallback2(
1723
+ (type, tasks, timeline) => {
1724
+ const action = {
1725
+ type,
1726
+ timestamp: /* @__PURE__ */ new Date(),
1727
+ previousTasks: deepClone(tasks),
1728
+ previousTimeline: deepClone(timeline)
1729
+ };
1730
+ setUndoStack((prev) => {
1731
+ const newStack = [...prev, action];
1732
+ if (newStack.length > MAX_UNDO_STACK_SIZE) {
1733
+ newStack.shift();
1734
+ }
1735
+ return newStack;
1736
+ });
1737
+ },
1738
+ []
1739
+ );
1740
+ const undo = useCallback2(() => {
1741
+ if (undoStack.length === 0) {
1742
+ return null;
1743
+ }
1744
+ const action = undoStack[undoStack.length - 1];
1745
+ setUndoStack((prev) => prev.slice(0, -1));
1746
+ return action;
1747
+ }, [undoStack]);
1748
+ const clearUndoStack = useCallback2(() => {
1749
+ setUndoStack([]);
1750
+ }, []);
1751
+ return /* @__PURE__ */ jsx3(
1752
+ UndoContext.Provider,
1753
+ {
1754
+ value: {
1755
+ pushUndoAction,
1756
+ undo,
1757
+ canUndo: undoStack.length > 0,
1758
+ clearUndoStack
1759
+ },
1760
+ children
1761
+ }
1762
+ );
1763
+ };
1764
+ var useUndo = () => {
1765
+ const context = useContext3(UndoContext);
1766
+ if (!context) {
1767
+ throw new Error("useUndo must be used within UndoProvider");
1768
+ }
1769
+ return context;
1770
+ };
1771
+
1772
+ // src/contexts/AppContext.tsx
1773
+ import { jsx as jsx4 } from "react/jsx-runtime";
1774
+ var AppContext = createContext4(void 0);
1698
1775
  var AppProvider = ({ children }) => {
1699
1776
  const { data, save, saveNow } = useStorage();
1777
+ const { pushUndoAction, undo, canUndo } = useUndo();
1700
1778
  const today = /* @__PURE__ */ new Date();
1701
- const [selectedDate, setSelectedDate] = useState3({
1779
+ const [selectedDate, setSelectedDate] = useState4({
1702
1780
  year: today.getFullYear(),
1703
1781
  month: today.getMonth(),
1704
1782
  day: today.getDate()
1705
1783
  });
1706
- const [tasks, setTasks] = useState3({});
1707
- const [timeline, setTimeline] = useState3(
1784
+ const [tasks, setTasks] = useState4({});
1785
+ const [timeline, setTimeline] = useState4(
1708
1786
  {}
1709
1787
  );
1710
- const [activePane, setActivePane] = useState3("tasks");
1711
- const [showHelp, setShowHelp] = useState3(false);
1788
+ const [activePane, setActivePane] = useState4("tasks");
1789
+ const [showHelp, setShowHelp] = useState4(false);
1712
1790
  const isInputModeRef = useRef2(false);
1713
- const [isInputModeState, setIsInputModeState] = useState3(false);
1714
- const [showOverview, setShowOverview] = useState3(false);
1715
- const setIsInputMode = useCallback2((mode) => {
1791
+ const [isInputModeState, setIsInputModeState] = useState4(false);
1792
+ const [showOverview, setShowOverview] = useState4(false);
1793
+ const setIsInputMode = useCallback3((mode) => {
1716
1794
  isInputModeRef.current = mode;
1717
1795
  setIsInputModeState(mode);
1718
1796
  }, []);
1719
1797
  const isInputMode = isInputModeRef.current;
1720
- const [overviewMonth, setOverviewMonth] = useState3({
1798
+ const [overviewMonth, setOverviewMonth] = useState4({
1721
1799
  year: today.getFullYear(),
1722
1800
  month: today.getMonth()
1723
1801
  });
1724
- const [exitConfirmation, setExitConfirmation] = useState3(false);
1725
- const [showThemeDialog, setShowThemeDialog] = useState3(false);
1726
- const [showClearTimelineDialog, setShowClearTimelineDialog] = useState3(false);
1727
- const clearTimelineForDate = useCallback2((dateStr) => {
1802
+ const [exitConfirmation, setExitConfirmation] = useState4(false);
1803
+ const [showThemeDialog, setShowThemeDialog] = useState4(false);
1804
+ const [showClearTimelineDialog, setShowClearTimelineDialog] = useState4(false);
1805
+ const clearTimelineForDate = useCallback3((dateStr) => {
1806
+ pushUndoAction("TIMELINE_CLEAR", tasks, timeline);
1728
1807
  setTimeline((prev) => {
1729
1808
  const newTimeline = { ...prev };
1730
1809
  delete newTimeline[dateStr];
1731
1810
  return newTimeline;
1732
1811
  });
1733
- }, []);
1812
+ }, [pushUndoAction, tasks, timeline]);
1813
+ const pushUndoableAction = useCallback3(
1814
+ (actionType) => {
1815
+ pushUndoAction(actionType, tasks, timeline);
1816
+ },
1817
+ [pushUndoAction, tasks, timeline]
1818
+ );
1819
+ const performUndo = useCallback3(() => {
1820
+ const action = undo();
1821
+ if (action) {
1822
+ setTasks(action.previousTasks);
1823
+ setTimeline(action.previousTimeline);
1824
+ }
1825
+ }, [undo]);
1734
1826
  const initialLoadDone = useRef2(false);
1735
1827
  const dataRef = useRef2(data);
1736
1828
  const saveRef = useRef2(save);
@@ -1754,7 +1846,7 @@ var AppProvider = ({ children }) => {
1754
1846
  });
1755
1847
  }
1756
1848
  }, [tasks, timeline]);
1757
- return /* @__PURE__ */ jsx3(
1849
+ return /* @__PURE__ */ jsx4(
1758
1850
  AppContext.Provider,
1759
1851
  {
1760
1852
  value: {
@@ -1782,14 +1874,17 @@ var AppProvider = ({ children }) => {
1782
1874
  setShowClearTimelineDialog,
1783
1875
  clearTimelineForDate,
1784
1876
  isModalOpen: showHelp || showThemeDialog || showOverview || showClearTimelineDialog,
1785
- saveNow
1877
+ saveNow,
1878
+ pushUndoableAction,
1879
+ performUndo,
1880
+ canUndo
1786
1881
  },
1787
1882
  children
1788
1883
  }
1789
1884
  );
1790
1885
  };
1791
1886
  var useApp = () => {
1792
- const context = useContext3(AppContext);
1887
+ const context = useContext4(AppContext);
1793
1888
  if (!context) {
1794
1889
  throw new Error("useApp must be used within AppProvider");
1795
1890
  }
@@ -1800,7 +1895,7 @@ var useApp = () => {
1800
1895
  import { useInput } from "ink";
1801
1896
  import { useEffect as useEffect4 } from "react";
1802
1897
  var useKeyboardNav = () => {
1803
- const { showHelp, setShowHelp, activePane, setActivePane, isInputMode, showOverview, setShowOverview, overviewMonth, setOverviewMonth, exitConfirmation, setExitConfirmation, showThemeDialog, setShowThemeDialog, showClearTimelineDialog, setShowClearTimelineDialog, saveNow } = useApp();
1898
+ const { showHelp, setShowHelp, activePane, setActivePane, isInputMode, showOverview, setShowOverview, overviewMonth, setOverviewMonth, exitConfirmation, setExitConfirmation, showThemeDialog, setShowThemeDialog, showClearTimelineDialog, setShowClearTimelineDialog, saveNow, performUndo, canUndo } = useApp();
1804
1899
  useEffect4(() => {
1805
1900
  if (exitConfirmation) {
1806
1901
  const timer = setTimeout(() => {
@@ -1840,6 +1935,10 @@ var useKeyboardNav = () => {
1840
1935
  return;
1841
1936
  }
1842
1937
  }
1938
+ if (key.ctrl && input === "u" && canUndo) {
1939
+ performUndo();
1940
+ return;
1941
+ }
1843
1942
  if (input === ":") {
1844
1943
  setShowOverview(!showOverview);
1845
1944
  return;
@@ -1875,11 +1974,11 @@ var useKeyboardNav = () => {
1875
1974
  };
1876
1975
 
1877
1976
  // src/hooks/useTerminalSize.ts
1878
- import { useState as useState4, useEffect as useEffect5 } from "react";
1977
+ import { useState as useState5, useEffect as useEffect5 } from "react";
1879
1978
  import { useStdout } from "ink";
1880
1979
  var useTerminalSize = () => {
1881
1980
  const { stdout } = useStdout();
1882
- const [size, setSize] = useState4({
1981
+ const [size, setSize] = useState5({
1883
1982
  width: stdout?.columns || 100,
1884
1983
  height: stdout?.rows || 30
1885
1984
  });
@@ -1905,11 +2004,11 @@ var useTerminalSize = () => {
1905
2004
 
1906
2005
  // src/components/layout/ThreeColumnLayout.tsx
1907
2006
  import { Box, Text } from "ink";
1908
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
1909
- var VerticalSeparator = ({ color, backgroundColor, height, isFocused }) => {
1910
- const char = isFocused ? "\u2503" : "\u2502";
1911
- const lines = height ? Array(height).fill(char) : [char];
1912
- return /* @__PURE__ */ jsx4(Box, { flexDirection: "column", backgroundColor, children: lines.map((c, i) => /* @__PURE__ */ jsx4(Text, { color, backgroundColor, children: c }, i)) });
2007
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
2008
+ var ThinSeparator = ({ color, backgroundColor, height }) => {
2009
+ const line = "\u2502";
2010
+ const lines = Array(height).fill(line).join("\n");
2011
+ return /* @__PURE__ */ jsx5(Box, { width: 1, flexShrink: 0, backgroundColor, children: /* @__PURE__ */ jsx5(Text, { color, children: lines }) });
1913
2012
  };
1914
2013
  var ThreeColumnLayout = ({
1915
2014
  leftPane,
@@ -1921,8 +2020,8 @@ var ThreeColumnLayout = ({
1921
2020
  activePane
1922
2021
  }) => {
1923
2022
  const { theme } = useTheme();
1924
- const focusedColor = theme.colors.focusIndicator;
1925
- const normalColor = theme.colors.border;
2023
+ const leftSeparatorColor = activePane === "calendar" || activePane === "tasks" ? theme.colors.focusIndicator : theme.colors.separator;
2024
+ const rightSeparatorColor = activePane === "tasks" || activePane === "timeline" ? theme.colors.focusIndicator : theme.colors.separator;
1926
2025
  const bgColor = theme.name !== "terminal" ? theme.colors.background : void 0;
1927
2026
  return /* @__PURE__ */ jsxs(
1928
2027
  Box,
@@ -1932,7 +2031,7 @@ var ThreeColumnLayout = ({
1932
2031
  height,
1933
2032
  backgroundColor: bgColor,
1934
2033
  children: [
1935
- /* @__PURE__ */ jsx4(
2034
+ /* @__PURE__ */ jsx5(
1936
2035
  Box,
1937
2036
  {
1938
2037
  width: leftWidth,
@@ -1942,16 +2041,15 @@ var ThreeColumnLayout = ({
1942
2041
  children: leftPane
1943
2042
  }
1944
2043
  ),
1945
- /* @__PURE__ */ jsx4(
1946
- VerticalSeparator,
2044
+ /* @__PURE__ */ jsx5(
2045
+ ThinSeparator,
1947
2046
  {
1948
- isFocused: activePane === "calendar" || activePane === "tasks",
1949
- color: activePane === "calendar" || activePane === "tasks" ? focusedColor : normalColor,
2047
+ color: leftSeparatorColor,
1950
2048
  backgroundColor: bgColor,
1951
- height
2049
+ height: height || 30
1952
2050
  }
1953
2051
  ),
1954
- /* @__PURE__ */ jsx4(
2052
+ /* @__PURE__ */ jsx5(
1955
2053
  Box,
1956
2054
  {
1957
2055
  flexGrow: 1,
@@ -1961,22 +2059,22 @@ var ThreeColumnLayout = ({
1961
2059
  children: centerPane
1962
2060
  }
1963
2061
  ),
1964
- /* @__PURE__ */ jsx4(
1965
- VerticalSeparator,
2062
+ /* @__PURE__ */ jsx5(
2063
+ ThinSeparator,
1966
2064
  {
1967
- isFocused: activePane === "tasks" || activePane === "timeline",
1968
- color: activePane === "tasks" || activePane === "timeline" ? focusedColor : normalColor,
2065
+ color: rightSeparatorColor,
1969
2066
  backgroundColor: bgColor,
1970
- height
2067
+ height: height || 30
1971
2068
  }
1972
2069
  ),
1973
- /* @__PURE__ */ jsx4(
2070
+ /* @__PURE__ */ jsx5(
1974
2071
  Box,
1975
2072
  {
1976
2073
  width: rightWidth,
1977
2074
  flexShrink: 0.3,
1978
2075
  flexDirection: "column",
1979
2076
  backgroundColor: bgColor,
2077
+ overflow: "hidden",
1980
2078
  children: rightPane
1981
2079
  }
1982
2080
  )
@@ -1986,7 +2084,7 @@ var ThreeColumnLayout = ({
1986
2084
  };
1987
2085
 
1988
2086
  // src/components/calendar/CalendarPane.tsx
1989
- import { useState as useState5, useEffect as useEffect6 } from "react";
2087
+ import { useState as useState6, useEffect as useEffect6 } from "react";
1990
2088
  import { Box as Box6, useInput as useInput2 } from "ink";
1991
2089
  import { addDays, addWeeks, subDays, subWeeks } from "date-fns";
1992
2090
 
@@ -1995,7 +2093,7 @@ import { Box as Box2 } from "ink";
1995
2093
 
1996
2094
  // src/components/common/ThemedText.tsx
1997
2095
  import { Text as Text2 } from "ink";
1998
- import { jsx as jsx5 } from "react/jsx-runtime";
2096
+ import { jsx as jsx6 } from "react/jsx-runtime";
1999
2097
  var ThemedText = ({
2000
2098
  children,
2001
2099
  backgroundColor,
@@ -2003,11 +2101,11 @@ var ThemedText = ({
2003
2101
  }) => {
2004
2102
  const { theme } = useTheme();
2005
2103
  const bgColor = backgroundColor ?? (theme.name !== "terminal" ? theme.colors.background : void 0);
2006
- return /* @__PURE__ */ jsx5(Text2, { ...props, backgroundColor: bgColor, children });
2104
+ return /* @__PURE__ */ jsx6(Text2, { ...props, backgroundColor: bgColor, children });
2007
2105
  };
2008
2106
 
2009
2107
  // src/components/layout/Pane.tsx
2010
- import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
2108
+ import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
2011
2109
  var Pane = ({
2012
2110
  children,
2013
2111
  title,
@@ -2027,8 +2125,9 @@ var Pane = ({
2027
2125
  paddingRight: 1,
2028
2126
  paddingX: 2,
2029
2127
  alignItems: center ? "center" : "flex-start",
2128
+ overflow: "hidden",
2030
2129
  children: [
2031
- title && /* @__PURE__ */ jsx6(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsx6(
2130
+ title && /* @__PURE__ */ jsx7(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsx7(
2032
2131
  ThemedText,
2033
2132
  {
2034
2133
  backgroundColor: isFocused ? theme.colors.focusIndicator : void 0,
@@ -2037,12 +2136,13 @@ var Pane = ({
2037
2136
  children: isFocused ? ` ${title.toUpperCase()} ` : title
2038
2137
  }
2039
2138
  ) }),
2040
- /* @__PURE__ */ jsx6(
2139
+ /* @__PURE__ */ jsx7(
2041
2140
  Box2,
2042
2141
  {
2043
2142
  flexDirection: "column",
2044
2143
  flexGrow: 1,
2045
- overflowY: "hidden",
2144
+ width: "100%",
2145
+ overflow: "hidden",
2046
2146
  alignItems: center ? "center" : "flex-start",
2047
2147
  children
2048
2148
  }
@@ -2057,7 +2157,7 @@ import { Box as Box4 } from "ink";
2057
2157
 
2058
2158
  // src/components/calendar/DayCell.tsx
2059
2159
  import { Box as Box3 } from "ink";
2060
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
2160
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
2061
2161
  var DayCell = ({ day }) => {
2062
2162
  const { theme } = useTheme();
2063
2163
  let textColor = theme.colors.calendarDayOtherMonth;
@@ -2074,7 +2174,7 @@ var DayCell = ({ day }) => {
2074
2174
  const dayNum = day.date.day.toString().padStart(2, " ");
2075
2175
  const leftBracket = isSelected ? "[" : isToday2 ? "(" : " ";
2076
2176
  const rightBracket = isSelected ? "]" : isToday2 ? ")" : " ";
2077
- return /* @__PURE__ */ jsx7(Box3, { width: 4, children: /* @__PURE__ */ jsxs3(ThemedText, { color: textColor, children: [
2177
+ return /* @__PURE__ */ jsx8(Box3, { width: 4, children: /* @__PURE__ */ jsxs3(ThemedText, { color: textColor, children: [
2078
2178
  leftBracket,
2079
2179
  dayNum,
2080
2180
  rightBracket
@@ -2082,22 +2182,22 @@ var DayCell = ({ day }) => {
2082
2182
  };
2083
2183
 
2084
2184
  // src/components/calendar/MonthView.tsx
2085
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
2185
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
2086
2186
  var MonthView = ({ calendarView }) => {
2087
2187
  const { theme } = useTheme();
2088
2188
  const dayHeaders = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
2089
2189
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
2090
- /* @__PURE__ */ jsx8(Box4, { marginBottom: 1, children: dayHeaders.map((day) => /* @__PURE__ */ jsx8(Box4, { width: 4, children: /* @__PURE__ */ jsx8(ThemedText, { color: theme.colors.calendarHeader, bold: true, children: day }) }, day)) }),
2091
- calendarView.weeks.map((week, weekIdx) => /* @__PURE__ */ jsx8(Box4, { marginBottom: 0, children: week.map((day) => /* @__PURE__ */ jsx8(DayCell, { day }, day.dateString)) }, weekIdx))
2190
+ /* @__PURE__ */ jsx9(Box4, { marginBottom: 1, children: dayHeaders.map((day) => /* @__PURE__ */ jsx9(Box4, { width: 4, children: /* @__PURE__ */ jsx9(ThemedText, { color: theme.colors.calendarHeader, bold: true, children: day }) }, day)) }),
2191
+ calendarView.weeks.map((week, weekIdx) => /* @__PURE__ */ jsx9(Box4, { marginBottom: 0, children: week.map((day) => /* @__PURE__ */ jsx9(DayCell, { day }, day.dateString)) }, weekIdx))
2092
2192
  ] });
2093
2193
  };
2094
2194
 
2095
2195
  // src/components/common/KeyboardHints.tsx
2096
2196
  import { Box as Box5, Text as Text3 } from "ink";
2097
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
2197
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
2098
2198
  var KeyboardHints = ({ hints }) => {
2099
2199
  const { theme } = useTheme();
2100
- return /* @__PURE__ */ jsx9(
2200
+ return /* @__PURE__ */ jsx10(
2101
2201
  Box5,
2102
2202
  {
2103
2203
  marginTop: 1,
@@ -2106,7 +2206,7 @@ var KeyboardHints = ({ hints }) => {
2106
2206
  columnGap: 2,
2107
2207
  width: "100%",
2108
2208
  children: hints.map((hint, index) => /* @__PURE__ */ jsxs5(Box5, { children: [
2109
- /* @__PURE__ */ jsx9(Text3, { color: theme.colors.focusIndicator, bold: true, children: hint.key }),
2209
+ /* @__PURE__ */ jsx10(Text3, { color: theme.colors.focusIndicator, bold: true, children: hint.key }),
2110
2210
  /* @__PURE__ */ jsxs5(Text3, { color: theme.colors.keyboardHint, children: [
2111
2211
  " ",
2112
2212
  hint.description
@@ -2210,7 +2310,7 @@ var CalendarService = class {
2210
2310
  var calendarService = new CalendarService();
2211
2311
 
2212
2312
  // src/components/calendar/CalendarPane.tsx
2213
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
2313
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
2214
2314
  var CalendarPane = () => {
2215
2315
  const { theme } = useTheme();
2216
2316
  const {
@@ -2221,7 +2321,7 @@ var CalendarPane = () => {
2221
2321
  isModalOpen,
2222
2322
  isInputMode
2223
2323
  } = useApp();
2224
- const [currentMonth, setCurrentMonth] = useState5({
2324
+ const [currentMonth, setCurrentMonth] = useState6({
2225
2325
  year: selectedDate.year,
2226
2326
  month: selectedDate.month
2227
2327
  });
@@ -2307,15 +2407,15 @@ var CalendarPane = () => {
2307
2407
  { isActive: isFocused && !isInputMode }
2308
2408
  );
2309
2409
  const monthName = calendarService.getMonthName(currentMonth.month);
2310
- return /* @__PURE__ */ jsx10(
2410
+ return /* @__PURE__ */ jsx11(
2311
2411
  Pane,
2312
2412
  {
2313
2413
  title: `${monthName} ${currentMonth.year}`,
2314
2414
  isFocused,
2315
2415
  center: true,
2316
2416
  children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", alignItems: "center", children: [
2317
- /* @__PURE__ */ jsx10(MonthView, { calendarView }),
2318
- /* @__PURE__ */ jsx10(
2417
+ /* @__PURE__ */ jsx11(MonthView, { calendarView }),
2418
+ /* @__PURE__ */ jsx11(
2319
2419
  KeyboardHints,
2320
2420
  {
2321
2421
  hints: [
@@ -2332,13 +2432,13 @@ var CalendarPane = () => {
2332
2432
  };
2333
2433
 
2334
2434
  // src/components/tasks/TasksPane.tsx
2335
- import { useState as useState6, useEffect as useEffect8, useMemo } from "react";
2435
+ import { useState as useState7, useEffect as useEffect8, useMemo } from "react";
2336
2436
  import { Box as Box8, Text as Text7, useInput as useInput4 } from "ink";
2337
2437
 
2338
2438
  // src/components/common/ControlledTextInput.tsx
2339
2439
  import { useRef as useRef3, useEffect as useEffect7 } from "react";
2340
2440
  import { Text as Text5, useInput as useInput3 } from "ink";
2341
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2441
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
2342
2442
  var ControlledTextInput = ({
2343
2443
  value,
2344
2444
  onChange,
@@ -2400,21 +2500,21 @@ var ControlledTextInput = ({
2400
2500
  const displayValue = pendingValueRef.current || "";
2401
2501
  const showPlaceholder = !displayValue && placeholder;
2402
2502
  return /* @__PURE__ */ jsxs7(Text5, { children: [
2403
- showPlaceholder ? /* @__PURE__ */ jsx11(Text5, { dimColor: true, color: placeholderColor, children: placeholder }) : /* @__PURE__ */ jsx11(Text5, { color, children: displayValue }),
2404
- focus && /* @__PURE__ */ jsx11(Text5, { backgroundColor: cursorColor, children: " " })
2503
+ showPlaceholder ? /* @__PURE__ */ jsx12(Text5, { dimColor: true, color: placeholderColor, children: placeholder }) : /* @__PURE__ */ jsx12(Text5, { color, children: displayValue }),
2504
+ focus && /* @__PURE__ */ jsx12(Text5, { backgroundColor: cursorColor, children: " " })
2405
2505
  ] });
2406
2506
  };
2407
2507
 
2408
2508
  // src/components/tasks/TaskHeader.tsx
2409
2509
  import { Box as Box7, Text as Text6 } from "ink";
2410
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
2510
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
2411
2511
  var TaskHeader = ({
2412
2512
  selectedDate,
2413
2513
  completionPercentage
2414
2514
  }) => {
2415
2515
  const { theme } = useTheme();
2416
2516
  return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginBottom: 1, children: [
2417
- /* @__PURE__ */ jsx12(Text6, { color: theme.colors.taskHeader, children: formatDate(selectedDate, "EEEE, MMMM d, yyyy") }),
2517
+ /* @__PURE__ */ jsx13(Text6, { color: theme.colors.taskHeader, children: formatDate(selectedDate, "EEEE, MMMM d, yyyy") }),
2418
2518
  /* @__PURE__ */ jsxs8(Text6, { color: theme.colors.taskHeader, children: [
2419
2519
  completionPercentage,
2420
2520
  "% completed"
@@ -2702,7 +2802,7 @@ var TimelineService = class {
2702
2802
  var timelineService = new TimelineService();
2703
2803
 
2704
2804
  // src/components/tasks/TasksPane.tsx
2705
- import { Fragment, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
2805
+ import { Fragment, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
2706
2806
  var TasksPane = () => {
2707
2807
  const {
2708
2808
  tasks,
@@ -2713,15 +2813,16 @@ var TasksPane = () => {
2713
2813
  selectedDate,
2714
2814
  isInputMode,
2715
2815
  setIsInputMode,
2716
- isModalOpen
2816
+ isModalOpen,
2817
+ pushUndoableAction
2717
2818
  } = useApp();
2718
2819
  const { theme } = useTheme();
2719
- const [expandedIds, setExpandedIds] = useState6(/* @__PURE__ */ new Set());
2720
- const [selectedIndex, setSelectedIndex] = useState6(0);
2721
- const [editMode, setEditMode] = useState6("none");
2722
- const [editValue, setEditValue] = useState6("");
2723
- const [parentTaskId, setParentTaskId] = useState6(null);
2724
- const [scrollOffset, setScrollOffset] = useState6(0);
2820
+ const [expandedIds, setExpandedIds] = useState7(/* @__PURE__ */ new Set());
2821
+ const [selectedIndex, setSelectedIndex] = useState7(0);
2822
+ const [editMode, setEditMode] = useState7("none");
2823
+ const [editValue, setEditValue] = useState7("");
2824
+ const [parentTaskId, setParentTaskId] = useState7(null);
2825
+ const [scrollOffset, setScrollOffset] = useState7(0);
2725
2826
  const { height: terminalHeight } = useTerminalSize();
2726
2827
  const visibleRows = useMemo(() => {
2727
2828
  return Math.max(5, terminalHeight - 11);
@@ -2729,7 +2830,7 @@ var TasksPane = () => {
2729
2830
  const dateStr = getDateString(
2730
2831
  new Date(selectedDate.year, selectedDate.month, selectedDate.day)
2731
2832
  );
2732
- const dayTasks = tasks[dateStr] || [];
2833
+ const dayTasks = useMemo(() => tasks[dateStr] || [], [tasks, dateStr]);
2733
2834
  const stats = taskService.getTaskStats(tasks, dateStr);
2734
2835
  const isFocused = activePane === "tasks" && !isModalOpen;
2735
2836
  const flatTasks = useMemo(() => {
@@ -2758,7 +2859,13 @@ var TasksPane = () => {
2758
2859
  }
2759
2860
  };
2760
2861
  collectParents(dayTasks);
2761
- setExpandedIds(allParentIds);
2862
+ setExpandedIds((prev) => {
2863
+ if (prev.size !== allParentIds.size) return allParentIds;
2864
+ for (const id of allParentIds) {
2865
+ if (!prev.has(id)) return allParentIds;
2866
+ }
2867
+ return prev;
2868
+ });
2762
2869
  }, [dayTasks]);
2763
2870
  useEffect8(() => {
2764
2871
  setSelectedIndex(0);
@@ -2770,12 +2877,15 @@ var TasksPane = () => {
2770
2877
  }
2771
2878
  }, [flatTasks.length, selectedIndex]);
2772
2879
  useEffect8(() => {
2773
- if (selectedIndex < scrollOffset) {
2774
- setScrollOffset(selectedIndex);
2775
- } else if (selectedIndex >= scrollOffset + visibleRows) {
2776
- setScrollOffset(selectedIndex - visibleRows + 1);
2777
- }
2778
- }, [selectedIndex, visibleRows, scrollOffset]);
2880
+ setScrollOffset((currentOffset) => {
2881
+ if (selectedIndex < currentOffset) {
2882
+ return selectedIndex;
2883
+ } else if (selectedIndex >= currentOffset + visibleRows) {
2884
+ return selectedIndex - visibleRows + 1;
2885
+ }
2886
+ return currentOffset;
2887
+ });
2888
+ }, [selectedIndex, visibleRows]);
2779
2889
  useEffect8(() => {
2780
2890
  setScrollOffset(0);
2781
2891
  }, [dateStr]);
@@ -2803,6 +2913,7 @@ var TasksPane = () => {
2803
2913
  const handleDeleteTask = () => {
2804
2914
  if (selectedTaskId) {
2805
2915
  try {
2916
+ pushUndoableAction("TASK_DELETE");
2806
2917
  const updated = taskService.deleteTask(tasks, selectedTaskId);
2807
2918
  setTasks(updated);
2808
2919
  const updatedTimeline = timelineService.removeEventsByTaskId(
@@ -2818,6 +2929,7 @@ var TasksPane = () => {
2818
2929
  const handleChangeState = (newState) => {
2819
2930
  if (selectedTaskId && selectedTask) {
2820
2931
  try {
2932
+ pushUndoableAction("TASK_UPDATE");
2821
2933
  const previousState = selectedTask.state;
2822
2934
  const updated = taskService.changeTaskState(
2823
2935
  tasks,
@@ -2880,6 +2992,7 @@ var TasksPane = () => {
2880
2992
  }
2881
2993
  try {
2882
2994
  if (editMode === "add") {
2995
+ pushUndoableAction("TASK_ADD");
2883
2996
  const newTask = taskService.createTask(trimmed, dateStr);
2884
2997
  const newTasks = {
2885
2998
  ...tasks,
@@ -2888,6 +3001,7 @@ var TasksPane = () => {
2888
3001
  setTasks(newTasks);
2889
3002
  setSelectedIndex(flatTasks.length);
2890
3003
  } else if (editMode === "addSubtask" && parentTaskId) {
3004
+ pushUndoableAction("TASK_ADD");
2891
3005
  const updated = taskService.addSubtask(tasks, parentTaskId, trimmed);
2892
3006
  setTasks(updated);
2893
3007
  const parentIndex = flatTasks.findIndex(
@@ -2897,6 +3011,7 @@ var TasksPane = () => {
2897
3011
  setSelectedIndex(parentIndex + 1);
2898
3012
  }
2899
3013
  } else if (editMode === "edit" && selectedTaskId) {
3014
+ pushUndoableAction("TASK_UPDATE");
2900
3015
  const updated = taskService.updateTask(tasks, selectedTaskId, {
2901
3016
  title: trimmed
2902
3017
  });
@@ -3015,6 +3130,7 @@ var TasksPane = () => {
3015
3130
  }
3016
3131
  if (input === "s" && selectedTask) {
3017
3132
  try {
3133
+ pushUndoableAction("TASK_UPDATE");
3018
3134
  if (selectedTask.startTime && !selectedTask.endTime) {
3019
3135
  const updated = taskService.updateTask(tasks, selectedTaskId, {
3020
3136
  startTime: void 0
@@ -3054,8 +3170,8 @@ var TasksPane = () => {
3054
3170
  },
3055
3171
  { isActive: isFocused && editMode === "none" }
3056
3172
  );
3057
- return /* @__PURE__ */ jsx13(Pane, { title: "Tasks", isFocused, children: /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", flexGrow: 1, width: "100%", children: [
3058
- /* @__PURE__ */ jsx13(
3173
+ return /* @__PURE__ */ jsx14(Pane, { title: "Tasks", isFocused, children: /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", flexGrow: 1, width: "100%", children: [
3174
+ /* @__PURE__ */ jsx14(
3059
3175
  TaskHeader,
3060
3176
  {
3061
3177
  selectedDate: new Date(selectedDate.year, selectedDate.month, selectedDate.day),
@@ -3063,8 +3179,8 @@ var TasksPane = () => {
3063
3179
  }
3064
3180
  ),
3065
3181
  editMode === "add" && /* @__PURE__ */ jsxs9(Box8, { marginY: 1, children: [
3066
- /* @__PURE__ */ jsx13(Text7, { color: theme.colors.focusIndicator, children: "> " }),
3067
- /* @__PURE__ */ jsx13(
3182
+ /* @__PURE__ */ jsx14(Text7, { color: theme.colors.focusIndicator, children: "> " }),
3183
+ /* @__PURE__ */ jsx14(
3068
3184
  ControlledTextInput,
3069
3185
  {
3070
3186
  value: editValue,
@@ -3079,9 +3195,9 @@ var TasksPane = () => {
3079
3195
  )
3080
3196
  ] }),
3081
3197
  editMode === "addSubtask" && /* @__PURE__ */ jsxs9(Box8, { marginY: 1, children: [
3082
- /* @__PURE__ */ jsx13(Text7, { color: theme.colors.focusIndicator, children: "> " }),
3083
- /* @__PURE__ */ jsx13(Text7, { color: theme.colors.keyboardHint, children: " " }),
3084
- /* @__PURE__ */ jsx13(
3198
+ /* @__PURE__ */ jsx14(Text7, { color: theme.colors.focusIndicator, children: "> " }),
3199
+ /* @__PURE__ */ jsx14(Text7, { color: theme.colors.keyboardHint, children: " " }),
3200
+ /* @__PURE__ */ jsx14(
3085
3201
  ControlledTextInput,
3086
3202
  {
3087
3203
  value: editValue,
@@ -3095,8 +3211,8 @@ var TasksPane = () => {
3095
3211
  }
3096
3212
  )
3097
3213
  ] }),
3098
- dayTasks.length === 0 && editMode !== "add" && editMode !== "addSubtask" ? /* @__PURE__ */ jsx13(Box8, { marginY: 1, children: /* @__PURE__ */ jsx13(Text7, { color: theme.colors.keyboardHint, dimColor: true, children: "No tasks. Press 'a' to add one." }) }) : /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", marginY: 1, paddingRight: 2, children: [
3099
- scrollOffset > 0 && /* @__PURE__ */ jsx13(Box8, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx13(Text7, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
3214
+ dayTasks.length === 0 && editMode !== "add" && editMode !== "addSubtask" ? /* @__PURE__ */ jsx14(Box8, { marginY: 1, children: /* @__PURE__ */ jsx14(Text7, { color: theme.colors.keyboardHint, dimColor: true, children: "No tasks. Press 'a' to add one." }) }) : /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", marginY: 1, paddingRight: 2, children: [
3215
+ scrollOffset > 0 && /* @__PURE__ */ jsx14(Box8, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx14(Text7, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
3100
3216
  flatTasks.slice(scrollOffset, scrollOffset + visibleRows).map(({ task, depth }, sliceIndex) => {
3101
3217
  const index = scrollOffset + sliceIndex;
3102
3218
  const isSelected = index === selectedIndex;
@@ -3104,8 +3220,8 @@ var TasksPane = () => {
3104
3220
  const isEditing = editMode === "edit" && isSelected;
3105
3221
  if (isEditing) {
3106
3222
  return /* @__PURE__ */ jsxs9(Box8, { children: [
3107
- /* @__PURE__ */ jsx13(Text7, { color: theme.colors.focusIndicator, children: "> " }),
3108
- /* @__PURE__ */ jsx13(
3223
+ /* @__PURE__ */ jsx14(Text7, { color: theme.colors.focusIndicator, children: "> " }),
3224
+ /* @__PURE__ */ jsx14(
3109
3225
  ControlledTextInput,
3110
3226
  {
3111
3227
  value: editValue,
@@ -3120,28 +3236,28 @@ var TasksPane = () => {
3120
3236
  ] }, task.id);
3121
3237
  }
3122
3238
  return /* @__PURE__ */ jsxs9(Box8, { children: [
3123
- /* @__PURE__ */ jsx13(
3239
+ /* @__PURE__ */ jsx14(
3124
3240
  Text7,
3125
3241
  {
3126
3242
  color: isSelected ? theme.colors.focusIndicator : theme.colors.foreground,
3127
3243
  children: isSelected ? ">" : " "
3128
3244
  }
3129
3245
  ),
3130
- /* @__PURE__ */ jsx13(Text7, { children: " " }),
3131
- /* @__PURE__ */ jsx13(Text7, { children: " ".repeat(depth) }),
3132
- /* @__PURE__ */ jsx13(
3246
+ /* @__PURE__ */ jsx14(Text7, { children: " " }),
3247
+ /* @__PURE__ */ jsx14(Text7, { children: " ".repeat(depth) }),
3248
+ /* @__PURE__ */ jsx14(
3133
3249
  Text7,
3134
3250
  {
3135
3251
  color: isSelected ? theme.colors.focusIndicator : getStateColor(task.state, theme),
3136
3252
  children: getCheckbox(task.state)
3137
3253
  }
3138
3254
  ),
3139
- /* @__PURE__ */ jsx13(Text7, { children: " " }),
3255
+ /* @__PURE__ */ jsx14(Text7, { children: " " }),
3140
3256
  task.children.length > 0 && /* @__PURE__ */ jsxs9(Fragment, { children: [
3141
- /* @__PURE__ */ jsx13(Text7, { children: isExpanded ? "\u25BC" : "\u25B6" }),
3142
- /* @__PURE__ */ jsx13(Text7, { children: " " })
3257
+ /* @__PURE__ */ jsx14(Text7, { color: theme.colors.foreground, children: isExpanded ? "\u25BC" : "\u25B6" }),
3258
+ /* @__PURE__ */ jsx14(Text7, { children: " " })
3143
3259
  ] }),
3144
- /* @__PURE__ */ jsx13(
3260
+ /* @__PURE__ */ jsx14(
3145
3261
  Text7,
3146
3262
  {
3147
3263
  color: isSelected ? theme.colors.focusIndicator : getStateColor(task.state, theme),
@@ -3162,9 +3278,9 @@ var TasksPane = () => {
3162
3278
  )
3163
3279
  ] }, task.id);
3164
3280
  }),
3165
- scrollOffset + visibleRows < flatTasks.length && /* @__PURE__ */ jsx13(Box8, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx13(Text7, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
3281
+ scrollOffset + visibleRows < flatTasks.length && /* @__PURE__ */ jsx14(Box8, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx14(Text7, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
3166
3282
  ] }),
3167
- /* @__PURE__ */ jsx13(
3283
+ /* @__PURE__ */ jsx14(
3168
3284
  KeyboardHints,
3169
3285
  {
3170
3286
  hints: [
@@ -3207,12 +3323,12 @@ function getStateColor(state, theme) {
3207
3323
  }
3208
3324
 
3209
3325
  // src/components/timeline/TimelinePane.tsx
3210
- import { useState as useState7, useEffect as useEffect9, useMemo as useMemo2 } from "react";
3326
+ import { useState as useState8, useEffect as useEffect9, useMemo as useMemo2 } from "react";
3211
3327
  import { Box as Box10, Text as Text9, useInput as useInput5 } from "ink";
3212
3328
 
3213
3329
  // src/components/timeline/TimelineEntry.tsx
3214
3330
  import { Box as Box9, Text as Text8 } from "ink";
3215
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
3331
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
3216
3332
  var TimelineEntry = ({
3217
3333
  event,
3218
3334
  isLast,
@@ -3237,33 +3353,32 @@ var TimelineEntry = ({
3237
3353
  const isFilledCircle = ["completed", "delegated", "delayed"].includes(
3238
3354
  event.type
3239
3355
  );
3240
- return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", children: [
3241
- /* @__PURE__ */ jsxs10(Box9, { children: [
3242
- /* @__PURE__ */ jsx14(Box9, { width: 3, justifyContent: "center", children: /* @__PURE__ */ jsx14(Text8, { color, children: isFilledCircle ? "\u25CF" : "\u25CB" }) }),
3243
- /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", flexGrow: 1, children: [
3244
- /* @__PURE__ */ jsx14(Box9, { children: /* @__PURE__ */ jsxs10(Text8, { children: [
3356
+ return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", width: "100%", children: [
3357
+ /* @__PURE__ */ jsxs10(Box9, { width: "100%", children: [
3358
+ /* @__PURE__ */ jsx15(Box9, { width: 3, flexShrink: 0, justifyContent: "center", children: /* @__PURE__ */ jsx15(Text8, { color, children: isFilledCircle ? "\u25CF" : "\u25CB" }) }),
3359
+ /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, children: [
3360
+ /* @__PURE__ */ jsxs10(Text8, { wrap: "wrap", color: theme.colors.foreground, children: [
3245
3361
  /* @__PURE__ */ jsxs10(Text8, { color, bold: true, children: [
3246
3362
  typeStr,
3247
3363
  ":"
3248
3364
  ] }),
3249
- /* @__PURE__ */ jsxs10(Text8, { color: theme.colors.foreground, children: [
3250
- " ",
3251
- event.taskTitle
3252
- ] })
3253
- ] }) }),
3254
- /* @__PURE__ */ jsx14(Box9, { children: /* @__PURE__ */ jsx14(Text8, { color: theme.colors.keyboardHint, dimColor: true, children: timeStr }) })
3365
+ " ",
3366
+ event.taskTitle
3367
+ ] }),
3368
+ /* @__PURE__ */ jsx15(Text8, { color: theme.colors.keyboardHint, dimColor: true, children: timeStr })
3255
3369
  ] })
3256
3370
  ] }),
3257
- !isLast && /* @__PURE__ */ jsx14(Box9, { children: /* @__PURE__ */ jsx14(Box9, { width: 3, justifyContent: "center", children: /* @__PURE__ */ jsx14(Text8, { color: hasNextSameTask ? color : theme.colors.border, children: "\u2502" }) }) })
3371
+ !isLast && /* @__PURE__ */ jsx15(Box9, { children: /* @__PURE__ */ jsx15(Box9, { width: 3, justifyContent: "center", children: /* @__PURE__ */ jsx15(Text8, { color: hasNextSameTask ? color : theme.colors.border, children: "\u2502" }) }) })
3258
3372
  ] });
3259
3373
  };
3260
3374
 
3261
3375
  // src/components/timeline/TimelinePane.tsx
3262
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3376
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
3263
3377
  var TimelinePane = () => {
3264
3378
  const { theme } = useTheme();
3265
3379
  const {
3266
3380
  selectedDate,
3381
+ tasks,
3267
3382
  timeline,
3268
3383
  setTimeline,
3269
3384
  activePane,
@@ -3271,15 +3386,49 @@ var TimelinePane = () => {
3271
3386
  isInputMode
3272
3387
  } = useApp();
3273
3388
  const isFocused = activePane === "timeline" && !isModalOpen;
3274
- const [scrollOffset, setScrollOffset] = useState7(0);
3389
+ const [scrollOffset, setScrollOffset] = useState8(0);
3275
3390
  const { height: terminalHeight } = useTerminalSize();
3276
3391
  const visibleRows = useMemo2(() => {
3277
- return Math.max(5, terminalHeight - 10);
3392
+ const availableHeight = Math.max(8, terminalHeight - 8);
3393
+ return Math.max(3, Math.floor(availableHeight / 4));
3278
3394
  }, [terminalHeight]);
3279
3395
  const dateStr = getDateString(
3280
3396
  new Date(selectedDate.year, selectedDate.month, selectedDate.day)
3281
3397
  );
3282
3398
  const dayEvents = timeline[dateStr] || [];
3399
+ const dayTasks = tasks[dateStr] || [];
3400
+ const taskHierarchyMap = useMemo2(() => {
3401
+ const map = /* @__PURE__ */ new Map();
3402
+ const traverse = (taskList, parentId) => {
3403
+ taskList.forEach((task) => {
3404
+ map.set(task.id, parentId);
3405
+ traverse(task.children, task.id);
3406
+ });
3407
+ };
3408
+ traverse(dayTasks);
3409
+ return map;
3410
+ }, [dayTasks]);
3411
+ const isAncestor = (taskAId, taskBId) => {
3412
+ let currentId = taskBId;
3413
+ while (currentId) {
3414
+ const parentId = taskHierarchyMap.get(currentId);
3415
+ if (!parentId) break;
3416
+ if (parentId === taskAId) return true;
3417
+ currentId = parentId;
3418
+ }
3419
+ return false;
3420
+ };
3421
+ const getTaskDepth = (taskId) => {
3422
+ let depth = 0;
3423
+ let currentId = taskId;
3424
+ while (currentId) {
3425
+ const parentId = taskHierarchyMap.get(currentId);
3426
+ if (!parentId) break;
3427
+ depth++;
3428
+ currentId = parentId;
3429
+ }
3430
+ return depth;
3431
+ };
3283
3432
  const sortedEvents = useMemo2(() => {
3284
3433
  const eventsByTask = /* @__PURE__ */ new Map();
3285
3434
  dayEvents.forEach((event) => {
@@ -3291,14 +3440,19 @@ var TimelinePane = () => {
3291
3440
  events.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
3292
3441
  });
3293
3442
  const taskGroups = Array.from(eventsByTask.entries()).sort(
3294
- ([, eventsA], [, eventsB]) => {
3443
+ ([taskIdA, eventsA], [taskIdB, eventsB]) => {
3444
+ if (isAncestor(taskIdA, taskIdB)) return -1;
3445
+ if (isAncestor(taskIdB, taskIdA)) return 1;
3446
+ const depthA = getTaskDepth(taskIdA);
3447
+ const depthB = getTaskDepth(taskIdB);
3448
+ if (depthA !== depthB) return depthA - depthB;
3295
3449
  const earliestA = eventsA[0]?.timestamp.getTime() || 0;
3296
3450
  const earliestB = eventsB[0]?.timestamp.getTime() || 0;
3297
3451
  return earliestA - earliestB;
3298
3452
  }
3299
3453
  );
3300
3454
  return taskGroups.flatMap(([, events]) => events);
3301
- }, [dayEvents]);
3455
+ }, [dayEvents, taskHierarchyMap]);
3302
3456
  useEffect9(() => {
3303
3457
  setScrollOffset(0);
3304
3458
  }, [dateStr]);
@@ -3321,17 +3475,6 @@ var TimelinePane = () => {
3321
3475
  )
3322
3476
  );
3323
3477
  }
3324
- if (input === "u" && key.ctrl) {
3325
- setScrollOffset(
3326
- (prev) => Math.max(prev - Math.floor(visibleRows / 2), 0)
3327
- );
3328
- }
3329
- if (input === "C") {
3330
- setTimeline({
3331
- ...timeline,
3332
- [dateStr]: []
3333
- });
3334
- }
3335
3478
  },
3336
3479
  { isActive: isFocused && !isInputMode }
3337
3480
  );
@@ -3341,18 +3484,18 @@ var TimelinePane = () => {
3341
3484
  );
3342
3485
  const canScrollUp = scrollOffset > 0;
3343
3486
  const canScrollDown = scrollOffset + visibleRows < sortedEvents.length;
3344
- return /* @__PURE__ */ jsx15(Pane, { title: "Timeline", isFocused, children: /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", flexGrow: 1, width: "100%", children: [
3487
+ return /* @__PURE__ */ jsx16(Pane, { title: "Timeline", isFocused, children: /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", flexGrow: 1, width: "100%", overflow: "hidden", children: [
3345
3488
  sortedEvents.length === 0 ? /* @__PURE__ */ jsxs11(Box10, { marginY: 1, flexDirection: "column", children: [
3346
- /* @__PURE__ */ jsx15(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "No activities yet." }),
3347
- /* @__PURE__ */ jsx15(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "Press 's' to start a task." })
3348
- ] }) : /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", children: [
3349
- canScrollUp && /* @__PURE__ */ jsx15(Box10, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx15(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
3350
- /* @__PURE__ */ jsx15(Box10, { flexDirection: "column", children: visibleEvents.map((event, index) => {
3489
+ /* @__PURE__ */ jsx16(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "No activities yet." }),
3490
+ /* @__PURE__ */ jsx16(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "Press 's' to start a task." })
3491
+ ] }) : /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", width: "100%", overflow: "hidden", children: [
3492
+ canScrollUp && /* @__PURE__ */ jsx16(Box10, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx16(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
3493
+ /* @__PURE__ */ jsx16(Box10, { flexDirection: "column", width: "100%", overflow: "hidden", children: visibleEvents.map((event, index) => {
3351
3494
  const globalIndex = scrollOffset + index;
3352
3495
  const isLast = globalIndex === sortedEvents.length - 1;
3353
3496
  const nextEvent = sortedEvents[globalIndex + 1];
3354
3497
  const hasNextSameTask = nextEvent && nextEvent.taskId === event.taskId;
3355
- return /* @__PURE__ */ jsx15(
3498
+ return /* @__PURE__ */ jsx16(
3356
3499
  TimelineEntry,
3357
3500
  {
3358
3501
  event,
@@ -3362,9 +3505,9 @@ var TimelinePane = () => {
3362
3505
  event.id
3363
3506
  );
3364
3507
  }) }),
3365
- canScrollDown && /* @__PURE__ */ jsx15(Box10, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx15(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
3508
+ canScrollDown && /* @__PURE__ */ jsx16(Box10, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx16(Text9, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
3366
3509
  ] }),
3367
- /* @__PURE__ */ jsx15(
3510
+ /* @__PURE__ */ jsx16(
3368
3511
  KeyboardHints,
3369
3512
  {
3370
3513
  hints: [
@@ -3384,7 +3527,7 @@ import { Box as Box12, useStdout as useStdout3 } from "ink";
3384
3527
 
3385
3528
  // src/components/common/FullscreenBackground.tsx
3386
3529
  import { Box as Box11, useStdout as useStdout2 } from "ink";
3387
- import { jsx as jsx16 } from "react/jsx-runtime";
3530
+ import { jsx as jsx17 } from "react/jsx-runtime";
3388
3531
  var FullscreenBackground = ({
3389
3532
  children,
3390
3533
  backgroundColor
@@ -3392,7 +3535,7 @@ var FullscreenBackground = ({
3392
3535
  const { stdout } = useStdout2();
3393
3536
  const width = stdout?.columns || 100;
3394
3537
  const height = stdout?.rows || 30;
3395
- return /* @__PURE__ */ jsx16(
3538
+ return /* @__PURE__ */ jsx17(
3396
3539
  Box11,
3397
3540
  {
3398
3541
  flexDirection: "column",
@@ -3405,17 +3548,17 @@ var FullscreenBackground = ({
3405
3548
  };
3406
3549
 
3407
3550
  // src/components/common/Modal.tsx
3408
- import { jsx as jsx17 } from "react/jsx-runtime";
3551
+ import { jsx as jsx18 } from "react/jsx-runtime";
3409
3552
  var Modal = ({ children }) => {
3410
3553
  const { theme } = useTheme();
3411
3554
  const { stdout } = useStdout3();
3412
3555
  const width = stdout?.columns || 100;
3413
3556
  const height = stdout?.rows || 30;
3414
- return /* @__PURE__ */ jsx17(
3557
+ return /* @__PURE__ */ jsx18(
3415
3558
  FullscreenBackground,
3416
3559
  {
3417
3560
  backgroundColor: theme.colors.modalOverlay || "black",
3418
- children: /* @__PURE__ */ jsx17(
3561
+ children: /* @__PURE__ */ jsx18(
3419
3562
  Box12,
3420
3563
  {
3421
3564
  flexDirection: "column",
@@ -3431,11 +3574,12 @@ var Modal = ({ children }) => {
3431
3574
  };
3432
3575
 
3433
3576
  // src/components/common/HelpDialog.tsx
3434
- import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
3577
+ import { Fragment as Fragment2, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
3435
3578
  var HelpDialog = () => {
3436
3579
  const { theme } = useTheme();
3437
3580
  const shortcuts = [
3438
3581
  { key: "Ctrl+C (twice)", action: "Quit application" },
3582
+ { key: "Ctrl+U", action: "Undo last action" },
3439
3583
  { key: "?", action: "Toggle help dialog" },
3440
3584
  { key: "Shift+;", action: "Show month overview" },
3441
3585
  { key: "t", action: "Select theme" },
@@ -3464,7 +3608,7 @@ var HelpDialog = () => {
3464
3608
  { key: "j/k", action: "Scroll timeline" },
3465
3609
  { key: "Shift+C", action: "Clear timeline" }
3466
3610
  ];
3467
- return /* @__PURE__ */ jsx18(Modal, { children: /* @__PURE__ */ jsxs12(
3611
+ return /* @__PURE__ */ jsx19(Modal, { children: /* @__PURE__ */ jsxs12(
3468
3612
  Box13,
3469
3613
  {
3470
3614
  flexDirection: "column",
@@ -3474,26 +3618,26 @@ var HelpDialog = () => {
3474
3618
  paddingY: 1,
3475
3619
  backgroundColor: theme.colors.modalBackground || theme.colors.background,
3476
3620
  children: [
3477
- /* @__PURE__ */ jsx18(Text10, { bold: true, color: theme.colors.calendarHeader, children: "Keyboard Shortcuts" }),
3478
- /* @__PURE__ */ jsx18(Box13, { flexDirection: "column", marginTop: 1, children: shortcuts.map((item, idx) => /* @__PURE__ */ jsx18(Box13, { marginY: 0, children: item.key ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
3479
- /* @__PURE__ */ jsx18(Box13, { width: 20, children: /* @__PURE__ */ jsx18(Text10, { color: theme.colors.timelineEventStarted, children: item.key }) }),
3480
- /* @__PURE__ */ jsx18(Text10, { color: theme.colors.foreground, children: item.action })
3481
- ] }) : /* @__PURE__ */ jsx18(Text10, { color: theme.colors.separator, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }, idx)) }),
3482
- /* @__PURE__ */ jsx18(Box13, { marginY: 1, children: /* @__PURE__ */ jsx18(Text10, { color: theme.colors.keyboardHint, dimColor: true, children: "Press '?' to close" }) })
3621
+ /* @__PURE__ */ jsx19(Text10, { bold: true, color: theme.colors.calendarHeader, children: "Keyboard Shortcuts" }),
3622
+ /* @__PURE__ */ jsx19(Box13, { flexDirection: "column", marginTop: 1, children: shortcuts.map((item, idx) => /* @__PURE__ */ jsx19(Box13, { marginY: 0, children: item.key ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
3623
+ /* @__PURE__ */ jsx19(Box13, { width: 20, children: /* @__PURE__ */ jsx19(Text10, { color: theme.colors.timelineEventStarted, children: item.key }) }),
3624
+ /* @__PURE__ */ jsx19(Text10, { color: theme.colors.foreground, children: item.action })
3625
+ ] }) : /* @__PURE__ */ jsx19(Text10, { color: theme.colors.separator, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }, idx)) }),
3626
+ /* @__PURE__ */ jsx19(Box13, { marginY: 1, children: /* @__PURE__ */ jsx19(Text10, { color: theme.colors.keyboardHint, dimColor: true, children: "Press '?' to close" }) })
3483
3627
  ]
3484
3628
  }
3485
3629
  ) });
3486
3630
  };
3487
3631
 
3488
3632
  // src/components/common/ThemeDialog.tsx
3489
- import { useState as useState8, useMemo as useMemo3, useEffect as useEffect10 } from "react";
3633
+ import { useState as useState9, useMemo as useMemo3, useEffect as useEffect10 } from "react";
3490
3634
  import { Box as Box14, Text as Text11, useInput as useInput6 } from "ink";
3491
- import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
3635
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
3492
3636
  var ThemeDialog = () => {
3493
3637
  const { theme, setTheme, themeName } = useTheme();
3494
3638
  const { setShowThemeDialog } = useApp();
3495
- const [searchQuery, setSearchQuery] = useState8("");
3496
- const [focusMode, setFocusMode] = useState8("search");
3639
+ const [searchQuery, setSearchQuery] = useState9("");
3640
+ const [focusMode, setFocusMode] = useState9("search");
3497
3641
  const lightThemes2 = useMemo3(() => getLightThemeNames(), []);
3498
3642
  const darkThemes2 = useMemo3(() => getDarkThemeNames(), []);
3499
3643
  const allThemes = useMemo3(() => {
@@ -3527,7 +3671,7 @@ var ThemeDialog = () => {
3527
3671
  const idx = themeItems.findIndex((item) => item.value === themeName);
3528
3672
  return idx >= 0 ? idx : 0;
3529
3673
  }, [themeItems, themeName]);
3530
- const [selectedIndex, setSelectedIndex] = useState8(initialIndex);
3674
+ const [selectedIndex, setSelectedIndex] = useState9(initialIndex);
3531
3675
  useEffect10(() => {
3532
3676
  setSelectedIndex(0);
3533
3677
  }, [searchQuery]);
@@ -3574,7 +3718,7 @@ var ThemeDialog = () => {
3574
3718
  const formatThemeName = (name) => {
3575
3719
  return name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
3576
3720
  };
3577
- return /* @__PURE__ */ jsx19(Modal, { children: /* @__PURE__ */ jsxs13(
3721
+ return /* @__PURE__ */ jsx20(Modal, { children: /* @__PURE__ */ jsxs13(
3578
3722
  Box14,
3579
3723
  {
3580
3724
  flexDirection: "column",
@@ -3585,13 +3729,13 @@ var ThemeDialog = () => {
3585
3729
  width: 44,
3586
3730
  backgroundColor: theme.colors.modalBackground || theme.colors.background,
3587
3731
  children: [
3588
- /* @__PURE__ */ jsx19(Text11, { bold: true, color: theme.colors.calendarHeader, underline: true, children: "Select Theme" }),
3732
+ /* @__PURE__ */ jsx20(Text11, { bold: true, color: theme.colors.calendarHeader, underline: true, children: "Select Theme" }),
3589
3733
  /* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: "row", alignItems: "center", children: [
3590
3734
  /* @__PURE__ */ jsxs13(Text11, { color: theme.colors.foreground, dimColor: true, children: [
3591
3735
  "Search:",
3592
3736
  " "
3593
3737
  ] }),
3594
- /* @__PURE__ */ jsx19(
3738
+ /* @__PURE__ */ jsx20(
3595
3739
  ControlledTextInput,
3596
3740
  {
3597
3741
  value: searchQuery,
@@ -3609,21 +3753,21 @@ var ThemeDialog = () => {
3609
3753
  }
3610
3754
  )
3611
3755
  ] }),
3612
- /* @__PURE__ */ jsx19(Box14, { flexDirection: "column", marginTop: 1, children: allThemes.map((item, idx) => {
3756
+ /* @__PURE__ */ jsx20(Box14, { flexDirection: "column", marginTop: 1, children: allThemes.map((item, idx) => {
3613
3757
  if (item.type === "separator") {
3614
- return /* @__PURE__ */ jsx19(
3758
+ return /* @__PURE__ */ jsx20(
3615
3759
  Box14,
3616
3760
  {
3617
3761
  marginTop: idx > 0 ? 1 : 0,
3618
3762
  marginBottom: 0,
3619
- children: /* @__PURE__ */ jsx19(Text11, { bold: true, color: theme.colors.calendarHeader, dimColor: true, children: item.value })
3763
+ children: /* @__PURE__ */ jsx20(Text11, { bold: true, color: theme.colors.calendarHeader, dimColor: true, children: item.value })
3620
3764
  },
3621
3765
  `sep-${idx}`
3622
3766
  );
3623
3767
  }
3624
3768
  const isSelected = item.value === selectedThemeName;
3625
3769
  const isCurrent = item.value === themeName;
3626
- return /* @__PURE__ */ jsx19(Box14, { paddingLeft: 1, children: /* @__PURE__ */ jsxs13(
3770
+ return /* @__PURE__ */ jsx20(Box14, { paddingLeft: 1, children: /* @__PURE__ */ jsxs13(
3627
3771
  Text11,
3628
3772
  {
3629
3773
  color: isSelected ? theme.colors.focusIndicator : theme.colors.foreground,
@@ -3636,7 +3780,7 @@ var ThemeDialog = () => {
3636
3780
  }
3637
3781
  ) }, item.value);
3638
3782
  }) }),
3639
- /* @__PURE__ */ jsx19(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text11, { color: theme.colors.keyboardHint, dimColor: true, children: focusMode === "search" ? "Type to search \u2022 \u2193 to navigate list \u2022 Esc to close" : "\u2191/\u2193 or k/j to navigate \u2022 Enter to select \u2022 Esc to close" }) })
3783
+ /* @__PURE__ */ jsx20(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text11, { color: theme.colors.keyboardHint, dimColor: true, children: focusMode === "search" ? "Type to search \u2022 \u2193 to navigate list \u2022 Esc to close" : "\u2191/\u2193 or k/j to navigate \u2022 Enter to select \u2022 Esc to close" }) })
3640
3784
  ]
3641
3785
  }
3642
3786
  ) });
@@ -3645,7 +3789,7 @@ var ThemeDialog = () => {
3645
3789
  // src/components/common/ClearTimelineDialog.tsx
3646
3790
  import { Box as Box15, Text as Text12, useInput as useInput7 } from "ink";
3647
3791
  import { format as format2 } from "date-fns";
3648
- import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
3792
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
3649
3793
  var ClearTimelineDialog = () => {
3650
3794
  const { theme } = useTheme();
3651
3795
  const {
@@ -3672,7 +3816,7 @@ var ClearTimelineDialog = () => {
3672
3816
  handleCancel();
3673
3817
  }
3674
3818
  });
3675
- return /* @__PURE__ */ jsx20(Modal, { children: /* @__PURE__ */ jsxs14(
3819
+ return /* @__PURE__ */ jsx21(Modal, { children: /* @__PURE__ */ jsxs14(
3676
3820
  Box15,
3677
3821
  {
3678
3822
  flexDirection: "column",
@@ -3682,39 +3826,39 @@ var ClearTimelineDialog = () => {
3682
3826
  paddingY: 2,
3683
3827
  backgroundColor: theme.colors.modalBackground || theme.colors.background,
3684
3828
  children: [
3685
- /* @__PURE__ */ jsx20(Box15, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx20(Text12, { bold: true, color: theme.colors.taskStateDelayed, children: "Clear Timeline" }) }),
3829
+ /* @__PURE__ */ jsx21(Box15, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx21(Text12, { bold: true, color: theme.colors.taskStateDelayed, children: "Clear Timeline" }) }),
3686
3830
  /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginY: 1, children: [
3687
- /* @__PURE__ */ jsx20(Text12, { color: theme.colors.foreground, children: "You are about to clear the timeline for" }),
3688
- /* @__PURE__ */ jsx20(Box15, { justifyContent: "center", marginY: 1, children: /* @__PURE__ */ jsx20(Text12, { bold: true, color: theme.colors.calendarHeader, children: formattedDate }) }),
3831
+ /* @__PURE__ */ jsx21(Text12, { color: theme.colors.foreground, children: "You are about to clear the timeline for" }),
3832
+ /* @__PURE__ */ jsx21(Box15, { justifyContent: "center", marginY: 1, children: /* @__PURE__ */ jsx21(Text12, { bold: true, color: theme.colors.calendarHeader, children: formattedDate }) }),
3689
3833
  eventCount > 0 ? /* @__PURE__ */ jsxs14(Text12, { color: theme.colors.keyboardHint, children: [
3690
3834
  "This will remove ",
3691
3835
  eventCount,
3692
3836
  " event",
3693
3837
  eventCount !== 1 ? "s" : "",
3694
3838
  " from the timeline."
3695
- ] }) : /* @__PURE__ */ jsx20(Text12, { color: theme.colors.keyboardHint, dimColor: true, children: "The timeline is already empty." })
3839
+ ] }) : /* @__PURE__ */ jsx21(Text12, { color: theme.colors.keyboardHint, dimColor: true, children: "The timeline is already empty." })
3696
3840
  ] }),
3697
3841
  /* @__PURE__ */ jsxs14(Box15, { marginTop: 2, justifyContent: "center", children: [
3698
- /* @__PURE__ */ jsx20(Text12, { color: theme.colors.foreground, children: "Are you sure? " }),
3699
- /* @__PURE__ */ jsx20(Text12, { color: theme.colors.taskStateCompleted, bold: true, children: "[Y]es" }),
3700
- /* @__PURE__ */ jsx20(Text12, { color: theme.colors.foreground, children: " / " }),
3701
- /* @__PURE__ */ jsx20(Text12, { color: theme.colors.taskStateDelayed, bold: true, children: "[N]o" })
3842
+ /* @__PURE__ */ jsx21(Text12, { color: theme.colors.foreground, children: "Are you sure? " }),
3843
+ /* @__PURE__ */ jsx21(Text12, { color: theme.colors.taskStateCompleted, bold: true, children: "[Y]es" }),
3844
+ /* @__PURE__ */ jsx21(Text12, { color: theme.colors.foreground, children: " / " }),
3845
+ /* @__PURE__ */ jsx21(Text12, { color: theme.colors.taskStateDelayed, bold: true, children: "[N]o" })
3702
3846
  ] }),
3703
- /* @__PURE__ */ jsx20(Box15, { marginTop: 2, justifyContent: "center", children: /* @__PURE__ */ jsx20(Text12, { color: theme.colors.keyboardHint, dimColor: true, children: "Press Y to confirm, N or Esc to cancel" }) })
3847
+ /* @__PURE__ */ jsx21(Box15, { marginTop: 2, justifyContent: "center", children: /* @__PURE__ */ jsx21(Text12, { color: theme.colors.keyboardHint, dimColor: true, children: "Press Y to confirm, N or Esc to cancel" }) })
3704
3848
  ]
3705
3849
  }
3706
3850
  ) });
3707
3851
  };
3708
3852
 
3709
3853
  // src/components/overview/OverviewScreen.tsx
3710
- import React9, { useMemo as useMemo4 } from "react";
3854
+ import React10, { useMemo as useMemo4 } from "react";
3711
3855
  import { Box as Box16, Text as Text13, useInput as useInput8 } from "ink";
3712
3856
  import { startOfMonth as startOfMonth2, endOfMonth as endOfMonth2, eachDayOfInterval as eachDayOfInterval2 } from "date-fns";
3713
- import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
3857
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
3714
3858
  var OverviewScreen = () => {
3715
3859
  const { theme } = useTheme();
3716
3860
  const { tasks, overviewMonth, setOverviewMonth, setShowOverview } = useApp();
3717
- const [scrollOffset, setScrollOffset] = React9.useState(0);
3861
+ const [scrollOffset, setScrollOffset] = React10.useState(0);
3718
3862
  const { height: terminalHeight } = useTerminalSize();
3719
3863
  const visibleRows = useMemo4(() => {
3720
3864
  return Math.max(2, Math.floor((terminalHeight - 9) / 3));
@@ -3750,7 +3894,7 @@ var OverviewScreen = () => {
3750
3894
  setOverviewMonth({ ...overviewMonth, month: newMonth });
3751
3895
  }
3752
3896
  };
3753
- React9.useEffect(() => {
3897
+ React10.useEffect(() => {
3754
3898
  setScrollOffset(0);
3755
3899
  }, [overviewMonth]);
3756
3900
  useInput8((input, key) => {
@@ -3791,18 +3935,18 @@ var OverviewScreen = () => {
3791
3935
  const canScrollDown = scrollOffset + visibleRows < rows;
3792
3936
  return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", padding: 1, width: "100%", height: "100%", children: [
3793
3937
  /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
3794
- /* @__PURE__ */ jsx21(Text13, { bold: true, color: theme.colors.focusIndicator, children: "Overview" }),
3795
- /* @__PURE__ */ jsx21(Text13, { color: theme.colors.foreground, children: monthName })
3938
+ /* @__PURE__ */ jsx22(Text13, { bold: true, color: theme.colors.focusIndicator, children: "Overview" }),
3939
+ /* @__PURE__ */ jsx22(Text13, { color: theme.colors.foreground, children: monthName })
3796
3940
  ] }),
3797
3941
  /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", flexGrow: 1, children: [
3798
- canScrollUp && /* @__PURE__ */ jsx21(Box16, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx21(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
3942
+ canScrollUp && /* @__PURE__ */ jsx22(Box16, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more above --" }) }),
3799
3943
  visibleRowData.map((_, index) => {
3800
3944
  const rowIndex = scrollOffset + index;
3801
- return /* @__PURE__ */ jsx21(Box16, { flexDirection: "row", marginBottom: 1, children: Array.from({ length: columns }).map((_2, colIndex) => {
3945
+ return /* @__PURE__ */ jsx22(Box16, { flexDirection: "row", marginBottom: 1, children: Array.from({ length: columns }).map((_2, colIndex) => {
3802
3946
  const dateIndex = rowIndex * columns + colIndex;
3803
3947
  const date = monthDates[dateIndex];
3804
3948
  if (!date) {
3805
- return /* @__PURE__ */ jsx21(
3949
+ return /* @__PURE__ */ jsx22(
3806
3950
  Box16,
3807
3951
  {
3808
3952
  flexDirection: "column",
@@ -3831,9 +3975,9 @@ var OverviewScreen = () => {
3831
3975
  flexBasis: 0,
3832
3976
  marginRight: colIndex === columns - 1 ? 0 : 2,
3833
3977
  children: [
3834
- /* @__PURE__ */ jsx21(Text13, { bold: true, color: theme.colors.calendarSelected, children: formatDate(date, "do MMM") }),
3978
+ /* @__PURE__ */ jsx22(Text13, { bold: true, color: theme.colors.calendarSelected, children: formatDate(date, "do MMM") }),
3835
3979
  /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
3836
- flatTasksWithDepth.length === 0 ? /* @__PURE__ */ jsx21(Text13, { dimColor: true, color: theme.colors.keyboardHint, children: "No tasks" }) : flatTasksWithDepth.slice(0, 10).map(({ task, depth }) => /* @__PURE__ */ jsx21(
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(
3837
3981
  TaskItem,
3838
3982
  {
3839
3983
  task,
@@ -3854,9 +3998,9 @@ var OverviewScreen = () => {
3854
3998
  );
3855
3999
  }) }, rowIndex);
3856
4000
  }),
3857
- canScrollDown && /* @__PURE__ */ jsx21(Box16, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx21(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
4001
+ canScrollDown && /* @__PURE__ */ jsx22(Box16, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx22(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "-- more below --" }) })
3858
4002
  ] }),
3859
- /* @__PURE__ */ jsx21(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text13, { color: theme.colors.keyboardHint, dimColor: true, children: "n/p or \u2190/\u2192: month | j/k or \u2193/\u2191: scroll | Esc: close | Shift+;: toggle" }) })
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" }) })
3860
4004
  ] });
3861
4005
  };
3862
4006
  var TaskItem = ({ task, theme, depth }) => {
@@ -3867,8 +4011,8 @@ var TaskItem = ({ task, theme, depth }) => {
3867
4011
  checkbox,
3868
4012
  " "
3869
4013
  ] }),
3870
- depth > 0 && /* @__PURE__ */ jsx21(Text13, { children: " ".repeat(depth) }),
3871
- /* @__PURE__ */ jsx21(
4014
+ depth > 0 && /* @__PURE__ */ jsx22(Text13, { children: " ".repeat(depth) }),
4015
+ /* @__PURE__ */ jsx22(
3872
4016
  Text13,
3873
4017
  {
3874
4018
  color,
@@ -3902,7 +4046,7 @@ function getStateColor2(state, theme) {
3902
4046
  }
3903
4047
 
3904
4048
  // src/App.tsx
3905
- import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
4049
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
3906
4050
  var AppContent = () => {
3907
4051
  const {
3908
4052
  showHelp,
@@ -3916,26 +4060,26 @@ var AppContent = () => {
3916
4060
  useKeyboardNav();
3917
4061
  const { width, height } = useTerminalSize();
3918
4062
  if (showThemeDialog) {
3919
- return /* @__PURE__ */ jsx22(ThemeDialog, {});
4063
+ return /* @__PURE__ */ jsx23(ThemeDialog, {});
3920
4064
  }
3921
4065
  if (showHelp) {
3922
- return /* @__PURE__ */ jsx22(HelpDialog, {});
4066
+ return /* @__PURE__ */ jsx23(HelpDialog, {});
3923
4067
  }
3924
4068
  if (showClearTimelineDialog) {
3925
- return /* @__PURE__ */ jsx22(ClearTimelineDialog, {});
4069
+ return /* @__PURE__ */ jsx23(ClearTimelineDialog, {});
3926
4070
  }
3927
- return /* @__PURE__ */ jsx22(FullscreenBackground, { backgroundColor: theme.colors.background || "black", children: /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", width, height, padding: 1, backgroundColor: theme.colors.background, children: [
3928
- showOverview ? /* @__PURE__ */ jsx22(OverviewScreen, {}) : /* @__PURE__ */ jsx22(
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(
3929
4073
  ThreeColumnLayout,
3930
4074
  {
3931
- leftPane: /* @__PURE__ */ jsx22(CalendarPane, {}),
3932
- centerPane: /* @__PURE__ */ jsx22(TasksPane, {}),
3933
- rightPane: /* @__PURE__ */ jsx22(TimelinePane, {}),
4075
+ leftPane: /* @__PURE__ */ jsx23(CalendarPane, {}),
4076
+ centerPane: /* @__PURE__ */ jsx23(TasksPane, {}),
4077
+ rightPane: /* @__PURE__ */ jsx23(TimelinePane, {}),
3934
4078
  activePane,
3935
4079
  height: height - 2
3936
4080
  }
3937
4081
  ),
3938
- exitConfirmation && /* @__PURE__ */ jsx22(Box17, { width: "100%", justifyContent: "center", paddingY: 1, children: /* @__PURE__ */ jsxs16(Text14, { backgroundColor: "red", color: "white", bold: true, children: [
4082
+ exitConfirmation && /* @__PURE__ */ jsx23(Box17, { width: "100%", justifyContent: "center", paddingY: 1, children: /* @__PURE__ */ jsxs16(Text14, { backgroundColor: "red", color: "white", bold: true, children: [
3939
4083
  " ",
3940
4084
  "Press Ctrl+C again to exit Epoch",
3941
4085
  " "
@@ -3943,12 +4087,12 @@ var AppContent = () => {
3943
4087
  ] }) });
3944
4088
  };
3945
4089
  var App = () => {
3946
- return /* @__PURE__ */ jsx22(StorageProvider, { children: /* @__PURE__ */ jsx22(ThemeProvider, { initialTheme: "dark", children: /* @__PURE__ */ jsx22(AppProvider, { children: /* @__PURE__ */ jsx22(AppContent, {}) }) }) });
4090
+ return /* @__PURE__ */ jsx23(StorageProvider, { children: /* @__PURE__ */ jsx23(ThemeProvider, { initialTheme: "dark", children: /* @__PURE__ */ jsx23(UndoProvider, { children: /* @__PURE__ */ jsx23(AppProvider, { children: /* @__PURE__ */ jsx23(AppContent, {}) }) }) }) });
3947
4091
  };
3948
4092
  var App_default = App;
3949
4093
 
3950
4094
  // src/index.tsx
3951
- import { jsx as jsx23 } from "react/jsx-runtime";
4095
+ import { jsx as jsx24 } from "react/jsx-runtime";
3952
4096
  console.clear();
3953
- var app = render(/* @__PURE__ */ jsx23(App_default, {}), { exitOnCtrlC: false });
4097
+ var app = render(/* @__PURE__ */ jsx24(App_default, {}), { exitOnCtrlC: false });
3954
4098
  global.__inkApp = app;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epoch-tui",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "A TUI app for daily task logging and time tracking",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",