@khemsok/tunl 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -38
- package/dist/cli.js +625 -361
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -55096,129 +55096,97 @@ var useTerminalDimensions = () => {
|
|
|
55096
55096
|
};
|
|
55097
55097
|
|
|
55098
55098
|
// src/cli.tsx
|
|
55099
|
-
import { execSync as
|
|
55099
|
+
import { execSync as execSync3 } from "child_process";
|
|
55100
55100
|
|
|
55101
55101
|
// src/app.tsx
|
|
55102
|
-
var
|
|
55102
|
+
var import_react21 = __toESM(require_react(), 1);
|
|
55103
|
+
|
|
55104
|
+
// src/utils/time.ts
|
|
55105
|
+
function formatMinutes(totalMinutes) {
|
|
55106
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
55107
|
+
const mins = totalMinutes % 60;
|
|
55108
|
+
if (hours > 0) {
|
|
55109
|
+
return `${hours}h ${mins}m`;
|
|
55110
|
+
}
|
|
55111
|
+
return `${mins}m`;
|
|
55112
|
+
}
|
|
55113
|
+
function formatTime(seconds) {
|
|
55114
|
+
const m2 = Math.floor(seconds / 60);
|
|
55115
|
+
const s = seconds % 60;
|
|
55116
|
+
return `${String(m2).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
55117
|
+
}
|
|
55118
|
+
|
|
55119
|
+
// src/theme.ts
|
|
55120
|
+
var COLORS = {
|
|
55121
|
+
text: "#CDD6F4",
|
|
55122
|
+
textBody: "#B8C0E0",
|
|
55123
|
+
textMuted: "#9399B2",
|
|
55124
|
+
textDim: "#7F849C",
|
|
55125
|
+
textDimmer: "#585B70",
|
|
55126
|
+
textLabel: "#6E738D",
|
|
55127
|
+
accent: "#7FDBCA",
|
|
55128
|
+
highlight: "#E0F0FF",
|
|
55129
|
+
white: "#FFFFFF",
|
|
55130
|
+
success: "#A6DA95",
|
|
55131
|
+
warning: "#EED49F",
|
|
55132
|
+
error: "#ED8796",
|
|
55133
|
+
orange: "#F5A97F",
|
|
55134
|
+
purple: "#C6A0F6",
|
|
55135
|
+
border: "#6C7086"
|
|
55136
|
+
};
|
|
55137
|
+
var CELEBRATION_COLORS = [
|
|
55138
|
+
COLORS.purple,
|
|
55139
|
+
COLORS.accent,
|
|
55140
|
+
COLORS.orange,
|
|
55141
|
+
COLORS.highlight,
|
|
55142
|
+
COLORS.warning
|
|
55143
|
+
];
|
|
55103
55144
|
|
|
55104
55145
|
// node_modules/@opentui/react/jsx-dev-runtime.js
|
|
55105
55146
|
var import_jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
|
|
55106
55147
|
|
|
55107
55148
|
// src/components/timer.tsx
|
|
55108
55149
|
var DIGITS = {
|
|
55109
|
-
"0": [
|
|
55110
|
-
|
|
55111
|
-
|
|
55112
|
-
|
|
55113
|
-
|
|
55114
|
-
|
|
55115
|
-
],
|
|
55116
|
-
"
|
|
55117
|
-
|
|
55118
|
-
|
|
55119
|
-
|
|
55120
|
-
" \u2588 ",
|
|
55121
|
-
" \u2584\u2588\u2584"
|
|
55122
|
-
],
|
|
55123
|
-
"2": [
|
|
55124
|
-
"\u2588\u2580\u2580\u2588",
|
|
55125
|
-
" \u2588",
|
|
55126
|
-
"\u2588\u2580\u2580\u2580",
|
|
55127
|
-
"\u2588 ",
|
|
55128
|
-
"\u2588\u2584\u2584\u2584"
|
|
55129
|
-
],
|
|
55130
|
-
"3": [
|
|
55131
|
-
"\u2588\u2580\u2580\u2588",
|
|
55132
|
-
" \u2588",
|
|
55133
|
-
" \u2580\u2580\u2588",
|
|
55134
|
-
" \u2588",
|
|
55135
|
-
"\u2588\u2584\u2584\u2588"
|
|
55136
|
-
],
|
|
55137
|
-
"4": [
|
|
55138
|
-
"\u2588 \u2588",
|
|
55139
|
-
"\u2588 \u2588",
|
|
55140
|
-
"\u2580\u2580\u2580\u2588",
|
|
55141
|
-
" \u2588",
|
|
55142
|
-
" \u2588"
|
|
55143
|
-
],
|
|
55144
|
-
"5": [
|
|
55145
|
-
"\u2588\u2580\u2580\u2580",
|
|
55146
|
-
"\u2588 ",
|
|
55147
|
-
"\u2580\u2580\u2580\u2588",
|
|
55148
|
-
" \u2588",
|
|
55149
|
-
"\u2588\u2584\u2584\u2588"
|
|
55150
|
-
],
|
|
55151
|
-
"6": [
|
|
55152
|
-
"\u2588\u2580\u2580\u2580",
|
|
55153
|
-
"\u2588 ",
|
|
55154
|
-
"\u2588\u2580\u2580\u2588",
|
|
55155
|
-
"\u2588 \u2588",
|
|
55156
|
-
"\u2588\u2584\u2584\u2588"
|
|
55157
|
-
],
|
|
55158
|
-
"7": [
|
|
55159
|
-
"\u2588\u2580\u2580\u2588",
|
|
55160
|
-
" \u2588",
|
|
55161
|
-
" \u2588",
|
|
55162
|
-
" \u2588 ",
|
|
55163
|
-
" \u2588 "
|
|
55164
|
-
],
|
|
55165
|
-
"8": [
|
|
55166
|
-
"\u2588\u2580\u2580\u2588",
|
|
55167
|
-
"\u2588 \u2588",
|
|
55168
|
-
"\u2588\u2580\u2580\u2588",
|
|
55169
|
-
"\u2588 \u2588",
|
|
55170
|
-
"\u2588\u2584\u2584\u2588"
|
|
55171
|
-
],
|
|
55172
|
-
"9": [
|
|
55173
|
-
"\u2588\u2580\u2580\u2588",
|
|
55174
|
-
"\u2588 \u2588",
|
|
55175
|
-
"\u2580\u2580\u2580\u2588",
|
|
55176
|
-
" \u2588",
|
|
55177
|
-
"\u2588\u2584\u2584\u2588"
|
|
55178
|
-
],
|
|
55179
|
-
":": [
|
|
55180
|
-
" ",
|
|
55181
|
-
" \u2580\u2580 ",
|
|
55182
|
-
" ",
|
|
55183
|
-
" \u2580\u2580 ",
|
|
55184
|
-
" "
|
|
55185
|
-
]
|
|
55150
|
+
"0": ["\u250C\u2500\u2510", "\u2502 \u2502", "\u2514\u2500\u2518"],
|
|
55151
|
+
"1": [" \u2500\u2510", " \u2502", " \u2500\u2518"],
|
|
55152
|
+
"2": ["\u250C\u2500\u2510", "\u250C\u2500\u2518", "\u2514\u2500 "],
|
|
55153
|
+
"3": ["\u250C\u2500\u2510", " \u2500\u2524", "\u2514\u2500\u2518"],
|
|
55154
|
+
"4": ["\u2502 \u2502", "\u2514\u2500\u2524", " \u2502"],
|
|
55155
|
+
"5": ["\u250C\u2500 ", "\u2514\u2500\u2510", "\u2500\u2500\u2518"],
|
|
55156
|
+
"6": ["\u250C\u2500 ", "\u251C\u2500\u2510", "\u2514\u2500\u2518"],
|
|
55157
|
+
"7": ["\u2500\u2500\u2510", " \u2502", " \u2502"],
|
|
55158
|
+
"8": ["\u250C\u2500\u2510", "\u251C\u2500\u2524", "\u2514\u2500\u2518"],
|
|
55159
|
+
"9": ["\u250C\u2500\u2510", "\u2514\u2500\u2524", " \u2502"],
|
|
55160
|
+
":": [" ", " \xB7 ", " \xB7 "]
|
|
55186
55161
|
};
|
|
55187
|
-
function
|
|
55188
|
-
const
|
|
55189
|
-
const
|
|
55190
|
-
return `${String(m2).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
55191
|
-
}
|
|
55192
|
-
function TimerDisplay({
|
|
55193
|
-
remaining,
|
|
55194
|
-
color,
|
|
55195
|
-
animTick
|
|
55196
|
-
}) {
|
|
55197
|
-
const timeStr = formatTime(remaining);
|
|
55198
|
-
const fg2 = color || "#E0F0FF";
|
|
55199
|
-
const colonVisible = animTick === undefined || animTick % 2 === 0;
|
|
55200
|
-
const colonLines = colonVisible ? DIGITS[":"] : [" ", " ", " ", " ", " "];
|
|
55201
|
-
const lines = ["", "", "", "", ""];
|
|
55162
|
+
function getTimerLines(seconds) {
|
|
55163
|
+
const timeStr = formatTime(seconds);
|
|
55164
|
+
const lines = ["", "", ""];
|
|
55202
55165
|
for (let i = 0;i < timeStr.length; i++) {
|
|
55203
55166
|
const char = timeStr[i];
|
|
55204
|
-
const digitLines =
|
|
55205
|
-
for (let row = 0;row <
|
|
55167
|
+
const digitLines = DIGITS[char] || DIGITS["0"];
|
|
55168
|
+
for (let row = 0;row < 3; row++) {
|
|
55206
55169
|
lines[row] += digitLines[row] + " ";
|
|
55207
55170
|
}
|
|
55208
55171
|
}
|
|
55172
|
+
return lines;
|
|
55173
|
+
}
|
|
55174
|
+
function TimerDisplay({ remaining, color }) {
|
|
55175
|
+
const timerLines = getTimerLines(remaining);
|
|
55176
|
+
const fg2 = color || COLORS.text;
|
|
55209
55177
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55210
55178
|
justifyContent: "center",
|
|
55211
55179
|
alignItems: "center",
|
|
55212
55180
|
width: "100%",
|
|
55213
55181
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55214
55182
|
fg: fg2,
|
|
55215
|
-
children:
|
|
55183
|
+
children: timerLines.join(`
|
|
55216
55184
|
`)
|
|
55217
55185
|
}, undefined, false, undefined, this)
|
|
55218
55186
|
}, undefined, false, undefined, this);
|
|
55219
55187
|
}
|
|
55220
55188
|
|
|
55221
|
-
// src/
|
|
55189
|
+
// src/utils/colors.ts
|
|
55222
55190
|
function interpolateColor(color1, color2, t2) {
|
|
55223
55191
|
const r = Math.round(color1[0] + (color2[0] - color1[0]) * t2);
|
|
55224
55192
|
const g2 = Math.round(color1[1] + (color2[1] - color1[1]) * t2);
|
|
@@ -55241,10 +55209,9 @@ function getGradientColor(progress) {
|
|
|
55241
55209
|
}
|
|
55242
55210
|
return interpolateColor(STOPS[STOPS.length - 2].color, STOPS[STOPS.length - 1].color, 1);
|
|
55243
55211
|
}
|
|
55244
|
-
|
|
55245
|
-
|
|
55246
|
-
|
|
55247
|
-
}) {
|
|
55212
|
+
|
|
55213
|
+
// src/components/progress-bar.tsx
|
|
55214
|
+
function ProgressBar({ progress, width }) {
|
|
55248
55215
|
const barWidth = Math.max(width - 10, 20);
|
|
55249
55216
|
const filledCount = Math.round(progress * barWidth);
|
|
55250
55217
|
const emptyCount = barWidth - filledCount;
|
|
@@ -55263,11 +55230,11 @@ function ProgressBar({
|
|
|
55263
55230
|
children: filled
|
|
55264
55231
|
}, undefined, false, undefined, this),
|
|
55265
55232
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55266
|
-
fg:
|
|
55233
|
+
fg: COLORS.textDimmer,
|
|
55267
55234
|
children: empty
|
|
55268
55235
|
}, undefined, false, undefined, this),
|
|
55269
55236
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55270
|
-
fg:
|
|
55237
|
+
fg: COLORS.textMuted,
|
|
55271
55238
|
children: ` ${String(percent).padStart(3)}%`
|
|
55272
55239
|
}, undefined, false, undefined, this)
|
|
55273
55240
|
]
|
|
@@ -55561,13 +55528,14 @@ var cityTheme = {
|
|
|
55561
55528
|
};
|
|
55562
55529
|
|
|
55563
55530
|
// src/components/art-canvas.tsx
|
|
55531
|
+
var FULL_CYCLE_TICKS = 375;
|
|
55564
55532
|
function ArtCanvas({
|
|
55565
55533
|
stage,
|
|
55566
55534
|
theme,
|
|
55567
55535
|
animTick
|
|
55568
55536
|
}) {
|
|
55569
|
-
const
|
|
55570
|
-
const artData = theme.generate ? theme.generate(
|
|
55537
|
+
const tickProgress = Math.min(animTick / FULL_CYCLE_TICKS, 1);
|
|
55538
|
+
const artData = theme.generate ? theme.generate(tickProgress, animTick, 70) : theme.stages[Math.min(Math.floor(tickProgress * (NUM_STAGES - 1)), theme.stages.length - 1)];
|
|
55571
55539
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55572
55540
|
flexGrow: 1,
|
|
55573
55541
|
width: "100%",
|
|
@@ -55603,7 +55571,7 @@ function StatusBar({
|
|
|
55603
55571
|
width: "100%",
|
|
55604
55572
|
borderStyle: "single",
|
|
55605
55573
|
border: ["top"],
|
|
55606
|
-
borderColor:
|
|
55574
|
+
borderColor: COLORS.border,
|
|
55607
55575
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55608
55576
|
justifyContent: "center",
|
|
55609
55577
|
alignItems: "center",
|
|
@@ -55611,77 +55579,89 @@ function StatusBar({
|
|
|
55611
55579
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55612
55580
|
children: [
|
|
55613
55581
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55614
|
-
fg:
|
|
55582
|
+
fg: COLORS.textMuted,
|
|
55615
55583
|
children: "q"
|
|
55616
55584
|
}, undefined, false, undefined, this),
|
|
55617
55585
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55618
|
-
fg:
|
|
55586
|
+
fg: COLORS.textDim,
|
|
55619
55587
|
children: " quit"
|
|
55620
55588
|
}, undefined, false, undefined, this),
|
|
55621
55589
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55622
|
-
fg:
|
|
55590
|
+
fg: COLORS.border,
|
|
55623
55591
|
children: " \xB7 "
|
|
55624
55592
|
}, undefined, false, undefined, this),
|
|
55625
55593
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55626
|
-
fg:
|
|
55594
|
+
fg: COLORS.textMuted,
|
|
55627
55595
|
children: "space"
|
|
55628
55596
|
}, undefined, false, undefined, this),
|
|
55629
55597
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55630
|
-
fg:
|
|
55598
|
+
fg: COLORS.textDim,
|
|
55631
55599
|
children: timerStatus === "idle" ? " start" : timerStatus === "running" ? " pause" : timerStatus === "paused" ? " resume" : " restart"
|
|
55632
55600
|
}, undefined, false, undefined, this),
|
|
55633
55601
|
timerStatus === "idle" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
|
|
55634
55602
|
children: [
|
|
55635
55603
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55636
|
-
fg:
|
|
55604
|
+
fg: COLORS.border,
|
|
55637
55605
|
children: " \xB7 "
|
|
55638
55606
|
}, undefined, false, undefined, this),
|
|
55639
55607
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55640
|
-
fg:
|
|
55608
|
+
fg: COLORS.textMuted,
|
|
55641
55609
|
children: "+/-"
|
|
55642
55610
|
}, undefined, false, undefined, this),
|
|
55643
55611
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55644
|
-
fg:
|
|
55612
|
+
fg: COLORS.textDim,
|
|
55645
55613
|
children: " time"
|
|
55646
55614
|
}, undefined, false, undefined, this),
|
|
55647
55615
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55648
|
-
fg:
|
|
55616
|
+
fg: COLORS.border,
|
|
55649
55617
|
children: " \xB7 "
|
|
55650
55618
|
}, undefined, false, undefined, this),
|
|
55651
55619
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55652
|
-
fg:
|
|
55620
|
+
fg: COLORS.textMuted,
|
|
55653
55621
|
children: "s"
|
|
55654
55622
|
}, undefined, false, undefined, this),
|
|
55655
55623
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55656
|
-
fg:
|
|
55624
|
+
fg: COLORS.textDim,
|
|
55657
55625
|
children: " sites"
|
|
55658
55626
|
}, undefined, false, undefined, this),
|
|
55659
55627
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55660
|
-
fg:
|
|
55628
|
+
fg: COLORS.border,
|
|
55661
55629
|
children: " \xB7 "
|
|
55662
55630
|
}, undefined, false, undefined, this),
|
|
55663
55631
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55664
|
-
fg:
|
|
55632
|
+
fg: COLORS.textMuted,
|
|
55665
55633
|
children: "t"
|
|
55666
55634
|
}, undefined, false, undefined, this),
|
|
55667
55635
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55668
|
-
fg:
|
|
55636
|
+
fg: COLORS.textDim,
|
|
55669
55637
|
children: " theme"
|
|
55638
|
+
}, undefined, false, undefined, this),
|
|
55639
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55640
|
+
fg: COLORS.border,
|
|
55641
|
+
children: " \xB7 "
|
|
55642
|
+
}, undefined, false, undefined, this),
|
|
55643
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55644
|
+
fg: COLORS.textMuted,
|
|
55645
|
+
children: "i"
|
|
55646
|
+
}, undefined, false, undefined, this),
|
|
55647
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55648
|
+
fg: COLORS.textDim,
|
|
55649
|
+
children: " stats"
|
|
55670
55650
|
}, undefined, false, undefined, this)
|
|
55671
55651
|
]
|
|
55672
55652
|
}, undefined, true, undefined, this),
|
|
55673
55653
|
(timerStatus === "running" || timerStatus === "paused") && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
|
|
55674
55654
|
children: [
|
|
55675
55655
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55676
|
-
fg:
|
|
55656
|
+
fg: COLORS.border,
|
|
55677
55657
|
children: " \xB7 "
|
|
55678
55658
|
}, undefined, false, undefined, this),
|
|
55679
55659
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55680
|
-
fg:
|
|
55660
|
+
fg: COLORS.textMuted,
|
|
55681
55661
|
children: "r"
|
|
55682
55662
|
}, undefined, false, undefined, this),
|
|
55683
55663
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55684
|
-
fg:
|
|
55664
|
+
fg: COLORS.textDim,
|
|
55685
55665
|
children: " stop"
|
|
55686
55666
|
}, undefined, false, undefined, this)
|
|
55687
55667
|
]
|
|
@@ -55689,11 +55669,11 @@ function StatusBar({
|
|
|
55689
55669
|
timerStatus === "running" && blockedCount > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
|
|
55690
55670
|
children: [
|
|
55691
55671
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55692
|
-
fg:
|
|
55672
|
+
fg: COLORS.border,
|
|
55693
55673
|
children: " \xB7 "
|
|
55694
55674
|
}, undefined, false, undefined, this),
|
|
55695
55675
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55696
|
-
fg:
|
|
55676
|
+
fg: COLORS.error,
|
|
55697
55677
|
children: [
|
|
55698
55678
|
"\u2715 ",
|
|
55699
55679
|
blockedCount,
|
|
@@ -55708,6 +55688,50 @@ function StatusBar({
|
|
|
55708
55688
|
}, undefined, false, undefined, this);
|
|
55709
55689
|
}
|
|
55710
55690
|
|
|
55691
|
+
// src/components/stats-screen.tsx
|
|
55692
|
+
function StatsScreen({ config }) {
|
|
55693
|
+
const timeStr = formatMinutes(config.totalMinutesFocused);
|
|
55694
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55695
|
+
flexDirection: "column",
|
|
55696
|
+
width: "100%",
|
|
55697
|
+
height: "100%",
|
|
55698
|
+
alignItems: "center",
|
|
55699
|
+
justifyContent: "center",
|
|
55700
|
+
children: [
|
|
55701
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55702
|
+
fg: COLORS.accent,
|
|
55703
|
+
children: "focus stats"
|
|
55704
|
+
}, undefined, false, undefined, this),
|
|
55705
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55706
|
+
height: 2
|
|
55707
|
+
}, undefined, false, undefined, this),
|
|
55708
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55709
|
+
fg: COLORS.text,
|
|
55710
|
+
children: " sessions " + config.totalSessions
|
|
55711
|
+
}, undefined, false, undefined, this),
|
|
55712
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55713
|
+
fg: COLORS.text,
|
|
55714
|
+
children: " total focused " + timeStr
|
|
55715
|
+
}, undefined, false, undefined, this),
|
|
55716
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55717
|
+
fg: COLORS.text,
|
|
55718
|
+
children: " day streak " + config.currentStreak
|
|
55719
|
+
}, undefined, false, undefined, this),
|
|
55720
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55721
|
+
fg: COLORS.text,
|
|
55722
|
+
children: " last session " + (config.lastSessionDate || "never")
|
|
55723
|
+
}, undefined, false, undefined, this),
|
|
55724
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55725
|
+
height: 2
|
|
55726
|
+
}, undefined, false, undefined, this),
|
|
55727
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55728
|
+
fg: COLORS.textMuted,
|
|
55729
|
+
children: "esc to go back"
|
|
55730
|
+
}, undefined, false, undefined, this)
|
|
55731
|
+
]
|
|
55732
|
+
}, undefined, true, undefined, this);
|
|
55733
|
+
}
|
|
55734
|
+
|
|
55711
55735
|
// src/components/onboarding.tsx
|
|
55712
55736
|
var import_react11 = __toESM(require_react(), 1);
|
|
55713
55737
|
|
|
@@ -55741,7 +55765,11 @@ var DEFAULTS = {
|
|
|
55741
55765
|
duration: 25,
|
|
55742
55766
|
blockedSites: DEFAULT_SITES,
|
|
55743
55767
|
theme: "city",
|
|
55744
|
-
noblock: false
|
|
55768
|
+
noblock: false,
|
|
55769
|
+
totalSessions: 0,
|
|
55770
|
+
totalMinutesFocused: 0,
|
|
55771
|
+
lastSessionDate: "",
|
|
55772
|
+
currentStreak: 0
|
|
55745
55773
|
};
|
|
55746
55774
|
function loadConfig() {
|
|
55747
55775
|
if (!existsSync3(CONFIG_PATH)) {
|
|
@@ -55796,7 +55824,7 @@ function Onboarding({
|
|
|
55796
55824
|
height: "100%",
|
|
55797
55825
|
children: [
|
|
55798
55826
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55799
|
-
fg:
|
|
55827
|
+
fg: COLORS.accent,
|
|
55800
55828
|
children: LOGO.join(`
|
|
55801
55829
|
`)
|
|
55802
55830
|
}, undefined, false, undefined, this),
|
|
@@ -55804,14 +55832,14 @@ function Onboarding({
|
|
|
55804
55832
|
height: 1
|
|
55805
55833
|
}, undefined, false, undefined, this),
|
|
55806
55834
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55807
|
-
fg:
|
|
55835
|
+
fg: COLORS.textBody,
|
|
55808
55836
|
children: TAGLINE
|
|
55809
55837
|
}, undefined, false, undefined, this),
|
|
55810
55838
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55811
55839
|
height: 2
|
|
55812
55840
|
}, undefined, false, undefined, this),
|
|
55813
55841
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55814
|
-
fg:
|
|
55842
|
+
fg: COLORS.textMuted,
|
|
55815
55843
|
children: "press space to begin setup"
|
|
55816
55844
|
}, undefined, false, undefined, this)
|
|
55817
55845
|
]
|
|
@@ -55826,21 +55854,21 @@ function Onboarding({
|
|
|
55826
55854
|
height: "100%",
|
|
55827
55855
|
children: [
|
|
55828
55856
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55829
|
-
fg:
|
|
55857
|
+
fg: COLORS.accent,
|
|
55830
55858
|
children: "sites to block during focus:"
|
|
55831
55859
|
}, undefined, false, undefined, this),
|
|
55832
55860
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55833
55861
|
height: 1
|
|
55834
55862
|
}, undefined, false, undefined, this),
|
|
55835
55863
|
DEFAULT_SITES.map((site, i) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55836
|
-
fg:
|
|
55864
|
+
fg: COLORS.warning,
|
|
55837
55865
|
children: " \u2713 " + site
|
|
55838
55866
|
}, i, false, undefined, this)),
|
|
55839
55867
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55840
55868
|
height: 2
|
|
55841
55869
|
}, undefined, false, undefined, this),
|
|
55842
55870
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55843
|
-
fg:
|
|
55871
|
+
fg: COLORS.textMuted,
|
|
55844
55872
|
children: "press space to confirm \xB7 use --block to add more"
|
|
55845
55873
|
}, undefined, false, undefined, this)
|
|
55846
55874
|
]
|
|
@@ -55854,7 +55882,7 @@ function Onboarding({
|
|
|
55854
55882
|
height: "100%",
|
|
55855
55883
|
children: [
|
|
55856
55884
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55857
|
-
fg:
|
|
55885
|
+
fg: COLORS.accent,
|
|
55858
55886
|
children: "focus duration:"
|
|
55859
55887
|
}, undefined, false, undefined, this),
|
|
55860
55888
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -55863,17 +55891,17 @@ function Onboarding({
|
|
|
55863
55891
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("ascii-font", {
|
|
55864
55892
|
text: String(duration),
|
|
55865
55893
|
font: "block",
|
|
55866
|
-
color:
|
|
55894
|
+
color: COLORS.highlight
|
|
55867
55895
|
}, undefined, false, undefined, this),
|
|
55868
55896
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55869
|
-
fg:
|
|
55897
|
+
fg: COLORS.textLabel,
|
|
55870
55898
|
children: "minutes"
|
|
55871
55899
|
}, undefined, false, undefined, this),
|
|
55872
55900
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55873
55901
|
height: 2
|
|
55874
55902
|
}, undefined, false, undefined, this),
|
|
55875
55903
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55876
|
-
fg:
|
|
55904
|
+
fg: COLORS.textMuted,
|
|
55877
55905
|
children: "\u2191/\u2193 to adjust \xB7 space to start"
|
|
55878
55906
|
}, undefined, false, undefined, this)
|
|
55879
55907
|
]
|
|
@@ -55956,14 +55984,14 @@ function SiteEditor({
|
|
|
55956
55984
|
justifyContent: "center",
|
|
55957
55985
|
children: [
|
|
55958
55986
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55959
|
-
fg:
|
|
55987
|
+
fg: COLORS.accent,
|
|
55960
55988
|
children: "edit blocked sites"
|
|
55961
55989
|
}, undefined, false, undefined, this),
|
|
55962
55990
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
55963
55991
|
height: 1
|
|
55964
55992
|
}, undefined, false, undefined, this),
|
|
55965
55993
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55966
|
-
fg:
|
|
55994
|
+
fg: COLORS.textBody,
|
|
55967
55995
|
children: "\u2191/\u2193 navigate \xB7 space toggle \xB7 enter save \xB7 esc cancel"
|
|
55968
55996
|
}, undefined, false, undefined, this),
|
|
55969
55997
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -55973,8 +56001,8 @@ function SiteEditor({
|
|
|
55973
56001
|
const isEnabled = enabled.has(site);
|
|
55974
56002
|
const isCursor = i === cursor;
|
|
55975
56003
|
const prefix = isEnabled ? "\u2713" : "\u25CB";
|
|
55976
|
-
const prefixColor = isEnabled ?
|
|
55977
|
-
const textColor = isCursor ?
|
|
56004
|
+
const prefixColor = isEnabled ? COLORS.success : COLORS.textMuted;
|
|
56005
|
+
const textColor = isCursor ? COLORS.white : COLORS.textBody;
|
|
55978
56006
|
const indicator = isCursor ? " \u25C0" : "";
|
|
55979
56007
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55980
56008
|
children: [
|
|
@@ -55987,7 +56015,7 @@ function SiteEditor({
|
|
|
55987
56015
|
children: site
|
|
55988
56016
|
}, undefined, false, undefined, this),
|
|
55989
56017
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55990
|
-
fg:
|
|
56018
|
+
fg: COLORS.accent,
|
|
55991
56019
|
children: indicator
|
|
55992
56020
|
}, undefined, false, undefined, this)
|
|
55993
56021
|
]
|
|
@@ -55996,11 +56024,11 @@ function SiteEditor({
|
|
|
55996
56024
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
55997
56025
|
children: [
|
|
55998
56026
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
55999
|
-
fg: cursor === allDisplaySites.length ?
|
|
56027
|
+
fg: cursor === allDisplaySites.length ? COLORS.white : COLORS.textBody,
|
|
56000
56028
|
children: " + add new site"
|
|
56001
56029
|
}, undefined, false, undefined, this),
|
|
56002
56030
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
56003
|
-
fg:
|
|
56031
|
+
fg: COLORS.accent,
|
|
56004
56032
|
children: cursor === allDisplaySites.length ? " \u25C0" : ""
|
|
56005
56033
|
}, undefined, false, undefined, this)
|
|
56006
56034
|
]
|
|
@@ -56013,15 +56041,15 @@ function SiteEditor({
|
|
|
56013
56041
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56014
56042
|
children: [
|
|
56015
56043
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
56016
|
-
fg:
|
|
56044
|
+
fg: COLORS.textBody,
|
|
56017
56045
|
children: " site: "
|
|
56018
56046
|
}, undefined, false, undefined, this),
|
|
56019
56047
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
56020
|
-
fg:
|
|
56048
|
+
fg: COLORS.white,
|
|
56021
56049
|
children: inputBuffer
|
|
56022
56050
|
}, undefined, false, undefined, this),
|
|
56023
56051
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
56024
|
-
fg:
|
|
56052
|
+
fg: COLORS.accent,
|
|
56025
56053
|
children: "_"
|
|
56026
56054
|
}, undefined, false, undefined, this)
|
|
56027
56055
|
]
|
|
@@ -56032,7 +56060,7 @@ function SiteEditor({
|
|
|
56032
56060
|
height: 1
|
|
56033
56061
|
}, undefined, false, undefined, this),
|
|
56034
56062
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56035
|
-
fg:
|
|
56063
|
+
fg: COLORS.textMuted,
|
|
56036
56064
|
children: `${enabled.size} site${enabled.size !== 1 ? "s" : ""} selected`
|
|
56037
56065
|
}, undefined, false, undefined, this)
|
|
56038
56066
|
]
|
|
@@ -56073,14 +56101,14 @@ function ThemePicker({
|
|
|
56073
56101
|
justifyContent: "center",
|
|
56074
56102
|
children: [
|
|
56075
56103
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56076
|
-
fg:
|
|
56104
|
+
fg: COLORS.accent,
|
|
56077
56105
|
children: "choose art theme"
|
|
56078
56106
|
}, undefined, false, undefined, this),
|
|
56079
56107
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
56080
56108
|
height: 1
|
|
56081
56109
|
}, undefined, false, undefined, this),
|
|
56082
56110
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56083
|
-
fg:
|
|
56111
|
+
fg: COLORS.textBody,
|
|
56084
56112
|
children: "\u2191/\u2193 navigate \xB7 space select \xB7 esc cancel"
|
|
56085
56113
|
}, undefined, false, undefined, this),
|
|
56086
56114
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -56090,8 +56118,8 @@ function ThemePicker({
|
|
|
56090
56118
|
const isCurrent = theme.name === currentTheme;
|
|
56091
56119
|
const isCursor = i === cursor;
|
|
56092
56120
|
const prefix = isCurrent ? "\u25C9" : "\u25CB";
|
|
56093
|
-
const prefixColor = isCurrent ?
|
|
56094
|
-
const textColor = isCursor ?
|
|
56121
|
+
const prefixColor = isCurrent ? COLORS.accent : COLORS.textMuted;
|
|
56122
|
+
const textColor = isCursor ? COLORS.white : COLORS.textBody;
|
|
56095
56123
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
56096
56124
|
flexDirection: "column",
|
|
56097
56125
|
alignItems: "center",
|
|
@@ -56107,13 +56135,13 @@ function ThemePicker({
|
|
|
56107
56135
|
children: theme.name
|
|
56108
56136
|
}, undefined, false, undefined, this),
|
|
56109
56137
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
56110
|
-
fg:
|
|
56138
|
+
fg: COLORS.accent,
|
|
56111
56139
|
children: isCursor ? " \u25C0" : ""
|
|
56112
56140
|
}, undefined, false, undefined, this)
|
|
56113
56141
|
]
|
|
56114
56142
|
}, undefined, true, undefined, this),
|
|
56115
56143
|
isCursor && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56116
|
-
fg:
|
|
56144
|
+
fg: COLORS.textDim,
|
|
56117
56145
|
children: " " + (DESCRIPTIONS[theme.name] || "")
|
|
56118
56146
|
}, undefined, false, undefined, this)
|
|
56119
56147
|
]
|
|
@@ -56644,7 +56672,26 @@ var spaceTheme = {
|
|
|
56644
56672
|
generate: generateSpace
|
|
56645
56673
|
};
|
|
56646
56674
|
|
|
56647
|
-
// src/
|
|
56675
|
+
// src/lib/session.ts
|
|
56676
|
+
function recordSession(durationMinutes) {
|
|
56677
|
+
const config = loadConfig();
|
|
56678
|
+
const today = new Date().toISOString().split("T")[0];
|
|
56679
|
+
const yesterday = new Date(Date.now() - 86400000).toISOString().split("T")[0];
|
|
56680
|
+
let streak = config.currentStreak;
|
|
56681
|
+
if (config.lastSessionDate === today) {} else if (config.lastSessionDate === yesterday) {
|
|
56682
|
+
streak += 1;
|
|
56683
|
+
} else {
|
|
56684
|
+
streak = 1;
|
|
56685
|
+
}
|
|
56686
|
+
saveConfig({
|
|
56687
|
+
totalSessions: config.totalSessions + 1,
|
|
56688
|
+
totalMinutesFocused: config.totalMinutesFocused + durationMinutes,
|
|
56689
|
+
lastSessionDate: today,
|
|
56690
|
+
currentStreak: streak
|
|
56691
|
+
});
|
|
56692
|
+
}
|
|
56693
|
+
|
|
56694
|
+
// src/lib/blocker.ts
|
|
56648
56695
|
import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
56649
56696
|
import { execSync } from "child_process";
|
|
56650
56697
|
import { homedir as homedir2 } from "os";
|
|
@@ -56656,49 +56703,64 @@ var PID_PATH = join3(homedir2(), ".tunl.pid");
|
|
|
56656
56703
|
function validateSite(site) {
|
|
56657
56704
|
return /^[a-zA-Z0-9.-]+$/.test(site);
|
|
56658
56705
|
}
|
|
56659
|
-
function
|
|
56660
|
-
|
|
56661
|
-
const
|
|
56662
|
-
|
|
56663
|
-
|
|
56664
|
-
|
|
56665
|
-
|
|
56706
|
+
function blockHosts(sites) {
|
|
56707
|
+
unblockHosts();
|
|
56708
|
+
const entries = sites.flatMap((s) => [
|
|
56709
|
+
`0.0.0.0 ${s}`,
|
|
56710
|
+
`0.0.0.0 www.${s}`,
|
|
56711
|
+
`:: ${s}`,
|
|
56712
|
+
`:: www.${s}`
|
|
56713
|
+
]).join(`
|
|
56666
56714
|
`);
|
|
56667
|
-
const
|
|
56715
|
+
const currentHosts = readFileSync2(HOSTS_PATH, "utf-8").trimEnd();
|
|
56716
|
+
const block = `${currentHosts}
|
|
56668
56717
|
${START_MARKER}
|
|
56669
56718
|
${entries}
|
|
56670
56719
|
${END_MARKER}
|
|
56671
56720
|
`;
|
|
56672
56721
|
const tmpPath = join3(homedir2(), ".tunl-block.tmp");
|
|
56673
56722
|
writeFileSync2(tmpPath, block);
|
|
56674
|
-
execSync(`sudo
|
|
56723
|
+
execSync(`sudo cp "${tmpPath}" ${HOSTS_PATH}`);
|
|
56675
56724
|
unlinkSync(tmpPath);
|
|
56676
|
-
try {
|
|
56677
|
-
execSync("sudo dscacheutil -flushcache 2>/dev/null");
|
|
56678
|
-
} catch {}
|
|
56679
|
-
try {
|
|
56680
|
-
execSync("sudo killall -HUP mDNSResponder 2>/dev/null");
|
|
56681
|
-
} catch {}
|
|
56682
56725
|
}
|
|
56683
|
-
function
|
|
56726
|
+
function unblockHosts() {
|
|
56684
56727
|
try {
|
|
56685
56728
|
const hosts = readFileSync2(HOSTS_PATH, "utf-8");
|
|
56686
56729
|
const startIdx = hosts.indexOf(START_MARKER);
|
|
56687
56730
|
const endIdx = hosts.indexOf(END_MARKER);
|
|
56688
56731
|
if (startIdx === -1 || endIdx === -1)
|
|
56689
56732
|
return;
|
|
56690
|
-
const cleaned = hosts.slice(0, startIdx) + hosts.slice(endIdx + END_MARKER.length)
|
|
56733
|
+
const cleaned = (hosts.slice(0, startIdx) + hosts.slice(endIdx + END_MARKER.length)).trimEnd() + `
|
|
56734
|
+
`;
|
|
56691
56735
|
const tmpPath = join3(homedir2(), ".tunl-hosts.tmp");
|
|
56692
56736
|
writeFileSync2(tmpPath, cleaned);
|
|
56693
56737
|
execSync(`sudo cp "${tmpPath}" ${HOSTS_PATH}`);
|
|
56694
56738
|
unlinkSync(tmpPath);
|
|
56695
|
-
|
|
56696
|
-
|
|
56697
|
-
|
|
56698
|
-
|
|
56699
|
-
|
|
56700
|
-
|
|
56739
|
+
} catch {}
|
|
56740
|
+
}
|
|
56741
|
+
function flushDNS() {
|
|
56742
|
+
try {
|
|
56743
|
+
execSync("sudo dscacheutil -flushcache 2>/dev/null");
|
|
56744
|
+
} catch {}
|
|
56745
|
+
try {
|
|
56746
|
+
execSync("sudo killall -HUP mDNSResponder 2>/dev/null");
|
|
56701
56747
|
} catch {}
|
|
56748
|
+
try {
|
|
56749
|
+
execSync("sudo killall mDNSResponderHelper 2>/dev/null");
|
|
56750
|
+
} catch {}
|
|
56751
|
+
}
|
|
56752
|
+
function blockSites(sites) {
|
|
56753
|
+
cleanupStaleBlocks();
|
|
56754
|
+
const validSites = sites.filter(validateSite);
|
|
56755
|
+
if (validSites.length === 0)
|
|
56756
|
+
return;
|
|
56757
|
+
writeFileSync2(PID_PATH, String(process.pid));
|
|
56758
|
+
blockHosts(validSites);
|
|
56759
|
+
flushDNS();
|
|
56760
|
+
}
|
|
56761
|
+
function unblockSites() {
|
|
56762
|
+
unblockHosts();
|
|
56763
|
+
flushDNS();
|
|
56702
56764
|
try {
|
|
56703
56765
|
if (existsSync5(PID_PATH))
|
|
56704
56766
|
unlinkSync(PID_PATH);
|
|
@@ -56714,8 +56776,21 @@ function cleanupStaleBlocks() {
|
|
|
56714
56776
|
unblockSites();
|
|
56715
56777
|
}
|
|
56716
56778
|
}
|
|
56779
|
+
|
|
56780
|
+
// src/lib/terminal.ts
|
|
56781
|
+
function destroyRenderer() {
|
|
56782
|
+
const renderer = globalThis.__tunl_renderer;
|
|
56783
|
+
if (renderer) {
|
|
56784
|
+
try {
|
|
56785
|
+
renderer.destroy();
|
|
56786
|
+
} catch {}
|
|
56787
|
+
globalThis.__tunl_renderer = null;
|
|
56788
|
+
}
|
|
56789
|
+
process.stdout.write("\x1B[?25h\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?1049l");
|
|
56790
|
+
}
|
|
56717
56791
|
function registerCleanup() {
|
|
56718
56792
|
const cleanup = () => {
|
|
56793
|
+
destroyRenderer();
|
|
56719
56794
|
try {
|
|
56720
56795
|
unblockSites();
|
|
56721
56796
|
} catch {}
|
|
@@ -56723,7 +56798,9 @@ function registerCleanup() {
|
|
|
56723
56798
|
};
|
|
56724
56799
|
process.on("SIGINT", cleanup);
|
|
56725
56800
|
process.on("SIGTERM", cleanup);
|
|
56801
|
+
process.on("exit", destroyRenderer);
|
|
56726
56802
|
process.on("uncaughtException", (err) => {
|
|
56803
|
+
destroyRenderer();
|
|
56727
56804
|
try {
|
|
56728
56805
|
unblockSites();
|
|
56729
56806
|
} catch {}
|
|
@@ -56732,37 +56809,12 @@ function registerCleanup() {
|
|
|
56732
56809
|
});
|
|
56733
56810
|
}
|
|
56734
56811
|
|
|
56735
|
-
// src/
|
|
56736
|
-
var
|
|
56737
|
-
function
|
|
56738
|
-
initialDuration,
|
|
56739
|
-
noblock,
|
|
56740
|
-
extraBlocks
|
|
56741
|
-
}) {
|
|
56742
|
-
const config = loadConfig();
|
|
56743
|
-
const [screen, setScreen] = import_react17.useState(config.isFirstRun ? "onboarding" : "timer");
|
|
56812
|
+
// src/hooks/use-timer.ts
|
|
56813
|
+
var import_react17 = __toESM(require_react(), 1);
|
|
56814
|
+
function useTimer(initialSeconds, onFinish) {
|
|
56744
56815
|
const [timerStatus, setTimerStatus] = import_react17.useState("idle");
|
|
56745
|
-
const [totalSeconds, setTotalSeconds] = import_react17.useState(
|
|
56746
|
-
const [remainingSeconds, setRemainingSeconds] = import_react17.useState(
|
|
56747
|
-
const [artStage, setArtStage] = import_react17.useState(0);
|
|
56748
|
-
const [blockedSites, setBlockedSites] = import_react17.useState(config.blockedSites);
|
|
56749
|
-
const [isBlocking, setIsBlocking] = import_react17.useState(false);
|
|
56750
|
-
const [celebrationColor, setCelebrationColor] = import_react17.useState("#E0F0FF");
|
|
56751
|
-
const [blockError, setBlockError] = import_react17.useState(null);
|
|
56752
|
-
const [animTick, setAnimTick] = import_react17.useState(0);
|
|
56753
|
-
const [currentTheme, setCurrentTheme] = import_react17.useState(ALL_THEMES.find((t2) => t2.name === config.theme) || cityTheme);
|
|
56754
|
-
const { width } = useTerminalDimensions();
|
|
56755
|
-
import_react17.useEffect(() => {
|
|
56756
|
-
registerCleanup();
|
|
56757
|
-
}, []);
|
|
56758
|
-
import_react17.useEffect(() => {
|
|
56759
|
-
if (screen === "onboarding" || screen === "sites" || screen === "themes")
|
|
56760
|
-
return;
|
|
56761
|
-
const interval = setInterval(() => {
|
|
56762
|
-
setAnimTick((t2) => t2 + 1);
|
|
56763
|
-
}, 800);
|
|
56764
|
-
return () => clearInterval(interval);
|
|
56765
|
-
}, [screen]);
|
|
56816
|
+
const [totalSeconds, setTotalSeconds] = import_react17.useState(initialSeconds);
|
|
56817
|
+
const [remainingSeconds, setRemainingSeconds] = import_react17.useState(initialSeconds);
|
|
56766
56818
|
import_react17.useEffect(() => {
|
|
56767
56819
|
if (timerStatus !== "running")
|
|
56768
56820
|
return;
|
|
@@ -56771,131 +56823,219 @@ function App({
|
|
|
56771
56823
|
if (prev <= 1) {
|
|
56772
56824
|
clearInterval(interval);
|
|
56773
56825
|
setTimerStatus("finished");
|
|
56774
|
-
|
|
56775
|
-
setScreen("completed");
|
|
56776
|
-
if (isBlocking) {
|
|
56777
|
-
try {
|
|
56778
|
-
unblockSites();
|
|
56779
|
-
} catch {}
|
|
56780
|
-
setIsBlocking(false);
|
|
56781
|
-
}
|
|
56826
|
+
onFinish();
|
|
56782
56827
|
return 0;
|
|
56783
56828
|
}
|
|
56784
|
-
|
|
56785
|
-
const elapsed = totalSeconds - next;
|
|
56786
|
-
const progress2 = elapsed / totalSeconds;
|
|
56787
|
-
const easedProgress = Math.sqrt(progress2);
|
|
56788
|
-
const newStage = Math.min(Math.floor(easedProgress * NUM_STAGES), NUM_STAGES - 1);
|
|
56789
|
-
setArtStage(newStage);
|
|
56790
|
-
return next;
|
|
56829
|
+
return prev - 1;
|
|
56791
56830
|
});
|
|
56792
56831
|
}, 1000);
|
|
56793
56832
|
return () => clearInterval(interval);
|
|
56794
|
-
}, [timerStatus
|
|
56795
|
-
|
|
56796
|
-
|
|
56833
|
+
}, [timerStatus]);
|
|
56834
|
+
function adjustDuration(deltaSeconds) {
|
|
56835
|
+
const newTotal = Math.max(totalSeconds + deltaSeconds, 60);
|
|
56836
|
+
setTotalSeconds(newTotal);
|
|
56837
|
+
setRemainingSeconds(newTotal);
|
|
56838
|
+
}
|
|
56839
|
+
function resetTimer() {
|
|
56840
|
+
setTimerStatus("idle");
|
|
56841
|
+
setRemainingSeconds(totalSeconds);
|
|
56842
|
+
}
|
|
56843
|
+
function setDuration(seconds) {
|
|
56844
|
+
setTotalSeconds(seconds);
|
|
56845
|
+
setRemainingSeconds(seconds);
|
|
56846
|
+
}
|
|
56847
|
+
return {
|
|
56848
|
+
timerStatus,
|
|
56849
|
+
totalSeconds,
|
|
56850
|
+
remainingSeconds,
|
|
56851
|
+
setTimerStatus,
|
|
56852
|
+
adjustDuration,
|
|
56853
|
+
resetTimer,
|
|
56854
|
+
setDuration
|
|
56855
|
+
};
|
|
56856
|
+
}
|
|
56857
|
+
|
|
56858
|
+
// src/hooks/use-animation.ts
|
|
56859
|
+
var import_react18 = __toESM(require_react(), 1);
|
|
56860
|
+
function useAnimation(timerStatus) {
|
|
56861
|
+
const [animTick, setAnimTick] = import_react18.useState(0);
|
|
56862
|
+
import_react18.useEffect(() => {
|
|
56863
|
+
if (timerStatus !== "running" && timerStatus !== "paused")
|
|
56864
|
+
return;
|
|
56865
|
+
const interval = setInterval(() => {
|
|
56866
|
+
setAnimTick((t2) => t2 + 1);
|
|
56867
|
+
}, 800);
|
|
56868
|
+
return () => clearInterval(interval);
|
|
56869
|
+
}, [timerStatus]);
|
|
56870
|
+
function resetAnimation() {
|
|
56871
|
+
setAnimTick(0);
|
|
56872
|
+
}
|
|
56873
|
+
return { animTick, resetAnimation };
|
|
56874
|
+
}
|
|
56875
|
+
|
|
56876
|
+
// src/hooks/use-celebration.ts
|
|
56877
|
+
var import_react19 = __toESM(require_react(), 1);
|
|
56878
|
+
function useCelebration(active) {
|
|
56879
|
+
const [celebrationColor, setCelebrationColor] = import_react19.useState(COLORS.highlight);
|
|
56880
|
+
import_react19.useEffect(() => {
|
|
56881
|
+
if (!active)
|
|
56797
56882
|
return;
|
|
56798
|
-
const colors = ["#C6A0F6", "#7FDBCA", "#F5A97F", "#E0F0FF", "#EED49F"];
|
|
56799
56883
|
let idx = 0;
|
|
56800
56884
|
const interval = setInterval(() => {
|
|
56801
|
-
idx = (idx + 1) %
|
|
56802
|
-
setCelebrationColor(
|
|
56885
|
+
idx = (idx + 1) % CELEBRATION_COLORS.length;
|
|
56886
|
+
setCelebrationColor(CELEBRATION_COLORS[idx]);
|
|
56803
56887
|
}, 400);
|
|
56804
56888
|
return () => clearInterval(interval);
|
|
56805
|
-
}, [
|
|
56806
|
-
|
|
56889
|
+
}, [active]);
|
|
56890
|
+
return celebrationColor;
|
|
56891
|
+
}
|
|
56892
|
+
|
|
56893
|
+
// src/hooks/use-blocker.ts
|
|
56894
|
+
var import_react20 = __toESM(require_react(), 1);
|
|
56895
|
+
function useBlocker() {
|
|
56896
|
+
const [isBlocking, setIsBlocking] = import_react20.useState(false);
|
|
56897
|
+
const [blockError, setBlockError] = import_react20.useState(null);
|
|
56898
|
+
function startBlocking(sites) {
|
|
56899
|
+
try {
|
|
56900
|
+
blockSites(sites);
|
|
56901
|
+
setIsBlocking(true);
|
|
56902
|
+
setBlockError(null);
|
|
56903
|
+
} catch (err) {
|
|
56904
|
+
setBlockError(err?.message || "Failed to block sites");
|
|
56905
|
+
}
|
|
56906
|
+
}
|
|
56907
|
+
function stopBlocking() {
|
|
56908
|
+
if (!isBlocking)
|
|
56909
|
+
return;
|
|
56910
|
+
try {
|
|
56911
|
+
unblockSites();
|
|
56912
|
+
} catch {}
|
|
56913
|
+
setIsBlocking(false);
|
|
56914
|
+
}
|
|
56915
|
+
return { isBlocking, blockError, startBlocking, stopBlocking };
|
|
56916
|
+
}
|
|
56917
|
+
|
|
56918
|
+
// src/app.tsx
|
|
56919
|
+
var ALL_THEMES = [cityTheme, forestTheme, spaceTheme];
|
|
56920
|
+
function App({ initialDuration, noblock, extraBlocks }) {
|
|
56921
|
+
const [config, setConfig] = import_react21.useState(() => loadConfig());
|
|
56922
|
+
const [screen, setScreen] = import_react21.useState(config.isFirstRun ? "onboarding" : "timer");
|
|
56923
|
+
const [blockedSites, setBlockedSites] = import_react21.useState(config.blockedSites);
|
|
56924
|
+
const [currentTheme, setCurrentTheme] = import_react21.useState(ALL_THEMES.find((t2) => t2.name === config.theme) || cityTheme);
|
|
56925
|
+
const initialSeconds = (initialDuration || config.duration) * 60;
|
|
56926
|
+
const { isBlocking, blockError, startBlocking, stopBlocking } = useBlocker();
|
|
56927
|
+
const timer = useTimer(initialSeconds, () => {
|
|
56928
|
+
setScreen("completed");
|
|
56929
|
+
recordSession(Math.round(timer.totalSeconds / 60));
|
|
56930
|
+
setConfig(loadConfig());
|
|
56931
|
+
stopBlocking();
|
|
56932
|
+
});
|
|
56933
|
+
const { animTick, resetAnimation } = useAnimation(timer.timerStatus);
|
|
56934
|
+
const celebrationColor = useCelebration(screen === "completed");
|
|
56935
|
+
const { width } = useTerminalDimensions();
|
|
56936
|
+
import_react21.useEffect(() => {
|
|
56937
|
+
registerCleanup();
|
|
56938
|
+
}, []);
|
|
56939
|
+
function startSession() {
|
|
56807
56940
|
if (!noblock && blockedSites.length > 0) {
|
|
56808
|
-
|
|
56809
|
-
|
|
56810
|
-
blockSites(sites);
|
|
56811
|
-
setIsBlocking(true);
|
|
56812
|
-
setBlockError(null);
|
|
56813
|
-
} catch (err) {
|
|
56814
|
-
setBlockError(err?.message || "Failed to block sites");
|
|
56815
|
-
}
|
|
56941
|
+
const sites = [...blockedSites, ...extraBlocks || []];
|
|
56942
|
+
startBlocking(sites);
|
|
56816
56943
|
}
|
|
56817
|
-
setTimerStatus("running");
|
|
56818
|
-
}
|
|
56944
|
+
timer.setTimerStatus("running");
|
|
56945
|
+
}
|
|
56946
|
+
function resetSession() {
|
|
56947
|
+
stopBlocking();
|
|
56948
|
+
timer.resetTimer();
|
|
56949
|
+
resetAnimation();
|
|
56950
|
+
}
|
|
56819
56951
|
useKeyboard((key) => {
|
|
56820
56952
|
if (key.ctrl && key.name === "c") {
|
|
56821
|
-
|
|
56822
|
-
try {
|
|
56823
|
-
unblockSites();
|
|
56824
|
-
} catch {}
|
|
56825
|
-
}
|
|
56953
|
+
stopBlocking();
|
|
56826
56954
|
process.exit(0);
|
|
56827
56955
|
}
|
|
56828
|
-
if (screen === "
|
|
56956
|
+
if (screen === "stats") {
|
|
56957
|
+
if (key.name === "escape" || key.name === "q" || key.name === "i") {
|
|
56958
|
+
setScreen("timer");
|
|
56959
|
+
}
|
|
56829
56960
|
return;
|
|
56961
|
+
}
|
|
56962
|
+
if (screen === "onboarding" || screen === "sites" || screen === "themes") {
|
|
56963
|
+
return;
|
|
56964
|
+
}
|
|
56830
56965
|
if (key.name === "q") {
|
|
56831
|
-
|
|
56832
|
-
try {
|
|
56833
|
-
unblockSites();
|
|
56834
|
-
} catch {}
|
|
56835
|
-
}
|
|
56966
|
+
stopBlocking();
|
|
56836
56967
|
process.exit(0);
|
|
56837
56968
|
}
|
|
56838
56969
|
if (screen === "timer") {
|
|
56839
|
-
|
|
56970
|
+
handleTimerKeys(key);
|
|
56971
|
+
}
|
|
56972
|
+
if (screen === "completed" && key.name === "space") {
|
|
56973
|
+
timer.resetTimer();
|
|
56974
|
+
resetAnimation();
|
|
56975
|
+
setScreen("timer");
|
|
56976
|
+
}
|
|
56977
|
+
});
|
|
56978
|
+
function handleTimerKeys(key) {
|
|
56979
|
+
if (timer.timerStatus === "idle") {
|
|
56980
|
+
if (key.name === "s") {
|
|
56840
56981
|
setScreen("sites");
|
|
56841
56982
|
return;
|
|
56842
56983
|
}
|
|
56843
|
-
if (key.name === "t"
|
|
56984
|
+
if (key.name === "t") {
|
|
56844
56985
|
setScreen("themes");
|
|
56845
56986
|
return;
|
|
56846
56987
|
}
|
|
56988
|
+
if (key.name === "i") {
|
|
56989
|
+
setScreen("stats");
|
|
56990
|
+
return;
|
|
56991
|
+
}
|
|
56847
56992
|
if (key.name === "space") {
|
|
56848
|
-
|
|
56849
|
-
|
|
56850
|
-
} else if (timerStatus === "running") {
|
|
56851
|
-
setTimerStatus("paused");
|
|
56852
|
-
} else if (timerStatus === "paused") {
|
|
56853
|
-
setTimerStatus("running");
|
|
56854
|
-
}
|
|
56993
|
+
startSession();
|
|
56994
|
+
return;
|
|
56855
56995
|
}
|
|
56856
|
-
if (key.name === "
|
|
56857
|
-
|
|
56858
|
-
|
|
56859
|
-
|
|
56860
|
-
|
|
56861
|
-
setIsBlocking(false);
|
|
56862
|
-
}
|
|
56863
|
-
setTimerStatus("idle");
|
|
56864
|
-
setRemainingSeconds(totalSeconds);
|
|
56865
|
-
setArtStage(0);
|
|
56996
|
+
if (key.name === "=" || key.name === "+") {
|
|
56997
|
+
const newTotal = timer.totalSeconds + 300;
|
|
56998
|
+
timer.adjustDuration(300);
|
|
56999
|
+
saveConfig({ duration: newTotal / 60 });
|
|
57000
|
+
return;
|
|
56866
57001
|
}
|
|
56867
|
-
if (
|
|
56868
|
-
|
|
56869
|
-
|
|
56870
|
-
|
|
56871
|
-
|
|
56872
|
-
}
|
|
56873
|
-
if (key.name === "-") {
|
|
56874
|
-
const newTotal = Math.max(totalSeconds - 300, 60);
|
|
56875
|
-
setTotalSeconds(newTotal);
|
|
56876
|
-
setRemainingSeconds(newTotal);
|
|
56877
|
-
}
|
|
57002
|
+
if (key.name === "-") {
|
|
57003
|
+
const newTotal = Math.max(timer.totalSeconds - 300, 60);
|
|
57004
|
+
timer.adjustDuration(-300);
|
|
57005
|
+
saveConfig({ duration: newTotal / 60 });
|
|
57006
|
+
return;
|
|
56878
57007
|
}
|
|
56879
57008
|
}
|
|
56880
|
-
if (
|
|
56881
|
-
if (
|
|
56882
|
-
|
|
56883
|
-
|
|
56884
|
-
setTimerStatus("
|
|
56885
|
-
setScreen("timer");
|
|
57009
|
+
if (key.name === "space") {
|
|
57010
|
+
if (timer.timerStatus === "running") {
|
|
57011
|
+
timer.setTimerStatus("paused");
|
|
57012
|
+
} else if (timer.timerStatus === "paused") {
|
|
57013
|
+
timer.setTimerStatus("running");
|
|
56886
57014
|
}
|
|
56887
57015
|
}
|
|
56888
|
-
|
|
56889
|
-
|
|
57016
|
+
if (key.name === "r" && (timer.timerStatus === "running" || timer.timerStatus === "paused")) {
|
|
57017
|
+
resetSession();
|
|
57018
|
+
}
|
|
57019
|
+
}
|
|
57020
|
+
function handleOnboardingComplete(result) {
|
|
56890
57021
|
setBlockedSites(result.blockedSites);
|
|
56891
|
-
|
|
56892
|
-
setRemainingSeconds(result.duration * 60);
|
|
57022
|
+
timer.setDuration(result.duration * 60);
|
|
56893
57023
|
saveConfig({
|
|
56894
57024
|
duration: result.duration,
|
|
56895
57025
|
blockedSites: result.blockedSites
|
|
56896
57026
|
});
|
|
56897
57027
|
setScreen("timer");
|
|
56898
|
-
}
|
|
57028
|
+
}
|
|
57029
|
+
function handleSitesSave(sites) {
|
|
57030
|
+
setBlockedSites(sites);
|
|
57031
|
+
saveConfig({ blockedSites: sites });
|
|
57032
|
+
setScreen("timer");
|
|
57033
|
+
}
|
|
57034
|
+
function handleThemeSelect(theme) {
|
|
57035
|
+
setCurrentTheme(theme);
|
|
57036
|
+
saveConfig({ theme: theme.name });
|
|
57037
|
+
setScreen("timer");
|
|
57038
|
+
}
|
|
56899
57039
|
if (screen === "onboarding") {
|
|
56900
57040
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Onboarding, {
|
|
56901
57041
|
onComplete: handleOnboardingComplete
|
|
@@ -56904,11 +57044,7 @@ function App({
|
|
|
56904
57044
|
if (screen === "sites") {
|
|
56905
57045
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SiteEditor, {
|
|
56906
57046
|
currentSites: blockedSites,
|
|
56907
|
-
onSave:
|
|
56908
|
-
setBlockedSites(sites);
|
|
56909
|
-
saveConfig({ blockedSites: sites });
|
|
56910
|
-
setScreen("timer");
|
|
56911
|
-
},
|
|
57047
|
+
onSave: handleSitesSave,
|
|
56912
57048
|
onCancel: () => setScreen("timer")
|
|
56913
57049
|
}, undefined, false, undefined, this);
|
|
56914
57050
|
}
|
|
@@ -56916,32 +57052,28 @@ function App({
|
|
|
56916
57052
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ThemePicker, {
|
|
56917
57053
|
themes: ALL_THEMES,
|
|
56918
57054
|
currentTheme: currentTheme.name,
|
|
56919
|
-
onSelect:
|
|
56920
|
-
setCurrentTheme(theme);
|
|
56921
|
-
saveConfig({ theme: theme.name });
|
|
56922
|
-
setScreen("timer");
|
|
56923
|
-
},
|
|
57055
|
+
onSelect: handleThemeSelect,
|
|
56924
57056
|
onCancel: () => setScreen("timer")
|
|
56925
57057
|
}, undefined, false, undefined, this);
|
|
56926
57058
|
}
|
|
56927
|
-
|
|
56928
|
-
|
|
57059
|
+
if (screen === "stats") {
|
|
57060
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(StatsScreen, {
|
|
57061
|
+
config
|
|
57062
|
+
}, undefined, false, undefined, this);
|
|
57063
|
+
}
|
|
57064
|
+
const progress = timer.totalSeconds > 0 ? 1 - timer.remainingSeconds / timer.totalSeconds : 0;
|
|
57065
|
+
const timerColor = screen === "completed" ? celebrationColor : COLORS.text;
|
|
57066
|
+
const displayStatus = screen === "completed" ? "finished" : timer.timerStatus;
|
|
56929
57067
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
56930
57068
|
flexDirection: "column",
|
|
56931
57069
|
width: "100%",
|
|
56932
57070
|
height: "100%",
|
|
56933
57071
|
children: [
|
|
56934
57072
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
56935
|
-
|
|
56936
|
-
alignItems: "center",
|
|
56937
|
-
width: "100%",
|
|
56938
|
-
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56939
|
-
fg: "#7FDBCA",
|
|
56940
|
-
children: screen === "completed" ? "\u2726 tunl \u2726" : "\u25C9 tunl"
|
|
56941
|
-
}, undefined, false, undefined, this)
|
|
57073
|
+
height: 1
|
|
56942
57074
|
}, undefined, false, undefined, this),
|
|
56943
57075
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(TimerDisplay, {
|
|
56944
|
-
remaining: remainingSeconds,
|
|
57076
|
+
remaining: timer.remainingSeconds,
|
|
56945
57077
|
color: timerColor
|
|
56946
57078
|
}, undefined, false, undefined, this),
|
|
56947
57079
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ProgressBar, {
|
|
@@ -56952,38 +57084,117 @@ function App({
|
|
|
56952
57084
|
justifyContent: "center",
|
|
56953
57085
|
alignItems: "center",
|
|
56954
57086
|
width: "100%",
|
|
56955
|
-
children:
|
|
56956
|
-
|
|
56957
|
-
|
|
56958
|
-
|
|
56959
|
-
|
|
56960
|
-
|
|
56961
|
-
|
|
56962
|
-
fg: "#9399B2",
|
|
56963
|
-
children: "press space to start focusing"
|
|
56964
|
-
}, undefined, false, undefined, this) : timerStatus === "paused" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56965
|
-
fg: "#EED49F",
|
|
56966
|
-
children: "paused \u2014 press space to resume"
|
|
56967
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
56968
|
-
fg: "#9399B2",
|
|
56969
|
-
children: "stay focused..."
|
|
57087
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(TimerStatusMessage, {
|
|
57088
|
+
screen,
|
|
57089
|
+
timerStatus: timer.timerStatus,
|
|
57090
|
+
blockError,
|
|
57091
|
+
config,
|
|
57092
|
+
totalSeconds: timer.totalSeconds,
|
|
57093
|
+
celebrationColor
|
|
56970
57094
|
}, undefined, false, undefined, this)
|
|
56971
57095
|
}, undefined, false, undefined, this),
|
|
56972
57096
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ArtCanvas, {
|
|
56973
|
-
stage:
|
|
57097
|
+
stage: 0,
|
|
56974
57098
|
theme: currentTheme,
|
|
56975
57099
|
animTick
|
|
56976
57100
|
}, undefined, false, undefined, this),
|
|
56977
57101
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(StatusBar, {
|
|
56978
57102
|
blockedCount: isBlocking ? blockedSites.length : 0,
|
|
56979
57103
|
blockedSites: isBlocking ? blockedSites : [],
|
|
56980
|
-
timerStatus:
|
|
57104
|
+
timerStatus: displayStatus
|
|
56981
57105
|
}, undefined, false, undefined, this)
|
|
56982
57106
|
]
|
|
56983
57107
|
}, undefined, true, undefined, this);
|
|
56984
57108
|
}
|
|
57109
|
+
function TimerStatusMessage({
|
|
57110
|
+
screen,
|
|
57111
|
+
timerStatus,
|
|
57112
|
+
blockError,
|
|
57113
|
+
config,
|
|
57114
|
+
totalSeconds
|
|
57115
|
+
}) {
|
|
57116
|
+
if (screen === "completed") {
|
|
57117
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
57118
|
+
children: [
|
|
57119
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
57120
|
+
fg: COLORS.accent,
|
|
57121
|
+
children: "done. "
|
|
57122
|
+
}, undefined, false, undefined, this),
|
|
57123
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
57124
|
+
fg: COLORS.textMuted,
|
|
57125
|
+
children: [
|
|
57126
|
+
config.totalSessions,
|
|
57127
|
+
" sessions \xB7",
|
|
57128
|
+
" ",
|
|
57129
|
+
config.totalMinutesFocused,
|
|
57130
|
+
" min total",
|
|
57131
|
+
config.currentStreak > 0 ? ` \xB7 ${config.currentStreak} day streak` : ""
|
|
57132
|
+
]
|
|
57133
|
+
}, undefined, true, undefined, this)
|
|
57134
|
+
]
|
|
57135
|
+
}, undefined, true, undefined, this);
|
|
57136
|
+
}
|
|
57137
|
+
if (blockError) {
|
|
57138
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
57139
|
+
fg: COLORS.error,
|
|
57140
|
+
children: blockError
|
|
57141
|
+
}, undefined, false, undefined, this);
|
|
57142
|
+
}
|
|
57143
|
+
if (timerStatus === "idle") {
|
|
57144
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
57145
|
+
children: [
|
|
57146
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
57147
|
+
fg: COLORS.border,
|
|
57148
|
+
children: "space to begin"
|
|
57149
|
+
}, undefined, false, undefined, this),
|
|
57150
|
+
config.totalSessions > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
|
|
57151
|
+
fg: COLORS.textDimmer,
|
|
57152
|
+
children: " \xB7 " + config.totalSessions + " sessions" + (config.currentStreak > 1 ? " \xB7 " + config.currentStreak + " day streak" : "")
|
|
57153
|
+
}, undefined, false, undefined, this)
|
|
57154
|
+
]
|
|
57155
|
+
}, undefined, true, undefined, this);
|
|
57156
|
+
}
|
|
57157
|
+
if (timerStatus === "paused") {
|
|
57158
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
57159
|
+
fg: COLORS.warning,
|
|
57160
|
+
children: "paused"
|
|
57161
|
+
}, undefined, false, undefined, this);
|
|
57162
|
+
}
|
|
57163
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
57164
|
+
fg: COLORS.border,
|
|
57165
|
+
children: " "
|
|
57166
|
+
}, undefined, false, undefined, this);
|
|
57167
|
+
}
|
|
56985
57168
|
|
|
56986
|
-
// src/
|
|
57169
|
+
// src/lib/browser-dns.ts
|
|
57170
|
+
import { existsSync as existsSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
57171
|
+
import { execSync as execSync2 } from "child_process";
|
|
57172
|
+
import { homedir as homedir3 } from "os";
|
|
57173
|
+
import { join as join4 } from "path";
|
|
57174
|
+
var BROWSERS = [
|
|
57175
|
+
"com.google.Chrome",
|
|
57176
|
+
"com.google.Chrome.canary",
|
|
57177
|
+
"company.thebrowser.Browser",
|
|
57178
|
+
"com.microsoft.Edge",
|
|
57179
|
+
"com.brave.Browser",
|
|
57180
|
+
"com.vivaldi.Vivaldi",
|
|
57181
|
+
"com.operasoftware.Opera"
|
|
57182
|
+
];
|
|
57183
|
+
var DNS_SETUP_FLAG = join4(homedir3(), ".tunl-dns-setup");
|
|
57184
|
+
function setupBrowserDNS() {
|
|
57185
|
+
if (existsSync6(DNS_SETUP_FLAG))
|
|
57186
|
+
return false;
|
|
57187
|
+
for (const bundle of BROWSERS) {
|
|
57188
|
+
try {
|
|
57189
|
+
execSync2(`defaults write ${bundle} BuiltInDnsClientEnabled -bool false 2>/dev/null`);
|
|
57190
|
+
execSync2(`defaults write ${bundle} DnsOverHttpsMode -string "off" 2>/dev/null`);
|
|
57191
|
+
} catch {}
|
|
57192
|
+
}
|
|
57193
|
+
writeFileSync3(DNS_SETUP_FLAG, new Date().toISOString());
|
|
57194
|
+
return true;
|
|
57195
|
+
}
|
|
57196
|
+
|
|
57197
|
+
// src/utils/args.ts
|
|
56987
57198
|
function parseArgs(argv) {
|
|
56988
57199
|
const opts = {
|
|
56989
57200
|
duration: undefined,
|
|
@@ -56991,6 +57202,7 @@ function parseArgs(argv) {
|
|
|
56991
57202
|
extraBlocks: [],
|
|
56992
57203
|
sites: undefined,
|
|
56993
57204
|
showConfig: false,
|
|
57205
|
+
showStats: false,
|
|
56994
57206
|
resetConfig: false
|
|
56995
57207
|
};
|
|
56996
57208
|
for (let i = 0;i < argv.length; i++) {
|
|
@@ -57008,10 +57220,19 @@ function parseArgs(argv) {
|
|
|
57008
57220
|
i++;
|
|
57009
57221
|
} else if (arg === "--config") {
|
|
57010
57222
|
opts.showConfig = true;
|
|
57223
|
+
} else if (arg === "--stats") {
|
|
57224
|
+
opts.showStats = true;
|
|
57011
57225
|
} else if (arg === "--reset") {
|
|
57012
57226
|
opts.resetConfig = true;
|
|
57013
57227
|
} else if (arg === "--help" || arg === "-h") {
|
|
57014
|
-
|
|
57228
|
+
printHelp();
|
|
57229
|
+
process.exit(0);
|
|
57230
|
+
}
|
|
57231
|
+
}
|
|
57232
|
+
return opts;
|
|
57233
|
+
}
|
|
57234
|
+
function printHelp() {
|
|
57235
|
+
console.log(`
|
|
57015
57236
|
tunl \u2014 Terminal Focus Timer with Art Reveal
|
|
57016
57237
|
|
|
57017
57238
|
Usage:
|
|
@@ -57020,6 +57241,7 @@ function parseArgs(argv) {
|
|
|
57020
57241
|
tunl --block "site.com" Add extra sites to block
|
|
57021
57242
|
tunl --sites "site1,site2" Set the full blocklist (saves to config)
|
|
57022
57243
|
tunl --noblock Timer + art only, no site blocking
|
|
57244
|
+
tunl --stats Show focus stats and streaks
|
|
57023
57245
|
tunl --config Show current saved config
|
|
57024
57246
|
tunl --reset Reset config (re-run onboarding)
|
|
57025
57247
|
tunl --help Show this help
|
|
@@ -57029,41 +57251,21 @@ function parseArgs(argv) {
|
|
|
57029
57251
|
q Quit
|
|
57030
57252
|
+/- Adjust time by 5 minutes (before starting)
|
|
57031
57253
|
`);
|
|
57032
|
-
process.exit(0);
|
|
57033
|
-
}
|
|
57034
|
-
}
|
|
57035
|
-
return opts;
|
|
57036
57254
|
}
|
|
57255
|
+
|
|
57256
|
+
// src/cli.tsx
|
|
57037
57257
|
async function main2() {
|
|
57038
57258
|
const args = parseArgs(process.argv.slice(2));
|
|
57259
|
+
if (args.showStats) {
|
|
57260
|
+
printStats();
|
|
57261
|
+
process.exit(0);
|
|
57262
|
+
}
|
|
57039
57263
|
if (args.showConfig) {
|
|
57040
|
-
|
|
57041
|
-
console.log(`
|
|
57042
|
-
\u25C9 tunl config (~/.tunl.json)
|
|
57043
|
-
`);
|
|
57044
|
-
console.log(` Duration: ${config.duration} minutes`);
|
|
57045
|
-
console.log(` Theme: ${config.theme}`);
|
|
57046
|
-
console.log(` No-block: ${config.noblock}`);
|
|
57047
|
-
console.log(` Sites: ${config.blockedSites.join(", ")}`);
|
|
57048
|
-
console.log(` First run: ${config.isFirstRun}
|
|
57049
|
-
`);
|
|
57264
|
+
printConfig();
|
|
57050
57265
|
process.exit(0);
|
|
57051
57266
|
}
|
|
57052
57267
|
if (args.resetConfig) {
|
|
57053
|
-
|
|
57054
|
-
const { homedir: homedir3 } = await import("os");
|
|
57055
|
-
const { join: join4 } = await import("path");
|
|
57056
|
-
const configPath = join4(homedir3(), ".tunl.json");
|
|
57057
|
-
if (existsSync4(configPath)) {
|
|
57058
|
-
unlinkSync2(configPath);
|
|
57059
|
-
console.log(`
|
|
57060
|
-
\u2713 Config reset. Next run will show onboarding.
|
|
57061
|
-
`);
|
|
57062
|
-
} else {
|
|
57063
|
-
console.log(`
|
|
57064
|
-
No config found. Already fresh.
|
|
57065
|
-
`);
|
|
57066
|
-
}
|
|
57268
|
+
await resetConfig();
|
|
57067
57269
|
process.exit(0);
|
|
57068
57270
|
}
|
|
57069
57271
|
if (args.sites) {
|
|
@@ -57072,6 +57274,18 @@ async function main2() {
|
|
|
57072
57274
|
\u2713 Blocklist updated: ${args.sites.join(", ")}
|
|
57073
57275
|
`);
|
|
57074
57276
|
}
|
|
57277
|
+
if (!args.noblock) {
|
|
57278
|
+
const needsRestart = setupBrowserDNS();
|
|
57279
|
+
if (needsRestart) {
|
|
57280
|
+
console.log(`
|
|
57281
|
+
\u25C9 tunl \u2014 first-time setup
|
|
57282
|
+
`);
|
|
57283
|
+
console.log(" Disabled browser built-in DNS so site blocking works.");
|
|
57284
|
+
console.log(" Please restart your browser (Chrome/Arc/Edge) for this to take effect.");
|
|
57285
|
+
console.log(` This only needs to happen once.
|
|
57286
|
+
`);
|
|
57287
|
+
}
|
|
57288
|
+
}
|
|
57075
57289
|
if (!args.noblock) {
|
|
57076
57290
|
console.log(`
|
|
57077
57291
|
\u25C9 tunl \u2014 Terminal Focus Timer
|
|
@@ -57080,7 +57294,7 @@ async function main2() {
|
|
|
57080
57294
|
console.log(` You'll be prompted for your password.
|
|
57081
57295
|
`);
|
|
57082
57296
|
try {
|
|
57083
|
-
|
|
57297
|
+
execSync3("sudo -v", { stdio: "inherit" });
|
|
57084
57298
|
console.log(`
|
|
57085
57299
|
\u2713 Ready! Entering focus mode...
|
|
57086
57300
|
`);
|
|
@@ -57097,6 +57311,7 @@ async function main2() {
|
|
|
57097
57311
|
useMouse: false,
|
|
57098
57312
|
enableMouseMovement: false
|
|
57099
57313
|
});
|
|
57314
|
+
globalThis.__tunl_renderer = renderer;
|
|
57100
57315
|
const root = createRoot(renderer);
|
|
57101
57316
|
root.render(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(App, {
|
|
57102
57317
|
initialDuration: args.duration,
|
|
@@ -57104,7 +57319,56 @@ async function main2() {
|
|
|
57104
57319
|
extraBlocks: args.extraBlocks
|
|
57105
57320
|
}, undefined, false, undefined, this));
|
|
57106
57321
|
}
|
|
57322
|
+
function printStats() {
|
|
57323
|
+
const config = loadConfig();
|
|
57324
|
+
const timeStr = formatMinutes(config.totalMinutesFocused);
|
|
57325
|
+
console.log(`
|
|
57326
|
+
\u25C9 tunl stats
|
|
57327
|
+
`);
|
|
57328
|
+
console.log(` Sessions: ${config.totalSessions}`);
|
|
57329
|
+
console.log(` Total focused: ${timeStr}`);
|
|
57330
|
+
console.log(` Day streak: ${config.currentStreak}`);
|
|
57331
|
+
console.log(` Last session: ${config.lastSessionDate || "never"}`);
|
|
57332
|
+
console.log(` Timer: ${config.duration} min`);
|
|
57333
|
+
console.log(` Theme: ${config.theme}
|
|
57334
|
+
`);
|
|
57335
|
+
}
|
|
57336
|
+
function printConfig() {
|
|
57337
|
+
const config = loadConfig();
|
|
57338
|
+
console.log(`
|
|
57339
|
+
\u25C9 tunl config (~/.tunl.json)
|
|
57340
|
+
`);
|
|
57341
|
+
console.log(` Duration: ${config.duration} minutes`);
|
|
57342
|
+
console.log(` Theme: ${config.theme}`);
|
|
57343
|
+
console.log(` No-block: ${config.noblock}`);
|
|
57344
|
+
console.log(` Sites: ${config.blockedSites.join(", ")}`);
|
|
57345
|
+
console.log(` First run: ${config.isFirstRun}
|
|
57346
|
+
`);
|
|
57347
|
+
}
|
|
57348
|
+
async function resetConfig() {
|
|
57349
|
+
const { unlinkSync: unlinkSync3, existsSync: existsSync4 } = await import("fs");
|
|
57350
|
+
const { homedir: homedir4 } = await import("os");
|
|
57351
|
+
const { join: join5 } = await import("path");
|
|
57352
|
+
const configPath = join5(homedir4(), ".tunl.json");
|
|
57353
|
+
if (existsSync4(configPath)) {
|
|
57354
|
+
unlinkSync3(configPath);
|
|
57355
|
+
console.log(`
|
|
57356
|
+
\u2713 Config reset. Next run will show onboarding.
|
|
57357
|
+
`);
|
|
57358
|
+
} else {
|
|
57359
|
+
console.log(`
|
|
57360
|
+
No config found. Already fresh.
|
|
57361
|
+
`);
|
|
57362
|
+
}
|
|
57363
|
+
}
|
|
57107
57364
|
main2().catch((err) => {
|
|
57365
|
+
const renderer = globalThis.__tunl_renderer;
|
|
57366
|
+
if (renderer) {
|
|
57367
|
+
try {
|
|
57368
|
+
renderer.destroy();
|
|
57369
|
+
} catch {}
|
|
57370
|
+
}
|
|
57371
|
+
process.stdout.write("\x1B[?25h\x1B[?1049l");
|
|
57108
57372
|
console.error("tunl error:", err);
|
|
57109
57373
|
process.exit(1);
|
|
57110
57374
|
});
|