helixevo 0.2.38 → 0.2.40

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
  'use client'
2
2
 
3
- import { useState, useEffect } from 'react'
3
+ import { useEffect, useState } from 'react'
4
4
 
5
5
  const REGISTRY_URL = 'https://registry.npmjs.org/helixevo/latest'
6
6
  const CHECK_INTERVAL_MS = 60 * 60 * 1000
@@ -39,7 +39,10 @@ export function UpdateBanner({ currentVersion }: { currentVersion: string }) {
39
39
  }
40
40
  check()
41
41
  const interval = setInterval(check, CHECK_INTERVAL_MS)
42
- return () => { mounted = false; clearInterval(interval) }
42
+ return () => {
43
+ mounted = false
44
+ clearInterval(interval)
45
+ }
43
46
  }, [currentVersion])
44
47
 
45
48
  if (!latestVersion || dismissed) return null
@@ -56,24 +59,21 @@ export function UpdateBanner({ currentVersion }: { currentVersion: string }) {
56
59
  setNewVersion(data.version)
57
60
  setState('reloading')
58
61
 
59
- // Next.js dev mode detects config change and auto-restarts.
60
- // Wait for recompilation, then poll until ready, then reload.
61
62
  let secs = 12
62
63
  setCountdown(secs)
63
64
  const timer = setInterval(async () => {
64
- secs--
65
+ secs -= 1
65
66
  setCountdown(secs)
66
67
  if (secs <= 0) {
67
68
  clearInterval(timer)
68
- // Try polling a few times before force-reloading
69
69
  for (let i = 0; i < 10; i++) {
70
70
  try {
71
71
  const check = await fetch('/?_v=' + Date.now(), { cache: 'no-store' })
72
72
  if (check.ok) break
73
73
  } catch {}
74
- await new Promise(r => setTimeout(r, 1500))
74
+ await new Promise((resolve) => setTimeout(resolve, 1500))
75
75
  }
76
- window.location.href = window.location.pathname + '?v=' + Date.now()
76
+ window.location.href = `${window.location.pathname}?v=${Date.now()}`
77
77
  }
78
78
  }, 1000)
79
79
  } else {
@@ -86,151 +86,107 @@ export function UpdateBanner({ currentVersion }: { currentVersion: string }) {
86
86
  }
87
87
  }
88
88
 
89
+ const tone = state === 'reloading' ? 'var(--green-border)' : state === 'error' ? 'var(--red-border)' : 'var(--purple-border)'
90
+ const progress = state === 'reloading' ? `${((12 - countdown) / 12) * 100}%` : '0%'
91
+
89
92
  return (
90
- <div style={{
91
- position: 'fixed',
92
- bottom: 20,
93
- right: 20,
94
- width: 320,
95
- background: 'var(--bg-card)',
96
- border: `1px solid ${state === 'reloading' ? 'var(--green-border)' : state === 'error' ? 'var(--red-border)' : 'var(--purple-border)'}`,
97
- borderRadius: 'var(--radius-lg)',
98
- boxShadow: 'var(--shadow-xl)',
99
- padding: '16px 18px',
100
- zIndex: 9999,
101
- animation: 'updateSlideIn 0.4s ease-out',
102
- }}>
103
- {/* Close */}
104
- {(state === 'idle' || state === 'error') && (
105
- <button
106
- onClick={() => setDismissed(true)}
107
- style={{
108
- position: 'absolute', top: 8, right: 10,
109
- background: 'none', border: 'none', cursor: 'pointer',
110
- color: 'var(--text-dim)', fontSize: 18, lineHeight: 1, padding: '2px 4px',
111
- }}
112
- >&times;</button>
113
- )}
114
-
115
- {/* Header */}
116
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
117
- <div style={{
118
- width: 28, height: 28, borderRadius: '50%',
119
- background: state === 'reloading' ? 'var(--green-light)' : state === 'error' ? 'var(--red-light)' : 'var(--purple-light)',
120
- display: 'flex', alignItems: 'center', justifyContent: 'center',
121
- }}>
122
- {(state === 'updating' || state === 'reloading') ? (
123
- <div style={{
124
- width: 14, height: 14,
125
- border: `2px solid ${state === 'reloading' ? 'var(--green-border)' : 'var(--purple-border)'}`,
126
- borderTopColor: state === 'reloading' ? 'var(--green)' : 'var(--purple)',
127
- borderRadius: '50%',
128
- animation: 'updateSpin 0.8s linear infinite',
129
- }} />
130
- ) : state === 'error' ? (
131
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--red)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
132
- <path d="M18 6L6 18M6 6l12 12" />
133
- </svg>
134
- ) : (
135
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--purple)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
136
- <path d="M12 19V5m-7 7l7-7 7 7" />
137
- </svg>
138
- )}
139
- </div>
140
- <div>
141
- <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)' }}>
142
- {state === 'updating' ? 'Installing...'
143
- : state === 'reloading' ? 'Reloading...'
144
- : state === 'error' ? 'Update Failed'
145
- : 'Update Available'}
146
- </div>
147
- <div style={{ fontSize: 11, color: 'var(--text-dim)' }}>
148
- {state === 'reloading'
149
- ? <>v{newVersion} installed — refreshing in {countdown}s</>
150
- : <>v{currentVersion} &rarr; <span style={{ color: 'var(--green)', fontWeight: 600 }}>v{latestVersion}</span></>
151
- }
93
+ <div
94
+ style={{
95
+ position: 'fixed',
96
+ right: 18,
97
+ bottom: 18,
98
+ width: 300,
99
+ zIndex: 9999,
100
+ borderRadius: 20,
101
+ border: `1px solid ${tone}`,
102
+ background: 'linear-gradient(180deg, rgba(255,255,255,0.95), rgba(249,245,239,0.94))',
103
+ boxShadow: 'var(--shadow-xl)',
104
+ overflow: 'hidden',
105
+ backdropFilter: 'blur(18px)',
106
+ }}
107
+ >
108
+ <div style={{ padding: '14px 16px 12px' }}>
109
+ {(state === 'idle' || state === 'error') ? (
110
+ <button
111
+ onClick={() => setDismissed(true)}
112
+ style={{
113
+ position: 'absolute',
114
+ top: 10,
115
+ right: 12,
116
+ border: 'none',
117
+ background: 'transparent',
118
+ color: 'var(--text-muted)',
119
+ fontSize: 16,
120
+ cursor: 'pointer',
121
+ }}
122
+ >
123
+ ×
124
+ </button>
125
+ ) : null}
126
+
127
+ <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
128
+ <div style={{ width: 34, height: 34, borderRadius: 12, background: state === 'error' ? 'var(--red-light)' : state === 'reloading' ? 'var(--green-light)' : 'var(--purple-light)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
129
+ {(state === 'updating' || state === 'reloading') ? (
130
+ <span style={{ width: 16, height: 16, border: `2px solid ${state === 'reloading' ? 'var(--green-border)' : 'var(--purple-border)'}`, borderTopColor: state === 'reloading' ? 'var(--green)' : 'var(--purple)', borderRadius: '50%', display: 'inline-block', animation: 'updateSpin 0.8s linear infinite' }} />
131
+ ) : state === 'error' ? (
132
+ <span style={{ color: 'var(--red)', fontWeight: 800 }}>!</span>
133
+ ) : (
134
+ <span style={{ color: 'var(--purple)', fontWeight: 800 }}>↑</span>
135
+ )}
152
136
  </div>
153
- </div>
154
- </div>
155
137
 
156
- {/* Idle */}
157
- {state === 'idle' && (
158
- <button onClick={handleUpdate} style={{
159
- width: '100%', padding: '9px 16px',
160
- background: 'var(--purple)', color: '#fff', border: 'none',
161
- borderRadius: 'var(--radius)', fontSize: 13, fontWeight: 600, cursor: 'pointer',
162
- display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
163
- }}>
164
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
165
- <path d="M12 19V5m-7 7l7-7 7 7" />
166
- </svg>
167
- Update Now
168
- </button>
169
- )}
170
-
171
- {/* Updating */}
172
- {state === 'updating' && (
173
- <div style={{
174
- padding: '9px 16px', background: 'var(--bg-section)',
175
- borderRadius: 'var(--radius)', fontSize: 12,
176
- color: 'var(--text-secondary)', textAlign: 'center',
177
- }}>
178
- Installing helixevo@latest...
179
- </div>
180
- )}
181
-
182
- {/* Reloading with countdown */}
183
- {state === 'reloading' && (
184
- <div>
185
- <div style={{
186
- padding: '9px 16px', background: 'var(--green-light)',
187
- borderRadius: 'var(--radius)', fontSize: 12,
188
- color: 'var(--green)', textAlign: 'center', fontWeight: 600,
189
- marginBottom: 8,
190
- }}>
191
- Dashboard will refresh automatically
192
- </div>
193
- {/* Progress bar */}
194
- <div style={{ height: 3, background: 'var(--bg-section)', borderRadius: 2, overflow: 'hidden' }}>
195
- <div style={{
196
- height: '100%', background: 'var(--green)',
197
- borderRadius: 2,
198
- width: `${((8 - countdown) / 8) * 100}%`,
199
- transition: 'width 1s linear',
200
- }} />
138
+ <div style={{ minWidth: 0 }}>
139
+ <div style={{ fontSize: 13, fontWeight: 800, color: 'var(--text)' }}>
140
+ {state === 'updating' ? 'Installing update…' : state === 'reloading' ? 'Reloading dashboard…' : state === 'error' ? 'Update failed' : 'Update available'}
141
+ </div>
142
+ <div style={{ marginTop: 4, fontSize: 11.5, color: 'var(--text-dim)', lineHeight: 1.5 }}>
143
+ {state === 'reloading'
144
+ ? <>v{newVersion} installed refreshing in {countdown}s</>
145
+ : <>v{currentVersion} → <span style={{ color: 'var(--green)', fontWeight: 700 }}>v{latestVersion}</span></>}
146
+ </div>
201
147
  </div>
202
148
  </div>
203
- )}
204
-
205
- {/* Error */}
206
- {state === 'error' && (
207
- <>
208
- <div style={{
209
- padding: '8px 12px', background: 'var(--red-light)',
210
- borderRadius: 'var(--radius)', fontSize: 11, color: 'var(--red)',
211
- marginBottom: 8, maxHeight: 60, overflow: 'auto',
212
- }}>
213
- {errorMsg}
214
- </div>
215
- <button onClick={handleUpdate} style={{
216
- width: '100%', padding: '7px 12px',
217
- background: 'var(--bg-section)', color: 'var(--text-secondary)',
218
- border: '1px solid var(--border)', borderRadius: 'var(--radius)',
219
- fontSize: 12, fontWeight: 600, cursor: 'pointer',
220
- }}>
221
- Retry
149
+
150
+ {state === 'idle' ? (
151
+ <button
152
+ onClick={handleUpdate}
153
+ className="badge badge-purple"
154
+ style={{ marginTop: 14, border: 'none', cursor: 'pointer', width: '100%', justifyContent: 'center', padding: '10px 12px' }}
155
+ >
156
+ Update now
222
157
  </button>
223
- </>
224
- )}
158
+ ) : null}
159
+
160
+ {state === 'updating' ? (
161
+ <div style={{ marginTop: 14, fontSize: 11.5, color: 'var(--text-dim)' }}>Installing <code>helixevo@latest</code> and preparing the dashboard refresh.</div>
162
+ ) : null}
163
+
164
+ {state === 'reloading' ? (
165
+ <div style={{ marginTop: 14 }}>
166
+ <div style={{ height: 5, borderRadius: 999, background: 'rgba(97,93,86,0.14)', overflow: 'hidden' }}>
167
+ <div style={{ height: '100%', width: progress, background: 'linear-gradient(90deg, var(--green), var(--blue))', borderRadius: 999, transition: 'width 1s linear' }} />
168
+ </div>
169
+ </div>
170
+ ) : null}
171
+
172
+ {state === 'error' ? (
173
+ <>
174
+ <div style={{ marginTop: 14, padding: '10px 12px', borderRadius: 12, background: 'var(--red-light)', color: 'var(--red)', fontSize: 11.5, lineHeight: 1.55 }}>
175
+ {errorMsg}
176
+ </div>
177
+ <button
178
+ onClick={handleUpdate}
179
+ className="badge badge-gray"
180
+ style={{ marginTop: 12, border: 'none', cursor: 'pointer', width: '100%', justifyContent: 'center', padding: '10px 12px' }}
181
+ >
182
+ Retry update
183
+ </button>
184
+ </>
185
+ ) : null}
186
+ </div>
225
187
 
226
188
  <style>{`
227
- @keyframes updateSlideIn {
228
- from { opacity: 0; transform: translateY(16px) scale(0.97); }
229
- to { opacity: 1; transform: translateY(0) scale(1); }
230
- }
231
- @keyframes updateSpin {
232
- to { transform: rotate(360deg); }
233
- }
189
+ @keyframes updateSpin { to { transform: rotate(360deg); } }
234
190
  `}</style>
235
191
  </div>
236
192
  )
@@ -90,9 +90,9 @@ export function loadHistory(): { iterations: Iteration[] } {
90
90
  }
91
91
 
92
92
  export function loadSkillTests(): { id: string; skill: string; input: string }[] {
93
- const newFile = readJsonl('skill-tests.jsonl')
93
+ const newFile = readJsonl<{ id: string; skill: string; input: string }>('skill-tests.jsonl')
94
94
  if (newFile.length > 0) return newFile
95
- return readJsonl('golden-cases.jsonl')
95
+ return readJsonl<{ id: string; skill: string; input: string }>('golden-cases.jsonl')
96
96
  }
97
97
 
98
98
  export function loadBuffer(): KnowledgeBuffer {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helixevo",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "description": "Self-evolving skill ecosystem for AI agents. Skills and projects co-evolve through multi-judge evaluation and a Pareto frontier.",
5
5
  "type": "module",
6
6
  "bin": {