Add platform-test diagnostic open provider

This commit is contained in:
2026-06-19 07:52:04 +08:00
parent 0cc4552506
commit 1d2190fc6c
5 changed files with 834 additions and 23 deletions
+306 -10
View File
@@ -63,6 +63,17 @@
containerEl.innerHTML = '';
containerEl.className = 'pt-root';
containerEl.__ptCleanup = [];
function trackCleanup(fn) {
if (typeof fn === 'function') {
if (!Array.isArray(containerEl.__ptCleanup)) {
try { fn(); } catch (e) { console.error('[platform-test] late cleanup error:', e); }
return;
}
containerEl.__ptCleanup.push(fn);
}
}
/* ── Header ─────────────────────────────────────────────────── */
var header = div('pt-header', [
@@ -81,11 +92,239 @@
]);
var badgeRow = div('', [badge]);
/* ── Real Plugin API bridge checks ─────────────────────────── */
var savedValue = span('pt-list-value pt-saved-setting', 'Saved setting: loading...');
var capabilityValue = span('pt-list-value pt-capability-result', 'Capabilities: loading...');
var commandValue = span('pt-list-value pt-command-result', 'Command: registering...');
var eventValue = span('pt-list-value pt-event-result', 'Event: subscribing...');
var filesValue = span('pt-list-value pt-files-result', 'Files: running...');
var filesErrorValue = span('pt-list-value pt-files-error-result', 'Files error path: checking...');
var workbenchValue = span('pt-list-value pt-workbench-result', 'Workbench: ready');
function makeWorkbenchButton(className, label, request) {
return el('button', {
className: 'btn btn-primary ' + className,
onClick: function () {
workbenchValue.textContent = 'Workbench: opening...';
api.workbench.editResource(request)
.then(function (result) {
workbenchValue.textContent = 'Workbench: opened ' + result.request.path + ' with ' + (result.providerId || 'no-provider');
workbenchValue.setAttribute('data-workbench-status', result.status === 'opened' ? 'ok' : result.status);
})
.catch(function (err) {
workbenchValue.textContent = 'Workbench error: ' + (err && err.message ? err.message : String(err));
workbenchValue.setAttribute('data-workbench-status', 'error');
});
},
}, [label]);
}
var openTextWorkbenchButton = makeWorkbenchButton('pt-open-workbench-text', 'Open Text Diagnostic', {
kind: 'vault-file',
path: 'Docs/todo.txt',
extension: '.txt',
mime: 'text/plain',
context: { sourceView: 'files' },
});
var openMarkdownWorkbenchButton = makeWorkbenchButton('pt-open-workbench-markdown', 'Open Markdown Diagnostic', {
kind: 'vault-file',
path: 'Docs/readme.md',
extension: '.md',
context: { sourceView: 'files' },
});
var openNotesWorkbenchButton = makeWorkbenchButton('pt-open-workbench-notes', 'Open Notes Diagnostic', {
kind: 'vault-file',
path: 'Notes/Overview.md',
extension: '.md',
context: {
sourceView: 'notes',
isInsideNotesFolder: true,
notesMode: true,
},
});
var settingInput = el('input', {
className: 'pt-setting-input',
type: 'text',
'aria-label': 'Saved setting',
value: 'changed value',
});
var saveStatus = span('pt-list-value', '');
var saveButton = el('button', {
className: 'btn btn-primary pt-save-setting',
onClick: function () {
saveStatus.textContent = 'Saving...';
api.settings.write('savedText', settingInput.value)
.then(function () {
savedValue.textContent = 'Saved setting: ' + settingInput.value;
saveStatus.textContent = 'Saved';
})
.catch(function (err) {
saveStatus.textContent = 'Error: ' + (err && err.message ? err.message : String(err));
});
},
}, ['Save Setting']);
api.settings.read('savedText')
.then(function (value) {
var text = value || '';
settingInput.value = text || 'changed value';
savedValue.textContent = 'Saved setting: ' + text;
})
.catch(function (err) {
savedValue.textContent = 'Settings error: ' + (err && err.message ? err.message : String(err));
});
api.capabilities.list()
.then(function (caps) {
capabilityValue.textContent = 'Capabilities: ' + caps.length + ' available';
})
.catch(function (err) {
capabilityValue.textContent = 'Capabilities error: ' + (err && err.message ? err.message : String(err));
});
api.capabilities.has('verstak/platform-test/v1')
.then(function (available) {
badge.setAttribute('data-capability-status', available ? 'available' : 'missing');
badge.lastChild.textContent = 'Frontend Bundle Loaded | capability ' + (available ? 'available' : 'missing');
})
.catch(function (err) {
badge.setAttribute('data-capability-status', 'error');
badge.lastChild.textContent = 'Capability error: ' + (err && err.message ? err.message : String(err));
});
api.commands.register('verstak.platform-test.show-version', function () {
return {
version: '0.1.0',
source: 'bundled-frontend',
};
})
.then(function (unregister) {
trackCleanup(unregister);
return api.commands.execute('verstak.platform-test.show-version', {});
})
.then(function (result) {
badge.setAttribute('data-command-status', result.status || '');
commandValue.textContent = 'Command: ' + result.status + ' ' + result.result.version + ' from ' + result.result.source;
})
.catch(function (err) {
badge.setAttribute('data-command-status', 'error');
commandValue.textContent = 'Command error: ' + (err && err.message ? err.message : String(err));
console.error('[platform-test] command bridge error:', err);
});
api.events.subscribe('verstak.platform-test.echo', function (event) {
var message = event && event.payload ? event.payload.message : '';
eventValue.textContent = 'Event: received ' + message;
eventValue.setAttribute('data-event-status', 'received');
})
.then(function (unsubscribe) {
trackCleanup(unsubscribe);
return api.events.publish('verstak.platform-test.echo', { message: 'hello-event' });
})
.catch(function (err) {
eventValue.textContent = 'Event error: ' + (err && err.message ? err.message : String(err));
eventValue.setAttribute('data-event-status', 'error');
});
api.files.createFolder('PlatformTest')
.catch(function (err) {
if (String(err).indexOf('conflict') === -1) throw err;
})
.then(function () {
return api.files.writeText('PlatformTest/files-api.txt', 'hello files', { createIfMissing: true, overwrite: true });
})
.then(function () {
return api.files.readText('PlatformTest/files-api.txt');
})
.then(function (text) {
if (text !== 'hello files') throw new Error('read mismatch');
return api.files.list('PlatformTest');
})
.then(function (entries) {
var found = entries.some(function (entry) {
return entry.relativePath === 'PlatformTest/files-api.txt';
});
if (!found) throw new Error('list missing file');
return api.files.move('PlatformTest/files-api.txt', 'PlatformTest/files-api-moved.txt', { overwrite: true });
})
.then(function () {
return api.files.trash('PlatformTest/files-api-moved.txt');
})
.then(function () {
filesValue.textContent = 'Files: wrote/read/listed/moved/trashed';
filesValue.setAttribute('data-files-status', 'ok');
})
.catch(function (err) {
filesValue.textContent = 'Files error: ' + (err && err.message ? err.message : String(err));
filesValue.setAttribute('data-files-status', 'error');
});
api.files.readText('.verstak/vault.json')
.then(function () {
filesErrorValue.textContent = 'Files error path: unexpectedly allowed';
filesErrorValue.setAttribute('data-files-error-status', 'error');
})
.catch(function (err) {
var message = err && err.message ? err.message : String(err);
if (message.indexOf('reserved-path') === -1 && message.indexOf('.verstak') === -1) {
filesErrorValue.textContent = 'Files error path: wrong error ' + message;
filesErrorValue.setAttribute('data-files-error-status', 'error');
return;
}
filesErrorValue.textContent = 'Files error path: rejected reserved-path';
filesErrorValue.setAttribute('data-files-error-status', 'expected');
});
var bridgeCard = div('pt-card', [
el('h3', { className: 'pt-card-title' }, ['Real Plugin API Bridge']),
el('ul', { className: 'pt-list' }, [
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Persisted setting'),
savedValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'New value'),
settingInput,
]),
el('li', { className: 'pt-list-item' }, [
saveButton,
saveStatus,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Capabilities'),
capabilityValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Command runtime'),
commandValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Event runtime'),
eventValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Files runtime'),
filesValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Files reserved path'),
filesErrorValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Workbench routing'),
openTextWorkbenchButton,
openMarkdownWorkbenchButton,
openNotesWorkbenchButton,
workbenchValue,
]),
]),
]);
/* ── Test results summary ──────────────────────────────────── */
var testsData = [
{ label: 'Plugin Registration', status: 'pass' },
{ label: 'Capability: verstak/platform-test/v1', status: 'pass' },
{ label: 'Capability: verstak/diagnostics/v1', status: 'pass' },
{ label: 'Capability: verstak/core/files/v1', status: 'pass' },
{ label: 'Capability: verstak/core/workbench/v1', status: 'pass' },
{ label: 'API Contract Compliance', status: 'pass' },
];
@@ -133,22 +372,29 @@
{ id: 'verstak/diagnostics/v1', label: 'Diagnostics API' },
{ id: 'verstak/core/vault/v1', label: 'Vault API (optional)' },
{ id: 'verstak/core/sync/v1', label: 'Sync API (optional)' },
{ id: 'verstak/core/workbench/v1', label: 'Workbench API' },
];
var capList = el('ul', { className: 'pt-list' });
knownCaps.forEach(function (cap) {
var available = api.capabilities && api.capabilities.has
? api.capabilities.has(cap.id)
: false;
var dot = el('span', {
className: 'pt-cap-dot ' + (available ? 'pt-cap-dot-ok' : 'pt-cap-dot-missing'),
className: 'pt-cap-dot pt-cap-dot-missing',
});
var statusVal = available ? '✓ Available' : '— Unavailable';
var statusText = span('pt-list-value', 'Checking...');
var item = el('li', { className: 'pt-list-item' }, [
el('span', { className: 'pt-list-label' }, [dot, ' ', cap.label]),
span('pt-list-value', statusVal),
statusText,
]);
capList.appendChild(item);
api.capabilities.has(cap.id)
.then(function (available) {
dot.className = 'pt-cap-dot ' + (available ? 'pt-cap-dot-ok' : 'pt-cap-dot-missing');
statusText.textContent = available ? '✓ Available' : '— Unavailable';
})
.catch(function () {
dot.className = 'pt-cap-dot pt-cap-dot-missing';
statusText.textContent = 'Error';
});
});
var capsCard = div('pt-card', [
@@ -188,6 +434,12 @@
{ label: 'settings.write', ok: typeof api.settings.write === 'function' },
{ label: 'commands.execute', ok: typeof api.commands.execute === 'function' },
{ label: 'capabilities.has', ok: typeof api.capabilities.has === 'function' },
{ label: 'files.list', ok: typeof api.files.list === 'function' },
{ label: 'files.readText', ok: typeof api.files.readText === 'function' },
{ label: 'files.writeText', ok: typeof api.files.writeText === 'function' },
{ label: 'files.trash', ok: typeof api.files.trash === 'function' },
{ label: 'workbench.openResource', ok: typeof api.workbench.openResource === 'function' },
{ label: 'workbench.editResource', ok: typeof api.workbench.editResource === 'function' },
];
apiChecks.forEach(function (chk) {
var dot = el('span', {
@@ -209,12 +461,55 @@
/* ── Assemble ──────────────────────────────────────────────── */
containerEl.appendChild(header);
containerEl.appendChild(badgeRow);
containerEl.appendChild(bridgeCard);
containerEl.appendChild(testsCard);
containerEl.appendChild(capsCard);
containerEl.appendChild(infoCard);
containerEl.appendChild(apiCard);
},
unmount: function (containerEl) {
if (Array.isArray(containerEl.__ptCleanup)) {
while (containerEl.__ptCleanup.length > 0) {
var cleanup = containerEl.__ptCleanup.pop();
try { cleanup(); } catch (e) { console.error('[platform-test] cleanup error:', e); }
}
}
containerEl.innerHTML = '';
containerEl.className = '';
delete containerEl.__ptCleanup;
},
};
/* ------------------------------------------------------------------ */
/* MarkdownDiagnosticProvider component */
/* ------------------------------------------------------------------ */
var MarkdownDiagnosticProvider = {
mount: function (containerEl, props) {
injectStyles();
var request = props && props.request ? props.request : {};
var context = request.context && (request.context.notesMode || request.context.isInsideNotesFolder)
? 'notes-markdown'
: ((request.extension === '.md' || request.extension === '.markdown') ? 'generic-markdown' : 'generic-text');
containerEl.innerHTML = '';
containerEl.className = 'pt-root';
var result = div('pt-card pt-workbench-result', [
el('h2', { className: 'pt-plugin-name' }, ['Workbench Diagnostic Provider']),
el('p', { className: 'pt-plugin-id' }, [
'Workbench: opened ' + (request.path || '') + ' with ' + ((props && props.providerId) || '') +
' mode=' + (request.mode || '') + ' context=' + context,
]),
]);
result.setAttribute('data-workbench-status', 'ok');
result.setAttribute('data-resource-path', request.path || '');
result.setAttribute('data-resource-mode', request.mode || '');
result.setAttribute('data-resource-context', context);
containerEl.appendChild(result);
},
unmount: function (containerEl) {
containerEl.innerHTML = '';
containerEl.className = '';
@@ -258,22 +553,22 @@
span('pt-counter-label', 'clicks (session only, no persistence)'),
]);
var incrementBtn = el('button', { className: 'pt-btn pt-btn-accent', onClick: function () {
var incrementBtn = el('button', { className: 'btn btn-primary', onClick: function () {
counterState.value += 1;
counterDisplay.firstChild.textContent = String(counterState.value);
}}, ['+ Increment']);
var decrementBtn = el('button', { className: 'pt-btn', onClick: function () {
var decrementBtn = el('button', { className: 'btn btn-secondary', onClick: function () {
counterState.value = Math.max(0, counterState.value - 1);
counterDisplay.firstChild.textContent = String(counterState.value);
}}, [' Decrement']);
var resetBtn = el('button', { className: 'pt-btn', onClick: function () {
var resetBtn = el('button', { className: 'btn btn-secondary', onClick: function () {
counterState.value = 0;
counterDisplay.firstChild.textContent = '0';
}}, ['↺ Reset']);
var btnGroup = div('', { style: { display: 'flex', gap: '0.5rem' } }, [
var btnGroup = el('div', { style: { display: 'flex', gap: '0.5rem' } }, [
incrementBtn, decrementBtn, resetBtn,
]);
@@ -331,6 +626,7 @@
components: {
DiagnosticsPanel: DiagnosticsPanel,
PlatformTestSettings: PlatformTestSettings,
MarkdownDiagnosticProvider: MarkdownDiagnosticProvider,
},
});
})();
+306 -10
View File
@@ -63,6 +63,17 @@
containerEl.innerHTML = '';
containerEl.className = 'pt-root';
containerEl.__ptCleanup = [];
function trackCleanup(fn) {
if (typeof fn === 'function') {
if (!Array.isArray(containerEl.__ptCleanup)) {
try { fn(); } catch (e) { console.error('[platform-test] late cleanup error:', e); }
return;
}
containerEl.__ptCleanup.push(fn);
}
}
/* ── Header ─────────────────────────────────────────────────── */
var header = div('pt-header', [
@@ -81,11 +92,239 @@
]);
var badgeRow = div('', [badge]);
/* ── Real Plugin API bridge checks ─────────────────────────── */
var savedValue = span('pt-list-value pt-saved-setting', 'Saved setting: loading...');
var capabilityValue = span('pt-list-value pt-capability-result', 'Capabilities: loading...');
var commandValue = span('pt-list-value pt-command-result', 'Command: registering...');
var eventValue = span('pt-list-value pt-event-result', 'Event: subscribing...');
var filesValue = span('pt-list-value pt-files-result', 'Files: running...');
var filesErrorValue = span('pt-list-value pt-files-error-result', 'Files error path: checking...');
var workbenchValue = span('pt-list-value pt-workbench-result', 'Workbench: ready');
function makeWorkbenchButton(className, label, request) {
return el('button', {
className: 'btn btn-primary ' + className,
onClick: function () {
workbenchValue.textContent = 'Workbench: opening...';
api.workbench.editResource(request)
.then(function (result) {
workbenchValue.textContent = 'Workbench: opened ' + result.request.path + ' with ' + (result.providerId || 'no-provider');
workbenchValue.setAttribute('data-workbench-status', result.status === 'opened' ? 'ok' : result.status);
})
.catch(function (err) {
workbenchValue.textContent = 'Workbench error: ' + (err && err.message ? err.message : String(err));
workbenchValue.setAttribute('data-workbench-status', 'error');
});
},
}, [label]);
}
var openTextWorkbenchButton = makeWorkbenchButton('pt-open-workbench-text', 'Open Text Diagnostic', {
kind: 'vault-file',
path: 'Docs/todo.txt',
extension: '.txt',
mime: 'text/plain',
context: { sourceView: 'files' },
});
var openMarkdownWorkbenchButton = makeWorkbenchButton('pt-open-workbench-markdown', 'Open Markdown Diagnostic', {
kind: 'vault-file',
path: 'Docs/readme.md',
extension: '.md',
context: { sourceView: 'files' },
});
var openNotesWorkbenchButton = makeWorkbenchButton('pt-open-workbench-notes', 'Open Notes Diagnostic', {
kind: 'vault-file',
path: 'Notes/Overview.md',
extension: '.md',
context: {
sourceView: 'notes',
isInsideNotesFolder: true,
notesMode: true,
},
});
var settingInput = el('input', {
className: 'pt-setting-input',
type: 'text',
'aria-label': 'Saved setting',
value: 'changed value',
});
var saveStatus = span('pt-list-value', '');
var saveButton = el('button', {
className: 'btn btn-primary pt-save-setting',
onClick: function () {
saveStatus.textContent = 'Saving...';
api.settings.write('savedText', settingInput.value)
.then(function () {
savedValue.textContent = 'Saved setting: ' + settingInput.value;
saveStatus.textContent = 'Saved';
})
.catch(function (err) {
saveStatus.textContent = 'Error: ' + (err && err.message ? err.message : String(err));
});
},
}, ['Save Setting']);
api.settings.read('savedText')
.then(function (value) {
var text = value || '';
settingInput.value = text || 'changed value';
savedValue.textContent = 'Saved setting: ' + text;
})
.catch(function (err) {
savedValue.textContent = 'Settings error: ' + (err && err.message ? err.message : String(err));
});
api.capabilities.list()
.then(function (caps) {
capabilityValue.textContent = 'Capabilities: ' + caps.length + ' available';
})
.catch(function (err) {
capabilityValue.textContent = 'Capabilities error: ' + (err && err.message ? err.message : String(err));
});
api.capabilities.has('verstak/platform-test/v1')
.then(function (available) {
badge.setAttribute('data-capability-status', available ? 'available' : 'missing');
badge.lastChild.textContent = 'Frontend Bundle Loaded | capability ' + (available ? 'available' : 'missing');
})
.catch(function (err) {
badge.setAttribute('data-capability-status', 'error');
badge.lastChild.textContent = 'Capability error: ' + (err && err.message ? err.message : String(err));
});
api.commands.register('verstak.platform-test.show-version', function () {
return {
version: '0.1.0',
source: 'bundled-frontend',
};
})
.then(function (unregister) {
trackCleanup(unregister);
return api.commands.execute('verstak.platform-test.show-version', {});
})
.then(function (result) {
badge.setAttribute('data-command-status', result.status || '');
commandValue.textContent = 'Command: ' + result.status + ' ' + result.result.version + ' from ' + result.result.source;
})
.catch(function (err) {
badge.setAttribute('data-command-status', 'error');
commandValue.textContent = 'Command error: ' + (err && err.message ? err.message : String(err));
console.error('[platform-test] command bridge error:', err);
});
api.events.subscribe('verstak.platform-test.echo', function (event) {
var message = event && event.payload ? event.payload.message : '';
eventValue.textContent = 'Event: received ' + message;
eventValue.setAttribute('data-event-status', 'received');
})
.then(function (unsubscribe) {
trackCleanup(unsubscribe);
return api.events.publish('verstak.platform-test.echo', { message: 'hello-event' });
})
.catch(function (err) {
eventValue.textContent = 'Event error: ' + (err && err.message ? err.message : String(err));
eventValue.setAttribute('data-event-status', 'error');
});
api.files.createFolder('PlatformTest')
.catch(function (err) {
if (String(err).indexOf('conflict') === -1) throw err;
})
.then(function () {
return api.files.writeText('PlatformTest/files-api.txt', 'hello files', { createIfMissing: true, overwrite: true });
})
.then(function () {
return api.files.readText('PlatformTest/files-api.txt');
})
.then(function (text) {
if (text !== 'hello files') throw new Error('read mismatch');
return api.files.list('PlatformTest');
})
.then(function (entries) {
var found = entries.some(function (entry) {
return entry.relativePath === 'PlatformTest/files-api.txt';
});
if (!found) throw new Error('list missing file');
return api.files.move('PlatformTest/files-api.txt', 'PlatformTest/files-api-moved.txt', { overwrite: true });
})
.then(function () {
return api.files.trash('PlatformTest/files-api-moved.txt');
})
.then(function () {
filesValue.textContent = 'Files: wrote/read/listed/moved/trashed';
filesValue.setAttribute('data-files-status', 'ok');
})
.catch(function (err) {
filesValue.textContent = 'Files error: ' + (err && err.message ? err.message : String(err));
filesValue.setAttribute('data-files-status', 'error');
});
api.files.readText('.verstak/vault.json')
.then(function () {
filesErrorValue.textContent = 'Files error path: unexpectedly allowed';
filesErrorValue.setAttribute('data-files-error-status', 'error');
})
.catch(function (err) {
var message = err && err.message ? err.message : String(err);
if (message.indexOf('reserved-path') === -1 && message.indexOf('.verstak') === -1) {
filesErrorValue.textContent = 'Files error path: wrong error ' + message;
filesErrorValue.setAttribute('data-files-error-status', 'error');
return;
}
filesErrorValue.textContent = 'Files error path: rejected reserved-path';
filesErrorValue.setAttribute('data-files-error-status', 'expected');
});
var bridgeCard = div('pt-card', [
el('h3', { className: 'pt-card-title' }, ['Real Plugin API Bridge']),
el('ul', { className: 'pt-list' }, [
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Persisted setting'),
savedValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'New value'),
settingInput,
]),
el('li', { className: 'pt-list-item' }, [
saveButton,
saveStatus,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Capabilities'),
capabilityValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Command runtime'),
commandValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Event runtime'),
eventValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Files runtime'),
filesValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Files reserved path'),
filesErrorValue,
]),
el('li', { className: 'pt-list-item' }, [
span('pt-list-label', 'Workbench routing'),
openTextWorkbenchButton,
openMarkdownWorkbenchButton,
openNotesWorkbenchButton,
workbenchValue,
]),
]),
]);
/* ── Test results summary ──────────────────────────────────── */
var testsData = [
{ label: 'Plugin Registration', status: 'pass' },
{ label: 'Capability: verstak/platform-test/v1', status: 'pass' },
{ label: 'Capability: verstak/diagnostics/v1', status: 'pass' },
{ label: 'Capability: verstak/core/files/v1', status: 'pass' },
{ label: 'Capability: verstak/core/workbench/v1', status: 'pass' },
{ label: 'API Contract Compliance', status: 'pass' },
];
@@ -133,22 +372,29 @@
{ id: 'verstak/diagnostics/v1', label: 'Diagnostics API' },
{ id: 'verstak/core/vault/v1', label: 'Vault API (optional)' },
{ id: 'verstak/core/sync/v1', label: 'Sync API (optional)' },
{ id: 'verstak/core/workbench/v1', label: 'Workbench API' },
];
var capList = el('ul', { className: 'pt-list' });
knownCaps.forEach(function (cap) {
var available = api.capabilities && api.capabilities.has
? api.capabilities.has(cap.id)
: false;
var dot = el('span', {
className: 'pt-cap-dot ' + (available ? 'pt-cap-dot-ok' : 'pt-cap-dot-missing'),
className: 'pt-cap-dot pt-cap-dot-missing',
});
var statusVal = available ? '✓ Available' : '— Unavailable';
var statusText = span('pt-list-value', 'Checking...');
var item = el('li', { className: 'pt-list-item' }, [
el('span', { className: 'pt-list-label' }, [dot, ' ', cap.label]),
span('pt-list-value', statusVal),
statusText,
]);
capList.appendChild(item);
api.capabilities.has(cap.id)
.then(function (available) {
dot.className = 'pt-cap-dot ' + (available ? 'pt-cap-dot-ok' : 'pt-cap-dot-missing');
statusText.textContent = available ? '✓ Available' : '— Unavailable';
})
.catch(function () {
dot.className = 'pt-cap-dot pt-cap-dot-missing';
statusText.textContent = 'Error';
});
});
var capsCard = div('pt-card', [
@@ -188,6 +434,12 @@
{ label: 'settings.write', ok: typeof api.settings.write === 'function' },
{ label: 'commands.execute', ok: typeof api.commands.execute === 'function' },
{ label: 'capabilities.has', ok: typeof api.capabilities.has === 'function' },
{ label: 'files.list', ok: typeof api.files.list === 'function' },
{ label: 'files.readText', ok: typeof api.files.readText === 'function' },
{ label: 'files.writeText', ok: typeof api.files.writeText === 'function' },
{ label: 'files.trash', ok: typeof api.files.trash === 'function' },
{ label: 'workbench.openResource', ok: typeof api.workbench.openResource === 'function' },
{ label: 'workbench.editResource', ok: typeof api.workbench.editResource === 'function' },
];
apiChecks.forEach(function (chk) {
var dot = el('span', {
@@ -209,12 +461,55 @@
/* ── Assemble ──────────────────────────────────────────────── */
containerEl.appendChild(header);
containerEl.appendChild(badgeRow);
containerEl.appendChild(bridgeCard);
containerEl.appendChild(testsCard);
containerEl.appendChild(capsCard);
containerEl.appendChild(infoCard);
containerEl.appendChild(apiCard);
},
unmount: function (containerEl) {
if (Array.isArray(containerEl.__ptCleanup)) {
while (containerEl.__ptCleanup.length > 0) {
var cleanup = containerEl.__ptCleanup.pop();
try { cleanup(); } catch (e) { console.error('[platform-test] cleanup error:', e); }
}
}
containerEl.innerHTML = '';
containerEl.className = '';
delete containerEl.__ptCleanup;
},
};
/* ------------------------------------------------------------------ */
/* MarkdownDiagnosticProvider component */
/* ------------------------------------------------------------------ */
var MarkdownDiagnosticProvider = {
mount: function (containerEl, props) {
injectStyles();
var request = props && props.request ? props.request : {};
var context = request.context && (request.context.notesMode || request.context.isInsideNotesFolder)
? 'notes-markdown'
: ((request.extension === '.md' || request.extension === '.markdown') ? 'generic-markdown' : 'generic-text');
containerEl.innerHTML = '';
containerEl.className = 'pt-root';
var result = div('pt-card pt-workbench-result', [
el('h2', { className: 'pt-plugin-name' }, ['Workbench Diagnostic Provider']),
el('p', { className: 'pt-plugin-id' }, [
'Workbench: opened ' + (request.path || '') + ' with ' + ((props && props.providerId) || '') +
' mode=' + (request.mode || '') + ' context=' + context,
]),
]);
result.setAttribute('data-workbench-status', 'ok');
result.setAttribute('data-resource-path', request.path || '');
result.setAttribute('data-resource-mode', request.mode || '');
result.setAttribute('data-resource-context', context);
containerEl.appendChild(result);
},
unmount: function (containerEl) {
containerEl.innerHTML = '';
containerEl.className = '';
@@ -258,22 +553,22 @@
span('pt-counter-label', 'clicks (session only, no persistence)'),
]);
var incrementBtn = el('button', { className: 'pt-btn pt-btn-accent', onClick: function () {
var incrementBtn = el('button', { className: 'btn btn-primary', onClick: function () {
counterState.value += 1;
counterDisplay.firstChild.textContent = String(counterState.value);
}}, ['+ Increment']);
var decrementBtn = el('button', { className: 'pt-btn', onClick: function () {
var decrementBtn = el('button', { className: 'btn btn-secondary', onClick: function () {
counterState.value = Math.max(0, counterState.value - 1);
counterDisplay.firstChild.textContent = String(counterState.value);
}}, [' Decrement']);
var resetBtn = el('button', { className: 'pt-btn', onClick: function () {
var resetBtn = el('button', { className: 'btn btn-secondary', onClick: function () {
counterState.value = 0;
counterDisplay.firstChild.textContent = '0';
}}, ['↺ Reset']);
var btnGroup = div('', { style: { display: 'flex', gap: '0.5rem' } }, [
var btnGroup = el('div', { style: { display: 'flex', gap: '0.5rem' } }, [
incrementBtn, decrementBtn, resetBtn,
]);
@@ -331,6 +626,7 @@
components: {
DiagnosticsPanel: DiagnosticsPanel,
PlatformTestSettings: PlatformTestSettings,
MarkdownDiagnosticProvider: MarkdownDiagnosticProvider,
},
});
})();
+31 -3
View File
@@ -12,11 +12,14 @@
"verstak/diagnostics/v1"
],
"requires": [
"verstak/core/plugin-manager/v1"
"verstak/core/plugin-manager/v1",
"verstak/core/capability-registry/v1"
],
"optionalRequires": [
"verstak/core/vault/v1",
"verstak/core/sync/v1"
"verstak/core/sync/v1",
"verstak/core/files/v1",
"verstak/core/workbench/v1"
],
"permissions": [
"vault.read",
@@ -24,7 +27,11 @@
"events.subscribe",
"ui.register",
"commands.register",
"storage.namespace"
"storage.namespace",
"files.read",
"files.write",
"files.delete",
"workbench.open"
],
"frontend": {
"entry": "frontend/dist/index.js"
@@ -74,6 +81,27 @@
"icon": "flask",
"component": "PlatformTestSettings"
}
],
"openProviders": [
{
"id": "verstak.platform-test.markdown-diagnostic",
"title": "Platform Test Markdown Diagnostic",
"priority": 100,
"component": "MarkdownDiagnosticProvider",
"supports": [
{
"kind": "vault-file",
"extensions": [".md", ".markdown"],
"contexts": ["generic-markdown", "notes-markdown"]
},
{
"kind": "vault-file",
"mime": ["text/plain"],
"extensions": [".txt", ".log"],
"contexts": ["generic-text"]
}
]
}
]
}
}