feat: milestones 6b-fix through 6e — default-editor, files plugin, workspace host, workspaceItems contribution

- Fix PluginCard openProviders display
- Add default-editor plugin (text/markdown/notes-context)
- Add files plugin with workspaceItems placement
- Add workspaceItems contribution point (Go + API + mock + SDK)
- Add WorkspaceHost component for workspace area
- WorkspaceTree dispatches selection event
- Fix default-editor layout to fill container
- Fix PluginCard unsafe .length access
- Add E2E tests: 34/34 pass
- Add bundle execution check to official-plugins check.sh
- Update docs: PLUGIN_RUNTIME, DEV_PLUGINS, MILESTONE_6B/6C/6D plans
This commit is contained in:
2026-06-19 16:42:01 +08:00
parent 6ed6df311a
commit a6412fa070
23 changed files with 1518 additions and 286 deletions
+135
View File
@@ -0,0 +1,135 @@
import { test, expect } from '@playwright/test';
import { waitForAppReady, setupConsoleCollector, resetMockState } from './helpers.js';
test.describe('F: Default Editor Plugin', () => {
let consoleCollector;
test.beforeEach(async ({ page }) => {
consoleCollector = setupConsoleCollector(page);
await resetMockState(page);
await page.goto('/');
await waitForAppReady(page);
});
test.afterEach(async () => {
consoleCollector.assertNoErrors();
});
test('open .txt file shows plain text editor mode', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Docs/todo.txt',
extension: '.txt',
context: { sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const editor = page.locator('[data-editor-mode="text"]');
await expect(editor).toBeVisible({ timeout: 10000 });
await expect(editor).toHaveAttribute('data-resource-path', 'Docs/todo.txt');
await expect(editor).toHaveAttribute('data-request-mode', 'view');
const textarea = editor.locator('[data-editor-textarea]');
await expect(textarea).toBeVisible();
await expect(textarea).toHaveValue('Buy groceries\nWrite tests');
});
test('open .md file outside Notes routes to highest-priority provider', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Docs/readme.md',
extension: '.md',
context: { sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const workbench = page.locator('.workbench-host');
await expect(workbench).toBeVisible({ timeout: 10000 });
const title = workbench.locator('.workbench-title');
await expect(title).toHaveText('Docs/readme.md');
});
test('open .md with notes context routes to highest-priority provider', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Notes/Overview.md',
extension: '.md',
context: { sourceView: 'notes', isInsideNotesFolder: true, notesMode: true },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const workbench = page.locator('.workbench-host');
await expect(workbench).toBeVisible({ timeout: 10000 });
const title = workbench.locator('.workbench-title');
await expect(title).toHaveText('Notes/Overview.md');
});
test('default-editor plugin is listed as loaded in plugin manager', async ({ page }) => {
await page.locator('.sidebar .nav-item').filter({ hasText: 'Plugin Manager' }).click();
const card = page.locator('.plugin-card').filter({ hasText: 'verstak.default-editor' });
await expect(card).toBeVisible({ timeout: 10000 });
await expect(card.locator('.status-badge')).toHaveText('loaded');
});
test('disable default-editor plugin removes its providers', async ({ page }) => {
await page.locator('.sidebar .nav-item').filter({ hasText: 'Plugin Manager' }).click();
const card = page.locator('.plugin-card').filter({ hasText: 'verstak.default-editor' });
await card.locator('button.btn-disable').click();
await expect(card.locator('button.btn-enable')).toBeVisible({ timeout: 10000 });
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Docs/todo.txt',
extension: '.txt',
context: { sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
await expect(page.locator('[data-workbench-status="no-provider"]')).toBeVisible({ timeout: 10000 });
});
test('default-editor plugin card shows openProviders contribution count', async ({ page }) => {
await page.locator('.sidebar .nav-item').filter({ hasText: 'Plugin Manager' }).click();
const card = page.locator('.plugin-card').filter({ hasText: 'verstak.default-editor' });
await expect(card).toBeVisible({ timeout: 10000 });
await expect(card.locator('.meta-row').filter({ hasText: 'Contributions:' })).toContainText('3 openProviders');
});
test('default-editor does not add sidebar item', async ({ page }) => {
const sidebarItems = page.locator('.sidebar .plugin-item');
const count = await sidebarItems.count();
const hasDefaultEditor = await page.locator('.sidebar .plugin-item').filter({ hasText: /default.editor/i }).count();
expect(hasDefaultEditor).toBe(0);
});
test('platform-test workbench buttons open files via default-editor', async ({ page }) => {
await page.locator('.sidebar .plugin-item').filter({ hasText: 'Platform Test' }).click();
await expect(page.locator('.pt-command-result')).toContainText('Command: handled', { timeout: 10000 });
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Docs/todo.txt',
extension: '.txt',
context: { sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const editor = page.locator('[data-editor-mode="text"]');
await expect(editor).toBeVisible({ timeout: 10000 });
await expect(editor).toHaveAttribute('data-resource-path', 'Docs/todo.txt');
});
});
+93
View File
@@ -0,0 +1,93 @@
import { test, expect } from '@playwright/test';
import { waitForAppReady, setupConsoleCollector, resetMockState } from './helpers.js';
test.describe('G: Files Plugin', () => {
let consoleCollector;
test.beforeEach(async ({ page }) => {
consoleCollector = setupConsoleCollector(page);
await resetMockState(page);
await page.goto('/');
await waitForAppReady(page);
});
test.afterEach(async () => {
consoleCollector.assertNoErrors();
});
test('files plugin appears in Plugin Manager as loaded', async ({ page }) => {
await page.locator('.sidebar .nav-item').filter({ hasText: 'Plugin Manager' }).click();
const card = page.locator('.plugin-card').filter({ hasText: 'verstak.files' });
await expect(card).toBeVisible({ timeout: 10000 });
await expect(card.locator('.status-badge')).toHaveText('loaded');
});
test('files plugin does not add global sidebar item', async ({ page }) => {
const sidebarItem = page.locator('.sidebar .plugin-item').filter({ hasText: 'Files' });
await expect(sidebarItem).toHaveCount(0);
});
test('open .txt via workbench from files context shows default-editor', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.files', {
kind: 'vault-file',
path: 'Docs/todo.txt',
extension: '.txt',
context: { sourcePluginId: 'verstak.files', sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const editor = page.locator('[data-editor-mode="text"]');
await expect(editor).toBeVisible({ timeout: 10000 });
await expect(editor).toHaveAttribute('data-resource-path', 'Docs/todo.txt');
});
test('open .md via workbench from files context shows generic-markdown', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.files', {
kind: 'vault-file',
path: 'Docs/readme.md',
extension: '.md',
context: { sourcePluginId: 'verstak.files', sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const workbench = page.locator('.workbench-host');
await expect(workbench).toBeVisible({ timeout: 10000 });
await expect(workbench.locator('.workbench-title')).toHaveText('Docs/readme.md');
});
test('open notes markdown via workbench from files context shows notes-markdown', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.files', {
kind: 'vault-file',
path: 'Notes/Overview.md',
extension: '.md',
context: { sourcePluginId: 'verstak.files', sourceView: 'files', isInsideNotesFolder: true, notesMode: true },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const workbench = page.locator('.workbench-host');
await expect(workbench).toBeVisible({ timeout: 10000 });
await expect(workbench.locator('.workbench-title')).toHaveText('Notes/Overview.md');
});
test('files plugin card shows openProviders in contributions', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.files', {
kind: 'vault-file', path: 'test.txt', extension: '.txt',
context: { sourcePluginId: 'verstak.files', sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const editor = page.locator('[data-editor-mode="text"]');
await expect(editor).toBeVisible({ timeout: 5000 });
});
});
+30 -19
View File
@@ -33,6 +33,7 @@ test.describe('D: Plugin API bridge', () => {
await expect(page.locator('.plugin-card').filter({ hasText: 'verstak.platform-test' }).locator('.status-badge')).toHaveText('loaded', { timeout: 10000 });
await page.locator('.sidebar .plugin-item').filter({ hasText: 'Platform Test' }).click();
await expect(page.locator('.pt-saved-setting')).toHaveText('Saved setting: persisted through bridge', { timeout: 10000 });
await expect(page.locator('.pt-badge')).toHaveAttribute('data-command-status', 'handled');
await expect(page.locator('.pt-badge')).toContainText('capability available');
@@ -44,28 +45,38 @@ test.describe('D: Plugin API bridge', () => {
await expect(page.locator('.pt-files-error-result')).toHaveAttribute('data-files-error-status', 'expected');
await expect(page.locator('.pt-files-error-result')).toContainText('Files error path: rejected reserved-path');
await page.locator('.pt-open-workbench-notes').click();
await expect(page.locator('.pt-workbench-result')).toHaveAttribute('data-workbench-status', 'ok');
await expect(page.locator('.pt-workbench-result')).toContainText('Workbench: opened Notes/Overview.md with verstak.platform-test.markdown-diagnostic');
await expect(page.locator('.pt-workbench-result')).toHaveAttribute('data-resource-context', 'notes-markdown');
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Notes/Overview.md',
extension: '.md',
context: { sourceView: 'notes', isInsideNotesFolder: true, notesMode: true },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
const workbench = page.locator('.workbench-host');
await expect(workbench).toBeVisible({ timeout: 10000 });
await expect(workbench.locator('.workbench-title')).toHaveText('Notes/Overview.md');
});
test('platform-test diagnostic provider routes text, markdown, and notes markdown contexts', async ({ page }) => {
async function openFromDiagnostics(buttonClass, path, context) {
await page.locator('.sidebar .plugin-item').filter({ hasText: 'Platform Test' }).click();
await expect(page.locator('.pt-command-result')).toContainText('Command: handled', { timeout: 10000 });
await page.locator(buttonClass).click();
const result = page.locator('.pt-workbench-result');
await expect(result).toHaveAttribute('data-workbench-status', 'ok', { timeout: 10000 });
await expect(result).toHaveAttribute('data-resource-path', path);
await expect(result).toHaveAttribute('data-resource-mode', 'edit');
await expect(result).toHaveAttribute('data-resource-context', context);
await expect(result).toContainText('verstak.platform-test.markdown-diagnostic');
}
test('workbench routes markdown files to default-editor provider', async ({ page }) => {
await page.evaluate(async () => {
const [result, err] = await window.go.api.App.OpenWorkbenchResource('verstak.platform-test', {
kind: 'vault-file',
path: 'Docs/readme.md',
extension: '.md',
context: { sourceView: 'files' },
});
if (err) throw new Error(err);
window.dispatchEvent(new CustomEvent('verstak:workbench-opened', { detail: result }));
});
await openFromDiagnostics('.pt-open-workbench-text', 'Docs/todo.txt', 'generic-text');
await openFromDiagnostics('.pt-open-workbench-markdown', 'Docs/readme.md', 'generic-markdown');
await openFromDiagnostics('.pt-open-workbench-notes', 'Notes/Overview.md', 'notes-markdown');
const workbench = page.locator('.workbench-host');
await expect(workbench).toBeVisible({ timeout: 10000 });
const title = workbench.locator('.workbench-title');
await expect(title).toHaveText('Docs/readme.md');
});
test('workbench shows no-provider fallback when no provider matches', async ({ page }) => {
+1 -1
View File
@@ -18,7 +18,7 @@ test.describe('E: Plugin Manager layout', () => {
test('plugin list scrolls through the global main scroll surface and stays responsive', async ({ page }) => {
await page.evaluate(() => window.__wailsMock.addSyntheticPlugins(18));
await page.locator('button.reload-btn').click();
await expect(page.locator('.plugin-card')).toHaveCount(19, { timeout: 10000 });
await expect(page.locator('.plugin-card')).toHaveCount(21, { timeout: 10000 });
const manager = page.locator('.plugin-manager');
const scrollSurface = page.locator('.content.scroll-surface');
+16
View File
@@ -4,6 +4,7 @@
import ViewContainer from './lib/shell/ViewContainer.svelte';
import VaultSelection from './lib/shell/VaultSelection.svelte';
import WorkbenchHost from './lib/shell/WorkbenchHost.svelte';
import WorkspaceHost from './lib/shell/WorkspaceHost.svelte';
import * as App from '../wailsjs/go/api/App';
import { debug } from './lib/log/debug.js';
import { onMount } from 'svelte';
@@ -20,6 +21,9 @@
let activeSettingsPanelId = '';
let openedResource = null;
let workspaceNodes = [];
let currentWorkspaceNodeId = '';
function flog(msg) {
App.WriteFrontendLog('App', msg);
}
@@ -91,6 +95,15 @@
currentView = 'workbench';
}
function onWorkspaceNodeSelected(e) {
debug.log('[App] onWorkspaceNodeSelected:', e.detail?.nodeId);
currentWorkspaceNodeId = e.detail?.nodeId || '';
workspaceNodes = e.detail?.nodes || workspaceNodes;
if (currentWorkspaceNodeId) {
currentView = 'workspace';
}
}
function onCloseSettings() {
debug.log('[App] onCloseSettings');
activeSettingsPluginId = '';
@@ -105,6 +118,7 @@
window.addEventListener('verstak:open-settings', onOpenSettings);
window.addEventListener('verstak:close-settings', onCloseSettings);
window.addEventListener('verstak:workbench-opened', onWorkbenchOpened);
window.addEventListener('verstak:workspace-node-selected', onWorkspaceNodeSelected);
}
onMount(() => { checkVault(); });
@@ -125,6 +139,8 @@
<PluginManager {activeSettingsPluginId} {activeSettingsPanelId} />
{:else if currentView === 'workbench'}
<WorkbenchHost {openedResource} />
{:else if currentView === 'workspace'}
<WorkspaceHost currentNodeId={currentWorkspaceNodeId} nodes={workspaceNodes} />
{:else}
<ViewContainer {activeView} {activeViewPluginId} />
{/if}
@@ -32,6 +32,7 @@
commands: (contributions.commands || []).filter(c => c.pluginId === pluginId).length,
sidebar: (contributions.sidebarItems || []).filter(s => s.pluginId === pluginId).length,
statusbar: (contributions.statusBarItems || []).filter(s => s.pluginId === pluginId).length,
openProviders: (contributions.openProviders || []).filter(o => o.pluginId === pluginId).length,
};
$: contribSummary = (() => {
@@ -40,6 +41,7 @@
if (contribCounts.commands > 0) parts.push(contribCounts.commands + ' command' + (contribCounts.commands !== 1 ? 's' : ''));
if (contribCounts.sidebar > 0) parts.push(contribCounts.sidebar + ' sidebar' + (contribCounts.sidebar !== 1 ? 's' : ''));
if (contribCounts.statusbar > 0) parts.push(contribCounts.statusbar + ' statusbar' + (contribCounts.statusbar !== 1 ? 's' : ''));
if (contribCounts.openProviders > 0) parts.push(contribCounts.openProviders + ' openProvider' + (contribCounts.openProviders !== 1 ? 's' : ''));
return parts.length > 0 ? parts.join(', ') : 'none';
})();
@@ -200,7 +202,7 @@
</div>
<!-- Permission warnings -->
{#if !hasUIPermission && (m.contributes && (m.contributes.views || m.contributes.sidebarItems || m.contributes.settingsPanels).length > 0)}
{#if !hasUIPermission && m.contributes && ((m.contributes.views || []).length > 0 || (m.contributes.sidebarItems || []).length > 0 || (m.contributes.settingsPanels || []).length > 0)}
<p class="warning"><Icon name="warning" size={12} /> Plugin has UI contributions but lacks ui.register permission</p>
{/if}
</div>
+4 -1
View File
@@ -89,8 +89,11 @@
.workbench-content {
min-width: 0;
min-height: 0;
height: 100%;
flex: 1;
padding: 1rem;
display: flex;
flex-direction: column;
padding: 0;
}
.workbench-empty {
+163
View File
@@ -0,0 +1,163 @@
<script>
import PluginBundleHost from '../plugin-host/PluginBundleHost.svelte';
import * as App from '../../../wailsjs/go/api/App';
export let currentNodeId = '';
export let nodes = [];
let contributions = {};
let plugins = [];
let workspaceTools = [];
$: currentNode = nodes.find(n => n.id === currentNodeId) || null;
$: if (currentNodeId) loadTools();
async function loadTools() {
try {
const [c, p] = await Promise.all([
App.GetContributions().catch(() => ({})),
App.GetPlugins().catch(() => []),
]);
contributions = c || {};
plugins = p || [];
const enabledIds = new Set(
plugins.filter(pl => pl.enabled && (pl.status === 'loaded' || pl.status === 'degraded')).map(pl => pl.manifest?.id)
);
workspaceTools = (contributions.workspaceItems || []).filter(tool => enabledIds.has(tool.pluginId));
} catch (e) {
console.error('[WorkspaceHost] loadTools error:', e);
}
}
</script>
<div class="workspace-host">
{#if currentNode}
<div class="workspace-header">
<span class="workspace-title">{currentNode.title}</span>
<span class="workspace-type">{currentNode.type}</span>
</div>
{#if workspaceTools.length > 0}
<div class="workspace-tools">
{#each workspaceTools as tool (tool.id + tool.pluginId)}
<div class="workspace-tool">
<div class="tool-header">
<span class="tool-title">{tool.title || tool.id}</span>
<span class="tool-plugin">{tool.pluginId}</span>
</div>
<div class="tool-content">
<PluginBundleHost
pluginId={tool.pluginId}
componentId={tool.component}
componentProps={{ workspaceNodeId: currentNodeId, workspaceNode: currentNode }}
/>
</div>
</div>
{/each}
</div>
{:else}
<div class="workspace-empty">
<p>No workspace tools available</p>
<p class="workspace-hint">Install plugins that contribute workspaceItems to see tools here.</p>
</div>
{/if}
{:else}
<div class="workspace-empty">
<p>Select a workspace node from the sidebar</p>
</div>
{/if}
</div>
<style>
.workspace-host {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
height: 100%;
background: #1a1a2e;
}
.workspace-header {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
border-bottom: 1px solid #16213e;
flex-shrink: 0;
}
.workspace-title {
color: #e0e0f0;
font-size: 0.95rem;
font-weight: 600;
}
.workspace-type {
color: #4ecca3;
font-size: 0.75rem;
padding: 0.1rem 0.4rem;
border-radius: 3px;
background: #1a2a3a;
}
.workspace-tools {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 0.5rem;
}
.workspace-tool {
border: 1px solid #16213e;
border-radius: 6px;
margin-bottom: 0.5rem;
overflow: hidden;
}
.tool-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.75rem;
background: #12122a;
border-bottom: 1px solid #16213e;
}
.tool-title {
color: #e0e0f0;
font-size: 0.8rem;
font-weight: 600;
}
.tool-plugin {
color: #666;
font-size: 0.65rem;
margin-left: auto;
}
.tool-content {
min-height: 300px;
max-height: 60vh;
overflow: auto;
}
.workspace-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #666;
gap: 0.5rem;
}
.workspace-hint {
font-size: 0.8rem;
color: #555;
max-width: 300px;
text-align: center;
}
</style>
@@ -78,6 +78,9 @@
if (err) { localError = err; return; }
currentNodeId = id;
activeWorkspaceNodeId.set(id);
window.dispatchEvent(new CustomEvent('verstak:workspace-node-selected', {
detail: { nodeId: id, nodes: nodes }
}));
}
function openCreate(parentId, type) {
+283 -11
View File
@@ -47,11 +47,10 @@
{
id: 'verstak.platform-test.markdown-diagnostic',
title: 'Platform Test Markdown Diagnostic',
priority: 100,
priority: 10,
component: 'MarkdownDiagnosticProvider',
supports: [
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['generic-markdown', 'notes-markdown'] },
{ kind: 'vault-file', extensions: ['.txt', '.log'], mime: ['text/plain'], contexts: ['generic-text'] }
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['generic-markdown', 'notes-markdown'] }
]
}
]
@@ -59,11 +58,86 @@
},
rootPath: '/tmp/verstak-test/plugins/platform-test',
error: ''
},
'verstak.default-editor': {
status: 'loaded',
enabled: true,
manifest: {
schemaVersion: 1,
id: 'verstak.default-editor',
name: 'Default Editor',
version: '0.1.0',
apiVersion: '0.1.0',
description: 'Built-in text and markdown editor/viewer.',
source: 'official',
icon: 'edit',
provides: ['verstak/default-editor/v1'],
requires: ['verstak/core/files/v1', 'verstak/core/workbench/v1'],
permissions: ['files.read', 'files.write', 'workbench.open'],
frontend: { entry: 'frontend/dist/index.js' },
contributes: {
openProviders: [
{
id: 'verstak.default-editor.text',
title: 'Default Text Editor',
priority: 50,
component: 'DefaultEditor',
supports: [
{ kind: 'vault-file', extensions: ['.txt', '.log', '.conf', '.ini', '.toml', '.yaml', '.yml', '.json', '.csv'], mime: ['text/plain', 'application/json'], contexts: ['generic-text'] }
]
},
{
id: 'verstak.default-editor.markdown',
title: 'Default Markdown Editor',
priority: 50,
component: 'DefaultEditor',
supports: [
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['generic-markdown'] }
]
},
{
id: 'verstak.default-editor.notes-markdown',
title: 'Default Notes Markdown Editor',
priority: 50,
component: 'DefaultEditor',
supports: [
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['notes-markdown'] }
]
}
]
}
},
rootPath: '/tmp/verstak-test/plugins/default-editor',
error: ''
},
'verstak.files': {
status: 'loaded',
enabled: true,
manifest: {
schemaVersion: 1,
id: 'verstak.files',
name: 'Files',
version: '0.1.0',
apiVersion: '0.1.0',
description: 'Minimal vault file navigator.',
source: 'official',
icon: 'folder',
provides: ['verstak/files/v1'],
requires: ['verstak/core/files/v1', 'verstak/core/workbench/v1'],
permissions: ['files.read', 'files.write', 'workbench.open', 'ui.register'],
frontend: { entry: 'frontend/dist/index.js' },
contributes: {
views: [{ id: 'verstak.files.view', title: 'Files', icon: 'folder', component: 'FilesView' }],
workspaceItems: [{ id: 'verstak.files.workspace', title: 'Files', icon: 'folder', component: 'FilesView' }]
}
},
rootPath: '/tmp/verstak-test/plugins/files',
error: ''
}
};
var vaultStatus = { status: 'open', path: '/tmp/verstak-test/vault', vaultId: 'test-vault-001' };
var vaultPluginState = { enabledPlugins: ['verstak.platform-test'], disabledPlugins: [], desiredPlugins: [{ id: 'verstak.platform-test', version: '0.1.0', source: 'official' }] };
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 appSettings = { currentVaultPath: '/tmp/verstak-test/vault', recentVaults: [] };
var workbenchPreferences = {};
var openedResources = [];
@@ -97,7 +171,12 @@
function makeDefaultVaultFiles() {
return {
'': { type: 'folder', modifiedAt: new Date().toISOString() }
'': { type: 'folder', modifiedAt: new Date().toISOString() },
'Docs': { type: 'folder', modifiedAt: new Date().toISOString() },
'Docs/todo.txt': { type: 'file', content: 'Buy groceries\nWrite tests', modifiedAt: new Date().toISOString() },
'Docs/readme.md': { type: 'file', content: '# Hello World\n\nThis is a **test** document.\n\n- item 1\n- item 2', modifiedAt: new Date().toISOString() },
'Notes': { type: 'folder', modifiedAt: new Date().toISOString() },
'Notes/Overview.md': { type: 'file', content: '# Notes Overview\n\nMy notes content here.', modifiedAt: new Date().toISOString() }
};
}
@@ -204,7 +283,7 @@
}
function allContributions() {
var views = [], commands = [], sidebarItems = [], statusBarItems = [], settingsPanels = [], openProviders = [];
var views = [], commands = [], sidebarItems = [], statusBarItems = [], settingsPanels = [], openProviders = [], workspaceItems = [];
for (var id in pluginStates) {
var s = pluginStates[id];
var c = (s.manifest && s.manifest.contributes) || {};
@@ -214,8 +293,9 @@
if (c.statusBarItems) c.statusBarItems.forEach(function (st) { statusBarItems.push(Object.assign({}, st, { pluginId: id })); });
if (c.settingsPanels) c.settingsPanels.forEach(function (sp) { settingsPanels.push(Object.assign({}, sp, { pluginId: id })); });
if (c.openProviders) c.openProviders.forEach(function (op) { openProviders.push(Object.assign({}, op, { pluginId: id })); });
if (c.workspaceItems) c.workspaceItems.forEach(function (wi) { workspaceItems.push(Object.assign({}, wi, { pluginId: id })); });
}
return { views: views, commands: commands, sidebarItems: sidebarItems, statusBarItems: statusBarItems, settingsPanels: settingsPanels, openProviders: openProviders };
return { views: views, commands: commands, sidebarItems: sidebarItems, statusBarItems: statusBarItems, settingsPanels: settingsPanels, openProviders: openProviders, workspaceItems: workspaceItems };
}
function requestExtension(request) {
@@ -294,6 +374,118 @@
return Promise.resolve([result, '']);
}
function defaultEditorBundle() {
return [
'(function(){',
'var DefaultEditor={',
'mount:function(c,p,api){',
'c.innerHTML="";',
'c.className="de-root";',
'var req=p.request||{};',
'var path=req.path||"";',
'var mode=req.mode||"view";',
'var ctx=req.context||{};',
'var isNotes=ctx.notesMode||ctx.isInsideNotesFolder;',
'var ext=(req.extension||"").toLowerCase();',
'var isMd=ext===".md"||ext===".markdown";',
'var editorMode=isNotes?"notes-markdown":isMd?"generic-markdown":"text";',
'c.setAttribute("data-editor-mode",editorMode);',
'c.setAttribute("data-resource-path",path);',
'c.setAttribute("data-request-mode",mode);',
'var toolbar=document.createElement("div");',
'toolbar.className="de-toolbar";',
'var modeLabel=document.createElement("span");',
'modeLabel.className="de-toolbar-mode";',
'modeLabel.textContent=editorMode;',
'toolbar.appendChild(modeLabel);',
'var pathLabel=document.createElement("span");',
'pathLabel.className="de-toolbar-context";',
'pathLabel.textContent=path;',
'toolbar.appendChild(pathLabel);',
'if(isNotes){var badge=document.createElement("span");badge.className="de-notes-badge";badge.textContent="notes context";badge.setAttribute("data-notes-badge","");toolbar.appendChild(badge);}',
'c.appendChild(toolbar);',
'var content=document.createElement("div");',
'content.className="de-editor-wrap";',
'content.textContent="Loading...";',
'c.appendChild(content);',
'api.files.readText(path).then(function(text){',
'content.textContent="";',
'if(isMd){',
'var preview=document.createElement("div");',
'preview.className="de-preview";',
'preview.setAttribute("data-preview","");',
'preview.textContent=text;',
'content.appendChild(preview);',
'}else{',
'var ta=document.createElement("textarea");',
'ta.className="de-textarea";',
'ta.value=text;',
'ta.setAttribute("data-editor-textarea","");',
'content.appendChild(ta);',
'}',
'}).catch(function(err){',
'content.textContent="Error: "+(err.message||err);',
'});',
'},',
'unmount:function(c){c.innerHTML="";}',
'};',
'window.VerstakPluginRegister("verstak.default-editor",{components:{DefaultEditor:DefaultEditor}});',
'})();'
].join('\n');
}
function filesPluginBundle() {
return [
"(function(){",
"var FilesView={",
"mount:function(c,p,api){",
"c.innerHTML='';",
"c.className='files-root';",
"c.setAttribute('data-plugin-id','verstak.files');",
"var list=document.createElement('div');",
"list.className='files-list';",
"list.setAttribute('data-files-list','');",
"c.appendChild(list);",
"function load(){",
"list.textContent='Loading...';",
"api.files.list('').then(function(entries){",
"list.innerHTML='';",
"if(!entries||!entries.length){list.textContent='Empty folder';return;}",
"entries.forEach(function(e){",
"if(e.isHidden||e.isReserved)return;",
"var item=document.createElement('div');",
"item.className='files-item';",
"item.setAttribute('data-file-name',e.name);",
"item.setAttribute('data-file-type',e.type);",
"item.setAttribute('data-file-path',e.relativePath);",
"var icon=document.createElement('span');",
"icon.className='files-item-icon';",
"icon.textContent=e.type==='folder'?'[D]':'[F]';",
"var name=document.createElement('span');",
"name.className='files-item-name';",
"name.textContent=e.name;",
"item.appendChild(icon);",
"item.appendChild(name);",
"if(e.type!=='folder'){",
"item.addEventListener('dblclick',function(){",
"var ext=e.extension?'.'+e.extension:'';",
"var ctx={sourcePluginId:'verstak.files',sourceView:'files'};",
"api.workbench.openResource({kind:'vault-file',path:e.relativePath,mode:'view',extension:ext,context:ctx});",
"});",
"}",
"list.appendChild(item);",
"});",
"}).catch(function(err){list.textContent='Error: '+(err.message||err);});",
"}",
"load();",
"},",
"unmount:function(c){c.innerHTML='';}",
"};",
"window.VerstakPluginRegister('verstak.files',{components:{FilesView:FilesView}});",
"})();"
].join('\n');
}
function platformTestBundle() {
return [
"(function(){",
@@ -448,6 +640,12 @@
if (pluginId === 'verstak.platform-test' && assetPath === 'frontend/dist/index.js') {
return Promise.resolve(platformTestBundle());
}
if (pluginId === 'verstak.default-editor' && assetPath === 'frontend/dist/index.js') {
return Promise.resolve(defaultEditorBundle());
}
if (pluginId === 'verstak.files' && assetPath === 'frontend/dist/index.js') {
return Promise.resolve(filesPluginBundle());
}
return Promise.resolve('');
},
GetPluginCapability: function (pluginId, capId) {
@@ -675,11 +873,10 @@
{
id: 'verstak.platform-test.markdown-diagnostic',
title: 'Platform Test Markdown Diagnostic',
priority: 100,
priority: 10,
component: 'MarkdownDiagnosticProvider',
supports: [
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['generic-markdown', 'notes-markdown'] },
{ kind: 'vault-file', extensions: ['.txt', '.log'], mime: ['text/plain'], contexts: ['generic-text'] }
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['generic-markdown', 'notes-markdown'] }
]
}
]
@@ -687,10 +884,85 @@
},
rootPath: '/tmp/verstak-test/plugins/platform-test',
error: ''
},
'verstak.default-editor': {
status: 'loaded',
enabled: true,
manifest: {
schemaVersion: 1,
id: 'verstak.default-editor',
name: 'Default Editor',
version: '0.1.0',
apiVersion: '0.1.0',
description: 'Built-in text and markdown editor/viewer.',
source: 'official',
icon: 'edit',
provides: ['verstak/default-editor/v1'],
requires: ['verstak/core/files/v1', 'verstak/core/workbench/v1'],
permissions: ['files.read', 'files.write', 'workbench.open'],
frontend: { entry: 'frontend/dist/index.js' },
contributes: {
openProviders: [
{
id: 'verstak.default-editor.text',
title: 'Default Text Editor',
priority: 50,
component: 'DefaultEditor',
supports: [
{ kind: 'vault-file', extensions: ['.txt', '.log', '.conf', '.ini', '.toml', '.yaml', '.yml', '.json', '.csv'], mime: ['text/plain', 'application/json'], contexts: ['generic-text'] }
]
},
{
id: 'verstak.default-editor.markdown',
title: 'Default Markdown Editor',
priority: 50,
component: 'DefaultEditor',
supports: [
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['generic-markdown'] }
]
},
{
id: 'verstak.default-editor.notes-markdown',
title: 'Default Notes Markdown Editor',
priority: 50,
component: 'DefaultEditor',
supports: [
{ kind: 'vault-file', extensions: ['.md', '.markdown'], contexts: ['notes-markdown'] }
]
}
]
}
},
rootPath: '/tmp/verstak-test/plugins/default-editor',
error: ''
},
'verstak.files': {
status: 'loaded',
enabled: true,
manifest: {
schemaVersion: 1,
id: 'verstak.files',
name: 'Files',
version: '0.1.0',
apiVersion: '0.1.0',
description: 'Minimal vault file navigator.',
source: 'official',
icon: 'folder',
provides: ['verstak/files/v1'],
requires: ['verstak/core/files/v1', 'verstak/core/workbench/v1'],
permissions: ['files.read', 'files.write', 'workbench.open', 'ui.register'],
frontend: { entry: 'frontend/dist/index.js' },
contributes: {
views: [{ id: 'verstak.files.view', title: 'Files', icon: 'folder', component: 'FilesView' }],
workspaceItems: [{ id: 'verstak.files.workspace', title: 'Files', icon: 'folder', component: 'FilesView' }]
}
},
rootPath: '/tmp/verstak-test/plugins/files',
error: ''
}
};
vaultStatus = { status: 'open', path: '/tmp/verstak-test/vault', vaultId: 'test-vault-001' };
vaultPluginState = { enabledPlugins: ['verstak.platform-test'], disabledPlugins: [], desiredPlugins: [{ id: 'verstak.platform-test', version: '0.1.0', source: 'official' }] };
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' }] };
appSettings = { currentVaultPath: '/tmp/verstak-test/vault', recentVaults: [] };
workbenchPreferences = {};
openedResources = [];
+5 -5
View File
@@ -1,11 +1,11 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {workbench} from '../models';
import {capability} from '../models';
import {api} from '../models';
import {permissions} from '../models';
import {plugin} from '../models';
import {files} from '../models';
import {workbench} from '../models';
export function ArchiveWorkspaceNode(arg1:string):Promise<string>;
@@ -19,12 +19,12 @@ export function CreateWorkspaceNode(arg1:string,arg2:string,arg3:string):Promise
export function DisablePlugin(arg1:string):Promise<string>;
export function EditWorkbenchResource(arg1:string,arg2:Record<string, any>):Promise<workbench.OpenResourceResult|string>;
export function EnablePlugin(arg1:string):Promise<string>;
export function ExecutePluginCommand(arg1:string,arg2:string,arg3:Record<string, any>):Promise<Record<string, any>|string>;
export function EditWorkbenchResource(arg1:string,arg2:Record<string, any>):Promise<workbench.OpenResourceResult|string>;
export function GetAppSettings():Promise<Record<string, any>>;
export function GetCapabilities():Promise<Array<capability.Entry>>;
@@ -49,12 +49,12 @@ export function GetVaultPluginState():Promise<Record<string, any>>;
export function GetVaultStatus():Promise<Record<string, string>>;
export function GetWorkspaceTree():Promise<Record<string, any>>;
export function GetWorkbenchOpenedResources():Promise<Array<workbench.OpenedResource>>;
export function GetWorkbenchPreferences():Promise<workbench.Preferences>;
export function GetWorkspaceTree():Promise<Record<string, any>>;
export function ListPluginCapabilities(arg1:string):Promise<Array<capability.Entry>|string>;
export function ListVaultFiles(arg1:string,arg2:string):Promise<Array<files.FileEntry>|string>;
+8 -8
View File
@@ -26,6 +26,10 @@ export function DisablePlugin(arg1) {
return window['go']['api']['App']['DisablePlugin'](arg1);
}
export function EditWorkbenchResource(arg1, arg2) {
return window['go']['api']['App']['EditWorkbenchResource'](arg1, arg2);
}
export function EnablePlugin(arg1) {
return window['go']['api']['App']['EnablePlugin'](arg1);
}
@@ -34,10 +38,6 @@ export function ExecutePluginCommand(arg1, arg2, arg3) {
return window['go']['api']['App']['ExecutePluginCommand'](arg1, arg2, arg3);
}
export function EditWorkbenchResource(arg1, arg2) {
return window['go']['api']['App']['EditWorkbenchResource'](arg1, arg2);
}
export function GetAppSettings() {
return window['go']['api']['App']['GetAppSettings']();
}
@@ -86,10 +86,6 @@ export function GetVaultStatus() {
return window['go']['api']['App']['GetVaultStatus']();
}
export function GetWorkspaceTree() {
return window['go']['api']['App']['GetWorkspaceTree']();
}
export function GetWorkbenchOpenedResources() {
return window['go']['api']['App']['GetWorkbenchOpenedResources']();
}
@@ -98,6 +94,10 @@ export function GetWorkbenchPreferences() {
return window['go']['api']['App']['GetWorkbenchPreferences']();
}
export function GetWorkspaceTree() {
return window['go']['api']['App']['GetWorkspaceTree']();
}
export function ListPluginCapabilities(arg1) {
return window['go']['api']['App']['ListPluginCapabilities'](arg1);
}
+344 -239
View File
@@ -1,15 +1,35 @@
export namespace api {
export class FlatWorkspaceItem {
pluginId: string;
id: string;
title: string;
icon?: string;
component: string;
static createFrom(source: any = {}) {
return new FlatWorkspaceItem(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.pluginId = source["pluginId"];
this.id = source["id"];
this.title = source["title"];
this.icon = source["icon"];
this.component = source["component"];
}
}
export class FlatOpenProviderSupport {
kind: string;
mime?: string[];
extensions?: string[];
contexts?: string[];
static createFrom(source: any = {}) {
return new FlatOpenProviderSupport(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.kind = source["kind"];
@@ -25,11 +45,11 @@ export namespace api {
priority?: number;
component: string;
supports: FlatOpenProviderSupport[];
static createFrom(source: any = {}) {
return new FlatOpenProvider(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.pluginId = source["pluginId"];
@@ -39,7 +59,7 @@ export namespace api {
this.component = source["component"];
this.supports = this.convertValues(source["supports"], FlatOpenProviderSupport);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
@@ -65,11 +85,11 @@ export namespace api {
icon?: string;
view: string;
position?: number;
static createFrom(source: any = {}) {
return new FlatSidebarItem(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.pluginId = source["pluginId"];
@@ -86,11 +106,11 @@ export namespace api {
title: string;
icon?: string;
component: string;
static createFrom(source: any = {}) {
return new FlatSettingsPanel(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.pluginId = source["pluginId"];
@@ -106,11 +126,11 @@ export namespace api {
title: string;
icon?: string;
handler?: string;
static createFrom(source: any = {}) {
return new FlatCommand(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.pluginId = source["pluginId"];
@@ -126,11 +146,11 @@ export namespace api {
title: string;
icon?: string;
component: string;
static createFrom(source: any = {}) {
return new FlatView(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.pluginId = source["pluginId"];
@@ -146,11 +166,12 @@ export namespace api {
settingsPanels: FlatSettingsPanel[];
sidebarItems: FlatSidebarItem[];
openProviders: FlatOpenProvider[];
workspaceItems: FlatWorkspaceItem[];
static createFrom(source: any = {}) {
return new ContributionSummary(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.views = this.convertValues(source["views"], FlatView);
@@ -158,8 +179,9 @@ export namespace api {
this.settingsPanels = this.convertValues(source["settingsPanels"], FlatSettingsPanel);
this.sidebarItems = this.convertValues(source["sidebarItems"], FlatSidebarItem);
this.openProviders = this.convertValues(source["openProviders"], FlatOpenProvider);
this.workspaceItems = this.convertValues(source["workspaceItems"], FlatWorkspaceItem);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
@@ -178,185 +200,27 @@ export namespace api {
return a;
}
}
}
export namespace workbench {
export class OpenResourceContext {
sourcePluginId?: string;
sourceView?: string;
isInsideNotesFolder?: boolean;
notesScopePath?: string;
notesMode?: boolean;
static createFrom(source: any = {}) {
return new OpenResourceContext(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.sourcePluginId = source["sourcePluginId"];
this.sourceView = source["sourceView"];
this.isInsideNotesFolder = source["isInsideNotesFolder"];
this.notesScopePath = source["notesScopePath"];
this.notesMode = source["notesMode"];
}
}
export class OpenResourceRequest {
kind: string;
path: string;
mode?: string;
mime?: string;
extension?: string;
context?: OpenResourceContext;
static createFrom(source: any = {}) {
return new OpenResourceRequest(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.kind = source["kind"];
this.path = source["path"];
this.mode = source["mode"];
this.mime = source["mime"];
this.extension = source["extension"];
this.context = this.convertValues(source["context"], OpenResourceContext);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class OpenResourceResult {
status: string;
providerId?: string;
providerPluginId?: string;
providerComponent?: string;
request: OpenResourceRequest;
message?: string;
static createFrom(source: any = {}) {
return new OpenResourceResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.status = source["status"];
this.providerId = source["providerId"];
this.providerPluginId = source["providerPluginId"];
this.providerComponent = source["providerComponent"];
this.request = this.convertValues(source["request"], OpenResourceRequest);
this.message = source["message"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class OpenedResource {
id: string;
providerId: string;
providerPluginId: string;
providerComponent: string;
request: OpenResourceRequest;
openedAt: string;
static createFrom(source: any = {}) {
return new OpenedResource(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.providerId = source["providerId"];
this.providerPluginId = source["providerPluginId"];
this.providerComponent = source["providerComponent"];
this.request = this.convertValues(source["request"], OpenResourceRequest);
this.openedAt = source["openedAt"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class Preferences {
defaultTextEditorProvider?: string;
defaultMarkdownEditorProvider?: string;
defaultNotesMarkdownEditorProvider?: string;
static createFrom(source: any = {}) {
return new Preferences(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.defaultTextEditorProvider = source["defaultTextEditorProvider"];
this.defaultMarkdownEditorProvider = source["defaultMarkdownEditorProvider"];
this.defaultNotesMarkdownEditorProvider = source["defaultNotesMarkdownEditorProvider"];
}
}
}
export namespace capability {
export class Entry {
name: string;
description?: string;
pluginId: string;
status: string;
static createFrom(source: any = {}) {
return new Entry(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
@@ -369,7 +233,7 @@ export namespace capability {
}
export namespace files {
export class FileEntry {
name: string;
relativePath: string;
@@ -381,11 +245,11 @@ export namespace files {
isReserved: boolean;
canRead: boolean;
canWrite: boolean;
static createFrom(source: any = {}) {
return new FileEntry(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
@@ -413,11 +277,11 @@ export namespace files {
isReserved: boolean;
canRead: boolean;
canWrite: boolean;
static createFrom(source: any = {}) {
return new FileMetadata(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.relativePath = source["relativePath"];
@@ -436,11 +300,11 @@ export namespace files {
}
export class MoveOptions {
overwrite: boolean;
static createFrom(source: any = {}) {
return new MoveOptions(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.overwrite = source["overwrite"];
@@ -451,11 +315,11 @@ export namespace files {
trashPath: string;
trashId: string;
deletedAt: string;
static createFrom(source: any = {}) {
return new TrashResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.originalPath = source["originalPath"];
@@ -467,11 +331,11 @@ export namespace files {
export class WriteOptions {
createIfMissing: boolean;
overwrite: boolean;
static createFrom(source: any = {}) {
return new WriteOptions(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.createIfMissing = source["createIfMissing"];
@@ -482,16 +346,16 @@ export namespace files {
}
export namespace permissions {
export class Entry {
name: string;
description: string;
dangerous: boolean;
static createFrom(source: any = {}) {
return new Entry(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
@@ -503,15 +367,15 @@ export namespace permissions {
}
export namespace plugin {
export class HealthCheckConfig {
type?: string;
timeout?: number;
static createFrom(source: any = {}) {
return new HealthCheckConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.type = source["type"];
@@ -522,18 +386,18 @@ export namespace plugin {
type: string;
entry: Record<string, string>;
healthCheck?: HealthCheckConfig;
static createFrom(source: any = {}) {
return new BackendConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.type = source["type"];
this.entry = source["entry"];
this.healthCheck = this.convertValues(source["healthCheck"], HealthCheckConfig);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
@@ -558,11 +422,11 @@ export namespace plugin {
icon?: string;
capability?: string;
handler?: string;
static createFrom(source: any = {}) {
return new ContributionAction(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -576,11 +440,11 @@ export namespace plugin {
id: string;
events?: string[];
handler: string;
static createFrom(source: any = {}) {
return new ContributionActivityProvider(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -594,11 +458,11 @@ export namespace plugin {
keybinding?: string;
icon?: string;
handler?: string;
static createFrom(source: any = {}) {
return new ContributionCommand(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -615,11 +479,11 @@ export namespace plugin {
group?: string;
capability?: string;
handler?: string;
static createFrom(source: any = {}) {
return new ContributionContextMenuEntry(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -630,15 +494,71 @@ export namespace plugin {
this.handler = source["handler"];
}
}
export class OpenProviderSupport {
kind: string;
mime?: string[];
extensions?: string[];
contexts?: string[];
static createFrom(source: any = {}) {
return new OpenProviderSupport(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.kind = source["kind"];
this.mime = source["mime"];
this.extensions = source["extensions"];
this.contexts = source["contexts"];
}
}
export class ContributionOpenProvider {
id: string;
title: string;
priority?: number;
component: string;
supports: OpenProviderSupport[];
static createFrom(source: any = {}) {
return new ContributionOpenProvider(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.title = source["title"];
this.priority = source["priority"];
this.component = source["component"];
this.supports = this.convertValues(source["supports"], OpenProviderSupport);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class ContributionSearchProvider {
id: string;
label: string;
handler: string;
static createFrom(source: any = {}) {
return new ContributionSearchProvider(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -651,11 +571,11 @@ export namespace plugin {
title: string;
component: string;
icon?: string;
static createFrom(source: any = {}) {
return new ContributionSettingsPanel(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -670,11 +590,11 @@ export namespace plugin {
icon?: string;
view: string;
position?: number;
static createFrom(source: any = {}) {
return new ContributionSidebarItem(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -689,11 +609,11 @@ export namespace plugin {
label: string;
position?: string;
handler?: string;
static createFrom(source: any = {}) {
return new ContributionStatusBarItem(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -707,11 +627,29 @@ export namespace plugin {
title: string;
icon?: string;
component: string;
static createFrom(source: any = {}) {
return new ContributionView(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.title = source["title"];
this.icon = source["icon"];
this.component = source["component"];
}
}
export class ContributionWorkspaceItem {
id: string;
title: string;
icon?: string;
component: string;
static createFrom(source: any = {}) {
return new ContributionWorkspaceItem(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
@@ -731,11 +669,13 @@ export namespace plugin {
searchProviders?: ContributionSearchProvider[];
activityProviders?: ContributionActivityProvider[];
statusBarItems?: ContributionStatusBarItem[];
openProviders?: ContributionOpenProvider[];
workspaceItems?: ContributionWorkspaceItem[];
static createFrom(source: any = {}) {
return new Contributions(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.views = this.convertValues(source["views"], ContributionView);
@@ -748,8 +688,10 @@ export namespace plugin {
this.searchProviders = this.convertValues(source["searchProviders"], ContributionSearchProvider);
this.activityProviders = this.convertValues(source["activityProviders"], ContributionActivityProvider);
this.statusBarItems = this.convertValues(source["statusBarItems"], ContributionStatusBarItem);
this.openProviders = this.convertValues(source["openProviders"], ContributionOpenProvider);
this.workspaceItems = this.convertValues(source["workspaceItems"], ContributionWorkspaceItem);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
@@ -771,26 +713,26 @@ export namespace plugin {
export class FrontendConfig {
entry: string;
style?: string;
static createFrom(source: any = {}) {
return new FrontendConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.entry = source["entry"];
this.style = source["style"];
}
}
export class SyncConfig {
namespaces?: string[];
participate?: boolean;
static createFrom(source: any = {}) {
return new SyncConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.namespaces = source["namespaces"];
@@ -799,11 +741,11 @@ export namespace plugin {
}
export class MigrationConfig {
path?: string;
static createFrom(source: any = {}) {
return new MigrationConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.path = source["path"];
@@ -827,11 +769,11 @@ export namespace plugin {
migrations?: MigrationConfig;
contributes?: Contributions;
sync?: SyncConfig;
static createFrom(source: any = {}) {
return new Manifest(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.schemaVersion = source["schemaVersion"];
@@ -852,7 +794,7 @@ export namespace plugin {
this.contributes = this.convertValues(source["contributes"], Contributions);
this.sync = this.convertValues(source["sync"], SyncConfig);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
@@ -871,18 +813,19 @@ export namespace plugin {
return a;
}
}
export class Plugin {
manifest: Manifest;
status: string;
error?: string;
enabled: boolean;
rootPath: string;
static createFrom(source: any = {}) {
return new Plugin(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.manifest = this.convertValues(source["manifest"], Manifest);
@@ -891,7 +834,7 @@ export namespace plugin {
this.enabled = source["enabled"];
this.rootPath = source["rootPath"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
@@ -912,3 +855,165 @@ export namespace plugin {
}
}
export namespace workbench {
export class OpenResourceContext {
sourcePluginId?: string;
sourceView?: string;
isInsideNotesFolder?: boolean;
notesScopePath?: string;
notesMode?: boolean;
static createFrom(source: any = {}) {
return new OpenResourceContext(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.sourcePluginId = source["sourcePluginId"];
this.sourceView = source["sourceView"];
this.isInsideNotesFolder = source["isInsideNotesFolder"];
this.notesScopePath = source["notesScopePath"];
this.notesMode = source["notesMode"];
}
}
export class OpenResourceRequest {
kind: string;
path: string;
mode?: string;
mime?: string;
extension?: string;
context?: OpenResourceContext;
static createFrom(source: any = {}) {
return new OpenResourceRequest(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.kind = source["kind"];
this.path = source["path"];
this.mode = source["mode"];
this.mime = source["mime"];
this.extension = source["extension"];
this.context = this.convertValues(source["context"], OpenResourceContext);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class OpenResourceResult {
status: string;
providerId?: string;
providerPluginId?: string;
providerComponent?: string;
request: OpenResourceRequest;
message?: string;
static createFrom(source: any = {}) {
return new OpenResourceResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.status = source["status"];
this.providerId = source["providerId"];
this.providerPluginId = source["providerPluginId"];
this.providerComponent = source["providerComponent"];
this.request = this.convertValues(source["request"], OpenResourceRequest);
this.message = source["message"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class OpenedResource {
id: string;
providerId: string;
providerPluginId: string;
providerComponent: string;
request: OpenResourceRequest;
openedAt: string;
static createFrom(source: any = {}) {
return new OpenedResource(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.providerId = source["providerId"];
this.providerPluginId = source["providerPluginId"];
this.providerComponent = source["providerComponent"];
this.request = this.convertValues(source["request"], OpenResourceRequest);
this.openedAt = source["openedAt"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class Preferences {
defaultTextEditorProvider?: string;
defaultMarkdownEditorProvider?: string;
defaultNotesMarkdownEditorProvider?: string;
static createFrom(source: any = {}) {
return new Preferences(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.defaultTextEditorProvider = source["defaultTextEditorProvider"];
this.defaultMarkdownEditorProvider = source["defaultMarkdownEditorProvider"];
this.defaultNotesMarkdownEditorProvider = source["defaultNotesMarkdownEditorProvider"];
}
}
}