taskchef 7.23.3 → 7.24.0
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/.codex-plugin/plugin.json +1 -1
- package/package.json +24 -2
- package/src/dashboard/app.js +2 -82
- package/src/dashboard/dist/app.js +164 -0
- package/src/dashboard/dist/app.js.map +1 -0
- package/src/dashboard/dist/index.html +16 -0
- package/src/dashboard/dist/styles.css +1 -0
- package/src/dashboard/react/App.tsx +432 -0
- package/src/dashboard/react/api.test.ts +24 -0
- package/src/dashboard/react/api.ts +95 -0
- package/src/dashboard/react/components/ActivityTimeline.tsx +63 -0
- package/src/dashboard/react/components/GitHubLinks.tsx +25 -0
- package/src/dashboard/react/components/NotificationCenter.test.tsx +71 -0
- package/src/dashboard/react/components/NotificationCenter.tsx +111 -0
- package/src/dashboard/react/components/RelativeTime.tsx +28 -0
- package/src/dashboard/react/components/ShimmerText.tsx +9 -0
- package/src/dashboard/react/components/StatusBadge.tsx +7 -0
- package/src/dashboard/react/components/TaskCard.tsx +62 -0
- package/src/dashboard/react/components/TaskDetail.test.tsx +62 -0
- package/src/dashboard/react/components/TaskDetail.tsx +251 -0
- package/src/dashboard/react/components/UsagePanel.tsx +41 -0
- package/src/dashboard/react/fixtures.ts +52 -0
- package/src/dashboard/react/index.html +15 -0
- package/src/dashboard/react/main.tsx +12 -0
- package/src/dashboard/react/overlay-state.test.ts +79 -0
- package/src/dashboard/react/overlay-state.ts +38 -0
- package/src/dashboard/react/presentation.test.tsx +128 -0
- package/src/dashboard/react/presentation.ts +80 -0
- package/src/dashboard/react/styles.css +123 -0
- package/src/dashboard/react/test-setup.ts +36 -0
- package/src/dashboard/react/types.ts +106 -0
- package/src/dashboard/styles.css +16 -3
- package/src/dashboard.js +29 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taskchef",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.24.0",
|
|
4
4
|
"description": "A non-blocking interactive dispatcher for visible Codex tasks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Favo Yang",
|
|
@@ -48,18 +48,40 @@
|
|
|
48
48
|
"node": ">=18"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
+
"@testing-library/jest-dom": "6.8.0",
|
|
52
|
+
"@testing-library/react": "16.3.0",
|
|
53
|
+
"@testing-library/user-event": "14.6.1",
|
|
54
|
+
"@types/node": "24.3.0",
|
|
55
|
+
"@types/react": "19.1.12",
|
|
56
|
+
"@types/react-dom": "19.1.9",
|
|
57
|
+
"@vitejs/plugin-react": "4.7.0",
|
|
51
58
|
"conventional-changelog-conventionalcommits": "^10.3.0",
|
|
59
|
+
"jsdom": "26.1.0",
|
|
52
60
|
"mermaid": "^11.17.0",
|
|
61
|
+
"typescript": "5.9.2",
|
|
62
|
+
"vite": "6.4.3",
|
|
63
|
+
"vitest": "3.2.7",
|
|
53
64
|
"yaml": "^2.9.0"
|
|
54
65
|
},
|
|
55
66
|
"scripts": {
|
|
56
67
|
"benchmark:dispatch": "node scripts/benchmark-dispatch-prepare.js",
|
|
57
68
|
"benchmark:e2e": "node scripts/e2e-benchmark.js",
|
|
58
|
-
"
|
|
69
|
+
"build:dashboard": "vite build --config vite.config.ts",
|
|
70
|
+
"check:dashboard": "tsc --noEmit -p tsconfig.dashboard.json",
|
|
71
|
+
"preview:dashboard": "node scripts/preview-dashboard.js",
|
|
72
|
+
"test": "npm run test:node && npm run test:dashboard",
|
|
73
|
+
"test:node": "node --test tests/*.test.js",
|
|
74
|
+
"test:dashboard": "vitest run --config vitest.config.ts"
|
|
59
75
|
},
|
|
60
76
|
"dependencies": {
|
|
77
|
+
"@mantine/core": "9.5.2",
|
|
78
|
+
"@mantine/hooks": "9.5.2",
|
|
79
|
+
"@mantine/notifications": "9.5.2",
|
|
61
80
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
81
|
+
"@tabler/icons-react": "3.35.0",
|
|
62
82
|
"proper-lockfile": "^4.1.2",
|
|
83
|
+
"react": "19.2.0",
|
|
84
|
+
"react-dom": "19.2.0",
|
|
63
85
|
"zod": "^4.4.3"
|
|
64
86
|
},
|
|
65
87
|
"bundleDependencies": [
|
package/src/dashboard/app.js
CHANGED
|
@@ -440,82 +440,8 @@ function usageBreakdownText(usage) {
|
|
|
440
440
|
].join(" · ");
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
const SHIMMER_STEP_MS = 420;
|
|
444
|
-
const SHIMMER_PAUSE_MS = 1_800;
|
|
445
|
-
const SHIMMER_PULSE_MS = 420;
|
|
446
|
-
const SHIMMER_ANIMATION_ID = "taskchef-text-shimmer";
|
|
447
|
-
const shimmerMotionQuery = globalThis.matchMedia?.("(prefers-reduced-motion: reduce)") ?? null;
|
|
448
|
-
const shimmerRecords = new Set();
|
|
449
|
-
|
|
450
|
-
function animateShimmerRecord(record, reducedMotion) {
|
|
451
|
-
for (const animation of record.animations) animation.cancel();
|
|
452
|
-
record.animations = [];
|
|
453
|
-
if (reducedMotion) return;
|
|
454
|
-
for (const [shimmerIndex, glyph] of record.glyphs.entries()) {
|
|
455
|
-
if (typeof glyph.animate !== "function") continue;
|
|
456
|
-
const pulseEnd = SHIMMER_PULSE_MS / record.cycleMs;
|
|
457
|
-
const animation = glyph.animate([
|
|
458
|
-
{ color: "var(--muted)", textShadow: "none", offset: 0 },
|
|
459
|
-
{
|
|
460
|
-
color: "var(--text)",
|
|
461
|
-
textShadow: "0 0 0.28em color-mix(in srgb, var(--text) 22%, transparent)",
|
|
462
|
-
offset: pulseEnd / 2,
|
|
463
|
-
},
|
|
464
|
-
{ color: "var(--muted)", textShadow: "none", offset: pulseEnd },
|
|
465
|
-
{ color: "var(--muted)", textShadow: "none", offset: 1 },
|
|
466
|
-
], {
|
|
467
|
-
delay: shimmerIndex * SHIMMER_STEP_MS,
|
|
468
|
-
duration: record.cycleMs,
|
|
469
|
-
easing: "linear",
|
|
470
|
-
iterations: Infinity,
|
|
471
|
-
});
|
|
472
|
-
animation.id = SHIMMER_ANIMATION_ID;
|
|
473
|
-
record.animations.push(animation);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
shimmerMotionQuery?.addEventListener?.("change", ({ matches }) => {
|
|
478
|
-
for (const record of shimmerRecords) animateShimmerRecord(record, matches);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
export function cancelTextShimmers(root) {
|
|
482
|
-
for (const record of shimmerRecords) {
|
|
483
|
-
if (root !== record.element && !root?.contains?.(record.element)) continue;
|
|
484
|
-
for (const animation of record.animations) animation.cancel();
|
|
485
|
-
shimmerRecords.delete(record);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
443
|
export function renderTextShimmer(element, value) {
|
|
490
|
-
|
|
491
|
-
const characters = Array.from(text);
|
|
492
|
-
const highlightedCharacters = characters.filter((character) => !/\s/u.test(character));
|
|
493
|
-
const cycleMs = Math.max(
|
|
494
|
-
SHIMMER_PULSE_MS + SHIMMER_PAUSE_MS,
|
|
495
|
-
(highlightedCharacters.length - 1) * SHIMMER_STEP_MS
|
|
496
|
-
+ SHIMMER_PULSE_MS
|
|
497
|
-
+ SHIMMER_PAUSE_MS,
|
|
498
|
-
);
|
|
499
|
-
const glyphs = [];
|
|
500
|
-
const nodes = characters.map((character) => {
|
|
501
|
-
if (/\s/u.test(character)) return document.createTextNode(character);
|
|
502
|
-
const glyph = document.createElement("span");
|
|
503
|
-
glyph.className = "text-shimmer-glyph";
|
|
504
|
-
glyph.textContent = character;
|
|
505
|
-
glyphs.push(glyph);
|
|
506
|
-
return glyph;
|
|
507
|
-
});
|
|
508
|
-
const accessibleText = document.createElement("span");
|
|
509
|
-
accessibleText.className = "visually-hidden";
|
|
510
|
-
accessibleText.textContent = text;
|
|
511
|
-
const animatedText = document.createElement("span");
|
|
512
|
-
animatedText.className = "text-shimmer-visual";
|
|
513
|
-
animatedText.setAttribute("aria-hidden", "true");
|
|
514
|
-
animatedText.append(...nodes);
|
|
515
|
-
element.replaceChildren(accessibleText, animatedText);
|
|
516
|
-
const record = { animations: [], cycleMs, element, glyphs };
|
|
517
|
-
shimmerRecords.add(record);
|
|
518
|
-
animateShimmerRecord(record, shimmerMotionQuery?.matches ?? false);
|
|
444
|
+
element.textContent = String(value ?? "");
|
|
519
445
|
return element;
|
|
520
446
|
}
|
|
521
447
|
|
|
@@ -851,7 +777,6 @@ function renderDialog(task, { highlightTurnRef = null } = {}) {
|
|
|
851
777
|
(task.relatedGitHubLinks?.length ?? 0) === 0 && !task.relatedGitHubLinksTruncated
|
|
852
778
|
);
|
|
853
779
|
const timeline = turnTimeline(detailedTask, { highlightTurnRef });
|
|
854
|
-
cancelTextShimmers(elements.dialogResults);
|
|
855
780
|
elements.dialogResults.replaceChildren(...timeline);
|
|
856
781
|
const highlightedTurn = timeline.find((item) => item.dataset.turnRef === highlightTurnRef) ?? null;
|
|
857
782
|
const replacedFocusedTurn = timeline.find((item) => item.dataset.turnRef === focusedTurnRef) ?? null;
|
|
@@ -891,7 +816,6 @@ function renderDialog(task, { highlightTurnRef = null } = {}) {
|
|
|
891
816
|
? "calculating"
|
|
892
817
|
: "unavailable",
|
|
893
818
|
};
|
|
894
|
-
cancelTextShimmers(elements.dialogUsage);
|
|
895
819
|
elements.dialogUsage.replaceChildren(usagePresentation(
|
|
896
820
|
taskUsage,
|
|
897
821
|
{ wholeTask: true },
|
|
@@ -1074,9 +998,7 @@ function render() {
|
|
|
1074
998
|
const value = label.dataset.statusLabel;
|
|
1075
999
|
label.textContent = statusFilterText(value, counts[value]);
|
|
1076
1000
|
}
|
|
1077
|
-
|
|
1078
|
-
cancelTextShimmers(elements.taskList);
|
|
1079
|
-
elements.taskList.replaceChildren(...cards);
|
|
1001
|
+
elements.taskList.replaceChildren(...visible.map(taskCard));
|
|
1080
1002
|
elements.emptyState.hidden = visible.length > 0;
|
|
1081
1003
|
elements.taskCount.textContent = `${visible.length} of ${state.tasks.length} task${state.tasks.length === 1 ? "" : "s"}`;
|
|
1082
1004
|
clearTimeout(dateRefreshTimer);
|
|
@@ -1181,8 +1103,6 @@ elements.dialog.addEventListener("cancel", (event) => {
|
|
|
1181
1103
|
if (!manualTransitionPending()) resetManualTransition({ focus: true });
|
|
1182
1104
|
});
|
|
1183
1105
|
elements.dialog.addEventListener("close", () => {
|
|
1184
|
-
cancelTextShimmers(elements.dialogResults);
|
|
1185
|
-
cancelTextShimmers(elements.dialogUsage);
|
|
1186
1106
|
if (!manualTransitionPending()) state.manualTransition = null;
|
|
1187
1107
|
state.detailNavigation = null;
|
|
1188
1108
|
renderNotifications();
|