free-coding-models 0.3.16 → 0.3.17
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/CHANGELOG.md +10 -0
- package/README.md +187 -120
- package/package.json +1 -1
- package/src/app.js +3 -1
- package/src/config.js +3 -0
- package/src/key-handler.js +16 -1
- package/src/overlays.js +17 -19
- package/src/render-table.js +13 -4
- package/src/theme.js +67 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## 0.3.17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Auto Light/Dark Theme**: Implemented automatic detection of the user's terminal theme (dark or light) so that the TUI is always readable. Added semantic color tokens, and users can override the theme as `dark`, `light`, or `auto` via the Settings interface.
|
|
9
|
+
|
|
10
|
+
## 0.3.16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **iFlow free coding models**: Added the iFlow provider to the README and TUI, supporting `deepseek-v3`, `mini-max-m2.5`, etc.
|
|
14
|
+
|
|
5
15
|
## 0.3.15
|
|
6
16
|
|
|
7
17
|
### Added
|
package/README.md
CHANGED
|
@@ -1,184 +1,251 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/npm/v/free-coding-models?color=76b900&label=npm&logo=npm" alt="npm version">
|
|
3
|
+
<img src="https://img.shields.io/node/v/free-coding-models?color=76b900&logo=node.js" alt="node version">
|
|
4
|
+
<img src="https://img.shields.io/npm/l/free-coding-models?color=76b900" alt="license">
|
|
5
|
+
<img src="https://img.shields.io/badge/models-160-76b900?logo=nvidia" alt="models count">
|
|
6
|
+
<img src="https://img.shields.io/badge/providers-20-blue" alt="providers count">
|
|
7
|
+
</p>
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
<h1 align="center">free-coding-models</h1>
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<strong>Find the fastest free coding model in seconds</strong><br>
|
|
13
|
+
<sub>Ping 160 models across 20 AI Free providers in real-time </sub><br><sub> Install Free API endpoints to your favorite AI coding tool: <br>OpenCode, OpenClaw, Crush, Goose, Aider, Qwen Code, OpenHands, Amp or Pi in one keystroke</sub>
|
|
14
|
+
</p>
|
|
6
15
|
|
|
7
|
-
## Install
|
|
8
16
|
|
|
9
|
-
```bash
|
|
10
|
-
pnpm install
|
|
11
|
-
pnpm start
|
|
12
|
-
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
<p align="center">
|
|
15
19
|
|
|
16
20
|
```bash
|
|
17
21
|
npm install -g free-coding-models
|
|
18
22
|
free-coding-models
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<a href="#-why-this-tool">Why</a> •
|
|
29
|
+
<a href="#-quick-start">Quick Start</a> •
|
|
30
|
+
<a href="#-providers">Providers</a> •
|
|
31
|
+
<a href="#-usage">Usage</a> •
|
|
32
|
+
<a href="#-tui-keys">TUI Keys</a> •
|
|
33
|
+
<a href="#-contributing">Contributing</a>
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="demo.gif" alt="free-coding-models demo" width="100%">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<sub>Made with ❤️ and ☕ by <a href="https://vanessadepraute.dev">Vanessa Depraute</a> (aka <a href="https://vavanessa.dev">Vava-Nessa</a>)</sub>
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 💡 Why this tool?
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
- Pings models continuously and shows latency, uptime, stability, verdict, and usage snapshots
|
|
25
|
-
- Lets you filter, sort, favorite, and compare models inside a full-screen TUI
|
|
26
|
-
- Launches supported coding tools with the currently selected model, after writing that exact selection as the tool default
|
|
27
|
-
- Installs provider catalogs into compatible external tool configs through the `Y` flow
|
|
48
|
+
There are **160+ free coding models** scattered across 20 providers. Which one is fastest right now? Which one is actually stable versus just lucky on the last ping?
|
|
28
49
|
|
|
29
|
-
|
|
50
|
+
This CLI pings them all in parallel, shows live latency, and calculates a **live Stability Score (0-100)**. Average latency alone is misleading if a model randomly spikes to 6 seconds; the stability score measures true reliability by combining **p95 latency** (30%), **jitter/variance** (30%), **spike rate** (20%), and **uptime** (20%).
|
|
30
51
|
|
|
31
|
-
|
|
52
|
+
It then writes the model you pick directly into your coding tool's config — so you go from "which model?" to "coding" in under 10 seconds.
|
|
32
53
|
|
|
33
|
-
|
|
34
|
-
- `OpenCode Desktop`
|
|
35
|
-
- `OpenClaw`
|
|
36
|
-
- `Crush`
|
|
37
|
-
- `Goose`
|
|
38
|
-
- `Pi`
|
|
39
|
-
- `Aider`
|
|
40
|
-
- `Qwen Code`
|
|
41
|
-
- `OpenHands`
|
|
42
|
-
- `Amp`
|
|
54
|
+
---
|
|
43
55
|
|
|
44
|
-
|
|
56
|
+
## ⚡ Quick Start
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
- `Codex`
|
|
48
|
-
- `Gemini`
|
|
49
|
-
- the old FCM global proxy / daemon / log overlay flow
|
|
58
|
+
**① Get a free API key** — you only need one to get started:
|
|
50
59
|
|
|
51
|
-
|
|
60
|
+
**160 coding models** across 20 providers, ranked by [SWE-bench Verified](https://www.swebench.com).
|
|
61
|
+
|
|
62
|
+
| Provider | Models | Tier range | Free tier | Env var |
|
|
63
|
+
|----------|--------|-----------|-----------|--------|
|
|
64
|
+
| [NVIDIA NIM](https://build.nvidia.com) | 44 | S+ → C | 40 req/min (no credit card needed) | `NVIDIA_API_KEY` |
|
|
65
|
+
| [iFlow](https://platform.iflow.cn) | 11 | S+ → A+ | Free for individuals (no req limits, 7-day key expiry) | `IFLOW_API_KEY` |
|
|
66
|
+
| [ZAI](https://z.ai) | 7 | S+ → S | Free tier (generous quota) | `ZAI_API_KEY` |
|
|
67
|
+
| [Alibaba DashScope](https://modelstudio.console.alibabacloud.com) | 8 | S+ → A | 1M free tokens per model (Singapore region, 90 days) | `DASHSCOPE_API_KEY` |
|
|
68
|
+
| [Groq](https://console.groq.com/keys) | 10 | S → B | 30‑50 RPM per model (varies by model) | `GROQ_API_KEY` |
|
|
69
|
+
| [Cerebras](https://cloud.cerebras.ai) | 7 | S+ → B | Generous free tier (developer tier 10× higher limits) | `CEREBRAS_API_KEY` |
|
|
70
|
+
| [SambaNova](https://sambanova.ai/developers) | 12 | S+ → B | Dev tier generous quota | `SAMBANOVA_API_KEY` |
|
|
71
|
+
| [OpenRouter](https://openrouter.ai/keys) | 11 | S+ → C | Free on :free: 50/day <$10, 1000/day ≥$10 (20 req/min) | `OPENROUTER_API_KEY` |
|
|
72
|
+
| [Hugging Face](https://huggingface.co/settings/tokens) | 2 | S → B | Free monthly credits (~$0.10) | `HUGGINGFACE_API_KEY` |
|
|
73
|
+
| [Together AI](https://api.together.ai/settings/api-keys) | 7 | S+ → A- | Credits/promos vary by account (check console) | `TOGETHER_API_KEY` |
|
|
74
|
+
| [DeepInfra](https://deepinfra.com/login) | 2 | A- → B+ | 200 concurrent requests (default) | `DEEPINFRA_API_KEY` |
|
|
75
|
+
| [Fireworks AI](https://fireworks.ai) | 2 | S | $1 credits – 10 req/min without payment | `FIREWORKS_API_KEY` |
|
|
76
|
+
| [Mistral Codestral](https://codestral.mistral.ai) | 1 | B+ | 30 req/min, 2000/day | `CODESTRAL_API_KEY` |
|
|
77
|
+
| [Hyperbolic](https://app.hyperbolic.ai/settings) | 10 | S+ → A- | $1 free trial credits | `HYPERBOLIC_API_KEY` |
|
|
78
|
+
| [Scaleway](https://console.scaleway.com/iam/api-keys) | 7 | S+ → B+ | 1M free tokens | `SCALEWAY_API_KEY` |
|
|
79
|
+
| [Google AI Studio](https://aistudio.google.com/apikey) | 3 | B → C | 14.4K req/day, 30/min | `GOOGLE_API_KEY` |
|
|
80
|
+
| [SiliconFlow](https://cloud.siliconflow.cn/account/ak) | 6 | S+ → A | Free models: usually 100 RPM, varies by model | `SILICONFLOW_API_KEY` |
|
|
81
|
+
| [Cloudflare Workers AI](https://dash.cloudflare.com) | 6 | S → B | Free: 10k neurons/day, text-gen 300 RPM | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` |
|
|
82
|
+
| [Perplexity API](https://www.perplexity.ai/settings/api) | 4 | A+ → B | Tiered limits by spend (default ~50 RPM) | `PERPLEXITY_API_KEY` |
|
|
83
|
+
| [Replicate](https://replicate.com/account/api-tokens) | 1 | A- | 6 req/min (no payment) – up to 3,000 RPM with payment | `REPLICATE_API_TOKEN` |
|
|
84
|
+
|
|
85
|
+
> 💡 One key is enough. Add more at any time with **`P`** inside the TUI.
|
|
86
|
+
|
|
87
|
+
### Tier scale
|
|
88
|
+
|
|
89
|
+
| Tier | SWE-bench | Best for |
|
|
90
|
+
|------|-----------|----------|
|
|
91
|
+
| **S+** | ≥ 70% | Complex refactors, real-world GitHub issues |
|
|
92
|
+
| **S** | 60–70% | Most coding tasks, strong general use |
|
|
93
|
+
| **A+/A** | 40–60% | Solid alternatives, targeted programming |
|
|
94
|
+
| **A-/B+** | 30–40% | Smaller tasks, constrained infra |
|
|
95
|
+
| **B/C** | < 30% | Code completion, edge/minimal setups |
|
|
96
|
+
|
|
97
|
+
**② Install and run:**
|
|
52
98
|
|
|
53
99
|
```bash
|
|
100
|
+
npm install -g free-coding-models
|
|
54
101
|
free-coding-models
|
|
55
102
|
```
|
|
56
103
|
|
|
57
|
-
|
|
104
|
+
On first run, you'll be prompted to enter your API key(s). You can skip providers and add more later with **`P`**.
|
|
58
105
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
free-coding-models --json
|
|
64
|
-
free-coding-models --recommend
|
|
65
|
-
free-coding-models --help
|
|
106
|
+
**③ Pick a model and launch your tool:**
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
↑↓ navigate → Enter to launch
|
|
66
110
|
```
|
|
67
111
|
|
|
68
|
-
|
|
112
|
+
The model you select is automatically written into your tool's config (OpenCode, OpenClaw, Crush, etc.) and the tool opens immediately. Done.
|
|
69
113
|
|
|
70
|
-
|
|
114
|
+
> 💡 You can also run `free-coding-models --goose --tier S` to pre-filter to S-tier models for Goose before the TUI even opens.
|
|
71
115
|
|
|
72
|
-
- `↑↓` navigate rows
|
|
73
|
-
- `Enter` launch/select the current model in the active tool mode
|
|
74
|
-
- `Z` cycle tool mode
|
|
75
|
-
- `T` cycle tier filter
|
|
76
|
-
- `D` cycle provider filter
|
|
77
|
-
- `R/O/M/L/A/S/C/H/V/B/U/G` sort columns
|
|
78
|
-
- `E` toggle configured models only
|
|
79
|
-
- `F` favorite/unfavorite the selected model
|
|
80
|
-
- `W` cycle ping cadence
|
|
81
|
-
- `P` open Settings
|
|
82
|
-
- `Y` open Install Endpoints
|
|
83
|
-
- `Q` open Smart Recommend
|
|
84
|
-
- `I` open feedback / bug report form
|
|
85
|
-
- `N` open changelog
|
|
86
|
-
- `K` open help
|
|
87
|
-
- `Ctrl+C` exit
|
|
88
116
|
|
|
89
|
-
## Settings
|
|
90
117
|
|
|
91
|
-
|
|
118
|
+
## 🚀 Usage
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
- enable or disable providers
|
|
95
|
-
- test provider keys
|
|
96
|
-
- check for updates
|
|
97
|
-
- toggle the terminal width warning
|
|
98
|
-
- clean discontinued proxy-era config left behind by older builds
|
|
120
|
+
### Common scenarios
|
|
99
121
|
|
|
100
|
-
|
|
122
|
+
```bash
|
|
123
|
+
# "I want the most reliable model right now"
|
|
124
|
+
free-coding-models --fiable
|
|
101
125
|
|
|
102
|
-
|
|
126
|
+
# "I want to configure Goose with an S-tier model"
|
|
127
|
+
free-coding-models --goose --tier S
|
|
103
128
|
|
|
104
|
-
|
|
129
|
+
# "I want NVIDIA's top models only"
|
|
130
|
+
free-coding-models --origin nvidia --tier S
|
|
105
131
|
|
|
106
|
-
|
|
132
|
+
# "Show me only elite models that are currently healthy"
|
|
133
|
+
free-coding-models --premium
|
|
107
134
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
3. Choose scope: all models or selected models
|
|
111
|
-
4. Write the managed config/env files
|
|
135
|
+
# "I want to script this — give me JSON"
|
|
136
|
+
free-coding-models --tier S --json | jq -r '.[0].modelId'
|
|
112
137
|
|
|
113
|
-
|
|
138
|
+
# "I want to configure OpenClaw with Groq's fastest model"
|
|
139
|
+
free-coding-models --openclaw --origin groq
|
|
140
|
+
```
|
|
114
141
|
|
|
115
|
-
|
|
142
|
+
### Tool launcher flags
|
|
116
143
|
|
|
117
|
-
|
|
144
|
+
| Flag | Launches |
|
|
145
|
+
|------|----------|
|
|
146
|
+
| `--opencode` | OpenCode CLI |
|
|
147
|
+
| `--opencode-desktop` | OpenCode Desktop |
|
|
148
|
+
| `--openclaw` | OpenClaw |
|
|
149
|
+
| `--crush` | Crush |
|
|
150
|
+
| `--goose` | Goose |
|
|
151
|
+
| `--aider` | Aider |
|
|
152
|
+
| `--qwen` | Qwen Code |
|
|
153
|
+
| `--openhands` | OpenHands |
|
|
154
|
+
| `--amp` | Amp |
|
|
155
|
+
| `--pi` | Pi |
|
|
118
156
|
|
|
119
|
-
|
|
157
|
+
Press **`Z`** in the TUI to cycle between tools without restarting.
|
|
120
158
|
|
|
121
|
-
|
|
122
|
-
- Selecting a model and pressing `Enter` updates the config and launches the target mode
|
|
159
|
+
→ **[Full flags reference](./docs/flags.md)**
|
|
123
160
|
|
|
124
|
-
|
|
161
|
+
---
|
|
125
162
|
|
|
126
|
-
|
|
127
|
-
- OpenClaw itself is not launched by FCM
|
|
163
|
+
## ⌨️ TUI Keys
|
|
128
164
|
|
|
129
|
-
|
|
165
|
+
| Key | Action |
|
|
166
|
+
|-----|--------|
|
|
167
|
+
| `↑↓` | Navigate models |
|
|
168
|
+
| `Enter` | Launch selected model in active tool |
|
|
169
|
+
| `Z` | Cycle target tool |
|
|
170
|
+
| `T` | Cycle tier filter |
|
|
171
|
+
| `D` | Cycle provider filter |
|
|
172
|
+
| `E` | Toggle configured-only mode |
|
|
173
|
+
| `F` | Favorite / unfavorite model |
|
|
174
|
+
| `R/S/C/M/O/L/A/H/V/B/U` | Sort columns |
|
|
175
|
+
| `P` | Settings (API keys, providers, updates) |
|
|
176
|
+
| `Y` | Install Endpoints (push provider into tool config) |
|
|
177
|
+
| `Q` | Smart Recommend overlay |
|
|
178
|
+
| `N` | Changelog |
|
|
179
|
+
| `W` | Cycle ping cadence |
|
|
180
|
+
| `I` | Feedback / bug report |
|
|
181
|
+
| `K` | Help overlay |
|
|
182
|
+
| `Ctrl+C` | Exit |
|
|
130
183
|
|
|
131
|
-
|
|
184
|
+
→ **[Stability score & column reference](./docs/stability.md)**
|
|
132
185
|
|
|
133
|
-
|
|
186
|
+
---
|
|
134
187
|
|
|
135
|
-
|
|
136
|
-
- it binds to localhost on a random port
|
|
137
|
-
- it shuts down automatically when OpenCode exits
|
|
188
|
+
## ✨ Features
|
|
138
189
|
|
|
139
|
-
|
|
190
|
+
- **Parallel pings** — all 160 models tested simultaneously via native `fetch`
|
|
191
|
+
- **Adaptive monitoring** — 2s burst for 60s → 10s normal → 30s idle
|
|
192
|
+
- **Stability score** — composite 0–100 (p95 latency, jitter, spike rate, uptime)
|
|
193
|
+
- **Smart ranking** — top 3 highlighted 🥇🥈🥉
|
|
194
|
+
- **Favorites** — pin models with `F`, persisted across sessions
|
|
195
|
+
- **Configured-only default** — only shows providers you have keys for
|
|
196
|
+
- **Keyless latency** — models ping even without an API key (show 🔑 NO KEY)
|
|
197
|
+
- **Smart Recommend** — questionnaire picks the best model for your task type
|
|
198
|
+
- **Install Endpoints** — push a full provider catalog into any tool's config (`Y`)
|
|
199
|
+
- **Width guardrail** — shows a warning instead of a broken table in narrow terminals
|
|
200
|
+
- **Auto-retry** — timeout models keep getting retried
|
|
140
201
|
|
|
141
|
-
|
|
202
|
+
---
|
|
142
203
|
|
|
143
|
-
|
|
204
|
+
## 📋 Contributing
|
|
144
205
|
|
|
145
|
-
|
|
206
|
+
We welcome contributions — issues, PRs, new provider integrations.
|
|
146
207
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
pnpm test:fcm:mock
|
|
150
|
-
```
|
|
208
|
+
**Q:** How accurate are the latency numbers?
|
|
209
|
+
**A:** Real round-trip times measured by your machine. Results depend on your network and provider load at that moment.
|
|
151
210
|
|
|
152
|
-
|
|
211
|
+
**Q:** Can I add a new provider?
|
|
212
|
+
**A:** Yes — see [`sources.js`](./sources.js) for the model catalog format.
|
|
153
213
|
|
|
154
|
-
|
|
214
|
+
→ **[Development guide](./docs/development.md)** · **[Config reference](./docs/config.md)** · **[Tool integrations](./docs/integrations.md)**
|
|
155
215
|
|
|
156
|
-
|
|
216
|
+
---
|
|
157
217
|
|
|
158
|
-
|
|
159
|
-
pnpm test
|
|
160
|
-
```
|
|
218
|
+
## 📧 Support
|
|
161
219
|
|
|
162
|
-
|
|
220
|
+
[GitHub Issues](https://github.com/vava-nessa/free-coding-models/issues) · [Discord](https://discord.gg/ZTNFHvvCkU)
|
|
163
221
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 📄 License
|
|
167
225
|
|
|
168
|
-
|
|
226
|
+
MIT © [vava](https://github.com/vava-nessa)
|
|
169
227
|
|
|
170
|
-
|
|
171
|
-
- Pure helpers and sorting logic: [`src/utils.js`](./src/utils.js)
|
|
172
|
-
- OpenCode launch/config helpers: [`src/opencode.js`](./src/opencode.js), [`src/opencode-config.js`](./src/opencode-config.js)
|
|
173
|
-
- External tool launchers: [`src/tool-launchers.js`](./src/tool-launchers.js)
|
|
174
|
-
- Endpoint installer flow: [`src/endpoint-installer.js`](./src/endpoint-installer.js)
|
|
228
|
+
---
|
|
175
229
|
|
|
176
|
-
|
|
230
|
+
<p align="center">
|
|
231
|
+
<strong>Contributors</strong><br>
|
|
232
|
+
<a href="https://github.com/vava-nessa"><img src="https://avatars.githubusercontent.com/u/5466264?v=4&s=60" width="60" height="60" style="border-radius:50%" alt="vava-nessa"></a>
|
|
233
|
+
<a href="https://github.com/erwinh22"><img src="https://avatars.githubusercontent.com/u/6641858?v=4&s=60" width="60" height="60" style="border-radius:50%" alt="erwinh22"></a>
|
|
234
|
+
<a href="https://github.com/whit3rabbit"><img src="https://avatars.githubusercontent.com/u/12357518?v=4&s=60" width="60" height="60" style="border-radius:50%" alt="whit3rabbit"></a>
|
|
235
|
+
<a href="https://github.com/skylaweber"><img src="https://avatars.githubusercontent.com/u/172871734?v=4&s=60" width="60" height="60" style="border-radius:50%" alt="skylaweber"></a>
|
|
236
|
+
<a href="https://github.com/PhucTruong-ctrl"><img src="https://github.com/PhucTruong-ctrl.png?s=60" width="60" height="60" style="border-radius:50%" alt="PhucTruong-ctrl"></a>
|
|
237
|
+
<br>
|
|
238
|
+
<sub>
|
|
239
|
+
<a href="https://github.com/vava-nessa">vava-nessa</a> ·
|
|
240
|
+
<a href="https://github.com/erwinh22">erwinh22</a> ·
|
|
241
|
+
<a href="https://github.com/whit3rabbit">whit3rabbit</a> ·
|
|
242
|
+
<a href="https://github.com/skylaweber">skylaweber</a> ·
|
|
243
|
+
<a href="https://github.com/PhucTruong-ctrl">PhucTruong-ctrl</a>
|
|
244
|
+
</sub>
|
|
245
|
+
</p>
|
|
177
246
|
|
|
178
|
-
The app surface is intentionally narrowed right now to keep releases stable:
|
|
179
247
|
|
|
180
|
-
- direct provider launches are the supported path
|
|
181
|
-
- the old cross-tool proxy stack has been removed from the app
|
|
182
|
-
- Claude Code, Codex, and Gemini stay hidden until the rewrite is production-ready
|
|
183
248
|
|
|
184
|
-
|
|
249
|
+
<p align="center">
|
|
250
|
+
<sub>Anonymous usage data collected to improve the tool. No personal information ever.</sub>
|
|
251
|
+
</p>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.17",
|
|
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",
|
package/src/app.js
CHANGED
|
@@ -126,6 +126,7 @@ import { getConfiguredInstallableProviders, installProviderEndpoints, refreshIns
|
|
|
126
126
|
import { loadCache, saveCache, clearCache, getCacheAge } from '../src/cache.js'
|
|
127
127
|
import { checkConfigSecurity } from '../src/security.js'
|
|
128
128
|
import { buildCliHelpText } from '../src/cli-help.js'
|
|
129
|
+
import { detectActiveTheme } from '../src/theme.js'
|
|
129
130
|
|
|
130
131
|
// 📖 mergedModels: cross-provider grouped model list (one entry per label, N providers each)
|
|
131
132
|
// 📖 mergedModelByLabel: fast lookup map from display label → merged model entry
|
|
@@ -174,7 +175,8 @@ const LOCAL_VERSION = pkg.version
|
|
|
174
175
|
|
|
175
176
|
export async function runApp(cliArgs, config) {
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
// 📖 Detect user active terminal theme
|
|
179
|
+
detectActiveTheme(config.settings?.theme || 'dark')
|
|
178
180
|
|
|
179
181
|
// 📖 Check config file security — warn and offer auto-fix if permissions are too open
|
|
180
182
|
const securityCheck = checkConfigSecurity()
|
package/src/config.js
CHANGED
|
@@ -210,6 +210,7 @@ function normalizeSettingsSection(settings) {
|
|
|
210
210
|
...safeSettings,
|
|
211
211
|
hideUnconfiguredModels: typeof safeSettings.hideUnconfiguredModels === 'boolean' ? safeSettings.hideUnconfiguredModels : true,
|
|
212
212
|
disableWidthsWarning: safeSettings.disableWidthsWarning === true,
|
|
213
|
+
theme: ['dark', 'light', 'auto'].includes(safeSettings.theme) ? safeSettings.theme : 'dark',
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
@@ -230,6 +231,7 @@ function normalizeProfileSettings(settings) {
|
|
|
230
231
|
..._emptyProfileSettings(),
|
|
231
232
|
...safeSettings,
|
|
232
233
|
disableWidthsWarning: safeSettings.disableWidthsWarning === true,
|
|
234
|
+
theme: ['dark', 'light', 'auto'].includes(safeSettings.theme) ? safeSettings.theme : 'dark',
|
|
233
235
|
}
|
|
234
236
|
}
|
|
235
237
|
|
|
@@ -842,6 +844,7 @@ export function _emptyProfileSettings() {
|
|
|
842
844
|
hideUnconfiguredModels: true, // 📖 true = default to providers that are actually configured
|
|
843
845
|
preferredToolMode: 'opencode', // 📖 remember the last Z-selected launcher across app restarts
|
|
844
846
|
disableWidthsWarning: false, // 📖 Disable widths warning (default off)
|
|
847
|
+
theme: 'dark', // 📖 'dark', 'light', or 'auto'
|
|
845
848
|
}
|
|
846
849
|
}
|
|
847
850
|
|
package/src/key-handler.js
CHANGED
|
@@ -200,6 +200,7 @@ export function createKeyHandler(ctx) {
|
|
|
200
200
|
noteUserActivity,
|
|
201
201
|
intervalToPingMode,
|
|
202
202
|
PING_MODE_CYCLE,
|
|
203
|
+
themeRowIdx,
|
|
203
204
|
setResults,
|
|
204
205
|
readline,
|
|
205
206
|
} = ctx
|
|
@@ -926,7 +927,8 @@ export function createKeyHandler(ctx) {
|
|
|
926
927
|
const providerKeys = Object.keys(sources)
|
|
927
928
|
const updateRowIdx = providerKeys.length
|
|
928
929
|
const widthWarningRowIdx = updateRowIdx + 1
|
|
929
|
-
const
|
|
930
|
+
const themeRowIdx = widthWarningRowIdx + 1
|
|
931
|
+
const cleanupLegacyProxyRowIdx = themeRowIdx + 1
|
|
930
932
|
const changelogViewRowIdx = cleanupLegacyProxyRowIdx + 1
|
|
931
933
|
// 📖 Profile system removed - API keys now persist permanently across all sessions
|
|
932
934
|
const maxRowIdx = changelogViewRowIdx
|
|
@@ -1108,6 +1110,19 @@ export function createKeyHandler(ctx) {
|
|
|
1108
1110
|
|| state.settingsCursor === cleanupLegacyProxyRowIdx
|
|
1109
1111
|
|| state.settingsCursor === changelogViewRowIdx
|
|
1110
1112
|
) return
|
|
1113
|
+
// 📖 Theme configuration cycle inside settings
|
|
1114
|
+
if (state.settingsCursor === themeRowIdx) {
|
|
1115
|
+
const themes = ['dark', 'light', 'auto']
|
|
1116
|
+
const currentTheme = state.config.settings?.theme || 'dark'
|
|
1117
|
+
const nextIndex = (themes.indexOf(currentTheme) + 1) % themes.length
|
|
1118
|
+
state.config.settings.theme = themes[nextIndex]
|
|
1119
|
+
saveConfig(state.config)
|
|
1120
|
+
try {
|
|
1121
|
+
const { detectActiveTheme } = await import('../src/theme.js')
|
|
1122
|
+
detectActiveTheme(state.config.settings.theme)
|
|
1123
|
+
} catch {}
|
|
1124
|
+
return
|
|
1125
|
+
}
|
|
1111
1126
|
// 📖 Widths Warning toggle (disable/enable)
|
|
1112
1127
|
if (state.settingsCursor === widthWarningRowIdx) {
|
|
1113
1128
|
toggleWidthsWarningSetting()
|
package/src/overlays.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { loadChangelog } from './changelog-loader.js'
|
|
24
24
|
import { buildCliHelpLines } from './cli-help.js'
|
|
25
|
+
import { themeColors } from './theme.js'
|
|
25
26
|
|
|
26
27
|
export function createOverlayRenderers(state, deps) {
|
|
27
28
|
const {
|
|
@@ -34,9 +35,6 @@ export function createOverlayRenderers(state, deps) {
|
|
|
34
35
|
resolveApiKeys,
|
|
35
36
|
isProviderEnabled,
|
|
36
37
|
TIER_CYCLE,
|
|
37
|
-
SETTINGS_OVERLAY_BG,
|
|
38
|
-
HELP_OVERLAY_BG,
|
|
39
|
-
RECOMMEND_OVERLAY_BG,
|
|
40
38
|
OVERLAY_PANEL_WIDTH,
|
|
41
39
|
keepOverlayTargetVisible,
|
|
42
40
|
sliceOverlayLines,
|
|
@@ -163,7 +161,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
163
161
|
|
|
164
162
|
const row = `${bullet}[ ${enabledBadge} ] ${providerName} ${keyDisplay.padEnd(30)} ${testBadge} ${rateSummary}`
|
|
165
163
|
cursorLineByRow[i] = lines.length
|
|
166
|
-
lines.push(isCursor ?
|
|
164
|
+
lines.push(isCursor ? themeColors.bgCursor(row) : row)
|
|
167
165
|
}
|
|
168
166
|
|
|
169
167
|
lines.push('')
|
|
@@ -216,7 +214,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
216
214
|
if (updateState === 'installing') updateStatus = chalk.cyan('Installing update…')
|
|
217
215
|
const updateRow = `${updateBullet}${chalk.bold(updateActionLabel).padEnd(44)} ${updateStatus}`
|
|
218
216
|
cursorLineByRow[updateRowIdx] = lines.length
|
|
219
|
-
lines.push(updateCursor ?
|
|
217
|
+
lines.push(updateCursor ? themeColors.bgCursor(updateRow) : updateRow)
|
|
220
218
|
// 📖 Width warning visibility row for the startup narrow-terminal overlay.
|
|
221
219
|
const disableWidthsWarning = Boolean(state.config.settings?.disableWidthsWarning)
|
|
222
220
|
const widthWarningBullet = state.settingsCursor === widthWarningRowIdx ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
@@ -225,7 +223,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
225
223
|
: chalk.greenBright('👁 Enabled')
|
|
226
224
|
const widthWarningRow = `${widthWarningBullet}${chalk.bold('Small Width Warnings').padEnd(44)} ${widthWarningStatus}`
|
|
227
225
|
cursorLineByRow[widthWarningRowIdx] = lines.length
|
|
228
|
-
lines.push(state.settingsCursor === widthWarningRowIdx ?
|
|
226
|
+
lines.push(state.settingsCursor === widthWarningRowIdx ? themeColors.bgCursor(widthWarningRow) : widthWarningRow)
|
|
229
227
|
if (updateState === 'error' && state.settingsUpdateError) {
|
|
230
228
|
lines.push(chalk.red(` ${state.settingsUpdateError}`))
|
|
231
229
|
}
|
|
@@ -234,13 +232,13 @@ export function createOverlayRenderers(state, deps) {
|
|
|
234
232
|
const cleanupLegacyProxyBullet = state.settingsCursor === cleanupLegacyProxyRowIdx ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
235
233
|
const cleanupLegacyProxyRow = `${cleanupLegacyProxyBullet}${chalk.bold('Clean Legacy Proxy Config').padEnd(44)} ${chalk.magentaBright('Enter remove discontinued bridge leftovers')}`
|
|
236
234
|
cursorLineByRow[cleanupLegacyProxyRowIdx] = lines.length
|
|
237
|
-
lines.push(state.settingsCursor === cleanupLegacyProxyRowIdx ?
|
|
235
|
+
lines.push(state.settingsCursor === cleanupLegacyProxyRowIdx ? themeColors.bgCursorLegacy(cleanupLegacyProxyRow) : cleanupLegacyProxyRow)
|
|
238
236
|
|
|
239
237
|
// 📖 Changelog viewer row
|
|
240
238
|
const changelogViewBullet = state.settingsCursor === changelogViewRowIdx ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
241
239
|
const changelogViewRow = `${changelogViewBullet}${chalk.bold('View Changelog').padEnd(44)} ${chalk.dim('Enter browse version history')}`
|
|
242
240
|
cursorLineByRow[changelogViewRowIdx] = lines.length
|
|
243
|
-
lines.push(state.settingsCursor === changelogViewRowIdx ?
|
|
241
|
+
lines.push(state.settingsCursor === changelogViewRowIdx ? themeColors.bgCursorSettingsList(changelogViewRow) : changelogViewRow)
|
|
244
242
|
|
|
245
243
|
// 📖 Profile system removed - API keys now persist permanently across all sessions
|
|
246
244
|
|
|
@@ -280,7 +278,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
280
278
|
const { visible, offset } = sliceOverlayLines(lines, state.settingsScrollOffset, state.terminalRows)
|
|
281
279
|
state.settingsScrollOffset = offset
|
|
282
280
|
|
|
283
|
-
const tintedLines = tintOverlayLines(visible,
|
|
281
|
+
const tintedLines = tintOverlayLines(visible, themeColors.overlayBgSettings, state.terminalCols)
|
|
284
282
|
const cleared = tintedLines.map(l => l + EL)
|
|
285
283
|
return cleared.join('\n')
|
|
286
284
|
}
|
|
@@ -347,7 +345,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
347
345
|
const bullet = isCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
348
346
|
const row = `${bullet}${chalk.bold(provider.label.padEnd(24))} ${chalk.dim(`${provider.modelCount} models`)}`
|
|
349
347
|
cursorLineByRow[idx] = lines.length
|
|
350
|
-
lines.push(isCursor ?
|
|
348
|
+
lines.push(isCursor ? themeColors.bgCursorInstall(row) : row)
|
|
351
349
|
})
|
|
352
350
|
}
|
|
353
351
|
|
|
@@ -371,7 +369,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
371
369
|
const bullet = isCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
372
370
|
const row = `${bullet}${chalk.bold(label.padEnd(26))} ${note}`
|
|
373
371
|
cursorLineByRow[idx] = lines.length
|
|
374
|
-
lines.push(isCursor ?
|
|
372
|
+
lines.push(isCursor ? themeColors.bgCursorInstall(row) : row)
|
|
375
373
|
})
|
|
376
374
|
|
|
377
375
|
lines.push('')
|
|
@@ -386,7 +384,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
386
384
|
const bullet = isCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
387
385
|
const row = `${bullet}${chalk.bold(scope.label)}`
|
|
388
386
|
cursorLineByRow[idx] = lines.length
|
|
389
|
-
lines.push(isCursor ?
|
|
387
|
+
lines.push(isCursor ? themeColors.bgCursorInstall(row) : row)
|
|
390
388
|
lines.push(chalk.dim(` ${scope.hint}`))
|
|
391
389
|
lines.push('')
|
|
392
390
|
})
|
|
@@ -409,7 +407,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
409
407
|
const tier = chalk.cyan(model.tier.padEnd(2))
|
|
410
408
|
const row = `${bullet}${checkbox} ${chalk.bold(model.label.padEnd(26))} ${tier} ${chalk.dim(model.ctx.padEnd(6))} ${chalk.dim(model.modelId)}`
|
|
411
409
|
cursorLineByRow[idx] = lines.length
|
|
412
|
-
lines.push(isCursor ?
|
|
410
|
+
lines.push(isCursor ? themeColors.bgCursorInstall(row) : row)
|
|
413
411
|
})
|
|
414
412
|
|
|
415
413
|
lines.push('')
|
|
@@ -443,7 +441,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
443
441
|
const { visible, offset } = sliceOverlayLines(lines, state.installEndpointsScrollOffset, state.terminalRows)
|
|
444
442
|
state.installEndpointsScrollOffset = offset
|
|
445
443
|
|
|
446
|
-
const tintedLines = tintOverlayLines(visible,
|
|
444
|
+
const tintedLines = tintOverlayLines(visible, themeColors.overlayBgSettings, state.terminalCols)
|
|
447
445
|
const cleared = tintedLines.map((line) => line + EL)
|
|
448
446
|
return cleared.join('\n')
|
|
449
447
|
}
|
|
@@ -539,7 +537,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
539
537
|
// 📖 Help overlay can be longer than viewport, so keep a dedicated scroll offset.
|
|
540
538
|
const { visible, offset } = sliceOverlayLines(lines, state.helpScrollOffset, state.terminalRows)
|
|
541
539
|
state.helpScrollOffset = offset
|
|
542
|
-
const tintedLines = tintOverlayLines(visible,
|
|
540
|
+
const tintedLines = tintOverlayLines(visible, themeColors.overlayBgHelp, state.terminalCols)
|
|
543
541
|
const cleared = tintedLines.map(l => l + EL)
|
|
544
542
|
return cleared.join('\n')
|
|
545
543
|
}
|
|
@@ -605,7 +603,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
605
603
|
const opt = q.options[i]
|
|
606
604
|
const isCursor = i === state.recommendCursor
|
|
607
605
|
const bullet = isCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
|
|
608
|
-
const label = isCursor ?
|
|
606
|
+
const label = isCursor ? themeColors.textBold(opt.label) : themeColors.text(opt.label)
|
|
609
607
|
lines.push(`${bullet}${label}`)
|
|
610
608
|
}
|
|
611
609
|
|
|
@@ -666,9 +664,9 @@ export function createOverlayRenderers(state, deps) {
|
|
|
666
664
|
const stabStr = stability === -1 ? '—' : String(stability)
|
|
667
665
|
|
|
668
666
|
const isCursor = i === state.recommendCursor
|
|
669
|
-
const highlight = isCursor ?
|
|
667
|
+
const highlight = isCursor ? themeColors.bgCursor : (s => s)
|
|
670
668
|
|
|
671
|
-
lines.push(highlight(` ${medal} ${chalk.bold('#' + (i + 1))} ${
|
|
669
|
+
lines.push(highlight(` ${medal} ${chalk.bold('#' + (i + 1))} ${themeColors.textBold(r.label)} ${chalk.dim('(' + providerName + ')')}`))
|
|
672
670
|
lines.push(highlight(` Score: ${chalk.bold.greenBright(String(rec.score) + '/100')} │ Tier: ${tierFn(r.tier)} │ SWE: ${chalk.cyan(sweStr)} │ Avg: ${chalk.yellow(avgStr)} │ CTX: ${chalk.cyan(ctxStr)} │ Stability: ${chalk.cyan(stabStr)}`))
|
|
673
671
|
lines.push('')
|
|
674
672
|
}
|
|
@@ -683,7 +681,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
683
681
|
lines.push('')
|
|
684
682
|
const { visible, offset } = sliceOverlayLines(lines, state.recommendScrollOffset, state.terminalRows)
|
|
685
683
|
state.recommendScrollOffset = offset
|
|
686
|
-
const tintedLines = tintOverlayLines(visible,
|
|
684
|
+
const tintedLines = tintOverlayLines(visible, themeColors.overlayBgRecommend, state.terminalCols)
|
|
687
685
|
const cleared2 = tintedLines.map(l => l + EL)
|
|
688
686
|
return cleared2.join('\n')
|
|
689
687
|
}
|
package/src/render-table.js
CHANGED
|
@@ -34,7 +34,16 @@
|
|
|
34
34
|
import chalk from 'chalk'
|
|
35
35
|
import { createRequire } from 'module'
|
|
36
36
|
import { sources } from '../sources.js'
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
TABLE_FIXED_LINES,
|
|
39
|
+
COL_MODEL,
|
|
40
|
+
TIER_CYCLE,
|
|
41
|
+
msCell,
|
|
42
|
+
spinCell,
|
|
43
|
+
PING_INTERVAL,
|
|
44
|
+
FRAMES
|
|
45
|
+
} from './constants.js'
|
|
46
|
+
import { themeColors } from './theme.js'
|
|
38
47
|
import { TIER_COLOR } from './tier-colors.js'
|
|
39
48
|
import { getAvg, getVerdict, getUptime, getStabilityScore, getVersionStatusInfo } from './utils.js'
|
|
40
49
|
import { usagePlaceholderForProvider } from './ping.js'
|
|
@@ -556,12 +565,12 @@ export function renderTable(results, pendingPings, frame, cursor = null, sortCol
|
|
|
556
565
|
const row = ' ' + num + COL_SEP + tier + COL_SEP + sweCell + COL_SEP + ctxCell + COL_SEP + nameCell + COL_SEP + sourceCell + COL_SEP + pingCell + COL_SEP + avgCell + COL_SEP + status + COL_SEP + speedCell + COL_SEP + stabCell + COL_SEP + uptimeCell + COL_SEP + tokensCell
|
|
557
566
|
|
|
558
567
|
if (isCursor) {
|
|
559
|
-
lines.push(
|
|
568
|
+
lines.push(themeColors.bgModelCursor(row))
|
|
560
569
|
} else if (r.isRecommended) {
|
|
561
570
|
// 📖 Medium green background for recommended models (distinguishable from favorites)
|
|
562
|
-
lines.push(
|
|
571
|
+
lines.push(themeColors.bgModelRecommended(row))
|
|
563
572
|
} else if (r.isFavorite) {
|
|
564
|
-
lines.push(
|
|
573
|
+
lines.push(themeColors.bgModelFavorite(row))
|
|
565
574
|
} else {
|
|
566
575
|
lines.push(row)
|
|
567
576
|
}
|
package/src/theme.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file theme.js
|
|
3
|
+
* @description Dynamic light/dark theme detector and semantic colour mappings.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import { execSync } from 'child_process'
|
|
8
|
+
|
|
9
|
+
let activeTheme = 'dark'
|
|
10
|
+
|
|
11
|
+
export function detectActiveTheme(configTheme) {
|
|
12
|
+
if (configTheme === 'dark' || configTheme === 'light') {
|
|
13
|
+
activeTheme = configTheme;
|
|
14
|
+
return activeTheme;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Auto detect
|
|
18
|
+
const fgbg = process.env.COLORFGBG || '';
|
|
19
|
+
if (fgbg.includes(';15') || fgbg.includes(';base03')) {
|
|
20
|
+
activeTheme = 'light';
|
|
21
|
+
return activeTheme;
|
|
22
|
+
} else if (fgbg) {
|
|
23
|
+
activeTheme = 'dark';
|
|
24
|
+
return activeTheme;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (process.platform === 'darwin') {
|
|
28
|
+
try {
|
|
29
|
+
const style = execSync('defaults read -g AppleInterfaceStyle 2>/dev/null', { timeout: 100 }).toString().trim();
|
|
30
|
+
activeTheme = style === 'Dark' ? 'dark' : 'light';
|
|
31
|
+
} catch {
|
|
32
|
+
activeTheme = 'light';
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
activeTheme = 'dark';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return activeTheme;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getTheme() {
|
|
42
|
+
return activeTheme;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Semantic colors
|
|
46
|
+
export const themeColors = {
|
|
47
|
+
text: (str) => activeTheme === 'light' ? chalk.black(str) : chalk.white(str),
|
|
48
|
+
textBold: (str) => activeTheme === 'light' ? chalk.black.bold(str) : chalk.white.bold(str),
|
|
49
|
+
dim: (str) => activeTheme === 'light' ? chalk.gray(str) : chalk.dim(str),
|
|
50
|
+
dimYellow: (str) => activeTheme === 'light' ? chalk.rgb(180, 150, 0)(str) : chalk.dim.yellow(str),
|
|
51
|
+
bgCursor: (str) => activeTheme === 'light' ? chalk.bgRgb(220, 220, 240).black(str) : chalk.bgRgb(30, 30, 60)(str),
|
|
52
|
+
bgCursorInstall: (str) => activeTheme === 'light' ? chalk.bgRgb(220, 220, 240).black(str) : chalk.bgRgb(24, 44, 62)(str),
|
|
53
|
+
bgCursorSettingsList: (str) => activeTheme === 'light' ? chalk.bgRgb(220, 240, 220).black(str) : chalk.bgRgb(30, 45, 30)(str),
|
|
54
|
+
bgCursorLegacy: (str) => activeTheme === 'light' ? chalk.bgRgb(240, 220, 240).black(str) : chalk.bgRgb(55, 25, 55)(str),
|
|
55
|
+
|
|
56
|
+
bgModelCursor: (str) => activeTheme === 'light' ? chalk.bgRgb(230, 210, 230).black(str) : chalk.bgRgb(155, 55, 135)(str),
|
|
57
|
+
bgModelRecommended: (str) => activeTheme === 'light' ? chalk.bgRgb(200, 240, 200).black(str) : chalk.bgRgb(15, 40, 15)(str),
|
|
58
|
+
bgModelFavorite: (str) => activeTheme === 'light' ? chalk.bgRgb(250, 230, 190).black(str) : chalk.bgRgb(88, 64, 10)(str),
|
|
59
|
+
|
|
60
|
+
overlayBgSettings: (str) => activeTheme === 'light' ? chalk.bgRgb(245, 245, 250).black(str) : chalk.bgRgb(0, 0, 0).white(str),
|
|
61
|
+
overlayBgHelp: (str) => activeTheme === 'light' ? chalk.bgRgb(250, 250, 250).black(str) : chalk.bgRgb(0, 0, 0).white(str),
|
|
62
|
+
overlayBgRecommend: (str) => activeTheme === 'light' ? chalk.bgRgb(240, 250, 245).black(str) : chalk.bgRgb(0, 0, 0).white(str),
|
|
63
|
+
overlayBgFeedback: (str) => activeTheme === 'light' ? chalk.bgRgb(255, 245, 245).black(str) : chalk.bgRgb(46, 20, 20).white(str),
|
|
64
|
+
|
|
65
|
+
// Header badges text color override
|
|
66
|
+
badgeText: (str) => activeTheme === 'light' ? chalk.rgb(255, 255, 255)(str) : chalk.rgb(0, 0, 0)(str),
|
|
67
|
+
}
|