@xmanrui/dsh-im 0.2.1 → 0.3.0
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/README.md +19 -11
- package/lib/client.js +1847 -1145
- package/lib/index.js +116 -115
- package/package.json +2 -2
- package/plugin-src/client/channel-logos.js +13 -0
- package/plugin-src/client/channels/dingtalk/index.js +1 -1
- package/plugin-src/client/channels/dingtalk/styles.js +8 -8
- package/plugin-src/client/channels/feishu/index.js +1 -2
- package/plugin-src/client/channels/feishu/styles.js +3 -3
- package/plugin-src/client/channels/qq/index.js +1 -1
- package/plugin-src/client/channels/shared/token-channel.js +1 -2
- package/plugin-src/client/channels/slack/api.js +11 -0
- package/plugin-src/client/channels/slack/index.js +130 -0
- package/plugin-src/client/channels/slack/styles.js +34 -0
- package/plugin-src/client/channels/wecom/index.js +1 -1
- package/plugin-src/client/channels/wecom/styles.js +1 -1
- package/plugin-src/client/channels/weixin/index.js +1 -2
- package/plugin-src/client/channels/weixin/styles.js +9 -9
- package/plugin-src/client/channels/whatsapp/index.js +1 -1
- package/plugin-src/client/credential-binding.js +1 -2
- package/plugin-src/client/i18n.js +443 -0
- package/plugin-src/client/index.js +29 -4
- package/plugin-src/client/styles.js +46 -38
- package/plugin-src/host/channels/shared/production.mjs +2 -2
- package/plugin-src/host/channels/slack/index.mjs +28 -0
- package/plugin-src/host/channels/slack/production.mjs +91 -0
- package/plugin-src/host/channels/slack/rpc.mjs +127 -0
- package/plugin-src/host/index.mjs +3 -0
- package/scripts/verify-package.mjs +10 -3
- package/src/channels/discord/discord-api.mjs +1 -1
- package/src/channels/slack/config-store.mjs +173 -0
- package/src/channels/slack/harness-client.mjs +3 -0
- package/src/channels/slack/manifest.mjs +31 -0
- package/src/channels/slack/slack-api.mjs +259 -0
- package/src/channels/slack/slack-bridge.mjs +15 -0
- package/src/channels/slack/slack-controller.mjs +318 -0
- package/src/channels/slack/slack-runtime.mjs +462 -0
- package/src/channels/slack/state-store.mjs +3 -0
- package/src/channels/weixin/weixin-api.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmanrui/dsh-im",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "通过扫码、App Manifest或机器人凭据把IM机器人接入DeepSeek Harness(支持飞书、微信、钉钉、企业微信、QQ、Slack、Telegram、Discord和WhatsApp)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -106,6 +106,19 @@ export function DiscordLogoGlyph({ size } = {}) {
|
|
|
106
106
|
}));
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
export function SlackLogoGlyph({ size } = {}) {
|
|
110
|
+
return h('svg', {
|
|
111
|
+
...dimensions(size),
|
|
112
|
+
viewBox: '0 0 24 24',
|
|
113
|
+
focusable: 'false',
|
|
114
|
+
'aria-hidden': 'true',
|
|
115
|
+
'data-im-channel-logo': 'slack',
|
|
116
|
+
}, h('path', {
|
|
117
|
+
fill: 'currentColor',
|
|
118
|
+
d: 'M6 15a2 2 0 1 1-2-2h2v2Zm1 0a2 2 0 1 1 4 0v5a2 2 0 1 1-4 0v-5Zm2-8a2 2 0 1 1 2-2v2H9Zm0 1a2 2 0 1 1 0 4H4a2 2 0 1 1 0-4h5Zm8 2a2 2 0 1 1 2 2h-2v-2Zm-1 0a2 2 0 1 1-4 0V5a2 2 0 1 1 4 0v5Zm-2 8a2 2 0 1 1-2 2v-2h2Zm0-1a2 2 0 1 1 0-4h5a2 2 0 1 1 0 4h-5Z',
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
109
122
|
export function WhatsappLogoGlyph({ size } = {}) {
|
|
110
123
|
return h('svg', {
|
|
111
124
|
...dimensions(size),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
|
|
4
|
+
import { h } from '../../i18n.js';
|
|
4
5
|
import {
|
|
5
6
|
DINGTALK_ENDPOINTS,
|
|
6
7
|
DINGTALK_RPC_CHANNEL,
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
} from './api.js';
|
|
14
15
|
import { installDingtalkStyles } from './styles.js';
|
|
15
16
|
|
|
16
|
-
const h = React.createElement;
|
|
17
17
|
const ACTIVE_PROVISION_STATES = new Set(['pending', 'scanned', 'authorizing', 'creating', 'connecting']);
|
|
18
18
|
|
|
19
19
|
export const name = 'dingtalk-settings';
|
|
@@ -6,7 +6,7 @@ const CSS = String.raw`
|
|
|
6
6
|
--ddt-accent-deep: #0958d9;
|
|
7
7
|
--ddt-accent-wash: #eaf3ff;
|
|
8
8
|
--ddt-success: var(--dsw-alias-state-success-primary, #20a162);
|
|
9
|
-
--ddt-warning: var(--dsw-alias-state-
|
|
9
|
+
--ddt-warning: var(--dsw-alias-state-warn-primary, #d97706);
|
|
10
10
|
--ddt-error: var(--dsw-alias-state-error-primary, #d54941);
|
|
11
11
|
width: 100%;
|
|
12
12
|
max-width: 880px;
|
|
@@ -27,13 +27,13 @@ const CSS = String.raw`
|
|
|
27
27
|
.ddt-heading p { margin-top: 5px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 20px; white-space: nowrap; }
|
|
28
28
|
.ddt-tools, .ddt-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
29
29
|
.ddt-tools { width: 100%; justify-content: space-between; flex-wrap: nowrap; }
|
|
30
|
-
.ddt-badge { min-height: 30px; display: inline-flex; align-items: center; gap: 7px; padding: 0 11px; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-
|
|
30
|
+
.ddt-badge { min-height: 30px; display: inline-flex; align-items: center; gap: 7px; padding: 0 11px; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font-size: 12px; white-space: nowrap; }
|
|
31
31
|
.ddt-dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: #aeb3bb; }
|
|
32
32
|
.ddt-dot[data-tone="success"] { background: var(--ddt-success); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ddt-success) 14%, transparent); }
|
|
33
33
|
.ddt-dot[data-tone="warning"] { background: var(--ddt-warning); }
|
|
34
34
|
.ddt-dot[data-tone="error"] { background: var(--ddt-error); }
|
|
35
|
-
.ddt-button { min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 0 13px; border: 1px solid var(--dsw-alias-
|
|
36
|
-
.ddt-button:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-
|
|
35
|
+
.ddt-button { min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 0 13px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; font-weight: 560; text-decoration: none; cursor: pointer; transition: border-color .15s ease, background .15s ease, transform .15s ease; }
|
|
36
|
+
.ddt-button:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
37
37
|
.ddt-button:active:not(:disabled) { transform: translateY(1px); }
|
|
38
38
|
.ddt-button:focus-visible { outline: 2px solid color-mix(in srgb, var(--ddt-accent) 70%, white); outline-offset: 2px; }
|
|
39
39
|
.ddt-button:disabled { cursor: not-allowed; opacity: .55; }
|
|
@@ -41,7 +41,7 @@ const CSS = String.raw`
|
|
|
41
41
|
.ddt-button[data-kind="primary"]:hover:not(:disabled) { border-color: var(--ddt-accent-deep); background: var(--ddt-accent-deep); }
|
|
42
42
|
.ddt-button[data-kind="danger"] { color: var(--ddt-error); }
|
|
43
43
|
.ddt-button[data-kind="quiet"] { min-height: 30px; padding: 0 10px; border-color: transparent; background: transparent; }
|
|
44
|
-
.ddt-card { overflow: hidden; border: 1px solid var(--dsw-alias-
|
|
44
|
+
.ddt-card { overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
45
45
|
.ddt-cardBody { padding: 24px; }
|
|
46
46
|
.ddt-empty { min-height: 230px; display: grid; grid-template-columns: minmax(0, 1fr) 180px; align-items: center; gap: 30px; }
|
|
47
47
|
.ddt-empty h3 { margin: 8px 0; font-size: 18px; }
|
|
@@ -51,7 +51,7 @@ const CSS = String.raw`
|
|
|
51
51
|
.ddt-brandMark svg { filter: drop-shadow(0 3px 8px rgb(0 35 96 / 16%)); }
|
|
52
52
|
.ddt-qrLayout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 34px; align-items: start; }
|
|
53
53
|
.ddt-qrColumn { display: flex; flex-direction: column; align-items: center; gap: 12px; }
|
|
54
|
-
.ddt-qrFrame { position: relative; width: min(270px, 100%); aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-
|
|
54
|
+
.ddt-qrFrame { position: relative; width: min(270px, 100%); aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 16px; background: #fff; }
|
|
55
55
|
.ddt-qrFrame::before { content: ''; position: absolute; inset: 6px; border: 1px solid rgb(22 119 255 / 10%); border-radius: 11px; pointer-events: none; }
|
|
56
56
|
.ddt-qrFrame img { display: block; width: 100%; height: 100%; object-fit: contain; }
|
|
57
57
|
.ddt-qrFallback { padding: 24px; color: #646a73; text-align: center; }
|
|
@@ -87,10 +87,10 @@ const CSS = String.raw`
|
|
|
87
87
|
.ddt-accountIdentity p { margin-top: 4px; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; }
|
|
88
88
|
.ddt-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
|
|
89
89
|
.ddt-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 20px 0; }
|
|
90
|
-
.ddt-metric { min-width: 0; padding: 12px; border-radius: 9px; background: var(--dsw-alias-
|
|
90
|
+
.ddt-metric { min-width: 0; padding: 12px; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
91
91
|
.ddt-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
|
|
92
92
|
.ddt-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
93
|
-
.ddt-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-
|
|
93
|
+
.ddt-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
|
94
94
|
.ddt-accountFooter .ddt-actions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
|
|
95
95
|
.ddt-accountFooter .ddt-button { flex: none; white-space: nowrap; }
|
|
96
96
|
.ddt-summary { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
|
|
3
3
|
import { FeishuLogoGlyph } from "../../channel-logos.js";
|
|
4
4
|
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from "../../credential-binding.js";
|
|
5
|
+
import { h } from "../../i18n.js";
|
|
5
6
|
import {
|
|
6
7
|
FEISHU_ENDPOINTS,
|
|
7
8
|
FEISHU_RPC_CHANNEL,
|
|
@@ -15,8 +16,6 @@ import {
|
|
|
15
16
|
import { useAnimationFrameScheduler } from "../../lifecycle.js";
|
|
16
17
|
import { installFeishuStyles } from "./styles.js";
|
|
17
18
|
|
|
18
|
-
const h = React.createElement;
|
|
19
|
-
|
|
20
19
|
export const name = "feishu-settings";
|
|
21
20
|
export const inject = ["slots", "connection"];
|
|
22
21
|
|
|
@@ -4,7 +4,7 @@ const CSS = String.raw`
|
|
|
4
4
|
.bxf-page {
|
|
5
5
|
--bxf-accent: var(--dsw-alias-state-business-primary, #3370ff);
|
|
6
6
|
--bxf-success: var(--dsw-alias-state-success-primary, #20a162);
|
|
7
|
-
--bxf-warning: var(--dsw-alias-state-
|
|
7
|
+
--bxf-warning: var(--dsw-alias-state-warn-primary, #d97706);
|
|
8
8
|
--bxf-error: var(--dsw-alias-state-error-primary, #d54941);
|
|
9
9
|
box-sizing: border-box;
|
|
10
10
|
width: 100%;
|
|
@@ -393,7 +393,7 @@ const CSS = String.raw`
|
|
|
393
393
|
|
|
394
394
|
.bxf-connectedTop { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
|
|
395
395
|
.bxf-botIdentity { min-width: 0; display: flex; align-items: center; gap: 13px; }
|
|
396
|
-
.bxf-avatar { flex: none; width: 48px; height: 48px; display: grid; place-items: center; overflow: hidden; border: 1px solid var(--dsw-alias-
|
|
396
|
+
.bxf-avatar { flex: none; width: 48px; height: 48px; display: grid; place-items: center; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 3px rgb(31 35 41 / 7%); }
|
|
397
397
|
.bxf-botName { min-width: 0; }
|
|
398
398
|
.bxf-botName h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 17px; line-height: 24px; font-weight: 650; }
|
|
399
399
|
.bxf-botName p { overflow: hidden; color: var(--dsw-alias-label-tertiary, #8f959e); font-family: var(--ds-font-family-code, monospace); font-size: 12px; line-height: 18px; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
|
|
@@ -407,7 +407,7 @@ const CSS = String.raw`
|
|
|
407
407
|
.bxf-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
|
|
408
408
|
.bxf-metric dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 550; margin: 3px 0 0; }
|
|
409
409
|
|
|
410
|
-
.bxf-connectedFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-
|
|
410
|
+
.bxf-connectedFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
|
411
411
|
.bxf-healthSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
|
|
412
412
|
.bxf-healthSummary[data-error="true"] { color: var(--bxf-error); }
|
|
413
413
|
.bxf-botActions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; justify-content: flex-end; }
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { QqLogoGlyph } from '../../channel-logos.js';
|
|
4
4
|
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
|
|
5
|
+
import { h } from '../../i18n.js';
|
|
5
6
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
6
7
|
import {
|
|
7
8
|
QQ_ENDPOINTS,
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
} from './api.js';
|
|
16
17
|
import { installQqStyles } from './styles.js';
|
|
17
18
|
|
|
18
|
-
const h = React.createElement;
|
|
19
19
|
const ACTIVE_STATES = new Set(['pending', 'refreshing', 'connecting']);
|
|
20
20
|
|
|
21
21
|
const Button = React.forwardRef(function Button({ children, kind = 'secondary', className = '', ...props }, ref) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { CredentialActionIcon, CredentialBindingPanel } from '../../credential-binding.js';
|
|
4
|
+
import { h } from '../../i18n.js';
|
|
4
5
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
5
6
|
|
|
6
|
-
const h = React.createElement;
|
|
7
|
-
|
|
8
7
|
const Button = React.forwardRef(function Button(
|
|
9
8
|
{ children, kind = 'secondary', className = '', ...props },
|
|
10
9
|
ref,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TOKEN_BOT_ENDPOINTS, createTokenChannelApi } from '../shared/token-api.js';
|
|
2
|
+
|
|
3
|
+
export const SLACK_RPC_CHANNEL = '/slack';
|
|
4
|
+
export const SLACK_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
|
|
5
|
+
|
|
6
|
+
const api = createTokenChannelApi('Slack', ' Socket Mode 长连接');
|
|
7
|
+
|
|
8
|
+
export const unwrapRpcResult = api.unwrapRpcResult;
|
|
9
|
+
export const normalizeSnapshot = api.normalizeSnapshot;
|
|
10
|
+
export const presentError = api.presentError;
|
|
11
|
+
export { api as slackClientApi };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { SlackLogoGlyph } from '../../channel-logos.js';
|
|
4
|
+
import { h } from '../../i18n.js';
|
|
5
|
+
import { createTokenChannelSettings } from '../shared/token-channel.js';
|
|
6
|
+
import {
|
|
7
|
+
SLACK_CREATE_APP_URL,
|
|
8
|
+
SLACK_APP_MANIFEST_YAML,
|
|
9
|
+
} from '../../../../src/channels/slack/manifest.mjs';
|
|
10
|
+
import { SLACK_ENDPOINTS, slackClientApi } from './api.js';
|
|
11
|
+
import { installSlackStyles } from './styles.js';
|
|
12
|
+
|
|
13
|
+
export function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) {
|
|
14
|
+
const [botToken, setBotToken] = React.useState('');
|
|
15
|
+
const [appToken, setAppToken] = React.useState('');
|
|
16
|
+
const [copied, setCopied] = React.useState(false);
|
|
17
|
+
const headingId = React.useId();
|
|
18
|
+
|
|
19
|
+
const copyManifest = async () => {
|
|
20
|
+
try {
|
|
21
|
+
await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
|
|
22
|
+
setCopied(true);
|
|
23
|
+
window.setTimeout(() => setCopied(false), 2_000);
|
|
24
|
+
} catch {
|
|
25
|
+
setCopied(false);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const submit = (event) => {
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
const normalizedBotToken = botToken.trim();
|
|
32
|
+
const normalizedAppToken = appToken.trim();
|
|
33
|
+
if (!normalizedBotToken || !normalizedAppToken || busy) return;
|
|
34
|
+
void onSubmit?.({ botToken: normalizedBotToken, appToken: normalizedAppToken });
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return h('section', {
|
|
38
|
+
className: 'ddt-card dim-surfaceCard dim-credentialPanel dsl-setup',
|
|
39
|
+
'aria-labelledby': headingId,
|
|
40
|
+
},
|
|
41
|
+
h('h3', { id: headingId, className: 'dim-credentialTitle' }, '接入 Slack 机器人'),
|
|
42
|
+
h('div', { className: 'dsl-guide' },
|
|
43
|
+
h('div', { className: 'dsl-guideCopy' },
|
|
44
|
+
h('strong', null, '先用 Manifest 创建并配置 Slack App'),
|
|
45
|
+
h('p', null, '复制配置后,在 Slack 选择 From a manifest;创建完成后生成 connections:write App Token,并将应用安装到工作区。')),
|
|
46
|
+
h('div', { className: 'dsl-guideActions' },
|
|
47
|
+
h('button', {
|
|
48
|
+
type: 'button',
|
|
49
|
+
className: 'ddt-button',
|
|
50
|
+
onClick: () => void copyManifest(),
|
|
51
|
+
disabled: busy,
|
|
52
|
+
}, copied ? h('span', { className: 'dsl-copyState' }, '已复制 Manifest') : '复制 Manifest'),
|
|
53
|
+
h('a', {
|
|
54
|
+
className: 'ddt-button',
|
|
55
|
+
href: SLACK_CREATE_APP_URL,
|
|
56
|
+
target: '_blank',
|
|
57
|
+
rel: 'noreferrer',
|
|
58
|
+
}, '打开 Slack 创建页'))),
|
|
59
|
+
h('form', { className: 'dim-credentialForm dim-credentialFormSingle', onSubmit: submit },
|
|
60
|
+
h('div', { className: 'dsl-fields' },
|
|
61
|
+
h('label', { className: 'dim-credentialField' },
|
|
62
|
+
h('span', null, 'Bot Token'),
|
|
63
|
+
h('input', {
|
|
64
|
+
type: 'password',
|
|
65
|
+
value: botToken,
|
|
66
|
+
onChange: (event) => setBotToken(event.target.value),
|
|
67
|
+
placeholder: 'xoxb-…',
|
|
68
|
+
maxLength: 4096,
|
|
69
|
+
autoCapitalize: 'none',
|
|
70
|
+
autoCorrect: 'off',
|
|
71
|
+
spellCheck: false,
|
|
72
|
+
autoComplete: 'new-password',
|
|
73
|
+
disabled: busy,
|
|
74
|
+
required: true,
|
|
75
|
+
})),
|
|
76
|
+
h('label', { className: 'dim-credentialField' },
|
|
77
|
+
h('span', null, 'App Token'),
|
|
78
|
+
h('input', {
|
|
79
|
+
type: 'password',
|
|
80
|
+
value: appToken,
|
|
81
|
+
onChange: (event) => setAppToken(event.target.value),
|
|
82
|
+
placeholder: 'xapp-…',
|
|
83
|
+
maxLength: 4096,
|
|
84
|
+
autoCapitalize: 'none',
|
|
85
|
+
autoCorrect: 'off',
|
|
86
|
+
spellCheck: false,
|
|
87
|
+
autoComplete: 'new-password',
|
|
88
|
+
disabled: busy,
|
|
89
|
+
required: true,
|
|
90
|
+
})),
|
|
91
|
+
h('p', { className: 'dsl-tokenHint' }, 'Bot Token 来自 OAuth & Permissions;App Token 来自 Basic Information,并且必须包含 connections:write。')),
|
|
92
|
+
error ? h('p', { className: 'dim-credentialError', role: 'alert' }, error.message ?? String(error)) : null,
|
|
93
|
+
h('div', { className: 'ddt-actions dim-viewActions dim-credentialActions' },
|
|
94
|
+
h('button', {
|
|
95
|
+
type: 'submit',
|
|
96
|
+
className: 'ddt-button',
|
|
97
|
+
'data-kind': 'primary',
|
|
98
|
+
disabled: busy || !botToken.trim() || !appToken.trim(),
|
|
99
|
+
}, busy ? '正在验证并连接…' : '验证并连接'),
|
|
100
|
+
h('button', {
|
|
101
|
+
type: 'button',
|
|
102
|
+
className: 'ddt-button',
|
|
103
|
+
onClick: onCancel,
|
|
104
|
+
disabled: busy,
|
|
105
|
+
}, '取消'))));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const channel = createTokenChannelSettings({
|
|
109
|
+
channel: 'Slack',
|
|
110
|
+
endpoints: SLACK_ENDPOINTS,
|
|
111
|
+
api: slackClientApi,
|
|
112
|
+
LogoGlyph: SlackLogoGlyph,
|
|
113
|
+
installStyles: installSlackStyles,
|
|
114
|
+
pageClass: 'dsl-page',
|
|
115
|
+
avatarClass: 'dsl-avatar',
|
|
116
|
+
connectionLabel: 'Socket Mode 长连接',
|
|
117
|
+
emptyTitle: '接入 Slack 机器人',
|
|
118
|
+
emptyDescription: '使用官方 App Manifest 快速配置机器人,再填写 Bot Token 与 App Token 建立本地 Socket Mode 连接。',
|
|
119
|
+
platformLabel: 'Slack 工作区',
|
|
120
|
+
CredentialPanel: SlackCredentialPanel,
|
|
121
|
+
credentialPayload: ({ botToken, appToken }) => ({ botToken, appToken }),
|
|
122
|
+
credentialAriaLabel: '使用 Manifest 和双 Token 接入 Slack 机器人',
|
|
123
|
+
credentialOpenLabel: '接入机器人',
|
|
124
|
+
credentialCloseLabel: '收起接入',
|
|
125
|
+
credentialNoun: 'Bot Token 与 App Token',
|
|
126
|
+
emptyActionLabel: '开始接入',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export const SlackSettingsTab = channel.SettingsTab;
|
|
130
|
+
export const SlackAccountCard = channel.AccountCard;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const SLACK_STYLE_ID = 'xmanrui-dsh-im-slack-settings';
|
|
2
|
+
|
|
3
|
+
const CSS = String.raw`
|
|
4
|
+
.dsl-page { --ddt-accent: #4a154b; --ddt-accent-deep: #321033; --ddt-accent-wash: #f7eef7; }
|
|
5
|
+
.dsl-avatar { color: #fff; background: #4a154b; }
|
|
6
|
+
.dsl-avatar svg { display: block; }
|
|
7
|
+
.dsl-setup { display: grid; gap: 18px; }
|
|
8
|
+
.dsl-guide { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 18px; padding: 16px; border: 1px solid color-mix(in srgb, #4a154b 18%, var(--dsw-alias-border-l2, #e5e6eb)); border-radius: 11px; background: color-mix(in srgb, #4a154b 4%, var(--dsw-alias-bg-layer-1, #fff)); }
|
|
9
|
+
.dsl-guideCopy { min-width: 0; }
|
|
10
|
+
.dsl-guideCopy strong { display: block; margin-bottom: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; }
|
|
11
|
+
.dsl-guideCopy p { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.6; }
|
|
12
|
+
.dsl-guideActions { display: flex; align-items: center; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
|
|
13
|
+
.dsl-guideActions .ddt-button { white-space: nowrap; }
|
|
14
|
+
.dsl-copyState { color: var(--dsw-alias-state-success-primary, #20a162); }
|
|
15
|
+
.dsl-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
|
16
|
+
.dsl-tokenHint { grid-column: 1 / -1; margin: -4px 0 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.55; }
|
|
17
|
+
@container (max-width: 680px) {
|
|
18
|
+
.dsl-guide { grid-template-columns: minmax(0, 1fr); }
|
|
19
|
+
.dsl-guideActions { justify-content: flex-start; }
|
|
20
|
+
.dsl-fields { grid-template-columns: minmax(0, 1fr); }
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export function installSlackStyles() {
|
|
25
|
+
if (typeof document === 'undefined') return () => {};
|
|
26
|
+
const existing = document.querySelector(`style[data-plugin-css="${SLACK_STYLE_ID}"]`);
|
|
27
|
+
if (existing) return () => {};
|
|
28
|
+
const style = document.createElement('style');
|
|
29
|
+
style.dataset.plugin = '@xmanrui/dsh-im';
|
|
30
|
+
style.dataset.pluginCss = SLACK_STYLE_ID;
|
|
31
|
+
style.textContent = CSS;
|
|
32
|
+
document.head.appendChild(style);
|
|
33
|
+
return () => style.remove();
|
|
34
|
+
}
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { WecomLogoGlyph } from '../../channel-logos.js';
|
|
4
4
|
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
|
|
5
|
+
import { h } from '../../i18n.js';
|
|
5
6
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
6
7
|
import {
|
|
7
8
|
WECOM_ENDPOINTS,
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
} from './api.js';
|
|
16
17
|
import { installWecomStyles } from './styles.js';
|
|
17
18
|
|
|
18
|
-
const h = React.createElement;
|
|
19
19
|
const ACTIVE_STATES = new Set(['pending', 'refreshing', 'connecting']);
|
|
20
20
|
|
|
21
21
|
const Button = React.forwardRef(function Button({ children, kind = 'secondary', className = '', ...props }, ref) {
|
|
@@ -2,7 +2,7 @@ export const WECOM_STYLE_ID = 'xmanrui-dsh-im-wecom-settings';
|
|
|
2
2
|
|
|
3
3
|
const CSS = String.raw`
|
|
4
4
|
.dwecom-page { --ddt-accent: #3370ff; --ddt-accent-deep: #245bdb; --ddt-accent-wash: #eef4ff; }
|
|
5
|
-
.dwecom-avatar, .dwecom-brand { color: #3370ff; background: #fff; border: 1px solid var(--dsw-alias-
|
|
5
|
+
.dwecom-avatar, .dwecom-brand { color: #3370ff; background: #fff; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); }
|
|
6
6
|
.dwecom-avatar svg, .dwecom-brand svg { display: block; }
|
|
7
7
|
`;
|
|
8
8
|
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { WeixinLogoGlyph } from '../../channel-logos.js';
|
|
4
4
|
import { QrActionIcon } from '../../credential-binding.js';
|
|
5
|
+
import { h } from '../../i18n.js';
|
|
5
6
|
import {
|
|
6
7
|
WEIXIN_ENDPOINTS,
|
|
7
8
|
WEIXIN_RPC_CHANNEL,
|
|
@@ -16,8 +17,6 @@ import {
|
|
|
16
17
|
import { createPollScheduler, useAnimationFrameScheduler } from '../../lifecycle.js';
|
|
17
18
|
import { installWeixinStyles } from './styles.js';
|
|
18
19
|
|
|
19
|
-
const h = React.createElement;
|
|
20
|
-
|
|
21
20
|
export const name = 'weixin-settings';
|
|
22
21
|
export const inject = ['slots', 'connection'];
|
|
23
22
|
|
|
@@ -5,7 +5,7 @@ const CSS = String.raw`
|
|
|
5
5
|
--dxw-accent: #07c160;
|
|
6
6
|
--dxw-accent-dark: #05994c;
|
|
7
7
|
--dxw-success: var(--dsw-alias-state-success-primary, #20a162);
|
|
8
|
-
--dxw-warning: var(--dsw-alias-state-
|
|
8
|
+
--dxw-warning: var(--dsw-alias-state-warn-primary, #d97706);
|
|
9
9
|
--dxw-error: var(--dsw-alias-state-error-primary, #d54941);
|
|
10
10
|
width: 100%;
|
|
11
11
|
max-width: 880px;
|
|
@@ -24,20 +24,20 @@ const CSS = String.raw`
|
|
|
24
24
|
.dxw-heading p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 20px; margin-top: 5px; white-space: nowrap; }
|
|
25
25
|
.dxw-tools, .dxw-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
26
26
|
.dxw-tools { width: 100%; justify-content: space-between; flex-wrap: nowrap; }
|
|
27
|
-
.dxw-badge { display: inline-flex; align-items: center; gap: 7px; min-height: 30px; padding: 0 11px; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-
|
|
27
|
+
.dxw-badge { display: inline-flex; align-items: center; gap: 7px; min-height: 30px; padding: 0 11px; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font-size: 12px; white-space: nowrap; }
|
|
28
28
|
.dxw-dot { width: 8px; height: 8px; border-radius: 50%; background: #aeb3bb; flex: none; }
|
|
29
29
|
.dxw-dot[data-tone="success"] { background: var(--dxw-success); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dxw-success) 14%, transparent); }
|
|
30
30
|
.dxw-dot[data-tone="warning"] { background: var(--dxw-warning); }
|
|
31
31
|
.dxw-dot[data-tone="error"] { background: var(--dxw-error); }
|
|
32
|
-
.dxw-button { min-height: 34px; border: 1px solid var(--dsw-alias-
|
|
33
|
-
.dxw-button:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-
|
|
32
|
+
.dxw-button { min-height: 34px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; padding: 0 13px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; font-weight: 560; cursor: pointer; text-decoration: none; transition: border-color .15s ease, background .15s ease, transform .15s ease; }
|
|
33
|
+
.dxw-button:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
34
34
|
.dxw-button:active:not(:disabled) { transform: translateY(1px); }
|
|
35
35
|
.dxw-button:focus-visible, .dxw-input:focus-visible { outline: 2px solid color-mix(in srgb, var(--dxw-accent) 70%, white); outline-offset: 2px; }
|
|
36
36
|
.dxw-button:disabled { cursor: not-allowed; opacity: .55; }
|
|
37
37
|
.dxw-button[data-kind="primary"] { color: white; border-color: var(--dxw-accent); background: var(--dxw-accent); }
|
|
38
38
|
.dxw-button[data-kind="primary"]:hover:not(:disabled) { border-color: var(--dxw-accent-dark); background: var(--dxw-accent-dark); }
|
|
39
39
|
.dxw-button[data-kind="danger"] { color: var(--dxw-error); }
|
|
40
|
-
.dxw-card { overflow: hidden; border: 1px solid var(--dsw-alias-
|
|
40
|
+
.dxw-card { overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
41
41
|
.dxw-cardBody { padding: 24px; }
|
|
42
42
|
.dxw-empty { min-height: 230px; display: grid; grid-template-columns: minmax(0, 1fr) 180px; align-items: center; gap: 30px; }
|
|
43
43
|
.dxw-empty h3 { font-size: 18px; margin-bottom: 8px; }
|
|
@@ -47,7 +47,7 @@ const CSS = String.raw`
|
|
|
47
47
|
.dxw-logo svg { width: 62px; height: 62px; }
|
|
48
48
|
.dxw-qrLayout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 34px; align-items: center; }
|
|
49
49
|
.dxw-qrColumn { display: flex; flex-direction: column; align-items: center; gap: 12px; }
|
|
50
|
-
.dxw-qrFrame { position: relative; width: 270px; aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-
|
|
50
|
+
.dxw-qrFrame { position: relative; width: 270px; aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 16px; background: white; }
|
|
51
51
|
.dxw-qrFrame img { display: block; width: 100%; height: 100%; object-fit: contain; }
|
|
52
52
|
.dxw-qrFallback { padding: 24px; text-align: center; color: #646a73; }
|
|
53
53
|
.dxw-expired { position: absolute; inset: 0; display: grid; place-items: center; padding: 30px; color: white; text-align: center; font-weight: 650; background: rgb(31 35 41 / 76%); backdrop-filter: blur(3px); }
|
|
@@ -63,7 +63,7 @@ const CSS = String.raw`
|
|
|
63
63
|
.dxw-verify h3 { margin: 8px 0; font-size: 19px; }
|
|
64
64
|
.dxw-verify p { color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.6; }
|
|
65
65
|
.dxw-codeRow { display: flex; justify-content: center; gap: 10px; margin: 24px 0 10px; }
|
|
66
|
-
.dxw-input { width: 190px; height: 42px; padding: 0 14px; border: 1px solid var(--dsw-alias-
|
|
66
|
+
.dxw-input { width: 190px; height: 42px; padding: 0 14px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-layer-1, white); color: inherit; font: inherit; font-size: 18px; letter-spacing: .16em; text-align: center; }
|
|
67
67
|
.dxw-statusNotice, .dxw-error { display: flex; align-items: center; gap: 10px; padding: 13px 15px; border: 1px solid color-mix(in srgb, var(--dxw-error) 28%, transparent); border-radius: 10px; color: var(--dxw-error); background: color-mix(in srgb, var(--dxw-error) 7%, transparent); font-size: 13px; }
|
|
68
68
|
.dxw-error { align-items: flex-start; flex-direction: column; padding: 22px; }
|
|
69
69
|
.dxw-error h3 { font-size: 17px; }
|
|
@@ -78,10 +78,10 @@ const CSS = String.raw`
|
|
|
78
78
|
.dxw-accountIdentity p { color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; margin-top: 4px; }
|
|
79
79
|
.dxw-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
|
|
80
80
|
.dxw-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 20px 0; }
|
|
81
|
-
.dxw-metric { padding: 12px 14px; border-radius: 9px; background: var(--dsw-alias-
|
|
81
|
+
.dxw-metric { padding: 12px 14px; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
82
82
|
.dxw-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
|
|
83
83
|
.dxw-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
84
|
-
.dxw-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-
|
|
84
|
+
.dxw-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
|
85
85
|
.dxw-accountFooter .dxw-actions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
|
|
86
86
|
.dxw-accountFooter .dxw-button { flex: none; white-space: nowrap; }
|
|
87
87
|
.dxw-summary { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { WhatsappLogoGlyph } from '../../channel-logos.js';
|
|
4
4
|
import { QrActionIcon } from '../../credential-binding.js';
|
|
5
|
+
import { h } from '../../i18n.js';
|
|
5
6
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
6
7
|
import {
|
|
7
8
|
WHATSAPP_ENDPOINTS,
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
} from './api.js';
|
|
15
16
|
import { installWhatsappStyles } from './styles.js';
|
|
16
17
|
|
|
17
|
-
const h = React.createElement;
|
|
18
18
|
const ACTIVE_STATES = new Set(['pending', 'connecting']);
|
|
19
19
|
|
|
20
20
|
const Button = React.forwardRef(function Button(
|