tink-harness 1.9.7 → 1.9.8

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,7 +1,7 @@
1
1
  {
2
2
  "name": "tink",
3
3
  "description": "A small harness layer for Claude Code and Codex.",
4
- "version": "1.9.7",
4
+ "version": "1.9.8",
5
5
  "author": {
6
6
  "name": "dotori"
7
7
  }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ All notable changes to Tink are tracked here.
6
6
 
7
7
  No unreleased changes yet.
8
8
 
9
+ ## [1.9.8] - 2026-06-11
10
+
11
+ ### Added
12
+
13
+ - Galaxy-styled harness map background: a slowly rotating 4-arm spiral of 760 particles (pink-to-cyan, screen-blended), 7 colored nebula glows, a breathing core glow, and a deeper space gradient — inspired by a Three.js galaxy reference while keeping the SVG map fully interactive.
14
+ - Neural signal pulses: glowing dots travel along every edge from source to target (3.2-7.4s, staggered), so connections read like firing synapses.
15
+
16
+ ### Changed
17
+
18
+ - Signal pulses follow the map state: they hide with core mode/filters, dim in focus mode, and stay bright only on edges related to the selected node. Clicking the galaxy background clears the selection. prefers-reduced-motion disables rotation and pulses.
19
+
9
20
  ## [1.9.7] - 2026-06-10
10
21
 
11
22
  ### Added
package/VERSIONING.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Versioning
2
2
 
3
- Current version: `1.9.7`
3
+ Current version: `1.9.8`
4
4
 
5
5
  Tink follows semver from `1.0.0` onward.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tink-harness",
3
- "version": "1.9.7",
3
+ "version": "1.9.8",
4
4
  "description": "Self-growing harnesses for Claude Code and Codex.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -673,6 +673,50 @@ function renderGraphCanvas(summary, copy) {
673
673
  delay: seed % 5000
674
674
  };
675
675
  });
676
+ const GALAXY = { count: 760, arms: 4, radius: 360, spin: 2.3, power: 2.4, cx: 545, cy: 340, flatten: 0.6 };
677
+ const lerpChannel = (from, to, t) => Math.round(from + (to - from) * t);
678
+ const galaxyDots = Array.from({ length: GALAXY.count }, (_, index) => {
679
+ const s1 = (hashString(`gal:${index}:a`) % 10000) / 10000;
680
+ const s2 = (hashString(`gal:${index}:b`) % 10000) / 10000;
681
+ const s3 = (hashString(`gal:${index}:c`) % 10000) / 10000;
682
+ const s4 = (hashString(`gal:${index}:d`) % 10000) / 10000;
683
+ const radius = Math.pow(s1, GALAXY.power) * GALAXY.radius;
684
+ const t = radius / GALAXY.radius;
685
+ const branchAngle = ((index % GALAXY.arms) / GALAXY.arms) * Math.PI * 2;
686
+ const spinAngle = t * GALAXY.spin;
687
+ const randomX = (s2 - 0.5) * 0.42 * radius;
688
+ const randomY = (s3 - 0.5) * 0.42 * radius;
689
+ const totalAngle = branchAngle + spinAngle;
690
+ // inside #FF66FF -> outside #66FFFF, like the reference galaxy
691
+ const color = `rgb(${lerpChannel(255, 102, t)}, ${lerpChannel(102, 255, t)}, 255)`;
692
+ return {
693
+ x: (GALAXY.cx + (Math.cos(totalAngle) * radius + randomX)).toFixed(1),
694
+ y: (GALAXY.cy + (Math.sin(totalAngle) * radius + randomY) * GALAXY.flatten).toFixed(1),
695
+ r: (0.5 + s4 * 1.1).toFixed(1),
696
+ color,
697
+ opacity: (0.12 + s4 * 0.34).toFixed(2)
698
+ };
699
+ });
700
+ const nebulae = Array.from({ length: 7 }, (_, index) => {
701
+ const seed = hashString(`nebula:${index}`);
702
+ return {
703
+ id: index,
704
+ hue: seed % 360,
705
+ x: 80 + (seed % 930),
706
+ y: 60 + ((seed >> 5) % 560),
707
+ r: 70 + ((seed >> 3) % 150),
708
+ opacity: (0.05 + ((seed >> 7) % 8) / 100).toFixed(2)
709
+ };
710
+ });
711
+ const pulses = edges.slice(0, 120).map((edge, index) => {
712
+ const seed = hashString(`pulse:${edge.source}:${edge.target}:${index}`);
713
+ return {
714
+ path: `M ${edge.sourceNode.x.toFixed(1)},${edge.sourceNode.y.toFixed(1)} L ${edge.targetNode.x.toFixed(1)},${edge.targetNode.y.toFixed(1)}`,
715
+ dur: (3.2 + (seed % 42) / 10).toFixed(1),
716
+ begin: -((seed >> 4) % 7000),
717
+ index
718
+ };
719
+ });
676
720
  const mapTitle = copy.knowledgeGraph || copy.harnessMap || 'Harness map';
677
721
  const mapEyebrow = copy.harnessMap && copy.harnessMap !== mapTitle ? copy.harnessMap : '';
678
722
  return `
@@ -698,9 +742,25 @@ function renderGraphCanvas(summary, copy) {
698
742
  <svg class="graph-canvas" viewBox="0 0 1090 680" role="img" aria-label="Harness health graph">
699
743
  <defs>
700
744
  <radialGradient id="graph-bg-grad" cx="50%" cy="42%" r="80%">
701
- <stop offset="0%" style="stop-color: #11141C"/>
702
- <stop offset="60%" style="stop-color: #0A0C12"/>
703
- <stop offset="100%" style="stop-color: #06070B"/>
745
+ <stop offset="0%" style="stop-color: #0B0E1A"/>
746
+ <stop offset="60%" style="stop-color: #05060F"/>
747
+ <stop offset="100%" style="stop-color: #000005"/>
748
+ </radialGradient>
749
+ <radialGradient id="galaxy-core-grad" cx="50%" cy="50%" r="50%">
750
+ <stop offset="0%" style="stop-color: #FFFFFF; stop-opacity: 0.55"/>
751
+ <stop offset="35%" style="stop-color: #C9B8FF; stop-opacity: 0.18"/>
752
+ <stop offset="100%" style="stop-color: #C9B8FF; stop-opacity: 0"/>
753
+ </radialGradient>
754
+ ${nebulae.map((nebula) => `
755
+ <radialGradient id="nebula-grad-${nebula.id}" cx="50%" cy="50%" r="50%">
756
+ <stop offset="0%" style="stop-color: hsl(${nebula.hue}, 80%, 60%); stop-opacity: 0.6"/>
757
+ <stop offset="100%" style="stop-color: hsl(${nebula.hue}, 80%, 60%); stop-opacity: 0"/>
758
+ </radialGradient>
759
+ `).join('')}
760
+ <radialGradient id="pulse-grad" cx="50%" cy="50%" r="50%">
761
+ <stop offset="0%" style="stop-color: #FFFFFF; stop-opacity: 1"/>
762
+ <stop offset="45%" style="stop-color: #9DC4FF; stop-opacity: 0.85"/>
763
+ <stop offset="100%" style="stop-color: #5B8DEF; stop-opacity: 0"/>
704
764
  </radialGradient>
705
765
  ${Object.entries(TYPE_COLORS).map(([type, color]) => `
706
766
  <radialGradient id="node-grad-${escapeAttr(type)}" cx="32%" cy="28%" r="78%">
@@ -716,6 +776,15 @@ function renderGraphCanvas(summary, copy) {
716
776
  <circle cx="${star.x}" cy="${star.y}" r="${star.r}" fill="#FFFFFF" opacity="${star.opacity}"${star.twinkle ? ` class="star-twinkle" style="--twinkle-delay: ${star.delay}ms"` : ''}/>
717
777
  `).join('')}
718
778
  </g>
779
+ <g class="galaxy-layer" aria-hidden="true">
780
+ ${nebulae.map((nebula) => `
781
+ <circle class="nebula" cx="${nebula.x}" cy="${nebula.y}" r="${nebula.r}" fill="url(#nebula-grad-${nebula.id})" opacity="${nebula.opacity}"/>
782
+ `).join('')}
783
+ <g class="galaxy-spiral">
784
+ ${galaxyDots.map((dot) => `<circle cx="${dot.x}" cy="${dot.y}" r="${dot.r}" fill="${dot.color}" opacity="${dot.opacity}"/>`).join('')}
785
+ </g>
786
+ <circle class="galaxy-core" cx="${GALAXY.cx}" cy="${GALAXY.cy}" r="120" fill="url(#galaxy-core-grad)"/>
787
+ </g>
719
788
  <g id="graph-viewport">
720
789
  <g class="edges">
721
790
  ${edges.map((edge, index) => `
@@ -734,6 +803,16 @@ function renderGraphCanvas(summary, copy) {
734
803
  />
735
804
  `).join('')}
736
805
  </g>
806
+ <g class="pulses" aria-hidden="true">
807
+ ${pulses.map((pulse) => `
808
+ <g class="pulse-wrap" data-pulse-index="${pulse.index}">
809
+ <circle class="edge-pulse" r="2.6" fill="url(#pulse-grad)" opacity="0">
810
+ <animateMotion dur="${pulse.dur}s" begin="${pulse.begin}ms" repeatCount="indefinite" path="${escapeAttr(pulse.path)}"/>
811
+ <animate attributeName="opacity" values="0;0.95;0.95;0" keyTimes="0;0.12;0.85;1" dur="${pulse.dur}s" begin="${pulse.begin}ms" repeatCount="indefinite"/>
812
+ </circle>
813
+ </g>
814
+ `).join('')}
815
+ </g>
737
816
  <g class="orbits" aria-hidden="true">
738
817
  ${orbitSystems.map((system) => `
739
818
  <g class="orbit-system" data-parent="${escapeAttr(system.parentId)}" transform="translate(${system.x.toFixed(1)} ${system.y.toFixed(1)})">
@@ -1382,7 +1461,18 @@ function renderScript(harnesses, copy) {
1382
1461
  };
1383
1462
  const graphCanvas = document.querySelector('.graph-canvas');
1384
1463
  const orbitSystems = Array.from(document.querySelectorAll('.orbit-system'));
1464
+ const pulseWraps = Array.from(document.querySelectorAll('.pulse-wrap'));
1385
1465
  const defaultSelectedPanel = selectedPanel ? selectedPanel.innerHTML : '';
1466
+ function syncPulses() {
1467
+ const hasSelection = graphCanvas && graphCanvas.classList.contains('has-selection');
1468
+ pulseWraps.forEach((wrap) => {
1469
+ const edge = edges[Number(wrap.dataset.pulseIndex)];
1470
+ if (!edge) return;
1471
+ const hidden = edge.classList.contains('is-hidden') || edge.classList.contains('is-filtered-out');
1472
+ wrap.classList.toggle('is-hidden', hidden);
1473
+ wrap.classList.toggle('is-dimmed', Boolean(hasSelection && !edge.classList.contains('is-related')));
1474
+ });
1475
+ }
1386
1476
  function syncOrbits() {
1387
1477
  const hasSelection = graphCanvas && graphCanvas.classList.contains('has-selection');
1388
1478
  orbitSystems.forEach((system) => {
@@ -1392,6 +1482,7 @@ function renderScript(harnesses, copy) {
1392
1482
  const related = parent && (parent.classList.contains('is-selected') || parent.classList.contains('is-related'));
1393
1483
  system.classList.toggle('is-dimmed', Boolean(hasSelection && !related));
1394
1484
  });
1485
+ syncPulses();
1395
1486
  }
1396
1487
  function clearSelection() {
1397
1488
  nodes.forEach((item) => item.classList.remove('is-selected', 'is-related'));
@@ -1576,7 +1667,7 @@ function renderScript(harnesses, copy) {
1576
1667
  suppressClick = false;
1577
1668
  return;
1578
1669
  }
1579
- if (event.target.classList.contains('graph-bg') || event.target.closest('.starfield')) {
1670
+ if (event.target.classList.contains('graph-bg') || event.target.closest('.starfield') || event.target.closest('.galaxy-layer')) {
1580
1671
  clearSelection();
1581
1672
  if (selectedPanel && defaultSelectedPanel) selectedPanel.innerHTML = defaultSelectedPanel;
1582
1673
  }
@@ -2265,6 +2356,39 @@ function renderStyles() {
2265
2356
  to { opacity: 0.6; }
2266
2357
  }
2267
2358
 
2359
+ .galaxy-layer { pointer-events: none; }
2360
+
2361
+ .nebula { mix-blend-mode: screen; }
2362
+
2363
+ .galaxy-spiral {
2364
+ transform-origin: 545px 340px;
2365
+ transform-box: view-box;
2366
+ animation: galaxy-rotate 420s linear infinite;
2367
+ mix-blend-mode: screen;
2368
+ }
2369
+
2370
+ @keyframes galaxy-rotate {
2371
+ to { transform: rotate(360deg); }
2372
+ }
2373
+
2374
+ .galaxy-core {
2375
+ mix-blend-mode: screen;
2376
+ animation: core-breathe 9s ease-in-out infinite alternate;
2377
+ }
2378
+
2379
+ @keyframes core-breathe {
2380
+ from { opacity: 0.5; }
2381
+ to { opacity: 0.9; }
2382
+ }
2383
+
2384
+ .pulse-wrap { transition: opacity 260ms ease; }
2385
+ .pulse-wrap.is-hidden { opacity: 0; }
2386
+ .pulse-wrap.is-dimmed { opacity: 0.08; }
2387
+
2388
+ .edge-pulse {
2389
+ filter: drop-shadow(0 0 4px rgba(157, 196, 255, 0.9));
2390
+ }
2391
+
2268
2392
  .orbit-ring {
2269
2393
  fill: none;
2270
2394
  stroke: var(--text-secondary);
@@ -2421,8 +2545,11 @@ function renderStyles() {
2421
2545
  .graph-canvas text,
2422
2546
  .orbit-spin,
2423
2547
  .star-twinkle,
2548
+ .galaxy-spiral,
2549
+ .galaxy-core,
2424
2550
  .page.is-active { animation: none; }
2425
2551
  .graph-node { transition: none; }
2552
+ .pulses { display: none; }
2426
2553
  }
2427
2554
 
2428
2555
  .map-caption {