@klarkxy/dsh-zhihu 0.2.6 → 0.2.8
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 +18 -11
- package/docs/README.zh-CN.md +14 -7
- package/lib/client.js +447 -86
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2426 -1
- package/lib/index.js.map +1 -1
- package/lib/tools.js +188 -0
- package/lib/tools.js.map +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +6 -4
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-]+)(
|
|
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.
|
|
622
|
-
* DisclosureRow; only
|
|
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
|
|
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:
|
|
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
|
|
699
|
-
*
|
|
700
|
-
*
|
|
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)("
|
|
1298
|
-
className: "
|
|
1299
|
-
children:
|
|
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
|
})
|
|
@@ -1727,6 +1723,358 @@ function ZhihuQuotaSection({ rpc }) {
|
|
|
1727
1723
|
});
|
|
1728
1724
|
}
|
|
1729
1725
|
//#endregion
|
|
1726
|
+
//#region src/client-sync-credentials.tsx
|
|
1727
|
+
/**
|
|
1728
|
+
* 知乎发布助手的应用凭据设置。
|
|
1729
|
+
*
|
|
1730
|
+
* 只有插件设置页(plugins.bundle.config)里才有这段表单:右侧栏工作台不发凭据,
|
|
1731
|
+
* 也不重复提供入口。凭据本身由宿主管理(remote.credentials),插件只读写引用。
|
|
1732
|
+
*/
|
|
1733
|
+
const ZHIHU_APP_KEY_REF = "ZHIHU_OPENAPI_APP_KEY";
|
|
1734
|
+
const ZHIHU_APP_SECRET_REF = "ZHIHU_OPENAPI_APP_SECRET";
|
|
1735
|
+
/** 申请发布助手应用凭据的官方入口。 */
|
|
1736
|
+
const ZHIHU_PUBLISHER_URL = "https://www.zhihu.com/playground/zhihu-publisher";
|
|
1737
|
+
/** 成对保存时的挂起标记;它与凭据引用名不同,不会撞上字段的「保存中…」。 */
|
|
1738
|
+
const PAIR_PENDING = "pair";
|
|
1739
|
+
const GRANT_KEY = /ZHIHU_OPENAPI_APP_KEY\s*[:=]\s*"([^"\r\n]+)"/;
|
|
1740
|
+
const GRANT_SECRET = /ZHIHU_OPENAPI_APP_SECRET\s*[:=]\s*"([^"\r\n]+)"/;
|
|
1741
|
+
const GRANT_MARKER = /ZHIHU_OPENAPI_APP_(?:KEY|SECRET)/;
|
|
1742
|
+
/**
|
|
1743
|
+
* 知乎发布助手把授权说明和两项凭据放在同一段文字里。只在两项都用引号写出时拆开;
|
|
1744
|
+
* 单独粘贴的 Key 或 Secret 保持原样,避免把普通文本误存成另一项。
|
|
1745
|
+
*/
|
|
1746
|
+
function parseZhihuPublisherGrant(value) {
|
|
1747
|
+
const key = GRANT_KEY.exec(value);
|
|
1748
|
+
const secret = GRANT_SECRET.exec(value);
|
|
1749
|
+
if (!key || !secret) return null;
|
|
1750
|
+
const appKey = key[1].trim();
|
|
1751
|
+
const appSecret = secret[1].trim();
|
|
1752
|
+
if (!appKey || !appSecret || appKey === appSecret) return null;
|
|
1753
|
+
return {
|
|
1754
|
+
appKey,
|
|
1755
|
+
appSecret
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* 文字里出现了凭据标记,但没能按受支持的格式读出两项凭据。这样的文本不能当凭据存:
|
|
1760
|
+
* 整段授权说明一旦落进密钥框,格式稍有出入就会变成一个坏凭据,而且要到发布时才报错。
|
|
1761
|
+
*/
|
|
1762
|
+
function looksLikeZhihuPublisherGrant(value) {
|
|
1763
|
+
return GRANT_MARKER.test(value);
|
|
1764
|
+
}
|
|
1765
|
+
const FIELD_COPY = [{
|
|
1766
|
+
ref: ZHIHU_APP_SECRET_REF,
|
|
1767
|
+
label: "App Secret",
|
|
1768
|
+
zh: "知乎发布助手的应用密钥",
|
|
1769
|
+
en: "Publisher app secret",
|
|
1770
|
+
hintZh: "知乎发布助手签发的应用密钥。整段授权说明粘贴在这里时,下面会自动填入用户名。",
|
|
1771
|
+
hintEn: "The app secret issued by the Zhihu publisher. Pasting the authorization text here fills in the username below."
|
|
1772
|
+
}, {
|
|
1773
|
+
ref: ZHIHU_APP_KEY_REF,
|
|
1774
|
+
label: "App Key",
|
|
1775
|
+
zh: "知乎主页用户名",
|
|
1776
|
+
en: "Profile username",
|
|
1777
|
+
hintZh: "知乎个人主页地址里的用户名,例如 www.zhihu.com/people/<你的用户名>。这不是密钥,以明文显示。",
|
|
1778
|
+
hintEn: "The username in your Zhihu profile URL, e.g. www.zhihu.com/people/<your username>. This is not a secret and is shown in plain text.",
|
|
1779
|
+
plain: true
|
|
1780
|
+
}];
|
|
1781
|
+
/** 只有 http(s) 可以成为链接;其余一律降级为文本。 */
|
|
1782
|
+
function safeUrl$1(url) {
|
|
1783
|
+
return /^https?:\/\//i.test(url) ? url : null;
|
|
1784
|
+
}
|
|
1785
|
+
function PublisherLink(props) {
|
|
1786
|
+
const url = safeUrl$1(props.url);
|
|
1787
|
+
if (!url) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: props.children });
|
|
1788
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1789
|
+
className: "zhihu-link",
|
|
1790
|
+
href: url,
|
|
1791
|
+
target: "_blank",
|
|
1792
|
+
rel: "noreferrer noopener",
|
|
1793
|
+
children: props.children
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
function ZhihuSyncCredentials(props) {
|
|
1797
|
+
const t = useT();
|
|
1798
|
+
const { credentials } = props;
|
|
1799
|
+
const alive = (0, react.useRef)(false);
|
|
1800
|
+
const [state, setState] = (0, react.useState)({ status: "loading" });
|
|
1801
|
+
const [drafts, setDrafts] = (0, react.useState)({});
|
|
1802
|
+
const [paired, setPaired] = (0, react.useState)(false);
|
|
1803
|
+
const [pending, setPending] = (0, react.useState)(null);
|
|
1804
|
+
const [confirmRef, setConfirmRef] = (0, react.useState)(null);
|
|
1805
|
+
const [failure, setFailure] = (0, react.useState)("");
|
|
1806
|
+
const [note, setNote] = (0, react.useState)("");
|
|
1807
|
+
const views = state.status === "ready" ? state.credentials : {};
|
|
1808
|
+
const writable = (ref) => views[ref]?.writable !== false;
|
|
1809
|
+
(0, react.useEffect)(() => {
|
|
1810
|
+
alive.current = true;
|
|
1811
|
+
return () => {
|
|
1812
|
+
alive.current = false;
|
|
1813
|
+
};
|
|
1814
|
+
}, []);
|
|
1815
|
+
const load = (0, react.useCallback)(async () => {
|
|
1816
|
+
setState((current) => current.status === "ready" ? current : { status: "loading" });
|
|
1817
|
+
try {
|
|
1818
|
+
const result = await credentials.describe({ refs: FIELD_COPY.map((field) => field.ref) });
|
|
1819
|
+
if (!alive.current) return;
|
|
1820
|
+
if (!result.ok) {
|
|
1821
|
+
setState({
|
|
1822
|
+
status: "error",
|
|
1823
|
+
error: result.error.message
|
|
1824
|
+
});
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
setState({
|
|
1828
|
+
status: "ready",
|
|
1829
|
+
credentials: result.value.credentials
|
|
1830
|
+
});
|
|
1831
|
+
} catch (cause) {
|
|
1832
|
+
if (!alive.current) return;
|
|
1833
|
+
setState({
|
|
1834
|
+
status: "error",
|
|
1835
|
+
error: cause instanceof Error ? cause.message : String(cause)
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
}, [credentials]);
|
|
1839
|
+
(0, react.useEffect)(() => {
|
|
1840
|
+
load();
|
|
1841
|
+
}, [load]);
|
|
1842
|
+
const save = async (refs) => {
|
|
1843
|
+
if (pending !== null) return;
|
|
1844
|
+
const writes = refs.map((ref) => ({
|
|
1845
|
+
ref,
|
|
1846
|
+
value: (drafts[ref] ?? "").trim()
|
|
1847
|
+
})).filter((item) => item.value && writable(item.ref));
|
|
1848
|
+
if (!writes.length) return;
|
|
1849
|
+
setPending(refs.length > 1 ? PAIR_PENDING : refs[0]);
|
|
1850
|
+
setFailure("");
|
|
1851
|
+
setNote("");
|
|
1852
|
+
try {
|
|
1853
|
+
for (const write of writes) {
|
|
1854
|
+
const result = await credentials.set(write);
|
|
1855
|
+
if (!alive.current) return;
|
|
1856
|
+
if (!result.ok) {
|
|
1857
|
+
setFailure(result.error.message);
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
setDrafts((current) => {
|
|
1862
|
+
const next = { ...current };
|
|
1863
|
+
for (const write of writes) delete next[write.ref];
|
|
1864
|
+
return next;
|
|
1865
|
+
});
|
|
1866
|
+
setPaired(false);
|
|
1867
|
+
setNote(writes.length > 1 ? ["已保存用户名与密钥。", "Saved the username and the secret."] : ["已保存。", "Saved."]);
|
|
1868
|
+
await load();
|
|
1869
|
+
} catch (cause) {
|
|
1870
|
+
if (!alive.current) return;
|
|
1871
|
+
setFailure(cause instanceof Error ? cause.message : String(cause));
|
|
1872
|
+
} finally {
|
|
1873
|
+
if (alive.current) setPending(null);
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
const clear = async (ref) => {
|
|
1877
|
+
setConfirmRef(null);
|
|
1878
|
+
if (pending !== null) return;
|
|
1879
|
+
setPending(ref);
|
|
1880
|
+
setFailure("");
|
|
1881
|
+
setNote("");
|
|
1882
|
+
try {
|
|
1883
|
+
const result = await credentials.unset({ ref });
|
|
1884
|
+
if (!alive.current) return;
|
|
1885
|
+
if (!result.ok) {
|
|
1886
|
+
setFailure(result.error.message);
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
setDrafts((current) => ({
|
|
1890
|
+
...current,
|
|
1891
|
+
[ref]: ""
|
|
1892
|
+
}));
|
|
1893
|
+
setPaired(false);
|
|
1894
|
+
setNote(["已清除。", "Cleared."]);
|
|
1895
|
+
await load();
|
|
1896
|
+
} catch (cause) {
|
|
1897
|
+
if (!alive.current) return;
|
|
1898
|
+
setFailure(cause instanceof Error ? cause.message : String(cause));
|
|
1899
|
+
} finally {
|
|
1900
|
+
if (alive.current) setPending(null);
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1903
|
+
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
1904
|
+
className: "dsh-ui-stack",
|
|
1905
|
+
"aria-label": t("知乎发布助手凭据", "Zhihu publisher credentials"),
|
|
1906
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1907
|
+
className: "dsh-ui-loading",
|
|
1908
|
+
role: "status",
|
|
1909
|
+
children: t("正在读取凭据状态…", "Loading credential status…")
|
|
1910
|
+
})
|
|
1911
|
+
});
|
|
1912
|
+
if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1913
|
+
className: "dsh-ui-stack",
|
|
1914
|
+
"aria-label": t("知乎发布助手凭据", "Zhihu publisher credentials"),
|
|
1915
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1916
|
+
className: "dsh-ui-error",
|
|
1917
|
+
role: "alert",
|
|
1918
|
+
children: clientText(state.error, t)
|
|
1919
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1920
|
+
className: "dsh-ui-actions",
|
|
1921
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
1922
|
+
onClick: () => void load(),
|
|
1923
|
+
children: t("重试", "Retry")
|
|
1924
|
+
})
|
|
1925
|
+
})]
|
|
1926
|
+
});
|
|
1927
|
+
const confirmed = confirmRef ? FIELD_COPY.find((field) => field.ref === confirmRef) : void 0;
|
|
1928
|
+
const pairReady = paired && (drafts["ZHIHU_OPENAPI_APP_KEY"] ?? "").trim() !== "" && (drafts["ZHIHU_OPENAPI_APP_SECRET"] ?? "").trim() !== "";
|
|
1929
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1930
|
+
className: "dsh-ui-stack",
|
|
1931
|
+
"aria-label": t("知乎发布助手凭据", "Zhihu publisher credentials"),
|
|
1932
|
+
children: [
|
|
1933
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
1934
|
+
className: "dsh-ui-heading",
|
|
1935
|
+
children: t("知乎发布助手", "Zhihu publisher")
|
|
1936
|
+
}),
|
|
1937
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1938
|
+
className: "dsh-ui-help",
|
|
1939
|
+
children: [
|
|
1940
|
+
t("发布助手使用另一组应用凭据,与搜索用的 Access Secret 无关。把知乎给出的授权说明粘贴到上面的密钥框即可,两项凭据要写成 ZHIHU_OPENAPI_APP_KEY:\"…\" 与 ZHIHU_OPENAPI_APP_SECRET:\"…\" 的形式,用户名会自动填到下面。", "The publisher uses its own app credentials, separate from the search Access Secret. Paste Zhihu’s authorization text into the secret field above — both credentials must be written as ZHIHU_OPENAPI_APP_KEY:\"…\" and ZHIHU_OPENAPI_APP_SECRET:\"…\" — and the username fills in below."),
|
|
1941
|
+
" ",
|
|
1942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PublisherLink, {
|
|
1943
|
+
url: ZHIHU_PUBLISHER_URL,
|
|
1944
|
+
children: t("申请凭据", "Get credentials")
|
|
1945
|
+
})
|
|
1946
|
+
]
|
|
1947
|
+
}),
|
|
1948
|
+
paired ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1949
|
+
className: "dsh-ui-hint",
|
|
1950
|
+
children: t("这两项来自同一段授权说明,可以一起保存。", "These two came from the same authorization text and can be saved together.")
|
|
1951
|
+
}) : null,
|
|
1952
|
+
FIELD_COPY.map((field) => {
|
|
1953
|
+
const view = state.credentials[field.ref];
|
|
1954
|
+
const locked = view?.writable === false;
|
|
1955
|
+
const configured = view?.configured === true;
|
|
1956
|
+
const draft = drafts[field.ref] ?? "";
|
|
1957
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1958
|
+
className: "dsh-ui-field",
|
|
1959
|
+
children: [
|
|
1960
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1961
|
+
className: "dsh-ui-label-row",
|
|
1962
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1963
|
+
className: "dsh-ui-label",
|
|
1964
|
+
id: `zhihu-sync-cred-${field.ref}`,
|
|
1965
|
+
children: field.label
|
|
1966
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1967
|
+
className: "dsh-ui-row",
|
|
1968
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: locked ? "warning" : configured ? "done" : "idle" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1969
|
+
className: "dsh-ui-meta",
|
|
1970
|
+
children: locked ? t("由环境变量提供(只读)", "Provided by an environment variable (read-only)") : configured ? t("已保存", "Saved") : t("未配置", "Not set")
|
|
1971
|
+
})]
|
|
1972
|
+
})]
|
|
1973
|
+
}),
|
|
1974
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1975
|
+
className: "dsh-ui-hint",
|
|
1976
|
+
children: t(field.hintZh, field.hintEn)
|
|
1977
|
+
}),
|
|
1978
|
+
renderInput({
|
|
1979
|
+
type: field.plain ? "text" : "password",
|
|
1980
|
+
value: draft,
|
|
1981
|
+
disabled: locked || pending !== null,
|
|
1982
|
+
"aria-label": field.label,
|
|
1983
|
+
"aria-describedby": `zhihu-sync-cred-${field.ref}`,
|
|
1984
|
+
placeholder: locked ? t("由环境变量提供", "Set by an environment variable") : t("粘贴凭据", "Paste the credential"),
|
|
1985
|
+
onChange: (value) => {
|
|
1986
|
+
const grant = parseZhihuPublisherGrant(value);
|
|
1987
|
+
if (grant) {
|
|
1988
|
+
const next = {};
|
|
1989
|
+
if (writable("ZHIHU_OPENAPI_APP_KEY")) next[ZHIHU_APP_KEY_REF] = grant.appKey;
|
|
1990
|
+
if (writable("ZHIHU_OPENAPI_APP_SECRET")) next[ZHIHU_APP_SECRET_REF] = grant.appSecret;
|
|
1991
|
+
setDrafts((current) => ({
|
|
1992
|
+
...current,
|
|
1993
|
+
...next
|
|
1994
|
+
}));
|
|
1995
|
+
setPaired(true);
|
|
1996
|
+
setFailure("");
|
|
1997
|
+
setNote(Object.keys(next).length === 2 ? ["已从授权说明读出用户名与密钥。", "Read the username and the secret from the authorization text."] : ["已从授权说明读出一项凭据;另一项当前是只读的,不会被改动。", "Read one credential from the authorization text; the other is read-only and is left alone."]);
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
if (looksLikeZhihuPublisherGrant(value)) {
|
|
2001
|
+
setFailure(["这段文字里有知乎凭据标记,但没能读出 App Key 与 App Secret。请确认两项都写成 ZHIHU_OPENAPI_APP_KEY:\"…\" 与 ZHIHU_OPENAPI_APP_SECRET:\"…\" 的形式。", "This text contains Zhihu credential markers, but no App Key and App Secret could be read from it. Both must be written as ZHIHU_OPENAPI_APP_KEY:\"…\" and ZHIHU_OPENAPI_APP_SECRET:\"…\"."]);
|
|
2002
|
+
setNote("");
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
setDrafts((current) => ({
|
|
2006
|
+
...current,
|
|
2007
|
+
[field.ref]: value
|
|
2008
|
+
}));
|
|
2009
|
+
if (!value.trim()) setPaired(false);
|
|
2010
|
+
setFailure("");
|
|
2011
|
+
setNote("");
|
|
2012
|
+
}
|
|
2013
|
+
}),
|
|
2014
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2015
|
+
className: "dsh-ui-actions",
|
|
2016
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2017
|
+
variant: "primary",
|
|
2018
|
+
disabled: locked || pending !== null || draft.trim() === "",
|
|
2019
|
+
onClick: () => void save([field.ref]),
|
|
2020
|
+
children: pending === field.ref ? t("保存中…", "Saving…") : t("保存", "Save")
|
|
2021
|
+
}), locked ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2022
|
+
variant: "danger",
|
|
2023
|
+
disabled: pending !== null || !configured,
|
|
2024
|
+
onClick: () => setConfirmRef(field.ref),
|
|
2025
|
+
children: pending === field.ref ? t("清除中…", "Clearing…") : t("清除", "Clear")
|
|
2026
|
+
})]
|
|
2027
|
+
})
|
|
2028
|
+
]
|
|
2029
|
+
}, field.ref);
|
|
2030
|
+
}),
|
|
2031
|
+
pairReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2032
|
+
className: "dsh-ui-actions",
|
|
2033
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2034
|
+
variant: "primary",
|
|
2035
|
+
disabled: pending !== null,
|
|
2036
|
+
onClick: () => void save([ZHIHU_APP_KEY_REF, ZHIHU_APP_SECRET_REF]),
|
|
2037
|
+
children: pending === PAIR_PENDING ? t("保存中…", "Saving…") : t("保存两项", "Save both")
|
|
2038
|
+
})
|
|
2039
|
+
}) : null,
|
|
2040
|
+
note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2041
|
+
className: "dsh-ui-notice",
|
|
2042
|
+
role: "status",
|
|
2043
|
+
children: clientText(note, t)
|
|
2044
|
+
}) : null,
|
|
2045
|
+
failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2046
|
+
className: "dsh-ui-error",
|
|
2047
|
+
role: "alert",
|
|
2048
|
+
children: clientText(failure, t)
|
|
2049
|
+
}) : null,
|
|
2050
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
2051
|
+
open: confirmed !== void 0,
|
|
2052
|
+
onClose: () => setConfirmRef(null),
|
|
2053
|
+
title: t("清除发布助手凭据?", "Clear the publisher credentials?"),
|
|
2054
|
+
closeLabel: t("关闭", "Close"),
|
|
2055
|
+
className: "zhihu-panel",
|
|
2056
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2057
|
+
className: "dsh-ui-actions",
|
|
2058
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2059
|
+
onClick: () => setConfirmRef(null),
|
|
2060
|
+
children: t("取消", "Cancel")
|
|
2061
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2062
|
+
variant: "danger",
|
|
2063
|
+
onClick: () => {
|
|
2064
|
+
if (confirmed) clear(confirmed.ref);
|
|
2065
|
+
},
|
|
2066
|
+
children: t("清除", "Clear")
|
|
2067
|
+
})]
|
|
2068
|
+
}),
|
|
2069
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2070
|
+
className: "dsh-ui-help",
|
|
2071
|
+
children: t("清除后,发布与预览会失败,直到重新填写凭据。", "Publishing and previewing fail until you enter the credentials again.")
|
|
2072
|
+
})
|
|
2073
|
+
})
|
|
2074
|
+
]
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
//#endregion
|
|
1730
2078
|
//#region src/client.tsx
|
|
1731
2079
|
const name = "dsh-zhihu-client";
|
|
1732
2080
|
const inject = [
|
|
@@ -1738,14 +2086,9 @@ const inject = [
|
|
|
1738
2086
|
];
|
|
1739
2087
|
const PACKAGE_NAME = "@klarkxy/dsh-zhihu";
|
|
1740
2088
|
const SLOT_ORDER = 120;
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
-
]
|
|
2089
|
+
const activityText = (children) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
|
|
2090
|
+
active: true,
|
|
2091
|
+
children
|
|
1749
2092
|
});
|
|
1750
2093
|
function wrapCredentials(remote) {
|
|
1751
2094
|
return {
|
|
@@ -1961,6 +2304,27 @@ function LinkOrText(props) {
|
|
|
1961
2304
|
children: props.label
|
|
1962
2305
|
});
|
|
1963
2306
|
}
|
|
2307
|
+
/** 直答的思考过程:官方 DisclosureRow 承载折叠,卡面用契约的 flat 卡片。 */
|
|
2308
|
+
function ReasoningDisclosure(props) {
|
|
2309
|
+
const t = useT();
|
|
2310
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
2311
|
+
const toggle = (0, react.useCallback)(() => setOpen((value) => !value), []);
|
|
2312
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2313
|
+
className: "zhihu-ask-reasoning dsh-ui-card dsh-ui-card--flat",
|
|
2314
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
|
|
2315
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutlineRegular, {}),
|
|
2316
|
+
title: t("思考过程", "Reasoning"),
|
|
2317
|
+
open,
|
|
2318
|
+
expandable: true,
|
|
2319
|
+
expandOnRowClick: true,
|
|
2320
|
+
onToggle: toggle,
|
|
2321
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2322
|
+
className: "dsh-ui-help",
|
|
2323
|
+
children: props.reasoning
|
|
2324
|
+
})
|
|
2325
|
+
})
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
1964
2328
|
function OutcomeView(props) {
|
|
1965
2329
|
const { outcome, stale } = props;
|
|
1966
2330
|
const t = useT();
|
|
@@ -1973,16 +2337,7 @@ function OutcomeView(props) {
|
|
|
1973
2337
|
let summary = "";
|
|
1974
2338
|
if (outcome.mode === "ask") {
|
|
1975
2339
|
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.
|
|
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", {
|
|
2340
|
+
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
2341
|
className: "zhihu-ask-content dsh-ui-wrap",
|
|
1987
2342
|
children: outcome.answer.content
|
|
1988
2343
|
})] });
|
|
@@ -2125,7 +2480,7 @@ function CapabilitiesNote(props) {
|
|
|
2125
2480
|
function SettingsShell(props) {
|
|
2126
2481
|
const t = useT();
|
|
2127
2482
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2128
|
-
className: "dsh-ui-
|
|
2483
|
+
className: "dsh-ui-form",
|
|
2129
2484
|
"data-testid": "zhihu-settings",
|
|
2130
2485
|
"aria-label": t("知乎凭证设置", "Zhihu credential settings"),
|
|
2131
2486
|
children: [props.children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CapabilitiesNote, {})]
|
|
@@ -2173,10 +2528,10 @@ function SettingsSection(props) {
|
|
|
2173
2528
|
(0, react.useEffect)(() => {
|
|
2174
2529
|
load();
|
|
2175
2530
|
}, [load]);
|
|
2176
|
-
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsShell, { children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2531
|
+
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsShell, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2177
2532
|
className: "dsh-ui-loading",
|
|
2178
2533
|
role: "status",
|
|
2179
|
-
children:
|
|
2534
|
+
children: activityText(t("正在读取凭证状态…", "Loading credential status…"))
|
|
2180
2535
|
}) });
|
|
2181
2536
|
if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsShell, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2182
2537
|
className: "dsh-ui-error",
|
|
@@ -2294,18 +2649,18 @@ function SettingsSection(props) {
|
|
|
2294
2649
|
children: clientText(note, t)
|
|
2295
2650
|
}) : null,
|
|
2296
2651
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2297
|
-
className: "dsh-ui-
|
|
2652
|
+
className: "dsh-ui-form-footer",
|
|
2298
2653
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2299
2654
|
variant: "danger",
|
|
2300
2655
|
className: "zhihu-button-danger",
|
|
2301
2656
|
disabled: busy || keyLocked || !configured,
|
|
2302
2657
|
onClick: () => setConfirmClear(true),
|
|
2303
|
-
children: pending === "clear" ?
|
|
2658
|
+
children: pending === "clear" ? activityText(t("清除中…", "Clearing…")) : t("清除", "Clear")
|
|
2304
2659
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
2305
2660
|
variant: "primary",
|
|
2306
2661
|
disabled: busy || keyLocked || keyValue.length === 0 || draftFailure !== void 0,
|
|
2307
2662
|
onClick: () => void save(),
|
|
2308
|
-
children: pending === "save" ?
|
|
2663
|
+
children: pending === "save" ? activityText(t("保存中…", "Saving…")) : t("保存", "Save")
|
|
2309
2664
|
})]
|
|
2310
2665
|
}),
|
|
2311
2666
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
@@ -2578,10 +2933,10 @@ function UsageSection(props) {
|
|
|
2578
2933
|
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
2579
2934
|
"data-testid": "zhihu-usage",
|
|
2580
2935
|
"aria-label": sectionLabel,
|
|
2581
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2936
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2582
2937
|
className: "dsh-ui-loading",
|
|
2583
2938
|
role: "status",
|
|
2584
|
-
children:
|
|
2939
|
+
children: activityText(t("正在读取用量…", "Loading usage…"))
|
|
2585
2940
|
})
|
|
2586
2941
|
});
|
|
2587
2942
|
if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -2761,6 +3116,7 @@ function KnowledgeSection(props) {
|
|
|
2761
3116
|
const [baseId, setBaseId] = (0, react.useState)("");
|
|
2762
3117
|
const [file, setFile] = (0, react.useState)(void 0);
|
|
2763
3118
|
const [fileKey, setFileKey] = (0, react.useState)(0);
|
|
3119
|
+
const fileInputRef = (0, react.useRef)(null);
|
|
2764
3120
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
2765
3121
|
const [note, setNote] = (0, react.useState)(void 0);
|
|
2766
3122
|
const [failure, setFailure] = (0, react.useState)(void 0);
|
|
@@ -2856,10 +3212,10 @@ function KnowledgeSection(props) {
|
|
|
2856
3212
|
})
|
|
2857
3213
|
]
|
|
2858
3214
|
}),
|
|
2859
|
-
list.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3215
|
+
list.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2860
3216
|
className: "dsh-ui-loading",
|
|
2861
3217
|
role: "status",
|
|
2862
|
-
children:
|
|
3218
|
+
children: activityText(t("正在读取知识库列表…", "Loading knowledge bases…"))
|
|
2863
3219
|
}) : null,
|
|
2864
3220
|
list.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2865
3221
|
className: "dsh-ui-error",
|
|
@@ -2916,6 +3272,8 @@ function KnowledgeSection(props) {
|
|
|
2916
3272
|
children: t("参考资料", "Reference file")
|
|
2917
3273
|
}),
|
|
2918
3274
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3275
|
+
ref: fileInputRef,
|
|
3276
|
+
tabIndex: -1,
|
|
2919
3277
|
id: "zhihu-kb-file",
|
|
2920
3278
|
type: "file",
|
|
2921
3279
|
accept: KB_ACCEPT,
|
|
@@ -2926,10 +3284,9 @@ function KnowledgeSection(props) {
|
|
|
2926
3284
|
}, fileKey),
|
|
2927
3285
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2928
3286
|
className: "dsh-ui-row",
|
|
2929
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
"aria-disabled": busy || void 0,
|
|
3287
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuButton, {
|
|
3288
|
+
disabled: busy,
|
|
3289
|
+
onClick: () => fileInputRef.current?.click(),
|
|
2933
3290
|
children: t("选择文件", "Choose file")
|
|
2934
3291
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2935
3292
|
className: "dsh-ui-meta dsh-ui-wrap",
|
|
@@ -2948,7 +3305,7 @@ function KnowledgeSection(props) {
|
|
|
2948
3305
|
variant: "primary",
|
|
2949
3306
|
disabled: busy || !file,
|
|
2950
3307
|
onClick: () => void upload(),
|
|
2951
|
-
children: busy ?
|
|
3308
|
+
children: busy ? activityText(t("上传中…", "Uploading…")) : t("确认上传", "Upload")
|
|
2952
3309
|
})
|
|
2953
3310
|
}) : null,
|
|
2954
3311
|
note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -3199,7 +3556,7 @@ function ZhihuSettings(props) {
|
|
|
3199
3556
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3200
3557
|
className: phase === "loading" ? "dsh-ui-loading" : "dsh-ui-hint",
|
|
3201
3558
|
role: "status",
|
|
3202
|
-
children: phase === "loading" ?
|
|
3559
|
+
children: phase === "loading" ? activityText(t("正在请求知乎…", "Requesting Zhihu…")) : phase === "idle" && mode !== "hot" && !query.trim() ? t("输入关键词后搜索。", "Enter keywords to search.") : null
|
|
3203
3560
|
}),
|
|
3204
3561
|
phase === "error" && failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3205
3562
|
className: "dsh-ui-error",
|
|
@@ -3216,12 +3573,16 @@ function ZhihuSettings(props) {
|
|
|
3216
3573
|
}, testOperation)]
|
|
3217
3574
|
});
|
|
3218
3575
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3219
|
-
className: "zhihu-panel zhihu-settings-embed",
|
|
3576
|
+
className: "zhihu-panel zhihu-settings-embed dsh-ui-page",
|
|
3220
3577
|
"data-testid": "zhihu-settings-embed",
|
|
3221
3578
|
"aria-label": t("知乎", "Zhihu"),
|
|
3222
3579
|
children: [
|
|
3223
3580
|
tablist,
|
|
3224
|
-
panel("settings", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
|
|
3581
|
+
panel("settings", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
|
|
3582
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsSection, { credentials }),
|
|
3583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuSyncCredentials, { credentials }),
|
|
3584
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuCallPolicySection, { rpc })
|
|
3585
|
+
] })),
|
|
3225
3586
|
panel("usage", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageSection, { rpc })),
|
|
3226
3587
|
panel("quota", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZhihuQuotaSection, { rpc })),
|
|
3227
3588
|
panel("knowledge", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnowledgeSection, { rpc })),
|