daily-focus 1.1.1-dev.3 → 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/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, y4, _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 == y4 && null != v4 && (y4 = 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 = y4, f4;
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, y4, d4, _5, k3, x4, M, I2, P2, A3, H2, T4, j4, F2 = u4.type;
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))), y4 = p5.props, d4 = p5.state, p5.__v = u4, v4) M && null == F2.getDerivedStateFromProps && null != p5.componentWillMount && p5.componentWillMount(), M && null != p5.componentDidMount && p5.__h.push(p5.componentDidMount);
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 !== y4 && null != p5.componentWillReceiveProps && p5.componentWillReceiveProps(x4, P2), u4.__v == t4.__v || !p5.__e && null != p5.shouldComponentUpdate && false === p5.shouldComponentUpdate(x4, p5.__s, P2)) {
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(y4, d4, _5);
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(y4, d4)), 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);
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, y4, w5, _5, m4 = i4.props || d, k3 = t4.props, x4 = t4.type;
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 ((y4 = e4[s4]) && "setAttribute" in y4 == !!x4 && (x4 ? y4.localName == x4 : 3 == y4.nodeType)) {
191
- u4 = y4, e4[s4] = null;
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[(y4 = u4.attributes[s4]).name] = y4.value;
202
- for (s4 in m4) y4 = m4[s4], "dangerouslySetInnerHTML" == s4 ? p5 = y4 : "children" == s4 || s4 in k3 || "value" == s4 && "defaultValue" in k3 || "checked" == s4 && "defaultChecked" in k3 || N(u4, s4, null, y4, o4);
203
- for (s4 in k3) y4 = k3[s4], "children" == s4 ? v4 = y4 : "dangerouslySetInnerHTML" == s4 ? h5 = y4 : "value" == s4 ? w5 = y4 : "checked" == s4 ? _5 = y4 : c4 && "function" != typeof y4 || m4[s4] === y4 || N(u4, s4, y4, m4[s4], o4);
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 d2 = 0;
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
- d2++;
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 y2(i4, t4) {
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 = d2 - 1;
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 === d2) return true;
603
- this.g = d2;
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 d3;
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 (d3) {
769
- var n3 = d3;
770
- d3 = void 0;
807
+ if (d4) {
808
+ var n3 = d4;
809
+ d4 = void 0;
771
810
  n3();
772
811
  }
773
- d3 = i4 && i4.S();
812
+ d4 = i4 && i4.S();
774
813
  }
775
- function y3(i4) {
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
- y3.displayName = "ReactiveTextNode";
806
- Object.defineProperties(l3.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value: y3 }, props: { configurable: true, get: function() {
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 = y2(r4);
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 y2(i4, n3);
997
+ return y3(i4, n3);
959
998
  }, []);
960
999
  }
961
1000
  var q2 = function(i4) {
@@ -1013,9 +1052,6 @@ function subscribe(onState, onError) {
1013
1052
  function postTimeZone(timeZone) {
1014
1053
  return post("/api/timezone", { timeZone });
1015
1054
  }
1016
- function postSession(body) {
1017
- return post("/api/session", body);
1018
- }
1019
1055
  function postBoardRefresh() {
1020
1056
  return post("/api/board/refresh");
1021
1057
  }
@@ -1080,40 +1116,35 @@ var VIEWS = ["today", "board", "tickets", "settings"];
1080
1116
  var RUN_PREFIX = "run:";
1081
1117
  var PANEL_PREFIX = "panel:";
1082
1118
  var VIEW_KEY = "daily-focus:view";
1083
- var UNATTENDED_SEEN_KEY = "daily-focus:unattended-seen";
1084
1119
  var AGENT_RUN_SEEN_KEY = "daily-focus:agent-run-seen";
1085
- var FOCUS_MODE_KEY = "daily-focus:focus-mode";
1086
1120
  var DETAIL_KEY = "daily-focus:detail-for";
1087
- var state = y2(null);
1121
+ var state = y3(null);
1088
1122
  function createUi(initial = {}) {
1089
1123
  return {
1090
- selectedId: y2(initial.selectedId ?? null),
1091
- clickedId: y2(initial.clickedId ?? null),
1092
- pending: y2(initial.pending ?? /* @__PURE__ */ new Set()),
1093
- menuFor: y2(initial.menuFor ?? null),
1094
- statusFor: y2(initial.statusFor ?? null),
1095
- detailFor: y2(initial.detailFor ?? null),
1096
- noteDraft: y2(initial.noteDraft ?? ""),
1097
- assistantDraft: y2(initial.assistantDraft ?? ""),
1098
- focusField: y2(initial.focusField ?? null),
1099
- openDrawers: y2(initial.openDrawers ?? /* @__PURE__ */ new Set()),
1100
- ticketMode: y2(initial.ticketMode ?? "sync"),
1101
- unattendedSeen: y2(initial.unattendedSeen ?? null),
1102
- agentReportSeen: y2(initial.agentReportSeen ?? null),
1103
- messageOpen: y2(initial.messageOpen ?? /* @__PURE__ */ new Map()),
1104
- view: y2(initial.view ?? "today"),
1105
- focusMode: y2(initial.focusMode ?? false),
1106
- theme: y2(initial.theme ?? "system"),
1107
- toast: y2(initial.toast ?? null),
1108
- connectionError: y2(initial.connectionError ?? null),
1109
- clock: y2(initial.clock ?? Date.now()),
1110
- settingsReturn: y2(initial.settingsReturn ?? "today"),
1111
- settings: y2(initial.settings ?? null),
1112
- settingsDraft: y2(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
1113
- settingsError: y2(initial.settingsError ?? null),
1114
- settingsSaving: y2(initial.settingsSaving ?? false),
1115
- texts: y2(initial.texts ?? {}),
1116
- 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)
1117
1148
  };
1118
1149
  }
1119
1150
  function isView(value) {
@@ -1125,9 +1156,7 @@ function remembered() {
1125
1156
  return {
1126
1157
  // Settings is a page to visit, so a reload returns to the view underneath.
1127
1158
  view: isView(view) && view !== "settings" ? view : "today",
1128
- unattendedSeen: localStorage.getItem(UNATTENDED_SEEN_KEY),
1129
1159
  agentReportSeen: localStorage.getItem(AGENT_RUN_SEEN_KEY),
1130
- focusMode: localStorage.getItem(FOCUS_MODE_KEY) === "1",
1131
1160
  detailFor: sessionStorage.getItem(DETAIL_KEY)
1132
1161
  };
1133
1162
  } catch {
@@ -1295,208 +1324,6 @@ function ThemeToggle({ ui: ui2, handlers: handlers2 }) {
1295
1324
  );
1296
1325
  }
1297
1326
 
1298
- // client/format.ts
1299
- var DAY_FORMAT = {
1300
- weekday: "long",
1301
- day: "numeric",
1302
- month: "long"
1303
- };
1304
- function formatters(timeZone) {
1305
- return {
1306
- time: new Intl.DateTimeFormat(void 0, { timeZone, hour: "2-digit", minute: "2-digit" }),
1307
- day: new Intl.DateTimeFormat(void 0, { timeZone, ...DAY_FORMAT }),
1308
- shortDay: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short" }),
1309
- shortDayYear: new Intl.DateTimeFormat(void 0, { timeZone, day: "numeric", month: "short", year: "numeric" }),
1310
- weekday: new Intl.DateTimeFormat(void 0, { timeZone, weekday: "long" })
1311
- };
1312
- }
1313
- var clockFormats = formatters();
1314
- var dateFormats = formatters("UTC");
1315
- var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
1316
- function formatted(value, kind) {
1317
- const bare = typeof value === "string" && DATE_ONLY.test(value);
1318
- const d4 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
1319
- return d4 && !Number.isNaN(d4.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d4) : "";
1320
- }
1321
- function parseDate(value) {
1322
- if (typeof value !== "string" || value === "") return null;
1323
- const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
1324
- if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
1325
- const d4 = new Date(value);
1326
- return Number.isNaN(d4.getTime()) ? null : d4;
1327
- }
1328
- function formatTime(value) {
1329
- return formatted(value, "time");
1330
- }
1331
- function formatDay(value) {
1332
- return formatted(value, "day");
1333
- }
1334
- function formatShortDay(value) {
1335
- return formatted(value, "shortDay");
1336
- }
1337
- function formatWeekday(value) {
1338
- return formatted(value, "weekday");
1339
- }
1340
- var dayKeyFmt = null;
1341
- var calendarZone;
1342
- function setCalendarZone(timeZone) {
1343
- if (timeZone === calendarZone) return;
1344
- calendarZone = timeZone;
1345
- try {
1346
- clockFormats = formatters(timeZone);
1347
- dayKeyFmt = timeZone ? new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }) : null;
1348
- } catch {
1349
- dayKeyFmt = null;
1350
- clockFormats = formatters();
1351
- }
1352
- }
1353
- function dayKey(d4) {
1354
- if (dayKeyFmt) {
1355
- const parts = dayKeyFmt.formatToParts(d4);
1356
- const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
1357
- return `${part("year")}-${part("month")}-${part("day")}`;
1358
- }
1359
- const m4 = String(d4.getMonth() + 1).padStart(2, "0");
1360
- const day = String(d4.getDate()).padStart(2, "0");
1361
- return `${d4.getFullYear()}-${m4}-${day}`;
1362
- }
1363
- function splitKey(key) {
1364
- const [y4, m4, d4] = key.split("-").map(Number);
1365
- return [y4, m4 - 1, d4];
1366
- }
1367
- function keyAt(ms) {
1368
- return new Date(ms).toISOString().slice(0, 10);
1369
- }
1370
- function addDays(key, days) {
1371
- const [y4, m4, d4] = splitKey(key);
1372
- return keyAt(Date.UTC(y4, m4, d4 + days));
1373
- }
1374
- function addMonths(key, months) {
1375
- const [y4, m4, d4] = splitKey(key);
1376
- const lastDay = new Date(Date.UTC(y4, m4 + months + 1, 0)).getUTCDate();
1377
- return keyAt(Date.UTC(y4, m4 + months, Math.min(d4, lastDay)));
1378
- }
1379
- function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
1380
- if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
1381
- const [y4, m4, d4] = splitKey(value);
1382
- const [ty, tm, td] = splitKey(dayKey(now));
1383
- return Math.round((Date.UTC(y4, m4, d4) - Date.UTC(ty, tm, td)) / 864e5);
1384
- }
1385
- const target = parseDate(value);
1386
- if (!target) return null;
1387
- const a4 = Date.UTC(...splitKey(dayKey(now)));
1388
- const b4 = Date.UTC(...splitKey(dayKey(target)));
1389
- return Math.round((b4 - a4) / 864e5);
1390
- }
1391
- function relativeDay(value, now = /* @__PURE__ */ new Date()) {
1392
- const diff = daysFromToday(value, now);
1393
- if (diff === null) return "";
1394
- if (diff === 0) return "today";
1395
- if (diff === 1) return "tomorrow";
1396
- if (diff === -1) return "yesterday";
1397
- if (diff > 1 && diff <= 7) return `in ${diff} days`;
1398
- if (diff < -1 && diff >= -7) return `${Math.abs(diff)} days ago`;
1399
- return formatShortDay(value);
1400
- }
1401
- function formatDuration(minutes) {
1402
- const h5 = Math.floor(minutes / 60);
1403
- const m4 = minutes % 60;
1404
- if (h5 === 0) return `${m4} min`;
1405
- if (m4 === 0) return `${h5} h`;
1406
- return `${h5} h ${m4} min`;
1407
- }
1408
- function relativeTime(value, now = /* @__PURE__ */ new Date()) {
1409
- const d4 = parseDate(value);
1410
- if (!d4) return "";
1411
- const seconds = Math.round((now.getTime() - d4.getTime()) / 1e3);
1412
- if (seconds < 60) return "just now";
1413
- const minutes = Math.round(seconds / 60);
1414
- if (minutes < 60) return `${minutes} min ago`;
1415
- const hours = Math.round(minutes / 60);
1416
- if (hours < 48) return `${hours} h ago`;
1417
- const days = Math.round(hours / 24);
1418
- if (days <= 30) return `${days} days ago`;
1419
- return dayKey(d4).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
1420
- }
1421
- function firstLine(text) {
1422
- const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
1423
- return line.replace(/^[#>*\-\s]+/, "").replace(/[*_`]/g, "").trim();
1424
- }
1425
-
1426
- // client/timer.ts
1427
- function clock(seconds) {
1428
- const total = Math.abs(seconds);
1429
- const mm = Math.floor(total / 60);
1430
- const ss = Math.floor(total % 60);
1431
- return `${seconds < 0 ? "+" : ""}${mm}:${String(ss).padStart(2, "0")}`;
1432
- }
1433
- function remainingSeconds(active, nowMs) {
1434
- return Math.round((new Date(active.endsAt).getTime() - nowMs) / 1e3);
1435
- }
1436
- function unattendedNotice(closed, handlers2) {
1437
- const stoppedAt = formatTime(closed.endedAt);
1438
- const headline = closed.reason === "away" ? `Timer stopped at ${stoppedAt} \u2014 nothing had touched the machine since.` : `Timer stopped at ${stoppedAt} \u2014 it had been left running.`;
1439
- return el(
1440
- "div",
1441
- { class: "timer__away", role: "status" },
1442
- el(
1443
- "span",
1444
- { class: "timer__away-body" },
1445
- el("span", { class: "timer__away-text" }, headline),
1446
- el(
1447
- "span",
1448
- { class: "timer__away-detail" },
1449
- `${formatDuration(closed.actualMinutes)} logged on \u201C${closed.title}\u201D. `,
1450
- closed.reason === "away" ? "The time you were away is not in the record." : "Treat that as an upper bound, not a measurement."
1451
- )
1452
- ),
1453
- el("button", { type: "button", class: "button", onClick: () => handlers2.startSession(closed.id) }, "Pick it back up"),
1454
- el(
1455
- "button",
1456
- {
1457
- type: "button",
1458
- class: "icon-button",
1459
- "aria-label": "Dismiss",
1460
- onClick: () => handlers2.dismissUnattended(closed.endedAt)
1461
- },
1462
- "\u2715"
1463
- )
1464
- );
1465
- }
1466
- function Timer({ state: state2, ui: ui2, handlers: handlers2 }) {
1467
- const { active, completedToday, minutesToday, unattendedClose } = state2.session;
1468
- const closed = unattendedClose && ui2.unattendedSeen.value !== unattendedClose.endedAt ? unattendedClose : null;
1469
- if (!active && completedToday === 0 && !closed) {
1470
- return el("div", { class: "timer", id: "timer", hidden: true });
1471
- }
1472
- const tally = completedToday > 0 ? `${completedToday} session${completedToday === 1 ? "" : "s"} today \xB7 ${formatDuration(minutesToday)}` : null;
1473
- if (!active) {
1474
- return el(
1475
- "div",
1476
- { class: "timer", id: "timer", hidden: false, "data-state": closed ? "stopped" : "idle" },
1477
- closed ? unattendedNotice(closed, handlers2) : null,
1478
- tally ? el("span", { class: "timer__tally" }, tally) : null
1479
- );
1480
- }
1481
- const remaining = remainingSeconds(active, ui2.clock.value);
1482
- const overrun = remaining < 0;
1483
- return el(
1484
- "div",
1485
- { class: "timer", id: "timer", hidden: false, "data-state": overrun ? "overrun" : "running" },
1486
- el("span", { class: "timer__clock" }, clock(remaining)),
1487
- el(
1488
- "span",
1489
- { class: "timer__title" },
1490
- active.title,
1491
- overrun ? el("span", { class: "timer__note" }, ` \xB7 ${active.minutes} min in, still counting`) : null,
1492
- // The one thing a kitchen timer can't tell you.
1493
- active.collidesWithNextEvent && !overrun ? el("span", { class: "timer__note timer__note--warn" }, ` \xB7 runs into ${formatTime(active.nextEventAt)}`) : null
1494
- ),
1495
- tally ? el("span", { class: "timer__tally" }, tally) : null,
1496
- el("button", { type: "button", class: "button", onClick: () => handlers2.stopSession() }, "Stop")
1497
- );
1498
- }
1499
-
1500
1327
  // client/actions.ts
1501
1328
  function afterRender(fn) {
1502
1329
  if (typeof requestAnimationFrame === "function") requestAnimationFrame(fn);
@@ -1550,7 +1377,6 @@ function adoptState(next) {
1550
1377
  state.value = next;
1551
1378
  ui.connectionError.value = null;
1552
1379
  });
1553
- startLocalTick();
1554
1380
  checkStore(next.storeId);
1555
1381
  checkAssetVersion(next.assetVersion);
1556
1382
  }
@@ -1593,7 +1419,8 @@ var PAST_TENSE = {
1593
1419
  dismiss: "Dismissed",
1594
1420
  snooze: "Snoozed",
1595
1421
  reopen: "Restored",
1596
- note: "Note saved"
1422
+ note: "Note saved",
1423
+ nudge: "Nudged, back tomorrow"
1597
1424
  };
1598
1425
  var lastAction = null;
1599
1426
  var parkedFrom = /* @__PURE__ */ new Map();
@@ -1605,6 +1432,9 @@ function unpark(id) {
1605
1432
  function undoLast() {
1606
1433
  if (lastAction && lastAction.action !== "reopen") void unpark(lastAction.id);
1607
1434
  }
1435
+ function isRemark(action) {
1436
+ return action === "note" || action === "nudge";
1437
+ }
1608
1438
  function optimistic(current, id, action, extra) {
1609
1439
  const status = action === "reopen" ? "open" : action === "dismiss" ? "dismissed" : action === "done" ? "done" : "snoozed";
1610
1440
  const statusAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -1631,12 +1461,12 @@ async function applyAction(id, action, extra = {}) {
1631
1461
  if (row && action === "snooze" && item && (item.status === "done" || item.status === "dismissed")) {
1632
1462
  parkedFrom.set(id, item.status === "done" ? "done" : "dismiss");
1633
1463
  }
1634
- if (action !== "note") state.value = optimistic(current, id, action, extra);
1464
+ if (!isRemark(action)) state.value = optimistic(current, id, action, extra);
1635
1465
  });
1636
1466
  try {
1637
1467
  state.value = await postAction({ id, action, ...extra });
1638
1468
  removePending(id);
1639
- if (action === "note") {
1469
+ if (isRemark(action)) {
1640
1470
  showToast(PAST_TENSE[action]);
1641
1471
  return;
1642
1472
  }
@@ -1710,6 +1540,27 @@ async function stopAssistant(id) {
1710
1540
  showToast(`Could not stop: ${err.message}`);
1711
1541
  }
1712
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
+ }
1713
1564
  async function runAgent() {
1714
1565
  if (!state.value?.agentRun?.enabled || state.value.agentRun.last?.status === "running") return;
1715
1566
  const ok = window.confirm(
@@ -1738,53 +1589,6 @@ async function askAgent(run, text) {
1738
1589
  showToast(`Could not ask the morning agent: ${err.message}`);
1739
1590
  }
1740
1591
  }
1741
- async function changeSession(body) {
1742
- if (body.action === "start" && "Notification" in window && Notification.permission === "default") {
1743
- void Notification.requestPermission();
1744
- }
1745
- try {
1746
- state.value = await postSession(body);
1747
- startLocalTick();
1748
- } catch (err) {
1749
- showToast(`Could not change the timer: ${err.message}`);
1750
- }
1751
- }
1752
- var tickTimer = null;
1753
- var announcedOverrunFor = null;
1754
- function startLocalTick() {
1755
- if (tickTimer) clearInterval(tickTimer);
1756
- tickTimer = setInterval(tick, 1e3);
1757
- tick();
1758
- }
1759
- function tick() {
1760
- const active = state.value?.session.active;
1761
- if (!active) {
1762
- document.title = "Daily Focus";
1763
- if (tickTimer) clearInterval(tickTimer);
1764
- tickTimer = null;
1765
- return;
1766
- }
1767
- const nowMs = Date.now();
1768
- ui.clock.value = nowMs;
1769
- const remaining = remainingSeconds(active, nowMs);
1770
- const mm = Math.floor(Math.abs(remaining) / 60);
1771
- const ss = String(Math.floor(Math.abs(remaining) % 60)).padStart(2, "0");
1772
- document.title = `${remaining < 0 ? "+" : ""}${mm}:${ss} \xB7 ${active.title}`;
1773
- if (remaining <= 0 && announcedOverrunFor !== active.startedAt) {
1774
- announcedOverrunFor = active.startedAt;
1775
- announceSessionEnd(active);
1776
- }
1777
- }
1778
- function announceSessionEnd(active) {
1779
- const label = active.minutes === 1 ? "1 minute" : `${active.minutes} minutes`;
1780
- showToast(`${label} in \u2014 still on "${active.title}"?`, null);
1781
- if ("Notification" in window && Notification.permission === "granted") {
1782
- new Notification("Still on this?", {
1783
- body: `${label} in on ${active.title}. Keep going, or stop and log it.`,
1784
- tag: "daily-focus-session"
1785
- });
1786
- }
1787
- }
1788
1592
  function showView(view) {
1789
1593
  if (ui.view.value === view) return;
1790
1594
  if (state.value && !availableViews(state.value).includes(view)) return;
@@ -1833,10 +1637,6 @@ function setTicketMode(mode) {
1833
1637
  closeMenus();
1834
1638
  });
1835
1639
  }
1836
- function setFocusMode(on) {
1837
- ui.focusMode.value = on;
1838
- remember(FOCUS_MODE_KEY, on ? "1" : null);
1839
- }
1840
1640
  function restartToast(page) {
1841
1641
  if (page.restart === "manual") showToast("Saved. Restart the dashboard to apply it.");
1842
1642
  else if (page.restart === "waiting" && state.value?.agentRun?.last?.status === "running") {
@@ -2059,21 +1859,16 @@ var handlers = {
2059
1859
  () => document.querySelector('#tickets .item[data-selected="true"]')?.scrollIntoView({ block: "center", behavior: "smooth" })
2060
1860
  );
2061
1861
  },
2062
- startSession: (id) => void changeSession({ action: "start", id }),
2063
- stopSession: () => void changeSession({ action: "stop" }),
2064
- dismissUnattended: (endedAt) => {
2065
- ui.unattendedSeen.value = endedAt;
2066
- remember(UNATTENDED_SEEN_KEY, endedAt);
2067
- },
2068
- // A nudge on Slack is invisible to GitHub, so it's recorded as a note: the agent
2069
- // reads it as free text, and the board's nudge timer starts over from it.
2070
- nudge: (id) => void applyAction(id, "note", { text: "Nudged reviewers" }),
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"),
2071
1865
  unpark: (id) => void unpark(id),
2072
1866
  refreshBoard: () => void refreshBoard(),
2073
1867
  refreshTickets: () => void refreshTickets(),
2074
1868
  refreshCalendar: () => void refreshCalendar(),
2075
1869
  ask: (id, body) => void ask(id, body),
2076
1870
  stopAssistant: (id) => void stopAssistant(id),
1871
+ copy: (text) => void copy(text),
2077
1872
  runAgent: () => void runAgent(),
2078
1873
  stopAgent: () => void stopAgent(),
2079
1874
  askAgent: (run, text) => void askAgent(run, text),
@@ -2092,7 +1887,6 @@ var handlers = {
2092
1887
  applyTheme(mode);
2093
1888
  ui.theme.value = mode;
2094
1889
  },
2095
- setFocusMode,
2096
1890
  openHelp: () => document.getElementById("help")?.showModal(),
2097
1891
  hideToast,
2098
1892
  openSettings,
@@ -2113,6 +1907,145 @@ var handlers = {
2113
1907
  listCalendars: () => void listCalendars()
2114
1908
  };
2115
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
+
2116
2049
  // client/failure.ts
2117
2050
  function failureNotice(failure, { fold, className, icon, lastGood, now, onRetry }, ui2, handlers2) {
2118
2051
  return el(
@@ -2306,7 +2239,7 @@ function renderMarkdown(value) {
2306
2239
  function anchor(href, label) {
2307
2240
  return el("a", { href: /^https?:\/\//i.test(href) ? href : "#", target: "_blank", rel: "noopener noreferrer" }, label);
2308
2241
  }
2309
- function renderMarkdownBlocks(value) {
2242
+ function renderMarkdownBlocks(value, options = {}) {
2310
2243
  const out = [];
2311
2244
  const lines = String(value).replace(/\r\n/g, "\n").split("\n");
2312
2245
  let i4 = 0;
@@ -2317,7 +2250,16 @@ function renderMarkdownBlocks(value) {
2317
2250
  i4++;
2318
2251
  while (i4 < lines.length && !/^\s*```/.test(lines[i4])) code.push(lines[i4++]);
2319
2252
  i4++;
2320
- out.push(el("pre", null, code.join("\n")));
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
+ );
2321
2263
  continue;
2322
2264
  }
2323
2265
  if (line.trim() === "") {
@@ -2327,7 +2269,7 @@ function renderMarkdownBlocks(value) {
2327
2269
  if (/^\s*>/.test(line)) {
2328
2270
  const quoted = [];
2329
2271
  while (i4 < lines.length && /^\s*>/.test(lines[i4])) quoted.push(lines[i4++].replace(/^\s*>\s?/, ""));
2330
- out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"))));
2272
+ out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"), options)));
2331
2273
  continue;
2332
2274
  }
2333
2275
  if (/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
@@ -2571,9 +2513,6 @@ function renderItem(item, state2, ui2, handlers2) {
2571
2513
  function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
2572
2514
  const now = new Date(state2.now);
2573
2515
  const selected = ui2.selectedId.value === item.id;
2574
- const active = state2.session?.active ?? null;
2575
- const running = active !== null && active.id === item.id;
2576
- const overrun = running && remainingSeconds(active, ui2.clock.value) < 0;
2577
2516
  const repo = githubRepo(item);
2578
2517
  const title = item.url ? el(
2579
2518
  "a",
@@ -2591,8 +2530,6 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
2591
2530
  "data-selected": String(selected),
2592
2531
  "data-priority": String(item.priority !== void 0 && item.status === "open"),
2593
2532
  "data-pending": String(ui2.pending.value.has(item.id)),
2594
- "data-running": String(running),
2595
- "data-overrun": String(overrun),
2596
2533
  "data-detail": String(ui2.detailFor.value === item.id),
2597
2534
  // A click on the card opens the panel on it: one way in, and the whole card is it.
2598
2535
  onClick: (event) => {
@@ -2612,31 +2549,21 @@ function ItemRow({ item, state: state2, ui: ui2, handlers: handlers2 }) {
2612
2549
  "div",
2613
2550
  { class: "item__body" },
2614
2551
  el("p", { class: "item__title" }, title),
2615
- renderMeta(item, state2, ui2, now),
2616
- 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)
2617
2555
  ),
2618
- renderActions(item, state2, ui2, handlers2),
2556
+ renderActions(item, ui2, handlers2),
2619
2557
  ui2.menuFor.value === item.id ? renderSnoozeMenu(item, now, handlers2) : null
2620
2558
  );
2621
2559
  }
2622
- function renderMeta(item, state2, ui2, now) {
2560
+ function renderMeta(item, state2, now) {
2623
2561
  const pills = [];
2624
2562
  const said = /* @__PURE__ */ new Set();
2625
2563
  const pill = (cls, text) => {
2626
2564
  pills.push(el("span", { class: cls }, text));
2627
2565
  said.add(text.trim().toLowerCase());
2628
2566
  };
2629
- const active = state2.session?.active ?? null;
2630
- if (active && active.id === item.id) {
2631
- pills.push(
2632
- el(
2633
- "span",
2634
- { class: "pill pill--running" },
2635
- el("span", { class: "item__running-clock" }, clock(remainingSeconds(active, ui2.clock.value))),
2636
- " focusing"
2637
- )
2638
- );
2639
- }
2640
2567
  if (item.priority !== void 0 && item.status === "open") {
2641
2568
  pill("pill pill--priority", `#${item.priority}`);
2642
2569
  }
@@ -2688,24 +2615,43 @@ function rowPillTexts(row, state2) {
2688
2615
  function rowPills(row, state2) {
2689
2616
  return rowPillTexts(row, state2).map(([cls, text]) => el("span", { class: cls }, text));
2690
2617
  }
2691
- function renderActions(item, state2, ui2, handlers2) {
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) {
2692
2651
  const buttons = [];
2693
2652
  const act = (id, action) => () => handlers2.onAction(id, action);
2694
- const running = state2.session?.active?.id === item.id;
2695
2653
  if (item.status === "open") {
2696
2654
  if (item.kind === "task") {
2697
- buttons.push(
2698
- el(
2699
- "button",
2700
- {
2701
- type: "button",
2702
- class: running ? "button button--primary" : "button",
2703
- title: running ? "Stop the focus session" : "Start a focus session on this",
2704
- onClick: () => running ? handlers2.stopSession() : handlers2.startSession(item.id)
2705
- },
2706
- running ? "Stop" : "Focus"
2707
- )
2708
- );
2709
2655
  buttons.push(el("button", { type: "button", class: "button", onClick: act(item.id, "done") }, "Done"));
2710
2656
  buttons.push(
2711
2657
  el(
@@ -2905,8 +2851,10 @@ function renderBoard(state2, ui2, handlers2) {
2905
2851
  const open = board.rows.filter((row) => row.status === "open");
2906
2852
  for (const court of COURT_ORDER) {
2907
2853
  const rows = open.filter((row) => row.court === court);
2908
- if (rows.length === 0) continue;
2909
- parts.push(section(COURT_TITLE[court], rows, state2, ui2, handlers2, null, renderPullRow));
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));
2910
2858
  }
2911
2859
  if (open.length === 0 && !board.reason) {
2912
2860
  parts.push(
@@ -2963,6 +2911,7 @@ function PullRow({ item: row, state: state2, ui: ui2, handlers: handlers2 }) {
2963
2911
  "data-id": row.id,
2964
2912
  "data-status": row.status,
2965
2913
  "data-court": row.court,
2914
+ "data-nudged": String(Boolean(row.nudgedAt)),
2966
2915
  "data-selected": String(selected),
2967
2916
  "data-pending": String(ui2.pending.value.has(row.id)),
2968
2917
  "data-detail": String(ui2.detailFor.value === row.id),
@@ -2991,7 +2940,8 @@ function PullRow({ item: row, state: state2, ui: ui2, handlers: handlers2 }) {
2991
2940
  ),
2992
2941
  renderPullMeta(row, state2, now),
2993
2942
  renderPullReasons(row, now),
2994
- renderCancelledChecks(row)
2943
+ renderCancelledChecks(row),
2944
+ renderAhead(row.id, state2)
2995
2945
  ),
2996
2946
  renderPullActions(row, ui2, handlers2),
2997
2947
  ui2.menuFor.value === row.id ? renderSnoozeMenu(row, now, handlers2, { indefinite: false }) : null
@@ -3010,6 +2960,9 @@ function renderPullMeta(row, state2, now) {
3010
2960
  if (row.mergeable === "CONFLICTING") pill("pill pill--overdue", "conflicts");
3011
2961
  if (row.autoMerge) pill("pill pill--good", "auto-merge on");
3012
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
+ }
3013
2966
  if (row.court === "blocked") {
3014
2967
  pill("pill", `blocked ${waitedFor(row.since, now)}`);
3015
2968
  } else if (row.court === "gate" || row.court === "checks") {
@@ -3114,7 +3067,7 @@ function renderPullActions(row, ui2, handlers2) {
3114
3067
  )
3115
3068
  );
3116
3069
  if (row.status === "open") {
3117
- if (row.court === "reviewers") {
3070
+ if (row.court === "reviewers" && !row.nudgedAt) {
3118
3071
  buttons.push(
3119
3072
  el(
3120
3073
  "button",
@@ -3151,7 +3104,7 @@ var TICKET_COURT_TITLE = {
3151
3104
  };
3152
3105
  var TICKET_COURT_ORDER = ["settled", "started", "idle"];
3153
3106
  var TICKET_COURT_HINT = {
3154
- settled: "Every pull request Jira has for 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.",
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.",
3155
3108
  started: "A pull request is open, so the work has begun. The status still says it has not.",
3156
3109
  idle: "Nothing in Jira links any code to these. Fine for work that is not code; worth a look otherwise."
3157
3110
  };
@@ -3362,7 +3315,8 @@ function TicketRowView({ item: row, state: state2, ui: ui2, handlers: handlers2
3362
3315
  { class: "item__body" },
3363
3316
  renderTicketStatus(row, state2, ui2, handlers2),
3364
3317
  el("p", { class: "item__title" }, title),
3365
- renderTicketMeta(row, state2, handlers2)
3318
+ renderTicketMeta(row, state2, handlers2),
3319
+ renderGitHubEvidence(row, now)
3366
3320
  ),
3367
3321
  renderTicketActions(row, ui2, handlers2),
3368
3322
  ui2.menuFor.value === row.id ? renderSnoozeMenu(row, now, handlers2, PARK_MENU) : null
@@ -3436,6 +3390,25 @@ function renderTicketMeta(row, state2, handlers2) {
3436
3390
  pills.push(...rowPills(row, state2));
3437
3391
  return pills.length > 0 ? el("div", { class: "item__meta item__meta--extra" }, pills) : null;
3438
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
+ }
3439
3412
  function outOfSyncFlag(row, state2, handlers2) {
3440
3413
  const hit = state2.tickets?.rows?.find((candidate) => candidate.id === row.id && candidate.status === "open");
3441
3414
  if (!hit) return null;
@@ -3746,8 +3719,6 @@ var SHORTCUTS = [
3746
3719
  [["Esc"], "Close the menu, then the panel"],
3747
3720
  [["o"], "Open the source link"],
3748
3721
  [["u"], "Undo the last action"],
3749
- [["f"], "Focus mode \u2014 hide all but the top item"],
3750
- [["p"], "Start a focus session on the selected item"],
3751
3722
  [["r"], "Refresh whichever board you are on"],
3752
3723
  [["w"], "Jira tickets: switch between out of sync and working on"],
3753
3724
  [[","], "Settings: your objective, where the agent looks, and the rest"],
@@ -3777,7 +3748,7 @@ function HelpDialog(_props) {
3777
3748
  el(
3778
3749
  "p",
3779
3750
  { class: "help__note" },
3780
- `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; it reads and drafts, never sends or posts. 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.`
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.`
3781
3752
  ),
3782
3753
  el("form", { method: "dialog" }, el("button", { type: "submit", class: "button" }, "Close"))
3783
3754
  );
@@ -3941,11 +3912,7 @@ function runStatus(run, now, handlers2) {
3941
3912
  );
3942
3913
  }
3943
3914
  function runDuration(run) {
3944
- const from = parseDate(run.startedAt);
3945
- const to = parseDate(run.endedAt);
3946
- if (!from || !to || to < from) return "";
3947
- const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
3948
- return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
3915
+ return durationBetween(run.startedAt, run.endedAt);
3949
3916
  }
3950
3917
  function runConversation(run, busy, answering, enabled, ui2, handlers2, inputRef) {
3951
3918
  const turns = run.turns.length ? el(
@@ -4101,8 +4068,9 @@ function RowPanel({ row, state: state2, ui: ui2, handlers: handlers2 }) {
4101
4068
  ) : el("p", { class: "flyout__empty" }, "Nothing noted yet. A note goes to the morning agent as free text."),
4102
4069
  k(NoteForm, { row, title: facts.title, ui: ui2, handlers: handlers2, inputRef: noteInput })
4103
4070
  ),
4104
- enabled ? renderAssistantSection(row, facts, state2, entry, ui2, handlers2, askInput) : entry.turns.length ? renderAssistantHistory(state2, entry) : null
4105
- )
4071
+ entry.turns.length ? renderAssistantSection(state2, entry, enabled, handlers2) : null
4072
+ ),
4073
+ enabled ? renderAssistantFooter(facts, state2, entry, ui2, handlers2, askInput, row) : null
4106
4074
  ];
4107
4075
  }
4108
4076
  function describeRow(row) {
@@ -4203,21 +4171,27 @@ function NoteForm({
4203
4171
  el("button", { type: "submit", class: "button button--primary flyout__submit" }, "Save")
4204
4172
  );
4205
4173
  }
4206
- function renderAssistantSection(row, facts, state2, entry, ui2, handlers2, inputRef) {
4207
- const source = "source" in row ? row.source : "github";
4208
- const actions = (state2.assistant?.quickActions ?? []).filter(
4209
- (action) => action.sources === null || action.sources.includes(source)
4210
- );
4174
+ function renderAssistantSection(state2, entry, enabled, handlers2) {
4211
4175
  const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
4212
4176
  return el(
4213
4177
  "section",
4214
4178
  { class: "flyout__section assistant", "data-running": String(entry.running) },
4215
4179
  el("h3", { class: "flyout__label" }, "Assistant"),
4216
- entry.turns.length ? el(
4180
+ el(
4217
4181
  "div",
4218
4182
  { class: "assistant__turns" },
4219
- entry.turns.map((turn) => renderAssistantTurn(turn, label))
4220
- ) : null,
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" },
4221
4195
  entry.running ? null : el(
4222
4196
  "div",
4223
4197
  { class: "assistant__quick" },
@@ -4237,20 +4211,6 @@ function renderAssistantSection(row, facts, state2, entry, ui2, handlers2, input
4237
4211
  k(AssistantComposer, { row, title: facts.title, entry, ui: ui2, handlers: handlers2, inputRef })
4238
4212
  );
4239
4213
  }
4240
- function renderAssistantHistory(state2, entry) {
4241
- const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
4242
- return el(
4243
- "section",
4244
- { class: "flyout__section assistant", "data-running": "false" },
4245
- el("h3", { class: "flyout__label" }, "Assistant"),
4246
- el(
4247
- "div",
4248
- { class: "assistant__turns" },
4249
- entry.turns.map((turn) => renderAssistantTurn(turn, label))
4250
- ),
4251
- el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
4252
- );
4253
- }
4254
4214
  function AssistantComposer({
4255
4215
  row,
4256
4216
  title,
@@ -4279,7 +4239,7 @@ function AssistantComposer({
4279
4239
  rows: 2,
4280
4240
  ref: inputRef,
4281
4241
  value: ui2.assistantDraft.value,
4282
- placeholder: entry.turns.length ? "Follow up\u2026" : "What do you need help with?",
4242
+ placeholder: entry.turns.length ? "Follow up\u2026" : "Ask the assistant about this\u2026",
4283
4243
  "aria-label": `Ask the assistant about ${title}`,
4284
4244
  disabled: entry.running,
4285
4245
  onInput: (event) => {
@@ -4295,19 +4255,84 @@ function AssistantComposer({
4295
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")
4296
4256
  );
4297
4257
  }
4298
- function renderAssistantTurn(turn, label) {
4258
+ function renderAssistantTurn(turn, label, handlers2) {
4299
4259
  const action = turn.action ? label.get(turn.action) : null;
4300
4260
  const extra = action && turn.request.startsWith(action.request) ? turn.request.slice(action.request.length).trim() : null;
4301
4261
  const asked = action ? [el("strong", null, action.label), extra ? ` \u2014 ${extra}` : ""] : [turn.request];
4302
- const status = turn.status === "running" ? el("p", { class: "assistant__status" }, "Working\u2026") : 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;
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;
4303
4265
  return el(
4304
4266
  "div",
4305
- { class: "assistant__turn", "data-status": turn.status },
4267
+ { key: turn.id, class: "assistant__turn", "data-status": turn.status },
4306
4268
  el("p", { class: "assistant__request" }, asked),
4307
- turn.reply ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(turn.reply)) : null,
4308
- status
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
+ )
4309
4314
  );
4310
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
+ }
4311
4336
 
4312
4337
  // client/settings.ts
4313
4338
  var VIEW_NAME = {
@@ -4843,14 +4868,8 @@ function boardRow(name, health, text, action) {
4843
4868
 
4844
4869
  // client/today.ts
4845
4870
  function TodayView({ state: state2, ui: ui2, handlers: handlers2 }) {
4846
- const focusBar = el(
4847
- "p",
4848
- { class: "focus-bar", id: "focus-bar", hidden: !ui2.focusMode.value },
4849
- "Focus mode \u2014 everything but the top item is hidden. ",
4850
- el("button", { type: "button", class: "button", id: "focus-exit", onClick: () => handlers2.setFocusMode(false) }, "Show everything")
4851
- );
4852
- if (state2.setup?.needed) return [focusBar, k(SetupCard, { state: state2, ui: ui2, handlers: handlers2 })];
4853
- return [focusBar, renderObjective(state2, handlers2), renderStats(state2), renderHeadline(state2), renderSections(state2, ui2, handlers2)];
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)];
4854
4873
  }
4855
4874
  function renderObjective(state2, handlers2 = null) {
4856
4875
  const focus = state2.focus;
@@ -4973,7 +4992,6 @@ function App({ ui: ui2, handlers: handlers2 }) {
4973
4992
  el("a", { class: "skip-link", href: "#main" }, "Skip to content"),
4974
4993
  k(Header, { state: current, ui: ui2, handlers: handlers2 }),
4975
4994
  k(Tabs, { state: current, ui: ui2, handlers: handlers2 }),
4976
- current ? k(Timer, { state: current, ui: ui2, handlers: handlers2 }) : el("div", { class: "timer", id: "timer", hidden: true }),
4977
4995
  el("div", { id: "banners", class: "banners" }, current ? renderBanners(current, ui2, handlers2) : null),
4978
4996
  el(
4979
4997
  "main",
@@ -5091,11 +5109,6 @@ function installKeyboard(handlers2) {
5091
5109
  event.preventDefault();
5092
5110
  moveSelection(-1);
5093
5111
  return;
5094
- case "f":
5095
- if (ui.view.value !== "today") return;
5096
- event.preventDefault();
5097
- handlers2.setFocusMode(!ui.focusMode.value);
5098
- return;
5099
5112
  case "1":
5100
5113
  event.preventDefault();
5101
5114
  handlers2.setView("today");
@@ -5140,7 +5153,6 @@ function installKeyboard(handlers2) {
5140
5153
  if (!item) return;
5141
5154
  const boardRow2 = ui.view.value !== "today";
5142
5155
  const status = "status" in item ? item.status : null;
5143
- const kind = "kind" in item ? item.kind : null;
5144
5156
  switch (event.key) {
5145
5157
  case "e":
5146
5158
  event.preventDefault();
@@ -5163,12 +5175,6 @@ function installKeyboard(handlers2) {
5163
5175
  event.preventDefault();
5164
5176
  handlers2.openDetail(item.id, "assistant");
5165
5177
  break;
5166
- case "p":
5167
- event.preventDefault();
5168
- if (boardRow2) break;
5169
- if (state.value?.session.active?.id === item.id) handlers2.stopSession();
5170
- else if (status === "open" && kind === "task") handlers2.startSession(item.id);
5171
- break;
5172
5178
  case "o":
5173
5179
  if (item.url) {
5174
5180
  event.preventDefault();
@@ -5188,15 +5194,9 @@ R(k(App, { ui, handlers }), root);
5188
5194
  j3(() => {
5189
5195
  document.body.dataset.view = ui.view.value;
5190
5196
  });
5191
- j3(() => {
5192
- document.body.dataset.focusMode = String(ui.focusMode.value);
5193
- });
5194
5197
  j3(() => {
5195
5198
  document.body.dataset.offline = String(ui.connectionError.value !== null);
5196
5199
  });
5197
- j3(() => {
5198
- document.body.dataset.sessionActive = String(state.value?.session.active != null);
5199
- });
5200
5200
  j3(() => {
5201
5201
  const current = state.value;
5202
5202
  const open = current !== null && (detailRun(current, ui) !== null || detailRow(current, ui) !== null);
@@ -5223,7 +5223,6 @@ j3(() => {
5223
5223
  });
5224
5224
  installKeyboard(handlers);
5225
5225
  await refresh();
5226
- startLocalTick();
5227
5226
  var zone = watchZone({
5228
5227
  browserZone,
5229
5228
  dashboardZone: () => state.value?.timeZone,