free-coding-models 0.5.10 → 0.5.11

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/src/tui/app.js CHANGED
@@ -343,6 +343,12 @@ export async function runApp(cliArgs, config, startupOptions = {}) {
343
343
  updateInstallFailures: startupUpdate.failures || 0,
344
344
  })
345
345
 
346
+ // 📖 `--playground` / `playground` subcommand: open the in-TUI chat
347
+ // 📖 overlay on first render so the user lands directly in the chat.
348
+ if (startupOptions.wantPlayground === true) {
349
+ state.playgroundOpen = true
350
+ }
351
+
346
352
  // 📖 Apply the pre-fetched last release date now that state is initialized
347
353
  state.lastReleaseDate = lastReleaseDate
348
354
 
@@ -759,7 +765,7 @@ export async function runApp(cliArgs, config, startupOptions = {}) {
759
765
  // 📖 usable right now: models enter/leave as soon as ping status changes.
760
766
  applyTierFilter()
761
767
  // 📖 Cache visible+sorted models each frame so Enter handler always matches the display
762
- if (!state.settingsOpen && !state.installEndpointsOpen && !state.toolInstallPromptOpen && !state.incompatibleFallbackOpen && !state.recommendOpen && !state.changelogOpen && !state.installedModelsOpen && !state.routerDashboardOpen && !state.commandPaletteOpen) {
768
+ if (!state.settingsOpen && !state.installEndpointsOpen && !state.toolInstallPromptOpen && !state.incompatibleFallbackOpen && !state.recommendOpen && !state.changelogOpen && !state.installedModelsOpen && !state.routerDashboardOpen && !state.playgroundOpen && !state.commandPaletteOpen) {
763
769
  const visible = state.results.filter(r => !r.hidden)
764
770
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection, {
765
771
  pinFavorites: state.favoritesPinnedAndSticky,
@@ -834,6 +840,8 @@ export async function runApp(cliArgs, config, startupOptions = {}) {
834
840
  ? overlays.renderInstalledModels()
835
841
  : state.routerDashboardOpen
836
842
  ? overlays.renderRouterDashboard()
843
+ : state.playgroundOpen
844
+ ? overlays.renderPlayground()
837
845
  : state.tokenUsageOpen
838
846
  ? overlays.renderTokenUsage()
839
847
  : state.routerOnboardingOpen
@@ -37,6 +37,7 @@ const ANALYSIS_FLAGS = [
37
37
 
38
38
  const CONFIG_FLAGS = [
39
39
  { flag: 'web | --web | --gui', description: 'Start the full-catalog realtime Web Dashboard' },
40
+ { flag: 'playground | --playground', description: 'Open the in-TUI Playground chat overlay (auto-starts the router if needed)' },
40
41
  { flag: '--daemon', description: 'Start the FCM Router daemon + web dashboard (same port)' },
41
42
  { flag: '--daemon-bg', description: 'Start the FCM Router daemon in the background' },
42
43
  { flag: '--daemon-status', description: 'Print FCM Router daemon status JSON' },
@@ -54,6 +55,7 @@ const EXAMPLES = [
54
55
  'free-coding-models --daemon-status',
55
56
  'free-coding-models --sync-set',
56
57
  'free-coding-models --sync-set my-coding-set',
58
+ 'free-coding-models --playground',
57
59
  'free-coding-models --openclaw --tier S',
58
60
  "free-coding-models --json | jq '.[0]'",
59
61
  ]
@@ -211,6 +211,7 @@ const BASE_COMMAND_TREE = [
211
211
  { id: 'open-changelog', label: 'Changelog', icon: '📋', type: 'page', description: 'Version history', keywords: ['changelog', 'release'] },
212
212
 
213
213
  { id: 'open-recommend', label: 'Smart recommend', shortcut: 'Q', icon: '🎯', type: 'page', description: 'Find best model for task', keywords: ['recommend', 'best model'] },
214
+ { id: 'open-playground', label: 'Playground', shortcut: ';', icon: '💬', type: 'page', description: 'Chat with the FCM router', keywords: ['playground', 'chat', 'try', 'prompt', 'router'] },
214
215
  { id: 'open-install-endpoints', label: 'Install endpoints', icon: '🔌', type: 'page', description: 'Install provider catalogs', keywords: ['install', 'endpoints', 'providers'] },
215
216
  { id: 'open-installed-models', label: 'Installed models', icon: '🗂️', type: 'page', description: 'View models configured in tools', keywords: ['installed', 'models', 'configured', 'tools', 'manager', 'goose', 'crush', 'aider'] },
216
217
  ]
@@ -55,6 +55,11 @@ import {
55
55
  restartRouterDashboardDaemon,
56
56
  toggleRouterDashboardProbePause,
57
57
  } from '../core/router-dashboard.js'
58
+ import {
59
+ closePlaygroundOverlay,
60
+ openPlaygroundOverlay,
61
+ handlePlaygroundKeypress,
62
+ } from '../core/playground.js'
58
63
  import { benchmarkModel } from '../core/benchmark.js'
59
64
 
60
65
  // 📖 Some providers need an explicit probe model because the first catalog entry
@@ -1283,6 +1288,7 @@ export function createKeyHandler(ctx) {
1283
1288
  || state.toolInstallPromptOpen
1284
1289
  || state.installedModelsOpen
1285
1290
  || state.routerDashboardOpen
1291
+ || state.playgroundOpen
1286
1292
  || state.recommendOpen
1287
1293
 
1288
1294
  || state.helpVisible
@@ -1475,6 +1481,7 @@ export function createKeyHandler(ctx) {
1475
1481
 
1476
1482
  case 'open-recommend': return openRecommendOverlay()
1477
1483
  case 'open-router-dashboard': return openRouterDashboardOverlay(state)
1484
+ case 'open-playground': return openPlaygroundOverlay(state)
1478
1485
  case 'open-token-usage': return openTokenUsageOverlay()
1479
1486
  case 'open-install-endpoints': return openInstallEndpointsOverlay()
1480
1487
  case 'open-installed-models': return openInstalledModelsOverlay()
@@ -1745,6 +1752,14 @@ export function createKeyHandler(ctx) {
1745
1752
  return
1746
1753
  }
1747
1754
 
1755
+ // 📖 Playground overlay: handles Enter, Esc, Backspace, Ctrl+S, Ctrl+L,
1756
+ // 📖 PageUp/PageDown, and printable input itself. The dedicated handler
1757
+ // 📖 in core/playground.js owns the draft and submission state.
1758
+ if (state.playgroundOpen) {
1759
+ if (handlePlaygroundKeypress(key.string != null ? key.string : key.name, { fetchFn: globalThis.fetch })) return
1760
+ return
1761
+ }
1762
+
1748
1763
  // 📖 Install Endpoints overlay: provider → tool → connection → scope → optional model subset.
1749
1764
  if (state.installEndpointsOpen) {
1750
1765
  if (key.ctrl && key.name === 'c') { exit(0); return }
@@ -2838,6 +2853,13 @@ export function createKeyHandler(ctx) {
2838
2853
  return
2839
2854
  }
2840
2855
 
2856
+ // 📖 ; (semicolon) key: open the Playground chat overlay. Avoids clashes
2857
+ // 📖 with P (settings), K (help), Z (tool), N (changelog), etc.
2858
+ if (key.name === ';' || (key.shift && key.name === ':')) {
2859
+ void openPlaygroundOverlay(state)
2860
+ return
2861
+ }
2862
+
2841
2863
  // 📖 Y key toggles favorites display mode (pinned+sticky vs normal rows).
2842
2864
  if (key.name === 'y' && !key.ctrl && !key.meta) {
2843
2865
  toggleFavoritesDisplayMode()
@@ -22,6 +22,7 @@
22
22
  import { loadChangelog } from '../core/changelog-loader.js'
23
23
  import { buildCliHelpLines } from './cli-help.js'
24
24
  import { renderRouterDashboard as renderRouterDashboardOverlay } from '../core/router-dashboard.js'
25
+ import { renderPlayground as renderPlaygroundOverlay } from '../core/playground.js'
25
26
  import { themeColors, getThemeStatusLabel, getProviderRgb } from './theme.js'
26
27
 
27
28
  export function createOverlayRenderers(state, deps) {
@@ -1334,6 +1335,15 @@ export function createOverlayRenderers(state, deps) {
1334
1335
  return renderRouterDashboardOverlay(state, { LOCAL_VERSION })
1335
1336
  }
1336
1337
 
1338
+ // ─── Playground overlay ───────────────────────────────────────────────────────
1339
+ // 📖 Renders the in-TUI chat playground when the user presses `;` or opens
1340
+ // 📖 it from the command palette. All chat traffic flows to the local
1341
+ // 📖 daemon over HTTP, so the TUI process never has to import the
1342
+ // 📖 provider catalog.
1343
+ function renderPlayground() {
1344
+ return renderPlaygroundOverlay(state, state.terminalRows, state.terminalCols)
1345
+ }
1346
+
1337
1347
  // ─── Incompatible fallback overlay ─────────────────────────────────────────
1338
1348
  // 📖 renderIncompatibleFallback shows when user presses Enter on a model that
1339
1349
  // 📖 is NOT compatible with the active tool. Two sections:
@@ -1615,6 +1625,7 @@ export function createOverlayRenderers(state, deps) {
1615
1625
  renderChangelog,
1616
1626
  renderInstalledModels,
1617
1627
  renderRouterDashboard,
1628
+ renderPlayground,
1618
1629
  renderIncompatibleFallback,
1619
1630
  renderTokenUsage,
1620
1631
  renderRouterOnboarding,
package/src/tui/theme.js CHANGED
@@ -53,6 +53,7 @@ const PALETTES = {
53
53
  feedback: [31, 13, 20],
54
54
  changelog: [12, 24, 44],
55
55
  commandPalette: [14, 20, 36],
56
+ playground: [10, 22, 18],
56
57
  },
57
58
  cursor: {
58
59
  defaultBg: [39, 55, 90],
@@ -99,6 +100,7 @@ const PALETTES = {
99
100
  feedback: [255, 247, 248],
100
101
  changelog: [244, 248, 255],
101
102
  commandPalette: [242, 247, 255],
103
+ playground: [244, 252, 246],
102
104
  },
103
105
  cursor: {
104
106
  defaultBg: [217, 231, 255],
@@ -326,5 +328,6 @@ export const themeColors = {
326
328
  overlayBgRecommend: (text) => paintBg(currentPalette().overlayBg.recommend, text, currentPalette().overlayFg),
327
329
  overlayBgFeedback: (text) => paintBg(currentPalette().overlayBg.feedback, text, currentPalette().overlayFg),
328
330
  overlayBgChangelog: (text) => paintBg(currentPalette().overlayBg.changelog, text, currentPalette().overlayFg),
331
+ overlayBgPlayground: (text) => paintBg(currentPalette().overlayBg.playground, text, currentPalette().overlayFg),
329
332
  overlayBgCommandPalette: (text) => paintBg(currentPalette().overlayBg.commandPalette, text, currentPalette().overlayFg),
330
333
  }