@houser/edus-plugin 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -20836,1308 +20836,1345 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20836
20836
  });
20837
20837
  }
20838
20838
  //#endregion
20839
- //#region lib/types/client/HtmlPublishPreview.js
20840
- function fileRef(address) {
20841
- if (!address.startsWith("dsh-resource://file/session/")) return null;
20842
- try {
20843
- const [sessionId, ...path] = address.slice(28).split(/[?#]/, 1)[0]?.split("/") ?? [];
20844
- if (sessionId === void 0 || sessionId === "" || path.length === 0) return null;
20845
- return {
20846
- sessionId: decodeURIComponent(sessionId),
20847
- path: path.map(decodeURIComponent).join("/")
20848
- };
20849
- } catch {
20850
- return null;
20851
- }
20852
- }
20853
- function basename(path) {
20854
- return path.split("/").at(-1) ?? path;
20839
+ //#region lib/types/client/LoginGuide.js
20840
+ /**
20841
+ * 对话模型必须登录课成账号才能用(网关以登录 token 鉴权计费),而 Harness 自带
20842
+ * 的账号与模型设置入口都已屏蔽,所以登录引导由本插件提供:
20843
+ *
20844
+ * - 全局登录弹窗(shell.overlay):未登录启动时自动弹出一次,可点右上角关闭;
20845
+ * - 输入框上方横幅(conversation.input.dock):会话内未登录时常驻;
20846
+ * - 侧边栏底部按钮(sidebar.footer.action):新会话页也能找到登录入口。
20847
+ *
20848
+ * 三处共用一个弹窗开关,登录成功后弹窗关闭、横幅消失;侧边栏按钮转为显示当前
20849
+ * 账号,点击打开账号信息弹窗(AccountDialogOverlay)。
20850
+ */
20851
+ let dialogOpen = false;
20852
+ /** 打开弹窗时附带的提示(如「登录状态已失效」),关闭时清空。 */
20853
+ let dialogNotice = "";
20854
+ const dialogListeners = /* @__PURE__ */ new Set();
20855
+ function setDialogOpen(next, notice = "") {
20856
+ if (dialogOpen === next && dialogNotice === notice) return;
20857
+ dialogOpen = next;
20858
+ dialogNotice = notice;
20859
+ for (const listener of dialogListeners) listener();
20855
20860
  }
20856
- /** 后端在未开通会员时返回的提示里带有这个字眼,此时改为引导扫码联系客服。 */
20857
- const MEMBERSHIP_HINT = "需要开通会员";
20858
- const SERVICE_QR = "https://cdn.juzhenai.cn/ai-edu/assets/wechat.webp";
20859
- const styles$1 = {
20860
- root: {
20861
- display: "flex",
20862
- flexDirection: "column",
20863
- height: "100%",
20864
- minHeight: 0,
20865
- background: "var(--dsw-alias-bg-base)"
20861
+ const EdusLoginDialog = {
20862
+ open(notice) {
20863
+ setDialogOpen(true, typeof notice === "string" ? notice : "");
20866
20864
  },
20867
- bar: {
20868
- display: "flex",
20869
- alignItems: "center",
20870
- gap: 7,
20871
- padding: "7px 10px",
20872
- borderBottom: "1px solid var(--dsw-alias-border-l2)",
20873
- background: "var(--dsw-alias-bg-layer-1)"
20865
+ close() {
20866
+ setDialogOpen(false);
20874
20867
  },
20875
- button: {
20876
- border: "1px solid var(--dsw-alias-border-l2)",
20877
- borderRadius: 6,
20878
- padding: "4px 8px",
20879
- background: "transparent",
20880
- color: "var(--dsw-alias-label-primary)",
20881
- font: "inherit",
20882
- fontSize: 12,
20883
- cursor: "pointer"
20868
+ notice: () => dialogNotice,
20869
+ subscribe(listener) {
20870
+ dialogListeners.add(listener);
20871
+ return () => {
20872
+ dialogListeners.delete(listener);
20873
+ };
20884
20874
  },
20885
- primary: {
20886
- border: "none",
20887
- borderRadius: 7,
20888
- padding: "6px 11px",
20889
- background: "var(--dsw-alias-button-primary-fill)",
20890
- color: "var(--dsw-alias-label-primary-inverted)",
20891
- font: "inherit",
20892
- fontSize: 12,
20893
- fontWeight: 650,
20894
- cursor: "pointer"
20875
+ getSnapshot: () => dialogOpen
20876
+ };
20877
+ function useDialogOpen() {
20878
+ return (0, react.useSyncExternalStore)(EdusLoginDialog.subscribe, EdusLoginDialog.getSnapshot, () => false);
20879
+ }
20880
+ /** 账号信息弹窗的开关,与登录弹窗相互独立。 */
20881
+ let accountOpen = false;
20882
+ const accountListeners = /* @__PURE__ */ new Set();
20883
+ function setAccountOpen(next) {
20884
+ if (accountOpen === next) return;
20885
+ accountOpen = next;
20886
+ for (const listener of accountListeners) listener();
20887
+ }
20888
+ const EdusAccountDialog = {
20889
+ open() {
20890
+ setAccountOpen(true);
20895
20891
  },
20896
- account: {
20897
- marginLeft: "auto",
20898
- color: "var(--dsw-alias-label-tertiary)",
20899
- fontSize: 11,
20900
- overflow: "hidden",
20901
- textOverflow: "ellipsis",
20902
- whiteSpace: "nowrap"
20892
+ close() {
20893
+ setAccountOpen(false);
20903
20894
  },
20904
- frame: {
20905
- flex: 1,
20906
- minHeight: 0,
20907
- width: "100%",
20908
- border: 0,
20909
- background: "#fff"
20895
+ subscribe(listener) {
20896
+ accountListeners.add(listener);
20897
+ return () => {
20898
+ accountListeners.delete(listener);
20899
+ };
20910
20900
  },
20911
- overlay: {
20901
+ getSnapshot: () => accountOpen
20902
+ };
20903
+ function useAccountOpen() {
20904
+ return (0, react.useSyncExternalStore)(EdusAccountDialog.subscribe, EdusAccountDialog.getSnapshot, () => false);
20905
+ }
20906
+ const ui = {
20907
+ backdrop: {
20912
20908
  position: "fixed",
20913
20909
  inset: 0,
20914
- zIndex: 1100,
20910
+ zIndex: 1200,
20915
20911
  display: "grid",
20916
20912
  placeItems: "center",
20917
- padding: 18,
20918
- background: "rgb(9 19 16 / 56%)",
20919
- backdropFilter: "blur(5px)"
20913
+ background: "rgb(0 0 0 / .35)",
20914
+ pointerEvents: "auto"
20920
20915
  },
20921
- dialog: {
20922
- width: "min(460px, 100%)",
20923
- overflow: "hidden",
20924
- border: "1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 70%, white)",
20925
- borderRadius: 16,
20916
+ card: {
20917
+ position: "relative",
20918
+ boxSizing: "border-box",
20919
+ width: "min(380px, calc(100vw - 32px))",
20926
20920
  background: "var(--dsw-alias-bg-base)",
20927
- boxShadow: "0 24px 70px rgb(0 0 0 / 32%)"
20928
- },
20929
- hero: {
20930
- padding: "22px 24px 18px",
20931
- background: "linear-gradient(135deg, #173329 0%, #29553d 100%)",
20932
- color: "#f7fbf8"
20921
+ color: "var(--dsw-alias-label-primary)",
20922
+ padding: "28px 28px 22px",
20923
+ borderRadius: 14,
20924
+ boxShadow: "0 20px 60px rgb(0 0 0 / 25%)"
20933
20925
  },
20934
- eyebrow: {
20935
- margin: 0,
20936
- color: "#b9ddc6",
20937
- fontSize: 11,
20938
- fontWeight: 700,
20939
- letterSpacing: ".12em"
20926
+ close: {
20927
+ position: "absolute",
20928
+ top: 12,
20929
+ right: 12,
20930
+ display: "grid",
20931
+ placeItems: "center",
20932
+ width: 28,
20933
+ height: 28,
20934
+ border: 0,
20935
+ borderRadius: 7,
20936
+ padding: 0,
20937
+ background: "transparent",
20938
+ color: "var(--dsw-alias-label-tertiary)",
20939
+ cursor: "pointer"
20940
20940
  },
20941
20941
  title: {
20942
- margin: "7px 0 0",
20943
- fontSize: 21,
20944
- lineHeight: 1.2,
20945
- letterSpacing: "-.02em"
20942
+ margin: 0,
20943
+ fontSize: 18,
20944
+ fontWeight: 650
20946
20945
  },
20947
- body: { padding: "20px 24px 22px" },
20948
- copy: {
20949
- margin: "0 0 16px",
20946
+ intro: {
20947
+ margin: "6px 0 8px",
20950
20948
  color: "var(--dsw-alias-label-secondary)",
20951
20949
  fontSize: 13,
20952
- lineHeight: 1.6
20950
+ lineHeight: 1.5
20953
20951
  },
20954
- field: {
20955
- boxSizing: "border-box",
20952
+ dialogField: {
20953
+ height: 40,
20954
+ borderRadius: 9,
20955
+ padding: "0 12px"
20956
+ },
20957
+ dialogPrimary: {
20956
20958
  width: "100%",
20957
- border: "1px solid var(--dsw-alias-border-l2)",
20958
- borderRadius: 8,
20959
- padding: "10px 11px",
20960
- color: "var(--dsw-alias-label-primary)",
20961
- background: "var(--dsw-alias-bg-base)",
20962
- font: "inherit",
20963
- fontSize: 13
20959
+ height: 42,
20960
+ borderRadius: 9,
20961
+ fontSize: 14
20964
20962
  },
20965
- option: {
20963
+ dock: {
20964
+ boxSizing: "border-box",
20965
+ width: "calc(100% - 2 * var(--dsh-composer-side-clearance) - 4 * var(--dsh-composer-dock-inset))",
20966
+ margin: "0 auto"
20967
+ },
20968
+ banner: {
20969
+ boxSizing: "border-box",
20966
20970
  display: "flex",
20967
- alignItems: "flex-start",
20971
+ alignItems: "center",
20968
20972
  gap: 10,
20969
- padding: 12,
20970
- border: "1px solid var(--dsw-alias-border-l2)",
20971
- borderRadius: 10,
20972
- cursor: "pointer"
20973
- },
20974
- optionSelected: {
20975
- borderColor: "var(--dsw-alias-brand-primary)",
20976
- background: "color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent)"
20977
- },
20978
- optionTitle: {
20979
- display: "block",
20973
+ width: "100%",
20974
+ maxWidth: "calc(var(--dsh-composer-card-max-width) - 4 * var(--dsh-composer-dock-inset))",
20975
+ height: 36,
20976
+ margin: "0 auto",
20977
+ padding: "4px 5px 4px 12px",
20978
+ borderRadius: "var(--dsw-radius-md)",
20979
+ background: "var(--dsw-alias-bg-base)",
20980
+ boxShadow: "var(--dsw-elevation-panel)",
20980
20981
  color: "var(--dsw-alias-label-primary)",
20981
- fontSize: 13,
20982
- fontWeight: 650
20982
+ fontSize: 13
20983
20983
  },
20984
- optionText: {
20985
- display: "block",
20986
- marginTop: 3,
20987
- color: "var(--dsw-alias-label-secondary)",
20988
- fontSize: 12,
20989
- lineHeight: 1.45
20984
+ bannerText: {
20985
+ flex: 1,
20986
+ minWidth: 0,
20987
+ overflow: "hidden",
20988
+ textOverflow: "ellipsis",
20989
+ whiteSpace: "nowrap"
20990
20990
  },
20991
- error: {
20992
- margin: "12px 0 0",
20993
- color: "var(--dsw-alias-state-error-primary)",
20991
+ primary: {
20992
+ flex: "none",
20993
+ height: 28,
20994
+ border: "none",
20995
+ borderRadius: "var(--dsw-radius-sm, 6px)",
20996
+ padding: "0 12px",
20997
+ background: "var(--dsw-alias-button-primary-fill)",
20998
+ color: "var(--dsw-alias-label-primary-inverted)",
20999
+ font: "inherit",
20994
21000
  fontSize: 12,
20995
- lineHeight: 1.45
20996
- },
20997
- actions: {
21001
+ fontWeight: 650,
21002
+ cursor: "pointer"
21003
+ },
21004
+ footer: {
21005
+ boxSizing: "border-box",
20998
21006
  display: "flex",
20999
- justifyContent: "flex-end",
21000
- gap: 8,
21001
- marginTop: 20
21007
+ alignItems: "center",
21008
+ justifyContent: "center",
21009
+ gap: 6,
21010
+ width: "100%",
21011
+ padding: "8px 10px",
21012
+ border: "none",
21013
+ borderRadius: 8,
21014
+ background: "var(--dsw-alias-button-primary-fill)",
21015
+ color: "var(--dsw-alias-label-primary-inverted)",
21016
+ font: "inherit",
21017
+ fontSize: 13,
21018
+ fontWeight: 650,
21019
+ cursor: "pointer"
21002
21020
  },
21003
- linkBox: {
21021
+ account: {
21022
+ boxSizing: "border-box",
21004
21023
  display: "flex",
21005
21024
  alignItems: "center",
21006
21025
  gap: 8,
21007
- padding: 10,
21008
- border: "1px solid var(--dsw-alias-border-l2)",
21009
- borderRadius: 9,
21010
- background: "var(--dsw-alias-bg-layer-1)"
21026
+ width: "100%",
21027
+ padding: "6px 8px",
21028
+ border: "none",
21029
+ borderRadius: 8,
21030
+ background: "var(--dsw-alias-bg-layer-1, rgb(0 0 0 / 4%))",
21031
+ color: "var(--dsw-alias-label-primary)",
21032
+ font: "inherit",
21033
+ fontSize: 13,
21034
+ textAlign: "left",
21035
+ cursor: "pointer",
21036
+ transition: "background 120ms"
21011
21037
  },
21012
- link: {
21013
- minWidth: 0,
21038
+ accountRail: {
21039
+ boxSizing: "border-box",
21040
+ display: "grid",
21041
+ placeItems: "center",
21042
+ width: 36,
21043
+ height: 36,
21044
+ marginBottom: 6,
21045
+ border: "none",
21046
+ borderRadius: 8,
21047
+ padding: 0,
21048
+ background: "var(--dsw-alias-bg-layer-1, rgb(0 0 0 / 4%))",
21049
+ cursor: "pointer",
21050
+ transition: "background 120ms"
21051
+ },
21052
+ accountHover: { background: "var(--dsw-alias-interactive-bg-hover, rgb(0 0 0 / 8%))" },
21053
+ chevron: {
21054
+ flex: "none",
21055
+ display: "grid",
21056
+ placeItems: "center",
21057
+ color: "var(--dsw-alias-label-tertiary)"
21058
+ },
21059
+ avatar: {
21060
+ flex: "none",
21061
+ display: "grid",
21062
+ placeItems: "center",
21063
+ width: 26,
21064
+ height: 26,
21065
+ borderRadius: "50%",
21066
+ background: "var(--dsw-alias-button-primary-fill)",
21067
+ color: "var(--dsw-alias-label-primary-inverted)"
21068
+ },
21069
+ accountName: {
21014
21070
  flex: 1,
21071
+ minWidth: 0,
21015
21072
  overflow: "hidden",
21016
- color: "var(--dsw-alias-brand-primary)",
21017
- fontSize: 12,
21018
21073
  textOverflow: "ellipsis",
21019
21074
  whiteSpace: "nowrap"
21020
21075
  },
21021
- success: {
21076
+ badge: {
21077
+ flex: "none",
21078
+ padding: "1px 6px",
21079
+ borderRadius: 999,
21080
+ background: "var(--dsw-alias-bg-l2, rgb(0 0 0 / 6%))",
21081
+ color: "var(--dsw-alias-brand-primary)",
21082
+ fontSize: 11,
21083
+ fontWeight: 600
21084
+ },
21085
+ profile: {
21086
+ display: "flex",
21087
+ alignItems: "center",
21088
+ gap: 12,
21089
+ marginBottom: 18
21090
+ },
21091
+ profileAvatar: {
21092
+ flex: "none",
21022
21093
  display: "grid",
21023
21094
  placeItems: "center",
21024
- width: 32,
21025
- height: 32,
21095
+ width: 44,
21096
+ height: 44,
21026
21097
  borderRadius: "50%",
21027
- background: "#e3f4e8",
21028
- color: "#237344",
21029
- fontSize: 18,
21030
- fontWeight: 700
21098
+ background: "var(--dsw-alias-button-primary-fill)",
21099
+ color: "var(--dsw-alias-label-primary-inverted)"
21031
21100
  },
21032
- memberCard: {
21101
+ profileName: {
21102
+ margin: 0,
21103
+ fontSize: 16,
21104
+ fontWeight: 650,
21105
+ overflow: "hidden",
21106
+ textOverflow: "ellipsis",
21107
+ whiteSpace: "nowrap"
21108
+ },
21109
+ profileSub: {
21110
+ margin: "2px 0 0",
21111
+ color: "var(--dsw-alias-label-tertiary)",
21112
+ fontSize: 12
21113
+ },
21114
+ section: {
21115
+ margin: "0 0 14px",
21116
+ padding: "12px 14px",
21117
+ borderRadius: 10,
21118
+ border: "1px solid var(--dsw-alias-border-l2)"
21119
+ },
21120
+ sectionTitle: {
21121
+ margin: "0 0 8px",
21122
+ color: "var(--dsw-alias-label-secondary)",
21123
+ fontSize: 12,
21124
+ fontWeight: 600
21125
+ },
21126
+ row: {
21127
+ display: "flex",
21128
+ justifyContent: "space-between",
21129
+ gap: 12,
21130
+ padding: "3px 0",
21131
+ fontSize: 13
21132
+ },
21133
+ rowLabel: { color: "var(--dsw-alias-label-tertiary)" },
21134
+ meter: {
21135
+ height: 6,
21136
+ margin: "6px 0 4px",
21137
+ borderRadius: 999,
21138
+ background: "var(--dsw-alias-border-l2)",
21139
+ overflow: "hidden"
21140
+ },
21141
+ status: {
21142
+ margin: "0 0 12px",
21143
+ color: "var(--dsw-alias-label-tertiary)",
21144
+ fontSize: 12
21145
+ },
21146
+ errorText: {
21147
+ margin: "0 0 12px",
21148
+ color: "var(--dsw-alias-state-error-primary)",
21149
+ fontSize: 12
21150
+ },
21151
+ support: {
21033
21152
  display: "flex",
21034
- gap: 14,
21035
21153
  alignItems: "center",
21036
- margin: "14px 0 0",
21037
- padding: 13,
21038
- border: "1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, var(--dsw-alias-border-l2))",
21039
- borderRadius: 11,
21040
- background: "var(--dsw-alias-bg-layer-1)"
21154
+ gap: 12,
21155
+ margin: "0",
21156
+ padding: "10px 12px",
21157
+ borderRadius: 10,
21158
+ background: "var(--dsw-alias-bg-layer-1, rgb(0 0 0 / 4%))"
21041
21159
  },
21042
- memberQr: {
21160
+ supportQr: {
21043
21161
  flex: "none",
21044
- width: 96,
21045
- height: 96,
21046
- borderRadius: 8,
21162
+ width: 84,
21163
+ height: 84,
21164
+ borderRadius: 6,
21047
21165
  background: "#fff",
21048
21166
  objectFit: "contain"
21049
21167
  },
21050
- memberTitle: {
21168
+ supportTitle: {
21051
21169
  margin: 0,
21052
- color: "var(--dsw-alias-label-primary)",
21053
21170
  fontSize: 13,
21054
21171
  fontWeight: 650
21055
21172
  },
21056
- memberText: {
21057
- margin: "5px 0 0",
21173
+ supportText: {
21174
+ margin: "4px 0 0",
21058
21175
  color: "var(--dsw-alias-label-secondary)",
21059
21176
  fontSize: 12,
21060
21177
  lineHeight: 1.55
21061
21178
  },
21062
- accountCard: {
21063
- margin: "0 0 15px",
21064
- padding: "9px 11px",
21065
- borderLeft: "3px solid var(--dsw-alias-brand-primary)",
21066
- borderRadius: 6,
21067
- background: "var(--dsw-alias-bg-layer-1)",
21068
- color: "var(--dsw-alias-label-secondary)",
21069
- fontSize: 12,
21070
- lineHeight: 1.5
21179
+ dialogActions: {
21180
+ display: "flex",
21181
+ justifyContent: "flex-end",
21182
+ gap: 8,
21183
+ marginTop: 4
21184
+ },
21185
+ quiet: {
21186
+ height: 32,
21187
+ border: "1px solid var(--dsw-alias-border-l2)",
21188
+ borderRadius: 8,
21189
+ padding: "0 14px",
21190
+ background: "transparent",
21191
+ color: "var(--dsw-alias-label-primary)",
21192
+ font: "inherit",
21193
+ fontSize: 13,
21194
+ cursor: "pointer"
21195
+ },
21196
+ danger: {
21197
+ height: 32,
21198
+ border: "none",
21199
+ borderRadius: 8,
21200
+ padding: "0 14px",
21201
+ background: "var(--dsw-alias-state-error-primary)",
21202
+ color: "#fff",
21203
+ font: "inherit",
21204
+ fontSize: 13,
21205
+ fontWeight: 600,
21206
+ cursor: "pointer"
21207
+ },
21208
+ footerRail: {
21209
+ boxSizing: "border-box",
21210
+ display: "grid",
21211
+ placeItems: "center",
21212
+ width: 36,
21213
+ height: 36,
21214
+ marginBottom: 6,
21215
+ border: "none",
21216
+ borderRadius: 8,
21217
+ background: "var(--dsw-alias-button-primary-fill)",
21218
+ color: "var(--dsw-alias-label-primary-inverted)",
21219
+ cursor: "pointer"
21071
21220
  }
21072
21221
  };
21073
- /** 普通报错直接展示文案;会员相关的报错额外给出客服二维码。 */
21074
- function PublishError({ message }) {
21075
- if (message === "") return null;
21076
- if (!message.includes(MEMBERSHIP_HINT)) return (0, react_jsx_runtime.jsx)("p", {
21077
- style: styles$1.error,
21078
- children: message
21222
+ function UserIcon() {
21223
+ return (0, react_jsx_runtime.jsxs)("svg", {
21224
+ width: 16,
21225
+ height: 16,
21226
+ viewBox: "0 0 16 16",
21227
+ fill: "none",
21228
+ stroke: "currentColor",
21229
+ strokeWidth: 1.5,
21230
+ strokeLinecap: "round",
21231
+ "aria-hidden": true,
21232
+ children: [(0, react_jsx_runtime.jsx)("circle", {
21233
+ cx: "8",
21234
+ cy: "5.5",
21235
+ r: "2.75"
21236
+ }), (0, react_jsx_runtime.jsx)("path", { d: "M2.75 13.5c.6-2.4 2.7-3.9 5.25-3.9s4.65 1.5 5.25 3.9" })]
21079
21237
  });
21080
- return (0, react_jsx_runtime.jsxs)("div", {
21081
- style: styles$1.memberCard,
21082
- children: [(0, react_jsx_runtime.jsx)("img", {
21083
- alt: "客服微信二维码",
21084
- src: SERVICE_QR,
21085
- style: styles$1.memberQr
21086
- }), (0, react_jsx_runtime.jsxs)("div", {
21087
- style: { minWidth: 0 },
21088
- children: [(0, react_jsx_runtime.jsx)("p", {
21089
- style: styles$1.memberTitle,
21090
- children: message
21091
- }), (0, react_jsx_runtime.jsx)("p", {
21092
- style: styles$1.memberText,
21093
- children: "该功能需要开通会员后使用。请用微信扫描左侧二维码添加客服咨询开通。"
21094
- })]
21095
- })]
21238
+ }
21239
+ function ChevronIcon() {
21240
+ return (0, react_jsx_runtime.jsx)("svg", {
21241
+ width: 14,
21242
+ height: 14,
21243
+ viewBox: "0 0 16 16",
21244
+ fill: "none",
21245
+ stroke: "currentColor",
21246
+ strokeWidth: 1.6,
21247
+ strokeLinecap: "round",
21248
+ strokeLinejoin: "round",
21249
+ "aria-hidden": true,
21250
+ children: (0, react_jsx_runtime.jsx)("path", { d: "M6 4l4 4-4 4" })
21096
21251
  });
21097
21252
  }
21098
- /** An extension-priority document body: native right-preview ergonomics plus a complete edus publishing flow. */
21099
- function HtmlPublishPreview({ content, resourceAddress, remote }) {
21253
+ function CloseIcon() {
21254
+ return (0, react_jsx_runtime.jsx)("svg", {
21255
+ width: 16,
21256
+ height: 16,
21257
+ viewBox: "0 0 16 16",
21258
+ fill: "none",
21259
+ stroke: "currentColor",
21260
+ strokeWidth: 1.6,
21261
+ strokeLinecap: "round",
21262
+ "aria-hidden": true,
21263
+ children: (0, react_jsx_runtime.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" })
21264
+ });
21265
+ }
21266
+ /** 全局登录弹窗:已登录时不渲染。对话、我的作品等所有需要登录的入口都打开它。 */
21267
+ function LoginDialogOverlay({ remote }) {
21268
+ const open = useDialogOpen();
21100
21269
  const account = useEdusLogin();
21101
- const [stage, setStage] = (0, react.useState)("closed");
21102
- const [destination, setDestination] = (0, react.useState)("new");
21103
- const [title, setTitle] = (0, react.useState)("");
21270
+ const visible = open && account === null;
21104
21271
  const [busy, setBusy] = (0, react.useState)(false);
21105
- const [error, setError] = (0, react.useState)("");
21106
- const [shareUrl, setShareUrl] = (0, react.useState)(null);
21107
- const [copied, setCopied] = (0, react.useState)(false);
21108
- const ref = (0, react.useMemo)(() => fileRef(resourceAddress), [resourceAddress]);
21109
- const source = (0, react.useMemo)(() => {
21110
- if (content.kind === "bytes") return new TextDecoder().decode(content.data);
21111
- return content.kind === "text" ? content.text : "";
21112
- }, [content]);
21113
- const marker = (0, react.useMemo)(() => extractMarker(source), [source]);
21114
- const currentAvailable = marker !== null && marker.owner === account?.phone;
21115
- const open = async () => {
21116
- setError("");
21117
- setShareUrl(null);
21118
- setCopied(false);
21119
- setTitle(ref === null ? "" : basename(ref.path).replace(/\.html?$/i, ""));
21120
- const stored = EdusAuthStore.getSnapshot();
21121
- if (stored === null) {
21122
- setStage("login");
21123
- return;
21124
- }
21125
- setBusy(true);
21126
- setStage("checking");
21127
- try {
21128
- const refreshed = await remote.loginInfo(stored.token);
21129
- EdusAuthStore.set(refreshed);
21130
- setDestination(marker !== null && marker.owner === refreshed.phone ? "current" : "new");
21131
- setStage("destination");
21132
- } catch {
21133
- EdusAuthStore.logout();
21134
- setError("登录状态已失效,请重新登录。");
21135
- setStage("login");
21136
- } finally {
21137
- setBusy(false);
21138
- }
21139
- };
21140
- const close = () => {
21141
- if (!busy) setStage("closed");
21142
- };
21143
- const afterLogin = (0, react.useCallback)((result) => {
21144
- EdusAuthStore.set(result);
21145
- setError("");
21146
- setBusy(false);
21147
- setDestination(marker !== null && marker.owner === result.phone ? "current" : "new");
21148
- setStage("destination");
21149
- }, [marker]);
21150
- const submitPublish = (0, react.useCallback)(async () => {
21151
- if (ref === null || account === null) return;
21152
- const newWork = destination === "new";
21153
- const publishTitle = title.trim();
21154
- if (newWork && publishTitle === "") {
21155
- setError("请为新作品填写标题。");
21156
- return;
21157
- }
21158
- setBusy(true);
21159
- setError("");
21160
- try {
21161
- const result = await remote.publishFile(account.token, ref.sessionId, ref.path, account.phone, publishTitle, newWork);
21162
- if (result.shareUrl === null) throw new Error("发布完成,但未返回分享链接。");
21163
- setShareUrl(result.shareUrl);
21164
- setStage("success");
21165
- } catch (reason) {
21166
- setError(reason instanceof Error ? reason.message : String(reason));
21167
- } finally {
21168
- setBusy(false);
21169
- }
21170
- }, [
21171
- account,
21172
- destination,
21173
- ref,
21174
- remote,
21175
- title
21176
- ]);
21177
- const copy = async () => {
21178
- if (shareUrl === null) return;
21179
- try {
21180
- await navigator.clipboard.writeText(shareUrl);
21181
- setCopied(true);
21182
- } catch {
21183
- setError("复制失败,请手动复制链接。");
21184
- }
21272
+ const close = (0, react.useCallback)(() => {
21273
+ if (!busy) EdusLoginDialog.close();
21274
+ }, [busy]);
21275
+ (0, react.useEffect)(() => {
21276
+ if (!visible) return;
21277
+ const onKey = (event) => {
21278
+ if (event.key === "Escape") close();
21279
+ };
21280
+ window.addEventListener("keydown", onKey);
21281
+ return () => {
21282
+ window.removeEventListener("keydown", onKey);
21283
+ };
21284
+ }, [visible, close]);
21285
+ if (!visible) return null;
21286
+ return (0, react_jsx_runtime.jsx)("div", {
21287
+ role: "dialog",
21288
+ "aria-modal": "true",
21289
+ "aria-labelledby": "edus-login-dialog-title",
21290
+ style: ui.backdrop,
21291
+ children: (0, react_jsx_runtime.jsxs)("div", {
21292
+ style: ui.card,
21293
+ children: [(0, react_jsx_runtime.jsx)("button", {
21294
+ type: "button",
21295
+ "aria-label": "关闭",
21296
+ title: "关闭",
21297
+ style: ui.close,
21298
+ disabled: busy,
21299
+ onClick: close,
21300
+ children: (0, react_jsx_runtime.jsx)(CloseIcon, {})
21301
+ }), (0, react_jsx_runtime.jsx)(EdusLoginPanel, {
21302
+ remote,
21303
+ intro: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("h2", {
21304
+ id: "edus-login-dialog-title",
21305
+ style: ui.title,
21306
+ children: "登录课成账号"
21307
+ }), (0, react_jsx_runtime.jsx)("p", {
21308
+ style: ui.intro,
21309
+ children: "登录后即可开始 AI 对话,查看和发布你的作品。"
21310
+ })] }),
21311
+ notice: EdusLoginDialog.notice(),
21312
+ onBusyChange: setBusy,
21313
+ onSuccess: (result) => {
21314
+ EdusAuthStore.set(result);
21315
+ EdusLoginDialog.close();
21316
+ },
21317
+ theme: {
21318
+ field: ui.dialogField,
21319
+ primary: ui.dialogPrimary
21320
+ }
21321
+ })]
21322
+ })
21323
+ });
21324
+ }
21325
+ /** 输入框上方的未登录横幅。 */
21326
+ function LoginDockBanner() {
21327
+ if (useEdusLogin() !== null) return null;
21328
+ return (0, react_jsx_runtime.jsx)("div", {
21329
+ style: ui.dock,
21330
+ children: (0, react_jsx_runtime.jsxs)("div", {
21331
+ style: ui.banner,
21332
+ children: [(0, react_jsx_runtime.jsx)("span", {
21333
+ style: ui.bannerText,
21334
+ children: "登录课成账号后即可开始对话"
21335
+ }), (0, react_jsx_runtime.jsx)("button", {
21336
+ type: "button",
21337
+ style: ui.primary,
21338
+ onClick: EdusLoginDialog.open,
21339
+ children: "登录"
21340
+ })]
21341
+ })
21342
+ });
21343
+ }
21344
+ /** 侧边栏底部的账号入口:未登录为登录按钮,已登录显示当前账号;收起为窄栏时只显示图标。 */
21345
+ function LoginFooterAction({ wide }) {
21346
+ const account = useEdusLogin();
21347
+ const [hover, setHover] = (0, react.useState)(false);
21348
+ if (account === null) return wide ? (0, react_jsx_runtime.jsxs)("button", {
21349
+ type: "button",
21350
+ style: ui.footer,
21351
+ onClick: EdusLoginDialog.open,
21352
+ children: [(0, react_jsx_runtime.jsx)(UserIcon, {}), "登录课成账号"]
21353
+ }) : (0, react_jsx_runtime.jsx)("button", {
21354
+ type: "button",
21355
+ title: "登录课成账号",
21356
+ "aria-label": "登录课成账号",
21357
+ style: ui.footerRail,
21358
+ onClick: EdusLoginDialog.open,
21359
+ children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21360
+ });
21361
+ const name = displayName(account);
21362
+ const hoverProps = {
21363
+ onMouseEnter: () => setHover(true),
21364
+ onMouseLeave: () => setHover(false)
21185
21365
  };
21186
- if (content.kind !== "bytes") return null;
21187
- return (0, react_jsx_runtime.jsxs)("div", {
21188
- style: styles$1.root,
21189
- "data-edus-html-preview": true,
21366
+ return wide ? (0, react_jsx_runtime.jsxs)("button", {
21367
+ type: "button",
21368
+ title: "账号信息",
21369
+ style: {
21370
+ ...ui.account,
21371
+ ...hover ? ui.accountHover : null
21372
+ },
21373
+ onClick: EdusAccountDialog.open,
21374
+ ...hoverProps,
21190
21375
  children: [
21191
- (0, react_jsx_runtime.jsxs)("div", {
21192
- style: styles$1.bar,
21193
- children: [(0, react_jsx_runtime.jsx)("button", {
21194
- style: styles$1.primary,
21195
- onClick: () => void open(),
21196
- children: "发布网页"
21197
- }), (0, react_jsx_runtime.jsx)("span", {
21198
- style: styles$1.account,
21199
- children: account === null ? "尚未登录" : `${account.title || "当前账号"} · ${account.phone}`
21200
- })]
21376
+ (0, react_jsx_runtime.jsx)("span", {
21377
+ style: ui.avatar,
21378
+ children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21201
21379
  }),
21202
- (0, react_jsx_runtime.jsx)("iframe", {
21203
- title: "HTML 网页预览",
21204
- sandbox: "allow-scripts",
21205
- srcDoc: source,
21206
- style: styles$1.frame
21380
+ (0, react_jsx_runtime.jsx)("span", {
21381
+ style: ui.accountName,
21382
+ children: name
21207
21383
  }),
21208
- stage !== "closed" && (0, react_jsx_runtime.jsx)("div", {
21209
- style: styles$1.overlay,
21210
- role: "presentation",
21211
- onMouseDown: close,
21212
- children: (0, react_jsx_runtime.jsxs)("section", {
21213
- style: styles$1.dialog,
21214
- role: "dialog",
21215
- "aria-modal": "true",
21216
- "aria-labelledby": "edus-publish-title",
21217
- onMouseDown: (event) => event.stopPropagation(),
21218
- children: [(0, react_jsx_runtime.jsxs)("header", {
21219
- style: styles$1.hero,
21220
- children: [(0, react_jsx_runtime.jsx)("p", {
21221
- style: styles$1.eyebrow,
21222
- children: "COURSE · PUBLISH"
21223
- }), (0, react_jsx_runtime.jsx)("h2", {
21224
- id: "edus-publish-title",
21225
- style: styles$1.title,
21226
- children: stage === "checking" ? "正在确认账号" : stage === "login" ? "先登录,再发布作品" : stage === "success" ? "作品已成功发布" : "选择发布目标"
21227
- })]
21228
- }), (0, react_jsx_runtime.jsxs)("div", {
21229
- style: styles$1.body,
21230
- children: [
21231
- stage === "checking" && (0, react_jsx_runtime.jsx)("p", {
21232
- style: styles$1.copy,
21233
- children: "正在通过课成账号服务确认当前登录状态…"
21234
- }),
21235
- stage === "login" && (0, react_jsx_runtime.jsx)(EdusLoginPanel, {
21236
- remote,
21237
- intro: (0, react_jsx_runtime.jsx)("p", {
21238
- style: styles$1.copy,
21239
- children: "登录后即可选择更新已关联作品,或创建一个新的作品发布。"
21240
- }),
21241
- notice: error,
21242
- submitLabel: "继续",
21243
- onCancel: close,
21244
- onBusyChange: setBusy,
21245
- onSuccess: afterLogin,
21246
- theme: {
21247
- field: styles$1.field,
21248
- button: styles$1.button,
21249
- primary: styles$1.primary,
21250
- error: styles$1.error
21251
- }
21252
- }),
21253
- stage === "destination" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
21254
- account !== null && (0, react_jsx_runtime.jsxs)("p", {
21255
- style: styles$1.accountCard,
21256
- children: [
21257
- "当前账号:",
21258
- (0, react_jsx_runtime.jsx)("strong", { children: account.title || account.phone }),
21259
- account.title !== "" ? `(${account.phone})` : ""
21260
- ]
21261
- }),
21262
- (0, react_jsx_runtime.jsx)("p", {
21263
- style: styles$1.copy,
21264
- children: "请选择本次内容的归属。发布会把当前 HTML 文件的最新内容写入所选作品。"
21265
- }),
21266
- (0, react_jsx_runtime.jsxs)("div", {
21267
- style: {
21268
- display: "grid",
21269
- gap: 9
21270
- },
21271
- children: [currentAvailable && (0, react_jsx_runtime.jsxs)("label", {
21272
- style: {
21273
- ...styles$1.option,
21274
- ...destination === "current" ? styles$1.optionSelected : {}
21275
- },
21276
- children: [(0, react_jsx_runtime.jsx)("input", {
21277
- name: "destination",
21278
- type: "radio",
21279
- checked: destination === "current",
21280
- onChange: () => setDestination("current")
21281
- }), (0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
21282
- style: styles$1.optionTitle,
21283
- children: "更新已关联作品"
21284
- }), (0, react_jsx_runtime.jsxs)("span", {
21285
- style: styles$1.optionText,
21286
- children: [
21287
- "继续发布到当前文件绑定的作品(",
21288
- marker.id.slice(0, 8),
21289
- "…)。"
21290
- ]
21291
- })] })]
21292
- }), (0, react_jsx_runtime.jsxs)("label", {
21293
- style: {
21294
- ...styles$1.option,
21295
- ...destination === "new" ? styles$1.optionSelected : {}
21296
- },
21297
- children: [(0, react_jsx_runtime.jsx)("input", {
21298
- name: "destination",
21299
- type: "radio",
21300
- checked: destination === "new",
21301
- onChange: () => setDestination("new")
21302
- }), (0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
21303
- style: styles$1.optionTitle,
21304
- children: "创建新作品并发布"
21305
- }), (0, react_jsx_runtime.jsx)("span", {
21306
- style: styles$1.optionText,
21307
- children: "创建新的分享页,并把当前文件重新关联到该作品。"
21308
- })] })]
21309
- })]
21310
- }),
21311
- destination === "new" && (0, react_jsx_runtime.jsxs)("div", {
21312
- style: { marginTop: 14 },
21313
- children: [(0, react_jsx_runtime.jsx)("label", {
21314
- style: {
21315
- display: "block",
21316
- marginBottom: 6,
21317
- color: "var(--dsw-alias-label-secondary)",
21318
- fontSize: 12
21319
- },
21320
- children: "作品标题"
21321
- }), (0, react_jsx_runtime.jsx)("input", {
21322
- style: styles$1.field,
21323
- autoFocus: true,
21324
- value: title,
21325
- placeholder: "例如:春季招生落地页",
21326
- onChange: (event) => setTitle(event.target.value)
21327
- })]
21328
- }),
21329
- (0, react_jsx_runtime.jsx)(PublishError, { message: error }),
21330
- (0, react_jsx_runtime.jsxs)("div", {
21331
- style: styles$1.actions,
21332
- children: [(0, react_jsx_runtime.jsx)("button", {
21333
- style: styles$1.button,
21334
- disabled: busy,
21335
- onClick: close,
21336
- children: "取消"
21337
- }), (0, react_jsx_runtime.jsx)("button", {
21338
- style: styles$1.primary,
21339
- disabled: busy,
21340
- onClick: () => void submitPublish(),
21341
- children: busy ? "发布中…" : destination === "new" ? "创建并发布" : "更新并发布"
21342
- })]
21343
- })
21344
- ] }),
21345
- stage === "success" && shareUrl !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
21346
- (0, react_jsx_runtime.jsxs)("div", {
21347
- style: {
21348
- display: "flex",
21349
- gap: 11,
21350
- alignItems: "center",
21351
- marginBottom: 14
21352
- },
21353
- children: [(0, react_jsx_runtime.jsx)("span", {
21354
- style: styles$1.success,
21355
- children: "✓"
21356
- }), (0, react_jsx_runtime.jsx)("p", {
21357
- style: {
21358
- ...styles$1.copy,
21359
- margin: 0
21360
- },
21361
- children: "作品已可通过分享链接访问。链接中的内容为本次发布版本。"
21362
- })]
21363
- }),
21364
- (0, react_jsx_runtime.jsxs)("div", {
21365
- style: styles$1.linkBox,
21366
- children: [(0, react_jsx_runtime.jsx)("a", {
21367
- style: styles$1.link,
21368
- href: shareUrl,
21369
- target: "_blank",
21370
- rel: "noreferrer",
21371
- title: shareUrl,
21372
- children: shareUrl
21373
- }), (0, react_jsx_runtime.jsx)("button", {
21374
- style: styles$1.button,
21375
- onClick: () => void copy(),
21376
- children: copied ? "已复制" : "复制"
21377
- })]
21378
- }),
21379
- error !== "" && (0, react_jsx_runtime.jsx)("p", {
21380
- style: styles$1.error,
21381
- children: error
21382
- }),
21383
- (0, react_jsx_runtime.jsx)("div", {
21384
- style: styles$1.actions,
21385
- children: (0, react_jsx_runtime.jsx)("button", {
21386
- style: styles$1.primary,
21387
- onClick: close,
21388
- children: "完成"
21389
- })
21390
- })
21391
- ] })
21392
- ]
21384
+ account.isVip && account.vipLevelName && (0, react_jsx_runtime.jsx)("span", {
21385
+ style: ui.badge,
21386
+ children: account.vipLevelName
21387
+ }),
21388
+ (0, react_jsx_runtime.jsx)("span", {
21389
+ style: ui.chevron,
21390
+ children: (0, react_jsx_runtime.jsx)(ChevronIcon, {})
21391
+ })
21392
+ ]
21393
+ }) : (0, react_jsx_runtime.jsx)("button", {
21394
+ type: "button",
21395
+ title: `${name} · 账号信息`,
21396
+ "aria-label": "账号信息",
21397
+ style: {
21398
+ ...ui.accountRail,
21399
+ ...hover ? ui.accountHover : null
21400
+ },
21401
+ onClick: EdusAccountDialog.open,
21402
+ ...hoverProps,
21403
+ children: (0, react_jsx_runtime.jsx)("span", {
21404
+ style: ui.avatar,
21405
+ children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21406
+ })
21407
+ });
21408
+ }
21409
+ /** 客服(技术支持)微信二维码:开通会员或遇到问题时扫码添加。 */
21410
+ const SUPPORT_QR = "https://cdn.juzhenai.cn/assets/jishu-weixin-qrcode.jpg";
21411
+ function maskPhone(phone) {
21412
+ return /^\d{11}$/.test(phone) ? `${phone.slice(0, 3)}****${phone.slice(7)}` : phone;
21413
+ }
21414
+ function displayName(account) {
21415
+ return account.title || maskPhone(account.phone);
21416
+ }
21417
+ function formatTime(value) {
21418
+ if (!value) return "—";
21419
+ const date = new Date(value);
21420
+ if (Number.isNaN(date.getTime())) return value;
21421
+ const pad = (n) => String(n).padStart(2, "0");
21422
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
21423
+ }
21424
+ /** 取整 */
21425
+ function formatPoints(value) {
21426
+ const amount = Number(value);
21427
+ return value === void 0 || value === "" || Number.isNaN(amount) ? "—" : Math.round(amount).toLocaleString("zh-CN");
21428
+ }
21429
+ function Row({ label, value }) {
21430
+ return (0, react_jsx_runtime.jsxs)("div", {
21431
+ style: ui.row,
21432
+ children: [(0, react_jsx_runtime.jsx)("span", {
21433
+ style: ui.rowLabel,
21434
+ children: label
21435
+ }), (0, react_jsx_runtime.jsx)("span", { children: value })]
21436
+ });
21437
+ }
21438
+ /** 账号信息弹窗:打开时用 token 拉一次最新信息,展示会员与模型用量,并提供退出登录。 */
21439
+ function AccountDialogOverlay({ remote }) {
21440
+ const open = useAccountOpen();
21441
+ const account = useEdusLogin();
21442
+ const visible = open && account !== null;
21443
+ const [refreshing, setRefreshing] = (0, react.useState)(false);
21444
+ const [error, setError] = (0, react.useState)("");
21445
+ const [confirmLogout, setConfirmLogout] = (0, react.useState)(false);
21446
+ const close = (0, react.useCallback)(() => {
21447
+ EdusAccountDialog.close();
21448
+ }, []);
21449
+ (0, react.useEffect)(() => {
21450
+ if (open && account === null) EdusAccountDialog.close();
21451
+ }, [open, account]);
21452
+ const token = account?.token;
21453
+ (0, react.useEffect)(() => {
21454
+ if (!open || token === void 0) return;
21455
+ let cancelled = false;
21456
+ setError("");
21457
+ setConfirmLogout(false);
21458
+ setRefreshing(true);
21459
+ remote.loginInfo(token).then((refreshed) => {
21460
+ if (!cancelled) EdusAuthStore.set(refreshed);
21461
+ }, (failure) => {
21462
+ if (!cancelled) setError(`刷新账号信息失败:${failure instanceof Error ? failure.message : String(failure)}`);
21463
+ }).finally(() => {
21464
+ if (!cancelled) setRefreshing(false);
21465
+ });
21466
+ return () => {
21467
+ cancelled = true;
21468
+ };
21469
+ }, [
21470
+ open,
21471
+ token,
21472
+ remote
21473
+ ]);
21474
+ (0, react.useEffect)(() => {
21475
+ if (!visible) return;
21476
+ const onKey = (event) => {
21477
+ if (event.key === "Escape") close();
21478
+ };
21479
+ window.addEventListener("keydown", onKey);
21480
+ return () => {
21481
+ window.removeEventListener("keydown", onKey);
21482
+ };
21483
+ }, [visible, close]);
21484
+ if (!visible) return null;
21485
+ const limit = account.vipMonthLimit ?? 0;
21486
+ const monthCost = Number(account.llmMonthCost);
21487
+ const ratio = limit > 0 && !Number.isNaN(monthCost) ? Math.min(1, monthCost / limit) : 0;
21488
+ return (0, react_jsx_runtime.jsx)("div", {
21489
+ role: "dialog",
21490
+ "aria-modal": "true",
21491
+ "aria-labelledby": "edus-account-dialog-title",
21492
+ style: ui.backdrop,
21493
+ onMouseDown: (event) => {
21494
+ if (event.target === event.currentTarget) close();
21495
+ },
21496
+ children: (0, react_jsx_runtime.jsxs)("div", {
21497
+ style: ui.card,
21498
+ children: [
21499
+ (0, react_jsx_runtime.jsx)("button", {
21500
+ type: "button",
21501
+ "aria-label": "关闭",
21502
+ title: "关闭",
21503
+ style: ui.close,
21504
+ onClick: close,
21505
+ children: (0, react_jsx_runtime.jsx)(CloseIcon, {})
21506
+ }),
21507
+ (0, react_jsx_runtime.jsxs)("div", {
21508
+ style: ui.profile,
21509
+ children: [(0, react_jsx_runtime.jsx)("span", {
21510
+ style: ui.profileAvatar,
21511
+ children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21512
+ }), (0, react_jsx_runtime.jsxs)("div", {
21513
+ style: { minWidth: 0 },
21514
+ children: [(0, react_jsx_runtime.jsx)("h2", {
21515
+ id: "edus-account-dialog-title",
21516
+ style: ui.profileName,
21517
+ children: displayName(account)
21518
+ }), (0, react_jsx_runtime.jsxs)("p", {
21519
+ style: ui.profileSub,
21520
+ children: [maskPhone(account.phone), account.isAdmin ? " · 管理员" : ""]
21521
+ })]
21522
+ })]
21523
+ }),
21524
+ refreshing && (0, react_jsx_runtime.jsx)("p", {
21525
+ style: ui.status,
21526
+ role: "status",
21527
+ children: "正在刷新账号信息…"
21528
+ }),
21529
+ error !== "" && (0, react_jsx_runtime.jsx)("p", {
21530
+ style: ui.errorText,
21531
+ children: error
21532
+ }),
21533
+ (0, react_jsx_runtime.jsxs)("section", {
21534
+ style: ui.section,
21535
+ children: [
21536
+ (0, react_jsx_runtime.jsx)("h3", {
21537
+ style: ui.sectionTitle,
21538
+ children: "会员"
21539
+ }),
21540
+ (0, react_jsx_runtime.jsx)(Row, {
21541
+ label: "当前版本",
21542
+ value: account.isVip ? account.vipLevelName || `VIP ${account.vipLevel}` : "未开通"
21543
+ }),
21544
+ account.isVip && (0, react_jsx_runtime.jsx)(Row, {
21545
+ label: "到期时间",
21546
+ value: formatTime(account.vipEndTime)
21547
+ }),
21548
+ account.llmStatus !== void 0 && account.llmStatus !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(Row, {
21549
+ label: "本月已用",
21550
+ value: limit > 0 ? `${formatPoints(account.llmMonthCost)} / ${formatPoints(limit)} 积分` : `${formatPoints(account.llmMonthCost)} 积分`
21551
+ }), limit > 0 && (0, react_jsx_runtime.jsx)("div", {
21552
+ style: ui.meter,
21553
+ "aria-hidden": true,
21554
+ children: (0, react_jsx_runtime.jsx)("div", { style: {
21555
+ width: `${ratio * 100}%`,
21556
+ height: "100%",
21557
+ background: ratio >= .9 ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-brand-primary)"
21558
+ } })
21559
+ })] }),
21560
+ account.isVip && limit === 0 && (0, react_jsx_runtime.jsx)(Row, {
21561
+ label: "每月额度",
21562
+ value: "不限"
21563
+ })
21564
+ ]
21565
+ }),
21566
+ (0, react_jsx_runtime.jsxs)("div", {
21567
+ style: ui.support,
21568
+ children: [(0, react_jsx_runtime.jsx)("img", {
21569
+ alt: "客服微信二维码",
21570
+ src: SUPPORT_QR,
21571
+ style: ui.supportQr
21572
+ }), (0, react_jsx_runtime.jsxs)("div", {
21573
+ style: { minWidth: 0 },
21574
+ children: [(0, react_jsx_runtime.jsx)("p", {
21575
+ style: ui.supportTitle,
21576
+ children: "联系客服"
21577
+ }), (0, react_jsx_runtime.jsx)("p", {
21578
+ style: ui.supportText,
21579
+ children: "需要开通会员或遇到问题,请用微信扫码添加客服。"
21580
+ })]
21393
21581
  })]
21582
+ }),
21583
+ (0, react_jsx_runtime.jsx)("div", {
21584
+ style: ui.dialogActions,
21585
+ children: confirmLogout ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
21586
+ (0, react_jsx_runtime.jsx)("span", {
21587
+ style: {
21588
+ alignSelf: "center",
21589
+ marginRight: "auto",
21590
+ fontSize: 13
21591
+ },
21592
+ children: "确定退出登录?"
21593
+ }),
21594
+ (0, react_jsx_runtime.jsx)("button", {
21595
+ type: "button",
21596
+ style: ui.quiet,
21597
+ onClick: () => setConfirmLogout(false),
21598
+ children: "取消"
21599
+ }),
21600
+ (0, react_jsx_runtime.jsx)("button", {
21601
+ type: "button",
21602
+ style: ui.danger,
21603
+ onClick: () => {
21604
+ EdusAuthStore.logout();
21605
+ close();
21606
+ },
21607
+ children: "退出"
21608
+ })
21609
+ ] }) : (0, react_jsx_runtime.jsx)("button", {
21610
+ type: "button",
21611
+ style: ui.quiet,
21612
+ onClick: () => setConfirmLogout(true),
21613
+ children: "退出登录"
21614
+ })
21394
21615
  })
21395
- })
21396
- ]
21616
+ ]
21617
+ })
21397
21618
  });
21398
21619
  }
21399
21620
  //#endregion
21400
- //#region lib/types/client/LoginGuide.js
21401
- /**
21402
- * 对话模型必须登录课成账号才能用(网关以登录 token 鉴权计费),而 Harness 自带
21403
- * 的账号与模型设置入口都已屏蔽,所以登录引导由本插件提供:
21404
- *
21405
- * - 全局登录弹窗(shell.overlay):未登录启动时自动弹出一次,可点右上角关闭;
21406
- * - 输入框上方横幅(conversation.input.dock):会话内未登录时常驻;
21407
- * - 侧边栏底部按钮(sidebar.footer.action):新会话页也能找到登录入口。
21408
- *
21409
- * 三处共用一个弹窗开关,登录成功后弹窗关闭、横幅消失;侧边栏按钮转为显示当前
21410
- * 账号,点击打开账号信息弹窗(AccountDialogOverlay)。
21411
- */
21412
- let dialogOpen = false;
21413
- const dialogListeners = /* @__PURE__ */ new Set();
21414
- function setDialogOpen(next) {
21415
- if (dialogOpen === next) return;
21416
- dialogOpen = next;
21417
- for (const listener of dialogListeners) listener();
21418
- }
21419
- const EdusLoginDialog = {
21420
- open() {
21421
- setDialogOpen(true);
21422
- },
21423
- close() {
21424
- setDialogOpen(false);
21425
- },
21426
- subscribe(listener) {
21427
- dialogListeners.add(listener);
21428
- return () => {
21429
- dialogListeners.delete(listener);
21621
+ //#region lib/types/client/HtmlPublishPreview.js
21622
+ function fileRef(address) {
21623
+ if (!address.startsWith("dsh-resource://file/session/")) return null;
21624
+ try {
21625
+ const [sessionId, ...path] = address.slice(28).split(/[?#]/, 1)[0]?.split("/") ?? [];
21626
+ if (sessionId === void 0 || sessionId === "" || path.length === 0) return null;
21627
+ return {
21628
+ sessionId: decodeURIComponent(sessionId),
21629
+ path: path.map(decodeURIComponent).join("/")
21430
21630
  };
21431
- },
21432
- getSnapshot: () => dialogOpen
21433
- };
21434
- function useDialogOpen() {
21435
- return (0, react.useSyncExternalStore)(EdusLoginDialog.subscribe, EdusLoginDialog.getSnapshot, () => false);
21436
- }
21437
- /** 账号信息弹窗的开关,与登录弹窗相互独立。 */
21438
- let accountOpen = false;
21439
- const accountListeners = /* @__PURE__ */ new Set();
21440
- function setAccountOpen(next) {
21441
- if (accountOpen === next) return;
21442
- accountOpen = next;
21443
- for (const listener of accountListeners) listener();
21631
+ } catch {
21632
+ return null;
21633
+ }
21444
21634
  }
21445
- const EdusAccountDialog = {
21446
- open() {
21447
- setAccountOpen(true);
21448
- },
21449
- close() {
21450
- setAccountOpen(false);
21451
- },
21452
- subscribe(listener) {
21453
- accountListeners.add(listener);
21454
- return () => {
21455
- accountListeners.delete(listener);
21456
- };
21457
- },
21458
- getSnapshot: () => accountOpen
21459
- };
21460
- function useAccountOpen() {
21461
- return (0, react.useSyncExternalStore)(EdusAccountDialog.subscribe, EdusAccountDialog.getSnapshot, () => false);
21635
+ function basename(path) {
21636
+ return path.split("/").at(-1) ?? path;
21462
21637
  }
21463
- const ui = {
21464
- backdrop: {
21465
- position: "fixed",
21466
- inset: 0,
21467
- zIndex: 1200,
21468
- display: "grid",
21469
- placeItems: "center",
21470
- background: "rgb(0 0 0 / .35)",
21471
- pointerEvents: "auto"
21472
- },
21473
- card: {
21474
- position: "relative",
21475
- boxSizing: "border-box",
21476
- width: "min(380px, calc(100vw - 32px))",
21477
- background: "var(--dsw-alias-bg-base)",
21478
- color: "var(--dsw-alias-label-primary)",
21479
- padding: "28px 28px 22px",
21480
- borderRadius: 14,
21481
- boxShadow: "0 20px 60px rgb(0 0 0 / 25%)"
21482
- },
21483
- close: {
21484
- position: "absolute",
21485
- top: 12,
21486
- right: 12,
21487
- display: "grid",
21488
- placeItems: "center",
21489
- width: 28,
21490
- height: 28,
21491
- border: 0,
21492
- borderRadius: 7,
21493
- padding: 0,
21494
- background: "transparent",
21495
- color: "var(--dsw-alias-label-tertiary)",
21496
- cursor: "pointer"
21497
- },
21498
- title: {
21499
- margin: 0,
21500
- fontSize: 18,
21501
- fontWeight: 650
21502
- },
21503
- intro: {
21504
- margin: "6px 0 8px",
21505
- color: "var(--dsw-alias-label-secondary)",
21506
- fontSize: 13,
21507
- lineHeight: 1.5
21508
- },
21509
- dialogField: {
21510
- height: 40,
21511
- borderRadius: 9,
21512
- padding: "0 12px"
21513
- },
21514
- dialogPrimary: {
21515
- width: "100%",
21516
- height: 42,
21517
- borderRadius: 9,
21518
- fontSize: 14
21519
- },
21520
- dock: {
21521
- boxSizing: "border-box",
21522
- width: "calc(100% - 2 * var(--dsh-composer-side-clearance) - 4 * var(--dsh-composer-dock-inset))",
21523
- margin: "0 auto"
21638
+ /** 后端在未开通会员时返回的提示里带有这个字眼,此时改为引导扫码联系客服。 */
21639
+ const MEMBERSHIP_HINT = "需要开通会员";
21640
+ const SERVICE_QR = "https://cdn.juzhenai.cn/ai-edu/assets/wechat.webp";
21641
+ const styles$1 = {
21642
+ root: {
21643
+ display: "flex",
21644
+ flexDirection: "column",
21645
+ height: "100%",
21646
+ minHeight: 0,
21647
+ background: "var(--dsw-alias-bg-base)"
21524
21648
  },
21525
- banner: {
21526
- boxSizing: "border-box",
21649
+ bar: {
21527
21650
  display: "flex",
21528
21651
  alignItems: "center",
21529
- gap: 10,
21530
- width: "100%",
21531
- maxWidth: "calc(var(--dsh-composer-card-max-width) - 4 * var(--dsh-composer-dock-inset))",
21532
- height: 36,
21533
- margin: "0 auto",
21534
- padding: "4px 5px 4px 12px",
21535
- borderRadius: "var(--dsw-radius-md)",
21536
- background: "var(--dsw-alias-bg-base)",
21537
- boxShadow: "var(--dsw-elevation-panel)",
21538
- color: "var(--dsw-alias-label-primary)",
21539
- fontSize: 13
21540
- },
21541
- bannerText: {
21542
- flex: 1,
21543
- minWidth: 0,
21544
- overflow: "hidden",
21545
- textOverflow: "ellipsis",
21546
- whiteSpace: "nowrap"
21547
- },
21548
- primary: {
21549
- flex: "none",
21550
- height: 28,
21551
- border: "none",
21552
- borderRadius: "var(--dsw-radius-sm, 6px)",
21553
- padding: "0 12px",
21554
- background: "var(--dsw-alias-button-primary-fill)",
21555
- color: "var(--dsw-alias-label-primary-inverted)",
21652
+ gap: 7,
21653
+ padding: "7px 10px",
21654
+ borderBottom: "1px solid var(--dsw-alias-border-l2)",
21655
+ background: "var(--dsw-alias-bg-layer-1)"
21656
+ },
21657
+ button: {
21658
+ border: "1px solid var(--dsw-alias-border-l2)",
21659
+ borderRadius: 6,
21660
+ padding: "4px 8px",
21661
+ background: "transparent",
21662
+ color: "var(--dsw-alias-label-primary)",
21556
21663
  font: "inherit",
21557
21664
  fontSize: 12,
21558
- fontWeight: 650,
21559
21665
  cursor: "pointer"
21560
21666
  },
21561
- footer: {
21562
- boxSizing: "border-box",
21563
- display: "flex",
21564
- alignItems: "center",
21565
- justifyContent: "center",
21566
- gap: 6,
21567
- width: "100%",
21568
- margin: "0 8px 6px",
21569
- padding: "8px 10px",
21667
+ primary: {
21570
21668
  border: "none",
21571
- borderRadius: 8,
21669
+ borderRadius: 7,
21670
+ padding: "6px 11px",
21572
21671
  background: "var(--dsw-alias-button-primary-fill)",
21573
21672
  color: "var(--dsw-alias-label-primary-inverted)",
21574
21673
  font: "inherit",
21575
- fontSize: 13,
21674
+ fontSize: 12,
21576
21675
  fontWeight: 650,
21577
21676
  cursor: "pointer"
21578
21677
  },
21579
21678
  account: {
21580
- boxSizing: "border-box",
21581
- display: "flex",
21582
- alignItems: "center",
21583
- gap: 8,
21584
- width: "100%",
21585
- margin: "0 8px 6px",
21586
- padding: "6px 8px",
21587
- border: "none",
21588
- borderRadius: 8,
21589
- background: "transparent",
21590
- color: "var(--dsw-alias-label-primary)",
21591
- font: "inherit",
21592
- fontSize: 13,
21593
- textAlign: "left",
21594
- cursor: "pointer"
21679
+ marginLeft: "auto",
21680
+ color: "var(--dsw-alias-label-tertiary)",
21681
+ fontSize: 11,
21682
+ overflow: "hidden",
21683
+ textOverflow: "ellipsis",
21684
+ whiteSpace: "nowrap"
21595
21685
  },
21596
- accountRail: {
21597
- boxSizing: "border-box",
21598
- display: "grid",
21599
- placeItems: "center",
21600
- width: 36,
21601
- height: 36,
21602
- marginBottom: 6,
21603
- border: "none",
21604
- borderRadius: 8,
21605
- padding: 0,
21606
- background: "transparent",
21607
- cursor: "pointer"
21686
+ frame: {
21687
+ flex: 1,
21688
+ minHeight: 0,
21689
+ width: "100%",
21690
+ border: 0,
21691
+ background: "#fff"
21608
21692
  },
21609
- avatar: {
21610
- flex: "none",
21693
+ overlay: {
21694
+ position: "fixed",
21695
+ inset: 0,
21696
+ zIndex: 1100,
21611
21697
  display: "grid",
21612
21698
  placeItems: "center",
21613
- width: 26,
21614
- height: 26,
21615
- borderRadius: "50%",
21616
- background: "var(--dsw-alias-button-primary-fill)",
21617
- color: "var(--dsw-alias-label-primary-inverted)"
21699
+ padding: 18,
21700
+ background: "rgb(9 19 16 / 56%)",
21701
+ backdropFilter: "blur(5px)"
21618
21702
  },
21619
- accountName: {
21620
- flex: 1,
21621
- minWidth: 0,
21703
+ dialog: {
21704
+ width: "min(460px, 100%)",
21622
21705
  overflow: "hidden",
21623
- textOverflow: "ellipsis",
21624
- whiteSpace: "nowrap"
21706
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 70%, white)",
21707
+ borderRadius: 16,
21708
+ background: "var(--dsw-alias-bg-base)",
21709
+ boxShadow: "0 24px 70px rgb(0 0 0 / 32%)"
21625
21710
  },
21626
- badge: {
21627
- flex: "none",
21628
- padding: "1px 6px",
21629
- borderRadius: 999,
21630
- background: "var(--dsw-alias-bg-l2, rgb(0 0 0 / 6%))",
21631
- color: "var(--dsw-alias-brand-primary)",
21632
- fontSize: 11,
21633
- fontWeight: 600
21711
+ hero: {
21712
+ padding: "22px 24px 18px",
21713
+ background: "linear-gradient(135deg, #173329 0%, #29553d 100%)",
21714
+ color: "#f7fbf8"
21634
21715
  },
21635
- profile: {
21636
- display: "flex",
21637
- alignItems: "center",
21638
- gap: 12,
21639
- marginBottom: 18
21716
+ eyebrow: {
21717
+ margin: 0,
21718
+ color: "#b9ddc6",
21719
+ fontSize: 11,
21720
+ fontWeight: 700,
21721
+ letterSpacing: ".12em"
21640
21722
  },
21641
- profileAvatar: {
21642
- flex: "none",
21643
- display: "grid",
21644
- placeItems: "center",
21645
- width: 44,
21646
- height: 44,
21647
- borderRadius: "50%",
21648
- background: "var(--dsw-alias-button-primary-fill)",
21649
- color: "var(--dsw-alias-label-primary-inverted)"
21723
+ title: {
21724
+ margin: "7px 0 0",
21725
+ fontSize: 21,
21726
+ lineHeight: 1.2,
21727
+ letterSpacing: "-.02em"
21650
21728
  },
21651
- profileName: {
21652
- margin: 0,
21653
- fontSize: 16,
21654
- fontWeight: 650,
21655
- overflow: "hidden",
21656
- textOverflow: "ellipsis",
21657
- whiteSpace: "nowrap"
21729
+ body: { padding: "20px 24px 22px" },
21730
+ copy: {
21731
+ margin: "0 0 16px",
21732
+ color: "var(--dsw-alias-label-secondary)",
21733
+ fontSize: 13,
21734
+ lineHeight: 1.6
21658
21735
  },
21659
- profileSub: {
21660
- margin: "2px 0 0",
21661
- color: "var(--dsw-alias-label-tertiary)",
21662
- fontSize: 12
21736
+ field: {
21737
+ boxSizing: "border-box",
21738
+ width: "100%",
21739
+ border: "1px solid var(--dsw-alias-border-l2)",
21740
+ borderRadius: 8,
21741
+ padding: "10px 11px",
21742
+ color: "var(--dsw-alias-label-primary)",
21743
+ background: "var(--dsw-alias-bg-base)",
21744
+ font: "inherit",
21745
+ fontSize: 13
21663
21746
  },
21664
- section: {
21665
- margin: "0 0 14px",
21666
- padding: "12px 14px",
21747
+ option: {
21748
+ display: "flex",
21749
+ alignItems: "flex-start",
21750
+ gap: 10,
21751
+ padding: 12,
21752
+ border: "1px solid var(--dsw-alias-border-l2)",
21667
21753
  borderRadius: 10,
21668
- border: "1px solid var(--dsw-alias-border-l2)"
21754
+ cursor: "pointer"
21669
21755
  },
21670
- sectionTitle: {
21671
- margin: "0 0 8px",
21756
+ optionSelected: {
21757
+ borderColor: "var(--dsw-alias-brand-primary)",
21758
+ background: "color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent)"
21759
+ },
21760
+ optionTitle: {
21761
+ display: "block",
21762
+ color: "var(--dsw-alias-label-primary)",
21763
+ fontSize: 13,
21764
+ fontWeight: 650
21765
+ },
21766
+ optionText: {
21767
+ display: "block",
21768
+ marginTop: 3,
21672
21769
  color: "var(--dsw-alias-label-secondary)",
21673
21770
  fontSize: 12,
21674
- fontWeight: 600
21771
+ lineHeight: 1.45
21675
21772
  },
21676
- row: {
21773
+ error: {
21774
+ margin: "12px 0 0",
21775
+ color: "var(--dsw-alias-state-error-primary)",
21776
+ fontSize: 12,
21777
+ lineHeight: 1.45
21778
+ },
21779
+ actions: {
21677
21780
  display: "flex",
21678
- justifyContent: "space-between",
21679
- gap: 12,
21680
- padding: "3px 0",
21681
- fontSize: 13
21781
+ justifyContent: "flex-end",
21782
+ gap: 8,
21783
+ marginTop: 20
21682
21784
  },
21683
- rowLabel: { color: "var(--dsw-alias-label-tertiary)" },
21684
- meter: {
21685
- height: 6,
21686
- margin: "6px 0 4px",
21687
- borderRadius: 999,
21688
- background: "var(--dsw-alias-border-l2)",
21689
- overflow: "hidden"
21785
+ linkBox: {
21786
+ display: "flex",
21787
+ alignItems: "center",
21788
+ gap: 8,
21789
+ padding: 10,
21790
+ border: "1px solid var(--dsw-alias-border-l2)",
21791
+ borderRadius: 9,
21792
+ background: "var(--dsw-alias-bg-layer-1)"
21690
21793
  },
21691
- status: {
21692
- margin: "0 0 12px",
21693
- color: "var(--dsw-alias-label-tertiary)",
21694
- fontSize: 12
21794
+ link: {
21795
+ minWidth: 0,
21796
+ flex: 1,
21797
+ overflow: "hidden",
21798
+ color: "var(--dsw-alias-brand-primary)",
21799
+ fontSize: 12,
21800
+ textOverflow: "ellipsis",
21801
+ whiteSpace: "nowrap"
21695
21802
  },
21696
- errorText: {
21697
- margin: "0 0 12px",
21698
- color: "var(--dsw-alias-state-error-primary)",
21699
- fontSize: 12
21803
+ success: {
21804
+ display: "grid",
21805
+ placeItems: "center",
21806
+ width: 32,
21807
+ height: 32,
21808
+ borderRadius: "50%",
21809
+ background: "#e3f4e8",
21810
+ color: "#237344",
21811
+ fontSize: 18,
21812
+ fontWeight: 700
21700
21813
  },
21701
- support: {
21814
+ memberCard: {
21702
21815
  display: "flex",
21816
+ gap: 14,
21703
21817
  alignItems: "center",
21704
- gap: 12,
21705
- margin: "0",
21706
- padding: "10px 12px",
21707
- borderRadius: 10,
21708
- background: "var(--dsw-alias-bg-layer-1, rgb(0 0 0 / 4%))"
21818
+ margin: "14px 0 0",
21819
+ padding: 13,
21820
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, var(--dsw-alias-border-l2))",
21821
+ borderRadius: 11,
21822
+ background: "var(--dsw-alias-bg-layer-1)"
21709
21823
  },
21710
- supportQr: {
21824
+ memberQr: {
21711
21825
  flex: "none",
21712
- width: 84,
21713
- height: 84,
21714
- borderRadius: 6,
21826
+ width: 96,
21827
+ height: 96,
21828
+ borderRadius: 8,
21715
21829
  background: "#fff",
21716
21830
  objectFit: "contain"
21717
21831
  },
21718
- supportTitle: {
21832
+ memberTitle: {
21719
21833
  margin: 0,
21834
+ color: "var(--dsw-alias-label-primary)",
21720
21835
  fontSize: 13,
21721
21836
  fontWeight: 650
21722
21837
  },
21723
- supportText: {
21724
- margin: "4px 0 0",
21838
+ memberText: {
21839
+ margin: "5px 0 0",
21725
21840
  color: "var(--dsw-alias-label-secondary)",
21726
21841
  fontSize: 12,
21727
21842
  lineHeight: 1.55
21728
21843
  },
21729
- dialogActions: {
21730
- display: "flex",
21731
- justifyContent: "flex-end",
21732
- gap: 8,
21733
- marginTop: 4
21734
- },
21735
- quiet: {
21736
- height: 32,
21737
- border: "1px solid var(--dsw-alias-border-l2)",
21738
- borderRadius: 8,
21739
- padding: "0 14px",
21740
- background: "transparent",
21741
- color: "var(--dsw-alias-label-primary)",
21742
- font: "inherit",
21743
- fontSize: 13,
21744
- cursor: "pointer"
21745
- },
21746
- danger: {
21747
- height: 32,
21748
- border: "none",
21749
- borderRadius: 8,
21750
- padding: "0 14px",
21751
- background: "var(--dsw-alias-state-error-primary)",
21752
- color: "#fff",
21753
- font: "inherit",
21754
- fontSize: 13,
21755
- fontWeight: 600,
21756
- cursor: "pointer"
21757
- },
21758
- footerRail: {
21759
- boxSizing: "border-box",
21760
- display: "grid",
21761
- placeItems: "center",
21762
- width: 36,
21763
- height: 36,
21764
- marginBottom: 6,
21765
- border: "none",
21766
- borderRadius: 8,
21767
- background: "var(--dsw-alias-button-primary-fill)",
21768
- color: "var(--dsw-alias-label-primary-inverted)",
21769
- cursor: "pointer"
21844
+ accountCard: {
21845
+ margin: "0 0 15px",
21846
+ padding: "9px 11px",
21847
+ borderLeft: "3px solid var(--dsw-alias-brand-primary)",
21848
+ borderRadius: 6,
21849
+ background: "var(--dsw-alias-bg-layer-1)",
21850
+ color: "var(--dsw-alias-label-secondary)",
21851
+ fontSize: 12,
21852
+ lineHeight: 1.5
21770
21853
  }
21771
21854
  };
21772
- function UserIcon() {
21773
- return (0, react_jsx_runtime.jsxs)("svg", {
21774
- width: 16,
21775
- height: 16,
21776
- viewBox: "0 0 16 16",
21777
- fill: "none",
21778
- stroke: "currentColor",
21779
- strokeWidth: 1.5,
21780
- strokeLinecap: "round",
21781
- "aria-hidden": true,
21782
- children: [(0, react_jsx_runtime.jsx)("circle", {
21783
- cx: "8",
21784
- cy: "5.5",
21785
- r: "2.75"
21786
- }), (0, react_jsx_runtime.jsx)("path", { d: "M2.75 13.5c.6-2.4 2.7-3.9 5.25-3.9s4.65 1.5 5.25 3.9" })]
21787
- });
21788
- }
21789
- function CloseIcon() {
21790
- return (0, react_jsx_runtime.jsx)("svg", {
21791
- width: 16,
21792
- height: 16,
21793
- viewBox: "0 0 16 16",
21794
- fill: "none",
21795
- stroke: "currentColor",
21796
- strokeWidth: 1.6,
21797
- strokeLinecap: "round",
21798
- "aria-hidden": true,
21799
- children: (0, react_jsx_runtime.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" })
21800
- });
21801
- }
21802
- /** 全局登录弹窗:已登录时不渲染。对话、我的作品等所有需要登录的入口都打开它。 */
21803
- function LoginDialogOverlay({ remote }) {
21804
- const open = useDialogOpen();
21805
- const account = useEdusLogin();
21806
- const visible = open && account === null;
21807
- const [busy, setBusy] = (0, react.useState)(false);
21808
- const close = (0, react.useCallback)(() => {
21809
- if (!busy) EdusLoginDialog.close();
21810
- }, [busy]);
21811
- (0, react.useEffect)(() => {
21812
- if (!visible) return;
21813
- const onKey = (event) => {
21814
- if (event.key === "Escape") close();
21815
- };
21816
- window.addEventListener("keydown", onKey);
21817
- return () => {
21818
- window.removeEventListener("keydown", onKey);
21819
- };
21820
- }, [visible, close]);
21821
- if (!visible) return null;
21822
- return (0, react_jsx_runtime.jsx)("div", {
21823
- role: "dialog",
21824
- "aria-modal": "true",
21825
- "aria-labelledby": "edus-login-dialog-title",
21826
- style: ui.backdrop,
21827
- children: (0, react_jsx_runtime.jsxs)("div", {
21828
- style: ui.card,
21829
- children: [(0, react_jsx_runtime.jsx)("button", {
21830
- type: "button",
21831
- "aria-label": "关闭",
21832
- title: "关闭",
21833
- style: ui.close,
21834
- disabled: busy,
21835
- onClick: close,
21836
- children: (0, react_jsx_runtime.jsx)(CloseIcon, {})
21837
- }), (0, react_jsx_runtime.jsx)(EdusLoginPanel, {
21838
- remote,
21839
- intro: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("h2", {
21840
- id: "edus-login-dialog-title",
21841
- style: ui.title,
21842
- children: "登录课成账号"
21843
- }), (0, react_jsx_runtime.jsx)("p", {
21844
- style: ui.intro,
21845
- children: "登录后即可开始 AI 对话,查看和发布你的作品。"
21846
- })] }),
21847
- onBusyChange: setBusy,
21848
- onSuccess: (result) => {
21849
- EdusAuthStore.set(result);
21850
- EdusLoginDialog.close();
21851
- },
21852
- theme: {
21853
- field: ui.dialogField,
21854
- primary: ui.dialogPrimary
21855
- }
21856
- })]
21857
- })
21858
- });
21859
- }
21860
- /** 输入框上方的未登录横幅。 */
21861
- function LoginDockBanner() {
21862
- if (useEdusLogin() !== null) return null;
21863
- return (0, react_jsx_runtime.jsx)("div", {
21864
- style: ui.dock,
21865
- children: (0, react_jsx_runtime.jsxs)("div", {
21866
- style: ui.banner,
21867
- children: [(0, react_jsx_runtime.jsx)("span", {
21868
- style: ui.bannerText,
21869
- children: "登录课成账号后即可开始对话"
21870
- }), (0, react_jsx_runtime.jsx)("button", {
21871
- type: "button",
21872
- style: ui.primary,
21873
- onClick: EdusLoginDialog.open,
21874
- children: "登录"
21875
- })]
21876
- })
21877
- });
21878
- }
21879
- /** 侧边栏底部的账号入口:未登录为登录按钮,已登录显示当前账号;收起为窄栏时只显示图标。 */
21880
- function LoginFooterAction({ wide }) {
21881
- const account = useEdusLogin();
21882
- if (account === null) return wide ? (0, react_jsx_runtime.jsxs)("button", {
21883
- type: "button",
21884
- style: ui.footer,
21885
- onClick: EdusLoginDialog.open,
21886
- children: [(0, react_jsx_runtime.jsx)(UserIcon, {}), "登录课成账号"]
21887
- }) : (0, react_jsx_runtime.jsx)("button", {
21888
- type: "button",
21889
- title: "登录课成账号",
21890
- "aria-label": "登录课成账号",
21891
- style: ui.footerRail,
21892
- onClick: EdusLoginDialog.open,
21893
- children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21894
- });
21895
- const name = displayName(account);
21896
- return wide ? (0, react_jsx_runtime.jsxs)("button", {
21897
- type: "button",
21898
- title: "账号信息",
21899
- style: ui.account,
21900
- onClick: EdusAccountDialog.open,
21901
- children: [
21902
- (0, react_jsx_runtime.jsx)("span", {
21903
- style: ui.avatar,
21904
- children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21905
- }),
21906
- (0, react_jsx_runtime.jsx)("span", {
21907
- style: ui.accountName,
21908
- children: name
21909
- }),
21910
- account.isVip && account.vipLevelName && (0, react_jsx_runtime.jsx)("span", {
21911
- style: ui.badge,
21912
- children: account.vipLevelName
21913
- })
21914
- ]
21915
- }) : (0, react_jsx_runtime.jsx)("button", {
21916
- type: "button",
21917
- title: `${name} · 账号信息`,
21918
- "aria-label": "账号信息",
21919
- style: ui.accountRail,
21920
- onClick: EdusAccountDialog.open,
21921
- children: (0, react_jsx_runtime.jsx)("span", {
21922
- style: ui.avatar,
21923
- children: (0, react_jsx_runtime.jsx)(UserIcon, {})
21924
- })
21855
+ /** 普通报错直接展示文案;会员相关的报错额外给出客服二维码。 */
21856
+ function PublishError({ message }) {
21857
+ if (message === "") return null;
21858
+ if (!message.includes(MEMBERSHIP_HINT)) return (0, react_jsx_runtime.jsx)("p", {
21859
+ style: styles$1.error,
21860
+ children: message
21925
21861
  });
21926
- }
21927
- /** 客服(技术支持)微信二维码:开通会员或遇到问题时扫码添加。 */
21928
- const SUPPORT_QR = "https://cdn.juzhenai.cn/assets/jishu-weixin-qrcode.jpg";
21929
- function maskPhone(phone) {
21930
- return /^\d{11}$/.test(phone) ? `${phone.slice(0, 3)}****${phone.slice(7)}` : phone;
21931
- }
21932
- function displayName(account) {
21933
- return account.title || maskPhone(account.phone);
21934
- }
21935
- function formatTime(value) {
21936
- if (!value) return "—";
21937
- const date = new Date(value);
21938
- if (Number.isNaN(date.getTime())) return value;
21939
- const pad = (n) => String(n).padStart(2, "0");
21940
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
21941
- }
21942
- /** 取整 */
21943
- function formatPoints(value) {
21944
- const amount = Number(value);
21945
- return value === void 0 || value === "" || Number.isNaN(amount) ? "—" : Math.round(amount).toLocaleString("zh-CN");
21946
- }
21947
- function Row({ label, value }) {
21948
21862
  return (0, react_jsx_runtime.jsxs)("div", {
21949
- style: ui.row,
21950
- children: [(0, react_jsx_runtime.jsx)("span", {
21951
- style: ui.rowLabel,
21952
- children: label
21953
- }), (0, react_jsx_runtime.jsx)("span", { children: value })]
21863
+ style: styles$1.memberCard,
21864
+ children: [(0, react_jsx_runtime.jsx)("img", {
21865
+ alt: "客服微信二维码",
21866
+ src: SERVICE_QR,
21867
+ style: styles$1.memberQr
21868
+ }), (0, react_jsx_runtime.jsxs)("div", {
21869
+ style: { minWidth: 0 },
21870
+ children: [(0, react_jsx_runtime.jsx)("p", {
21871
+ style: styles$1.memberTitle,
21872
+ children: message
21873
+ }), (0, react_jsx_runtime.jsx)("p", {
21874
+ style: styles$1.memberText,
21875
+ children: "该功能需要开通会员后使用。请用微信扫描左侧二维码添加客服咨询开通。"
21876
+ })]
21877
+ })]
21954
21878
  });
21955
21879
  }
21956
- /** 账号信息弹窗:打开时用 token 拉一次最新信息,展示会员与模型用量,并提供退出登录。 */
21957
- function AccountDialogOverlay({ remote }) {
21958
- const open = useAccountOpen();
21880
+ /** An extension-priority document body: native right-preview ergonomics plus a complete edus publishing flow. */
21881
+ function HtmlPublishPreview({ content, resourceAddress, remote }) {
21959
21882
  const account = useEdusLogin();
21960
- const visible = open && account !== null;
21961
- const [refreshing, setRefreshing] = (0, react.useState)(false);
21883
+ const [stage, setStage] = (0, react.useState)("closed");
21884
+ const [destination, setDestination] = (0, react.useState)("new");
21885
+ const [title, setTitle] = (0, react.useState)("");
21886
+ const [busy, setBusy] = (0, react.useState)(false);
21962
21887
  const [error, setError] = (0, react.useState)("");
21963
- const [confirmLogout, setConfirmLogout] = (0, react.useState)(false);
21964
- const close = (0, react.useCallback)(() => {
21965
- EdusAccountDialog.close();
21966
- }, []);
21967
- (0, react.useEffect)(() => {
21968
- if (open && account === null) EdusAccountDialog.close();
21969
- }, [open, account]);
21970
- const token = account?.token;
21888
+ const [shareUrl, setShareUrl] = (0, react.useState)(null);
21889
+ const [copied, setCopied] = (0, react.useState)(false);
21890
+ const ref = (0, react.useMemo)(() => fileRef(resourceAddress), [resourceAddress]);
21891
+ const source = (0, react.useMemo)(() => {
21892
+ if (content.kind === "bytes") return new TextDecoder().decode(content.data);
21893
+ return content.kind === "text" ? content.text : "";
21894
+ }, [content]);
21895
+ const marker = (0, react.useMemo)(() => extractMarker(source), [source]);
21896
+ const currentAvailable = marker !== null && marker.owner === account?.phone;
21897
+ const [awaitingLogin, setAwaitingLogin] = (0, react.useState)(false);
21898
+ const loginOpen = (0, react.useSyncExternalStore)(EdusLoginDialog.subscribe, EdusLoginDialog.getSnapshot, () => false);
21971
21899
  (0, react.useEffect)(() => {
21972
- if (!open || token === void 0) return;
21973
- let cancelled = false;
21900
+ if (!awaitingLogin) return;
21901
+ if (account !== null) {
21902
+ setAwaitingLogin(false);
21903
+ setDestination(marker !== null && marker.owner === account.phone ? "current" : "new");
21904
+ setStage("destination");
21905
+ } else if (!loginOpen) setAwaitingLogin(false);
21906
+ }, [
21907
+ awaitingLogin,
21908
+ account,
21909
+ loginOpen,
21910
+ marker
21911
+ ]);
21912
+ const requestLogin = (notice) => {
21913
+ setStage("closed");
21914
+ setAwaitingLogin(true);
21915
+ EdusLoginDialog.open(notice);
21916
+ };
21917
+ const open = async () => {
21974
21918
  setError("");
21975
- setConfirmLogout(false);
21976
- setRefreshing(true);
21977
- remote.loginInfo(token).then((refreshed) => {
21978
- if (!cancelled) EdusAuthStore.set(refreshed);
21979
- }, (failure) => {
21980
- if (!cancelled) setError(`刷新账号信息失败:${failure instanceof Error ? failure.message : String(failure)}`);
21981
- }).finally(() => {
21982
- if (!cancelled) setRefreshing(false);
21983
- });
21984
- return () => {
21985
- cancelled = true;
21986
- };
21919
+ setShareUrl(null);
21920
+ setCopied(false);
21921
+ setTitle(ref === null ? "" : basename(ref.path).replace(/\.html?$/i, ""));
21922
+ const stored = EdusAuthStore.getSnapshot();
21923
+ if (stored === null) {
21924
+ requestLogin();
21925
+ return;
21926
+ }
21927
+ setBusy(true);
21928
+ setStage("checking");
21929
+ try {
21930
+ const refreshed = await remote.loginInfo(stored.token);
21931
+ EdusAuthStore.set(refreshed);
21932
+ setDestination(marker !== null && marker.owner === refreshed.phone ? "current" : "new");
21933
+ setStage("destination");
21934
+ } catch {
21935
+ EdusAuthStore.logout();
21936
+ requestLogin("登录状态已失效,请重新登录。");
21937
+ } finally {
21938
+ setBusy(false);
21939
+ }
21940
+ };
21941
+ const close = () => {
21942
+ if (!busy) setStage("closed");
21943
+ };
21944
+ const submitPublish = (0, react.useCallback)(async () => {
21945
+ if (ref === null || account === null) return;
21946
+ const newWork = destination === "new";
21947
+ const publishTitle = title.trim();
21948
+ if (newWork && publishTitle === "") {
21949
+ setError("请为新作品填写标题。");
21950
+ return;
21951
+ }
21952
+ setBusy(true);
21953
+ setError("");
21954
+ try {
21955
+ const result = await remote.publishFile(account.token, ref.sessionId, ref.path, account.phone, publishTitle, newWork);
21956
+ if (result.shareUrl === null) throw new Error("发布完成,但未返回分享链接。");
21957
+ setShareUrl(result.shareUrl);
21958
+ setStage("success");
21959
+ } catch (reason) {
21960
+ setError(reason instanceof Error ? reason.message : String(reason));
21961
+ } finally {
21962
+ setBusy(false);
21963
+ }
21987
21964
  }, [
21988
- open,
21989
- token,
21990
- remote
21965
+ account,
21966
+ destination,
21967
+ ref,
21968
+ remote,
21969
+ title
21991
21970
  ]);
21992
- (0, react.useEffect)(() => {
21993
- if (!visible) return;
21994
- const onKey = (event) => {
21995
- if (event.key === "Escape") close();
21996
- };
21997
- window.addEventListener("keydown", onKey);
21998
- return () => {
21999
- window.removeEventListener("keydown", onKey);
22000
- };
22001
- }, [visible, close]);
22002
- if (!visible) return null;
22003
- const limit = account.vipMonthLimit ?? 0;
22004
- const monthCost = Number(account.llmMonthCost);
22005
- const ratio = limit > 0 && !Number.isNaN(monthCost) ? Math.min(1, monthCost / limit) : 0;
22006
- return (0, react_jsx_runtime.jsx)("div", {
22007
- role: "dialog",
22008
- "aria-modal": "true",
22009
- "aria-labelledby": "edus-account-dialog-title",
22010
- style: ui.backdrop,
22011
- onMouseDown: (event) => {
22012
- if (event.target === event.currentTarget) close();
22013
- },
22014
- children: (0, react_jsx_runtime.jsxs)("div", {
22015
- style: ui.card,
22016
- children: [
22017
- (0, react_jsx_runtime.jsx)("button", {
22018
- type: "button",
22019
- "aria-label": "关闭",
22020
- title: "关闭",
22021
- style: ui.close,
22022
- onClick: close,
22023
- children: (0, react_jsx_runtime.jsx)(CloseIcon, {})
22024
- }),
22025
- (0, react_jsx_runtime.jsxs)("div", {
22026
- style: ui.profile,
22027
- children: [(0, react_jsx_runtime.jsx)("span", {
22028
- style: ui.profileAvatar,
22029
- children: (0, react_jsx_runtime.jsx)(UserIcon, {})
22030
- }), (0, react_jsx_runtime.jsxs)("div", {
22031
- style: { minWidth: 0 },
22032
- children: [(0, react_jsx_runtime.jsx)("h2", {
22033
- id: "edus-account-dialog-title",
22034
- style: ui.profileName,
22035
- children: displayName(account)
22036
- }), (0, react_jsx_runtime.jsxs)("p", {
22037
- style: ui.profileSub,
22038
- children: [maskPhone(account.phone), account.isAdmin ? " · 管理员" : ""]
22039
- })]
22040
- })]
22041
- }),
22042
- refreshing && (0, react_jsx_runtime.jsx)("p", {
22043
- style: ui.status,
22044
- role: "status",
22045
- children: "正在刷新账号信息…"
22046
- }),
22047
- error !== "" && (0, react_jsx_runtime.jsx)("p", {
22048
- style: ui.errorText,
22049
- children: error
22050
- }),
22051
- (0, react_jsx_runtime.jsxs)("section", {
22052
- style: ui.section,
22053
- children: [
22054
- (0, react_jsx_runtime.jsx)("h3", {
22055
- style: ui.sectionTitle,
22056
- children: "会员"
22057
- }),
22058
- (0, react_jsx_runtime.jsx)(Row, {
22059
- label: "当前版本",
22060
- value: account.isVip ? account.vipLevelName || `VIP ${account.vipLevel}` : "未开通"
22061
- }),
22062
- account.isVip && (0, react_jsx_runtime.jsx)(Row, {
22063
- label: "到期时间",
22064
- value: formatTime(account.vipEndTime)
22065
- }),
22066
- account.llmStatus !== void 0 && account.llmStatus !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(Row, {
22067
- label: "本月已用",
22068
- value: limit > 0 ? `${formatPoints(account.llmMonthCost)} / ${formatPoints(limit)} 积分` : `${formatPoints(account.llmMonthCost)} 积分`
22069
- }), limit > 0 && (0, react_jsx_runtime.jsx)("div", {
22070
- style: ui.meter,
22071
- "aria-hidden": true,
22072
- children: (0, react_jsx_runtime.jsx)("div", { style: {
22073
- width: `${ratio * 100}%`,
22074
- height: "100%",
22075
- background: ratio >= .9 ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-brand-primary)"
22076
- } })
22077
- })] }),
22078
- account.isVip && limit === 0 && (0, react_jsx_runtime.jsx)(Row, {
22079
- label: "每月额度",
22080
- value: "不限"
22081
- })
22082
- ]
22083
- }),
22084
- (0, react_jsx_runtime.jsxs)("div", {
22085
- style: ui.support,
22086
- children: [(0, react_jsx_runtime.jsx)("img", {
22087
- alt: "客服微信二维码",
22088
- src: SUPPORT_QR,
22089
- style: ui.supportQr
22090
- }), (0, react_jsx_runtime.jsxs)("div", {
22091
- style: { minWidth: 0 },
21971
+ const copy = async () => {
21972
+ if (shareUrl === null) return;
21973
+ try {
21974
+ await navigator.clipboard.writeText(shareUrl);
21975
+ setCopied(true);
21976
+ } catch {
21977
+ setError("复制失败,请手动复制链接。");
21978
+ }
21979
+ };
21980
+ if (content.kind !== "bytes") return null;
21981
+ return (0, react_jsx_runtime.jsxs)("div", {
21982
+ style: styles$1.root,
21983
+ "data-edus-html-preview": true,
21984
+ children: [
21985
+ (0, react_jsx_runtime.jsxs)("div", {
21986
+ style: styles$1.bar,
21987
+ children: [(0, react_jsx_runtime.jsx)("button", {
21988
+ style: styles$1.primary,
21989
+ onClick: () => void open(),
21990
+ children: "发布网页"
21991
+ }), (0, react_jsx_runtime.jsx)("span", {
21992
+ style: styles$1.account,
21993
+ children: account === null ? "尚未登录" : `${account.title || "当前账号"} · ${account.phone}`
21994
+ })]
21995
+ }),
21996
+ (0, react_jsx_runtime.jsx)("iframe", {
21997
+ title: "HTML 网页预览",
21998
+ sandbox: "allow-scripts",
21999
+ srcDoc: source,
22000
+ style: styles$1.frame
22001
+ }),
22002
+ stage !== "closed" && (0, react_jsx_runtime.jsx)("div", {
22003
+ style: styles$1.overlay,
22004
+ role: "presentation",
22005
+ onMouseDown: close,
22006
+ children: (0, react_jsx_runtime.jsxs)("section", {
22007
+ style: styles$1.dialog,
22008
+ role: "dialog",
22009
+ "aria-modal": "true",
22010
+ "aria-labelledby": "edus-publish-title",
22011
+ onMouseDown: (event) => event.stopPropagation(),
22012
+ children: [(0, react_jsx_runtime.jsxs)("header", {
22013
+ style: styles$1.hero,
22092
22014
  children: [(0, react_jsx_runtime.jsx)("p", {
22093
- style: ui.supportTitle,
22094
- children: "联系客服"
22095
- }), (0, react_jsx_runtime.jsx)("p", {
22096
- style: ui.supportText,
22097
- children: "需要开通会员或遇到问题,请用微信扫码添加客服。"
22015
+ style: styles$1.eyebrow,
22016
+ children: "COURSE · PUBLISH"
22017
+ }), (0, react_jsx_runtime.jsx)("h2", {
22018
+ id: "edus-publish-title",
22019
+ style: styles$1.title,
22020
+ children: stage === "checking" ? "正在确认账号" : stage === "success" ? "作品已成功发布" : "选择发布目标"
22098
22021
  })]
22022
+ }), (0, react_jsx_runtime.jsxs)("div", {
22023
+ style: styles$1.body,
22024
+ children: [
22025
+ stage === "checking" && (0, react_jsx_runtime.jsx)("p", {
22026
+ style: styles$1.copy,
22027
+ children: "正在通过课成账号服务确认当前登录状态…"
22028
+ }),
22029
+ stage === "destination" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
22030
+ account !== null && (0, react_jsx_runtime.jsxs)("p", {
22031
+ style: styles$1.accountCard,
22032
+ children: [
22033
+ "当前账号:",
22034
+ (0, react_jsx_runtime.jsx)("strong", { children: account.title || account.phone }),
22035
+ account.title !== "" ? `(${account.phone})` : ""
22036
+ ]
22037
+ }),
22038
+ (0, react_jsx_runtime.jsx)("p", {
22039
+ style: styles$1.copy,
22040
+ children: "请选择本次内容的归属。发布会把当前 HTML 文件的最新内容写入所选作品。"
22041
+ }),
22042
+ (0, react_jsx_runtime.jsxs)("div", {
22043
+ style: {
22044
+ display: "grid",
22045
+ gap: 9
22046
+ },
22047
+ children: [currentAvailable && (0, react_jsx_runtime.jsxs)("label", {
22048
+ style: {
22049
+ ...styles$1.option,
22050
+ ...destination === "current" ? styles$1.optionSelected : {}
22051
+ },
22052
+ children: [(0, react_jsx_runtime.jsx)("input", {
22053
+ name: "destination",
22054
+ type: "radio",
22055
+ checked: destination === "current",
22056
+ onChange: () => setDestination("current")
22057
+ }), (0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
22058
+ style: styles$1.optionTitle,
22059
+ children: "更新已关联作品"
22060
+ }), (0, react_jsx_runtime.jsxs)("span", {
22061
+ style: styles$1.optionText,
22062
+ children: [
22063
+ "继续发布到当前文件绑定的作品(",
22064
+ marker.id.slice(0, 8),
22065
+ "…)。"
22066
+ ]
22067
+ })] })]
22068
+ }), (0, react_jsx_runtime.jsxs)("label", {
22069
+ style: {
22070
+ ...styles$1.option,
22071
+ ...destination === "new" ? styles$1.optionSelected : {}
22072
+ },
22073
+ children: [(0, react_jsx_runtime.jsx)("input", {
22074
+ name: "destination",
22075
+ type: "radio",
22076
+ checked: destination === "new",
22077
+ onChange: () => setDestination("new")
22078
+ }), (0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
22079
+ style: styles$1.optionTitle,
22080
+ children: "创建新作品并发布"
22081
+ }), (0, react_jsx_runtime.jsx)("span", {
22082
+ style: styles$1.optionText,
22083
+ children: "创建新的分享页,并把当前文件重新关联到该作品。"
22084
+ })] })]
22085
+ })]
22086
+ }),
22087
+ destination === "new" && (0, react_jsx_runtime.jsxs)("div", {
22088
+ style: { marginTop: 14 },
22089
+ children: [(0, react_jsx_runtime.jsx)("label", {
22090
+ style: {
22091
+ display: "block",
22092
+ marginBottom: 6,
22093
+ color: "var(--dsw-alias-label-secondary)",
22094
+ fontSize: 12
22095
+ },
22096
+ children: "作品标题"
22097
+ }), (0, react_jsx_runtime.jsx)("input", {
22098
+ style: styles$1.field,
22099
+ autoFocus: true,
22100
+ value: title,
22101
+ placeholder: "例如:春季招生落地页",
22102
+ onChange: (event) => setTitle(event.target.value)
22103
+ })]
22104
+ }),
22105
+ (0, react_jsx_runtime.jsx)(PublishError, { message: error }),
22106
+ (0, react_jsx_runtime.jsxs)("div", {
22107
+ style: styles$1.actions,
22108
+ children: [(0, react_jsx_runtime.jsx)("button", {
22109
+ style: styles$1.button,
22110
+ disabled: busy,
22111
+ onClick: close,
22112
+ children: "取消"
22113
+ }), (0, react_jsx_runtime.jsx)("button", {
22114
+ style: styles$1.primary,
22115
+ disabled: busy,
22116
+ onClick: () => void submitPublish(),
22117
+ children: busy ? "发布中…" : destination === "new" ? "创建并发布" : "更新并发布"
22118
+ })]
22119
+ })
22120
+ ] }),
22121
+ stage === "success" && shareUrl !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
22122
+ (0, react_jsx_runtime.jsxs)("div", {
22123
+ style: {
22124
+ display: "flex",
22125
+ gap: 11,
22126
+ alignItems: "center",
22127
+ marginBottom: 14
22128
+ },
22129
+ children: [(0, react_jsx_runtime.jsx)("span", {
22130
+ style: styles$1.success,
22131
+ children: "✓"
22132
+ }), (0, react_jsx_runtime.jsx)("p", {
22133
+ style: {
22134
+ ...styles$1.copy,
22135
+ margin: 0
22136
+ },
22137
+ children: "作品已可通过分享链接访问。链接中的内容为本次发布版本。"
22138
+ })]
22139
+ }),
22140
+ (0, react_jsx_runtime.jsxs)("div", {
22141
+ style: styles$1.linkBox,
22142
+ children: [(0, react_jsx_runtime.jsx)("a", {
22143
+ style: styles$1.link,
22144
+ href: shareUrl,
22145
+ target: "_blank",
22146
+ rel: "noreferrer",
22147
+ title: shareUrl,
22148
+ children: shareUrl
22149
+ }), (0, react_jsx_runtime.jsx)("button", {
22150
+ style: styles$1.button,
22151
+ onClick: () => void copy(),
22152
+ children: copied ? "已复制" : "复制"
22153
+ })]
22154
+ }),
22155
+ error !== "" && (0, react_jsx_runtime.jsx)("p", {
22156
+ style: styles$1.error,
22157
+ children: error
22158
+ }),
22159
+ (0, react_jsx_runtime.jsx)("div", {
22160
+ style: styles$1.actions,
22161
+ children: (0, react_jsx_runtime.jsx)("button", {
22162
+ style: styles$1.primary,
22163
+ onClick: close,
22164
+ children: "完成"
22165
+ })
22166
+ })
22167
+ ] })
22168
+ ]
22099
22169
  })]
22100
- }),
22101
- (0, react_jsx_runtime.jsx)("div", {
22102
- style: ui.dialogActions,
22103
- children: confirmLogout ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
22104
- (0, react_jsx_runtime.jsx)("span", {
22105
- style: {
22106
- alignSelf: "center",
22107
- marginRight: "auto",
22108
- fontSize: 13
22109
- },
22110
- children: "确定退出登录?"
22111
- }),
22112
- (0, react_jsx_runtime.jsx)("button", {
22113
- type: "button",
22114
- style: ui.quiet,
22115
- onClick: () => setConfirmLogout(false),
22116
- children: "取消"
22117
- }),
22118
- (0, react_jsx_runtime.jsx)("button", {
22119
- type: "button",
22120
- style: ui.danger,
22121
- onClick: () => {
22122
- EdusAuthStore.logout();
22123
- close();
22124
- },
22125
- children: "退出"
22126
- })
22127
- ] }) : (0, react_jsx_runtime.jsx)("button", {
22128
- type: "button",
22129
- style: ui.quiet,
22130
- onClick: () => setConfirmLogout(true),
22131
- children: "退出登录"
22132
- })
22133
22170
  })
22134
- ]
22135
- })
22171
+ })
22172
+ ]
22136
22173
  });
22137
22174
  }
22138
22175
  //#endregion
22139
22176
  //#region lib/types/client/MyWorksPanel.js
22140
- const PAGE_SIZE = 8;
22177
+ const PAGE_SIZE = 20;
22141
22178
  function Icon({ name, size = 16 }) {
22142
22179
  return (0, react_jsx_runtime.jsx)("svg", {
22143
22180
  "aria-hidden": "true",
@@ -22337,14 +22374,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22337
22374
  fontSize: 13,
22338
22375
  textAlign: "center"
22339
22376
  },
22340
- footer: {
22377
+ more: {
22341
22378
  display: "flex",
22342
22379
  alignItems: "center",
22343
- justifyContent: "space-between",
22344
- gap: 12,
22345
- padding: "14px 24px 18px",
22346
- borderTop: "1px solid var(--dsw-alias-border-l2)",
22347
- color: "var(--dsw-alias-label-secondary)",
22380
+ justifyContent: "center",
22381
+ gap: 8,
22382
+ minHeight: 48,
22383
+ marginTop: 16,
22384
+ color: "var(--dsw-alias-label-tertiary)",
22348
22385
  fontSize: 12
22349
22386
  },
22350
22387
  login: {
@@ -22404,7 +22441,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22404
22441
  size
22405
22442
  });
22406
22443
  }
22407
- /** 主列的「我的作品」页:登录后按页展示当前账号发布过的网页作品。 */
22444
+ /** 主列的「我的作品」页:登录后展示当前账号发布过的网页作品,滚动到底部自动加载下一页。 */
22408
22445
  function MyWorksPanel({ remote }) {
22409
22446
  const account = useEdusLogin();
22410
22447
  const [list, setList] = (0, react.useState)([]);
@@ -22413,26 +22450,81 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22413
22450
  const [searchInput, setSearchInput] = (0, react.useState)("");
22414
22451
  const [search, setSearch] = (0, react.useState)("");
22415
22452
  const [loading, setLoading] = (0, react.useState)(false);
22453
+ const [loadingMore, setLoadingMore] = (0, react.useState)(false);
22416
22454
  const [sharing, setSharing] = (0, react.useState)(null);
22417
22455
  const [error, setError] = (0, react.useState)("");
22418
22456
  const [notice, setNotice] = (0, react.useState)("");
22419
- const pages = Math.max(1, Math.ceil(total / PAGE_SIZE));
22457
+ const hasMore = list.length < total;
22458
+ const token = account?.token;
22459
+ const bodyRef = (0, react.useRef)(null);
22460
+ const sentinelRef = (0, react.useRef)(null);
22461
+ const requestRef = (0, react.useRef)(0);
22462
+ /** 第 1 页替换列表并回到顶部,后续页追加到末尾。 */
22420
22463
  const load = (0, react.useCallback)(async (token, nextPage, nextSearch) => {
22421
- setLoading(true);
22464
+ const id = ++requestRef.current;
22465
+ const append = nextPage > 1;
22466
+ if (append) setLoadingMore(true);
22467
+ else {
22468
+ setLoading(true);
22469
+ setLoadingMore(false);
22470
+ bodyRef.current?.scrollTo({ top: 0 });
22471
+ }
22422
22472
  setError("");
22423
- setNotice("");
22473
+ if (!append) setNotice("");
22424
22474
  try {
22425
22475
  const result = await remote.htmlList(token, nextPage, PAGE_SIZE, nextSearch || void 0);
22426
- setList(result.list);
22427
- setTotal(result.total);
22476
+ if (id !== requestRef.current) return;
22477
+ setList((prev) => {
22478
+ if (!append) return result.list;
22479
+ const seen = new Set(prev.map((work) => work.id));
22480
+ return [...prev, ...result.list.filter((work) => !seen.has(work.id))];
22481
+ });
22482
+ setTotal(append && result.list.length === 0 ? 0 : result.total);
22428
22483
  setPage(result.page);
22429
22484
  setSearch(nextSearch);
22430
22485
  } catch (reason) {
22486
+ if (id !== requestRef.current) return;
22431
22487
  setError(reason instanceof Error ? reason.message : String(reason));
22432
22488
  } finally {
22433
- setLoading(false);
22489
+ if (id === requestRef.current) {
22490
+ setLoading(false);
22491
+ setLoadingMore(false);
22492
+ }
22434
22493
  }
22435
22494
  }, [remote]);
22495
+ const loadMore = (0, react.useCallback)(() => {
22496
+ if (token === void 0 || loading || loadingMore || !hasMore) return;
22497
+ load(token, page + 1, search);
22498
+ }, [
22499
+ hasMore,
22500
+ load,
22501
+ loading,
22502
+ loadingMore,
22503
+ page,
22504
+ search,
22505
+ token
22506
+ ]);
22507
+ /**
22508
+ * 哨兵元素进入滚动区可视范围(预留 200px)时加载下一页。依赖变化会重建观察器,
22509
+ * observe() 会立即回报一次当前状态,所以首屏内容不足一屏时也会继续补页。
22510
+ * 出错后停止自动加载,改由底部的重试按钮触发,避免失败请求循环。
22511
+ */
22512
+ (0, react.useEffect)(() => {
22513
+ const root = bodyRef.current, target = sentinelRef.current;
22514
+ if (root === null || target === null || error !== "") return;
22515
+ const observer = new IntersectionObserver((entries) => {
22516
+ if (entries.some((entry) => entry.isIntersecting)) loadMore();
22517
+ }, {
22518
+ root,
22519
+ rootMargin: "0px 0px 200px 0px"
22520
+ });
22521
+ observer.observe(target);
22522
+ return () => observer.disconnect();
22523
+ }, [
22524
+ error,
22525
+ loadMore,
22526
+ list.length
22527
+ ]);
22436
22528
  const share = async (work, action) => {
22437
22529
  if (account === null) return;
22438
22530
  const popup = action === "open" ? window.open("about:blank", "_blank") : null;
@@ -22461,17 +22553,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22461
22553
  * 这也顺带把过期 token 挡在渲染之前。登录在统一弹窗里完成,跟随 token 变化
22462
22554
  * 重新拉取,弹窗登录成功后列表随即出现。
22463
22555
  */
22464
- const token = account?.token;
22465
22556
  (0, react.useEffect)(() => {
22466
22557
  setNotice("");
22467
22558
  const stored = EdusAuthStore.getSnapshot();
22468
22559
  if (stored === null) {
22560
+ requestRef.current++;
22469
22561
  setList([]);
22470
22562
  setTotal(0);
22471
22563
  setPage(1);
22472
22564
  setSearch("");
22473
22565
  setSearchInput("");
22474
22566
  setLoading(false);
22567
+ setLoadingMore(false);
22475
22568
  return;
22476
22569
  }
22477
22570
  setError("");
@@ -22540,150 +22633,139 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22540
22633
  type: "button",
22541
22634
  style: styles.button,
22542
22635
  disabled: loading,
22543
- onClick: () => void load(account.token, page, search),
22636
+ onClick: () => {
22637
+ setSearchInput("");
22638
+ load(account.token, 1, "");
22639
+ },
22544
22640
  children: [(0, react_jsx_runtime.jsx)(Icon, {
22545
22641
  name: "refresh",
22546
22642
  size: 15
22547
- }), "刷新"]
22643
+ }), "重置"]
22644
+ }),
22645
+ notice !== "" && (0, react_jsx_runtime.jsx)("span", {
22646
+ role: "status",
22647
+ style: styles.toast,
22648
+ children: notice
22548
22649
  })
22549
22650
  ]
22550
22651
  }),
22551
22652
  (0, react_jsx_runtime.jsxs)("div", {
22653
+ ref: bodyRef,
22552
22654
  style: styles.body,
22553
- children: [account === null ? (0, react_jsx_runtime.jsxs)("div", {
22554
- style: styles.login,
22555
- children: [
22556
- (0, react_jsx_runtime.jsx)(Icon, {
22557
- name: "folder",
22558
- size: 30
22559
- }),
22560
- (0, react_jsx_runtime.jsx)("span", { children: "作品列表仅展示当前账号创建的内容" }),
22561
- error !== "" && (0, react_jsx_runtime.jsx)("p", {
22562
- style: styles.error,
22563
- children: error
22564
- }),
22565
- (0, react_jsx_runtime.jsx)("button", {
22566
- type: "button",
22567
- style: {
22568
- ...styles.button,
22569
- ...styles.primary,
22570
- ...styles.loginButton
22571
- },
22572
- onClick: EdusLoginDialog.open,
22573
- children: "立即登录"
22574
- })
22575
- ]
22576
- }) : loading ? (0, react_jsx_runtime.jsx)("div", {
22577
- style: styles.center,
22578
- children: "正在读取作品列表…"
22579
- }) : list.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
22580
- style: styles.center,
22581
- children: (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)(Icon, {
22582
- name: "folder",
22583
- size: 30
22584
- }), (0, react_jsx_runtime.jsx)("p", { children: search === "" ? "还没有发布过作品" : "没有找到匹配的作品" })] })
22585
- }) : (0, react_jsx_runtime.jsx)("div", {
22586
- style: styles.grid,
22587
- children: list.map((work) => (0, react_jsx_runtime.jsxs)("article", {
22588
- style: styles.card,
22655
+ children: [
22656
+ account === null ? (0, react_jsx_runtime.jsxs)("div", {
22657
+ style: styles.login,
22589
22658
  children: [
22590
- work.thumb ? (0, react_jsx_runtime.jsx)("img", {
22591
- alt: "",
22592
- src: work.thumb,
22593
- style: styles.thumb
22594
- }) : (0, react_jsx_runtime.jsx)("div", {
22595
- style: styles.placeholder,
22596
- children: "暂无预览图"
22659
+ (0, react_jsx_runtime.jsx)(Icon, {
22660
+ name: "folder",
22661
+ size: 30
22597
22662
  }),
22598
- (0, react_jsx_runtime.jsxs)("div", {
22599
- style: styles.cardBody,
22600
- children: [(0, react_jsx_runtime.jsx)("h3", {
22601
- title: work.title,
22602
- style: styles.cardTitle,
22603
- children: work.title || "未命名作品"
22604
- }), (0, react_jsx_runtime.jsxs)("div", {
22605
- style: styles.meta,
22606
- children: [
22607
- (0, react_jsx_runtime.jsx)("span", {
22608
- style: styles.badge,
22609
- children: statusOf(work.status)
22610
- }),
22611
- (0, react_jsx_runtime.jsx)("span", { children: formatDate(work.updatedAt) }),
22612
- (0, react_jsx_runtime.jsxs)("span", {
22613
- style: { marginLeft: "auto" },
22614
- children: [work.views, " 次浏览"]
22615
- })
22616
- ]
22617
- })]
22663
+ (0, react_jsx_runtime.jsx)("span", { children: "作品列表仅展示当前账号创建的内容" }),
22664
+ error !== "" && (0, react_jsx_runtime.jsx)("p", {
22665
+ style: styles.error,
22666
+ children: error
22618
22667
  }),
22619
- (0, react_jsx_runtime.jsxs)("div", {
22620
- style: styles.cardActions,
22621
- children: [(0, react_jsx_runtime.jsxs)("button", {
22622
- type: "button",
22623
- style: styles.cardAction,
22624
- disabled: sharing === work.id || work.status !== 2,
22625
- onClick: () => void share(work, "open"),
22626
- children: [(0, react_jsx_runtime.jsx)(Icon, {
22627
- name: "open",
22628
- size: 14
22629
- }), "打开"]
22630
- }), (0, react_jsx_runtime.jsxs)("button", {
22631
- type: "button",
22632
- style: {
22633
- ...styles.cardAction,
22634
- borderRight: 0
22635
- },
22636
- disabled: sharing === work.id || work.status !== 2,
22637
- onClick: () => void share(work, "copy"),
22638
- children: [(0, react_jsx_runtime.jsx)(Icon, {
22639
- name: "copy",
22640
- size: 14
22641
- }), sharing === work.id ? "处理中…" : "复制链接"]
22642
- })]
22668
+ (0, react_jsx_runtime.jsx)("button", {
22669
+ type: "button",
22670
+ style: {
22671
+ ...styles.button,
22672
+ ...styles.primary,
22673
+ ...styles.loginButton
22674
+ },
22675
+ onClick: EdusLoginDialog.open,
22676
+ children: "立即登录"
22643
22677
  })
22644
22678
  ]
22645
- }, work.id))
22646
- }), account !== null && error !== "" && (0, react_jsx_runtime.jsx)("p", {
22647
- style: {
22648
- ...styles.error,
22649
- marginTop: 14
22650
- },
22651
- children: error
22652
- })]
22653
- }),
22654
- account !== null && (0, react_jsx_runtime.jsxs)("footer", {
22655
- style: styles.footer,
22656
- children: [
22657
- (0, react_jsx_runtime.jsxs)("span", { children: [
22658
- "第 ",
22659
- page,
22660
- " / ",
22661
- pages,
22662
- " 页"
22663
- ] }),
22664
- notice !== "" && (0, react_jsx_runtime.jsx)("span", {
22665
- role: "status",
22666
- style: styles.toast,
22667
- children: notice
22679
+ }) : loading ? (0, react_jsx_runtime.jsx)("div", {
22680
+ style: styles.center,
22681
+ children: "正在读取作品列表…"
22682
+ }) : list.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
22683
+ style: styles.center,
22684
+ children: (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)(Icon, {
22685
+ name: "folder",
22686
+ size: 30
22687
+ }), (0, react_jsx_runtime.jsx)("p", { children: search === "" ? "还没有发布过作品" : "没有找到匹配的作品" })] })
22688
+ }) : (0, react_jsx_runtime.jsx)("div", {
22689
+ style: styles.grid,
22690
+ children: list.map((work) => (0, react_jsx_runtime.jsxs)("article", {
22691
+ style: styles.card,
22692
+ children: [
22693
+ work.thumb ? (0, react_jsx_runtime.jsx)("img", {
22694
+ alt: "",
22695
+ src: work.thumb,
22696
+ style: styles.thumb
22697
+ }) : (0, react_jsx_runtime.jsx)("div", {
22698
+ style: styles.placeholder,
22699
+ children: "暂无预览图"
22700
+ }),
22701
+ (0, react_jsx_runtime.jsxs)("div", {
22702
+ style: styles.cardBody,
22703
+ children: [(0, react_jsx_runtime.jsx)("h3", {
22704
+ title: work.title,
22705
+ style: styles.cardTitle,
22706
+ children: work.title || "未命名作品"
22707
+ }), (0, react_jsx_runtime.jsxs)("div", {
22708
+ style: styles.meta,
22709
+ children: [
22710
+ (0, react_jsx_runtime.jsx)("span", {
22711
+ style: styles.badge,
22712
+ children: statusOf(work.status)
22713
+ }),
22714
+ (0, react_jsx_runtime.jsx)("span", { children: formatDate(work.updatedAt) }),
22715
+ (0, react_jsx_runtime.jsxs)("span", {
22716
+ style: { marginLeft: "auto" },
22717
+ children: [work.views, " 次浏览"]
22718
+ })
22719
+ ]
22720
+ })]
22721
+ }),
22722
+ (0, react_jsx_runtime.jsxs)("div", {
22723
+ style: styles.cardActions,
22724
+ children: [(0, react_jsx_runtime.jsxs)("button", {
22725
+ type: "button",
22726
+ style: styles.cardAction,
22727
+ disabled: sharing === work.id || work.status !== 2,
22728
+ onClick: () => void share(work, "open"),
22729
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
22730
+ name: "open",
22731
+ size: 14
22732
+ }), "打开"]
22733
+ }), (0, react_jsx_runtime.jsxs)("button", {
22734
+ type: "button",
22735
+ style: {
22736
+ ...styles.cardAction,
22737
+ borderRight: 0
22738
+ },
22739
+ disabled: sharing === work.id || work.status !== 2,
22740
+ onClick: () => void share(work, "copy"),
22741
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
22742
+ name: "copy",
22743
+ size: 14
22744
+ }), sharing === work.id ? "处理中…" : "复制链接"]
22745
+ })]
22746
+ })
22747
+ ]
22748
+ }, work.id))
22668
22749
  }),
22669
- (0, react_jsx_runtime.jsxs)("div", {
22750
+ account !== null && error !== "" && (0, react_jsx_runtime.jsx)("p", {
22670
22751
  style: {
22671
- display: "flex",
22672
- gap: 8
22752
+ ...styles.error,
22753
+ marginTop: 14
22673
22754
  },
22674
- children: [(0, react_jsx_runtime.jsx)("button", {
22675
- type: "button",
22676
- style: styles.button,
22677
- disabled: loading || page <= 1,
22678
- onClick: () => void load(account.token, page - 1, search),
22679
- children: "上一页"
22680
- }), (0, react_jsx_runtime.jsx)("button", {
22755
+ children: error
22756
+ }),
22757
+ account !== null && !loading && list.length > 0 && (0, react_jsx_runtime.jsx)("div", {
22758
+ ref: sentinelRef,
22759
+ style: styles.more,
22760
+ children: loadingMore ? "正在加载更多…" : error !== "" && hasMore ? (0, react_jsx_runtime.jsx)("button", {
22681
22761
  type: "button",
22682
22762
  style: styles.button,
22683
- disabled: loading || page >= pages,
22684
- onClick: () => void load(account.token, page + 1, search),
22685
- children: "下一页"
22686
- })]
22763
+ onClick: () => {
22764
+ setError("");
22765
+ loadMore();
22766
+ },
22767
+ children: "重试加载"
22768
+ }) : hasMore ? "" : `已加载全部 ${list.length} 个作品`
22687
22769
  })
22688
22770
  ]
22689
22771
  })
@@ -22729,7 +22811,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22729
22811
  label: () => "教学网页",
22730
22812
  description: () => "根据教学需求和配图,生成适合课堂投屏的网页",
22731
22813
  icon: _deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular,
22732
- section: () => "任务"
22814
+ section: () => "任务",
22815
+ token: () => "教学网页"
22733
22816
  }), "edus webpage command face");
22734
22817
  });
22735
22818
  }