@harborppm/cli 0.83.0 → 0.84.0
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/dist/app.mjs +194 -96
- package/package.json +1 -1
package/dist/app.mjs
CHANGED
|
@@ -3085,7 +3085,36 @@ var init_barnacles = __esmMin((() => {
|
|
|
3085
3085
|
plural$7 = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
3086
3086
|
}));
|
|
3087
3087
|
|
|
3088
|
+
// src/lib/local-day.ts
|
|
3089
|
+
init_indexes();
|
|
3090
|
+
init_premortem();
|
|
3091
|
+
init_barnacles();
|
|
3092
|
+
init_format();
|
|
3093
|
+
/**
|
|
3094
|
+
* The day a saved moment fell on where this person is: "2026-11-05T01:30:00Z"
|
|
3095
|
+
* is still the 4th in New York. A plain day ("2026-11-05") is itself.
|
|
3096
|
+
*/
|
|
3097
|
+
function localDay(stamp) {
|
|
3098
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(stamp)) return stamp;
|
|
3099
|
+
const d = new Date(stamp);
|
|
3100
|
+
return Number.isNaN(d.getTime()) ? stamp.slice(0, 10) : todayISO(d);
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3088
3103
|
// src/lib/task-keys.ts
|
|
3104
|
+
/**
|
|
3105
|
+
* Short keys for tasks: a project's key and a task's number within it,
|
|
3106
|
+
* "RR-42". They're what someone types in a branch name, a pull request or a
|
|
3107
|
+
* commit, so once given they don't change, except that a task moved to
|
|
3108
|
+
* another project takes that project's next number.
|
|
3109
|
+
*
|
|
3110
|
+
* The server settles keys on every write (a save, an agent's change, an
|
|
3111
|
+
* intake form, the nightly sweep) and on first load: projects without a key
|
|
3112
|
+
* get one, tasks without a number get the next ones in the order they were
|
|
3113
|
+
* made, and when two tabs gave out the same number at once the task made
|
|
3114
|
+
* first keeps it. In the browser, `write()` numbers only the tasks it adds or
|
|
3115
|
+
* moves (`numberNewTasks`), so a tab never invents numbers for old tasks that
|
|
3116
|
+
* the server would number differently.
|
|
3117
|
+
*/
|
|
3089
3118
|
/** "Ridge Runner" → "RR", "Website" → "WEB", "Q4 launch plan" → "QLP". */
|
|
3090
3119
|
function baseKey(name) {
|
|
3091
3120
|
const words = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^A-Za-z0-9 ]+/g, " ").split(/\s+/).filter((w) => /[A-Za-z]/.test(w));
|
|
@@ -3248,7 +3277,6 @@ function findByKey(data, text) {
|
|
|
3248
3277
|
}
|
|
3249
3278
|
var MAX_KEY, KEY, made, kept, KEY_TEXT;
|
|
3250
3279
|
var init_task_keys = __esmMin((() => {
|
|
3251
|
-
init_indexes();
|
|
3252
3280
|
MAX_KEY = 5;
|
|
3253
3281
|
KEY = /^[A-Z][A-Z0-9]{0,9}$/;
|
|
3254
3282
|
made = (a, b) => a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
@@ -3365,11 +3393,9 @@ var init_charter_lines = __esmMin((() => {
|
|
|
3365
3393
|
}));
|
|
3366
3394
|
|
|
3367
3395
|
// src/lib/charter.ts
|
|
3368
|
-
init_barnacles();
|
|
3369
3396
|
init_thanks();
|
|
3370
3397
|
init_date_fns();
|
|
3371
3398
|
init_date_text();
|
|
3372
|
-
init_premortem();
|
|
3373
3399
|
init_charter_lines();
|
|
3374
3400
|
const CHARTER_LABEL = {
|
|
3375
3401
|
why: "Why",
|
|
@@ -3487,7 +3513,7 @@ init_date_fns();
|
|
|
3487
3513
|
init_blockers();
|
|
3488
3514
|
init_date_text();
|
|
3489
3515
|
init_premortem();
|
|
3490
|
-
const day$8 = (iso) => parseIso(iso
|
|
3516
|
+
const day$8 = (iso) => parseIso(localDay(iso));
|
|
3491
3517
|
const between = (a, b) => differenceInCalendarDays(day$8(b), day$8(a));
|
|
3492
3518
|
/** When the project was marked Done: its last move to Done in its health history. */
|
|
3493
3519
|
function finishedOn(project, tasks) {
|
|
@@ -3509,7 +3535,7 @@ function voyageLog(data, projectId) {
|
|
|
3509
3535
|
return {
|
|
3510
3536
|
task: t,
|
|
3511
3537
|
daysLate: between(t.dueDate, t.completedAt),
|
|
3512
|
-
waitedOn: blockerIds(t).map((id) => byId.get(id)).filter((b) => Boolean(b?.completedAt && b.completedAt
|
|
3538
|
+
waitedOn: blockerIds(t).map((id) => byId.get(id)).filter((b) => Boolean(b?.completedAt && localDay(b.completedAt) > t.dueDate.slice(0, 10))).sort((a, b) => b.completedAt.localeCompare(a.completedAt))[0] ?? null
|
|
3513
3539
|
};
|
|
3514
3540
|
}).filter((s) => s.daysLate > 0).sort((a, b) => b.daysLate - a.daysLate).slice(0, 3);
|
|
3515
3541
|
const raid = data.raidItems.filter((r) => r.projectId === projectId);
|
|
@@ -5626,6 +5652,7 @@ const ATTENTION = ["at-risk", "off-track"];
|
|
|
5626
5652
|
function matches$1(row, f) {
|
|
5627
5653
|
if (f.health === "attention" && !ATTENTION.includes(row.health)) return false;
|
|
5628
5654
|
if (f.ownerId && row.ownerId !== f.ownerId) return false;
|
|
5655
|
+
if (f.hideDone && row.health === "done") return false;
|
|
5629
5656
|
return true;
|
|
5630
5657
|
}
|
|
5631
5658
|
function milestoneOf(m, today) {
|
|
@@ -5642,7 +5669,8 @@ function milestoneOf(m, today) {
|
|
|
5642
5669
|
/**
|
|
5643
5670
|
* Programs by start date, each followed by its projects by start date unless
|
|
5644
5671
|
* collapsed. A filter keeps a program when it or any of its projects match,
|
|
5645
|
-
* and keeps the projects that match.
|
|
5672
|
+
* and keeps the projects that match. So with done work hidden, a Done
|
|
5673
|
+
* program still heads any of its projects that aren't done.
|
|
5646
5674
|
*/
|
|
5647
5675
|
function roadmapRows(data, portfolioId, filter, collapsed, today) {
|
|
5648
5676
|
const programs = data.programs.filter((p) => p.portfolioId === portfolioId).sort((a, b) => a.startDate.localeCompare(b.startDate) || byName(a.name, b.name));
|
|
@@ -5666,7 +5694,7 @@ function roadmapRows(data, portfolioId, filter, collapsed, today) {
|
|
|
5666
5694
|
baselineEnd: null,
|
|
5667
5695
|
slip: null,
|
|
5668
5696
|
waitsOn: blockerIds(program).filter((id) => programIds.has(id)),
|
|
5669
|
-
milestones: data.milestones.filter((m) => m.programId === program.id).sort((a, b) => a.dueDate.localeCompare(b.dueDate)).map((m) => milestoneOf(m, today)),
|
|
5697
|
+
milestones: data.milestones.filter((m) => m.programId === program.id && !(filter.hideDone && m.done)).sort((a, b) => a.dueDate.localeCompare(b.dueDate)).map((m) => milestoneOf(m, today)),
|
|
5670
5698
|
projectCount: projects.length
|
|
5671
5699
|
});
|
|
5672
5700
|
if (collapsed.has(program.id)) continue;
|
|
@@ -5689,6 +5717,18 @@ function roadmapRows(data, portfolioId, filter, collapsed, today) {
|
|
|
5689
5717
|
}
|
|
5690
5718
|
return out;
|
|
5691
5719
|
}
|
|
5720
|
+
/** How many programs and projects on this roadmap are Done: what hiding done work tucks away. */
|
|
5721
|
+
function roadmapDoneCount(data, portfolioId) {
|
|
5722
|
+
const programIds = /* @__PURE__ */ new Set();
|
|
5723
|
+
let count = 0;
|
|
5724
|
+
for (const p of data.programs) {
|
|
5725
|
+
if (p.portfolioId !== portfolioId) continue;
|
|
5726
|
+
programIds.add(p.id);
|
|
5727
|
+
if (p.health === "done") count++;
|
|
5728
|
+
}
|
|
5729
|
+
for (const p of data.projects) if (programIds.has(p.programId) && p.health === "done") count++;
|
|
5730
|
+
return count;
|
|
5731
|
+
}
|
|
5692
5732
|
|
|
5693
5733
|
// src/lib/search.ts
|
|
5694
5734
|
function tokensOf(query) {
|
|
@@ -8952,7 +8992,7 @@ function TaskLine({ task, projects, people, today, selected, picked = false, wid
|
|
|
8952
8992
|
});
|
|
8953
8993
|
}
|
|
8954
8994
|
/** Up/down within a list of `length`, wrapping neither way. */
|
|
8955
|
-
function step(at, delta, length) {
|
|
8995
|
+
function step$1(at, delta, length) {
|
|
8956
8996
|
if (length === 0) return 0;
|
|
8957
8997
|
return Math.max(0, Math.min(length - 1, at + delta));
|
|
8958
8998
|
}
|
|
@@ -9009,8 +9049,8 @@ function CalendarView({ active, width, height, go }) {
|
|
|
9009
9049
|
useInput((input, key) => {
|
|
9010
9050
|
if (inList) {
|
|
9011
9051
|
if (key.escape) setInList(false);
|
|
9012
|
-
else if (key.upArrow || input === "k") setAt((i) => step(i, -1, agenda.length));
|
|
9013
|
-
else if (key.downArrow || input === "j") setAt((i) => step(i, 1, agenda.length));
|
|
9052
|
+
else if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, agenda.length));
|
|
9053
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, agenda.length));
|
|
9014
9054
|
else if (key.return && selected) go.openTask(selected.id);
|
|
9015
9055
|
else if (input === "x" && selected) moveTask(store, go, selected, "done");
|
|
9016
9056
|
return;
|
|
@@ -9699,12 +9739,12 @@ function HistoryView({ active, width, height, go }) {
|
|
|
9699
9739
|
});
|
|
9700
9740
|
};
|
|
9701
9741
|
useInput((input, key) => {
|
|
9702
|
-
if (key.upArrow || input === "k") setAt((i) => step(i, -1, rows.length));
|
|
9742
|
+
if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, rows.length));
|
|
9703
9743
|
else if (key.downArrow || input === "j") {
|
|
9704
9744
|
if (at >= rows.length - 2) loadMore();
|
|
9705
|
-
setAt((i) => step(i, 1, rows.length));
|
|
9706
|
-
} else if (key.pageDown) setAt((i) => step(i, 10, rows.length));
|
|
9707
|
-
else if (key.pageUp) setAt((i) => step(i, -10, rows.length));
|
|
9745
|
+
setAt((i) => step$1(i, 1, rows.length));
|
|
9746
|
+
} else if (key.pageDown) setAt((i) => step$1(i, 10, rows.length));
|
|
9747
|
+
else if (key.pageUp) setAt((i) => step$1(i, -10, rows.length));
|
|
9708
9748
|
else if (input === "s") go.pick("History of", [
|
|
9709
9749
|
...portfolios.map((p) => ({
|
|
9710
9750
|
label: p.name,
|
|
@@ -9824,6 +9864,36 @@ function HistoryView({ active, width, height, go }) {
|
|
|
9824
9864
|
});
|
|
9825
9865
|
}
|
|
9826
9866
|
|
|
9867
|
+
// src/lib/read-marks.ts
|
|
9868
|
+
/**
|
|
9869
|
+
* Inbox read marks. A harbor keeps one list of them (`readPingIds`), shared
|
|
9870
|
+
* by everyone in it, so each mark names whose it is: reading a mention that
|
|
9871
|
+
* names Sam and Aya clears it from Sam's Inbox only. Marks written before
|
|
9872
|
+
* they were personal (the bare line id) still count as read. Pure; the Inbox
|
|
9873
|
+
* makes the marks it hands the store, so none of this is in the first load.
|
|
9874
|
+
*/
|
|
9875
|
+
/** FNV-1a over the text, as eight hex digits. */
|
|
9876
|
+
function hash8(text) {
|
|
9877
|
+
let h = 2166136261;
|
|
9878
|
+
for (let i = 0; i < text.length; i++) {
|
|
9879
|
+
h ^= text.charCodeAt(i);
|
|
9880
|
+
h = Math.imul(h, 16777619);
|
|
9881
|
+
}
|
|
9882
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
9883
|
+
}
|
|
9884
|
+
/** A saved row id holds 64 characters. */
|
|
9885
|
+
const MAX_MARK = 64;
|
|
9886
|
+
/** The mark that says this person has read this Inbox line. */
|
|
9887
|
+
function readMark(personId, pingId) {
|
|
9888
|
+
const mark = `r${hash8(personId)}:${pingId}`;
|
|
9889
|
+
return mark.length <= MAX_MARK ? mark : `r${hash8(personId)}:#${hash8(pingId)}`;
|
|
9890
|
+
}
|
|
9891
|
+
/** Whether a person has read a line, given the harbor's marks. */
|
|
9892
|
+
function readBy(marks, personId) {
|
|
9893
|
+
const set = marks instanceof Set ? marks : new Set(marks ?? []);
|
|
9894
|
+
return (pingId) => set.has(pingId) || set.has(readMark(personId, pingId));
|
|
9895
|
+
}
|
|
9896
|
+
|
|
9827
9897
|
// src/lib/inbox.ts
|
|
9828
9898
|
init_date_fns();
|
|
9829
9899
|
init_indexes();
|
|
@@ -10091,22 +10161,22 @@ function InboxView({ active, width, height, go }) {
|
|
|
10091
10161
|
me,
|
|
10092
10162
|
store
|
|
10093
10163
|
]);
|
|
10094
|
-
const read =
|
|
10095
|
-
const unread = pings.filter((p) => !read
|
|
10164
|
+
const read = readBy(readIds, me);
|
|
10165
|
+
const unread = pings.filter((p) => !read(p.id)).length;
|
|
10096
10166
|
const [at, setAt] = useState(0);
|
|
10097
10167
|
const ping = pings[Math.min(at, pings.length - 1)];
|
|
10098
10168
|
const open = (p) => {
|
|
10099
|
-
store.getState().readPing(p.id);
|
|
10169
|
+
store.getState().readPing(readMark(me, p.id));
|
|
10100
10170
|
if (p.taskId) go.openTask(p.taskId);
|
|
10101
10171
|
else if (p.raidId) go.openPanel("raid", p.raidId);
|
|
10102
10172
|
else if (p.programId) go.openProgram(p.programId);
|
|
10103
10173
|
};
|
|
10104
10174
|
useInput((input, key) => {
|
|
10105
|
-
if (key.upArrow || input === "k") setAt((i) => step(i, -1, pings.length));
|
|
10106
|
-
else if (key.downArrow || input === "j") setAt((i) => step(i, 1, pings.length));
|
|
10175
|
+
if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, pings.length));
|
|
10176
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, pings.length));
|
|
10107
10177
|
else if (key.return && ping) open(ping);
|
|
10108
|
-
else if (input === " " && ping) store.getState().readPing(ping.id);
|
|
10109
|
-
else if (input === "a")
|
|
10178
|
+
else if (input === " " && ping) store.getState().readPing(readMark(me, ping.id));
|
|
10179
|
+
else if (input === "a") store.getState().catchUp(pings.map((p) => readMark(me, p.id)));
|
|
10110
10180
|
}, { isActive: active });
|
|
10111
10181
|
const view = windowOf(pings, at, Math.max(1, height - 2));
|
|
10112
10182
|
return /* @__PURE__ */ jsxs(Box, {
|
|
@@ -10125,7 +10195,7 @@ function InboxView({ active, width, height, go }) {
|
|
|
10125
10195
|
}) : null,
|
|
10126
10196
|
view.items.map((p, i) => {
|
|
10127
10197
|
const on = active && view.start + i === at;
|
|
10128
|
-
const fresh = !read
|
|
10198
|
+
const fresh = !read(p.id);
|
|
10129
10199
|
return /* @__PURE__ */ jsxs(Text, {
|
|
10130
10200
|
color: on ? theme.selected : void 0,
|
|
10131
10201
|
bold: on || fresh,
|
|
@@ -12108,10 +12178,10 @@ function RowList({ title, right, rows, empty, active, width, height, onOpen, key
|
|
|
12108
12178
|
const [at, setAt] = useState(0);
|
|
12109
12179
|
const row = rows[Math.min(at, rows.length - 1)];
|
|
12110
12180
|
useInput((input, key) => {
|
|
12111
|
-
if (key.upArrow || input === "k") setAt((i) => step(i, -1, rows.length));
|
|
12112
|
-
else if (key.downArrow || input === "j") setAt((i) => step(i, 1, rows.length));
|
|
12113
|
-
else if (key.pageDown) setAt((i) => step(i, 10, rows.length));
|
|
12114
|
-
else if (key.pageUp) setAt((i) => step(i, -10, rows.length));
|
|
12181
|
+
if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, rows.length));
|
|
12182
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, rows.length));
|
|
12183
|
+
else if (key.pageDown) setAt((i) => step$1(i, 10, rows.length));
|
|
12184
|
+
else if (key.pageUp) setAt((i) => step$1(i, -10, rows.length));
|
|
12115
12185
|
else if (key.return && row) onOpen(row);
|
|
12116
12186
|
else keys[input]?.(row);
|
|
12117
12187
|
}, { isActive: active });
|
|
@@ -12741,8 +12811,8 @@ function SearchBox({ width, onOpen, onClose }) {
|
|
|
12741
12811
|
]);
|
|
12742
12812
|
useInput((input, key) => {
|
|
12743
12813
|
if (key.escape) return onClose();
|
|
12744
|
-
if (key.upArrow) return setAt((i) => step(i, -1, hits.length));
|
|
12745
|
-
if (key.downArrow) return setAt((i) => step(i, 1, hits.length));
|
|
12814
|
+
if (key.upArrow) return setAt((i) => step$1(i, -1, hits.length));
|
|
12815
|
+
if (key.downArrow) return setAt((i) => step$1(i, 1, hits.length));
|
|
12746
12816
|
if (key.return) {
|
|
12747
12817
|
const hit = hits[Math.min(at, hits.length - 1)];
|
|
12748
12818
|
if (hit) onOpen(hit.item.id);
|
|
@@ -13000,8 +13070,8 @@ function Palette({ width, base, open, onClose }) {
|
|
|
13000
13070
|
const hits = useMemo(() => matchCommands(all, query, 12), [all, query]);
|
|
13001
13071
|
useInput((input, key) => {
|
|
13002
13072
|
if (key.escape) return onClose();
|
|
13003
|
-
if (key.upArrow) return setAt((i) => step(i, -1, hits.length));
|
|
13004
|
-
if (key.downArrow) return setAt((i) => step(i, 1, hits.length));
|
|
13073
|
+
if (key.upArrow) return setAt((i) => step$1(i, -1, hits.length));
|
|
13074
|
+
if (key.downArrow) return setAt((i) => step$1(i, 1, hits.length));
|
|
13005
13075
|
if (key.return) {
|
|
13006
13076
|
const hit = hits[Math.min(at, hits.length - 1)];
|
|
13007
13077
|
onClose();
|
|
@@ -13108,8 +13178,8 @@ function PremortemPanel({ id, width, height, go, active, onClose }) {
|
|
|
13108
13178
|
useInput((input, key) => {
|
|
13109
13179
|
if (key.escape) return onClose();
|
|
13110
13180
|
if (!project || !pm) return;
|
|
13111
|
-
if (key.upArrow || input === "k") return setAt((i) => step(i, -1, rows.length));
|
|
13112
|
-
if (key.downArrow || input === "j") return setAt((i) => step(i, 1, rows.length));
|
|
13181
|
+
if (key.upArrow || input === "k") return setAt((i) => step$1(i, -1, rows.length));
|
|
13182
|
+
if (key.downArrow || input === "j") return setAt((i) => step$1(i, 1, rows.length));
|
|
13113
13183
|
if (key.return && row?.risk) return go.openPanel("raid", row.risk.id);
|
|
13114
13184
|
if (viewOnly) return;
|
|
13115
13185
|
if (pm.phase === "none") {
|
|
@@ -23485,25 +23555,39 @@ var init_focus = __esmMin((() => {
|
|
|
23485
23555
|
// src/lib/recurrence.ts
|
|
23486
23556
|
/** The due date one interval after `from`. */
|
|
23487
23557
|
function nextDueDate(from, repeat) {
|
|
23488
|
-
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
|
|
23558
|
+
return iso(step(/* @__PURE__ */ new Date(`${from}T12:00:00`), repeat, 1));
|
|
23559
|
+
}
|
|
23560
|
+
/**
|
|
23561
|
+
* The next date in a series that started on `due`, at least one interval
|
|
23562
|
+
* on and no earlier than `today`. A weekly Monday meeting ticked off late on
|
|
23563
|
+
* Wednesday comes back the next Monday, so the series keeps its day; a
|
|
23564
|
+
* monthly one on the 31st keeps the 31st where the month has one.
|
|
23565
|
+
*/
|
|
23566
|
+
function nextSeriesDate(due, repeat, today) {
|
|
23567
|
+
const start = /* @__PURE__ */ new Date(`${due}T12:00:00`);
|
|
23568
|
+
let n = 1;
|
|
23569
|
+
let next = iso(step(start, repeat, n));
|
|
23570
|
+
while (next < today && n < 2e3) next = iso(step(start, repeat, ++n));
|
|
23571
|
+
return next;
|
|
23492
23572
|
}
|
|
23493
23573
|
/**
|
|
23494
23574
|
* The next occurrence of a repeating task once this one is done: same
|
|
23495
|
-
* title, owner, priority and checklist (unticked),
|
|
23496
|
-
* the
|
|
23497
|
-
*
|
|
23575
|
+
* title, owner, priority and checklist (unticked), back in To do. It is due
|
|
23576
|
+
* on the series' next date that isn't already past (see `nextSeriesDate`),
|
|
23577
|
+
* or one interval after today when the task had no due date. Null when the
|
|
23578
|
+
* task does not repeat, or when `siblings` (the project's tasks) already
|
|
23579
|
+
* hold an open copy of the series due on or after that date, so finishing,
|
|
23580
|
+
* reopening and finishing again doesn't queue a second one.
|
|
23498
23581
|
*/
|
|
23499
|
-
function nextOccurrence(task, id, today, createdAt) {
|
|
23582
|
+
function nextOccurrence(task, id, today, createdAt, siblings = []) {
|
|
23500
23583
|
if (!task.repeat) return null;
|
|
23501
|
-
const
|
|
23584
|
+
const dueDate = task.dueDate ? nextSeriesDate(task.dueDate, task.repeat, today) : nextDueDate(today, task.repeat);
|
|
23585
|
+
if (siblings.some((t) => t.status !== "done" && t.repeat === task.repeat && t.title === task.title && (t.dueDate ?? "") >= dueDate)) return null;
|
|
23502
23586
|
return {
|
|
23503
23587
|
...task,
|
|
23504
23588
|
id,
|
|
23505
23589
|
status: "todo",
|
|
23506
|
-
dueDate
|
|
23590
|
+
dueDate,
|
|
23507
23591
|
createdAt,
|
|
23508
23592
|
completedAt: null,
|
|
23509
23593
|
blockedById: null,
|
|
@@ -23514,7 +23598,7 @@ function nextOccurrence(task, id, today, createdAt) {
|
|
|
23514
23598
|
}))
|
|
23515
23599
|
};
|
|
23516
23600
|
}
|
|
23517
|
-
var REPEAT_LABEL, iso;
|
|
23601
|
+
var REPEAT_LABEL, iso, step;
|
|
23518
23602
|
var init_recurrence = __esmMin((() => {
|
|
23519
23603
|
init_date_fns();
|
|
23520
23604
|
REPEAT_LABEL = {
|
|
@@ -23525,6 +23609,7 @@ var init_recurrence = __esmMin((() => {
|
|
|
23525
23609
|
iso = (d) => {
|
|
23526
23610
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
23527
23611
|
};
|
|
23612
|
+
step = (d, repeat, n) => repeat === "weekly" ? addDays(d, 7 * n) : repeat === "biweekly" ? addDays(d, 14 * n) : addMonths$1(d, n);
|
|
23528
23613
|
}));
|
|
23529
23614
|
|
|
23530
23615
|
// src/lib/assignment.ts
|
|
@@ -23700,7 +23785,7 @@ function tasksSlice({ set, get, write }) {
|
|
|
23700
23785
|
return stampAssignment(row, finalTask, get().currentUserId, stamp());
|
|
23701
23786
|
});
|
|
23702
23787
|
const finished = task && patch.status === "done" && task.status !== "done" ? tasks.find((t) => t.id === id) : void 0;
|
|
23703
|
-
const follow = finished ? nextOccurrence(finished, uid("t"), todayISO(), stamp()) : null;
|
|
23788
|
+
const follow = finished ? nextOccurrence(finished, uid("t"), todayISO(), stamp(), tasksOf(tasks, finished.projectId)) : null;
|
|
23704
23789
|
write({
|
|
23705
23790
|
tasks: follow ? [...tasks, follow] : tasks,
|
|
23706
23791
|
comments: newComments.length ? [...get().comments, ...newComments] : get().comments,
|
|
@@ -23732,7 +23817,7 @@ function tasksSlice({ set, get, write }) {
|
|
|
23732
23817
|
created: false,
|
|
23733
23818
|
currentUserId: get().currentUserId
|
|
23734
23819
|
});
|
|
23735
|
-
const follow = status === "done" ? nextOccurrence(finalRow, uid("t"), todayISO(), stamp()) : null;
|
|
23820
|
+
const follow = status === "done" ? nextOccurrence(finalRow, uid("t"), todayISO(), stamp(), tasksOf(get().tasks, task.projectId)) : null;
|
|
23736
23821
|
const moved = get().tasks.map((row) => row.id === id ? finalRow : row);
|
|
23737
23822
|
write({
|
|
23738
23823
|
tasks: follow ? [...moved, follow] : moved,
|
|
@@ -23937,15 +24022,15 @@ function tasksSlice({ set, get, write }) {
|
|
|
23937
24022
|
github: withRef(t.github, ref)
|
|
23938
24023
|
} : t) });
|
|
23939
24024
|
},
|
|
23940
|
-
readPing: (
|
|
24025
|
+
readPing: (mark) => {
|
|
23941
24026
|
const ids = get().readPingIds ?? [];
|
|
23942
|
-
if (ids.includes(
|
|
23943
|
-
set({ readPingIds: [...ids,
|
|
24027
|
+
if (ids.includes(mark)) return;
|
|
24028
|
+
set({ readPingIds: [...ids, mark] });
|
|
23944
24029
|
},
|
|
23945
|
-
catchUp: (
|
|
23946
|
-
if (
|
|
24030
|
+
catchUp: (marks) => {
|
|
24031
|
+
if (marks.length === 0) return;
|
|
23947
24032
|
const have = new Set(get().readPingIds ?? []);
|
|
23948
|
-
for (const
|
|
24033
|
+
for (const mark of marks) have.add(mark);
|
|
23949
24034
|
set({ readPingIds: [...have] });
|
|
23950
24035
|
},
|
|
23951
24036
|
tickVoyage: (ids) => {
|
|
@@ -24014,7 +24099,7 @@ function tasksSlice({ set, get, write }) {
|
|
|
24014
24099
|
})));
|
|
24015
24100
|
const final = stampAssignment(row, automated, me, at);
|
|
24016
24101
|
if (final.status === "done" && row.status !== "done") {
|
|
24017
|
-
const follow = nextOccurrence(final, uid("t"), todayISO(), at);
|
|
24102
|
+
const follow = nextOccurrence(final, uid("t"), todayISO(), at, [...tasksOf(s.tasks, row.projectId), ...follows]);
|
|
24018
24103
|
if (follow) follows.push(follow);
|
|
24019
24104
|
}
|
|
24020
24105
|
return final;
|
|
@@ -24033,7 +24118,8 @@ function tasksSlice({ set, get, write }) {
|
|
|
24033
24118
|
toggleFocus: (taskId) => {
|
|
24034
24119
|
const personId = get().currentUserId;
|
|
24035
24120
|
const date = todayISO();
|
|
24036
|
-
const
|
|
24121
|
+
const live = byId(get().tasks);
|
|
24122
|
+
const ids = ((get().ropes ?? []).find((r) => r.personId === personId && r.date === date)?.taskIds ?? []).filter((id) => live.has(id));
|
|
24037
24123
|
if (ids.includes(taskId)) {
|
|
24038
24124
|
set({ ropes: upsertFocus(get().ropes, personId, date, ids.filter((id) => id !== taskId)) });
|
|
24039
24125
|
return "removed";
|
|
@@ -24133,6 +24219,7 @@ var init_tasks = __esmMin((() => {
|
|
|
24133
24219
|
init_work();
|
|
24134
24220
|
init_shared();
|
|
24135
24221
|
init_recurrence();
|
|
24222
|
+
init_indexes();
|
|
24136
24223
|
init_assignment();
|
|
24137
24224
|
init_bulk();
|
|
24138
24225
|
init_task_people();
|
|
@@ -27330,8 +27417,8 @@ function DockPanel({ id, width, height, go, active, onClose }) {
|
|
|
27330
27417
|
useInput((input, key) => {
|
|
27331
27418
|
if (key.escape) return onClose();
|
|
27332
27419
|
if (!project) return;
|
|
27333
|
-
if (key.upArrow || input === "k") return setAt((i) => step(i, -1, changes.length));
|
|
27334
|
-
if (key.downArrow || input === "j") return setAt((i) => step(i, 1, changes.length));
|
|
27420
|
+
if (key.upArrow || input === "k") return setAt((i) => step$1(i, -1, changes.length));
|
|
27421
|
+
if (key.downArrow || input === "j") return setAt((i) => step$1(i, 1, changes.length));
|
|
27335
27422
|
if (input === "a" || input === "n") return newChange();
|
|
27336
27423
|
if (input === "d" && changes.length) {
|
|
27337
27424
|
const i = Math.min(at, changes.length - 1);
|
|
@@ -27509,8 +27596,8 @@ function LifeboatsPanel({ id, width, height, go, active, onClose }) {
|
|
|
27509
27596
|
};
|
|
27510
27597
|
useInput((input, key) => {
|
|
27511
27598
|
if (key.escape) return onClose();
|
|
27512
|
-
if (key.upArrow || input === "k") return setAt((i) => step(i, -1, lines.length));
|
|
27513
|
-
if (key.downArrow || input === "j") return setAt((i) => step(i, 1, lines.length));
|
|
27599
|
+
if (key.upArrow || input === "k") return setAt((i) => step$1(i, -1, lines.length));
|
|
27600
|
+
if (key.downArrow || input === "j") return setAt((i) => step$1(i, 1, lines.length));
|
|
27514
27601
|
if (key.return && line) return choose(line);
|
|
27515
27602
|
}, { isActive: active !== false });
|
|
27516
27603
|
const rows = Math.max(1, Math.floor((height - 6 - notes.length) / 3));
|
|
@@ -27642,8 +27729,8 @@ function ShorePanel({ id, width, height, go, active, onClose }) {
|
|
|
27642
27729
|
useInput((input, key) => {
|
|
27643
27730
|
if (key.escape) return onClose();
|
|
27644
27731
|
if (!answer) return;
|
|
27645
|
-
if (key.upArrow || input === "k") return setAt((i) => step(i, -1, SHORE_KINDS.length));
|
|
27646
|
-
if (key.downArrow || input === "j") return setAt((i) => step(i, 1, SHORE_KINDS.length));
|
|
27732
|
+
if (key.upArrow || input === "k") return setAt((i) => step$1(i, -1, SHORE_KINDS.length));
|
|
27733
|
+
if (key.downArrow || input === "j") return setAt((i) => step$1(i, 1, SHORE_KINDS.length));
|
|
27647
27734
|
if (viewOnly) return;
|
|
27648
27735
|
if (input === "p") return go.ask("The channel's incoming-webhook address", (text) => {
|
|
27649
27736
|
const url = text.trim();
|
|
@@ -27802,8 +27889,8 @@ function WelcomeView({ when, active, width, height, go, onDone }) {
|
|
|
27802
27889
|
const look = w.lookFirst;
|
|
27803
27890
|
useInput((input, key) => {
|
|
27804
27891
|
if (key.escape || input === "g") return onDone();
|
|
27805
|
-
if (key.upArrow || input === "k") setAt((i) => step(i, -1, look.length));
|
|
27806
|
-
else if (key.downArrow || input === "j") setAt((i) => step(i, 1, look.length));
|
|
27892
|
+
if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, look.length));
|
|
27893
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, look.length));
|
|
27807
27894
|
else if (key.return) {
|
|
27808
27895
|
const l = look[at];
|
|
27809
27896
|
if (!l) return onDone();
|
|
@@ -28212,11 +28299,11 @@ function ProgramView({ programId, active, width, height, go, onBack }) {
|
|
|
28212
28299
|
return setShowDone((v) => !v);
|
|
28213
28300
|
}
|
|
28214
28301
|
if (side === "projects") {
|
|
28215
|
-
if (key.upArrow || input === "k") setPAt((i) => step(i, -1, mine.length));
|
|
28216
|
-
else if (key.downArrow || input === "j") setPAt((i) => step(i, 1, mine.length));
|
|
28302
|
+
if (key.upArrow || input === "k") setPAt((i) => step$1(i, -1, mine.length));
|
|
28303
|
+
else if (key.downArrow || input === "j") setPAt((i) => step$1(i, 1, mine.length));
|
|
28217
28304
|
else if (key.return && project) go.openProject(project.id);
|
|
28218
|
-
} else if (key.upArrow || input === "k") setMAt((i) => step(i, -1, marks.length));
|
|
28219
|
-
else if (key.downArrow || input === "j") setMAt((i) => step(i, 1, marks.length));
|
|
28305
|
+
} else if (key.upArrow || input === "k") setMAt((i) => step$1(i, -1, marks.length));
|
|
28306
|
+
else if (key.downArrow || input === "j") setMAt((i) => step$1(i, 1, marks.length));
|
|
28220
28307
|
else if (key.return && mark) go.openPanel("milestone", mark.id);
|
|
28221
28308
|
else if (input === " " && mark) store.getState().toggleMilestone(mark.id);
|
|
28222
28309
|
else if (input === "d" && mark) go.confirm(`Delete the milestone “${mark.name}”? u undoes it.`, () => store.getState().deleteMilestone(mark.id), "Delete");
|
|
@@ -28440,8 +28527,8 @@ function PortfolioView({ portfolioId, active, width, height, go, onBack }) {
|
|
|
28440
28527
|
if (input === "n") return go.create("program", { portfolioId });
|
|
28441
28528
|
if (input === "v") return setLayout((l) => l === "board" ? "bands" : "board");
|
|
28442
28529
|
if (input === "D" && split.done.length > 0) return setShowDone((v) => !v);
|
|
28443
|
-
if (key.upArrow || input === "k") setAt((i) => step(i, -1, list.length));
|
|
28444
|
-
else if (key.downArrow || input === "j") setAt((i) => step(i, 1, list.length));
|
|
28530
|
+
if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, list.length));
|
|
28531
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, list.length));
|
|
28445
28532
|
else if (key.return && program) go.openProgram(program.id);
|
|
28446
28533
|
}, { isActive: active });
|
|
28447
28534
|
if (!portfolio) return /* @__PURE__ */ jsx(Text, {
|
|
@@ -28830,8 +28917,8 @@ function ProjectView({ projectId, active, width, height, go, onBack }) {
|
|
|
28830
28917
|
if (input === "v") return setMode((m) => m === "board" ? "list" : m === "list" ? "budget" : m === "budget" ? "files" : "board");
|
|
28831
28918
|
if (mode === "files") {
|
|
28832
28919
|
const f = files[Math.min(fileAt, files.length - 1)];
|
|
28833
|
-
if (key.upArrow || input === "k") setFileAt((i) => step(i, -1, files.length));
|
|
28834
|
-
else if (key.downArrow || input === "j") setFileAt((i) => step(i, 1, files.length));
|
|
28920
|
+
if (key.upArrow || input === "k") setFileAt((i) => step$1(i, -1, files.length));
|
|
28921
|
+
else if (key.downArrow || input === "j") setFileAt((i) => step$1(i, 1, files.length));
|
|
28835
28922
|
else if (key.return && f) go.openTask(f.task.id);
|
|
28836
28923
|
return;
|
|
28837
28924
|
}
|
|
@@ -28875,8 +28962,8 @@ function ProjectView({ projectId, active, width, height, go, onBack }) {
|
|
|
28875
28962
|
if (mode === "budget") {
|
|
28876
28963
|
const entries = project ? budgetOf({ spendEntries }, project, /* @__PURE__ */ new Date()).entries : [];
|
|
28877
28964
|
const entry = entries[Math.min(spendAt, entries.length - 1)];
|
|
28878
|
-
if (key.upArrow || input === "k") setSpendAt((i) => step(i, -1, entries.length));
|
|
28879
|
-
else if (key.downArrow || input === "j") setSpendAt((i) => step(i, 1, entries.length));
|
|
28965
|
+
if (key.upArrow || input === "k") setSpendAt((i) => step$1(i, -1, entries.length));
|
|
28966
|
+
else if (key.downArrow || input === "j") setSpendAt((i) => step$1(i, 1, entries.length));
|
|
28880
28967
|
else if (input === "a" || input === "n") go.create("spend", { projectId });
|
|
28881
28968
|
else if (input === "d" && entry) go.confirm(`Take out ${money(entry.amount)}${entry.note ? ` for “${entry.note}”` : ""}? u undoes it.`, () => store.getState().removeSpend(entry.id), "Take it out");
|
|
28882
28969
|
return;
|
|
@@ -28892,20 +28979,20 @@ function ProjectView({ projectId, active, width, height, go, onBack }) {
|
|
|
28892
28979
|
if (input === "x" && selected) return moveTask(store, go, selected, "done");
|
|
28893
28980
|
if (input === "d" && selected) return go.confirm(`Delete ${keyOf$1({ projects }, selected)} “${selected.title}”? u undoes it.`, () => store.getState().deleteTask(selected.id), "Delete");
|
|
28894
28981
|
if (mode === "board") {
|
|
28895
|
-
if (key.leftArrow || input === "h") setCol((c) => step(c, -1, columns.length));
|
|
28896
|
-
else if (key.rightArrow || input === "l") setCol((c) => step(c, 1, columns.length));
|
|
28982
|
+
if (key.leftArrow || input === "h") setCol((c) => step$1(c, -1, columns.length));
|
|
28983
|
+
else if (key.rightArrow || input === "l") setCol((c) => step$1(c, 1, columns.length));
|
|
28897
28984
|
else if (key.upArrow || input === "k") setRowByCol((r) => ({
|
|
28898
28985
|
...r,
|
|
28899
|
-
[col]: step(row, -1, column?.tasks.length ?? 0)
|
|
28986
|
+
[col]: step$1(row, -1, column?.tasks.length ?? 0)
|
|
28900
28987
|
}));
|
|
28901
28988
|
else if (key.downArrow || input === "j") setRowByCol((r) => ({
|
|
28902
28989
|
...r,
|
|
28903
|
-
[col]: step(row, 1, column?.tasks.length ?? 0)
|
|
28990
|
+
[col]: step$1(row, 1, column?.tasks.length ?? 0)
|
|
28904
28991
|
}));
|
|
28905
|
-
} else if (key.upArrow || input === "k") setListAt((i) => step(i, -1, list.length));
|
|
28906
|
-
else if (key.downArrow || input === "j") setListAt((i) => step(i, 1, list.length));
|
|
28907
|
-
else if (key.pageDown) setListAt((i) => step(i, 10, list.length));
|
|
28908
|
-
else if (key.pageUp) setListAt((i) => step(i, -10, list.length));
|
|
28992
|
+
} else if (key.upArrow || input === "k") setListAt((i) => step$1(i, -1, list.length));
|
|
28993
|
+
else if (key.downArrow || input === "j") setListAt((i) => step$1(i, 1, list.length));
|
|
28994
|
+
else if (key.pageDown) setListAt((i) => step$1(i, 10, list.length));
|
|
28995
|
+
else if (key.pageUp) setListAt((i) => step$1(i, -10, list.length));
|
|
28909
28996
|
}, { isActive: active });
|
|
28910
28997
|
if (!project) return /* @__PURE__ */ jsx(Box, {
|
|
28911
28998
|
paddingX: 1,
|
|
@@ -29376,7 +29463,8 @@ const NAME = 26;
|
|
|
29376
29463
|
/**
|
|
29377
29464
|
* A portfolio's programs and projects across the months, as on the
|
|
29378
29465
|
* website's roadmap: bars in each one's health, ◆ for milestones. [ ] for
|
|
29379
|
-
* another portfolio, z to zoom, ←→ to page through time
|
|
29466
|
+
* another portfolio, z to zoom, ←→ to page through time, D to hide or show
|
|
29467
|
+
* Done work (shown at first, as on the website).
|
|
29380
29468
|
*/
|
|
29381
29469
|
function RoadmapView({ active, width, height, go }) {
|
|
29382
29470
|
const { today } = useDeps();
|
|
@@ -29388,6 +29476,7 @@ function RoadmapView({ active, width, height, go }) {
|
|
|
29388
29476
|
const [zoom, setZoom] = useState("half");
|
|
29389
29477
|
const [shift, setShift] = useState(0);
|
|
29390
29478
|
const [at, setAt] = useState(0);
|
|
29479
|
+
const [hideDone, setHideDone] = useState(false);
|
|
29391
29480
|
const portfolio = portfolios[Math.min(pf, portfolios.length - 1)];
|
|
29392
29481
|
const day = today();
|
|
29393
29482
|
const w = useMemo(() => roadmapWindow(zoom, day, shift), [
|
|
@@ -29401,14 +29490,20 @@ function RoadmapView({ active, width, height, go }) {
|
|
|
29401
29490
|
milestones
|
|
29402
29491
|
}, portfolio.id, {
|
|
29403
29492
|
health: "all",
|
|
29404
|
-
ownerId: null
|
|
29493
|
+
ownerId: null,
|
|
29494
|
+
hideDone
|
|
29405
29495
|
}, /* @__PURE__ */ new Set(), day) : [], [
|
|
29406
29496
|
portfolio,
|
|
29407
29497
|
programs,
|
|
29408
29498
|
projects,
|
|
29409
29499
|
milestones,
|
|
29410
|
-
day
|
|
29500
|
+
day,
|
|
29501
|
+
hideDone
|
|
29411
29502
|
]);
|
|
29503
|
+
const doneCount = portfolio ? roadmapDoneCount({
|
|
29504
|
+
programs,
|
|
29505
|
+
projects
|
|
29506
|
+
}, portfolio.id) : 0;
|
|
29412
29507
|
const row = rows[Math.min(at, rows.length - 1)];
|
|
29413
29508
|
useInput((input, key) => {
|
|
29414
29509
|
if (input === "[") setPf((i) => (i - 1 + portfolios.length) % Math.max(1, portfolios.length));
|
|
@@ -29417,8 +29512,11 @@ function RoadmapView({ active, width, height, go }) {
|
|
|
29417
29512
|
else if (key.leftArrow || input === "h") setShift((s) => s - 1);
|
|
29418
29513
|
else if (key.rightArrow || input === "l") setShift((s) => s + 1);
|
|
29419
29514
|
else if (input === "t") setShift(0);
|
|
29420
|
-
else if (
|
|
29421
|
-
|
|
29515
|
+
else if (input === "D" && (doneCount > 0 || hideDone)) {
|
|
29516
|
+
setHideDone((v) => !v);
|
|
29517
|
+
setAt(0);
|
|
29518
|
+
} else if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, rows.length));
|
|
29519
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, rows.length));
|
|
29422
29520
|
else if (key.return && row) {
|
|
29423
29521
|
if (row.kind === "project") go.openProject(row.id);
|
|
29424
29522
|
else go.openProgram(row.id);
|
|
@@ -29434,7 +29532,7 @@ function RoadmapView({ active, width, height, go }) {
|
|
|
29434
29532
|
paddingX: 1,
|
|
29435
29533
|
children: [
|
|
29436
29534
|
/* @__PURE__ */ jsxs(Heading, {
|
|
29437
|
-
right: `${label} · [ ] portfolio · z zoom · ←→ time · t today`,
|
|
29535
|
+
right: `${label}${doneCount ? ` · ${doneCount} done ${hideDone ? "hidden" : "shown"} (D)` : ""} · [ ] portfolio · z zoom · ←→ time · t today`,
|
|
29438
29536
|
children: ["Roadmap: ", portfolio?.name ?? "no portfolios yet"]
|
|
29439
29537
|
}),
|
|
29440
29538
|
/* @__PURE__ */ jsxs(Text, {
|
|
@@ -29443,7 +29541,7 @@ function RoadmapView({ active, width, height, go }) {
|
|
|
29443
29541
|
}),
|
|
29444
29542
|
rows.length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
29445
29543
|
color: theme.muted,
|
|
29446
|
-
children: "Nothing scheduled in this portfolio."
|
|
29544
|
+
children: hideDone && doneCount ? "Everything in this portfolio is done. D shows it." : "Nothing scheduled in this portfolio."
|
|
29447
29545
|
}) : null,
|
|
29448
29546
|
view.items.map((r, i) => {
|
|
29449
29547
|
const on = active && view.start + i === at;
|
|
@@ -30161,8 +30259,8 @@ function TaskPanel({ taskId, width, height, go, active = true, onClose }) {
|
|
|
30161
30259
|
useInput((input, key) => {
|
|
30162
30260
|
if (!task) return onClose();
|
|
30163
30261
|
if (key.escape) return onClose();
|
|
30164
|
-
if (key.upArrow || input === "k") return setAt((i) => step(i, -1, rows));
|
|
30165
|
-
if (key.downArrow || input === "j") return setAt((i) => step(i, 1, rows));
|
|
30262
|
+
if (key.upArrow || input === "k") return setAt((i) => step$1(i, -1, rows));
|
|
30263
|
+
if (key.downArrow || input === "j") return setAt((i) => step$1(i, 1, rows));
|
|
30166
30264
|
if (input === "c") return setEditing({ kind: "comment" });
|
|
30167
30265
|
if (input === "a") return setEditing({ kind: "step" });
|
|
30168
30266
|
if (input === "C") {
|
|
@@ -30659,8 +30757,8 @@ function WaitsEditor({ task, onDone }) {
|
|
|
30659
30757
|
const set = (ids) => store.getState().updateTask(task.id, blockersPatch(ids));
|
|
30660
30758
|
useInput((input, key) => {
|
|
30661
30759
|
if (key.escape) return onDone();
|
|
30662
|
-
if (key.upArrow) return setAt((i) => step(i, -1, list.length));
|
|
30663
|
-
if (key.downArrow) return setAt((i) => step(i, 1, list.length));
|
|
30760
|
+
if (key.upArrow) return setAt((i) => step$1(i, -1, list.length));
|
|
30761
|
+
if (key.downArrow) return setAt((i) => step$1(i, 1, list.length));
|
|
30664
30762
|
if (key.return) {
|
|
30665
30763
|
const pick = list[Math.min(at, list.length - 1)];
|
|
30666
30764
|
if (!pick) return onDone();
|
|
@@ -30769,10 +30867,10 @@ function TodayView({ active, width, height, go }) {
|
|
|
30769
30867
|
const selected = mine[Math.min(at, mine.length - 1)];
|
|
30770
30868
|
const late = mine.filter((t) => isLate(t, day)).length;
|
|
30771
30869
|
useInput((input, key) => {
|
|
30772
|
-
if (key.upArrow || input === "k") setAt((i) => step(i, -1, mine.length));
|
|
30773
|
-
else if (key.downArrow || input === "j") setAt((i) => step(i, 1, mine.length));
|
|
30774
|
-
else if (key.pageDown) setAt((i) => step(i, 10, mine.length));
|
|
30775
|
-
else if (key.pageUp) setAt((i) => step(i, -10, mine.length));
|
|
30870
|
+
if (key.upArrow || input === "k") setAt((i) => step$1(i, -1, mine.length));
|
|
30871
|
+
else if (key.downArrow || input === "j") setAt((i) => step$1(i, 1, mine.length));
|
|
30872
|
+
else if (key.pageDown) setAt((i) => step$1(i, 10, mine.length));
|
|
30873
|
+
else if (key.pageUp) setAt((i) => step$1(i, -10, mine.length));
|
|
30776
30874
|
else if (key.return && selected) go.openTask(selected.id);
|
|
30777
30875
|
else if (input === "x" && selected) moveTask(store, go, selected, "done");
|
|
30778
30876
|
else if (input === "n" && projects.length === 0) go.create("project");
|
|
@@ -30967,8 +31065,8 @@ function WeekView({ active, width, height, go }) {
|
|
|
30967
31065
|
if (input === "t") return goWeek(0);
|
|
30968
31066
|
if (input === "v") return setReview((v) => !v);
|
|
30969
31067
|
if (review) return;
|
|
30970
|
-
if (key.upArrow || input === "k") setAt(step(sel, -1, picks.length));
|
|
30971
|
-
else if (key.downArrow || input === "j") setAt(step(sel, 1, picks.length));
|
|
31068
|
+
if (key.upArrow || input === "k") setAt(step$1(sel, -1, picks.length));
|
|
31069
|
+
else if (key.downArrow || input === "j") setAt(step$1(sel, 1, picks.length));
|
|
30972
31070
|
else if (key.return && task) go.openTask(task.id);
|
|
30973
31071
|
else if (input === "p" && line?.kind === "open") go.pick(`Plan “${line.task.title}” on`, dayChoices(), (d) => {
|
|
30974
31072
|
if (store.getState().planTask(line.task.id, d)) go.notify(`On ${dayName(d)}. h changes its hours.`);
|