taskchef 6.1.1 → 6.1.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "6.1.1",
3
+ "version": "6.1.3",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "6.1.1",
3
+ "version": "6.1.3",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -10,7 +10,7 @@ export async function openTaskFromControl(event, taskId, {
10
10
  method: "POST",
11
11
  });
12
12
  const result = await response.json();
13
- showMessage(result.message);
13
+ if (result.message) showMessage(result.message);
14
14
  } finally {
15
15
  control.disabled = false;
16
16
  }
@@ -25,6 +25,7 @@ const elements = {
25
25
  connectionLabel: document.querySelector("#connection-label"),
26
26
  copyThreadId: document.querySelector("#copy-thread-id"),
27
27
  dashboardMessage: document.querySelector("#dashboard-message"),
28
+ dashboardMessageText: document.querySelector("#dashboard-message-text"),
28
29
  dateFilter: document.querySelector("#date-filter"),
29
30
  dialog: document.querySelector("#task-dialog"),
30
31
  dialogInstruction: document.querySelector("#dialog-instruction"),
@@ -32,6 +33,7 @@ const elements = {
32
33
  dialogProject: document.querySelector("#dialog-project"),
33
34
  dialogSummary: document.querySelector("#dialog-summary"),
34
35
  dialogTitle: document.querySelector("#dialog-title"),
36
+ dismissDashboardMessage: document.querySelector("#dismiss-dashboard-message"),
35
37
  emptyState: document.querySelector("#empty-state"),
36
38
  notifications: document.querySelector("#notifications"),
37
39
  openProject: document.querySelector("#open-codex"),
@@ -227,7 +229,7 @@ function applySnapshot(snapshot) {
227
229
  }
228
230
 
229
231
  function showMessage(message) {
230
- elements.dashboardMessage.textContent = message;
232
+ elements.dashboardMessageText.textContent = message;
231
233
  elements.dashboardMessage.hidden = false;
232
234
  }
233
235
 
@@ -245,6 +247,9 @@ events.addEventListener("dashboard-error", (event) => {
245
247
  elements.projectFilter.addEventListener("change", render);
246
248
  elements.statusFilter.addEventListener("change", render);
247
249
  elements.dateFilter.addEventListener("change", render);
250
+ elements.dismissDashboardMessage.addEventListener("click", () => {
251
+ elements.dashboardMessage.hidden = true;
252
+ });
248
253
  elements.clearNotifications.addEventListener("click", () => {
249
254
  state.notifications = [];
250
255
  renderNotifications();
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <title>TaskChef dashboard</title>
7
+ <title>TaskChef Dashboard</title>
8
8
  <link rel="stylesheet" href="/styles.css">
9
9
  <script src="/app.js" type="module"></script>
10
10
  </head>
@@ -12,7 +12,13 @@
12
12
  <header class="site-header">
13
13
  <div>
14
14
  <p class="eyebrow">TaskChef</p>
15
- <h1>TaskChef dashboard</h1>
15
+ <div class="title-row">
16
+ <picture class="dashboard-icon" aria-hidden="true">
17
+ <source srcset="/assets/taskchef-dark.svg" media="(prefers-color-scheme: dark)">
18
+ <img src="/assets/taskchef.svg" alt="" width="48" height="48">
19
+ </picture>
20
+ <h1>TaskChef Dashboard</h1>
21
+ </div>
16
22
  <p class="subtitle">Latest delegated work, updated as TaskChef reports change.</p>
17
23
  </div>
18
24
  <div class="connection" role="status" aria-live="polite">
@@ -46,7 +52,10 @@
46
52
  <p id="task-count" class="task-count" aria-live="polite"></p>
47
53
  </section>
48
54
 
49
- <div id="dashboard-message" class="dashboard-message" role="status" hidden></div>
55
+ <div id="dashboard-message" class="dashboard-message" hidden>
56
+ <span id="dashboard-message-text" role="status" aria-live="polite"></span>
57
+ <button id="dismiss-dashboard-message" class="icon-button" type="button" aria-label="Dismiss dashboard message">×</button>
58
+ </div>
50
59
  <section aria-labelledby="tasks-heading">
51
60
  <h2 id="tasks-heading" class="visually-hidden">Tasks</h2>
52
61
  <div id="task-list" class="task-list"></div>
@@ -45,6 +45,11 @@ h1 { margin-bottom: 6px; font-size: clamp(2rem, 5vw, 3.4rem); line-height: 1; le
45
45
  h2 { line-height: 1.2; }
46
46
  h3 { margin-bottom: 8px; font-size: 0.84rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
47
47
 
48
+ .title-row { display: flex; align-items: center; gap: clamp(10px, 2vw, 16px); margin-bottom: 6px; }
49
+ .title-row h1 { margin-bottom: 0; }
50
+ .dashboard-icon { display: block; flex: none; width: clamp(38px, 6vw, 48px); aspect-ratio: 1; }
51
+ .dashboard-icon img { display: block; width: 100%; height: 100%; object-fit: contain; }
52
+
48
53
  .eyebrow { margin-bottom: 10px; color: var(--accent); font-size: 0.78rem; font-weight: 750; letter-spacing: 0.15em; text-transform: uppercase; }
49
54
  .subtitle, .task-project, time { color: var(--muted); }
50
55
  .subtitle { margin-bottom: 0; }
@@ -85,7 +90,10 @@ time { font-size: 0.78rem; }
85
90
  .empty-state { padding: 60px 20px; border: 1px dashed var(--border); border-radius: 10px; text-align: center; color: var(--muted); }
86
91
  .empty-state h2 { margin-bottom: 6px; color: var(--text); }
87
92
 
88
- .dashboard-message { margin-bottom: 18px; padding: 11px 14px; border: 1px solid #d8c58e; border-radius: 7px; background: #fff7de; color: #604613; }
93
+ .dashboard-message { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; padding: 8px 8px 8px 14px; border: 1px solid #d8c58e; border-radius: 7px; background: #fff7de; color: #604613; }
94
+ .dashboard-message[hidden] { display: none; }
95
+ .dashboard-message span { flex: 1; }
96
+ .dashboard-message .icon-button { flex: none; padding: 4px 7px; color: inherit; }
89
97
 
90
98
  .notifications { position: fixed; right: 20px; bottom: 20px; z-index: 3; width: min(380px, calc(100vw - 40px)); padding: 14px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); box-shadow: var(--shadow); }
91
99
  .notifications-heading { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
@@ -120,6 +128,7 @@ pre { max-height: 280px; margin: 0; padding: 14px; overflow: auto; border-radius
120
128
 
121
129
  @media (max-width: 650px) {
122
130
  .site-header { align-items: flex-start; padding-top: 32px; }
131
+ .title-row { align-items: flex-start; }
123
132
  .toolbar { align-items: stretch; flex-direction: column; }
124
133
  .toolbar label, .toolbar select { width: 100%; }
125
134
  .task-count { margin: 0; }
package/src/dashboard.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
 
20
20
  const TASKS_FILE_NAME = "tasks.jsonl";
21
21
  const STATIC_ROOT = fileURLToPath(new URL("./dashboard/", import.meta.url));
22
+ const ASSET_ROOT = fileURLToPath(new URL("../assets/", import.meta.url));
22
23
  const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1"]);
23
24
  const DEFAULT_MAX_FILE_BYTES = 16 * 1024 * 1024;
24
25
  const DEFAULT_MAX_TASKS = 2_000;
@@ -36,11 +37,13 @@ const CONTENT_SECURITY_POLICY = [
36
37
  ].join("; ");
37
38
 
38
39
  const STATIC_FILES = new Map([
39
- ["/", ["index.html", "text/html; charset=utf-8"]],
40
- ["/actions.js", ["actions.js", "text/javascript; charset=utf-8"]],
41
- ["/app.js", ["app.js", "text/javascript; charset=utf-8"]],
42
- ["/state.js", ["state.js", "text/javascript; charset=utf-8"]],
43
- ["/styles.css", ["styles.css", "text/css; charset=utf-8"]],
40
+ ["/", [path.join(STATIC_ROOT, "index.html"), "text/html; charset=utf-8"]],
41
+ ["/actions.js", [path.join(STATIC_ROOT, "actions.js"), "text/javascript; charset=utf-8"]],
42
+ ["/app.js", [path.join(STATIC_ROOT, "app.js"), "text/javascript; charset=utf-8"]],
43
+ ["/state.js", [path.join(STATIC_ROOT, "state.js"), "text/javascript; charset=utf-8"]],
44
+ ["/styles.css", [path.join(STATIC_ROOT, "styles.css"), "text/css; charset=utf-8"]],
45
+ ["/assets/taskchef-dark.svg", [path.join(ASSET_ROOT, "taskchef-dark.svg"), "image/svg+xml"]],
46
+ ["/assets/taskchef.svg", [path.join(ASSET_ROOT, "taskchef.svg"), "image/svg+xml"]],
44
47
  ]);
45
48
 
46
49
  export function dashboardAuthority(host, port) {
@@ -494,7 +497,7 @@ export async function createDashboardServer({
494
497
  if (isCodexThreadDeepLinkId(task.threadId)) {
495
498
  if (openThread) await openThread(task.threadId);
496
499
  else await openThreadInCodex(task.threadId);
497
- sendJson(response, 202, { message: "Opened this task in Codex." });
500
+ sendJson(response, 202, {});
498
501
  return;
499
502
  }
500
503
  const trustedProject = (await readConfig(monitor.workspace, { checkPaths: false })).projects
@@ -539,8 +542,8 @@ export async function createDashboardServer({
539
542
  sendJson(response, 404, { message: "Not found." });
540
543
  return;
541
544
  }
542
- const [fileName, contentType] = staticFile;
543
- const body = await readFile(path.join(STATIC_ROOT, fileName));
545
+ const [filePath, contentType] = staticFile;
546
+ const body = await readFile(filePath);
544
547
  response.writeHead(200, securityHeaders(contentType));
545
548
  response.end(method === "HEAD" ? undefined : body);
546
549
  };