Improve WhatsApp delivery suggestions and peer labels (ops.30).

Surface chat/access-list contacts as selectable targets with clearer nicknames, and keep conversation peers usable for ops schedulers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-06 02:31:42 +08:00
parent e84630565c
commit 356538ac18
19 changed files with 827 additions and 324 deletions

View file

@ -571,8 +571,8 @@ var React25 = __toESM(require("react"), 1);
// package.json
var package_default = {
name: "dsh-im-ops",
version: "4.9.1-ops.22",
description: "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 \u2014 access/session policies owned here.",
version: "4.9.1-ops.30",
description: "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 - access/session policies owned here.",
keywords: [
"deepseek-harness",
"dsh",
@ -1725,12 +1725,16 @@ var EN = Object.freeze({
"\u5DF2\u804A\u4F1A\u8BDD": "Conversation",
"\u4ECE\u4F1A\u8BDD\u9009\u62E9targetID": "Select a targetID from conversations",
"\u9009\u62E9\u540E\u4F1A\u81EA\u52A8\u586B\u5199\u76EE\u6807\u4FE1\u606F\u548C\u8C03\u7528\u522B\u540D\uFF0C\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002": "Selecting a conversation fills the target details and call alias. Review them before saving.",
"\u4F1A\u5217\u51FA\u5DF2\u804A\u8FC7\u7684\u79C1\u804A/\u7FA4\u804A\uFF0C\u4EE5\u53CA\u8BBF\u95EE\u540D\u5355\u91CC\u8BB0\u4F4F\u7684\u8054\u7CFB\u4EBA\u4E0E\u7FA4\uFF08\u663E\u793A\u4E3A\u6635\u79F0 \xB7 \u7535\u8BDD / \u7FA4\u540D\uFF09\u3002": "Lists known DMs and groups, plus remembered access-list contacts and groups (shown as nickname \xB7 phone / group name).",
"\u53EF\u9009\u6295\u9012\u4F1A\u8BDD": "Available delivery conversations",
"\u4F8B\u5982 \u5F20\u4E09 \xB7 13800138000": "For example, Alice \xB7 13800138000",
"\u6B63\u5728\u5237\u65B0\u2026": "Refreshing\u2026",
"\u5237\u65B0": "Refresh",
"\u6B63\u5728\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\u2026": "Loading known conversations\u2026",
"\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not load known conversations. Try again later.",
"\u8FD8\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u4F1A\u8BDD": "No conversations to choose from",
"\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u4E0E\u673A\u5668\u4EBA\u804A\u4E00\u6761\u6D88\u606F\uFF0C\u518D\u5237\u65B0\u3002": "Send the bot a message on the platform, then refresh.",
"\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u4E0E\u673A\u5668\u4EBA\u804A\u4E00\u6761\u6D88\u606F\uFF08\u79C1\u804A\u6216\u7FA4\u804A\uFF09\uFF0C\u6216\u5148\u5728\u300C\u8BBF\u95EE\u8BBE\u7F6E\u300D\u91CC\u51FA\u73B0\u8FC7\u8054\u7CFB\u4EBA/\u7FA4\uFF0C\u518D\u5237\u65B0\u3002": "Chat with the bot first (DM or group), or wait until contacts/groups appear under Access settings, then refresh.",
"\u5DF2\u6DFB\u52A0": "Added",
"\u624B\u52A8\u586B\u5199\uFF08\u9AD8\u7EA7\uFF09": "Enter manually (advanced)",
"\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u76EE\u6807\u4F1A\u8BDD\u786E\u8BA4\u3002": "Test message sent. Confirm it in the target conversation.",
@ -13901,7 +13905,7 @@ function TargetForm({
value: name2,
disabled: testing,
maxLength: 80,
placeholder: "\u4F8B\u5982 \u6BCF\u65E5\u6C47\u62A5\u7FA4",
placeholder: definition.label === "WhatsApp" ? "\u4F8B\u5982 \u5F20\u4E09 \xB7 13800138000" : "\u4F8B\u5982 \u6BCF\u65E5\u6C47\u62A5\u7FA4",
onChange: (event) => setName(event.target.value)
})
),
@ -13975,12 +13979,6 @@ function TargetForm({
)
);
}
function suggestionOptionLabel(definition, suggestion, added) {
const name2 = suggestionDisplayName(suggestion);
const type = localizeText(kindLabel(definition, suggestion.kind));
const route = fieldsFor(definition, suggestion.kind).map((field) => maskRouteValue(suggestion.route?.[field.key])).filter(Boolean).join(" \xB7 ");
return [name2, type, route, added ? localizeText("\u5DF2\u6DFB\u52A0") : null].filter(Boolean).join(" \xB7 ");
}
function TargetSuggestionPicker({
definition,
phase,
@ -13993,6 +13991,7 @@ function TargetSuggestionPicker({
onCancel
}) {
const configured = new Set(targets.map((target) => routeIdentity(definition, target)).filter(Boolean));
const channelHint = definition.label === "WhatsApp" ? "\u4F1A\u5217\u51FA\u5DF2\u804A\u8FC7\u7684\u79C1\u804A/\u7FA4\u804A\uFF0C\u4EE5\u53CA\u8BBF\u95EE\u540D\u5355\u91CC\u8BB0\u4F4F\u7684\u8054\u7CFB\u4EBA\u4E0E\u7FA4\uFF08\u663E\u793A\u4E3A\u6635\u79F0 \xB7 \u7535\u8BDD / \u7FA4\u540D\uFF09\u3002" : "\u9009\u62E9\u540E\u4F1A\u81EA\u52A8\u586B\u5199\u76EE\u6807\u4FE1\u606F\u548C\u8C03\u7528\u522B\u540D\uFF0C\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002";
return h2(
"section",
{ className: "dim-targetSuggestions", "aria-label": "\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9" },
@ -14003,7 +14002,7 @@ function TargetSuggestionPicker({
"div",
null,
h2("h3", null, "\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9"),
h2("p", null, "\u9009\u62E9\u540E\u4F1A\u81EA\u52A8\u586B\u5199\u76EE\u6807\u4FE1\u606F\u548C\u8C03\u7528\u522B\u540D\uFF0C\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002")
h2("p", null, channelHint)
),
h2(DeliveryButton, {
onClick: () => void onRefresh(),
@ -14019,33 +14018,42 @@ function TargetSuggestionPicker({
"div",
{ className: "dim-targetSuggestionState" },
h2("strong", null, "\u8FD8\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u4F1A\u8BDD"),
h2("p", null, "\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u4E0E\u673A\u5668\u4EBA\u804A\u4E00\u6761\u6D88\u606F\uFF0C\u518D\u5237\u65B0\u3002")
h2("p", null, "\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u4E0E\u673A\u5668\u4EBA\u804A\u4E00\u6761\u6D88\u606F\uFF08\u79C1\u804A\u6216\u7FA4\u804A\uFF09\uFF0C\u6216\u5148\u5728\u300C\u8BBF\u95EE\u8BBE\u7F6E\u300D\u91CC\u51FA\u73B0\u8FC7\u8054\u7CFB\u4EBA/\u7FA4\uFF0C\u518D\u5237\u65B0\u3002")
) : h2(
"label",
{ className: "dim-targetSuggestionField" },
h2("span", null, "\u5DF2\u804A\u4F1A\u8BDD"),
h2(
"select",
{
name: "suggestion",
value: "",
onChange: (event) => {
if (event.target.value === "") return;
const suggestion = suggestions[Number(event.target.value)];
if (suggestion) onSelect(suggestion);
}
},
h2("option", { value: "", disabled: true }, "\u4ECE\u4F1A\u8BDD\u9009\u62E9targetID"),
suggestions.map((suggestion, index) => {
const identity = routeIdentity(definition, suggestion);
const added = configured.has(identity);
return React23.createElement("option", {
key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
value: String(index),
disabled: added
}, suggestionOptionLabel(definition, suggestion, added));
})
)
"ul",
{ className: "dim-targetSuggestionList", role: "listbox", "aria-label": "\u53EF\u9009\u6295\u9012\u4F1A\u8BDD" },
suggestions.map((suggestion, index) => {
const identity = routeIdentity(definition, suggestion);
const added = configured.has(identity);
return h2(
"li",
{ key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}` },
h2(
"button",
{
type: "button",
className: "dim-targetSuggestionItem",
role: "option",
"aria-disabled": added ? "true" : void 0,
disabled: added,
onClick: () => {
if (!added) onSelect(suggestion);
}
},
// Platform display names must stay raw (bypass h() i18n phrase rewrite).
React23.createElement(
"strong",
null,
suggestionDisplayName(suggestion) || suggestionFallbackName(definition, suggestion)
),
h2("span", null, [
localizeText(kindLabel(definition, suggestion.kind)),
added ? localizeText("\u5DF2\u6DFB\u52A0") : null
].filter(Boolean).join(" \xB7 ")),
React23.createElement("code", null, fieldsFor(definition, suggestion.kind).map((field) => maskRouteValue(suggestion.route?.[field.key])).filter(Boolean).join(" \xB7 "))
)
);
})
),
h2(
"div",
@ -14932,6 +14940,13 @@ body[data-ds-dark-theme] .dim-page select option, body[data-ds-dark-theme] .dim-
.dim-targetSuggestionField { min-width: 0; display: grid; gap: 5px; margin-top: 12px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
.dim-targetSuggestionField select { width: 100%; min-width: 0; height: 38px; padding: 0 10px; 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: 12px; cursor: pointer; }
.dim-targetSuggestionField select:focus { outline: 2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 28%, transparent); border-color: var(--dsw-alias-state-business-primary, #3370ff); }
.dim-targetSuggestionList { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; max-height: 280px; overflow: auto; }
.dim-targetSuggestionItem { width: 100%; box-sizing: border-box; display: grid; gap: 2px; text-align: left; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; font: inherit; cursor: pointer; }
.dim-targetSuggestionItem:hover:not(:disabled) { border-color: var(--dsw-alias-state-business-primary, #3370ff); }
.dim-targetSuggestionItem:disabled { opacity: 0.55; cursor: not-allowed; }
.dim-targetSuggestionItem strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; line-height: 20px; font-weight: 600; }
.dim-targetSuggestionItem span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
.dim-targetSuggestionItem code { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 16px; word-break: break-all; }
.dim-targetForm { margin-top: 14px; padding: 14px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
.dim-targetFormHeading h3 { margin: 0; font-size: 14px; line-height: 21px; }
.dim-targetFormHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }

File diff suppressed because one or more lines are too long