@klarkxy/dsh-zhihu 0.2.6 → 0.2.7

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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [简体中文](docs/README.zh-CN.md)
6
6
 
7
- Use Zhihu as a research source for the model: site search, web search, the hot list, Zhida answers, knowledge bases, question discovery, and creator statistics. Every request uses your own Zhihu Access Secret. Results are reference material, not conclusions.
7
+ Use Zhihu as a research source for the model: site search, web search, hot list, Zhida answers, knowledge bases, question discovery, and creator statistics. Every request uses your own Zhihu Access Secret; results are reference material, not conclusions.
8
8
 
9
9
  ## Install
10
10
 
@@ -16,32 +16,32 @@ Replace `web` with your profile name and restart DSH Web. Use the full scoped na
16
16
 
17
17
  ## Use
18
18
 
19
- Open **Plugins → Zhihu** and enter your Access Secret under **Settings**. The other tabs are:
19
+ Open **Plugins → Zhihu** and enter your Access Secret under **Settings**. Other tabs:
20
20
 
21
- - **Stats**: local daily calls, failures, and results;
22
- - **Official quota**: your remaining free allowance as reported by Zhihu;
21
+ - **Stats**: local daily calls, failures, and result counts;
22
+ - **Official quota**: remaining free allowance as reported by Zhihu;
23
23
  - **Knowledge base**: upload and search reference files;
24
- - **Test**: run any search or query by hand.
24
+ - **Test**: run any search or query by hand, expand an answer's reasoning, and inspect or copy structured response data.
25
25
 
26
26
  With the Web Search plugin installed, Zhihu web search also appears in its provider list.
27
27
 
28
28
  ## Give the tools to the model
29
29
 
30
- The plugin enables these model tools by default. No agent preset changes are needed:
30
+ These model tools are enabled by default; no agent preset changes needed:
31
31
 
32
32
  | Tool | Purpose |
33
33
  | --- | --- |
34
34
  | `zhihu_search`, `zhihu_global_search`, `zhihu_hot_list` | Zhihu search, web search, hot list |
35
- | `zhihu_ask` | Zhida AI answers based on Zhihu content |
35
+ | `zhihu_ask` | Zhida AI answers from Zhihu content |
36
36
  | `zhihu_knowledge_search` | Search knowledge bases |
37
37
  | `zhihu_question_recommendations`, `zhihu_question_answers` | Find questions and read answer summaries |
38
- | `zhihu_user_content_detail`, `zhihu_user_content_comments` | Read your own published content and its comments |
38
+ | `zhihu_user_content_detail`, `zhihu_user_content_comments` | Read your published content and its comments |
39
39
  | `zhihu_creator_account_stats`, `zhihu_creator_content_stats` | Your creator statistics |
40
40
  | `zhihu_quota` | Check official quota |
41
41
 
42
42
  ## Cost protection
43
43
 
44
- By default the plugin only uses Zhihu's free allowance: before each request it checks the official quota and skips the request if none is left. It also stops after 100 requests per day (Beijing time). Both can be changed in the plugin configuration:
44
+ By default the plugin uses only Zhihu's free allowance: before each request it checks the official quota and skips the request if none is left. It also stops after 100 requests per day (Beijing time). Both are configurable:
45
45
 
46
46
  ```yaml
47
47
  config:
@@ -4,7 +4,7 @@
4
4
 
5
5
  [English](../README.md)
6
6
 
7
- 把知乎交给模型当调研信源:站内搜索、全网搜索、热榜、知乎直答、知识库、问题发现和创作数据。每次请求都只用你自己的知乎 Access Secret;取回的内容是参考资料,不是定论。
7
+ 把知乎交给模型当调研信源:站内搜索、全网搜索、热榜、知乎直答、知识库、问题发现和创作数据。每次请求只用你自己的知乎 Access Secret;结果是参考资料,不是定论。
8
8
 
9
9
  ## 安装
10
10
 
@@ -21,13 +21,13 @@ dsh plugin --profile web add @klarkxy/dsh-zhihu
21
21
  - **统计**:本机记录的每日调用、失败和结果条数;
22
22
  - **官方用量**:知乎返回的剩余免费额度;
23
23
  - **知识库**:上传并检索参考文件;
24
- - **测试**:手动执行各类搜索和查询。
24
+ - **测试**:手动执行各类搜索和查询,展开直答的思考过程,查看或复制结构化返回数据。
25
25
 
26
- 安装了网页搜索插件时,「知乎全网搜索」也会出现在它的服务列表中。
26
+ 安装网页搜索插件后,「知乎全网搜索」也会出现在它的服务列表中。
27
27
 
28
28
  ## 让模型使用
29
29
 
30
- 插件默认启用以下模型工具,无需修改 Agent 预设:
30
+ 以下模型工具默认启用,无需修改 Agent 预设:
31
31
 
32
32
  | 工具 | 用途 |
33
33
  | --- | --- |
@@ -41,7 +41,7 @@ dsh plugin --profile web add @klarkxy/dsh-zhihu
41
41
 
42
42
  ## 费用保护
43
43
 
44
- 默认只使用知乎的免费额度:每次请求前先查询官方额度,额度不足就不发送。另外每天(北京时间)最多 100 次请求。两项都可以在插件配置中修改:
44
+ 默认只用知乎的免费额度:每次请求前先查官方额度,额度不足就不发送。另外每天(北京时间)最多 100 次请求。两项都可在插件配置中修改:
45
45
 
46
46
  ```yaml
47
47
  config:
package/lib/client.js CHANGED
@@ -353,6 +353,17 @@ ${scope} {
353
353
  /* --- Layout scaffolding. --- */
354
354
  & .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
355
355
  & .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
356
+ /* Plugin detail pages share one reading column. These are layout recipes;
357
+ * controls keep their own primitive styles. Form spacing follows the host's
358
+ * settings-form/fields.module.css and SettingsForm.module.css. */
359
+ & .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }
360
+ & .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }
361
+ &.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }
362
+ & .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }
363
+ & .dsh-ui-fields-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; min-width: 0; margin: 0; padding: 0; border: 0; }
364
+ @media (max-width: 560px) {
365
+ & .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }
366
+ }
356
367
  & .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
357
368
  & .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
358
369
  & .dsh-ui-spacer { flex: 1; min-width: 0; }
@@ -380,6 +391,9 @@ ${scope} {
380
391
  * official primitive, so this only owns the vertical rhythm. --- */
381
392
  & .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
382
393
  & .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
394
+ /* Grid fields share a row, so the stacked-field divider must not win later in
395
+ * the cascade. Reset it here, after the field recipe. */
396
+ & .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }
383
397
  & .dsh-ui-label { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 20px; font-weight: 500; color: var(--dsw-alias-label-primary); }
384
398
  & .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
385
399
  & .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
@@ -570,7 +584,7 @@ ${scope} {
570
584
  & *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
571
585
  }
572
586
  }
573
- `.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
587
+ `.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?=\s*\{)/gm, (_match, className) => `&${className}, & ${className}`);
574
588
  }
575
589
  //#endregion
576
590
  //#region src/client-styles.ts
@@ -592,16 +606,6 @@ ${scope} {
592
606
  */
593
607
  const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
594
608
  .zhihu-panel {
595
- /* --- Plugin page. The contract's rules nest under the scope selector, so the
596
- * root's own column and reading width are the one part it cannot reach. --- */
597
- &.zhihu-settings-embed {
598
- display: flex;
599
- flex-direction: column;
600
- gap: 12px;
601
- width: 100%;
602
- max-width: 760px;
603
- min-width: 0;
604
- }
605
609
  /* The tab strip is a primitive; this only places it in the page column. */
606
610
  .zhihu-tabs { margin: 0; }
607
611
 
@@ -618,9 +622,8 @@ const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
618
622
  background: var(--dsw-alias-interactive-bg-hover-danger);
619
623
  }
620
624
 
621
- /* --- Disclosures. The capability and fine-print blocks use the host
622
- * DisclosureRow; only the reasoning block is still a native <summary>. --- */
623
- .zhihu-ask-reasoning > summary { cursor: pointer; width: fit-content; }
625
+ /* --- Disclosures. Capability, fine print and the reasoning block all use the
626
+ * host DisclosureRow; only its detail list needs layout here. --- */
624
627
  .zhihu-guide-list { margin: 4px 0 0; padding-inline-start: 20px; display: grid; gap: 6px; }
625
628
 
626
629
  /* --- Result rows. The card, the row and the name all come from the contract; a
@@ -634,10 +637,9 @@ const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
634
637
  .zhihu-ask-content { white-space: pre-wrap; }
635
638
 
636
639
  /* --- Open platform. The section is the last block of its tab, so it keeps a
637
- * trailing hairline; the raw response scrolls instead of growing the page. --- */
640
+ * trailing hairline; the response is the official JsonTree inspector. --- */
638
641
  .zhihu-open-platform { padding-bottom: 16px; border-bottom: 0.5px solid var(--dsw-alias-border-l2); }
639
642
  .zhihu-open-platform-quota > legend { padding: 0 8px 0 0; }
640
- .zhihu-open-platform-data { max-height: 480px; overflow: auto; margin: 8px 0 0; }
641
643
 
642
644
  /* --- Usage. The stat tiles are a grid and the day list is a table, neither of
643
645
  * which the contract has a recipe for; figures align in the columns they sit
@@ -661,7 +663,7 @@ const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
661
663
  .zhihu-chart-bar-ok { fill: var(--dsw-alias-state-business-primary); }
662
664
  .zhihu-chart-bar-fail { fill: var(--dsw-alias-state-error-primary); }
663
665
  .zhihu-chart-hit { fill: transparent; }
664
- .zhihu-chart-grid { stroke: var(--dsw-alias-border-l2); stroke-width: 1; }
666
+ .zhihu-chart-grid { stroke: var(--dsw-alias-border-l2); stroke-width: 0.5; }
665
667
  .zhihu-chart-axis,
666
668
  .zhihu-chart-tick,
667
669
  .zhihu-chart-value { fill: var(--dsw-alias-label-secondary); font-size: var(--dsh-content-font-size-secondary); font-family: var(--ds-font-family-code); }
@@ -695,9 +697,9 @@ const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
695
697
  .zhihu-quota-table td { padding: 5px 6px; }
696
698
  }
697
699
 
698
- /* --- File field. The input is visually hidden but stays focusable and in the
699
- * accessibility tree; its <label> is the visible trigger, so a click on the
700
- * label opens the file dialog natively and keyboard focus rings the label. --- */
700
+ /* --- File field. The input is visually hidden but stays in the accessibility
701
+ * tree; the official Button beside it is the visible trigger and forwards the
702
+ * click, so its own focus ring and disabled state apply. --- */
701
703
  .zhihu-file {
702
704
  position: absolute;
703
705
  width: 1px;
@@ -709,30 +711,6 @@ const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
709
711
  white-space: nowrap;
710
712
  border: 0;
711
713
  }
712
- .zhihu-file-trigger {
713
- display: inline-flex;
714
- align-items: center;
715
- padding: 4px 12px;
716
- border: 0.5px solid var(--dsw-alias-border-l2);
717
- border-radius: var(--dsw-radius-md);
718
- color: var(--dsw-alias-label-primary);
719
- cursor: pointer;
720
- flex: none;
721
- }
722
- .zhihu-file-trigger:hover { background: var(--dsw-alias-interactive-bg-hover); }
723
- .zhihu-file-trigger[aria-disabled="true"] { opacity: .5; pointer-events: none; }
724
- .zhihu-file:focus-visible + .dsh-ui-row .zhihu-file-trigger { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; }
725
-
726
- /* 活动反馈:三点呼吸(pulse-dots),参数改写自 Amicro(MIT License,
727
- Copyright (c) 2026 Syed Subhan Uddin);装饰元素 aria-hidden。 */
728
- .zhihu-dots { display: inline-flex; align-items: center; gap: 3px; margin-inline-end: .4em; vertical-align: middle; color: var(--dsw-alias-state-business-primary); }
729
- .zhihu-dots i { width: .32em; height: .32em; min-width: 3px; min-height: 3px; border-radius: 50%; background: currentColor; animation: zhihu-activity-pulse 1.4s ease infinite; }
730
- .zhihu-dots i:nth-child(2) { animation-delay: .2s; }
731
- .zhihu-dots i:nth-child(3) { animation-delay: .4s; }
732
- @media (prefers-reduced-motion: reduce) {
733
- /* Stop the loop outright so the three dots stay lit instead of flickering. */
734
- .zhihu-dots i { animation: none; }
735
- }
736
714
  }
737
715
 
738
716
  /* --- Links. Ordinary DSH paints a light fill on a bare <a>, so plugin links
@@ -744,9 +722,6 @@ const zhihuClientStyles = `${officialUiCss("zhihu-panel")}
744
722
  color: var(--dsw-alias-brand-primary);
745
723
  background: none;
746
724
  }
747
-
748
- /* Keyframes cannot nest inside a style rule; the name is plugin-prefixed. */
749
- @keyframes zhihu-activity-pulse { 0%, 100% { opacity: .2; } 50% { opacity: 1; } }
750
725
  `;
751
726
  //#endregion
752
727
  //#region src/client-host-ui.tsx
@@ -891,6 +866,21 @@ const createOpenPlatformForm = () => ({
891
866
  apiIds: []
892
867
  });
893
868
  const isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
869
+ /** Localized copy for the official JSON inspector (the package is cordis-free). */
870
+ function jsonTreeLabels(t) {
871
+ return {
872
+ copyValue: t("复制值", "Copy value"),
873
+ copyJson: t("复制 JSON", "Copy JSON"),
874
+ copyPath: t("复制路径", "Copy path"),
875
+ copyPrettyJson: t("复制格式化 JSON", "Copy pretty JSON"),
876
+ copyCompactJson: t("复制紧凑 JSON", "Copy compact JSON"),
877
+ copied: t("已复制", "Copied"),
878
+ copyFailed: t("复制失败", "Copy failed"),
879
+ collapseNode: t("折叠节点", "Collapse node"),
880
+ expandNode: t("展开节点", "Expand node"),
881
+ copyButtonTitle: (action) => t(`${action}:点击复制`, `${action}: click to copy`)
882
+ };
883
+ }
894
884
  /** Int64 stays decimal text throughout the browser; never round-trip through Number. */
895
885
  function openPlatformOffset(value) {
896
886
  if (typeof value !== "string" || !/^\d+$/.test(value)) return null;
@@ -1294,9 +1284,15 @@ function ZhihuOpenPlatformSection({ rpc, quotaOnly = false, selectedOperation })
1294
1284
  className: "dsh-ui-banner dsh-ui-banner--danger",
1295
1285
  children: t("响应缺少正文(Body),未取得全文。", "The response has no body (Body); full text was not retrieved.")
1296
1286
  }),
1297
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1298
- className: "zhihu-open-platform-data dsh-ui-code",
1299
- children: JSON.stringify(result.data, null, 2)
1287
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1288
+ className: "dsh-ui-scroll",
1289
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonTree, {
1290
+ data: result.data,
1291
+ label: t("返回数据", "Response data"),
1292
+ copyable: true,
1293
+ expandTopLevel: true,
1294
+ labels: jsonTreeLabels(t)
1295
+ })
1300
1296
  })
1301
1297
  ]
1302
1298
  })
@@ -1738,14 +1734,9 @@ const inject = [
1738
1734
  ];
1739
1735
  const PACKAGE_NAME = "@klarkxy/dsh-zhihu";
1740
1736
  const SLOT_ORDER = 120;
1741
- const activityDots = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1742
- className: "zhihu-dots",
1743
- "aria-hidden": "true",
1744
- children: [
1745
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}),
1746
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}),
1747
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {})
1748
- ]
1737
+ const activityText = (children) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
1738
+ active: true,
1739
+ children
1749
1740
  });
1750
1741
  function wrapCredentials(remote) {
1751
1742
  return {
@@ -1961,6 +1952,27 @@ function LinkOrText(props) {
1961
1952
  children: props.label
1962
1953
  });
1963
1954
  }
1955
+ /** 直答的思考过程:官方 DisclosureRow 承载折叠,卡面用契约的 flat 卡片。 */
1956
+ function ReasoningDisclosure(props) {
1957
+ const t = useT();
1958
+ const [open, setOpen] = (0, react.useState)(false);
1959
+ const toggle = (0, react.useCallback)(() => setOpen((value) => !value), []);
1960
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1961
+ className: "zhihu-ask-reasoning dsh-ui-card dsh-ui-card--flat",
1962
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
1963
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutlineRegular, {}),
1964
+ title: t("思考过程", "Reasoning"),
1965
+ open,
1966
+ expandable: true,
1967
+ expandOnRowClick: true,
1968
+ onToggle: toggle,
1969
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1970
+ className: "dsh-ui-help",
1971
+ children: props.reasoning
1972
+ })
1973
+ })
1974
+ });
1975
+ }
1964
1976
  function OutcomeView(props) {
1965
1977
  const { outcome, stale } = props;
1966
1978
  const t = useT();
@@ -1973,16 +1985,7 @@ function OutcomeView(props) {
1973
1985
  let summary = "";
1974
1986
  if (outcome.mode === "ask") {
1975
1987
  summary = t(`直答(${outcome.answer.model || "默认模型"})`, `Direct answer (${outcome.answer.model || "default model"})`);
1976
- body = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [outcome.answer.reasoning ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
1977
- className: "zhihu-ask-reasoning dsh-ui-card dsh-ui-card--flat",
1978
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
1979
- className: "dsh-ui-heading",
1980
- children: t("思考过程", "Reasoning")
1981
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1982
- className: "dsh-ui-help",
1983
- children: outcome.answer.reasoning
1984
- })]
1985
- }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1988
+ body = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [outcome.answer.reasoning ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningDisclosure, { reasoning: outcome.answer.reasoning }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1986
1989
  className: "zhihu-ask-content dsh-ui-wrap",
1987
1990
  children: outcome.answer.content
1988
1991
  })] });
@@ -2125,7 +2128,7 @@ function CapabilitiesNote(props) {
2125
2128
  function SettingsShell(props) {
2126
2129
  const t = useT();
2127
2130
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2128
- className: "dsh-ui-stack",
2131
+ className: "dsh-ui-form",
2129
2132
  "data-testid": "zhihu-settings",
2130
2133
  "aria-label": t("知乎凭证设置", "Zhihu credential settings"),
2131
2134
  children: [props.children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CapabilitiesNote, {})]
@@ -2173,10 +2176,10 @@ function SettingsSection(props) {
2173
2176
  (0, react.useEffect)(() => {
2174
2177
  load();
2175
2178
  }, [load]);
2176
- if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsShell, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2179
+ if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsShell, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2177
2180
  className: "dsh-ui-loading",
2178
2181
  role: "status",
2179
- children: [activityDots(), t("正在读取凭证状态…", "Loading credential status…")]
2182
+ children: activityText(t("正在读取凭证状态…", "Loading credential status…"))
2180
2183
  }) });
2181
2184
  if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsShell, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2182
2185
  className: "dsh-ui-error",
@@ -2294,18 +2297,18 @@ function SettingsSection(props) {
2294
2297
  children: clientText(note, t)
2295
2298
  }) : null,
2296
2299
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2297
- className: "dsh-ui-actions",
2300
+ className: "dsh-ui-form-footer",
2298
2301
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2299
2302
  variant: "danger",
2300
2303
  className: "zhihu-button-danger",
2301
2304
  disabled: busy || keyLocked || !configured,
2302
2305
  onClick: () => setConfirmClear(true),
2303
- children: pending === "clear" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [activityDots(), t("清除中…", "Clearing…")] }) : t("清除", "Clear")
2306
+ children: pending === "clear" ? activityText(t("清除中…", "Clearing…")) : t("清除", "Clear")
2304
2307
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2305
2308
  variant: "primary",
2306
2309
  disabled: busy || keyLocked || keyValue.length === 0 || draftFailure !== void 0,
2307
2310
  onClick: () => void save(),
2308
- children: pending === "save" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [activityDots(), t("保存中…", "Saving…")] }) : t("保存", "Save")
2311
+ children: pending === "save" ? activityText(t("保存中…", "Saving…")) : t("保存", "Save")
2309
2312
  })]
2310
2313
  }),
2311
2314
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
@@ -2578,10 +2581,10 @@ function UsageSection(props) {
2578
2581
  if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
2579
2582
  "data-testid": "zhihu-usage",
2580
2583
  "aria-label": sectionLabel,
2581
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2584
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2582
2585
  className: "dsh-ui-loading",
2583
2586
  role: "status",
2584
- children: [activityDots(), t("正在读取用量…", "Loading usage…")]
2587
+ children: activityText(t("正在读取用量…", "Loading usage…"))
2585
2588
  })
2586
2589
  });
2587
2590
  if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
@@ -2761,6 +2764,7 @@ function KnowledgeSection(props) {
2761
2764
  const [baseId, setBaseId] = (0, react.useState)("");
2762
2765
  const [file, setFile] = (0, react.useState)(void 0);
2763
2766
  const [fileKey, setFileKey] = (0, react.useState)(0);
2767
+ const fileInputRef = (0, react.useRef)(null);
2764
2768
  const [busy, setBusy] = (0, react.useState)(false);
2765
2769
  const [note, setNote] = (0, react.useState)(void 0);
2766
2770
  const [failure, setFailure] = (0, react.useState)(void 0);
@@ -2856,10 +2860,10 @@ function KnowledgeSection(props) {
2856
2860
  })
2857
2861
  ]
2858
2862
  }),
2859
- list.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2863
+ list.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2860
2864
  className: "dsh-ui-loading",
2861
2865
  role: "status",
2862
- children: [activityDots(), t("正在读取知识库列表…", "Loading knowledge bases…")]
2866
+ children: activityText(t("正在读取知识库列表…", "Loading knowledge bases…"))
2863
2867
  }) : null,
2864
2868
  list.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2865
2869
  className: "dsh-ui-error",
@@ -2916,6 +2920,8 @@ function KnowledgeSection(props) {
2916
2920
  children: t("参考资料", "Reference file")
2917
2921
  }),
2918
2922
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2923
+ ref: fileInputRef,
2924
+ tabIndex: -1,
2919
2925
  id: "zhihu-kb-file",
2920
2926
  type: "file",
2921
2927
  accept: KB_ACCEPT,
@@ -2926,10 +2932,9 @@ function KnowledgeSection(props) {
2926
2932
  }, fileKey),
2927
2933
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2928
2934
  className: "dsh-ui-row",
2929
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2930
- htmlFor: "zhihu-kb-file",
2931
- className: "zhihu-file-trigger",
2932
- "aria-disabled": busy || void 0,
2935
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
2936
+ disabled: busy,
2937
+ onClick: () => fileInputRef.current?.click(),
2933
2938
  children: t("选择文件", "Choose file")
2934
2939
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2935
2940
  className: "dsh-ui-meta dsh-ui-wrap",
@@ -2948,7 +2953,7 @@ function KnowledgeSection(props) {
2948
2953
  variant: "primary",
2949
2954
  disabled: busy || !file,
2950
2955
  onClick: () => void upload(),
2951
- children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [activityDots(), t("上传中…", "Uploading…")] }) : t("确认上传", "Upload")
2956
+ children: busy ? activityText(t("上传中…", "Uploading…")) : t("确认上传", "Upload")
2952
2957
  })
2953
2958
  }) : null,
2954
2959
  note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -3199,7 +3204,7 @@ function ZhihuSettings(props) {
3199
3204
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3200
3205
  className: phase === "loading" ? "dsh-ui-loading" : "dsh-ui-hint",
3201
3206
  role: "status",
3202
- children: phase === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [activityDots(), t("正在请求知乎…", "Requesting Zhihu…")] }) : phase === "idle" && mode !== "hot" && !query.trim() ? t("输入关键词后搜索。", "Enter keywords to search.") : null
3207
+ children: phase === "loading" ? activityText(t("正在请求知乎…", "Requesting Zhihu…")) : phase === "idle" && mode !== "hot" && !query.trim() ? t("输入关键词后搜索。", "Enter keywords to search.") : null
3203
3208
  }),
3204
3209
  phase === "error" && failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3205
3210
  className: "dsh-ui-error",
@@ -3216,7 +3221,7 @@ function ZhihuSettings(props) {
3216
3221
  }, testOperation)]
3217
3222
  });
3218
3223
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3219
- className: "zhihu-panel zhihu-settings-embed",
3224
+ className: "zhihu-panel zhihu-settings-embed dsh-ui-page",
3220
3225
  "data-testid": "zhihu-settings-embed",
3221
3226
  "aria-label": t("知乎", "Zhihu"),
3222
3227
  children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klarkxy/dsh-zhihu",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Zhihu search, agent tools, knowledge bases, and usage tracking for DeepSeek Harness.",
5
5
  "icon": "./icon.svg",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -96,7 +96,7 @@
96
96
  },
97
97
  "dependencies": {
98
98
  "zod": "^4.1.5",
99
- "@klarkxy/dsh-plugin-kit": "0.4.0"
99
+ "@klarkxy/dsh-plugin-kit": "0.5.0"
100
100
  },
101
101
  "peerDependencies": {
102
102
  "@deepseek-ai/cordis": ">=4.0.4",
@@ -153,6 +153,6 @@
153
153
  "registry": "https://registry.npmjs.org/"
154
154
  },
155
155
  "dshRelease": {
156
- "contentHash": "5cdb8a30c07f5540681cb45bf1d6badd5bcde7fa6d164884ab066f8818311aed"
156
+ "contentHash": "0aafaa4e89da79665a21f91ad6eab94c93fb7233415b806fe26e2ef33d2c3d09"
157
157
  }
158
158
  }