633 lines
27 KiB
JavaScript
633 lines
27 KiB
JavaScript
/* ===========================================================
|
||
Platform Test Plugin — Verstak v2 Frontend Bundle
|
||
Contract: window.VerstakPluginRegister(id, { components })
|
||
=========================================================== */
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Style injection */
|
||
/* Loads style.css once into the document head */
|
||
/* ------------------------------------------------------------------ */
|
||
function injectStyles() {
|
||
if (document.getElementById('pt-style-injected')) return;
|
||
var link = document.createElement('link');
|
||
link.id = 'pt-style-injected';
|
||
link.rel = 'stylesheet';
|
||
link.href = 'frontend/style.css';
|
||
document.head.appendChild(link);
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Utilities */
|
||
/* ------------------------------------------------------------------ */
|
||
function el(tag, attrs, children) {
|
||
var elem = document.createElement(tag);
|
||
if (attrs) {
|
||
Object.keys(attrs).forEach(function (k) {
|
||
if (k === 'className') { elem.className = attrs[k]; }
|
||
else if (k === 'style' && typeof attrs[k] === 'object') {
|
||
Object.assign(elem.style, attrs[k]);
|
||
}
|
||
else if (k.slice(0, 2) === 'on') {
|
||
elem.addEventListener(k.slice(2).toLowerCase(), attrs[k]);
|
||
}
|
||
else { elem.setAttribute(k, attrs[k]); }
|
||
});
|
||
}
|
||
if (children) {
|
||
(Array.isArray(children) ? children : [children]).forEach(function (c) {
|
||
if (c == null) return;
|
||
elem.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
|
||
});
|
||
}
|
||
return elem;
|
||
}
|
||
|
||
function div(className, children) {
|
||
return el('div', { className: className }, children);
|
||
}
|
||
|
||
function span(className, text) {
|
||
return el('span', { className: className }, [text]);
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* DiagnosticsPanel component */
|
||
/* ------------------------------------------------------------------ */
|
||
var DiagnosticsPanel = {
|
||
mount: function (containerEl, props, api) {
|
||
/* Inject shared styles */
|
||
injectStyles();
|
||
|
||
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', [
|
||
span('pt-icon', '◉'),
|
||
div('pt-title-group', [
|
||
el('h2', { className: 'pt-plugin-name' }, ['Platform Diagnostics']),
|
||
el('p', { className: 'pt-plugin-id' }, [api.pluginId]),
|
||
]),
|
||
span('pt-version', 'v' + (props && props.version ? props.version : '0.1.0')),
|
||
]);
|
||
|
||
/* ── Status badge ──────────────────────────────────────────── */
|
||
var badge = div('pt-badge pt-badge-success', [
|
||
el('span', {}, ['✅']),
|
||
el('span', {}, ['Frontend Bundle Loaded']),
|
||
]);
|
||
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' },
|
||
];
|
||
|
||
var totalTests = testsData.length;
|
||
var passedTests = testsData.filter(function (t) { return t.status === 'pass'; }).length;
|
||
|
||
var summaryRow = div('pt-test-summary', [
|
||
div('pt-test-stat', [
|
||
span('pt-test-stat-value pt-pass', String(passedTests)),
|
||
span('pt-test-stat-label', 'Passed'),
|
||
]),
|
||
div('pt-test-stat', [
|
||
span('pt-test-stat-value pt-fail', String(totalTests - passedTests)),
|
||
span('pt-test-stat-label', 'Failed'),
|
||
]),
|
||
div('pt-test-stat', [
|
||
span('pt-test-stat-value', String(totalTests)),
|
||
span('pt-test-stat-label', 'Total'),
|
||
]),
|
||
div('pt-test-stat', [
|
||
span('pt-test-stat-value pt-pass', '100%'),
|
||
span('pt-test-stat-label', 'Success Rate'),
|
||
]),
|
||
]);
|
||
|
||
var testsList = el('ul', { className: 'pt-list' });
|
||
testsData.forEach(function (t) {
|
||
var dot = el('span', { className: 'pt-cap-dot pt-cap-dot-ok' });
|
||
var item = el('li', { className: 'pt-list-item' }, [
|
||
el('span', { className: 'pt-list-label' }, [dot, ' ', t.label]),
|
||
span('pt-list-value pt-pass', t.status === 'pass' ? '✓ PASS' : '✗ FAIL'),
|
||
]);
|
||
testsList.appendChild(item);
|
||
});
|
||
|
||
var testsCard = div('pt-card', [
|
||
el('h3', { className: 'pt-card-title' }, ['Test Results']),
|
||
summaryRow,
|
||
testsList,
|
||
]);
|
||
|
||
/* ── Capabilities status via API ───────────────────────────── */
|
||
var knownCaps = [
|
||
{ id: 'verstak/platform-test/v1', label: 'Platform Test API' },
|
||
{ 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 dot = el('span', {
|
||
className: 'pt-cap-dot pt-cap-dot-missing',
|
||
});
|
||
var statusText = span('pt-list-value', 'Checking...');
|
||
var item = el('li', { className: 'pt-list-item' }, [
|
||
el('span', { className: 'pt-list-label' }, [dot, ' ', cap.label]),
|
||
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', [
|
||
el('h3', { className: 'pt-card-title' }, ['Registered Capabilities']),
|
||
capList,
|
||
]);
|
||
|
||
/* ── Plugin info ───────────────────────────────────────────── */
|
||
var infoList = el('ul', { className: 'pt-list' });
|
||
var infoItems = [
|
||
{ label: 'Plugin ID', value: api.pluginId },
|
||
{ label: 'Bundle Status', value: 'Loaded ✓' },
|
||
{ label: 'Registration Scheme', value: 'VerstakPluginRegister' },
|
||
{ label: 'Components', value: 'DiagnosticsPanel, PlatformTestSettings' },
|
||
{ label: 'Container', value: containerEl.tagName.toLowerCase() + (containerEl.id ? '#' + containerEl.id : '') },
|
||
];
|
||
infoItems.forEach(function (item) {
|
||
infoList.appendChild(
|
||
el('li', { className: 'pt-list-item' }, [
|
||
span('pt-list-label', item.label),
|
||
span('pt-list-value', item.value),
|
||
])
|
||
);
|
||
});
|
||
|
||
var infoCard = div('pt-card', [
|
||
el('h3', { className: 'pt-card-title' }, ['Plugin Info']),
|
||
infoList,
|
||
]);
|
||
|
||
/* ── Host API status ───────────────────────────────────────── */
|
||
var apiStatusList = el('ul', { className: 'pt-list' });
|
||
var apiChecks = [
|
||
{ label: 'events.publish', ok: typeof api.events.publish === 'function' },
|
||
{ label: 'events.subscribe', ok: typeof api.events.subscribe === 'function' },
|
||
{ label: 'settings.read', ok: typeof api.settings.read === 'function' },
|
||
{ 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', {
|
||
className: 'pt-cap-dot ' + (chk.ok ? 'pt-cap-dot-ok' : 'pt-cap-dot-missing'),
|
||
});
|
||
apiStatusList.appendChild(
|
||
el('li', { className: 'pt-list-item' }, [
|
||
el('span', { className: 'pt-list-label' }, [dot, ' ', chk.label]),
|
||
span('pt-list-value', chk.ok ? '✓ Ready' : '✗ Missing'),
|
||
])
|
||
);
|
||
});
|
||
|
||
var apiCard = div('pt-card', [
|
||
el('h3', { className: 'pt-card-title' }, ['Host API Methods']),
|
||
apiStatusList,
|
||
]);
|
||
|
||
/* ── 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 = '';
|
||
},
|
||
};
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* PlatformTestSettings component */
|
||
/* ------------------------------------------------------------------ */
|
||
var PlatformTestSettings = {
|
||
mount: function (containerEl, props, api) {
|
||
injectStyles();
|
||
|
||
containerEl.innerHTML = '';
|
||
containerEl.className = 'pt-root';
|
||
|
||
/* ── Counter state (local, not persisted) ──────────────────── */
|
||
var counterState = { value: 0 };
|
||
|
||
/* ── Header ─────────────────────────────────────────────────── */
|
||
var header = div('pt-header', [
|
||
span('pt-icon', '⚙️'),
|
||
div('pt-title-group', [
|
||
el('h2', { className: 'pt-plugin-name' }, ['Platform Test Settings']),
|
||
el('p', { className: 'pt-plugin-id' }, [api.pluginId]),
|
||
]),
|
||
]);
|
||
|
||
/* ── Info card ──────────────────────────────────────────────── */
|
||
var infoCard = div('pt-card', [
|
||
el('p', { style: { margin: '0', color: '#a0a0b8', fontSize: '0.85rem' } }, [
|
||
'Settings panel loaded from plugin frontend bundle via ',
|
||
el('code', { style: { background: '#1a1a2e', padding: '0.1rem 0.3rem', borderRadius: '3px', color: '#4ecca3' } }, ['VerstakPluginRegister']),
|
||
' contract.',
|
||
]),
|
||
]);
|
||
|
||
/* ── Counter section ────────────────────────────────────────── */
|
||
var counterDisplay = div('pt-counter', [
|
||
el('span', { className: 'pt-counter-value' }, [String(counterState.value)]),
|
||
span('pt-counter-label', 'clicks (session only, no persistence)'),
|
||
]);
|
||
|
||
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: '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: 'btn btn-secondary', onClick: function () {
|
||
counterState.value = 0;
|
||
counterDisplay.firstChild.textContent = '0';
|
||
}}, ['↺ Reset']);
|
||
|
||
var btnGroup = el('div', { style: { display: 'flex', gap: '0.5rem' } }, [
|
||
incrementBtn, decrementBtn, resetBtn,
|
||
]);
|
||
|
||
var counterCard = div('pt-card', [
|
||
el('h3', { className: 'pt-card-title' }, ['Interactive Counter (Local State)']),
|
||
counterDisplay,
|
||
btnGroup,
|
||
el('p', { style: { marginTop: '0.75rem', color: '#6c6c8a', fontSize: '0.7rem' } }, [
|
||
'This counter is a local demo. State is not persisted — refreshing resets it.',
|
||
]),
|
||
]);
|
||
|
||
/* ── Settings stub ─────────────────────────────────────────── */
|
||
var settingsDemoList = el('ul', { className: 'pt-list' });
|
||
var settingsItems = [
|
||
{ label: 'Auto-run on load', value: 'true' },
|
||
{ label: 'Verbose logging', value: 'false' },
|
||
{ label: 'Theme override', value: 'inherit' },
|
||
{ label: 'Notifications', value: 'enabled' },
|
||
];
|
||
settingsItems.forEach(function (s) {
|
||
settingsDemoList.appendChild(
|
||
el('li', { className: 'pt-list-item' }, [
|
||
span('pt-list-label', s.label),
|
||
span('pt-list-value', s.value),
|
||
])
|
||
);
|
||
});
|
||
|
||
var settingsCard = div('pt-card', [
|
||
el('h3', { className: 'pt-card-title' }, ['Plugin Settings (Demo)']),
|
||
settingsDemoList,
|
||
el('p', { style: { marginTop: '0.5rem', color: '#6c6c8a', fontSize: '0.7rem' } }, [
|
||
'Use api.settings.read() / api.settings.write() for persisted settings.',
|
||
]),
|
||
]);
|
||
|
||
/* ── Assemble ──────────────────────────────────────────────── */
|
||
containerEl.appendChild(header);
|
||
containerEl.appendChild(infoCard);
|
||
containerEl.appendChild(counterCard);
|
||
containerEl.appendChild(settingsCard);
|
||
},
|
||
|
||
unmount: function (containerEl) {
|
||
containerEl.innerHTML = '';
|
||
containerEl.className = '';
|
||
},
|
||
};
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Register with the host */
|
||
/* ------------------------------------------------------------------ */
|
||
window.VerstakPluginRegister('verstak.platform-test', {
|
||
components: {
|
||
DiagnosticsPanel: DiagnosticsPanel,
|
||
PlatformTestSettings: PlatformTestSettings,
|
||
MarkdownDiagnosticProvider: MarkdownDiagnosticProvider,
|
||
},
|
||
});
|
||
})();
|