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/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) {
@@ -1010,8 +1049,8 @@ function subscribe(onState, onError) {
1010
1049
  source.addEventListener("open", () => onError(null));
1011
1050
  return () => source.close();
1012
1051
  }
1013
- function postSession(body) {
1014
- return post("/api/session", body);
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 = y2(null);
1121
+ var state = y3(null);
1085
1122
  function createUi(initial = {}) {
1086
1123
  return {
1087
- selectedId: y2(initial.selectedId ?? null),
1088
- clickedId: y2(initial.clickedId ?? null),
1089
- pending: y2(initial.pending ?? /* @__PURE__ */ new Set()),
1090
- menuFor: y2(initial.menuFor ?? null),
1091
- statusFor: y2(initial.statusFor ?? null),
1092
- detailFor: y2(initial.detailFor ?? null),
1093
- noteDraft: y2(initial.noteDraft ?? ""),
1094
- assistantDraft: y2(initial.assistantDraft ?? ""),
1095
- focusField: y2(initial.focusField ?? null),
1096
- openDrawers: y2(initial.openDrawers ?? /* @__PURE__ */ new Set()),
1097
- ticketMode: y2(initial.ticketMode ?? "sync"),
1098
- unattendedSeen: y2(initial.unattendedSeen ?? null),
1099
- agentReportSeen: y2(initial.agentReportSeen ?? null),
1100
- messageOpen: y2(initial.messageOpen ?? /* @__PURE__ */ new Map()),
1101
- view: y2(initial.view ?? "today"),
1102
- focusMode: y2(initial.focusMode ?? false),
1103
- theme: y2(initial.theme ?? "system"),
1104
- toast: y2(initial.toast ?? null),
1105
- connectionError: y2(initial.connectionError ?? null),
1106
- clock: y2(initial.clock ?? Date.now()),
1107
- settingsReturn: y2(initial.settingsReturn ?? "today"),
1108
- settings: y2(initial.settings ?? null),
1109
- settingsDraft: y2(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
1110
- settingsError: y2(initial.settingsError ?? null),
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 !== "note") state.value = optimistic(current, id, action, extra);
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 === "note") {
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
- startSession: (id) => void changeSession({ action: "start", id }),
2049
- stopSession: () => void changeSession({ action: "stop" }),
2050
- dismissUnattended: (endedAt) => {
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
- 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
+ );
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, ui2, now),
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, state2, ui2, handlers2),
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, ui2, now) {
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
- 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) {
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
- if (rows.length === 0) continue;
2895
- 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));
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 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.",
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; 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.`
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
- const from = parseDate(run.startedAt);
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
- enabled ? renderAssistantSection(row, facts, state2, entry, ui2, handlers2, askInput) : entry.turns.length ? renderAssistantHistory(state2, entry) : null
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(row, facts, state2, entry, ui2, handlers2, inputRef) {
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
- entry.turns.length ? el(
4180
+ el(
4157
4181
  "div",
4158
4182
  { class: "assistant__turns" },
4159
- entry.turns.map((turn) => renderAssistantTurn(turn, label))
4160
- ) : 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" },
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" : "What do you need help with?",
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
- 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;
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
- turn.reply ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(turn.reply)) : null,
4248
- 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
+ )
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
- const focusBar = el(
4787
- "p",
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
- startLocalTick();
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) => adoptState(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