kobar-project/kobar/kobar-plugin-developer

Claude Code and agent skill by kobar-project/kobar

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Build, fix or review KoBar plugins (plugin API v1, KoBar 1.4.0 and later). Covers the trust model, the window globals, registering sidebar buttons, panels, settings panels and bar widgets, the rules the host enforces, a complete working example, and pointers to the full API reference, host behaviour, manifest/publishing and platform notes in references/.

Install

npx vskill@latest install kobar-project/kobar/kobar-plugin-developer

SKILL.md

KoBar Plugin Developer

KoBar is a frameless, transparent, always-on-top Electron sidebar. A plugin is a manifest.json plus one JavaScript file that the host injects into the bar's renderer, where it registers buttons, panels and widgets through window.KoBarExtensions.

This guide describes plugin API v1 as shipped in KoBar 1.4.0 and later. KoBar 1.4.0 (the "remaster") changed how the host loads, places, themes, installs and updates plugins; code written from older notes often still runs but breaks in the places listed in section 6. Version notes below name the first public release that has a behaviour (1.2 and 1.3 were never released).

FileRead it when you
SKILL.md (this file)start a plugin, or need the rules and the working example
`references/api.md`call window.api, read useAppStore, use a registry member or a host DOM event
`references/host-behaviour.md`place or style a panel, store data, handle workspaces, hit a permission or security limit
`references/manifest-and-publishing.md`write the manifest, build, package, publish, update, or test locally
`references/platforms.md`target macOS as well as Windows

1. Trust model (read first)

Plugins run with full privileges. A plugin is not sandboxed: it runs in the same renderer realm as KoBar itself, so it can read and write all of localStorage (including the app's stores and other plugins' data), call every window.api method, change the DOM and the app store, and read other workspaces' and other plugins' data. Installing a plugin means trusting its code completely. The host adds friction (confirmation dialogs, rate limits, path checks, allow-lists; see references/host-behaviour.md section 9), but it cannot prove that a call came from a real user action, so none of this is isolation. Write plugins that never touch data that is not theirs.

2. Rules the host enforces or relies on

  1. One classic script, bundled. The host wraps your entry file in (() => { … })() and runs it as a blob

<script data-extension-id="<id>">. import/export statements are syntax errors there. Multi-file or JSX sources must be bundled to a single IIFE file with react / react-dom as externals mapped to window.React / window.ReactDOM.

  1. No Node.js. require, fs, child_process and @electron/remote do not exist. Host services come only through the

globals in section 3.

  1. Register synchronously, at the top level of your script. The registry records which plugin registered what by looking

at document.currentScript, which is only set while your script's first, synchronous run is executing. A registration made later (after await, in a timer or a promise) has no known owner; then the host cannot unload your plugin alone and reloads every plugin whenever any plugin is disabled.

  1. Know your own id. Read it once at the top level: const PLUGIN_ID = document.currentScript?.dataset.extensionId ?? 'your.manifest.id';.

It is your manifest id, or dev-<id> when the plugin runs from the local playground.

  1. Register your settings panel under your plugin id (registerSettingsPanel(PLUGIN_ID, …)). The host looks it up by that

id; any other key is never shown.

  1. Use the same id for your sidebar button and its panel. KoBar 1.4.0 to 1.4.3 mark the button active only then; later

releases also match a button and a panel registered by the same plugin, but the shared id works everywhere.

  1. One root element per panel, readable on dark. The host wraps v1 plugin UI in a dark colour scheme, moves your panel's

root element with the CSS translate property when the user drags it, and never restyles your content. Do not set translate on that root.

  1. Make interactive UI hit-testable. The window is a click-through overlay. Your panel inherits hit-testing from the bar,

but anything you portal elsewhere (for example ReactDOM.createPortal(…, document.body)) needs the pointer-events-auto no-drag-region classes.

  1. Clean up after yourself. Disabling or unloading a plugin removes its script and registrations, but timers, window

listeners and window.api.on* subscriptions started outside React keep running until the window reloads, and a re-enable runs your script again next to them. Start such work inside components and stop it in the useEffect cleanup (every on* method returns an unsubscribe function), or guard it with a flag on window.

  1. Own your storage names. Prefix localStorage keys with your plugin id. Ids and keys starting with kobar or kobox

are reserved for KoBar and its official plugins.

  1. Expect refusals. Many window.api calls can be refused or limited (user confirmation, rate limit, a path the user did

not pick, an unsupported platform). Check every result and feature-check host additions with typeof window.api.x === 'function'.

3. Globals

GlobalWhat it is
window.ReactThe host's React 19. Use createElement and the hooks. Never bundle your own React.
window.ReactDOMThe host's react-dom module (createPortal, flushSync). It is not react-dom/client: there is no createRoot.
window.useAppStoreThe host's Zustand store: call it as a hook (window.useAppStore((s) => s.edgePosition)) or use getState(), setState(), subscribe(). Fields: references/api.md section 3.
window.apiThe IPC bridge to the main process. Full list with status: references/api.md section 1.
window.KoBarExtensionsThe extension registry: registerSidebarButton, registerPanel, registerSettingsPanel, registerInlineWidget, getData, setData (newer hosts also setBarItemState, onBarItemChange). See references/api.md section 2.

4. A complete plugin

manifest.json:

json
{
"id": "com.example.hello",
"name": "Hello",
"version": "1.0.0",
"description": "Says hello from the KoBar sidebar.",
"author": "Your Name",
"entry": "index.js",
"icon": "waving_hand",
"githubRepo": "your-name/kobar-hello"
}

index.js (no build step needed; KoBar wraps it in a function, so top-level const is private to the plugin):

javascript
// Read once, synchronously: `com.example.hello`, or `dev-com.example.hello` in the playground.
const PLUGIN_ID = document.currentScript?.dataset.extensionId ?? 'com.example.hello';
const { createElement: h, useState } = window.React;
const { useAppStore, KoBarExtensions } = window;
const STRINGS = {
en: { title: 'Hello', greet: 'Hello, {name}!', name: 'Name', close: 'Close' },
tr: { title: 'Merhaba', greet: 'Merhaba, {name}!', name: 'Ad', close: 'Kapat' },
};
const tr = (lang, key) => (STRINGS[lang] ?? STRINGS.en)[key] ?? STRINGS.en[key];
// Place the panel next to the bar. The panel is rendered inside the bar's wrapper (position: relative),
// so 100% means "just outside the bar" on every edge, docked or free-floating.
function placement(edge) {
const gap = 10;
if (edge === 'left') return { left: '100%', top: 0, marginLeft: gap };
if (edge === 'right') return { right: '100%', top: 0, marginRight: gap };
if (edge === 'top') return { top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: gap };
return { bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: gap };
}
function HelloPanel({ onClose }) {
const edge = useAppStore((s) => s.edgePosition);
const lang = useAppStore((s) => s.language);
const { name } = KoBarExtensions.getData(PLUGIN_ID, { name: 'KoBar' });
return h(
'div',
{
// One root element. Hit-testing comes from the bar wrapper; the classes keep it working if you portal it later.
className: 'pointer-events-auto no-drag-region',
style: {
position: 'absolute',
...placement(edge),
width: 260,
padding: 16,
borderRadius: 12,
background: 'var(--theme-bg-dark)',
border: '1px solid var(--theme-border)',
color: 'rgba(255, 255, 255, 0.92)',
boxShadow: '0 8px 28px rgba(0, 0, 0, 0.32)',
},
},
h('h2', { style: { margin: '0 0 8px', fontSize: 15 } }, tr(lang, 'title')),
h('p', { style: { margin: '0 0 12px' } }, tr(lang, 'greet').replace('{name}', name)),
h('button', { className: 'text-primary', onClick: onClose }, tr(lang, 'close'))
);
}
function HelloSettings() {
const lang = useAppStore((s) => s.language);
const [data, setDataState] = useState(() => KoBarExtensions.getData(PLUGIN_ID, { name: 'KoBar' }));
const update = (name) => {
const next = { ...data, name };
setDataState(next);
KoBarExtensions.setData(PLUGIN_ID, next); // workspace-aware, persisted, included in backups
};
return h(
'label',
{ style: { display: 'flex', gap: 8, alignItems: 'center' } },
tr(lang, 'name'),
h('input', { value: data.name, onChange: (e) => update(e.target.value), style: { flex: 1 } })
);
}
// All registrations: synchronous, top level, one shared id for button and panel.
KoBarExtensions.registerSidebarButton({
id: PLUGIN_ID,
icon: 'waving_hand', // Material Symbols Outlined name
label: tr(useAppStore.getState().language, 'title'), // tooltip; read once at registration
onClick: (event, anchorRect) => {
const store = useAppStore.getState();
const isOpen = store.activeExtensionPanelId === PLUGIN_ID;
store.closeAllUtilityPopups();
if (!isOpen) useAppStore.setState({ activeExtensionPanelId: PLUGIN_ID, activeExtensionAnchorRect: anchorRect });
},
});
KoBarExtensions.registerPanel(PLUGIN_ID, { id: PLUGIN_ID, render: (props) => h(HelloPanel, props) });
KoBarExtensions.registerSettingsPanel(PLUGIN_ID, { id: PLUGIN_ID, render: () => h(HelloSettings) });

Where things appear:

  • The button sits in the bar in the user's order (featureOrder); its label is the tooltip.
  • The panel opens next to the bar while activeExtensionPanelId equals its id. The host passes onClose and

anchorRect. anchorRect is the bar's rectangle in window coordinates, not the button's; the button's own rectangle is event.currentTarget.getBoundingClientRect(). The user can drag the panel along the bar (Alt+Arrow keys, double-click to reset); see references/host-behaviour.md section 2.

  • The settings panel is reached from Settings > Plugins > Installed (the "tune" icon). The Settings window has no plugin

runtime, so your settings panel is rendered in the main window's note panel, inside a dark wrapper.

  • An inline widget (registerInlineWidget(id, { id, render })) is rendered directly in the bar instead of a button; it is

the right choice for a small toggle or status control. Give its root pointer-events-auto if it is interactive.

5. Localisation

useAppStore.getState().language is one of en, tr, de, es, fr, ja, ru, ar, hi, zh (ar is right-to-left). Ship your own strings with an English fallback, as above. store.t(key) translates host keys only; do not rely on it for your plugin's text. Re-read the language in components (the hook form re-renders on change); a button label is read once at registration.

6. What changed in KoBar 1.4.0 (check old plugins against this)

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
68
Repository forks
7
7-day trend score
21

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/kobar-project/kobar/kobar-plugin-developer/badge)](https://verified-skill.com/skills/kobar-project/kobar/kobar-plugin-developer)

Something wrong with this skill or its page? Tell us.