|
1 | 1 | import { api, downloadAuthenticatedFile, openAuthenticatedFile, uploadFile, type ActivityItem, type AgentTemplate, type Channel, type ChannelFile, type GlobalThread, type MemoryItem, type Message, type ThreadState, type RoutingModel } from "./api.ts"; |
2 | 2 | import { h, clear, icon, md, timeLabel } from "./dom.ts"; |
3 | 3 | import { S, avatar, appAlert, appConfirm, appPrompt } from "./app.ts"; |
| 4 | +import { NOTIFICATION_SOUNDS, channelNotificationPreference, previewNotification, setChannelNotificationPreference } from "./notifications.ts"; |
4 | 5 |
|
5 | 6 | export type ChannelView = "chat" | "board" | "threads" | "files" | "terminal" | "memory" | "activity" | "settings"; |
6 | 7 |
|
@@ -614,6 +615,21 @@ export function renderChannelSettings(container: HTMLElement, channel: Channel, |
614 | 615 | const provider = h("select", { class: "field" }, h("option", { value: "" }, "Loading providers…")) as HTMLSelectElement; |
615 | 616 | const model = h("select", { class: "field" }, h("option", { value: channel.agent?.model || "" }, channel.agent?.model || "Choose a model")) as HTMLSelectElement; |
616 | 617 | const status = h("p", { class: "min-h-5 text-sm text-muted" }); |
| 618 | + const notificationPreference = channelNotificationPreference(channel.id); |
| 619 | + const channelMuted = h("input", { type: "checkbox", checked: notificationPreference.muted, class: "accent-accent" }) as HTMLInputElement; |
| 620 | + const channelSound = h("select", { class: "field" }, ...NOTIFICATION_SOUNDS.map((item) => h("option", { value: item.value, selected: item.value === notificationPreference.sound }, item.label))) as HTMLSelectElement; |
| 621 | + const notificationStatus = h("p", { class: "min-h-5 text-sm text-muted" }); |
| 622 | + const saveChannelNotifications = async (): Promise<void> => { |
| 623 | + channelMuted.disabled = true; channelSound.disabled = true; notificationStatus.textContent = "Saving…"; |
| 624 | + try { |
| 625 | + await setChannelNotificationPreference(channel.id, { muted: channelMuted.checked, sound: channelSound.value as typeof notificationPreference.sound }); |
| 626 | + notificationStatus.textContent = channelMuted.checked ? `#${channel.name} is muted for your account.` : `#${channel.name} will use ${channelSound.selectedOptions[0]?.textContent || "this sound"}.`; |
| 627 | + } catch (error) { notificationStatus.textContent = (error as Error).message; } |
| 628 | + finally { channelMuted.disabled = false; channelSound.disabled = channelMuted.checked; } |
| 629 | + }; |
| 630 | + channelMuted.onchange = () => { channelSound.disabled = channelMuted.checked; void saveChannelNotifications(); }; |
| 631 | + channelSound.onchange = () => { previewNotification(channelSound.value as typeof notificationPreference.sound); void saveChannelNotifications(); }; |
| 632 | + channelSound.disabled = channelMuted.checked; |
617 | 633 | let loadSequence = 0; |
618 | 634 | let modelLoading = false; |
619 | 635 | let changeModelButton: HTMLButtonElement | null = null; |
@@ -744,6 +760,11 @@ export function renderChannelSettings(container: HTMLElement, channel: Channel, |
744 | 760 | ? null |
745 | 761 | : h("button", { class: "btn-primary text-sm", onclick: () => { void saveName(); } }, "Rename"))), |
746 | 762 | h("div", { class: "card space-y-3 p-4" }, h("h3", { class: "font-semibold text-fg" }, "Purpose"), purpose, h("div", { class: "flex justify-end" }, h("button", { class: "btn-primary text-sm", onclick: () => { void savePurpose(); } }, "Save purpose"))), |
| 763 | + h("div", { class: "card space-y-3 p-4", dataset: { channelNotifications: "" } }, |
| 764 | + h("div", {}, h("h3", { class: "font-semibold text-fg" }, "Notification sound"), h("p", { class: "mt-1 text-sm leading-6 text-muted" }, "Private to your account. Global mute in Settings → Notifications always takes priority.")), |
| 765 | + h("label", { class: "flex items-center gap-3 rounded-lg border border-line bg-panel p-3 text-sm font-semibold text-fg" }, channelMuted, `Mute #${channel.name}`), |
| 766 | + h("label", { class: "block space-y-1 text-xs font-semibold text-fg" }, "Ping sound", channelSound), |
| 767 | + notificationStatus), |
747 | 768 | h("div", { class: "card space-y-3 p-4" }, |
748 | 769 | h("div", {}, h("h3", { class: "font-semibold text-fg" }, "Agent avatar"), h("p", { class: "mt-1 text-sm text-muted" }, "Pick a flat color or upload a custom image for this resident agent.")), |
749 | 770 | agentAvatar, h("div", { class: "mt-2" }, h("span", { class: "mb-1 block text-xs font-semibold text-muted" }, "Default colors"), avatarColorRow)), |
|
0 commit comments