summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src/context/notification.tsx
diff options
context:
space:
mode:
authorDavid Hill <[email protected]>2025-12-12 20:23:06 +0000
committerDavid Hill <[email protected]>2025-12-12 20:23:06 +0000
commit9b77246246ab0d81aaee23514c343ccb93c36847 (patch)
tree09749dd9d91275b7fec4035fafbefc9e4cd0cb6c /packages/desktop/src/context/notification.tsx
parentcf3bc1e0a6324a01ae10a2849476d7dc07e4876b (diff)
parentd51c6ca39f382ef616157680ca7366b6ee58713e (diff)
downloadopencode-9b77246246ab0d81aaee23514c343ccb93c36847.tar.gz
opencode-9b77246246ab0d81aaee23514c343ccb93c36847.zip
Merge branch 'dev' of https://github.com/sst/opencode into dev
Diffstat (limited to 'packages/desktop/src/context/notification.tsx')
-rw-r--r--packages/desktop/src/context/notification.tsx106
1 files changed, 106 insertions, 0 deletions
diff --git a/packages/desktop/src/context/notification.tsx b/packages/desktop/src/context/notification.tsx
new file mode 100644
index 000000000..744e4fdf3
--- /dev/null
+++ b/packages/desktop/src/context/notification.tsx
@@ -0,0 +1,106 @@
+import { createStore } from "solid-js/store"
+import { createSimpleContext } from "@opencode-ai/ui/context"
+import { makePersisted } from "@solid-primitives/storage"
+import { useGlobalSDK } from "./global-sdk"
+import { EventSessionError } from "@opencode-ai/sdk/v2"
+import { makeAudioPlayer } from "@solid-primitives/audio"
+import idleSound from "@opencode-ai/ui/audio/staplebops-01.aac"
+
+type NotificationBase = {
+ directory?: string
+ session?: string
+ metadata?: any
+ time: number
+ viewed: boolean
+}
+
+type TurnCompleteNotification = NotificationBase & {
+ type: "turn-complete"
+}
+
+type ErrorNotification = NotificationBase & {
+ type: "error"
+ error: EventSessionError["properties"]["error"]
+}
+
+export type Notification = TurnCompleteNotification | ErrorNotification
+
+export const { use: useNotification, provider: NotificationProvider } = createSimpleContext({
+ name: "Notification",
+ init: () => {
+ const idlePlayer = makeAudioPlayer(idleSound)
+ const globalSDK = useGlobalSDK()
+
+ const [store, setStore] = makePersisted(
+ createStore({
+ list: [] as Notification[],
+ }),
+ {
+ name: "notification.v1",
+ },
+ )
+
+ // onMount(() => {
+ // const daysToKeep = 7
+ // // setStore("list", (n) => n.filter((n) => !n.viewed && n.time + 1000 * 60 * 60 * 24 * daysToKeep < Date.now()))
+ // })
+
+ globalSDK.event.listen((e) => {
+ const directory = e.name
+ const event = e.details
+ const base = {
+ directory,
+ time: Date.now(),
+ viewed: false,
+ }
+ switch (event.type) {
+ case "session.idle": {
+ idlePlayer.play()
+ const session = event.properties.sessionID
+ setStore("list", store.list.length, {
+ ...base,
+ type: "turn-complete",
+ session,
+ })
+ break
+ }
+ case "session.error": {
+ const session = event.properties.sessionID ?? "global"
+ // errorPlayer.play()
+ setStore("list", store.list.length, {
+ ...base,
+ type: "error",
+ session,
+ error: "error" in event.properties ? event.properties.error : undefined,
+ })
+ break
+ }
+ }
+ })
+
+ return {
+ session: {
+ all(session: string) {
+ return store.list.filter((n) => n.session === session)
+ },
+ unseen(session: string) {
+ return store.list.filter((n) => n.session === session && !n.viewed)
+ },
+ markViewed(session: string) {
+ setStore("list", (n) => n.session === session, "viewed", true)
+ },
+ },
+ project: {
+ all(directory: string) {
+ return store.list.filter((n) => n.directory === directory)
+ },
+ unseen(directory: string) {
+ return store.list.filter((n) => n.directory === directory && !n.viewed)
+ },
+ markViewed(directory: string) {
+ setStore("list", (n) => n.directory === directory, "viewed", true)
+ },
+ },
+ }
+ },
+})