free-coding-models 0.1.82 → 0.1.84

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.
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @file favorites.js
3
+ * @description Favorites management for model rows — persisted per user in ~/.free-coding-models.json.
4
+ * Extracted from bin/free-coding-models.js to allow unit testing in isolation.
5
+ *
6
+ * @details
7
+ * Favorites are stored as an ordered array of strings in the format "providerKey/modelId"
8
+ * (e.g. "groq/llama-3.1-70b-versatile"). Insertion order matters: it determines the
9
+ * `favoriteRank` used by `sortResultsWithPinnedFavorites` to keep pinned rows at the top.
10
+ *
11
+ * How it works at runtime:
12
+ * 1. On startup, `syncFavoriteFlags()` is called once to attach `isFavorite`/`favoriteRank`
13
+ * metadata to every result row based on the persisted favorites list.
14
+ * 2. When the user presses F, `toggleFavoriteModel()` mutates the config array and persists
15
+ * immediately via `saveConfig()`.
16
+ * 3. The renderer reads `r.isFavorite` and `r.favoriteRank` from the row to decide whether
17
+ * to show the ⭐ prefix and how to sort the row relative to non-favorites.
18
+ *
19
+ * @functions
20
+ * → ensureFavoritesConfig(config) — Ensure config.favorites is a clean deduped array
21
+ * → toFavoriteKey(providerKey, modelId) — Build the canonical "providerKey/modelId" string
22
+ * → syncFavoriteFlags(results, config) — Attach isFavorite/favoriteRank to result rows
23
+ * → toggleFavoriteModel(config, providerKey, modelId) — Add/remove favorite and persist
24
+ *
25
+ * @exports
26
+ * ensureFavoritesConfig, toFavoriteKey, syncFavoriteFlags, toggleFavoriteModel
27
+ *
28
+ * @see src/config.js — saveConfig used here to persist changes immediately
29
+ * @see bin/free-coding-models.js — calls syncFavoriteFlags on startup and toggleFavoriteModel on F key
30
+ */
31
+
32
+ import { saveConfig } from './config.js'
33
+
34
+ /**
35
+ * 📖 Ensure favorites config shape exists and remains clean.
36
+ * 📖 Stored format: ["providerKey/modelId", ...] in insertion order.
37
+ * @param {Record<string, unknown>} config
38
+ */
39
+ export function ensureFavoritesConfig(config) {
40
+ if (!Array.isArray(config.favorites)) config.favorites = []
41
+ const seen = new Set()
42
+ config.favorites = config.favorites.filter((entry) => {
43
+ if (typeof entry !== 'string' || entry.trim().length === 0) return false
44
+ if (seen.has(entry)) return false
45
+ seen.add(entry)
46
+ return true
47
+ })
48
+ }
49
+
50
+ /**
51
+ * 📖 Build deterministic key used to persist one favorite model row.
52
+ * @param {string} providerKey
53
+ * @param {string} modelId
54
+ * @returns {string}
55
+ */
56
+ export function toFavoriteKey(providerKey, modelId) {
57
+ return `${providerKey}/${modelId}`
58
+ }
59
+
60
+ /**
61
+ * 📖 Sync per-row favorite metadata from config (used by renderer and sorter).
62
+ * 📖 Mutates each row in-place — adds favoriteKey, isFavorite, favoriteRank.
63
+ * @param {Array<Record<string, unknown>>} results
64
+ * @param {Record<string, unknown>} config
65
+ */
66
+ export function syncFavoriteFlags(results, config) {
67
+ ensureFavoritesConfig(config)
68
+ const favoriteRankMap = new Map(config.favorites.map((entry, index) => [entry, index]))
69
+ for (const row of results) {
70
+ const favoriteKey = toFavoriteKey(row.providerKey, row.modelId)
71
+ const rank = favoriteRankMap.get(favoriteKey)
72
+ row.favoriteKey = favoriteKey
73
+ row.isFavorite = rank !== undefined
74
+ row.favoriteRank = rank !== undefined ? rank : Number.MAX_SAFE_INTEGER
75
+ }
76
+ }
77
+
78
+ /**
79
+ * 📖 Toggle favorite state and persist immediately.
80
+ * 📖 Returns true when row is now favorite, false when removed.
81
+ * @param {Record<string, unknown>} config
82
+ * @param {string} providerKey
83
+ * @param {string} modelId
84
+ * @returns {boolean}
85
+ */
86
+ export function toggleFavoriteModel(config, providerKey, modelId) {
87
+ ensureFavoritesConfig(config)
88
+ const favoriteKey = toFavoriteKey(providerKey, modelId)
89
+ const existingIndex = config.favorites.indexOf(favoriteKey)
90
+ if (existingIndex >= 0) {
91
+ config.favorites.splice(existingIndex, 1)
92
+ saveConfig(config)
93
+ return false
94
+ }
95
+ config.favorites.push(favoriteKey)
96
+ saveConfig(config)
97
+ return true
98
+ }