daily-focus 1.2.0-dev.8 → 2.0.0-dev.14
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 +25 -7
- package/SETUP.md +156 -105
- package/dist/{audit-DNTFZOHH.js → audit-AQUBVWPA.js} +4 -3
- package/dist/{chunk-KZNY45XW.js → chunk-72NUDSOV.js} +126 -119
- package/dist/{chunk-GQ3NCWRT.js → chunk-ITUK7XKS.js} +6 -19
- package/dist/{chunk-SX5FPS3A.js → chunk-JTMBBUMD.js} +454 -22
- package/dist/chunk-QG4XXKBP.js +18 -0
- package/dist/chunk-U6EPOXEW.js +570 -0
- package/dist/{chunk-P3E2B2SD.js → chunk-WPXIQPYX.js} +1 -1
- package/dist/{chunk-6SMUSPZG.js → chunk-YPGKXTBX.js} +2 -2
- package/dist/cli.js +19 -12
- package/dist/{demo-6Q22AAC7.js → demo-KE6WDWMA.js} +194 -40
- package/dist/{init-XDMAXU2S.js → init-5KJVHWJU.js} +41 -6
- package/dist/{seed-TWT7EDXJ.js → seed-T4VEY6DT.js} +4 -3
- package/dist/{server-PIYOIUKJ.js → server-FFRNYEAO.js} +2000 -743
- package/dist/{service-ZUHI3VKH.js → service-LQ46YRCZ.js} +7 -6
- package/package.json +2 -2
- package/prompts/README.md +20 -4
- package/prompts/assistant.md +14 -3
- package/prompts/morning-brief-personal.md +37 -0
- package/prompts/morning-brief-work.md +37 -0
- package/prompts/onboarding.md +41 -0
- package/public/app.js +976 -381
- package/public/style.css +821 -100
- package/dist/chunk-V2UN3JYL.js +0 -140
package/public/app.js
CHANGED
|
@@ -69,9 +69,9 @@ function H() {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
function L(n3, l5, u4, t4, i4, r4, o4, e4, f4, c4, a4) {
|
|
72
|
-
var s4, h5, p5, v4,
|
|
73
|
-
for (f4 = T(u4, l5, g4, f4, m4), s4 = 0; s4 < m4; s4++) null != (p5 = u4.__k[s4]) && (h5 = -1 != p5.__i && g4[p5.__i] || d, p5.__i = s4, _5 = q(n3, p5, h5, i4, r4, o4, e4, f4, c4, a4), v4 = p5.__e, p5.ref && h5.ref != p5.ref && (h5.ref && J(h5.ref, null, p5), a4.push(p5.ref, p5.__c || v4, p5)), null ==
|
|
74
|
-
return u4.__e =
|
|
72
|
+
var s4, h5, p5, v4, y5, _5, g4 = t4 && t4.__k || w, m4 = l5.length;
|
|
73
|
+
for (f4 = T(u4, l5, g4, f4, m4), s4 = 0; s4 < m4; s4++) null != (p5 = u4.__k[s4]) && (h5 = -1 != p5.__i && g4[p5.__i] || d, p5.__i = s4, _5 = q(n3, p5, h5, i4, r4, o4, e4, f4, c4, a4), v4 = p5.__e, p5.ref && h5.ref != p5.ref && (h5.ref && J(h5.ref, null, p5), a4.push(p5.ref, p5.__c || v4, p5)), null == y5 && null != v4 && (y5 = v4), 4 & p5.__u ? (f4 = j(p5, f4, n3), h5.__e && (h5.__e = null)) : "function" == typeof p5.type && void 0 !== _5 ? f4 = _5 : v4 && (f4 = v4.nextSibling), p5.__u &= -7);
|
|
74
|
+
return u4.__e = y5, f4;
|
|
75
75
|
}
|
|
76
76
|
function T(n3, l5, u4, t4, i4) {
|
|
77
77
|
var r4, o4, e4, f4, c4, a4 = u4.length, s4 = a4, h5 = 0;
|
|
@@ -131,29 +131,29 @@ function V(n3) {
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
function q(n3, u4, t4, i4, r4, o4, e4, f4, c4, a4) {
|
|
134
|
-
var s4, h5, p5, v4,
|
|
134
|
+
var s4, h5, p5, v4, y5, d5, _5, k3, x4, M, I2, P2, A3, H2, T4, j4, F2 = u4.type;
|
|
135
135
|
if (void 0 !== u4.constructor) return null;
|
|
136
136
|
128 & t4.__u && (c4 = !!(32 & t4.__u), o4 = [f4 = u4.__e = t4.__e]), (s4 = l.__b) && s4(u4);
|
|
137
137
|
n: if ("function" == typeof F2) {
|
|
138
138
|
h5 = e4.length;
|
|
139
139
|
try {
|
|
140
|
-
if (x4 = u4.props, M = F2.prototype && F2.prototype.render, I2 = (s4 = F2.contextType) && i4[s4.__c], P2 = s4 ? I2 ? I2.props.value : s4.__ : i4, t4.__c ? k3 = (p5 = u4.__c = t4.__c).__ = p5.__E : (M ? u4.__c = p5 = new F2(x4, P2) : (u4.__c = p5 = new C(x4, P2), p5.constructor = F2, p5.render = Q), I2 && I2.sub(p5), p5.state || (p5.state = {}), p5.__n = i4, v4 = p5.__d = true, p5.__h = [], p5._sb = []), M && null == p5.__s && (p5.__s = p5.state), M && null != F2.getDerivedStateFromProps && (p5.__s == p5.state && (p5.__s = m({}, p5.__s)), m(p5.__s, F2.getDerivedStateFromProps(x4, p5.__s))),
|
|
140
|
+
if (x4 = u4.props, M = F2.prototype && F2.prototype.render, I2 = (s4 = F2.contextType) && i4[s4.__c], P2 = s4 ? I2 ? I2.props.value : s4.__ : i4, t4.__c ? k3 = (p5 = u4.__c = t4.__c).__ = p5.__E : (M ? u4.__c = p5 = new F2(x4, P2) : (u4.__c = p5 = new C(x4, P2), p5.constructor = F2, p5.render = Q), I2 && I2.sub(p5), p5.state || (p5.state = {}), p5.__n = i4, v4 = p5.__d = true, p5.__h = [], p5._sb = []), M && null == p5.__s && (p5.__s = p5.state), M && null != F2.getDerivedStateFromProps && (p5.__s == p5.state && (p5.__s = m({}, p5.__s)), m(p5.__s, F2.getDerivedStateFromProps(x4, p5.__s))), y5 = p5.props, d5 = p5.state, p5.__v = u4, v4) M && null == F2.getDerivedStateFromProps && null != p5.componentWillMount && p5.componentWillMount(), M && null != p5.componentDidMount && p5.__h.push(p5.componentDidMount);
|
|
141
141
|
else {
|
|
142
|
-
if (M && null == F2.getDerivedStateFromProps && x4 !==
|
|
142
|
+
if (M && null == F2.getDerivedStateFromProps && x4 !== y5 && null != p5.componentWillReceiveProps && p5.componentWillReceiveProps(x4, P2), u4.__v == t4.__v || !p5.__e && null != p5.shouldComponentUpdate && false === p5.shouldComponentUpdate(x4, p5.__s, P2)) {
|
|
143
143
|
u4.__v != t4.__v && (p5.props = x4, p5.state = p5.__s, p5.__d = false), u4.__e = t4.__e, u4.__k = t4.__k, u4.__k.some(function(n4) {
|
|
144
144
|
n4 && (n4.__ = u4);
|
|
145
145
|
}), w.push.apply(p5.__h, p5._sb), p5._sb = [], p5.__h.length && e4.push(p5), f4 = $(t4);
|
|
146
146
|
break n;
|
|
147
147
|
}
|
|
148
148
|
null != p5.componentWillUpdate && p5.componentWillUpdate(x4, p5.__s, P2), M && null != p5.componentDidUpdate && p5.__h.push(function() {
|
|
149
|
-
p5.componentDidUpdate(
|
|
149
|
+
p5.componentDidUpdate(y5, d5, _5);
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
if (p5.context = P2, p5.props = x4, p5.__P = n3, p5.__e = false, A3 = l.__r, H2 = 0, M) p5.state = p5.__s, p5.__d = false, A3 && A3(u4), s4 = p5.render(p5.props, p5.state, p5.context), w.push.apply(p5.__h, p5._sb), p5._sb = [];
|
|
153
153
|
else do {
|
|
154
154
|
p5.__d = false, A3 && A3(u4), s4 = p5.render(p5.props, p5.state, p5.context), p5.state = p5.__s;
|
|
155
155
|
} while (p5.__d && ++H2 < 25);
|
|
156
|
-
p5.state = p5.__s, null != p5.getChildContext && (i4 = m(m({}, i4), p5.getChildContext())), M && !v4 && null != p5.getSnapshotBeforeUpdate && (_5 = p5.getSnapshotBeforeUpdate(
|
|
156
|
+
p5.state = p5.__s, null != p5.getChildContext && (i4 = m(m({}, i4), p5.getChildContext())), M && !v4 && null != p5.getSnapshotBeforeUpdate && (_5 = p5.getSnapshotBeforeUpdate(y5, d5)), T4 = null != s4 && s4.type === S && null == s4.key ? E(s4.props.children) : s4, f4 = L(n3, g(T4) ? T4 : [T4], u4, t4, i4, r4, o4, e4, f4, c4, a4), p5.base = u4.__e, u4.__u &= -161, p5.__h.length && e4.push(p5), k3 && (p5.__E = p5.__ = null);
|
|
157
157
|
} catch (n4) {
|
|
158
158
|
if (e4.length = h5, u4.__v = null, c4 || null != o4) {
|
|
159
159
|
if (n4.then) {
|
|
@@ -185,10 +185,10 @@ function E(n3) {
|
|
|
185
185
|
return "object" != typeof n3 || null == n3 || n3.__b > 0 ? n3 : g(n3) ? n3.map(E) : void 0 !== n3.constructor ? null : m({}, n3);
|
|
186
186
|
}
|
|
187
187
|
function G(u4, t4, i4, r4, o4, e4, f4, c4, a4) {
|
|
188
|
-
var s4, h5, p5, v4,
|
|
188
|
+
var s4, h5, p5, v4, y5, w5, _5, m4 = i4.props || d, k3 = t4.props, x4 = t4.type;
|
|
189
189
|
if ("svg" == x4 ? o4 = "http://www.w3.org/2000/svg" : "math" == x4 ? o4 = "http://www.w3.org/1998/Math/MathML" : o4 || (o4 = "http://www.w3.org/1999/xhtml"), null != e4) {
|
|
190
|
-
for (s4 = 0; s4 < e4.length; s4++) if ((
|
|
191
|
-
u4 =
|
|
190
|
+
for (s4 = 0; s4 < e4.length; s4++) if ((y5 = e4[s4]) && "setAttribute" in y5 == !!x4 && (x4 ? y5.localName == x4 : 3 == y5.nodeType)) {
|
|
191
|
+
u4 = y5, e4[s4] = null;
|
|
192
192
|
break;
|
|
193
193
|
}
|
|
194
194
|
}
|
|
@@ -198,9 +198,9 @@ function G(u4, t4, i4, r4, o4, e4, f4, c4, a4) {
|
|
|
198
198
|
}
|
|
199
199
|
if (null == x4) m4 === k3 || c4 && u4.data == k3 || (u4.data = k3);
|
|
200
200
|
else {
|
|
201
|
-
if (e4 = "textarea" == x4 && null != k3.defaultValue ? null : e4 && n.call(u4.childNodes), !c4 && null != e4) for (m4 = {}, s4 = 0; s4 < u4.attributes.length; s4++) m4[(
|
|
202
|
-
for (s4 in m4)
|
|
203
|
-
for (s4 in k3)
|
|
201
|
+
if (e4 = "textarea" == x4 && null != k3.defaultValue ? null : e4 && n.call(u4.childNodes), !c4 && null != e4) for (m4 = {}, s4 = 0; s4 < u4.attributes.length; s4++) m4[(y5 = u4.attributes[s4]).name] = y5.value;
|
|
202
|
+
for (s4 in m4) y5 = m4[s4], "dangerouslySetInnerHTML" == s4 ? p5 = y5 : "children" == s4 || s4 in k3 || "value" == s4 && "defaultValue" in k3 || "checked" == s4 && "defaultChecked" in k3 || N(u4, s4, null, y5, o4);
|
|
203
|
+
for (s4 in k3) y5 = k3[s4], "children" == s4 ? v4 = y5 : "dangerouslySetInnerHTML" == s4 ? h5 = y5 : "value" == s4 ? w5 = y5 : "checked" == s4 ? _5 = y5 : c4 && "function" != typeof y5 || m4[s4] === y5 || N(u4, s4, y5, m4[s4], o4);
|
|
204
204
|
if (h5) c4 || p5 && (h5.__html == p5.__html || h5.__html == u4.innerHTML) || (u4.innerHTML = h5.__html), t4.__k = [];
|
|
205
205
|
else if (p5 && (u4.innerHTML = ""), L("template" == t4.type ? u4.content : u4, g(v4) ? v4 : [v4], t4, i4, r4, "foreignObject" == x4 ? "http://www.w3.org/1999/xhtml" : o4, e4, f4, e4 ? e4[0] : i4.__k && $(i4, 0), c4, a4), null != e4) for (s4 = e4.length; s4--; ) b(e4[s4]);
|
|
206
206
|
c4 && "textarea" != x4 || (s4 = "value", "progress" == x4 && null == w5 ? u4.removeAttribute("value") : null != w5 && (w5 !== u4[s4] || "progress" == x4 && !w5 || "option" == x4 && w5 != m4[s4]) && N(u4, s4, w5, m4[s4], o4), s4 = "checked", null != _5 && _5 != u4[s4] && N(u4, s4, _5, m4[s4], o4));
|
|
@@ -274,6 +274,42 @@ function s2(n3, t4) {
|
|
|
274
274
|
var u4 = r2.__H || (r2.__H = { __: [], __h: [] });
|
|
275
275
|
return n3 >= u4.__.length && u4.__.push({}), u4.__[n3];
|
|
276
276
|
}
|
|
277
|
+
function d2(n3) {
|
|
278
|
+
return o2 = 1, y2(D2, n3);
|
|
279
|
+
}
|
|
280
|
+
function y2(n3, u4, i4) {
|
|
281
|
+
var o4 = s2(t2++, 2);
|
|
282
|
+
if (o4.t = n3, !o4.__c && (o4.__ = [i4 ? i4(u4) : D2(void 0, u4), function(n4) {
|
|
283
|
+
var t4 = o4.__N ? o4.__N[0] : o4.__[0], r4 = o4.t(t4, n4);
|
|
284
|
+
t4 !== r4 && (o4.__N = [r4, o4.__[1]], o4.__c.setState({}));
|
|
285
|
+
}], o4.__c = r2, !r2.__f)) {
|
|
286
|
+
var f4 = function(n4, t4, r4) {
|
|
287
|
+
if (!o4.__c.__H) return true;
|
|
288
|
+
var u5 = false, i5 = o4.__c.props !== n4;
|
|
289
|
+
if (o4.__c.__H.__.some(function(n5) {
|
|
290
|
+
if (n5.__N) {
|
|
291
|
+
u5 = true;
|
|
292
|
+
var t5 = n5.__[0];
|
|
293
|
+
n5.__ = n5.__N, n5.__N = void 0, t5 !== n5.__[0] && (i5 = true);
|
|
294
|
+
}
|
|
295
|
+
}), c4) {
|
|
296
|
+
var f5 = c4.call(this, n4, t4, r4);
|
|
297
|
+
return u5 ? f5 || i5 : f5;
|
|
298
|
+
}
|
|
299
|
+
return !u5 || i5;
|
|
300
|
+
};
|
|
301
|
+
r2.__f = true;
|
|
302
|
+
var c4 = r2.shouldComponentUpdate, e4 = r2.componentWillUpdate;
|
|
303
|
+
r2.componentWillUpdate = function(n4, t4, r4) {
|
|
304
|
+
if (this.__e) {
|
|
305
|
+
var u5 = c4;
|
|
306
|
+
c4 = void 0, f4(n4, t4, r4), c4 = u5;
|
|
307
|
+
}
|
|
308
|
+
e4 && e4.call(this, n4, t4, r4);
|
|
309
|
+
}, r2.shouldComponentUpdate = f4;
|
|
310
|
+
}
|
|
311
|
+
return o4.__N || o4.__;
|
|
312
|
+
}
|
|
277
313
|
function h2(n3, u4) {
|
|
278
314
|
var i4 = s2(t2++, 3);
|
|
279
315
|
!c2.__s && C2(i4.__H, u4) && (i4.__ = n3, i4.u = u4, r2.__H.__h.push(i4));
|
|
@@ -360,6 +396,9 @@ function C2(n3, t4) {
|
|
|
360
396
|
return t5 !== n3[r4];
|
|
361
397
|
});
|
|
362
398
|
}
|
|
399
|
+
function D2(n3, t4) {
|
|
400
|
+
return "function" == typeof t4 ? t4(n3) : t4;
|
|
401
|
+
}
|
|
363
402
|
|
|
364
403
|
// node_modules/@preact/signals-core/dist/signals-core.module.js
|
|
365
404
|
var i3 = /* @__PURE__ */ Symbol.for("preact-signals");
|
|
@@ -430,7 +469,7 @@ var s3 = 0;
|
|
|
430
469
|
var u3 = 0;
|
|
431
470
|
var e3 = 0;
|
|
432
471
|
var c3 = void 0;
|
|
433
|
-
var
|
|
472
|
+
var d3 = 0;
|
|
434
473
|
function a3(i4) {
|
|
435
474
|
if (void 0 !== o3) {
|
|
436
475
|
var t4 = i4.n;
|
|
@@ -543,7 +582,7 @@ Object.defineProperty(l3.prototype, "value", { get: function() {
|
|
|
543
582
|
})(this);
|
|
544
583
|
this.v = i4;
|
|
545
584
|
this.i++;
|
|
546
|
-
|
|
585
|
+
d3++;
|
|
547
586
|
v3++;
|
|
548
587
|
try {
|
|
549
588
|
for (var n3 = this.t; void 0 !== n3; n3 = n3.x) n3.t.N();
|
|
@@ -552,7 +591,7 @@ Object.defineProperty(l3.prototype, "value", { get: function() {
|
|
|
552
591
|
}
|
|
553
592
|
}
|
|
554
593
|
} });
|
|
555
|
-
function
|
|
594
|
+
function y3(i4, t4) {
|
|
556
595
|
return new l3(i4, t4);
|
|
557
596
|
}
|
|
558
597
|
function w3(i4) {
|
|
@@ -590,7 +629,7 @@ function p3(i4, t4) {
|
|
|
590
629
|
l3.call(this, void 0, t4);
|
|
591
630
|
this.x = i4;
|
|
592
631
|
this.s = void 0;
|
|
593
|
-
this.g =
|
|
632
|
+
this.g = d3 - 1;
|
|
594
633
|
this.f = 4;
|
|
595
634
|
}
|
|
596
635
|
p3.prototype = new l3();
|
|
@@ -599,8 +638,8 @@ p3.prototype.h = function() {
|
|
|
599
638
|
if (1 & this.f) return false;
|
|
600
639
|
if (32 == (36 & this.f)) return true;
|
|
601
640
|
this.f &= -5;
|
|
602
|
-
if (this.g ===
|
|
603
|
-
this.g =
|
|
641
|
+
if (this.g === d3) return true;
|
|
642
|
+
this.g = d3;
|
|
604
643
|
this.f |= 1;
|
|
605
644
|
if (this.i > 0 && !w3(this)) {
|
|
606
645
|
this.f &= -2;
|
|
@@ -754,7 +793,7 @@ function j3(i4, t4) {
|
|
|
754
793
|
// node_modules/@preact/signals/dist/signals.module.js
|
|
755
794
|
var l4;
|
|
756
795
|
var h4;
|
|
757
|
-
var
|
|
796
|
+
var d4;
|
|
758
797
|
var p4 = "undefined" != typeof window && !!window.__PREACT_SIGNALS_DEVTOOLS__;
|
|
759
798
|
var _4 = [];
|
|
760
799
|
j3(function() {
|
|
@@ -765,14 +804,14 @@ function g3(i4, r4) {
|
|
|
765
804
|
});
|
|
766
805
|
}
|
|
767
806
|
function b3(i4) {
|
|
768
|
-
if (
|
|
769
|
-
var n3 =
|
|
770
|
-
|
|
807
|
+
if (d4) {
|
|
808
|
+
var n3 = d4;
|
|
809
|
+
d4 = void 0;
|
|
771
810
|
n3();
|
|
772
811
|
}
|
|
773
|
-
|
|
812
|
+
d4 = i4 && i4.S();
|
|
774
813
|
}
|
|
775
|
-
function
|
|
814
|
+
function y4(i4) {
|
|
776
815
|
var n3 = this, t4 = i4.data, f4 = useSignal(t4);
|
|
777
816
|
f4.name = "ReactiveDom";
|
|
778
817
|
f4.value = t4;
|
|
@@ -802,8 +841,8 @@ function y3(i4) {
|
|
|
802
841
|
}, []), a4 = e4[0], v4 = e4[1];
|
|
803
842
|
return a4.value ? v4.peek() : v4.value;
|
|
804
843
|
}
|
|
805
|
-
|
|
806
|
-
Object.defineProperties(l3.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value:
|
|
844
|
+
y4.displayName = "ReactiveTextNode";
|
|
845
|
+
Object.defineProperties(l3.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value: y4 }, props: { configurable: true, get: function() {
|
|
807
846
|
var i4 = this;
|
|
808
847
|
return { data: { get value() {
|
|
809
848
|
return i4.value;
|
|
@@ -887,7 +926,7 @@ g3("diffed", function(i4, n3) {
|
|
|
887
926
|
i4(n3);
|
|
888
927
|
});
|
|
889
928
|
function w4(i4, n3, r4, t4) {
|
|
890
|
-
var o4 = n3 in i4 && void 0 === i4.ownerSVGElement, f4 =
|
|
929
|
+
var o4 = n3 in i4 && void 0 === i4.ownerSVGElement, f4 = y3(r4);
|
|
891
930
|
return { o: function(i5, n4) {
|
|
892
931
|
f4.value = i5;
|
|
893
932
|
t4 = n4;
|
|
@@ -955,7 +994,7 @@ C.prototype.shouldComponentUpdate = function(i4, n3) {
|
|
|
955
994
|
};
|
|
956
995
|
function useSignal(i4, n3) {
|
|
957
996
|
return T2(function() {
|
|
958
|
-
return
|
|
997
|
+
return y3(i4, n3);
|
|
959
998
|
}, []);
|
|
960
999
|
}
|
|
961
1000
|
var q2 = function(i4) {
|
|
@@ -1071,6 +1110,24 @@ function postObjective(objective, blocker) {
|
|
|
1071
1110
|
function postListCalendars() {
|
|
1072
1111
|
return postJson("/api/calendars/list", {});
|
|
1073
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
|
+
}
|
|
1074
1131
|
|
|
1075
1132
|
// client/state.ts
|
|
1076
1133
|
var VIEWS = ["today", "board", "tickets", "settings"];
|
|
@@ -1079,33 +1136,40 @@ var PANEL_PREFIX = "panel:";
|
|
|
1079
1136
|
var VIEW_KEY = "daily-focus:view";
|
|
1080
1137
|
var AGENT_RUN_SEEN_KEY = "daily-focus:agent-run-seen";
|
|
1081
1138
|
var DETAIL_KEY = "daily-focus:detail-for";
|
|
1082
|
-
var state =
|
|
1139
|
+
var state = y3(null);
|
|
1083
1140
|
function createUi(initial = {}) {
|
|
1084
1141
|
return {
|
|
1085
|
-
selectedId:
|
|
1086
|
-
clickedId:
|
|
1087
|
-
pending:
|
|
1088
|
-
menuFor:
|
|
1089
|
-
statusFor:
|
|
1090
|
-
detailFor:
|
|
1091
|
-
noteDraft:
|
|
1092
|
-
assistantDraft:
|
|
1093
|
-
focusField:
|
|
1094
|
-
openDrawers:
|
|
1095
|
-
ticketMode:
|
|
1096
|
-
agentReportSeen:
|
|
1097
|
-
messageOpen:
|
|
1098
|
-
view:
|
|
1099
|
-
theme:
|
|
1100
|
-
toast:
|
|
1101
|
-
connectionError:
|
|
1102
|
-
settingsReturn:
|
|
1103
|
-
settings:
|
|
1104
|
-
settingsDraft:
|
|
1105
|
-
settingsError:
|
|
1106
|
-
settingsSaving:
|
|
1107
|
-
texts:
|
|
1108
|
-
calendars:
|
|
1142
|
+
selectedId: y3(initial.selectedId ?? null),
|
|
1143
|
+
clickedId: y3(initial.clickedId ?? null),
|
|
1144
|
+
pending: y3(initial.pending ?? /* @__PURE__ */ new Set()),
|
|
1145
|
+
menuFor: y3(initial.menuFor ?? null),
|
|
1146
|
+
statusFor: y3(initial.statusFor ?? null),
|
|
1147
|
+
detailFor: y3(initial.detailFor ?? null),
|
|
1148
|
+
noteDraft: y3(initial.noteDraft ?? ""),
|
|
1149
|
+
assistantDraft: y3(initial.assistantDraft ?? ""),
|
|
1150
|
+
focusField: y3(initial.focusField ?? null),
|
|
1151
|
+
openDrawers: y3(initial.openDrawers ?? /* @__PURE__ */ new Set()),
|
|
1152
|
+
ticketMode: y3(initial.ticketMode ?? "sync"),
|
|
1153
|
+
agentReportSeen: y3(initial.agentReportSeen ?? null),
|
|
1154
|
+
messageOpen: y3(initial.messageOpen ?? /* @__PURE__ */ new Map()),
|
|
1155
|
+
view: y3(initial.view ?? "today"),
|
|
1156
|
+
theme: y3(initial.theme ?? "system"),
|
|
1157
|
+
toast: y3(initial.toast ?? null),
|
|
1158
|
+
connectionError: y3(initial.connectionError ?? null),
|
|
1159
|
+
settingsReturn: y3(initial.settingsReturn ?? "today"),
|
|
1160
|
+
settings: y3(initial.settings ?? null),
|
|
1161
|
+
settingsDraft: y3(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
|
|
1162
|
+
settingsError: y3(initial.settingsError ?? null),
|
|
1163
|
+
settingsSaving: y3(initial.settingsSaving ?? false),
|
|
1164
|
+
texts: y3(initial.texts ?? {}),
|
|
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)
|
|
1109
1173
|
};
|
|
1110
1174
|
}
|
|
1111
1175
|
function isView(value) {
|
|
@@ -1501,6 +1565,27 @@ async function stopAssistant(id) {
|
|
|
1501
1565
|
showToast(`Could not stop: ${err.message}`);
|
|
1502
1566
|
}
|
|
1503
1567
|
}
|
|
1568
|
+
async function copy(text) {
|
|
1569
|
+
try {
|
|
1570
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
1571
|
+
await navigator.clipboard.writeText(text);
|
|
1572
|
+
} else {
|
|
1573
|
+
const field = document.createElement("textarea");
|
|
1574
|
+
field.value = text;
|
|
1575
|
+
field.setAttribute("readonly", "");
|
|
1576
|
+
field.style.position = "fixed";
|
|
1577
|
+
field.style.opacity = "0";
|
|
1578
|
+
document.body.append(field);
|
|
1579
|
+
field.select();
|
|
1580
|
+
const copied = document.execCommand("copy");
|
|
1581
|
+
field.remove();
|
|
1582
|
+
if (!copied) throw new Error("the browser would not copy");
|
|
1583
|
+
}
|
|
1584
|
+
showToast("Copied");
|
|
1585
|
+
} catch (err) {
|
|
1586
|
+
showToast(`Could not copy: ${err.message}`);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1504
1589
|
async function runAgent() {
|
|
1505
1590
|
if (!state.value?.agentRun?.enabled || state.value.agentRun.last?.status === "running") return;
|
|
1506
1591
|
const ok = window.confirm(
|
|
@@ -1606,7 +1691,7 @@ async function loadSettings() {
|
|
|
1606
1691
|
showToast(`Could not load the settings: ${err.message}`);
|
|
1607
1692
|
}
|
|
1608
1693
|
}
|
|
1609
|
-
async function sendSettings(values
|
|
1694
|
+
async function sendSettings(values) {
|
|
1610
1695
|
if (ui.settingsSaving.value) return;
|
|
1611
1696
|
n2(() => {
|
|
1612
1697
|
ui.settingsSaving.value = true;
|
|
@@ -1616,17 +1701,14 @@ async function sendSettings(values, fromDraft) {
|
|
|
1616
1701
|
const page = await postSettings(values);
|
|
1617
1702
|
n2(() => {
|
|
1618
1703
|
ui.settings.value = page;
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
ui.settingsDraft.value = draft;
|
|
1623
|
-
}
|
|
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;
|
|
1624
1707
|
});
|
|
1625
1708
|
restartToast(page);
|
|
1626
1709
|
if (page.restart === "waiting") void followRestart();
|
|
1627
1710
|
} catch (err) {
|
|
1628
|
-
|
|
1629
|
-
else showToast(`Could not save: ${err.message}`);
|
|
1711
|
+
ui.settingsError.value = err.message;
|
|
1630
1712
|
} finally {
|
|
1631
1713
|
ui.settingsSaving.value = false;
|
|
1632
1714
|
}
|
|
@@ -1704,6 +1786,60 @@ async function listCalendars() {
|
|
|
1704
1786
|
ui.calendars.value = err.message;
|
|
1705
1787
|
}
|
|
1706
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
|
+
}
|
|
1707
1843
|
function openSettings(section2 = null) {
|
|
1708
1844
|
if (ui.view.value !== "settings") {
|
|
1709
1845
|
try {
|
|
@@ -1808,6 +1944,7 @@ var handlers = {
|
|
|
1808
1944
|
refreshCalendar: () => void refreshCalendar(),
|
|
1809
1945
|
ask: (id, body) => void ask(id, body),
|
|
1810
1946
|
stopAssistant: (id) => void stopAssistant(id),
|
|
1947
|
+
copy: (text) => void copy(text),
|
|
1811
1948
|
runAgent: () => void runAgent(),
|
|
1812
1949
|
stopAgent: () => void stopAgent(),
|
|
1813
1950
|
askAgent: (run, text) => void askAgent(run, text),
|
|
@@ -1836,14 +1973,39 @@ var handlers = {
|
|
|
1836
1973
|
ui.settingsDraft.value = /* @__PURE__ */ new Map();
|
|
1837
1974
|
ui.settingsError.value = null;
|
|
1838
1975
|
}),
|
|
1839
|
-
saveSettings: (
|
|
1840
|
-
chooseSettings: (values) => void sendSettings(values, false),
|
|
1976
|
+
saveSettings: () => void sendSettings(Object.fromEntries(ui.settingsDraft.value)),
|
|
1841
1977
|
loadText: (name) => void loadText(name),
|
|
1842
1978
|
editText: (name, text) => patchText(name, { draft: text }),
|
|
1843
1979
|
saveText: (name, overwrite) => void saveText(name, overwrite),
|
|
1844
1980
|
reloadText: (name) => void loadText(name),
|
|
1845
1981
|
saveObjective: (objective, blocker) => void saveObjective(objective, blocker),
|
|
1846
|
-
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()
|
|
1847
2009
|
};
|
|
1848
2010
|
|
|
1849
2011
|
// client/format.ts
|
|
@@ -1866,15 +2028,15 @@ var dateFormats = formatters("UTC");
|
|
|
1866
2028
|
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
1867
2029
|
function formatted(value, kind) {
|
|
1868
2030
|
const bare = typeof value === "string" && DATE_ONLY.test(value);
|
|
1869
|
-
const
|
|
1870
|
-
return
|
|
2031
|
+
const d5 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
|
|
2032
|
+
return d5 && !Number.isNaN(d5.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d5) : "";
|
|
1871
2033
|
}
|
|
1872
2034
|
function parseDate(value) {
|
|
1873
2035
|
if (typeof value !== "string" || value === "") return null;
|
|
1874
2036
|
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
1875
2037
|
if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
|
|
1876
|
-
const
|
|
1877
|
-
return Number.isNaN(
|
|
2038
|
+
const d5 = new Date(value);
|
|
2039
|
+
return Number.isNaN(d5.getTime()) ? null : d5;
|
|
1878
2040
|
}
|
|
1879
2041
|
function formatTime(value) {
|
|
1880
2042
|
return formatted(value, "time");
|
|
@@ -1901,37 +2063,37 @@ function setCalendarZone(timeZone) {
|
|
|
1901
2063
|
clockFormats = formatters();
|
|
1902
2064
|
}
|
|
1903
2065
|
}
|
|
1904
|
-
function dayKey(
|
|
2066
|
+
function dayKey(d5) {
|
|
1905
2067
|
if (dayKeyFmt) {
|
|
1906
|
-
const parts = dayKeyFmt.formatToParts(
|
|
2068
|
+
const parts = dayKeyFmt.formatToParts(d5);
|
|
1907
2069
|
const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
|
|
1908
2070
|
return `${part("year")}-${part("month")}-${part("day")}`;
|
|
1909
2071
|
}
|
|
1910
|
-
const m4 = String(
|
|
1911
|
-
const day = String(
|
|
1912
|
-
return `${
|
|
2072
|
+
const m4 = String(d5.getMonth() + 1).padStart(2, "0");
|
|
2073
|
+
const day = String(d5.getDate()).padStart(2, "0");
|
|
2074
|
+
return `${d5.getFullYear()}-${m4}-${day}`;
|
|
1913
2075
|
}
|
|
1914
2076
|
function splitKey(key) {
|
|
1915
|
-
const [
|
|
1916
|
-
return [
|
|
2077
|
+
const [y5, m4, d5] = key.split("-").map(Number);
|
|
2078
|
+
return [y5, m4 - 1, d5];
|
|
1917
2079
|
}
|
|
1918
2080
|
function keyAt(ms) {
|
|
1919
2081
|
return new Date(ms).toISOString().slice(0, 10);
|
|
1920
2082
|
}
|
|
1921
2083
|
function addDays(key, days) {
|
|
1922
|
-
const [
|
|
1923
|
-
return keyAt(Date.UTC(
|
|
2084
|
+
const [y5, m4, d5] = splitKey(key);
|
|
2085
|
+
return keyAt(Date.UTC(y5, m4, d5 + days));
|
|
1924
2086
|
}
|
|
1925
2087
|
function addMonths(key, months) {
|
|
1926
|
-
const [
|
|
1927
|
-
const lastDay = new Date(Date.UTC(
|
|
1928
|
-
return keyAt(Date.UTC(
|
|
2088
|
+
const [y5, m4, d5] = splitKey(key);
|
|
2089
|
+
const lastDay = new Date(Date.UTC(y5, m4 + months + 1, 0)).getUTCDate();
|
|
2090
|
+
return keyAt(Date.UTC(y5, m4 + months, Math.min(d5, lastDay)));
|
|
1929
2091
|
}
|
|
1930
2092
|
function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
|
|
1931
2093
|
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1932
|
-
const [
|
|
2094
|
+
const [y5, m4, d5] = splitKey(value);
|
|
1933
2095
|
const [ty, tm, td] = splitKey(dayKey(now));
|
|
1934
|
-
return Math.round((Date.UTC(
|
|
2096
|
+
return Math.round((Date.UTC(y5, m4, d5) - Date.UTC(ty, tm, td)) / 864e5);
|
|
1935
2097
|
}
|
|
1936
2098
|
const target = parseDate(value);
|
|
1937
2099
|
if (!target) return null;
|
|
@@ -1957,9 +2119,9 @@ function formatDuration(minutes) {
|
|
|
1957
2119
|
return `${h5} h ${m4} min`;
|
|
1958
2120
|
}
|
|
1959
2121
|
function relativeTime(value, now = /* @__PURE__ */ new Date()) {
|
|
1960
|
-
const
|
|
1961
|
-
if (!
|
|
1962
|
-
const seconds = Math.round((now.getTime() -
|
|
2122
|
+
const d5 = parseDate(value);
|
|
2123
|
+
if (!d5) return "";
|
|
2124
|
+
const seconds = Math.round((now.getTime() - d5.getTime()) / 1e3);
|
|
1963
2125
|
if (seconds < 60) return "just now";
|
|
1964
2126
|
const minutes = Math.round(seconds / 60);
|
|
1965
2127
|
if (minutes < 60) return `${minutes} min ago`;
|
|
@@ -1967,7 +2129,18 @@ function relativeTime(value, now = /* @__PURE__ */ new Date()) {
|
|
|
1967
2129
|
if (hours < 48) return `${hours} h ago`;
|
|
1968
2130
|
const days = Math.round(hours / 24);
|
|
1969
2131
|
if (days <= 30) return `${days} days ago`;
|
|
1970
|
-
return dayKey(
|
|
2132
|
+
return dayKey(d5).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
|
|
2133
|
+
}
|
|
2134
|
+
function durationBetween(start, end) {
|
|
2135
|
+
const from = parseDate(start);
|
|
2136
|
+
const to = parseDate(end);
|
|
2137
|
+
if (!from || !to || to < from) return "";
|
|
2138
|
+
const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
|
|
2139
|
+
return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
|
|
2140
|
+
}
|
|
2141
|
+
function formatClock(seconds) {
|
|
2142
|
+
const whole = Math.max(0, Math.floor(seconds));
|
|
2143
|
+
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
1971
2144
|
}
|
|
1972
2145
|
function firstLine(text) {
|
|
1973
2146
|
const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
|
|
@@ -2167,7 +2340,7 @@ function renderMarkdown(value) {
|
|
|
2167
2340
|
function anchor(href, label) {
|
|
2168
2341
|
return el("a", { href: /^https?:\/\//i.test(href) ? href : "#", target: "_blank", rel: "noopener noreferrer" }, label);
|
|
2169
2342
|
}
|
|
2170
|
-
function renderMarkdownBlocks(value) {
|
|
2343
|
+
function renderMarkdownBlocks(value, options = {}) {
|
|
2171
2344
|
const out = [];
|
|
2172
2345
|
const lines = String(value).replace(/\r\n/g, "\n").split("\n");
|
|
2173
2346
|
let i4 = 0;
|
|
@@ -2178,7 +2351,16 @@ function renderMarkdownBlocks(value) {
|
|
|
2178
2351
|
i4++;
|
|
2179
2352
|
while (i4 < lines.length && !/^\s*```/.test(lines[i4])) code.push(lines[i4++]);
|
|
2180
2353
|
i4++;
|
|
2181
|
-
|
|
2354
|
+
const text = code.join("\n");
|
|
2355
|
+
const { onCopy } = options;
|
|
2356
|
+
out.push(
|
|
2357
|
+
onCopy ? el(
|
|
2358
|
+
"div",
|
|
2359
|
+
{ class: "code-block" },
|
|
2360
|
+
el("pre", null, text),
|
|
2361
|
+
el("button", { type: "button", class: "code-block__copy", title: "Copy to the clipboard", onClick: () => onCopy(text) }, "Copy")
|
|
2362
|
+
) : el("pre", null, text)
|
|
2363
|
+
);
|
|
2182
2364
|
continue;
|
|
2183
2365
|
}
|
|
2184
2366
|
if (line.trim() === "") {
|
|
@@ -2188,7 +2370,7 @@ function renderMarkdownBlocks(value) {
|
|
|
2188
2370
|
if (/^\s*>/.test(line)) {
|
|
2189
2371
|
const quoted = [];
|
|
2190
2372
|
while (i4 < lines.length && /^\s*>/.test(lines[i4])) quoted.push(lines[i4++].replace(/^\s*>\s?/, ""));
|
|
2191
|
-
out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"))));
|
|
2373
|
+
out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"), options)));
|
|
2192
2374
|
continue;
|
|
2193
2375
|
}
|
|
2194
2376
|
if (/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
|
|
@@ -2297,10 +2479,11 @@ function agentRunBanner(run, state2, handlers2) {
|
|
|
2297
2479
|
}
|
|
2298
2480
|
};
|
|
2299
2481
|
if (run.status === "running") {
|
|
2482
|
+
const helping = (run.steps ?? []).filter((step) => step.subagent && step.state === "running").length;
|
|
2300
2483
|
return bannerNode(
|
|
2301
2484
|
"info",
|
|
2302
2485
|
"i",
|
|
2303
|
-
`The morning agent is writing a new brief, started ${relativeTime(run.startedAt, now)}
|
|
2486
|
+
`The morning agent is writing a new brief, started ${relativeTime(run.startedAt, now)}` + (helping ? `, with ${helping} ${helping === 1 ? "subagent" : "subagents"} at work.` : "."),
|
|
2304
2487
|
[el("button", { type: "button", class: "button banner__action", onClick: () => handlers2.stopAgent() }, "Stop")],
|
|
2305
2488
|
strip
|
|
2306
2489
|
);
|
|
@@ -2469,7 +2652,8 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2469
2652
|
{ class: "item__body" },
|
|
2470
2653
|
el("p", { class: "item__title" }, title),
|
|
2471
2654
|
renderMeta(item, state2, now),
|
|
2472
|
-
item.detail ? el("p", { class: "item__detail" }, renderMarkdown(item.detail)) : null
|
|
2655
|
+
item.detail ? el("p", { class: "item__detail" }, renderMarkdown(item.detail)) : null,
|
|
2656
|
+
renderAhead(item.id, state2)
|
|
2473
2657
|
),
|
|
2474
2658
|
renderActions(item, ui2, handlers2),
|
|
2475
2659
|
ui2.menuFor.value === item.id ? renderSnoozeMenu(item, now, handlers2) : null
|
|
@@ -2533,6 +2717,38 @@ function rowPillTexts(row, state2) {
|
|
|
2533
2717
|
function rowPills(row, state2) {
|
|
2534
2718
|
return rowPillTexts(row, state2).map(([cls, text]) => el("span", { class: cls }, text));
|
|
2535
2719
|
}
|
|
2720
|
+
var READING = {
|
|
2721
|
+
"assess-review": "Reading the review\u2026",
|
|
2722
|
+
"explain-ci": "Reading the failing checks\u2026"
|
|
2723
|
+
};
|
|
2724
|
+
function renderAhead(id, state2) {
|
|
2725
|
+
const auto = state2.assistant?.auto;
|
|
2726
|
+
const keys = auto?.rows?.[id];
|
|
2727
|
+
if (!auto || !keys?.length) return null;
|
|
2728
|
+
const turn = (state2.assistant.items?.[id]?.turns ?? []).findLast((candidate) => candidate.auto !== null && keys.includes(candidate.auto));
|
|
2729
|
+
const mark = el("span", { class: "item__ahead-mark", "aria-hidden": "true" }, "\u2726");
|
|
2730
|
+
if (!turn) {
|
|
2731
|
+
return auto.enabled && auto.usedToday >= auto.limit ? el("p", { class: "item__ahead item__ahead--quiet" }, mark, `Not answered ahead: today's ${auto.limit} are used.`) : null;
|
|
2732
|
+
}
|
|
2733
|
+
if (turn.status === "running") {
|
|
2734
|
+
return el("p", { class: "item__ahead item__ahead--quiet", "data-status": "running" }, mark, READING[turn.action ?? ""] ?? "Reading\u2026");
|
|
2735
|
+
}
|
|
2736
|
+
if (turn.status === "failed") {
|
|
2737
|
+
return el("p", { class: "item__ahead item__ahead--quiet" }, mark, "Could not answer ahead; the panel says why.");
|
|
2738
|
+
}
|
|
2739
|
+
const verdict = turn.status === "done" ? verdictLine(turn.reply) : "";
|
|
2740
|
+
if (!verdict) return null;
|
|
2741
|
+
return el(
|
|
2742
|
+
"p",
|
|
2743
|
+
{ class: "item__ahead", title: `Answered ahead ${relativeTime(turn.endedAt ?? turn.startedAt, new Date(state2.now))}. Open the panel for the whole answer.` },
|
|
2744
|
+
mark,
|
|
2745
|
+
renderMarkdown(verdict)
|
|
2746
|
+
);
|
|
2747
|
+
}
|
|
2748
|
+
function verdictLine(reply) {
|
|
2749
|
+
const line = reply.split("\n").find((candidate) => candidate.trim() !== "") ?? "";
|
|
2750
|
+
return line.replace(/^\s*(?:#{1,6}\s+|>\s*|[-*•]\s+|\d+[.)]\s+)/, "").trim();
|
|
2751
|
+
}
|
|
2536
2752
|
function renderActions(item, ui2, handlers2) {
|
|
2537
2753
|
const buttons = [];
|
|
2538
2754
|
const act = (id, action) => () => handlers2.onAction(id, action);
|
|
@@ -2826,7 +3042,8 @@ function PullRow({ item: row, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2826
3042
|
),
|
|
2827
3043
|
renderPullMeta(row, state2, now),
|
|
2828
3044
|
renderPullReasons(row, now),
|
|
2829
|
-
renderCancelledChecks(row)
|
|
3045
|
+
renderCancelledChecks(row),
|
|
3046
|
+
renderAhead(row.id, state2)
|
|
2830
3047
|
),
|
|
2831
3048
|
renderPullActions(row, ui2, handlers2),
|
|
2832
3049
|
ui2.menuFor.value === row.id ? renderSnoozeMenu(row, now, handlers2, { indefinite: false }) : null
|
|
@@ -3719,6 +3936,7 @@ function describeWhen(value, now) {
|
|
|
3719
3936
|
return `${named ? day : date && date.getFullYear() === now.getFullYear() ? formatShortDay(value) : formatDay(value)} at ${formatTime(value)}`;
|
|
3720
3937
|
}
|
|
3721
3938
|
function runReport(run, ui2, handlers2) {
|
|
3939
|
+
if (run.steps?.length) return runWork(run, ui2, handlers2);
|
|
3722
3940
|
const messages = run.messages ?? (run.report ? [run.report] : []);
|
|
3723
3941
|
const running = run.status === "running";
|
|
3724
3942
|
const earlier = !running && messages.length && messages.at(-1) === run.report ? messages.slice(0, -1) : messages;
|
|
@@ -3777,6 +3995,92 @@ function runReport(run, ui2, handlers2) {
|
|
|
3777
3995
|
) : null
|
|
3778
3996
|
);
|
|
3779
3997
|
}
|
|
3998
|
+
function runWork(run, ui2, handlers2) {
|
|
3999
|
+
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
|
+
}
|
|
4008
|
+
const last = run.steps.findLast((step) => step.by === null && step.kind === "message");
|
|
4009
|
+
const steps2 = last && sameWords(last.text, run.report) ? run.steps.filter((step) => step !== last) : run.steps;
|
|
4010
|
+
const handoffs = steps2.filter((step) => step.subagent).length;
|
|
4011
|
+
return el(
|
|
4012
|
+
"section",
|
|
4013
|
+
{ class: "flyout__section" },
|
|
4014
|
+
el("h3", { class: "flyout__label" }, run.status === "done" ? "Its report" : "What it said last"),
|
|
4015
|
+
run.report ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(run.report)) : el("p", { class: "flyout__empty" }, "It said nothing."),
|
|
4016
|
+
steps2.length ? el(
|
|
4017
|
+
"details",
|
|
4018
|
+
{ class: "agent-run__list" },
|
|
4019
|
+
el(
|
|
4020
|
+
"summary",
|
|
4021
|
+
null,
|
|
4022
|
+
`How it got there, in ${steps2.length} ${steps2.length === 1 ? "step" : "steps"}` + (handoffs ? `, with ${handoffs} ${handoffs === 1 ? "subagent" : "subagents"}` : "")
|
|
4023
|
+
),
|
|
4024
|
+
runSteps(run, steps2, null, ui2, handlers2)
|
|
4025
|
+
) : null
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
4028
|
+
var NESTING = 4;
|
|
4029
|
+
function runSteps(run, steps2, by, ui2, handlers2, depth = 0) {
|
|
4030
|
+
const own = steps2.filter((step) => step.by === by);
|
|
4031
|
+
return el(
|
|
4032
|
+
"ol",
|
|
4033
|
+
{ class: "assistant__steps agent-run__steps" },
|
|
4034
|
+
own.length ? null : el("li", { class: "assistant__step assistant__step--message" }, "Nothing yet."),
|
|
4035
|
+
own.map((step, index) => {
|
|
4036
|
+
if (step.kind === "message") {
|
|
4037
|
+
return el("li", { key: index, class: "agent-run__said" }, renderMarkdownBlocks(step.text));
|
|
4038
|
+
}
|
|
4039
|
+
if (step.subagent && depth < NESTING) return runHandoff(run, steps2, step, ui2, handlers2, depth, index);
|
|
4040
|
+
return el(
|
|
4041
|
+
"li",
|
|
4042
|
+
{ key: index, class: "assistant__step", "data-state": step.state },
|
|
4043
|
+
el("span", { class: "assistant__step-text" }, step.text),
|
|
4044
|
+
step.detail ? el("code", { class: "assistant__step-detail", title: step.detail }, step.detail) : null
|
|
4045
|
+
);
|
|
4046
|
+
})
|
|
4047
|
+
);
|
|
4048
|
+
}
|
|
4049
|
+
function runHandoff(run, steps2, step, ui2, handlers2, depth, index) {
|
|
4050
|
+
const id = step.subagent;
|
|
4051
|
+
const own = steps2.filter((candidate) => candidate.by === id);
|
|
4052
|
+
const latest = own.at(-1);
|
|
4053
|
+
const calls = own.filter((candidate) => candidate.kind === "tool").length;
|
|
4054
|
+
const doing = step.state === "running" ? latest ? latest.kind === "message" ? firstLine(latest.text) : latest.text : "starting" : `${calls} ${calls === 1 ? "step" : "steps"}`;
|
|
4055
|
+
const key = `${run.id}:${id}`;
|
|
4056
|
+
const fallback = step.state === "running";
|
|
4057
|
+
const open = ui2.messageOpen.value.has(key) ? ui2.messageOpen.value.get(key) : fallback;
|
|
4058
|
+
return el(
|
|
4059
|
+
"li",
|
|
4060
|
+
{ key: index, class: "assistant__step agent-run__handoff", "data-state": step.state },
|
|
4061
|
+
el(
|
|
4062
|
+
"details",
|
|
4063
|
+
{
|
|
4064
|
+
open,
|
|
4065
|
+
// As the message folds do: a toggle back to the default is forgotten.
|
|
4066
|
+
onToggle: (event) => {
|
|
4067
|
+
const isOpen = event.currentTarget.open;
|
|
4068
|
+
handlers2.toggleMessage(key, isOpen === fallback ? null : isOpen);
|
|
4069
|
+
}
|
|
4070
|
+
},
|
|
4071
|
+
el(
|
|
4072
|
+
"summary",
|
|
4073
|
+
null,
|
|
4074
|
+
el("span", { class: "assistant__step-text" }, `Subagent: ${step.detail ?? "unnamed"}`),
|
|
4075
|
+
el("span", { class: "assistant__step-detail" }, doing)
|
|
4076
|
+
),
|
|
4077
|
+
runSteps(run, steps2, id, ui2, handlers2, depth + 1)
|
|
4078
|
+
)
|
|
4079
|
+
);
|
|
4080
|
+
}
|
|
4081
|
+
function sameWords(a4, b4) {
|
|
4082
|
+
return a4.replace(/\s+/g, " ").trim() === b4.replace(/\s+/g, " ").trim();
|
|
4083
|
+
}
|
|
3780
4084
|
function runStatus(run, now, handlers2) {
|
|
3781
4085
|
if (run.status === "running") {
|
|
3782
4086
|
return el(
|
|
@@ -3797,11 +4101,7 @@ function runStatus(run, now, handlers2) {
|
|
|
3797
4101
|
);
|
|
3798
4102
|
}
|
|
3799
4103
|
function runDuration(run) {
|
|
3800
|
-
|
|
3801
|
-
const to = parseDate(run.endedAt);
|
|
3802
|
-
if (!from || !to || to < from) return "";
|
|
3803
|
-
const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
|
|
3804
|
-
return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
|
|
4104
|
+
return durationBetween(run.startedAt, run.endedAt);
|
|
3805
4105
|
}
|
|
3806
4106
|
function runConversation(run, busy, answering, enabled, ui2, handlers2, inputRef) {
|
|
3807
4107
|
const turns = run.turns.length ? el(
|
|
@@ -3957,8 +4257,9 @@ function RowPanel({ row, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
3957
4257
|
) : el("p", { class: "flyout__empty" }, "Nothing noted yet. A note goes to the morning agent as free text."),
|
|
3958
4258
|
k(NoteForm, { row, title: facts.title, ui: ui2, handlers: handlers2, inputRef: noteInput })
|
|
3959
4259
|
),
|
|
3960
|
-
|
|
3961
|
-
)
|
|
4260
|
+
entry.turns.length ? renderAssistantSection(state2, entry, enabled, handlers2) : null
|
|
4261
|
+
),
|
|
4262
|
+
enabled ? renderAssistantFooter(facts, state2, entry, ui2, handlers2, askInput, row) : null
|
|
3962
4263
|
];
|
|
3963
4264
|
}
|
|
3964
4265
|
function describeRow(row) {
|
|
@@ -4059,21 +4360,27 @@ function NoteForm({
|
|
|
4059
4360
|
el("button", { type: "submit", class: "button button--primary flyout__submit" }, "Save")
|
|
4060
4361
|
);
|
|
4061
4362
|
}
|
|
4062
|
-
function renderAssistantSection(
|
|
4063
|
-
const source = "source" in row ? row.source : "github";
|
|
4064
|
-
const actions = (state2.assistant?.quickActions ?? []).filter(
|
|
4065
|
-
(action) => action.sources === null || action.sources.includes(source)
|
|
4066
|
-
);
|
|
4363
|
+
function renderAssistantSection(state2, entry, enabled, handlers2) {
|
|
4067
4364
|
const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
|
|
4068
4365
|
return el(
|
|
4069
4366
|
"section",
|
|
4070
4367
|
{ class: "flyout__section assistant", "data-running": String(entry.running) },
|
|
4071
4368
|
el("h3", { class: "flyout__label" }, "Assistant"),
|
|
4072
|
-
|
|
4369
|
+
el(
|
|
4073
4370
|
"div",
|
|
4074
4371
|
{ class: "assistant__turns" },
|
|
4075
|
-
entry.turns.map((turn) => renderAssistantTurn(turn, label))
|
|
4076
|
-
)
|
|
4372
|
+
entry.turns.map((turn) => renderAssistantTurn(turn, label, handlers2))
|
|
4373
|
+
),
|
|
4374
|
+
enabled ? null : el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4377
|
+
function renderAssistantFooter(facts, state2, entry, ui2, handlers2, inputRef, row) {
|
|
4378
|
+
const actions = (state2.assistant?.quickActions ?? []).filter(
|
|
4379
|
+
(action) => action.sources === null || action.sources.includes(facts.source)
|
|
4380
|
+
);
|
|
4381
|
+
return el(
|
|
4382
|
+
"div",
|
|
4383
|
+
{ class: "flyout__footer" },
|
|
4077
4384
|
entry.running ? null : el(
|
|
4078
4385
|
"div",
|
|
4079
4386
|
{ class: "assistant__quick" },
|
|
@@ -4093,20 +4400,6 @@ function renderAssistantSection(row, facts, state2, entry, ui2, handlers2, input
|
|
|
4093
4400
|
k(AssistantComposer, { row, title: facts.title, entry, ui: ui2, handlers: handlers2, inputRef })
|
|
4094
4401
|
);
|
|
4095
4402
|
}
|
|
4096
|
-
function renderAssistantHistory(state2, entry) {
|
|
4097
|
-
const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
|
|
4098
|
-
return el(
|
|
4099
|
-
"section",
|
|
4100
|
-
{ class: "flyout__section assistant", "data-running": "false" },
|
|
4101
|
-
el("h3", { class: "flyout__label" }, "Assistant"),
|
|
4102
|
-
el(
|
|
4103
|
-
"div",
|
|
4104
|
-
{ class: "assistant__turns" },
|
|
4105
|
-
entry.turns.map((turn) => renderAssistantTurn(turn, label))
|
|
4106
|
-
),
|
|
4107
|
-
el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
|
|
4108
|
-
);
|
|
4109
|
-
}
|
|
4110
4403
|
function AssistantComposer({
|
|
4111
4404
|
row,
|
|
4112
4405
|
title,
|
|
@@ -4135,7 +4428,7 @@ function AssistantComposer({
|
|
|
4135
4428
|
rows: 2,
|
|
4136
4429
|
ref: inputRef,
|
|
4137
4430
|
value: ui2.assistantDraft.value,
|
|
4138
|
-
placeholder: entry.turns.length ? "Follow up\u2026" : "
|
|
4431
|
+
placeholder: entry.turns.length ? "Follow up\u2026" : "Ask the assistant about this\u2026",
|
|
4139
4432
|
"aria-label": `Ask the assistant about ${title}`,
|
|
4140
4433
|
disabled: entry.running,
|
|
4141
4434
|
onInput: (event) => {
|
|
@@ -4151,17 +4444,506 @@ function AssistantComposer({
|
|
|
4151
4444
|
entry.running ? el("button", { type: "button", class: "button flyout__submit", onClick: () => handlers2.stopAssistant(row.id) }, "Stop") : el("button", { type: "submit", class: "button button--primary flyout__submit" }, "Send")
|
|
4152
4445
|
);
|
|
4153
4446
|
}
|
|
4154
|
-
function renderAssistantTurn(turn, label) {
|
|
4447
|
+
function renderAssistantTurn(turn, label, handlers2) {
|
|
4155
4448
|
const action = turn.action ? label.get(turn.action) : null;
|
|
4156
4449
|
const extra = action && turn.request.startsWith(action.request) ? turn.request.slice(action.request.length).trim() : null;
|
|
4157
4450
|
const asked = action ? [el("strong", null, action.label), extra ? ` \u2014 ${extra}` : ""] : [turn.request];
|
|
4158
|
-
|
|
4451
|
+
if (turn.auto) asked.push(el("span", { class: "assistant__auto", title: "The dashboard asked this on its own, when the pull request started waiting on you." }, " \xB7 asked ahead"));
|
|
4452
|
+
const { body, created } = turn.status === "done" ? splitCreated(turn.reply) : { body: turn.reply, created: [] };
|
|
4453
|
+
const status = turn.status === "failed" ? el("p", { class: "assistant__status assistant__status--bad" }, `Failed: ${turn.error}`) : turn.status === "aborted" ? el("p", { class: "assistant__status" }, `Stopped: ${turn.error}`) : null;
|
|
4159
4454
|
return el(
|
|
4160
4455
|
"div",
|
|
4161
|
-
{ class: "assistant__turn", "data-status": turn.status },
|
|
4456
|
+
{ key: turn.id, class: "assistant__turn", "data-status": turn.status },
|
|
4162
4457
|
el("p", { class: "assistant__request" }, asked),
|
|
4163
|
-
|
|
4164
|
-
|
|
4458
|
+
renderWork(turn),
|
|
4459
|
+
body ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(body, { onCopy: (text) => handlers2.copy(text) })) : null,
|
|
4460
|
+
status,
|
|
4461
|
+
turn.status === "done" && (body || created.length) ? el(
|
|
4462
|
+
"div",
|
|
4463
|
+
{ class: "assistant__after" },
|
|
4464
|
+
created.map(
|
|
4465
|
+
(link) => el("a", { class: "button", href: link.url, target: "_blank", rel: "noopener noreferrer", title: link.url }, `${link.label} \u2197`)
|
|
4466
|
+
),
|
|
4467
|
+
body ? el("button", { type: "button", class: "button", onClick: () => handlers2.copy(body) }, "Copy reply") : null
|
|
4468
|
+
) : null
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
function renderWork(turn) {
|
|
4472
|
+
const steps2 = turn.steps ?? [];
|
|
4473
|
+
if (turn.status === "running") {
|
|
4474
|
+
return el(
|
|
4475
|
+
"div",
|
|
4476
|
+
{ class: "assistant__work" },
|
|
4477
|
+
el("p", { class: "assistant__status assistant__status--working" }, "Working \xB7 ", k(Elapsed, { since: turn.startedAt })),
|
|
4478
|
+
steps2.length ? renderSteps(steps2) : null
|
|
4479
|
+
);
|
|
4480
|
+
}
|
|
4481
|
+
const took = durationBetween(turn.startedAt, turn.endedAt);
|
|
4482
|
+
if (!steps2.length) return took ? el("p", { class: "assistant__took" }, `Answered in ${took}`) : null;
|
|
4483
|
+
const failed = steps2.filter((step) => step.state === "failed").length;
|
|
4484
|
+
const summary = [
|
|
4485
|
+
took ? `${turn.status === "done" ? "Worked" : "Ran"} for ${took}` : null,
|
|
4486
|
+
`${steps2.length} ${steps2.length === 1 ? "step" : "steps"}`,
|
|
4487
|
+
failed ? `${failed} failed` : null
|
|
4488
|
+
].filter(Boolean).join(" \xB7 ");
|
|
4489
|
+
return el("details", { class: "assistant__work", open: turn.status === "failed" }, el("summary", null, summary), renderSteps(steps2));
|
|
4490
|
+
}
|
|
4491
|
+
function renderSteps(steps2) {
|
|
4492
|
+
return el(
|
|
4493
|
+
"ol",
|
|
4494
|
+
{ class: "assistant__steps" },
|
|
4495
|
+
steps2.map(
|
|
4496
|
+
(step, index) => step.kind === "message" ? el("li", { key: index, class: "assistant__step assistant__step--message" }, renderMarkdown(step.text.trim())) : el(
|
|
4497
|
+
"li",
|
|
4498
|
+
{ key: index, class: "assistant__step", "data-state": step.state },
|
|
4499
|
+
el("span", { class: "assistant__step-text" }, step.text),
|
|
4500
|
+
step.detail ? el("code", { class: "assistant__step-detail", title: step.detail }, step.detail) : null
|
|
4501
|
+
)
|
|
4502
|
+
)
|
|
4503
|
+
);
|
|
4504
|
+
}
|
|
4505
|
+
function Elapsed({ since }) {
|
|
4506
|
+
const [now, setNow] = d2(() => Date.now());
|
|
4507
|
+
h2(() => {
|
|
4508
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
4509
|
+
return () => clearInterval(timer);
|
|
4510
|
+
}, []);
|
|
4511
|
+
const start = parseDate(since)?.getTime() ?? now;
|
|
4512
|
+
return el("span", { class: "assistant__clock" }, formatClock((now - start) / 1e3));
|
|
4513
|
+
}
|
|
4514
|
+
var CREATED = /^\s*(?:[-*]\s+)?Created:\s*\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)\s*\.?\s*$/i;
|
|
4515
|
+
function splitCreated(reply) {
|
|
4516
|
+
const created = [];
|
|
4517
|
+
const kept = [];
|
|
4518
|
+
for (const line of reply.split("\n")) {
|
|
4519
|
+
const match = CREATED.exec(line);
|
|
4520
|
+
if (match) created.push({ label: match[1].trim(), url: match[2] });
|
|
4521
|
+
else kept.push(line);
|
|
4522
|
+
}
|
|
4523
|
+
return { body: created.length ? kept.join("\n").trim() : reply, created };
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
// client/onboarding.ts
|
|
4527
|
+
var SECONDS_PER_STEP = 20;
|
|
4528
|
+
function OnboardingView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4529
|
+
const flow = state2.onboarding;
|
|
4530
|
+
const open = flow.steps.filter((step) => step.status === "open");
|
|
4531
|
+
const editing = ui2.setupEditing.value;
|
|
4532
|
+
h2(() => {
|
|
4533
|
+
if (!flow.detected) handlers2.detectSetup();
|
|
4534
|
+
}, [flow.detected]);
|
|
4535
|
+
return el(
|
|
4536
|
+
"div",
|
|
4537
|
+
{ class: "onboarding" },
|
|
4538
|
+
el(
|
|
4539
|
+
"header",
|
|
4540
|
+
{ class: "onboarding__bar" },
|
|
4541
|
+
el("span", { class: "onboarding__name" }, "Daily Focus"),
|
|
4542
|
+
progress(flow)
|
|
4543
|
+
),
|
|
4544
|
+
el(
|
|
4545
|
+
"main",
|
|
4546
|
+
{ class: "onboarding__column", id: "main" },
|
|
4547
|
+
flow.steps.length > 0 && flow.steps.every((step) => step.status === "open") && flow.steps[0].id === flow.current ? el(
|
|
4548
|
+
"p",
|
|
4549
|
+
{ class: "onboarding__lede" },
|
|
4550
|
+
"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."
|
|
4551
|
+
) : null,
|
|
4552
|
+
el(
|
|
4553
|
+
"ol",
|
|
4554
|
+
{ class: "onboarding__thread" },
|
|
4555
|
+
flow.steps.map((step) => {
|
|
4556
|
+
if (step.id === flow.current || step.id === editing) {
|
|
4557
|
+
return el("li", { key: step.id }, k(StepCard, { step, ui: ui2, handlers: handlers2, current: step.id === flow.current }));
|
|
4558
|
+
}
|
|
4559
|
+
if (step.status === "open") return null;
|
|
4560
|
+
return el("li", { key: step.id }, doneRow(step, handlers2, ui2.setupBusy.value !== null));
|
|
4561
|
+
}),
|
|
4562
|
+
flow.current === null ? el("li", { key: "finish" }, k(FinishCard, { flow, ui: ui2, handlers: handlers2 })) : ahead(flow, open)
|
|
4563
|
+
)
|
|
4564
|
+
)
|
|
4565
|
+
);
|
|
4566
|
+
}
|
|
4567
|
+
function progress(flow) {
|
|
4568
|
+
const total = flow.steps.length + 1;
|
|
4569
|
+
const left = flow.steps.filter((step) => step.status === "open").length;
|
|
4570
|
+
const seconds = left * SECONDS_PER_STEP;
|
|
4571
|
+
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`;
|
|
4572
|
+
const done = total - left - (flow.current === null ? 0 : 1);
|
|
4573
|
+
return el(
|
|
4574
|
+
"div",
|
|
4575
|
+
{ class: "onboarding__progress" },
|
|
4576
|
+
el(
|
|
4577
|
+
"div",
|
|
4578
|
+
{ class: "onboarding__track", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": total, "aria-valuenow": done, "aria-label": "Setup" },
|
|
4579
|
+
el("i", { style: { width: `${Math.round(done / total * 100)}%` } })
|
|
4580
|
+
),
|
|
4581
|
+
el("span", null, text)
|
|
4582
|
+
);
|
|
4583
|
+
}
|
|
4584
|
+
function doneRow(step, handlers2, busy) {
|
|
4585
|
+
return el(
|
|
4586
|
+
"div",
|
|
4587
|
+
{ class: "onboarding__done", "data-status": step.status, "data-step": step.id },
|
|
4588
|
+
el("span", { class: "onboarding__dot", "aria-hidden": "true" }),
|
|
4589
|
+
el("span", { class: "onboarding__summary" }, step.summary ?? `${step.topic[0].toUpperCase()}${step.topic.slice(1)}: decide later.`),
|
|
4590
|
+
el("button", { type: "button", class: "onboarding__change", disabled: busy, onClick: () => handlers2.editSetupStep(step.id) }, "Change")
|
|
4591
|
+
);
|
|
4592
|
+
}
|
|
4593
|
+
function ahead(flow, open) {
|
|
4594
|
+
const rest = open.filter((step) => step.id !== flow.current);
|
|
4595
|
+
const looking = rest.find((step) => step.proposal?.status === "working");
|
|
4596
|
+
const topics = rest.filter((step) => step !== looking).map((step) => step.topic);
|
|
4597
|
+
const later = topics.length > 4 ? [...topics.slice(0, 3), `${topics.length - 3} more`] : topics;
|
|
4598
|
+
return el(
|
|
4599
|
+
"li",
|
|
4600
|
+
{ key: "ahead", class: "onboarding__ahead" },
|
|
4601
|
+
looking ? el(
|
|
4602
|
+
"div",
|
|
4603
|
+
{ class: "onboarding__next" },
|
|
4604
|
+
el("span", { class: "onboarding__dot onboarding__dot--looking", "aria-hidden": "true" }),
|
|
4605
|
+
el("p", null, `Next, ${looking.topic}. It's already looking:`),
|
|
4606
|
+
steps(looking)
|
|
4607
|
+
) : null,
|
|
4608
|
+
el(
|
|
4609
|
+
"p",
|
|
4610
|
+
{ class: "onboarding__then" },
|
|
4611
|
+
el("span", { class: "onboarding__dot onboarding__dot--later", "aria-hidden": "true" }),
|
|
4612
|
+
later.length ? `Then ${listed([...later, "your first brief"])}.` : "Then your first brief."
|
|
4613
|
+
)
|
|
4614
|
+
);
|
|
4615
|
+
}
|
|
4616
|
+
function steps(step, count = 3) {
|
|
4617
|
+
const shown = (step.proposal?.steps ?? []).filter((s4) => s4.kind === "tool").slice(-count);
|
|
4618
|
+
const waiting = step.proposal?.status === "working" && !shown.some((s4) => s4.state === "running");
|
|
4619
|
+
return el(
|
|
4620
|
+
"ul",
|
|
4621
|
+
{ class: "onboarding__steps" },
|
|
4622
|
+
shown.map((s4, index) => el("li", { key: index, "data-state": s4.state, title: s4.detail ?? void 0 }, s4.text)),
|
|
4623
|
+
waiting ? el("li", { key: "waiting", "data-state": "running" }, shown.length ? "Writing its answer\u2026" : "Starting\u2026") : null
|
|
4624
|
+
);
|
|
4625
|
+
}
|
|
4626
|
+
function listed(names) {
|
|
4627
|
+
if (names.length <= 1) return names.join("");
|
|
4628
|
+
return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
|
|
4629
|
+
}
|
|
4630
|
+
function baseAnswer(step) {
|
|
4631
|
+
if (step.answer && step.answer.later !== true) return step.answer;
|
|
4632
|
+
return {
|
|
4633
|
+
choices: Object.fromEntries(step.card.choices.map((choice) => [choice.id, choice.selected])),
|
|
4634
|
+
fields: Object.fromEntries(step.card.fields.map((field) => [field.id, field.value]))
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
function StepCard({ step, ui: ui2, handlers: handlers2, current }) {
|
|
4638
|
+
const saved = ui2.setupDrafts.value.get(step.id);
|
|
4639
|
+
const draft = saved?.answer ?? baseAnswer(step);
|
|
4640
|
+
const stale = saved !== void 0 && saved.version !== step.card.version;
|
|
4641
|
+
const busy = ui2.setupBusy.value !== null;
|
|
4642
|
+
const error = ui2.setupError.value?.step === step.id ? ui2.setupError.value.message : null;
|
|
4643
|
+
const editing = ui2.setupEditing.value === step.id;
|
|
4644
|
+
const proposal = step.proposal;
|
|
4645
|
+
const looking = proposal?.status === "working";
|
|
4646
|
+
const edit = (next) => handlers2.editSetupDraft(step.id, next, saved?.version ?? step.card.version);
|
|
4647
|
+
const tell = ui2.setupTell.value.get(step.id) ?? "";
|
|
4648
|
+
const empty = step.card.choices.every((choice) => choice.options.length === 0) && step.card.fields.length === 0;
|
|
4649
|
+
const conversation = proposal?.conversation ?? [];
|
|
4650
|
+
const revising = conversation.at(-1)?.from === "you";
|
|
4651
|
+
const confirming = looking && ui2.setupConfirm.value === step.id;
|
|
4652
|
+
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;
|
|
4653
|
+
return el(
|
|
4654
|
+
"section",
|
|
4655
|
+
{ class: "onboarding__card", "data-current": String(current), "data-step": step.id, "aria-labelledby": `setup-${step.id}` },
|
|
4656
|
+
el("span", { class: "onboarding__dot onboarding__dot--open", "aria-hidden": "true" }),
|
|
4657
|
+
el("h2", { id: `setup-${step.id}`, tabIndex: -1 }, step.title),
|
|
4658
|
+
conversation.length ? el(
|
|
4659
|
+
"ol",
|
|
4660
|
+
{ class: "onboarding__talk" },
|
|
4661
|
+
conversation.map(
|
|
4662
|
+
(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)
|
|
4663
|
+
)
|
|
4664
|
+
) : step.card.why && !(looking && empty) ? el("p", { class: "onboarding__why" }, sparkle(), el("span", null, step.card.why)) : null,
|
|
4665
|
+
looking ? el(
|
|
4666
|
+
"div",
|
|
4667
|
+
{ class: "onboarding__looking", role: "status" },
|
|
4668
|
+
revising ? null : el("p", null, empty ? `Looking into ${step.topic}\u2026` : "Still looking. You can answer now, or wait for its pick."),
|
|
4669
|
+
steps(step)
|
|
4670
|
+
) : null,
|
|
4671
|
+
// Under what it said, so a reply reads as one, and what comes back lands here too.
|
|
4672
|
+
step.card.revisable && !looking ? el(
|
|
4673
|
+
"form",
|
|
4674
|
+
{
|
|
4675
|
+
class: "onboarding__tell",
|
|
4676
|
+
onSubmit: (event) => {
|
|
4677
|
+
event.preventDefault();
|
|
4678
|
+
if (tell.trim() && !busy) handlers2.reviseSetup(step.id, tell, draft);
|
|
4679
|
+
}
|
|
4680
|
+
},
|
|
4681
|
+
el("input", {
|
|
4682
|
+
type: "text",
|
|
4683
|
+
value: tell,
|
|
4684
|
+
"aria-label": step.card.reply ?? "Tell it what to change",
|
|
4685
|
+
placeholder: step.card.reply ?? "Tell it what to change",
|
|
4686
|
+
onInput: (event) => handlers2.editSetupTell(step.id, event.currentTarget.value)
|
|
4687
|
+
}),
|
|
4688
|
+
el("button", { type: "submit", class: "button", disabled: busy || tell.trim() === "" }, "Send")
|
|
4689
|
+
) : null,
|
|
4690
|
+
proposal?.status === "failed" ? el(
|
|
4691
|
+
"p",
|
|
4692
|
+
{ class: "onboarding__note" },
|
|
4693
|
+
`It couldn't look into this: ${proposal.error}. `,
|
|
4694
|
+
el("button", { type: "button", class: "link-button", disabled: busy, onClick: () => handlers2.retrySetup(step.id) }, "Try again")
|
|
4695
|
+
) : null,
|
|
4696
|
+
stale ? el(
|
|
4697
|
+
"p",
|
|
4698
|
+
{ class: "onboarding__note" },
|
|
4699
|
+
"It has a new suggestion since you started changing this. ",
|
|
4700
|
+
el("button", { type: "button", class: "link-button", onClick: () => handlers2.editSetupDraft(step.id, null, 0) }, "Use it")
|
|
4701
|
+
) : null,
|
|
4702
|
+
step.card.problem?.blocking ? problem(step, busy, handlers2) : null,
|
|
4703
|
+
el(
|
|
4704
|
+
"form",
|
|
4705
|
+
{
|
|
4706
|
+
class: "onboarding__form",
|
|
4707
|
+
onSubmit: (event) => {
|
|
4708
|
+
event.preventDefault();
|
|
4709
|
+
if (busy) return;
|
|
4710
|
+
if (looking && !confirming) return handlers2.confirmSetup(step.id);
|
|
4711
|
+
handlers2.answerSetup(step.id, draft);
|
|
4712
|
+
}
|
|
4713
|
+
},
|
|
4714
|
+
// Fields first: a card with both asks for the few words before the list.
|
|
4715
|
+
step.card.fields.length ? el(
|
|
4716
|
+
"div",
|
|
4717
|
+
{ class: "onboarding__fields" },
|
|
4718
|
+
step.card.fields.map(
|
|
4719
|
+
(field) => fieldView(field, draft.fields[field.id] ?? "", (value) => edit({ ...draft, fields: { ...draft.fields, [field.id]: value } }))
|
|
4720
|
+
)
|
|
4721
|
+
) : null,
|
|
4722
|
+
step.card.choices.map(
|
|
4723
|
+
(choice) => k(ChoiceView, {
|
|
4724
|
+
choice,
|
|
4725
|
+
picked: draft.choices[choice.id] ?? [],
|
|
4726
|
+
onPick: (values) => edit({ ...draft, choices: { ...draft.choices, [choice.id]: values } })
|
|
4727
|
+
})
|
|
4728
|
+
),
|
|
4729
|
+
step.card.problem && !step.card.problem.blocking ? problem(step, busy, handlers2) : null,
|
|
4730
|
+
error ? el("p", { class: "onboarding__error", role: "alert" }, error) : null,
|
|
4731
|
+
confirming ? el(
|
|
4732
|
+
"div",
|
|
4733
|
+
{ class: "onboarding__actions onboarding__confirm", role: "alert" },
|
|
4734
|
+
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?"),
|
|
4735
|
+
el("button", { type: "submit", class: "button button--primary", disabled: busy }, "Continue without it"),
|
|
4736
|
+
el("button", { type: "button", class: "button onboarding__quiet", onClick: () => handlers2.confirmSetup(null) }, "Wait for it")
|
|
4737
|
+
) : el(
|
|
4738
|
+
"div",
|
|
4739
|
+
{ class: "onboarding__actions" },
|
|
4740
|
+
el(
|
|
4741
|
+
"button",
|
|
4742
|
+
{ type: "submit", class: "button button--primary", disabled: held !== null, title: held ?? void 0 },
|
|
4743
|
+
ui2.setupBusy.value === step.id ? "Saving\u2026" : step.card.empty && step.card.fields.every((field) => !(draft.fields[field.id] ?? "").trim()) ? step.card.empty : "Continue"
|
|
4744
|
+
),
|
|
4745
|
+
step.card.later ? el(
|
|
4746
|
+
"button",
|
|
4747
|
+
{ type: "button", class: "button onboarding__quiet", disabled: busy, onClick: () => handlers2.answerSetup(step.id, { later: true }) },
|
|
4748
|
+
step.card.later
|
|
4749
|
+
) : null,
|
|
4750
|
+
editing ? el("button", { type: "button", class: "button onboarding__quiet", onClick: () => handlers2.editSetupStep(null) }, "Cancel") : null
|
|
4751
|
+
)
|
|
4752
|
+
)
|
|
4753
|
+
);
|
|
4754
|
+
}
|
|
4755
|
+
function problem(step, busy, handlers2) {
|
|
4756
|
+
const found = step.card.problem;
|
|
4757
|
+
return el(
|
|
4758
|
+
"div",
|
|
4759
|
+
{ class: "onboarding__problem", "data-blocking": String(found.blocking) },
|
|
4760
|
+
el("p", null, found.message),
|
|
4761
|
+
found.links?.length ? el(
|
|
4762
|
+
"p",
|
|
4763
|
+
{ class: "onboarding__links" },
|
|
4764
|
+
found.links.map((link) => el("a", { key: link.url, href: link.url, target: "_blank", rel: "noopener" }, link.label))
|
|
4765
|
+
) : null,
|
|
4766
|
+
found.command ? el("pre", null, el("code", null, found.command)) : null,
|
|
4767
|
+
el("button", { type: "button", class: "button", disabled: busy, onClick: () => handlers2.retrySetup(step.id) }, "Check again")
|
|
4768
|
+
);
|
|
4769
|
+
}
|
|
4770
|
+
function sparkle() {
|
|
4771
|
+
return el(
|
|
4772
|
+
"svg",
|
|
4773
|
+
{ class: "onboarding__sparkle", width: 14, height: 14, viewBox: "0 0 14 14", "aria-hidden": "true" },
|
|
4774
|
+
el("path", { d: "M7 0l1.6 5.4L14 7l-5.4 1.6L7 14l-1.6-5.4L0 7l5.4-1.6z", fill: "currentColor" })
|
|
4775
|
+
);
|
|
4776
|
+
}
|
|
4777
|
+
function ChoiceView({ choice, picked, onPick }) {
|
|
4778
|
+
const [adding, setAdding] = d2(false);
|
|
4779
|
+
const [typed, setTyped] = d2("");
|
|
4780
|
+
const known = new Set(choice.options.map((option) => option.value));
|
|
4781
|
+
const own = picked.filter((value) => !known.has(value));
|
|
4782
|
+
const toggle = (value) => {
|
|
4783
|
+
if (!choice.multiple) return onPick([value]);
|
|
4784
|
+
onPick(picked.includes(value) ? picked.filter((v4) => v4 !== value) : [...picked, value]);
|
|
4785
|
+
};
|
|
4786
|
+
const add = () => {
|
|
4787
|
+
const value = typed.trim();
|
|
4788
|
+
if (value) onPick(choice.multiple ? [...picked.filter((v4) => v4 !== value), value] : [value]);
|
|
4789
|
+
setTyped("");
|
|
4790
|
+
setAdding(false);
|
|
4791
|
+
};
|
|
4792
|
+
const adder = choice.custom ? adding ? el(
|
|
4793
|
+
"div",
|
|
4794
|
+
{ class: "onboarding__add" },
|
|
4795
|
+
el("input", {
|
|
4796
|
+
type: "text",
|
|
4797
|
+
value: typed,
|
|
4798
|
+
placeholder: choice.custom,
|
|
4799
|
+
"aria-label": choice.custom,
|
|
4800
|
+
ref: (input) => input?.focus(),
|
|
4801
|
+
onInput: (event) => setTyped(event.currentTarget.value),
|
|
4802
|
+
onKeyDown: (event) => {
|
|
4803
|
+
if (event.key === "Enter") {
|
|
4804
|
+
event.preventDefault();
|
|
4805
|
+
add();
|
|
4806
|
+
}
|
|
4807
|
+
if (event.key === "Escape") setAdding(false);
|
|
4808
|
+
}
|
|
4809
|
+
}),
|
|
4810
|
+
el("button", { type: "button", class: "button", onClick: add }, "Add")
|
|
4811
|
+
) : el("button", { type: "button", class: "onboarding__add-button", onClick: () => setAdding(true) }, choice.multiple ? choice.custom : "Other\u2026") : null;
|
|
4812
|
+
const label = choice.label ? el("legend", { class: "onboarding__legend" }, choice.label) : null;
|
|
4813
|
+
const rows = choice.multiple || choice.options.some((option) => option.detail);
|
|
4814
|
+
if (!rows) {
|
|
4815
|
+
return el(
|
|
4816
|
+
"fieldset",
|
|
4817
|
+
{ class: "onboarding__choice" },
|
|
4818
|
+
label,
|
|
4819
|
+
el(
|
|
4820
|
+
"div",
|
|
4821
|
+
{ class: "onboarding__pills", role: "radiogroup" },
|
|
4822
|
+
[...choice.options, ...own.map((value) => ({ value, label: value, detail: null, group: null }))].map(
|
|
4823
|
+
(option) => el(
|
|
4824
|
+
"button",
|
|
4825
|
+
{
|
|
4826
|
+
type: "button",
|
|
4827
|
+
key: option.value,
|
|
4828
|
+
role: "radio",
|
|
4829
|
+
class: "onboarding__pill",
|
|
4830
|
+
"aria-checked": String(picked.includes(option.value)),
|
|
4831
|
+
onClick: () => toggle(option.value)
|
|
4832
|
+
},
|
|
4833
|
+
option.label
|
|
4834
|
+
)
|
|
4835
|
+
),
|
|
4836
|
+
adder
|
|
4837
|
+
)
|
|
4838
|
+
);
|
|
4839
|
+
}
|
|
4840
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4841
|
+
for (const option of choice.options) groups.set(option.group, [...groups.get(option.group) ?? [], option]);
|
|
4842
|
+
if (own.length) groups.set("Added by you", own.map((value) => ({ value, label: value, detail: null, group: "Added by you" })));
|
|
4843
|
+
return el(
|
|
4844
|
+
"fieldset",
|
|
4845
|
+
{ class: "onboarding__choice" },
|
|
4846
|
+
label,
|
|
4847
|
+
el(
|
|
4848
|
+
"div",
|
|
4849
|
+
{ class: "onboarding__list" },
|
|
4850
|
+
[...groups].map(
|
|
4851
|
+
([group, options]) => el(
|
|
4852
|
+
"div",
|
|
4853
|
+
{ class: "onboarding__group", key: group ?? "" },
|
|
4854
|
+
group ? el("p", { class: "onboarding__group-name" }, group) : null,
|
|
4855
|
+
options.map(
|
|
4856
|
+
(option) => el(
|
|
4857
|
+
"label",
|
|
4858
|
+
{ class: "onboarding__option", key: option.value },
|
|
4859
|
+
el("input", {
|
|
4860
|
+
type: choice.multiple ? "checkbox" : "radio",
|
|
4861
|
+
name: choice.id,
|
|
4862
|
+
checked: picked.includes(option.value),
|
|
4863
|
+
onChange: () => toggle(option.value)
|
|
4864
|
+
}),
|
|
4865
|
+
el("span", { class: "onboarding__box", "data-kind": choice.multiple ? "check" : "radio", "aria-hidden": "true" }),
|
|
4866
|
+
el("span", { class: "onboarding__option-text" }, option.label, option.detail ? el("small", null, option.detail) : null)
|
|
4867
|
+
)
|
|
4868
|
+
)
|
|
4869
|
+
)
|
|
4870
|
+
),
|
|
4871
|
+
choice.options.length === 0 && own.length === 0 && !choice.custom ? el("p", { class: "onboarding__none" }, "Nothing to choose from.") : null,
|
|
4872
|
+
adder ? el("div", { class: "onboarding__adder" }, adder) : null
|
|
4873
|
+
)
|
|
4874
|
+
);
|
|
4875
|
+
}
|
|
4876
|
+
function fieldView(field, value, onInput) {
|
|
4877
|
+
return el(
|
|
4878
|
+
"div",
|
|
4879
|
+
{ class: "onboarding__field", key: field.id },
|
|
4880
|
+
el(
|
|
4881
|
+
"label",
|
|
4882
|
+
null,
|
|
4883
|
+
el("span", { class: "onboarding__field-label" }, field.label),
|
|
4884
|
+
el("input", {
|
|
4885
|
+
type: "text",
|
|
4886
|
+
value,
|
|
4887
|
+
placeholder: field.placeholder ?? void 0,
|
|
4888
|
+
onInput: (event) => onInput(event.currentTarget.value)
|
|
4889
|
+
})
|
|
4890
|
+
),
|
|
4891
|
+
field.help ? el("small", null, field.help) : null,
|
|
4892
|
+
// Offered, never filled in: a click puts one in the field, to edit or keep.
|
|
4893
|
+
field.suggestions?.length ? el(
|
|
4894
|
+
"ul",
|
|
4895
|
+
{ class: "onboarding__suggestions" },
|
|
4896
|
+
field.suggestions.map(
|
|
4897
|
+
(suggestion) => el(
|
|
4898
|
+
"li",
|
|
4899
|
+
{ key: suggestion.text },
|
|
4900
|
+
el(
|
|
4901
|
+
"button",
|
|
4902
|
+
{ type: "button", "aria-pressed": String(value === suggestion.text), onClick: () => onInput(suggestion.text) },
|
|
4903
|
+
el("span", null, suggestion.text),
|
|
4904
|
+
suggestion.detail ? el("small", null, suggestion.detail) : null
|
|
4905
|
+
)
|
|
4906
|
+
)
|
|
4907
|
+
)
|
|
4908
|
+
) : null
|
|
4909
|
+
);
|
|
4910
|
+
}
|
|
4911
|
+
function FinishCard({ flow, ui: ui2, handlers: handlers2 }) {
|
|
4912
|
+
const writes = flow.steps.find((step) => step.id === "cli")?.status === "answered";
|
|
4913
|
+
const busy = ui2.setupBusy.value !== null;
|
|
4914
|
+
const error = (ui2.setupError.value?.step === "finish" ? ui2.setupError.value.message : null) ?? flow.error;
|
|
4915
|
+
const sources = ui2.setupSources.value;
|
|
4916
|
+
return el(
|
|
4917
|
+
"section",
|
|
4918
|
+
{ class: "onboarding__card onboarding__finish", "data-current": "true", "aria-labelledby": "setup-finish" },
|
|
4919
|
+
el("span", { class: "onboarding__dot onboarding__dot--open", "aria-hidden": "true" }),
|
|
4920
|
+
el("h2", { id: "setup-finish", tabIndex: -1 }, writes ? "Ready for your first brief" : "Ready to finish"),
|
|
4921
|
+
el(
|
|
4922
|
+
"p",
|
|
4923
|
+
{ class: "onboarding__why" },
|
|
4924
|
+
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."
|
|
4925
|
+
),
|
|
4926
|
+
el(
|
|
4927
|
+
"details",
|
|
4928
|
+
{
|
|
4929
|
+
class: "onboarding__preview",
|
|
4930
|
+
onToggle: (event) => {
|
|
4931
|
+
if (event.currentTarget.open && sources === null) handlers2.loadSetupSources();
|
|
4932
|
+
}
|
|
4933
|
+
},
|
|
4934
|
+
el("summary", null, "What the morning agent reads each morning"),
|
|
4935
|
+
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.")
|
|
4936
|
+
),
|
|
4937
|
+
error ? el("p", { class: "onboarding__error", role: "alert" }, error) : null,
|
|
4938
|
+
el(
|
|
4939
|
+
"div",
|
|
4940
|
+
{ class: "onboarding__actions" },
|
|
4941
|
+
el(
|
|
4942
|
+
"button",
|
|
4943
|
+
{ type: "button", class: "button button--primary", disabled: busy, onClick: () => handlers2.finishSetup() },
|
|
4944
|
+
ui2.setupBusy.value === "finish" ? "Saving\u2026" : writes ? "Write my first brief" : "Finish setup"
|
|
4945
|
+
)
|
|
4946
|
+
)
|
|
4165
4947
|
);
|
|
4166
4948
|
}
|
|
4167
4949
|
|
|
@@ -4239,7 +5021,7 @@ function objectiveCard(state2, ui2, handlers2) {
|
|
|
4239
5021
|
)
|
|
4240
5022
|
);
|
|
4241
5023
|
}
|
|
4242
|
-
function objectiveForm(objective, blocker, handlers2
|
|
5024
|
+
function objectiveForm(objective, blocker, handlers2) {
|
|
4243
5025
|
return el(
|
|
4244
5026
|
"form",
|
|
4245
5027
|
{
|
|
@@ -4270,7 +5052,7 @@ ${blocker}`,
|
|
|
4270
5052
|
el("span", { class: "field__label" }, "Blocked on (optional)"),
|
|
4271
5053
|
el("input", { class: "field__input", name: "blocker", defaultValue: blocker, placeholder: "What is in the way today, if anything", autocomplete: "off" })
|
|
4272
5054
|
),
|
|
4273
|
-
el("div", { class: "objective-form__actions" }, el("button", { type: "submit", class: "button button--primary" },
|
|
5055
|
+
el("div", { class: "objective-form__actions" }, el("button", { type: "submit", class: "button button--primary" }, "Save objective"))
|
|
4274
5056
|
);
|
|
4275
5057
|
}
|
|
4276
5058
|
function sourcesCard(state2, ui2, handlers2) {
|
|
@@ -4346,7 +5128,7 @@ function flagValue(raw) {
|
|
|
4346
5128
|
}
|
|
4347
5129
|
function settingField(setting, draft, handlers2) {
|
|
4348
5130
|
const id = `setting-${setting.key}`;
|
|
4349
|
-
const locked = setting.source === "
|
|
5131
|
+
const locked = setting.source === "fixed";
|
|
4350
5132
|
const value = shownValue(setting, draft);
|
|
4351
5133
|
const onChange = (event) => {
|
|
4352
5134
|
const next = event.currentTarget.value;
|
|
@@ -4354,13 +5136,13 @@ function settingField(setting, draft, handlers2) {
|
|
|
4354
5136
|
};
|
|
4355
5137
|
let input;
|
|
4356
5138
|
if (setting.kind === "choice" || setting.kind === "flag") {
|
|
4357
|
-
const
|
|
5139
|
+
const choices = setting.kind === "flag" ? [{ value: "on", label: "On" }, { value: "off", label: "Off" }] : setting.choices ?? [];
|
|
4358
5140
|
const current = setting.kind === "flag" ? flagValue(value) : value;
|
|
4359
5141
|
input = el(
|
|
4360
5142
|
"select",
|
|
4361
5143
|
{ class: "field__input", id, disabled: locked, value: current, onChange },
|
|
4362
5144
|
el("option", { value: "" }, `Default: ${setting.fallback}`),
|
|
4363
|
-
|
|
5145
|
+
choices.map((choice) => el("option", { value: choice.value, key: choice.value }, choice.label))
|
|
4364
5146
|
);
|
|
4365
5147
|
} else {
|
|
4366
5148
|
input = el("input", {
|
|
@@ -4376,7 +5158,7 @@ function settingField(setting, draft, handlers2) {
|
|
|
4376
5158
|
onInput: onChange
|
|
4377
5159
|
});
|
|
4378
5160
|
}
|
|
4379
|
-
const note =
|
|
5161
|
+
const note = locked ? "Fixed in the demo, so that nothing in it runs." : null;
|
|
4380
5162
|
return el(
|
|
4381
5163
|
"div",
|
|
4382
5164
|
{ class: "field", key: setting.key, "data-changed": String(draft.has(setting.key)) },
|
|
@@ -4399,9 +5181,9 @@ function calendarPicker(state2, ui2, handlers2, settings) {
|
|
|
4399
5181
|
);
|
|
4400
5182
|
}
|
|
4401
5183
|
const setting = settings.find((entry) => entry.key === "DAILY_FOCUS_CALENDARS");
|
|
4402
|
-
if (!setting || setting.source === "
|
|
5184
|
+
if (!setting || setting.source === "fixed") return null;
|
|
4403
5185
|
const chosen = shownValue(setting, ui2.settingsDraft.value).split(",").map((name) => name.trim()).filter(Boolean);
|
|
4404
|
-
const
|
|
5186
|
+
const listed2 = ui2.calendars.value;
|
|
4405
5187
|
const toggle = (title, on) => {
|
|
4406
5188
|
const others = chosen.filter((name) => name.toLowerCase() !== title.toLowerCase());
|
|
4407
5189
|
const next = on ? [...others, title] : others;
|
|
@@ -4411,11 +5193,11 @@ function calendarPicker(state2, ui2, handlers2, settings) {
|
|
|
4411
5193
|
"div",
|
|
4412
5194
|
{ class: "calendar-picker" },
|
|
4413
5195
|
el("button", { type: "button", class: "button", onClick: () => handlers2.listCalendars() }, "List the calendars on this Mac"),
|
|
4414
|
-
typeof
|
|
4415
|
-
Array.isArray(
|
|
5196
|
+
typeof listed2 === "string" ? el("p", { class: "settings-card__warning" }, listed2) : null,
|
|
5197
|
+
Array.isArray(listed2) ? el(
|
|
4416
5198
|
"ul",
|
|
4417
5199
|
{ class: "calendar-picker__list" },
|
|
4418
|
-
|
|
5200
|
+
listed2.map(
|
|
4419
5201
|
(entry) => el(
|
|
4420
5202
|
"li",
|
|
4421
5203
|
{ key: `${entry.title}
|
|
@@ -4458,248 +5240,57 @@ function saveBar(ui2, handlers2) {
|
|
|
4458
5240
|
}
|
|
4459
5241
|
|
|
4460
5242
|
// client/setup.ts
|
|
4461
|
-
|
|
4462
|
-
function SetupCard({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4463
|
-
const setup = state2.setup;
|
|
5243
|
+
function FirstBriefCard({ state: state2, handlers: handlers2 }) {
|
|
4464
5244
|
const agent = state2.agentRun;
|
|
4465
|
-
const
|
|
4466
|
-
const
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
el(
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
),
|
|
4490
|
-
// A choice, so neither answer is drawn as the one to pick.
|
|
4491
|
-
choices([
|
|
4492
|
-
["Work", () => handlers2.chooseSettings({ DAILY_FOCUS_PROFILE: "work" })],
|
|
4493
|
-
["Personal life", () => handlers2.chooseSettings({ DAILY_FOCUS_PROFILE: "personal" })]
|
|
4494
|
-
])
|
|
4495
|
-
]
|
|
4496
|
-
),
|
|
4497
|
-
step(
|
|
4498
|
-
Boolean(state2.focus?.objective),
|
|
4499
|
-
"What are you trying to achieve?",
|
|
4500
|
-
state2.focus?.objective ? el("p", null, state2.focus.objective) : [
|
|
4501
|
-
el("p", null, "The agent ranks the day against it. Leave it for now if nothing stands out."),
|
|
4502
|
-
objectiveForm("", "", handlers2, "Set objective")
|
|
4503
|
-
]
|
|
4504
|
-
),
|
|
4505
|
-
step(
|
|
4506
|
-
agent.cli !== null,
|
|
4507
|
-
"Which agent writes the brief?",
|
|
4508
|
-
agent.cli ? [
|
|
4509
|
-
el(
|
|
4510
|
-
"p",
|
|
4511
|
-
null,
|
|
4512
|
-
`${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."
|
|
4513
|
-
),
|
|
4514
|
-
agentAdvanced(agent.cli, ui2, handlers2)
|
|
4515
|
-
] : agentChoice(state2, handlers2)
|
|
4516
|
-
),
|
|
4517
|
-
step(setup.sources.exists && setup.sources.placeholders === 0, "Who are you, and where should it look?", sourcesStep(state2, ui2, handlers2)),
|
|
4518
|
-
step(null, "What else should the dashboard show?", boards(state2, handlers2)),
|
|
4519
|
-
step(
|
|
4520
|
-
null,
|
|
4521
|
-
"Your first brief",
|
|
4522
|
-
[
|
|
4523
|
-
el(
|
|
4524
|
-
"p",
|
|
4525
|
-
null,
|
|
4526
|
-
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.")
|
|
4527
|
-
),
|
|
4528
|
-
placeholders ? el("p", { class: "setup__note setup__note--todo" }, "Replace the source list's placeholders first, or the agent goes looking for them.") : null,
|
|
4529
|
-
el(
|
|
4530
|
-
"button",
|
|
4531
|
-
{
|
|
4532
|
-
type: "button",
|
|
4533
|
-
class: "button button--primary",
|
|
4534
|
-
disabled: !agent.enabled || running || state2.restart === "waiting" || placeholders,
|
|
4535
|
-
title: !agent.enabled ? "Choose the agent first" : placeholders ? "Replace the source list's placeholders first" : void 0,
|
|
4536
|
-
onClick: () => handlers2.runAgent()
|
|
4537
|
-
},
|
|
4538
|
-
running ? "Running\u2026" : "Run the morning agent now"
|
|
4539
|
-
)
|
|
4540
|
-
]
|
|
4541
|
-
)
|
|
4542
|
-
)
|
|
4543
|
-
);
|
|
4544
|
-
}
|
|
4545
|
-
function step(done, title, body) {
|
|
4546
|
-
return el(
|
|
4547
|
-
"li",
|
|
4548
|
-
{ class: "setup__step", "data-done": done === null ? void 0 : String(done) },
|
|
4549
|
-
el(
|
|
4550
|
-
"span",
|
|
4551
|
-
{ class: "setup__mark", "aria-label": done === null ? void 0 : done ? "Done" : "To do" },
|
|
4552
|
-
done === null ? "\xB7" : done ? "\u2713" : "\u25CB"
|
|
4553
|
-
),
|
|
4554
|
-
el("div", { class: "setup__body" }, el("h3", { class: "setup__step-title" }, title), body)
|
|
4555
|
-
);
|
|
4556
|
-
}
|
|
4557
|
-
function choices(options) {
|
|
4558
|
-
return el(
|
|
4559
|
-
"div",
|
|
4560
|
-
{ class: "setup__choices" },
|
|
4561
|
-
options.map(([label, onClick, title]) => el("button", { type: "button", class: "button", key: label, title, onClick }, label))
|
|
4562
|
-
);
|
|
4563
|
-
}
|
|
4564
|
-
function agentChoice(state2, handlers2) {
|
|
4565
|
-
const found = ["claude", "codex"].filter((cli) => state2.setup.clis[cli]);
|
|
4566
|
-
if (found.length === 0) {
|
|
4567
|
-
return [
|
|
4568
|
-
el(
|
|
4569
|
-
"p",
|
|
4570
|
-
null,
|
|
4571
|
-
"The brief is written by a coding-agent CLI you already use. Install ",
|
|
4572
|
-
el("a", { href: "https://code.claude.com/docs", target: "_blank", rel: "noopener" }, "Claude Code"),
|
|
4573
|
-
" or the ",
|
|
4574
|
-
el("a", { href: "https://developers.openai.com/codex/cli", target: "_blank", rel: "noopener" }, "Codex CLI"),
|
|
4575
|
-
", log in, and restart the dashboard; it looks for both each time it starts."
|
|
4576
|
-
)
|
|
5245
|
+
const last = agent.last;
|
|
5246
|
+
const running = last?.status === "running";
|
|
5247
|
+
const restarting = state2.restart === "waiting";
|
|
5248
|
+
let title;
|
|
5249
|
+
let text;
|
|
5250
|
+
let actions;
|
|
5251
|
+
if (restarting) {
|
|
5252
|
+
title = "Saving your settings";
|
|
5253
|
+
text = "The dashboard restarts to apply them, then starts on your first brief.";
|
|
5254
|
+
actions = [];
|
|
5255
|
+
} else if (!agent.enabled) {
|
|
5256
|
+
title = "Choose who writes your brief";
|
|
5257
|
+
text = "Each morning a coding-agent CLI you already use writes it: Claude Code or Codex. The boards work meanwhile.";
|
|
5258
|
+
actions = [el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.openSettings("agent") }, "Choose in Settings")];
|
|
5259
|
+
} else if (running) {
|
|
5260
|
+
title = "Writing your first brief";
|
|
5261
|
+
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.";
|
|
5262
|
+
actions = [el("button", { type: "button", class: "button", onClick: () => handlers2.openRun(last.id) }, "Watch it work")];
|
|
5263
|
+
} else if (last && last.status !== "done") {
|
|
5264
|
+
title = "Your first brief didn\u2019t finish";
|
|
5265
|
+
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.";
|
|
5266
|
+
actions = [
|
|
5267
|
+
el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.runAgent() }, "Try again"),
|
|
5268
|
+
el("button", { type: "button", class: "button", onClick: () => handlers2.openRun(last.id) }, "Read its report")
|
|
4577
5269
|
];
|
|
5270
|
+
} else if (last) {
|
|
5271
|
+
title = "Your first brief didn\u2019t arrive";
|
|
5272
|
+
text = "The run finished without a brief this dashboard could read. Its report says why.";
|
|
5273
|
+
actions = [
|
|
5274
|
+
el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.runAgent() }, "Run it again"),
|
|
5275
|
+
el("button", { type: "button", class: "button", onClick: () => handlers2.openRun(last.id) }, "Read its report")
|
|
5276
|
+
];
|
|
5277
|
+
} else {
|
|
5278
|
+
title = "Write your first brief";
|
|
5279
|
+
text = "It takes a few minutes. After that it runs by itself each scheduled morning.";
|
|
5280
|
+
actions = [el("button", { type: "button", class: "button button--primary", onClick: () => handlers2.runAgent() }, "Write it now")];
|
|
4578
5281
|
}
|
|
4579
|
-
return [
|
|
4580
|
-
el("p", null, "The dashboard runs a coding-agent CLI you already use, headless, each morning. Found on this machine:"),
|
|
4581
|
-
choices(
|
|
4582
|
-
found.map(
|
|
4583
|
-
(cli) => [`Use ${CLI_LABEL[cli]}`, () => handlers2.chooseSettings({ DAILY_FOCUS_AGENT: cli }), state2.setup.clis[cli] ?? void 0]
|
|
4584
|
-
)
|
|
4585
|
-
)
|
|
4586
|
-
];
|
|
4587
|
-
}
|
|
4588
|
-
var AGENT_ADVANCED = "setup:agent-advanced";
|
|
4589
|
-
function agentAdvanced(cli, ui2, handlers2) {
|
|
4590
|
-
const keys = ["DAILY_FOCUS_AGENT_MODEL", "DAILY_FOCUS_AGENT_EFFORT", ...cli === "claude" ? ["DAILY_FOCUS_AGENT_TOOLS"] : []];
|
|
4591
|
-
const page = ui2.settings.value;
|
|
4592
|
-
const draft = ui2.settingsDraft.value;
|
|
4593
|
-
const changed = keys.some((key) => draft.has(key));
|
|
4594
|
-
const error = changed ? ui2.settingsError.value : null;
|
|
4595
|
-
return el(
|
|
4596
|
-
"details",
|
|
4597
|
-
{
|
|
4598
|
-
class: "setup__advanced",
|
|
4599
|
-
open: ui2.openDrawers.value.has(AGENT_ADVANCED),
|
|
4600
|
-
onToggle: (event) => {
|
|
4601
|
-
const open = event.currentTarget.open;
|
|
4602
|
-
if (open && !ui2.settings.peek()) handlers2.loadSettings();
|
|
4603
|
-
handlers2.toggleDrawer(AGENT_ADVANCED, open);
|
|
4604
|
-
}
|
|
4605
|
-
},
|
|
4606
|
-
el("summary", null, cli === "claude" ? "Advanced: model, effort and tools" : "Advanced: model and effort"),
|
|
4607
|
-
page ? [
|
|
4608
|
-
...keys.flatMap((key) => {
|
|
4609
|
-
const setting = page.settings.find((entry) => entry.key === key);
|
|
4610
|
-
return setting ? [settingField(setting, draft, handlers2)] : [];
|
|
4611
|
-
}),
|
|
4612
|
-
error ? el("p", { class: "setup__note setup__note--todo", role: "alert" }, error) : null,
|
|
4613
|
-
el(
|
|
4614
|
-
"div",
|
|
4615
|
-
{ class: "setup__advanced-actions" },
|
|
4616
|
-
el(
|
|
4617
|
-
"button",
|
|
4618
|
-
{ type: "button", class: "button", disabled: !changed || ui2.settingsSaving.value, onClick: () => handlers2.saveSettings(keys) },
|
|
4619
|
-
ui2.settingsSaving.value ? "Saving\u2026" : "Save"
|
|
4620
|
-
)
|
|
4621
|
-
)
|
|
4622
|
-
] : el("p", null, "Loading the settings\u2026")
|
|
4623
|
-
);
|
|
4624
|
-
}
|
|
4625
|
-
var SOURCES_EDITOR = "setup:sources";
|
|
4626
|
-
function sourcesStep(state2, ui2, handlers2) {
|
|
4627
|
-
const sources = state2.setup.sources;
|
|
4628
|
-
const open = ui2.openDrawers.value.has(SOURCES_EDITOR);
|
|
4629
|
-
const toggle = () => {
|
|
4630
|
-
if (!open && !ui2.texts.value.sources) handlers2.loadText("sources");
|
|
4631
|
-
handlers2.toggleDrawer(SOURCES_EDITOR, !open);
|
|
4632
|
-
};
|
|
4633
|
-
return [
|
|
4634
|
-
el(
|
|
4635
|
-
"p",
|
|
4636
|
-
null,
|
|
4637
|
-
"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."
|
|
4638
|
-
),
|
|
4639
|
-
sources.exists ? el(
|
|
4640
|
-
"p",
|
|
4641
|
-
{ class: sources.placeholders > 0 ? "setup__note setup__note--todo" : "setup__note" },
|
|
4642
|
-
sources.placeholders > 0 ? `It still has ${sources.placeholders} of the template's placeholders. Replace them, or delete what doesn't apply.` : "Written."
|
|
4643
|
-
) : el("p", { class: "setup__note" }, "It starts from a template, with what gh and git know about you already filled in."),
|
|
4644
|
-
el(
|
|
4645
|
-
"button",
|
|
4646
|
-
{ type: "button", class: "button", "aria-expanded": String(open), onClick: toggle },
|
|
4647
|
-
open ? "Close" : sources.exists ? "Edit it" : "Write it"
|
|
4648
|
-
),
|
|
4649
|
-
open ? el("div", { class: "setup__editor" }, textEditor("sources", ui2.texts.value.sources, handlers2, "sources.md")) : null
|
|
4650
|
-
];
|
|
4651
|
-
}
|
|
4652
|
-
var HEALTH_MARK = { ok: "\u2713", checking: "\u2026", problem: "!", todo: "\u25CB" };
|
|
4653
|
-
var HEALTH_LABEL = {
|
|
4654
|
-
ok: "Connected",
|
|
4655
|
-
checking: "Checking",
|
|
4656
|
-
problem: "Not working",
|
|
4657
|
-
todo: "To do"
|
|
4658
|
-
};
|
|
4659
|
-
function boardHealth(board, via, service) {
|
|
4660
|
-
if (board.reason) return ["problem", board.reason.message];
|
|
4661
|
-
if (board.fetchedAt) return ["ok", `Connected, through ${via}.`];
|
|
4662
|
-
return ["checking", `Checking ${service} through ${via}\u2026`];
|
|
4663
|
-
}
|
|
4664
|
-
function boards(state2, handlers2) {
|
|
4665
|
-
const rows = [];
|
|
4666
|
-
const off = (key, label) => el("button", { type: "button", class: "button", onClick: () => handlers2.chooseSettings({ [key]: "off" }) }, `Turn off ${label}`);
|
|
4667
|
-
if (state2.board.enabled) {
|
|
4668
|
-
rows.push(boardRow("Pull requests", ...boardHealth(state2.board, "gh", "GitHub"), off("DAILY_FOCUS_GITHUB", "the pull request board")));
|
|
4669
|
-
}
|
|
4670
|
-
if (state2.tickets.enabled) {
|
|
4671
|
-
rows.push(boardRow("Jira tickets", ...boardHealth(state2.tickets, "acli", "Jira"), off("DAILY_FOCUS_JIRA", "the Jira board")));
|
|
4672
|
-
}
|
|
4673
|
-
const calendar = state2.setup.calendar;
|
|
4674
|
-
if (calendar.supported && state2.agendaSource) {
|
|
4675
|
-
const live = state2.agendaSource.live;
|
|
4676
|
-
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"];
|
|
4677
|
-
rows.push(
|
|
4678
|
-
boardRow(
|
|
4679
|
-
"Calendar",
|
|
4680
|
-
health,
|
|
4681
|
-
text,
|
|
4682
|
-
calendar.built && calendar.chosen === 0 ? el("button", { type: "button", class: "button", onClick: () => handlers2.openSettings("calendar") }, "Choose calendars") : off("DAILY_FOCUS_CALENDAR", "the live agenda")
|
|
4683
|
-
)
|
|
4684
|
-
);
|
|
4685
|
-
}
|
|
4686
|
-
if (rows.length === 0) return el("p", null, "Nothing else is switched on. The boards can be turned on in Settings.");
|
|
4687
|
-
return el("ul", { class: "setup__boards" }, rows);
|
|
4688
|
-
}
|
|
4689
|
-
function boardRow(name, health, text, action) {
|
|
4690
5282
|
return el(
|
|
4691
|
-
"
|
|
4692
|
-
{ class: "
|
|
4693
|
-
el("
|
|
4694
|
-
el("
|
|
4695
|
-
el("
|
|
4696
|
-
action
|
|
5283
|
+
"section",
|
|
5284
|
+
{ class: "first-brief", id: "first-brief", "aria-labelledby": "first-brief-title", "data-state": running ? "running" : "idle" },
|
|
5285
|
+
el("h2", { class: "first-brief__title", id: "first-brief-title" }, title),
|
|
5286
|
+
el("p", { class: "first-brief__text" }, text),
|
|
5287
|
+
actions.length ? el("div", { class: "first-brief__actions" }, actions) : null
|
|
4697
5288
|
);
|
|
4698
5289
|
}
|
|
4699
5290
|
|
|
4700
5291
|
// client/today.ts
|
|
4701
5292
|
function TodayView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4702
|
-
if (state2.setup?.needed) return [k(
|
|
5293
|
+
if (state2.setup?.needed) return [k(FirstBriefCard, { state: state2, handlers: handlers2 })];
|
|
4703
5294
|
return [renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
|
|
4704
5295
|
}
|
|
4705
5296
|
function renderObjective(state2, handlers2 = null) {
|
|
@@ -4756,15 +5347,15 @@ function focusTimeStat(agenda, now) {
|
|
|
4756
5347
|
const tone = minutes === 0 ? "critical" : minutes < 60 ? "critical" : null;
|
|
4757
5348
|
return stat("Focus time left", minutes === 0 ? "None" : formatDuration(minutes), tone, `Day ends ${agenda.dayEnd}`);
|
|
4758
5349
|
}
|
|
4759
|
-
function objectiveStat(
|
|
4760
|
-
if (!
|
|
4761
|
-
if (
|
|
5350
|
+
function objectiveStat(progress2) {
|
|
5351
|
+
if (!progress2) return null;
|
|
5352
|
+
if (progress2.workingDaysSince === null) {
|
|
4762
5353
|
return stat("Since objective progress", "\u2014", null, "Nothing recorded yet");
|
|
4763
5354
|
}
|
|
4764
|
-
const days =
|
|
5355
|
+
const days = progress2.workingDaysSince;
|
|
4765
5356
|
const tone = days === 0 ? "good" : days >= 3 ? "critical" : null;
|
|
4766
5357
|
const value = days === 0 ? "Today" : days === 1 ? "1 day" : `${days} days`;
|
|
4767
|
-
return stat("Since objective progress", value, tone,
|
|
5358
|
+
return stat("Since objective progress", value, tone, progress2.lastTitle);
|
|
4768
5359
|
}
|
|
4769
5360
|
function renderHeadline(state2) {
|
|
4770
5361
|
const headline = state2.brief.headline;
|
|
@@ -4819,6 +5410,7 @@ function App({ ui: ui2, handlers: handlers2 }) {
|
|
|
4819
5410
|
const view = ui2.view.value;
|
|
4820
5411
|
const run = current ? detailRun(current, ui2) : null;
|
|
4821
5412
|
const row = current && !run ? detailRow(current, ui2) : null;
|
|
5413
|
+
if (current?.onboarding) return [k(OnboardingView, { state: current, ui: ui2, handlers: handlers2 }), k(Toast, { ui: ui2, handlers: handlers2 })];
|
|
4822
5414
|
return [
|
|
4823
5415
|
el("a", { class: "skip-link", href: "#main" }, "Skip to content"),
|
|
4824
5416
|
k(Header, { state: current, ui: ui2, handlers: handlers2 }),
|
|
@@ -4982,12 +5574,12 @@ function installKeyboard(handlers2) {
|
|
|
4982
5574
|
}
|
|
4983
5575
|
const item = selectedItem();
|
|
4984
5576
|
if (!item) return;
|
|
4985
|
-
const
|
|
5577
|
+
const boardRow = ui.view.value !== "today";
|
|
4986
5578
|
const status = "status" in item ? item.status : null;
|
|
4987
5579
|
switch (event.key) {
|
|
4988
5580
|
case "e":
|
|
4989
5581
|
event.preventDefault();
|
|
4990
|
-
if (!
|
|
5582
|
+
if (!boardRow && status === "open") handlers2.onAction(item.id, "done");
|
|
4991
5583
|
break;
|
|
4992
5584
|
case "s":
|
|
4993
5585
|
event.preventDefault();
|
|
@@ -4995,7 +5587,7 @@ function installKeyboard(handlers2) {
|
|
|
4995
5587
|
break;
|
|
4996
5588
|
case "x":
|
|
4997
5589
|
event.preventDefault();
|
|
4998
|
-
if (!
|
|
5590
|
+
if (!boardRow && status === "open") handlers2.onAction(item.id, "dismiss");
|
|
4999
5591
|
break;
|
|
5000
5592
|
case "n":
|
|
5001
5593
|
event.preventDefault();
|
|
@@ -5025,6 +5617,9 @@ R(k(App, { ui, handlers }), root);
|
|
|
5025
5617
|
j3(() => {
|
|
5026
5618
|
document.body.dataset.view = ui.view.value;
|
|
5027
5619
|
});
|
|
5620
|
+
j3(() => {
|
|
5621
|
+
document.body.dataset.onboarding = String(Boolean(state.value?.onboarding));
|
|
5622
|
+
});
|
|
5028
5623
|
j3(() => {
|
|
5029
5624
|
document.body.dataset.offline = String(ui.connectionError.value !== null);
|
|
5030
5625
|
});
|