@houser/edus-plugin 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,7 +34,7 @@ npm run dev
34
34
  │ ├── Brand.tsx # 品牌名称与 Logo React 组件
35
35
  │ └── logo.png # 裁切、缩放后的透明 Logo 源文件
36
36
  ├── scripts/
37
- │ └── embed-logo.mjs # 构建时将 PNG 内联为 data URL
37
+ │ └── generate-typert.mjs # 构建时生成 typert 协议桥接代码
38
38
  └── lib/ # 构建产物(自动生成,不提交)
39
39
  ├── index.js
40
40
  ├── client.js
@@ -70,6 +70,52 @@ npm run build # 清理后依次执行以上两步
70
70
 
71
71
  ## 发布版本
72
72
 
73
- 发布版本的时候必须指定 tag,tag 会关联 deepseek harness 版本(一般就是当前分支的名称)
73
+ ### 必须先构建
74
74
 
75
- 示例 `npm publish --registry=https://registry.npmjs.org --tag dsh-v0.1.6-alpha.2`
75
+ `package.json` 中没有 `prepare` / `prepublishOnly` / `prepack` 钩子,`npm publish` **不会**自动构建。而 `files` 字段声明的全部是 `lib/` 下的产物,且 `lib/` 已被 `.gitignore` 忽略。因此发布前必须手动执行构建,否则会把上一次遗留的旧产物发出去,或发出一个缺少入口文件的空包。
76
+
77
+ ### 发布步骤
78
+
79
+ ```bash
80
+ # 1. 同步依赖(切换分支或改过 peerDependencies 后必做)
81
+ pnpm install
82
+
83
+ # 2. 构建 + 测试:clean → build:host → build:client → vitest
84
+ npm run check
85
+
86
+ # 3. 确认打包内容:应包含 lib/index.js、lib/client.js、
87
+ # lib/typert.*.js、lib/types/**/*.d.ts、cordis.yml
88
+ npm run pack:dry
89
+
90
+ # 4. 升版本号(会自动创建 git commit 和 tag)
91
+ npm version patch
92
+
93
+ # 5. 发布,必须带 --tag
94
+ npm publish --registry=https://registry.npmjs.org --tag dsh-v0.1.6-alpha.2
95
+
96
+ # 6. 推送版本提交
97
+ git push && git push --tags
98
+
99
+ # npm 需要登录
100
+ npm login --registry=https://registry.npmjs.org/
101
+ ```
102
+
103
+ ### 关于 `--tag`
104
+
105
+ 发布必须指定 npm dist-tag,用于关联对应的 DeepSeek Harness 版本(一般就是当前分支名称)。**不要**使用默认的 `latest`:不同 Harness 版本的 peerDependencies 不兼容,混用 `latest` 会让安装方拿到与自己 Harness 版本不匹配的插件。
106
+
107
+ dist-tag 取值与 `peerDependencies` 中 `@deepseek-ai/dsh-*` 的版本保持一致,例如当前 peer 版本为 `0.1.6-alpha.2`,则 tag 为 `dsh-v0.1.6-alpha.2`。
108
+
109
+ 安装方按 tag 取包:
110
+
111
+ ```bash
112
+ npm install @houser/edus-plugin@dsh-v0.1.6-alpha.2
113
+ ```
114
+
115
+ ### 发布前检查清单
116
+
117
+ - [ ] `peerDependencies` 中的 `@deepseek-ai/dsh-*` 版本与目标 Harness 版本一致
118
+ - [ ] `npm run check` 通过(构建 + 测试)
119
+ - [ ] `npm run pack:dry` 的文件列表完整,且不含 `src/`、`node_modules/` 等多余内容
120
+ - [ ] 替换过 `src/client/logo.png` 的话,已重新构建(data URL 在构建时生成)
121
+ - [ ] `--tag` 与目标 Harness 版本对应,且不是 `latest`
package/lib/client.js CHANGED
@@ -6351,9 +6351,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6351
6351
  ...base.error,
6352
6352
  ...theme.error
6353
6353
  };
6354
+ const busyRef = (0, react.useRef)(busy);
6355
+ const busyChangeRef = (0, react.useRef)(onBusyChange);
6356
+ busyRef.current = busy;
6357
+ busyChangeRef.current = onBusyChange;
6354
6358
  (0, react.useEffect)(() => {
6355
6359
  onBusyChange?.(busy);
6356
6360
  }, [busy, onBusyChange]);
6361
+ (0, react.useEffect)(() => () => {
6362
+ if (busyRef.current) busyChangeRef.current?.(false);
6363
+ }, []);
6357
6364
  const switchMode = (next) => {
6358
6365
  setMode(next);
6359
6366
  setError("");
@@ -6495,6 +6502,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6495
6502
  function basename(path) {
6496
6503
  return path.split("/").at(-1) ?? path;
6497
6504
  }
6505
+ /** 后端在未开通会员时返回的提示里带有这个字眼,此时改为引导扫码联系客服。 */
6506
+ const MEMBERSHIP_HINT = "需要开通会员";
6507
+ const SERVICE_QR = "https://cdn.juzhenai.cn/ai-edu/assets/wechat.webp";
6498
6508
  const styles$1 = {
6499
6509
  root: {
6500
6510
  display: "flex",
@@ -6668,6 +6678,36 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6668
6678
  fontSize: 18,
6669
6679
  fontWeight: 700
6670
6680
  },
6681
+ memberCard: {
6682
+ display: "flex",
6683
+ gap: 14,
6684
+ alignItems: "center",
6685
+ margin: "14px 0 0",
6686
+ padding: 13,
6687
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, var(--dsw-alias-border-l2))",
6688
+ borderRadius: 11,
6689
+ background: "var(--dsw-alias-bg-layer-1)"
6690
+ },
6691
+ memberQr: {
6692
+ flex: "none",
6693
+ width: 96,
6694
+ height: 96,
6695
+ borderRadius: 8,
6696
+ background: "#fff",
6697
+ objectFit: "contain"
6698
+ },
6699
+ memberTitle: {
6700
+ margin: 0,
6701
+ color: "var(--dsw-alias-label-primary)",
6702
+ fontSize: 13,
6703
+ fontWeight: 650
6704
+ },
6705
+ memberText: {
6706
+ margin: "5px 0 0",
6707
+ color: "var(--dsw-alias-label-secondary)",
6708
+ fontSize: 12,
6709
+ lineHeight: 1.55
6710
+ },
6671
6711
  accountCard: {
6672
6712
  margin: "0 0 15px",
6673
6713
  padding: "9px 11px",
@@ -6679,6 +6719,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6679
6719
  lineHeight: 1.5
6680
6720
  }
6681
6721
  };
6722
+ /** 普通报错直接展示文案;会员相关的报错额外给出客服二维码。 */
6723
+ function PublishError({ message }) {
6724
+ if (message === "") return null;
6725
+ if (!message.includes(MEMBERSHIP_HINT)) return (0, react_jsx_runtime.jsx)("p", {
6726
+ style: styles$1.error,
6727
+ children: message
6728
+ });
6729
+ return (0, react_jsx_runtime.jsxs)("div", {
6730
+ style: styles$1.memberCard,
6731
+ children: [(0, react_jsx_runtime.jsx)("img", {
6732
+ alt: "客服微信二维码",
6733
+ src: SERVICE_QR,
6734
+ style: styles$1.memberQr
6735
+ }), (0, react_jsx_runtime.jsxs)("div", {
6736
+ style: { minWidth: 0 },
6737
+ children: [(0, react_jsx_runtime.jsx)("p", {
6738
+ style: styles$1.memberTitle,
6739
+ children: message
6740
+ }), (0, react_jsx_runtime.jsx)("p", {
6741
+ style: styles$1.memberText,
6742
+ children: "该功能需要开通会员后使用。请用微信扫描左侧二维码添加客服咨询开通。"
6743
+ })]
6744
+ })]
6745
+ });
6746
+ }
6682
6747
  /** An extension-priority document body: native right-preview ergonomics plus a complete edus publishing flow. */
6683
6748
  function HtmlPublishPreview({ content, resourceAddress, remote }) {
6684
6749
  const account = useEdusLogin();
@@ -6727,6 +6792,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6727
6792
  const afterLogin = (0, react.useCallback)((result) => {
6728
6793
  EdusAuthStore.set(result);
6729
6794
  setError("");
6795
+ setBusy(false);
6730
6796
  setDestination(marker !== null && marker.owner === result.phone ? "current" : "new");
6731
6797
  setStage("destination");
6732
6798
  }, [marker]);
@@ -6909,10 +6975,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6909
6975
  onChange: (event) => setTitle(event.target.value)
6910
6976
  })]
6911
6977
  }),
6912
- error !== "" && (0, react_jsx_runtime.jsx)("p", {
6913
- style: styles$1.error,
6914
- children: error
6915
- }),
6978
+ (0, react_jsx_runtime.jsx)(PublishError, { message: error }),
6916
6979
  (0, react_jsx_runtime.jsxs)("div", {
6917
6980
  style: styles$1.actions,
6918
6981
  children: [(0, react_jsx_runtime.jsx)("button", {
@@ -6983,27 +7046,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6983
7046
  });
6984
7047
  }
6985
7048
  //#endregion
6986
- //#region lib/types/client/MyWorksAction.js
7049
+ //#region lib/types/client/MyWorksPanel.js
6987
7050
  const PAGE_SIZE = 8;
6988
- let dialogOpen = false;
6989
- const dialogListeners = /* @__PURE__ */ new Set();
6990
- const dialog = {
6991
- getSnapshot: () => dialogOpen,
6992
- subscribe(listener) {
6993
- dialogListeners.add(listener);
6994
- return () => {
6995
- dialogListeners.delete(listener);
6996
- };
6997
- },
6998
- open() {
6999
- dialogOpen = true;
7000
- for (const listener of dialogListeners) listener();
7001
- },
7002
- close() {
7003
- dialogOpen = false;
7004
- for (const listener of dialogListeners) listener();
7005
- }
7006
- };
7007
7051
  function Icon({ name, size = 16 }) {
7008
7052
  return (0, react_jsx_runtime.jsx)("svg", {
7009
7053
  "aria-hidden": "true",
@@ -7016,7 +7060,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7016
7060
  strokeLinecap: "round",
7017
7061
  strokeLinejoin: "round",
7018
7062
  children: {
7019
- close: (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: (0, react_jsx_runtime.jsx)("path", { d: "M3 3l10 10M13 3L3 13" }) }),
7020
7063
  copy: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("rect", {
7021
7064
  x: "5.5",
7022
7065
  y: "5.5",
@@ -7049,72 +7092,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7049
7092
  }
7050
7093
  }
7051
7094
  const styles = {
7052
- triggerRow: {
7053
- width: "calc(100% + 4px)",
7054
- display: "flex",
7055
- alignItems: "center",
7056
- gap: 8,
7057
- flex: "none",
7058
- margin: "4px -2px"
7059
- },
7060
- triggerRowRail: {
7061
- width: 36,
7062
- margin: "8px 0 10px"
7063
- },
7064
- trigger: {
7065
- boxSizing: "border-box",
7066
- width: "auto",
7095
+ page: {
7067
7096
  minWidth: 0,
7068
- height: 42,
7069
- display: "flex",
7070
- alignItems: "center",
7071
- justifyContent: "flex-start",
7097
+ minHeight: 0,
7072
7098
  flex: 1,
7073
- gap: 8,
7074
- margin: 0,
7075
- padding: "0 10px 0 8px",
7076
- overflow: "hidden",
7077
- border: 0,
7078
- borderRadius: 12,
7079
- background: "transparent",
7080
- color: "var(--dsw-alias-label-primary)",
7081
- font: "inherit",
7082
- fontSize: 14,
7083
- lineHeight: "22px",
7084
- cursor: "pointer",
7085
- whiteSpace: "nowrap"
7086
- },
7087
- triggerRail: {
7088
- width: 36,
7089
- height: 36,
7090
- flex: "none",
7091
- gap: 0,
7092
- padding: 0,
7093
- borderRadius: "50%",
7094
- justifyContent: "center"
7095
- },
7096
- overlay: {
7097
- position: "fixed",
7098
- inset: 0,
7099
- zIndex: 1200,
7100
- display: "grid",
7101
- placeItems: "center",
7102
- padding: 24,
7103
- background: "var(--dsw-alias-bg-mask-1, rgb(0 0 0 / 32%))",
7104
- backdropFilter: "blur(5px)",
7105
- pointerEvents: "auto"
7106
- },
7107
- dialog: {
7108
- width: "min(960px, calc(100vw - 32px))",
7109
- height: "min(780px, calc(100vh - 32px))",
7110
7099
  display: "flex",
7111
7100
  flexDirection: "column",
7112
7101
  overflow: "hidden",
7113
- border: "1px solid var(--dsw-alias-border-inverted)",
7114
- borderRadius: 22,
7115
- background: "var(--dsw-alias-bg-layer-2)",
7116
- color: "var(--dsw-alias-label-primary)",
7117
- boxShadow: "var(--dsw-shadow-lv3, 0 28px 90px rgb(0 0 0 / 30%))"
7102
+ background: "var(--dsw-alias-bg-base)",
7103
+ color: "var(--dsw-alias-label-primary)"
7118
7104
  },
7119
7105
  header: {
7120
7106
  display: "flex",
@@ -7140,18 +7126,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7140
7126
  alignItems: "center",
7141
7127
  gap: 12
7142
7128
  },
7143
- close: {
7144
- width: 32,
7145
- height: 32,
7146
- display: "grid",
7147
- placeItems: "center",
7148
- flex: "none",
7149
- border: 0,
7150
- borderRadius: 9,
7151
- background: "transparent",
7152
- color: "var(--dsw-alias-label-secondary)",
7153
- cursor: "pointer"
7154
- },
7155
7129
  toolbar: {
7156
7130
  display: "flex",
7157
7131
  alignItems: "center",
@@ -7162,6 +7136,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7162
7136
  search: {
7163
7137
  minWidth: 0,
7164
7138
  flex: 1,
7139
+ maxWidth: 420,
7165
7140
  height: 36,
7166
7141
  boxSizing: "border-box",
7167
7142
  border: "1px solid var(--dsw-alias-border-l2)",
@@ -7209,7 +7184,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7209
7184
  overflow: "hidden",
7210
7185
  border: "1px solid var(--dsw-alias-border-l2)",
7211
7186
  borderRadius: 14,
7212
- background: "var(--dsw-alias-bg-base)"
7187
+ background: "var(--dsw-alias-bg-layer-2)"
7213
7188
  },
7214
7189
  thumb: {
7215
7190
  aspectRatio: "3 / 2",
@@ -7240,14 +7215,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7240
7215
  textOverflow: "ellipsis",
7241
7216
  whiteSpace: "nowrap"
7242
7217
  },
7243
- description: {
7244
- height: 38,
7245
- margin: "7px 0 11px",
7246
- overflow: "hidden",
7247
- color: "var(--dsw-alias-label-secondary)",
7248
- fontSize: 12,
7249
- lineHeight: 1.55
7250
- },
7251
7218
  meta: {
7252
7219
  display: "flex",
7253
7220
  alignItems: "center",
@@ -7336,7 +7303,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7336
7303
  border: 0,
7337
7304
  padding: 0,
7338
7305
  background: "transparent",
7339
- color: "var(--dsw-alias-label-tertiary)",
7306
+ color: "orange",
7340
7307
  font: "inherit",
7341
7308
  fontSize: 12,
7342
7309
  lineHeight: 1,
@@ -7377,37 +7344,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7377
7344
  if (status === 3) return "失败";
7378
7345
  return `状态 ${status}`;
7379
7346
  }
7380
- function MyWorksAction({ wide }) {
7381
- return (0, react_jsx_runtime.jsx)("div", {
7382
- style: {
7383
- ...styles.triggerRow,
7384
- ...!wide ? styles.triggerRowRail : {}
7385
- },
7386
- children: (0, react_jsx_runtime.jsxs)("button", {
7387
- type: "button",
7388
- "aria-label": "我的作品",
7389
- title: wide ? void 0 : "我的作品",
7390
- style: {
7391
- ...styles.trigger,
7392
- ...!wide ? styles.triggerRail : {}
7393
- },
7394
- onClick: dialog.open,
7395
- onMouseEnter: (event) => {
7396
- event.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover)";
7397
- },
7398
- onMouseLeave: (event) => {
7399
- event.currentTarget.style.background = "transparent";
7400
- },
7401
- children: [(0, react_jsx_runtime.jsx)(Icon, {
7402
- name: "folder",
7403
- size: wide ? 16 : 18
7404
- }), wide && (0, react_jsx_runtime.jsx)("span", { children: "我的作品" })]
7405
- })
7347
+ /**
7348
+ * 侧边栏「我的作品」行的图标。
7349
+ *
7350
+ * 按钮、标签、选中态都归 ui-sidebar 的 PanelRow:这里只按它要的尺寸画图形,
7351
+ * 并用 currentColor 让选中态的配色由外壳决定。
7352
+ */
7353
+ function MyWorksPanelIcon({ size }) {
7354
+ return (0, react_jsx_runtime.jsx)(Icon, {
7355
+ name: "folder",
7356
+ size
7406
7357
  });
7407
7358
  }
7408
- function MyWorksDialog({ remote }) {
7359
+ /** 主列的「我的作品」页:登录后按页展示当前账号发布过的网页作品。 */
7360
+ function MyWorksPanel({ remote }) {
7409
7361
  const account = useEdusLogin();
7410
- const open = (0, react.useSyncExternalStore)(dialog.subscribe, dialog.getSnapshot, () => false);
7411
7362
  const [list, setList] = (0, react.useState)([]);
7412
7363
  const [page, setPage] = (0, react.useState)(1);
7413
7364
  const [total, setTotal] = (0, react.useState)(0);
@@ -7440,7 +7391,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7440
7391
  setError("");
7441
7392
  await load(result.token, 1, "");
7442
7393
  }, [load]);
7443
- /** 退出后弹窗停留在登录面板,因此顺手把上一个账号的列表状态清干净。 */
7394
+ /** 退出后页面停留在登录面板,因此顺手把上一个账号的列表状态清干净。 */
7444
7395
  const logout = (0, react.useCallback)(() => {
7445
7396
  setConfirmLogout(false);
7446
7397
  EdusAuthStore.logout();
@@ -7476,298 +7427,281 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7476
7427
  setSharing(null);
7477
7428
  }
7478
7429
  };
7430
+ /**
7431
+ * 主列面板在切走时会被卸载,所以每次进入都从存储的登录态重新拉一次列表;
7432
+ * 这也顺带把过期 token 挡在渲染之前。
7433
+ */
7479
7434
  (0, react.useEffect)(() => {
7480
- if (!open) return;
7481
7435
  setError("");
7482
7436
  setNotice("");
7483
7437
  setConfirmLogout(false);
7484
7438
  const stored = EdusAuthStore.getSnapshot();
7485
- if (stored === null) setLoading(false);
7486
- if (stored !== null) {
7487
- setLoading(true);
7488
- remote.loginInfo(stored.token).then(async (refreshed) => {
7489
- EdusAuthStore.set(refreshed);
7490
- await load(refreshed.token, 1, "");
7491
- }).catch(() => {
7492
- EdusAuthStore.logout();
7493
- setError("登录状态已失效,请重新登录。");
7494
- setLoading(false);
7495
- });
7439
+ if (stored === null) {
7440
+ setLoading(false);
7441
+ return;
7496
7442
  }
7497
- const onKeyDown = (event) => {
7498
- if (event.key === "Escape") dialog.close();
7499
- };
7500
- document.addEventListener("keydown", onKeyDown);
7501
- return () => document.removeEventListener("keydown", onKeyDown);
7502
- }, [
7503
- load,
7504
- open,
7505
- remote
7506
- ]);
7507
- if (!open) return null;
7508
- return (0, react_jsx_runtime.jsx)("div", {
7509
- style: styles.overlay,
7510
- role: "presentation",
7511
- onMouseDown: dialog.close,
7512
- children: (0, react_jsx_runtime.jsxs)("section", {
7513
- style: styles.dialog,
7514
- role: "dialog",
7515
- "aria-modal": "true",
7516
- "aria-labelledby": "edus-my-works-title",
7517
- onMouseDown: (event) => event.stopPropagation(),
7518
- children: [
7519
- (0, react_jsx_runtime.jsxs)("header", {
7520
- style: styles.header,
7521
- children: [(0, react_jsx_runtime.jsxs)("div", {
7443
+ setLoading(true);
7444
+ remote.loginInfo(stored.token).then(async (refreshed) => {
7445
+ EdusAuthStore.set(refreshed);
7446
+ await load(refreshed.token, 1, "");
7447
+ }).catch(() => {
7448
+ EdusAuthStore.logout();
7449
+ setError("登录状态已失效,请重新登录。");
7450
+ setLoading(false);
7451
+ });
7452
+ }, [load, remote]);
7453
+ return (0, react_jsx_runtime.jsxs)("section", {
7454
+ style: styles.page,
7455
+ "aria-labelledby": "edus-my-works-title",
7456
+ children: [
7457
+ (0, react_jsx_runtime.jsx)("header", {
7458
+ style: styles.header,
7459
+ children: (0, react_jsx_runtime.jsxs)("div", {
7460
+ style: {
7461
+ minWidth: 0,
7462
+ flex: 1
7463
+ },
7464
+ children: [(0, react_jsx_runtime.jsx)("h2", {
7465
+ id: "edus-my-works-title",
7466
+ style: styles.title,
7467
+ children: "我的作品"
7468
+ }), (0, react_jsx_runtime.jsxs)("p", {
7522
7469
  style: {
7523
- minWidth: 0,
7524
- flex: 1
7470
+ ...styles.subtitle,
7471
+ ...styles.subtitleRow
7525
7472
  },
7526
- children: [(0, react_jsx_runtime.jsx)("h2", {
7527
- id: "edus-my-works-title",
7528
- style: styles.title,
7529
- children: "我的作品"
7530
- }), (0, react_jsx_runtime.jsxs)("p", {
7473
+ children: [(0, react_jsx_runtime.jsx)("span", {
7531
7474
  style: {
7532
- ...styles.subtitle,
7533
- ...styles.subtitleRow
7475
+ minWidth: 0,
7476
+ flex: 1,
7477
+ overflow: "hidden",
7478
+ textOverflow: "ellipsis",
7479
+ whiteSpace: "nowrap"
7534
7480
  },
7535
- children: [(0, react_jsx_runtime.jsx)("span", {
7536
- style: {
7537
- minWidth: 0,
7538
- overflow: "hidden",
7539
- textOverflow: "ellipsis",
7540
- whiteSpace: "nowrap"
7541
- },
7542
- children: account === null ? "登录课成账号,查看你发布过的网页作品。" : `${account.title || account.phone} · 共 ${total} 个作品`
7543
- }), account !== null && (confirmLogout ? (0, react_jsx_runtime.jsxs)("span", {
7544
- style: styles.confirmRow,
7545
- children: [
7546
- (0, react_jsx_runtime.jsx)("span", { children: "确定退出登录?" }),
7547
- (0, react_jsx_runtime.jsx)("button", {
7548
- type: "button",
7549
- style: styles.danger,
7550
- onClick: logout,
7551
- children: "退出"
7552
- }),
7553
- (0, react_jsx_runtime.jsx)("button", {
7554
- type: "button",
7555
- style: styles.quiet,
7556
- onClick: () => setConfirmLogout(false),
7557
- children: "取消"
7558
- })
7559
- ]
7560
- }) : (0, react_jsx_runtime.jsxs)("button", {
7561
- type: "button",
7562
- title: "退出登录",
7563
- style: { ...styles.quiet },
7564
- onClick: () => setConfirmLogout(true),
7565
- children: [(0, react_jsx_runtime.jsx)(Icon, {
7566
- name: "logout",
7567
- size: 13
7568
- }), "退出登录"]
7569
- }))]
7570
- })]
7571
- }), (0, react_jsx_runtime.jsx)("button", {
7572
- type: "button",
7573
- "aria-label": "关闭我的作品",
7574
- style: styles.close,
7575
- onClick: dialog.close,
7576
- children: (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
7577
- })]
7578
- }),
7579
- account !== null && (0, react_jsx_runtime.jsxs)("form", {
7580
- style: styles.toolbar,
7581
- onSubmit: (event) => {
7582
- event.preventDefault();
7583
- load(account.token, 1, searchInput.trim());
7584
- },
7585
- children: [
7586
- (0, react_jsx_runtime.jsx)("input", {
7587
- "aria-label": "搜索作品",
7588
- style: styles.search,
7589
- value: searchInput,
7590
- placeholder: "搜索标题或描述",
7591
- onChange: (event) => setSearchInput(event.target.value)
7592
- }),
7593
- (0, react_jsx_runtime.jsxs)("button", {
7594
- type: "submit",
7595
- style: {
7596
- ...styles.button,
7597
- ...styles.primary
7598
- },
7599
- disabled: loading,
7600
- children: [(0, react_jsx_runtime.jsx)(Icon, {
7601
- name: "search",
7602
- size: 15
7603
- }), "搜索"]
7604
- }),
7605
- (0, react_jsx_runtime.jsxs)("button", {
7606
- type: "button",
7607
- style: styles.button,
7608
- disabled: loading,
7609
- onClick: () => void load(account.token, page, search),
7610
- children: [(0, react_jsx_runtime.jsx)(Icon, {
7611
- name: "refresh",
7612
- size: 15
7613
- }), "刷新"]
7614
- })
7615
- ]
7616
- }),
7617
- (0, react_jsx_runtime.jsxs)("div", {
7618
- style: styles.body,
7619
- children: [account === null ? (0, react_jsx_runtime.jsx)("div", {
7620
- style: styles.login,
7621
- children: (0, react_jsx_runtime.jsx)(EdusLoginPanel, {
7622
- remote,
7623
- intro: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("strong", { children: "登录课成账号" }), (0, react_jsx_runtime.jsx)("p", {
7624
- style: {
7625
- ...styles.subtitle,
7626
- margin: "5px 0 0"
7627
- },
7628
- children: "作品列表仅展示当前账号创建的内容。"
7629
- })] }),
7630
- notice: error,
7631
- submitLabel: "登录并查看作品",
7632
- onSuccess: afterLogin,
7633
- theme: {
7634
- field: styles.field,
7635
- button: styles.button,
7636
- primary: {
7637
- ...styles.button,
7638
- ...styles.primary,
7639
- height: 40
7640
- },
7641
- error: styles.error
7642
- }
7643
- })
7644
- }) : loading ? (0, react_jsx_runtime.jsx)("div", {
7645
- style: styles.center,
7646
- children: "正在读取作品列表…"
7647
- }) : list.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
7648
- style: styles.center,
7649
- children: (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)(Icon, {
7650
- name: "folder",
7651
- size: 30
7652
- }), (0, react_jsx_runtime.jsx)("p", { children: search === "" ? "还没有发布过作品" : "没有找到匹配的作品" })] })
7653
- }) : (0, react_jsx_runtime.jsx)("div", {
7654
- style: styles.grid,
7655
- children: list.map((work) => (0, react_jsx_runtime.jsxs)("article", {
7656
- style: styles.card,
7481
+ children: account === null ? "登录课成账号,查看你发布过的网页作品。" : `${account.title || account.phone} · 共 ${total} 个作品`
7482
+ }), account !== null && (confirmLogout ? (0, react_jsx_runtime.jsxs)("span", {
7483
+ style: styles.confirmRow,
7657
7484
  children: [
7658
- work.thumb ? (0, react_jsx_runtime.jsx)("img", {
7659
- alt: "",
7660
- src: work.thumb,
7661
- style: styles.thumb
7662
- }) : (0, react_jsx_runtime.jsx)("div", {
7663
- style: styles.placeholder,
7664
- children: "暂无预览图"
7485
+ (0, react_jsx_runtime.jsx)("span", { children: "确定退出登录?" }),
7486
+ (0, react_jsx_runtime.jsx)("button", {
7487
+ type: "button",
7488
+ style: styles.danger,
7489
+ onClick: logout,
7490
+ children: "退出"
7665
7491
  }),
7666
- (0, react_jsx_runtime.jsxs)("div", {
7667
- style: styles.cardBody,
7668
- children: [(0, react_jsx_runtime.jsx)("h3", {
7669
- title: work.title,
7670
- style: styles.cardTitle,
7671
- children: work.title || "未命名作品"
7672
- }), (0, react_jsx_runtime.jsxs)("div", {
7673
- style: styles.meta,
7674
- children: [
7675
- (0, react_jsx_runtime.jsx)("span", {
7676
- style: styles.badge,
7677
- children: statusOf(work.status)
7678
- }),
7679
- (0, react_jsx_runtime.jsx)("span", { children: formatDate(work.updatedAt) }),
7680
- (0, react_jsx_runtime.jsxs)("span", {
7681
- style: { marginLeft: "auto" },
7682
- children: [work.views, " 次浏览"]
7683
- })
7684
- ]
7685
- })]
7686
- }),
7687
- (0, react_jsx_runtime.jsxs)("div", {
7688
- style: styles.cardActions,
7689
- children: [(0, react_jsx_runtime.jsxs)("button", {
7690
- type: "button",
7691
- style: styles.cardAction,
7692
- disabled: sharing === work.id || work.status !== 2,
7693
- onClick: () => void share(work, "open"),
7694
- children: [(0, react_jsx_runtime.jsx)(Icon, {
7695
- name: "open",
7696
- size: 14
7697
- }), "打开"]
7698
- }), (0, react_jsx_runtime.jsxs)("button", {
7699
- type: "button",
7700
- style: {
7701
- ...styles.cardAction,
7702
- borderRight: 0
7703
- },
7704
- disabled: sharing === work.id || work.status !== 2,
7705
- onClick: () => void share(work, "copy"),
7706
- children: [(0, react_jsx_runtime.jsx)(Icon, {
7707
- name: "copy",
7708
- size: 14
7709
- }), sharing === work.id ? "处理中…" : "复制链接"]
7710
- })]
7492
+ (0, react_jsx_runtime.jsx)("button", {
7493
+ type: "button",
7494
+ style: styles.quiet,
7495
+ onClick: () => setConfirmLogout(false),
7496
+ children: "取消"
7711
7497
  })
7712
7498
  ]
7713
- }, work.id))
7714
- }), account !== null && error !== "" && (0, react_jsx_runtime.jsx)("p", {
7499
+ }) : (0, react_jsx_runtime.jsxs)("button", {
7500
+ type: "button",
7501
+ title: "退出登录",
7502
+ style: { ...styles.quiet },
7503
+ onClick: () => setConfirmLogout(true),
7504
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
7505
+ name: "logout",
7506
+ size: 13
7507
+ }), "退出登录"]
7508
+ }))]
7509
+ })]
7510
+ })
7511
+ }),
7512
+ account !== null && (0, react_jsx_runtime.jsxs)("form", {
7513
+ style: styles.toolbar,
7514
+ onSubmit: (event) => {
7515
+ event.preventDefault();
7516
+ load(account.token, 1, searchInput.trim());
7517
+ },
7518
+ children: [
7519
+ (0, react_jsx_runtime.jsx)("input", {
7520
+ "aria-label": "搜索作品",
7521
+ style: styles.search,
7522
+ value: searchInput,
7523
+ placeholder: "搜索标题或描述",
7524
+ onChange: (event) => setSearchInput(event.target.value)
7525
+ }),
7526
+ (0, react_jsx_runtime.jsxs)("button", {
7527
+ type: "submit",
7715
7528
  style: {
7716
- ...styles.error,
7717
- marginTop: 14
7529
+ ...styles.button,
7530
+ ...styles.primary
7718
7531
  },
7719
- children: error
7720
- })]
7721
- }),
7722
- account !== null && (0, react_jsx_runtime.jsxs)("footer", {
7723
- style: styles.footer,
7724
- children: [
7725
- (0, react_jsx_runtime.jsxs)("span", { children: [
7726
- "第 ",
7727
- page,
7728
- " / ",
7729
- pages,
7730
- " 页"
7731
- ] }),
7732
- notice !== "" && (0, react_jsx_runtime.jsx)("span", {
7733
- role: "status",
7734
- style: styles.toast,
7735
- children: notice
7736
- }),
7737
- (0, react_jsx_runtime.jsxs)("div", {
7532
+ disabled: loading,
7533
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
7534
+ name: "search",
7535
+ size: 15
7536
+ }), "搜索"]
7537
+ }),
7538
+ (0, react_jsx_runtime.jsxs)("button", {
7539
+ type: "button",
7540
+ style: styles.button,
7541
+ disabled: loading,
7542
+ onClick: () => void load(account.token, page, search),
7543
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
7544
+ name: "refresh",
7545
+ size: 15
7546
+ }), "刷新"]
7547
+ })
7548
+ ]
7549
+ }),
7550
+ (0, react_jsx_runtime.jsxs)("div", {
7551
+ style: styles.body,
7552
+ children: [account === null ? (0, react_jsx_runtime.jsx)("div", {
7553
+ style: styles.login,
7554
+ children: (0, react_jsx_runtime.jsx)(EdusLoginPanel, {
7555
+ remote,
7556
+ intro: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("strong", { children: "登录课成账号" }), (0, react_jsx_runtime.jsx)("p", {
7738
7557
  style: {
7739
- display: "flex",
7740
- gap: 8
7558
+ ...styles.subtitle,
7559
+ margin: "5px 0 0"
7741
7560
  },
7742
- children: [(0, react_jsx_runtime.jsx)("button", {
7743
- type: "button",
7744
- style: styles.button,
7745
- disabled: loading || page <= 1,
7746
- onClick: () => void load(account.token, page - 1, search),
7747
- children: "上一页"
7748
- }), (0, react_jsx_runtime.jsx)("button", {
7749
- type: "button",
7750
- style: styles.button,
7751
- disabled: loading || page >= pages,
7752
- onClick: () => void load(account.token, page + 1, search),
7753
- children: "下一页"
7754
- })]
7755
- })
7756
- ]
7757
- })
7758
- ]
7759
- })
7561
+ children: "作品列表仅展示当前账号创建的内容。"
7562
+ })] }),
7563
+ notice: error,
7564
+ submitLabel: "登录并查看作品",
7565
+ onSuccess: afterLogin,
7566
+ theme: {
7567
+ field: styles.field,
7568
+ button: styles.button,
7569
+ primary: {
7570
+ ...styles.button,
7571
+ ...styles.primary,
7572
+ height: 40
7573
+ },
7574
+ error: styles.error
7575
+ }
7576
+ })
7577
+ }) : loading ? (0, react_jsx_runtime.jsx)("div", {
7578
+ style: styles.center,
7579
+ children: "正在读取作品列表…"
7580
+ }) : list.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
7581
+ style: styles.center,
7582
+ children: (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)(Icon, {
7583
+ name: "folder",
7584
+ size: 30
7585
+ }), (0, react_jsx_runtime.jsx)("p", { children: search === "" ? "还没有发布过作品" : "没有找到匹配的作品" })] })
7586
+ }) : (0, react_jsx_runtime.jsx)("div", {
7587
+ style: styles.grid,
7588
+ children: list.map((work) => (0, react_jsx_runtime.jsxs)("article", {
7589
+ style: styles.card,
7590
+ children: [
7591
+ work.thumb ? (0, react_jsx_runtime.jsx)("img", {
7592
+ alt: "",
7593
+ src: work.thumb,
7594
+ style: styles.thumb
7595
+ }) : (0, react_jsx_runtime.jsx)("div", {
7596
+ style: styles.placeholder,
7597
+ children: "暂无预览图"
7598
+ }),
7599
+ (0, react_jsx_runtime.jsxs)("div", {
7600
+ style: styles.cardBody,
7601
+ children: [(0, react_jsx_runtime.jsx)("h3", {
7602
+ title: work.title,
7603
+ style: styles.cardTitle,
7604
+ children: work.title || "未命名作品"
7605
+ }), (0, react_jsx_runtime.jsxs)("div", {
7606
+ style: styles.meta,
7607
+ children: [
7608
+ (0, react_jsx_runtime.jsx)("span", {
7609
+ style: styles.badge,
7610
+ children: statusOf(work.status)
7611
+ }),
7612
+ (0, react_jsx_runtime.jsx)("span", { children: formatDate(work.updatedAt) }),
7613
+ (0, react_jsx_runtime.jsxs)("span", {
7614
+ style: { marginLeft: "auto" },
7615
+ children: [work.views, " 次浏览"]
7616
+ })
7617
+ ]
7618
+ })]
7619
+ }),
7620
+ (0, react_jsx_runtime.jsxs)("div", {
7621
+ style: styles.cardActions,
7622
+ children: [(0, react_jsx_runtime.jsxs)("button", {
7623
+ type: "button",
7624
+ style: styles.cardAction,
7625
+ disabled: sharing === work.id || work.status !== 2,
7626
+ onClick: () => void share(work, "open"),
7627
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
7628
+ name: "open",
7629
+ size: 14
7630
+ }), "打开"]
7631
+ }), (0, react_jsx_runtime.jsxs)("button", {
7632
+ type: "button",
7633
+ style: {
7634
+ ...styles.cardAction,
7635
+ borderRight: 0
7636
+ },
7637
+ disabled: sharing === work.id || work.status !== 2,
7638
+ onClick: () => void share(work, "copy"),
7639
+ children: [(0, react_jsx_runtime.jsx)(Icon, {
7640
+ name: "copy",
7641
+ size: 14
7642
+ }), sharing === work.id ? "处理中…" : "复制链接"]
7643
+ })]
7644
+ })
7645
+ ]
7646
+ }, work.id))
7647
+ }), account !== null && error !== "" && (0, react_jsx_runtime.jsx)("p", {
7648
+ style: {
7649
+ ...styles.error,
7650
+ marginTop: 14
7651
+ },
7652
+ children: error
7653
+ })]
7654
+ }),
7655
+ account !== null && (0, react_jsx_runtime.jsxs)("footer", {
7656
+ style: styles.footer,
7657
+ children: [
7658
+ (0, react_jsx_runtime.jsxs)("span", { children: [
7659
+ "第 ",
7660
+ page,
7661
+ " / ",
7662
+ pages,
7663
+ " 页"
7664
+ ] }),
7665
+ notice !== "" && (0, react_jsx_runtime.jsx)("span", {
7666
+ role: "status",
7667
+ style: styles.toast,
7668
+ children: notice
7669
+ }),
7670
+ (0, react_jsx_runtime.jsxs)("div", {
7671
+ style: {
7672
+ display: "flex",
7673
+ gap: 8
7674
+ },
7675
+ children: [(0, react_jsx_runtime.jsx)("button", {
7676
+ type: "button",
7677
+ style: styles.button,
7678
+ disabled: loading || page <= 1,
7679
+ onClick: () => void load(account.token, page - 1, search),
7680
+ children: "上一页"
7681
+ }), (0, react_jsx_runtime.jsx)("button", {
7682
+ type: "button",
7683
+ style: styles.button,
7684
+ disabled: loading || page >= pages,
7685
+ onClick: () => void load(account.token, page + 1, search),
7686
+ children: "下一页"
7687
+ })]
7688
+ })
7689
+ ]
7690
+ })
7691
+ ]
7760
7692
  });
7761
7693
  }
7762
7694
  //#endregion
7763
7695
  //#region lib/types/client/index.js
7764
7696
  const inject = ["slots", "remote"];
7697
+ /** 侧边栏行与它打开的主列面板共用这个 id;两者缺一,点击就会抛错。 */
7698
+ const WORKS_PANEL_ID = "edus-works";
7765
7699
  function unwrap(result) {
7766
7700
  if (!result.ok) throw new Error(result.error.message);
7767
7701
  return result.value;
7768
7702
  }
7769
7703
  /**
7770
- * Register the right-sidebar HTML publish extension and the works dialog.
7704
+ * Register the right-sidebar HTML publish extension and the works panel.
7771
7705
  *
7772
7706
  * 产品品牌不在这里:它属于产品外壳,由 dsh-desktop 的构建注入,业务插件
7773
7707
  * 不占用品牌槽位,也就不会因为换品牌而需要重新发版。
@@ -7824,16 +7758,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7824
7758
  unwrap(await bridge.htmlVersionDelete(token, id));
7825
7759
  }
7826
7760
  };
7827
- ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
7828
- name: "sidebar.footer.action",
7829
- id: "edus:my-works"
7830
- }, (props) => (0, react_jsx_runtime.jsx)(MyWorksAction, { ...props })));
7831
- const overlaySlots = ctx.slots;
7832
- overlaySlots.inject("shell.overlay", () => overlaySlots.register({
7833
- name: "shell.overlay",
7834
- id: "edus:my-works-dialog",
7761
+ const mainSlots = ctx.slots;
7762
+ mainSlots.inject("main", () => mainSlots.register({
7763
+ name: "main",
7764
+ key: WORKS_PANEL_ID,
7835
7765
  inject: () => ({ remote })
7836
- }, ({ remote: injectedRemote }) => (0, react_jsx_runtime.jsx)(MyWorksDialog, { remote: injectedRemote })));
7766
+ }, ({ remote: injectedRemote }) => (0, react_jsx_runtime.jsx)(MyWorksPanel, { remote: injectedRemote })));
7767
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
7768
+ name: "sidebar.panellist",
7769
+ id: WORKS_PANEL_ID,
7770
+ order: -1,
7771
+ label: "我的作品"
7772
+ }, (props) => (0, react_jsx_runtime.jsx)(MyWorksPanelIcon, { ...props })));
7837
7773
  ctx.inject(["slots", "documentPreviews"], (child) => applyHtmlPublishPreview(child, remote));
7838
7774
  }
7839
7775
  function applyHtmlPublishPreview(ctx, remote) {
@@ -0,0 +1,14 @@
1
+ import type { SidebarPanelIconOwnerProps } from '@deepseek-ai/dsh-client-ui-sidebar/client';
2
+ import type { EdusRemote } from './HtmlWorkbench.tsx';
3
+ /**
4
+ * 侧边栏「我的作品」行的图标。
5
+ *
6
+ * 按钮、标签、选中态都归 ui-sidebar 的 PanelRow:这里只按它要的尺寸画图形,
7
+ * 并用 currentColor 让选中态的配色由外壳决定。
8
+ */
9
+ export declare function MyWorksPanelIcon({ size }: SidebarPanelIconOwnerProps): import("react").JSX.Element;
10
+ /** 主列的「我的作品」页:登录后按页展示当前账号发布过的网页作品。 */
11
+ export declare function MyWorksPanel({ remote }: {
12
+ readonly remote: EdusRemote;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=MyWorksPanel.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  export declare const inject: string[];
3
3
  /**
4
- * Register the right-sidebar HTML publish extension and the works dialog.
4
+ * Register the right-sidebar HTML publish extension and the works panel.
5
5
  *
6
6
  * 产品品牌不在这里:它属于产品外壳,由 dsh-desktop 的构建注入,业务插件
7
7
  * 不占用品牌槽位,也就不会因为换品牌而需要重新发版。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@houser/edus-plugin",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -1,7 +0,0 @@
1
- import type { SidebarFooterActionOwnerProps } from '@deepseek-ai/dsh-client-ui-sidebar/client';
2
- import type { EdusRemote } from './HtmlWorkbench.tsx';
3
- export declare function MyWorksAction({ wide }: SidebarFooterActionOwnerProps): import("react").JSX.Element;
4
- export declare function MyWorksDialog({ remote }: {
5
- readonly remote: EdusRemote;
6
- }): import("react").JSX.Element | null;
7
- //# sourceMappingURL=MyWorksAction.d.ts.map