fix: show Laya model download progress and surface errors in Settings (#12)
Track download progress in layaService (bytes/total, phase, error state) and expose it through the laya status route. Render a progress bar during model download in Settings via a layaStatusPresentation service, and display download/status errors inline instead of failing silently. Cover progress tracking and status presentation with tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
63
public/js/services/layaStatusPresentation.js
Normal file
63
public/js/services/layaStatusPresentation.js
Normal file
@ -0,0 +1,63 @@
|
||||
(function (root, factory) {
|
||||
const service = factory();
|
||||
if (typeof module === 'object' && module.exports) module.exports = service;
|
||||
if (root) root.LayaStatusPresentation = service;
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
||||
const PHASE_LABELS = {
|
||||
idle: 'Model not downloaded',
|
||||
checking: 'Checking local AI model…',
|
||||
downloading: 'Downloading model…',
|
||||
verifying: 'Verifying model…',
|
||||
preparing: 'Preparing download…',
|
||||
ready: 'Model ready',
|
||||
failed: 'Model download failed',
|
||||
deleting: 'Removing model…'
|
||||
};
|
||||
|
||||
function errorParts(status) {
|
||||
const value = status.error || status.lastError;
|
||||
const fallback = status.lastError;
|
||||
if (!value) return { message: '', detail: '' };
|
||||
if (typeof value === 'string') return {
|
||||
message: value,
|
||||
detail: typeof fallback === 'object' ? String(fallback.detail || '') : ''
|
||||
};
|
||||
return {
|
||||
message: String(value.message || 'The local AI model operation failed.'),
|
||||
detail: String(value.detail || '')
|
||||
};
|
||||
}
|
||||
|
||||
function presentLayaStatus(status = {}, mode = 'algo') {
|
||||
const phase = PHASE_LABELS[status.phase] ? status.phase : 'idle';
|
||||
const busy = Boolean(status.downloading) || ['checking', 'downloading', 'verifying', 'preparing', 'deleting'].includes(phase);
|
||||
const percentValue = Number(status.progressPercent);
|
||||
const determined = phase === 'downloading' && status.progressPercent !== null &&
|
||||
status.progressPercent !== undefined && Number.isFinite(percentValue);
|
||||
const progressPercent = determined ? Math.max(0, Math.min(100, Math.round(percentValue))) : null;
|
||||
const error = errorParts(status);
|
||||
const failed = phase === 'failed';
|
||||
return {
|
||||
phase,
|
||||
statusLabel: status.modelPresent && phase === 'idle'
|
||||
? (status.runtimeAvailable ? 'Model ready' : 'Model downloaded · runtime unavailable')
|
||||
: PHASE_LABELS[phase],
|
||||
busy,
|
||||
showProgress: phase === 'downloading',
|
||||
progressIndeterminate: phase === 'downloading' && progressPercent === null,
|
||||
progressPercent,
|
||||
bytesDownloaded: status.bytesDownloaded !== null && status.bytesDownloaded !== undefined && Number.isFinite(Number(status.bytesDownloaded))
|
||||
? Number(status.bytesDownloaded) : null,
|
||||
totalBytes: status.totalBytes !== null && status.totalBytes !== undefined && Number.isFinite(Number(status.totalBytes))
|
||||
? Number(status.totalBytes) : null,
|
||||
errorMessage: failed ? (error.message || 'The local AI model could not be downloaded.') : '',
|
||||
errorDetail: failed ? error.detail : '',
|
||||
downloadLabel: failed ? 'Retry download' : 'Download model',
|
||||
showDownload: !busy && !status.modelPresent && (phase === 'idle' || failed),
|
||||
showRemove: !busy && Boolean(status.modelPresent) && !failed,
|
||||
showUseStandard: failed && mode !== 'algo'
|
||||
};
|
||||
}
|
||||
|
||||
return { presentLayaStatus };
|
||||
});
|
||||
Reference in New Issue
Block a user