@naxodev/opencode-music-player 0.1.3 → 0.1.5

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.
Files changed (2) hide show
  1. package/index.tsx +46 -29
  2. package/package.json +1 -1
package/index.tsx CHANGED
@@ -1,12 +1,11 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
2
  import { Plugin } from "@opencode-ai/plugin/tui"
3
- import { createStore, produce } from "solid-js/store"
4
3
  import { createSessionSystemMedia, openNowPlayingApp } from "./system-media.ts"
5
4
  import { isMac, mergeArtworkCompletion, type SessionMedia } from "./types.ts"
6
5
  import { CompactPlayer, SidebarPlayer, type UiState } from "./ui.tsx"
7
6
 
8
7
  type Context = Plugin.Context
9
- type SessionStore = UiState
8
+ type SessionStore = UiState & { loadingOwners?: string[] }
10
9
  type SeekIntent = {
11
10
  positionMs: number
12
11
  resolves: Array<() => void>
@@ -83,14 +82,18 @@ export function createController(
83
82
  context: Context,
84
83
  dependencies: ControllerDependencies = controllerDependencies,
85
84
  ): Controller {
86
- // Host storage may use another Solid runtime when this plugin comes from npm.
87
- const [session, setSessionStore] = createStore<SessionStore>({
88
- loading: false,
89
- error: null,
90
- player: null,
91
- })
92
- const setSession = (update: (draft: SessionStore) => void) =>
93
- setSessionStore(produce(update))
85
+ const [session, setSession] = context.storage.memory<SessionStore>(
86
+ "music-player.session.v6",
87
+ {
88
+ initial: {
89
+ loading: false,
90
+ loadingOwners: [],
91
+ error: null,
92
+ player: null,
93
+ },
94
+ },
95
+ )
96
+ const loadingOwner = crypto.randomUUID()
94
97
  const media = dependencies.createSessionMedia()
95
98
  let disposed = false
96
99
  let lifecycleGeneration = 0
@@ -132,12 +135,18 @@ export function createController(
132
135
  transportError = message
133
136
  publishError()
134
137
  }
135
- const updateLoading = () => {
136
- if (!isActive()) return
138
+ const setLoadingOwner = (active: boolean) => {
137
139
  setSession((draft) => {
138
- draft.loading = pending.size > 0
140
+ const owners = new Set(draft.loadingOwners ?? [])
141
+ if (active) owners.add(loadingOwner)
142
+ else owners.delete(loadingOwner)
143
+ draft.loadingOwners = [...owners]
144
+ draft.loading = owners.size > 0
139
145
  })
140
146
  }
147
+ const updateLoading = () => {
148
+ if (isActive()) setLoadingOwner(pending.size > 0)
149
+ }
141
150
  const settle = (resolve: () => void) => {
142
151
  if (!pending.delete(resolve)) return
143
152
  resolve()
@@ -319,11 +328,7 @@ export function createController(
319
328
  activeSeek = null
320
329
  for (const resolve of [...pending]) settle(resolve)
321
330
  void media.dispose().catch(() => {})
322
- if (session.loading) {
323
- setSession((draft) => {
324
- draft.loading = false
325
- })
326
- }
331
+ setLoadingOwner(false)
327
332
  },
328
333
  }
329
334
  }
@@ -404,22 +409,34 @@ export function createMusicPlayerPlugin(options?: {
404
409
  }
405
410
  : controllerDependencies,
406
411
  )
412
+ // Slot renderers must observe host-owned state before package components run.
413
+ const observeSession = () => {
414
+ void ctrl.session.loading
415
+ void ctrl.session.error
416
+ void ctrl.session.player
417
+ }
407
418
  const unsubApp = context.ui.slot({
408
419
  append: "session.composer.top",
409
- render: () => <AppHost context={context} ctrl={ctrl} />,
420
+ render: () => {
421
+ observeSession()
422
+ return <AppHost context={context} ctrl={ctrl} />
423
+ },
410
424
  })
411
425
  const unsubSidebar = context.ui.slot({
412
426
  append: "sidebar.content",
413
- render: () => (
414
- <SidebarPlayer
415
- context={context}
416
- state={ctrl.session}
417
- onPlayPause={() => void ctrl.playPause()}
418
- onNext={() => void ctrl.next()}
419
- onPrev={() => void ctrl.prev()}
420
- onSeek={(positionMs) => void ctrl.seek(positionMs)}
421
- />
422
- ),
427
+ render: () => {
428
+ observeSession()
429
+ return (
430
+ <SidebarPlayer
431
+ context={context}
432
+ state={ctrl.session}
433
+ onPlayPause={() => void ctrl.playPause()}
434
+ onNext={() => void ctrl.next()}
435
+ onPrev={() => void ctrl.prev()}
436
+ onSeek={(positionMs) => void ctrl.seek(positionMs)}
437
+ />
438
+ )
439
+ },
423
440
  })
424
441
  return () => {
425
442
  unsubApp()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naxodev/opencode-music-player",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "macOS system Now Playing controls for the OpenCode 2 TUI",
5
5
  "license": "MIT",
6
6
  "author": {