From eb91279a3bca23047be11a6f568924cd5519c385 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sat, 7 Nov 2026 03:21:35 +0100 Subject: [PATCH] feat(local-model): a memory map and real colour in the chooser, the button first The owner's verdict on the chooser: nice, but not nice enough, too much just blue. He also wants "Run a local model" on top of the start screen, above Run Claude Code. The memory bar is now a map: the model, its context and the brief peaks side by side on the track of what this Mac can give a model, each with its size in a legend, and the verdict on the fit beside the figure. recommend() sends what the need is made of as `Candidate.memory`. Colour now carries meaning: green for good and tested, amber for tight, orange for slow, teal for the context. Violet is the feature's own colour, on its start screen button and its main action. Every row has a size tile and a speed gauge scaled to the fastest row in the list. The light skins get deeper values of the same colours, and the window is 40 px taller to hold it without scrolling. Co-Authored-By: Claude Opus 5.5 --- src/local-model/recommend.ts | 5 + src/types/local-model.ts | 6 + src/web/public/i18n.js | 9 +- src/web/public/index.html | 6 +- src/web/public/local-model-ui.js | 120 ++++++++--- src/web/public/styles.css | 308 +++++++++++++++++++++++------ test/local-model-recommend.test.ts | 2 + test/local-model-ui.test.ts | 87 ++++++-- 8 files changed, 432 insertions(+), 111 deletions(-) diff --git a/src/local-model/recommend.ts b/src/local-model/recommend.ts index 54a747f4..1d3c84cf 100644 --- a/src/local-model/recommend.ts +++ b/src/local-model/recommend.ts @@ -295,6 +295,11 @@ function buildCandidate( ...(harness.row.slowFirstTurn ? { slowFirstTurn: true } : {}), needBytes: need, needGb: formatGb(need), + memory: { + modelBytes: model.bytes + model.overheadBytes, + contextBytes: model.heldBytesPerToken * ctx, + peakBytes: model.peakExtraBytes, + }, downloadBytes: model.bytes, downloadGb: formatGb(model.bytes), budgetShare: round(need / budgetBytes, 3), diff --git a/src/types/local-model.ts b/src/types/local-model.ts index 5baa6903..16725a6e 100644 --- a/src/types/local-model.ts +++ b/src/types/local-model.ts @@ -481,6 +481,12 @@ export interface Candidate { needBytes: number; /** `needBytes` as decimal GB text, no unit. */ needGb: string; + /** + * What `needBytes` is made of, for the chooser's memory map: the model (`bytes + + * overheadBytes`), its context in use (`heldBytesPerToken * context`) and the brief peaks + * (`peakExtraBytes`). The three add up to `needBytes`. + */ + memory?: { modelBytes: number; contextBytes: number; peakBytes: number }; /** The model download in bytes (`LocalModelEntry.bytes`). */ downloadBytes: number; /** `downloadBytes` as decimal GB text, no unit. */ diff --git a/src/web/public/i18n.js b/src/web/public/i18n.js index ac8622f7..f1362f5c 100644 --- a/src/web/public/i18n.js +++ b/src/web/public/i18n.js @@ -1435,9 +1435,12 @@ estimated: '估算', 'The selected model needs {share}% of the memory this Mac can give a model': '所选模型需要此 Mac 可分配给模型的内存的 {share}%', - 'The model at its peak, with all {context} tokens of context in use': - '模型的峰值占用({context} 个 Token 的上下文全部用满时)', - 'What stays free': '剩余可用', + 'The model': '模型', + 'Context, {context} tokens': '上下文,{context} 个 Token', + 'Brief peaks': '短时峰值', + 'Stays free': '剩余可用', + 'Good fit': '宽裕', + 'Tight fit': '偏紧', 'Models that fit this Mac': '此 Mac 装得下的模型', Speed: '速度', Download: '下载', diff --git a/src/web/public/index.html b/src/web/public/index.html index c3c502c0..ddcc162f 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -510,10 +510,9 @@ it sits under them as a quiet link; settings-ui.js rewrites its contents on every state change and owns its inline display. -->
-