fix: localize shell operation errors
This commit is contained in:
parent
6179ad73e7
commit
963049add3
|
|
@ -124,4 +124,20 @@ test.describe('Command Palette', () => {
|
|||
await expect(page.locator('.modal[aria-label="Plugin Settings"]')).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.locator('.modal-header h3')).toContainText('Sync');
|
||||
});
|
||||
|
||||
test('keeps technical command failures out of the palette status', async ({ page }) => {
|
||||
await page.evaluate(() => {
|
||||
window.go.api.App.PluginSyncNow = () => Promise.resolve('[plugin:verstak.sync] sync.now failed: internal service code 42');
|
||||
});
|
||||
|
||||
await page.keyboard.press(process.platform === 'darwin' ? 'Meta+K' : 'Control+K');
|
||||
const palette = page.locator('.command-palette');
|
||||
await palette.locator('[data-command-palette-input]').fill('sync now');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
const status = page.locator('[data-command-palette-status="error"]');
|
||||
await expect(status).toContainText('Could not run Sync Now. Please try again.');
|
||||
await expect(status).not.toContainText('[plugin:');
|
||||
await expect(status).not.toContainText('sync.now');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -96,8 +96,8 @@ export default {
|
|||
'common.loading': 'Loading...',
|
||||
'common.browse': 'Browse...',
|
||||
'vaultSelection.chooseNew': 'Choose or enter a folder for the new vault.',
|
||||
'vaultSelection.createError': 'Could not create vault: {error}',
|
||||
'vaultSelection.openError': 'Could not open vault: {error}',
|
||||
'vaultSelection.createError': 'Could not create the vault. Please try again.',
|
||||
'vaultSelection.openError': 'Could not open the vault. Please try again.',
|
||||
'vaultSelection.chooseExisting': 'Choose or enter an existing vault.',
|
||||
'vaultSelection.subtitle': 'Choose a vault to start working',
|
||||
'vaultSelection.createTitle': 'Create a new vault',
|
||||
|
|
@ -135,6 +135,18 @@ export default {
|
|||
'workspaceTree.templateToolNoTabs': 'No Deal tab provided',
|
||||
'workspaceTree.templateWillBeIncomplete': 'This Deal will be created without the unavailable components.',
|
||||
'workspaceTree.templateIncompleteCreated': '«{name}» was created with unavailable components.',
|
||||
'workspaceTree.templatesError': 'Could not load Deal templates. Please try again.',
|
||||
'workspaceTree.loadError': 'Could not load Deals. Please try again.',
|
||||
'workspaceTree.selectError': 'Could not select this Deal. Please try again.',
|
||||
'workspaceTree.createError': 'Could not create the Deal. Please try again.',
|
||||
'workspaceTree.renameError': 'Could not rename the Deal. Please try again.',
|
||||
'workspaceTree.trashError': 'Could not move the Deal to trash. Please try again.',
|
||||
'pluginManager.loadError': 'Could not load plugins. Please try again.',
|
||||
'pluginManager.reloadError': 'Could not reload plugins. Please try again.',
|
||||
'pluginManager.enableError': 'Could not enable the plugin. Please try again.',
|
||||
'pluginManager.disableError': 'Could not disable the plugin. Please try again.',
|
||||
'pluginManager.settingsLoadError': 'Could not load plugin settings. Please try again.',
|
||||
'pluginManager.settingsUnavailable': 'Plugin settings are unavailable.',
|
||||
'command.openOverview': 'Open Overview',
|
||||
'command.openFiles': 'Open Files',
|
||||
'command.openActivity': 'Open Activity',
|
||||
|
|
@ -146,6 +158,7 @@ export default {
|
|||
'command.openPluginManager': 'Open Plugin Manager',
|
||||
'command.handled': '{title} handled',
|
||||
'command.result': '{title} {status}',
|
||||
'command.failed': 'Could not run {title}. Please try again.',
|
||||
'command.statusHandled': 'handled',
|
||||
'command.palette': 'Command Palette',
|
||||
'command.run': 'Run command',
|
||||
|
|
@ -166,14 +179,14 @@ export default {
|
|||
'workbench.noResource': 'No resource opened',
|
||||
'bundle.noFrontend': 'Plugin has no frontend bundle',
|
||||
'bundle.notFound': 'Plugin not found',
|
||||
'bundle.loadFailed': 'Failed to load bundle: {error}',
|
||||
'bundle.loadFailed': 'Could not load the plugin interface. Please try again.',
|
||||
'bundle.emptyContent': 'empty content',
|
||||
'bundle.executionError': 'Bundle execution error: {error}',
|
||||
'bundle.executionError': 'Could not start the plugin interface. Please try again.',
|
||||
'bundle.registrationMissing': 'Bundle loaded but no VerstakPluginRegister call detected',
|
||||
'bundle.componentMissing': 'Component "{component}" not found in bundle. Available: {available}',
|
||||
'bundle.mountError': 'Component mount error: {error}',
|
||||
'bundle.componentMissing': 'The requested plugin interface is unavailable.',
|
||||
'bundle.mountError': 'Could not open the plugin interface. Please try again.',
|
||||
'bundle.mountUnavailable': 'Mount container not available',
|
||||
'bundle.unexpectedError': 'Unexpected error: {error}',
|
||||
'bundle.unexpectedError': 'Could not open the plugin interface. Please try again.',
|
||||
'bundle.unknownError': 'Unknown error',
|
||||
'bundle.frontendEntry': 'Frontend entry',
|
||||
'bundle.availableComponents': 'Available components',
|
||||
|
|
|
|||
|
|
@ -96,8 +96,8 @@ export default {
|
|||
'common.loading': 'Загрузка...',
|
||||
'common.browse': 'Обзор...',
|
||||
'vaultSelection.chooseNew': 'Выберите или введите папку для нового хранилища.',
|
||||
'vaultSelection.createError': 'Не удалось создать хранилище: {error}',
|
||||
'vaultSelection.openError': 'Не удалось открыть хранилище: {error}',
|
||||
'vaultSelection.createError': 'Не удалось создать хранилище. Повторите попытку.',
|
||||
'vaultSelection.openError': 'Не удалось открыть хранилище. Повторите попытку.',
|
||||
'vaultSelection.chooseExisting': 'Выберите или введите существующее хранилище.',
|
||||
'vaultSelection.subtitle': 'Выберите хранилище, чтобы начать работу',
|
||||
'vaultSelection.createTitle': 'Создать новое хранилище',
|
||||
|
|
@ -135,6 +135,18 @@ export default {
|
|||
'workspaceTree.templateToolNoTabs': 'Не предоставляет вкладку Дела',
|
||||
'workspaceTree.templateWillBeIncomplete': 'Дело будет создано без недоступных компонентов.',
|
||||
'workspaceTree.templateIncompleteCreated': 'Дело «{name}» создано с недоступными компонентами.',
|
||||
'workspaceTree.templatesError': 'Не удалось загрузить шаблоны Дел. Повторите попытку.',
|
||||
'workspaceTree.loadError': 'Не удалось загрузить Дела. Повторите попытку.',
|
||||
'workspaceTree.selectError': 'Не удалось выбрать это Дело. Повторите попытку.',
|
||||
'workspaceTree.createError': 'Не удалось создать Дело. Повторите попытку.',
|
||||
'workspaceTree.renameError': 'Не удалось переименовать Дело. Повторите попытку.',
|
||||
'workspaceTree.trashError': 'Не удалось переместить Дело в корзину. Повторите попытку.',
|
||||
'pluginManager.loadError': 'Не удалось загрузить плагины. Повторите попытку.',
|
||||
'pluginManager.reloadError': 'Не удалось перезагрузить плагины. Повторите попытку.',
|
||||
'pluginManager.enableError': 'Не удалось включить плагин. Повторите попытку.',
|
||||
'pluginManager.disableError': 'Не удалось выключить плагин. Повторите попытку.',
|
||||
'pluginManager.settingsLoadError': 'Не удалось загрузить настройки плагина. Повторите попытку.',
|
||||
'pluginManager.settingsUnavailable': 'Настройки плагина недоступны.',
|
||||
'command.openOverview': 'Открыть обзор',
|
||||
'command.openFiles': 'Открыть файлы',
|
||||
'command.openActivity': 'Открыть активность',
|
||||
|
|
@ -146,6 +158,7 @@ export default {
|
|||
'command.openPluginManager': 'Открыть менеджер плагинов',
|
||||
'command.handled': 'Выполнено: {title}',
|
||||
'command.result': '{title}: {status}',
|
||||
'command.failed': 'Не удалось выполнить команду «{title}». Повторите попытку.',
|
||||
'command.statusHandled': 'выполнено',
|
||||
'command.palette': 'Палитра команд',
|
||||
'command.run': 'Выполнить команду',
|
||||
|
|
@ -166,14 +179,14 @@ export default {
|
|||
'workbench.noResource': 'Ресурс не открыт',
|
||||
'bundle.noFrontend': 'У плагина нет frontend bundle',
|
||||
'bundle.notFound': 'Плагин не найден',
|
||||
'bundle.loadFailed': 'Не удалось загрузить bundle: {error}',
|
||||
'bundle.loadFailed': 'Не удалось загрузить интерфейс плагина. Повторите попытку.',
|
||||
'bundle.emptyContent': 'пустое содержимое',
|
||||
'bundle.executionError': 'Ошибка выполнения bundle: {error}',
|
||||
'bundle.executionError': 'Не удалось запустить интерфейс плагина. Повторите попытку.',
|
||||
'bundle.registrationMissing': 'Bundle загружен, но вызов VerstakPluginRegister не обнаружен',
|
||||
'bundle.componentMissing': 'Компонент «{component}» не найден в bundle. Доступны: {available}',
|
||||
'bundle.mountError': 'Ошибка подключения компонента: {error}',
|
||||
'bundle.componentMissing': 'Запрошенный интерфейс плагина недоступен.',
|
||||
'bundle.mountError': 'Не удалось открыть интерфейс плагина. Повторите попытку.',
|
||||
'bundle.mountUnavailable': 'Контейнер для подключения недоступен',
|
||||
'bundle.unexpectedError': 'Непредвиденная ошибка: {error}',
|
||||
'bundle.unexpectedError': 'Не удалось открыть интерфейс плагина. Повторите попытку.',
|
||||
'bundle.unknownError': 'Неизвестная ошибка',
|
||||
'bundle.frontendEntry': 'Точка входа frontend',
|
||||
'bundle.availableComponents': 'Доступные компоненты',
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@
|
|||
return { value: result, error: '' };
|
||||
}
|
||||
|
||||
function reportError(key, fallback, details) {
|
||||
console.warn('[PluginBundleHost] ' + key + ':', details);
|
||||
return tr(key, undefined, fallback);
|
||||
}
|
||||
|
||||
async function loadAndMount(pId, compId, nextPropsKey) {
|
||||
// If same plugin+component and already mounted, skip
|
||||
if (currentPluginId === pId && currentComponent === compId && currentPropsKey === nextPropsKey && loadState === 'loaded') {
|
||||
|
|
@ -125,7 +130,7 @@
|
|||
const content = assetResult.value;
|
||||
if (assetResult.error || !content) {
|
||||
loadState = 'error';
|
||||
errorText = tr('bundle.loadFailed', { error: assetResult.error || tr('bundle.emptyContent') });
|
||||
errorText = reportError('bundle.loadFailed', 'Could not load the plugin interface. Please try again.', assetResult.error || tr('bundle.emptyContent'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -136,8 +141,7 @@
|
|||
fn();
|
||||
} catch (e) {
|
||||
loadState = 'error';
|
||||
errorText = tr('bundle.executionError', { error: e.message });
|
||||
console.error('[PluginBundleHost] bundle exec error:', e);
|
||||
errorText = reportError('bundle.executionError', 'Could not start the plugin interface. Please try again.', e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -154,10 +158,7 @@
|
|||
const comp = components[compId];
|
||||
if (!comp || !comp.mount) {
|
||||
loadState = 'error';
|
||||
errorText = tr('bundle.componentMissing', {
|
||||
component: compId,
|
||||
available: Object.keys(components).join(', ') || tr('common.none'),
|
||||
});
|
||||
errorText = tr('bundle.componentMissing', undefined, 'The requested plugin interface is unavailable.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -177,8 +178,7 @@
|
|||
errorText = '';
|
||||
} catch (e) {
|
||||
loadState = 'error';
|
||||
errorText = tr('bundle.mountError', { error: e.message });
|
||||
console.error('[PluginBundleHost] mount error:', e);
|
||||
errorText = reportError('bundle.mountError', 'Could not open the plugin interface. Please try again.', e);
|
||||
}
|
||||
} else {
|
||||
loadState = 'error';
|
||||
|
|
@ -186,8 +186,7 @@
|
|||
}
|
||||
} catch (e) {
|
||||
loadState = 'error';
|
||||
errorText = tr('bundle.unexpectedError', { error: e.message || e });
|
||||
console.error('[PluginBundleHost] error:', e);
|
||||
errorText = reportError('bundle.unexpectedError', 'Could not open the plugin interface. Please try again.', e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,17 +207,18 @@
|
|||
<div class="host-state error">
|
||||
<Icon name="warning" size={24} class="error-icon" />
|
||||
<p class="error-title">{tr('pluginView.error')}</p>
|
||||
<div class="error-details">
|
||||
<p class="error-message">{errorText || tr('bundle.unknownError')}</p>
|
||||
<details class="error-details">
|
||||
<summary>{tr('common.details')}</summary>
|
||||
<p><strong>{tr('common.plugin')}:</strong> {currentPluginId || tr('common.unknown')}</p>
|
||||
<p><strong>{tr('common.component')}:</strong> {currentComponent || tr('common.unknown')}</p>
|
||||
<p class="error-message">{errorText || tr('bundle.unknownError')}</p>
|
||||
{#if pluginInfo}
|
||||
<p class="error-meta">{tr('bundle.frontendEntry')}: {pluginInfo.entry || tr('common.none')}</p>
|
||||
{/if}
|
||||
{#if getComponentList().length > 0}
|
||||
<p class="error-meta">{tr('bundle.availableComponents')}: {getComponentList().join(', ')}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
|
|
@ -303,11 +303,21 @@
|
|||
max-width: 400px;
|
||||
text-align: left;
|
||||
background: #16213e;
|
||||
padding: 1rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #0f3460;
|
||||
}
|
||||
|
||||
.error-details[open] {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.error-details summary {
|
||||
cursor: pointer;
|
||||
color: #e0e0f0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.error-details p {
|
||||
margin: 0.3rem 0;
|
||||
}
|
||||
|
|
@ -318,12 +328,8 @@
|
|||
|
||||
.error-message {
|
||||
color: #e94560;
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.5rem !important;
|
||||
padding: 0.5rem;
|
||||
background: rgba(233, 69, 96, 0.1);
|
||||
border-radius: 4px;
|
||||
max-width: 400px;
|
||||
margin: 0 0 0.75rem;
|
||||
}
|
||||
|
||||
.error-meta {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@
|
|||
}, 4000);
|
||||
}
|
||||
|
||||
function reportError(key, fallback, details) {
|
||||
debug.log('[PluginManager] ' + key + ':', String(details));
|
||||
WriteFrontendLog('PluginManager', key + ': ' + String(details)).catch(() => {});
|
||||
return tr(key, undefined, fallback);
|
||||
}
|
||||
|
||||
function notifyPluginsChanged() {
|
||||
window.dispatchEvent(new CustomEvent('verstak:plugins-changed'));
|
||||
}
|
||||
|
|
@ -89,14 +95,14 @@
|
|||
ReadPluginSettings(pluginId).then(result => {
|
||||
const unpacked = unpackBackendResult(result);
|
||||
if (unpacked.error) {
|
||||
settingsError = unpacked.error;
|
||||
settingsError = reportError('pluginManager.settingsLoadError', 'Could not load plugin settings. Please try again.', unpacked.error);
|
||||
settingsData = {};
|
||||
return;
|
||||
}
|
||||
settingsData = unpacked.value || {};
|
||||
}).catch(() => { settingsData = {}; });
|
||||
} else {
|
||||
settingsError = `Settings panel not found for plugin "${pluginId}". Check that the plugin is enabled and has settingsPanels in its manifest.`;
|
||||
settingsError = tr('pluginManager.settingsUnavailable', undefined, 'Plugin settings are unavailable.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +133,7 @@
|
|||
} catch (e) {
|
||||
debug.log('[PluginManager] loadAll: GetPlugins ERROR:', String(e));
|
||||
WriteFrontendLog('PluginManager', 'loadAll: GetPlugins ERROR: ' + String(e));
|
||||
error = 'GetPlugins: ' + String(e);
|
||||
error = reportError('pluginManager.loadError', 'Could not load plugins. Please try again.', e);
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -191,7 +197,7 @@
|
|||
resultMsg = `Reloaded ${count} plugin(s). ${summary}`;
|
||||
} catch (e) {
|
||||
debug.log('[PluginManager] reload: ReloadPlugins ERROR:', String(e));
|
||||
error = 'Reload: ' + String(e);
|
||||
error = reportError('pluginManager.reloadError', 'Could not reload plugins. Please try again.', e);
|
||||
reloading = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -211,7 +217,7 @@
|
|||
if (err) {
|
||||
debug.log('[PluginManager] enablePlugin: ERROR:', err);
|
||||
actionFeedback = { ...actionFeedback, [pluginId]: null };
|
||||
error = 'Enable: ' + err;
|
||||
error = reportError('pluginManager.enableError', 'Could not enable the plugin. Please try again.', err);
|
||||
return;
|
||||
}
|
||||
debug.log('[PluginManager] enablePlugin: success, reloading...');
|
||||
|
|
@ -232,7 +238,7 @@
|
|||
if (err) {
|
||||
debug.log('[PluginManager] disablePlugin: ERROR:', err);
|
||||
actionFeedback = { ...actionFeedback, [pluginId]: null };
|
||||
error = 'Disable: ' + err;
|
||||
error = reportError('pluginManager.disableError', 'Could not disable the plugin. Please try again.', err);
|
||||
return;
|
||||
}
|
||||
debug.log('[PluginManager] disablePlugin: success, reloading...');
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import * as App from '../../../wailsjs/go/api/App';
|
||||
import { executePluginCommand } from '../plugin-host/VerstakPluginAPI.js';
|
||||
import { i18n } from '../i18n/index.js';
|
||||
import { debug } from '../log/debug.js';
|
||||
|
||||
let open = false;
|
||||
let query = '';
|
||||
|
|
@ -194,7 +195,12 @@
|
|||
closePalette();
|
||||
setStatus('success', tr('command.result', { title: command.title || command.id, status: result.status || tr('command.statusHandled') }));
|
||||
} catch (err) {
|
||||
setStatus('error', `${command.title || command.id}: ${err?.message || String(err)}`);
|
||||
const details = err?.message || String(err);
|
||||
debug.log('[CommandPalette] command failed:', details);
|
||||
App.WriteFrontendLog('CommandPalette', `command failed: ${details}`).catch(() => {});
|
||||
setStatus('error', tr('command.failed', {
|
||||
title: command.title || command.id,
|
||||
}, `Could not run ${command.title || command.id}. Please try again.`));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@
|
|||
void activeLocale;
|
||||
return i18n.t(key, params, fallback);
|
||||
})(locale);
|
||||
|
||||
function reportError(key, fallback, details) {
|
||||
console.warn('[VaultSelection] operation failed:', details);
|
||||
return tr(key, undefined, fallback);
|
||||
}
|
||||
|
||||
onDestroy(unsubscribeLocale);
|
||||
|
||||
onMount(async () => {
|
||||
|
|
@ -54,13 +60,13 @@
|
|||
try {
|
||||
const createErr = await App.CreateVault(newVaultPath.trim());
|
||||
if (createErr) {
|
||||
error = tr('vaultSelection.createError', { error: createErr });
|
||||
error = reportError('vaultSelection.createError', 'Could not create the vault. Please try again.', createErr);
|
||||
creating = false;
|
||||
return;
|
||||
}
|
||||
const openErr = await App.OpenVault(newVaultPath.trim());
|
||||
if (openErr) {
|
||||
error = tr('vaultSelection.openError', { error: openErr });
|
||||
error = reportError('vaultSelection.openError', 'Could not open the vault. Please try again.', openErr);
|
||||
creating = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -70,7 +76,7 @@
|
|||
}
|
||||
window.dispatchEvent(new CustomEvent('verstak:vault-opened'));
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
error = reportError('vaultSelection.createError', 'Could not create the vault. Please try again.', e);
|
||||
creating = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +91,7 @@
|
|||
try {
|
||||
const openErr = await App.OpenVault(openVaultPath.trim());
|
||||
if (openErr) {
|
||||
error = tr('vaultSelection.openError', { error: openErr });
|
||||
error = reportError('vaultSelection.openError', 'Could not open the vault. Please try again.', openErr);
|
||||
opening = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -95,7 +101,7 @@
|
|||
}
|
||||
window.dispatchEvent(new CustomEvent('verstak:vault-opened'));
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
error = reportError('vaultSelection.openError', 'Could not open the vault. Please try again.', e);
|
||||
opening = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +112,7 @@
|
|||
try {
|
||||
const openErr = await App.OpenVault(path);
|
||||
if (openErr) {
|
||||
error = tr('vaultSelection.openError', { error: openErr });
|
||||
error = reportError('vaultSelection.openError', 'Could not open the vault. Please try again.', openErr);
|
||||
opening = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -116,7 +122,7 @@
|
|||
}
|
||||
window.dispatchEvent(new CustomEvent('verstak:vault-opened'));
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
error = reportError('vaultSelection.openError', 'Could not open the vault. Please try again.', e);
|
||||
opening = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@
|
|||
return i18n.t(key, params, fallback);
|
||||
})(locale);
|
||||
|
||||
function reportError(key, fallback, details) {
|
||||
console.warn('[WorkspaceTree] operation failed:', details);
|
||||
return tr(key, undefined, fallback);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
unsubscribeLocale = i18n.subscribe((nextLocale) => {
|
||||
const changed = locale !== nextLocale;
|
||||
|
|
@ -128,7 +133,7 @@
|
|||
]);
|
||||
const [list, err] = resultOrError(templates, []);
|
||||
if (err) {
|
||||
createError = err;
|
||||
createError = reportError('workspaceTree.templatesError', 'Could not load Deal templates. Please try again.', err);
|
||||
workspaceTemplates = [];
|
||||
return;
|
||||
}
|
||||
|
|
@ -154,7 +159,7 @@
|
|||
selectedTemplateId = workspaceTemplates[0]?.id || '';
|
||||
}
|
||||
} catch (error) {
|
||||
createError = String(error);
|
||||
createError = reportError('workspaceTree.templatesError', 'Could not load Deal templates. Please try again.', error);
|
||||
workspaceTemplates = [];
|
||||
} finally {
|
||||
templatesLoading = false;
|
||||
|
|
@ -188,7 +193,7 @@
|
|||
try {
|
||||
const [list, err] = resultOrError(await App.ListWorkspaces(), []);
|
||||
if (err) {
|
||||
localError = err;
|
||||
localError = reportError('workspaceTree.loadError', 'Could not load Deals. Please try again.', err);
|
||||
workspaces = [];
|
||||
} else {
|
||||
workspaces = list || [];
|
||||
|
|
@ -209,7 +214,7 @@
|
|||
activeWorkspaceId.set(currentWorkspaceId);
|
||||
}
|
||||
} catch (e) {
|
||||
localError = String(e);
|
||||
localError = reportError('workspaceTree.loadError', 'Could not load Deals. Please try again.', e);
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
|
@ -218,7 +223,7 @@
|
|||
const id = wsName(workspace);
|
||||
const err = await App.SetCurrentWorkspace(id);
|
||||
if (err) {
|
||||
localError = err;
|
||||
localError = reportError('workspaceTree.selectError', 'Could not select this Deal. Please try again.', err);
|
||||
return;
|
||||
}
|
||||
currentWorkspaceId = id;
|
||||
|
|
@ -247,7 +252,7 @@
|
|||
createError = '';
|
||||
const [, err] = resultOrError(await App.CreateWorkspace(name, selectedTemplate.id), null);
|
||||
if (err) {
|
||||
createError = err;
|
||||
createError = reportError('workspaceTree.createError', 'Could not create the Deal. Please try again.', err);
|
||||
creating = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -299,7 +304,7 @@
|
|||
busyId = oldName;
|
||||
const err = await App.RenameWorkspace(oldName, newName);
|
||||
if (err) {
|
||||
localError = err;
|
||||
localError = reportError('workspaceTree.renameError', 'Could not rename the Deal. Please try again.', err);
|
||||
busyId = '';
|
||||
return;
|
||||
}
|
||||
|
|
@ -317,7 +322,7 @@
|
|||
busyId = name;
|
||||
const [, err] = resultOrError(await App.TrashWorkspace(name), null);
|
||||
if (err) {
|
||||
localError = err;
|
||||
localError = reportError('workspaceTree.trashError', 'Could not move the Deal to trash. Please try again.', err);
|
||||
busyId = '';
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const files = [
|
||||
'src/lib/shell/CommandPalette.svelte',
|
||||
'src/lib/plugin-manager/PluginManager.svelte',
|
||||
'src/lib/shell/VaultSelection.svelte',
|
||||
'src/lib/shell/WorkspaceTree.svelte',
|
||||
'src/lib/plugin-host/PluginBundleHost.svelte',
|
||||
];
|
||||
const patterns = [
|
||||
/(?:^|\n)\s*(?:error|localError|createError)\s*=\s*(?:err|String\()/,
|
||||
/(?:^|\n)\s*error\s*=\s*['"].*['"]\s*\+\s*(?:err|String\()/,
|
||||
/tr\([^\n]*\{\s*error\s*:/,
|
||||
];
|
||||
const violations = [];
|
||||
|
||||
for (const relativePath of files) {
|
||||
const source = fs.readFileSync(path.join(root, relativePath), 'utf8');
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.test(source)) violations.push(relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.length) {
|
||||
throw new Error(`Technical backend errors reach desktop UI in: ${[...new Set(violations)].join(', ')}`);
|
||||
}
|
||||
|
||||
const bundleHost = fs.readFileSync(path.join(root, 'src/lib/plugin-host/PluginBundleHost.svelte'), 'utf8');
|
||||
if (!/<p class="error-message">\{errorText \|\| tr\('bundle\.unknownError'\)\}<\/p>\s*<details class="error-details">/.test(bundleHost)) {
|
||||
throw new Error('Plugin bundle errors must keep the user-facing message outside expandable technical details');
|
||||
}
|
||||
|
||||
console.log('desktop UI does not interpolate raw backend errors');
|
||||
Loading…
Reference in New Issue