Add browser inbox receiver
This commit is contained in:
@@ -71,6 +71,24 @@ function dispatchLocalEvent(pluginId, eventName, payload) {
|
||||
});
|
||||
}
|
||||
|
||||
function dispatchBackendEvent(event) {
|
||||
if (!event || !event.name) return;
|
||||
const handlers = getEventHandlers(event.name).slice();
|
||||
handlers.forEach(function(handler) {
|
||||
try {
|
||||
handler(event);
|
||||
} catch (e) {
|
||||
console.error('[VerstakPluginAPI] backend event handler error:', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.__VERSTAK_DISPATCH_BACKEND_EVENT__ = dispatchBackendEvent;
|
||||
|
||||
if (!window.__VERSTAK_BACKEND_EVENT_BRIDGE__ && window.runtime && typeof window.runtime.EventsOnMultiple === 'function') {
|
||||
window.__VERSTAK_BACKEND_EVENT_BRIDGE__ = window.runtime.EventsOnMultiple('verstak:plugin-event', dispatchBackendEvent, -1);
|
||||
}
|
||||
|
||||
function commandKey(pluginId, commandId) {
|
||||
return pluginId + ':' + commandId;
|
||||
}
|
||||
@@ -150,7 +168,9 @@ export function createPluginAPI(pluginId) {
|
||||
await callBackendErrorString(pluginId, 'events.publish(' + type + ')', function() {
|
||||
return App.PublishPluginEvent(pluginId, type, payload || {});
|
||||
});
|
||||
dispatchLocalEvent(pluginId, type, payload || {});
|
||||
if (!window.__VERSTAK_BACKEND_EVENT_BRIDGE__) {
|
||||
dispatchLocalEvent(pluginId, type, payload || {});
|
||||
}
|
||||
},
|
||||
subscribe: function(type, handler) {
|
||||
assertActive('events.subscribe(' + type + ')');
|
||||
|
||||
@@ -133,11 +133,35 @@
|
||||
},
|
||||
rootPath: '/tmp/verstak-test/plugins/files',
|
||||
error: ''
|
||||
},
|
||||
'verstak.sync': {
|
||||
status: 'loaded',
|
||||
enabled: true,
|
||||
manifest: {
|
||||
schemaVersion: 1,
|
||||
id: 'verstak.sync',
|
||||
name: 'Sync',
|
||||
version: '0.1.0',
|
||||
apiVersion: '0.1.0',
|
||||
description: 'Synchronize vault data across devices.',
|
||||
source: 'official',
|
||||
icon: 'refresh-cw',
|
||||
provides: ['verstak/sync/v1', 'verstak/sync.status/v1'],
|
||||
requires: ['verstak/core/files/v1'],
|
||||
permissions: ['files.read', 'files.write', 'network.remote', 'storage.namespace', 'sync.participate', 'ui.register'],
|
||||
frontend: { entry: 'frontend/dist/index.js' },
|
||||
contributes: {
|
||||
settingsPanels: [{ id: 'verstak.sync.settings', title: 'Sync', component: 'SyncSettings' }],
|
||||
statusBarItems: [{ id: 'verstak.sync.status', label: 'Sync', position: 'right' }]
|
||||
}
|
||||
},
|
||||
rootPath: '/tmp/verstak-test/plugins/sync',
|
||||
error: ''
|
||||
}
|
||||
};
|
||||
|
||||
var vaultStatus = { status: 'open', path: '/tmp/verstak-test/vault', vaultId: 'test-vault-001' };
|
||||
var vaultPluginState = { enabledPlugins: ['verstak.platform-test', 'verstak.default-editor', 'verstak.files'], disabledPlugins: [], desiredPlugins: [{ id: 'verstak.platform-test', version: '0.1.0', source: 'official' }, { id: 'verstak.default-editor', version: '0.1.0', source: 'official' }, { id: 'verstak.files', version: '0.1.0', source: 'official' }] };
|
||||
var vaultPluginState = { enabledPlugins: ['verstak.platform-test', 'verstak.default-editor', 'verstak.files', 'verstak.sync'], disabledPlugins: [], desiredPlugins: [{ id: 'verstak.platform-test', version: '0.1.0', source: 'official' }, { id: 'verstak.default-editor', version: '0.1.0', source: 'official' }, { id: 'verstak.files', version: '0.1.0', source: 'official' }, { id: 'verstak.sync', version: '0.1.0', source: 'official' }] };
|
||||
var appSettings = { currentVaultPath: '/tmp/verstak-test/vault', recentVaults: [] };
|
||||
var workbenchPreferences = {};
|
||||
var openedResources = [];
|
||||
@@ -149,6 +173,7 @@
|
||||
window.__wailsMockExternalOpens = [];
|
||||
var workspaceTree = makeDefaultWorkspaceTree();
|
||||
var reloadResponseMode = 'tuple';
|
||||
var syncState = makeDefaultSyncState();
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────
|
||||
function makeDefaultWorkspaceTree() {
|
||||
@@ -197,6 +222,24 @@
|
||||
};
|
||||
}
|
||||
|
||||
function makeDefaultSyncState() {
|
||||
return {
|
||||
configured: false,
|
||||
serverUrl: '',
|
||||
deviceId: 'mock-device',
|
||||
deviceName: '',
|
||||
connected: false,
|
||||
revoked: false,
|
||||
tokenStored: false,
|
||||
unpushedOps: 0,
|
||||
lastSyncAt: '',
|
||||
syncInterval: 0,
|
||||
lastError: '',
|
||||
statusLabel: 'disabled',
|
||||
serverSequence: 0
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeVaultPath(relativePath, allowRoot) {
|
||||
var p = String(relativePath || '');
|
||||
if (p.indexOf('\x00') !== -1) return { error: 'invalid-path: null-byte' };
|
||||
@@ -273,6 +316,7 @@
|
||||
caps.push({ name: 'verstak/core/capability-registry/v1', description: 'Capability registry', pluginId: 'verstak-desktop', status: 'stable' });
|
||||
caps.push({ name: 'verstak/core/files/v1', description: 'Files API', pluginId: 'verstak-desktop', status: 'stable' });
|
||||
caps.push({ name: 'verstak/core/workbench/v1', description: 'Workbench routing', pluginId: 'verstak-desktop', status: 'stable' });
|
||||
caps.push({ name: 'verstak/core/sync/v1', description: 'Sync API', pluginId: 'verstak-desktop', status: 'stable' });
|
||||
for (var id in pluginStates) {
|
||||
var s = pluginStates[id];
|
||||
if (s.status === 'loaded' && s.enabled && s.manifest && s.manifest.provides) {
|
||||
@@ -296,10 +340,39 @@
|
||||
{ name: 'files.write', description: 'Write vault files', dangerous: true },
|
||||
{ name: 'files.delete', description: 'Trash vault files', dangerous: true },
|
||||
{ name: 'files.openExternal', description: 'Open vault files and folders externally', dangerous: true },
|
||||
{ name: 'workbench.open', description: 'Request Workbench open/edit routing', dangerous: false }
|
||||
{ name: 'workbench.open', description: 'Request Workbench open/edit routing', dangerous: false },
|
||||
{ name: 'network.remote', description: 'Connect to remote network services', dangerous: true },
|
||||
{ name: 'sync.participate', description: 'Participate in vault sync', dangerous: true }
|
||||
];
|
||||
}
|
||||
|
||||
function syncStatusDTO() {
|
||||
return {
|
||||
configured: syncState.configured,
|
||||
serverUrl: syncState.serverUrl,
|
||||
deviceId: syncState.deviceId,
|
||||
deviceName: syncState.deviceName,
|
||||
connected: syncState.connected,
|
||||
revoked: syncState.revoked,
|
||||
tokenStored: syncState.tokenStored,
|
||||
unpushedOps: syncState.unpushedOps,
|
||||
lastSyncAt: syncState.lastSyncAt,
|
||||
syncInterval: syncState.syncInterval,
|
||||
lastError: syncState.lastError,
|
||||
statusLabel: syncState.statusLabel
|
||||
};
|
||||
}
|
||||
|
||||
function requirePluginSyncPermission(pluginId, remote) {
|
||||
var err = requirePluginPermission(pluginId, 'sync.participate');
|
||||
if (err) return err;
|
||||
if (remote) {
|
||||
err = requirePluginPermission(pluginId, 'network.remote');
|
||||
if (err) return err;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function allContributions() {
|
||||
var views = [], commands = [], sidebarItems = [], statusBarItems = [], settingsPanels = [], openProviders = [], workspaceItems = [];
|
||||
for (var id in pluginStates) {
|
||||
@@ -998,6 +1071,60 @@
|
||||
workbenchPreferences = Object.assign({}, workbenchPreferences, preferences || {});
|
||||
return Promise.resolve('');
|
||||
},
|
||||
PluginSyncStatus: function (pluginId) {
|
||||
var err = requirePluginSyncPermission(pluginId, false);
|
||||
if (err) return Promise.resolve([{}, err]);
|
||||
return Promise.resolve([syncStatusDTO(), '']);
|
||||
},
|
||||
PluginSyncConfigure: function (pluginId, serverUrl) {
|
||||
var err = requirePluginSyncPermission(pluginId, true);
|
||||
if (err) return Promise.resolve(err);
|
||||
syncState.configured = true;
|
||||
syncState.serverUrl = serverUrl || '';
|
||||
syncState.deviceId = 'mock-device';
|
||||
syncState.deviceName = 'mock-device';
|
||||
syncState.connected = true;
|
||||
syncState.revoked = false;
|
||||
syncState.tokenStored = true;
|
||||
syncState.lastError = '';
|
||||
syncState.statusLabel = 'connected';
|
||||
pluginSettings[pluginId] = Object.assign({}, pluginSettings[pluginId] || {}, {
|
||||
serverUrl: syncState.serverUrl,
|
||||
syncStatus: syncState.statusLabel
|
||||
});
|
||||
return Promise.resolve('');
|
||||
},
|
||||
PluginSyncDisconnect: function (pluginId) {
|
||||
var err = requirePluginSyncPermission(pluginId, false);
|
||||
if (err) return Promise.resolve(err);
|
||||
syncState = makeDefaultSyncState();
|
||||
pluginSettings[pluginId] = Object.assign({}, pluginSettings[pluginId] || {}, {
|
||||
serverUrl: '',
|
||||
syncStatus: syncState.statusLabel
|
||||
});
|
||||
return Promise.resolve('');
|
||||
},
|
||||
PluginSyncTestConnection: function (pluginId, serverUrl) {
|
||||
var err = requirePluginSyncPermission(pluginId, true);
|
||||
if (err) return Promise.resolve(err);
|
||||
if (!serverUrl) return Promise.resolve('server URL is required');
|
||||
return Promise.resolve('');
|
||||
},
|
||||
PluginSyncSetInterval: function (pluginId, minutes) {
|
||||
var err = requirePluginSyncPermission(pluginId, false);
|
||||
if (err) return Promise.resolve(err);
|
||||
syncState.syncInterval = Number(minutes) || 0;
|
||||
return Promise.resolve('');
|
||||
},
|
||||
PluginSyncNow: function (pluginId) {
|
||||
var err = requirePluginSyncPermission(pluginId, true);
|
||||
if (err) return Promise.resolve([{}, err]);
|
||||
if (!syncState.configured) return Promise.resolve([{}, 'sync not configured']);
|
||||
syncState.lastSyncAt = new Date().toISOString();
|
||||
syncState.lastError = '';
|
||||
syncState.statusLabel = 'connected';
|
||||
return Promise.resolve([{ pushed: 0, pulled: 0, serverSequence: syncState.serverSequence }, '']);
|
||||
},
|
||||
GetPluginAssetContent: function (pluginId, assetPath) {
|
||||
if (pluginId === 'verstak.platform-test' && assetPath === 'frontend/dist/index.js') {
|
||||
return Promise.resolve(platformTestBundle());
|
||||
@@ -1415,10 +1542,34 @@
|
||||
},
|
||||
rootPath: '/tmp/verstak-test/plugins/files',
|
||||
error: ''
|
||||
},
|
||||
'verstak.sync': {
|
||||
status: 'loaded',
|
||||
enabled: true,
|
||||
manifest: {
|
||||
schemaVersion: 1,
|
||||
id: 'verstak.sync',
|
||||
name: 'Sync',
|
||||
version: '0.1.0',
|
||||
apiVersion: '0.1.0',
|
||||
description: 'Synchronize vault data across devices.',
|
||||
source: 'official',
|
||||
icon: 'refresh-cw',
|
||||
provides: ['verstak/sync/v1', 'verstak/sync.status/v1'],
|
||||
requires: ['verstak/core/files/v1'],
|
||||
permissions: ['files.read', 'files.write', 'network.remote', 'storage.namespace', 'sync.participate', 'ui.register'],
|
||||
frontend: { entry: 'frontend/dist/index.js' },
|
||||
contributes: {
|
||||
settingsPanels: [{ id: 'verstak.sync.settings', title: 'Sync', component: 'SyncSettings' }],
|
||||
statusBarItems: [{ id: 'verstak.sync.status', label: 'Sync', position: 'right' }]
|
||||
}
|
||||
},
|
||||
rootPath: '/tmp/verstak-test/plugins/sync',
|
||||
error: ''
|
||||
}
|
||||
};
|
||||
vaultStatus = { status: 'open', path: '/tmp/verstak-test/vault', vaultId: 'test-vault-001' };
|
||||
vaultPluginState = { enabledPlugins: ['verstak.platform-test', 'verstak.default-editor', 'verstak.files'], disabledPlugins: [], desiredPlugins: [{ id: 'verstak.platform-test', version: '0.1.0', source: 'official' }, { id: 'verstak.default-editor', version: '0.1.0', source: 'official' }, { id: 'verstak.files', version: '0.1.0', source: 'official' }] };
|
||||
vaultPluginState = { enabledPlugins: ['verstak.platform-test', 'verstak.default-editor', 'verstak.files', 'verstak.sync'], disabledPlugins: [], desiredPlugins: [{ id: 'verstak.platform-test', version: '0.1.0', source: 'official' }, { id: 'verstak.default-editor', version: '0.1.0', source: 'official' }, { id: 'verstak.files', version: '0.1.0', source: 'official' }, { id: 'verstak.sync', version: '0.1.0', source: 'official' }] };
|
||||
appSettings = { currentVaultPath: '/tmp/verstak-test/vault', recentVaults: [] };
|
||||
workbenchPreferences = {};
|
||||
openedResources = [];
|
||||
@@ -1428,6 +1579,7 @@
|
||||
window.__wailsMockExternalOpens = [];
|
||||
workspaceTree = makeDefaultWorkspaceTree();
|
||||
reloadResponseMode = 'tuple';
|
||||
syncState = makeDefaultSyncState();
|
||||
},
|
||||
setPluginStatus: function (pluginId, status, enabled) {
|
||||
if (pluginStates[pluginId]) {
|
||||
|
||||
Reference in New Issue
Block a user