@xmanrui/dsh-im 0.1.0 → 0.2.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/lib/client.js CHANGED
@@ -40,7 +40,7 @@ __export(index_exports, {
40
40
  name: () => name
41
41
  });
42
42
  module.exports = __toCommonJS(index_exports);
43
- var React10 = __toESM(require("react"), 1);
43
+ var React11 = __toESM(require("react"), 1);
44
44
 
45
45
  // plugin-src/client/channel-logos.js
46
46
  var React = __toESM(require("react"), 1);
@@ -147,6 +147,18 @@ function DiscordLogoGlyph({ size } = {}) {
147
147
  d: "M20.32 4.37a19.8 19.8 0 0 0-4.89-1.51c-.21.38-.46.89-.63 1.29a18.4 18.4 0 0 0-5.59 0 13 13 0 0 0-.64-1.29c-1.71.29-3.36.8-4.89 1.52C.59 9.09-.25 13.68.17 18.2a19.9 19.9 0 0 0 6 3.04c.48-.66.91-1.36 1.28-2.1-.7-.26-1.37-.58-2-.96.17-.12.33-.25.49-.38 3.86 1.79 8.04 1.79 11.86 0 .16.13.32.26.49.38-.64.38-1.31.7-2.01.97.37.73.8 1.44 1.28 2.09a19.8 19.8 0 0 0 6-3.04c.49-5.24-.84-9.79-3.24-13.83ZM8.02 15.42c-1.16 0-2.11-1.07-2.11-2.38s.93-2.38 2.11-2.38c1.18 0 2.13 1.08 2.11 2.38 0 1.31-.93 2.38-2.11 2.38Zm7.95 0c-1.16 0-2.11-1.07-2.11-2.38s.93-2.38 2.11-2.38c1.18 0 2.13 1.08 2.11 2.38 0 1.31-.93 2.38-2.11 2.38Z"
148
148
  }));
149
149
  }
150
+ function WhatsappLogoGlyph({ size } = {}) {
151
+ return h("svg", {
152
+ ...dimensions(size),
153
+ viewBox: "0 0 24 24",
154
+ focusable: "false",
155
+ "aria-hidden": "true",
156
+ "data-im-channel-logo": "whatsapp"
157
+ }, h("path", {
158
+ fill: "currentColor",
159
+ d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.074-.297-.149-1.255-.462-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.521.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a12.8 12.8 0 0 0-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479s1.065 2.875 1.213 3.074c.149.198 2.095 3.2 5.077 4.487.709.306 1.262.489 1.694.626.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.29.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.981.999-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.83 9.83 0 0 1 2.893 6.991c-.003 5.45-4.437 9.884-9.886 9.884m8.413-18.297A11.8 11.8 0 0 0 12.055 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.9 11.9 0 0 0 5.688 1.448h.005c6.557 0 11.892-5.335 11.895-11.893a11.82 11.82 0 0 0-3.486-8.413"
160
+ }));
161
+ }
150
162
 
151
163
  // plugin-src/client/channels/dingtalk/api.js
152
164
  var DINGTALK_RPC_CHANNEL = "/dingtalk";
@@ -186,8 +198,8 @@ function optionalString(value, maxLength = 240) {
186
198
  return trimmed ? trimmed.slice(0, maxLength) : void 0;
187
199
  }
188
200
  function opaqueId(value) {
189
- const id4 = optionalString(value, 128);
190
- return id4 && /^[a-z\d_-]+$/i.test(id4) ? id4 : void 0;
201
+ const id5 = optionalString(value, 128);
202
+ return id5 && /^[a-z\d_-]+$/i.test(id5) ? id5 : void 0;
191
203
  }
192
204
  function timestamp(value) {
193
205
  if (typeof value === "number" && Number.isFinite(value)) return value;
@@ -1418,7 +1430,7 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
1418
1430
  deleteBot: "bot.delete"
1419
1431
  });
1420
1432
  function createTokenChannelApi(channel3, connectionSummary) {
1421
- const unwrapRpcResult8 = (result) => {
1433
+ const unwrapRpcResult9 = (result) => {
1422
1434
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
1423
1435
  throw new Error(`${channel3} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
1424
1436
  }
@@ -1429,7 +1441,7 @@ function createTokenChannelApi(channel3, connectionSummary) {
1429
1441
  }
1430
1442
  return result.value;
1431
1443
  };
1432
- const normalizeBot6 = (value) => {
1444
+ const normalizeBot7 = (value) => {
1433
1445
  if (!isRecord2(value) || !id(value.botId)) return void 0;
1434
1446
  const connected = value.connected === true;
1435
1447
  const state = ACCOUNT_STATES2.has(value.state) ? value.state : "offline";
@@ -1455,23 +1467,23 @@ function createTokenChannelApi(channel3, connectionSummary) {
1455
1467
  } : null
1456
1468
  };
1457
1469
  };
1458
- const normalizeSnapshot7 = (value) => {
1470
+ const normalizeSnapshot8 = (value) => {
1459
1471
  const source = isRecord2(value?.snapshot) ? value.snapshot : value;
1460
1472
  if (!isRecord2(source) || !Array.isArray(source.bots)) {
1461
1473
  throw new Error(`${channel3} \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868`);
1462
1474
  }
1463
- const bots = source.bots.map(normalizeBot6).filter(Boolean);
1475
+ const bots = source.bots.map(normalizeBot7).filter(Boolean);
1464
1476
  return {
1465
1477
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
1466
1478
  bots,
1467
1479
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length }
1468
1480
  };
1469
1481
  };
1470
- const presentError8 = (error) => ({
1482
+ const presentError9 = (error) => ({
1471
1483
  code: text(error?.code, `${channel3.toUpperCase()}_ERROR`, 80),
1472
1484
  message: text(error?.message, `${channel3}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
1473
1485
  });
1474
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult8, normalizeSnapshot: normalizeSnapshot7, presentError: presentError8 });
1486
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult9, normalizeSnapshot: normalizeSnapshot8, presentError: presentError9 });
1475
1487
  }
1476
1488
 
1477
1489
  // plugin-src/client/channels/discord/api.js
@@ -1519,7 +1531,14 @@ function createTokenChannelSettings(definition) {
1519
1531
  tokenPlaceholder,
1520
1532
  emptyTitle,
1521
1533
  emptyDescription,
1522
- platformLabel
1534
+ platformLabel,
1535
+ CredentialPanel = null,
1536
+ credentialPayload = ({ secret }) => ({ token: secret }),
1537
+ credentialAriaLabel = `\u4F7F\u7528 Bot Token \u63A5\u5165 ${channel3} \u673A\u5668\u4EBA`,
1538
+ credentialOpenLabel = "\u624B\u52A8\u63A5\u5165",
1539
+ credentialCloseLabel = "\u6536\u8D77\u51ED\u636E",
1540
+ credentialNoun = "Bot Token",
1541
+ emptyActionLabel = "\u586B\u5199 Bot Token"
1523
1542
  } = definition;
1524
1543
  function AccountCard5({ account, busy, removing, onReconnect, onRequestRemove, onConfirmRemove, onCancelRemove }) {
1525
1544
  const state = busy === "reconnect" ? "connecting" : account.state;
@@ -1599,7 +1618,7 @@ function createTokenChannelSettings(definition) {
1599
1618
  "div",
1600
1619
  { className: "ddt-confirm dim-confirm", role: "alertdialog" },
1601
1620
  h4("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`),
1602
- h4("p", null, `\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 Bot Token\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002${platformLabel}\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002`),
1621
+ h4("p", null, `\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 ${credentialNoun}\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002${platformLabel}\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002`),
1603
1622
  h4(
1604
1623
  "div",
1605
1624
  { className: "ddt-actions dim-viewActions" },
@@ -1673,11 +1692,14 @@ function createTokenChannelSettings(definition) {
1673
1692
  window.clearInterval(timer);
1674
1693
  };
1675
1694
  }, [loadStatus, model.phase]);
1676
- const bindCredentials = React4.useCallback(async ({ secret }) => {
1695
+ const bindCredentials = React4.useCallback(async (values) => {
1677
1696
  setBusy(true);
1678
1697
  setCredentialError(null);
1679
1698
  try {
1680
- const snapshot = api3.normalizeSnapshot(await invoke(endpoints.bindCredentials, { token: secret }));
1699
+ const snapshot = api3.normalizeSnapshot(await invoke(
1700
+ endpoints.bindCredentials,
1701
+ credentialPayload(values)
1702
+ ));
1681
1703
  if (!mounted.current) return;
1682
1704
  setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
1683
1705
  setCredentialOpen(false);
@@ -1755,8 +1777,8 @@ function createTokenChannelSettings(definition) {
1755
1777
  },
1756
1778
  disabled: busy,
1757
1779
  "aria-pressed": credentialOpen,
1758
- "aria-label": `\u4F7F\u7528 Bot Token \u63A5\u5165 ${channel3} \u673A\u5668\u4EBA`
1759
- }, h4(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165")
1780
+ "aria-label": credentialAriaLabel
1781
+ }, h4(CredentialActionIcon), credentialOpen ? credentialCloseLabel : credentialOpenLabel)
1760
1782
  ),
1761
1783
  model.totals.configured > 0 ? h4(
1762
1784
  "div",
@@ -1781,7 +1803,15 @@ function createTokenChannelSettings(definition) {
1781
1803
  ) : h4(
1782
1804
  React4.Fragment,
1783
1805
  null,
1784
- credentialOpen ? h4(CredentialBindingPanel, {
1806
+ credentialOpen ? CredentialPanel ? h4(CredentialPanel, {
1807
+ busy,
1808
+ error: credentialError,
1809
+ onSubmit: bindCredentials,
1810
+ onCancel: () => {
1811
+ setCredentialOpen(false);
1812
+ setCredentialError(null);
1813
+ }
1814
+ }) : h4(CredentialBindingPanel, {
1785
1815
  channel: channel3,
1786
1816
  secretLabel: "Bot Token",
1787
1817
  secretPlaceholder: tokenPlaceholder,
@@ -1816,7 +1846,7 @@ function createTokenChannelSettings(definition) {
1816
1846
  h4(Button3, {
1817
1847
  kind: "primary",
1818
1848
  onClick: () => setCredentialOpen(true)
1819
- }, "\u586B\u5199 Bot Token")
1849
+ }, emptyActionLabel)
1820
1850
  )
1821
1851
  ),
1822
1852
  h4("div", {
@@ -2988,14 +3018,14 @@ var HEALTH_LABELS = {
2988
3018
  offline: "\u8FDE\u63A5\u4E2D\u65AD",
2989
3019
  error: "\u9700\u8981\u5904\u7406"
2990
3020
  };
2991
- function formatCheckedTime(timestamp6) {
2992
- if (!timestamp6) return "\u5C1A\u672A\u68C0\u67E5";
3021
+ function formatCheckedTime(timestamp7) {
3022
+ if (!timestamp7) return "\u5C1A\u672A\u68C0\u67E5";
2993
3023
  try {
2994
3024
  return new Intl.DateTimeFormat("zh-CN", {
2995
3025
  hour: "2-digit",
2996
3026
  minute: "2-digit",
2997
3027
  second: "2-digit"
2998
- }).format(new Date(timestamp6));
3028
+ }).format(new Date(timestamp7));
2999
3029
  } catch {
3000
3030
  return "\u521A\u521A";
3001
3031
  }
@@ -3334,14 +3364,14 @@ function FeishuSettingsTab({ rpcCall }) {
3334
3364
  FEISHU_ENDPOINTS.beginProvisioning,
3335
3365
  { locale: "zh-CN" }
3336
3366
  ));
3337
- const timestamp6 = Date.now();
3338
- setNow(timestamp6);
3367
+ const timestamp7 = Date.now();
3368
+ setNow(timestamp7);
3339
3369
  setModel((current) => ({
3340
3370
  ...current,
3341
3371
  provisioning: {
3342
3372
  phase: "qr",
3343
3373
  ...provision2,
3344
- durationMs: Math.max(1, provision2.expiresAt - timestamp6),
3374
+ durationMs: Math.max(1, provision2.expiresAt - timestamp7),
3345
3375
  expired: false
3346
3376
  }
3347
3377
  }));
@@ -3397,9 +3427,9 @@ function FeishuSettingsTab({ rpcCall }) {
3397
3427
  React6.useEffect(() => {
3398
3428
  if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
3399
3429
  const tick = () => {
3400
- const timestamp6 = Date.now();
3401
- setNow(timestamp6);
3402
- if (timestamp6 >= countdownExpiresAt) {
3430
+ const timestamp7 = Date.now();
3431
+ setNow(timestamp7);
3432
+ if (timestamp7 >= countdownExpiresAt) {
3403
3433
  setModel((current) => current.provisioning?.attemptId === countdownAttemptId ? { ...current, provisioning: { ...current.provisioning, expired: true } } : current);
3404
3434
  }
3405
3435
  };
@@ -5440,14 +5470,14 @@ function ProvisionError3({ provision, busy, onRetry, onClose }) {
5440
5470
  )
5441
5471
  );
5442
5472
  }
5443
- function checkedTime5(timestamp6) {
5444
- if (!timestamp6) return "\u5C1A\u672A\u68C0\u67E5";
5473
+ function checkedTime5(timestamp7) {
5474
+ if (!timestamp7) return "\u5C1A\u672A\u68C0\u67E5";
5445
5475
  try {
5446
5476
  return new Intl.DateTimeFormat("zh-CN", {
5447
5477
  hour: "2-digit",
5448
5478
  minute: "2-digit",
5449
5479
  second: "2-digit"
5450
- }).format(new Date(timestamp6));
5480
+ }).format(new Date(timestamp7));
5451
5481
  } catch {
5452
5482
  return "\u521A\u521A";
5453
5483
  }
@@ -5855,9 +5885,668 @@ function WeixinSettingsTab({ rpcCall }) {
5855
5885
  );
5856
5886
  }
5857
5887
 
5888
+ // plugin-src/client/channels/whatsapp/api.js
5889
+ var WHATSAPP_RPC_CHANNEL = "/whatsapp";
5890
+ var WHATSAPP_ENDPOINTS = Object.freeze({
5891
+ status: "connection.status",
5892
+ beginProvisioning: "provision.begin",
5893
+ pollProvisioning: "provision.poll",
5894
+ cancelProvisioning: "provision.cancel",
5895
+ reconnectBot: "bot.reconnect",
5896
+ deleteBot: "bot.delete"
5897
+ });
5898
+ var PROVISION_STATES5 = /* @__PURE__ */ new Set(["starting", "pending", "connecting", "connected", "failed", "cancelled"]);
5899
+ var BOT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
5900
+ var QR_DATA_URL4 = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
5901
+ function isRecord7(value) {
5902
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5903
+ }
5904
+ function text4(value, fallback, max = 240) {
5905
+ return typeof value === "string" && value.trim() ? value.trim().slice(0, max) : fallback;
5906
+ }
5907
+ function id4(value) {
5908
+ const result = text4(value, "", 128);
5909
+ return /^[a-z\d_-]+$/i.test(result) ? result : void 0;
5910
+ }
5911
+ function timestamp6(value) {
5912
+ if (typeof value === "number" && Number.isFinite(value)) return value;
5913
+ const parsed = typeof value === "string" ? Date.parse(value) : Number.NaN;
5914
+ return Number.isNaN(parsed) ? void 0 : parsed;
5915
+ }
5916
+ function unwrapRpcResult8(result) {
5917
+ if (!isRecord7(result) || typeof result.ok !== "boolean") {
5918
+ throw new Error("WhatsApp \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
5919
+ }
5920
+ if (!result.ok) {
5921
+ const error = new Error(text4(result.error?.message, "WhatsApp \u64CD\u4F5C\u5931\u8D25"));
5922
+ error.code = text4(result.error?.code, "WHATSAPP_RPC_ERROR", 80);
5923
+ throw error;
5924
+ }
5925
+ return result.value;
5926
+ }
5927
+ function safeQrSource6(value) {
5928
+ return typeof value === "string" && value.length <= 2 * 1024 * 1024 && QR_DATA_URL4.test(value) ? value : void 0;
5929
+ }
5930
+ function normalizeProvisioning6(value, now = Date.now()) {
5931
+ const source = isRecord7(value?.provisioning) ? value.provisioning : value;
5932
+ if (!isRecord7(source)) throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u8FDB\u5EA6");
5933
+ const attemptId = id4(source.attemptId);
5934
+ if (!attemptId) throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u626B\u7801\u4EFB\u52A1");
5935
+ const reported = text4(source.status, "failed", 32);
5936
+ const result = {
5937
+ attemptId,
5938
+ status: PROVISION_STATES5.has(reported) ? reported : "failed",
5939
+ expiresAt: timestamp6(source.expiresAt) ?? now + 6e4,
5940
+ pollIntervalMs: Math.min(5e3, Math.max(500, Number(source.pollIntervalMs) || 1e3)),
5941
+ qrRevision: Number.isSafeInteger(source.qrRevision) ? source.qrRevision : 0
5942
+ };
5943
+ const qrCodeDataUrl = safeQrSource6(source.qrCodeDataUrl);
5944
+ if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
5945
+ if (id4(source.botId)) result.botId = id4(source.botId);
5946
+ if (isRecord7(source.error)) result.error = {
5947
+ code: text4(source.error.code, "WHATSAPP_PROVISION_FAILED", 80),
5948
+ message: text4(source.error.message, "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210")
5949
+ };
5950
+ return result;
5951
+ }
5952
+ function normalizeBot6(value) {
5953
+ if (!isRecord7(value) || !id4(value.botId)) return void 0;
5954
+ const connected = value.connected === true;
5955
+ const state = BOT_STATES.has(value.state) ? value.state : "offline";
5956
+ return {
5957
+ botId: id4(value.botId),
5958
+ connected,
5959
+ state: connected ? "connected" : state,
5960
+ bot: {
5961
+ name: text4(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
5962
+ idMasked: text4(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
5963
+ },
5964
+ health: {
5965
+ summary: text4(value.health?.summary, connected ? "WhatsApp Web \u5173\u8054\u8BBE\u5907\u8FD0\u884C\u6B63\u5E38" : "WhatsApp \u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
5966
+ lastCheckedAt: timestamp6(value.health?.lastCheckedAt)
5967
+ },
5968
+ error: isRecord7(value.error) ? {
5969
+ code: text4(value.error.code, "WHATSAPP_ACCOUNT_ERROR", 80),
5970
+ message: text4(value.error.message, "WhatsApp \u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA")
5971
+ } : null
5972
+ };
5973
+ }
5974
+ function normalizeSnapshot7(value) {
5975
+ const source = isRecord7(value?.snapshot) ? value.snapshot : value;
5976
+ if (!isRecord7(source) || !Array.isArray(source.bots)) {
5977
+ throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
5978
+ }
5979
+ const bots = source.bots.map(normalizeBot6).filter(Boolean);
5980
+ return {
5981
+ revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
5982
+ bots,
5983
+ totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
5984
+ provisioning: source.provisioning ? normalizeProvisioning6(source.provisioning) : null
5985
+ };
5986
+ }
5987
+ function presentError8(error) {
5988
+ return {
5989
+ code: text4(error?.code, "WHATSAPP_ERROR", 80),
5990
+ message: text4(error?.message, "WhatsApp \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
5991
+ };
5992
+ }
5993
+ function formatRemaining6(milliseconds) {
5994
+ const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1e3) || 0);
5995
+ return `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`;
5996
+ }
5997
+
5998
+ // plugin-src/client/channels/whatsapp/index.js
5999
+ var React10 = __toESM(require("react"), 1);
6000
+
6001
+ // plugin-src/client/channels/whatsapp/styles.js
6002
+ var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
6003
+ var CSS8 = String.raw`
6004
+ .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
6005
+ .dwa-avatar { color: #fff; background: #25d366; }
6006
+ .dwa-avatar svg { display: block; }
6007
+ `;
6008
+ function installWhatsappStyles() {
6009
+ if (typeof document === "undefined") return () => {
6010
+ };
6011
+ const existing = document.querySelector(`style[data-plugin-css="${WHATSAPP_STYLE_ID}"]`);
6012
+ if (existing) return () => {
6013
+ };
6014
+ const style = document.createElement("style");
6015
+ style.dataset.plugin = "@xmanrui/dsh-im";
6016
+ style.dataset.pluginCss = WHATSAPP_STYLE_ID;
6017
+ style.textContent = CSS8;
6018
+ document.head.appendChild(style);
6019
+ return () => style.remove();
6020
+ }
6021
+
6022
+ // plugin-src/client/channels/whatsapp/index.js
6023
+ var h9 = React10.createElement;
6024
+ var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
6025
+ var Button13 = React10.forwardRef(function Button14({ children, kind = "secondary", className = "", ...props }, ref) {
6026
+ return h9("button", {
6027
+ ...props,
6028
+ ref,
6029
+ type: "button",
6030
+ className: `ddt-button ${className}`.trim(),
6031
+ "data-kind": kind
6032
+ }, children);
6033
+ });
6034
+ function checkedTime6(value) {
6035
+ if (!value) return "\u5C1A\u672A\u68C0\u67E5";
6036
+ try {
6037
+ return new Intl.DateTimeFormat("zh-CN", {
6038
+ hour: "2-digit",
6039
+ minute: "2-digit",
6040
+ second: "2-digit"
6041
+ }).format(new Date(value));
6042
+ } catch {
6043
+ return "\u521A\u521A";
6044
+ }
6045
+ }
6046
+ function Heading6({ totals, busy, onAdd, addButtonRef }) {
6047
+ return h9(
6048
+ "div",
6049
+ { className: "ddt-heading" },
6050
+ h9(
6051
+ "div",
6052
+ { className: "ddt-tools" },
6053
+ h9(
6054
+ "div",
6055
+ { className: "dim-bindActions" },
6056
+ h9(Button13, {
6057
+ kind: "primary",
6058
+ className: "dim-scanButton",
6059
+ onClick: onAdd,
6060
+ disabled: busy,
6061
+ ref: addButtonRef,
6062
+ "aria-label": "\u626B\u7801\u63A5\u5165 WhatsApp \u673A\u5668\u4EBA"
6063
+ }, h9(QrActionIcon), busy ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA")
6064
+ ),
6065
+ totals.configured > 0 ? h9(
6066
+ "div",
6067
+ { className: "ddt-badge dim-onlineBadge" },
6068
+ h9("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`)
6069
+ ) : null
6070
+ )
6071
+ );
6072
+ }
6073
+ function LoadingView6() {
6074
+ return h9("div", {
6075
+ className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView",
6076
+ "aria-busy": "true"
6077
+ }, h9("div", { className: "ddt-spinner dim-spinner" }), "\u6B63\u5728\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001\u2026");
6078
+ }
6079
+ function EmptyView6({ busy, onStart }) {
6080
+ return h9(
6081
+ "div",
6082
+ { className: "ddt-card dim-surfaceCard" },
6083
+ h9(
6084
+ "div",
6085
+ { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" },
6086
+ h9(
6087
+ "div",
6088
+ { className: "dim-emptyCopy" },
6089
+ h9(
6090
+ "div",
6091
+ { className: "ddt-stateLabel dim-stateLabel" },
6092
+ h9("span", { className: "ddt-dot dim-stateDot" }),
6093
+ h9("span", null, "\u5C1A\u672A\u63A5\u5165 WhatsApp \u673A\u5668\u4EBA")
6094
+ ),
6095
+ h9("h3", null, "\u626B\u7801\u7ED1\u5B9A WhatsApp \u673A\u5668\u4EBA"),
6096
+ h9("p", null, "\u4F7F\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801\u5373\u53EF\u63A5\u5165\u3002"),
6097
+ h9(
6098
+ "div",
6099
+ { className: "ddt-actions dim-viewActions" },
6100
+ h9(
6101
+ Button13,
6102
+ { kind: "primary", onClick: onStart, disabled: busy },
6103
+ busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u4E8C\u7EF4\u7801"
6104
+ )
6105
+ )
6106
+ ),
6107
+ h9("div", {
6108
+ className: "ddt-brandMark dim-emptyBrand dwa-avatar",
6109
+ "aria-hidden": "true"
6110
+ }, h9(WhatsappLogoGlyph, { size: 64 }))
6111
+ )
6112
+ );
6113
+ }
6114
+ function QrPanel5({ provision, now, busy, onRefresh, onCancel }) {
6115
+ const source = safeQrSource6(provision.qrCodeDataUrl);
6116
+ const remaining = Math.max(0, provision.expiresAt - now);
6117
+ const duration = Math.max(1, provision.durationMs ?? 6e4);
6118
+ const progress = Math.round(Math.min(1, remaining / duration) * 100);
6119
+ return h9(
6120
+ "div",
6121
+ { className: "ddt-card dim-surfaceCard" },
6122
+ h9(
6123
+ "div",
6124
+ { className: "ddt-cardBody ddt-qrLayout dim-surfaceBody dim-qrLayout" },
6125
+ h9(
6126
+ "div",
6127
+ { className: "ddt-qrColumn dim-qrColumn" },
6128
+ h9(
6129
+ "div",
6130
+ { className: "ddt-qrFrame dim-qrFrame" },
6131
+ source ? h9("img", {
6132
+ src: source,
6133
+ alt: "\u7528\u4E8E\u5173\u8054 WhatsApp \u8BBE\u5907\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801"
6134
+ }) : h9("div", { className: "ddt-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u6B63\u5728\u751F\u6210\u2026")
6135
+ ),
6136
+ h9(
6137
+ "div",
6138
+ { className: "ddt-countdown dim-countdown" },
6139
+ h9(
6140
+ "div",
6141
+ { className: "ddt-countdownTop dim-countdownTop" },
6142
+ h9("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"),
6143
+ h9("strong", null, formatRemaining6(remaining))
6144
+ ),
6145
+ h9("div", {
6146
+ className: "ddt-progress dim-progress",
6147
+ style: { "--ddt-progress": `${progress}%` }
6148
+ }, h9("span"))
6149
+ )
6150
+ ),
6151
+ h9(
6152
+ "div",
6153
+ { className: "ddt-qrCopy dim-qrCopy" },
6154
+ h9(
6155
+ "div",
6156
+ { className: "ddt-stateLabel dim-stateLabel" },
6157
+ h9("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }),
6158
+ h9("span", null, "\u7B49\u5F85 WhatsApp \u626B\u7801")
6159
+ ),
6160
+ h9("h3", null, "\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801"),
6161
+ h9(
6162
+ "ol",
6163
+ { className: "ddt-steps dim-steps" },
6164
+ h9("li", null, "\u6253\u5F00 WhatsApp \u2192 \u8BBE\u7F6E \u2192 \u5DF2\u5173\u8054\u8BBE\u5907"),
6165
+ h9("li", null, "\u70B9\u51FB\u201C\u5173\u8054\u8BBE\u5907\u201D\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801")
6166
+ ),
6167
+ h9(
6168
+ "div",
6169
+ { className: "ddt-actions dim-viewActions" },
6170
+ h9(Button13, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6171
+ h9(Button13, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
6172
+ )
6173
+ )
6174
+ )
6175
+ );
6176
+ }
6177
+ function ProvisionView3({ provision, busy, onRetry, onClose }) {
6178
+ if (provision.status === "starting" || provision.status === "connecting") {
6179
+ const starting = provision.status === "starting";
6180
+ return h9(
6181
+ "div",
6182
+ {
6183
+ className: "ddt-card ddt-loading dim-surfaceCard dim-specialView",
6184
+ "aria-busy": "true"
6185
+ },
6186
+ h9("div", { className: "ddt-spinner dim-spinner" }),
6187
+ h9("h3", null, starting ? "\u6B63\u5728\u751F\u6210 WhatsApp \u4E8C\u7EF4\u7801" : "\u5DF2\u626B\u7801\uFF0C\u6B63\u5728\u8FDE\u63A5 WhatsApp"),
6188
+ h9("p", null, starting ? "\u6B63\u5728\u5EFA\u7ACB\u5B89\u5168\u7684\u5173\u8054\u8BBE\u5907\u4F1A\u8BDD\u3002" : "\u5173\u8054\u8BBE\u5907\u6B63\u5728\u63A5\u5165 DeepSeek Harness\u3002")
6189
+ );
6190
+ }
6191
+ const error = provision.error ?? {
6192
+ code: "WHATSAPP_PROVISION_FAILED",
6193
+ message: "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210"
6194
+ };
6195
+ return h9(
6196
+ "div",
6197
+ { className: "ddt-card dim-surfaceCard" },
6198
+ h9(
6199
+ "div",
6200
+ { className: "ddt-inlineError dim-inlineError", role: "alert" },
6201
+ h9("h3", null, "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210"),
6202
+ h9("p", null, error.message),
6203
+ h9("span", { className: "ddt-errorCode" }, error.code),
6204
+ h9(
6205
+ "div",
6206
+ { className: "ddt-actions dim-viewActions" },
6207
+ h9(Button13, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6208
+ h9(Button13, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
6209
+ )
6210
+ )
6211
+ );
6212
+ }
6213
+ function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) {
6214
+ return h9(
6215
+ "div",
6216
+ { className: "ddt-confirm dim-confirm", role: "alertdialog" },
6217
+ h9("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`),
6218
+ h9("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 WhatsApp \u5173\u8054\u8BBE\u5907\u548C\u4F1A\u8BDD\u6620\u5C04\u3002"),
6219
+ h9(
6220
+ "div",
6221
+ { className: "ddt-actions dim-viewActions" },
6222
+ h9(Button13, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
6223
+ h9(
6224
+ Button13,
6225
+ { kind: "danger", onClick: onConfirm, disabled: busy },
6226
+ busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165"
6227
+ )
6228
+ )
6229
+ );
6230
+ }
6231
+ function WhatsappAccountCard({
6232
+ account,
6233
+ busy,
6234
+ removing,
6235
+ onReconnect,
6236
+ onRequestRemove,
6237
+ onConfirmRemove,
6238
+ onCancelRemove
6239
+ }) {
6240
+ const state = busy === "reconnect" ? "connecting" : account.state;
6241
+ const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
6242
+ const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
6243
+ const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
6244
+ return h9(
6245
+ "article",
6246
+ { className: "ddt-card dim-botCard", "data-bot-id": account.botId },
6247
+ h9(
6248
+ "div",
6249
+ { className: "ddt-cardBody dim-botCardBody" },
6250
+ h9(
6251
+ "div",
6252
+ { className: "ddt-accountTop dim-botCardTop" },
6253
+ h9(
6254
+ "div",
6255
+ { className: "ddt-accountIdentity dim-botIdentity" },
6256
+ h9("div", {
6257
+ className: "ddt-avatar dim-botAvatar dwa-avatar",
6258
+ "aria-hidden": "true"
6259
+ }, h9(WhatsappLogoGlyph, { size: 29 })),
6260
+ h9(
6261
+ "div",
6262
+ { className: "dim-botName" },
6263
+ h9("h3", null, account.bot.name),
6264
+ h9("p", null, account.bot.idMasked)
6265
+ )
6266
+ ),
6267
+ h9(
6268
+ "div",
6269
+ { className: "ddt-health dim-botHealth" },
6270
+ h9("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
6271
+ h9("span", null, stateLabel)
6272
+ )
6273
+ ),
6274
+ h9(
6275
+ "dl",
6276
+ { className: "ddt-metrics dim-botMetrics" },
6277
+ h9(
6278
+ "div",
6279
+ { className: "ddt-metric dim-botMetric" },
6280
+ h9("dt", null, "\u6D88\u606F\u901A\u9053"),
6281
+ h9("dd", null, account.connected ? "WhatsApp Web" : "\u79BB\u7EBF")
6282
+ ),
6283
+ h9(
6284
+ "div",
6285
+ { className: "ddt-metric dim-botMetric" },
6286
+ h9("dt", null, "\u6700\u8FD1\u68C0\u67E5"),
6287
+ h9("dd", null, checkedTime6(account.health.lastCheckedAt))
6288
+ )
6289
+ ),
6290
+ h9(
6291
+ "div",
6292
+ { className: "ddt-accountFooter dim-cardFooter" },
6293
+ summary ? h9("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
6294
+ h9(
6295
+ "div",
6296
+ { className: "ddt-actions dim-cardActions" },
6297
+ h9(Button13, {
6298
+ className: "dim-cardAction",
6299
+ onClick: onReconnect,
6300
+ disabled: Boolean(busy)
6301
+ }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
6302
+ h9(Button13, {
6303
+ className: "dim-cardAction",
6304
+ kind: "danger",
6305
+ onClick: onRequestRemove,
6306
+ disabled: Boolean(busy)
6307
+ }, "\u79FB\u9664\u63A5\u5165")
6308
+ )
6309
+ )
6310
+ ),
6311
+ removing ? h9(RemoveConfirmation5, {
6312
+ account,
6313
+ busy: busy === "delete",
6314
+ onConfirm: onConfirmRemove,
6315
+ onCancel: onCancelRemove
6316
+ }) : null
6317
+ );
6318
+ }
6319
+ function WhatsappSettingsTab({ rpcCall }) {
6320
+ const [model, setModel] = React10.useState({
6321
+ phase: "loading",
6322
+ bots: [],
6323
+ totals: { configured: 0, connected: 0 },
6324
+ error: null
6325
+ });
6326
+ const [provision, setProvision] = React10.useState(null);
6327
+ const [busy, setBusy] = React10.useState(false);
6328
+ const [busyByBot, setBusyByBot] = React10.useState({});
6329
+ const [removeTarget, setRemoveTarget] = React10.useState(null);
6330
+ const [now, setNow] = React10.useState(Date.now());
6331
+ const mounted = React10.useRef(true);
6332
+ const addButtonRef = React10.useRef(null);
6333
+ React10.useEffect(() => {
6334
+ const disposeDingtalk = installDingtalkStyles();
6335
+ const disposeWhatsapp = installWhatsappStyles();
6336
+ mounted.current = true;
6337
+ return () => {
6338
+ mounted.current = false;
6339
+ disposeWhatsapp();
6340
+ disposeDingtalk();
6341
+ };
6342
+ }, []);
6343
+ const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => {
6344
+ if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
6345
+ return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
6346
+ }, [rpcCall]);
6347
+ const loadStatus = React10.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
6348
+ if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
6349
+ try {
6350
+ const snapshot = normalizeSnapshot7(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
6351
+ if (!mounted.current || signal?.aborted) return void 0;
6352
+ setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
6353
+ if (restore && snapshot.provisioning) setProvision({
6354
+ ...snapshot.provisioning,
6355
+ durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
6356
+ });
6357
+ return snapshot;
6358
+ } catch (error) {
6359
+ if (error?.name !== "AbortError" && mounted.current && !signal?.aborted) {
6360
+ setModel((current) => ({
6361
+ ...current,
6362
+ phase: silent ? current.phase : "error",
6363
+ error: presentError8(error)
6364
+ }));
6365
+ }
6366
+ return void 0;
6367
+ }
6368
+ }, [invoke]);
6369
+ React10.useEffect(() => {
6370
+ const controller = new AbortController();
6371
+ void loadStatus({ signal: controller.signal, restore: true });
6372
+ return () => controller.abort();
6373
+ }, [loadStatus]);
6374
+ React10.useEffect(() => {
6375
+ if (model.phase !== "ready") return void 0;
6376
+ const controller = new AbortController();
6377
+ const timer = window.setInterval(
6378
+ () => void loadStatus({ signal: controller.signal, silent: true }),
6379
+ 15e3
6380
+ );
6381
+ return () => {
6382
+ controller.abort();
6383
+ window.clearInterval(timer);
6384
+ };
6385
+ }, [loadStatus, model.phase]);
6386
+ React10.useEffect(() => {
6387
+ if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
6388
+ const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
6389
+ return () => window.clearInterval(timer);
6390
+ }, [provision?.attemptId, provision?.status]);
6391
+ const startProvisioning = React10.useCallback(async (replace = false) => {
6392
+ setBusy(true);
6393
+ try {
6394
+ if (replace && provision?.attemptId) {
6395
+ await invoke(WHATSAPP_ENDPOINTS.cancelProvisioning, { attemptId: provision.attemptId });
6396
+ }
6397
+ if (!mounted.current) return;
6398
+ setProvision({ status: "starting" });
6399
+ const started = normalizeProvisioning6(await invoke(WHATSAPP_ENDPOINTS.beginProvisioning, {}));
6400
+ if (!mounted.current) return;
6401
+ setNow(Date.now());
6402
+ setProvision({ ...started, durationMs: Math.max(1, started.expiresAt - Date.now()) });
6403
+ } catch (error) {
6404
+ if (mounted.current) setProvision({ status: "failed", error: presentError8(error) });
6405
+ } finally {
6406
+ if (mounted.current) setBusy(false);
6407
+ }
6408
+ }, [invoke, provision?.attemptId]);
6409
+ const closeProvision = React10.useCallback(async () => {
6410
+ setBusy(true);
6411
+ try {
6412
+ if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
6413
+ await invoke(WHATSAPP_ENDPOINTS.cancelProvisioning, { attemptId: provision.attemptId });
6414
+ }
6415
+ if (mounted.current) setProvision(null);
6416
+ } finally {
6417
+ if (mounted.current) setBusy(false);
6418
+ }
6419
+ }, [invoke, provision?.attemptId, provision?.status]);
6420
+ React10.useEffect(() => {
6421
+ const attemptId = provision?.attemptId;
6422
+ if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
6423
+ const controller = new AbortController();
6424
+ let disposed = false;
6425
+ let timer;
6426
+ const schedule = (delay) => {
6427
+ if (disposed || controller.signal.aborted) return;
6428
+ timer = window.setTimeout(() => void poll(), delay);
6429
+ };
6430
+ const poll = async () => {
6431
+ try {
6432
+ const current = normalizeProvisioning6(await invoke(
6433
+ WHATSAPP_ENDPOINTS.pollProvisioning,
6434
+ { attemptId },
6435
+ controller.signal
6436
+ ));
6437
+ if (disposed || controller.signal.aborted || !mounted.current) return;
6438
+ if (current.status === "connected") {
6439
+ setProvision(null);
6440
+ await loadStatus({ signal: controller.signal, silent: true });
6441
+ return;
6442
+ }
6443
+ setProvision((previous) => ({
6444
+ ...current,
6445
+ durationMs: previous?.durationMs ?? Math.max(1, current.expiresAt - Date.now())
6446
+ }));
6447
+ if (ACTIVE_STATES3.has(current.status)) schedule(current.pollIntervalMs);
6448
+ } catch (error) {
6449
+ if (!disposed && !controller.signal.aborted && mounted.current) {
6450
+ setProvision({ status: "failed", error: presentError8(error) });
6451
+ }
6452
+ }
6453
+ };
6454
+ schedule(provision.pollIntervalMs ?? 1e3);
6455
+ return () => {
6456
+ disposed = true;
6457
+ controller.abort();
6458
+ if (timer) window.clearTimeout(timer);
6459
+ };
6460
+ }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
6461
+ const botAction = React10.useCallback(async (account, operation, endpoint, payload) => {
6462
+ setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
6463
+ try {
6464
+ const snapshot = normalizeSnapshot7(await invoke(endpoint, payload));
6465
+ if (mounted.current) {
6466
+ setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
6467
+ }
6468
+ } finally {
6469
+ if (mounted.current) setBusyByBot((current) => {
6470
+ const next = { ...current };
6471
+ delete next[account.botId];
6472
+ return next;
6473
+ });
6474
+ }
6475
+ }, [invoke]);
6476
+ const botList = model.bots.length > 0 ? h9(
6477
+ "section",
6478
+ { className: "dim-listSection" },
6479
+ h9(
6480
+ "div",
6481
+ { className: "ddt-listHeading dim-listHeading" },
6482
+ h9("h3", null, "\u5DF2\u63A5\u5165\u7684 WhatsApp \u673A\u5668\u4EBA")
6483
+ ),
6484
+ h9("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h9("li", { key: account.botId }, h9(WhatsappAccountCard, {
6485
+ account,
6486
+ busy: busyByBot[account.botId],
6487
+ removing: removeTarget === account.botId,
6488
+ onReconnect: () => void botAction(
6489
+ account,
6490
+ "reconnect",
6491
+ WHATSAPP_ENDPOINTS.reconnectBot,
6492
+ { botId: account.botId }
6493
+ ),
6494
+ onRequestRemove: () => setRemoveTarget(account.botId),
6495
+ onCancelRemove: () => setRemoveTarget(null),
6496
+ onConfirmRemove: async () => {
6497
+ await botAction(account, "delete", WHATSAPP_ENDPOINTS.deleteBot, {
6498
+ botId: account.botId,
6499
+ confirm: true
6500
+ });
6501
+ if (mounted.current) setRemoveTarget(null);
6502
+ }
6503
+ }))))
6504
+ ) : null;
6505
+ return h9(
6506
+ "section",
6507
+ {
6508
+ className: "ddt-page dwa-page dim-channelPage",
6509
+ "aria-label": "WhatsApp \u8BBE\u7F6E"
6510
+ },
6511
+ h9(Heading6, {
6512
+ totals: model.totals,
6513
+ busy,
6514
+ onAdd: () => void startProvisioning(false),
6515
+ addButtonRef
6516
+ }),
6517
+ model.phase === "loading" ? h9(LoadingView6) : model.phase === "error" ? h9(
6518
+ "div",
6519
+ { className: "ddt-card dim-surfaceCard" },
6520
+ h9(
6521
+ "div",
6522
+ { className: "ddt-inlineError dim-inlineError" },
6523
+ h9("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001"),
6524
+ h9("p", null, model.error?.message),
6525
+ h9(Button13, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
6526
+ )
6527
+ ) : h9(
6528
+ React10.Fragment,
6529
+ null,
6530
+ provision?.status === "pending" ? h9(QrPanel5, {
6531
+ provision,
6532
+ now,
6533
+ busy,
6534
+ onRefresh: () => void startProvisioning(true),
6535
+ onCancel: () => void closeProvision()
6536
+ }) : provision ? h9(ProvisionView3, {
6537
+ provision,
6538
+ busy,
6539
+ onRetry: () => void startProvisioning(true),
6540
+ onClose: () => void closeProvision()
6541
+ }) : model.bots.length === 0 ? h9(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null,
6542
+ botList
6543
+ )
6544
+ );
6545
+ }
6546
+
5858
6547
  // plugin-src/client/styles.js
5859
6548
  var IM_STYLE_ID = "xmanrui-dsh-im-settings";
5860
- var CSS8 = String.raw`
6549
+ var CSS9 = String.raw`
5861
6550
  .dim-page {
5862
6551
  --dim-blue: #3370ff;
5863
6552
  --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent);
@@ -5894,11 +6583,13 @@ var CSS8 = String.raw`
5894
6583
  .dim-logoTelegram svg { width: 21px; height: 21px; }
5895
6584
  .dim-logoDiscord { color: white; background: #5865f2; }
5896
6585
  .dim-logoDiscord svg { width: 21px; height: 21px; }
6586
+ .dim-logoWhatsapp { color: white; background: #25d366; }
6587
+ .dim-logoWhatsapp svg { width: 21px; height: 21px; }
5897
6588
  .dim-channelCopy { min-width: 0; display: block; }
5898
6589
  .dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; }
5899
6590
  .dim-divider { width: 1px; min-height: 520px; background: var(--dsw-alias-line-divider, #eef0f3); }
5900
6591
  .dim-panel { min-width: 0; container-type: inline-size; }
5901
- .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page, .dim-panel .dqq-page, .dim-panel .dwecom-page { width: 100%; max-width: none; padding: 0 0 24px; }
6592
+ .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page, .dim-panel .dqq-page, .dim-panel .dwecom-page, .dim-panel .dwa-page { width: 100%; max-width: none; padding: 0 0 24px; }
5902
6593
  .dim-panel .bxf-heading, .dim-panel .dxw-heading, .dim-panel .ddt-heading { justify-content: flex-end; }
5903
6594
  .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; justify-content: space-between; }
5904
6595
  .dim-panel .dim-bindActions { min-width: 0; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; }
@@ -6044,13 +6735,13 @@ function installImStyles() {
6044
6735
  const style = document.createElement("style");
6045
6736
  style.dataset.plugin = "@xmanrui/dsh-im";
6046
6737
  style.dataset.pluginCss = IM_STYLE_ID;
6047
- style.textContent = CSS8;
6738
+ style.textContent = CSS9;
6048
6739
  document.head.appendChild(style);
6049
6740
  return () => style.remove();
6050
6741
  }
6051
6742
 
6052
6743
  // plugin-src/client/index.js
6053
- var h9 = React10.createElement;
6744
+ var h10 = React11.createElement;
6054
6745
  var name = "im-settings";
6055
6746
  var inject = ["slots", "connection"];
6056
6747
  var CHANNELS = Object.freeze([
@@ -6060,57 +6751,66 @@ var CHANNELS = Object.freeze([
6060
6751
  { id: "wecom", label: "\u4F01\u4E1A\u5FAE\u4FE1" },
6061
6752
  { id: "qq", label: "QQ" },
6062
6753
  { id: "telegram", label: "Telegram" },
6063
- { id: "discord", label: "Discord" }
6754
+ { id: "discord", label: "Discord" },
6755
+ { id: "whatsapp", label: "WhatsApp" }
6064
6756
  ]);
6065
6757
  function WeixinLogo() {
6066
- return h9(
6758
+ return h10(
6067
6759
  "span",
6068
6760
  { className: "dim-logo dim-logoWeixin", "aria-hidden": "true" },
6069
- h9(WeixinLogoGlyph)
6761
+ h10(WeixinLogoGlyph)
6070
6762
  );
6071
6763
  }
6072
6764
  function FeishuLogo() {
6073
- return h9(
6765
+ return h10(
6074
6766
  "span",
6075
6767
  { className: "dim-logo dim-logoFeishu", "aria-hidden": "true" },
6076
- h9(FeishuLogoGlyph)
6768
+ h10(FeishuLogoGlyph)
6077
6769
  );
6078
6770
  }
6079
6771
  function DingtalkLogo() {
6080
- return h9(
6772
+ return h10(
6081
6773
  "span",
6082
6774
  { className: "dim-logo dim-logoDingtalk", "aria-hidden": "true" },
6083
- h9(DingtalkLogoGlyph)
6775
+ h10(DingtalkLogoGlyph)
6084
6776
  );
6085
6777
  }
6086
6778
  function QqLogo() {
6087
- return h9("span", { className: "dim-logo dim-logoQq", "aria-hidden": "true" }, h9(QqLogoGlyph));
6779
+ return h10("span", { className: "dim-logo dim-logoQq", "aria-hidden": "true" }, h10(QqLogoGlyph));
6088
6780
  }
6089
6781
  function WecomLogo() {
6090
- return h9("span", { className: "dim-logo dim-logoWecom", "aria-hidden": "true" }, h9(WecomLogoGlyph));
6782
+ return h10("span", { className: "dim-logo dim-logoWecom", "aria-hidden": "true" }, h10(WecomLogoGlyph));
6091
6783
  }
6092
6784
  function TelegramLogo() {
6093
- return h9(
6785
+ return h10(
6094
6786
  "span",
6095
6787
  { className: "dim-logo dim-logoTelegram", "aria-hidden": "true" },
6096
- h9(TelegramLogoGlyph)
6788
+ h10(TelegramLogoGlyph)
6097
6789
  );
6098
6790
  }
6099
6791
  function DiscordLogo() {
6100
- return h9(
6792
+ return h10(
6101
6793
  "span",
6102
6794
  { className: "dim-logo dim-logoDiscord", "aria-hidden": "true" },
6103
- h9(DiscordLogoGlyph)
6795
+ h10(DiscordLogoGlyph)
6796
+ );
6797
+ }
6798
+ function WhatsappLogo() {
6799
+ return h10(
6800
+ "span",
6801
+ { className: "dim-logo dim-logoWhatsapp", "aria-hidden": "true" },
6802
+ h10(WhatsappLogoGlyph)
6104
6803
  );
6105
6804
  }
6106
6805
  function ChannelLogo({ channel: channel3 }) {
6107
- if (channel3 === "weixin") return h9(WeixinLogo);
6108
- if (channel3 === "feishu") return h9(FeishuLogo);
6109
- if (channel3 === "dingtalk") return h9(DingtalkLogo);
6110
- if (channel3 === "wecom") return h9(WecomLogo);
6111
- if (channel3 === "qq") return h9(QqLogo);
6112
- if (channel3 === "telegram") return h9(TelegramLogo);
6113
- return h9(DiscordLogo);
6806
+ if (channel3 === "weixin") return h10(WeixinLogo);
6807
+ if (channel3 === "feishu") return h10(FeishuLogo);
6808
+ if (channel3 === "dingtalk") return h10(DingtalkLogo);
6809
+ if (channel3 === "wecom") return h10(WecomLogo);
6810
+ if (channel3 === "qq") return h10(QqLogo);
6811
+ if (channel3 === "telegram") return h10(TelegramLogo);
6812
+ if (channel3 === "discord") return h10(DiscordLogo);
6813
+ return h10(WhatsappLogo);
6114
6814
  }
6115
6815
  function IMSettingsTab({
6116
6816
  dingtalkRpcCall,
@@ -6119,25 +6819,26 @@ function IMSettingsTab({
6119
6819
  qqRpcCall,
6120
6820
  telegramRpcCall,
6121
6821
  wecomRpcCall,
6122
- weixinRpcCall
6822
+ weixinRpcCall,
6823
+ whatsappRpcCall
6123
6824
  }) {
6124
- const [selected, setSelected] = React10.useState("weixin");
6825
+ const [selected, setSelected] = React11.useState("weixin");
6125
6826
  const active = CHANNELS.find((channel3) => channel3.id === selected) ?? CHANNELS[0];
6126
- return h9(
6827
+ return h10(
6127
6828
  "section",
6128
6829
  { className: "dim-page", "aria-label": "IM\u673A\u5668\u4EBA\u8BBE\u7F6E" },
6129
- h9(
6830
+ h10(
6130
6831
  "header",
6131
6832
  { className: "dim-title" },
6132
- h9("p", null, "\u628A\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness")
6833
+ h10("p", null, "\u628A\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness")
6133
6834
  ),
6134
- h9(
6835
+ h10(
6135
6836
  "div",
6136
6837
  { className: "dim-layout" },
6137
- h9(
6838
+ h10(
6138
6839
  "nav",
6139
6840
  { className: "dim-rail", role: "tablist", "aria-label": "IM \u6E20\u9053" },
6140
- CHANNELS.map((channel3) => h9(
6841
+ CHANNELS.map((channel3) => h10(
6141
6842
  "button",
6142
6843
  {
6143
6844
  key: channel3.id,
@@ -6149,21 +6850,21 @@ function IMSettingsTab({
6149
6850
  "aria-controls": `dim-panel-${channel3.id}`,
6150
6851
  onClick: () => setSelected(channel3.id)
6151
6852
  },
6152
- h9(ChannelLogo, { channel: channel3.id }),
6153
- h9(
6853
+ h10(ChannelLogo, { channel: channel3.id }),
6854
+ h10(
6154
6855
  "span",
6155
6856
  { className: "dim-channelCopy" },
6156
- h9("strong", null, channel3.label)
6857
+ h10("strong", null, channel3.label)
6157
6858
  )
6158
6859
  ))
6159
6860
  ),
6160
- h9("div", { className: "dim-divider", "aria-hidden": "true" }),
6161
- h9("main", {
6861
+ h10("div", { className: "dim-divider", "aria-hidden": "true" }),
6862
+ h10("main", {
6162
6863
  className: "dim-panel",
6163
6864
  role: "tabpanel",
6164
6865
  id: `dim-panel-${active.id}`,
6165
6866
  "aria-labelledby": `dim-tab-${active.id}`
6166
- }, active.id === "weixin" ? h9(WeixinSettingsTab, { rpcCall: weixinRpcCall }) : active.id === "feishu" ? h9(FeishuSettingsTab, { rpcCall: feishuRpcCall }) : active.id === "dingtalk" ? h9(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall }) : active.id === "wecom" ? h9(WecomSettingsTab, { rpcCall: wecomRpcCall }) : active.id === "qq" ? h9(QqSettingsTab, { rpcCall: qqRpcCall }) : active.id === "telegram" ? h9(TelegramSettingsTab, { rpcCall: telegramRpcCall }) : h9(DiscordSettingsTab, { rpcCall: discordRpcCall }))
6867
+ }, active.id === "weixin" ? h10(WeixinSettingsTab, { rpcCall: weixinRpcCall }) : active.id === "feishu" ? h10(FeishuSettingsTab, { rpcCall: feishuRpcCall }) : active.id === "dingtalk" ? h10(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall }) : active.id === "wecom" ? h10(WecomSettingsTab, { rpcCall: wecomRpcCall }) : active.id === "qq" ? h10(QqSettingsTab, { rpcCall: qqRpcCall }) : active.id === "telegram" ? h10(TelegramSettingsTab, { rpcCall: telegramRpcCall }) : active.id === "discord" ? h10(DiscordSettingsTab, { rpcCall: discordRpcCall }) : h10(WhatsappSettingsTab, { rpcCall: whatsappRpcCall }))
6167
6868
  )
6168
6869
  );
6169
6870
  }
@@ -6176,6 +6877,7 @@ function apply(ctx) {
6176
6877
  installQqStyles(),
6177
6878
  installTelegramStyles(),
6178
6879
  installDiscordStyles(),
6880
+ installWhatsappStyles(),
6179
6881
  installImStyles()
6180
6882
  ];
6181
6883
  return () => {
@@ -6189,6 +6891,7 @@ function apply(ctx) {
6189
6891
  const wecomRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WECOM_RPC_CHANNEL, endpoint, payload, signal);
6190
6892
  const telegramRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(TELEGRAM_RPC_CHANNEL, endpoint, payload, signal);
6191
6893
  const discordRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(DISCORD_RPC_CHANNEL, endpoint, payload, signal);
6894
+ const whatsappRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WHATSAPP_RPC_CHANNEL, endpoint, payload, signal);
6192
6895
  ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
6193
6896
  name: "settings.plugins.tab",
6194
6897
  id: "im",
@@ -6201,7 +6904,8 @@ function apply(ctx) {
6201
6904
  qqRpcCall,
6202
6905
  telegramRpcCall,
6203
6906
  wecomRpcCall,
6204
- weixinRpcCall
6907
+ weixinRpcCall,
6908
+ whatsappRpcCall
6205
6909
  })
6206
6910
  }, IMSettingsTab));
6207
6911
  }