@xmanrui/dsh-im 4.2.1 → 4.4.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.
Files changed (74) hide show
  1. package/README.en.md +4 -2
  2. package/README.md +4 -2
  3. package/lib/client.js +1267 -787
  4. package/lib/index.js +237 -235
  5. package/package.json +1 -1
  6. package/plugin-src/client/access-policy-settings.js +351 -0
  7. package/plugin-src/client/channel-card-meta.js +2 -1
  8. package/plugin-src/client/channels/dingtalk/api.js +5 -0
  9. package/plugin-src/client/channels/dingtalk/index.js +1 -0
  10. package/plugin-src/client/channels/feishu/api.js +5 -0
  11. package/plugin-src/client/channels/feishu/index.js +1 -0
  12. package/plugin-src/client/channels/qq/api.js +5 -0
  13. package/plugin-src/client/channels/qq/index.js +1 -0
  14. package/plugin-src/client/channels/shared/token-api.js +5 -0
  15. package/plugin-src/client/channels/shared/token-channel.js +1 -0
  16. package/plugin-src/client/channels/telegram/api.js +2 -17
  17. package/plugin-src/client/channels/telegram/index.js +0 -112
  18. package/plugin-src/client/channels/telegram/styles.js +0 -28
  19. package/plugin-src/client/channels/wecom/api.js +5 -0
  20. package/plugin-src/client/channels/wecom/index.js +1 -0
  21. package/plugin-src/client/channels/weixin/api.js +5 -0
  22. package/plugin-src/client/channels/weixin/index.js +1 -0
  23. package/plugin-src/client/channels/whatsapp/api.js +4 -10
  24. package/plugin-src/client/channels/whatsapp/index.js +1 -125
  25. package/plugin-src/client/channels/whatsapp/styles.js +0 -25
  26. package/plugin-src/client/context-enhancement.js +207 -102
  27. package/plugin-src/client/delivery-settings.js +61 -9
  28. package/plugin-src/client/i18n.js +71 -3
  29. package/plugin-src/client/index.js +1 -0
  30. package/plugin-src/client/styles.js +59 -8
  31. package/plugin-src/host/channels/dingtalk/production.mjs +12 -1
  32. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  33. package/plugin-src/host/channels/feishu/production.mjs +39 -1
  34. package/plugin-src/host/channels/feishu/rpc.mjs +19 -2
  35. package/plugin-src/host/channels/qq/production.mjs +12 -1
  36. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  37. package/plugin-src/host/channels/shared/access-policy-production.mjs +137 -0
  38. package/plugin-src/host/channels/shared/access-policy-rpc.mjs +17 -0
  39. package/plugin-src/host/channels/shared/production.mjs +15 -1
  40. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  41. package/plugin-src/host/channels/slack/production.mjs +12 -1
  42. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  43. package/plugin-src/host/channels/telegram/rpc.mjs +2 -49
  44. package/plugin-src/host/channels/wecom/production.mjs +12 -1
  45. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  46. package/plugin-src/host/channels/weixin/production.mjs +12 -1
  47. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  48. package/plugin-src/host/channels/whatsapp/production.mjs +16 -2
  49. package/plugin-src/host/channels/whatsapp/rpc.mjs +10 -15
  50. package/scripts/verify-package.mjs +4 -3
  51. package/src/channels/dingtalk/dingtalk-bridge.mjs +61 -9
  52. package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -0
  53. package/src/channels/discord/discord-runtime.mjs +29 -1
  54. package/src/channels/feishu/bridge.mjs +286 -120
  55. package/src/channels/feishu/feishu-runtime.mjs +4 -0
  56. package/src/channels/feishu/message-utils.mjs +5 -0
  57. package/src/channels/qq/qq-bridge.mjs +59 -17
  58. package/src/channels/qq/qq-runtime.mjs +25 -3
  59. package/src/channels/shared/access-policy.mjs +210 -0
  60. package/src/channels/shared/bot-workspace-store.mjs +176 -33
  61. package/src/channels/shared/command-permission.mjs +32 -0
  62. package/src/channels/shared/compact-command.mjs +5 -2
  63. package/src/channels/shared/context-enhancement.mjs +73 -20
  64. package/src/channels/shared/i18n-en/shared-c.mjs +2 -0
  65. package/src/channels/shared/inbound-access.mjs +43 -0
  66. package/src/channels/shared/text-harness-bridge.mjs +47 -4
  67. package/src/channels/shared/workspace-command.mjs +10 -1
  68. package/src/channels/slack/slack-runtime.mjs +4 -0
  69. package/src/channels/telegram/telegram-runtime.mjs +5 -13
  70. package/src/channels/wecom/wecom-bridge.mjs +51 -4
  71. package/src/channels/wecom/wecom-runtime.mjs +4 -0
  72. package/src/channels/weixin/weixin-bridge.mjs +59 -16
  73. package/src/channels/weixin/weixin-runtime.mjs +4 -0
  74. package/src/channels/whatsapp/whatsapp-runtime.mjs +42 -20
@@ -4,34 +4,6 @@ const CSS = String.raw`
4
4
  .dtg-page { --ddt-accent: #229ed9; --ddt-accent-deep: #1687bd; --ddt-accent-wash: #eaf7fd; }
5
5
  .dtg-avatar { color: #fff; background: #229ed9; }
6
6
  .dtg-avatar svg { display: block; }
7
- .dtg-access { min-width: 0; width: 100%; max-width: 100%; display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
8
- .dtg-accessHeading { position: relative; min-width: 0; max-width: 100%; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 12px; }
9
- .dtg-accessHeading > strong { min-width: 0; font-size: 13px; overflow-wrap: anywhere; }
10
- .dtg-accessStatus { min-width: 0; max-width: 100%; flex: 0 1 auto; display: inline-flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px; }
11
- .dtg-accessBadge { min-width: 0; max-width: 100%; flex: 0 1 auto; padding: 3px 8px; border-radius: 999px; color: #1687bd; background: #eaf7fd; font-size: 11px; font-weight: 700; overflow-wrap: anywhere; text-align: center; }
12
- .dtg-accessBadge[data-mode="private-allowlist"] { color: #a15c00; background: #fff3d6; }
13
- .dtg-accessHelp { position: static; display: inline-flex; flex: none; }
14
- .dtg-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #229ed9 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1687bd; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
15
- .dtg-accessHelpButton:hover { border-color: #229ed9; color: #1178a8; background: #eaf7fd; }
16
- .dtg-accessHelpButton:focus-visible { outline: none; border-color: #229ed9; box-shadow: 0 0 0 3px color-mix(in srgb, #229ed9 18%, transparent); }
17
- .dtg-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: min(300px, 100%); max-width: 100%; display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
18
- .dtg-accessTooltipItem { display: grid; gap: 2px; }
19
- .dtg-accessTooltipItem + .dtg-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
20
- .dtg-accessTooltipItem strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 17px; font-weight: 700; }
21
- .dtg-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; font-weight: 400; }
22
- .dtg-accessHelp:hover .dtg-accessTooltip, .dtg-accessHelp:focus-within .dtg-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
23
- .dtg-accessField { min-width: 0; max-width: 100%; display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
24
- .dtg-accessField select, .dtg-accessField textarea { min-width: 0; width: 100%; max-width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
25
- .dtg-accessField select { height: 34px; padding: 0 9px; }
26
- .dtg-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
27
- .dtg-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
28
- .dtg-accessField > span, .dtg-accessField small { overflow-wrap: anywhere; }
29
- .dtg-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
30
- .dtg-accessWarning, .dtg-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
31
- .dtg-accessWarning { color: #a15c00; }
32
- .dtg-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
33
- .dtg-accessActions { min-width: 0; max-width: 100%; display: flex; justify-content: flex-end; flex-wrap: wrap; }
34
- .dtg-accessActions .ddt-button { max-width: 100%; white-space: normal; }
35
7
  `;
36
8
 
37
9
  export function installTelegramStyles() {
@@ -1,5 +1,6 @@
1
1
  import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
2
2
  import { normalizeLastMessageError } from '../../last-message-error.js';
3
+ import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
3
4
  import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
4
5
 
5
6
  export const WECOM_RPC_CHANNEL = '/wecom';
@@ -15,6 +16,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
15
16
  setWorkspace: 'bot.workspace.set',
16
17
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
17
18
  setContextEnhancement: 'bot.context-enhancement.set',
19
+ setAccessPolicy: 'bot.access-policy.set',
18
20
  });
19
21
 
20
22
  const PROVISION_STATES = new Set(['starting', 'pending', 'refreshing', 'connecting', 'connected', 'failed', 'cancelled']);
@@ -99,6 +101,9 @@ function normalizeBot(value) {
99
101
  workspace: text(value.workspace, '', 4_096),
100
102
  agentPreset: normalizeAgentPresetId(value.agentPreset),
101
103
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
104
+ ...(Object.hasOwn(value, 'accessPolicy')
105
+ ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
106
+ : {}),
102
107
  bot: {
103
108
  name: text(value.bot?.name, '企业微信机器人', 100),
104
109
  appIdMasked: text(value.bot?.appIdMasked, '应用标识已安全保存', 140),
@@ -196,6 +196,7 @@ export function AccountCard({
196
196
  botId: account.botId,
197
197
  botName: account.bot.name,
198
198
  connected: account.connected,
199
+ accessPolicy: account.accessPolicy,
199
200
  }))),
200
201
  h(WorkspaceEditor, {
201
202
  workspace: account.workspace,
@@ -1,5 +1,6 @@
1
1
  import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
2
2
  import { normalizeLastMessageError } from '../../last-message-error.js';
3
+ import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
3
4
  import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
4
5
 
5
6
  export const WEIXIN_RPC_CHANNEL = '/weixin';
@@ -14,6 +15,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
14
15
  setWorkspace: 'bot.workspace.set',
15
16
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
16
17
  setContextEnhancement: 'bot.context-enhancement.set',
18
+ setAccessPolicy: 'bot.access-policy.set',
17
19
  });
18
20
 
19
21
  const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
@@ -124,6 +126,9 @@ function normalizeBot(value) {
124
126
  workspace: string(value.workspace).slice(0, 4_096),
125
127
  agentPreset: normalizeAgentPresetId(value.agentPreset),
126
128
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
129
+ ...(Object.hasOwn(value, 'accessPolicy')
130
+ ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
131
+ : {}),
127
132
  bot: {
128
133
  name: string(value.bot.name, '微信机器人'),
129
134
  accountIdMasked: string(value.bot.accountIdMasked, '已安全保存'),
@@ -237,6 +237,7 @@ export function AccountCard({
237
237
  botId: account.botId,
238
238
  botName: account.bot.name,
239
239
  connected: account.connected,
240
+ accessPolicy: account.accessPolicy,
240
241
  }))),
241
242
  h(WorkspaceEditor, {
242
243
  workspace: account.workspace,
@@ -1,5 +1,6 @@
1
1
  import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
2
2
  import { normalizeLastMessageError } from '../../last-message-error.js';
3
+ import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
3
4
  import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
4
5
 
5
6
  export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
@@ -91,16 +92,9 @@ function normalizeBot(value) {
91
92
  workspace: text(value.workspace, '', 4_096),
92
93
  agentPreset: normalizeAgentPresetId(value.agentPreset),
93
94
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
94
- accessPolicy: {
95
- accessMode: ['self-only', 'private-allowlist', 'open'].includes(
96
- value.accessPolicy?.accessMode,
97
- ) ? value.accessPolicy.accessMode : 'self-only',
98
- allowedNumbers: Array.isArray(value.accessPolicy?.allowedNumbers)
99
- ? [...new Set(value.accessPolicy.allowedNumbers.filter((entry) => (
100
- typeof entry === 'string' && /^[1-9]\d{4,14}$/.test(entry)
101
- )))]
102
- : [],
103
- },
95
+ ...(Object.hasOwn(value, 'accessPolicy')
96
+ ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
97
+ : {}),
104
98
  bot: {
105
99
  name: text(value.bot?.name, 'WhatsApp机器人', 100),
106
100
  idMasked: text(value.bot?.idMasked, 'WhatsApp账号', 140),
@@ -31,119 +31,6 @@ import { installWhatsappStyles } from './styles.js';
31
31
 
32
32
  const ACTIVE_STATES = new Set(['pending', 'connecting']);
33
33
 
34
- function accessPolicyFor(account) {
35
- const accessMode = ['self-only', 'private-allowlist', 'open'].includes(
36
- account?.accessPolicy?.accessMode,
37
- ) ? account.accessPolicy.accessMode : 'self-only';
38
- return {
39
- accessMode,
40
- allowedNumbers: Array.isArray(account?.accessPolicy?.allowedNumbers)
41
- ? account.accessPolicy.allowedNumbers : [],
42
- };
43
- }
44
-
45
- function allowedNumbersFromText(value) {
46
- const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
47
- const normalized = entries.map((entry) => entry.replace(/^\+/, ''));
48
- if (normalized.some((entry) => !/^[1-9]\d{4,14}$/.test(entry))) {
49
- throw new TypeError('电话号码必须包含国家或地区代码,每行一个。');
50
- }
51
- return [...new Set(normalized)];
52
- }
53
-
54
- export function WhatsappAccessSettings({ account, busy = false, onSave }) {
55
- const policy = accessPolicyFor(account);
56
- const sourceNumbers = policy.allowedNumbers.join('\n');
57
- const helpId = React.useId();
58
- const [accessMode, setAccessMode] = React.useState(policy.accessMode);
59
- const [allowedNumbers, setAllowedNumbers] = React.useState(sourceNumbers);
60
- const [error, setError] = React.useState(null);
61
-
62
- React.useEffect(() => {
63
- setAccessMode(policy.accessMode);
64
- setAllowedNumbers(sourceNumbers);
65
- setError(null);
66
- }, [policy.accessMode, sourceNumbers]);
67
-
68
- const save = async (event) => {
69
- event.preventDefault();
70
- setError(null);
71
- try {
72
- const normalized = allowedNumbersFromText(allowedNumbers);
73
- if (typeof onSave !== 'function') throw new Error('WhatsApp 访问设置暂不可用。');
74
- await onSave({ accessMode, allowedNumbers: normalized });
75
- } catch (caught) {
76
- setError(caught?.message ?? 'WhatsApp 访问设置保存失败。');
77
- }
78
- };
79
-
80
- const allowlistEnabled = accessMode === 'private-allowlist';
81
- const labels = {
82
- 'self-only': '仅自己模式',
83
- 'private-allowlist': '指定联系人模式',
84
- open: '开放响应模式',
85
- };
86
- return h('form', { className: 'dwa-access', onSubmit: save },
87
- h('div', { className: 'dwa-accessHeading' },
88
- h('strong', null, '访问设置'),
89
- h('span', { className: 'dwa-accessStatus' },
90
- h('span', { className: 'dwa-accessBadge', 'data-mode': policy.accessMode },
91
- ['已生效:', labels[policy.accessMode]]),
92
- h('span', { className: 'dwa-accessHelp' },
93
- h('button', {
94
- type: 'button',
95
- className: 'dwa-accessHelpButton',
96
- 'aria-label': '查看 WhatsApp 访问模式说明',
97
- 'aria-describedby': helpId,
98
- }, h('span', { 'aria-hidden': 'true' }, '?')),
99
- h('span', { id: helpId, className: 'dwa-accessTooltip', role: 'tooltip' },
100
- h('span', { className: 'dwa-accessTooltipItem' },
101
- h('strong', null, '仅自己模式'),
102
- h('span', null, '只响应已绑定 WhatsApp 账号的自聊消息。')),
103
- h('span', { className: 'dwa-accessTooltipItem' },
104
- h('strong', null, '指定联系人模式'),
105
- h('span', null, '响应自聊和白名单联系人的私聊,忽略群聊。')),
106
- h('span', { className: 'dwa-accessTooltipItem' },
107
- h('strong', null, '开放响应模式'),
108
- h('span', null, '响应所有私聊、已绑定账号自己发出的群聊消息,以及其他群成员的提及或回复。')))))),
109
- h('label', { className: 'dwa-accessField' },
110
- h('span', null, '模式'),
111
- h('select', {
112
- value: accessMode,
113
- disabled: busy,
114
- 'aria-label': 'WhatsApp 访问模式',
115
- onChange: (event) => { setAccessMode(event.target.value); setError(null); },
116
- },
117
- h('option', { value: 'self-only' }, '仅自己模式(默认)'),
118
- h('option', { value: 'private-allowlist' }, '指定联系人模式'),
119
- h('option', { value: 'open' }, '开放响应模式'))),
120
- allowlistEnabled
121
- ? h('label', { className: 'dwa-accessField' },
122
- h('span', null, '允许私聊的 WhatsApp 电话号码'),
123
- h('textarea', {
124
- value: allowedNumbers,
125
- disabled: busy,
126
- rows: 3,
127
- placeholder: '每行一个含国家或地区代码的号码',
128
- 'aria-label': '允许私聊的 WhatsApp 电话号码',
129
- onChange: (event) => { setAllowedNumbers(event.target.value); setError(null); },
130
- }),
131
- h('small', null, '可以包含开头的 +,保存时会自动移除。'))
132
- : null,
133
- allowlistEnabled && allowedNumbers.trim() === ''
134
- ? h('p', { className: 'dwa-accessWarning', role: 'status' },
135
- '白名单为空;保存后将只接受自聊消息。')
136
- : null,
137
- error ? h('p', { className: 'dwa-accessError', role: 'alert' }, error) : null,
138
- h('div', { className: 'dwa-accessActions' },
139
- h('button', {
140
- type: 'submit',
141
- className: 'ddt-button',
142
- 'data-kind': 'secondary',
143
- disabled: busy,
144
- }, busy ? '正在保存…' : '保存访问设置')));
145
- }
146
-
147
34
  const Button = React.forwardRef(function Button(
148
35
  { children, kind = 'secondary', className = '', ...props },
149
36
  ref,
@@ -300,7 +187,6 @@ export function WhatsappAccountCard({
300
187
  onWorkspaceSave,
301
188
  onAgentPresetSave,
302
189
  onContextEnhancementSave,
303
- onAccessPolicySave,
304
190
  onRequestRemove,
305
191
  onConfirmRemove,
306
192
  onCancelRemove,
@@ -333,6 +219,7 @@ export function WhatsappAccountCard({
333
219
  botId: account.botId,
334
220
  botName: account.bot.name,
335
221
  connected: account.connected,
222
+ accessPolicy: account.accessPolicy,
336
223
  }))),
337
224
  h(WorkspaceEditor, {
338
225
  workspace: account.workspace,
@@ -349,11 +236,6 @@ export function WhatsappAccountCard({
349
236
  disabled: Boolean(busy),
350
237
  onSave: onContextEnhancementSave,
351
238
  }),
352
- h(WhatsappAccessSettings, {
353
- account,
354
- busy: Boolean(busy),
355
- onSave: onAccessPolicySave,
356
- }),
357
239
  h('div', { className: 'ddt-accountFooter dim-cardFooter' },
358
240
  h('div', { className: 'dim-cardFooterLayout' },
359
241
  h('div', { className: 'ddt-actions dim-cardActions' },
@@ -617,12 +499,6 @@ export function WhatsappSettingsTab({ rpcCall }) {
617
499
  WHATSAPP_ENDPOINTS.setContextEnhancement,
618
500
  { botId: account.botId, config },
619
501
  ),
620
- onAccessPolicySave: (accessPolicy) => botAction(
621
- account,
622
- 'access',
623
- WHATSAPP_ENDPOINTS.setAccessPolicy,
624
- { botId: account.botId, ...accessPolicy },
625
- ),
626
502
  onRequestRemove: () => setRemoveTarget(account.botId),
627
503
  onCancelRemove: () => setRemoveTarget(null),
628
504
  onConfirmRemove: async () => {
@@ -4,31 +4,6 @@ const CSS = String.raw`
4
4
  .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
5
5
  .dwa-avatar { color: #fff; background: #25d366; }
6
6
  .dwa-avatar svg { display: block; }
7
- .dwa-access { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
8
- .dwa-accessHeading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
9
- .dwa-accessHeading > strong { font-size: 13px; }
10
- .dwa-accessStatus { min-width: 0; display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
11
- .dwa-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #08785f; background: #eafbf0; font-size: 11px; font-weight: 700; }
12
- .dwa-accessBadge[data-mode="private-allowlist"] { color: #0f6f8f; background: #eaf7fd; }
13
- .dwa-accessBadge[data-mode="open"] { color: #a15c00; background: #fff3d6; }
14
- .dwa-accessHelp { position: relative; display: inline-flex; flex: none; }
15
- .dwa-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #25d366 34%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #128c7e; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; }
16
- .dwa-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: 270px; max-width: min(290px, calc(100vw - 48px)); display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
17
- .dwa-accessTooltipItem { display: grid; gap: 2px; }
18
- .dwa-accessTooltipItem + .dwa-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
19
- .dwa-accessTooltipItem strong { font-size: 12px; line-height: 17px; }
20
- .dwa-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
21
- .dwa-accessHelp:hover .dwa-accessTooltip, .dwa-accessHelp:focus-within .dwa-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
22
- .dwa-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
23
- .dwa-accessField select, .dwa-accessField textarea { width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
24
- .dwa-accessField select { height: 34px; padding: 0 9px; }
25
- .dwa-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
26
- .dwa-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
27
- .dwa-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
28
- .dwa-accessWarning, .dwa-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
29
- .dwa-accessWarning { color: #a15c00; }
30
- .dwa-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
31
- .dwa-accessActions { display: flex; justify-content: flex-end; }
32
7
  `;
33
8
 
34
9
  export function installWhatsappStyles() {