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/key-handler.js
CHANGED
|
@@ -31,7 +31,8 @@ import { loadChangelog } from './changelog-loader.js'
|
|
|
31
31
|
import { loadConfig, replaceConfigContents } from './config.js'
|
|
32
32
|
import { cleanupLegacyProxyArtifacts } from './legacy-proxy-cleanup.js'
|
|
33
33
|
import { cycleThemeSetting, detectActiveTheme } from './theme.js'
|
|
34
|
-
import {
|
|
34
|
+
import { buildCommandPaletteTree, flattenCommandTree, filterCommandPaletteEntries } from './command-palette.js'
|
|
35
|
+
import { WIDTH_WARNING_MIN_COLS } from './constants.js'
|
|
35
36
|
|
|
36
37
|
// π Some providers need an explicit probe model because the first catalog entry
|
|
37
38
|
// π is not guaranteed to be accepted by their chat endpoint.
|
|
@@ -447,38 +448,6 @@ export function createKeyHandler(ctx) {
|
|
|
447
448
|
}
|
|
448
449
|
}
|
|
449
450
|
|
|
450
|
-
// π Keep the width-warning runtime state synced with the persisted Settings toggle
|
|
451
|
-
// π so the overlay reacts immediately when the user enables or disables it.
|
|
452
|
-
function syncWidthsWarningState() {
|
|
453
|
-
const widthsWarningDisabled = state.config.settings?.disableWidthsWarning === true
|
|
454
|
-
state.disableWidthsWarning = widthsWarningDisabled
|
|
455
|
-
|
|
456
|
-
if (widthsWarningDisabled) {
|
|
457
|
-
state.widthWarningStartedAt = null
|
|
458
|
-
state.widthWarningDismissed = false
|
|
459
|
-
return
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
state.widthWarningShowCount = 0
|
|
463
|
-
if ((state.terminalCols || 80) < 166) {
|
|
464
|
-
state.widthWarningStartedAt = Date.now()
|
|
465
|
-
state.widthWarningDismissed = false
|
|
466
|
-
return
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
state.widthWarningStartedAt = null
|
|
470
|
-
state.widthWarningDismissed = false
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// π Toggle the width-warning setting and apply the effect immediately instead
|
|
474
|
-
// π of waiting for a resize or restart.
|
|
475
|
-
function toggleWidthsWarningSetting() {
|
|
476
|
-
if (!state.config.settings) state.config.settings = {}
|
|
477
|
-
state.config.settings.disableWidthsWarning = !state.config.settings.disableWidthsWarning
|
|
478
|
-
syncWidthsWarningState()
|
|
479
|
-
saveConfig(state.config)
|
|
480
|
-
}
|
|
481
|
-
|
|
482
451
|
// π Theme switches need to update both persisted preference and the live
|
|
483
452
|
// π semantic palette immediately so every screen redraw adopts the new colors.
|
|
484
453
|
function applyThemeSetting(nextTheme) {
|
|
@@ -640,6 +609,7 @@ export function createKeyHandler(ctx) {
|
|
|
640
609
|
function resetViewSettings() {
|
|
641
610
|
state.tierFilterMode = 0
|
|
642
611
|
state.originFilterMode = 0
|
|
612
|
+
state.customTextFilter = null // π Clear ephemeral text filter on view reset
|
|
643
613
|
state.sortColumn = 'avg'
|
|
644
614
|
state.sortDirection = 'asc'
|
|
645
615
|
if (!state.config.settings || typeof state.config.settings !== 'object') state.config.settings = {}
|
|
@@ -684,8 +654,33 @@ export function createKeyHandler(ctx) {
|
|
|
684
654
|
}
|
|
685
655
|
|
|
686
656
|
function refreshCommandPaletteResults() {
|
|
687
|
-
const
|
|
688
|
-
|
|
657
|
+
const tree = buildCommandPaletteTree(state.results || [])
|
|
658
|
+
const flat = flattenCommandTree(tree, state.commandPaletteExpandedIds)
|
|
659
|
+
state.commandPaletteResults = filterCommandPaletteEntries(flat, state.commandPaletteQuery)
|
|
660
|
+
|
|
661
|
+
const query = (state.commandPaletteQuery || '').trim()
|
|
662
|
+
if (query.length > 0) {
|
|
663
|
+
state.commandPaletteResults.unshift({
|
|
664
|
+
id: 'filter-custom-text-apply',
|
|
665
|
+
label: `π Apply text filter: ${query}`,
|
|
666
|
+
type: 'command',
|
|
667
|
+
depth: 0,
|
|
668
|
+
hasChildren: false,
|
|
669
|
+
isExpanded: false,
|
|
670
|
+
filterQuery: query,
|
|
671
|
+
})
|
|
672
|
+
} else if (state.customTextFilter) {
|
|
673
|
+
state.commandPaletteResults.unshift({
|
|
674
|
+
id: 'filter-custom-text-remove',
|
|
675
|
+
label: `β Remove custom filter: ${state.customTextFilter}`,
|
|
676
|
+
type: 'command',
|
|
677
|
+
depth: 0,
|
|
678
|
+
hasChildren: false,
|
|
679
|
+
isExpanded: false,
|
|
680
|
+
filterQuery: null,
|
|
681
|
+
})
|
|
682
|
+
}
|
|
683
|
+
|
|
689
684
|
if (state.commandPaletteCursor >= state.commandPaletteResults.length) {
|
|
690
685
|
state.commandPaletteCursor = Math.max(0, state.commandPaletteResults.length - 1)
|
|
691
686
|
}
|
|
@@ -713,7 +708,43 @@ export function createKeyHandler(ctx) {
|
|
|
713
708
|
if (!entry?.id) return
|
|
714
709
|
|
|
715
710
|
if (entry.id.startsWith('filter-tier-')) {
|
|
716
|
-
setTierFilterFromCommand(entry.
|
|
711
|
+
setTierFilterFromCommand(entry.tier ?? null)
|
|
712
|
+
return
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (entry.id.startsWith('filter-provider-') && entry.id !== 'filter-provider-cycle') {
|
|
716
|
+
if (entry.providerKey === null || entry.providerKey === undefined) {
|
|
717
|
+
state.originFilterMode = 0 // All
|
|
718
|
+
} else {
|
|
719
|
+
state.originFilterMode = ORIGIN_CYCLE.findIndex(key => key === entry.providerKey) + 1
|
|
720
|
+
if (state.originFilterMode <= 0) state.originFilterMode = 0
|
|
721
|
+
}
|
|
722
|
+
applyTierFilter()
|
|
723
|
+
refreshVisibleSorted({ resetCursor: true })
|
|
724
|
+
persistUiSettings()
|
|
725
|
+
return
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (entry.id.startsWith('filter-model-')) {
|
|
729
|
+
if (entry.modelId && entry.providerKey) {
|
|
730
|
+
state.customTextFilter = `${entry.providerKey}/${entry.modelId}`
|
|
731
|
+
applyTierFilter()
|
|
732
|
+
refreshVisibleSorted({ resetCursor: true })
|
|
733
|
+
}
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// π Custom text filter β apply or remove the free-text filter from the command palette.
|
|
738
|
+
if (entry.id === 'filter-custom-text-apply') {
|
|
739
|
+
state.customTextFilter = entry.filterQuery || null
|
|
740
|
+
applyTierFilter()
|
|
741
|
+
refreshVisibleSorted({ resetCursor: true })
|
|
742
|
+
return
|
|
743
|
+
}
|
|
744
|
+
if (entry.id === 'filter-custom-text-remove') {
|
|
745
|
+
state.customTextFilter = null
|
|
746
|
+
applyTierFilter()
|
|
747
|
+
refreshVisibleSorted({ resetCursor: true })
|
|
717
748
|
return
|
|
718
749
|
}
|
|
719
750
|
|
|
@@ -789,6 +820,7 @@ export function createKeyHandler(ctx) {
|
|
|
789
820
|
if (key.ctrl && key.name === 'c') { exit(0); return }
|
|
790
821
|
|
|
791
822
|
const pageStep = Math.max(1, (state.terminalRows || 1) - 10)
|
|
823
|
+
const selected = state.commandPaletteResults[state.commandPaletteCursor]
|
|
792
824
|
|
|
793
825
|
if (key.name === 'escape') {
|
|
794
826
|
closeCommandPalette()
|
|
@@ -806,6 +838,23 @@ export function createKeyHandler(ctx) {
|
|
|
806
838
|
state.commandPaletteCursor = state.commandPaletteCursor < count - 1 ? state.commandPaletteCursor + 1 : 0
|
|
807
839
|
return
|
|
808
840
|
}
|
|
841
|
+
if (key.name === 'left') {
|
|
842
|
+
if (selected?.hasChildren && selected.isExpanded) {
|
|
843
|
+
state.commandPaletteExpandedIds.delete(selected.id)
|
|
844
|
+
refreshCommandPaletteResults()
|
|
845
|
+
}
|
|
846
|
+
return
|
|
847
|
+
}
|
|
848
|
+
if (key.name === 'right') {
|
|
849
|
+
if (selected?.hasChildren && !selected.isExpanded) {
|
|
850
|
+
state.commandPaletteExpandedIds.add(selected.id)
|
|
851
|
+
refreshCommandPaletteResults()
|
|
852
|
+
} else if (selected?.type === 'command') {
|
|
853
|
+
closeCommandPalette()
|
|
854
|
+
executeCommandPaletteEntry(selected)
|
|
855
|
+
}
|
|
856
|
+
return
|
|
857
|
+
}
|
|
809
858
|
if (key.name === 'pageup') {
|
|
810
859
|
state.commandPaletteCursor = Math.max(0, state.commandPaletteCursor - pageStep)
|
|
811
860
|
return
|
|
@@ -831,9 +880,17 @@ export function createKeyHandler(ctx) {
|
|
|
831
880
|
return
|
|
832
881
|
}
|
|
833
882
|
if (key.name === 'return') {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
883
|
+
if (selected?.hasChildren) {
|
|
884
|
+
if (selected.isExpanded) {
|
|
885
|
+
state.commandPaletteExpandedIds.delete(selected.id)
|
|
886
|
+
} else {
|
|
887
|
+
state.commandPaletteExpandedIds.add(selected.id)
|
|
888
|
+
}
|
|
889
|
+
refreshCommandPaletteResults()
|
|
890
|
+
} else {
|
|
891
|
+
closeCommandPalette()
|
|
892
|
+
executeCommandPaletteEntry(selected)
|
|
893
|
+
}
|
|
837
894
|
return
|
|
838
895
|
}
|
|
839
896
|
if (str && str.length === 1 && !key.ctrl && !key.meta) {
|
|
@@ -1384,8 +1441,7 @@ export function createKeyHandler(ctx) {
|
|
|
1384
1441
|
if (state.settingsOpen) {
|
|
1385
1442
|
const providerKeys = Object.keys(sources)
|
|
1386
1443
|
const updateRowIdx = providerKeys.length
|
|
1387
|
-
const
|
|
1388
|
-
const themeRowIdx = widthWarningRowIdx + 1
|
|
1444
|
+
const themeRowIdx = updateRowIdx + 1
|
|
1389
1445
|
const cleanupLegacyProxyRowIdx = themeRowIdx + 1
|
|
1390
1446
|
const changelogViewRowIdx = cleanupLegacyProxyRowIdx + 1
|
|
1391
1447
|
// π Profile system removed - API keys now persist permanently across all sessions
|
|
@@ -1530,12 +1586,6 @@ export function createKeyHandler(ctx) {
|
|
|
1530
1586
|
return
|
|
1531
1587
|
}
|
|
1532
1588
|
|
|
1533
|
-
// π Widths Warning toggle (Enter to toggle)
|
|
1534
|
-
if (state.settingsCursor === widthWarningRowIdx) {
|
|
1535
|
-
toggleWidthsWarningSetting()
|
|
1536
|
-
return
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
1589
|
if (state.settingsCursor === themeRowIdx) {
|
|
1540
1590
|
cycleGlobalTheme()
|
|
1541
1591
|
return
|
|
@@ -1579,11 +1629,6 @@ export function createKeyHandler(ctx) {
|
|
|
1579
1629
|
cycleGlobalTheme()
|
|
1580
1630
|
return
|
|
1581
1631
|
}
|
|
1582
|
-
// π Widths Warning toggle (disable/enable)
|
|
1583
|
-
if (state.settingsCursor === widthWarningRowIdx) {
|
|
1584
|
-
toggleWidthsWarningSetting()
|
|
1585
|
-
return
|
|
1586
|
-
}
|
|
1587
1632
|
// π Profile system removed - API keys now persist permanently across all sessions
|
|
1588
1633
|
|
|
1589
1634
|
// π Toggle enabled/disabled for selected provider
|
|
@@ -1598,7 +1643,6 @@ export function createKeyHandler(ctx) {
|
|
|
1598
1643
|
if (key.name === 't') {
|
|
1599
1644
|
if (
|
|
1600
1645
|
state.settingsCursor === updateRowIdx
|
|
1601
|
-
|| state.settingsCursor === widthWarningRowIdx
|
|
1602
1646
|
|| state.settingsCursor === themeRowIdx
|
|
1603
1647
|
|| state.settingsCursor === cleanupLegacyProxyRowIdx
|
|
1604
1648
|
|| state.settingsCursor === changelogViewRowIdx
|
|
@@ -1663,11 +1707,7 @@ export function createKeyHandler(ctx) {
|
|
|
1663
1707
|
return
|
|
1664
1708
|
}
|
|
1665
1709
|
|
|
1666
|
-
// π Y key
|
|
1667
|
-
if (key.name === 'y') {
|
|
1668
|
-
openInstallEndpointsOverlay()
|
|
1669
|
-
return
|
|
1670
|
-
}
|
|
1710
|
+
// π Y key freed β Install Endpoints is now accessible only via Settings (P) or Command Palette (Ctrl+P).
|
|
1671
1711
|
|
|
1672
1712
|
// π Profile system removed - API keys now persist permanently across all sessions
|
|
1673
1713
|
|
|
@@ -1680,7 +1720,7 @@ export function createKeyHandler(ctx) {
|
|
|
1680
1720
|
}
|
|
1681
1721
|
|
|
1682
1722
|
// π Sorting keys: R=rank, O=origin, M=model, L=latest ping, A=avg ping, S=SWE-bench, C=context, H=health, V=verdict, B=stability, U=uptime, G=usage
|
|
1683
|
-
// π T is reserved for tier filter cycling. Y now
|
|
1723
|
+
// π T is reserved for tier filter cycling. Y is now free (Install Endpoints moved to Settings/Palette).
|
|
1684
1724
|
// π D is now reserved for provider filter cycling
|
|
1685
1725
|
// π Shift+R is reserved for reset view settings
|
|
1686
1726
|
const sortKeys = {
|
|
@@ -1805,7 +1845,7 @@ export function createKeyHandler(ctx) {
|
|
|
1805
1845
|
}
|
|
1806
1846
|
|
|
1807
1847
|
// π Esc can dismiss the narrow-terminal warning immediately without quitting the app.
|
|
1808
|
-
if (key.name === 'escape' && state.terminalCols > 0 && state.terminalCols <
|
|
1848
|
+
if (key.name === 'escape' && state.terminalCols > 0 && state.terminalCols < WIDTH_WARNING_MIN_COLS) {
|
|
1809
1849
|
state.widthWarningDismissed = true
|
|
1810
1850
|
return
|
|
1811
1851
|
}
|
package/src/overlays.js
CHANGED
|
@@ -93,8 +93,7 @@ export function createOverlayRenderers(state, deps) {
|
|
|
93
93
|
function renderSettings() {
|
|
94
94
|
const providerKeys = Object.keys(sources)
|
|
95
95
|
const updateRowIdx = providerKeys.length
|
|
96
|
-
const
|
|
97
|
-
const themeRowIdx = widthWarningRowIdx + 1
|
|
96
|
+
const themeRowIdx = updateRowIdx + 1
|
|
98
97
|
const cleanupLegacyProxyRowIdx = themeRowIdx + 1
|
|
99
98
|
const changelogViewRowIdx = cleanupLegacyProxyRowIdx + 1
|
|
100
99
|
const EL = '\x1b[K'
|
|
@@ -220,14 +219,6 @@ export function createOverlayRenderers(state, deps) {
|
|
|
220
219
|
const updateRow = `${bullet(updateCursor)}${themeColors.textBold(updateActionLabel).padEnd(44)} ${updateStatus}`
|
|
221
220
|
cursorLineByRow[updateRowIdx] = lines.length
|
|
222
221
|
lines.push(updateCursor ? themeColors.bgCursor(updateRow) : updateRow)
|
|
223
|
-
// π Width warning visibility row for the startup narrow-terminal overlay.
|
|
224
|
-
const disableWidthsWarning = Boolean(state.config.settings?.disableWidthsWarning)
|
|
225
|
-
const widthWarningStatus = disableWidthsWarning
|
|
226
|
-
? themeColors.errorBold('π Disabled')
|
|
227
|
-
: themeColors.successBold('π Enabled')
|
|
228
|
-
const widthWarningRow = `${bullet(state.settingsCursor === widthWarningRowIdx)}${themeColors.textBold('Small Width Warnings').padEnd(44)} ${widthWarningStatus}`
|
|
229
|
-
cursorLineByRow[widthWarningRowIdx] = lines.length
|
|
230
|
-
lines.push(state.settingsCursor === widthWarningRowIdx ? themeColors.bgCursor(widthWarningRow) : widthWarningRow)
|
|
231
222
|
const themeStatus = getThemeStatusLabel(activeThemeSetting())
|
|
232
223
|
const themeStatusColor = themeStatus.includes('Dark') ? themeColors.warningBold : themeColors.info
|
|
233
224
|
const themeRow = `${bullet(state.settingsCursor === themeRowIdx)}${themeColors.textBold('Global Theme').padEnd(44)} ${themeStatusColor(themeStatus)}`
|
|
@@ -537,18 +528,16 @@ export function createOverlayRenderers(state, deps) {
|
|
|
537
528
|
|
|
538
529
|
// βββ Command palette renderer ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
539
530
|
// π renderCommandPalette draws a centered floating modal over the live table.
|
|
540
|
-
// π
|
|
541
|
-
// π so ping updates continue to animate in the background behind the palette.
|
|
531
|
+
// π Supports hierarchical categories with expand/collapse and rich colors.
|
|
542
532
|
function renderCommandPalette() {
|
|
543
533
|
const terminalRows = state.terminalRows || 24
|
|
544
534
|
const terminalCols = state.terminalCols || 80
|
|
545
|
-
const panelWidth = Math.max(
|
|
546
|
-
const panelInnerWidth = Math.max(
|
|
535
|
+
const panelWidth = Math.max(52, Math.min(100, terminalCols - 8))
|
|
536
|
+
const panelInnerWidth = Math.max(32, panelWidth - 4)
|
|
547
537
|
const panelPad = 2
|
|
548
538
|
const panelOuterWidth = panelWidth + (panelPad * 2)
|
|
549
|
-
const
|
|
550
|
-
const
|
|
551
|
-
const bodyRows = Math.max(6, Math.min(16, terminalRows - 12))
|
|
539
|
+
const headerRowCount = 4
|
|
540
|
+
const bodyRows = Math.max(8, Math.min(18, terminalRows - 12))
|
|
552
541
|
|
|
553
542
|
const truncatePlain = (text, width) => {
|
|
554
543
|
if (width <= 1) return ''
|
|
@@ -568,30 +557,70 @@ export function createOverlayRenderers(state, deps) {
|
|
|
568
557
|
}
|
|
569
558
|
|
|
570
559
|
const allResults = Array.isArray(state.commandPaletteResults) ? state.commandPaletteResults.slice(0, 80) : []
|
|
571
|
-
const
|
|
560
|
+
const panelLines = []
|
|
572
561
|
const cursorLineByRow = {}
|
|
573
|
-
let category = null
|
|
574
562
|
|
|
575
563
|
if (allResults.length === 0) {
|
|
576
|
-
|
|
564
|
+
panelLines.push(themeColors.dim(' No commands found. Try a different search.'))
|
|
577
565
|
} else {
|
|
578
566
|
for (let idx = 0; idx < allResults.length; idx++) {
|
|
579
567
|
const entry = allResults[idx]
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
568
|
+
const isCursor = idx === state.commandPaletteCursor
|
|
569
|
+
|
|
570
|
+
const indent = ' '.repeat(entry.depth || 0)
|
|
571
|
+
const expandIndicator = entry.hasChildren
|
|
572
|
+
? (entry.isExpanded ? themeColors.infoBold('βΌ') : themeColors.dim('βΆ'))
|
|
573
|
+
: themeColors.dim('β’')
|
|
574
|
+
|
|
575
|
+
// π Only use icon from entry, label should NOT include emoji
|
|
576
|
+
const iconPrefix = entry.icon ? `${entry.icon} ` : ''
|
|
577
|
+
const plainLabel = truncatePlain(entry.label, panelInnerWidth - indent.length - iconPrefix.length - 4)
|
|
578
|
+
const label = entry.matchPositions ? highlightMatch(plainLabel, entry.matchPositions) : plainLabel
|
|
579
|
+
|
|
580
|
+
let rowLine
|
|
581
|
+
if (entry.type === 'category') {
|
|
582
|
+
rowLine = `${indent}${expandIndicator} ${iconPrefix}${themeColors.headerBold(label)}`
|
|
583
|
+
} else if (entry.type === 'subcategory') {
|
|
584
|
+
rowLine = `${indent}${expandIndicator} ${iconPrefix}${themeColors.textBold(label)}`
|
|
585
|
+
} else if (entry.type === 'page') {
|
|
586
|
+
// π Pages are at root level with icon + label + shortcut + description
|
|
587
|
+
const shortcut = entry.shortcut ? themeColors.dim(` (${entry.shortcut})`) : ''
|
|
588
|
+
const description = entry.description ? themeColors.dim(` β ${entry.description}`) : ''
|
|
589
|
+
rowLine = `${expandIndicator} ${iconPrefix}${themeColors.textBold(label)}${shortcut}${description}`
|
|
590
|
+
} else if (entry.type === 'action') {
|
|
591
|
+
// π Actions are at root level with icon + label + shortcut + description
|
|
592
|
+
const shortcut = entry.shortcut ? themeColors.dim(` (${entry.shortcut})`) : ''
|
|
593
|
+
const description = entry.description ? themeColors.dim(` β ${entry.description}`) : ''
|
|
594
|
+
rowLine = `${expandIndicator} ${iconPrefix}${themeColors.textBold(label)}${shortcut}${description}`
|
|
595
|
+
} else {
|
|
596
|
+
// π Regular commands in submenus
|
|
597
|
+
const shortcut = entry.shortcut ? themeColors.dim(` (${entry.shortcut})`) : ''
|
|
598
|
+
const description = entry.description ? themeColors.dim(` β ${entry.description}`) : ''
|
|
599
|
+
// π Color tiers and providers
|
|
600
|
+
let coloredLabel = label
|
|
601
|
+
let prefixWithIcon = iconPrefix
|
|
602
|
+
|
|
603
|
+
if (entry.providerKey && !entry.icon) {
|
|
604
|
+
// π Model filter: add provider icon
|
|
605
|
+
const providerIcon = 'π’'
|
|
606
|
+
prefixWithIcon = `${providerIcon} `
|
|
607
|
+
coloredLabel = themeColors.provider(entry.providerKey, label, { bold: false })
|
|
608
|
+
} else if (entry.tier) {
|
|
609
|
+
coloredLabel = themeColors.tier(entry.tier, label)
|
|
610
|
+
} else if (entry.providerKey) {
|
|
611
|
+
coloredLabel = themeColors.provider(entry.providerKey, label, { bold: false })
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
rowLine = `${indent} ${expandIndicator} ${prefixWithIcon}${coloredLabel}${shortcut}${description}`
|
|
583
615
|
}
|
|
584
616
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const row = `${pointer}${padEndDisplay(label, labelMax)}${entry.shortcut ? ` ${shortcutText}` : ''}`
|
|
593
|
-
cursorLineByRow[idx] = groupedLines.length
|
|
594
|
-
groupedLines.push(isCursor ? themeColors.bgCursor(row) : row)
|
|
617
|
+
cursorLineByRow[idx] = panelLines.length
|
|
618
|
+
|
|
619
|
+
if (isCursor) {
|
|
620
|
+
panelLines.push(themeColors.bgCursor(rowLine))
|
|
621
|
+
} else {
|
|
622
|
+
panelLines.push(rowLine)
|
|
623
|
+
}
|
|
595
624
|
}
|
|
596
625
|
}
|
|
597
626
|
|
|
@@ -599,44 +628,43 @@ export function createOverlayRenderers(state, deps) {
|
|
|
599
628
|
state.commandPaletteScrollOffset = keepOverlayTargetVisible(
|
|
600
629
|
state.commandPaletteScrollOffset,
|
|
601
630
|
targetLine,
|
|
602
|
-
|
|
631
|
+
panelLines.length,
|
|
603
632
|
bodyRows
|
|
604
633
|
)
|
|
605
|
-
const { visible, offset } = sliceOverlayLines(
|
|
634
|
+
const { visible, offset } = sliceOverlayLines(panelLines, state.commandPaletteScrollOffset, bodyRows)
|
|
606
635
|
state.commandPaletteScrollOffset = offset
|
|
607
636
|
|
|
608
637
|
const query = state.commandPaletteQuery || ''
|
|
609
638
|
const queryWithCursor = query.length > 0
|
|
610
|
-
?
|
|
611
|
-
: themeColors.
|
|
639
|
+
? `${query}${themeColors.accentBold('β')}`
|
|
640
|
+
: themeColors.accentBold('β') + themeColors.dim(' Search commandsβ¦')
|
|
612
641
|
|
|
613
|
-
const
|
|
614
|
-
const title = themeColors.
|
|
642
|
+
const headerLines = []
|
|
643
|
+
const title = themeColors.headerBold('β‘οΈ Command Palette')
|
|
615
644
|
const titleLeft = ` ${title}`
|
|
616
|
-
const titleRight = themeColors.dim('Esc
|
|
617
|
-
const titleWidth = Math.max(1, panelInnerWidth - 1 - displayWidth('Esc
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
645
|
+
const titleRight = themeColors.dim('Esc')
|
|
646
|
+
const titleWidth = Math.max(1, panelInnerWidth - 1 - displayWidth('Esc'))
|
|
647
|
+
headerLines.push(`${padEndDisplay(titleLeft, titleWidth)} ${titleRight}`)
|
|
648
|
+
headerLines.push(` ${padEndDisplay(`> ${queryWithCursor}`, panelInnerWidth)}`)
|
|
649
|
+
headerLines.push(themeColors.dim(` ${'β'.repeat(Math.max(1, panelInnerWidth))}`))
|
|
650
|
+
|
|
651
|
+
const footerLines = [
|
|
652
|
+
themeColors.dim(` ${'β'.repeat(Math.max(1, panelInnerWidth))}`),
|
|
653
|
+
` ${padEndDisplay(themeColors.dim('β΅ Select β’ β β Expand'), panelInnerWidth)}`,
|
|
654
|
+
` ${padEndDisplay(themeColors.dim('ββ Navigate β’ Type search'), panelInnerWidth)}`,
|
|
655
|
+
]
|
|
625
656
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
657
|
+
const allPanelLines = [...headerLines, ...visible, ...footerLines]
|
|
658
|
+
|
|
659
|
+
while (allPanelLines.length < bodyRows + headerRowCount + 3) {
|
|
660
|
+
allPanelLines.splice(headerLines.length + visible.length, 0, ` ${' '.repeat(panelInnerWidth)}`)
|
|
629
661
|
}
|
|
630
662
|
|
|
631
|
-
panelLines.push(themeColors.dim(` ${'-'.repeat(Math.max(1, panelInnerWidth))}`))
|
|
632
|
-
panelLines.push(` ${padEndDisplay(themeColors.dim('ββ navigate β’ Enter run β’ Type to search'), panelInnerWidth)}`)
|
|
633
|
-
panelLines.push(` ${padEndDisplay(themeColors.dim('PgUp/PgDn β’ Home/End'), panelInnerWidth)}`)
|
|
634
|
-
|
|
635
663
|
const blankPaddedLine = ' '.repeat(panelOuterWidth)
|
|
636
664
|
const paddedPanelLines = [
|
|
637
665
|
blankPaddedLine,
|
|
638
666
|
blankPaddedLine,
|
|
639
|
-
...
|
|
667
|
+
...allPanelLines.map((line) => `${' '.repeat(panelPad)}${padEndDisplay(line, panelWidth)}${' '.repeat(panelPad)}`),
|
|
640
668
|
blankPaddedLine,
|
|
641
669
|
blankPaddedLine,
|
|
642
670
|
]
|
|
@@ -650,8 +678,6 @@ export function createOverlayRenderers(state, deps) {
|
|
|
650
678
|
return themeColors.overlayBgCommandPalette(padded)
|
|
651
679
|
})
|
|
652
680
|
|
|
653
|
-
// π Absolute cursor positioning overlays the palette on top of the existing table.
|
|
654
|
-
// π The next frame starts with ALT_HOME, so this remains stable without manual cleanup.
|
|
655
681
|
return tintedLines
|
|
656
682
|
.map((line, idx) => `\x1b[${top + idx};${left}H${line}`)
|
|
657
683
|
.join('')
|
|
@@ -725,11 +751,10 @@ export function createOverlayRenderers(state, deps) {
|
|
|
725
751
|
lines.push('')
|
|
726
752
|
lines.push(` ${heading('Controls')}`)
|
|
727
753
|
lines.push(` ${key('W')} Toggle ping mode ${hint('(speed 2s β normal 10s β slow 30s β forced 4s)')}`)
|
|
728
|
-
lines.push(` ${key('Ctrl+P')} Open command palette ${hint('(search and run actions quickly)')}`)
|
|
754
|
+
lines.push(` ${key('Ctrl+P')} Open β‘οΈ command palette ${hint('(search and run actions quickly)')}`)
|
|
729
755
|
lines.push(` ${key('E')} Toggle configured models only ${hint('(enabled by default)')}`)
|
|
730
756
|
lines.push(` ${key('Z')} Cycle tool mode ${hint('(OpenCode β Desktop β OpenClaw β Crush β Goose β Pi β Aider β Qwen β OpenHands β Amp)')}`)
|
|
731
757
|
lines.push(` ${key('F')} Toggle favorite on selected row ${hint('(β pinned at top, persisted)')}`)
|
|
732
|
-
lines.push(` ${key('Y')} Install endpoints ${hint('(provider catalog β compatible tools, direct provider only)')}`)
|
|
733
758
|
lines.push(` ${key('Q')} Smart Recommend ${hint('(π― find the best model for your task β questionnaire + live analysis)')}`)
|
|
734
759
|
lines.push(` ${key('G')} Cycle theme ${hint('(auto β dark β light)')}`)
|
|
735
760
|
lines.push(` ${themeColors.errorBold('I')} Feedback, bugs & requests ${hint('(π send anonymous feedback, bug reports, or feature requests)')}`)
|
package/src/product-flags.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file src/product-flags.js
|
|
3
|
-
* @description Product-level
|
|
3
|
+
* @description Product-level flags and feature gates.
|
|
4
4
|
*
|
|
5
5
|
* @details
|
|
6
|
-
* π
|
|
7
|
-
* π
|
|
8
|
-
* π
|
|
9
|
-
*
|
|
10
|
-
* @exports PROXY_DISABLED_NOTICE
|
|
6
|
+
* π Previously held PROXY_DISABLED_NOTICE for the proxy bridge rebuild.
|
|
7
|
+
* π That notice was removed in 0.3.22 after the proxy surface was fully
|
|
8
|
+
* π retired. File kept as a home for future product-level flags.
|
|
11
9
|
*/
|
|
12
|
-
|
|
13
|
-
// π Public note rendered in the main TUI footer and reused in CLI/runtime guards.
|
|
14
|
-
export const PROXY_DISABLED_NOTICE = 'βΉοΈ Proxy is temporarily disabled while we rebuild it into a much more stable bridge for external tools.'
|