refactor(channels): make built-in channels self-contained (#4908)

* refactor(channels): own setup and instance contracts

* refactor(channels): isolate management contracts

* refactor(channels): normalize activation contracts

* fix(channels): enforce management contracts

* refactor(channels): finish setup ownership migration

* fix(channels): harden management contracts

* fix(channels): enforce lazy loading and runtime ownership

* fix(feishu): make multi-instance startup idempotent

* fix(webui): render channel setup contracts cleanly

* fix(feishu): stop websocket clients cleanly

* fix(channels): enforce persistence and activation gates

* fix(channels): preserve global feature action scope

* fix(channels): apply defaults for single plugins

* fix(channels): enforce management contract boundaries

* refactor(feishu): remove identity helper indirection

* fix(channels): preserve management setup contracts

* refactor(channels): generalize instance settings UI

* refactor(channels): package channel plugins with web UI metadata

* refactor(channels): make built-ins self-contained packages

* test(channels): colocate tests with channel packages

* fix(dingtalk): use official brand icon

* feat(channels): colocate webui translations

* docs(channels): clarify plugin ownership

* test(exec): remove output wait race

* refactor(channels): unify plugin descriptors

* fix(channels): enforce descriptor-owned contracts

* refactor(channels): finish package-owned plugin setup

* refactor(channels): use repository-owned packages only

* fix(channels): self-describe dependencies and runtime state

* fix(channels): warn about legacy entry points
This commit is contained in:
chengyongru
2026-07-19 23:30:49 +08:00
committed by GitHub
parent 7aaac37bca
commit 462a0dfb0f
388 changed files with 17093 additions and 5110 deletions
+15 -106
View File
@@ -8,6 +8,7 @@ import {
} from "react";
import { Moon, PanelLeft, ShieldCheck, Sun, X } from "lucide-react";
import { useTranslation } from "react-i18next";
import { channelUiPresentation } from "@/channel-plugins/registry";
import { DeleteConfirm } from "@/components/DeleteConfirm";
import { RenameChatDialog } from "@/components/RenameChatDialog";
import { Sidebar } from "@/components/Sidebar";
@@ -95,106 +96,6 @@ type ShellRoute = {
settingsSection: SettingsSectionKey;
};
type PairingChannelPresentation = {
label: string;
initials: string;
color: string;
logoUrl?: string;
};
const PAIRING_CHANNEL_PRESENTATION: Record<string, PairingChannelPresentation> = {
dingtalk: {
label: "DingTalk",
initials: "DT",
color: "#FF6A00",
logoUrl: "https://www.dingtalk.com/favicon.ico",
},
discord: {
label: "Discord",
initials: "DC",
color: "#5865F2",
logoUrl: "https://discord.com/favicon.ico",
},
email: {
label: "Email",
initials: "EM",
color: "#EA4335",
logoUrl: "https://gmail.com/favicon.ico",
},
feishu: {
label: "Feishu",
initials: "FS",
color: "#3370FF",
logoUrl: "https://www.feishu.cn/favicon.ico",
},
lark: {
label: "Lark",
initials: "LK",
color: "#3370FF",
logoUrl: "https://www.larksuite.com/favicon.ico",
},
matrix: {
label: "Matrix",
initials: "M",
color: "#111827",
logoUrl: "https://matrix.org/favicon.ico",
},
msteams: {
label: "Microsoft Teams",
initials: "MT",
color: "#6264A7",
logoUrl: "https://www.microsoft.com/favicon.ico",
},
napcat: {
label: "NapCat",
initials: "NC",
color: "#7C3AED",
logoUrl: "https://napneko.github.io/favicon.ico",
},
qq: {
label: "QQ",
initials: "QQ",
color: "#12B7F5",
logoUrl: "https://im.qq.com/favicon.ico",
},
signal: {
label: "Signal",
initials: "SG",
color: "#3A76F0",
logoUrl: "https://signal.org/favicon.ico",
},
slack: {
label: "Slack",
initials: "SL",
color: "#611F69",
logoUrl: "https://slack.com/favicon.ico",
},
telegram: {
label: "Telegram",
initials: "TG",
color: "#229ED9",
logoUrl: "https://telegram.org/favicon.ico",
},
wecom: {
label: "WeCom",
initials: "WC",
color: "#2F7DFF",
logoUrl: "https://work.weixin.qq.com/favicon.ico",
},
weixin: {
label: "WeChat",
initials: "WX",
color: "#07C160",
logoUrl: "https://weixin.qq.com/favicon.ico",
},
whatsapp: {
label: "WhatsApp",
initials: "WA",
color: "#25D366",
logoUrl: "https://www.whatsapp.com/favicon.ico",
},
};
const SETTINGS_SECTION_KEYS: SettingsSectionKey[] = [
"overview",
"appearance",
@@ -624,11 +525,9 @@ function PairingCodePopup({
}
function PairingChannelBadge({ channel }: { channel: string }) {
const key = pairingChannelKey(channel);
const presentation = PAIRING_CHANNEL_PRESENTATION[key];
const label = presentation?.label ?? channelLabel(channel);
const initials = presentation?.initials ?? label.slice(0, 2).toUpperCase();
const color = presentation?.color ?? "#10B981";
const presentation = pairingChannelPresentation(channel);
const initials = presentation.initials;
const color = presentation.color;
const logoUrls = useMemo(
() => logoFallbackUrls(presentation?.logoUrl),
[presentation?.logoUrl],
@@ -765,8 +664,18 @@ function pairingChannelKey(channel: string): string {
}
function channelLabel(channel: string): string {
return pairingChannelPresentation(channel).label;
}
function pairingChannelPresentation(channel: string) {
const key = pairingChannelKey(channel);
return PAIRING_CHANNEL_PRESENTATION[key]?.label ?? channel;
const plugin = channelUiPresentation(key);
return {
label: plugin?.displayName ?? channel,
initials: plugin?.initials ?? channel.slice(0, 2).toUpperCase(),
color: plugin?.color ?? "#10B981",
logoUrl: plugin?.logoUrl,
};
}
function formatPairingExpiry(seconds: number | null | undefined): string {