daily-focus 1.2.0-dev.5 → 1.2.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -11
- package/SETUP.md +9 -6
- package/dist/{audit-ITRXIX6T.js → audit-DNTFZOHH.js} +6 -5
- package/dist/{chunk-AIQO2YAI.js → chunk-6SMUSPZG.js} +2 -2
- package/dist/{chunk-26JG3D5C.js → chunk-GQ3NCWRT.js} +113 -272
- package/dist/{chunk-RNE27FQY.js → chunk-KZNY45XW.js} +0 -5
- package/dist/{chunk-UY6HQLGE.js → chunk-P3E2B2SD.js} +1 -1
- package/dist/cli.js +6 -6
- package/dist/{demo-KVFAVGO7.js → demo-6Q22AAC7.js} +33 -24
- package/dist/{init-57WHZZNG.js → init-XDMAXU2S.js} +1 -9
- package/dist/{seed-VALN2TCQ.js → seed-TWT7EDXJ.js} +3 -3
- package/dist/{server-SOCS3SZJ.js → server-PIYOIUKJ.js} +336 -251
- package/dist/{service-NAAAV67B.js → service-ZUHI3VKH.js} +1 -1
- package/package.json +1 -1
- package/prompts/README.md +2 -3
- package/prompts/morning-brief-personal.md +1 -25
- package/prompts/morning-brief-work.md +8 -33
- package/public/app.js +175 -345
- package/public/style.css +12 -170
package/public/app.js
CHANGED
|
@@ -1013,9 +1013,6 @@ function subscribe(onState, onError) {
|
|
|
1013
1013
|
function postTimeZone(timeZone) {
|
|
1014
1014
|
return post("/api/timezone", { timeZone });
|
|
1015
1015
|
}
|
|
1016
|
-
function postSession(body) {
|
|
1017
|
-
return post("/api/session", body);
|
|
1018
|
-
}
|
|
1019
1016
|
function postBoardRefresh() {
|
|
1020
1017
|
return post("/api/board/refresh");
|
|
1021
1018
|
}
|
|
@@ -1080,9 +1077,7 @@ var VIEWS = ["today", "board", "tickets", "settings"];
|
|
|
1080
1077
|
var RUN_PREFIX = "run:";
|
|
1081
1078
|
var PANEL_PREFIX = "panel:";
|
|
1082
1079
|
var VIEW_KEY = "daily-focus:view";
|
|
1083
|
-
var UNATTENDED_SEEN_KEY = "daily-focus:unattended-seen";
|
|
1084
1080
|
var AGENT_RUN_SEEN_KEY = "daily-focus:agent-run-seen";
|
|
1085
|
-
var FOCUS_MODE_KEY = "daily-focus:focus-mode";
|
|
1086
1081
|
var DETAIL_KEY = "daily-focus:detail-for";
|
|
1087
1082
|
var state = y2(null);
|
|
1088
1083
|
function createUi(initial = {}) {
|
|
@@ -1098,15 +1093,12 @@ function createUi(initial = {}) {
|
|
|
1098
1093
|
focusField: y2(initial.focusField ?? null),
|
|
1099
1094
|
openDrawers: y2(initial.openDrawers ?? /* @__PURE__ */ new Set()),
|
|
1100
1095
|
ticketMode: y2(initial.ticketMode ?? "sync"),
|
|
1101
|
-
unattendedSeen: y2(initial.unattendedSeen ?? null),
|
|
1102
1096
|
agentReportSeen: y2(initial.agentReportSeen ?? null),
|
|
1103
1097
|
messageOpen: y2(initial.messageOpen ?? /* @__PURE__ */ new Map()),
|
|
1104
1098
|
view: y2(initial.view ?? "today"),
|
|
1105
|
-
focusMode: y2(initial.focusMode ?? false),
|
|
1106
1099
|
theme: y2(initial.theme ?? "system"),
|
|
1107
1100
|
toast: y2(initial.toast ?? null),
|
|
1108
1101
|
connectionError: y2(initial.connectionError ?? null),
|
|
1109
|
-
clock: y2(initial.clock ?? Date.now()),
|
|
1110
1102
|
settingsReturn: y2(initial.settingsReturn ?? "today"),
|
|
1111
1103
|
settings: y2(initial.settings ?? null),
|
|
1112
1104
|
settingsDraft: y2(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
|
|
@@ -1125,9 +1117,7 @@ function remembered() {
|
|
|
1125
1117
|
return {
|
|
1126
1118
|
// Settings is a page to visit, so a reload returns to the view underneath.
|
|
1127
1119
|
view: isView(view) && view !== "settings" ? view : "today",
|
|
1128
|
-
unattendedSeen: localStorage.getItem(UNATTENDED_SEEN_KEY),
|
|
1129
1120
|
agentReportSeen: localStorage.getItem(AGENT_RUN_SEEN_KEY),
|
|
1130
|
-
focusMode: localStorage.getItem(FOCUS_MODE_KEY) === "1",
|
|
1131
1121
|
detailFor: sessionStorage.getItem(DETAIL_KEY)
|
|
1132
1122
|
};
|
|
1133
1123
|
} catch {
|
|
@@ -1295,208 +1285,6 @@ function ThemeToggle({ ui: ui2, handlers: handlers2 }) {
|
|
|
1295
1285
|
);
|
|
1296
1286
|
}
|
|
1297
1287
|
|
|
1298
|
-
// client/format.ts
|
|
1299
|
-
var DAY_FORMAT = {
|
|
1300
|
-
weekday: "long",
|
|
1301
|
-
day: "numeric",
|
|
1302
|
-
month: "long"
|
|
1303
|
-
};
|
|
1304
|
-
function formatters(timeZone) {
|
|
1305
|
-
return {
|
|
1306
|
-
time: new Intl.DateTimeFormat(void 0, { timeZone, hour: "2-digit", minute: "2-digit" }),
|
|
1307
|
-
day: new Intl.DateTimeFormat(void 0, { timeZone, ...DAY_FORMAT }),
|
|
1308
|
-
shortDay: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short" }),
|
|
1309
|
-
shortDayYear: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short", year: "numeric" }),
|
|
1310
|
-
weekday: new Intl.DateTimeFormat(void 0, { timeZone, weekday: "long" })
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
|
-
var clockFormats = formatters();
|
|
1314
|
-
var dateFormats = formatters("UTC");
|
|
1315
|
-
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
1316
|
-
function formatted(value, kind) {
|
|
1317
|
-
const bare = typeof value === "string" && DATE_ONLY.test(value);
|
|
1318
|
-
const d4 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
|
|
1319
|
-
return d4 && !Number.isNaN(d4.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d4) : "";
|
|
1320
|
-
}
|
|
1321
|
-
function parseDate(value) {
|
|
1322
|
-
if (typeof value !== "string" || value === "") return null;
|
|
1323
|
-
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
1324
|
-
if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
|
|
1325
|
-
const d4 = new Date(value);
|
|
1326
|
-
return Number.isNaN(d4.getTime()) ? null : d4;
|
|
1327
|
-
}
|
|
1328
|
-
function formatTime(value) {
|
|
1329
|
-
return formatted(value, "time");
|
|
1330
|
-
}
|
|
1331
|
-
function formatDay(value) {
|
|
1332
|
-
return formatted(value, "day");
|
|
1333
|
-
}
|
|
1334
|
-
function formatShortDay(value) {
|
|
1335
|
-
return formatted(value, "shortDay");
|
|
1336
|
-
}
|
|
1337
|
-
function formatWeekday(value) {
|
|
1338
|
-
return formatted(value, "weekday");
|
|
1339
|
-
}
|
|
1340
|
-
var dayKeyFmt = null;
|
|
1341
|
-
var calendarZone;
|
|
1342
|
-
function setCalendarZone(timeZone) {
|
|
1343
|
-
if (timeZone === calendarZone) return;
|
|
1344
|
-
calendarZone = timeZone;
|
|
1345
|
-
try {
|
|
1346
|
-
clockFormats = formatters(timeZone);
|
|
1347
|
-
dayKeyFmt = timeZone ? new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }) : null;
|
|
1348
|
-
} catch {
|
|
1349
|
-
dayKeyFmt = null;
|
|
1350
|
-
clockFormats = formatters();
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
function dayKey(d4) {
|
|
1354
|
-
if (dayKeyFmt) {
|
|
1355
|
-
const parts = dayKeyFmt.formatToParts(d4);
|
|
1356
|
-
const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
|
|
1357
|
-
return `${part("year")}-${part("month")}-${part("day")}`;
|
|
1358
|
-
}
|
|
1359
|
-
const m4 = String(d4.getMonth() + 1).padStart(2, "0");
|
|
1360
|
-
const day = String(d4.getDate()).padStart(2, "0");
|
|
1361
|
-
return `${d4.getFullYear()}-${m4}-${day}`;
|
|
1362
|
-
}
|
|
1363
|
-
function splitKey(key) {
|
|
1364
|
-
const [y4, m4, d4] = key.split("-").map(Number);
|
|
1365
|
-
return [y4, m4 - 1, d4];
|
|
1366
|
-
}
|
|
1367
|
-
function keyAt(ms) {
|
|
1368
|
-
return new Date(ms).toISOString().slice(0, 10);
|
|
1369
|
-
}
|
|
1370
|
-
function addDays(key, days) {
|
|
1371
|
-
const [y4, m4, d4] = splitKey(key);
|
|
1372
|
-
return keyAt(Date.UTC(y4, m4, d4 + days));
|
|
1373
|
-
}
|
|
1374
|
-
function addMonths(key, months) {
|
|
1375
|
-
const [y4, m4, d4] = splitKey(key);
|
|
1376
|
-
const lastDay = new Date(Date.UTC(y4, m4 + months + 1, 0)).getUTCDate();
|
|
1377
|
-
return keyAt(Date.UTC(y4, m4 + months, Math.min(d4, lastDay)));
|
|
1378
|
-
}
|
|
1379
|
-
function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
|
|
1380
|
-
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1381
|
-
const [y4, m4, d4] = splitKey(value);
|
|
1382
|
-
const [ty, tm, td] = splitKey(dayKey(now));
|
|
1383
|
-
return Math.round((Date.UTC(y4, m4, d4) - Date.UTC(ty, tm, td)) / 864e5);
|
|
1384
|
-
}
|
|
1385
|
-
const target = parseDate(value);
|
|
1386
|
-
if (!target) return null;
|
|
1387
|
-
const a4 = Date.UTC(...splitKey(dayKey(now)));
|
|
1388
|
-
const b4 = Date.UTC(...splitKey(dayKey(target)));
|
|
1389
|
-
return Math.round((b4 - a4) / 864e5);
|
|
1390
|
-
}
|
|
1391
|
-
function relativeDay(value, now = /* @__PURE__ */ new Date()) {
|
|
1392
|
-
const diff = daysFromToday(value, now);
|
|
1393
|
-
if (diff === null) return "";
|
|
1394
|
-
if (diff === 0) return "today";
|
|
1395
|
-
if (diff === 1) return "tomorrow";
|
|
1396
|
-
if (diff === -1) return "yesterday";
|
|
1397
|
-
if (diff > 1 && diff <= 7) return `in ${diff} days`;
|
|
1398
|
-
if (diff < -1 && diff >= -7) return `${Math.abs(diff)} days ago`;
|
|
1399
|
-
return formatShortDay(value);
|
|
1400
|
-
}
|
|
1401
|
-
function formatDuration(minutes) {
|
|
1402
|
-
const h5 = Math.floor(minutes / 60);
|
|
1403
|
-
const m4 = minutes % 60;
|
|
1404
|
-
if (h5 === 0) return `${m4} min`;
|
|
1405
|
-
if (m4 === 0) return `${h5} h`;
|
|
1406
|
-
return `${h5} h ${m4} min`;
|
|
1407
|
-
}
|
|
1408
|
-
function relativeTime(value, now = /* @__PURE__ */ new Date()) {
|
|
1409
|
-
const d4 = parseDate(value);
|
|
1410
|
-
if (!d4) return "";
|
|
1411
|
-
const seconds = Math.round((now.getTime() - d4.getTime()) / 1e3);
|
|
1412
|
-
if (seconds < 60) return "just now";
|
|
1413
|
-
const minutes = Math.round(seconds / 60);
|
|
1414
|
-
if (minutes < 60) return `${minutes} min ago`;
|
|
1415
|
-
const hours = Math.round(minutes / 60);
|
|
1416
|
-
if (hours < 48) return `${hours} h ago`;
|
|
1417
|
-
const days = Math.round(hours / 24);
|
|
1418
|
-
if (days <= 30) return `${days} days ago`;
|
|
1419
|
-
return dayKey(d4).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
|
|
1420
|
-
}
|
|
1421
|
-
function firstLine(text) {
|
|
1422
|
-
const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
|
|
1423
|
-
return line.replace(/^[#>*\-\s]+/, "").replace(/[*_`]/g, "").trim();
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
// client/timer.ts
|
|
1427
|
-
function clock(seconds) {
|
|
1428
|
-
const total = Math.abs(seconds);
|
|
1429
|
-
const mm = Math.floor(total / 60);
|
|
1430
|
-
const ss = Math.floor(total % 60);
|
|
1431
|
-
return `${seconds < 0 ? "+" : ""}${mm}:${String(ss).padStart(2, "0")}`;
|
|
1432
|
-
}
|
|
1433
|
-
function remainingSeconds(active, nowMs) {
|
|
1434
|
-
return Math.round((new Date(active.endsAt).getTime() - nowMs) / 1e3);
|
|
1435
|
-
}
|
|
1436
|
-
function unattendedNotice(closed, handlers2) {
|
|
1437
|
-
const stoppedAt = formatTime(closed.endedAt);
|
|
1438
|
-
const headline = closed.reason === "away" ? `Timer stopped at ${stoppedAt} \u2014 nothing had touched the machine since.` : `Timer stopped at ${stoppedAt} \u2014 it had been left running.`;
|
|
1439
|
-
return el(
|
|
1440
|
-
"div",
|
|
1441
|
-
{ class: "timer__away", role: "status" },
|
|
1442
|
-
el(
|
|
1443
|
-
"span",
|
|
1444
|
-
{ class: "timer__away-body" },
|
|
1445
|
-
el("span", { class: "timer__away-text" }, headline),
|
|
1446
|
-
el(
|
|
1447
|
-
"span",
|
|
1448
|
-
{ class: "timer__away-detail" },
|
|
1449
|
-
`${formatDuration(closed.actualMinutes)} logged on \u201C${closed.title}\u201D. `,
|
|
1450
|
-
closed.reason === "away" ? "The time you were away is not in the record." : "Treat that as an upper bound, not a measurement."
|
|
1451
|
-
)
|
|
1452
|
-
),
|
|
1453
|
-
el("button", { type: "button", class: "button", onClick: () => handlers2.startSession(closed.id) }, "Pick it back up"),
|
|
1454
|
-
el(
|
|
1455
|
-
"button",
|
|
1456
|
-
{
|
|
1457
|
-
type: "button",
|
|
1458
|
-
class: "icon-button",
|
|
1459
|
-
"aria-label": "Dismiss",
|
|
1460
|
-
onClick: () => handlers2.dismissUnattended(closed.endedAt)
|
|
1461
|
-
},
|
|
1462
|
-
"\u2715"
|
|
1463
|
-
)
|
|
1464
|
-
);
|
|
1465
|
-
}
|
|
1466
|
-
function Timer({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
1467
|
-
const { active, completedToday, minutesToday, unattendedClose } = state2.session;
|
|
1468
|
-
const closed = unattendedClose && ui2.unattendedSeen.value !== unattendedClose.endedAt ? unattendedClose : null;
|
|
1469
|
-
if (!active && completedToday === 0 && !closed) {
|
|
1470
|
-
return el("div", { class: "timer", id: "timer", hidden: true });
|
|
1471
|
-
}
|
|
1472
|
-
const tally = completedToday > 0 ? `${completedToday} session${completedToday === 1 ? "" : "s"} today \xB7 ${formatDuration(minutesToday)}` : null;
|
|
1473
|
-
if (!active) {
|
|
1474
|
-
return el(
|
|
1475
|
-
"div",
|
|
1476
|
-
{ class: "timer", id: "timer", hidden: false, "data-state": closed ? "stopped" : "idle" },
|
|
1477
|
-
closed ? unattendedNotice(closed, handlers2) : null,
|
|
1478
|
-
tally ? el("span", { class: "timer__tally" }, tally) : null
|
|
1479
|
-
);
|
|
1480
|
-
}
|
|
1481
|
-
const remaining = remainingSeconds(active, ui2.clock.value);
|
|
1482
|
-
const overrun = remaining < 0;
|
|
1483
|
-
return el(
|
|
1484
|
-
"div",
|
|
1485
|
-
{ class: "timer", id: "timer", hidden: false, "data-state": overrun ? "overrun" : "running" },
|
|
1486
|
-
el("span", { class: "timer__clock" }, clock(remaining)),
|
|
1487
|
-
el(
|
|
1488
|
-
"span",
|
|
1489
|
-
{ class: "timer__title" },
|
|
1490
|
-
active.title,
|
|
1491
|
-
overrun ? el("span", { class: "timer__note" }, ` \xB7 ${active.minutes} min in, still counting`) : null,
|
|
1492
|
-
// The one thing a kitchen timer can't tell you.
|
|
1493
|
-
active.collidesWithNextEvent && !overrun ? el("span", { class: "timer__note timer__note--warn" }, ` \xB7 runs into ${formatTime(active.nextEventAt)}`) : null
|
|
1494
|
-
),
|
|
1495
|
-
tally ? el("span", { class: "timer__tally" }, tally) : null,
|
|
1496
|
-
el("button", { type: "button", class: "button", onClick: () => handlers2.stopSession() }, "Stop")
|
|
1497
|
-
);
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
1288
|
// client/actions.ts
|
|
1501
1289
|
function afterRender(fn) {
|
|
1502
1290
|
if (typeof requestAnimationFrame === "function") requestAnimationFrame(fn);
|
|
@@ -1550,7 +1338,6 @@ function adoptState(next) {
|
|
|
1550
1338
|
state.value = next;
|
|
1551
1339
|
ui.connectionError.value = null;
|
|
1552
1340
|
});
|
|
1553
|
-
startLocalTick();
|
|
1554
1341
|
checkStore(next.storeId);
|
|
1555
1342
|
checkAssetVersion(next.assetVersion);
|
|
1556
1343
|
}
|
|
@@ -1593,7 +1380,8 @@ var PAST_TENSE = {
|
|
|
1593
1380
|
dismiss: "Dismissed",
|
|
1594
1381
|
snooze: "Snoozed",
|
|
1595
1382
|
reopen: "Restored",
|
|
1596
|
-
note: "Note saved"
|
|
1383
|
+
note: "Note saved",
|
|
1384
|
+
nudge: "Nudged, back tomorrow"
|
|
1597
1385
|
};
|
|
1598
1386
|
var lastAction = null;
|
|
1599
1387
|
var parkedFrom = /* @__PURE__ */ new Map();
|
|
@@ -1605,6 +1393,9 @@ function unpark(id) {
|
|
|
1605
1393
|
function undoLast() {
|
|
1606
1394
|
if (lastAction && lastAction.action !== "reopen") void unpark(lastAction.id);
|
|
1607
1395
|
}
|
|
1396
|
+
function isRemark(action) {
|
|
1397
|
+
return action === "note" || action === "nudge";
|
|
1398
|
+
}
|
|
1608
1399
|
function optimistic(current, id, action, extra) {
|
|
1609
1400
|
const status = action === "reopen" ? "open" : action === "dismiss" ? "dismissed" : action === "done" ? "done" : "snoozed";
|
|
1610
1401
|
const statusAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1631,12 +1422,12 @@ async function applyAction(id, action, extra = {}) {
|
|
|
1631
1422
|
if (row && action === "snooze" && item && (item.status === "done" || item.status === "dismissed")) {
|
|
1632
1423
|
parkedFrom.set(id, item.status === "done" ? "done" : "dismiss");
|
|
1633
1424
|
}
|
|
1634
|
-
if (action
|
|
1425
|
+
if (!isRemark(action)) state.value = optimistic(current, id, action, extra);
|
|
1635
1426
|
});
|
|
1636
1427
|
try {
|
|
1637
1428
|
state.value = await postAction({ id, action, ...extra });
|
|
1638
1429
|
removePending(id);
|
|
1639
|
-
if (action
|
|
1430
|
+
if (isRemark(action)) {
|
|
1640
1431
|
showToast(PAST_TENSE[action]);
|
|
1641
1432
|
return;
|
|
1642
1433
|
}
|
|
@@ -1738,53 +1529,6 @@ async function askAgent(run, text) {
|
|
|
1738
1529
|
showToast(`Could not ask the morning agent: ${err.message}`);
|
|
1739
1530
|
}
|
|
1740
1531
|
}
|
|
1741
|
-
async function changeSession(body) {
|
|
1742
|
-
if (body.action === "start" && "Notification" in window && Notification.permission === "default") {
|
|
1743
|
-
void Notification.requestPermission();
|
|
1744
|
-
}
|
|
1745
|
-
try {
|
|
1746
|
-
state.value = await postSession(body);
|
|
1747
|
-
startLocalTick();
|
|
1748
|
-
} catch (err) {
|
|
1749
|
-
showToast(`Could not change the timer: ${err.message}`);
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
var tickTimer = null;
|
|
1753
|
-
var announcedOverrunFor = null;
|
|
1754
|
-
function startLocalTick() {
|
|
1755
|
-
if (tickTimer) clearInterval(tickTimer);
|
|
1756
|
-
tickTimer = setInterval(tick, 1e3);
|
|
1757
|
-
tick();
|
|
1758
|
-
}
|
|
1759
|
-
function tick() {
|
|
1760
|
-
const active = state.value?.session.active;
|
|
1761
|
-
if (!active) {
|
|
1762
|
-
document.title = "Daily Focus";
|
|
1763
|
-
if (tickTimer) clearInterval(tickTimer);
|
|
1764
|
-
tickTimer = null;
|
|
1765
|
-
return;
|
|
1766
|
-
}
|
|
1767
|
-
const nowMs = Date.now();
|
|
1768
|
-
ui.clock.value = nowMs;
|
|
1769
|
-
const remaining = remainingSeconds(active, nowMs);
|
|
1770
|
-
const mm = Math.floor(Math.abs(remaining) / 60);
|
|
1771
|
-
const ss = String(Math.floor(Math.abs(remaining) % 60)).padStart(2, "0");
|
|
1772
|
-
document.title = `${remaining < 0 ? "+" : ""}${mm}:${ss} \xB7 ${active.title}`;
|
|
1773
|
-
if (remaining <= 0 && announcedOverrunFor !== active.startedAt) {
|
|
1774
|
-
announcedOverrunFor = active.startedAt;
|
|
1775
|
-
announceSessionEnd(active);
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
function announceSessionEnd(active) {
|
|
1779
|
-
const label = active.minutes === 1 ? "1 minute" : `${active.minutes} minutes`;
|
|
1780
|
-
showToast(`${label} in \u2014 still on "${active.title}"?`, null);
|
|
1781
|
-
if ("Notification" in window && Notification.permission === "granted") {
|
|
1782
|
-
new Notification("Still on this?", {
|
|
1783
|
-
body: `${label} in on ${active.title}. Keep going, or stop and log it.`,
|
|
1784
|
-
tag: "daily-focus-session"
|
|
1785
|
-
});
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
1532
|
function showView(view) {
|
|
1789
1533
|
if (ui.view.value === view) return;
|
|
1790
1534
|
if (state.value && !availableViews(state.value).includes(view)) return;
|
|
@@ -1833,10 +1577,6 @@ function setTicketMode(mode) {
|
|
|
1833
1577
|
closeMenus();
|
|
1834
1578
|
});
|
|
1835
1579
|
}
|
|
1836
|
-
function setFocusMode(on) {
|
|
1837
|
-
ui.focusMode.value = on;
|
|
1838
|
-
remember(FOCUS_MODE_KEY, on ? "1" : null);
|
|
1839
|
-
}
|
|
1840
1580
|
function restartToast(page) {
|
|
1841
1581
|
if (page.restart === "manual") showToast("Saved. Restart the dashboard to apply it.");
|
|
1842
1582
|
else if (page.restart === "waiting" && state.value?.agentRun?.last?.status === "running") {
|
|
@@ -2059,15 +1799,9 @@ var handlers = {
|
|
|
2059
1799
|
() => document.querySelector('#tickets .item[data-selected="true"]')?.scrollIntoView({ block: "center", behavior: "smooth" })
|
|
2060
1800
|
);
|
|
2061
1801
|
},
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
ui.unattendedSeen.value = endedAt;
|
|
2066
|
-
remember(UNATTENDED_SEEN_KEY, endedAt);
|
|
2067
|
-
},
|
|
2068
|
-
// A nudge on Slack is invisible to GitHub, so it's recorded as a note: the agent
|
|
2069
|
-
// reads it as free text, and the board's nudge timer starts over from it.
|
|
2070
|
-
nudge: (id) => void applyAction(id, "note", { text: "Nudged reviewers" }),
|
|
1802
|
+
// A nudge on Slack is invisible to GitHub, so it's recorded: the board's nudge
|
|
1803
|
+
// timer starts over from it, and the agent reads that the user asked.
|
|
1804
|
+
nudge: (id) => void applyAction(id, "nudge"),
|
|
2071
1805
|
unpark: (id) => void unpark(id),
|
|
2072
1806
|
refreshBoard: () => void refreshBoard(),
|
|
2073
1807
|
refreshTickets: () => void refreshTickets(),
|
|
@@ -2092,7 +1826,6 @@ var handlers = {
|
|
|
2092
1826
|
applyTheme(mode);
|
|
2093
1827
|
ui.theme.value = mode;
|
|
2094
1828
|
},
|
|
2095
|
-
setFocusMode,
|
|
2096
1829
|
openHelp: () => document.getElementById("help")?.showModal(),
|
|
2097
1830
|
hideToast,
|
|
2098
1831
|
openSettings,
|
|
@@ -2113,6 +1846,134 @@ var handlers = {
|
|
|
2113
1846
|
listCalendars: () => void listCalendars()
|
|
2114
1847
|
};
|
|
2115
1848
|
|
|
1849
|
+
// client/format.ts
|
|
1850
|
+
var DAY_FORMAT = {
|
|
1851
|
+
weekday: "long",
|
|
1852
|
+
day: "numeric",
|
|
1853
|
+
month: "long"
|
|
1854
|
+
};
|
|
1855
|
+
function formatters(timeZone) {
|
|
1856
|
+
return {
|
|
1857
|
+
time: new Intl.DateTimeFormat(void 0, { timeZone, hour: "2-digit", minute: "2-digit" }),
|
|
1858
|
+
day: new Intl.DateTimeFormat(void 0, { timeZone, ...DAY_FORMAT }),
|
|
1859
|
+
shortDay: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short" }),
|
|
1860
|
+
shortDayYear: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short", year: "numeric" }),
|
|
1861
|
+
weekday: new Intl.DateTimeFormat(void 0, { timeZone, weekday: "long" })
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
var clockFormats = formatters();
|
|
1865
|
+
var dateFormats = formatters("UTC");
|
|
1866
|
+
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
1867
|
+
function formatted(value, kind) {
|
|
1868
|
+
const bare = typeof value === "string" && DATE_ONLY.test(value);
|
|
1869
|
+
const d4 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
|
|
1870
|
+
return d4 && !Number.isNaN(d4.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d4) : "";
|
|
1871
|
+
}
|
|
1872
|
+
function parseDate(value) {
|
|
1873
|
+
if (typeof value !== "string" || value === "") return null;
|
|
1874
|
+
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
1875
|
+
if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
|
|
1876
|
+
const d4 = new Date(value);
|
|
1877
|
+
return Number.isNaN(d4.getTime()) ? null : d4;
|
|
1878
|
+
}
|
|
1879
|
+
function formatTime(value) {
|
|
1880
|
+
return formatted(value, "time");
|
|
1881
|
+
}
|
|
1882
|
+
function formatDay(value) {
|
|
1883
|
+
return formatted(value, "day");
|
|
1884
|
+
}
|
|
1885
|
+
function formatShortDay(value) {
|
|
1886
|
+
return formatted(value, "shortDay");
|
|
1887
|
+
}
|
|
1888
|
+
function formatWeekday(value) {
|
|
1889
|
+
return formatted(value, "weekday");
|
|
1890
|
+
}
|
|
1891
|
+
var dayKeyFmt = null;
|
|
1892
|
+
var calendarZone;
|
|
1893
|
+
function setCalendarZone(timeZone) {
|
|
1894
|
+
if (timeZone === calendarZone) return;
|
|
1895
|
+
calendarZone = timeZone;
|
|
1896
|
+
try {
|
|
1897
|
+
clockFormats = formatters(timeZone);
|
|
1898
|
+
dayKeyFmt = timeZone ? new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }) : null;
|
|
1899
|
+
} catch {
|
|
1900
|
+
dayKeyFmt = null;
|
|
1901
|
+
clockFormats = formatters();
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
function dayKey(d4) {
|
|
1905
|
+
if (dayKeyFmt) {
|
|
1906
|
+
const parts = dayKeyFmt.formatToParts(d4);
|
|
1907
|
+
const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
|
|
1908
|
+
return `${part("year")}-${part("month")}-${part("day")}`;
|
|
1909
|
+
}
|
|
1910
|
+
const m4 = String(d4.getMonth() + 1).padStart(2, "0");
|
|
1911
|
+
const day = String(d4.getDate()).padStart(2, "0");
|
|
1912
|
+
return `${d4.getFullYear()}-${m4}-${day}`;
|
|
1913
|
+
}
|
|
1914
|
+
function splitKey(key) {
|
|
1915
|
+
const [y4, m4, d4] = key.split("-").map(Number);
|
|
1916
|
+
return [y4, m4 - 1, d4];
|
|
1917
|
+
}
|
|
1918
|
+
function keyAt(ms) {
|
|
1919
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
1920
|
+
}
|
|
1921
|
+
function addDays(key, days) {
|
|
1922
|
+
const [y4, m4, d4] = splitKey(key);
|
|
1923
|
+
return keyAt(Date.UTC(y4, m4, d4 + days));
|
|
1924
|
+
}
|
|
1925
|
+
function addMonths(key, months) {
|
|
1926
|
+
const [y4, m4, d4] = splitKey(key);
|
|
1927
|
+
const lastDay = new Date(Date.UTC(y4, m4 + months + 1, 0)).getUTCDate();
|
|
1928
|
+
return keyAt(Date.UTC(y4, m4 + months, Math.min(d4, lastDay)));
|
|
1929
|
+
}
|
|
1930
|
+
function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
|
|
1931
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1932
|
+
const [y4, m4, d4] = splitKey(value);
|
|
1933
|
+
const [ty, tm, td] = splitKey(dayKey(now));
|
|
1934
|
+
return Math.round((Date.UTC(y4, m4, d4) - Date.UTC(ty, tm, td)) / 864e5);
|
|
1935
|
+
}
|
|
1936
|
+
const target = parseDate(value);
|
|
1937
|
+
if (!target) return null;
|
|
1938
|
+
const a4 = Date.UTC(...splitKey(dayKey(now)));
|
|
1939
|
+
const b4 = Date.UTC(...splitKey(dayKey(target)));
|
|
1940
|
+
return Math.round((b4 - a4) / 864e5);
|
|
1941
|
+
}
|
|
1942
|
+
function relativeDay(value, now = /* @__PURE__ */ new Date()) {
|
|
1943
|
+
const diff = daysFromToday(value, now);
|
|
1944
|
+
if (diff === null) return "";
|
|
1945
|
+
if (diff === 0) return "today";
|
|
1946
|
+
if (diff === 1) return "tomorrow";
|
|
1947
|
+
if (diff === -1) return "yesterday";
|
|
1948
|
+
if (diff > 1 && diff <= 7) return `in ${diff} days`;
|
|
1949
|
+
if (diff < -1 && diff >= -7) return `${Math.abs(diff)} days ago`;
|
|
1950
|
+
return formatShortDay(value);
|
|
1951
|
+
}
|
|
1952
|
+
function formatDuration(minutes) {
|
|
1953
|
+
const h5 = Math.floor(minutes / 60);
|
|
1954
|
+
const m4 = minutes % 60;
|
|
1955
|
+
if (h5 === 0) return `${m4} min`;
|
|
1956
|
+
if (m4 === 0) return `${h5} h`;
|
|
1957
|
+
return `${h5} h ${m4} min`;
|
|
1958
|
+
}
|
|
1959
|
+
function relativeTime(value, now = /* @__PURE__ */ new Date()) {
|
|
1960
|
+
const d4 = parseDate(value);
|
|
1961
|
+
if (!d4) return "";
|
|
1962
|
+
const seconds = Math.round((now.getTime() - d4.getTime()) / 1e3);
|
|
1963
|
+
if (seconds < 60) return "just now";
|
|
1964
|
+
const minutes = Math.round(seconds / 60);
|
|
1965
|
+
if (minutes < 60) return `${minutes} min ago`;
|
|
1966
|
+
const hours = Math.round(minutes / 60);
|
|
1967
|
+
if (hours < 48) return `${hours} h ago`;
|
|
1968
|
+
const days = Math.round(hours / 24);
|
|
1969
|
+
if (days <= 30) return `${days} days ago`;
|
|
1970
|
+
return dayKey(d4).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
|
|
1971
|
+
}
|
|
1972
|
+
function firstLine(text) {
|
|
1973
|
+
const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
|
|
1974
|
+
return line.replace(/^[#>*\-\s]+/, "").replace(/[*_`]/g, "").trim();
|
|
1975
|
+
}
|
|
1976
|
+
|
|
2116
1977
|
// client/failure.ts
|
|
2117
1978
|
function failureNotice(failure, { fold, className, icon, lastGood, now, onRetry }, ui2, handlers2) {
|
|
2118
1979
|
return el(
|
|
@@ -2571,9 +2432,6 @@ function renderItem(item, state2, ui2, handlers2) {
|
|
|
2571
2432
|
function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
2572
2433
|
const now = new Date(state2.now);
|
|
2573
2434
|
const selected = ui2.selectedId.value === item.id;
|
|
2574
|
-
const active = state2.session?.active ?? null;
|
|
2575
|
-
const running = active !== null && active.id === item.id;
|
|
2576
|
-
const overrun = running && remainingSeconds(active, ui2.clock.value) < 0;
|
|
2577
2435
|
const repo = githubRepo(item);
|
|
2578
2436
|
const title = item.url ? el(
|
|
2579
2437
|
"a",
|
|
@@ -2591,8 +2449,6 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2591
2449
|
"data-selected": String(selected),
|
|
2592
2450
|
"data-priority": String(item.priority !== void 0 && item.status === "open"),
|
|
2593
2451
|
"data-pending": String(ui2.pending.value.has(item.id)),
|
|
2594
|
-
"data-running": String(running),
|
|
2595
|
-
"data-overrun": String(overrun),
|
|
2596
2452
|
"data-detail": String(ui2.detailFor.value === item.id),
|
|
2597
2453
|
// A click on the card opens the panel on it: one way in, and the whole card is it.
|
|
2598
2454
|
onClick: (event) => {
|
|
@@ -2612,31 +2468,20 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2612
2468
|
"div",
|
|
2613
2469
|
{ class: "item__body" },
|
|
2614
2470
|
el("p", { class: "item__title" }, title),
|
|
2615
|
-
renderMeta(item, state2,
|
|
2471
|
+
renderMeta(item, state2, now),
|
|
2616
2472
|
item.detail ? el("p", { class: "item__detail" }, renderMarkdown(item.detail)) : null
|
|
2617
2473
|
),
|
|
2618
|
-
renderActions(item,
|
|
2474
|
+
renderActions(item, ui2, handlers2),
|
|
2619
2475
|
ui2.menuFor.value === item.id ? renderSnoozeMenu(item, now, handlers2) : null
|
|
2620
2476
|
);
|
|
2621
2477
|
}
|
|
2622
|
-
function renderMeta(item, state2,
|
|
2478
|
+
function renderMeta(item, state2, now) {
|
|
2623
2479
|
const pills = [];
|
|
2624
2480
|
const said = /* @__PURE__ */ new Set();
|
|
2625
2481
|
const pill = (cls, text) => {
|
|
2626
2482
|
pills.push(el("span", { class: cls }, text));
|
|
2627
2483
|
said.add(text.trim().toLowerCase());
|
|
2628
2484
|
};
|
|
2629
|
-
const active = state2.session?.active ?? null;
|
|
2630
|
-
if (active && active.id === item.id) {
|
|
2631
|
-
pills.push(
|
|
2632
|
-
el(
|
|
2633
|
-
"span",
|
|
2634
|
-
{ class: "pill pill--running" },
|
|
2635
|
-
el("span", { class: "item__running-clock" }, clock(remainingSeconds(active, ui2.clock.value))),
|
|
2636
|
-
" focusing"
|
|
2637
|
-
)
|
|
2638
|
-
);
|
|
2639
|
-
}
|
|
2640
2485
|
if (item.priority !== void 0 && item.status === "open") {
|
|
2641
2486
|
pill("pill pill--priority", `#${item.priority}`);
|
|
2642
2487
|
}
|
|
@@ -2688,24 +2533,11 @@ function rowPillTexts(row, state2) {
|
|
|
2688
2533
|
function rowPills(row, state2) {
|
|
2689
2534
|
return rowPillTexts(row, state2).map(([cls, text]) => el("span", { class: cls }, text));
|
|
2690
2535
|
}
|
|
2691
|
-
function renderActions(item,
|
|
2536
|
+
function renderActions(item, ui2, handlers2) {
|
|
2692
2537
|
const buttons = [];
|
|
2693
2538
|
const act = (id, action) => () => handlers2.onAction(id, action);
|
|
2694
|
-
const running = state2.session?.active?.id === item.id;
|
|
2695
2539
|
if (item.status === "open") {
|
|
2696
2540
|
if (item.kind === "task") {
|
|
2697
|
-
buttons.push(
|
|
2698
|
-
el(
|
|
2699
|
-
"button",
|
|
2700
|
-
{
|
|
2701
|
-
type: "button",
|
|
2702
|
-
class: running ? "button button--primary" : "button",
|
|
2703
|
-
title: running ? "Stop the focus session" : "Start a focus session on this",
|
|
2704
|
-
onClick: () => running ? handlers2.stopSession() : handlers2.startSession(item.id)
|
|
2705
|
-
},
|
|
2706
|
-
running ? "Stop" : "Focus"
|
|
2707
|
-
)
|
|
2708
|
-
);
|
|
2709
2541
|
buttons.push(el("button", { type: "button", class: "button", onClick: act(item.id, "done") }, "Done"));
|
|
2710
2542
|
buttons.push(
|
|
2711
2543
|
el(
|
|
@@ -2905,8 +2737,10 @@ function renderBoard(state2, ui2, handlers2) {
|
|
|
2905
2737
|
const open = board.rows.filter((row) => row.status === "open");
|
|
2906
2738
|
for (const court of COURT_ORDER) {
|
|
2907
2739
|
const rows = open.filter((row) => row.court === court);
|
|
2908
|
-
|
|
2909
|
-
|
|
2740
|
+
const nudged = court === "reviewers" ? rows.filter((row) => row.nudgedAt) : [];
|
|
2741
|
+
const waiting = rows.filter((row) => !row.nudgedAt);
|
|
2742
|
+
if (waiting.length > 0) parts.push(section(COURT_TITLE[court], waiting, state2, ui2, handlers2, null, renderPullRow));
|
|
2743
|
+
if (nudged.length > 0) parts.push(section("Nudged", nudged, state2, ui2, handlers2, null, renderPullRow));
|
|
2910
2744
|
}
|
|
2911
2745
|
if (open.length === 0 && !board.reason) {
|
|
2912
2746
|
parts.push(
|
|
@@ -2963,6 +2797,7 @@ function PullRow({ item: row, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2963
2797
|
"data-id": row.id,
|
|
2964
2798
|
"data-status": row.status,
|
|
2965
2799
|
"data-court": row.court,
|
|
2800
|
+
"data-nudged": String(Boolean(row.nudgedAt)),
|
|
2966
2801
|
"data-selected": String(selected),
|
|
2967
2802
|
"data-pending": String(ui2.pending.value.has(row.id)),
|
|
2968
2803
|
"data-detail": String(ui2.detailFor.value === row.id),
|
|
@@ -3010,6 +2845,9 @@ function renderPullMeta(row, state2, now) {
|
|
|
3010
2845
|
if (row.mergeable === "CONFLICTING") pill("pill pill--overdue", "conflicts");
|
|
3011
2846
|
if (row.autoMerge) pill("pill pill--good", "auto-merge on");
|
|
3012
2847
|
if (row.nudge) pill("pill pill--age", "time to ask");
|
|
2848
|
+
if (row.nudgedAt && row.nudgedUntil) {
|
|
2849
|
+
pill("pill", `nudged ${relativeTime(row.nudgedAt, now)}, back ${relativeDay(row.nudgedUntil, now)} ${formatTime(row.nudgedUntil)}`);
|
|
2850
|
+
}
|
|
3013
2851
|
if (row.court === "blocked") {
|
|
3014
2852
|
pill("pill", `blocked ${waitedFor(row.since, now)}`);
|
|
3015
2853
|
} else if (row.court === "gate" || row.court === "checks") {
|
|
@@ -3114,7 +2952,7 @@ function renderPullActions(row, ui2, handlers2) {
|
|
|
3114
2952
|
)
|
|
3115
2953
|
);
|
|
3116
2954
|
if (row.status === "open") {
|
|
3117
|
-
if (row.court === "reviewers") {
|
|
2955
|
+
if (row.court === "reviewers" && !row.nudgedAt) {
|
|
3118
2956
|
buttons.push(
|
|
3119
2957
|
el(
|
|
3120
2958
|
"button",
|
|
@@ -3151,7 +2989,7 @@ var TICKET_COURT_TITLE = {
|
|
|
3151
2989
|
};
|
|
3152
2990
|
var TICKET_COURT_ORDER = ["settled", "started", "idle"];
|
|
3153
2991
|
var TICKET_COURT_HINT = {
|
|
3154
|
-
settled: "Every pull request
|
|
2992
|
+
settled: "Every pull request linked to these is closed. Move them on, or say what is still to come \u2014 a pull request nobody has written yet is invisible from here.",
|
|
3155
2993
|
started: "A pull request is open, so the work has begun. The status still says it has not.",
|
|
3156
2994
|
idle: "Nothing in Jira links any code to these. Fine for work that is not code; worth a look otherwise."
|
|
3157
2995
|
};
|
|
@@ -3362,7 +3200,8 @@ function TicketRowView({ item: row, state: state2, ui: ui2, handlers: handlers2
|
|
|
3362
3200
|
{ class: "item__body" },
|
|
3363
3201
|
renderTicketStatus(row, state2, ui2, handlers2),
|
|
3364
3202
|
el("p", { class: "item__title" }, title),
|
|
3365
|
-
renderTicketMeta(row, state2, handlers2)
|
|
3203
|
+
renderTicketMeta(row, state2, handlers2),
|
|
3204
|
+
renderGitHubEvidence(row, now)
|
|
3366
3205
|
),
|
|
3367
3206
|
renderTicketActions(row, ui2, handlers2),
|
|
3368
3207
|
ui2.menuFor.value === row.id ? renderSnoozeMenu(row, now, handlers2, PARK_MENU) : null
|
|
@@ -3436,6 +3275,25 @@ function renderTicketMeta(row, state2, handlers2) {
|
|
|
3436
3275
|
pills.push(...rowPills(row, state2));
|
|
3437
3276
|
return pills.length > 0 ? el("div", { class: "item__meta item__meta--extra" }, pills) : null;
|
|
3438
3277
|
}
|
|
3278
|
+
var EVIDENCE_SHOWN = 3;
|
|
3279
|
+
function renderGitHubEvidence(row, now) {
|
|
3280
|
+
const pulls = row.pulls ?? [];
|
|
3281
|
+
if (!row.fromGitHub || pulls.length === 0) return null;
|
|
3282
|
+
const parts = ["GitHub, ahead of Jira: "];
|
|
3283
|
+
pulls.slice(0, EVIDENCE_SHOWN).forEach((pull, i4) => {
|
|
3284
|
+
if (i4 > 0) parts.push(" \xB7 ");
|
|
3285
|
+
parts.push(
|
|
3286
|
+
el("a", { href: pull.url, target: "_blank", rel: "noopener noreferrer", title: pull.title }, `${pull.repo}#${pull.number}`),
|
|
3287
|
+
` ${describePull(pull, now)}`
|
|
3288
|
+
);
|
|
3289
|
+
});
|
|
3290
|
+
if (pulls.length > EVIDENCE_SHOWN) parts.push(` \xB7 and ${pulls.length - EVIDENCE_SHOWN} more`);
|
|
3291
|
+
return el("p", { class: "item__reason item__reason--waiting" }, parts);
|
|
3292
|
+
}
|
|
3293
|
+
function describePull(pull, now) {
|
|
3294
|
+
if (pull.state === "open" || pull.state === "draft") return pull.state;
|
|
3295
|
+
return pull.closedAt ? `${pull.state} ${relativeTime(pull.closedAt, now)}` : pull.state;
|
|
3296
|
+
}
|
|
3439
3297
|
function outOfSyncFlag(row, state2, handlers2) {
|
|
3440
3298
|
const hit = state2.tickets?.rows?.find((candidate) => candidate.id === row.id && candidate.status === "open");
|
|
3441
3299
|
if (!hit) return null;
|
|
@@ -3746,8 +3604,6 @@ var SHORTCUTS = [
|
|
|
3746
3604
|
[["Esc"], "Close the menu, then the panel"],
|
|
3747
3605
|
[["o"], "Open the source link"],
|
|
3748
3606
|
[["u"], "Undo the last action"],
|
|
3749
|
-
[["f"], "Focus mode \u2014 hide all but the top item"],
|
|
3750
|
-
[["p"], "Start a focus session on the selected item"],
|
|
3751
3607
|
[["r"], "Refresh whichever board you are on"],
|
|
3752
3608
|
[["w"], "Jira tickets: switch between out of sync and working on"],
|
|
3753
3609
|
[[","], "Settings: your objective, where the agent looks, and the rest"],
|
|
@@ -4843,14 +4699,8 @@ function boardRow(name, health, text, action) {
|
|
|
4843
4699
|
|
|
4844
4700
|
// client/today.ts
|
|
4845
4701
|
function TodayView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
{ class: "focus-bar", id: "focus-bar", hidden: !ui2.focusMode.value },
|
|
4849
|
-
"Focus mode \u2014 everything but the top item is hidden. ",
|
|
4850
|
-
el("button", { type: "button", class: "button", id: "focus-exit", onClick: () => handlers2.setFocusMode(false) }, "Show everything")
|
|
4851
|
-
);
|
|
4852
|
-
if (state2.setup?.needed) return [focusBar, k(SetupCard, { state: state2, ui: ui2, handlers: handlers2 })];
|
|
4853
|
-
return [focusBar, renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
|
|
4702
|
+
if (state2.setup?.needed) return [k(SetupCard, { state: state2, ui: ui2, handlers: handlers2 })];
|
|
4703
|
+
return [renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
|
|
4854
4704
|
}
|
|
4855
4705
|
function renderObjective(state2, handlers2 = null) {
|
|
4856
4706
|
const focus = state2.focus;
|
|
@@ -4973,7 +4823,6 @@ function App({ ui: ui2, handlers: handlers2 }) {
|
|
|
4973
4823
|
el("a", { class: "skip-link", href: "#main" }, "Skip to content"),
|
|
4974
4824
|
k(Header, { state: current, ui: ui2, handlers: handlers2 }),
|
|
4975
4825
|
k(Tabs, { state: current, ui: ui2, handlers: handlers2 }),
|
|
4976
|
-
current ? k(Timer, { state: current, ui: ui2, handlers: handlers2 }) : el("div", { class: "timer", id: "timer", hidden: true }),
|
|
4977
4826
|
el("div", { id: "banners", class: "banners" }, current ? renderBanners(current, ui2, handlers2) : null),
|
|
4978
4827
|
el(
|
|
4979
4828
|
"main",
|
|
@@ -5091,11 +4940,6 @@ function installKeyboard(handlers2) {
|
|
|
5091
4940
|
event.preventDefault();
|
|
5092
4941
|
moveSelection(-1);
|
|
5093
4942
|
return;
|
|
5094
|
-
case "f":
|
|
5095
|
-
if (ui.view.value !== "today") return;
|
|
5096
|
-
event.preventDefault();
|
|
5097
|
-
handlers2.setFocusMode(!ui.focusMode.value);
|
|
5098
|
-
return;
|
|
5099
4943
|
case "1":
|
|
5100
4944
|
event.preventDefault();
|
|
5101
4945
|
handlers2.setView("today");
|
|
@@ -5140,7 +4984,6 @@ function installKeyboard(handlers2) {
|
|
|
5140
4984
|
if (!item) return;
|
|
5141
4985
|
const boardRow2 = ui.view.value !== "today";
|
|
5142
4986
|
const status = "status" in item ? item.status : null;
|
|
5143
|
-
const kind = "kind" in item ? item.kind : null;
|
|
5144
4987
|
switch (event.key) {
|
|
5145
4988
|
case "e":
|
|
5146
4989
|
event.preventDefault();
|
|
@@ -5163,12 +5006,6 @@ function installKeyboard(handlers2) {
|
|
|
5163
5006
|
event.preventDefault();
|
|
5164
5007
|
handlers2.openDetail(item.id, "assistant");
|
|
5165
5008
|
break;
|
|
5166
|
-
case "p":
|
|
5167
|
-
event.preventDefault();
|
|
5168
|
-
if (boardRow2) break;
|
|
5169
|
-
if (state.value?.session.active?.id === item.id) handlers2.stopSession();
|
|
5170
|
-
else if (status === "open" && kind === "task") handlers2.startSession(item.id);
|
|
5171
|
-
break;
|
|
5172
5009
|
case "o":
|
|
5173
5010
|
if (item.url) {
|
|
5174
5011
|
event.preventDefault();
|
|
@@ -5188,15 +5025,9 @@ R(k(App, { ui, handlers }), root);
|
|
|
5188
5025
|
j3(() => {
|
|
5189
5026
|
document.body.dataset.view = ui.view.value;
|
|
5190
5027
|
});
|
|
5191
|
-
j3(() => {
|
|
5192
|
-
document.body.dataset.focusMode = String(ui.focusMode.value);
|
|
5193
|
-
});
|
|
5194
5028
|
j3(() => {
|
|
5195
5029
|
document.body.dataset.offline = String(ui.connectionError.value !== null);
|
|
5196
5030
|
});
|
|
5197
|
-
j3(() => {
|
|
5198
|
-
document.body.dataset.sessionActive = String(state.value?.session.active != null);
|
|
5199
|
-
});
|
|
5200
5031
|
j3(() => {
|
|
5201
5032
|
const current = state.value;
|
|
5202
5033
|
const open = current !== null && (detailRun(current, ui) !== null || detailRow(current, ui) !== null);
|
|
@@ -5223,7 +5054,6 @@ j3(() => {
|
|
|
5223
5054
|
});
|
|
5224
5055
|
installKeyboard(handlers);
|
|
5225
5056
|
await refresh();
|
|
5226
|
-
startLocalTick();
|
|
5227
5057
|
var zone = watchZone({
|
|
5228
5058
|
browserZone,
|
|
5229
5059
|
dashboardZone: () => state.value?.timeZone,
|