daily-focus 2.0.0-dev.14 → 2.0.0-dev.16
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/package.json +5 -1
- package/prompts/morning-brief-personal.md +4 -2
- package/prompts/morning-brief-work.md +4 -2
- package/public/app.js +195 -57
- package/public/fonts/IBMPlexSans-Variable.woff2 +0 -0
- package/public/fonts/IBMPlexSerif-Italic.woff2 +0 -0
- package/public/fonts/IBMPlexSerif-Medium.woff2 +0 -0
- package/public/fonts/IBMPlexSerif-Regular.woff2 +0 -0
- package/public/fonts/IBMPlexSerif-SemiBold.woff2 +0 -0
- package/public/fonts/OFL.txt +93 -0
- package/public/style.css +1111 -453
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "daily-focus",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.16",
|
|
4
4
|
"description": "An interactive dashboard of what you should focus on today at work",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dashboard",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"public/index.html",
|
|
29
29
|
"public/style.css",
|
|
30
30
|
"public/app.js",
|
|
31
|
+
"public/fonts/",
|
|
31
32
|
"prompts/",
|
|
32
33
|
"schema/",
|
|
33
34
|
"tools/dfcal/build.sh",
|
|
@@ -66,5 +67,8 @@
|
|
|
66
67
|
"happy-dom": "20.14.5",
|
|
67
68
|
"preact": "10.29.8",
|
|
68
69
|
"typescript": "7.0.2"
|
|
70
|
+
},
|
|
71
|
+
"allowScripts": {
|
|
72
|
+
"esbuild": true
|
|
69
73
|
}
|
|
70
74
|
}
|
|
@@ -340,7 +340,7 @@ Then the general editorial bar:
|
|
|
340
340
|
{
|
|
341
341
|
"version": 1,
|
|
342
342
|
"generatedAt": "<ISO 8601 timestamp, with offset>",
|
|
343
|
-
"generatedBy": "<
|
|
343
|
+
"generatedBy": "<your model or agent id, such as codex, and nothing else>",
|
|
344
344
|
"date": "<today, YYYY-MM-DD>",
|
|
345
345
|
"headline": "<one or two sentences, or omit>",
|
|
346
346
|
"items": [ … ]
|
|
@@ -418,7 +418,9 @@ Check all of these, and fix anything that fails:
|
|
|
418
418
|
6. Every `url` starts with `http://` or `https://`, and came from a tool rather than
|
|
419
419
|
being built by you.
|
|
420
420
|
7. Every `start`/`end` has a timezone offset, unless deliberately all-day.
|
|
421
|
-
8. `generatedAt` is now, and `generatedBy` names whoever ran
|
|
421
|
+
8. `generatedAt` is now, and `generatedBy` names whoever ran — your model or agent
|
|
422
|
+
id and nothing else: not a working directory, a path or an account. The dashboard
|
|
423
|
+
prints it after "updated … by" in its header.
|
|
422
424
|
9. **When `focus.md` sets an objective, at least one item offers a concrete path to
|
|
423
425
|
it** — a real one you found, or the synthesised one. With no objective, no item has
|
|
424
426
|
`advancesObjective`.
|
|
@@ -512,7 +512,7 @@ Then the general editorial bar:
|
|
|
512
512
|
{
|
|
513
513
|
"version": 1,
|
|
514
514
|
"generatedAt": "<ISO 8601 timestamp, with offset>",
|
|
515
|
-
"generatedBy": "<
|
|
515
|
+
"generatedBy": "<your model or agent id, such as codex, and nothing else>",
|
|
516
516
|
"date": "<today, YYYY-MM-DD>",
|
|
517
517
|
"headline": "<one or two sentences, or omit>",
|
|
518
518
|
"dayStart": "09:00",
|
|
@@ -601,7 +601,9 @@ Check all of these, and fix anything that fails:
|
|
|
601
601
|
6. Every `url` starts with `http://` or `https://`.
|
|
602
602
|
7. Every `start`/`end` has a timezone offset, unless deliberately all-day.
|
|
603
603
|
8. `generatedAt` is now, and `generatedBy` names whoever ran — your model or agent
|
|
604
|
-
id
|
|
604
|
+
id and nothing else: not a working directory, a path or an account. The dashboard
|
|
605
|
+
prints it after "updated … by" in its header, so `codex` reads well there and
|
|
606
|
+
`codex (/root)` does not.
|
|
605
607
|
9. **When `focus.md` sets an objective, at least one item offers a concrete path to
|
|
606
608
|
it** — a real one you found, or the synthesised "no path today" item. A brief with
|
|
607
609
|
no route to the objective and no acknowledgement of that fact is a failed run.
|
package/public/app.js
CHANGED
|
@@ -2480,10 +2480,11 @@ function agentRunBanner(run, state2, handlers2) {
|
|
|
2480
2480
|
};
|
|
2481
2481
|
if (run.status === "running") {
|
|
2482
2482
|
const helping = (run.steps ?? []).filter((step) => step.subagent && step.state === "running").length;
|
|
2483
|
+
const doing = briefLanded(run, state2.brief?.generatedAt) ? "The brief is in. The morning agent is checking it and writing its report" : "The morning agent is writing a new brief";
|
|
2483
2484
|
return bannerNode(
|
|
2484
2485
|
"info",
|
|
2485
2486
|
"i",
|
|
2486
|
-
|
|
2487
|
+
`${doing}, started ${relativeTime(run.startedAt, now)}` + (helping ? `, with ${helping} ${helping === 1 ? "subagent" : "subagents"} at work.` : "."),
|
|
2487
2488
|
[el("button", { type: "button", class: "button banner__action", onClick: () => handlers2.stopAgent() }, "Stop")],
|
|
2488
2489
|
strip
|
|
2489
2490
|
);
|
|
@@ -2504,6 +2505,11 @@ function agentRunBanner(run, state2, handlers2) {
|
|
|
2504
2505
|
strip
|
|
2505
2506
|
);
|
|
2506
2507
|
}
|
|
2508
|
+
function briefLanded(run, generatedAt) {
|
|
2509
|
+
const started = parseDate(run.startedAt)?.getTime();
|
|
2510
|
+
const landed = generatedAt ? parseDate(generatedAt)?.getTime() : void 0;
|
|
2511
|
+
return started !== void 0 && landed !== void 0 && landed >= started;
|
|
2512
|
+
}
|
|
2507
2513
|
function isHeldOverDayOff(state2) {
|
|
2508
2514
|
if (state2.brief.generatedAt === null || state2.schedule.runsToday) return false;
|
|
2509
2515
|
const today = dayKey(new Date(state2.now));
|
|
@@ -2640,7 +2646,11 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2640
2646
|
},
|
|
2641
2647
|
el(
|
|
2642
2648
|
"span",
|
|
2643
|
-
|
|
2649
|
+
// A ranked row's number stands in the margin where the source dot would be:
|
|
2650
|
+
// the stylesheet draws it off `data-rank` and hides the dot, since the
|
|
2651
|
+
// source is named in the meta line anyway and the rank is what this row
|
|
2652
|
+
// is for.
|
|
2653
|
+
{ class: "item__mark", "data-rank": item.priority !== void 0 && item.status === "open" ? String(item.priority) : void 0 },
|
|
2644
2654
|
el("span", {
|
|
2645
2655
|
class: "item__dot",
|
|
2646
2656
|
style: `background:${sourceColor(item.source)}`,
|
|
@@ -2666,9 +2676,6 @@ function renderMeta(item, state2, now) {
|
|
|
2666
2676
|
pills.push(el("span", { class: cls }, text));
|
|
2667
2677
|
said.add(text.trim().toLowerCase());
|
|
2668
2678
|
};
|
|
2669
|
-
if (item.priority !== void 0 && item.status === "open") {
|
|
2670
|
-
pill("pill pill--priority", `#${item.priority}`);
|
|
2671
|
-
}
|
|
2672
2679
|
for (const [cls, text] of rowPillTexts(item, state2)) pill(cls, text);
|
|
2673
2680
|
if (item.advancesObjective) {
|
|
2674
2681
|
pill("pill pill--objective", "objective");
|
|
@@ -3730,7 +3737,7 @@ function briefRefresh(state2, handlers2) {
|
|
|
3730
3737
|
const agent = state2.agentRun;
|
|
3731
3738
|
if (!agent?.enabled) return null;
|
|
3732
3739
|
const running = agent.last?.status === "running";
|
|
3733
|
-
const label = running ? `The morning agent is writing a new brief, started ${formatTime(agent.last?.startedAt)}` : `Run the morning agent now, for a fresh brief${nextRunPhraseFor(agent)}`;
|
|
3740
|
+
const label = running ? agent.last && briefLanded(agent.last, state2.brief.generatedAt) ? `The brief is in; the morning agent is checking it and writing its report, started ${formatTime(agent.last.startedAt)}` : `The morning agent is writing a new brief, started ${formatTime(agent.last?.startedAt)}` : `Run the morning agent now, for a fresh brief${nextRunPhraseFor(agent)}`;
|
|
3734
3741
|
return el(
|
|
3735
3742
|
"button",
|
|
3736
3743
|
{
|
|
@@ -3897,8 +3904,8 @@ function RunPanel({ run, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
3897
3904
|
el(
|
|
3898
3905
|
"div",
|
|
3899
3906
|
{ class: "flyout__body", ref: body, onScroll },
|
|
3900
|
-
runStatus(run, now, handlers2),
|
|
3901
|
-
runReport(run, ui2, handlers2),
|
|
3907
|
+
run.status === "running" ? null : runStatus(run, now, handlers2),
|
|
3908
|
+
runReport(run, { landed: briefLanded(run, state2.brief?.generatedAt), now }, ui2, handlers2),
|
|
3902
3909
|
run.status === "running" ? null : runConversation(run, busy, answering, enabled, ui2, handlers2, askInput),
|
|
3903
3910
|
runList(run, runs, now, agent, handlers2)
|
|
3904
3911
|
)
|
|
@@ -3935,8 +3942,8 @@ function describeWhen(value, now) {
|
|
|
3935
3942
|
const named = day === "today" || day === "tomorrow" || day === "yesterday";
|
|
3936
3943
|
return `${named ? day : date && date.getFullYear() === now.getFullYear() ? formatShortDay(value) : formatDay(value)} at ${formatTime(value)}`;
|
|
3937
3944
|
}
|
|
3938
|
-
function runReport(run, ui2, handlers2) {
|
|
3939
|
-
if (run.steps?.length) return runWork(run, ui2, handlers2);
|
|
3945
|
+
function runReport(run, live, ui2, handlers2) {
|
|
3946
|
+
if (run.steps?.length) return runWork(run, live, ui2, handlers2);
|
|
3940
3947
|
const messages = run.messages ?? (run.report ? [run.report] : []);
|
|
3941
3948
|
const running = run.status === "running";
|
|
3942
3949
|
const earlier = !running && messages.length && messages.at(-1) === run.report ? messages.slice(0, -1) : messages;
|
|
@@ -3975,12 +3982,9 @@ function runReport(run, ui2, handlers2) {
|
|
|
3975
3982
|
})
|
|
3976
3983
|
) : null;
|
|
3977
3984
|
if (running) {
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
el("h3", { class: "flyout__label" }, "What it is doing"),
|
|
3982
|
-
list ?? el("p", { class: "flyout__empty" }, "Nothing yet.")
|
|
3983
|
-
);
|
|
3985
|
+
const latest = earlier.at(-1);
|
|
3986
|
+
const summary = `${latest ? firstLine(latest) : "Starting"} \xB7 ${earlier.length} ${earlier.length === 1 ? "message" : "messages"}`;
|
|
3987
|
+
return liveSection(run, live, summary, [list, el("ol", { class: "assistant__steps agent-run__steps" }, waitingLine(earlier.length > 0))], ui2, handlers2);
|
|
3984
3988
|
}
|
|
3985
3989
|
return el(
|
|
3986
3990
|
"section",
|
|
@@ -3995,16 +3999,9 @@ function runReport(run, ui2, handlers2) {
|
|
|
3995
3999
|
) : null
|
|
3996
4000
|
);
|
|
3997
4001
|
}
|
|
3998
|
-
function runWork(run, ui2, handlers2) {
|
|
4002
|
+
function runWork(run, live, ui2, handlers2) {
|
|
3999
4003
|
const running = run.status === "running";
|
|
4000
|
-
if (running)
|
|
4001
|
-
return el(
|
|
4002
|
-
"section",
|
|
4003
|
-
{ class: "flyout__section" },
|
|
4004
|
-
el("h3", { class: "flyout__label" }, "What it is doing"),
|
|
4005
|
-
runSteps(run, run.steps, null, ui2, handlers2)
|
|
4006
|
-
);
|
|
4007
|
-
}
|
|
4004
|
+
if (running) return liveSection(run, live, workSummary(run), runSteps(run, run.steps, null, true, ui2, handlers2), ui2, handlers2);
|
|
4008
4005
|
const last = run.steps.findLast((step) => step.by === null && step.kind === "message");
|
|
4009
4006
|
const steps2 = last && sameWords(last.text, run.report) ? run.steps.filter((step) => step !== last) : run.steps;
|
|
4010
4007
|
const handoffs = steps2.filter((step) => step.subagent).length;
|
|
@@ -4021,29 +4018,122 @@ function runWork(run, ui2, handlers2) {
|
|
|
4021
4018
|
null,
|
|
4022
4019
|
`How it got there, in ${steps2.length} ${steps2.length === 1 ? "step" : "steps"}` + (handoffs ? `, with ${handoffs} ${handoffs === 1 ? "subagent" : "subagents"}` : "")
|
|
4023
4020
|
),
|
|
4024
|
-
runSteps(run, steps2, null, ui2, handlers2)
|
|
4021
|
+
runSteps(run, steps2, null, false, ui2, handlers2)
|
|
4025
4022
|
) : null
|
|
4026
4023
|
);
|
|
4027
4024
|
}
|
|
4025
|
+
function liveSection(run, live, summary, content, ui2, handlers2) {
|
|
4026
|
+
const phase = live.landed ? "The brief is in; checking it and writing its report" : "Writing a new brief";
|
|
4027
|
+
return el(
|
|
4028
|
+
"section",
|
|
4029
|
+
{ class: "flyout__section" },
|
|
4030
|
+
el("h3", { class: "flyout__label" }, `${phase}, started ${relativeTime(run.startedAt, live.now)}`),
|
|
4031
|
+
el(
|
|
4032
|
+
"div",
|
|
4033
|
+
{ class: "agent-run__live" },
|
|
4034
|
+
workFold(run, summary, content, ui2, handlers2),
|
|
4035
|
+
el("button", { type: "button", class: "button", onClick: () => handlers2.stopAgent() }, "Stop")
|
|
4036
|
+
)
|
|
4037
|
+
);
|
|
4038
|
+
}
|
|
4039
|
+
function workFold(run, summary, content, ui2, handlers2) {
|
|
4040
|
+
const key = `${run.id}:work`;
|
|
4041
|
+
return el(
|
|
4042
|
+
"details",
|
|
4043
|
+
{
|
|
4044
|
+
class: "agent-run__list agent-run__list--working",
|
|
4045
|
+
open: ui2.messageOpen.value.get(key) ?? false,
|
|
4046
|
+
onToggle: (event) => {
|
|
4047
|
+
const isOpen = event.currentTarget.open;
|
|
4048
|
+
handlers2.toggleMessage(key, isOpen ? true : null);
|
|
4049
|
+
}
|
|
4050
|
+
},
|
|
4051
|
+
el("summary", null, summary),
|
|
4052
|
+
content
|
|
4053
|
+
);
|
|
4054
|
+
}
|
|
4055
|
+
function workSummary(run) {
|
|
4056
|
+
const own = run.steps.filter((step) => step.by === null);
|
|
4057
|
+
const inFlight = own.findLast((step) => step.kind === "tool" && step.state === "running");
|
|
4058
|
+
const latest = own.at(-1);
|
|
4059
|
+
const working = run.steps.filter((step) => step.subagent && step.state === "running");
|
|
4060
|
+
const count = `${run.steps.length} ${run.steps.length === 1 ? "step" : "steps"}`;
|
|
4061
|
+
if (inFlight?.subagent) {
|
|
4062
|
+
return `Waiting on ${working.map((step) => step.detail ?? "a subagent").join(", ") || "a subagent"} \xB7 ${count}`;
|
|
4063
|
+
}
|
|
4064
|
+
const doing = inFlight ? inFlight.text : latest ? latest.kind === "message" ? firstLine(latest.text) : latest.text : "Starting";
|
|
4065
|
+
const helping = working.length;
|
|
4066
|
+
return `${doing} \xB7 ${count}${helping ? `, ${helping} ${helping === 1 ? "subagent" : "subagents"} at work` : ""}`;
|
|
4067
|
+
}
|
|
4068
|
+
function waitingLine(begun) {
|
|
4069
|
+
return el(
|
|
4070
|
+
"li",
|
|
4071
|
+
{ class: "assistant__step assistant__step--waiting", "data-state": "running" },
|
|
4072
|
+
el("span", { class: "assistant__step-text" }, begun ? "Thinking\u2026" : "Starting\u2026")
|
|
4073
|
+
);
|
|
4074
|
+
}
|
|
4028
4075
|
var NESTING = 4;
|
|
4029
|
-
|
|
4030
|
-
|
|
4076
|
+
var CALLS_FOLD = 2;
|
|
4077
|
+
function runSteps(run, steps2, by, active, ui2, handlers2, depth = 0) {
|
|
4078
|
+
const own = steps2.map((step, index) => ({ step, index })).filter(({ step }) => step.by === by);
|
|
4079
|
+
const inFlight = own.some(({ step }) => step.state === "running");
|
|
4080
|
+
const items = [];
|
|
4081
|
+
let calls = [];
|
|
4082
|
+
const flush = () => {
|
|
4083
|
+
if (calls.length >= CALLS_FOLD) items.push(callsFold(run, by, calls, ui2, handlers2));
|
|
4084
|
+
else for (const placed of calls) items.push(callLine(placed));
|
|
4085
|
+
calls = [];
|
|
4086
|
+
};
|
|
4087
|
+
for (const placed of own) {
|
|
4088
|
+
const { step, index } = placed;
|
|
4089
|
+
const folds = step.subagent && depth < NESTING;
|
|
4090
|
+
if (step.kind === "tool" && !folds && step.state !== "running") {
|
|
4091
|
+
calls.push(placed);
|
|
4092
|
+
continue;
|
|
4093
|
+
}
|
|
4094
|
+
flush();
|
|
4095
|
+
if (step.kind === "message") items.push(el("li", { key: index, class: "agent-run__said" }, renderMarkdownBlocks(step.text)));
|
|
4096
|
+
else if (folds) items.push(runHandoff(run, steps2, step, ui2, handlers2, depth, index));
|
|
4097
|
+
else items.push(callLine(placed));
|
|
4098
|
+
}
|
|
4099
|
+
flush();
|
|
4031
4100
|
return el(
|
|
4032
4101
|
"ol",
|
|
4033
4102
|
{ class: "assistant__steps agent-run__steps" },
|
|
4034
|
-
own.length ? null : el("li", { class: "assistant__step assistant__step--message" }, "Nothing yet."),
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4103
|
+
own.length || active ? null : el("li", { class: "assistant__step assistant__step--message" }, "Nothing yet."),
|
|
4104
|
+
items,
|
|
4105
|
+
active && !inFlight ? waitingLine(own.length > 0) : null
|
|
4106
|
+
);
|
|
4107
|
+
}
|
|
4108
|
+
function callLine({ step, index }) {
|
|
4109
|
+
return el(
|
|
4110
|
+
"li",
|
|
4111
|
+
{ key: index, class: "assistant__step", "data-state": step.state },
|
|
4112
|
+
el("span", { class: "assistant__step-text" }, step.text),
|
|
4113
|
+
step.detail ? el("code", { class: "assistant__step-detail", title: step.detail }, step.detail) : null
|
|
4114
|
+
);
|
|
4115
|
+
}
|
|
4116
|
+
function callsFold(run, by, calls, ui2, handlers2) {
|
|
4117
|
+
const key = `${run.id}:${by ?? "run"}:calls@${calls[0].index}`;
|
|
4118
|
+
const failed = calls.filter(({ step }) => step.state === "failed").length;
|
|
4119
|
+
const names = [...new Set(calls.map(({ step }) => step.text))];
|
|
4120
|
+
const named = names.length <= 2 ? names.join(", ") : `${names.slice(0, 2).join(", ")} and ${names.length - 2} more`;
|
|
4121
|
+
const summary = `${calls.length} calls${failed ? `, ${failed} failed` : ""} \xB7 ${named}`;
|
|
4122
|
+
return el(
|
|
4123
|
+
"li",
|
|
4124
|
+
{ key, class: "assistant__step agent-run__calls", "data-state": failed ? "failed" : "done" },
|
|
4125
|
+
el(
|
|
4126
|
+
"details",
|
|
4127
|
+
{
|
|
4128
|
+
open: ui2.messageOpen.value.get(key) ?? false,
|
|
4129
|
+
onToggle: (event) => {
|
|
4130
|
+
const isOpen = event.currentTarget.open;
|
|
4131
|
+
handlers2.toggleMessage(key, isOpen ? true : null);
|
|
4132
|
+
}
|
|
4133
|
+
},
|
|
4134
|
+
el("summary", null, summary),
|
|
4135
|
+
el("ol", { class: "assistant__steps agent-run__steps" }, calls.map((placed) => callLine(placed)))
|
|
4136
|
+
)
|
|
4047
4137
|
);
|
|
4048
4138
|
}
|
|
4049
4139
|
function runHandoff(run, steps2, step, ui2, handlers2, depth, index) {
|
|
@@ -4053,8 +4143,7 @@ function runHandoff(run, steps2, step, ui2, handlers2, depth, index) {
|
|
|
4053
4143
|
const calls = own.filter((candidate) => candidate.kind === "tool").length;
|
|
4054
4144
|
const doing = step.state === "running" ? latest ? latest.kind === "message" ? firstLine(latest.text) : latest.text : "starting" : `${calls} ${calls === 1 ? "step" : "steps"}`;
|
|
4055
4145
|
const key = `${run.id}:${id}`;
|
|
4056
|
-
const
|
|
4057
|
-
const open = ui2.messageOpen.value.has(key) ? ui2.messageOpen.value.get(key) : fallback;
|
|
4146
|
+
const open = ui2.messageOpen.value.get(key) ?? false;
|
|
4058
4147
|
return el(
|
|
4059
4148
|
"li",
|
|
4060
4149
|
{ key: index, class: "assistant__step agent-run__handoff", "data-state": step.state },
|
|
@@ -4065,7 +4154,7 @@ function runHandoff(run, steps2, step, ui2, handlers2, depth, index) {
|
|
|
4065
4154
|
// As the message folds do: a toggle back to the default is forgotten.
|
|
4066
4155
|
onToggle: (event) => {
|
|
4067
4156
|
const isOpen = event.currentTarget.open;
|
|
4068
|
-
handlers2.toggleMessage(key, isOpen
|
|
4157
|
+
handlers2.toggleMessage(key, isOpen ? true : null);
|
|
4069
4158
|
}
|
|
4070
4159
|
},
|
|
4071
4160
|
el(
|
|
@@ -4074,7 +4163,7 @@ function runHandoff(run, steps2, step, ui2, handlers2, depth, index) {
|
|
|
4074
4163
|
el("span", { class: "assistant__step-text" }, `Subagent: ${step.detail ?? "unnamed"}`),
|
|
4075
4164
|
el("span", { class: "assistant__step-detail" }, doing)
|
|
4076
4165
|
),
|
|
4077
|
-
runSteps(run, steps2, id, ui2, handlers2, depth + 1)
|
|
4166
|
+
runSteps(run, steps2, id, step.state === "running", ui2, handlers2, depth + 1)
|
|
4078
4167
|
)
|
|
4079
4168
|
);
|
|
4080
4169
|
}
|
|
@@ -4082,14 +4171,6 @@ function sameWords(a4, b4) {
|
|
|
4082
4171
|
return a4.replace(/\s+/g, " ").trim() === b4.replace(/\s+/g, " ").trim();
|
|
4083
4172
|
}
|
|
4084
4173
|
function runStatus(run, now, handlers2) {
|
|
4085
|
-
if (run.status === "running") {
|
|
4086
|
-
return el(
|
|
4087
|
-
"p",
|
|
4088
|
-
{ class: "agent-run__status" },
|
|
4089
|
-
`Writing a new brief, started ${relativeTime(run.startedAt, now)}. `,
|
|
4090
|
-
el("button", { type: "button", class: "button", onClick: () => handlers2.stopAgent() }, "Stop")
|
|
4091
|
-
);
|
|
4092
|
-
}
|
|
4093
4174
|
const took = runDuration(run);
|
|
4094
4175
|
if (run.status === "done") {
|
|
4095
4176
|
return el("p", { class: "agent-run__status" }, `Finished at ${formatTime(run.endedAt)}${took ? `, in ${took}` : ""}.`);
|
|
@@ -4961,6 +5042,11 @@ function SettingsView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
4961
5042
|
}, []);
|
|
4962
5043
|
const page = ui2.settings.value;
|
|
4963
5044
|
const draft = ui2.settingsDraft.value;
|
|
5045
|
+
const sections = [
|
|
5046
|
+
{ id: "objective", title: "Your objective" },
|
|
5047
|
+
{ id: "sources", title: "Where the agent looks" },
|
|
5048
|
+
...page?.groups ?? []
|
|
5049
|
+
];
|
|
4964
5050
|
return el(
|
|
4965
5051
|
"div",
|
|
4966
5052
|
{ class: "settings" },
|
|
@@ -4970,6 +5056,7 @@ function SettingsView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
4970
5056
|
el("h2", { class: "settings__title" }, "Settings"),
|
|
4971
5057
|
el("button", { type: "button", class: "button", onClick: () => handlers2.closeSettings() }, `Back to ${VIEW_NAME[ui2.settingsReturn.value]}`)
|
|
4972
5058
|
),
|
|
5059
|
+
k(SectionIndex, { sections }),
|
|
4973
5060
|
page?.error ? el("p", { class: "settings__problem", role: "alert" }, page.error) : null,
|
|
4974
5061
|
objectiveCard(state2, ui2, handlers2),
|
|
4975
5062
|
sourcesCard(state2, ui2, handlers2),
|
|
@@ -4994,6 +5081,29 @@ function SettingsView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
4994
5081
|
saveBar(ui2, handlers2)
|
|
4995
5082
|
);
|
|
4996
5083
|
}
|
|
5084
|
+
function SectionIndex({ sections }) {
|
|
5085
|
+
const [current, setCurrent] = d2(sections[0]?.id ?? null);
|
|
5086
|
+
h2(() => {
|
|
5087
|
+
const look = () => {
|
|
5088
|
+
let seen = sections[0]?.id ?? null;
|
|
5089
|
+
for (const section2 of sections) {
|
|
5090
|
+
const card = document.getElementById(`settings-${section2.id}`);
|
|
5091
|
+
if (card && card.getBoundingClientRect().top <= 120) seen = section2.id;
|
|
5092
|
+
}
|
|
5093
|
+
setCurrent(seen);
|
|
5094
|
+
};
|
|
5095
|
+
look();
|
|
5096
|
+
window.addEventListener("scroll", look, { passive: true });
|
|
5097
|
+
return () => window.removeEventListener("scroll", look);
|
|
5098
|
+
}, [sections.map((section2) => section2.id).join(" ")]);
|
|
5099
|
+
return el(
|
|
5100
|
+
"nav",
|
|
5101
|
+
{ class: "settings__nav", "aria-label": "Settings sections" },
|
|
5102
|
+
sections.map(
|
|
5103
|
+
(section2) => el("a", { key: section2.id, href: `#settings-${section2.id}`, "aria-current": section2.id === current ? "true" : void 0 }, section2.title)
|
|
5104
|
+
)
|
|
5105
|
+
);
|
|
5106
|
+
}
|
|
4997
5107
|
function objectiveCard(state2, ui2, handlers2) {
|
|
4998
5108
|
const focus = state2.focus;
|
|
4999
5109
|
return el(
|
|
@@ -5122,6 +5232,7 @@ function shownValue(setting, draft) {
|
|
|
5122
5232
|
if (draft.has(setting.key)) return draft.get(setting.key) ?? "";
|
|
5123
5233
|
return setting.source === "default" ? "" : setting.value ?? "";
|
|
5124
5234
|
}
|
|
5235
|
+
var SEGMENTS = 4;
|
|
5125
5236
|
function flagValue(raw) {
|
|
5126
5237
|
if (raw === "") return "";
|
|
5127
5238
|
return ["off", "false", "0", "no"].includes(raw.trim().toLowerCase()) ? "off" : "on";
|
|
@@ -5138,7 +5249,30 @@ function settingField(setting, draft, handlers2) {
|
|
|
5138
5249
|
if (setting.kind === "choice" || setting.kind === "flag") {
|
|
5139
5250
|
const choices = setting.kind === "flag" ? [{ value: "on", label: "On" }, { value: "off", label: "Off" }] : setting.choices ?? [];
|
|
5140
5251
|
const current = setting.kind === "flag" ? flagValue(value) : value;
|
|
5141
|
-
|
|
5252
|
+
const fallback = choices.find((choice) => choice.label.toLowerCase() === setting.fallback.toLowerCase());
|
|
5253
|
+
input = choices.length <= SEGMENTS && fallback ? el(
|
|
5254
|
+
"div",
|
|
5255
|
+
{ class: "segmented", id, role: "radiogroup", "aria-label": setting.label, "aria-disabled": String(locked) },
|
|
5256
|
+
choices.map((choice) => {
|
|
5257
|
+
const isDefault = choice.value === fallback.value;
|
|
5258
|
+
const checked = current === "" ? isDefault : current === choice.value;
|
|
5259
|
+
return el(
|
|
5260
|
+
"button",
|
|
5261
|
+
{
|
|
5262
|
+
type: "button",
|
|
5263
|
+
key: choice.value,
|
|
5264
|
+
role: "radio",
|
|
5265
|
+
class: "segmented__option",
|
|
5266
|
+
"aria-checked": String(checked),
|
|
5267
|
+
disabled: locked,
|
|
5268
|
+
title: isDefault ? "The default" : void 0,
|
|
5269
|
+
onClick: () => handlers2.editSetting(setting.key, isDefault ? null : choice.value)
|
|
5270
|
+
},
|
|
5271
|
+
choice.label,
|
|
5272
|
+
checked && current === "" ? el("small", null, "default") : null
|
|
5273
|
+
);
|
|
5274
|
+
})
|
|
5275
|
+
) : el(
|
|
5142
5276
|
"select",
|
|
5143
5277
|
{ class: "field__input", id, disabled: locked, value: current, onChange },
|
|
5144
5278
|
el("option", { value: "" }, `Default: ${setting.fallback}`),
|
|
@@ -5291,7 +5425,10 @@ function FirstBriefCard({ state: state2, handlers: handlers2 }) {
|
|
|
5291
5425
|
// client/today.ts
|
|
5292
5426
|
function TodayView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
5293
5427
|
if (state2.setup?.needed) return [k(FirstBriefCard, { state: state2, handlers: handlers2 })];
|
|
5294
|
-
return [
|
|
5428
|
+
return [
|
|
5429
|
+
el("div", { class: "today__masthead" }, renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2)),
|
|
5430
|
+
renderSections(state2, ui2, handlers2)
|
|
5431
|
+
];
|
|
5295
5432
|
}
|
|
5296
5433
|
function renderObjective(state2, handlers2 = null) {
|
|
5297
5434
|
const focus = state2.focus;
|
|
@@ -5413,8 +5550,9 @@ function App({ ui: ui2, handlers: handlers2 }) {
|
|
|
5413
5550
|
if (current?.onboarding) return [k(OnboardingView, { state: current, ui: ui2, handlers: handlers2 }), k(Toast, { ui: ui2, handlers: handlers2 })];
|
|
5414
5551
|
return [
|
|
5415
5552
|
el("a", { class: "skip-link", href: "#main" }, "Skip to content"),
|
|
5416
|
-
|
|
5417
|
-
|
|
5553
|
+
// The band: the masthead's navy runs behind both, and on Today on behind
|
|
5554
|
+
// the objective below (see `.band` in the stylesheet).
|
|
5555
|
+
el("div", { class: "band" }, k(Header, { state: current, ui: ui2, handlers: handlers2 }), k(Tabs, { state: current, ui: ui2, handlers: handlers2 })),
|
|
5418
5556
|
el("div", { id: "banners", class: "banners" }, current ? renderBanners(current, ui2, handlers2) : null),
|
|
5419
5557
|
el(
|
|
5420
5558
|
"main",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
|
|
5
|
+
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|