@naxodev/opencode-music-player 0.2.4 → 0.2.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.
- package/package.json +7 -6
- package/system-media.ts +37 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naxodev/opencode-music-player",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "macOS system Now Playing controls for the OpenCode 2 TUI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -62,20 +62,21 @@
|
|
|
62
62
|
"format:check": "prettier --check .",
|
|
63
63
|
"check": "tsc --noEmit",
|
|
64
64
|
"test": "bun test --preload @opentui/solid/preload",
|
|
65
|
-
"pack:check": "bun run scripts/verify-pack.ts",
|
|
65
|
+
"pack:check": "bun run ../../scripts/music-release.ts opencode-music-player --staged-only && bun run scripts/verify-pack.ts",
|
|
66
|
+
"prepublish:core": "bun run ../../scripts/music-release.ts opencode-music-player",
|
|
66
67
|
"smoke:package": "bun run scripts/package-smoke.ts",
|
|
67
68
|
"validate": "bun run format:check && bun run check && bun run test && bun run pack:check"
|
|
68
69
|
},
|
|
69
70
|
"dependencies": {
|
|
70
71
|
"@naxodev/music-core": "^0.1.1",
|
|
71
72
|
"@opencode-ai/plugin": "0.0.0-next-17444",
|
|
72
|
-
"@opentui/core": "0.5.
|
|
73
|
-
"@opentui/solid": "0.5.
|
|
73
|
+
"@opentui/core": "0.5.4",
|
|
74
|
+
"@opentui/solid": "0.5.4",
|
|
74
75
|
"pngjs": "^7.0.0",
|
|
75
|
-
"solid-js": "1.9.
|
|
76
|
+
"solid-js": "1.9.15"
|
|
76
77
|
},
|
|
77
78
|
"devDependencies": {
|
|
78
|
-
"@opencode-ai/theme": "0.0.0
|
|
79
|
+
"@opencode-ai/theme": "0.0.0",
|
|
79
80
|
"@types/bun": "^1.3.14",
|
|
80
81
|
"@types/pngjs": "^6.0.5",
|
|
81
82
|
"prettier": "3.9.6",
|
package/system-media.ts
CHANGED
|
@@ -52,7 +52,9 @@ const waitingArtwork = new Map<string, DeferredArtwork>()
|
|
|
52
52
|
type ArtworkResolver = typeof resolveArtworkDetails
|
|
53
53
|
|
|
54
54
|
/** Public-contract-only seam for the reconnecting session adapter. */
|
|
55
|
-
export type SessionClientFactory = (
|
|
55
|
+
export type SessionClientFactory = (
|
|
56
|
+
signal?: AbortSignal,
|
|
57
|
+
) => Promise<ReconnectingMusicSessionClient>
|
|
56
58
|
export type SessionSystemMediaOverrides = {
|
|
57
59
|
readonly createClient?: SessionClientFactory
|
|
58
60
|
readonly resolveArtworkDetails?: ArtworkResolver
|
|
@@ -60,11 +62,12 @@ export type SessionSystemMediaOverrides = {
|
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
let sessionClientSequence = 0
|
|
63
|
-
const createOpenCodeSessionClient: SessionClientFactory = () =>
|
|
65
|
+
const createOpenCodeSessionClient: SessionClientFactory = (signal) =>
|
|
64
66
|
createReconnectingMusicSessionClient({
|
|
65
67
|
clientId: `opencode-music-player-${++sessionClientSequence}`,
|
|
66
68
|
hostKind: "opencode",
|
|
67
69
|
capabilities: [...baselineCapabilities],
|
|
70
|
+
...(signal ? { signal } : {}),
|
|
68
71
|
})
|
|
69
72
|
|
|
70
73
|
type PresentationHost = {
|
|
@@ -302,6 +305,7 @@ export function createSessionSystemMedia(
|
|
|
302
305
|
const now = overrides.now ?? Date.now
|
|
303
306
|
const listeners = new Set<(event: SessionMediaEvent) => void>()
|
|
304
307
|
const presentationListeners = new Set<ArtworkPresentationListener>()
|
|
308
|
+
const acquisitionAbort = new AbortController()
|
|
305
309
|
let disposed = false
|
|
306
310
|
let currentArtworkIdentity: string | null = null
|
|
307
311
|
let client: ReconnectingMusicSessionClient | undefined
|
|
@@ -315,10 +319,10 @@ export function createSessionSystemMedia(
|
|
|
315
319
|
let clientReleased = false
|
|
316
320
|
let disposal: Promise<void> | undefined
|
|
317
321
|
|
|
318
|
-
const releaseClient = (next: ReconnectingMusicSessionClient) => {
|
|
319
|
-
if (clientReleased) return
|
|
322
|
+
const releaseClient = async (next: ReconnectingMusicSessionClient) => {
|
|
323
|
+
if (clientReleased) return
|
|
320
324
|
clientReleased = true
|
|
321
|
-
|
|
325
|
+
await next.dispose()
|
|
322
326
|
}
|
|
323
327
|
const emit = (event: SessionMediaEvent) => {
|
|
324
328
|
if (disposed) return
|
|
@@ -458,10 +462,12 @@ export function createSessionSystemMedia(
|
|
|
458
462
|
]
|
|
459
463
|
}
|
|
460
464
|
const clientPromise = Promise.resolve()
|
|
461
|
-
.then(factory)
|
|
465
|
+
.then(() => factory(acquisitionAbort.signal))
|
|
462
466
|
.then((next) => {
|
|
463
467
|
if (disposed) {
|
|
464
|
-
void releaseClient(next)
|
|
468
|
+
void releaseClient(next).catch((error) => {
|
|
469
|
+
console.error("Failed to dispose late music session client", error)
|
|
470
|
+
})
|
|
465
471
|
return next
|
|
466
472
|
}
|
|
467
473
|
install(next)
|
|
@@ -528,18 +534,30 @@ export function createSessionSystemMedia(
|
|
|
528
534
|
dispose() {
|
|
529
535
|
if (disposal) return disposal
|
|
530
536
|
disposed = true
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
(
|
|
541
|
-
()
|
|
542
|
-
|
|
537
|
+
disposal = Promise.resolve().then(async () => {
|
|
538
|
+
const errors: unknown[] = []
|
|
539
|
+
for (const unsubscribe of unsubscribers.splice(0)) {
|
|
540
|
+
try {
|
|
541
|
+
unsubscribe()
|
|
542
|
+
} catch (error) {
|
|
543
|
+
errors.push(error)
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
listeners.clear()
|
|
547
|
+
presentationListeners.clear()
|
|
548
|
+
removeArtworkInterests(host)
|
|
549
|
+
// Acquisition is cooperative. A late client is released by its callback.
|
|
550
|
+
if (client) {
|
|
551
|
+
try {
|
|
552
|
+
await releaseClient(client)
|
|
553
|
+
} catch (error) {
|
|
554
|
+
errors.push(error)
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (errors.length)
|
|
558
|
+
throw new AggregateError(errors, "music session cleanup failed")
|
|
559
|
+
})
|
|
560
|
+
acquisitionAbort.abort()
|
|
543
561
|
return disposal
|
|
544
562
|
},
|
|
545
563
|
}
|