taskchef 7.23.2 → 7.23.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.
- package/.codex-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/src/dashboard/app.js +92 -5
- package/src/dashboard/styles.css +3 -11
package/package.json
CHANGED
package/src/dashboard/app.js
CHANGED
|
@@ -440,15 +440,94 @@ 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
|
+
export function renderTextShimmer(element, value) {
|
|
490
|
+
const text = String(value ?? "");
|
|
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);
|
|
519
|
+
return element;
|
|
520
|
+
}
|
|
521
|
+
|
|
443
522
|
export function usagePresentation(usage, { wholeTask = false } = {}) {
|
|
444
523
|
const container = document.createElement("div");
|
|
445
524
|
container.className = `usage-summary usage-${usage?.status ?? "unavailable"}`;
|
|
446
525
|
if (usage?.status === "pending" || usage?.status === "calculating") {
|
|
447
526
|
const text = document.createElement("span");
|
|
448
527
|
text.className = "text-shimmer";
|
|
449
|
-
text
|
|
528
|
+
renderTextShimmer(text, usage.status === "pending"
|
|
450
529
|
? "Tokens pending · available when turn finishes"
|
|
451
|
-
: "Calculating token usage…";
|
|
530
|
+
: "Calculating token usage…");
|
|
452
531
|
container.replaceChildren(text);
|
|
453
532
|
return container;
|
|
454
533
|
}
|
|
@@ -537,7 +616,8 @@ function turnTimeline(task, { highlightTurnRef = null } = {}) {
|
|
|
537
616
|
resultLabel.textContent = "Result";
|
|
538
617
|
const result = document.createElement("p");
|
|
539
618
|
result.className = `preserve-lines${turnStatus === "working" ? " text-shimmer" : ""}`;
|
|
540
|
-
|
|
619
|
+
if (turnStatus === "working") renderTextShimmer(result, presentation.summary);
|
|
620
|
+
else appendLinkedText(result, presentation.summary, task);
|
|
541
621
|
const turnMetadata = document.createElement("p");
|
|
542
622
|
turnMetadata.className = "result-history-turn";
|
|
543
623
|
turnMetadata.textContent = `Turn ref ${turn.turnRef ?? "not recorded"}; Codex turn ${turn.turnId ?? "unavailable"}`;
|
|
@@ -771,6 +851,7 @@ function renderDialog(task, { highlightTurnRef = null } = {}) {
|
|
|
771
851
|
(task.relatedGitHubLinks?.length ?? 0) === 0 && !task.relatedGitHubLinksTruncated
|
|
772
852
|
);
|
|
773
853
|
const timeline = turnTimeline(detailedTask, { highlightTurnRef });
|
|
854
|
+
cancelTextShimmers(elements.dialogResults);
|
|
774
855
|
elements.dialogResults.replaceChildren(...timeline);
|
|
775
856
|
const highlightedTurn = timeline.find((item) => item.dataset.turnRef === highlightTurnRef) ?? null;
|
|
776
857
|
const replacedFocusedTurn = timeline.find((item) => item.dataset.turnRef === focusedTurnRef) ?? null;
|
|
@@ -810,6 +891,7 @@ function renderDialog(task, { highlightTurnRef = null } = {}) {
|
|
|
810
891
|
? "calculating"
|
|
811
892
|
: "unavailable",
|
|
812
893
|
};
|
|
894
|
+
cancelTextShimmers(elements.dialogUsage);
|
|
813
895
|
elements.dialogUsage.replaceChildren(usagePresentation(
|
|
814
896
|
taskUsage,
|
|
815
897
|
{ wholeTask: true },
|
|
@@ -954,7 +1036,8 @@ function taskCard(task) {
|
|
|
954
1036
|
resultLabel.textContent = "Result";
|
|
955
1037
|
const result = document.createElement("span");
|
|
956
1038
|
result.className = `preserve-lines${latest.resultStatus === "working" ? " text-shimmer" : ""}`;
|
|
957
|
-
|
|
1039
|
+
if (latest.resultStatus === "working") renderTextShimmer(result, latest.resultSummary);
|
|
1040
|
+
else appendLinkedText(result, latest.resultSummary, task);
|
|
958
1041
|
summary.replaceChildren(requestLabel, request, resultLabel, result);
|
|
959
1042
|
const relatedLinks = relatedGitHubLinks(task, { compact: true });
|
|
960
1043
|
const time = timestampControl(
|
|
@@ -991,7 +1074,9 @@ function render() {
|
|
|
991
1074
|
const value = label.dataset.statusLabel;
|
|
992
1075
|
label.textContent = statusFilterText(value, counts[value]);
|
|
993
1076
|
}
|
|
994
|
-
|
|
1077
|
+
const cards = visible.map(taskCard);
|
|
1078
|
+
cancelTextShimmers(elements.taskList);
|
|
1079
|
+
elements.taskList.replaceChildren(...cards);
|
|
995
1080
|
elements.emptyState.hidden = visible.length > 0;
|
|
996
1081
|
elements.taskCount.textContent = `${visible.length} of ${state.tasks.length} task${state.tasks.length === 1 ? "" : "s"}`;
|
|
997
1082
|
clearTimeout(dateRefreshTimer);
|
|
@@ -1096,6 +1181,8 @@ elements.dialog.addEventListener("cancel", (event) => {
|
|
|
1096
1181
|
if (!manualTransitionPending()) resetManualTransition({ focus: true });
|
|
1097
1182
|
});
|
|
1098
1183
|
elements.dialog.addEventListener("close", () => {
|
|
1184
|
+
cancelTextShimmers(elements.dialogResults);
|
|
1185
|
+
cancelTextShimmers(elements.dialogUsage);
|
|
1099
1186
|
if (!manualTransitionPending()) state.manualTransition = null;
|
|
1100
1187
|
state.detailNavigation = null;
|
|
1101
1188
|
renderNotifications();
|
package/src/dashboard/styles.css
CHANGED
|
@@ -185,20 +185,12 @@ dialog section + section { margin-top: 24px; }
|
|
|
185
185
|
.usage-pending, .usage-calculating { display: flex; align-items: center; }
|
|
186
186
|
.text-shimmer {
|
|
187
187
|
color: var(--muted);
|
|
188
|
-
background: linear-gradient(100deg, var(--muted) 46%, var(--text) 50%, var(--muted) 54%);
|
|
189
|
-
background-size: 250% 100%;
|
|
190
|
-
background-position: 100% 0;
|
|
191
|
-
background-clip: text;
|
|
192
|
-
-webkit-background-clip: text;
|
|
193
|
-
-webkit-text-fill-color: transparent;
|
|
194
|
-
animation: text-shimmer 6.4s ease-in-out infinite;
|
|
195
188
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
72%, 100% { background-position: -100% 0; }
|
|
189
|
+
.text-shimmer-glyph {
|
|
190
|
+
color: var(--muted);
|
|
199
191
|
}
|
|
200
192
|
@media (prefers-reduced-motion: reduce) {
|
|
201
|
-
.text-shimmer {
|
|
193
|
+
.text-shimmer-glyph { color: currentcolor; text-shadow: none; }
|
|
202
194
|
}
|
|
203
195
|
pre { max-height: 280px; margin: 0; padding: 14px; overflow: auto; border-radius: 7px; background: var(--surface-muted); white-space: pre-wrap; overflow-wrap: anywhere; font: 0.86rem/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
204
196
|
.metadata { display: grid; grid-template-columns: minmax(100px, 150px) 1fr; margin: 0; font-size: 0.88rem; }
|