opencode-plugin-kit 1.0.0-alpha.6 → 1.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,12 @@ Published on [npm](https://www.npmjs.com/package/opencode-plugin-kit):
16
16
  npm install opencode-plugin-kit
17
17
  ```
18
18
 
19
+ > **Still a prerelease.** `latest` currently resolves to `1.0.0-alpha.6` — the
20
+ > first OpenCode v2 build. The publish workflow tags every prerelease under
21
+ > its own identifier (`alpha`, `beta`, …) and leaves `latest` alone, so later
22
+ > alphas will _not_ move `latest` automatically. To track the newest build,
23
+ > install `opencode-plugin-kit@alpha`.
24
+
19
25
  Plugins depending on the kit declare it in `dependencies`; the host peer
20
26
  (`@opencode/plugin`) must match your OpenCode v2 runtime version (see each
21
27
  plugin's README).
@@ -189,6 +195,41 @@ const totals = walkMessages(
189
195
  const { input, output, cost } = sumProviderTokens(context, sessionID, "opencode")
190
196
  ```
191
197
 
198
+ ### Collapsible
199
+
200
+ Imported from the `collapsible` subpath so the Solid JSX stays out of the main
201
+ entry.
202
+
203
+ ```ts
204
+ import { CollapsibleSection, CollapsibleGroup } from "opencode-plugin-kit/collapsible"
205
+
206
+ // From a host component that already has the plugin context:
207
+ const { theme } = usePlugin()
208
+
209
+ CollapsibleSection({
210
+ title: "Skills",
211
+ count: items.length,
212
+ threshold: 2, // Collapse the body at or below this count
213
+ pinned: <Note />, // Always visible between header and body
214
+ theme, // Header colors; pass the host's theme
215
+ children: <List />,
216
+ })
217
+
218
+ CollapsibleGroup({
219
+ title: "Providers",
220
+ count: ids.length,
221
+ defaultCollapsed: true,
222
+ theme,
223
+ children: (collapsed) => (collapsed() ? <Summary /> : <List />),
224
+ })
225
+ ```
226
+
227
+ `theme` is optional but **recommended**. Without it the kit falls back to
228
+ `usePlugin()`, which throws when the kit is installed from `node_modules`
229
+ (it resolves its own copy of `@opencode/plugin` / `solid-js`, so the host's
230
+ plugin context is not visible). If the context is missing the header falls back
231
+ to unthemed text rather than throwing.
232
+
192
233
  ## Consuming Plugins
193
234
 
194
235
  | Plugin | What it does |
@@ -198,14 +239,30 @@ const { input, output, cost } = sumProviderTokens(context, sessionID, "opencode"
198
239
  | [opencode-skill-lister](https://github.com/ranjithrajv/opencode-skill-lister) | Skills list in sidebar |
199
240
  | [opencode-plugin-manager](https://github.com/ranjithrajv/opencode-plugin-manager) | Plugin manager in sidebar |
200
241
 
201
- Link locally with `"opencode-plugin-kit": "file:../opencode-plugin-kit"` in the
202
- consumer's `package.json`, then `npm install` (or `bun install`).
242
+ Install from the registry:
243
+
244
+ ```sh
245
+ npm install opencode-plugin-kit@alpha
246
+ ```
247
+
248
+ The `latest` dist-tag tracks the newest v1 alpha; v2 releases ship under the
249
+ `alpha` dist-tag, so pin `opencode-plugin-kit@alpha` (or an explicit range) until
250
+ `latest` moves to v2.
251
+
252
+ For local development, link the sibling checkout with
253
+ `"opencode-plugin-kit": "file:../opencode-plugin-kit"` in the consumer's
254
+ `package.json`, then `npm install` (or `bun install`). Note that a linked
255
+ checkout shares one module graph with the host, so the collapsible components
256
+ find the plugin context without a `theme` prop; a registry install does not.
203
257
 
204
258
  ## Compatibility
205
259
 
206
260
  The OpenCode v2 host plugin API's types are the spec. Kit consumes the context
207
261
  structurally — anything the host ships with the expected members satisfies it.
208
262
 
263
+ - **Floor: `@opencode/plugin` 2.0.3.** Earlier 2.x releases have no `./tui`
264
+ subpath export, so nothing that imports `@opencode/plugin/tui` can resolve it.
265
+ Verified by typecheck and the type-tripwire against 2.0.3, 2.0.10 and 2.0.18.
209
266
  - **No runtime dependency** on `@opencode/plugin` — types only
210
267
  - **Optional peer** — declared as optional so consumers share one copy
211
268
  - **CI tripwire** — `host.test-d.ts` pins published host types against kit's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-plugin-kit",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.7",
4
4
  "description": "Shared building blocks for OpenCode sidebar plugins: provider vocabulary, row formatting, defensive message traversal, connected-provider tracking, polling fetcher, cached resources, and a persisted view/filter picker (registry + slash command + dialog + toast).",
5
5
  "keywords": [
6
6
  "opencode",
@@ -32,8 +32,9 @@
32
32
  "fmt": "vp fmt",
33
33
  "lint": "vp lint",
34
34
  "typecheck": "tsc --noEmit",
35
- "test:types": "vitest run --typecheck",
36
- "test:coverage": "vitest run --coverage.enabled"
35
+ "test": "vp test",
36
+ "test:types": "vp test --typecheck",
37
+ "test:coverage": "vp exec vitest run --coverage"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@opencode/plugin": "2.0.15",
@@ -53,7 +54,7 @@
53
54
  "zod": "^4.5.4"
54
55
  },
55
56
  "peerDependencies": {
56
- "@opencode/plugin": "^2.0.15",
57
+ "@opencode/plugin": "^2.0.3",
57
58
  "solid-js": "^1.9.12",
58
59
  "zod": "^4.5.4"
59
60
  },
@@ -14,6 +14,39 @@ import { createSignal, Show } from "solid-js"
14
14
  import type { JSX } from "solid-js"
15
15
  import { usePlugin } from "@opencode/plugin/tui"
16
16
 
17
+ /**
18
+ * Theme tokens a collapsible header needs. Derived from the host theme so
19
+ * callers can pass their `usePlugin().theme` straight through, and so we
20
+ * don't pin the host's color type (e.g. `RGBA`) here.
21
+ */
22
+ type HostTheme = ReturnType<typeof usePlugin>["theme"]
23
+ export interface CollapsibleTheme {
24
+ text: {
25
+ base?: HostTheme["text"]["base"]
26
+ muted?: HostTheme["text"]["muted"]
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Resolve the theme for a header.
32
+ *
33
+ * Prefer an explicitly passed `theme`. When this package is consumed from
34
+ * `node_modules`, `usePlugin()` here resolves a different copy of
35
+ * `@opencode/plugin` (and `solid-js`) than the host renders with, so the
36
+ * host's plugin context is not visible and `usePlugin()` throws
37
+ * "PluginContextProvider is missing". Callers that already hold the host
38
+ * context pass `theme` down; the `usePlugin()` fallback keeps working for
39
+ * consumers whose module graph is deduped (tests, monorepos).
40
+ */
41
+ function resolveTheme(explicit?: CollapsibleTheme): CollapsibleTheme {
42
+ if (explicit) return explicit
43
+ try {
44
+ return usePlugin().theme
45
+ } catch {
46
+ return { text: {} }
47
+ }
48
+ }
49
+
17
50
  /** The top-level collapsible sidebar section. */
18
51
  export function CollapsibleSection(props: {
19
52
  title: string
@@ -26,9 +59,11 @@ export function CollapsibleSection(props: {
26
59
  /** Always-visible content between the header and the collapsible body
27
60
  * (e.g. the plugin manager's status note). */
28
61
  pinned?: JSX.Element
62
+ /** Header colors; pass the host's `usePlugin().theme` from the consumer. */
63
+ theme?: CollapsibleTheme
29
64
  children: JSX.Element
30
65
  }) {
31
- const theme = usePlugin().theme
66
+ const theme = resolveTheme(props.theme)
32
67
  const [expanded, setExpanded] = createSignal(false)
33
68
  const count = () => props.count ?? 0
34
69
  const threshold = () => props.threshold ?? 2
@@ -62,9 +97,11 @@ export function CollapsibleGroup(props: {
62
97
  title: string
63
98
  count: number
64
99
  defaultCollapsed?: boolean
100
+ /** Header colors; pass the host's `usePlugin().theme` from the consumer. */
101
+ theme?: CollapsibleTheme
65
102
  children: JSX.Element | ((collapsed: () => boolean) => JSX.Element)
66
103
  }) {
67
- const theme = usePlugin().theme
104
+ const theme = resolveTheme(props.theme)
68
105
  const [collapsed, setCollapsed] = createSignal(props.defaultCollapsed ?? false)
69
106
  return (
70
107
  <box flexDirection="column">
package/src/toast.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // The host's toast UI is optional at the edges (beta API): every plugin
4
4
  // hand-rolled the same try/catch fallback around `ui.toast.show`.
5
- import type { KitContext, KitUI, ToastInput } from "./host.ts"
5
+ import type { KitUI, ToastInput } from "./host.ts"
6
6
 
7
7
  /** Show a toast; silently no-ops when the host has no toast UI or it throws.
8
8
  * Takes a minimal structural slice — the full host context's `ui.slot` is