daily-focus 1.1.1-dev.2 → 1.2.0-dev.10
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 +34 -20
- package/SETUP.md +103 -21
- package/dist/{audit-W3O6CLOF.js → audit-7ZCLKF46.js} +10 -4
- package/dist/{chunk-Q5OKXJXT.js → chunk-64XB7UDN.js} +1 -1
- package/dist/{chunk-OPASVYLR.js → chunk-J7ZYVUG6.js} +5 -6
- package/dist/chunk-ODJGQCM3.js +1003 -0
- package/dist/{chunk-UM2EPNWR.js → chunk-RZNQJ6UG.js} +123 -282
- package/dist/chunk-Z4SFMMRQ.js +107 -0
- package/dist/cli.js +9 -8
- package/dist/{demo-OQLT3QWP.js → demo-BYLLMKSQ.js} +135 -32
- package/dist/{init-DEVCAQZZ.js → init-EMZDG2I7.js} +1 -9
- package/dist/{seed-SMKNFYTC.js → seed-JU4LQ33D.js} +3 -3
- package/dist/{server-5O3LQVEY.js → server-WQYUOKB3.js} +459 -385
- package/dist/{service-6ZFOHPWN.js → service-PUJ3YDH5.js} +1 -1
- package/package.json +2 -2
- package/prompts/README.md +2 -3
- package/prompts/assistant.md +14 -3
- package/prompts/morning-brief-personal.md +1 -25
- package/prompts/morning-brief-work.md +8 -33
- package/public/app.js +506 -431
- package/public/style.css +198 -181
- package/dist/chunk-GFCWPYEI.js +0 -456
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) {
|
|
@@ -1010,8 +1049,8 @@ function subscribe(onState, onError) {
|
|
|
1010
1049
|
source.addEventListener("open", () => onError(null));
|
|
1011
1050
|
return () => source.close();
|
|
1012
1051
|
}
|
|
1013
|
-
function
|
|
1014
|
-
return post("/api/
|
|
1052
|
+
function postTimeZone(timeZone) {
|
|
1053
|
+
return post("/api/timezone", { timeZone });
|
|
1015
1054
|
}
|
|
1016
1055
|
function postBoardRefresh() {
|
|
1017
1056
|
return post("/api/board/refresh");
|
|
@@ -1077,40 +1116,35 @@ var VIEWS = ["today", "board", "tickets", "settings"];
|
|
|
1077
1116
|
var RUN_PREFIX = "run:";
|
|
1078
1117
|
var PANEL_PREFIX = "panel:";
|
|
1079
1118
|
var VIEW_KEY = "daily-focus:view";
|
|
1080
|
-
var UNATTENDED_SEEN_KEY = "daily-focus:unattended-seen";
|
|
1081
1119
|
var AGENT_RUN_SEEN_KEY = "daily-focus:agent-run-seen";
|
|
1082
|
-
var FOCUS_MODE_KEY = "daily-focus:focus-mode";
|
|
1083
1120
|
var DETAIL_KEY = "daily-focus:detail-for";
|
|
1084
|
-
var state =
|
|
1121
|
+
var state = y3(null);
|
|
1085
1122
|
function createUi(initial = {}) {
|
|
1086
1123
|
return {
|
|
1087
|
-
selectedId:
|
|
1088
|
-
clickedId:
|
|
1089
|
-
pending:
|
|
1090
|
-
menuFor:
|
|
1091
|
-
statusFor:
|
|
1092
|
-
detailFor:
|
|
1093
|
-
noteDraft:
|
|
1094
|
-
assistantDraft:
|
|
1095
|
-
focusField:
|
|
1096
|
-
openDrawers:
|
|
1097
|
-
ticketMode:
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
settingsSaving: y2(initial.settingsSaving ?? false),
|
|
1112
|
-
texts: y2(initial.texts ?? {}),
|
|
1113
|
-
calendars: y2(initial.calendars ?? null)
|
|
1124
|
+
selectedId: y3(initial.selectedId ?? null),
|
|
1125
|
+
clickedId: y3(initial.clickedId ?? null),
|
|
1126
|
+
pending: y3(initial.pending ?? /* @__PURE__ */ new Set()),
|
|
1127
|
+
menuFor: y3(initial.menuFor ?? null),
|
|
1128
|
+
statusFor: y3(initial.statusFor ?? null),
|
|
1129
|
+
detailFor: y3(initial.detailFor ?? null),
|
|
1130
|
+
noteDraft: y3(initial.noteDraft ?? ""),
|
|
1131
|
+
assistantDraft: y3(initial.assistantDraft ?? ""),
|
|
1132
|
+
focusField: y3(initial.focusField ?? null),
|
|
1133
|
+
openDrawers: y3(initial.openDrawers ?? /* @__PURE__ */ new Set()),
|
|
1134
|
+
ticketMode: y3(initial.ticketMode ?? "sync"),
|
|
1135
|
+
agentReportSeen: y3(initial.agentReportSeen ?? null),
|
|
1136
|
+
messageOpen: y3(initial.messageOpen ?? /* @__PURE__ */ new Map()),
|
|
1137
|
+
view: y3(initial.view ?? "today"),
|
|
1138
|
+
theme: y3(initial.theme ?? "system"),
|
|
1139
|
+
toast: y3(initial.toast ?? null),
|
|
1140
|
+
connectionError: y3(initial.connectionError ?? null),
|
|
1141
|
+
settingsReturn: y3(initial.settingsReturn ?? "today"),
|
|
1142
|
+
settings: y3(initial.settings ?? null),
|
|
1143
|
+
settingsDraft: y3(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
|
|
1144
|
+
settingsError: y3(initial.settingsError ?? null),
|
|
1145
|
+
settingsSaving: y3(initial.settingsSaving ?? false),
|
|
1146
|
+
texts: y3(initial.texts ?? {}),
|
|
1147
|
+
calendars: y3(initial.calendars ?? null)
|
|
1114
1148
|
};
|
|
1115
1149
|
}
|
|
1116
1150
|
function isView(value) {
|
|
@@ -1122,9 +1156,7 @@ function remembered() {
|
|
|
1122
1156
|
return {
|
|
1123
1157
|
// Settings is a page to visit, so a reload returns to the view underneath.
|
|
1124
1158
|
view: isView(view) && view !== "settings" ? view : "today",
|
|
1125
|
-
unattendedSeen: localStorage.getItem(UNATTENDED_SEEN_KEY),
|
|
1126
1159
|
agentReportSeen: localStorage.getItem(AGENT_RUN_SEEN_KEY),
|
|
1127
|
-
focusMode: localStorage.getItem(FOCUS_MODE_KEY) === "1",
|
|
1128
1160
|
detailFor: sessionStorage.getItem(DETAIL_KEY)
|
|
1129
1161
|
};
|
|
1130
1162
|
} catch {
|
|
@@ -1292,197 +1324,6 @@ function ThemeToggle({ ui: ui2, handlers: handlers2 }) {
|
|
|
1292
1324
|
);
|
|
1293
1325
|
}
|
|
1294
1326
|
|
|
1295
|
-
// client/format.ts
|
|
1296
|
-
var timeFmt = new Intl.DateTimeFormat(void 0, { hour: "2-digit", minute: "2-digit" });
|
|
1297
|
-
var dayFmt = new Intl.DateTimeFormat(void 0, {
|
|
1298
|
-
weekday: "long",
|
|
1299
|
-
day: "numeric",
|
|
1300
|
-
month: "long"
|
|
1301
|
-
});
|
|
1302
|
-
var shortDayFmt = new Intl.DateTimeFormat(void 0, { day: "numeric", month: "short" });
|
|
1303
|
-
var shortDayYearFmt = new Intl.DateTimeFormat(void 0, { day: "numeric", month: "short", year: "numeric" });
|
|
1304
|
-
var weekdayFmt = new Intl.DateTimeFormat(void 0, { weekday: "long" });
|
|
1305
|
-
function parseDate(value) {
|
|
1306
|
-
if (typeof value !== "string" || value === "") return null;
|
|
1307
|
-
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
1308
|
-
if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
|
|
1309
|
-
const d4 = new Date(value);
|
|
1310
|
-
return Number.isNaN(d4.getTime()) ? null : d4;
|
|
1311
|
-
}
|
|
1312
|
-
function formatTime(value) {
|
|
1313
|
-
const d4 = parseDate(value);
|
|
1314
|
-
return d4 ? timeFmt.format(d4) : "";
|
|
1315
|
-
}
|
|
1316
|
-
function formatDay(value) {
|
|
1317
|
-
const d4 = parseDate(value);
|
|
1318
|
-
return d4 ? dayFmt.format(d4) : "";
|
|
1319
|
-
}
|
|
1320
|
-
function formatShortDay(value) {
|
|
1321
|
-
const d4 = parseDate(value);
|
|
1322
|
-
return d4 ? shortDayFmt.format(d4) : "";
|
|
1323
|
-
}
|
|
1324
|
-
function formatWeekday(value) {
|
|
1325
|
-
const d4 = parseDate(value);
|
|
1326
|
-
return d4 ? weekdayFmt.format(d4) : "";
|
|
1327
|
-
}
|
|
1328
|
-
var dayKeyFmt = null;
|
|
1329
|
-
var calendarZone;
|
|
1330
|
-
function setCalendarZone(timeZone) {
|
|
1331
|
-
if (timeZone === calendarZone) return;
|
|
1332
|
-
calendarZone = timeZone;
|
|
1333
|
-
try {
|
|
1334
|
-
dayKeyFmt = timeZone ? new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }) : null;
|
|
1335
|
-
} catch {
|
|
1336
|
-
dayKeyFmt = null;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
function dayKey(d4) {
|
|
1340
|
-
if (dayKeyFmt) {
|
|
1341
|
-
const parts = dayKeyFmt.formatToParts(d4);
|
|
1342
|
-
const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
|
|
1343
|
-
return `${part("year")}-${part("month")}-${part("day")}`;
|
|
1344
|
-
}
|
|
1345
|
-
const m4 = String(d4.getMonth() + 1).padStart(2, "0");
|
|
1346
|
-
const day = String(d4.getDate()).padStart(2, "0");
|
|
1347
|
-
return `${d4.getFullYear()}-${m4}-${day}`;
|
|
1348
|
-
}
|
|
1349
|
-
function splitKey(key) {
|
|
1350
|
-
const [y4, m4, d4] = key.split("-").map(Number);
|
|
1351
|
-
return [y4, m4 - 1, d4];
|
|
1352
|
-
}
|
|
1353
|
-
function keyAt(ms) {
|
|
1354
|
-
return new Date(ms).toISOString().slice(0, 10);
|
|
1355
|
-
}
|
|
1356
|
-
function addDays(key, days) {
|
|
1357
|
-
const [y4, m4, d4] = splitKey(key);
|
|
1358
|
-
return keyAt(Date.UTC(y4, m4, d4 + days));
|
|
1359
|
-
}
|
|
1360
|
-
function addMonths(key, months) {
|
|
1361
|
-
const [y4, m4, d4] = splitKey(key);
|
|
1362
|
-
const lastDay = new Date(Date.UTC(y4, m4 + months + 1, 0)).getUTCDate();
|
|
1363
|
-
return keyAt(Date.UTC(y4, m4 + months, Math.min(d4, lastDay)));
|
|
1364
|
-
}
|
|
1365
|
-
function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
|
|
1366
|
-
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1367
|
-
const [y4, m4, d4] = splitKey(value);
|
|
1368
|
-
const [ty, tm, td] = splitKey(dayKey(now));
|
|
1369
|
-
return Math.round((Date.UTC(y4, m4, d4) - Date.UTC(ty, tm, td)) / 864e5);
|
|
1370
|
-
}
|
|
1371
|
-
const target = parseDate(value);
|
|
1372
|
-
if (!target) return null;
|
|
1373
|
-
const a4 = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
1374
|
-
const b4 = new Date(target.getFullYear(), target.getMonth(), target.getDate()).getTime();
|
|
1375
|
-
return Math.round((b4 - a4) / 864e5);
|
|
1376
|
-
}
|
|
1377
|
-
function relativeDay(value, now = /* @__PURE__ */ new Date()) {
|
|
1378
|
-
const diff = daysFromToday(value, now);
|
|
1379
|
-
if (diff === null) return "";
|
|
1380
|
-
if (diff === 0) return "today";
|
|
1381
|
-
if (diff === 1) return "tomorrow";
|
|
1382
|
-
if (diff === -1) return "yesterday";
|
|
1383
|
-
if (diff > 1 && diff <= 7) return `in ${diff} days`;
|
|
1384
|
-
if (diff < -1 && diff >= -7) return `${Math.abs(diff)} days ago`;
|
|
1385
|
-
return formatShortDay(value);
|
|
1386
|
-
}
|
|
1387
|
-
function formatDuration(minutes) {
|
|
1388
|
-
const h5 = Math.floor(minutes / 60);
|
|
1389
|
-
const m4 = minutes % 60;
|
|
1390
|
-
if (h5 === 0) return `${m4} min`;
|
|
1391
|
-
if (m4 === 0) return `${h5} h`;
|
|
1392
|
-
return `${h5} h ${m4} min`;
|
|
1393
|
-
}
|
|
1394
|
-
function relativeTime(value, now = /* @__PURE__ */ new Date()) {
|
|
1395
|
-
const d4 = parseDate(value);
|
|
1396
|
-
if (!d4) return "";
|
|
1397
|
-
const seconds = Math.round((now.getTime() - d4.getTime()) / 1e3);
|
|
1398
|
-
if (seconds < 60) return "just now";
|
|
1399
|
-
const minutes = Math.round(seconds / 60);
|
|
1400
|
-
if (minutes < 60) return `${minutes} min ago`;
|
|
1401
|
-
const hours = Math.round(minutes / 60);
|
|
1402
|
-
if (hours < 48) return `${hours} h ago`;
|
|
1403
|
-
const days = Math.round(hours / 24);
|
|
1404
|
-
if (days <= 30) return `${days} days ago`;
|
|
1405
|
-
return d4.getFullYear() === now.getFullYear() ? formatShortDay(value) : shortDayYearFmt.format(d4);
|
|
1406
|
-
}
|
|
1407
|
-
function firstLine(text) {
|
|
1408
|
-
const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
|
|
1409
|
-
return line.replace(/^[#>*\-\s]+/, "").replace(/[*_`]/g, "").trim();
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
// client/timer.ts
|
|
1413
|
-
function clock(seconds) {
|
|
1414
|
-
const total = Math.abs(seconds);
|
|
1415
|
-
const mm = Math.floor(total / 60);
|
|
1416
|
-
const ss = Math.floor(total % 60);
|
|
1417
|
-
return `${seconds < 0 ? "+" : ""}${mm}:${String(ss).padStart(2, "0")}`;
|
|
1418
|
-
}
|
|
1419
|
-
function remainingSeconds(active, nowMs) {
|
|
1420
|
-
return Math.round((new Date(active.endsAt).getTime() - nowMs) / 1e3);
|
|
1421
|
-
}
|
|
1422
|
-
function unattendedNotice(closed, handlers2) {
|
|
1423
|
-
const stoppedAt = formatTime(closed.endedAt);
|
|
1424
|
-
const headline = closed.reason === "away" ? `Timer stopped at ${stoppedAt} \u2014 nothing had touched the machine since.` : `Timer stopped at ${stoppedAt} \u2014 it had been left running.`;
|
|
1425
|
-
return el(
|
|
1426
|
-
"div",
|
|
1427
|
-
{ class: "timer__away", role: "status" },
|
|
1428
|
-
el(
|
|
1429
|
-
"span",
|
|
1430
|
-
{ class: "timer__away-body" },
|
|
1431
|
-
el("span", { class: "timer__away-text" }, headline),
|
|
1432
|
-
el(
|
|
1433
|
-
"span",
|
|
1434
|
-
{ class: "timer__away-detail" },
|
|
1435
|
-
`${formatDuration(closed.actualMinutes)} logged on \u201C${closed.title}\u201D. `,
|
|
1436
|
-
closed.reason === "away" ? "The time you were away is not in the record." : "Treat that as an upper bound, not a measurement."
|
|
1437
|
-
)
|
|
1438
|
-
),
|
|
1439
|
-
el("button", { type: "button", class: "button", onClick: () => handlers2.startSession(closed.id) }, "Pick it back up"),
|
|
1440
|
-
el(
|
|
1441
|
-
"button",
|
|
1442
|
-
{
|
|
1443
|
-
type: "button",
|
|
1444
|
-
class: "icon-button",
|
|
1445
|
-
"aria-label": "Dismiss",
|
|
1446
|
-
onClick: () => handlers2.dismissUnattended(closed.endedAt)
|
|
1447
|
-
},
|
|
1448
|
-
"\u2715"
|
|
1449
|
-
)
|
|
1450
|
-
);
|
|
1451
|
-
}
|
|
1452
|
-
function Timer({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
1453
|
-
const { active, completedToday, minutesToday, unattendedClose } = state2.session;
|
|
1454
|
-
const closed = unattendedClose && ui2.unattendedSeen.value !== unattendedClose.endedAt ? unattendedClose : null;
|
|
1455
|
-
if (!active && completedToday === 0 && !closed) {
|
|
1456
|
-
return el("div", { class: "timer", id: "timer", hidden: true });
|
|
1457
|
-
}
|
|
1458
|
-
const tally = completedToday > 0 ? `${completedToday} session${completedToday === 1 ? "" : "s"} today \xB7 ${formatDuration(minutesToday)}` : null;
|
|
1459
|
-
if (!active) {
|
|
1460
|
-
return el(
|
|
1461
|
-
"div",
|
|
1462
|
-
{ class: "timer", id: "timer", hidden: false, "data-state": closed ? "stopped" : "idle" },
|
|
1463
|
-
closed ? unattendedNotice(closed, handlers2) : null,
|
|
1464
|
-
tally ? el("span", { class: "timer__tally" }, tally) : null
|
|
1465
|
-
);
|
|
1466
|
-
}
|
|
1467
|
-
const remaining = remainingSeconds(active, ui2.clock.value);
|
|
1468
|
-
const overrun = remaining < 0;
|
|
1469
|
-
return el(
|
|
1470
|
-
"div",
|
|
1471
|
-
{ class: "timer", id: "timer", hidden: false, "data-state": overrun ? "overrun" : "running" },
|
|
1472
|
-
el("span", { class: "timer__clock" }, clock(remaining)),
|
|
1473
|
-
el(
|
|
1474
|
-
"span",
|
|
1475
|
-
{ class: "timer__title" },
|
|
1476
|
-
active.title,
|
|
1477
|
-
overrun ? el("span", { class: "timer__note" }, ` \xB7 ${active.minutes} min in, still counting`) : null,
|
|
1478
|
-
// The one thing a kitchen timer can't tell you.
|
|
1479
|
-
active.collidesWithNextEvent && !overrun ? el("span", { class: "timer__note timer__note--warn" }, ` \xB7 runs into ${formatTime(active.nextEventAt)}`) : null
|
|
1480
|
-
),
|
|
1481
|
-
tally ? el("span", { class: "timer__tally" }, tally) : null,
|
|
1482
|
-
el("button", { type: "button", class: "button", onClick: () => handlers2.stopSession() }, "Stop")
|
|
1483
|
-
);
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
1327
|
// client/actions.ts
|
|
1487
1328
|
function afterRender(fn) {
|
|
1488
1329
|
if (typeof requestAnimationFrame === "function") requestAnimationFrame(fn);
|
|
@@ -1536,7 +1377,6 @@ function adoptState(next) {
|
|
|
1536
1377
|
state.value = next;
|
|
1537
1378
|
ui.connectionError.value = null;
|
|
1538
1379
|
});
|
|
1539
|
-
startLocalTick();
|
|
1540
1380
|
checkStore(next.storeId);
|
|
1541
1381
|
checkAssetVersion(next.assetVersion);
|
|
1542
1382
|
}
|
|
@@ -1579,7 +1419,8 @@ var PAST_TENSE = {
|
|
|
1579
1419
|
dismiss: "Dismissed",
|
|
1580
1420
|
snooze: "Snoozed",
|
|
1581
1421
|
reopen: "Restored",
|
|
1582
|
-
note: "Note saved"
|
|
1422
|
+
note: "Note saved",
|
|
1423
|
+
nudge: "Nudged, back tomorrow"
|
|
1583
1424
|
};
|
|
1584
1425
|
var lastAction = null;
|
|
1585
1426
|
var parkedFrom = /* @__PURE__ */ new Map();
|
|
@@ -1591,6 +1432,9 @@ function unpark(id) {
|
|
|
1591
1432
|
function undoLast() {
|
|
1592
1433
|
if (lastAction && lastAction.action !== "reopen") void unpark(lastAction.id);
|
|
1593
1434
|
}
|
|
1435
|
+
function isRemark(action) {
|
|
1436
|
+
return action === "note" || action === "nudge";
|
|
1437
|
+
}
|
|
1594
1438
|
function optimistic(current, id, action, extra) {
|
|
1595
1439
|
const status = action === "reopen" ? "open" : action === "dismiss" ? "dismissed" : action === "done" ? "done" : "snoozed";
|
|
1596
1440
|
const statusAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1617,12 +1461,12 @@ async function applyAction(id, action, extra = {}) {
|
|
|
1617
1461
|
if (row && action === "snooze" && item && (item.status === "done" || item.status === "dismissed")) {
|
|
1618
1462
|
parkedFrom.set(id, item.status === "done" ? "done" : "dismiss");
|
|
1619
1463
|
}
|
|
1620
|
-
if (action
|
|
1464
|
+
if (!isRemark(action)) state.value = optimistic(current, id, action, extra);
|
|
1621
1465
|
});
|
|
1622
1466
|
try {
|
|
1623
1467
|
state.value = await postAction({ id, action, ...extra });
|
|
1624
1468
|
removePending(id);
|
|
1625
|
-
if (action
|
|
1469
|
+
if (isRemark(action)) {
|
|
1626
1470
|
showToast(PAST_TENSE[action]);
|
|
1627
1471
|
return;
|
|
1628
1472
|
}
|
|
@@ -1696,6 +1540,27 @@ async function stopAssistant(id) {
|
|
|
1696
1540
|
showToast(`Could not stop: ${err.message}`);
|
|
1697
1541
|
}
|
|
1698
1542
|
}
|
|
1543
|
+
async function copy(text) {
|
|
1544
|
+
try {
|
|
1545
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
1546
|
+
await navigator.clipboard.writeText(text);
|
|
1547
|
+
} else {
|
|
1548
|
+
const field = document.createElement("textarea");
|
|
1549
|
+
field.value = text;
|
|
1550
|
+
field.setAttribute("readonly", "");
|
|
1551
|
+
field.style.position = "fixed";
|
|
1552
|
+
field.style.opacity = "0";
|
|
1553
|
+
document.body.append(field);
|
|
1554
|
+
field.select();
|
|
1555
|
+
const copied = document.execCommand("copy");
|
|
1556
|
+
field.remove();
|
|
1557
|
+
if (!copied) throw new Error("the browser would not copy");
|
|
1558
|
+
}
|
|
1559
|
+
showToast("Copied");
|
|
1560
|
+
} catch (err) {
|
|
1561
|
+
showToast(`Could not copy: ${err.message}`);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1699
1564
|
async function runAgent() {
|
|
1700
1565
|
if (!state.value?.agentRun?.enabled || state.value.agentRun.last?.status === "running") return;
|
|
1701
1566
|
const ok = window.confirm(
|
|
@@ -1724,53 +1589,6 @@ async function askAgent(run, text) {
|
|
|
1724
1589
|
showToast(`Could not ask the morning agent: ${err.message}`);
|
|
1725
1590
|
}
|
|
1726
1591
|
}
|
|
1727
|
-
async function changeSession(body) {
|
|
1728
|
-
if (body.action === "start" && "Notification" in window && Notification.permission === "default") {
|
|
1729
|
-
void Notification.requestPermission();
|
|
1730
|
-
}
|
|
1731
|
-
try {
|
|
1732
|
-
state.value = await postSession(body);
|
|
1733
|
-
startLocalTick();
|
|
1734
|
-
} catch (err) {
|
|
1735
|
-
showToast(`Could not change the timer: ${err.message}`);
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
var tickTimer = null;
|
|
1739
|
-
var announcedOverrunFor = null;
|
|
1740
|
-
function startLocalTick() {
|
|
1741
|
-
if (tickTimer) clearInterval(tickTimer);
|
|
1742
|
-
tickTimer = setInterval(tick, 1e3);
|
|
1743
|
-
tick();
|
|
1744
|
-
}
|
|
1745
|
-
function tick() {
|
|
1746
|
-
const active = state.value?.session.active;
|
|
1747
|
-
if (!active) {
|
|
1748
|
-
document.title = "Daily Focus";
|
|
1749
|
-
if (tickTimer) clearInterval(tickTimer);
|
|
1750
|
-
tickTimer = null;
|
|
1751
|
-
return;
|
|
1752
|
-
}
|
|
1753
|
-
const nowMs = Date.now();
|
|
1754
|
-
ui.clock.value = nowMs;
|
|
1755
|
-
const remaining = remainingSeconds(active, nowMs);
|
|
1756
|
-
const mm = Math.floor(Math.abs(remaining) / 60);
|
|
1757
|
-
const ss = String(Math.floor(Math.abs(remaining) % 60)).padStart(2, "0");
|
|
1758
|
-
document.title = `${remaining < 0 ? "+" : ""}${mm}:${ss} \xB7 ${active.title}`;
|
|
1759
|
-
if (remaining <= 0 && announcedOverrunFor !== active.startedAt) {
|
|
1760
|
-
announcedOverrunFor = active.startedAt;
|
|
1761
|
-
announceSessionEnd(active);
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
function announceSessionEnd(active) {
|
|
1765
|
-
const label = active.minutes === 1 ? "1 minute" : `${active.minutes} minutes`;
|
|
1766
|
-
showToast(`${label} in \u2014 still on "${active.title}"?`, null);
|
|
1767
|
-
if ("Notification" in window && Notification.permission === "granted") {
|
|
1768
|
-
new Notification("Still on this?", {
|
|
1769
|
-
body: `${label} in on ${active.title}. Keep going, or stop and log it.`,
|
|
1770
|
-
tag: "daily-focus-session"
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
1592
|
function showView(view) {
|
|
1775
1593
|
if (ui.view.value === view) return;
|
|
1776
1594
|
if (state.value && !availableViews(state.value).includes(view)) return;
|
|
@@ -1819,10 +1637,6 @@ function setTicketMode(mode) {
|
|
|
1819
1637
|
closeMenus();
|
|
1820
1638
|
});
|
|
1821
1639
|
}
|
|
1822
|
-
function setFocusMode(on) {
|
|
1823
|
-
ui.focusMode.value = on;
|
|
1824
|
-
remember(FOCUS_MODE_KEY, on ? "1" : null);
|
|
1825
|
-
}
|
|
1826
1640
|
function restartToast(page) {
|
|
1827
1641
|
if (page.restart === "manual") showToast("Saved. Restart the dashboard to apply it.");
|
|
1828
1642
|
else if (page.restart === "waiting" && state.value?.agentRun?.last?.status === "running") {
|
|
@@ -2045,21 +1859,16 @@ var handlers = {
|
|
|
2045
1859
|
() => document.querySelector('#tickets .item[data-selected="true"]')?.scrollIntoView({ block: "center", behavior: "smooth" })
|
|
2046
1860
|
);
|
|
2047
1861
|
},
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
ui.unattendedSeen.value = endedAt;
|
|
2052
|
-
remember(UNATTENDED_SEEN_KEY, endedAt);
|
|
2053
|
-
},
|
|
2054
|
-
// A nudge on Slack is invisible to GitHub, so it's recorded as a note: the agent
|
|
2055
|
-
// reads it as free text, and the board's nudge timer starts over from it.
|
|
2056
|
-
nudge: (id) => void applyAction(id, "note", { text: "Nudged reviewers" }),
|
|
1862
|
+
// A nudge on Slack is invisible to GitHub, so it's recorded: the board's nudge
|
|
1863
|
+
// timer starts over from it, and the agent reads that the user asked.
|
|
1864
|
+
nudge: (id) => void applyAction(id, "nudge"),
|
|
2057
1865
|
unpark: (id) => void unpark(id),
|
|
2058
1866
|
refreshBoard: () => void refreshBoard(),
|
|
2059
1867
|
refreshTickets: () => void refreshTickets(),
|
|
2060
1868
|
refreshCalendar: () => void refreshCalendar(),
|
|
2061
1869
|
ask: (id, body) => void ask(id, body),
|
|
2062
1870
|
stopAssistant: (id) => void stopAssistant(id),
|
|
1871
|
+
copy: (text) => void copy(text),
|
|
2063
1872
|
runAgent: () => void runAgent(),
|
|
2064
1873
|
stopAgent: () => void stopAgent(),
|
|
2065
1874
|
askAgent: (run, text) => void askAgent(run, text),
|
|
@@ -2078,7 +1887,6 @@ var handlers = {
|
|
|
2078
1887
|
applyTheme(mode);
|
|
2079
1888
|
ui.theme.value = mode;
|
|
2080
1889
|
},
|
|
2081
|
-
setFocusMode,
|
|
2082
1890
|
openHelp: () => document.getElementById("help")?.showModal(),
|
|
2083
1891
|
hideToast,
|
|
2084
1892
|
openSettings,
|
|
@@ -2099,6 +1907,145 @@ var handlers = {
|
|
|
2099
1907
|
listCalendars: () => void listCalendars()
|
|
2100
1908
|
};
|
|
2101
1909
|
|
|
1910
|
+
// client/format.ts
|
|
1911
|
+
var DAY_FORMAT = {
|
|
1912
|
+
weekday: "long",
|
|
1913
|
+
day: "numeric",
|
|
1914
|
+
month: "long"
|
|
1915
|
+
};
|
|
1916
|
+
function formatters(timeZone) {
|
|
1917
|
+
return {
|
|
1918
|
+
time: new Intl.DateTimeFormat(void 0, { timeZone, hour: "2-digit", minute: "2-digit" }),
|
|
1919
|
+
day: new Intl.DateTimeFormat(void 0, { timeZone, ...DAY_FORMAT }),
|
|
1920
|
+
shortDay: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short" }),
|
|
1921
|
+
shortDayYear: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short", year: "numeric" }),
|
|
1922
|
+
weekday: new Intl.DateTimeFormat(void 0, { timeZone, weekday: "long" })
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
var clockFormats = formatters();
|
|
1926
|
+
var dateFormats = formatters("UTC");
|
|
1927
|
+
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
1928
|
+
function formatted(value, kind) {
|
|
1929
|
+
const bare = typeof value === "string" && DATE_ONLY.test(value);
|
|
1930
|
+
const d5 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
|
|
1931
|
+
return d5 && !Number.isNaN(d5.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d5) : "";
|
|
1932
|
+
}
|
|
1933
|
+
function parseDate(value) {
|
|
1934
|
+
if (typeof value !== "string" || value === "") return null;
|
|
1935
|
+
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
1936
|
+
if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
|
|
1937
|
+
const d5 = new Date(value);
|
|
1938
|
+
return Number.isNaN(d5.getTime()) ? null : d5;
|
|
1939
|
+
}
|
|
1940
|
+
function formatTime(value) {
|
|
1941
|
+
return formatted(value, "time");
|
|
1942
|
+
}
|
|
1943
|
+
function formatDay(value) {
|
|
1944
|
+
return formatted(value, "day");
|
|
1945
|
+
}
|
|
1946
|
+
function formatShortDay(value) {
|
|
1947
|
+
return formatted(value, "shortDay");
|
|
1948
|
+
}
|
|
1949
|
+
function formatWeekday(value) {
|
|
1950
|
+
return formatted(value, "weekday");
|
|
1951
|
+
}
|
|
1952
|
+
var dayKeyFmt = null;
|
|
1953
|
+
var calendarZone;
|
|
1954
|
+
function setCalendarZone(timeZone) {
|
|
1955
|
+
if (timeZone === calendarZone) return;
|
|
1956
|
+
calendarZone = timeZone;
|
|
1957
|
+
try {
|
|
1958
|
+
clockFormats = formatters(timeZone);
|
|
1959
|
+
dayKeyFmt = timeZone ? new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }) : null;
|
|
1960
|
+
} catch {
|
|
1961
|
+
dayKeyFmt = null;
|
|
1962
|
+
clockFormats = formatters();
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
function dayKey(d5) {
|
|
1966
|
+
if (dayKeyFmt) {
|
|
1967
|
+
const parts = dayKeyFmt.formatToParts(d5);
|
|
1968
|
+
const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
|
|
1969
|
+
return `${part("year")}-${part("month")}-${part("day")}`;
|
|
1970
|
+
}
|
|
1971
|
+
const m4 = String(d5.getMonth() + 1).padStart(2, "0");
|
|
1972
|
+
const day = String(d5.getDate()).padStart(2, "0");
|
|
1973
|
+
return `${d5.getFullYear()}-${m4}-${day}`;
|
|
1974
|
+
}
|
|
1975
|
+
function splitKey(key) {
|
|
1976
|
+
const [y5, m4, d5] = key.split("-").map(Number);
|
|
1977
|
+
return [y5, m4 - 1, d5];
|
|
1978
|
+
}
|
|
1979
|
+
function keyAt(ms) {
|
|
1980
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
1981
|
+
}
|
|
1982
|
+
function addDays(key, days) {
|
|
1983
|
+
const [y5, m4, d5] = splitKey(key);
|
|
1984
|
+
return keyAt(Date.UTC(y5, m4, d5 + days));
|
|
1985
|
+
}
|
|
1986
|
+
function addMonths(key, months) {
|
|
1987
|
+
const [y5, m4, d5] = splitKey(key);
|
|
1988
|
+
const lastDay = new Date(Date.UTC(y5, m4 + months + 1, 0)).getUTCDate();
|
|
1989
|
+
return keyAt(Date.UTC(y5, m4 + months, Math.min(d5, lastDay)));
|
|
1990
|
+
}
|
|
1991
|
+
function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
|
|
1992
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1993
|
+
const [y5, m4, d5] = splitKey(value);
|
|
1994
|
+
const [ty, tm, td] = splitKey(dayKey(now));
|
|
1995
|
+
return Math.round((Date.UTC(y5, m4, d5) - Date.UTC(ty, tm, td)) / 864e5);
|
|
1996
|
+
}
|
|
1997
|
+
const target = parseDate(value);
|
|
1998
|
+
if (!target) return null;
|
|
1999
|
+
const a4 = Date.UTC(...splitKey(dayKey(now)));
|
|
2000
|
+
const b4 = Date.UTC(...splitKey(dayKey(target)));
|
|
2001
|
+
return Math.round((b4 - a4) / 864e5);
|
|
2002
|
+
}
|
|
2003
|
+
function relativeDay(value, now = /* @__PURE__ */ new Date()) {
|
|
2004
|
+
const diff = daysFromToday(value, now);
|
|
2005
|
+
if (diff === null) return "";
|
|
2006
|
+
if (diff === 0) return "today";
|
|
2007
|
+
if (diff === 1) return "tomorrow";
|
|
2008
|
+
if (diff === -1) return "yesterday";
|
|
2009
|
+
if (diff > 1 && diff <= 7) return `in ${diff} days`;
|
|
2010
|
+
if (diff < -1 && diff >= -7) return `${Math.abs(diff)} days ago`;
|
|
2011
|
+
return formatShortDay(value);
|
|
2012
|
+
}
|
|
2013
|
+
function formatDuration(minutes) {
|
|
2014
|
+
const h5 = Math.floor(minutes / 60);
|
|
2015
|
+
const m4 = minutes % 60;
|
|
2016
|
+
if (h5 === 0) return `${m4} min`;
|
|
2017
|
+
if (m4 === 0) return `${h5} h`;
|
|
2018
|
+
return `${h5} h ${m4} min`;
|
|
2019
|
+
}
|
|
2020
|
+
function relativeTime(value, now = /* @__PURE__ */ new Date()) {
|
|
2021
|
+
const d5 = parseDate(value);
|
|
2022
|
+
if (!d5) return "";
|
|
2023
|
+
const seconds = Math.round((now.getTime() - d5.getTime()) / 1e3);
|
|
2024
|
+
if (seconds < 60) return "just now";
|
|
2025
|
+
const minutes = Math.round(seconds / 60);
|
|
2026
|
+
if (minutes < 60) return `${minutes} min ago`;
|
|
2027
|
+
const hours = Math.round(minutes / 60);
|
|
2028
|
+
if (hours < 48) return `${hours} h ago`;
|
|
2029
|
+
const days = Math.round(hours / 24);
|
|
2030
|
+
if (days <= 30) return `${days} days ago`;
|
|
2031
|
+
return dayKey(d5).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
|
|
2032
|
+
}
|
|
2033
|
+
function durationBetween(start, end) {
|
|
2034
|
+
const from = parseDate(start);
|
|
2035
|
+
const to = parseDate(end);
|
|
2036
|
+
if (!from || !to || to < from) return "";
|
|
2037
|
+
const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
|
|
2038
|
+
return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
|
|
2039
|
+
}
|
|
2040
|
+
function formatClock(seconds) {
|
|
2041
|
+
const whole = Math.max(0, Math.floor(seconds));
|
|
2042
|
+
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
2043
|
+
}
|
|
2044
|
+
function firstLine(text) {
|
|
2045
|
+
const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
|
|
2046
|
+
return line.replace(/^[#>*\-\s]+/, "").replace(/[*_`]/g, "").trim();
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2102
2049
|
// client/failure.ts
|
|
2103
2050
|
function failureNotice(failure, { fold, className, icon, lastGood, now, onRetry }, ui2, handlers2) {
|
|
2104
2051
|
return el(
|
|
@@ -2292,7 +2239,7 @@ function renderMarkdown(value) {
|
|
|
2292
2239
|
function anchor(href, label) {
|
|
2293
2240
|
return el("a", { href: /^https?:\/\//i.test(href) ? href : "#", target: "_blank", rel: "noopener noreferrer" }, label);
|
|
2294
2241
|
}
|
|
2295
|
-
function renderMarkdownBlocks(value) {
|
|
2242
|
+
function renderMarkdownBlocks(value, options = {}) {
|
|
2296
2243
|
const out = [];
|
|
2297
2244
|
const lines = String(value).replace(/\r\n/g, "\n").split("\n");
|
|
2298
2245
|
let i4 = 0;
|
|
@@ -2303,7 +2250,16 @@ function renderMarkdownBlocks(value) {
|
|
|
2303
2250
|
i4++;
|
|
2304
2251
|
while (i4 < lines.length && !/^\s*```/.test(lines[i4])) code.push(lines[i4++]);
|
|
2305
2252
|
i4++;
|
|
2306
|
-
|
|
2253
|
+
const text = code.join("\n");
|
|
2254
|
+
const { onCopy } = options;
|
|
2255
|
+
out.push(
|
|
2256
|
+
onCopy ? el(
|
|
2257
|
+
"div",
|
|
2258
|
+
{ class: "code-block" },
|
|
2259
|
+
el("pre", null, text),
|
|
2260
|
+
el("button", { type: "button", class: "code-block__copy", title: "Copy to the clipboard", onClick: () => onCopy(text) }, "Copy")
|
|
2261
|
+
) : el("pre", null, text)
|
|
2262
|
+
);
|
|
2307
2263
|
continue;
|
|
2308
2264
|
}
|
|
2309
2265
|
if (line.trim() === "") {
|
|
@@ -2313,7 +2269,7 @@ function renderMarkdownBlocks(value) {
|
|
|
2313
2269
|
if (/^\s*>/.test(line)) {
|
|
2314
2270
|
const quoted = [];
|
|
2315
2271
|
while (i4 < lines.length && /^\s*>/.test(lines[i4])) quoted.push(lines[i4++].replace(/^\s*>\s?/, ""));
|
|
2316
|
-
out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"))));
|
|
2272
|
+
out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"), options)));
|
|
2317
2273
|
continue;
|
|
2318
2274
|
}
|
|
2319
2275
|
if (/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
|
|
@@ -2557,9 +2513,6 @@ function renderItem(item, state2, ui2, handlers2) {
|
|
|
2557
2513
|
function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
2558
2514
|
const now = new Date(state2.now);
|
|
2559
2515
|
const selected = ui2.selectedId.value === item.id;
|
|
2560
|
-
const active = state2.session?.active ?? null;
|
|
2561
|
-
const running = active !== null && active.id === item.id;
|
|
2562
|
-
const overrun = running && remainingSeconds(active, ui2.clock.value) < 0;
|
|
2563
2516
|
const repo = githubRepo(item);
|
|
2564
2517
|
const title = item.url ? el(
|
|
2565
2518
|
"a",
|
|
@@ -2577,8 +2530,6 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2577
2530
|
"data-selected": String(selected),
|
|
2578
2531
|
"data-priority": String(item.priority !== void 0 && item.status === "open"),
|
|
2579
2532
|
"data-pending": String(ui2.pending.value.has(item.id)),
|
|
2580
|
-
"data-running": String(running),
|
|
2581
|
-
"data-overrun": String(overrun),
|
|
2582
2533
|
"data-detail": String(ui2.detailFor.value === item.id),
|
|
2583
2534
|
// A click on the card opens the panel on it: one way in, and the whole card is it.
|
|
2584
2535
|
onClick: (event) => {
|
|
@@ -2598,31 +2549,21 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2598
2549
|
"div",
|
|
2599
2550
|
{ class: "item__body" },
|
|
2600
2551
|
el("p", { class: "item__title" }, title),
|
|
2601
|
-
renderMeta(item, state2,
|
|
2602
|
-
item.detail ? el("p", { class: "item__detail" }, renderMarkdown(item.detail)) : null
|
|
2552
|
+
renderMeta(item, state2, now),
|
|
2553
|
+
item.detail ? el("p", { class: "item__detail" }, renderMarkdown(item.detail)) : null,
|
|
2554
|
+
renderAhead(item.id, state2)
|
|
2603
2555
|
),
|
|
2604
|
-
renderActions(item,
|
|
2556
|
+
renderActions(item, ui2, handlers2),
|
|
2605
2557
|
ui2.menuFor.value === item.id ? renderSnoozeMenu(item, now, handlers2) : null
|
|
2606
2558
|
);
|
|
2607
2559
|
}
|
|
2608
|
-
function renderMeta(item, state2,
|
|
2560
|
+
function renderMeta(item, state2, now) {
|
|
2609
2561
|
const pills = [];
|
|
2610
2562
|
const said = /* @__PURE__ */ new Set();
|
|
2611
2563
|
const pill = (cls, text) => {
|
|
2612
2564
|
pills.push(el("span", { class: cls }, text));
|
|
2613
2565
|
said.add(text.trim().toLowerCase());
|
|
2614
2566
|
};
|
|
2615
|
-
const active = state2.session?.active ?? null;
|
|
2616
|
-
if (active && active.id === item.id) {
|
|
2617
|
-
pills.push(
|
|
2618
|
-
el(
|
|
2619
|
-
"span",
|
|
2620
|
-
{ class: "pill pill--running" },
|
|
2621
|
-
el("span", { class: "item__running-clock" }, clock(remainingSeconds(active, ui2.clock.value))),
|
|
2622
|
-
" focusing"
|
|
2623
|
-
)
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
2567
|
if (item.priority !== void 0 && item.status === "open") {
|
|
2627
2568
|
pill("pill pill--priority", `#${item.priority}`);
|
|
2628
2569
|
}
|
|
@@ -2674,24 +2615,43 @@ function rowPillTexts(row, state2) {
|
|
|
2674
2615
|
function rowPills(row, state2) {
|
|
2675
2616
|
return rowPillTexts(row, state2).map(([cls, text]) => el("span", { class: cls }, text));
|
|
2676
2617
|
}
|
|
2677
|
-
|
|
2618
|
+
var READING = {
|
|
2619
|
+
"assess-review": "Reading the review\u2026",
|
|
2620
|
+
"explain-ci": "Reading the failing checks\u2026"
|
|
2621
|
+
};
|
|
2622
|
+
function renderAhead(id, state2) {
|
|
2623
|
+
const auto = state2.assistant?.auto;
|
|
2624
|
+
const keys = auto?.rows?.[id];
|
|
2625
|
+
if (!auto || !keys?.length) return null;
|
|
2626
|
+
const turn = (state2.assistant.items?.[id]?.turns ?? []).findLast((candidate) => candidate.auto !== null && keys.includes(candidate.auto));
|
|
2627
|
+
const mark = el("span", { class: "item__ahead-mark", "aria-hidden": "true" }, "\u2726");
|
|
2628
|
+
if (!turn) {
|
|
2629
|
+
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;
|
|
2630
|
+
}
|
|
2631
|
+
if (turn.status === "running") {
|
|
2632
|
+
return el("p", { class: "item__ahead item__ahead--quiet", "data-status": "running" }, mark, READING[turn.action ?? ""] ?? "Reading\u2026");
|
|
2633
|
+
}
|
|
2634
|
+
if (turn.status === "failed") {
|
|
2635
|
+
return el("p", { class: "item__ahead item__ahead--quiet" }, mark, "Could not answer ahead; the panel says why.");
|
|
2636
|
+
}
|
|
2637
|
+
const verdict = turn.status === "done" ? verdictLine(turn.reply) : "";
|
|
2638
|
+
if (!verdict) return null;
|
|
2639
|
+
return el(
|
|
2640
|
+
"p",
|
|
2641
|
+
{ class: "item__ahead", title: `Answered ahead ${relativeTime(turn.endedAt ?? turn.startedAt, new Date(state2.now))}. Open the panel for the whole answer.` },
|
|
2642
|
+
mark,
|
|
2643
|
+
renderMarkdown(verdict)
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
function verdictLine(reply) {
|
|
2647
|
+
const line = reply.split("\n").find((candidate) => candidate.trim() !== "") ?? "";
|
|
2648
|
+
return line.replace(/^\s*(?:#{1,6}\s+|>\s*|[-*•]\s+|\d+[.)]\s+)/, "").trim();
|
|
2649
|
+
}
|
|
2650
|
+
function renderActions(item, ui2, handlers2) {
|
|
2678
2651
|
const buttons = [];
|
|
2679
2652
|
const act = (id, action) => () => handlers2.onAction(id, action);
|
|
2680
|
-
const running = state2.session?.active?.id === item.id;
|
|
2681
2653
|
if (item.status === "open") {
|
|
2682
2654
|
if (item.kind === "task") {
|
|
2683
|
-
buttons.push(
|
|
2684
|
-
el(
|
|
2685
|
-
"button",
|
|
2686
|
-
{
|
|
2687
|
-
type: "button",
|
|
2688
|
-
class: running ? "button button--primary" : "button",
|
|
2689
|
-
title: running ? "Stop the focus session" : "Start a focus session on this",
|
|
2690
|
-
onClick: () => running ? handlers2.stopSession() : handlers2.startSession(item.id)
|
|
2691
|
-
},
|
|
2692
|
-
running ? "Stop" : "Focus"
|
|
2693
|
-
)
|
|
2694
|
-
);
|
|
2695
2655
|
buttons.push(el("button", { type: "button", class: "button", onClick: act(item.id, "done") }, "Done"));
|
|
2696
2656
|
buttons.push(
|
|
2697
2657
|
el(
|
|
@@ -2891,8 +2851,10 @@ function renderBoard(state2, ui2, handlers2) {
|
|
|
2891
2851
|
const open = board.rows.filter((row) => row.status === "open");
|
|
2892
2852
|
for (const court of COURT_ORDER) {
|
|
2893
2853
|
const rows = open.filter((row) => row.court === court);
|
|
2894
|
-
|
|
2895
|
-
|
|
2854
|
+
const nudged = court === "reviewers" ? rows.filter((row) => row.nudgedAt) : [];
|
|
2855
|
+
const waiting = rows.filter((row) => !row.nudgedAt);
|
|
2856
|
+
if (waiting.length > 0) parts.push(section(COURT_TITLE[court], waiting, state2, ui2, handlers2, null, renderPullRow));
|
|
2857
|
+
if (nudged.length > 0) parts.push(section("Nudged", nudged, state2, ui2, handlers2, null, renderPullRow));
|
|
2896
2858
|
}
|
|
2897
2859
|
if (open.length === 0 && !board.reason) {
|
|
2898
2860
|
parts.push(
|
|
@@ -2949,6 +2911,7 @@ function PullRow({ item: row, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2949
2911
|
"data-id": row.id,
|
|
2950
2912
|
"data-status": row.status,
|
|
2951
2913
|
"data-court": row.court,
|
|
2914
|
+
"data-nudged": String(Boolean(row.nudgedAt)),
|
|
2952
2915
|
"data-selected": String(selected),
|
|
2953
2916
|
"data-pending": String(ui2.pending.value.has(row.id)),
|
|
2954
2917
|
"data-detail": String(ui2.detailFor.value === row.id),
|
|
@@ -2977,7 +2940,8 @@ function PullRow({ item: row, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
2977
2940
|
),
|
|
2978
2941
|
renderPullMeta(row, state2, now),
|
|
2979
2942
|
renderPullReasons(row, now),
|
|
2980
|
-
renderCancelledChecks(row)
|
|
2943
|
+
renderCancelledChecks(row),
|
|
2944
|
+
renderAhead(row.id, state2)
|
|
2981
2945
|
),
|
|
2982
2946
|
renderPullActions(row, ui2, handlers2),
|
|
2983
2947
|
ui2.menuFor.value === row.id ? renderSnoozeMenu(row, now, handlers2, { indefinite: false }) : null
|
|
@@ -2996,6 +2960,9 @@ function renderPullMeta(row, state2, now) {
|
|
|
2996
2960
|
if (row.mergeable === "CONFLICTING") pill("pill pill--overdue", "conflicts");
|
|
2997
2961
|
if (row.autoMerge) pill("pill pill--good", "auto-merge on");
|
|
2998
2962
|
if (row.nudge) pill("pill pill--age", "time to ask");
|
|
2963
|
+
if (row.nudgedAt && row.nudgedUntil) {
|
|
2964
|
+
pill("pill", `nudged ${relativeTime(row.nudgedAt, now)}, back ${relativeDay(row.nudgedUntil, now)} ${formatTime(row.nudgedUntil)}`);
|
|
2965
|
+
}
|
|
2999
2966
|
if (row.court === "blocked") {
|
|
3000
2967
|
pill("pill", `blocked ${waitedFor(row.since, now)}`);
|
|
3001
2968
|
} else if (row.court === "gate" || row.court === "checks") {
|
|
@@ -3100,7 +3067,7 @@ function renderPullActions(row, ui2, handlers2) {
|
|
|
3100
3067
|
)
|
|
3101
3068
|
);
|
|
3102
3069
|
if (row.status === "open") {
|
|
3103
|
-
if (row.court === "reviewers") {
|
|
3070
|
+
if (row.court === "reviewers" && !row.nudgedAt) {
|
|
3104
3071
|
buttons.push(
|
|
3105
3072
|
el(
|
|
3106
3073
|
"button",
|
|
@@ -3137,7 +3104,7 @@ var TICKET_COURT_TITLE = {
|
|
|
3137
3104
|
};
|
|
3138
3105
|
var TICKET_COURT_ORDER = ["settled", "started", "idle"];
|
|
3139
3106
|
var TICKET_COURT_HINT = {
|
|
3140
|
-
settled: "Every pull request
|
|
3107
|
+
settled: "Every pull request linked to these is closed. Move them on, or say what is still to come \u2014 a pull request nobody has written yet is invisible from here.",
|
|
3141
3108
|
started: "A pull request is open, so the work has begun. The status still says it has not.",
|
|
3142
3109
|
idle: "Nothing in Jira links any code to these. Fine for work that is not code; worth a look otherwise."
|
|
3143
3110
|
};
|
|
@@ -3348,7 +3315,8 @@ function TicketRowView({ item: row, state: state2, ui: ui2, handlers: handlers2
|
|
|
3348
3315
|
{ class: "item__body" },
|
|
3349
3316
|
renderTicketStatus(row, state2, ui2, handlers2),
|
|
3350
3317
|
el("p", { class: "item__title" }, title),
|
|
3351
|
-
renderTicketMeta(row, state2, handlers2)
|
|
3318
|
+
renderTicketMeta(row, state2, handlers2),
|
|
3319
|
+
renderGitHubEvidence(row, now)
|
|
3352
3320
|
),
|
|
3353
3321
|
renderTicketActions(row, ui2, handlers2),
|
|
3354
3322
|
ui2.menuFor.value === row.id ? renderSnoozeMenu(row, now, handlers2, PARK_MENU) : null
|
|
@@ -3422,6 +3390,25 @@ function renderTicketMeta(row, state2, handlers2) {
|
|
|
3422
3390
|
pills.push(...rowPills(row, state2));
|
|
3423
3391
|
return pills.length > 0 ? el("div", { class: "item__meta item__meta--extra" }, pills) : null;
|
|
3424
3392
|
}
|
|
3393
|
+
var EVIDENCE_SHOWN = 3;
|
|
3394
|
+
function renderGitHubEvidence(row, now) {
|
|
3395
|
+
const pulls = row.pulls ?? [];
|
|
3396
|
+
if (!row.fromGitHub || pulls.length === 0) return null;
|
|
3397
|
+
const parts = ["GitHub, ahead of Jira: "];
|
|
3398
|
+
pulls.slice(0, EVIDENCE_SHOWN).forEach((pull, i4) => {
|
|
3399
|
+
if (i4 > 0) parts.push(" \xB7 ");
|
|
3400
|
+
parts.push(
|
|
3401
|
+
el("a", { href: pull.url, target: "_blank", rel: "noopener noreferrer", title: pull.title }, `${pull.repo}#${pull.number}`),
|
|
3402
|
+
` ${describePull(pull, now)}`
|
|
3403
|
+
);
|
|
3404
|
+
});
|
|
3405
|
+
if (pulls.length > EVIDENCE_SHOWN) parts.push(` \xB7 and ${pulls.length - EVIDENCE_SHOWN} more`);
|
|
3406
|
+
return el("p", { class: "item__reason item__reason--waiting" }, parts);
|
|
3407
|
+
}
|
|
3408
|
+
function describePull(pull, now) {
|
|
3409
|
+
if (pull.state === "open" || pull.state === "draft") return pull.state;
|
|
3410
|
+
return pull.closedAt ? `${pull.state} ${relativeTime(pull.closedAt, now)}` : pull.state;
|
|
3411
|
+
}
|
|
3425
3412
|
function outOfSyncFlag(row, state2, handlers2) {
|
|
3426
3413
|
const hit = state2.tickets?.rows?.find((candidate) => candidate.id === row.id && candidate.status === "open");
|
|
3427
3414
|
if (!hit) return null;
|
|
@@ -3458,6 +3445,46 @@ function parkButton(row, ui2, handlers2, menuKey) {
|
|
|
3458
3445
|
);
|
|
3459
3446
|
}
|
|
3460
3447
|
|
|
3448
|
+
// client/zone.ts
|
|
3449
|
+
var ZONE_CHECK_MS = 6e4;
|
|
3450
|
+
function browserZone() {
|
|
3451
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
3452
|
+
}
|
|
3453
|
+
function sameZone(a4, b4) {
|
|
3454
|
+
if (a4 === b4) return true;
|
|
3455
|
+
try {
|
|
3456
|
+
const canonical = (zone2) => new Intl.DateTimeFormat("en", { timeZone: zone2 }).resolvedOptions().timeZone;
|
|
3457
|
+
return canonical(a4) === canonical(b4);
|
|
3458
|
+
} catch {
|
|
3459
|
+
return false;
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
function watchZone(deps) {
|
|
3463
|
+
let seen = deps.browserZone();
|
|
3464
|
+
let sending = null;
|
|
3465
|
+
function tellIfElsewhere() {
|
|
3466
|
+
const here = deps.browserZone();
|
|
3467
|
+
const there = deps.dashboardZone();
|
|
3468
|
+
if (there === void 0 || sameZone(here, there) || sending === here) return;
|
|
3469
|
+
sending = here;
|
|
3470
|
+
deps.send(here).catch((err) => console.warn(`[daily-focus] could not tell the dashboard this browser is in ${here}:`, err)).finally(() => {
|
|
3471
|
+
sending = null;
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3474
|
+
return {
|
|
3475
|
+
look() {
|
|
3476
|
+
seen = deps.browserZone();
|
|
3477
|
+
if (deps.visible()) tellIfElsewhere();
|
|
3478
|
+
},
|
|
3479
|
+
check() {
|
|
3480
|
+
const here = deps.browserZone();
|
|
3481
|
+
if (here === seen) return;
|
|
3482
|
+
seen = here;
|
|
3483
|
+
tellIfElsewhere();
|
|
3484
|
+
}
|
|
3485
|
+
};
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3461
3488
|
// client/chrome.ts
|
|
3462
3489
|
function Header({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
3463
3490
|
const error = ui2.connectionError.value;
|
|
@@ -3468,7 +3495,8 @@ function Header({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
3468
3495
|
"div",
|
|
3469
3496
|
{ class: "page-header__titles" },
|
|
3470
3497
|
el("h1", null, "Daily Focus"),
|
|
3471
|
-
el("p", { class: "page-header__date", id: "header-date" }, state2 ? headerDate(state2) : "")
|
|
3498
|
+
el("p", { class: "page-header__date", id: "header-date" }, state2 ? headerDate(state2) : ""),
|
|
3499
|
+
state2 ? zoneLine(state2) : null
|
|
3472
3500
|
),
|
|
3473
3501
|
el(
|
|
3474
3502
|
"div",
|
|
@@ -3562,6 +3590,11 @@ function headerDate(state2) {
|
|
|
3562
3590
|
const today = dayKey(new Date(state2.now));
|
|
3563
3591
|
return formatDay(state2.brief.date ?? today) || formatDay(today);
|
|
3564
3592
|
}
|
|
3593
|
+
function zoneLine(state2) {
|
|
3594
|
+
if (sameZone(browserZone(), state2.timeZone)) return null;
|
|
3595
|
+
const at = state2.agentRun?.schedule ? ` \xB7 Morning agent at ${state2.agentRun.schedule.at}` : "";
|
|
3596
|
+
return el("p", { class: "page-header__zone", id: "header-zone" }, `Times in ${state2.timeZone}${at}`);
|
|
3597
|
+
}
|
|
3565
3598
|
function freshness(state2, handlers2) {
|
|
3566
3599
|
let text;
|
|
3567
3600
|
let stale;
|
|
@@ -3686,8 +3719,6 @@ var SHORTCUTS = [
|
|
|
3686
3719
|
[["Esc"], "Close the menu, then the panel"],
|
|
3687
3720
|
[["o"], "Open the source link"],
|
|
3688
3721
|
[["u"], "Undo the last action"],
|
|
3689
|
-
[["f"], "Focus mode \u2014 hide all but the top item"],
|
|
3690
|
-
[["p"], "Start a focus session on the selected item"],
|
|
3691
3722
|
[["r"], "Refresh whichever board you are on"],
|
|
3692
3723
|
[["w"], "Jira tickets: switch between out of sync and working on"],
|
|
3693
3724
|
[[","], "Settings: your objective, where the agent looks, and the rest"],
|
|
@@ -3717,7 +3748,7 @@ function HelpDialog(_props) {
|
|
|
3717
3748
|
el(
|
|
3718
3749
|
"p",
|
|
3719
3750
|
{ class: "help__note" },
|
|
3720
|
-
`Done, snoozed and dismissed all tell the morning agent to leave an item out of tomorrow's brief. A note is sent along with it as free text. On both boards only snooze and note apply: a pull request stays until it's merged or closed, and a ticket whose status looks wrong is put right in Jira rather than here. Click a card to open its panel, where its notes and the assistant live; the panel stays on the item it was opened for. The assistant is a CLI run on your machine
|
|
3751
|
+
`Done, snoozed and dismissed all tell the morning agent to leave an item out of tomorrow's brief. A note is sent along with it as free text. On both boards only snooze and note apply: a pull request stays until it's merged or closed, and a ticket whose status looks wrong is put right in Jira rather than here. Click a card to open its panel, where its notes and the assistant live; the panel stays on the item it was opened for. The assistant is a CLI run on your machine, told to read and draft, never to send or post. Click "updated \u2026 ago" in the header to open the morning agent's latest run: its report, earlier runs, and a place to ask it why it did what it did.`
|
|
3721
3752
|
),
|
|
3722
3753
|
el("form", { method: "dialog" }, el("button", { type: "submit", class: "button" }, "Close"))
|
|
3723
3754
|
);
|
|
@@ -3881,11 +3912,7 @@ function runStatus(run, now, handlers2) {
|
|
|
3881
3912
|
);
|
|
3882
3913
|
}
|
|
3883
3914
|
function runDuration(run) {
|
|
3884
|
-
|
|
3885
|
-
const to = parseDate(run.endedAt);
|
|
3886
|
-
if (!from || !to || to < from) return "";
|
|
3887
|
-
const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
|
|
3888
|
-
return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
|
|
3915
|
+
return durationBetween(run.startedAt, run.endedAt);
|
|
3889
3916
|
}
|
|
3890
3917
|
function runConversation(run, busy, answering, enabled, ui2, handlers2, inputRef) {
|
|
3891
3918
|
const turns = run.turns.length ? el(
|
|
@@ -4041,8 +4068,9 @@ function RowPanel({ row, state: state2, ui: ui2, handlers: handlers2 }) {
|
|
|
4041
4068
|
) : el("p", { class: "flyout__empty" }, "Nothing noted yet. A note goes to the morning agent as free text."),
|
|
4042
4069
|
k(NoteForm, { row, title: facts.title, ui: ui2, handlers: handlers2, inputRef: noteInput })
|
|
4043
4070
|
),
|
|
4044
|
-
|
|
4045
|
-
)
|
|
4071
|
+
entry.turns.length ? renderAssistantSection(state2, entry, enabled, handlers2) : null
|
|
4072
|
+
),
|
|
4073
|
+
enabled ? renderAssistantFooter(facts, state2, entry, ui2, handlers2, askInput, row) : null
|
|
4046
4074
|
];
|
|
4047
4075
|
}
|
|
4048
4076
|
function describeRow(row) {
|
|
@@ -4143,21 +4171,27 @@ function NoteForm({
|
|
|
4143
4171
|
el("button", { type: "submit", class: "button button--primary flyout__submit" }, "Save")
|
|
4144
4172
|
);
|
|
4145
4173
|
}
|
|
4146
|
-
function renderAssistantSection(
|
|
4147
|
-
const source = "source" in row ? row.source : "github";
|
|
4148
|
-
const actions = (state2.assistant?.quickActions ?? []).filter(
|
|
4149
|
-
(action) => action.sources === null || action.sources.includes(source)
|
|
4150
|
-
);
|
|
4174
|
+
function renderAssistantSection(state2, entry, enabled, handlers2) {
|
|
4151
4175
|
const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
|
|
4152
4176
|
return el(
|
|
4153
4177
|
"section",
|
|
4154
4178
|
{ class: "flyout__section assistant", "data-running": String(entry.running) },
|
|
4155
4179
|
el("h3", { class: "flyout__label" }, "Assistant"),
|
|
4156
|
-
|
|
4180
|
+
el(
|
|
4157
4181
|
"div",
|
|
4158
4182
|
{ class: "assistant__turns" },
|
|
4159
|
-
entry.turns.map((turn) => renderAssistantTurn(turn, label))
|
|
4160
|
-
)
|
|
4183
|
+
entry.turns.map((turn) => renderAssistantTurn(turn, label, handlers2))
|
|
4184
|
+
),
|
|
4185
|
+
enabled ? null : el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
|
|
4186
|
+
);
|
|
4187
|
+
}
|
|
4188
|
+
function renderAssistantFooter(facts, state2, entry, ui2, handlers2, inputRef, row) {
|
|
4189
|
+
const actions = (state2.assistant?.quickActions ?? []).filter(
|
|
4190
|
+
(action) => action.sources === null || action.sources.includes(facts.source)
|
|
4191
|
+
);
|
|
4192
|
+
return el(
|
|
4193
|
+
"div",
|
|
4194
|
+
{ class: "flyout__footer" },
|
|
4161
4195
|
entry.running ? null : el(
|
|
4162
4196
|
"div",
|
|
4163
4197
|
{ class: "assistant__quick" },
|
|
@@ -4177,20 +4211,6 @@ function renderAssistantSection(row, facts, state2, entry, ui2, handlers2, input
|
|
|
4177
4211
|
k(AssistantComposer, { row, title: facts.title, entry, ui: ui2, handlers: handlers2, inputRef })
|
|
4178
4212
|
);
|
|
4179
4213
|
}
|
|
4180
|
-
function renderAssistantHistory(state2, entry) {
|
|
4181
|
-
const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
|
|
4182
|
-
return el(
|
|
4183
|
-
"section",
|
|
4184
|
-
{ class: "flyout__section assistant", "data-running": "false" },
|
|
4185
|
-
el("h3", { class: "flyout__label" }, "Assistant"),
|
|
4186
|
-
el(
|
|
4187
|
-
"div",
|
|
4188
|
-
{ class: "assistant__turns" },
|
|
4189
|
-
entry.turns.map((turn) => renderAssistantTurn(turn, label))
|
|
4190
|
-
),
|
|
4191
|
-
el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
|
|
4192
|
-
);
|
|
4193
|
-
}
|
|
4194
4214
|
function AssistantComposer({
|
|
4195
4215
|
row,
|
|
4196
4216
|
title,
|
|
@@ -4219,7 +4239,7 @@ function AssistantComposer({
|
|
|
4219
4239
|
rows: 2,
|
|
4220
4240
|
ref: inputRef,
|
|
4221
4241
|
value: ui2.assistantDraft.value,
|
|
4222
|
-
placeholder: entry.turns.length ? "Follow up\u2026" : "
|
|
4242
|
+
placeholder: entry.turns.length ? "Follow up\u2026" : "Ask the assistant about this\u2026",
|
|
4223
4243
|
"aria-label": `Ask the assistant about ${title}`,
|
|
4224
4244
|
disabled: entry.running,
|
|
4225
4245
|
onInput: (event) => {
|
|
@@ -4235,19 +4255,84 @@ function AssistantComposer({
|
|
|
4235
4255
|
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")
|
|
4236
4256
|
);
|
|
4237
4257
|
}
|
|
4238
|
-
function renderAssistantTurn(turn, label) {
|
|
4258
|
+
function renderAssistantTurn(turn, label, handlers2) {
|
|
4239
4259
|
const action = turn.action ? label.get(turn.action) : null;
|
|
4240
4260
|
const extra = action && turn.request.startsWith(action.request) ? turn.request.slice(action.request.length).trim() : null;
|
|
4241
4261
|
const asked = action ? [el("strong", null, action.label), extra ? ` \u2014 ${extra}` : ""] : [turn.request];
|
|
4242
|
-
|
|
4262
|
+
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"));
|
|
4263
|
+
const { body, created } = turn.status === "done" ? splitCreated(turn.reply) : { body: turn.reply, created: [] };
|
|
4264
|
+
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;
|
|
4243
4265
|
return el(
|
|
4244
4266
|
"div",
|
|
4245
|
-
{ class: "assistant__turn", "data-status": turn.status },
|
|
4267
|
+
{ key: turn.id, class: "assistant__turn", "data-status": turn.status },
|
|
4246
4268
|
el("p", { class: "assistant__request" }, asked),
|
|
4247
|
-
|
|
4248
|
-
|
|
4269
|
+
renderWork(turn),
|
|
4270
|
+
body ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(body, { onCopy: (text) => handlers2.copy(text) })) : null,
|
|
4271
|
+
status,
|
|
4272
|
+
turn.status === "done" && (body || created.length) ? el(
|
|
4273
|
+
"div",
|
|
4274
|
+
{ class: "assistant__after" },
|
|
4275
|
+
created.map(
|
|
4276
|
+
(link) => el("a", { class: "button", href: link.url, target: "_blank", rel: "noopener noreferrer", title: link.url }, `${link.label} \u2197`)
|
|
4277
|
+
),
|
|
4278
|
+
body ? el("button", { type: "button", class: "button", onClick: () => handlers2.copy(body) }, "Copy reply") : null
|
|
4279
|
+
) : null
|
|
4280
|
+
);
|
|
4281
|
+
}
|
|
4282
|
+
function renderWork(turn) {
|
|
4283
|
+
const steps = turn.steps ?? [];
|
|
4284
|
+
if (turn.status === "running") {
|
|
4285
|
+
return el(
|
|
4286
|
+
"div",
|
|
4287
|
+
{ class: "assistant__work" },
|
|
4288
|
+
el("p", { class: "assistant__status assistant__status--working" }, "Working \xB7 ", k(Elapsed, { since: turn.startedAt })),
|
|
4289
|
+
steps.length ? renderSteps(steps) : null
|
|
4290
|
+
);
|
|
4291
|
+
}
|
|
4292
|
+
const took = durationBetween(turn.startedAt, turn.endedAt);
|
|
4293
|
+
if (!steps.length) return took ? el("p", { class: "assistant__took" }, `Answered in ${took}`) : null;
|
|
4294
|
+
const failed = steps.filter((step2) => step2.state === "failed").length;
|
|
4295
|
+
const summary = [
|
|
4296
|
+
took ? `${turn.status === "done" ? "Worked" : "Ran"} for ${took}` : null,
|
|
4297
|
+
`${steps.length} ${steps.length === 1 ? "step" : "steps"}`,
|
|
4298
|
+
failed ? `${failed} failed` : null
|
|
4299
|
+
].filter(Boolean).join(" \xB7 ");
|
|
4300
|
+
return el("details", { class: "assistant__work", open: turn.status === "failed" }, el("summary", null, summary), renderSteps(steps));
|
|
4301
|
+
}
|
|
4302
|
+
function renderSteps(steps) {
|
|
4303
|
+
return el(
|
|
4304
|
+
"ol",
|
|
4305
|
+
{ class: "assistant__steps" },
|
|
4306
|
+
steps.map(
|
|
4307
|
+
(step2, index) => step2.kind === "message" ? el("li", { key: index, class: "assistant__step assistant__step--message" }, renderMarkdown(step2.text.trim())) : el(
|
|
4308
|
+
"li",
|
|
4309
|
+
{ key: index, class: "assistant__step", "data-state": step2.state },
|
|
4310
|
+
el("span", { class: "assistant__step-text" }, step2.text),
|
|
4311
|
+
step2.detail ? el("code", { class: "assistant__step-detail", title: step2.detail }, step2.detail) : null
|
|
4312
|
+
)
|
|
4313
|
+
)
|
|
4249
4314
|
);
|
|
4250
4315
|
}
|
|
4316
|
+
function Elapsed({ since }) {
|
|
4317
|
+
const [now, setNow] = d2(() => Date.now());
|
|
4318
|
+
h2(() => {
|
|
4319
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
4320
|
+
return () => clearInterval(timer);
|
|
4321
|
+
}, []);
|
|
4322
|
+
const start = parseDate(since)?.getTime() ?? now;
|
|
4323
|
+
return el("span", { class: "assistant__clock" }, formatClock((now - start) / 1e3));
|
|
4324
|
+
}
|
|
4325
|
+
var CREATED = /^\s*(?:[-*]\s+)?Created:\s*\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)\s*\.?\s*$/i;
|
|
4326
|
+
function splitCreated(reply) {
|
|
4327
|
+
const created = [];
|
|
4328
|
+
const kept = [];
|
|
4329
|
+
for (const line of reply.split("\n")) {
|
|
4330
|
+
const match = CREATED.exec(line);
|
|
4331
|
+
if (match) created.push({ label: match[1].trim(), url: match[2] });
|
|
4332
|
+
else kept.push(line);
|
|
4333
|
+
}
|
|
4334
|
+
return { body: created.length ? kept.join("\n").trim() : reply, created };
|
|
4335
|
+
}
|
|
4251
4336
|
|
|
4252
4337
|
// client/settings.ts
|
|
4253
4338
|
var VIEW_NAME = {
|
|
@@ -4783,14 +4868,8 @@ function boardRow(name, health, text, action) {
|
|
|
4783
4868
|
|
|
4784
4869
|
// client/today.ts
|
|
4785
4870
|
function TodayView({ state: state2, ui: ui2, handlers: handlers2 }) {
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
{ class: "focus-bar", id: "focus-bar", hidden: !ui2.focusMode.value },
|
|
4789
|
-
"Focus mode \u2014 everything but the top item is hidden. ",
|
|
4790
|
-
el("button", { type: "button", class: "button", id: "focus-exit", onClick: () => handlers2.setFocusMode(false) }, "Show everything")
|
|
4791
|
-
);
|
|
4792
|
-
if (state2.setup?.needed) return [focusBar, k(SetupCard, { state: state2, ui: ui2, handlers: handlers2 })];
|
|
4793
|
-
return [focusBar, renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
|
|
4871
|
+
if (state2.setup?.needed) return [k(SetupCard, { state: state2, ui: ui2, handlers: handlers2 })];
|
|
4872
|
+
return [renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
|
|
4794
4873
|
}
|
|
4795
4874
|
function renderObjective(state2, handlers2 = null) {
|
|
4796
4875
|
const focus = state2.focus;
|
|
@@ -4913,7 +4992,6 @@ function App({ ui: ui2, handlers: handlers2 }) {
|
|
|
4913
4992
|
el("a", { class: "skip-link", href: "#main" }, "Skip to content"),
|
|
4914
4993
|
k(Header, { state: current, ui: ui2, handlers: handlers2 }),
|
|
4915
4994
|
k(Tabs, { state: current, ui: ui2, handlers: handlers2 }),
|
|
4916
|
-
current ? k(Timer, { state: current, ui: ui2, handlers: handlers2 }) : el("div", { class: "timer", id: "timer", hidden: true }),
|
|
4917
4995
|
el("div", { id: "banners", class: "banners" }, current ? renderBanners(current, ui2, handlers2) : null),
|
|
4918
4996
|
el(
|
|
4919
4997
|
"main",
|
|
@@ -5031,11 +5109,6 @@ function installKeyboard(handlers2) {
|
|
|
5031
5109
|
event.preventDefault();
|
|
5032
5110
|
moveSelection(-1);
|
|
5033
5111
|
return;
|
|
5034
|
-
case "f":
|
|
5035
|
-
if (ui.view.value !== "today") return;
|
|
5036
|
-
event.preventDefault();
|
|
5037
|
-
handlers2.setFocusMode(!ui.focusMode.value);
|
|
5038
|
-
return;
|
|
5039
5112
|
case "1":
|
|
5040
5113
|
event.preventDefault();
|
|
5041
5114
|
handlers2.setView("today");
|
|
@@ -5080,7 +5153,6 @@ function installKeyboard(handlers2) {
|
|
|
5080
5153
|
if (!item) return;
|
|
5081
5154
|
const boardRow2 = ui.view.value !== "today";
|
|
5082
5155
|
const status = "status" in item ? item.status : null;
|
|
5083
|
-
const kind = "kind" in item ? item.kind : null;
|
|
5084
5156
|
switch (event.key) {
|
|
5085
5157
|
case "e":
|
|
5086
5158
|
event.preventDefault();
|
|
@@ -5103,12 +5175,6 @@ function installKeyboard(handlers2) {
|
|
|
5103
5175
|
event.preventDefault();
|
|
5104
5176
|
handlers2.openDetail(item.id, "assistant");
|
|
5105
5177
|
break;
|
|
5106
|
-
case "p":
|
|
5107
|
-
event.preventDefault();
|
|
5108
|
-
if (boardRow2) break;
|
|
5109
|
-
if (state.value?.session.active?.id === item.id) handlers2.stopSession();
|
|
5110
|
-
else if (status === "open" && kind === "task") handlers2.startSession(item.id);
|
|
5111
|
-
break;
|
|
5112
5178
|
case "o":
|
|
5113
5179
|
if (item.url) {
|
|
5114
5180
|
event.preventDefault();
|
|
@@ -5128,15 +5194,9 @@ R(k(App, { ui, handlers }), root);
|
|
|
5128
5194
|
j3(() => {
|
|
5129
5195
|
document.body.dataset.view = ui.view.value;
|
|
5130
5196
|
});
|
|
5131
|
-
j3(() => {
|
|
5132
|
-
document.body.dataset.focusMode = String(ui.focusMode.value);
|
|
5133
|
-
});
|
|
5134
5197
|
j3(() => {
|
|
5135
5198
|
document.body.dataset.offline = String(ui.connectionError.value !== null);
|
|
5136
5199
|
});
|
|
5137
|
-
j3(() => {
|
|
5138
|
-
document.body.dataset.sessionActive = String(state.value?.session.active != null);
|
|
5139
|
-
});
|
|
5140
5200
|
j3(() => {
|
|
5141
5201
|
const current = state.value;
|
|
5142
5202
|
const open = current !== null && (detailRun(current, ui) !== null || detailRow(current, ui) !== null);
|
|
@@ -5163,16 +5223,31 @@ j3(() => {
|
|
|
5163
5223
|
});
|
|
5164
5224
|
installKeyboard(handlers);
|
|
5165
5225
|
await refresh();
|
|
5166
|
-
|
|
5226
|
+
var zone = watchZone({
|
|
5227
|
+
browserZone,
|
|
5228
|
+
dashboardZone: () => state.value?.timeZone,
|
|
5229
|
+
visible: () => document.visibilityState === "visible",
|
|
5230
|
+
send: async (timeZone) => adoptState(await postTimeZone(timeZone))
|
|
5231
|
+
});
|
|
5232
|
+
zone.look();
|
|
5233
|
+
document.addEventListener("visibilitychange", () => zone.look());
|
|
5234
|
+
window.addEventListener("focus", () => zone.look());
|
|
5235
|
+
setInterval(() => zone.check(), ZONE_CHECK_MS);
|
|
5167
5236
|
followLocation();
|
|
5168
5237
|
window.addEventListener("popstate", followLocation);
|
|
5169
5238
|
window.addEventListener("hashchange", followLocation);
|
|
5239
|
+
var reconnected = false;
|
|
5170
5240
|
subscribe(
|
|
5171
5241
|
// A server push is authoritative, but must not yank a row out from under an
|
|
5172
5242
|
// in-flight click; adoptState keeps anything pending at its optimistic status.
|
|
5173
|
-
(next) =>
|
|
5243
|
+
(next) => {
|
|
5244
|
+
adoptState(next);
|
|
5245
|
+
if (reconnected) zone.look();
|
|
5246
|
+
reconnected = false;
|
|
5247
|
+
},
|
|
5174
5248
|
(err) => {
|
|
5175
5249
|
ui.connectionError.value = err;
|
|
5250
|
+
if (err === null) reconnected = true;
|
|
5176
5251
|
}
|
|
5177
5252
|
);
|
|
5178
5253
|
//# sourceMappingURL=app.js.map
|