hiwork-core 0.0.8 → 0.0.10
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-policy.js +13 -1
- package/lib/client.js +108 -21
- package/lib/index.js +15 -4
- package/lib/types/client/controller.d.ts +13 -0
- package/lib/types/client/daily-card.d.ts +32 -1
- package/lib/types/client/session-cost.d.ts +19 -13
- package/lib/types/client/styles.d.ts +10 -2
- package/lib/types/client-policy.d.ts +8 -0
- package/lib/types/index.d.ts +12 -3
- package/package.json +1 -1
package/lib/client-policy.js
CHANGED
|
@@ -15,6 +15,8 @@ export const DEFAULT_LOGIN_MODE = 'both';
|
|
|
15
15
|
export const CLIENT_POLICY_PATH = '/api/client/policy';
|
|
16
16
|
/** 策略拉取默认超时:免登录只读接口,超过 5s 视同不可达(fail-open)。 */
|
|
17
17
|
export const CLIENT_POLICY_TIMEOUT_MS = 5_000;
|
|
18
|
+
/** fail-open 缺省:旧网关不返回 passwordLogin 时,密码表单仍视作可用。 */
|
|
19
|
+
export const DEFAULT_PASSWORD_LOGIN = true;
|
|
18
20
|
/**
|
|
19
21
|
* 拼策略 URL:`{baseURL}/api/client/policy?platform=&arch=&version=&device_id=`。
|
|
20
22
|
* 参数固定顺序、逐个 `encodeURIComponent`(device_id 可能是任意 ASCII,含 `+/=` 也必须安全);
|
|
@@ -47,6 +49,16 @@ function parseLoginMode(payload) {
|
|
|
47
49
|
const value = payload.loginMode;
|
|
48
50
|
return value === 'browser' || value === 'form' || value === 'both' ? value : undefined;
|
|
49
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* 解析 `passwordLogin`:只认布尔值;缺失 / 非法值一律 {@link DEFAULT_PASSWORD_LOGIN}
|
|
54
|
+
* (fail-open:旧网关不返回该字段,不能因此收起密码入口)。
|
|
55
|
+
*/
|
|
56
|
+
function parsePasswordLogin(payload) {
|
|
57
|
+
if (typeof payload !== 'object' || payload === null)
|
|
58
|
+
return DEFAULT_PASSWORD_LOGIN;
|
|
59
|
+
const value = payload.passwordLogin;
|
|
60
|
+
return typeof value === 'boolean' ? value : DEFAULT_PASSWORD_LOGIN;
|
|
61
|
+
}
|
|
50
62
|
/**
|
|
51
63
|
* 拉取并解析客户端策略。**永不抛异常**:任何失败都返回 undefined(fail-open)。
|
|
52
64
|
*
|
|
@@ -73,7 +85,7 @@ export async function fetchClientPolicy(opts) {
|
|
|
73
85
|
return undefined;
|
|
74
86
|
}
|
|
75
87
|
const loginMode = parseLoginMode(payload);
|
|
76
|
-
return loginMode === undefined ? undefined : { loginMode };
|
|
88
|
+
return loginMode === undefined ? undefined : { loginMode, passwordLogin: parsePasswordLogin(payload) };
|
|
77
89
|
}
|
|
78
90
|
catch {
|
|
79
91
|
// 网络异常 / 超时(AbortSignal.timeout 抛 TimeoutError)/ 非法 URL:一律 fail-open。
|
package/lib/client.js
CHANGED
|
@@ -78,7 +78,11 @@ var textInput = {
|
|
|
78
78
|
var frameSurfaceSelector = '[class*="_frame"]:has(> [class*="_sidebarCol"])';
|
|
79
79
|
var frameCss = [
|
|
80
80
|
`${frameSurfaceSelector} { background: var(--dsw-specific-sidebar-fill, #f9fafb); }`,
|
|
81
|
+
// 原生会话区根节点:两代链路并存,见上方注释。
|
|
81
82
|
`html[data-dsh-shell="win32"] [class*="_centerCol"] > div > [class*="_root"] { border-top-left-radius: 16px; }`,
|
|
83
|
+
`html[data-dsh-shell="win32"] [class*="_centerCol"] [data-slot="main.conversation"] > [class*="_root"] { border-top-left-radius: 16px; }`,
|
|
84
|
+
// HiWork 扩展页 takeover 根节点(中心列直接子元素)。
|
|
85
|
+
`html[data-dsh-shell="win32"] [class*="_centerCol"] > [data-hiwork-feature-root] { border-top-left-radius: 16px; }`,
|
|
82
86
|
`[class*="_sidebarCol"] { border-right: 0 !important; }`
|
|
83
87
|
].join("\n");
|
|
84
88
|
var hostHideCss = [
|
|
@@ -125,6 +129,9 @@ function authorizeUrlOf(value) {
|
|
|
125
129
|
function browserLoginPendingOf(value) {
|
|
126
130
|
return authorizeUrlOf(value) !== void 0 || value?.browserLoginRequest !== void 0;
|
|
127
131
|
}
|
|
132
|
+
function passwordLoginOf(value) {
|
|
133
|
+
return value?.passwordLogin !== false;
|
|
134
|
+
}
|
|
128
135
|
function browserLoginViewOf(value) {
|
|
129
136
|
const loginMode = normalizeLoginMode(value?.loginMode);
|
|
130
137
|
if (loginMode === "form") return { loginMode, browserLoginUrl: void 0, browserLoginPending: false };
|
|
@@ -162,7 +169,8 @@ function project(value) {
|
|
|
162
169
|
budgets: value?.budgets,
|
|
163
170
|
keys: value?.keys,
|
|
164
171
|
activeKeyId: value?.keyId ?? value?.keys?.[0]?.id,
|
|
165
|
-
...browserLoginViewOf(value)
|
|
172
|
+
...browserLoginViewOf(value),
|
|
173
|
+
passwordLogin: passwordLoginOf(value)
|
|
166
174
|
};
|
|
167
175
|
if (value?.pendingLogin !== void 0) return { ...base, status: "logging-in" };
|
|
168
176
|
if (value?.lastLogin !== void 0) {
|
|
@@ -189,6 +197,7 @@ var AiRouterCardController = class {
|
|
|
189
197
|
keys: void 0,
|
|
190
198
|
activeKeyId: void 0,
|
|
191
199
|
loginMode: "both",
|
|
200
|
+
passwordLogin: true,
|
|
192
201
|
browserLoginUrl: void 0,
|
|
193
202
|
browserLoginPending: false
|
|
194
203
|
});
|
|
@@ -1016,12 +1025,15 @@ function removeStaleHeroGreeting(doc) {
|
|
|
1016
1025
|
|
|
1017
1026
|
// src/client/daily-card.ts
|
|
1018
1027
|
var DAILY_CARD_STYLE_ID = "dcu-daily-card-style";
|
|
1028
|
+
var DAILY_MAIN_FONT_SIZE_MAX = 64;
|
|
1029
|
+
var DAILY_MAIN_FONT_SIZE_MIN = 24;
|
|
1019
1030
|
var DAILY_CARD_STYLE = `
|
|
1020
1031
|
[data-dcu-daily-card]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;width:100%;max-width:min(880px,100%);margin:6px auto 44px;cursor:pointer;user-select:none;text-align:center}
|
|
1021
1032
|
[data-dcu-daily-prefix]{display:inline-flex;align-items:center;gap:14px;font-size:20px;line-height:1.4;font-weight:500;letter-spacing:.22em;text-indent:.22em;color:rgba(24,24,27,.35);white-space:nowrap}
|
|
1022
1033
|
[data-dcu-daily-prefix]::before,[data-dcu-daily-prefix]::after{content:"";display:inline-block;width:44px;height:2px;border-radius:1px;background:currentColor;opacity:.5}
|
|
1023
1034
|
[data-dcu-daily-prefix][hidden]{display:none}
|
|
1024
|
-
[data-dcu-daily-main]{margin-top:6px;font-size
|
|
1035
|
+
[data-dcu-daily-main]{margin-top:6px;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:${DAILY_MAIN_FONT_SIZE_MAX}px;line-height:1.25;font-weight:650;letter-spacing:.005em;background:linear-gradient(100deg,#2f6bf0,#1d4ed8 45%,#0e7fc2);-webkit-background-clip:text;background-clip:text;color:transparent;transition:filter .18s ease}
|
|
1036
|
+
[data-dcu-daily-main][data-dcu-daily-truncated]{text-align:left}
|
|
1025
1037
|
[data-dcu-daily-card]:hover [data-dcu-daily-main]{filter:brightness(1.12)}
|
|
1026
1038
|
[data-dcu-daily-card]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:4px;border-radius:6px}
|
|
1027
1039
|
[data-dcu-daily-shuffle]{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;margin-top:10px;padding:3px 10px;border-radius:8px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:13px;line-height:20px;letter-spacing:.08em;text-indent:.08em;cursor:pointer;transition:color .18s ease}
|
|
@@ -1060,6 +1072,31 @@ function ensureDailyCardStyle(doc) {
|
|
|
1060
1072
|
style.textContent = DAILY_CARD_STYLE;
|
|
1061
1073
|
doc.head.append(style);
|
|
1062
1074
|
}
|
|
1075
|
+
function solveDailyMainFontSize(availableWidth, measure) {
|
|
1076
|
+
if (!(availableWidth > 0)) return null;
|
|
1077
|
+
if (measure(DAILY_MAIN_FONT_SIZE_MAX) <= availableWidth) return null;
|
|
1078
|
+
if (measure(DAILY_MAIN_FONT_SIZE_MIN) > availableWidth) return DAILY_MAIN_FONT_SIZE_MIN;
|
|
1079
|
+
let low = DAILY_MAIN_FONT_SIZE_MIN;
|
|
1080
|
+
let high = DAILY_MAIN_FONT_SIZE_MAX;
|
|
1081
|
+
while (high - low > 1) {
|
|
1082
|
+
const mid = Math.floor((low + high) / 2);
|
|
1083
|
+
if (measure(mid) <= availableWidth) low = mid;
|
|
1084
|
+
else high = mid;
|
|
1085
|
+
}
|
|
1086
|
+
return low;
|
|
1087
|
+
}
|
|
1088
|
+
function fitDailyMainText(card) {
|
|
1089
|
+
const main = card.querySelector("[data-dcu-daily-main]");
|
|
1090
|
+
if (main === null) return null;
|
|
1091
|
+
const availableWidth = card.clientWidth;
|
|
1092
|
+
const size = solveDailyMainFontSize(availableWidth, (fontSize) => {
|
|
1093
|
+
main.style.fontSize = `${fontSize}px`;
|
|
1094
|
+
return main.scrollWidth;
|
|
1095
|
+
});
|
|
1096
|
+
main.style.fontSize = size === null ? "" : `${size}px`;
|
|
1097
|
+
main.toggleAttribute("data-dcu-daily-truncated", size === DAILY_MAIN_FONT_SIZE_MIN && main.scrollWidth > availableWidth);
|
|
1098
|
+
return size;
|
|
1099
|
+
}
|
|
1063
1100
|
function findDailyCardMount(doc) {
|
|
1064
1101
|
const hero = doc.querySelector('[data-phase="hero"]');
|
|
1065
1102
|
if (hero === null) return null;
|
|
@@ -1068,6 +1105,7 @@ function findDailyCardMount(doc) {
|
|
|
1068
1105
|
return headline;
|
|
1069
1106
|
}
|
|
1070
1107
|
var modalKeyHandlers = /* @__PURE__ */ new WeakMap();
|
|
1108
|
+
var cardHandles = /* @__PURE__ */ new WeakMap();
|
|
1071
1109
|
function modalHeadline(item) {
|
|
1072
1110
|
return item.title !== "" ? item.title : item.content;
|
|
1073
1111
|
}
|
|
@@ -1107,6 +1145,36 @@ function buildCard(doc, initial, date, quotes) {
|
|
|
1107
1145
|
shuffle.setAttribute("aria-label", "\u6362\u4E00\u53E5");
|
|
1108
1146
|
shuffle.append(createRefreshIcon(doc), doc.createTextNode("\u6362\u4E00\u53E5"));
|
|
1109
1147
|
root.append(prefix, main, shuffle);
|
|
1148
|
+
let lastFitWidth = -1;
|
|
1149
|
+
let fitFrame;
|
|
1150
|
+
const fit = (reflow = false) => {
|
|
1151
|
+
const width = root.clientWidth;
|
|
1152
|
+
if (!reflow && width === lastFitWidth) return;
|
|
1153
|
+
lastFitWidth = width;
|
|
1154
|
+
fitDailyMainText(root);
|
|
1155
|
+
};
|
|
1156
|
+
const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
1157
|
+
if (fitFrame !== void 0) return;
|
|
1158
|
+
fitFrame = window.requestAnimationFrame(() => {
|
|
1159
|
+
fitFrame = void 0;
|
|
1160
|
+
fit();
|
|
1161
|
+
});
|
|
1162
|
+
}) : null;
|
|
1163
|
+
if (resizeObserver !== null) resizeObserver.observe(root);
|
|
1164
|
+
void doc.fonts?.ready.then(() => {
|
|
1165
|
+
fit(true);
|
|
1166
|
+
}).catch(() => {
|
|
1167
|
+
});
|
|
1168
|
+
cardHandles.set(root, {
|
|
1169
|
+
fit,
|
|
1170
|
+
dispose: () => {
|
|
1171
|
+
if (resizeObserver !== null) resizeObserver.disconnect();
|
|
1172
|
+
if (fitFrame !== void 0) {
|
|
1173
|
+
window.cancelAnimationFrame(fitFrame);
|
|
1174
|
+
fitFrame = void 0;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1110
1178
|
let current = initial;
|
|
1111
1179
|
const applyItem = (next) => {
|
|
1112
1180
|
current = next;
|
|
@@ -1114,6 +1182,7 @@ function buildCard(doc, initial, date, quotes) {
|
|
|
1114
1182
|
prefix.hidden = next.subtitle === "";
|
|
1115
1183
|
main.textContent = next.content;
|
|
1116
1184
|
root.setAttribute("aria-label", `\u6BCF\u65E5\u4E00\u53E5\uFF1A${modalHeadline(next)}`);
|
|
1185
|
+
fit(true);
|
|
1117
1186
|
};
|
|
1118
1187
|
applyItem(initial);
|
|
1119
1188
|
root.addEventListener("click", (e) => {
|
|
@@ -1228,12 +1297,16 @@ function decorateDailyCard(doc, quotes) {
|
|
|
1228
1297
|
removeStaleDailyCard(doc);
|
|
1229
1298
|
const card = buildCard(doc, view, view.date, quotes);
|
|
1230
1299
|
parent.insertBefore(card, anchor);
|
|
1300
|
+
cardHandles.get(card)?.fit(true);
|
|
1231
1301
|
}).catch(() => {
|
|
1232
1302
|
});
|
|
1233
1303
|
}
|
|
1234
1304
|
function removeStaleDailyCard(doc) {
|
|
1235
1305
|
closeDailyModal(doc);
|
|
1236
|
-
for (const stale of Array.from(doc.querySelectorAll("[data-dcu-daily-card]")))
|
|
1306
|
+
for (const stale of Array.from(doc.querySelectorAll("[data-dcu-daily-card]"))) {
|
|
1307
|
+
cardHandles.get(stale)?.dispose();
|
|
1308
|
+
stale.remove();
|
|
1309
|
+
}
|
|
1237
1310
|
}
|
|
1238
1311
|
function observeHeroDecorations(doc, quotes) {
|
|
1239
1312
|
if (doc.head === null) return () => {
|
|
@@ -1450,9 +1523,10 @@ function LoginGateModal(props) {
|
|
|
1450
1523
|
}, [props.browserLoginUrl]);
|
|
1451
1524
|
const showBrowserLogin = props.loginMode !== "form";
|
|
1452
1525
|
const waiting = showBrowserLogin && props.browserLoginPending;
|
|
1453
|
-
const
|
|
1526
|
+
const passwordFormOffered = props.passwordLogin || props.loginMode === "form";
|
|
1527
|
+
const formVisible = props.loginMode === "form" || passwordFormOffered && formOpen;
|
|
1454
1528
|
const noKey = props.lastError?.code === "NO_KEY_ASSIGNED";
|
|
1455
|
-
const error = describeLoginError(props.lastError) ?? submitError;
|
|
1529
|
+
const error = (props.lastError?.code === "AUTH_FAILED" && !passwordFormOffered ? "\u5F53\u524D\u7F51\u5173\u5DF2\u542F\u7528\u7EDF\u4E00\u8BA4\u8BC1\uFF0C\u8BF7\u70B9\u51FB\u300C\u4F7F\u7528\u6D4F\u89C8\u5668\u767B\u5F55\u300D\u5B8C\u6210\u767B\u5F55" : describeLoginError(props.lastError)) ?? submitError;
|
|
1456
1530
|
const loginDisabled = props.loggingIn || username === "" || password === "";
|
|
1457
1531
|
const submit = () => {
|
|
1458
1532
|
if (loginDisabled) return;
|
|
@@ -1486,7 +1560,7 @@ function LoginGateModal(props) {
|
|
|
1486
1560
|
] }),
|
|
1487
1561
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-brand": true, type: "submit", style: loginDisabled ? loginButtonDisabled : loginButton, disabled: loginDisabled, children: props.loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B \u5F55" }),
|
|
1488
1562
|
noKey ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, type: "button", style: linkButton, onClick: () => void props.scope.set("refreshRequest", { at: (/* @__PURE__ */ new Date()).toISOString() }), children: "\u6211\u5DF2\u5206\u914D KEY\uFF0C\u5237\u65B0" }) : null
|
|
1489
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-form-toggle": true, type: "button", style: toggleStyle, onClick: () => setFormOpen(true), children: props.loginMode === "browser" ? "\u6D4F\u89C8\u5668\u767B\u5F55\u4E0D\u53EF\u7528\uFF1F\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" : "\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" });
|
|
1563
|
+
] }) : !passwordFormOffered ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-form-toggle": true, type: "button", style: toggleStyle, onClick: () => setFormOpen(true), children: props.loginMode === "browser" ? "\u6D4F\u89C8\u5668\u767B\u5F55\u4E0D\u53EF\u7528\uFF1F\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" : "\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" });
|
|
1490
1564
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: overlayStyle, children: [
|
|
1491
1565
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OceanWaves, {}),
|
|
1492
1566
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("main", { id: "hiwork-core-frame", "data-hiwork-core-frame": true, style: contentFrameStyle, children: [
|
|
@@ -1553,7 +1627,7 @@ function mountLoginGate(scope, storeCredential) {
|
|
|
1553
1627
|
root = (0, import_client.createRoot)(gateContainer);
|
|
1554
1628
|
if (appRoot !== null) appRoot.inert = true;
|
|
1555
1629
|
}
|
|
1556
|
-
root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginGateModal, { scope, storeCredential, lastError: value?.lastError, loggingIn: value?.pendingLogin !== void 0, initialUsername: value?.username, initialRememberPassword: value?.rememberPassword === true, ...browserLoginViewOf(value) }));
|
|
1630
|
+
root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginGateModal, { scope, storeCredential, lastError: value?.lastError, loggingIn: value?.pendingLogin !== void 0, initialUsername: value?.username, initialRememberPassword: value?.rememberPassword === true, passwordLogin: passwordLoginOf(value), ...browserLoginViewOf(value) }));
|
|
1557
1631
|
};
|
|
1558
1632
|
render();
|
|
1559
1633
|
const dispose = scope.subscribe(() => render());
|
|
@@ -2120,8 +2194,15 @@ function rfc3339Of(time) {
|
|
|
2120
2194
|
const date = new Date(time);
|
|
2121
2195
|
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
2122
2196
|
}
|
|
2123
|
-
function
|
|
2124
|
-
|
|
2197
|
+
function closingTurnTailData(snapshot, messageId) {
|
|
2198
|
+
for (const key of snapshot.order) {
|
|
2199
|
+
const node = snapshot.nodes.get(key);
|
|
2200
|
+
if (node === void 0 || node.kind !== "turn-tail") continue;
|
|
2201
|
+
const data = node.data;
|
|
2202
|
+
if (data === void 0) continue;
|
|
2203
|
+
if (data.closing?.finalNode?.messageId === messageId) return data;
|
|
2204
|
+
}
|
|
2205
|
+
return void 0;
|
|
2125
2206
|
}
|
|
2126
2207
|
function sessionCostKey(sessionId, match) {
|
|
2127
2208
|
return `${sessionId}\0${match.turn}\0${JSON.stringify(match.items)}`;
|
|
@@ -2178,30 +2259,35 @@ var SessionCostStore = class {
|
|
|
2178
2259
|
}
|
|
2179
2260
|
};
|
|
2180
2261
|
var costStyle = {
|
|
2262
|
+
// 字号/颜色与同一行的「用量」「用时」胶囊同源变量,保证在动作行里不显得突兀。
|
|
2181
2263
|
color: textTertiary,
|
|
2182
|
-
fontSize:
|
|
2264
|
+
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
2183
2265
|
lineHeight: "18px",
|
|
2184
2266
|
whiteSpace: "nowrap",
|
|
2185
2267
|
cursor: "default"
|
|
2186
2268
|
};
|
|
2187
|
-
function SessionCost({
|
|
2188
|
-
const
|
|
2189
|
-
const
|
|
2269
|
+
function SessionCost({ messageId, sessionId, useChat, store }) {
|
|
2270
|
+
const data = useChat((snapshot) => closingTurnTailData(snapshot, messageId));
|
|
2271
|
+
const match = sessionCostMatchOf(data);
|
|
2272
|
+
const key = match === null ? null : sessionCostKey(sessionId, match);
|
|
2273
|
+
const [shown, setShown] = (0, import_react6.useState)(void 0);
|
|
2190
2274
|
(0, import_react6.useEffect)(() => {
|
|
2275
|
+
if (match === null || key === null) return;
|
|
2191
2276
|
let disposed = false;
|
|
2192
2277
|
const controller = new AbortController();
|
|
2193
|
-
void store.estimate({ sessionId, turn:
|
|
2194
|
-
if (!disposed)
|
|
2278
|
+
void store.estimate({ sessionId, turn: match.turn, items: match.items, signal: controller.signal }).then((value) => {
|
|
2279
|
+
if (!disposed) setShown({ key, result: value });
|
|
2195
2280
|
}).catch(() => {
|
|
2196
|
-
if (!disposed)
|
|
2281
|
+
if (!disposed) setShown({ key, result: void 0 });
|
|
2197
2282
|
});
|
|
2198
2283
|
return () => {
|
|
2199
2284
|
disposed = true;
|
|
2200
2285
|
controller.abort();
|
|
2201
2286
|
};
|
|
2202
2287
|
}, [store, key]);
|
|
2203
|
-
if (result === void 0) return null;
|
|
2204
|
-
const
|
|
2288
|
+
if (match === null || key === null || shown === void 0 || shown.key !== key || shown.result === void 0) return null;
|
|
2289
|
+
const result = shown.result;
|
|
2290
|
+
const estimated = !result.channelResolved || match.ambiguous;
|
|
2205
2291
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: costStyle, "data-hiwork-session-cost": estimated ? "estimated" : "exact", title: SESSION_COST_HINT, children: `\u2248 ${formatYuan(result.cost)}${estimated ? SESSION_COST_ESTIMATED_SUFFIX : ""}` });
|
|
2206
2292
|
}
|
|
2207
2293
|
|
|
@@ -2771,9 +2857,10 @@ function apply(ctx) {
|
|
|
2771
2857
|
label: () => "\u7528\u91CF\u4E0E\u6210\u672C",
|
|
2772
2858
|
inject: () => ({ hooks: controller.inject().hooks, usage })
|
|
2773
2859
|
}, UsageSection));
|
|
2774
|
-
ctx.slots.inject("conversation.chat.
|
|
2775
|
-
name: "conversation.chat.
|
|
2776
|
-
|
|
2860
|
+
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register({
|
|
2861
|
+
name: "conversation.chat.assistant-actions",
|
|
2862
|
+
id: "hiwork-session-cost",
|
|
2863
|
+
order: 20,
|
|
2777
2864
|
inject: () => ({ store: sessionCost })
|
|
2778
2865
|
}, SessionCost));
|
|
2779
2866
|
ctx.effect(() => observeAccountChange(controller.inject().hooks.aiRouterCard, () => {
|
package/lib/index.js
CHANGED
|
@@ -3137,7 +3137,7 @@ function isUnloading(ctx) {
|
|
|
3137
3137
|
}
|
|
3138
3138
|
|
|
3139
3139
|
// src/version.ts
|
|
3140
|
-
var PKG_VERSION = false ? "dev" : "0.0.
|
|
3140
|
+
var PKG_VERSION = false ? "dev" : "0.0.10";
|
|
3141
3141
|
|
|
3142
3142
|
// src/ai-router-client.ts
|
|
3143
3143
|
var AUTH_AUTHORIZE_PATH = "/api/auth/authorize";
|
|
@@ -3766,6 +3766,7 @@ async function completeBrowserLogin(input, opts) {
|
|
|
3766
3766
|
var DEFAULT_LOGIN_MODE = "both";
|
|
3767
3767
|
var CLIENT_POLICY_PATH = "/api/client/policy";
|
|
3768
3768
|
var CLIENT_POLICY_TIMEOUT_MS = 5e3;
|
|
3769
|
+
var DEFAULT_PASSWORD_LOGIN = true;
|
|
3769
3770
|
function buildClientPolicyURL(opts) {
|
|
3770
3771
|
const baseURL = typeof opts.baseURL === "string" ? opts.baseURL.trim().replace(/\/+$/, "") : "";
|
|
3771
3772
|
if (baseURL === "") return void 0;
|
|
@@ -3783,6 +3784,11 @@ function parseLoginMode(payload) {
|
|
|
3783
3784
|
const value = payload.loginMode;
|
|
3784
3785
|
return value === "browser" || value === "form" || value === "both" ? value : void 0;
|
|
3785
3786
|
}
|
|
3787
|
+
function parsePasswordLogin(payload) {
|
|
3788
|
+
if (typeof payload !== "object" || payload === null) return DEFAULT_PASSWORD_LOGIN;
|
|
3789
|
+
const value = payload.passwordLogin;
|
|
3790
|
+
return typeof value === "boolean" ? value : DEFAULT_PASSWORD_LOGIN;
|
|
3791
|
+
}
|
|
3786
3792
|
async function fetchClientPolicy(opts) {
|
|
3787
3793
|
const url = buildClientPolicyURL(opts);
|
|
3788
3794
|
if (url === void 0) return void 0;
|
|
@@ -3800,7 +3806,7 @@ async function fetchClientPolicy(opts) {
|
|
|
3800
3806
|
return void 0;
|
|
3801
3807
|
}
|
|
3802
3808
|
const loginMode = parseLoginMode(payload);
|
|
3803
|
-
return loginMode === void 0 ? void 0 : { loginMode };
|
|
3809
|
+
return loginMode === void 0 ? void 0 : { loginMode, passwordLogin: parsePasswordLogin(payload) };
|
|
3804
3810
|
} catch {
|
|
3805
3811
|
return void 0;
|
|
3806
3812
|
}
|
|
@@ -4422,6 +4428,7 @@ var Config = Schema.object({
|
|
|
4422
4428
|
browserLogin: Schema.object({ authorizeUrl: Schema.string().required(), startedAt: Schema.string().required() }).default(void 0),
|
|
4423
4429
|
// 三值枚举:非法值写入直接被 schema 拒绝(客户端还会再归一一次,双保险)。
|
|
4424
4430
|
loginMode: Schema.union([Schema.const("browser"), Schema.const("form"), Schema.const("both")]),
|
|
4431
|
+
passwordLogin: Schema.boolean(),
|
|
4425
4432
|
budgets: Schema.array(Schema.object({
|
|
4426
4433
|
model_name: Schema.string().required(),
|
|
4427
4434
|
total_budget: Schema.number(),
|
|
@@ -4593,6 +4600,7 @@ function registerIpcMessageHandler(ports) {
|
|
|
4593
4600
|
}
|
|
4594
4601
|
async function projectLoginMode(opts) {
|
|
4595
4602
|
let loginMode = DEFAULT_LOGIN_MODE;
|
|
4603
|
+
let passwordLogin = DEFAULT_PASSWORD_LOGIN;
|
|
4596
4604
|
try {
|
|
4597
4605
|
const baseURL = opts.baseURL;
|
|
4598
4606
|
if (baseURL !== void 0 && baseURL !== "") {
|
|
@@ -4605,13 +4613,16 @@ async function projectLoginMode(opts) {
|
|
|
4605
4613
|
deviceId: device.deviceId,
|
|
4606
4614
|
...opts.timeoutMs === void 0 ? {} : { timeoutMs: opts.timeoutMs }
|
|
4607
4615
|
});
|
|
4608
|
-
if (policy !== void 0)
|
|
4616
|
+
if (policy !== void 0) {
|
|
4617
|
+
loginMode = policy.loginMode;
|
|
4618
|
+
passwordLogin = policy.passwordLogin;
|
|
4619
|
+
}
|
|
4609
4620
|
}
|
|
4610
4621
|
} catch (error) {
|
|
4611
4622
|
opts.log?.(`hiwork-core: \u5BA2\u6237\u7AEF\u7B56\u7565\u62C9\u53D6\u5931\u8D25\uFF0C\u767B\u5F55\u65B9\u5F0F\u56DE\u9000 both\uFF08${errorName(error)}\uFF09`);
|
|
4612
4623
|
}
|
|
4613
4624
|
try {
|
|
4614
|
-
await opts.state.patch({ set: { loginMode } });
|
|
4625
|
+
await opts.state.patch({ set: { loginMode, passwordLogin } });
|
|
4615
4626
|
} catch (error) {
|
|
4616
4627
|
opts.log?.(`hiwork-core: \u5BA2\u6237\u7AEF\u7B56\u7565\u5199\u5165\u5931\u8D25\uFF08${errorName(error)}\uFF09`);
|
|
4617
4628
|
}
|
|
@@ -49,6 +49,11 @@ export interface AiRouterCardSettings {
|
|
|
49
49
|
keys?: GatewayKeyView[];
|
|
50
50
|
/** 网关策略下发的登录方式;缺省或非法值一律按 `both`。 */
|
|
51
51
|
loginMode?: LoginMode;
|
|
52
|
+
/**
|
|
53
|
+
* 网关策略下发:账号密码直连表单是否仍是受支持的登录方式。统一认证生效后网关报
|
|
54
|
+
* false(密码已由 IAM 持有,直连表单必然失败);缺省/非法一律按 true 展示入口。
|
|
55
|
+
*/
|
|
56
|
+
passwordLogin?: boolean;
|
|
52
57
|
/** 客户端写:请求 Host 发起浏览器授权登录(Host 完成后清掉本字段)。 */
|
|
53
58
|
browserLoginRequest?: {
|
|
54
59
|
at: string;
|
|
@@ -91,6 +96,8 @@ export interface AiRouterCardState {
|
|
|
91
96
|
activeKeyId: string | undefined;
|
|
92
97
|
/** 登录方式(投影后永不为 undefined;缺省/非法值归一为 both)。 */
|
|
93
98
|
loginMode: LoginMode;
|
|
99
|
+
/** 账号密码表单是否仍是受支持的入口(缺省 true:绝不收起可用入口)。 */
|
|
100
|
+
passwordLogin: boolean;
|
|
94
101
|
/** 授权页 URL:有值即等待浏览器授权中。 */
|
|
95
102
|
browserLoginUrl: string | undefined;
|
|
96
103
|
/** 是否等待浏览器授权(含请求已发出、URL 未回写的窗口期)。 */
|
|
@@ -135,6 +142,12 @@ export declare function authorizeUrlOf(value: AiRouterCardSettings | undefined):
|
|
|
135
142
|
* 后一种窗口期也算等待,避免用户重复点击发起第二次授权。
|
|
136
143
|
*/
|
|
137
144
|
export declare function browserLoginPendingOf(value: AiRouterCardSettings | undefined): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* 账号密码直连表单是否仍是受支持的登录方式(网关策略下发)。
|
|
147
|
+
* 只有显式 false 才收起入口——缺省 / 非法值一律按 true(fail-open:旧网关不返回该
|
|
148
|
+
* 字段时不能把用户挡在密码入口之外)。
|
|
149
|
+
*/
|
|
150
|
+
export declare function passwordLoginOf(value: AiRouterCardSettings | undefined): boolean;
|
|
138
151
|
/**
|
|
139
152
|
* 浏览器登录的投影视图(登录门禁与卡片共用):`form` 模式下一律不暴露浏览器登录
|
|
140
153
|
* 入口——连残留的授权 URL 也一并抹掉,避免渲染层对旧 URL 再 `window.open` 一次。
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* 任何失败都不出错误 UI、不打断主流程。问候语轮换(decorateHeroGreeting)不受影响、
|
|
11
11
|
* 照常每帧执行。
|
|
12
12
|
*
|
|
13
|
+
* 主行恒为单行:CSS 侧 nowrap 打底,字号按卡片可用宽度二分收敛(见
|
|
14
|
+
* solveDailyMainFontSize)——条目越长字越小,绝不折行、也不撑破卡片;下限按「池内
|
|
15
|
+
* 最长条目(24 字)在常规窗口宽度下不被截断」取值,低到下限仍放不下的极端条目才截断
|
|
16
|
+
* 显示,不会把两端一起裁掉。
|
|
17
|
+
*
|
|
13
18
|
* dcu 标记与 hiwork-dsh-ui 保持同名:两插件同环境共存时互认对方已挂的卡片,
|
|
14
19
|
* decorateDailyCard 的幂等检查只会让先挂者生效,不会双挂载。
|
|
15
20
|
*/
|
|
@@ -20,8 +25,34 @@ export interface DailyQuoteSource {
|
|
|
20
25
|
getRandom(excludeId?: number): Promise<DailyQuoteView | null>;
|
|
21
26
|
}
|
|
22
27
|
export declare const DAILY_CARD_STYLE_ID = "dcu-daily-card-style";
|
|
23
|
-
|
|
28
|
+
/**
|
|
29
|
+
* 主行字号:基准(一行放得下时)与下限(再长也不缩到不可读,剩余溢出走截断)。
|
|
30
|
+
* 下限由内容池约束:最长条目 24 字在 690px 卡片宽度下需要 ≤28px,取 24px 留出余量
|
|
31
|
+
* (更窄的窗口或更长的条目才走省略号)。
|
|
32
|
+
*/
|
|
33
|
+
export declare const DAILY_MAIN_FONT_SIZE_MAX = 64;
|
|
34
|
+
export declare const DAILY_MAIN_FONT_SIZE_MIN = 24;
|
|
35
|
+
export declare const DAILY_CARD_STYLE = "\n[data-dcu-daily-card]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;width:100%;max-width:min(880px,100%);margin:6px auto 44px;cursor:pointer;user-select:none;text-align:center}\n[data-dcu-daily-prefix]{display:inline-flex;align-items:center;gap:14px;font-size:20px;line-height:1.4;font-weight:500;letter-spacing:.22em;text-indent:.22em;color:rgba(24,24,27,.35);white-space:nowrap}\n[data-dcu-daily-prefix]::before,[data-dcu-daily-prefix]::after{content:\"\";display:inline-block;width:44px;height:2px;border-radius:1px;background:currentColor;opacity:.5}\n[data-dcu-daily-prefix][hidden]{display:none}\n[data-dcu-daily-main]{margin-top:6px;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:64px;line-height:1.25;font-weight:650;letter-spacing:.005em;background:linear-gradient(100deg,#2f6bf0,#1d4ed8 45%,#0e7fc2);-webkit-background-clip:text;background-clip:text;color:transparent;transition:filter .18s ease}\n[data-dcu-daily-main][data-dcu-daily-truncated]{text-align:left}\n[data-dcu-daily-card]:hover [data-dcu-daily-main]{filter:brightness(1.12)}\n[data-dcu-daily-card]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:4px;border-radius:6px}\n[data-dcu-daily-shuffle]{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;margin-top:10px;padding:3px 10px;border-radius:8px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:13px;line-height:20px;letter-spacing:.08em;text-indent:.08em;cursor:pointer;transition:color .18s ease}\n[data-dcu-daily-shuffle] svg{flex:none;width:13px;height:13px;transition:transform .45s ease}\n[data-dcu-daily-shuffle]:hover svg{transform:rotate(360deg)}\n[data-dcu-daily-shuffle]:hover{color:var(--dsw-alias-label-secondary,#3f3f46)}\n[data-dcu-daily-shuffle]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-dcu-daily-prefix]{color:rgba(242,244,248,.38)}\nbody[data-ds-dark-theme] [data-dcu-daily-main]{background:linear-gradient(100deg,#6b9bff,#3d7bff 45%,#4cc3ff);-webkit-background-clip:text;background-clip:text;color:transparent}\nbody[data-ds-dark-theme] [data-dcu-daily-shuffle]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-dcu-daily-shuffle]:hover{color:var(--dsw-alias-label-secondary,#a6adb8)}\n[data-dcu-daily-modal]{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(15,17,23,.55)}\n[data-dcu-daily-modal-box]{box-sizing:border-box;width:min(620px,100%);max-height:88vh;overflow:auto;border-radius:18px;border:1px solid var(--dsw-alias-border-subtle,rgba(24,24,27,.12));background:var(--dsw-alias-background-primary,#ffffff);box-shadow:0 24px 64px rgba(0,0,0,.28);padding:24px 28px;color:var(--dsw-alias-label-primary,#18181b)}\n[data-dcu-daily-modal-meta]{display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:12.5px;line-height:18px;letter-spacing:.02em}\n[data-dcu-daily-modal-title]{margin-top:14px;font-size:24px;line-height:1.45;font-weight:650;color:var(--dsw-alias-label-primary,#18181b)}\n[data-dcu-daily-modal-body]{margin-top:16px;color:var(--dsw-alias-label-secondary,#3f3f46);font-size:14.5px;line-height:1.95}\n[data-dcu-daily-modal-body] > p{margin:0 0 10px}\n[data-dcu-daily-modal-body] > p:last-child{margin-bottom:0}\n[data-dcu-daily-modal-close]{appearance:none;margin-top:20px;padding:7px 18px;border:1px solid var(--dsw-alias-border-strong,rgba(24,24,27,.2));border-radius:9px;background:var(--dsw-alias-button-secondary-fill,transparent);color:var(--dsw-alias-label-secondary,#3f3f46);font-size:13px;line-height:20px;cursor:pointer}\n[data-dcu-daily-modal-close]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(24,24,27,.06))}\n[data-dcu-daily-modal-close]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-dcu-daily-modal]{background:rgba(0,0,0,.62)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-box]{border-color:var(--dsw-alias-border-subtle,rgba(255,255,255,.12));background:var(--dsw-alias-background-primary,#10131b);color:var(--dsw-alias-label-primary,#f2f4f8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-meta]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-title]{color:var(--dsw-alias-label-primary,#f2f4f8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-body]{color:var(--dsw-alias-label-secondary,#a6adb8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-close]{border-color:var(--dsw-alias-border-subtle,rgba(255,255,255,.16));color:var(--dsw-alias-label-secondary,#a6adb8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-close]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08))}\n@media (prefers-reduced-motion: reduce){[data-dcu-daily-card] [data-dcu-daily-main],[data-dcu-daily-shuffle],[data-dcu-daily-shuffle] svg,[data-dcu-daily-modal-box]{transition:none}}\n";
|
|
24
36
|
export declare function ensureDailyCardStyle(doc: Document): void;
|
|
37
|
+
/**
|
|
38
|
+
* 主行字号求解(纯函数,measure 注入以便测试):基准字号一行放得下 → null(调用方清掉
|
|
39
|
+
* 内联样式,回到 CSS 基准);否则二分出「放得下」的最大整数字号;低到下限仍放不下时按
|
|
40
|
+
* 下限返回(剩余溢出交给 CSS 截断兜底)。availableWidth 非正(无布局信息:容器隐藏 /
|
|
41
|
+
* jsdom)→ null,不动任何样式。
|
|
42
|
+
*
|
|
43
|
+
* 二分成立的前提是「文本宽度随字号单调递增」:字号与 em 单位字距同比例缩放,故对中文、
|
|
44
|
+
* 西文、混排均成立。
|
|
45
|
+
*/
|
|
46
|
+
export declare function solveDailyMainFontSize(availableWidth: number, measure: (fontSize: number) => number): number | null;
|
|
47
|
+
/**
|
|
48
|
+
* 把一张卡片的主行字号收敛到「一行放得下」:可用宽度取卡片内容盒宽度(卡片宽度由 CSS
|
|
49
|
+
* 的 width/max-width 决定,主行溢出不会撑大它),文本宽度取主行 scrollWidth(被截断时
|
|
50
|
+
* 仍是完整文本宽度,故二分不受裁剪影响)。返回最终写入的字号;null = 未写内联样式。
|
|
51
|
+
*
|
|
52
|
+
* 下限仍放不下的极端条目打上 `data-dcu-daily-truncated`:居中溢出会把两端一起裁掉、
|
|
53
|
+
* 看不出被截断,该状态改为左对齐以便省略号落在可见边上。
|
|
54
|
+
*/
|
|
55
|
+
export declare function fitDailyMainText(card: HTMLElement): number | null;
|
|
25
56
|
/**
|
|
26
57
|
* 探测 hero 页面并把主行插到 headline(后缀稳定,前缀哈希忽略)的「前一个兄弟」位置:
|
|
27
58
|
* 返回 headline 元素本身作为插入锚点(调用方以 headline.parentElement.insertBefore(card, headline) 落位)。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ChatSnapshot, TurnTailChatData } from '@deepseek-ai/dsh-client-ui-chat/client';
|
|
3
3
|
/** 计价端点名;必须与 Host `src/usage-rpc.ts` 的 `USAGE_RPC_ENDPOINTS` 一致(由测试锁定)。 */
|
|
4
4
|
export declare const SESSION_COST_ENDPOINT = "estimate";
|
|
5
5
|
/** 缓存 TTL:5 分钟(与设置页取数一致;同一轮的金额不会随滚动反复请求)。 */
|
|
@@ -50,11 +50,19 @@ export interface SessionCostRpcPort {
|
|
|
50
50
|
*/
|
|
51
51
|
export declare function sessionCostMatchOf(data: TurnTailChatData | undefined): SessionCostMatch | null;
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* 在该会话的 Chat 快照里,按「收尾消息 id」找到该轮的 turn-tail 数据。
|
|
54
|
+
*
|
|
55
|
+
* 为什么按 messageId 反查:本模块挂在 `conversation.chat.assistant-actions`,owner 只有
|
|
56
|
+
* `messageId`;而用量挂在同一轮的 `'turn-tail'` 节点上。ui-chat 正是拿
|
|
57
|
+
* `closing.finalNode.messageId` 去 renderSlot 这个槽的(见 `TurnTailNodeView`),
|
|
58
|
+
* 所以两个 id 是同一个值——不需要再猜轮号,也不会跨轮串号。
|
|
59
|
+
*
|
|
60
|
+
* 该轮尚未收尾、或节点不在已加载窗口内 → 返回 undefined(不显示金额)。
|
|
61
|
+
* @param snapshot - 当前 Chat 快照(`useChat` 的选择器入参)。
|
|
62
|
+
* @param messageId - 收尾 assistant 消息 id(槽位 owner 下发)。
|
|
63
|
+
* @returns 该轮 turn-tail 数据;找不到时 undefined。
|
|
56
64
|
*/
|
|
57
|
-
export declare function
|
|
65
|
+
export declare function closingTurnTailData(snapshot: ChatSnapshot, messageId: string): TurnTailChatData | undefined;
|
|
58
66
|
/** 缓存键:会话 + 轮次 + 计价项内容(滚动/重挂载不会重复请求同一轮)。 */
|
|
59
67
|
export declare function sessionCostKey(sessionId: string, match: Pick<SessionCostMatch, 'turn' | 'items'>): string;
|
|
60
68
|
/**
|
|
@@ -102,18 +110,16 @@ export interface SessionCostFace {
|
|
|
102
110
|
store: SessionCostStore;
|
|
103
111
|
}
|
|
104
112
|
/**
|
|
105
|
-
*
|
|
106
|
-
* `sessionId
|
|
113
|
+
* 组件 props:owner(`messageId`)+ 会话标准件(`sessionId` / `useChat`)+ 注入面。
|
|
114
|
+
* `sessionId`(ui-session)与 `useChat`(ui-chat)都由框架下发,不在这里重复声明。
|
|
107
115
|
*/
|
|
108
|
-
export type SessionCostProps = PropsRuntime<'conversation.chat.
|
|
109
|
-
matched: SessionCostMatch;
|
|
110
|
-
};
|
|
116
|
+
export type SessionCostProps = PropsRuntime<'conversation.chat.assistant-actions'> & InjectFace<SessionCostFace>;
|
|
111
117
|
/**
|
|
112
|
-
*
|
|
118
|
+
* 渲染一轮的估算金额(挂在该轮收尾消息的动作行里,与 DSH 的「用量」胶囊同一行)。
|
|
113
119
|
*
|
|
114
120
|
* 三种状态:全部计价且渠道已解析、单组合 → `≈ ¥x`;渠道未解析或该轮多组合 →
|
|
115
121
|
* `≈ ¥x(估算)`;其余(未计价 / 失败 / 加载中)→ **不渲染任何内容**。
|
|
116
|
-
* @param props -
|
|
122
|
+
* @param props - 槽位 props(owner 的 `messageId` + 标准件 `sessionId` / `useChat` + 注入面)。
|
|
117
123
|
* @returns 金额文本节点,或 null(不影响对话渲染)。
|
|
118
124
|
*/
|
|
119
|
-
export declare function SessionCost({
|
|
125
|
+
export declare function SessionCost({ messageId, sessionId, useChat, store }: SessionCostProps): import("react").JSX.Element | null;
|
|
@@ -46,8 +46,16 @@ export declare const textInput: CSSProperties;
|
|
|
46
46
|
* data-dsh-shell=process.platform;Windows 顶栏与侧栏同色,圆角呼应顶栏,
|
|
47
47
|
* 其余平台(含无该属性的纯浏览器预览)默认关闭,回到直角。
|
|
48
48
|
* 内容区根节点类名是 CSS module 哈希前缀(如 wSkVaW_root),语义后缀稳定;
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* 命中内容区根节点一律走「中心列 + 直接子链」,避免误伤内容区里 10 个嵌套组件
|
|
50
|
+
* 的 _root。中心列到根节点中间隔几层,两代宿主不同,故规则并存:
|
|
51
|
+
* - `0.1.2-rc.1` 及更早:main 槽直接把会话区根节点渲染进中心列,链路是
|
|
52
|
+
* `centerCol > 槽位 div > _root`;
|
|
53
|
+
* - `0.1.5-rc.1` 起:main 槽先渲染 ConversationPanel,再由它声明的
|
|
54
|
+
* `main.conversation` 子槽渲染根节点,中间多一层 `data-slot` 槽位 div,
|
|
55
|
+
* 旧直接子链静默失配(实测表现:Windows 内容区左上角圆角消失),改用槽位属性定位。
|
|
56
|
+
* 中心列里除了原生会话还有 HiWork 扩展页(feature-center 的 takeover 根节点,
|
|
57
|
+
* 是中心列的直接子元素):同一处「内容区左上角」在两种页面上都要有圆角,否则
|
|
58
|
+
* 切到扩展页又变回直角。
|
|
51
59
|
* 框架底色同理不能只认 `_frame` 后缀:该后缀被 6 个 CSS module 复用(主框架
|
|
52
60
|
* AppFrame 之外还有会话右侧快速索引 TurnNavigator、附件图 MessageImage、
|
|
53
61
|
* 提问面板 QuestionComposer/PlanReviewPanel、只读输入框 SubagentReadOnlyComposer),
|
|
@@ -22,7 +22,15 @@ export declare const CLIENT_POLICY_TIMEOUT_MS = 5000;
|
|
|
22
22
|
/** 策略响应中本插件消费的部分。 */
|
|
23
23
|
export interface ClientPolicy {
|
|
24
24
|
loginMode: LoginMode;
|
|
25
|
+
/**
|
|
26
|
+
* 账号密码直连表单是否仍是受支持的登录方式(网关身份源切到统一认证后为 false)。
|
|
27
|
+
* 缺省(旧网关不返回该字段)/ 非法值一律按 true——绝不因为策略不全就收起可用的
|
|
28
|
+
* 登录入口,把用户锁在门外。
|
|
29
|
+
*/
|
|
30
|
+
passwordLogin: boolean;
|
|
25
31
|
}
|
|
32
|
+
/** fail-open 缺省:旧网关不返回 passwordLogin 时,密码表单仍视作可用。 */
|
|
33
|
+
export declare const DEFAULT_PASSWORD_LOGIN = true;
|
|
26
34
|
/** 拉取策略所需入参(deviceId 由 `resolveDeviceInfo` 解析,永非空)。 */
|
|
27
35
|
export interface FetchClientPolicyOptions {
|
|
28
36
|
/** 网关地址;缺省 / 空串直接返回 undefined(不发起请求)。 */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -56,6 +56,13 @@ export interface Config {
|
|
|
56
56
|
* 一律写 `both`(fail-open,绝不把用户锁在门外)。
|
|
57
57
|
*/
|
|
58
58
|
loginMode?: LoginMode;
|
|
59
|
+
/**
|
|
60
|
+
* 账号密码直连表单是否仍是受支持的登录方式:Host 从同一策略投影。
|
|
61
|
+
* 网关身份源切到统一认证(IAM)后为 false——密码由 IAM 持有,直连表单必然失败,
|
|
62
|
+
* 客户端据此收起密码入口,避免「密码正确却提示密码错误」。缺省 / 拉取失败一律
|
|
63
|
+
* 写 true(fail-open:绝不收起可用入口)。
|
|
64
|
+
*/
|
|
65
|
+
passwordLogin?: boolean;
|
|
59
66
|
budgets?: Array<{
|
|
60
67
|
model_name: string;
|
|
61
68
|
total_budget?: number | null;
|
|
@@ -140,11 +147,13 @@ export interface IpcCallbackPorts {
|
|
|
140
147
|
*/
|
|
141
148
|
export declare function registerIpcMessageHandler(ports: IpcCallbackPorts): () => void;
|
|
142
149
|
/**
|
|
143
|
-
* 拉取客户端策略并把 `loginMode` 投影进 ai-router
|
|
150
|
+
* 拉取客户端策略并把 `loginMode` / `passwordLogin` 投影进 ai-router 命名空间
|
|
151
|
+
* (spec §5.4 / §6.1)。
|
|
144
152
|
*
|
|
145
153
|
* fail-open 是硬要求:缺 baseURL / 设备信息取不到 / 网络失败 / 响应非法一律投影
|
|
146
|
-
* `DEFAULT_LOGIN_MODE`(`both
|
|
147
|
-
*
|
|
154
|
+
* `DEFAULT_LOGIN_MODE`(`both`)与 `DEFAULT_PASSWORD_LOGIN`(`true`),**且只写这两个
|
|
155
|
+
* 字段**——不碰 lastError、不碰任何登录请求字段,策略拉取失败绝不影响登录与供给。
|
|
156
|
+
* **本函数永不抛异常**。
|
|
148
157
|
*
|
|
149
158
|
* @returns 最终投影的登录方式(测试与调用方均可断言)。
|
|
150
159
|
*/
|