@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/README.md +32 -10
- package/docs/README.zh-CN.md +39 -10
- package/lib/client.js +217 -310
- package/lib/contracts.d.ts +3 -2
- package/lib/contracts.js +3 -2
- package/lib/contracts.js.map +1 -1
- package/lib/index.d.ts +12 -2
- package/lib/index.js +89 -111
- package/lib/index.js.map +1 -1
- package/lib/open-platform.js +211 -15
- package/lib/open-platform.js.map +1 -1
- package/lib/tools.js +5 -5
- package/lib/tools.js.map +1 -1
- package/package.json +18 -13
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
|
|
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
|
-
"
|
|
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
|
|
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
|
|
851
|
-
|
|
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(
|
|
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({
|
|
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
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
|
1199
|
-
} catch
|
|
1200
|
-
|
|
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("
|
|
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(
|
|
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(
|
|
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
|
|
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("
|
|
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(
|
|
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
|
|
1758
|
+
function failureOf(code, message) {
|
|
1816
1759
|
if (code === "token-missing") return {
|
|
1817
1760
|
kind: "credential",
|
|
1818
|
-
text:
|
|
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:
|
|
1765
|
+
text: [`请求失败:${clientText(message, translator("zh"))}`, `Request failed: ${clientText(message, translator("en"))}`]
|
|
1823
1766
|
};
|
|
1824
1767
|
}
|
|
1825
|
-
function networkFailure(cause
|
|
1768
|
+
function networkFailure(cause) {
|
|
1826
1769
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1827
1770
|
return {
|
|
1828
1771
|
kind: "network",
|
|
1829
|
-
text:
|
|
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
|
|
1825
|
+
function ZhihuTestSelector({ value, onChange }) {
|
|
1883
1826
|
const t = useT();
|
|
1884
1827
|
const options = testOptions(t);
|
|
1885
|
-
return renderSelect(
|
|
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(
|
|
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(
|
|
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(
|
|
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: "
|
|
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
|
|
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:
|
|
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
|
|
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(
|
|
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(
|
|
2806
|
+
setFailure([`上传失败:${result.error.message}`, `Upload failed: ${result.error.message}`]);
|
|
2875
2807
|
return;
|
|
2876
2808
|
}
|
|
2877
|
-
setNote(
|
|
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(
|
|
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(
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
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 = (
|
|
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
|