From 4f1cc9f219110197305db8333b4470b0e45b5a91 Mon Sep 17 00:00:00 2001 From: xmanrui <841206367@qq.com> Date: Sat, 15 Aug 2026 12:56:33 +0800 Subject: [PATCH] Keep DingTalk QR visible in IM panel --- THIRD_PARTY_NOTICES.md | 2 +- lib/client.js | 24 ++++++++++++++++++++---- package-lock.json | 6 +++--- package.json | 2 +- plugin-src/client/styles.js | 8 +++++++- scripts/verify-package.mjs | 4 ++++ test/client-ui.test.mjs | 15 +++++++++++++++ 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 60dd601..f488cb5 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -1,6 +1,6 @@ # Third-party notices -This package composes [`@xmanrui/dsh-feishu`](https://github.com/xmanrui/dsh-feishu) at commit `aad650feabadd511241aa58b236d64273d5e397f`, [`@xmanrui/dsh-weixin`](https://github.com/xmanrui/dsh-weixin) at commit `76d076771b2c84fb4c5598c2344d486695eda080`, and [`@xmanrui/dsh-dingtalk`](https://github.com/xmanrui/dsh-dingtalk) at commit `67f7211b8011f0623b47ce9e01004bbab8e6614f`, all under the MIT License. +This package composes [`@xmanrui/dsh-feishu`](https://github.com/xmanrui/dsh-feishu) at commit `aad650feabadd511241aa58b236d64273d5e397f`, [`@xmanrui/dsh-weixin`](https://github.com/xmanrui/dsh-weixin) at commit `76d076771b2c84fb4c5598c2344d486695eda080`, and [`@xmanrui/dsh-dingtalk`](https://github.com/xmanrui/dsh-dingtalk) at commit `6a8aaccf4b264bb8ccdc094bf9f058420714e0f4`, all under the MIT License. The channel client components are adapted from those projects and preserve their security model: secrets remain in the DeepSeek Harness Host credential provider and never enter the browser-facing RPC response. diff --git a/lib/client.js b/lib/client.js index 9cb1d80..0c4e8f9 100644 --- a/lib/client.js +++ b/lib/client.js @@ -293,6 +293,7 @@ var CSS = String.raw` flex-direction: column; gap: 18px; padding: 2px 0 28px; + container-type: inline-size; color: var(--dsw-alias-label-primary, #1f2329); box-sizing: border-box; } @@ -328,19 +329,20 @@ var CSS = String.raw` .ddt-empty .ddt-actions { margin-top: 20px; } .ddt-brandMark { width: 110px; height: 110px; display: grid; place-items: center; justify-self: center; border-radius: 28px; color: #fff; background: linear-gradient(145deg, #2997ff, var(--ddt-accent)); box-shadow: 0 18px 45px rgb(22 119 255 / 23%); } .ddt-brandMark svg { filter: drop-shadow(0 3px 8px rgb(0 35 96 / 16%)); } -.ddt-qrLayout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 34px; align-items: center; } +.ddt-qrLayout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 34px; align-items: start; } .ddt-qrColumn { display: flex; flex-direction: column; align-items: center; gap: 12px; } -.ddt-qrFrame { position: relative; width: 270px; aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); border-radius: 16px; background: #fff; } +.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-line-border, #e5e6eb); border-radius: 16px; background: #fff; } .ddt-qrFrame::before { content: ''; position: absolute; inset: 6px; border: 1px solid rgb(22 119 255 / 10%); border-radius: 11px; pointer-events: none; } .ddt-qrFrame img { display: block; width: 100%; height: 100%; object-fit: contain; } .ddt-qrFallback { padding: 24px; color: #646a73; text-align: center; } .ddt-expired { position: absolute; inset: 0; display: grid; place-items: center; padding: 30px; color: #fff; text-align: center; font-weight: 650; white-space: pre-line; background: rgb(31 35 41 / 76%); backdrop-filter: blur(3px); } -.ddt-countdown { width: 270px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; } +.ddt-countdown { width: min(270px, 100%); color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; } .ddt-countdownTop { display: flex; justify-content: space-between; margin-bottom: 6px; } .ddt-countdown strong { color: var(--dsw-alias-label-primary, #1f2329); font-variant-numeric: tabular-nums; } .ddt-progress { height: 4px; overflow: hidden; border-radius: 99px; background: #eef0f3; } .ddt-progress span { display: block; width: var(--ddt-progress); height: 100%; background: var(--ddt-accent); transition: width .2s linear; } .ddt-stateLabel { display: inline-flex; align-items: center; gap: 8px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; font-weight: 600; } +.ddt-qrCopy { min-width: 0; overflow-wrap: anywhere; } .ddt-qrCopy h3 { margin: 9px 0 8px; font-size: 18px; } .ddt-qrCopy > p { color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.65; } .ddt-steps { margin: 18px 0 16px; padding: 0; list-style: none; counter-reset: ddt-step; } @@ -391,6 +393,14 @@ var CSS = String.raw` .ddt-confirm .ddt-actions { margin-top: 13px; } .ddt-visuallyHidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } @keyframes ddt-spin { to { transform: rotate(360deg); } } +@container (max-width: 680px) { + .ddt-empty { grid-template-columns: minmax(0, 1fr); } + .ddt-brandMark { display: none; } + .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; } + .ddt-qrColumn { width: 100%; min-width: 0; } + .ddt-qrCopy { width: 100%; } + .ddt-metrics { grid-template-columns: minmax(0, 1fr); } +} @media (max-width: 720px) { .ddt-heading, .ddt-accountTop, .ddt-accountFooter { flex-direction: column; align-items: stretch; } .ddt-heading p { white-space: normal; } @@ -3851,11 +3861,17 @@ var CSS4 = String.raw` .dim-channelCopy { min-width: 0; display: block; } .dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; } .dim-divider { width: 1px; min-height: 520px; background: var(--dsw-alias-line-divider, #eef0f3); } -.dim-panel { min-width: 0; } +.dim-panel { min-width: 0; container-type: inline-size; } .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page { width: 100%; max-width: none; padding: 0 0 24px; } .dim-panel .bxf-heading, .dim-panel .dxw-heading, .dim-panel .ddt-heading { justify-content: flex-end; } .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; justify-content: flex-end; } .dim-panel .ddt-headingCopy { display: none; } +.dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); } +@container (max-width: 680px) { + .dim-panel .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; } + .dim-panel .ddt-qrColumn { width: 100%; min-width: 0; } + .dim-panel .ddt-qrCopy { width: 100%; min-width: 0; overflow-wrap: anywhere; } +} @media (max-width: 840px) { .dim-title { align-items: flex-start; } .dim-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; } diff --git a/package-lock.json b/package-lock.json index 27eb739..b42664d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/67f7211b8011f0623b47ce9e01004bbab8e6614f.tar.gz", + "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/6a8aaccf4b264bb8ccdc094bf9f058420714e0f4.tar.gz", "@xmanrui/dsh-feishu": "https://github.com/xmanrui/dsh-feishu/archive/aad650feabadd511241aa58b236d64273d5e397f.tar.gz", "@xmanrui/dsh-weixin": "https://github.com/xmanrui/dsh-weixin/archive/76d076771b2c84fb4c5598c2344d486695eda080.tar.gz" }, @@ -550,8 +550,8 @@ }, "node_modules/@xmanrui/dsh-dingtalk": { "version": "0.1.0", - "resolved": "https://github.com/xmanrui/dsh-dingtalk/archive/67f7211b8011f0623b47ce9e01004bbab8e6614f.tar.gz", - "integrity": "sha512-aOA3vvEL7mKt/NIlMRVJTWB+V5E+J3gWgqE0Q19in8KTrEwYqUk1TYe08B/OdikEghwaQnNfri2bqqpgsCzCqg==", + "resolved": "https://github.com/xmanrui/dsh-dingtalk/archive/6a8aaccf4b264bb8ccdc094bf9f058420714e0f4.tar.gz", + "integrity": "sha512-b9qD/t+uLCjbIu3DAg+4PomhnrfyoQEeKY9FYxdcyTYBcJWDbrae8KA/7EVwEJooqnYqVRuB8nABqMpYLwV5cw==", "license": "MIT", "dependencies": { "dingtalk-stream": "2.1.4", diff --git a/package.json b/package.json index 284bb5c..e913cd3 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "node": ">=22.19" }, "dependencies": { - "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/67f7211b8011f0623b47ce9e01004bbab8e6614f.tar.gz", + "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/6a8aaccf4b264bb8ccdc094bf9f058420714e0f4.tar.gz", "@xmanrui/dsh-feishu": "https://github.com/xmanrui/dsh-feishu/archive/aad650feabadd511241aa58b236d64273d5e397f.tar.gz", "@xmanrui/dsh-weixin": "https://github.com/xmanrui/dsh-weixin/archive/76d076771b2c84fb4c5598c2344d486695eda080.tar.gz" }, diff --git a/plugin-src/client/styles.js b/plugin-src/client/styles.js index 72d6cab..3303870 100644 --- a/plugin-src/client/styles.js +++ b/plugin-src/client/styles.js @@ -33,11 +33,17 @@ const CSS = String.raw` .dim-channelCopy { min-width: 0; display: block; } .dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; } .dim-divider { width: 1px; min-height: 520px; background: var(--dsw-alias-line-divider, #eef0f3); } -.dim-panel { min-width: 0; } +.dim-panel { min-width: 0; container-type: inline-size; } .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page { width: 100%; max-width: none; padding: 0 0 24px; } .dim-panel .bxf-heading, .dim-panel .dxw-heading, .dim-panel .ddt-heading { justify-content: flex-end; } .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; justify-content: flex-end; } .dim-panel .ddt-headingCopy { display: none; } +.dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); } +@container (max-width: 680px) { + .dim-panel .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; } + .dim-panel .ddt-qrColumn { width: 100%; min-width: 0; } + .dim-panel .ddt-qrCopy { width: 100%; min-width: 0; overflow-wrap: anywhere; } +} @media (max-width: 840px) { .dim-title { align-items: flex-start; } .dim-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; } diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index 0bfe433..82f015d 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -33,6 +33,10 @@ if ((client.match(/ctx\.slots\.inject\("settings\.plugins\.tab"/g) ?? []).length if (/role:\s*["']switch|type:\s*["']checkbox/.test(client)) { throw new Error('client bundle contains a channel enable switch'); } +if (!client.includes('container-type: inline-size') + || !client.includes('@container (max-width: 680px)')) { + throw new Error('client bundle does not contain the narrow-panel DingTalk QR layout'); +} if (!host.includes('@xmanrui/dsh-feishu') || !host.includes('@xmanrui/dsh-weixin') || !host.includes('@xmanrui/dsh-dingtalk')) { diff --git a/test/client-ui.test.mjs b/test/client-ui.test.mjs index 2559a71..fec4a09 100644 --- a/test/client-ui.test.mjs +++ b/test/client-ui.test.mjs @@ -1,4 +1,5 @@ import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; import test from 'node:test'; import React from 'react'; @@ -6,6 +7,8 @@ import { renderToStaticMarkup } from 'react-dom/server'; import { IMSettingsTab } from '../plugin-src/client/index.js'; +const STYLES_URL = new URL('../plugin-src/client/styles.js', import.meta.url); + test('IM settings renders three compact logo channel tabs without enable switches', () => { const markup = renderToStaticMarkup(React.createElement(IMSettingsTab, { feishuRpcCall: async () => ({ ok: true, value: {} }), @@ -27,3 +30,15 @@ test('IM settings renders three compact logo channel tabs without enable switche assert.doesNotMatch(markup, /dim-chevron|扫码绑定<\/small>|扫码接入<\/small>/); assert.doesNotMatch(markup, />INSTANT MESSAGING<|>Channel<|>微信设置 { + const styles = await readFile(STYLES_URL, 'utf8'); + assert.match(styles, /\.dim-panel \{ min-width: 0; container-type: inline-size; \}/); + assert.match( + styles, + /@container \(max-width: 680px\)[\s\S]*\.dim-panel \.ddt-qrLayout \{ grid-template-columns: minmax\(0, 1fr\); justify-items: center;/, + ); + assert.match(styles, /\.dim-panel \.ddt-qrFrame, \.dim-panel \.ddt-countdown \{ width: min\(270px, 100%\); \}/); + assert.match(styles, /\.dim-panel \.ddt-qrColumn \{ width: 100%; min-width: 0; \}/); + assert.match(styles, /\.dim-panel \.ddt-qrCopy \{ width: 100%; min-width: 0; overflow-wrap: anywhere; \}/); +});