blumefi 2.6.0 → 2.6.1

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.
Files changed (2) hide show
  1. package/cli.js +50 -22
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -16,6 +16,8 @@ const c = {
16
16
  yellow: s => `\x1b[33m${s}\x1b[39m`,
17
17
  red: s => `\x1b[31m${s}\x1b[39m`,
18
18
  white: s => `\x1b[97m${s}\x1b[39m`,
19
+ cyan: s => `\x1b[36m${s}\x1b[39m`,
20
+ brightGreen: s => `\x1b[92m${s}\x1b[39m`,
19
21
  }
20
22
 
21
23
  function progressBar(pct, width = 16) {
@@ -1098,7 +1100,7 @@ async function cmdPadSearch(query) {
1098
1100
  }
1099
1101
  }
1100
1102
 
1101
- function renderTokenTable(tokens, total, chain, filter) {
1103
+ function renderTokenTable(tokens, total, chain, filter, frame = 0) {
1102
1104
  const lines = []
1103
1105
  if (!tokens.length) {
1104
1106
  lines.push(c.dim(` No tokens found (${chain}, filter: ${filter})`))
@@ -1136,12 +1138,28 @@ function renderTokenTable(tokens, total, chain, filter) {
1136
1138
 
1137
1139
  for (const t of tokens) {
1138
1140
  const pct = Math.min(t.progress || 0, 100)
1139
- const pctStr = `${Math.round(pct)}%`
1140
- const bar = progressBar(pct) + ' ' + (pct >= 100 ? c.green(pctStr) : c.yellow(pctStr))
1141
- const status = t.graduated ? c.green('Graduated') : c.yellow('Active')
1141
+ const hot = pct >= 75 && !t.graduated
1142
+ const pulse = hot && frame % 2 === 0
1143
+
1144
+ let bar, pctStr, status, indicator
1145
+ if (hot) {
1146
+ pctStr = `${Math.round(pct)}%`
1147
+ const barFn = pulse ? c.brightGreen : c.green
1148
+ const clamped = Math.max(0, Math.min(pct, 100))
1149
+ const filled = Math.round((clamped / 100) * 16)
1150
+ const empty = 16 - filled
1151
+ bar = barFn('█'.repeat(filled)) + c.dim('░'.repeat(empty)) + ' ' + (pulse ? c.bold(c.brightGreen(pctStr)) : c.bold(c.yellow(pctStr)))
1152
+ indicator = pulse ? ' ▲' : ''
1153
+ status = pulse ? c.bold(c.brightGreen('Hot')) : c.yellow('Active')
1154
+ } else {
1155
+ pctStr = `${Math.round(pct)}%`
1156
+ bar = progressBar(pct) + ' ' + (pct >= 100 ? c.green(pctStr) : c.yellow(pctStr))
1157
+ indicator = ''
1158
+ status = t.graduated ? c.green('Graduated') : c.yellow('Active')
1159
+ }
1142
1160
 
1143
1161
  const row =
1144
- padRight(c.bold(t.symbol || '???'), cols.symbol) +
1162
+ padRight(c.bold(t.symbol || '???') + indicator, cols.symbol) +
1145
1163
  padRight((t.name || '—').slice(0, 18), cols.name) +
1146
1164
  padLeft(fmtPrice(t.price), cols.price) + ' ' +
1147
1165
  padLeft(fmtNum(t.marketCap), cols.mcap) + ' ' +
@@ -1186,7 +1204,8 @@ async function cmdPadTokens() {
1186
1204
  }
1187
1205
 
1188
1206
  // Live dashboard mode
1189
- const REFRESH = 5
1207
+ const FETCH_INTERVAL = 5000 // fetch new data every 5s
1208
+ const RENDER_INTERVAL = 500 // redraw for animation every 500ms
1190
1209
  const hideCursor = () => process.stdout.write('\x1b[?25l')
1191
1210
  const showCursor = () => process.stdout.write('\x1b[?25h')
1192
1211
  const clearScreen = () => process.stdout.write('\x1b[2J\x1b[H')
@@ -1203,30 +1222,39 @@ async function cmdPadTokens() {
1203
1222
 
1204
1223
  hideCursor()
1205
1224
 
1225
+ let cachedTokens = [], cachedTotal = 0, lastFetch = 0, fetchError = null, frame = 0
1226
+
1206
1227
  while (running) {
1207
- let output
1208
- try {
1209
- const data = await apiFetch(`/pad/tokens?chain=${chain}&filter=${filter}&sort=${sort}&limit=${limit}&offset=0`)
1210
- const tokens = data.tokens || data.data || []
1211
- const total = data.total || tokens.length
1212
- output = renderTokenTable(tokens, total, chain, filter)
1213
- } catch (err) {
1214
- output = c.red(` Error fetching data: ${err.message}`) + '\n' + c.dim(' Will retry...')
1228
+ // Fetch new data if stale
1229
+ const now = Date.now()
1230
+ if (now - lastFetch >= FETCH_INTERVAL) {
1231
+ try {
1232
+ const data = await apiFetch(`/pad/tokens?chain=${chain}&filter=${filter}&sort=${sort}&limit=${limit}&offset=0`)
1233
+ cachedTokens = data.tokens || data.data || []
1234
+ cachedTotal = data.total || cachedTokens.length
1235
+ fetchError = null
1236
+ } catch (err) {
1237
+ fetchError = err.message
1238
+ }
1239
+ lastFetch = Date.now()
1215
1240
  }
1216
1241
 
1242
+ // Render current frame
1217
1243
  clearScreen()
1218
1244
  console.log('')
1219
- console.log(output)
1245
+ if (fetchError && !cachedTokens.length) {
1246
+ console.log(c.red(` Error fetching data: ${fetchError}`) + '\n' + c.dim(' Will retry...'))
1247
+ } else {
1248
+ console.log(renderTokenTable(cachedTokens, cachedTotal, chain, filter, frame))
1249
+ }
1220
1250
 
1221
- const now = new Date()
1222
- const ts = now.toLocaleTimeString('en-US', { hour12: false })
1251
+ const ts = new Date().toLocaleTimeString('en-US', { hour12: false })
1252
+ const nextFetchIn = Math.max(0, Math.ceil((FETCH_INTERVAL - (Date.now() - lastFetch)) / 1000))
1223
1253
  console.log('')
1224
- console.log(c.dim(` Updated ${ts} · Refreshing in ${REFRESH}s · Ctrl+C to exit`))
1254
+ console.log(c.dim(` Updated ${ts} · Refreshing in ${nextFetchIn}s · Ctrl+C to exit`))
1225
1255
 
1226
- // Countdown sleep — break early if stopped
1227
- for (let i = 0; i < REFRESH * 10 && running; i++) {
1228
- await new Promise(r => setTimeout(r, 100))
1229
- }
1256
+ frame++
1257
+ await new Promise(r => setTimeout(r, RENDER_INTERVAL))
1230
1258
  }
1231
1259
  }
1232
1260
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blumefi",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "BlumeFi CLI — DeFi reimagined for the agentic era. Trade, chat, and interact with the Blume ecosystem from the command line.",
5
5
  "main": "cli.js",
6
6
  "bin": {