@klarkxy/dsh-zhihu 0.1.10 → 0.2.1

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
@@ -1,168 +1,7 @@
1
- function __createDshNodeShims() {
2
- class EventEmitter {
3
- constructor() {
4
- this._e = Object.create(null)
5
- }
6
- on(type, fn) {
7
- (this._e[type] ||= []).push(fn)
8
- return this
9
- }
10
- addListener(type, fn) {
11
- return this.on(type, fn)
12
- }
13
- once(type, fn) {
14
- const wrap = (...args) => {
15
- this.off(type, wrap)
16
- fn(...args)
17
- }
18
- return this.on(type, wrap)
19
- }
20
- off(type, fn) {
21
- const list = this._e[type]
22
- if (list) this._e[type] = list.filter((item) => item !== fn)
23
- return this
24
- }
25
- removeListener(type, fn) {
26
- return this.off(type, fn)
27
- }
28
- emit(type, ...args) {
29
- const list = this._e[type]
30
- if (!list) return false
31
- for (const fn of list.slice()) fn(...args)
32
- return true
33
- }
34
- removeAllListeners(type) {
35
- if (type) delete this._e[type]
36
- else this._e = Object.create(null)
37
- return this
38
- }
39
- listeners(type) {
40
- return (this._e[type] || []).slice()
41
- }
42
- setMaxListeners() {
43
- return this
44
- }
45
- }
46
-
47
- class Buffer extends Uint8Array {
48
- static from(value, encodingOrOffset, length) {
49
- if (typeof value === 'string') return new Buffer(new TextEncoder().encode(value))
50
- if (value instanceof ArrayBuffer) return new Buffer(new Uint8Array(value, encodingOrOffset, length))
51
- if (ArrayBuffer.isView(value)) return new Buffer(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
52
- if (Array.isArray(value) || value && typeof value.length === 'number') return new Buffer(Uint8Array.from(value))
53
- return new Buffer(0)
54
- }
55
- static alloc(size, fill) {
56
- const buf = new Buffer(size)
57
- if (fill !== undefined && fill !== 0) {
58
- if (typeof fill === 'string') {
59
- const bytes = new TextEncoder().encode(fill)
60
- for (let i = 0; i < size; i += 1) buf[i] = bytes[i % bytes.length]
61
- } else buf.fill(fill)
62
- }
63
- return buf
64
- }
65
- static allocUnsafe(size) {
66
- return new Buffer(size)
67
- }
68
- static allocUnsafeSlow(size) {
69
- return new Buffer(size)
70
- }
71
- static concat(list, totalLength) {
72
- const length = totalLength ?? list.reduce((sum, item) => sum + item.length, 0)
73
- const out = new Buffer(length)
74
- let offset = 0
75
- for (const item of list) {
76
- out.set(item, offset)
77
- offset += item.length
78
- }
79
- return out
80
- }
81
- static isBuffer(value) {
82
- return value instanceof Buffer
83
- }
84
- static byteLength(value) {
85
- if (typeof value === 'string') return new TextEncoder().encode(value).length
86
- return value?.byteLength ?? value?.length ?? 0
87
- }
88
- static isEncoding(encoding) {
89
- return /^(utf8|utf-8|ascii|latin1|binary|hex|base64|base64url)$/i.test(String(encoding || ''))
90
- }
91
- toString(encoding) {
92
- if (encoding === 'hex') return [...this].map((byte) => byte.toString(16).padStart(2, '0')).join('')
93
- if (encoding === 'base64') {
94
- let binary = ''
95
- this.forEach((byte) => {
96
- binary += String.fromCharCode(byte)
97
- })
98
- return btoa(binary)
99
- }
100
- return new TextDecoder().decode(this)
101
- }
102
- slice(start, end) {
103
- return Buffer.from(super.subarray(start, end))
104
- }
105
- copy(target, targetStart = 0, start = 0, end = this.length) {
106
- const sliced = this.subarray(start, end)
107
- target.set(sliced, targetStart)
108
- return sliced.length
109
- }
110
- write(string, offset = 0) {
111
- const bytes = new TextEncoder().encode(string)
112
- const written = Math.min(bytes.length, this.length - offset)
113
- this.set(bytes.subarray(0, written), offset)
114
- return written
115
- }
116
- }
117
-
118
- function inherits(ctor, superCtor) {
119
- ctor.super_ = superCtor
120
- ctor.prototype = Object.create(superCtor.prototype, {
121
- constructor: { value: ctor, writable: true, configurable: true },
122
- })
123
- }
124
-
125
- if (!globalThis.Buffer) globalThis.Buffer = Buffer
126
-
127
- return {
128
- buffer: { Buffer, SlowBuffer: Buffer, INSPECT_MAX_BYTES: 50, kMaxLength: 0x7fffffff },
129
- stream: {
130
- Stream: EventEmitter,
131
- Readable: EventEmitter,
132
- Writable: EventEmitter,
133
- Duplex: EventEmitter,
134
- Transform: EventEmitter,
135
- PassThrough: EventEmitter,
136
- },
137
- events: { EventEmitter },
138
- util: {
139
- deprecate(fn) {
140
- return fn
141
- },
142
- inherits,
143
- inspect() {
144
- return ''
145
- },
146
- format(value) {
147
- return String(value)
148
- },
149
- types: { isBuffer: Buffer.isBuffer },
150
- },
151
- }
152
- }
153
-
154
1
  window.__ModuleLoader__.load({
155
2
  id: "@klarkxy/dsh-zhihu",
156
3
  factory: (dshRequire) => {
157
- var __nodeShims;
158
- var process = globalThis.process || { env: {}, nextTick: function (fn) { var args = [].slice.call(arguments, 1); queueMicrotask(function () { fn.apply(null, args); }); } };
159
- var require = function (id) {
160
- if (id === 'buffer' || id === 'stream' || id === 'util' || id === 'events') {
161
- if (!__nodeShims) __nodeShims = __createDshNodeShims();
162
- return __nodeShims[id];
163
- }
164
- return dshRequire(id);
165
- };
4
+ var require = dshRequire;
166
5
  var module = { exports: {} };
167
6
  var exports = module.exports;
168
7
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
@@ -170,6 +9,92 @@ let react = require("react");
170
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
171
10
  let react_jsx_runtime = require("react/jsx-runtime");
172
11
  //#region src/client-locale.ts
12
+ const responseErrors = {
13
+ "RPC 返回格式无效。": "Invalid RPC response format.",
14
+ "开放平台响应格式无效。": "Invalid Open Platform response format.",
15
+ "响应额度项无效。": "Invalid quota item in the response.",
16
+ "分页响应格式无效。": "Invalid paging response format.",
17
+ "请求已取消": "Request cancelled",
18
+ "查询不超过 4000 字符": "The query must be non-empty and no longer than 4,000 characters",
19
+ "缺少文件内容。": "File content is missing.",
20
+ "文件内容无效或超过 20 MB": "File content is invalid or exceeds 20 MB",
21
+ "搜索词不能为空。": "Search keywords cannot be empty.",
22
+ "知识库文件名不能为空。": "The knowledge base file name cannot be empty.",
23
+ "知识库文件名不能包含控制字符。": "The knowledge base file name cannot contain control characters.",
24
+ "知识库文件名不能超过 255 个 UTF-8 字节。": "The knowledge base file name cannot exceed 255 UTF-8 bytes.",
25
+ "知识库文件内容不能为空。": "The knowledge base file content cannot be empty.",
26
+ "知识库文件大小不能超过 20MB。": "The knowledge base file cannot exceed 20 MB.",
27
+ "当前环境没有可用的 fetch。": "No fetch implementation is available in this environment.",
28
+ "知乎响应缺少 {Code,Message,Data} 信封。": "The Zhihu response has no {Code,Message,Data} envelope.",
29
+ "知乎响应缺少有效的 Code 字段。": "The Zhihu response has no valid Code field.",
30
+ "知乎直答响应不是 OpenAI 兼容格式。": "The Zhihu direct answer response is not in an OpenAI-compatible format.",
31
+ "知乎直答返回为空。": "The Zhihu direct answer response is empty.",
32
+ "分页信息不完整:缺少有效的 Paging.IsEnd,请停止翻页。": "Paging is incomplete: no valid Paging.IsEnd. Stop paging.",
33
+ "分页信息不完整:NextOffset 缺失、无效或未前进,请停止翻页;不要按条数计算偏移。": "Paging is incomplete: NextOffset is missing, invalid or did not advance. Stop paging; do not calculate an offset from item counts.",
34
+ "只允许请求知乎开放平台。": "Only Zhihu Open Platform requests are allowed.",
35
+ "参数必须是对象。": "Parameters must be an object.",
36
+ "这些接口仅使用当前 Access Secret,不接受 token 参数或 OAuth 身份切换。": "These APIs use the current Access Secret only; token parameters and OAuth identity switching are not supported.",
37
+ "offset 超出 Int64 范围。": "offset exceeds the Int64 range.",
38
+ "questionUrl 不能为空。": "questionUrl cannot be empty.",
39
+ "contentUrl 不能为空。": "contentUrl cannot be empty.",
40
+ "知乎链接不能包含空白、控制字符或反斜线。": "Zhihu links cannot contain whitespace, control characters or backslashes.",
41
+ "不是有效的知乎 URL。": "Invalid Zhihu URL.",
42
+ "必须使用无账号密码、无非标准端口的 HTTPS 知乎链接。": "Use an HTTPS Zhihu link without credentials or a non-standard port.",
43
+ "questionUrl 仅支持 https://www.zhihu.com/question/{id}。": "questionUrl only supports https://www.zhihu.com/question/{id}.",
44
+ "contentUrl 仅支持知乎回答、专栏文章、想法或视频链接。": "contentUrl only supports Zhihu answer, article, pin or video links.",
45
+ "startDate 与 endDate 必须同时提供或同时省略。": "Provide both startDate and endDate, or omit both.",
46
+ "endDate 不得早于 startDate。": "endDate cannot be before startDate.",
47
+ "主题关键词不能为空;省略 query 才会按画像推荐。": "Topic keywords cannot be empty; omit query to get profile-based recommendations.",
48
+ "contentType 必须是 all、answer、article、pin 或 zvideo。": "contentType must be all, answer, article, pin or zvideo.",
49
+ "apiIds 包含未知官方额度项。": "apiIds contains unknown official quota items.",
50
+ "order 必须是 score、reverse 或 ascending。": "order must be score, reverse or ascending."
51
+ };
52
+ const operationLabels = {
53
+ "知乎搜索": "Zhihu search",
54
+ "知乎全网搜索": "Zhihu web search",
55
+ "知乎热榜": "Zhihu trending",
56
+ "知乎直答": "Zhihu direct answer",
57
+ "知乎知识库列表": "Zhihu knowledge base list",
58
+ "知乎知识库检索": "Zhihu knowledge base search",
59
+ "知乎知识库上传": "Zhihu knowledge base upload",
60
+ "适合回答的问题": "Questions to answer",
61
+ "问题回答摘要": "Answer summaries",
62
+ "本人创作全文": "My full content",
63
+ "本人创作评论": "Comments on my content",
64
+ "账号创作数据": "Account creator stats",
65
+ "单篇创作数据": "Per-post creator stats",
66
+ "官方每日额度": "Official daily quota"
67
+ };
68
+ function diagnosticText(text, t) {
69
+ if (responseErrors[text]) return t(text, responseErrors[text]);
70
+ const upstream = text.match(/^知乎开放平台返回错误(code=(\d+)):(.*)$/s);
71
+ if (upstream) return t(text, `Zhihu Open Platform returned an error (code=${upstream[1]}): ${upstream[2]}`);
72
+ const blocked = text.match(/^知乎开放平台被风控拒绝:(.*)。请不要立即重试。$/s);
73
+ if (blocked) return t(text, `Zhihu Open Platform rejected the request through risk control: ${blocked[1]}. Do not retry immediately.`);
74
+ for (const [label, english] of Object.entries(operationLabels)) {
75
+ if (!text.startsWith(label)) continue;
76
+ const detail = text.slice(label.length);
77
+ const http = detail.match(/^返回 HTTP (\d{3})。$/);
78
+ if (http) return t(text, `${english} returned HTTP ${http[1]}.`);
79
+ const timeout = detail.match(/^超时((\d+) 秒),请稍后再试。$/);
80
+ if (timeout) return t(text, `${english} timed out (${timeout[1]} seconds). Try again later.`);
81
+ if (detail === "响应不是合法 JSON。") return t(text, `${english} did not return valid JSON.`);
82
+ if (detail === "的 Data 必须是 JSON 对象。") return t(text, `${english} Data must be a JSON object.`);
83
+ if (detail.startsWith("失败:")) return t(text, `${english} failed: ${detail.slice(3)}`);
84
+ if (detail.startsWith("返回错误:")) return t(text, `${english} returned an error: ${detail.slice(5)}`);
85
+ }
86
+ return text;
87
+ }
88
+ function clientText(text, t) {
89
+ if (typeof text !== "string") return t(text[0], text[1]);
90
+ const separator = text.indexOf(":");
91
+ if (separator > 0 && /^[a-z-]+$/.test(text.slice(0, separator))) {
92
+ const code = text.slice(0, separator);
93
+ if (code === "token-missing") return t("未配置有效的知乎 Access Secret,请到设置页配置。", "A valid Zhihu Access Secret is missing. Configure it on the Settings tab.");
94
+ return `${code}: ${diagnosticText(text.slice(separator + 1), t)}`;
95
+ }
96
+ return diagnosticText(text, t);
97
+ }
173
98
  function zhihuLocale(value) {
174
99
  return String(value ?? "zh").startsWith("zh") ? "zh" : "en";
175
100
  }
@@ -203,13 +128,14 @@ const ZHIHU_QUOTA_IDS = [
203
128
  "global_search",
204
129
  "zhihu_search",
205
130
  "hot_list",
206
- "question_answers",
131
+ "question_discovery",
207
132
  "zhida_openai",
208
133
  "tools",
209
134
  "knowledge",
210
135
  "user_data",
211
136
  "creator"
212
137
  ];
138
+ const normalizeZhihuQuotaId = (value) => value === "question_answers" ? "question_discovery" : value;
213
139
  //#endregion
214
140
  //#region src/client-state.ts
215
141
  /**
@@ -548,7 +474,7 @@ ${scope} {
548
474
  border: 0.5px solid var(--dsw-alias-border-l3);
549
475
  border-radius: var(--dsw-radius-md);
550
476
  background: var(--dsw-alias-bg-layer-1);
551
- font-family: var(--ds-font-family-code, ui-monospace, 'Cascadia Mono', Consolas, monospace);
477
+ font-family: var(--ds-font-family-code);
552
478
  font-size: 12px;
553
479
  line-height: 20px;
554
480
  color: var(--dsw-alias-label-secondary);
@@ -847,38 +773,14 @@ function zhihuQueryKeyDown(event, run) {
847
773
  run.search();
848
774
  }
849
775
  }
850
- function hostComponentsFromRenderProps(props) {
851
- if (!props || typeof props !== "object") return {};
852
- const record = props;
853
- const sources = [record];
854
- if (record.owner && typeof record.owner === "object") sources.push(record.owner);
855
- let Select;
856
- let Dialog;
857
- let Button;
858
- let Input;
859
- for (const source of sources) {
860
- if (typeof source.Select === "function") Select = source.Select;
861
- if (typeof source.Dialog === "function") Dialog = source.Dialog;
862
- if (typeof source.Button === "function") Button = source.Button;
863
- if (typeof source.Input === "function") Input = source.Input;
864
- }
865
- return {
866
- Select,
867
- Dialog,
868
- Button,
869
- Input
870
- };
871
- }
872
- function renderInput(Input, props) {
873
- if (Input) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, { ...props });
874
- const { onChange, ref, ...rest } = props;
776
+ function renderInput(props) {
777
+ const { onChange, ...rest } = props;
875
778
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
876
779
  ...rest,
877
780
  onChange: (event) => onChange(event.target.value)
878
781
  });
879
782
  }
880
- function renderSelect(Select, props, className) {
881
- if (Select) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Select, { ...props });
783
+ function renderSelect(props, className = "dsh-ui-select") {
882
784
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
883
785
  id: props.id,
884
786
  className,
@@ -892,28 +794,14 @@ function renderSelect(Select, props, className) {
892
794
  }, option.value === "" ? "__empty" : option.value))
893
795
  });
894
796
  }
895
- /**
896
- * The host's own Button still wins whenever the slot supplies one, so a page
897
- * rendered inside DSH keeps the surrounding chrome. Standalone there is no host
898
- * to defer to, and the plugin renders the official primitive instead of a
899
- * hand-rolled `<button>`: the two legacy variant names the call sites use are
900
- * mapped onto it here. `danger` has no primitive variant, so it keeps the
901
- * outlined appearance and takes its tone from a class in the plugin stylesheet
902
- * — the way every other destructive action in the host reads.
903
- */
797
+ /** Map existing plugin variants onto the injected official Button. */
904
798
  const BUTTON_VARIANT = {
905
799
  default: "outline",
906
800
  primary: "primary",
907
801
  danger: "outline",
908
802
  icon: "ghost"
909
803
  };
910
- const ZhihuButton = (0, react.forwardRef)(function ZhihuButton({ host, variant, className, ...rest }, ref) {
911
- if (host) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(host, {
912
- ref,
913
- variant,
914
- className,
915
- ...rest
916
- });
804
+ const ZhihuButton = (0, react.forwardRef)(function ZhihuButton({ variant, className, ...rest }, ref) {
917
805
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
918
806
  ref,
919
807
  variant: BUTTON_VARIANT[variant ?? "default"],
@@ -956,7 +844,7 @@ const OPERATION_COPY = [
956
844
  },
957
845
  {
958
846
  value: "quota",
959
- zh: "官方每日额度",
847
+ zh: "官方每日免费额度",
960
848
  en: "Official daily quota"
961
849
  }
962
850
  ];
@@ -1062,6 +950,10 @@ function buildOpenPlatformParams(operation, form, t = translator()) {
1062
950
  }
1063
951
  }
1064
952
  if (operation === "quota") {
953
+ if (Array.isArray(form.apiIds)) form = {
954
+ ...form,
955
+ apiIds: form.apiIds.map((value) => normalizeZhihuQuotaId(value))
956
+ };
1065
957
  if (!Array.isArray(form.apiIds) || form.apiIds.some((id) => !ZHIHU_QUOTA_IDS.includes(id))) throw new Error(t("包含未知的额度项。", "Contains an unknown quota item."));
1066
958
  if (form.apiIds.length) params.apiIds = [...new Set(form.apiIds)];
1067
959
  }
@@ -1081,9 +973,12 @@ function parseOpenPlatformResponse(operation, raw) {
1081
973
  }
1082
974
  const value = raw.value;
1083
975
  if (!isRecord(value) || value.version !== 1 || value.operation !== operation || !isRecord(value.data) || !jsonValue(value.data)) throw new Error("开放平台响应格式无效。");
1084
- if (value.quotaId !== void 0 && !ZHIHU_QUOTA_IDS.includes(value.quotaId)) throw new Error("响应额度项无效。");
976
+ if (value.quotaId !== void 0 && !ZHIHU_QUOTA_IDS.includes(normalizeZhihuQuotaId(value.quotaId))) throw new Error("响应额度项无效。");
1085
977
  if (value.paging !== void 0 && (!isRecord(value.paging) || typeof value.paging.canContinue !== "boolean" || value.paging.isEnd !== void 0 && typeof value.paging.isEnd !== "boolean" || value.paging.warning !== void 0 && typeof value.paging.warning !== "string")) throw new Error("分页响应格式无效。");
1086
- return value;
978
+ return {
979
+ ...value,
980
+ ...value.quotaId === void 0 ? {} : { quotaId: normalizeZhihuQuotaId(value.quotaId) }
981
+ };
1087
982
  }
1088
983
  function openPlatformNextOffset(result, currentOffset) {
1089
984
  if (!result || !["question.answers", "content.comments"].includes(result.operation) || result.paging?.canContinue !== true || result.paging.isEnd === true) return null;
@@ -1102,7 +997,7 @@ async function requestOpenPlatform(rpc, state, operation, params, publish) {
1102
997
  if (state.isCurrent(ticket)) publish({ error: error instanceof Error ? error.message : String(error) });
1103
998
  }
1104
999
  }
1105
- function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = false, selectedOperation }) {
1000
+ function ZhihuOpenPlatformSection({ rpc, quotaOnly = false, selectedOperation }) {
1106
1001
  const t = useT();
1107
1002
  const [operation, setOperation] = (0, react.useState)(quotaOnly ? "quota" : "question.recommendations");
1108
1003
  const [form, setForm] = (0, react.useState)(createOpenPlatformForm);
@@ -1153,7 +1048,7 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1153
1048
  className: "dsh-ui-label",
1154
1049
  children: label
1155
1050
  }),
1156
- renderInput(Input, {
1051
+ renderInput({
1157
1052
  value: form[key],
1158
1053
  onChange: (value) => change(key, value),
1159
1054
  "aria-label": label,
@@ -1176,7 +1071,7 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1176
1071
  className: "dsh-ui-label",
1177
1072
  children: label
1178
1073
  }),
1179
- renderSelect(Select, {
1074
+ renderSelect({
1180
1075
  value: form[key],
1181
1076
  onChange: (value) => change(key, value),
1182
1077
  "aria-label": label,
@@ -1195,9 +1090,17 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1195
1090
  offset
1196
1091
  };
1197
1092
  try {
1198
- params = buildOpenPlatformParams(activeOperation, nextForm, t);
1199
- } catch (cause) {
1200
- setError(cause instanceof Error ? cause.message : String(cause));
1093
+ params = buildOpenPlatformParams(activeOperation, nextForm);
1094
+ } catch {
1095
+ const message = (locale) => {
1096
+ try {
1097
+ buildOpenPlatformParams(activeOperation, nextForm, translator(locale));
1098
+ return "";
1099
+ } catch (cause) {
1100
+ return cause instanceof Error ? cause.message : String(cause);
1101
+ }
1102
+ };
1103
+ setError([message("zh"), message("en")]);
1201
1104
  return;
1202
1105
  }
1203
1106
  if (offset !== void 0) setForm(nextForm);
@@ -1215,7 +1118,7 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1215
1118
  const paged = activeOperation === "question.answers" || activeOperation === "content.comments";
1216
1119
  const dated = activeOperation === "creator.account.stats" || activeOperation === "creator.content.stats";
1217
1120
  const options = openPlatformOptions(t);
1218
- const title = quotaOnly ? t("官方每日额度", "Official daily quota") : t("问题与创作", "Questions and creator");
1121
+ const title = quotaOnly ? t("官方每日免费额度", "Official daily quota") : t("问题与创作", "Questions and creator");
1219
1122
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1220
1123
  className: "zhihu-open-platform dsh-ui-stack",
1221
1124
  "aria-label": title,
@@ -1228,7 +1131,7 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1228
1131
  className: "dsh-ui-help",
1229
1132
  children: quotaOnly ? t("查询知乎返回的官方额度,不推算余额。", "Shows the quota Zhihu reports; no balance is inferred.") : t("手动触发,不自动翻页或重试。", "Manual trigger only; no automatic paging or retry.")
1230
1133
  }),
1231
- !quotaOnly && selectedOperation === void 0 && renderSelect(Select, {
1134
+ !quotaOnly && selectedOperation === void 0 && renderSelect({
1232
1135
  value: activeOperation,
1233
1136
  options,
1234
1137
  "aria-label": t("开放平台操作", "Open Platform operation"),
@@ -1317,23 +1220,20 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1317
1220
  className: "dsh-ui-actions",
1318
1221
  children: [
1319
1222
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
1320
- host: Button,
1321
1223
  variant: "primary",
1322
1224
  disabled: loading,
1323
1225
  onClick: () => run(),
1324
1226
  children: activeOperation === "quota" ? t("查询官方额度", "Check quota") : t("执行请求", "Run request")
1325
1227
  }),
1326
1228
  loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
1327
- host: Button,
1328
1229
  onClick: () => {
1329
1230
  state.cancel();
1330
1231
  setLoading(false);
1331
- setError(t("已取消;晚到的结果将被丢弃。", "Cancelled; a late response will be discarded."));
1232
+ setError(["已取消;晚到的结果将被丢弃。", "Cancelled; a late response will be discarded."]);
1332
1233
  },
1333
1234
  children: t("取消请求", "Cancel")
1334
1235
  }),
1335
1236
  nextOffset !== null && !loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
1336
- host: Button,
1337
1237
  onClick: () => run(nextOffset),
1338
1238
  children: t("下一页", "Next page")
1339
1239
  })
@@ -1347,7 +1247,7 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1347
1247
  error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1348
1248
  className: "dsh-ui-error",
1349
1249
  role: "alert",
1350
- children: error
1250
+ children: clientText(error, t)
1351
1251
  }),
1352
1252
  result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1353
1253
  className: "dsh-ui-stack",
@@ -1362,7 +1262,7 @@ function ZhihuOpenPlatformSection({ rpc, Button, Input, Select, quotaOnly = fals
1362
1262
  }),
1363
1263
  result.paging?.warning && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1364
1264
  className: "dsh-ui-banner",
1365
- children: result.paging.warning
1265
+ children: clientText(result.paging.warning, t)
1366
1266
  }),
1367
1267
  !stale && paged && result.paging?.canContinue && nextOffset === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1368
1268
  className: "dsh-ui-banner",
@@ -1395,6 +1295,52 @@ function OpenPlatformDetails({ quotaOnly }) {
1395
1295
  });
1396
1296
  }
1397
1297
  //#endregion
1298
+ //#region src/client-call-policy.tsx
1299
+ function ZhihuCallPolicySummary({ policy }) {
1300
+ const t = useT();
1301
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1302
+ className: "dsh-ui-help",
1303
+ children: t(`${policy.allowPaid ? "已显式允许付费" : "仅使用免费额度"} · 每日上限 ${policy.dailyCallLimit} 次(北京时间)`, `${policy.allowPaid ? "Paid calls explicitly enabled" : "Free allowance only"} · ${policy.dailyCallLimit} calls per Beijing day`)
1304
+ });
1305
+ }
1306
+ function ZhihuCallPolicySection({ rpc }) {
1307
+ const t = useT();
1308
+ const [policy, setPolicy] = (0, react.useState)(null);
1309
+ const [failed, setFailed] = (0, react.useState)(false);
1310
+ (0, react.useEffect)(() => {
1311
+ let alive = true;
1312
+ rpc.call(ZHIHU_RPC_CHANNEL, "policy.status", {}).then((result) => {
1313
+ if (!alive) return;
1314
+ const value = result;
1315
+ if (value?.ok && typeof value.value?.allowPaid === "boolean" && typeof value.value.dailyCallLimit === "number") setPolicy(value.value);
1316
+ else setFailed(true);
1317
+ }, () => {
1318
+ if (alive) setFailed(true);
1319
+ });
1320
+ return () => {
1321
+ alive = false;
1322
+ };
1323
+ }, [rpc]);
1324
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1325
+ className: "dsh-ui-stack",
1326
+ "aria-label": t("调用保护", "Call protection"),
1327
+ children: [
1328
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1329
+ className: "dsh-ui-heading",
1330
+ children: t("调用保护", "Call protection")
1331
+ }),
1332
+ policy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuCallPolicySummary, { policy }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1333
+ className: "dsh-ui-help",
1334
+ children: failed ? t("保护配置不可用;请检查插件配置。", "Protection settings unavailable. Check plugin configuration.") : t("读取保护配置…", "Loading protection settings…")
1335
+ }),
1336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1337
+ className: "dsh-ui-hint",
1338
+ children: t("付费须在插件配置显式启用。未确认计费的能力仅允许使用已确认的免费额度。本地保护无法排除同账号其他客户端竞争导致的扣费;调用统计不是账单。", "Enable paid calls explicitly in plugin configuration. Unpriced capabilities require verified free allowance. Other clients on the same account can still cause charges; local counts are not billing.")
1339
+ })
1340
+ ]
1341
+ });
1342
+ }
1343
+ //#endregion
1398
1344
  //#region src/client-quota.tsx
1399
1345
  const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1400
1346
  const identityFields = /* @__PURE__ */ new Set([
@@ -1669,7 +1615,7 @@ function QuotaCharts({ data }) {
1669
1615
  ]
1670
1616
  });
1671
1617
  }
1672
- function ZhihuQuotaSection({ rpc, Button }) {
1618
+ function ZhihuQuotaSection({ rpc }) {
1673
1619
  const state = (0, react.useRef)(createZhihuClientState()).current;
1674
1620
  const [snapshot, setSnapshot] = (0, react.useState)(null);
1675
1621
  const [loading, setLoading] = (0, react.useState)(false);
@@ -1698,7 +1644,7 @@ function ZhihuQuotaSection({ rpc, Button }) {
1698
1644
  refresh();
1699
1645
  return () => state.cancel();
1700
1646
  }, [refresh, state]);
1701
- const title = t("官方额度用量", "Official quota");
1647
+ const title = t("官方免费额度用量", "Official quota");
1702
1648
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1703
1649
  className: "dsh-ui-stack",
1704
1650
  "aria-label": title,
@@ -1718,17 +1664,15 @@ function ZhihuQuotaSection({ rpc, Button }) {
1718
1664
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1719
1665
  className: "dsh-ui-actions",
1720
1666
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
1721
- host: Button,
1722
1667
  variant: "primary",
1723
1668
  disabled: loading,
1724
1669
  onClick: refresh,
1725
1670
  children: t("刷新官方额度", "Refresh quota")
1726
1671
  }), loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
1727
- host: Button,
1728
1672
  onClick: () => {
1729
1673
  state.cancel();
1730
1674
  setLoading(false);
1731
- setError(t("已取消;晚到的结果将被丢弃。", "Cancelled; a late response will be discarded."));
1675
+ setError(["已取消;晚到的结果将被丢弃。", "Cancelled; a late response will be discarded."]);
1732
1676
  },
1733
1677
  children: t("取消请求", "Cancel")
1734
1678
  })]
@@ -1741,7 +1685,7 @@ function ZhihuQuotaSection({ rpc, Button }) {
1741
1685
  error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1742
1686
  className: "dsh-ui-error",
1743
1687
  role: "alert",
1744
- children: error
1688
+ children: clientText(error, t)
1745
1689
  }),
1746
1690
  snapshot && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1747
1691
  className: "dsh-ui-stack",
@@ -1772,7 +1716,6 @@ const inject = [
1772
1716
  ];
1773
1717
  const PACKAGE_NAME = "@klarkxy/dsh-zhihu";
1774
1718
  const SLOT_ORDER = 120;
1775
- const SLOT_LABEL = "知乎";
1776
1719
  const activityDots = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1777
1720
  className: "zhihu-dots",
1778
1721
  "aria-hidden": "true",
@@ -1812,21 +1755,21 @@ function safeUrl(url) {
1812
1755
  return /^https?:\/\//i.test(url) ? url : null;
1813
1756
  }
1814
1757
  /** Credential absence and transport failure read differently from a plain bad request. */
1815
- function failureOf(code, message, t = translator()) {
1758
+ function failureOf(code, message) {
1816
1759
  if (code === "token-missing") return {
1817
1760
  kind: "credential",
1818
- text: t("未配置知乎 Access Secret 或凭证不可用,请到「设置」页配置。", "Zhihu Access Secret is missing or unavailable. Set it on the Settings tab.")
1761
+ text: ["未配置知乎 Access Secret 或凭证不可用,请到「设置」页配置。", "Zhihu Access Secret is missing or unavailable. Set it on the Settings tab."]
1819
1762
  };
1820
1763
  return {
1821
1764
  kind: "request",
1822
- text: t(`请求失败:${message}`, `Request failed: ${message}`)
1765
+ text: [`请求失败:${clientText(message, translator("zh"))}`, `Request failed: ${clientText(message, translator("en"))}`]
1823
1766
  };
1824
1767
  }
1825
- function networkFailure(cause, t = translator()) {
1768
+ function networkFailure(cause) {
1826
1769
  const detail = cause instanceof Error ? cause.message : String(cause);
1827
1770
  return {
1828
1771
  kind: "network",
1829
- text: t(`网络或连接失败:${detail}`, `Network or connection failed: ${detail}`)
1772
+ text: [`网络或连接失败:${detail}`, `Network or connection failed: ${detail}`]
1830
1773
  };
1831
1774
  }
1832
1775
  /**
@@ -1879,10 +1822,10 @@ function testOptions(t) {
1879
1822
  })), ...openPlatformOptions(t).filter((option) => option.value !== "quota")];
1880
1823
  }
1881
1824
  const TEST_OPTIONS = testOptions(translator("zh"));
1882
- function ZhihuTestSelector({ value, onChange, Select }) {
1825
+ function ZhihuTestSelector({ value, onChange }) {
1883
1826
  const t = useT();
1884
1827
  const options = testOptions(t);
1885
- return renderSelect(Select, {
1828
+ return renderSelect({
1886
1829
  "aria-label": t("测试功能", "Feature to test"),
1887
1830
  value,
1888
1831
  options,
@@ -2216,11 +2159,10 @@ function SettingsSection(props) {
2216
2159
  if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsShell, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2217
2160
  className: "dsh-ui-error",
2218
2161
  role: "alert",
2219
- children: t(`读取失败:${state.error}`, `Could not load: ${state.error}`)
2162
+ children: t(`读取失败:${clientText(state.error, t)}`, `Could not load: ${clientText(state.error, t)}`)
2220
2163
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2221
2164
  className: "dsh-ui-actions",
2222
2165
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2223
- host: props.Button,
2224
2166
  onClick: () => void load(),
2225
2167
  children: t("重试", "Retry")
2226
2168
  })
@@ -2249,7 +2191,7 @@ function SettingsSection(props) {
2249
2191
  return;
2250
2192
  }
2251
2193
  setKeyDraft("");
2252
- setNote(t("已保存。", "Saved."));
2194
+ setNote(["已保存。", "Saved."]);
2253
2195
  await load();
2254
2196
  } catch (cause) {
2255
2197
  if (!alive.current) return;
@@ -2272,7 +2214,7 @@ function SettingsSection(props) {
2272
2214
  return;
2273
2215
  }
2274
2216
  setKeyDraft("");
2275
- setNote(t("已清除。", "Cleared."));
2217
+ setNote(["已清除。", "Cleared."]);
2276
2218
  await load();
2277
2219
  } catch (cause) {
2278
2220
  if (!alive.current) return;
@@ -2303,7 +2245,7 @@ function SettingsSection(props) {
2303
2245
  children: t("获取密钥", "Get a key")
2304
2246
  })]
2305
2247
  }),
2306
- renderInput(props.Input, {
2248
+ renderInput({
2307
2249
  type: "password",
2308
2250
  className: "zhihu-input",
2309
2251
  value: keyDraft,
@@ -2322,24 +2264,22 @@ function SettingsSection(props) {
2322
2264
  failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2323
2265
  className: "dsh-ui-error",
2324
2266
  role: "alert",
2325
- children: failure
2267
+ children: clientText(failure, t)
2326
2268
  }) : null,
2327
2269
  note !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2328
2270
  className: "dsh-ui-notice",
2329
2271
  role: "status",
2330
- children: note
2272
+ children: clientText(note, t)
2331
2273
  }) : null,
2332
2274
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2333
2275
  className: "dsh-ui-actions",
2334
2276
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2335
- host: props.Button,
2336
2277
  variant: "danger",
2337
2278
  className: "zhihu-button-danger",
2338
2279
  disabled: busy || keyLocked || !configured,
2339
2280
  onClick: () => setConfirmClear(true),
2340
2281
  children: pending === "clear" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [activityDots(), t("清除中…", "Clearing…")] }) : t("清除", "Clear")
2341
2282
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2342
- host: props.Button,
2343
2283
  variant: "primary",
2344
2284
  disabled: busy || keyLocked || keyValue.length === 0 || draftFailure !== void 0,
2345
2285
  onClick: () => void save(),
@@ -2355,11 +2295,9 @@ function SettingsSection(props) {
2355
2295
  footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2356
2296
  className: "dsh-ui-actions",
2357
2297
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2358
- host: props.Button,
2359
2298
  onClick: () => setConfirmClear(false),
2360
2299
  children: t("取消", "Cancel")
2361
2300
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2362
- host: props.Button,
2363
2301
  variant: "danger",
2364
2302
  className: "zhihu-button-danger",
2365
2303
  onClick: () => void clear(),
@@ -2595,7 +2533,7 @@ function UsageSection(props) {
2595
2533
  if (!isUsageSummary(result.value)) {
2596
2534
  setState({
2597
2535
  status: "error",
2598
- error: "invalid response shape"
2536
+ error: ["响应格式与契约不符。", "The response does not match the expected shape."]
2599
2537
  });
2600
2538
  return;
2601
2539
  }
@@ -2631,11 +2569,10 @@ function UsageSection(props) {
2631
2569
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2632
2570
  className: "dsh-ui-error",
2633
2571
  role: "alert",
2634
- children: t(`读取失败:${state.error}`, `Could not load: ${state.error}`)
2572
+ children: t(`读取失败:${clientText(state.error, t)}`, `Could not load: ${clientText(state.error, t)}`)
2635
2573
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2636
2574
  className: "dsh-ui-actions",
2637
2575
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2638
- host: props.Button,
2639
2576
  onClick: () => void load(),
2640
2577
  children: t("重试", "Retry")
2641
2578
  })
@@ -2656,7 +2593,6 @@ function UsageSection(props) {
2656
2593
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2657
2594
  className: "dsh-ui-actions",
2658
2595
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2659
- host: props.Button,
2660
2596
  onClick: () => void load(),
2661
2597
  children: t("刷新统计", "Refresh")
2662
2598
  })
@@ -2817,7 +2753,7 @@ function KnowledgeSection(props) {
2817
2753
  if (!result.ok) {
2818
2754
  setList({
2819
2755
  status: "error",
2820
- failure: failureOf(result.error.code, result.error.message, t)
2756
+ failure: failureOf(result.error.code, result.error.message)
2821
2757
  });
2822
2758
  return;
2823
2759
  }
@@ -2826,7 +2762,7 @@ function KnowledgeSection(props) {
2826
2762
  status: "error",
2827
2763
  failure: {
2828
2764
  kind: "request",
2829
- text: t("响应格式与契约不符。", "The response does not match the expected shape.")
2765
+ text: ["响应格式与契约不符。", "The response does not match the expected shape."]
2830
2766
  }
2831
2767
  });
2832
2768
  return;
@@ -2839,14 +2775,10 @@ function KnowledgeSection(props) {
2839
2775
  if (!gate.isCurrent(ticket)) return;
2840
2776
  setList({
2841
2777
  status: "error",
2842
- failure: networkFailure(cause, t)
2778
+ failure: networkFailure(cause)
2843
2779
  });
2844
2780
  }
2845
- }, [
2846
- gate,
2847
- rpc,
2848
- t
2849
- ]);
2781
+ }, [gate, rpc]);
2850
2782
  (0, react.useEffect)(() => {
2851
2783
  load();
2852
2784
  }, [load]);
@@ -2855,7 +2787,7 @@ function KnowledgeSection(props) {
2855
2787
  setFailure(void 0);
2856
2788
  setNote(void 0);
2857
2789
  if (file.size > KB_MAX_BYTES) {
2858
- setFailure(t("文件超过 20 MB 上限。", "The file exceeds the 20 MB limit."));
2790
+ setFailure(["文件超过 20 MB 上限。", "The file exceeds the 20 MB limit."]);
2859
2791
  return;
2860
2792
  }
2861
2793
  const { ticket, signal } = gate.begin();
@@ -2871,17 +2803,17 @@ function KnowledgeSection(props) {
2871
2803
  if (!gate.isCurrent(ticket)) return;
2872
2804
  const result = raw;
2873
2805
  if (!result.ok) {
2874
- setFailure(t(`上传失败:${result.error.message}`, `Upload failed: ${result.error.message}`));
2806
+ setFailure([`上传失败:${result.error.message}`, `Upload failed: ${result.error.message}`]);
2875
2807
  return;
2876
2808
  }
2877
- setNote(t("已上传到知乎知识库。", "Uploaded to the Zhihu knowledge base."));
2809
+ setNote(["已上传到知乎知识库。", "Uploaded to the Zhihu knowledge base."]);
2878
2810
  setFile(void 0);
2879
2811
  setFileKey((key) => key + 1);
2880
2812
  await load();
2881
2813
  } catch (cause) {
2882
2814
  if (!gate.isCurrent(ticket)) return;
2883
2815
  const detail = cause instanceof Error ? cause.message : String(cause);
2884
- setFailure(t(`上传失败:${detail}`, `Upload failed: ${detail}`));
2816
+ setFailure([`上传失败:${detail}`, `Upload failed: ${detail}`]);
2885
2817
  } finally {
2886
2818
  setBusy(false);
2887
2819
  }
@@ -2910,11 +2842,10 @@ function KnowledgeSection(props) {
2910
2842
  list.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2911
2843
  className: "dsh-ui-error",
2912
2844
  role: "alert",
2913
- children: list.failure.text
2845
+ children: clientText(list.failure.text, t)
2914
2846
  }), list.failure.kind !== "credential" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2915
2847
  className: "dsh-ui-actions",
2916
2848
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2917
- host: props.Button,
2918
2849
  onClick: () => void load(),
2919
2850
  children: t("重试", "Retry")
2920
2851
  })
@@ -2929,7 +2860,7 @@ function KnowledgeSection(props) {
2929
2860
  }),
2930
2861
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2931
2862
  className: "dsh-ui-row",
2932
- children: [renderSelect(props.Select, {
2863
+ children: [renderSelect({
2933
2864
  id: "zhihu-kb-base",
2934
2865
  value: baseId,
2935
2866
  disabled: busy,
@@ -2943,7 +2874,6 @@ function KnowledgeSection(props) {
2943
2874
  label: t(`${base.name}${base.isDefault ? "(默认)" : ""} · ${base.contentCount} 篇`, `${base.name}${base.isDefault ? " (default)" : ""} · ${base.contentCount} docs`)
2944
2875
  }))]
2945
2876
  }, "dsh-ui-select"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2946
- host: props.Button,
2947
2877
  disabled: busy,
2948
2878
  onClick: () => void load(),
2949
2879
  children: t("刷新", "Refresh")
@@ -2993,7 +2923,6 @@ function KnowledgeSection(props) {
2993
2923
  list.status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2994
2924
  className: "dsh-ui-actions",
2995
2925
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2996
- host: props.Button,
2997
2926
  variant: "primary",
2998
2927
  disabled: busy || !file,
2999
2928
  onClick: () => void upload(),
@@ -3003,12 +2932,12 @@ function KnowledgeSection(props) {
3003
2932
  note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3004
2933
  className: "dsh-ui-notice",
3005
2934
  role: "status",
3006
- children: note
2935
+ children: clientText(note, t)
3007
2936
  }) : null,
3008
2937
  failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3009
2938
  className: "dsh-ui-error",
3010
2939
  role: "alert",
3011
- children: failure
2940
+ children: clientText(failure, t)
3012
2941
  }) : null
3013
2942
  ]
3014
2943
  });
@@ -3037,7 +2966,7 @@ function nextVisitedTabs(visited, next) {
3037
2966
  return visited.includes(next) ? [...visited] : [...visited, next];
3038
2967
  }
3039
2968
  function ZhihuSettings(props) {
3040
- const { rpc, credentials, Select, Button, Input } = props;
2969
+ const { rpc, credentials } = props;
3041
2970
  const t = useT();
3042
2971
  const gateRef = (0, react.useRef)(null);
3043
2972
  if (!gateRef.current) gateRef.current = createZhihuClientState();
@@ -3083,7 +3012,7 @@ function ZhihuSettings(props) {
3083
3012
  if (!parsed) {
3084
3013
  setFailure({
3085
3014
  kind: "request",
3086
- text: t("响应格式与契约不符。", "The response does not match the expected shape.")
3015
+ text: ["响应格式与契约不符。", "The response does not match the expected shape."]
3087
3016
  });
3088
3017
  setPhase("error");
3089
3018
  return;
@@ -3093,12 +3022,12 @@ function ZhihuSettings(props) {
3093
3022
  setPhase("done");
3094
3023
  } else if (response.error.code === "cancelled") setPhase("idle");
3095
3024
  else {
3096
- setFailure(failureOf(response.error.code, response.error.message, t));
3025
+ setFailure(failureOf(response.error.code, response.error.message));
3097
3026
  setPhase("error");
3098
3027
  }
3099
3028
  } catch (cause) {
3100
3029
  if (!gate.isCurrent(ticket)) return;
3101
- setFailure(networkFailure(cause, t));
3030
+ setFailure(networkFailure(cause));
3102
3031
  setPhase("error");
3103
3032
  }
3104
3033
  }, [
@@ -3182,12 +3111,11 @@ function ZhihuSettings(props) {
3182
3111
  className: "dsh-ui-field",
3183
3112
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuTestSelector, {
3184
3113
  value: testOperation,
3185
- onChange: onTestOperationChange,
3186
- Select
3114
+ onChange: onTestOperationChange
3187
3115
  }), isSearchOperation ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
3188
3116
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3189
3117
  className: "dsh-ui-row",
3190
- children: mode === "ask" ? renderSelect(Select, {
3118
+ children: mode === "ask" ? renderSelect({
3191
3119
  "aria-label": t("直答模型", "Answer model"),
3192
3120
  value: askModel,
3193
3121
  onChange: (next) => onAskModelChange(next),
@@ -3219,7 +3147,7 @@ function ZhihuSettings(props) {
3219
3147
  id: "zhihu-scope-hint",
3220
3148
  children: t("至少保留一个检索范围。", "Keep at least one scope selected.")
3221
3149
  })] }) : null,
3222
- renderInput(Input, {
3150
+ renderInput({
3223
3151
  type: "search",
3224
3152
  className: "zhihu-input",
3225
3153
  "data-testid": "zhihu-query",
@@ -3233,14 +3161,12 @@ function ZhihuSettings(props) {
3233
3161
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3234
3162
  className: "dsh-ui-actions",
3235
3163
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
3236
- host: Button,
3237
3164
  variant: "primary",
3238
3165
  "data-testid": "zhihu-search",
3239
3166
  disabled: searchDisabled,
3240
3167
  onClick: () => void runSearch(),
3241
3168
  children: mode === "hot" ? t("获取热榜", "Get trending") : t("搜索", "Search")
3242
3169
  }), phase === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
3243
- host: Button,
3244
3170
  onClick: () => {
3245
3171
  gate.cancel();
3246
3172
  setPhase("idle");
@@ -3256,7 +3182,7 @@ function ZhihuSettings(props) {
3256
3182
  phase === "error" && failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3257
3183
  className: "dsh-ui-error",
3258
3184
  role: "alert",
3259
- children: failure.text
3185
+ children: clientText(failure.text, t)
3260
3186
  }) : null,
3261
3187
  phase === "done" && outcome ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutcomeView, {
3262
3188
  outcome,
@@ -3264,10 +3190,7 @@ function ZhihuSettings(props) {
3264
3190
  }) : null
3265
3191
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuOpenPlatformSection, {
3266
3192
  selectedOperation: testOperation,
3267
- rpc,
3268
- Button,
3269
- Input,
3270
- Select
3193
+ rpc
3271
3194
  }, testOperation)]
3272
3195
  });
3273
3196
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
@@ -3276,24 +3199,10 @@ function ZhihuSettings(props) {
3276
3199
  "aria-label": t("知乎", "Zhihu"),
3277
3200
  children: [
3278
3201
  tablist,
3279
- panel("settings", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsSection, {
3280
- credentials,
3281
- Button,
3282
- Input
3283
- })),
3284
- panel("usage", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageSection, {
3285
- rpc,
3286
- Button
3287
- })),
3288
- panel("quota", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuQuotaSection, {
3289
- rpc,
3290
- Button
3291
- })),
3292
- panel("knowledge", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnowledgeSection, {
3293
- rpc,
3294
- Select,
3295
- Button
3296
- })),
3202
+ panel("settings", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsSection, { credentials }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuCallPolicySection, { rpc })] })),
3203
+ panel("usage", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageSection, { rpc })),
3204
+ panel("quota", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuQuotaSection, { rpc })),
3205
+ panel("knowledge", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnowledgeSection, { rpc })),
3297
3206
  panel("test", testPanel)
3298
3207
  ]
3299
3208
  });
@@ -3319,17 +3228,15 @@ function apply(ctx) {
3319
3228
  } catch {
3320
3229
  locale = void 0;
3321
3230
  }
3322
- const settingsRender = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuSettingsRoot, {
3231
+ const settingsRender = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuSettingsRoot, {
3323
3232
  rpc: client.connection.rpc,
3324
3233
  credentials: wrapCredentials(client.remote.credentials),
3325
- locale,
3326
- ...hostComponentsFromRenderProps(props)
3234
+ locale
3327
3235
  });
3328
3236
  ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
3329
3237
  name: "plugins.bundle.config",
3330
3238
  key: PACKAGE_NAME,
3331
- order: SLOT_ORDER,
3332
- label: SLOT_LABEL
3239
+ order: SLOT_ORDER
3333
3240
  }, settingsRender)), "zhihu.settings");
3334
3241
  }
3335
3242
  //#endregion