free-coding-models 0.3.19 → 0.3.22
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 +44 -1
- package/README.md +17 -8
- package/package.json +1 -1
- package/src/app.js +29 -35
- package/src/cli-help.js +1 -2
- package/src/command-palette.js +232 -59
- package/src/config.js +0 -3
- package/src/constants.js +5 -0
- package/src/key-handler.js +100 -60
- package/src/overlays.js +85 -60
- package/src/product-flags.js +4 -9
- package/src/render-table.js +134 -67
- package/src/testfcm.js +1 -1
- package/src/theme.js +2 -0
- package/src/utils.js +0 -2
package/src/command-palette.js
CHANGED
|
@@ -1,64 +1,200 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file command-palette.js
|
|
3
3
|
* @description Command palette registry and fuzzy search helpers for the main TUI.
|
|
4
|
+
* Now supports hierarchical categories with expandable/collapsible groups.
|
|
4
5
|
*
|
|
5
6
|
* @functions
|
|
6
|
-
* → `
|
|
7
|
+
* → `buildCommandPaletteTree` — builds the hierarchical command tree with categories and subcategories
|
|
8
|
+
* → `flattenCommandTree` — converts tree to flat list for filtering (respects expansion state)
|
|
7
9
|
* → `fuzzyMatchCommand` — scores a query against one string and returns match positions
|
|
8
10
|
* → `filterCommandPaletteEntries` — returns sorted command matches for a query
|
|
9
11
|
*
|
|
10
|
-
* @exports {
|
|
12
|
+
* @exports { buildCommandPaletteTree, flattenCommandTree, fuzzyMatchCommand, filterCommandPaletteEntries }
|
|
11
13
|
*
|
|
12
14
|
* @see src/key-handler.js
|
|
13
15
|
* @see src/overlays.js
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
18
|
+
// 📖 Base command tree template (will be enhanced with dynamic model list)
|
|
19
|
+
const BASE_COMMAND_TREE = [
|
|
20
|
+
{
|
|
21
|
+
id: 'filters',
|
|
22
|
+
label: 'Filters',
|
|
23
|
+
icon: '🔍',
|
|
24
|
+
children: [
|
|
25
|
+
{
|
|
26
|
+
id: 'filter-tier',
|
|
27
|
+
label: 'Filter by tier',
|
|
28
|
+
icon: '📊',
|
|
29
|
+
children: [
|
|
30
|
+
{ id: 'filter-tier-all', label: 'All tiers', tier: null, shortcut: 'T', description: 'Show all models', keywords: ['filter', 'tier', 'all'] },
|
|
31
|
+
{ id: 'filter-tier-splus', label: 'S+ tier', tier: 'S+', description: 'Best coding models', keywords: ['filter', 'tier', 's+'] },
|
|
32
|
+
{ id: 'filter-tier-s', label: 'S tier', tier: 'S', description: 'Excellent models', keywords: ['filter', 'tier', 's'] },
|
|
33
|
+
{ id: 'filter-tier-aplus', label: 'A+ tier', tier: 'A+', description: 'Very good models', keywords: ['filter', 'tier', 'a+'] },
|
|
34
|
+
{ id: 'filter-tier-a', label: 'A tier', tier: 'A', description: 'Good models', keywords: ['filter', 'tier', 'a'] },
|
|
35
|
+
{ id: 'filter-tier-aminus', label: 'A- tier', tier: 'A-', description: 'Solid models', keywords: ['filter', 'tier', 'a-'] },
|
|
36
|
+
{ id: 'filter-tier-bplus', label: 'B+ tier', tier: 'B+', description: 'Fair models', keywords: ['filter', 'tier', 'b+'] },
|
|
37
|
+
{ id: 'filter-tier-b', label: 'B tier', tier: 'B', description: 'Basic models', keywords: ['filter', 'tier', 'b'] },
|
|
38
|
+
{ id: 'filter-tier-c', label: 'C tier', tier: 'C', description: 'Limited models', keywords: ['filter', 'tier', 'c'] },
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'filter-provider',
|
|
43
|
+
label: 'Filter by provider',
|
|
44
|
+
icon: '🏢',
|
|
45
|
+
children: [
|
|
46
|
+
{ id: 'filter-provider-cycle', label: 'Cycle provider', shortcut: 'D', description: 'Switch between providers', keywords: ['filter', 'provider', 'origin'] },
|
|
47
|
+
{ id: 'filter-provider-all', label: 'All providers', providerKey: null, description: 'Show all providers', keywords: ['filter', 'provider', 'all'] },
|
|
48
|
+
{ id: 'filter-provider-nvidia', label: 'NVIDIA NIM', providerKey: 'nvidiaNim', description: 'NVIDIA models', keywords: ['filter', 'provider', 'nvidia', 'nim'] },
|
|
49
|
+
{ id: 'filter-provider-groq', label: 'Groq', providerKey: 'groq', description: 'Groq models', keywords: ['filter', 'provider', 'groq'] },
|
|
50
|
+
{ id: 'filter-provider-cerebras', label: 'Cerebras', providerKey: 'cerebras', description: 'Cerebras models', keywords: ['filter', 'provider', 'cerebras'] },
|
|
51
|
+
{ id: 'filter-provider-sambanova', label: 'SambaNova', providerKey: 'sambanova', description: 'SambaNova models', keywords: ['filter', 'provider', 'sambanova'] },
|
|
52
|
+
{ id: 'filter-provider-openrouter', label: 'OpenRouter', providerKey: 'openrouter', description: 'OpenRouter models', keywords: ['filter', 'provider', 'openrouter'] },
|
|
53
|
+
{ id: 'filter-provider-together', label: 'Together AI', providerKey: 'together', description: 'Together models', keywords: ['filter', 'provider', 'together'] },
|
|
54
|
+
{ id: 'filter-provider-deepinfra', label: 'DeepInfra', providerKey: 'deepinfra', description: 'DeepInfra models', keywords: ['filter', 'provider', 'deepinfra'] },
|
|
55
|
+
{ id: 'filter-provider-fireworks', label: 'Fireworks AI', providerKey: 'fireworks', description: 'Fireworks models', keywords: ['filter', 'provider', 'fireworks'] },
|
|
56
|
+
{ id: 'filter-provider-hyperbolic', label: 'Hyperbolic', providerKey: 'hyperbolic', description: 'Hyperbolic models', keywords: ['filter', 'provider', 'hyperbolic'] },
|
|
57
|
+
{ id: 'filter-provider-google', label: 'Google AI', providerKey: 'google', description: 'Google models', keywords: ['filter', 'provider', 'google'] },
|
|
58
|
+
{ id: 'filter-provider-huggingface', label: 'Hugging Face', providerKey: 'huggingface', description: 'Hugging Face models', keywords: ['filter', 'provider', 'huggingface'] },
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'filter-model',
|
|
63
|
+
label: 'Filter by model',
|
|
64
|
+
icon: '🤖',
|
|
65
|
+
children: []
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'filter-other',
|
|
69
|
+
label: 'Other filters',
|
|
70
|
+
icon: '⚙️',
|
|
71
|
+
children: [
|
|
72
|
+
{ id: 'filter-configured-toggle', label: 'Toggle configured-only', shortcut: 'E', description: 'Show only configured providers', keywords: ['filter', 'configured', 'keys'] },
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'sort',
|
|
79
|
+
label: 'Sort',
|
|
80
|
+
icon: '📶',
|
|
81
|
+
children: [
|
|
82
|
+
{ id: 'sort-rank', label: 'Sort by rank', shortcut: 'R', description: 'Rank by SWE score', keywords: ['sort', 'rank'] },
|
|
83
|
+
{ id: 'sort-tier', label: 'Sort by tier', description: 'Group by quality tier', keywords: ['sort', 'tier'] },
|
|
84
|
+
{ id: 'sort-provider', label: 'Sort by provider', shortcut: 'O', description: 'Group by provider', keywords: ['sort', 'origin', 'provider'] },
|
|
85
|
+
{ id: 'sort-model', label: 'Sort by model', shortcut: 'M', description: 'Alphabetical order', keywords: ['sort', 'model', 'name'] },
|
|
86
|
+
{ id: 'sort-latest-ping', label: 'Sort by latest ping', shortcut: 'L', description: 'Recent response time', keywords: ['sort', 'latest', 'ping'] },
|
|
87
|
+
{ id: 'sort-avg-ping', label: 'Sort by avg ping', shortcut: 'A', description: 'Average response time', keywords: ['sort', 'avg', 'average', 'ping'] },
|
|
88
|
+
{ id: 'sort-swe', label: 'Sort by SWE score', shortcut: 'S', description: 'Coding ability score', keywords: ['sort', 'swe', 'score'] },
|
|
89
|
+
{ id: 'sort-ctx', label: 'Sort by context', shortcut: 'C', description: 'Context window size', keywords: ['sort', 'context', 'ctx'] },
|
|
90
|
+
{ id: 'sort-health', label: 'Sort by health', shortcut: 'H', description: 'Current model status', keywords: ['sort', 'health', 'condition'] },
|
|
91
|
+
{ id: 'sort-verdict', label: 'Sort by verdict', shortcut: 'V', description: 'Overall assessment', keywords: ['sort', 'verdict'] },
|
|
92
|
+
{ id: 'sort-stability', label: 'Sort by stability', shortcut: 'B', description: 'Reliability score', keywords: ['sort', 'stability'] },
|
|
93
|
+
{ id: 'sort-uptime', label: 'Sort by uptime', shortcut: 'U', description: 'Success rate', keywords: ['sort', 'uptime'] },
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
// 📖 Pages - directly at root level, not in submenu
|
|
97
|
+
{ id: 'open-settings', label: 'Settings', shortcut: 'P', icon: '⚙️', type: 'page', description: 'API keys and preferences', keywords: ['settings', 'config', 'api key'] },
|
|
98
|
+
{ id: 'open-help', label: 'Help', shortcut: 'K', icon: '❓', type: 'page', description: 'Show all shortcuts', keywords: ['help', 'shortcuts', 'hotkeys'] },
|
|
99
|
+
{ id: 'open-changelog', label: 'Changelog', shortcut: 'N', icon: '📋', type: 'page', description: 'Version history', keywords: ['changelog', 'release'] },
|
|
100
|
+
{ id: 'open-feedback', label: 'Feedback', shortcut: 'I', icon: '📝', type: 'page', description: 'Report bugs or requests', keywords: ['feedback', 'bug', 'request'] },
|
|
101
|
+
{ id: 'open-recommend', label: 'Smart recommend', shortcut: 'Q', icon: '🎯', type: 'page', description: 'Find best model for task', keywords: ['recommend', 'best model'] },
|
|
102
|
+
{ id: 'open-install-endpoints', label: 'Install endpoints', icon: '🔌', type: 'page', description: 'Install provider catalogs', keywords: ['install', 'endpoints', 'providers'] },
|
|
103
|
+
// 📖 Actions - directly at root level, not in submenu
|
|
104
|
+
{ id: 'action-cycle-theme', label: 'Cycle theme', shortcut: 'G', icon: '🌗', type: 'action', description: 'Switch dark/light/auto', keywords: ['theme', 'dark', 'light', 'auto'] },
|
|
105
|
+
{ id: 'action-cycle-tool-mode', label: 'Target Tool', shortcut: 'Z', icon: '🔄', type: 'action', description: 'Change target AI Coding CLI Tool.', keywords: ['tool', 'mode', 'launcher', 'target'] },
|
|
106
|
+
{ id: 'action-cycle-ping-mode', label: 'Cycle ping mode', shortcut: 'W', icon: '⚡', type: 'action', description: 'Adjust ping speed', keywords: ['ping', 'cadence', 'speed', 'slow'] },
|
|
107
|
+
{ id: 'action-toggle-favorite', label: 'Toggle favorite', shortcut: 'F', icon: '⭐', type: 'action', description: 'Pin to favorites', keywords: ['favorite', 'star'] },
|
|
108
|
+
{ id: 'action-reset-view', label: 'Reset view', shortcut: 'Shift+R', icon: '🔄', type: 'action', description: 'Reset filters and sort', keywords: ['reset', 'view', 'sort', 'filters'] },
|
|
60
109
|
]
|
|
61
110
|
|
|
111
|
+
/**
|
|
112
|
+
* 📖 Build the command palette tree with dynamic model filters.
|
|
113
|
+
* @param {Array} visibleModels - Optional list of visible models to create model filter entries
|
|
114
|
+
* @returns {Array} The command tree with model filters added
|
|
115
|
+
*/
|
|
116
|
+
export function buildCommandPaletteTree(visibleModels = []) {
|
|
117
|
+
// 📖 Clone the base tree
|
|
118
|
+
const tree = JSON.parse(JSON.stringify(BASE_COMMAND_TREE))
|
|
119
|
+
|
|
120
|
+
// 📖 Find the filter-model category and add dynamic model entries
|
|
121
|
+
const filterModelCategory = tree.find(cat => cat.id === 'filters')
|
|
122
|
+
?.children.find(sub => sub.id === 'filter-model')
|
|
123
|
+
|
|
124
|
+
if (filterModelCategory && Array.isArray(visibleModels) && visibleModels.length > 0) {
|
|
125
|
+
// 📖 Add top 20 most-used or most relevant models
|
|
126
|
+
const topModels = visibleModels
|
|
127
|
+
.filter(m => !m.hidden && m.status !== 'noauth')
|
|
128
|
+
.slice(0, 20)
|
|
129
|
+
|
|
130
|
+
for (const model of topModels) {
|
|
131
|
+
filterModelCategory.children.push({
|
|
132
|
+
id: `filter-model-${model.providerKey}-${model.modelId}`,
|
|
133
|
+
label: model.label,
|
|
134
|
+
modelId: model.modelId,
|
|
135
|
+
providerKey: model.providerKey,
|
|
136
|
+
keywords: ['filter', 'model', model.label.toLowerCase(), model.modelId.toLowerCase()],
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return tree
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 📖 Flatten the command tree into a list, respecting which nodes are expanded.
|
|
146
|
+
* @param {Array} tree - The command tree
|
|
147
|
+
* @param {Set} expandedIds - Set of IDs that are expanded
|
|
148
|
+
* @returns {Array} Flat list with type markers ('category' | 'subcategory' | 'command' | 'page' | 'action')
|
|
149
|
+
*/
|
|
150
|
+
export function flattenCommandTree(tree, expandedIds = new Set()) {
|
|
151
|
+
const result = []
|
|
152
|
+
|
|
153
|
+
function traverse(nodes, depth = 0) {
|
|
154
|
+
for (const node of nodes) {
|
|
155
|
+
// 📖 Check if this is a direct page/action (not in a submenu)
|
|
156
|
+
if (node.type === 'page' || node.type === 'action') {
|
|
157
|
+
result.push({
|
|
158
|
+
...node,
|
|
159
|
+
type: node.type,
|
|
160
|
+
depth: 0,
|
|
161
|
+
hasChildren: false,
|
|
162
|
+
isExpanded: false,
|
|
163
|
+
})
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const isExpanded = expandedIds.has(node.id)
|
|
168
|
+
const hasChildren = Array.isArray(node.children) && node.children.length > 0
|
|
169
|
+
|
|
170
|
+
if (hasChildren) {
|
|
171
|
+
result.push({
|
|
172
|
+
...node,
|
|
173
|
+
type: depth === 0 ? 'category' : 'subcategory',
|
|
174
|
+
depth,
|
|
175
|
+
hasChildren,
|
|
176
|
+
isExpanded,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
if (isExpanded) {
|
|
180
|
+
traverse(node.children, depth + 1)
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
result.push({
|
|
184
|
+
...node,
|
|
185
|
+
type: 'command',
|
|
186
|
+
depth,
|
|
187
|
+
hasChildren: false,
|
|
188
|
+
isExpanded: false,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
traverse(tree)
|
|
195
|
+
return result
|
|
196
|
+
}
|
|
197
|
+
|
|
62
198
|
const ID_TO_TIER = {
|
|
63
199
|
'filter-tier-all': null,
|
|
64
200
|
'filter-tier-splus': 'S+',
|
|
@@ -71,11 +207,31 @@ const ID_TO_TIER = {
|
|
|
71
207
|
'filter-tier-c': 'C',
|
|
72
208
|
}
|
|
73
209
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
210
|
+
/**
|
|
211
|
+
* 📖 Legacy function for backward compatibility - builds flat list from tree.
|
|
212
|
+
* 📖 Expands all categories so every command is searchable by fuzzyMatchCommand.
|
|
213
|
+
* @param {Array} visibleModels - Optional list of visible models for model filter entries
|
|
214
|
+
*/
|
|
215
|
+
export function buildCommandPaletteEntries(visibleModels = []) {
|
|
216
|
+
const tree = buildCommandPaletteTree(visibleModels)
|
|
217
|
+
// 📖 Collect every node id that has children so flattenCommandTree traverses into them.
|
|
218
|
+
const allIds = new Set()
|
|
219
|
+
function collectIds(nodes) {
|
|
220
|
+
for (const n of nodes) {
|
|
221
|
+
allIds.add(n.id)
|
|
222
|
+
if (Array.isArray(n.children)) collectIds(n.children)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
collectIds(tree)
|
|
226
|
+
const flat = flattenCommandTree(tree, allIds)
|
|
227
|
+
return flat.map((entry) => {
|
|
228
|
+
// 📖 Copy tier and providerKey properties to tierValue for backward compatibility
|
|
229
|
+
const result = { ...entry }
|
|
230
|
+
if (entry.tier !== undefined) {
|
|
231
|
+
result.tierValue = entry.tier
|
|
232
|
+
}
|
|
233
|
+
return result
|
|
234
|
+
})
|
|
79
235
|
}
|
|
80
236
|
|
|
81
237
|
/**
|
|
@@ -126,15 +282,20 @@ export function fuzzyMatchCommand(query, text) {
|
|
|
126
282
|
|
|
127
283
|
/**
|
|
128
284
|
* 📖 Filter and rank command palette entries by fuzzy score.
|
|
129
|
-
*
|
|
285
|
+
* Now handles hierarchical structure with expandable categories.
|
|
286
|
+
* @param {Array} flatEntries - Flattened command tree entries
|
|
130
287
|
* @param {string} query
|
|
131
|
-
* @returns {Array
|
|
288
|
+
* @returns {Array} Sorted and filtered entries with match scores
|
|
132
289
|
*/
|
|
133
|
-
export function filterCommandPaletteEntries(
|
|
290
|
+
export function filterCommandPaletteEntries(flatEntries, query) {
|
|
134
291
|
const normalizedQuery = (query || '').trim()
|
|
292
|
+
|
|
293
|
+
if (!normalizedQuery) {
|
|
294
|
+
return flatEntries
|
|
295
|
+
}
|
|
135
296
|
|
|
136
297
|
const ranked = []
|
|
137
|
-
for (const entry of
|
|
298
|
+
for (const entry of flatEntries) {
|
|
138
299
|
const labelMatch = fuzzyMatchCommand(normalizedQuery, entry.label)
|
|
139
300
|
let bestScore = labelMatch.score
|
|
140
301
|
let matchPositions = labelMatch.positions
|
|
@@ -145,7 +306,6 @@ export function filterCommandPaletteEntries(entries, query) {
|
|
|
145
306
|
const keywordMatch = fuzzyMatchCommand(normalizedQuery, keyword)
|
|
146
307
|
if (!keywordMatch.matched) continue
|
|
147
308
|
matched = true
|
|
148
|
-
// 📖 Keyword matches should rank below direct label matches.
|
|
149
309
|
const keywordScore = Math.max(1, keywordMatch.score - 7)
|
|
150
310
|
if (keywordScore > bestScore) {
|
|
151
311
|
bestScore = keywordScore
|
|
@@ -158,11 +318,24 @@ export function filterCommandPaletteEntries(entries, query) {
|
|
|
158
318
|
ranked.push({ ...entry, score: bestScore, matchPositions })
|
|
159
319
|
}
|
|
160
320
|
|
|
321
|
+
// Auto-expand categories that contain matches
|
|
322
|
+
const result = []
|
|
323
|
+
const idsToExpand = new Set()
|
|
324
|
+
|
|
325
|
+
// First pass: mark all categories containing matched items
|
|
326
|
+
for (const entry of ranked) {
|
|
327
|
+
if (entry.type === 'command' && entry.matchPositions) {
|
|
328
|
+
// Find parent categories
|
|
329
|
+
let current = result.find(r => r.id === entry.id)
|
|
330
|
+
if (current) {
|
|
331
|
+
idsToExpand.add(entry.parentId)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
161
336
|
ranked.sort((a, b) => {
|
|
162
337
|
if (b.score !== a.score) return b.score - a.score
|
|
163
|
-
|
|
164
|
-
const bCat = COMMAND_CATEGORY_ORDER.indexOf(b.category)
|
|
165
|
-
if (aCat !== bCat) return aCat - bCat
|
|
338
|
+
if (a.depth !== b.depth) return a.depth - b.depth
|
|
166
339
|
return a.label.localeCompare(b.label)
|
|
167
340
|
})
|
|
168
341
|
|
package/src/config.js
CHANGED
|
@@ -209,7 +209,6 @@ function normalizeSettingsSection(settings) {
|
|
|
209
209
|
return {
|
|
210
210
|
...safeSettings,
|
|
211
211
|
hideUnconfiguredModels: typeof safeSettings.hideUnconfiguredModels === 'boolean' ? safeSettings.hideUnconfiguredModels : true,
|
|
212
|
-
disableWidthsWarning: safeSettings.disableWidthsWarning === true,
|
|
213
212
|
theme: ['dark', 'light', 'auto'].includes(safeSettings.theme) ? safeSettings.theme : 'auto',
|
|
214
213
|
}
|
|
215
214
|
}
|
|
@@ -230,7 +229,6 @@ function normalizeProfileSettings(settings) {
|
|
|
230
229
|
return {
|
|
231
230
|
..._emptyProfileSettings(),
|
|
232
231
|
...safeSettings,
|
|
233
|
-
disableWidthsWarning: safeSettings.disableWidthsWarning === true,
|
|
234
232
|
theme: ['dark', 'light', 'auto'].includes(safeSettings.theme) ? safeSettings.theme : 'auto',
|
|
235
233
|
}
|
|
236
234
|
}
|
|
@@ -843,7 +841,6 @@ export function _emptyProfileSettings() {
|
|
|
843
841
|
pingInterval: 10000, // 📖 default ms between pings in the steady "normal" mode
|
|
844
842
|
hideUnconfiguredModels: true, // 📖 true = default to providers that are actually configured
|
|
845
843
|
preferredToolMode: 'opencode', // 📖 remember the last Z-selected launcher across app restarts
|
|
846
|
-
disableWidthsWarning: false, // 📖 Disable widths warning (default off)
|
|
847
844
|
theme: 'auto', // 📖 'auto' follows the terminal/OS theme, override with 'dark' or 'light' if needed
|
|
848
845
|
}
|
|
849
846
|
}
|
package/src/constants.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* - `CELL_W` is derived from `COL_MS` and used by `msCell` / `spinCell`.
|
|
18
18
|
* - `TABLE_HEADER_LINES` + `TABLE_FOOTER_LINES` = `TABLE_FIXED_LINES` must stay in sync
|
|
19
19
|
* with the actual number of lines rendered by `renderTable()` in bin/.
|
|
20
|
+
* - `WIDTH_WARNING_MIN_COLS` controls when the narrow-terminal startup warning appears.
|
|
20
21
|
* - Overlay background colours (chalk.bgRgb) make each overlay panel visually distinct.
|
|
21
22
|
*
|
|
22
23
|
* @functions
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
* FRAMES, TIER_CYCLE,
|
|
31
32
|
* SETTINGS_OVERLAY_BG, HELP_OVERLAY_BG, RECOMMEND_OVERLAY_BG, LOG_OVERLAY_BG,
|
|
32
33
|
* OVERLAY_PANEL_WIDTH,
|
|
34
|
+
* WIDTH_WARNING_MIN_COLS,
|
|
33
35
|
* TABLE_HEADER_LINES, TABLE_FOOTER_LINES, TABLE_FIXED_LINES,
|
|
34
36
|
* msCell, spinCell
|
|
35
37
|
*
|
|
@@ -83,6 +85,9 @@ export const LOG_OVERLAY_BG = chalk.bgRgb(0, 0, 0) // 📖 Dark blue-gree
|
|
|
83
85
|
// 📖 tint fills the panel consistently regardless of content length.
|
|
84
86
|
export const OVERLAY_PANEL_WIDTH = 116
|
|
85
87
|
|
|
88
|
+
// 📖 Narrow-terminal warning appears only below this width.
|
|
89
|
+
export const WIDTH_WARNING_MIN_COLS = 80
|
|
90
|
+
|
|
86
91
|
// 📖 Table row-budget constants — must stay in sync with renderTable()'s actual output.
|
|
87
92
|
// 📖 If this drifts, model rows overflow and can push the title row out of view.
|
|
88
93
|
export const TABLE_HEADER_LINES = 4 // 📖 title, spacer, column headers, separator
|