feat: add whisper progress tracking and UI updates for download status

This commit is contained in:
Илья Глазунов 2026-02-16 19:55:39 +03:00
parent 526bc4e877
commit 0d56e06724
4 changed files with 147 additions and 0 deletions

View File

@ -382,6 +382,7 @@ export class CheatingDaddyApp extends LitElement {
_storageLoaded: { state: true }, _storageLoaded: { state: true },
_updateAvailable: { state: true }, _updateAvailable: { state: true },
_whisperDownloading: { state: true }, _whisperDownloading: { state: true },
_whisperProgress: { state: true },
}; };
constructor() { constructor() {
@ -407,6 +408,7 @@ export class CheatingDaddyApp extends LitElement {
this._timerInterval = null; this._timerInterval = null;
this._updateAvailable = false; this._updateAvailable = false;
this._whisperDownloading = false; this._whisperDownloading = false;
this._whisperProgress = null;
this._localVersion = ""; this._localVersion = "";
this._loadFromStorage(); this._loadFromStorage();
@ -485,6 +487,10 @@ export class CheatingDaddyApp extends LitElement {
); );
ipcRenderer.on("whisper-downloading", (_, downloading) => { ipcRenderer.on("whisper-downloading", (_, downloading) => {
this._whisperDownloading = downloading; this._whisperDownloading = downloading;
if (!downloading) this._whisperProgress = null;
});
ipcRenderer.on("whisper-progress", (_, progress) => {
this._whisperProgress = progress;
}); });
} }
} }
@ -500,6 +506,7 @@ export class CheatingDaddyApp extends LitElement {
ipcRenderer.removeAllListeners("click-through-toggled"); ipcRenderer.removeAllListeners("click-through-toggled");
ipcRenderer.removeAllListeners("reconnect-failed"); ipcRenderer.removeAllListeners("reconnect-failed");
ipcRenderer.removeAllListeners("whisper-downloading"); ipcRenderer.removeAllListeners("whisper-downloading");
ipcRenderer.removeAllListeners("whisper-progress");
} }
} }
@ -778,6 +785,7 @@ export class CheatingDaddyApp extends LitElement {
.onStart=${() => this.handleStart()} .onStart=${() => this.handleStart()}
.onExternalLink=${(url) => this.handleExternalLinkClick(url)} .onExternalLink=${(url) => this.handleExternalLinkClick(url)}
.whisperDownloading=${this._whisperDownloading} .whisperDownloading=${this._whisperDownloading}
.whisperProgress=${this._whisperProgress}
></main-view> ></main-view>
`; `;

View File

@ -151,6 +151,63 @@ export class MainView extends LitElement {
} }
} }
/* ── Whisper download progress ── */
.whisper-progress-container {
margin-top: 8px;
padding: 8px 10px;
background: var(--bg-elevated, rgba(255, 255, 255, 0.05));
border-radius: var(--radius-sm, 6px);
border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
}
.whisper-progress-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
font-size: 11px;
color: var(--text-secondary, #999);
}
.whisper-progress-file {
font-family: var(--font-mono, monospace);
font-size: 10px;
color: var(--text-secondary, #999);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.whisper-progress-pct {
font-variant-numeric: tabular-nums;
font-weight: 600;
color: var(--accent, #6cb4ee);
}
.whisper-progress-track {
height: 4px;
background: var(--border, rgba(255, 255, 255, 0.1));
border-radius: 2px;
overflow: hidden;
}
.whisper-progress-bar {
height: 100%;
background: var(--accent, #6cb4ee);
border-radius: 2px;
transition: width 0.3s ease;
min-width: 0;
}
.whisper-progress-size {
margin-top: 4px;
font-size: 10px;
color: var(--text-tertiary, #666);
text-align: right;
}
/* ── Start button ── */ /* ── Start button ── */
.start-button { .start-button {
@ -422,6 +479,7 @@ export class MainView extends LitElement {
onProfileChange: { type: Function }, onProfileChange: { type: Function },
isInitializing: { type: Boolean }, isInitializing: { type: Boolean },
whisperDownloading: { type: Boolean }, whisperDownloading: { type: Boolean },
whisperProgress: { type: Object },
// Internal state // Internal state
_mode: { state: true }, _mode: { state: true },
_token: { state: true }, _token: { state: true },
@ -453,6 +511,7 @@ export class MainView extends LitElement {
this.onProfileChange = () => {}; this.onProfileChange = () => {};
this.isInitializing = false; this.isInitializing = false;
this.whisperDownloading = false; this.whisperDownloading = false;
this.whisperProgress = null;
this._mode = "byok"; this._mode = "byok";
this._token = ""; this._token = "";
@ -893,6 +952,40 @@ export class MainView extends LitElement {
this.requestUpdate(); this.requestUpdate();
} }
_formatBytes(bytes) {
if (!bytes || bytes === 0) return "0 B";
const units = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, i)).toFixed(i > 1 ? 1 : 0) + " " + units[i];
}
_renderWhisperProgress() {
const p = this.whisperProgress;
if (!p) return "";
const pct = Math.round(p.progress || 0);
const fileName = p.file ? p.file.split("/").pop() : "";
return html`
<div class="whisper-progress-container">
<div class="whisper-progress-header">
<span class="whisper-progress-file" title=${p.file || ""}
>${fileName || "Preparing..."}</span
>
<span class="whisper-progress-pct">${pct}%</span>
</div>
<div class="whisper-progress-track">
<div class="whisper-progress-bar" style="width: ${pct}%"></div>
</div>
${p.total
? html`<div class="whisper-progress-size">
${this._formatBytes(p.loaded)} / ${this._formatBytes(p.total)}
</div>`
: ""}
</div>
`;
}
_handleProfileChange(e) { _handleProfileChange(e) {
this.onProfileChange(e.target.value); this.onProfileChange(e.target.value);
} }
@ -1261,6 +1354,9 @@ export class MainView extends LitElement {
: "Downloaded automatically on first use"} : "Downloaded automatically on first use"}
</div> </div>
`} `}
${this.whisperDownloading && this.whisperProgress
? this._renderWhisperProgress()
: ""}
</div> </div>
${this._renderStartButton()} ${this._renderStartButton()}

View File

@ -263,6 +263,15 @@ function spawnWhisperWorker() {
case "status": case "status":
sendToRenderer("update-status", msg.message); sendToRenderer("update-status", msg.message);
break; break;
case "progress":
sendToRenderer("whisper-progress", {
file: msg.file,
progress: msg.progress,
loaded: msg.loaded,
total: msg.total,
status: msg.status,
});
break;
} }
}); });

View File

@ -155,6 +155,40 @@ async function loadModel(modelName, cacheDir, device = "cpu") {
{ {
dtype: "q8", dtype: "q8",
device: dev, device: dev,
progress_callback: (progress) => {
// progress: { status, name?, file?, progress?, loaded?, total? }
if (
progress.status === "download" ||
progress.status === "progress"
) {
send({
type: "progress",
file: progress.file || progress.name || "",
progress: progress.progress ?? 0,
loaded: progress.loaded ?? 0,
total: progress.total ?? 0,
status: progress.status,
});
} else if (progress.status === "done") {
send({
type: "progress",
file: progress.file || progress.name || "",
progress: 100,
loaded: progress.total ?? 0,
total: progress.total ?? 0,
status: "done",
});
} else if (progress.status === "initiate") {
send({
type: "progress",
file: progress.file || progress.name || "",
progress: 0,
loaded: 0,
total: 0,
status: "initiate",
});
}
},
}, },
); );