daily-focus 1.2.0-dev.10 → 1.2.0-dev.11
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 -3
- package/SETUP.md +60 -18
- package/dist/{audit-7ZCLKF46.js → audit-F26VGPOO.js} +3 -3
- package/dist/{chunk-J7ZYVUG6.js → chunk-4DZEBUPU.js} +5 -1
- package/dist/{chunk-RZNQJ6UG.js → chunk-CPJ6LOVC.js} +1 -1
- package/dist/{chunk-ODJGQCM3.js → chunk-EV44BC2F.js} +22 -5
- package/dist/{chunk-Z4SFMMRQ.js → chunk-GVLNDQVQ.js} +2 -2
- package/dist/{chunk-64XB7UDN.js → chunk-HVCCIZGA.js} +1 -1
- package/dist/cli.js +6 -6
- package/dist/{demo-BYLLMKSQ.js → demo-UMGG3VKY.js} +4 -4
- package/dist/{init-EMZDG2I7.js → init-MFPXASX3.js} +1 -1
- package/dist/{seed-JU4LQ33D.js → seed-S32JX5MW.js} +3 -3
- package/dist/{server-WQYUOKB3.js → server-EDPYEZCP.js} +1701 -209
- package/dist/{service-PUJ3YDH5.js → service-XPFMYIHR.js} +1 -1
- package/package.json +1 -1
- package/prompts/README.md +6 -4
- package/prompts/onboarding.md +41 -0
- package/public/app.js +611 -273
- package/public/style.css +573 -75
package/public/app.js
CHANGED
|
@@ -1110,6 +1110,24 @@ function postObjective(objective, blocker) {
|
|
|
1110
1110
|
function postListCalendars() {
|
|
1111
1111
|
return postJson("/api/calendars/list", {});
|
|
1112
1112
|
}
|
|
1113
|
+
function postSetupAnswer(step, answer) {
|
|
1114
|
+
return post("/api/onboarding/answer", { step, answer });
|
|
1115
|
+
}
|
|
1116
|
+
function postSetupRevise(step, text, draft) {
|
|
1117
|
+
return post("/api/onboarding/revise", { step, text, draft });
|
|
1118
|
+
}
|
|
1119
|
+
function postSetupDetect() {
|
|
1120
|
+
return post("/api/onboarding/detect");
|
|
1121
|
+
}
|
|
1122
|
+
function postSetupRetry(step) {
|
|
1123
|
+
return post("/api/onboarding/retry", { step });
|
|
1124
|
+
}
|
|
1125
|
+
function postSetupFinish() {
|
|
1126
|
+
return post("/api/onboarding/finish");
|
|
1127
|
+
}
|
|
1128
|
+
async function fetchSetupSources() {
|
|
1129
|
+
return jsonFrom(await fetch("/api/onboarding/sources"), "GET /api/onboarding/sources");
|
|
1130
|
+
}
|
|
1113
1131
|
|
|
1114
1132
|
// client/state.ts
|
|
1115
1133
|
var VIEWS = ["today", "board", "tickets", "settings"];
|
|
@@ -1144,7 +1162,14 @@ function createUi(initial = {}) {
|
|
|
1144
1162
|
settingsError: y3(initial.settingsError ?? null),
|
|
1145
1163
|
settingsSaving: y3(initial.settingsSaving ?? false),
|
|
1146
1164
|
texts: y3(initial.texts ?? {}),
|
|
1147
|
-
calendars: y3(initial.calendars ?? null)
|
|
1165
|
+
calendars: y3(initial.calendars ?? null),
|
|
1166
|
+
setupDrafts: y3(initial.setupDrafts ?? /* @__PURE__ */ new Map()),
|
|
1167
|
+
setupEditing: y3(initial.setupEditing ?? null),
|
|
1168
|
+
setupConfirm: y3(initial.setupConfirm ?? null),
|
|
1169
|
+
setupBusy: y3(initial.setupBusy ?? null),
|
|
1170
|
+
setupError: y3(initial.setupError ?? null),
|
|
1171
|
+
setupTell: y3(initial.setupTell ?? /* @__PURE__ */ new Map()),
|
|
1172
|
+
setupSources: y3(initial.setupSources ?? null)
|
|
1148
1173
|
};
|
|
1149
1174
|
}
|
|
1150
1175
|
function isView(value) {
|
|
@@ -1666,7 +1691,7 @@ async function loadSettings() {
|
|
|
1666
1691
|
showToast(`Could not load the settings: ${err.message}`);
|
|
1667
1692
|
}
|
|
1668
1693
|
}
|
|
1669
|
-
async function sendSettings(values
|
|
1694
|
+
async function sendSettings(values) {
|
|
1670
1695
|
if (ui.settingsSaving.value) return;
|
|
1671
1696
|
n2(() => {
|
|
1672
1697
|
ui.settingsSaving.value = true;
|
|
@@ -1676,17 +1701,14 @@ async function sendSettings(values, fromDraft) {
|
|
|
1676
1701
|
const page = await postSettings(values);
|
|
1677
1702
|
n2(() => {
|
|
1678
1703
|
ui.settings.value = page;
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
ui.settingsDraft.value = draft;
|
|
1683
|
-
}
|
|
1704
|
+
const draft = new Map(ui.settingsDraft.value);
|
|
1705
|
+
for (const [key, value] of Object.entries(values)) if (draft.get(key) === value) draft.delete(key);
|
|
1706
|
+
ui.settingsDraft.value = draft;
|
|
1684
1707
|
});
|
|
1685
1708
|
restartToast(page);
|
|
1686
1709
|
if (page.restart === "waiting") void followRestart();
|
|
1687
1710
|
} catch (err) {
|
|
1688
|
-
|
|
1689
|
-
else showToast(`Could not save: ${err.message}`);
|
|
1711
|
+
ui.settingsError.value = err.message;
|
|
1690
1712
|
} finally {
|
|
1691
1713
|
ui.settingsSaving.value = false;
|
|
1692
1714
|
}
|
|
@@ -1764,6 +1786,60 @@ async function listCalendars() {
|
|
|
1764
1786
|
ui.calendars.value = err.message;
|
|
1765
1787
|
}
|
|
1766
1788
|
}
|
|
1789
|
+
async function setupRequest(step, send) {
|
|
1790
|
+
if (ui.setupBusy.value) return false;
|
|
1791
|
+
n2(() => {
|
|
1792
|
+
ui.setupBusy.value = step;
|
|
1793
|
+
ui.setupError.value = null;
|
|
1794
|
+
});
|
|
1795
|
+
try {
|
|
1796
|
+
adoptState(await send());
|
|
1797
|
+
return true;
|
|
1798
|
+
} catch (err) {
|
|
1799
|
+
ui.setupError.value = { step, message: err.message };
|
|
1800
|
+
return false;
|
|
1801
|
+
} finally {
|
|
1802
|
+
ui.setupBusy.value = null;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
function forgetDraft(step) {
|
|
1806
|
+
if (!ui.setupDrafts.value.has(step)) return;
|
|
1807
|
+
const next = new Map(ui.setupDrafts.value);
|
|
1808
|
+
next.delete(step);
|
|
1809
|
+
ui.setupDrafts.value = next;
|
|
1810
|
+
}
|
|
1811
|
+
async function answerSetup(step, answer) {
|
|
1812
|
+
if (!await setupRequest(step, () => postSetupAnswer(step, answer))) return;
|
|
1813
|
+
n2(() => {
|
|
1814
|
+
forgetDraft(step);
|
|
1815
|
+
if (ui.setupEditing.value === step) ui.setupEditing.value = null;
|
|
1816
|
+
if (ui.setupConfirm.value === step) ui.setupConfirm.value = null;
|
|
1817
|
+
ui.setupSources.value = null;
|
|
1818
|
+
});
|
|
1819
|
+
afterRender(() => {
|
|
1820
|
+
const heading = document.querySelector('.onboarding__card[data-current="true"] h2');
|
|
1821
|
+
heading?.focus({ preventScroll: true });
|
|
1822
|
+
heading?.closest(".onboarding__card")?.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
async function reviseSetup(step, text, draft) {
|
|
1826
|
+
if (!await setupRequest(step, () => postSetupRevise(step, text, draft))) return;
|
|
1827
|
+
n2(() => {
|
|
1828
|
+
forgetDraft(step);
|
|
1829
|
+
if (ui.setupConfirm.value === step) ui.setupConfirm.value = null;
|
|
1830
|
+
const tell = new Map(ui.setupTell.value);
|
|
1831
|
+
tell.delete(step);
|
|
1832
|
+
ui.setupTell.value = tell;
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
async function loadSetupSources() {
|
|
1836
|
+
ui.setupSources.value = "loading";
|
|
1837
|
+
try {
|
|
1838
|
+
ui.setupSources.value = await fetchSetupSources();
|
|
1839
|
+
} catch (err) {
|
|
1840
|
+
ui.setupSources.value = { error: err.message };
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1767
1843
|
function openSettings(section2 = null) {
|
|
1768
1844
|
if (ui.view.value !== "settings") {
|
|
1769
1845
|
try {
|
|
@@ -1897,14 +1973,39 @@ var handlers = {
|
|
|
1897
1973
|
ui.settingsDraft.value = /* @__PURE__ */ new Map();
|
|
1898
1974
|
ui.settingsError.value = null;
|
|
1899
1975
|
}),
|
|
1900
|
-
saveSettings: (
|
|
1901
|
-
chooseSettings: (values) => void sendSettings(values, false),
|
|
1976
|
+
saveSettings: () => void sendSettings(Object.fromEntries(ui.settingsDraft.value)),
|
|
1902
1977
|
loadText: (name) => void loadText(name),
|
|
1903
1978
|
editText: (name, text) => patchText(name, { draft: text }),
|
|
1904
1979
|
saveText: (name, overwrite) => void saveText(name, overwrite),
|
|
1905
1980
|
reloadText: (name) => void loadText(name),
|
|
1906
1981
|
saveObjective: (objective, blocker) => void saveObjective(objective, blocker),
|
|
1907
|
-
listCalendars: () => void listCalendars()
|
|
1982
|
+
listCalendars: () => void listCalendars(),
|
|
1983
|
+
answerSetup: (step, answer) => void answerSetup(step, answer),
|
|
1984
|
+
editSetupDraft: (step, answer, version) => {
|
|
1985
|
+
if (answer === null) return forgetDraft(step);
|
|
1986
|
+
const next = new Map(ui.setupDrafts.value);
|
|
1987
|
+
next.set(step, { version, answer });
|
|
1988
|
+
ui.setupDrafts.value = next;
|
|
1989
|
+
},
|
|
1990
|
+
editSetupStep: (step) => n2(() => {
|
|
1991
|
+
ui.setupEditing.value = step;
|
|
1992
|
+
ui.setupError.value = null;
|
|
1993
|
+
}),
|
|
1994
|
+
confirmSetup: (step) => {
|
|
1995
|
+
ui.setupConfirm.value = step;
|
|
1996
|
+
},
|
|
1997
|
+
editSetupTell: (step, text) => {
|
|
1998
|
+
const next = new Map(ui.setupTell.value);
|
|
1999
|
+
next.set(step, text);
|
|
2000
|
+
ui.setupTell.value = next;
|
|
2001
|
+
},
|
|
2002
|
+
reviseSetup: (step, text, draft) => void reviseSetup(step, text, draft),
|
|
2003
|
+
retrySetup: (step) => void setupRequest(step, () => postSetupRetry(step)),
|
|
2004
|
+
detectSetup: () => void setupRequest("detect", () => postSetupDetect()),
|
|
2005
|
+
finishSetup: () => void setupRequest("finish", () => postSetupFinish()).then((finished) => {
|
|
2006
|
+
if (finished && state.value?.restart === "waiting") void followRestart();
|
|
2007
|
+
}),
|
|
2008
|
+
loadSetupSources: () => void loadSetupSources()
|
|
1908
2009
|
};
|
|
1909
2010
|
|
|
1910
2011
|
// client/format.ts
|
|
@@ -4280,35 +4381,35 @@ function renderAssistantTurn(turn, label, handlers2) {
|
|
|
4280
4381
|
);
|
|
4281
4382
|
}
|
|
4282
4383
|
function renderWork(turn) {
|
|
4283
|
-
const
|
|
4384
|
+
const steps2 = turn.steps ?? [];
|
|
4284
4385
|
if (turn.status === "running") {
|
|
4285
4386
|
return el(
|
|
4286
4387
|
"div",
|
|
4287
4388
|
{ class: "assistant__work" },
|
|
4288
4389
|
el("p", { class: "assistant__status assistant__status--working" }, "Working \xB7 ", k(Elapsed, { since: turn.startedAt })),
|
|
4289
|
-
|
|
4390
|
+
steps2.length ? renderSteps(steps2) : null
|
|
4290
4391
|
);
|
|
4291
4392
|
}
|
|
4292
4393
|
const took = durationBetween(turn.startedAt, turn.endedAt);
|
|
4293
|
-
if (!
|
|
4294
|
-
const failed =
|
|
4394
|
+
if (!steps2.length) return took ? el("p", { class: "assistant__took" }, `Answered in ${took}`) : null;
|
|
4395
|
+
const failed = steps2.filter((step) => step.state === "failed").length;
|
|
4295
4396
|
const summary = [
|
|
4296
4397
|
took ? `${turn.status === "done" ? "Worked" : "Ran"} for ${took}` : null,
|
|
4297
|
-
`${
|
|
4398
|
+
`${steps2.length} ${steps2.length === 1 ? "step" : "steps"}`,
|
|
4298
4399
|
failed ? `${failed} failed` : null
|
|
4299
4400
|
].filter(Boolean).join(" \xB7 ");
|
|
4300
|
-
return el("details", { class: "assistant__work", open: turn.status === "failed" }, el("summary", null, summary), renderSteps(
|
|
4401
|
+
return el("details", { class: "assistant__work", open: turn.status === "failed" }, el("summary", null, summary), renderSteps(steps2));
|
|
4301
4402
|
}
|
|
4302
|
-
function renderSteps(
|
|
4403
|
+
function renderSteps(steps2) {
|
|
4303
4404
|
return el(
|
|
4304
4405
|
"ol",
|
|
4305
4406
|
{ class: "assistant__steps" },
|
|
4306
|
-
|
|
4307
|
-
(
|
|
4407
|
+
steps2.map(
|
|
4408
|
+
(step, index) => step.kind === "message" ? el("li", { key: index, class: "assistant__step assistant__step--message" }, renderMarkdown(step.text.trim())) : el(
|
|
4308
4409
|
"li",
|
|
4309
|
-
{ key: index, class: "assistant__step", "data-state":
|
|
4310
|
-
el("span", { class: "assistant__step-text" },
|
|
4311
|
-
|
|
4410
|
+
{ key: index, class: "assistant__step", "data-state": step.state },
|
|
4411
|
+
el("span", { class: "assistant__step-text" }, step.text),
|
|
4412
|
+
step.detail ? el("code", { class: "assistant__step-detail", title: step.detail }, step.detail) : null
|
|
4312
4413
|
)
|
|
4313
4414
|
)
|
|
4314
4415
|
);
|
|
@@ -4334,6 +4435,430 @@ function splitCreated(reply) {
|
|
|
4334
4435
|
return { body: created.length ? kept.join("\n").trim() : reply, created };
|
|
4335
4436
|
}
|
|
4336
4437
|
|
|
4438
|
+
// client/onboarding.ts
|
|
4439
|
+
var SECONDS_PER_STEP = 20;
|
|
4440
|
+
function OnboardingView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4441
|
+
const flow = state2.onboarding;
|
|
4442
|
+
const open = flow.steps.filter((step) => step.status === "open");
|
|
4443
|
+
const editing = ui2.setupEditing.value;
|
|
4444
|
+
h2(() => {
|
|
4445
|
+
if (!flow.detected) handlers2.detectSetup();
|
|
4446
|
+
}, [flow.detected]);
|
|
4447
|
+
return el(
|
|
4448
|
+
"div",
|
|
4449
|
+
{ class: "onboarding" },
|
|
4450
|
+
el(
|
|
4451
|
+
"header",
|
|
4452
|
+
{ class: "onboarding__bar" },
|
|
4453
|
+
el("span", { class: "onboarding__name" }, "Daily Focus"),
|
|
4454
|
+
progress(flow)
|
|
4455
|
+
),
|
|
4456
|
+
el(
|
|
4457
|
+
"main",
|
|
4458
|
+
{ class: "onboarding__column", id: "main" },
|
|
4459
|
+
flow.steps.length > 0 && flow.steps.every((step) => step.status === "open") && flow.steps[0].id === flow.current ? el(
|
|
4460
|
+
"p",
|
|
4461
|
+
{ class: "onboarding__lede" },
|
|
4462
|
+
"Each morning an agent reads your mail, calendar, pull requests and the rest, and writes a short brief of what needs doing today. A few questions get it ready; most are answered already, so confirm or change them."
|
|
4463
|
+
) : null,
|
|
4464
|
+
el(
|
|
4465
|
+
"ol",
|
|
4466
|
+
{ class: "onboarding__thread" },
|
|
4467
|
+
flow.steps.map((step) => {
|
|
4468
|
+
if (step.id === flow.current || step.id === editing) {
|
|
4469
|
+
return el("li", { key: step.id }, k(StepCard, { step, ui: ui2, handlers: handlers2, current: step.id === flow.current }));
|
|
4470
|
+
}
|
|
4471
|
+
if (step.status === "open") return null;
|
|
4472
|
+
return el("li", { key: step.id }, doneRow(step, handlers2, ui2.setupBusy.value !== null));
|
|
4473
|
+
}),
|
|
4474
|
+
flow.current === null ? el("li", { key: "finish" }, k(FinishCard, { flow, ui: ui2, handlers: handlers2 })) : ahead(flow, open)
|
|
4475
|
+
)
|
|
4476
|
+
)
|
|
4477
|
+
);
|
|
4478
|
+
}
|
|
4479
|
+
function progress(flow) {
|
|
4480
|
+
const total = flow.steps.length + 1;
|
|
4481
|
+
const left = flow.steps.filter((step) => step.status === "open").length;
|
|
4482
|
+
const seconds = left * SECONDS_PER_STEP;
|
|
4483
|
+
const text = flow.current === null ? "Ready when you are" : seconds < 60 ? "Under a minute left" : seconds < 120 ? "About a minute left" : `About ${Math.round(seconds / 60)} minutes left`;
|
|
4484
|
+
const done = total - left - (flow.current === null ? 0 : 1);
|
|
4485
|
+
return el(
|
|
4486
|
+
"div",
|
|
4487
|
+
{ class: "onboarding__progress" },
|
|
4488
|
+
el(
|
|
4489
|
+
"div",
|
|
4490
|
+
{ class: "onboarding__track", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": total, "aria-valuenow": done, "aria-label": "Setup" },
|
|
4491
|
+
el("i", { style: { width: `${Math.round(done / total * 100)}%` } })
|
|
4492
|
+
),
|
|
4493
|
+
el("span", null, text)
|
|
4494
|
+
);
|
|
4495
|
+
}
|
|
4496
|
+
function doneRow(step, handlers2, busy) {
|
|
4497
|
+
return el(
|
|
4498
|
+
"div",
|
|
4499
|
+
{ class: "onboarding__done", "data-status": step.status, "data-step": step.id },
|
|
4500
|
+
el("span", { class: "onboarding__dot", "aria-hidden": "true" }),
|
|
4501
|
+
el("span", { class: "onboarding__summary" }, step.summary ?? `${step.topic[0].toUpperCase()}${step.topic.slice(1)}: decide later.`),
|
|
4502
|
+
step.locked ? el("span", { class: "onboarding__locked", title: "Set in the environment the dashboard was started with" }, "Set by the environment") : el("button", { type: "button", class: "onboarding__change", disabled: busy, onClick: () => handlers2.editSetupStep(step.id) }, "Change")
|
|
4503
|
+
);
|
|
4504
|
+
}
|
|
4505
|
+
function ahead(flow, open) {
|
|
4506
|
+
const rest = open.filter((step) => step.id !== flow.current);
|
|
4507
|
+
const looking = rest.find((step) => step.proposal?.status === "working");
|
|
4508
|
+
const topics = rest.filter((step) => step !== looking).map((step) => step.topic);
|
|
4509
|
+
const later = topics.length > 4 ? [...topics.slice(0, 3), `${topics.length - 3} more`] : topics;
|
|
4510
|
+
return el(
|
|
4511
|
+
"li",
|
|
4512
|
+
{ key: "ahead", class: "onboarding__ahead" },
|
|
4513
|
+
looking ? el(
|
|
4514
|
+
"div",
|
|
4515
|
+
{ class: "onboarding__next" },
|
|
4516
|
+
el("span", { class: "onboarding__dot onboarding__dot--looking", "aria-hidden": "true" }),
|
|
4517
|
+
el("p", null, `Next, ${looking.topic}. It's already looking:`),
|
|
4518
|
+
steps(looking)
|
|
4519
|
+
) : null,
|
|
4520
|
+
el(
|
|
4521
|
+
"p",
|
|
4522
|
+
{ class: "onboarding__then" },
|
|
4523
|
+
el("span", { class: "onboarding__dot onboarding__dot--later", "aria-hidden": "true" }),
|
|
4524
|
+
later.length ? `Then ${listed([...later, "your first brief"])}.` : "Then your first brief."
|
|
4525
|
+
)
|
|
4526
|
+
);
|
|
4527
|
+
}
|
|
4528
|
+
function steps(step, count = 3) {
|
|
4529
|
+
const shown = (step.proposal?.steps ?? []).filter((s4) => s4.kind === "tool").slice(-count);
|
|
4530
|
+
const waiting = step.proposal?.status === "working" && !shown.some((s4) => s4.state === "running");
|
|
4531
|
+
return el(
|
|
4532
|
+
"ul",
|
|
4533
|
+
{ class: "onboarding__steps" },
|
|
4534
|
+
shown.map((s4, index) => el("li", { key: index, "data-state": s4.state, title: s4.detail ?? void 0 }, s4.text)),
|
|
4535
|
+
waiting ? el("li", { key: "waiting", "data-state": "running" }, shown.length ? "Writing its answer\u2026" : "Starting\u2026") : null
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
function listed(names) {
|
|
4539
|
+
if (names.length <= 1) return names.join("");
|
|
4540
|
+
return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
|
|
4541
|
+
}
|
|
4542
|
+
function baseAnswer(step) {
|
|
4543
|
+
if (step.answer && step.answer.later !== true) return step.answer;
|
|
4544
|
+
return {
|
|
4545
|
+
choices: Object.fromEntries(step.card.choices.map((choice) => [choice.id, choice.selected])),
|
|
4546
|
+
fields: Object.fromEntries(step.card.fields.map((field) => [field.id, field.value]))
|
|
4547
|
+
};
|
|
4548
|
+
}
|
|
4549
|
+
function StepCard({ step, ui: ui2, handlers: handlers2, current }) {
|
|
4550
|
+
const saved = ui2.setupDrafts.value.get(step.id);
|
|
4551
|
+
const draft = saved?.answer ?? baseAnswer(step);
|
|
4552
|
+
const stale = saved !== void 0 && saved.version !== step.card.version;
|
|
4553
|
+
const busy = ui2.setupBusy.value !== null;
|
|
4554
|
+
const error = ui2.setupError.value?.step === step.id ? ui2.setupError.value.message : null;
|
|
4555
|
+
const editing = ui2.setupEditing.value === step.id;
|
|
4556
|
+
const proposal = step.proposal;
|
|
4557
|
+
const looking = proposal?.status === "working";
|
|
4558
|
+
const edit = (next) => handlers2.editSetupDraft(step.id, next, saved?.version ?? step.card.version);
|
|
4559
|
+
const tell = ui2.setupTell.value.get(step.id) ?? "";
|
|
4560
|
+
const empty = step.card.choices.every((choice) => choice.options.length === 0) && step.card.fields.length === 0;
|
|
4561
|
+
const conversation = proposal?.conversation ?? [];
|
|
4562
|
+
const revising = conversation.at(-1)?.from === "you";
|
|
4563
|
+
const confirming = looking && ui2.setupConfirm.value === step.id;
|
|
4564
|
+
const held = busy ? "Saving your last answer\u2026" : empty && looking ? "Still looking; this fills in when it is done" : empty && step.card.problem?.blocking ? "Fix what it says above, or decide later" : null;
|
|
4565
|
+
return el(
|
|
4566
|
+
"section",
|
|
4567
|
+
{ class: "onboarding__card", "data-current": String(current), "data-step": step.id, "aria-labelledby": `setup-${step.id}` },
|
|
4568
|
+
el("span", { class: "onboarding__dot onboarding__dot--open", "aria-hidden": "true" }),
|
|
4569
|
+
el("h2", { id: `setup-${step.id}`, tabIndex: -1 }, step.title),
|
|
4570
|
+
conversation.length ? el(
|
|
4571
|
+
"ol",
|
|
4572
|
+
{ class: "onboarding__talk" },
|
|
4573
|
+
conversation.map(
|
|
4574
|
+
(line, index) => line.from === "it" ? el("li", { key: index, class: "onboarding__why" }, sparkle(), el("span", null, line.text)) : el("li", { key: index, class: "onboarding__said" }, line.text)
|
|
4575
|
+
)
|
|
4576
|
+
) : step.card.why && !(looking && empty) ? el("p", { class: "onboarding__why" }, sparkle(), el("span", null, step.card.why)) : null,
|
|
4577
|
+
looking ? el(
|
|
4578
|
+
"div",
|
|
4579
|
+
{ class: "onboarding__looking", role: "status" },
|
|
4580
|
+
revising ? null : el("p", null, empty ? `Looking into ${step.topic}\u2026` : "Still looking. You can answer now, or wait for its pick."),
|
|
4581
|
+
steps(step)
|
|
4582
|
+
) : null,
|
|
4583
|
+
// Under what it said, so a reply reads as one, and what comes back lands here too.
|
|
4584
|
+
step.card.revisable && !looking ? el(
|
|
4585
|
+
"form",
|
|
4586
|
+
{
|
|
4587
|
+
class: "onboarding__tell",
|
|
4588
|
+
onSubmit: (event) => {
|
|
4589
|
+
event.preventDefault();
|
|
4590
|
+
if (tell.trim() && !busy) handlers2.reviseSetup(step.id, tell, draft);
|
|
4591
|
+
}
|
|
4592
|
+
},
|
|
4593
|
+
el("input", {
|
|
4594
|
+
type: "text",
|
|
4595
|
+
value: tell,
|
|
4596
|
+
"aria-label": step.card.reply ?? "Tell it what to change",
|
|
4597
|
+
placeholder: step.card.reply ?? "Tell it what to change",
|
|
4598
|
+
onInput: (event) => handlers2.editSetupTell(step.id, event.currentTarget.value)
|
|
4599
|
+
}),
|
|
4600
|
+
el("button", { type: "submit", class: "button", disabled: busy || tell.trim() === "" }, "Send")
|
|
4601
|
+
) : null,
|
|
4602
|
+
proposal?.status === "failed" ? el(
|
|
4603
|
+
"p",
|
|
4604
|
+
{ class: "onboarding__note" },
|
|
4605
|
+
`It couldn't look into this: ${proposal.error}. `,
|
|
4606
|
+
el("button", { type: "button", class: "link-button", disabled: busy, onClick: () => handlers2.retrySetup(step.id) }, "Try again")
|
|
4607
|
+
) : null,
|
|
4608
|
+
stale ? el(
|
|
4609
|
+
"p",
|
|
4610
|
+
{ class: "onboarding__note" },
|
|
4611
|
+
"It has a new suggestion since you started changing this. ",
|
|
4612
|
+
el("button", { type: "button", class: "link-button", onClick: () => handlers2.editSetupDraft(step.id, null, 0) }, "Use it")
|
|
4613
|
+
) : null,
|
|
4614
|
+
step.card.problem?.blocking ? problem(step, busy, handlers2) : null,
|
|
4615
|
+
el(
|
|
4616
|
+
"form",
|
|
4617
|
+
{
|
|
4618
|
+
class: "onboarding__form",
|
|
4619
|
+
onSubmit: (event) => {
|
|
4620
|
+
event.preventDefault();
|
|
4621
|
+
if (busy) return;
|
|
4622
|
+
if (looking && !confirming) return handlers2.confirmSetup(step.id);
|
|
4623
|
+
handlers2.answerSetup(step.id, draft);
|
|
4624
|
+
}
|
|
4625
|
+
},
|
|
4626
|
+
// Fields first: a card with both asks for the few words before the list.
|
|
4627
|
+
step.card.fields.length ? el(
|
|
4628
|
+
"div",
|
|
4629
|
+
{ class: "onboarding__fields" },
|
|
4630
|
+
step.card.fields.map(
|
|
4631
|
+
(field) => fieldView(field, draft.fields[field.id] ?? "", (value) => edit({ ...draft, fields: { ...draft.fields, [field.id]: value } }))
|
|
4632
|
+
)
|
|
4633
|
+
) : null,
|
|
4634
|
+
step.card.choices.map(
|
|
4635
|
+
(choice) => k(ChoiceView, {
|
|
4636
|
+
choice,
|
|
4637
|
+
picked: draft.choices[choice.id] ?? [],
|
|
4638
|
+
onPick: (values) => edit({ ...draft, choices: { ...draft.choices, [choice.id]: values } })
|
|
4639
|
+
})
|
|
4640
|
+
),
|
|
4641
|
+
step.card.problem && !step.card.problem.blocking ? problem(step, busy, handlers2) : null,
|
|
4642
|
+
error ? el("p", { class: "onboarding__error", role: "alert" }, error) : null,
|
|
4643
|
+
confirming ? el(
|
|
4644
|
+
"div",
|
|
4645
|
+
{ class: "onboarding__actions onboarding__confirm", role: "alert" },
|
|
4646
|
+
el("p", null, revising ? "It\u2019s still working on your change." : "It\u2019s still filling this in.", " Continue with what\u2019s here, and stop it?"),
|
|
4647
|
+
el("button", { type: "submit", class: "button button--primary", disabled: busy }, "Continue without it"),
|
|
4648
|
+
el("button", { type: "button", class: "button onboarding__quiet", onClick: () => handlers2.confirmSetup(null) }, "Wait for it")
|
|
4649
|
+
) : el(
|
|
4650
|
+
"div",
|
|
4651
|
+
{ class: "onboarding__actions" },
|
|
4652
|
+
el(
|
|
4653
|
+
"button",
|
|
4654
|
+
{ type: "submit", class: "button button--primary", disabled: held !== null, title: held ?? void 0 },
|
|
4655
|
+
ui2.setupBusy.value === step.id ? "Saving\u2026" : step.card.empty && step.card.fields.every((field) => !(draft.fields[field.id] ?? "").trim()) ? step.card.empty : "Continue"
|
|
4656
|
+
),
|
|
4657
|
+
step.card.later ? el(
|
|
4658
|
+
"button",
|
|
4659
|
+
{ type: "button", class: "button onboarding__quiet", disabled: busy, onClick: () => handlers2.answerSetup(step.id, { later: true }) },
|
|
4660
|
+
step.card.later
|
|
4661
|
+
) : null,
|
|
4662
|
+
editing ? el("button", { type: "button", class: "button onboarding__quiet", onClick: () => handlers2.editSetupStep(null) }, "Cancel") : null
|
|
4663
|
+
)
|
|
4664
|
+
)
|
|
4665
|
+
);
|
|
4666
|
+
}
|
|
4667
|
+
function problem(step, busy, handlers2) {
|
|
4668
|
+
const found = step.card.problem;
|
|
4669
|
+
return el(
|
|
4670
|
+
"div",
|
|
4671
|
+
{ class: "onboarding__problem", "data-blocking": String(found.blocking) },
|
|
4672
|
+
el("p", null, found.message),
|
|
4673
|
+
found.links?.length ? el(
|
|
4674
|
+
"p",
|
|
4675
|
+
{ class: "onboarding__links" },
|
|
4676
|
+
found.links.map((link) => el("a", { key: link.url, href: link.url, target: "_blank", rel: "noopener" }, link.label))
|
|
4677
|
+
) : null,
|
|
4678
|
+
found.command ? el("pre", null, el("code", null, found.command)) : null,
|
|
4679
|
+
el("button", { type: "button", class: "button", disabled: busy, onClick: () => handlers2.retrySetup(step.id) }, "Check again")
|
|
4680
|
+
);
|
|
4681
|
+
}
|
|
4682
|
+
function sparkle() {
|
|
4683
|
+
return el(
|
|
4684
|
+
"svg",
|
|
4685
|
+
{ class: "onboarding__sparkle", width: 14, height: 14, viewBox: "0 0 14 14", "aria-hidden": "true" },
|
|
4686
|
+
el("path", { d: "M7 0l1.6 5.4L14 7l-5.4 1.6L7 14l-1.6-5.4L0 7l5.4-1.6z", fill: "currentColor" })
|
|
4687
|
+
);
|
|
4688
|
+
}
|
|
4689
|
+
function ChoiceView({ choice, picked, onPick }) {
|
|
4690
|
+
const [adding, setAdding] = d2(false);
|
|
4691
|
+
const [typed, setTyped] = d2("");
|
|
4692
|
+
const known = new Set(choice.options.map((option) => option.value));
|
|
4693
|
+
const own = picked.filter((value) => !known.has(value));
|
|
4694
|
+
const toggle = (value) => {
|
|
4695
|
+
if (!choice.multiple) return onPick([value]);
|
|
4696
|
+
onPick(picked.includes(value) ? picked.filter((v4) => v4 !== value) : [...picked, value]);
|
|
4697
|
+
};
|
|
4698
|
+
const add = () => {
|
|
4699
|
+
const value = typed.trim();
|
|
4700
|
+
if (value) onPick(choice.multiple ? [...picked.filter((v4) => v4 !== value), value] : [value]);
|
|
4701
|
+
setTyped("");
|
|
4702
|
+
setAdding(false);
|
|
4703
|
+
};
|
|
4704
|
+
const adder = choice.custom ? adding ? el(
|
|
4705
|
+
"div",
|
|
4706
|
+
{ class: "onboarding__add" },
|
|
4707
|
+
el("input", {
|
|
4708
|
+
type: "text",
|
|
4709
|
+
value: typed,
|
|
4710
|
+
placeholder: choice.custom,
|
|
4711
|
+
"aria-label": choice.custom,
|
|
4712
|
+
ref: (input) => input?.focus(),
|
|
4713
|
+
onInput: (event) => setTyped(event.currentTarget.value),
|
|
4714
|
+
onKeyDown: (event) => {
|
|
4715
|
+
if (event.key === "Enter") {
|
|
4716
|
+
event.preventDefault();
|
|
4717
|
+
add();
|
|
4718
|
+
}
|
|
4719
|
+
if (event.key === "Escape") setAdding(false);
|
|
4720
|
+
}
|
|
4721
|
+
}),
|
|
4722
|
+
el("button", { type: "button", class: "button", onClick: add }, "Add")
|
|
4723
|
+
) : el("button", { type: "button", class: "onboarding__add-button", onClick: () => setAdding(true) }, choice.multiple ? choice.custom : "Other\u2026") : null;
|
|
4724
|
+
const label = choice.label ? el("legend", { class: "onboarding__legend" }, choice.label) : null;
|
|
4725
|
+
const rows = choice.multiple || choice.options.some((option) => option.detail);
|
|
4726
|
+
if (!rows) {
|
|
4727
|
+
return el(
|
|
4728
|
+
"fieldset",
|
|
4729
|
+
{ class: "onboarding__choice" },
|
|
4730
|
+
label,
|
|
4731
|
+
el(
|
|
4732
|
+
"div",
|
|
4733
|
+
{ class: "onboarding__pills", role: "radiogroup" },
|
|
4734
|
+
[...choice.options, ...own.map((value) => ({ value, label: value, detail: null, group: null }))].map(
|
|
4735
|
+
(option) => el(
|
|
4736
|
+
"button",
|
|
4737
|
+
{
|
|
4738
|
+
type: "button",
|
|
4739
|
+
key: option.value,
|
|
4740
|
+
role: "radio",
|
|
4741
|
+
class: "onboarding__pill",
|
|
4742
|
+
"aria-checked": String(picked.includes(option.value)),
|
|
4743
|
+
onClick: () => toggle(option.value)
|
|
4744
|
+
},
|
|
4745
|
+
option.label
|
|
4746
|
+
)
|
|
4747
|
+
),
|
|
4748
|
+
adder
|
|
4749
|
+
)
|
|
4750
|
+
);
|
|
4751
|
+
}
|
|
4752
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4753
|
+
for (const option of choice.options) groups.set(option.group, [...groups.get(option.group) ?? [], option]);
|
|
4754
|
+
if (own.length) groups.set("Added by you", own.map((value) => ({ value, label: value, detail: null, group: "Added by you" })));
|
|
4755
|
+
return el(
|
|
4756
|
+
"fieldset",
|
|
4757
|
+
{ class: "onboarding__choice" },
|
|
4758
|
+
label,
|
|
4759
|
+
el(
|
|
4760
|
+
"div",
|
|
4761
|
+
{ class: "onboarding__list" },
|
|
4762
|
+
[...groups].map(
|
|
4763
|
+
([group, options]) => el(
|
|
4764
|
+
"div",
|
|
4765
|
+
{ class: "onboarding__group", key: group ?? "" },
|
|
4766
|
+
group ? el("p", { class: "onboarding__group-name" }, group) : null,
|
|
4767
|
+
options.map(
|
|
4768
|
+
(option) => el(
|
|
4769
|
+
"label",
|
|
4770
|
+
{ class: "onboarding__option", key: option.value },
|
|
4771
|
+
el("input", {
|
|
4772
|
+
type: choice.multiple ? "checkbox" : "radio",
|
|
4773
|
+
name: choice.id,
|
|
4774
|
+
checked: picked.includes(option.value),
|
|
4775
|
+
onChange: () => toggle(option.value)
|
|
4776
|
+
}),
|
|
4777
|
+
el("span", { class: "onboarding__box", "data-kind": choice.multiple ? "check" : "radio", "aria-hidden": "true" }),
|
|
4778
|
+
el("span", { class: "onboarding__option-text" }, option.label, option.detail ? el("small", null, option.detail) : null)
|
|
4779
|
+
)
|
|
4780
|
+
)
|
|
4781
|
+
)
|
|
4782
|
+
),
|
|
4783
|
+
choice.options.length === 0 && own.length === 0 && !choice.custom ? el("p", { class: "onboarding__none" }, "Nothing to choose from.") : null,
|
|
4784
|
+
adder ? el("div", { class: "onboarding__adder" }, adder) : null
|
|
4785
|
+
)
|
|
4786
|
+
);
|
|
4787
|
+
}
|
|
4788
|
+
function fieldView(field, value, onInput) {
|
|
4789
|
+
return el(
|
|
4790
|
+
"div",
|
|
4791
|
+
{ class: "onboarding__field", key: field.id },
|
|
4792
|
+
el(
|
|
4793
|
+
"label",
|
|
4794
|
+
null,
|
|
4795
|
+
el("span", { class: "onboarding__field-label" }, field.label),
|
|
4796
|
+
el("input", {
|
|
4797
|
+
type: "text",
|
|
4798
|
+
value,
|
|
4799
|
+
placeholder: field.placeholder ?? void 0,
|
|
4800
|
+
onInput: (event) => onInput(event.currentTarget.value)
|
|
4801
|
+
})
|
|
4802
|
+
),
|
|
4803
|
+
field.help ? el("small", null, field.help) : null,
|
|
4804
|
+
// Offered, never filled in: a click puts one in the field, to edit or keep.
|
|
4805
|
+
field.suggestions?.length ? el(
|
|
4806
|
+
"ul",
|
|
4807
|
+
{ class: "onboarding__suggestions" },
|
|
4808
|
+
field.suggestions.map(
|
|
4809
|
+
(suggestion) => el(
|
|
4810
|
+
"li",
|
|
4811
|
+
{ key: suggestion.text },
|
|
4812
|
+
el(
|
|
4813
|
+
"button",
|
|
4814
|
+
{ type: "button", "aria-pressed": String(value === suggestion.text), onClick: () => onInput(suggestion.text) },
|
|
4815
|
+
el("span", null, suggestion.text),
|
|
4816
|
+
suggestion.detail ? el("small", null, suggestion.detail) : null
|
|
4817
|
+
)
|
|
4818
|
+
)
|
|
4819
|
+
)
|
|
4820
|
+
) : null
|
|
4821
|
+
);
|
|
4822
|
+
}
|
|
4823
|
+
function FinishCard({ flow, ui: ui2, handlers: handlers2 }) {
|
|
4824
|
+
const writes = flow.steps.find((step) => step.id === "cli")?.status === "answered";
|
|
4825
|
+
const busy = ui2.setupBusy.value !== null;
|
|
4826
|
+
const error = (ui2.setupError.value?.step === "finish" ? ui2.setupError.value.message : null) ?? flow.error;
|
|
4827
|
+
const sources = ui2.setupSources.value;
|
|
4828
|
+
return el(
|
|
4829
|
+
"section",
|
|
4830
|
+
{ class: "onboarding__card onboarding__finish", "data-current": "true", "aria-labelledby": "setup-finish" },
|
|
4831
|
+
el("span", { class: "onboarding__dot onboarding__dot--open", "aria-hidden": "true" }),
|
|
4832
|
+
el("h2", { id: "setup-finish", tabIndex: -1 }, writes ? "Ready for your first brief" : "Ready to finish"),
|
|
4833
|
+
el(
|
|
4834
|
+
"p",
|
|
4835
|
+
{ class: "onboarding__why" },
|
|
4836
|
+
writes ? "It takes a few minutes, and you can watch it work. After that it runs by itself, and everything here can be changed in Settings." : "Without an agent there is no brief to write yet. Choose one in Settings when you are ready; the boards work meanwhile."
|
|
4837
|
+
),
|
|
4838
|
+
el(
|
|
4839
|
+
"details",
|
|
4840
|
+
{
|
|
4841
|
+
class: "onboarding__preview",
|
|
4842
|
+
onToggle: (event) => {
|
|
4843
|
+
if (event.currentTarget.open && sources === null) handlers2.loadSetupSources();
|
|
4844
|
+
}
|
|
4845
|
+
},
|
|
4846
|
+
el("summary", null, "What the morning agent reads each morning"),
|
|
4847
|
+
sources === null || sources === "loading" ? el("p", null, "Loading\u2026") : "error" in sources ? el("p", null, `Could not show it: ${sources.error}`) : sources.kept ? el("p", null, "Your source list is already written, and setup leaves it as it is.") : sources.text ? el("pre", null, sources.text) : el("p", null, "Nothing yet: the agent briefs from what it can reach, and says what it missed.")
|
|
4848
|
+
),
|
|
4849
|
+
error ? el("p", { class: "onboarding__error", role: "alert" }, error) : null,
|
|
4850
|
+
el(
|
|
4851
|
+
"div",
|
|
4852
|
+
{ class: "onboarding__actions" },
|
|
4853
|
+
el(
|
|
4854
|
+
"button",
|
|
4855
|
+
{ type: "button", class: "button button--primary", disabled: busy, onClick: () => handlers2.finishSetup() },
|
|
4856
|
+
ui2.setupBusy.value === "finish" ? "Saving\u2026" : writes ? "Write my first brief" : "Finish setup"
|
|
4857
|
+
)
|
|
4858
|
+
)
|
|
4859
|
+
);
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4337
4862
|
// client/settings.ts
|
|
4338
4863
|
var VIEW_NAME = {
|
|
4339
4864
|
today: "Today",
|
|
@@ -4408,7 +4933,7 @@ function objectiveCard(state2, ui2, handlers2) {
|
|
|
4408
4933
|
)
|
|
4409
4934
|
);
|
|
4410
4935
|
}
|
|
4411
|
-
function objectiveForm(objective, blocker, handlers2
|
|
4936
|
+
function objectiveForm(objective, blocker, handlers2) {
|
|
4412
4937
|
return el(
|
|
4413
4938
|
"form",
|
|
4414
4939
|
{
|
|
@@ -4439,7 +4964,7 @@ ${blocker}`,
|
|
|
4439
4964
|
el("span", { class: "field__label" }, "Blocked on (optional)"),
|
|
4440
4965
|
el("input", { class: "field__input", name: "blocker", defaultValue: blocker, placeholder: "What is in the way today, if anything", autocomplete: "off" })
|
|
4441
4966
|
),
|
|
4442
|
-
el("div", { class: "objective-form__actions" }, el("button", { type: "submit", class: "button button--primary" },
|
|
4967
|
+
el("div", { class: "objective-form__actions" }, el("button", { type: "submit", class: "button button--primary" }, "Save objective"))
|
|
4443
4968
|
);
|
|
4444
4969
|
}
|
|
4445
4970
|
function sourcesCard(state2, ui2, handlers2) {
|
|
@@ -4523,13 +5048,13 @@ function settingField(setting, draft, handlers2) {
|
|
|
4523
5048
|
};
|
|
4524
5049
|
let input;
|
|
4525
5050
|
if (setting.kind === "choice" || setting.kind === "flag") {
|
|
4526
|
-
const
|
|
5051
|
+
const choices = setting.kind === "flag" ? [{ value: "on", label: "On" }, { value: "off", label: "Off" }] : setting.choices ?? [];
|
|
4527
5052
|
const current = setting.kind === "flag" ? flagValue(value) : value;
|
|
4528
5053
|
input = el(
|
|
4529
5054
|
"select",
|
|
4530
5055
|
{ class: "field__input", id, disabled: locked, value: current, onChange },
|
|
4531
5056
|
el("option", { value: "" }, `Default: ${setting.fallback}`),
|
|
4532
|
-
|
|
5057
|
+
choices.map((choice) => el("option", { value: choice.value, key: choice.value }, choice.label))
|
|
4533
5058
|
);
|
|
4534
5059
|
} else {
|
|
4535
5060
|
input = el("input", {
|
|
@@ -4570,7 +5095,7 @@ function calendarPicker(state2, ui2, handlers2, settings) {
|
|
|
4570
5095
|
const setting = settings.find((entry) => entry.key === "DAILY_FOCUS_CALENDARS");
|
|
4571
5096
|
if (!setting || setting.source === "environment") return null;
|
|
4572
5097
|
const chosen = shownValue(setting, ui2.settingsDraft.value).split(",").map((name) => name.trim()).filter(Boolean);
|
|
4573
|
-
const
|
|
5098
|
+
const listed2 = ui2.calendars.value;
|
|
4574
5099
|
const toggle = (title, on) => {
|
|
4575
5100
|
const others = chosen.filter((name) => name.toLowerCase() !== title.toLowerCase());
|
|
4576
5101
|
const next = on ? [...others, title] : others;
|
|
@@ -4580,11 +5105,11 @@ function calendarPicker(state2, ui2, handlers2, settings) {
|
|
|
4580
5105
|
"div",
|
|
4581
5106
|
{ class: "calendar-picker" },
|
|
4582
5107
|
el("button", { type: "button", class: "button", onClick: () => handlers2.listCalendars() }, "List the calendars on this Mac"),
|
|
4583
|
-
typeof
|
|
4584
|
-
Array.isArray(
|
|
5108
|
+
typeof listed2 === "string" ? el("p", { class: "settings-card__warning" }, listed2) : null,
|
|
5109
|
+
Array.isArray(listed2) ? el(
|
|
4585
5110
|
"ul",
|
|
4586
5111
|
{ class: "calendar-picker__list" },
|
|
4587
|
-
|
|
5112
|
+
listed2.map(
|
|
4588
5113
|
(entry) => el(
|
|
4589
5114
|
"li",
|
|
4590
5115
|
{ key: `${entry.title}
|
|
@@ -4627,248 +5152,57 @@ function saveBar(ui2, handlers2) {
|
|
|
4627
5152
|
}
|
|
4628
5153
|
|
|
4629
5154
|
// client/setup.ts
|
|
4630
|
-
|
|
4631
|
-
function SetupCard({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4632
|
-
const setup = state2.setup;
|
|
5155
|
+
function FirstBriefCard({ state: state2, handlers: handlers2 }) {
|
|
4633
5156
|
const agent = state2.agentRun;
|
|
4634
|
-
const
|
|
4635
|
-
const
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
el(
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
),
|
|
4659
|
-
// A choice, so neither answer is drawn as the one to pick.
|
|
4660
|
-
choices([
|
|
4661
|
-
["Work", () => handlers2.chooseSettings({ DAILY_FOCUS_PROFILE: "work" })],
|
|
4662
|
-
["Personal life", () => handlers2.chooseSettings({ DAILY_FOCUS_PROFILE: "personal" })]
|
|
4663
|
-
])
|
|
4664
|
-
]
|
|
4665
|
-
),
|
|
4666
|
-
step(
|
|
4667
|
-
Boolean(state2.focus?.objective),
|
|
4668
|
-
"What are you trying to achieve?",
|
|
4669
|
-
state2.focus?.objective ? el("p", null, state2.focus.objective) : [
|
|
4670
|
-
el("p", null, "The agent ranks the day against it. Leave it for now if nothing stands out."),
|
|
4671
|
-
objectiveForm("", "", handlers2, "Set objective")
|
|
4672
|
-
]
|
|
4673
|
-
),
|
|
4674
|
-
step(
|
|
4675
|
-
agent.cli !== null,
|
|
4676
|
-
"Which agent writes the brief?",
|
|
4677
|
-
agent.cli ? [
|
|
4678
|
-
el(
|
|
4679
|
-
"p",
|
|
4680
|
-
null,
|
|
4681
|
-
`${CLI_LABEL[agent.cli]}. ` + (agent.schedule ? `After the first brief, it runs by itself each scheduled morning at ${agent.schedule.at}. ` : "It runs when you press refresh. ") + "It has to be logged in; the first run says if it is not."
|
|
4682
|
-
),
|
|
4683
|
-
agentAdvanced(agent.cli, ui2, handlers2)
|
|
4684
|
-
] : agentChoice(state2, handlers2)
|
|
4685
|
-
),
|
|
4686
|
-
step(setup.sources.exists && setup.sources.placeholders === 0, "Who are you, and where should it look?", sourcesStep(state2, ui2, handlers2)),
|
|
4687
|
-
step(null, "What else should the dashboard show?", boards(state2, handlers2)),
|
|
4688
|
-
step(
|
|
4689
|
-
null,
|
|
4690
|
-
"Your first brief",
|
|
4691
|
-
[
|
|
4692
|
-
el(
|
|
4693
|
-
"p",
|
|
4694
|
-
null,
|
|
4695
|
-
running ? "The morning agent is writing it now. Click the banner above to watch." : 'The morning agent writes it now, which takes a few minutes, and from then on by itself every scheduled morning. When it is done, click "updated \u2026 ago" at the top to read its report: what it found, and anything it could not reach.' + (setup.sources.exists ? "" : " Without a source list it briefs from what it can reach on its own, and says what it missed.")
|
|
4696
|
-
),
|
|
4697
|
-
placeholders ? el("p", { class: "setup__note setup__note--todo" }, "Replace the source list's placeholders first, or the agent goes looking for them.") : null,
|
|
4698
|
-
el(
|
|
4699
|
-
"button",
|
|
4700
|
-
{
|
|
4701
|
-
type: "button",
|
|
4702
|
-
class: "button button--primary",
|
|
4703
|
-
disabled: !agent.enabled || running || state2.restart === "waiting" || placeholders,
|
|
4704
|
-
title: !agent.enabled ? "Choose the agent first" : placeholders ? "Replace the source list's placeholders first" : void 0,
|
|
4705
|
-
onClick: () => handlers2.runAgent()
|
|
4706
|
-
},
|
|
4707
|
-
running ? "Running\u2026" : "Run the morning agent now"
|
|
4708
|
-
)
|
|
4709
|
-
]
|
|
4710
|
-
)
|
|
4711
|
-
)
|
|
4712
|
-
);
|
|
4713
|
-
}
|
|
4714
|
-
function step(done, title, body) {
|
|
4715
|
-
return el(
|
|
4716
|
-
"li",
|
|
4717
|
-
{ class: "setup__step", "data-done": done === null ? void 0 : String(done) },
|
|
4718
|
-
el(
|
|
4719
|
-
"span",
|
|
4720
|
-
{ class: "setup__mark", "aria-label": done === null ? void 0 : done ? "Done" : "To do" },
|
|
4721
|
-
done === null ? "\xB7" : done ? "\u2713" : "\u25CB"
|
|
4722
|
-
),
|
|
4723
|
-
el("div", { class: "setup__body" }, el("h3", { class: "setup__step-title" }, title), body)
|
|
4724
|
-
);
|
|
4725
|
-
}
|
|
4726
|
-
function choices(options) {
|
|
4727
|
-
return el(
|
|
4728
|
-
"div",
|
|
4729
|
-
{ class: "setup__choices" },
|
|
4730
|
-
options.map(([label, onClick, title]) => el("button", { type: "button", class: "button", key: label, title, onClick }, label))
|
|
4731
|
-
);
|
|
4732
|
-
}
|
|
4733
|
-
function agentChoice(state2, handlers2) {
|
|
4734
|
-
const found = ["claude", "codex"].filter((cli) => state2.setup.clis[cli]);
|
|
4735
|
-
if (found.length === 0) {
|
|
4736
|
-
return [
|
|
4737
|
-
el(
|
|
4738
|
-
"p",
|
|
4739
|
-
null,
|
|
4740
|
-
"The brief is written by a coding-agent CLI you already use. Install ",
|
|
4741
|
-
el("a", { href: "https://code.claude.com/docs", target: "_blank", rel: "noopener" }, "Claude Code"),
|
|
4742
|
-
" or the ",
|
|
4743
|
-
el("a", { href: "https://developers.openai.com/codex/cli", target: "_blank", rel: "noopener" }, "Codex CLI"),
|
|
4744
|
-
", log in, and restart the dashboard; it looks for both each time it starts."
|
|
4745
|
-
)
|
|
5157
|
+
const last = agent.last;
|
|
5158
|
+
const running = last?.status === "running";
|
|
5159
|
+
const restarting = state2.restart === "waiting";
|
|
5160
|
+
let title;
|
|
5161
|
+
let text;
|
|
5162
|
+
let actions;
|
|
5163
|
+
if (restarting) {
|
|
5164
|
+
title = "Saving your settings";
|
|
5165
|
+
text = "The dashboard restarts to apply them, then starts on your first brief.";
|
|
5166
|
+
actions = [];
|
|
5167
|
+
} else if (!agent.enabled) {
|
|
5168
|
+
title = "Choose who writes your brief";
|
|
5169
|
+
text = "Each morning a coding-agent CLI you already use writes it: Claude Code or Codex. The boards work meanwhile.";
|
|
5170
|
+
actions = [el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.openSettings("agent") }, "Choose in Settings")];
|
|
5171
|
+
} else if (running) {
|
|
5172
|
+
title = "Writing your first brief";
|
|
5173
|
+
text = "The morning agent is reading your mail, calendar and the rest. It takes a few minutes; the brief appears here when it is done.";
|
|
5174
|
+
actions = [el("button", { type: "button", class: "button", onClick: () => handlers2.openRun(last.id) }, "Watch it work")];
|
|
5175
|
+
} else if (last && last.status !== "done") {
|
|
5176
|
+
title = "Your first brief didn\u2019t finish";
|
|
5177
|
+
text = last.status === "aborted" ? "It was stopped before it wrote a brief." : last.error ? `The run failed: ${last.error}.` : "The run stopped before it wrote a brief.";
|
|
5178
|
+
actions = [
|
|
5179
|
+
el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.runAgent() }, "Try again"),
|
|
5180
|
+
el("button", { type: "button", class: "button", onClick: () => handlers2.openRun(last.id) }, "Read its report")
|
|
4746
5181
|
];
|
|
5182
|
+
} else if (last) {
|
|
5183
|
+
title = "Your first brief didn\u2019t arrive";
|
|
5184
|
+
text = "The run finished without a brief this dashboard could read. Its report says why.";
|
|
5185
|
+
actions = [
|
|
5186
|
+
el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.runAgent() }, "Run it again"),
|
|
5187
|
+
el("button", { type: "button", class: "button", onClick: () => handlers2.openRun(last.id) }, "Read its report")
|
|
5188
|
+
];
|
|
5189
|
+
} else {
|
|
5190
|
+
title = "Write your first brief";
|
|
5191
|
+
text = "It takes a few minutes. After that it runs by itself each scheduled morning.";
|
|
5192
|
+
actions = [el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.runAgent() }, "Write it now")];
|
|
4747
5193
|
}
|
|
4748
|
-
return [
|
|
4749
|
-
el("p", null, "The dashboard runs a coding-agent CLI you already use, headless, each morning. Found on this machine:"),
|
|
4750
|
-
choices(
|
|
4751
|
-
found.map(
|
|
4752
|
-
(cli) => [`Use ${CLI_LABEL[cli]}`, () => handlers2.chooseSettings({ DAILY_FOCUS_AGENT: cli }), state2.setup.clis[cli] ?? void 0]
|
|
4753
|
-
)
|
|
4754
|
-
)
|
|
4755
|
-
];
|
|
4756
|
-
}
|
|
4757
|
-
var AGENT_ADVANCED = "setup:agent-advanced";
|
|
4758
|
-
function agentAdvanced(cli, ui2, handlers2) {
|
|
4759
|
-
const keys = ["DAILY_FOCUS_AGENT_MODEL", "DAILY_FOCUS_AGENT_EFFORT", ...cli === "claude" ? ["DAILY_FOCUS_AGENT_TOOLS"] : []];
|
|
4760
|
-
const page = ui2.settings.value;
|
|
4761
|
-
const draft = ui2.settingsDraft.value;
|
|
4762
|
-
const changed = keys.some((key) => draft.has(key));
|
|
4763
|
-
const error = changed ? ui2.settingsError.value : null;
|
|
4764
|
-
return el(
|
|
4765
|
-
"details",
|
|
4766
|
-
{
|
|
4767
|
-
class: "setup__advanced",
|
|
4768
|
-
open: ui2.openDrawers.value.has(AGENT_ADVANCED),
|
|
4769
|
-
onToggle: (event) => {
|
|
4770
|
-
const open = event.currentTarget.open;
|
|
4771
|
-
if (open && !ui2.settings.peek()) handlers2.loadSettings();
|
|
4772
|
-
handlers2.toggleDrawer(AGENT_ADVANCED, open);
|
|
4773
|
-
}
|
|
4774
|
-
},
|
|
4775
|
-
el("summary", null, cli === "claude" ? "Advanced: model, effort and tools" : "Advanced: model and effort"),
|
|
4776
|
-
page ? [
|
|
4777
|
-
...keys.flatMap((key) => {
|
|
4778
|
-
const setting = page.settings.find((entry) => entry.key === key);
|
|
4779
|
-
return setting ? [settingField(setting, draft, handlers2)] : [];
|
|
4780
|
-
}),
|
|
4781
|
-
error ? el("p", { class: "setup__note setup__note--todo", role: "alert" }, error) : null,
|
|
4782
|
-
el(
|
|
4783
|
-
"div",
|
|
4784
|
-
{ class: "setup__advanced-actions" },
|
|
4785
|
-
el(
|
|
4786
|
-
"button",
|
|
4787
|
-
{ type: "button", class: "button", disabled: !changed || ui2.settingsSaving.value, onClick: () => handlers2.saveSettings(keys) },
|
|
4788
|
-
ui2.settingsSaving.value ? "Saving\u2026" : "Save"
|
|
4789
|
-
)
|
|
4790
|
-
)
|
|
4791
|
-
] : el("p", null, "Loading the settings\u2026")
|
|
4792
|
-
);
|
|
4793
|
-
}
|
|
4794
|
-
var SOURCES_EDITOR = "setup:sources";
|
|
4795
|
-
function sourcesStep(state2, ui2, handlers2) {
|
|
4796
|
-
const sources = state2.setup.sources;
|
|
4797
|
-
const open = ui2.openDrawers.value.has(SOURCES_EDITOR);
|
|
4798
|
-
const toggle = () => {
|
|
4799
|
-
if (!open && !ui2.texts.value.sources) handlers2.loadText("sources");
|
|
4800
|
-
handlers2.toggleDrawer(SOURCES_EDITOR, !open);
|
|
4801
|
-
};
|
|
4802
|
-
return [
|
|
4803
|
-
el(
|
|
4804
|
-
"p",
|
|
4805
|
-
null,
|
|
4806
|
-
"A short list the agent reads every morning. First who you are: your name as meeting notes write it, your email and your GitHub login, so it can tell your to-dos from everyone else's. Then where to look: which calendars, mailboxes and documents are yours."
|
|
4807
|
-
),
|
|
4808
|
-
sources.exists ? el(
|
|
4809
|
-
"p",
|
|
4810
|
-
{ class: sources.placeholders > 0 ? "setup__note setup__note--todo" : "setup__note" },
|
|
4811
|
-
sources.placeholders > 0 ? `It still has ${sources.placeholders} of the template's placeholders. Replace them, or delete what doesn't apply.` : "Written."
|
|
4812
|
-
) : el("p", { class: "setup__note" }, "It starts from a template, with what gh and git know about you already filled in."),
|
|
4813
|
-
el(
|
|
4814
|
-
"button",
|
|
4815
|
-
{ type: "button", class: "button", "aria-expanded": String(open), onClick: toggle },
|
|
4816
|
-
open ? "Close" : sources.exists ? "Edit it" : "Write it"
|
|
4817
|
-
),
|
|
4818
|
-
open ? el("div", { class: "setup__editor" }, textEditor("sources", ui2.texts.value.sources, handlers2, "sources.md")) : null
|
|
4819
|
-
];
|
|
4820
|
-
}
|
|
4821
|
-
var HEALTH_MARK = { ok: "\u2713", checking: "\u2026", problem: "!", todo: "\u25CB" };
|
|
4822
|
-
var HEALTH_LABEL = {
|
|
4823
|
-
ok: "Connected",
|
|
4824
|
-
checking: "Checking",
|
|
4825
|
-
problem: "Not working",
|
|
4826
|
-
todo: "To do"
|
|
4827
|
-
};
|
|
4828
|
-
function boardHealth(board, via, service) {
|
|
4829
|
-
if (board.reason) return ["problem", board.reason.message];
|
|
4830
|
-
if (board.fetchedAt) return ["ok", `Connected, through ${via}.`];
|
|
4831
|
-
return ["checking", `Checking ${service} through ${via}\u2026`];
|
|
4832
|
-
}
|
|
4833
|
-
function boards(state2, handlers2) {
|
|
4834
|
-
const rows = [];
|
|
4835
|
-
const off = (key, label) => el("button", { type: "button", class: "button", onClick: () => handlers2.chooseSettings({ [key]: "off" }) }, `Turn off ${label}`);
|
|
4836
|
-
if (state2.board.enabled) {
|
|
4837
|
-
rows.push(boardRow("Pull requests", ...boardHealth(state2.board, "gh", "GitHub"), off("DAILY_FOCUS_GITHUB", "the pull request board")));
|
|
4838
|
-
}
|
|
4839
|
-
if (state2.tickets.enabled) {
|
|
4840
|
-
rows.push(boardRow("Jira tickets", ...boardHealth(state2.tickets, "acli", "Jira"), off("DAILY_FOCUS_JIRA", "the Jira board")));
|
|
4841
|
-
}
|
|
4842
|
-
const calendar = state2.setup.calendar;
|
|
4843
|
-
if (calendar.supported && state2.agendaSource) {
|
|
4844
|
-
const live = state2.agendaSource.live;
|
|
4845
|
-
const [health, text] = !calendar.built ? ["todo", `Needs the calendar helper: run ${calendar.buildCommand} once. Until then the agenda uses the brief.`] : calendar.chosen === 0 ? ["todo", "No calendars chosen yet. Choose them, and the agenda follows them live."] : live ? ["ok", `Connected, reading ${calendar.chosen} ${calendar.chosen === 1 ? "calendar" : "calendars"} from Calendar.app.`] : state2.agendaSource.problem ? ["problem", state2.agendaSource.problem] : ["checking", "Checking Calendar.app\u2026"];
|
|
4846
|
-
rows.push(
|
|
4847
|
-
boardRow(
|
|
4848
|
-
"Calendar",
|
|
4849
|
-
health,
|
|
4850
|
-
text,
|
|
4851
|
-
calendar.built && calendar.chosen === 0 ? el("button", { type: "button", class: "button", onClick: () => handlers2.openSettings("calendar") }, "Choose calendars") : off("DAILY_FOCUS_CALENDAR", "the live agenda")
|
|
4852
|
-
)
|
|
4853
|
-
);
|
|
4854
|
-
}
|
|
4855
|
-
if (rows.length === 0) return el("p", null, "Nothing else is switched on. The boards can be turned on in Settings.");
|
|
4856
|
-
return el("ul", { class: "setup__boards" }, rows);
|
|
4857
|
-
}
|
|
4858
|
-
function boardRow(name, health, text, action) {
|
|
4859
5194
|
return el(
|
|
4860
|
-
"
|
|
4861
|
-
{ class: "
|
|
4862
|
-
el("
|
|
4863
|
-
el("
|
|
4864
|
-
el("
|
|
4865
|
-
action
|
|
5195
|
+
"section",
|
|
5196
|
+
{ class: "first-brief", id: "first-brief", "aria-labelledby": "first-brief-title", "data-state": running ? "running" : "idle" },
|
|
5197
|
+
el("h2", { class: "first-brief__title", id: "first-brief-title" }, title),
|
|
5198
|
+
el("p", { class: "first-brief__text" }, text),
|
|
5199
|
+
actions.length ? el("div", { class: "first-brief__actions" }, actions) : null
|
|
4866
5200
|
);
|
|
4867
5201
|
}
|
|
4868
5202
|
|
|
4869
5203
|
// client/today.ts
|
|
4870
5204
|
function TodayView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4871
|
-
if (state2.setup?.needed) return [k(
|
|
5205
|
+
if (state2.setup?.needed) return [k(FirstBriefCard, { state: state2, handlers: handlers2 })];
|
|
4872
5206
|
return [renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
|
|
4873
5207
|
}
|
|
4874
5208
|
function renderObjective(state2, handlers2 = null) {
|
|
@@ -4925,15 +5259,15 @@ function focusTimeStat(agenda, now) {
|
|
|
4925
5259
|
const tone = minutes === 0 ? "critical" : minutes < 60 ? "critical" : null;
|
|
4926
5260
|
return stat("Focus time left", minutes === 0 ? "None" : formatDuration(minutes), tone, `Day ends ${agenda.dayEnd}`);
|
|
4927
5261
|
}
|
|
4928
|
-
function objectiveStat(
|
|
4929
|
-
if (!
|
|
4930
|
-
if (
|
|
5262
|
+
function objectiveStat(progress2) {
|
|
5263
|
+
if (!progress2) return null;
|
|
5264
|
+
if (progress2.workingDaysSince === null) {
|
|
4931
5265
|
return stat("Since objective progress", "\u2014", null, "Nothing recorded yet");
|
|
4932
5266
|
}
|
|
4933
|
-
const days =
|
|
5267
|
+
const days = progress2.workingDaysSince;
|
|
4934
5268
|
const tone = days === 0 ? "good" : days >= 3 ? "critical" : null;
|
|
4935
5269
|
const value = days === 0 ? "Today" : days === 1 ? "1 day" : `${days} days`;
|
|
4936
|
-
return stat("Since objective progress", value, tone,
|
|
5270
|
+
return stat("Since objective progress", value, tone, progress2.lastTitle);
|
|
4937
5271
|
}
|
|
4938
5272
|
function renderHeadline(state2) {
|
|
4939
5273
|
const headline = state2.brief.headline;
|
|
@@ -4988,6 +5322,7 @@ function App({ ui: ui2, handlers: handlers2 }) {
|
|
|
4988
5322
|
const view = ui2.view.value;
|
|
4989
5323
|
const run = current ? detailRun(current, ui2) : null;
|
|
4990
5324
|
const row = current && !run ? detailRow(current, ui2) : null;
|
|
5325
|
+
if (current?.onboarding) return [k(OnboardingView, { state: current, ui: ui2, handlers: handlers2 }), k(Toast, { ui: ui2, handlers: handlers2 })];
|
|
4991
5326
|
return [
|
|
4992
5327
|
el("a", { class: "skip-link", href: "#main" }, "Skip to content"),
|
|
4993
5328
|
k(Header, { state: current, ui: ui2, handlers: handlers2 }),
|
|
@@ -5151,12 +5486,12 @@ function installKeyboard(handlers2) {
|
|
|
5151
5486
|
}
|
|
5152
5487
|
const item = selectedItem();
|
|
5153
5488
|
if (!item) return;
|
|
5154
|
-
const
|
|
5489
|
+
const boardRow = ui.view.value !== "today";
|
|
5155
5490
|
const status = "status" in item ? item.status : null;
|
|
5156
5491
|
switch (event.key) {
|
|
5157
5492
|
case "e":
|
|
5158
5493
|
event.preventDefault();
|
|
5159
|
-
if (!
|
|
5494
|
+
if (!boardRow && status === "open") handlers2.onAction(item.id, "done");
|
|
5160
5495
|
break;
|
|
5161
5496
|
case "s":
|
|
5162
5497
|
event.preventDefault();
|
|
@@ -5164,7 +5499,7 @@ function installKeyboard(handlers2) {
|
|
|
5164
5499
|
break;
|
|
5165
5500
|
case "x":
|
|
5166
5501
|
event.preventDefault();
|
|
5167
|
-
if (!
|
|
5502
|
+
if (!boardRow && status === "open") handlers2.onAction(item.id, "dismiss");
|
|
5168
5503
|
break;
|
|
5169
5504
|
case "n":
|
|
5170
5505
|
event.preventDefault();
|
|
@@ -5194,6 +5529,9 @@ R(k(App, { ui, handlers }), root);
|
|
|
5194
5529
|
j3(() => {
|
|
5195
5530
|
document.body.dataset.view = ui.view.value;
|
|
5196
5531
|
});
|
|
5532
|
+
j3(() => {
|
|
5533
|
+
document.body.dataset.onboarding = String(Boolean(state.value?.onboarding));
|
|
5534
|
+
});
|
|
5197
5535
|
j3(() => {
|
|
5198
5536
|
document.body.dataset.offline = String(ui.connectionError.value !== null);
|
|
5199
5537
|
});
|