free-coding-models 0.1.76 β†’ 0.1.78

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.
package/README.md CHANGED
@@ -79,6 +79,7 @@
79
79
  - **πŸ’» OpenCode integration** β€” Auto-detects NIM setup, sets model as default, launches OpenCode
80
80
  - **🦞 OpenClaw integration** β€” Sets selected model as default provider in `~/.openclaw/openclaw.json`
81
81
  - **πŸ“ Feature Request (J key)** β€” Send anonymous feedback directly to the project team via a full-screen overlay with multi-line input (includes anonymous OS/terminal metadata in message footer only)
82
+ - **πŸ› Bug Report (I key)** β€” Send anonymous bug reports directly to the project team via a full-screen overlay with multi-line input (includes anonymous OS/terminal metadata in message footer only)
82
83
  - **🎨 Clean output** β€” Zero scrollback pollution, interface stays open until Ctrl+C
83
84
  - **πŸ“Ά Status indicators** β€” UP βœ… Β· No Key πŸ”‘ Β· Timeout ⏳ Β· Overloaded πŸ”₯ Β· Not Found 🚫
84
85
  - **πŸ” Keyless latency** β€” Models are pinged even without an API key β€” a `πŸ”‘ NO KEY` status confirms the server is reachable with real latency shown, so you can compare providers before committing to a key
@@ -171,6 +171,54 @@ async function sendFeatureRequest(message) {
171
171
  }
172
172
  }
173
173
 
174
+ // πŸ“– Discord bug report webhook configuration (anonymous bug reports)
175
+ const DISCORD_BUG_WEBHOOK_URL = 'https://discord.com/api/webhooks/1476715954409963743/5cOLf7U_891f1jwxRBLIp2RIP9xYhr4rWtOhipzKKwVdFVl1Bj89X_fB6I_uGXZiGT9E'
176
+ const DISCORD_BUG_BOT_NAME = 'TUI Bug Report'
177
+ const DISCORD_BUG_EMBED_COLOR = 0xFF5733 // Rouge (RGB: 255, 87, 51)
178
+
179
+ // πŸ“– sendBugReport: Send anonymous bug report to Discord via webhook
180
+ // πŸ“– Called when user presses I key, types message, and presses Enter
181
+ // πŸ“– Returns success/error status for UI feedback
182
+ async function sendBugReport(message) {
183
+ try {
184
+ // πŸ“– Collect anonymous telemetry for context (no personal data)
185
+ const system = getTelemetrySystem()
186
+ const terminal = getTelemetryTerminal()
187
+ const nodeVersion = process.version
188
+ const arch = process.arch
189
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'Unknown'
190
+
191
+ // πŸ“– Build Discord embed with rich metadata in footer (compact format)
192
+ const embed = {
193
+ description: message,
194
+ color: DISCORD_BUG_EMBED_COLOR,
195
+ timestamp: new Date().toISOString(),
196
+ footer: {
197
+ text: `v${LOCAL_VERSION} β€’ ${system} β€’ ${terminal} β€’ ${nodeVersion} β€’ ${arch} β€’ ${timezone}`
198
+ }
199
+ }
200
+
201
+ const response = await fetch(DISCORD_BUG_WEBHOOK_URL, {
202
+ method: 'POST',
203
+ headers: { 'content-type': 'application/json' },
204
+ body: JSON.stringify({
205
+ username: DISCORD_BUG_BOT_NAME,
206
+ embeds: [embed]
207
+ }),
208
+ signal: AbortSignal.timeout(10000) // πŸ“– 10s timeout for webhook
209
+ })
210
+
211
+ if (!response.ok) {
212
+ throw new Error(`HTTP ${response.status}`)
213
+ }
214
+
215
+ return { success: true, error: null }
216
+ } catch (error) {
217
+ const message = error instanceof Error ? error.message : 'Unknown error'
218
+ return { success: false, error: message }
219
+ }
220
+ }
221
+
174
222
  // πŸ“– parseTelemetryEnv: Convert env var strings into booleans.
175
223
  // πŸ“– Returns true/false when value is recognized, otherwise null.
176
224
  function parseTelemetryEnv(value) {
@@ -1338,8 +1386,8 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
1338
1386
  : chalk.rgb(0, 200, 255)('Enterβ†’OpenCode')
1339
1387
  // πŸ“– Line 1: core navigation + sorting shortcuts
1340
1388
  lines.push(chalk.dim(` ↑↓ Navigate β€’ `) + actionHint + chalk.dim(` β€’ `) + chalk.yellow('F') + chalk.dim(` Favorite β€’ R/Y/O/M/L/A/S/C/H/V/B/U Sort β€’ `) + chalk.yellow('T') + chalk.dim(` Tier β€’ `) + chalk.yellow('N') + chalk.dim(` Origin β€’ W↓/X↑ (${intervalSec}s) β€’ `) + chalk.rgb(255, 100, 50).bold('Z') + chalk.dim(` Mode β€’ `) + chalk.yellow('P') + chalk.dim(` Settings β€’ `) + chalk.rgb(0, 255, 80).bold('K') + chalk.dim(` Help`))
1341
- // πŸ“– Line 2: profiles, recommend, feature request, and extended hints β€” gives visibility to less-obvious features
1342
- lines.push(chalk.dim(` `) + chalk.rgb(200, 150, 255).bold('⇧P') + chalk.dim(` Cycle profile β€’ `) + chalk.rgb(200, 150, 255).bold('⇧S') + chalk.dim(` Save profile β€’ `) + chalk.rgb(0, 200, 180).bold('Q') + chalk.dim(` Smart Recommend β€’ `) + chalk.rgb(57, 255, 20).bold('J') + chalk.dim(` Request feature β€’ `) + chalk.yellow('E') + chalk.dim(`/`) + chalk.yellow('D') + chalk.dim(` Tier ↑↓ β€’ `) + chalk.yellow('Esc') + chalk.dim(` Close overlay β€’ Ctrl+C Exit`))
1389
+ // πŸ“– Line 2: profiles, recommend, feature request, bug report, and extended hints β€” gives visibility to less-obvious features
1390
+ lines.push(chalk.dim(` `) + chalk.rgb(200, 150, 255).bold('⇧P') + chalk.dim(` Cycle profile β€’ `) + chalk.rgb(200, 150, 255).bold('⇧S') + chalk.dim(` Save profile β€’ `) + chalk.rgb(0, 200, 180).bold('Q') + chalk.dim(` Smart Recommend β€’ `) + chalk.rgb(57, 255, 20).bold('J') + chalk.dim(` Request feature β€’ `) + chalk.rgb(255, 87, 51).bold('I') + chalk.dim(` Report bug β€’ `) + chalk.yellow('E') + chalk.dim(`/`) + chalk.yellow('D') + chalk.dim(` Tier ↑↓ β€’ `) + chalk.yellow('Esc') + chalk.dim(` Close overlay β€’ Ctrl+C Exit`))
1343
1391
  lines.push('')
1344
1392
  lines.push(
1345
1393
  chalk.rgb(255, 150, 200)(' Made with πŸ’– & β˜• by \x1b]8;;https://github.com/vava-nessa\x1b\\vava-nessa\x1b]8;;\x1b\\') +
@@ -2749,31 +2797,35 @@ async function main() {
2749
2797
  // Silently fail - don't block the app if npm registry is unreachable
2750
2798
  }
2751
2799
 
2752
- // πŸ“– Show update notification menu if a new version is available
2800
+ // πŸ“– Auto-update system: force updates and handle changelog automatically
2753
2801
  if (latestVersion) {
2754
- const action = await promptUpdateNotification(latestVersion)
2755
- if (action === 'update') {
2756
- runUpdate(latestVersion)
2757
- return // runUpdate will restart the process
2758
- } else if (action === 'changelogs') {
2759
- console.log()
2760
- console.log(chalk.cyan(' Opening changelog in browser...'))
2761
- console.log()
2762
- const { execSync } = require('child_process')
2763
- const changelogUrl = 'https://github.com/vava-nessa/free-coding-models/releases'
2764
- try {
2765
- if (isMac) {
2766
- execSync(`open "${changelogUrl}"`, { stdio: 'ignore' })
2767
- } else if (isWindows) {
2768
- execSync(`start "" "${changelogUrl}"`, { stdio: 'ignore' })
2769
- } else {
2770
- execSync(`xdg-open "${changelogUrl}"`, { stdio: 'ignore' })
2771
- }
2772
- } catch {
2773
- console.log(chalk.dim(` Could not open browser. Visit: ${changelogUrl}`))
2802
+ console.log()
2803
+ console.log(chalk.bold.red(' ⚠ AUTO-UPDATE AVAILABLE'))
2804
+ console.log(chalk.red(` Version ${latestVersion} will be installed automatically`))
2805
+ console.log(chalk.dim(' Opening changelog in browser...'))
2806
+ console.log()
2807
+
2808
+ // πŸ“– Open changelog automatically
2809
+ const { execSync } = require('child_process')
2810
+ const changelogUrl = 'https://github.com/vava-nessa/free-coding-models/releases'
2811
+ try {
2812
+ if (isMac) {
2813
+ execSync(`open "${changelogUrl}"`, { stdio: 'ignore' })
2814
+ } else if (isWindows) {
2815
+ execSync(`start "" "${changelogUrl}"`, { stdio: 'ignore' })
2816
+ } else {
2817
+ execSync(`xdg-open "${changelogUrl}"`, { stdio: 'ignore' })
2774
2818
  }
2819
+ console.log(chalk.green(' βœ… Changelog opened in browser'))
2820
+ } catch {
2821
+ console.log(chalk.yellow(' ⚠ Could not open browser automatically'))
2822
+ console.log(chalk.dim(` Visit manually: ${changelogUrl}`))
2775
2823
  }
2776
- // If action is null (Continue without update) or changelogs, proceed to main app
2824
+
2825
+ // πŸ“– Force update immediately
2826
+ console.log(chalk.cyan(' πŸš€ Starting auto-update...'))
2827
+ runUpdate(latestVersion)
2828
+ return // runUpdate will restart the process
2777
2829
  }
2778
2830
 
2779
2831
  // πŸ“– Build results from MODELS β€” only include enabled providers
@@ -2865,11 +2917,16 @@ async function main() {
2865
2917
  activeProfile: getActiveProfileName(config), // πŸ“– Currently loaded profile name (or null)
2866
2918
  profileSaveMode: false, // πŸ“– Whether the inline "Save profile" name input is active
2867
2919
  profileSaveBuffer: '', // πŸ“– Typed characters for the profile name being saved
2868
- // πŸ“– Feature Request state (! key opens it)
2920
+ // πŸ“– Feature Request state (J key opens it)
2869
2921
  featureRequestOpen: false, // πŸ“– Whether the feature request overlay is active
2870
2922
  featureRequestBuffer: '', // πŸ“– Typed characters for the feature request message
2871
2923
  featureRequestStatus: 'idle', // πŸ“– 'idle'|'sending'|'success'|'error' β€” webhook send status
2872
2924
  featureRequestError: null, // πŸ“– Last webhook error message
2925
+ // πŸ“– Bug Report state (I key opens it)
2926
+ bugReportOpen: false, // πŸ“– Whether the bug report overlay is active
2927
+ bugReportBuffer: '', // πŸ“– Typed characters for the bug report message
2928
+ bugReportStatus: 'idle', // πŸ“– 'idle'|'sending'|'success'|'error' β€” webhook send status
2929
+ bugReportError: null, // πŸ“– Last webhook error message
2873
2930
  }
2874
2931
 
2875
2932
  // πŸ“– Re-clamp viewport on terminal resize
@@ -3147,6 +3204,7 @@ async function main() {
3147
3204
  lines.push(` ${chalk.yellow('F')} Toggle favorite on selected row ${chalk.dim('(⭐ pinned at top, persisted)')}`)
3148
3205
  lines.push(` ${chalk.yellow('Q')} Smart Recommend ${chalk.dim('(🎯 find the best model for your task β€” questionnaire + live analysis)')}`)
3149
3206
  lines.push(` ${chalk.rgb(57, 255, 20).bold('J')} Request Feature ${chalk.dim('(πŸ“ send anonymous feedback to the project team)')}`)
3207
+ lines.push(` ${chalk.rgb(255, 87, 51).bold('I')} Report Bug ${chalk.dim('(πŸ› send anonymous bug report to the project team)')}`)
3150
3208
  lines.push(` ${chalk.yellow('P')} Open settings ${chalk.dim('(manage API keys, provider toggles, analytics, manual update)')}`)
3151
3209
  lines.push(` ${chalk.yellow('Shift+P')} Cycle config profile ${chalk.dim('(switch between saved profiles live)')}`)
3152
3210
  lines.push(` ${chalk.yellow('Shift+S')} Save current config as a named profile ${chalk.dim('(inline prompt β€” type name + Enter)')}`)
@@ -3491,6 +3549,112 @@ async function main() {
3491
3549
  return cleared.join('\n')
3492
3550
  }
3493
3551
 
3552
+ // ─── Bug Report overlay renderer ─────────────────────────────────────────
3553
+ // πŸ“– renderBugReport: Draw the overlay for anonymous Discord bug reports.
3554
+ // πŸ“– Shows an input field where users can type bug reports, then sends to Discord webhook.
3555
+ function renderBugReport() {
3556
+ const EL = '\x1b[K'
3557
+ const lines = []
3558
+
3559
+ // πŸ“– Calculate available space for multi-line input
3560
+ const maxInputWidth = OVERLAY_PANEL_WIDTH - 8 // 8 = padding (4 spaces each side)
3561
+ const maxInputLines = 10 // Show up to 10 lines of input
3562
+
3563
+ // πŸ“– Split buffer into lines for display (with wrapping)
3564
+ const wrapText = (text, width) => {
3565
+ const words = text.split(' ')
3566
+ const lines = []
3567
+ let currentLine = ''
3568
+
3569
+ for (const word of words) {
3570
+ const testLine = currentLine ? currentLine + ' ' + word : word
3571
+ if (testLine.length <= width) {
3572
+ currentLine = testLine
3573
+ } else {
3574
+ if (currentLine) lines.push(currentLine)
3575
+ currentLine = word
3576
+ }
3577
+ }
3578
+ if (currentLine) lines.push(currentLine)
3579
+ return lines
3580
+ }
3581
+
3582
+ const inputLines = wrapText(state.bugReportBuffer, maxInputWidth)
3583
+ const displayLines = inputLines.slice(0, maxInputLines)
3584
+
3585
+ // πŸ“– Header
3586
+ lines.push('')
3587
+ lines.push(` ${chalk.bold.rgb(255, 87, 51)('πŸ› Bug Report')} ${chalk.dim('β€” send anonymous bug reports to the project team')}`)
3588
+ lines.push('')
3589
+
3590
+ // πŸ“– Status messages (if any)
3591
+ if (state.bugReportStatus === 'sending') {
3592
+ lines.push(` ${chalk.yellow('⏳ Sending...')}`)
3593
+ lines.push('')
3594
+ } else if (state.bugReportStatus === 'success') {
3595
+ lines.push(` ${chalk.greenBright.bold('βœ… Successfully sent!')} ${chalk.dim('Closing overlay in 3 seconds...')}`)
3596
+ lines.push('')
3597
+ lines.push(` ${chalk.dim('Thank you for your feedback! Your bug report has been sent to the project team.')}`)
3598
+ lines.push('')
3599
+ } else if (state.bugReportStatus === 'error') {
3600
+ lines.push(` ${chalk.red('❌ Error:')} ${chalk.yellow(state.bugReportError || 'Failed to send')}`)
3601
+ lines.push(` ${chalk.dim('Press Backspace to edit, or Esc to close')}`)
3602
+ lines.push('')
3603
+ } else {
3604
+ lines.push(` ${chalk.dim('Describe the bug you encountered. Press Enter to send, Esc to cancel.')}`)
3605
+ lines.push(` ${chalk.dim('Your message will be sent anonymously to the project team.')}`)
3606
+ lines.push('')
3607
+ }
3608
+
3609
+ // πŸ“– Input box with border
3610
+ lines.push(chalk.dim(` β”Œβ”€ ${chalk.cyan('Bug Details')} ${chalk.dim(`(${state.bugReportBuffer.length}/500 chars)`)} ─${'─'.repeat(maxInputWidth - 24)}┐`))
3611
+
3612
+ // πŸ“– Display input lines (or placeholder if empty)
3613
+ if (displayLines.length === 0 && state.bugReportStatus === 'idle') {
3614
+ lines.push(chalk.dim(` β”‚${' '.repeat(maxInputWidth)}β”‚`))
3615
+ lines.push(chalk.dim(` β”‚ ${chalk.white.italic('Describe what happened...')}${' '.repeat(Math.max(0, maxInputWidth - 31))}β”‚`))
3616
+ } else {
3617
+ for (const line of displayLines) {
3618
+ const padded = line.padEnd(maxInputWidth)
3619
+ lines.push(` β”‚ ${chalk.white(padded)} β”‚`)
3620
+ }
3621
+ }
3622
+
3623
+ // πŸ“– Fill remaining space if needed
3624
+ const linesToFill = Math.max(0, maxInputLines - Math.max(displayLines.length, 1))
3625
+ for (let i = 0; i < linesToFill; i++) {
3626
+ lines.push(chalk.dim(` β”‚${' '.repeat(maxInputWidth)}β”‚`))
3627
+ }
3628
+
3629
+ // πŸ“– Cursor indicator (only when not sending/success)
3630
+ if (state.bugReportStatus === 'idle' || state.bugReportStatus === 'error') {
3631
+ const cursorLine = inputLines.length > 0 ? inputLines.length - 1 : 0
3632
+ const lastDisplayLine = displayLines.length - 1
3633
+ // Add cursor indicator to the last line
3634
+ if (lines.length > 0 && displayLines.length > 0) {
3635
+ const lastLineIdx = lines.findIndex(l => l.includes('β”‚ ') && !l.includes('Bug Details'))
3636
+ if (lastLineIdx >= 0 && lastLineIdx < lines.length) {
3637
+ // Add cursor blink
3638
+ const lastLine = lines[lastLineIdx]
3639
+ if (lastLine.includes('β”‚')) {
3640
+ lines[lastLineIdx] = lastLine.replace(/\s+β”‚$/, chalk.rgb(255, 87, 51).bold('▏') + ' β”‚')
3641
+ }
3642
+ }
3643
+ }
3644
+ }
3645
+
3646
+ lines.push(chalk.dim(` β””${'─'.repeat(maxInputWidth + 2)}β”˜`))
3647
+
3648
+ lines.push('')
3649
+ lines.push(chalk.dim(' Enter Send β€’ Esc Cancel β€’ Backspace Delete'))
3650
+
3651
+ // πŸ“– Apply overlay tint and return
3652
+ const BUG_REPORT_OVERLAY_BG = chalk.bgRgb(46, 20, 20) // Dark red-ish background (RGB: 46, 20, 20)
3653
+ const tintedLines = tintOverlayLines(lines, BUG_REPORT_OVERLAY_BG)
3654
+ const cleared = tintedLines.map(l => l + EL)
3655
+ return cleared.join('\n')
3656
+ }
3657
+
3494
3658
  // πŸ“– stopRecommendAnalysis: cleanup timers if user cancels during analysis
3495
3659
  function stopRecommendAnalysis() {
3496
3660
  if (state.recommendAnalysisTimer) { clearInterval(state.recommendAnalysisTimer); state.recommendAnalysisTimer = null }
@@ -3674,6 +3838,73 @@ async function main() {
3674
3838
  return
3675
3839
  }
3676
3840
 
3841
+ // πŸ“– Bug Report overlay: intercept ALL keys while overlay is active.
3842
+ // πŸ“– Enter β†’ send to Discord, Esc β†’ cancel, Backspace β†’ delete char, printable β†’ append to buffer.
3843
+ if (state.bugReportOpen) {
3844
+ if (key.ctrl && key.name === 'c') { exit(0); return }
3845
+
3846
+ if (key.name === 'escape') {
3847
+ // πŸ“– Cancel bug report β€” close overlay
3848
+ state.bugReportOpen = false
3849
+ state.bugReportBuffer = ''
3850
+ state.bugReportStatus = 'idle'
3851
+ state.bugReportError = null
3852
+ return
3853
+ }
3854
+
3855
+ if (key.name === 'return') {
3856
+ // πŸ“– Send bug report to Discord webhook
3857
+ const message = state.bugReportBuffer.trim()
3858
+ if (message.length > 0 && state.bugReportStatus !== 'sending') {
3859
+ state.bugReportStatus = 'sending'
3860
+ const result = await sendBugReport(message)
3861
+ if (result.success) {
3862
+ // πŸ“– Success β€” show confirmation briefly, then close overlay after 3 seconds
3863
+ state.bugReportStatus = 'success'
3864
+ setTimeout(() => {
3865
+ state.bugReportOpen = false
3866
+ state.bugReportBuffer = ''
3867
+ state.bugReportStatus = 'idle'
3868
+ state.bugReportError = null
3869
+ }, 3000)
3870
+ } else {
3871
+ // πŸ“– Error β€” show error message, keep overlay open
3872
+ state.bugReportStatus = 'error'
3873
+ state.bugReportError = result.error || 'Unknown error'
3874
+ }
3875
+ }
3876
+ return
3877
+ }
3878
+
3879
+ if (key.name === 'backspace') {
3880
+ // πŸ“– Don't allow editing while sending or after success
3881
+ if (state.bugReportStatus === 'sending' || state.bugReportStatus === 'success') return
3882
+ state.bugReportBuffer = state.bugReportBuffer.slice(0, -1)
3883
+ // πŸ“– Clear error status when user starts editing again
3884
+ if (state.bugReportStatus === 'error') {
3885
+ state.bugReportStatus = 'idle'
3886
+ state.bugReportError = null
3887
+ }
3888
+ return
3889
+ }
3890
+
3891
+ // πŸ“– Append printable characters (str is the raw character typed)
3892
+ // πŸ“– Limit to 500 characters (Discord embed description limit)
3893
+ if (str && str.length === 1 && !key.ctrl && !key.meta) {
3894
+ // πŸ“– Don't allow editing while sending or after success
3895
+ if (state.bugReportStatus === 'sending' || state.bugReportStatus === 'success') return
3896
+ if (state.bugReportBuffer.length < 500) {
3897
+ state.bugReportBuffer += str
3898
+ // πŸ“– Clear error status when user starts editing again
3899
+ if (state.bugReportStatus === 'error') {
3900
+ state.bugReportStatus = 'idle'
3901
+ state.bugReportError = null
3902
+ }
3903
+ }
3904
+ }
3905
+ return
3906
+ }
3907
+
3677
3908
  // πŸ“– Help overlay: full keyboard navigation + key swallowing while overlay is open.
3678
3909
  if (state.helpVisible) {
3679
3910
  const pageStep = Math.max(1, (state.terminalRows || 1) - 2)
@@ -4155,6 +4386,15 @@ async function main() {
4155
4386
  return
4156
4387
  }
4157
4388
 
4389
+ // πŸ“– I key: open Bug Report overlay (anonymous Discord bug reports)
4390
+ if (key.name === 'i') {
4391
+ state.bugReportOpen = true
4392
+ state.bugReportBuffer = ''
4393
+ state.bugReportStatus = 'idle'
4394
+ state.bugReportError = null
4395
+ return
4396
+ }
4397
+
4158
4398
  // πŸ“– Interval adjustment keys: W=decrease (faster), X=increase (slower)
4159
4399
  // πŸ“– Minimum 1s, maximum 60s
4160
4400
  if (key.name === 'w') {
@@ -4288,11 +4528,11 @@ async function main() {
4288
4528
 
4289
4529
  process.stdin.on('keypress', onKeyPress)
4290
4530
 
4291
- // πŸ“– Animation loop: render settings overlay, recommend overlay, help overlay, feature request overlay, OR main table
4531
+ // πŸ“– Animation loop: render settings overlay, recommend overlay, help overlay, feature request overlay, bug report overlay, OR main table
4292
4532
  const ticker = setInterval(() => {
4293
4533
  state.frame++
4294
4534
  // πŸ“– Cache visible+sorted models each frame so Enter handler always matches the display
4295
- if (!state.settingsOpen && !state.recommendOpen && !state.featureRequestOpen) {
4535
+ if (!state.settingsOpen && !state.recommendOpen && !state.featureRequestOpen && !state.bugReportOpen) {
4296
4536
  const visible = state.results.filter(r => !r.hidden)
4297
4537
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection)
4298
4538
  }
@@ -4302,9 +4542,11 @@ async function main() {
4302
4542
  ? renderRecommend()
4303
4543
  : state.featureRequestOpen
4304
4544
  ? renderFeatureRequest()
4305
- : state.helpVisible
4306
- ? renderHelp()
4307
- : renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, tierFilterMode, state.scrollOffset, state.terminalRows, originFilterMode, state.activeProfile, state.profileSaveMode, state.profileSaveBuffer)
4545
+ : state.bugReportOpen
4546
+ ? renderBugReport()
4547
+ : state.helpVisible
4548
+ ? renderHelp()
4549
+ : renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, tierFilterMode, state.scrollOffset, state.terminalRows, originFilterMode, state.activeProfile, state.profileSaveMode, state.profileSaveBuffer)
4308
4550
  process.stdout.write(ALT_HOME + content)
4309
4551
  }, Math.round(1000 / FPS))
4310
4552
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "description": "Find the fastest coding LLM models in seconds β€” ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
5
5
  "keywords": [
6
6
  "nvidia",