tink-harness 1.9.2 → 1.9.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,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.2",
4
+ "version": "1.9.3",
5
5
  "author": {
6
6
  "name": "dotori"
7
7
  }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ All notable changes to Tink are tracked here.
6
6
 
7
7
  No unreleased changes yet.
8
8
 
9
+ ## [1.9.3] - 2026-06-10
10
+
11
+ ### Added
12
+
13
+ - Graph nodes now drift gently with per-node organic float motion, staggered entrance animation, and a pulse ring on the selected node.
14
+ - Edges fade in progressively and respond to selection with smooth opacity/width transitions.
15
+ - Added a node-type color legend under the graph and polished the tooltip with fade/slide motion.
16
+
17
+ ### Changed
18
+
19
+ - Graph hover now scales the node smoothly instead of only changing the stroke.
20
+ - The graph tab shows only graph-related information; honors prefers-reduced-motion.
21
+
9
22
  ## [1.9.2] - 2026-06-10
10
23
 
11
24
  ### Added
package/VERSIONING.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Versioning
2
2
 
3
- Current version: `1.9.2`
3
+ Current version: `1.9.3`
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.2",
3
+ "version": "1.9.3",
4
4
  "description": "Self-growing harnesses for Claude Code and Codex.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -614,9 +614,10 @@ function renderGraphCanvas(summary, copy) {
614
614
  <svg class="graph-canvas" viewBox="0 0 1090 680" role="img" aria-label="Harness health graph">
615
615
  <rect width="1090" height="680" fill="var(--bg-card)"/>
616
616
  <g class="edges">
617
- ${edges.map((edge) => `
617
+ ${edges.map((edge, index) => `
618
618
  <line
619
619
  class="graph-edge"
620
+ style="--edge-delay: ${Math.min(index * 5, 850)}ms"
620
621
  data-source="${escapeAttr(edge.source)}"
621
622
  data-target="${escapeAttr(edge.target)}"
622
623
  x1="${edge.sourceNode.x.toFixed(1)}"
@@ -630,9 +631,17 @@ function renderGraphCanvas(summary, copy) {
630
631
  `).join('')}
631
632
  </g>
632
633
  <g class="nodes">
633
- ${nodes.map((node) => `
634
+ ${nodes.map((node, index) => {
635
+ const seed = hashString(node.id);
636
+ const floatDuration = (5 + (seed % 50) / 10).toFixed(1);
637
+ const floatDelay = -(seed % 4000);
638
+ const floatX = ((seed % 7) - 3).toFixed(1);
639
+ const floatY = (((seed >> 3) % 7) - 3).toFixed(1);
640
+ return `
641
+ <g class="node-float" style="--float-dur: ${floatDuration}s; --float-delay: ${floatDelay}ms; --float-x: ${floatX}px; --float-y: ${floatY}px">
634
642
  <circle
635
643
  class="graph-node ${node.type === 'harness' ? 'is-interactive' : ''}"
644
+ style="--enter-delay: ${Math.min(index * 9, 1100)}ms"
636
645
  tabindex="${node.type === 'harness' ? '0' : '-1'}"
637
646
  role="${node.type === 'harness' ? 'button' : 'presentation'}"
638
647
  aria-label="${escapeAttr(`${copy.tooltipPrefix}: ${node.label}`)}"
@@ -653,7 +662,9 @@ function renderGraphCanvas(summary, copy) {
653
662
  >
654
663
  <title>${escapeHtml(node.id)}</title>
655
664
  </circle>
656
- `).join('')}
665
+ </g>
666
+ `;
667
+ }).join('')}
657
668
  </g>
658
669
  <g class="labels">
659
670
  ${strongest.map((node) => `
@@ -665,9 +676,13 @@ function renderGraphCanvas(summary, copy) {
665
676
  <div class="map-caption">
666
677
  <span id="graph-status">${escapeHtml(copy.showingAll)}</span>
667
678
  <span>${escapeHtml(copy.nodeSize)}</span>
668
- <span>${escapeHtml(copy.colorType)}</span>
669
679
  <span>${escapeHtml(copy.linesRelations)}</span>
670
680
  </div>
681
+ <div class="map-legend" aria-label="${escapeAttr(copy.nodeTypes || 'Node types')}">
682
+ ${['harness', 'rule', 'memory', 'stage', 'signal', 'evidence', 'score'].map((type) => `
683
+ <span class="legend-chip"><i style="background: ${escapeAttr(TYPE_COLORS[type] || TYPE_COLORS.unknown)}"></i>${escapeHtml(type)}</span>
684
+ `).join('')}
685
+ </div>
671
686
  </section>
672
687
  `;
673
688
  }
@@ -1764,23 +1779,55 @@ function renderStyles() {
1764
1779
 
1765
1780
  .graph-canvas text {
1766
1781
  pointer-events: none;
1782
+ fill: var(--text-secondary);
1783
+ font-size: 11px;
1784
+ font-family: var(--font-mono);
1785
+ animation: label-in 600ms ease 900ms backwards;
1786
+ }
1787
+
1788
+ @keyframes label-in {
1789
+ from { opacity: 0; }
1790
+ to { opacity: 1; }
1767
1791
  }
1768
1792
 
1769
1793
  .graph-edge {
1770
1794
  stroke: var(--chart-line);
1771
1795
  stroke-width: var(--chart-line-w);
1772
1796
  opacity: 0.24;
1797
+ transition: opacity 220ms ease, stroke-width 220ms ease;
1798
+ animation: edge-in 700ms ease var(--edge-delay, 0ms) backwards;
1799
+ }
1800
+
1801
+ @keyframes edge-in {
1802
+ from { opacity: 0; }
1773
1803
  }
1774
1804
 
1775
1805
  .graph-edge.is-related { opacity: 0.9; stroke-width: 2px; }
1776
1806
 
1807
+ .node-float {
1808
+ animation: node-float var(--float-dur, 6s) ease-in-out var(--float-delay, 0ms) infinite alternate;
1809
+ }
1810
+
1811
+ @keyframes node-float {
1812
+ from { transform: translate(0, 0); }
1813
+ to { transform: translate(var(--float-x, 2px), var(--float-y, -2px)); }
1814
+ }
1815
+
1777
1816
  .graph-node {
1778
1817
  cursor: default;
1779
1818
  outline: none;
1780
- transition: none;
1781
1819
  vector-effect: non-scaling-stroke;
1782
1820
  shape-rendering: geometricPrecision;
1783
1821
  paint-order: stroke fill;
1822
+ transform-box: fill-box;
1823
+ transform-origin: center;
1824
+ transition: opacity 220ms ease, transform 220ms cubic-bezier(0.2, 0.8, 0.3, 1.1), stroke 160ms ease, stroke-opacity 160ms ease, fill-opacity 160ms ease;
1825
+ animation: node-enter 500ms cubic-bezier(0.2, 0.8, 0.3, 1.2) var(--enter-delay, 0ms) backwards;
1826
+ }
1827
+
1828
+ @keyframes node-enter {
1829
+ from { opacity: 0; transform: scale(0.2); }
1830
+ to { opacity: 1; transform: scale(1); }
1784
1831
  }
1785
1832
 
1786
1833
  .graph-node.is-interactive {
@@ -1793,33 +1840,87 @@ function renderStyles() {
1793
1840
  }
1794
1841
 
1795
1842
  .graph-node.is-interactive:hover,
1796
- .graph-node.is-interactive:focus-visible,
1843
+ .graph-node.is-interactive:focus-visible {
1844
+ stroke: var(--accent);
1845
+ stroke-opacity: 1;
1846
+ transform: scale(1.28);
1847
+ }
1848
+
1797
1849
  .graph-node.is-selected {
1798
1850
  stroke: var(--accent);
1799
1851
  stroke-opacity: 1;
1852
+ animation: node-pulse 1.6s ease-in-out infinite;
1853
+ }
1854
+
1855
+ @keyframes node-pulse {
1856
+ 0%, 100% { stroke-width: 1.8px; transform: scale(1.12); }
1857
+ 50% { stroke-width: 5px; transform: scale(1.2); }
1800
1858
  }
1801
1859
 
1802
1860
  .graph-node.is-related { opacity: 1; }
1803
1861
  .graph-node.is-hidden,
1804
1862
  .graph-edge.is-hidden,
1805
1863
  .graph-node.is-filtered-out,
1806
- .graph-edge.is-filtered-out { opacity: 0.15; pointer-events: none; }
1864
+ .graph-edge.is-filtered-out { opacity: 0.12; pointer-events: none; }
1807
1865
 
1808
1866
  .graph-tooltip {
1809
1867
  position: fixed;
1810
1868
  z-index: 20;
1811
- display: none;
1869
+ visibility: hidden;
1870
+ opacity: 0;
1871
+ transform: translateY(4px);
1872
+ transition: opacity 160ms ease, transform 160ms ease, visibility 0s linear 160ms;
1812
1873
  max-width: 230px;
1813
1874
  padding: 8px 10px;
1814
- border: 1px solid var(--border-default);
1875
+ border: 1px solid var(--border-hover);
1815
1876
  border-radius: var(--radius-md);
1816
- background: var(--bg-card);
1877
+ background: var(--bg-selected);
1817
1878
  color: var(--text-primary);
1818
1879
  font-size: 11px;
1819
1880
  line-height: 1.3;
1820
1881
  pointer-events: none;
1882
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
1883
+ }
1884
+ .graph-tooltip.is-visible {
1885
+ visibility: visible;
1886
+ opacity: 1;
1887
+ transform: translateY(0);
1888
+ transition: opacity 160ms ease, transform 160ms ease;
1889
+ }
1890
+
1891
+ .map-legend {
1892
+ display: flex;
1893
+ gap: var(--space-3);
1894
+ margin-top: var(--space-3);
1895
+ padding-top: var(--space-3);
1896
+ border-top: 1px solid var(--border-default);
1897
+ flex-wrap: wrap;
1898
+ }
1899
+
1900
+ .legend-chip {
1901
+ display: inline-flex;
1902
+ align-items: center;
1903
+ gap: 6px;
1904
+ color: var(--text-secondary);
1905
+ font-size: 11px;
1906
+ font-family: var(--font-mono);
1907
+ }
1908
+
1909
+ .legend-chip i {
1910
+ width: 9px;
1911
+ height: 9px;
1912
+ border-radius: 50%;
1913
+ display: inline-block;
1914
+ }
1915
+
1916
+ @media (prefers-reduced-motion: reduce) {
1917
+ .node-float,
1918
+ .graph-node,
1919
+ .graph-edge,
1920
+ .graph-canvas text,
1921
+ .page.is-active { animation: none; }
1922
+ .graph-node { transition: none; }
1821
1923
  }
1822
- .graph-tooltip.is-visible { display: block; }
1823
1924
 
1824
1925
  .map-caption {
1825
1926
  display: flex;