taskchef 7.23.3 → 7.23.4

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": "7.23.3",
3
+ "version": "7.23.4",
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": "7.23.3",
3
+ "version": "7.23.4",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -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
- 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);
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
- const cards = visible.map(taskCard);
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();
@@ -185,12 +185,25 @@ 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
+ display: inline-block;
189
+ width: fit-content;
190
+ max-width: 100%;
191
+ justify-self: start;
192
+ vertical-align: top;
193
+ background: linear-gradient(100deg, var(--muted) 48%, var(--text) 50%, var(--muted) 52%);
194
+ background-size: 250% 100%;
195
+ background-position: 100% 0;
196
+ background-clip: text;
197
+ -webkit-background-clip: text;
198
+ -webkit-text-fill-color: transparent;
199
+ animation: text-shimmer-sweep 6.4s linear infinite;
188
200
  }
189
- .text-shimmer-glyph {
190
- color: var(--muted);
201
+ @keyframes text-shimmer-sweep {
202
+ 0% { background-position: 100% 0; }
203
+ 72%, 100% { background-position: -100% 0; }
191
204
  }
192
205
  @media (prefers-reduced-motion: reduce) {
193
- .text-shimmer-glyph { color: currentcolor; text-shadow: none; }
206
+ .text-shimmer { background: none; animation: none; -webkit-text-fill-color: currentcolor; }
194
207
  }
195
208
  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; }
196
209
  .metadata { display: grid; grid-template-columns: minmax(100px, 150px) 1fr; margin: 0; font-size: 0.88rem; }