Plugins and Extensions
Install, review, and build Zync plugins, panels, themes, icon themes, and editor providers.
Zync extensions are local packages. Install them from Settings > Plugins > Marketplace, inspect them under Installed, or load a local package from Developer. A restart may be required after an install or toggle because workers and editor providers are initialized during startup.
Extension types
Section titled “Extension types”| Type | Runtime | Typical use |
|---|---|---|
| Tool or background plugin | Sandboxed Web Worker | Commands, status-bar values, notifications, and background integration |
| Panel plugin | Sandboxed iframe | A custom HTML interface registered as a workspace panel |
| Color theme | Host theme registry for trusted built-ins | App shell and terminal colors |
| Icon theme | Plugin asset resolver | File-type and service icons |
| Editor provider | Sandboxed editor iframe | An alternative editor for local and remote files |
The main.js entry is executed in a Web Worker. It has no DOM or module loader, so bundle dependencies into the package or use browser-compatible JavaScript. A panel registered by the worker is rendered separately in an iframe.
Permissions and side effects
Section titled “Permissions and side effects”Declare the capabilities your package uses in manifest.json so users and marketplace reviewers can inspect the request:
{ "id": "com.example.plugin.deploy", "name": "Deploy helper", "version": "1.0.0", "main": "main.js", "permissions": ["terminal", "statusBar", "ui", "panel"]}The host bridge mediates privileged operations. Sending terminal input, opening a terminal with a command, or running an SSH command requires a user confirmation. File and SSH results are returned to the requesting runtime only. Keep permissions narrow and do not hide destructive operations behind a command with an unclear label.
Plugins do not receive raw Local Vault records, decrypted SSH credentials, provider API keys, or vault passphrases. A plugin can only use the APIs exposed by its bridge and the data a user explicitly makes available through an action.
Worker API
Section titled “Worker API”Register work from the ready event:
zync.on('ready', () => { zync.commands.register('example.ping', 'Example: Ping', async () => { zync.ui.notify({ type: 'info', message: 'Pong' }); });
zync.statusBar.set('example', 'Ready');});The worker API includes:
zync.commands.register(id, title, handler)for command palette entrieszync.terminal.send(text)for terminal input after confirmationzync.statusBar.set(id, text)andzync.statusBar.clear(id)zync.ui.notify(options)andzync.ui.onNotifyAction(callback)zync.fs.readFile,writeFile,ls,exists, andmkdirzync.window.showQuickPick(items, options)andzync.window.create(options)zync.plugins.list()for the loaded plugin descriptorszync.theme.set(themeName)for an allowed theme choicezync.logger.log(message)for app logs
Use Notifications for the plugin notification payload and action response contract.
Panel plugins
Section titled “Panel plugins”Register a panel with HTML that can use the injected window.zync shim:
zync.on('ready', () => { const html = ` <html> <body> <button id="uptime">Run uptime</button> <pre id="output"></pre> <script> document.querySelector('#uptime').onclick = async () => { const result = await window.zync.ssh.exec('uptime'); document.querySelector('#output').textContent = result; }; </script> </body> </html>`;
zync.panel.register('example.panel', 'Example panel', html);});Panel HTML runs in an iframe with scripts and modal dialogs enabled, but it does not get access to the parent document. The parent validates the message source and generation before applying a request. Terminal input and SSH execution still require confirmation.
Keep panel content self-contained. Use textContent for command output and escape any user or host data before inserting it into HTML.
Editor provider contract
Section titled “Editor provider contract”An editor provider is a plugin with type: "editor-provider". Its editor.entry points to the HTML entry file. Capabilities and selection metadata are declared in the manifest:
{ "id": "com.example.editor.my-editor", "name": "My Editor", "version": "1.0.0", "type": "editor-provider", "editor": { "entry": "editor.html", "displayName": "My Editor", "priority": 100, "defaultFor": [".md"], "supports": ["search", "replace", "syntax-highlight", "save"], "fileExtensions": [".md"], "largeFileLimitMb": 20 }}The editor entry runs in an iframe with sandbox="allow-scripts" and an opaque origin. It communicates with Zync through the injected window.zyncEditor bridge:
emitReady()when the editor is initializedemitChange({ content })when the document changesemitDirtyChange(dirty)for unsaved staterequestSave(content)to ask Zync to persist the documentrequestClose()to close the editorreportError(code, message, fatal)for recoverable or fatal errors
The host sends zync:editor:bootstrap and document messages back to the frame. Folding is provided by the editor itself. A provider should declare only capabilities it actually implements.
Icon theme contract
Section titled “Icon theme contract”An icon theme uses type: "icon-theme" and an iconsPath directory containing its SVG assets:
{ "id": "com.example.icons.my-icons", "name": "My Icons", "version": "1.0.0", "type": "icon-theme", "iconsPath": "icons/"}The icon resolver maps file extensions and known service names to files under iconsPath. Keep filenames and mappings aligned with the assets shipped in the package. Zync falls back from the active icon theme to local cache, Lucide, and a generic icon when an asset is missing.
Theme plugins
Section titled “Theme plugins”Color theme manifests use style, mode, preview_bg, and preview_accent:
{ "id": "com.example.theme.my-theme", "name": "My Theme", "version": "1.0.0", "style": "theme.css", "mode": "dark", "preview_bg": "#10131a", "preview_accent": "#7c9cff"}Theme CSS targets Zync’s theme variables under a theme selector. Use the built-in themes as references and test the terminal ANSI colors as well as the app shell. For the current built-in list and count, see Themes.
For security, only app-owned built-in theme CSS is injected into the host document. Third-party plugin styles remain sandboxed, and editor-provider styles stay inside the editor iframe. Do not design a third-party theme that depends on writing global host CSS unless the runtime explicitly adds support for it.
Packaging and installing locally
Section titled “Packaging and installing locally”Put manifest.json at the root of the archive with its referenced files:
zip -r my-plugin.zip manifest.json main.jsFor a panel or editor, include the HTML and any bundled assets too:
zip -r my-editor.zip manifest.json editor.html dist/In Settings > Plugins > Developer, choose the local install option and select the package or plugin directory supported by your build. Disable or remove a package from Installed if it misbehaves, then restart when Zync requests it.
Marketplace and registries
Section titled “Marketplace and registries”The official registry is maintained in zync-extensions. A marketplace entry needs at least id, name, version, and downloadUrl:
{ "id": "com.example.plugin.deploy", "name": "Deploy helper", "version": "1.0.0", "description": "Runs a reviewed deployment workflow.", "author": "Example", "type": "plugin", "downloadUrl": "https://example.com/releases/deploy-1.0.0.zip"}Zync can also read a self-hosted HTTPS registry as a flat array or an object with plugins and themes arrays. Keep download URLs stable and publish checksums or release signatures through your distribution channel when possible.
Publish to the official marketplace
Section titled “Publish to the official marketplace”- Fork the zync-extensions repository.
- Package the extension with
manifest.jsonat the archive root and publish the archive at a stable HTTPS release URL. - Add or update its entry in the registry with the current version, type, download URL, and descriptive metadata.
- Validate that a clean Zync installation can download, install, enable, and remove the package.
- Open a pull request with the registry change and the extension source or review link.
The in-app path is Settings > Plugins > Marketplace for published packages and Settings > Plugins > Developer for local development installs.
Plugin notifications
Section titled “Plugin notifications”Worker plugins can request a toast, inbox record, or both through zync.ui.notify. Payloads are JSON only, so action entries contain IDs and labels rather than JavaScript functions:
await zync.ui.notify({ type: 'error', message: 'Deploy failed', persist: true, channel: 'both', actions: [{ id: 'retry', label: 'Retry' }],});Register zync.ui.onNotifyAction when an actionable notification needs a worker response. Zync namespaces plugin notification IDs by plugin, waits for a bounded response, and keeps the original notification visible when the action fails or times out. See Notifications for the end-user inbox and preference behavior.