diff --git a/plugins/activity/frontend/src/index.js b/plugins/activity/frontend/src/index.js index e7abeec..27b1e3f 100644 --- a/plugins/activity/frontend/src/index.js +++ b/plugins/activity/frontend/src/index.js @@ -563,6 +563,14 @@ var disposed = false; var unsubscribers = []; + function reportError(key, fallback, err) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.activity] ' + key, err); + } + statusText = tr(key, null, fallback); + statusClass = 'error'; + } + var toolbar = el('div', { className: 'activity-toolbar' }); var titleEl = el('span', { className: 'activity-title', textContent: scope.mode === 'global' ? tr('ui.title', null, 'Activity') : tr('ui.workspaceTitle', { workspace: scope.label }, 'Activity · ' + scope.label) }); var countEl = el('span', { className: 'activity-count' }); @@ -634,8 +642,7 @@ ? events.filter(function (item) { return !item._storageKey || item._storageKey === GLOBAL_KEY; }) : events; return api.settings.write(scope.key, storageEvents(toStore)).then(persistSessionRegistry).then(persistCandidateCaches).catch(function (err) { - statusText = tr('ui.saveError', { error: err && err.message ? err.message : String(err) }, 'Could not save activity: ' + (err && err.message ? err.message : String(err))); - statusClass = 'error'; + reportError('ui.saveError', 'Could not save activity. Please try again.', err); }); } @@ -668,8 +675,7 @@ statusText = tr('ui.cleared', null, 'Activity cleared'); statusClass = ''; }).catch(function (err) { - statusText = tr('ui.clearError', { error: err && err.message ? err.message : String(err) }, 'Could not clear activity: ' + (err && err.message ? err.message : String(err))); - statusClass = 'error'; + reportError('ui.clearError', 'Could not clear activity. Please try again.', err); }); } @@ -797,8 +803,7 @@ statusText = tr('ui.dismissed', null, 'Candidate dismissed'); statusClass = ''; }).catch(function (err) { - statusText = 'Could not dismiss candidate: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; + reportError('ui.dismissError', 'Could not dismiss the suggestion. Please try again.', err); }).then(render); } @@ -870,8 +875,7 @@ updateCandidates(); return persistSessionRegistry().then(persistCandidateCaches); }).catch(function (err) { - statusText = 'Could not load activity: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; + reportError('ui.loadError', 'Could not load activity. Please try again.', err); }); } @@ -898,8 +902,7 @@ return api.commands.register(WORKLOG_COMMAND_ID, listWorkSessionCandidates).then(function (unregister) { if (typeof unregister === 'function') unsubscribers.push(unregister); }).catch(function (err) { - statusText = 'Activity commands unavailable: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; + reportError('ui.commandsUnavailable', 'Activity actions are unavailable. Please try again.', err); }); } @@ -917,8 +920,7 @@ statusText = scope.mode === 'global' ? 'Listening for all activity' : 'Listening for workspace activity'; statusClass = ''; }).catch(function (err) { - statusText = 'Activity subscriptions unavailable: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; + reportError('ui.subscriptionsUnavailable', 'Activity updates are unavailable. Please try again.', err); }); } diff --git a/plugins/activity/locales/en.json b/plugins/activity/locales/en.json index 2dd6ad1..05f4baa 100644 --- a/plugins/activity/locales/en.json +++ b/plugins/activity/locales/en.json @@ -14,9 +14,13 @@ "ui.clearWorkspaceWarning": "This permanently deletes recorded activity for {workspace}. Activity in other cases remains.", "ui.confirmClear": "Clear activity", "ui.cancel": "Cancel", - "ui.saveError": "Could not save activity: {error}", + "ui.saveError": "Could not save activity. Please try again.", "ui.cleared": "Activity cleared", - "ui.clearError": "Could not clear activity: {error}", + "ui.clearError": "Could not clear activity. Please try again.", + "ui.dismissError": "Could not dismiss the suggestion. Please try again.", + "ui.loadError": "Could not load activity. Please try again.", + "ui.commandsUnavailable": "Activity actions are unavailable. Please try again.", + "ui.subscriptionsUnavailable": "Activity updates are unavailable. Please try again.", "ui.empty": "No activity events yet", "ui.emptyHint": "File changes, browser captures, and conversions will appear here.", "ui.details": "Details", diff --git a/plugins/activity/locales/ru.json b/plugins/activity/locales/ru.json index d2d6db4..e1d4f7d 100644 --- a/plugins/activity/locales/ru.json +++ b/plugins/activity/locales/ru.json @@ -14,9 +14,13 @@ "ui.clearWorkspaceWarning": "Будет безвозвратно удалена записанная активность Дела «{workspace}». Активность других Дел останется.", "ui.confirmClear": "Очистить активность", "ui.cancel": "Отмена", - "ui.saveError": "Не удалось сохранить активность: {error}", + "ui.saveError": "Не удалось сохранить активность. Повторите попытку.", "ui.cleared": "Активность очищена", - "ui.clearError": "Не удалось очистить активность: {error}", + "ui.clearError": "Не удалось очистить активность. Повторите попытку.", + "ui.dismissError": "Не удалось отклонить предложение. Повторите попытку.", + "ui.loadError": "Не удалось загрузить активность. Повторите попытку.", + "ui.commandsUnavailable": "Действия с активностью недоступны. Повторите попытку.", + "ui.subscriptionsUnavailable": "Обновления активности недоступны. Повторите попытку.", "ui.empty": "Событий активности пока нет", "ui.emptyHint": "Здесь появятся изменения файлов, материалы из браузера и преобразования.", "ui.details": "Подробнее", diff --git a/plugins/browser-inbox/frontend/src/index.js b/plugins/browser-inbox/frontend/src/index.js index 423a773..be1699d 100644 --- a/plugins/browser-inbox/frontend/src/index.js +++ b/plugins/browser-inbox/frontend/src/index.js @@ -376,6 +376,16 @@ if (api && api.i18n && typeof api.i18n.t === 'function') return api.i18n.t(key, params, fallback); return fallback || key; } + + function reportError(key, fallback, err) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.browser-inbox] ' + key, err); + } + statusText = tr(key, null, fallback); + statusClass = 'error'; + render(); + } + statusText = tr('ui.connecting', null, 'Connecting to receiver events...'); var toolbar = el('div', { className: 'browser-inbox-toolbar' }); @@ -496,9 +506,7 @@ function publishMutation(action, payload, verify, verifySettings) { if (!api || !api.events || typeof api.events.publish !== 'function') { - statusText = 'Could not save inbox: events API unavailable'; - statusClass = 'error'; - render(); + reportError('ui.saveError', 'Could not update the browser inbox. Please try again.'); return Promise.resolve(false); } return api.events.publish(MUTATION_EVENT, Object.assign({ action: action }, payload || {})).then(function () { @@ -516,9 +524,7 @@ } return true; }).catch(function (err) { - statusText = 'Could not save inbox: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; - render(); + reportError('ui.saveError', 'Could not update the browser inbox. Please try again.', err); return false; }); } @@ -646,9 +652,7 @@ function createNoteFromCapture(capture) { if (!capture || !capture.workspaceRootPath) return Promise.resolve(); if (!api || !api.files || typeof api.files.writeText !== 'function') { - statusText = 'Could not create note: files API unavailable'; - statusClass = 'error'; - render(); + reportError('ui.createNoteError', 'Could not create the note. Please try again.'); return Promise.resolve(); } var title = noteTitle(capture); @@ -677,18 +681,14 @@ statusClass = ''; return archiveCapture(capture.captureId); }).catch(function (err) { - statusText = 'Could not create note: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; - render(); + reportError('ui.createNoteError', 'Could not create the note. Please try again.', err); }); } function createLinkFromCapture(capture) { if (!capture || !capture.workspaceRootPath || !capture.url) return Promise.resolve(); if (!api || !api.files || typeof api.files.writeText !== 'function') { - statusText = 'Could not create link: files API unavailable'; - statusClass = 'error'; - render(); + reportError('ui.createLinkError', 'Could not create the link. Please try again.'); return Promise.resolve(); } var title = noteTitle(capture); @@ -729,27 +729,21 @@ statusClass = ''; return archiveCapture(capture.captureId); }).catch(function (err) { - statusText = 'Could not create link: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; - render(); + reportError('ui.createLinkError', 'Could not create the link. Please try again.', err); }); } function openCaptureURL(capture) { if (!capture || !capture.url || !api || !api.files || typeof api.files.openURL !== 'function') return Promise.resolve(); return api.files.openURL(capture.url).catch(function (err) { - statusText = 'Could not open link: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; - render(); + reportError('ui.openLinkError', 'Could not open the link. Please try again.', err); }); } function createFileFromCapture(capture) { if (!capture || !capture.workspaceRootPath || capture.kind !== 'file' || !capture.fileName || (!capture.fileText && !capture.fileDataBase64)) return Promise.resolve(); if (!api || !api.files || (capture.fileDataBase64 ? typeof api.files.writeBytes !== 'function' : typeof api.files.writeText !== 'function')) { - statusText = 'Could not create file: files API unavailable'; - statusClass = 'error'; - render(); + reportError('ui.createFileError', 'Could not create the file. Please try again.'); return Promise.resolve(); } var fileName = safeFileFilename(capture.fileName); @@ -785,9 +779,7 @@ statusClass = ''; return archiveCapture(capture.captureId); }).catch(function (err) { - statusText = 'Could not create file: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; - render(); + reportError('ui.createFileError', 'Could not create the file. Please try again.', err); }); } @@ -1034,8 +1026,7 @@ }); }); }).catch(function (err) { - statusText = 'Could not load inbox: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; + reportError('ui.loadError', 'Could not load the browser inbox. Please try again.', err); }); } @@ -1093,8 +1084,7 @@ statusText = scope.mode === 'global' ? 'Receiver ready for all workspaces' : 'Receiver ready for workspace'; statusClass = ''; }).catch(function (err) { - statusText = 'Receiver unavailable: ' + (err && err.message ? err.message : String(err)); - statusClass = 'error'; + reportError('ui.receiverError', 'The browser receiver is unavailable. Please try again.', err); }); } @@ -1201,6 +1191,13 @@ statusEl.className = 'browser-inbox-settings-status' + (isError ? ' error' : ''); } + function reportError(key, fallback, err) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.browser-inbox.settings] ' + key, err); + } + setStatus(tr(key, null, fallback), true); + } + function setBusy(busy) { copyURLButton.disabled = busy; copyTokenButton.disabled = busy; @@ -1226,7 +1223,7 @@ applyPairing(pairing); setStatus('', false); }).catch(function (err) { - setStatus(text(err && err.message ? err.message : err), true); + reportError('ui.pairingLoadError', 'Could not load browser connection settings. Please try again.', err); }).then(function () { setBusy(false); }); @@ -1241,7 +1238,7 @@ navigator.clipboard.writeText(value).then(function () { setStatus(tr('ui.copied', { label: label }, '{label} copied'), false); }).catch(function (err) { - setStatus(text(err && err.message ? err.message : err), true); + reportError('ui.clipboardError', 'Could not copy to the clipboard. Please try again.', err); }); } @@ -1261,7 +1258,7 @@ applyPairing(pairing); setStatus(tr('ui.tokenRotated', null, 'Token rotated'), false); }).catch(function (err) { - setStatus(text(err && err.message ? err.message : err), true); + reportError('ui.tokenRotateError', 'Could not rotate the pairing token. Please try again.', err); }).then(function () { setBusy(false); }); diff --git a/plugins/browser-inbox/locales/en.json b/plugins/browser-inbox/locales/en.json index 84456c6..4e4d1d6 100644 --- a/plugins/browser-inbox/locales/en.json +++ b/plugins/browser-inbox/locales/en.json @@ -35,5 +35,15 @@ "ui.copied": "{label} copied", "ui.rotateConfirm": "Rotate pairing token?", "ui.rotating": "Rotating...", - "ui.tokenRotated": "Token rotated" + "ui.tokenRotated": "Token rotated", + "ui.saveError": "Could not update the browser inbox. Please try again.", + "ui.createNoteError": "Could not create the note. Please try again.", + "ui.createLinkError": "Could not create the link. Please try again.", + "ui.openLinkError": "Could not open the link. Please try again.", + "ui.createFileError": "Could not create the file. Please try again.", + "ui.loadError": "Could not load the browser inbox. Please try again.", + "ui.receiverError": "The browser receiver is unavailable. Please try again.", + "ui.pairingLoadError": "Could not load browser connection settings. Please try again.", + "ui.clipboardError": "Could not copy to the clipboard. Please try again.", + "ui.tokenRotateError": "Could not rotate the pairing token. Please try again." } diff --git a/plugins/browser-inbox/locales/ru.json b/plugins/browser-inbox/locales/ru.json index 3cb6de2..7f0abbd 100644 --- a/plugins/browser-inbox/locales/ru.json +++ b/plugins/browser-inbox/locales/ru.json @@ -35,5 +35,15 @@ "ui.copied": "{label} скопирован", "ui.rotateConfirm": "Сменить токен сопряжения?", "ui.rotating": "Смена токена...", - "ui.tokenRotated": "Токен изменён" + "ui.tokenRotated": "Токен изменён", + "ui.saveError": "Не удалось обновить входящие из браузера. Повторите попытку.", + "ui.createNoteError": "Не удалось создать заметку. Повторите попытку.", + "ui.createLinkError": "Не удалось создать ссылку. Повторите попытку.", + "ui.openLinkError": "Не удалось открыть ссылку. Повторите попытку.", + "ui.createFileError": "Не удалось создать файл. Повторите попытку.", + "ui.loadError": "Не удалось загрузить входящие из браузера. Повторите попытку.", + "ui.receiverError": "Приёмник браузера недоступен. Повторите попытку.", + "ui.pairingLoadError": "Не удалось загрузить параметры подключения браузера. Повторите попытку.", + "ui.clipboardError": "Не удалось скопировать в буфер обмена. Повторите попытку.", + "ui.tokenRotateError": "Не удалось сменить токен сопряжения. Повторите попытку." } diff --git a/plugins/default-editor/frontend/src/index.js b/plugins/default-editor/frontend/src/index.js index 806d528..bc3c0db 100644 --- a/plugins/default-editor/frontend/src/index.js +++ b/plugins/default-editor/frontend/src/index.js @@ -495,10 +495,10 @@ rebuildEditorArea(); }).catch(function (err) { if (disposed) return; + console.warn('[default-editor] load error:', err); editorWrap.innerHTML = ''; editorWrap.appendChild(el('div', { className: 'de-error' }, [ - el('div', {}, [tr('ui.loadFailed', null, 'Failed to load file')]), - el('div', { className: 'de-error-msg' }, [(err && err.message) ? err.message : String(err)]) + el('div', {}, [tr('ui.loadFailed', null, 'Could not load the file. Please try again.')]) ])); }); } diff --git a/plugins/default-editor/locales/en.json b/plugins/default-editor/locales/en.json index 4729b54..3e5dc19 100644 --- a/plugins/default-editor/locales/en.json +++ b/plugins/default-editor/locales/en.json @@ -18,5 +18,5 @@ "ui.saved": "Saved", "ui.discardConfirm": "Discard unsaved changes and reload from disk?", "ui.loading": "Loading...", - "ui.loadFailed": "Failed to load file" + "ui.loadFailed": "Could not load the file. Please try again." } diff --git a/plugins/default-editor/locales/ru.json b/plugins/default-editor/locales/ru.json index 984a52c..48a58ce 100644 --- a/plugins/default-editor/locales/ru.json +++ b/plugins/default-editor/locales/ru.json @@ -18,5 +18,5 @@ "ui.saved": "Сохранено", "ui.discardConfirm": "Отменить несохранённые изменения и перечитать файл с диска?", "ui.loading": "Загрузка...", - "ui.loadFailed": "Не удалось загрузить файл" + "ui.loadFailed": "Не удалось загрузить файл. Повторите попытку." } diff --git a/plugins/file-preview/frontend/src/index.js b/plugins/file-preview/frontend/src/index.js index 7ac52c5..4003b57 100644 --- a/plugins/file-preview/frontend/src/index.js +++ b/plugins/file-preview/frontend/src/index.js @@ -143,8 +143,9 @@ return null; }); }).catch(function (err) { + console.warn('[file-preview] preview error:', err); body.className = 'fp-error'; - body.textContent = tr('ui.error', { error: err && err.message ? err.message : String(err) }, 'Preview error: ' + (err && err.message ? err.message : String(err))); + body.textContent = tr('ui.error', null, 'Could not preview this file. Please try again.'); }); if (api.i18n && typeof api.i18n.onDidChangeLocale === 'function') { api.i18n.onDidChangeLocale(function () { diff --git a/plugins/file-preview/locales/en.json b/plugins/file-preview/locales/en.json index d291af8..5f2ce08 100644 --- a/plugins/file-preview/locales/en.json +++ b/plugins/file-preview/locales/en.json @@ -5,5 +5,5 @@ "ui.openExternal": "Open External", "ui.preview": "Preview", "ui.loading": "Loading...", - "ui.error": "Preview error: {error}" + "ui.error": "Could not preview this file. Please try again." } diff --git a/plugins/file-preview/locales/ru.json b/plugins/file-preview/locales/ru.json index 0f1596e..a38ad56 100644 --- a/plugins/file-preview/locales/ru.json +++ b/plugins/file-preview/locales/ru.json @@ -5,5 +5,5 @@ "ui.openExternal": "Открыть во внешнем приложении", "ui.preview": "Просмотр", "ui.loading": "Загрузка...", - "ui.error": "Ошибка просмотра: {error}" + "ui.error": "Не удалось открыть предварительный просмотр файла. Повторите попытку." } diff --git a/plugins/files/frontend/src/index.js b/plugins/files/frontend/src/index.js index 09c692d..2d6f7d9 100644 --- a/plugins/files/frontend/src/index.js +++ b/plugins/files/frontend/src/index.js @@ -320,14 +320,14 @@ return Promise.reject(new Error('clipboard unavailable')); } - function showExternalFallback(entry, mode, reason) { + function showExternalFallback(entry, mode) { if (!entry) return; var pathToShow = entry.relativePath; if (mode === 'explorer' && entry.type !== 'folder') { pathToShow = parentPath(entry.relativePath) || entry.relativePath; } var title = mode === 'explorer' ? 'Show in Explorer' : 'Open External'; - var message = title + ' failed.\n' + (reason ? String(reason) + '\n' : '') + 'Vault-relative path:\n' + pathToShow; + var message = title + ' failed.\nVault-relative path:\n' + pathToShow; confirmModal(message, { confirmText: 'Copy Path', cancelText: 'Close' }).then(function (copy) { if (!copy) return; copyTextToClipboard(pathToShow).catch(function (err) { @@ -374,6 +374,13 @@ return fallback || key; } + function reportError(key, fallback, err) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.files] ' + key, err); + } + return tr(key, null, fallback); + } + function scopedPath(local) { local = cleanPath(local); return workspaceRoot ? (local ? workspaceRoot + '/' + local : workspaceRoot) : local; @@ -714,10 +721,10 @@ renderList(); }).catch(function (err) { if (disposed) return; + console.warn('[verstak.files] load error:', err); listContainer.innerHTML = ''; listContainer.appendChild(el('div', { className: 'files-error' }, [ - el('div', {}, [tr('ui.loadFailed', null, 'Failed to load files')]), - el('div', { className: 'files-error-msg' }, [(err && err.message) ? err.message : String(err)]) + el('div', {}, [tr('ui.loadFailed', null, 'Could not load files. Please try again.')]) ])); }); } @@ -863,7 +870,7 @@ api.workbench.openResource({ kind: 'vault-file', path: full, mode: 'edit', extension: ext ? '.' + ext : '', context: { sourcePluginId: 'verstak.files', sourceView: 'files' } }).catch(function () {}); } }).catch(function (err) { - setCreateError('Error: ' + ((err && err.message) ? err.message : String(err))); + setCreateError(reportError('ui.createError', 'Could not create this item. Please try again.', err)); }); } @@ -923,7 +930,7 @@ setRenameError('A file with that name already exists'); return; } - setRenameError('Error: ' + ((err && err.message) ? err.message : String(err))); + setRenameError(reportError('ui.renameError', 'Could not rename this item. Please try again.', err)); }); }); } @@ -935,7 +942,9 @@ if (!ok) return; api.files.trash(entry.relativePath).then(function () { loadEntries(); - }).catch(function (err) { window.alert((err && err.message) ? err.message : String(err)); }); + }).catch(function (err) { + window.alert(reportError('ui.trashError', 'Could not move this item to trash. Please try again.', err)); + }); }); } else if (count > 1) { confirmModal('Move ' + count + ' items to trash?', { danger: true }).then(function (ok) { @@ -994,11 +1003,12 @@ var filesApi = api && api.files; var action = mode === 'explorer' ? filesApi && filesApi.showInFolder : filesApi && filesApi.openExternal; if (typeof action !== 'function') { - showExternalFallback(entry, mode, 'files external-open API is unavailable.'); + showExternalFallback(entry, mode); return; } action(entry.relativePath).catch(function (err) { - showExternalFallback(entry, mode, err && err.message ? err.message : err); + console.warn('[verstak.files] external open error:', err); + showExternalFallback(entry, mode); }); } diff --git a/plugins/files/locales/en.json b/plugins/files/locales/en.json index dc8ff8a..c8f900d 100644 --- a/plugins/files/locales/en.json +++ b/plugins/files/locales/en.json @@ -28,5 +28,8 @@ "ui.noMatches": "No matches", "ui.clearFilter": "Clear filter", "ui.loading": "Loading...", - "ui.loadFailed": "Failed to load files" + "ui.loadFailed": "Could not load files. Please try again.", + "ui.createError": "Could not create this item. Please try again.", + "ui.renameError": "Could not rename this item. Please try again.", + "ui.trashError": "Could not move this item to trash. Please try again." } diff --git a/plugins/files/locales/ru.json b/plugins/files/locales/ru.json index 15dea45..099a4f0 100644 --- a/plugins/files/locales/ru.json +++ b/plugins/files/locales/ru.json @@ -28,5 +28,8 @@ "ui.noMatches": "Совпадений нет", "ui.clearFilter": "Сбросить фильтр", "ui.loading": "Загрузка...", - "ui.loadFailed": "Не удалось загрузить файлы" + "ui.loadFailed": "Не удалось загрузить файлы. Повторите попытку.", + "ui.createError": "Не удалось создать этот элемент. Повторите попытку.", + "ui.renameError": "Не удалось переименовать этот элемент. Повторите попытку.", + "ui.trashError": "Не удалось переместить элемент в корзину. Повторите попытку." } diff --git a/plugins/journal/frontend/src/index.js b/plugins/journal/frontend/src/index.js index 933b817..aaa6e58 100644 --- a/plugins/journal/frontend/src/index.js +++ b/plugins/journal/frontend/src/index.js @@ -285,6 +285,14 @@ var statusClass = ''; var modalHost = el('div', { className: 'journal-modal-host', hidden: 'hidden' }); + function reportError(key, fallback, err) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.journal] ' + key, err); + } + statusText = tr(key, null, fallback); + statusClass = 'error'; + } + var toolbar = el('div', { className: 'journal-toolbar' }); var titleEl = el('span', { className: 'journal-title', textContent: scope.mode === 'global' ? tr('ui.title', null, 'Journal') : tr('ui.workspaceTitle', { workspace: scope.label }, 'Journal · ' + scope.label) }); var countEl = el('span', { className: 'journal-count' }); @@ -311,8 +319,7 @@ var target = cleanWorkspace(workspaceRoot || scope.workspaceRoot); if (!target) return Promise.resolve(); return api.settings.write(WORKLOG_PREFIX + encodeKey(target), storageEntries(values || entries)).catch(function (err) { - statusText = tr('ui.saveError', { error: err && err.message ? err.message : String(err) }, 'Could not save journal: ' + (err && err.message ? err.message : String(err))); - statusClass = 'error'; + reportError('ui.saveError', 'Could not save journal. Please try again.', err); }); } @@ -339,8 +346,7 @@ statusText = tr('ui.aggregating', null, 'Aggregating worklogs'); statusClass = ''; }).catch(function (err) { - statusText = tr('ui.loadError', { error: err && err.message ? err.message : String(err) }, 'Could not load journal: ' + (err && err.message ? err.message : String(err))); - statusClass = 'error'; + reportError('ui.loadError', 'Could not load journal. Please try again.', err); }); } return api.settings.read(scope.key).then(function (stored) { @@ -348,8 +354,7 @@ statusText = tr('ui.ready', null, 'Ready'); statusClass = ''; }).catch(function (err) { - statusText = tr('ui.loadError', { error: err && err.message ? err.message : String(err) }, 'Could not load journal: ' + (err && err.message ? err.message : String(err))); - statusClass = 'error'; + reportError('ui.loadError', 'Could not load journal. Please try again.', err); }); } diff --git a/plugins/journal/locales/en.json b/plugins/journal/locales/en.json index 4c74be4..80d3e29 100644 --- a/plugins/journal/locales/en.json +++ b/plugins/journal/locales/en.json @@ -8,9 +8,9 @@ "ui.title": "Journal", "ui.workspaceTitle": "Journal · {workspace}", "ui.add": "Add", - "ui.saveError": "Could not save journal: {error}", + "ui.saveError": "Could not save journal. Please try again.", "ui.aggregating": "Aggregating worklogs", - "ui.loadError": "Could not load journal: {error}", + "ui.loadError": "Could not load journal. Please try again.", "ui.ready": "Ready", "ui.workItem": "Work item", "ui.body": "Body", diff --git a/plugins/journal/locales/ru.json b/plugins/journal/locales/ru.json index 6f955bf..60fe0a0 100644 --- a/plugins/journal/locales/ru.json +++ b/plugins/journal/locales/ru.json @@ -8,9 +8,9 @@ "ui.title": "Журнал", "ui.workspaceTitle": "Журнал · {workspace}", "ui.add": "Добавить", - "ui.saveError": "Не удалось сохранить журнал: {error}", + "ui.saveError": "Не удалось сохранить журнал. Повторите попытку.", "ui.aggregating": "Сбор записей о работе", - "ui.loadError": "Не удалось загрузить журнал: {error}", + "ui.loadError": "Не удалось загрузить журнал. Повторите попытку.", "ui.ready": "Готово", "ui.workItem": "Выполненная работа", "ui.body": "Описание", diff --git a/plugins/notes/frontend/src/index.js b/plugins/notes/frontend/src/index.js index 5013921..af5de61 100644 --- a/plugins/notes/frontend/src/index.js +++ b/plugins/notes/frontend/src/index.js @@ -324,6 +324,13 @@ } } + function userFacingError(key, fallback, err) { + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.notes] ' + key, err); + } + return tr(key, null, fallback); + } + function loadNotes() { listContainer.innerHTML = ''; listContainer.appendChild(el('div', { className: 'notes-empty' }, [tr('ui.loading', null, 'Loading...')])); @@ -335,7 +342,7 @@ renderList(); }).catch(function (err) { if (disposed) return; - renderEmpty('Error: ' + (err.message || err)); + renderEmpty(userFacingError('ui.loadError', 'Could not load notes. Please try again.', err)); }); } @@ -379,8 +386,7 @@ }).then(function () { if (!disposed) setStatus(action.label + ' complete', 'success'); }).catch(function (err) { - console.error('[notes] contribution action failed:', err); - if (!disposed) setStatus('Error: ' + (err && err.message ? err.message : err), 'error'); + if (!disposed) setStatus(userFacingError('ui.actionError', 'Could not complete this note action. Please try again.', err), 'error'); }); } @@ -533,7 +539,7 @@ }).catch(function () {}); } }).catch(function (err) { - setStatus('Error: ' + (err.message || err), 'error'); + setStatus(userFacingError('ui.createError', 'Could not create the note. Please try again.', err), 'error'); }); } @@ -568,7 +574,7 @@ setStatus(tr('ui.renamed', null, 'Note renamed'), 'success'); loadNotes(); }).catch(function (err) { - setStatus('Error: ' + (err.message || err), 'error'); + setStatus(userFacingError('ui.renameError', 'Could not rename the note. Please try again.', err), 'error'); }); } @@ -585,7 +591,7 @@ setStatus(tr('ui.trashed', null, 'Note moved to trash'), 'success'); loadNotes(); }).catch(function (err) { - setStatus('Error: ' + (err.message || err), 'error'); + setStatus(userFacingError('ui.trashError', 'Could not move the note to trash. Please try again.', err), 'error'); }); }); } diff --git a/plugins/notes/locales/en.json b/plugins/notes/locales/en.json index f941530..451c9f3 100644 --- a/plugins/notes/locales/en.json +++ b/plugins/notes/locales/en.json @@ -28,5 +28,10 @@ "ui.existingFile": " Existing file: {path}.", "ui.chooseDifferent": " Please choose a different title.", "ui.trashTitle": "Move Note to Trash", - "ui.trashConfirm": "Move \"{title}\" to trash?" + "ui.trashConfirm": "Move \"{title}\" to trash?", + "ui.loadError": "Could not load notes. Please try again.", + "ui.actionError": "Could not complete this note action. Please try again.", + "ui.createError": "Could not create the note. Please try again.", + "ui.renameError": "Could not rename the note. Please try again.", + "ui.trashError": "Could not move the note to trash. Please try again." } diff --git a/plugins/notes/locales/ru.json b/plugins/notes/locales/ru.json index 3987a83..267b864 100644 --- a/plugins/notes/locales/ru.json +++ b/plugins/notes/locales/ru.json @@ -28,5 +28,10 @@ "ui.existingFile": " Существующий файл: {path}.", "ui.chooseDifferent": " Выберите другое название.", "ui.trashTitle": "Переместить заметку в корзину", - "ui.trashConfirm": "Переместить «{title}» в корзину?" + "ui.trashConfirm": "Переместить «{title}» в корзину?", + "ui.loadError": "Не удалось загрузить заметки. Повторите попытку.", + "ui.actionError": "Не удалось выполнить действие с заметкой. Повторите попытку.", + "ui.createError": "Не удалось создать заметку. Повторите попытку.", + "ui.renameError": "Не удалось переименовать заметку. Повторите попытку.", + "ui.trashError": "Не удалось переместить заметку в корзину. Повторите попытку." } diff --git a/plugins/search/frontend/src/index.js b/plugins/search/frontend/src/index.js index 20f64a5..3413fbd 100644 --- a/plugins/search/frontend/src/index.js +++ b/plugins/search/frontend/src/index.js @@ -277,7 +277,8 @@ try { providers = await api.contributions.list('searchProviders'); } catch (err) { - output.errors.push(err && err.message ? err.message : String(err)); + console.warn('[verstak.search] list search providers:', err); + output.errors.push(true); return output; } providers = Array.isArray(providers) ? providers : []; @@ -294,7 +295,8 @@ var normalized = normalizeProviderResults(provider, response && response.result); output.results = output.results.concat(normalized.slice(0, remaining - output.results.length)); } catch (err) { - output.errors.push(err && err.message ? err.message : String(err)); + console.warn('[verstak.search] provider search:', err); + output.errors.push(true); } } return output; @@ -371,7 +373,9 @@ if (typeof alertEl.removeAttribute === 'function') alertEl.removeAttribute('hidden'); alertEl.appendChild(el('details', {}, [ el('summary', {}, [tr('ui.providersFailed', null, 'Some plugin search providers did not respond')]), - el('div', {}, [state.providerErrors.join('; ')]) + el('div', {}, [state.providerErrors.map(function () { + return tr('ui.providerUnavailable', null, 'A search provider is unavailable.'); + }).join(' ')]) ])); } else if (typeof alertEl.setAttribute === 'function') { alertEl.setAttribute('hidden', 'hidden'); @@ -460,8 +464,9 @@ state.providerErrors = external.errors; } catch (err) { if (seq !== searchSeq) return; + console.warn('[verstak.search] search:', err); state.results = []; - state.error = err && err.message ? err.message : String(err); + state.error = tr('ui.searchError', null, 'Could not search the vault. Please try again.'); state.providerErrors = []; } finally { if (seq !== searchSeq) return; diff --git a/plugins/search/locales/en.json b/plugins/search/locales/en.json index e5e50a6..d55bbf5 100644 --- a/plugins/search/locales/en.json +++ b/plugins/search/locales/en.json @@ -10,6 +10,8 @@ "ui.searching": "Searching...", "ui.search": "Search", "ui.providersFailed": "Some plugin search providers did not respond", + "ui.providerUnavailable": "A search provider is unavailable.", + "ui.searchError": "Could not search the vault. Please try again.", "ui.noResults": "No results", "ui.open": "Open", "ui.count": "{count} result(s)" diff --git a/plugins/search/locales/ru.json b/plugins/search/locales/ru.json index 5417d72..8eef0b5 100644 --- a/plugins/search/locales/ru.json +++ b/plugins/search/locales/ru.json @@ -10,6 +10,8 @@ "ui.searching": "Поиск...", "ui.search": "Искать", "ui.providersFailed": "Некоторые поставщики поиска не ответили", + "ui.providerUnavailable": "Один из источников поиска недоступен.", + "ui.searchError": "Не удалось выполнить поиск в хранилище. Повторите попытку.", "ui.noResults": "Ничего не найдено", "ui.open": "Открыть", "ui.count": "Результатов: {count}" diff --git a/plugins/secrets/frontend/src/index.js b/plugins/secrets/frontend/src/index.js index 27752d6..18c3aea 100644 --- a/plugins/secrets/frontend/src/index.js +++ b/plugins/secrets/frontend/src/index.js @@ -169,8 +169,8 @@ function userFacingError(action, err) { var raw = (err && err.message) ? err.message : String(err || ''); - if (typeof console !== 'undefined' && typeof console.error === 'function') { - console.error('[verstak.secrets] ' + action + ' failed', err); + if (typeof console !== 'undefined' && typeof console.warn === 'function') { + console.warn('[verstak.secrets] ' + action + ' failed', err); } if (action === 'unlock') { var minimumLength = raw.match(/master password must be at least (\d+) characters/i); diff --git a/plugins/sync/frontend/src/SyncSettings.svelte b/plugins/sync/frontend/src/SyncSettings.svelte index 5e47601..c81cf00 100644 --- a/plugins/sync/frontend/src/SyncSettings.svelte +++ b/plugins/sync/frontend/src/SyncSettings.svelte @@ -28,11 +28,9 @@ const INPUT_STYLE = 'width:100%;background:#0f3460;border:1px solid #1a3a5c;color:#e0e0f0;padding:8px 10px;border-radius:4px;font-size:0.85rem;box-sizing:border-box;height:36px;' const INPUT_FOCUS_STYLE = INPUT_STYLE + 'outline:none;border-color:#4ecca3;' - function sanitizeError(msg) { - if (!msg) return tr('ui.unknownError', null, 'Unknown error') - let s = String(msg).replace(/<[^>]+>/g, '') - if (s.length > 200) s = s.substring(0, 200) + '...' - return s + function reportError(key, fallback, error) { + console.warn('[verstak.sync] operation failed:', error) + return tr(key, null, fallback) } function syncAPI() { @@ -51,15 +49,21 @@ syncInterval = saved.syncInterval || 5 } } - } catch (_) {} + } catch (error) { + console.warn('[verstak.sync] settings load failed:', error) + } try { settings = await syncAPI().status() if (settings) { if (settings.serverUrl) serverUrl = settings.serverUrl if (settings.syncInterval != null) syncInterval = settings.syncInterval if (settings.syncInterval > 0) autoSync = true + if (settings.lastError) console.warn('[verstak.sync] last sync failed:', settings.lastError) } - } catch (_) { settings = null } + } catch (error) { + console.warn('[verstak.sync] status load failed:', error) + settings = null + } } load() @@ -80,7 +84,7 @@ resultMsg = tr('ui.settingsSaved', null, 'Settings saved.') resultKind = '' } catch (e) { - errorMsg = sanitizeError(e.message || e) + errorMsg = reportError('ui.saveFailed', 'Could not save sync settings. Please try again.', e) } loading = false } @@ -97,7 +101,7 @@ connectionResult = tr('ui.connectionSuccessful', null, 'Connection successful.') } catch (e) { connectionOk = false - connectionResult = tr('ui.connectionFailed', { error: sanitizeError(e.message || e) }, 'Connection failed: {error}') + connectionResult = reportError('ui.connectionFailed', 'Could not connect. Check the server address and credentials.', e) } loading = false } @@ -115,7 +119,7 @@ password = '' await load() } catch (e) { - errorMsg = sanitizeError(e.message || e) + errorMsg = reportError('ui.connectFailed', 'Could not connect this device. Please try again.', e) } loading = false } @@ -129,23 +133,8 @@ return parts.join(' · ') } - function conflictField(conflict, keys) { - for (const key of keys) { - const value = conflict && conflict[key] - if (value != null && String(value).trim()) return String(value) - } - return '' - } - - function formatSyncConflict(conflict) { - const entityType = conflictField(conflict, ['entity_type', 'entityType']) || 'item' - const entityId = conflictField(conflict, ['entity_id', 'entityId', 'path']) || 'unknown' - const opId = conflictField(conflict, ['op_id', 'opId']) - const reason = conflictField(conflict, ['reason', 'message']) - const parts = [entityType + ': ' + entityId] - if (opId) parts.push('op ' + opId) - if (reason) parts.push(reason) - return parts.join(' · ') + function formatSyncConflict() { + return tr('ui.syncConflictItem', null, 'A synchronization conflict needs attention.') } async function runSyncNow() { @@ -163,7 +152,7 @@ resultKind = warning ? 'warning' : '' await load() } catch (e) { - errorMsg = sanitizeError(e.message || e) + errorMsg = reportError('ui.syncFailed', 'Could not synchronize. Please try again.', e) } loading = false } @@ -194,7 +183,7 @@ settings = null await load() } catch (e) { - errorMsg = sanitizeError(e.message || e) + errorMsg = reportError('ui.disconnectFailed', 'Could not disconnect from the server. Please try again.', e) } loading = false } @@ -209,7 +198,7 @@ resultKind = '' await load() } catch (e) { - errorMsg = sanitizeError(e.message || e) + errorMsg = reportError('ui.resetKeyFailed', 'Could not reset the sync key. Please try again.', e) } loading = false } @@ -243,7 +232,7 @@ {/if} {#if settings && settings.lastError && !errorMsg}