daily-focus 2.0.0-dev.17 → 2.0.0-dev.19

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/SETUP.md CHANGED
@@ -431,19 +431,29 @@ Download `Daily-Focus.dmg` from the [latest release](https://github.com/watson/d
431
431
  open it and drag the app to Applications, then open it from there.
432
432
  It has no window and no Dock icon: look for it in the menu bar.
433
433
 
434
- The dashboard updates without a new disk image. **Updates** in the menu shows two
435
- versions, the app's and the dashboard's, and which releases to take: **Stable
436
- Releases**, or **Development Builds**, a build of every change as soon as it is
437
- merged. A few times a day the app asks npm for the newest release, and when it
438
- finds a newer dashboard, it posts a notification and the menu offers **Install
439
- Dashboard**. Installing downloads it from the GitHub release, a few hundred
440
- kilobytes, and runs it only if it is signed by the same developer as the app
441
- and nothing in it has changed. The dashboard restarts into it once the morning
442
- agent and the assistant aren't busy. If it doesn't start, the app goes back to the
443
- one it had and says so. The app itself changes less often; when a release brings a
444
- new one, or a dashboard that needs one, the menu links to its disk image instead.
445
- A dashboard that `daily-focus service` or a terminal runs is updated the way it was
446
- installed, not by the app.
434
+ The app keeps itself up to date without a new disk image. **Updates** in the menu
435
+ shows two versions, the app's and the dashboard's, and which releases to take:
436
+ **Stable Releases**, or **Development Builds**, a build of every change as soon as
437
+ it is merged. A few times a day the app asks npm for the newest release, and when
438
+ it finds one, it posts a notification and the menu offers **Update Daily Focus**.
439
+ Updating fetches only what the release changed. Most releases change only the
440
+ dashboard, a few hundred kilobytes, and the dashboard restarts into it; if it
441
+ doesn't start, the app goes back to the one it had and says so. When a release
442
+ changes the app itself, the app downloads the new app, about 40 MB, puts it in its
443
+ own place and opens it again, and the new app carries the new dashboard. Either
444
+ way it installs nothing that isn't signed by the same developer as the app, with
445
+ nothing in it changed. It waits until the morning agent, the assistant and the
446
+ setup screen aren't at work; pressing **Update** while one is asks whether to wait
447
+ for it or stop it. An app that can't change the folder it is in, such as one
448
+ opened straight from the disk image, links to the new disk image instead, and
449
+ still updates its dashboard on its own. A dashboard that `daily-focus service` or
450
+ a terminal runs is updated the way it was installed, not by the app.
451
+
452
+ Updates install only when you ask, unless you turn on **Update Automatically**
453
+ under **Updates**; the app asks once, when setup is finished. Then an update goes
454
+ in on its own once nobody has used the dashboard for half an hour, never while
455
+ anything in it is at work. Using it means clicking, typing or scrolling in a tab
456
+ you can see, from whichever computer you read it on.
447
457
 
448
458
  To build it yourself from a checkout:
449
459
 
package/dist/cli.js CHANGED
@@ -68,7 +68,7 @@ async function demo(open, profile) {
68
68
  );
69
69
  }
70
70
  async function start(open, onStarted, options = {}) {
71
- const { runServer } = await import("./server-FFRNYEAO.js");
71
+ const { runServer } = await import("./server-V44HFRLA.js");
72
72
  await runServer({
73
73
  ...options,
74
74
  onStarted: (url) => {
@@ -4989,7 +4989,7 @@ function configure(sources) {
4989
4989
  return { config: loadConfig(layerSettings(sources, {})), warnings };
4990
4990
  }
4991
4991
  }
4992
- function statusOf(state, config, busy) {
4992
+ function statusOf(state, config, busyWith, usedAt) {
4993
4993
  const last = state.agentRun.last;
4994
4994
  return {
4995
4995
  version: VERSION2,
@@ -4997,7 +4997,9 @@ function statusOf(state, config, busy) {
4997
4997
  profile: config.profile,
4998
4998
  setupNeeded: state.setup.needed,
4999
4999
  restartPending: state.restart !== null,
5000
- busy,
5000
+ busy: busyWith.length > 0,
5001
+ busyWith,
5002
+ usedAt: usedAt.toISOString(),
5001
5003
  brief: {
5002
5004
  generatedAt: state.brief.generatedAt,
5003
5005
  ageHours: state.brief.ageHours,
@@ -5072,6 +5074,7 @@ async function startServer(env, options = {}) {
5072
5074
  restartWhenIdle();
5073
5075
  }
5074
5076
  });
5077
+ let usedAt = /* @__PURE__ */ new Date();
5075
5078
  const onboarding = new Onboarding(config, {
5076
5079
  onChange: () => {
5077
5080
  void broadcast();
@@ -5660,7 +5663,18 @@ ${entry.source}`)).map(({ title, source }) => ({ title, source })).sort((a, b) =
5660
5663
  return;
5661
5664
  }
5662
5665
  if (path === "/api/status" && req.method === "GET") {
5663
- sendJSON(res, 200, statusOf(await buildState(), config, agent.busy || assistant.busy));
5666
+ const busyWith = [
5667
+ ...agent.busy ? ["agent"] : [],
5668
+ ...assistant.busy ? ["assistant"] : [],
5669
+ ...onboarding.busy ? ["setup"] : []
5670
+ ];
5671
+ sendJSON(res, 200, statusOf(await buildState(), config, busyWith, usedAt));
5672
+ return;
5673
+ }
5674
+ if (path === "/api/presence" && req.method === "POST") {
5675
+ usedAt = /* @__PURE__ */ new Date();
5676
+ res.writeHead(204, { "cache-control": "no-store" });
5677
+ res.end();
5664
5678
  return;
5665
5679
  }
5666
5680
  if (path === "/api/health" && req.method === "GET") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daily-focus",
3
- "version": "2.0.0-dev.17",
3
+ "version": "2.0.0-dev.19",
4
4
  "description": "An interactive dashboard of what you should focus on today at work",
5
5
  "keywords": [
6
6
  "dashboard",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "daily-focus": {
43
43
  "appInterface": 1,
44
- "appSource": "b9481d3565f319ac0190aee10fd633c8f06438e0"
44
+ "appSource": "6a3ab505f4ac808ebaabb673be81176e3f699c22"
45
45
  },
46
46
  "scripts": {
47
47
  "start": "node src/server.ts",
package/public/app.js CHANGED
@@ -1052,6 +1052,14 @@ function subscribe(onState, onError) {
1052
1052
  function postTimeZone(timeZone) {
1053
1053
  return post("/api/timezone", { timeZone });
1054
1054
  }
1055
+ async function postPresence() {
1056
+ const res = await fetch("/api/presence", {
1057
+ method: "POST",
1058
+ headers: { "content-type": "application/json" },
1059
+ body: "{}"
1060
+ });
1061
+ if (!res.ok) throw new Error(`POST /api/presence returned ${res.status}`);
1062
+ }
1055
1063
  function postBoardRefresh() {
1056
1064
  return post("/api/board/refresh");
1057
1065
  }
@@ -5746,6 +5754,21 @@ function installKeyboard(handlers2) {
5746
5754
  });
5747
5755
  }
5748
5756
 
5757
+ // client/presence.ts
5758
+ var PRESENCE_EVERY_MS = 6e4;
5759
+ function watchPresence(deps) {
5760
+ let last = -Infinity;
5761
+ return {
5762
+ noticed() {
5763
+ if (!deps.visible()) return;
5764
+ const now = deps.now();
5765
+ if (now - last < PRESENCE_EVERY_MS) return;
5766
+ last = now;
5767
+ deps.send().catch((err) => console.warn("[daily-focus] could not tell the dashboard it is in use:", err));
5768
+ }
5769
+ };
5770
+ }
5771
+
5749
5772
  // client/main.ts
5750
5773
  ui.theme.value = stampedTheme();
5751
5774
  j3(() => setCalendarZone(state.value?.timeZone));
@@ -5797,6 +5820,16 @@ zone.look();
5797
5820
  document.addEventListener("visibilitychange", () => zone.look());
5798
5821
  window.addEventListener("focus", () => zone.look());
5799
5822
  setInterval(() => zone.check(), ZONE_CHECK_MS);
5823
+ var presence = watchPresence({
5824
+ visible: () => document.visibilityState === "visible",
5825
+ now: Date.now,
5826
+ send: postPresence
5827
+ });
5828
+ presence.noticed();
5829
+ for (const event of ["pointerdown", "keydown", "wheel", "touchstart"]) {
5830
+ window.addEventListener(event, () => presence.noticed(), { capture: true, passive: true });
5831
+ }
5832
+ document.addEventListener("visibilitychange", () => presence.noticed());
5800
5833
  followLocation();
5801
5834
  window.addEventListener("popstate", followLocation);
5802
5835
  window.addEventListener("hashchange", followLocation);