dsh-xdpool 2.4.4 → 2.4.5

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/client.js +106 -176
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Changelog
2
2
  本项目的所有重要变更都会记录在此文件。
3
3
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
+ ## 2.4.5 — 侧栏面板能滚动了
5
+
6
+ > 2.4.4 把账号池搬到侧栏那行之后,页面在折叠处被切掉、怎么都翻不到下半页。这不是新功能缺失,是我把设置页里的组件原样塞进了另一个容器,而**那个容器不滚动**。
7
+
8
+ ### 面板被裁切
9
+
10
+ `@deepseek-ai/dsh-client-ui-layout` 把中央列样式成 `display:flex; flex-direction:column; overflow:hidden`——设置对话框自己带滚动容器,所以同一个组件放在设置里能滚动,放进主区域就只能被裁掉。现在页面外面包一层 `.dsm-xdpool-page`(`height:100%; min-height:0; overflow:auto`),由它当滚动容器;顺带补上设置对话框原本提供的内边距,否则卡片会贴着窗口边缘。设置页那条路径**不受影响**:那里宿主已经在滚,再加一层会双份。
11
+
12
+ ### 其他
13
+
14
+ - 侧栏那行标签保持 `XD Pool`(中英文都不译)。
15
+
4
16
  ## 2.4.4 — 扫码登录后仍显示「登录已被拒绝」,以及侧栏入口
5
17
 
6
18
  > 两个都不算新功能:一个是**我上一版自己的代码**把刚扫的凭据判成「旧的」;另一个是入口位置——账号池一直只能从「设置」里进。
package/lib/client.js CHANGED
@@ -1670,39 +1670,19 @@ window.__ModuleLoader__.load({
1670
1670
  ):active:not(:disabled){transform:none}
1671
1671
  }
1672
1672
 
1673
- /* ── Docked entry beside the settings control ────────────────────────────────
1674
- The sidebar button and the overlay are two separate registrations with no
1675
- shared ancestor, so neither can carry the shell's own custom properties: the
1676
- easing curves are re-declared here rather than inherited. Everything below is
1677
- scoped to .dsm-xdpool-dock so it cannot reach into the host's sidebar. */
1678
- .dsm-xdpool-dock{--ease-instrument:cubic-bezier(.16,1,.3,1);position:relative;z-index:1;display:flex;align-items:center;gap:8px;width:100%;min-width:0;padding:6px 8px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--dsw-alias-label-secondary,#c6c9d0);font:inherit;font-size:12px;line-height:17px;font-weight:560;text-align:left;cursor:pointer;overflow:hidden;transition:color .2s var(--ease-instrument),background .2s var(--ease-instrument),border-color .2s var(--ease-instrument)}
1679
- .dsm-xdpool-dock:hover{color:var(--dsw-alias-label-primary,#e8e8ea);background:color-mix(in srgb, var(--dsw-alias-label-primary,#e8e8ea) 7%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-border-l2,#ffffff1f) 70%, transparent)}
1680
- .dsm-xdpool-dock-on{color:var(--dsw-alias-label-primary,#e8e8ea);background:color-mix(in srgb, var(--dsw-alias-state-business-primary,#5686fe) 14%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-state-business-primary,#5686fe) 40%, transparent)}
1681
- /* The sweep is the same gesture the card's own buttons make, so the dock reads
1682
- as part of the plugin rather than a host control. */
1683
- .dsm-xdpool-dock::after{content:"";position:absolute;top:0;bottom:0;left:-60%;width:42%;pointer-events:none;background:linear-gradient(100deg,transparent,rgb(255 255 255 / 12%),transparent);transform:skewX(-18deg);transition:left .5s var(--ease-instrument)}
1684
- .dsm-xdpool-dock:hover::after{left:118%}
1685
- .dsm-xdpool-dockicon{flex:none;width:16px;height:16px;background-color:currentColor;-webkit-mask-image:var(--dsm-xdpool-dock-glyph);mask-image:var(--dsm-xdpool-dock-glyph);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:16px 16px;mask-size:16px 16px}
1686
- .dsm-xdpool-docklabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1687
- /* No dark backdrop: the panel is a companion to the conversation, not a modal,
1688
- and dimming the whole host for a settings card reads as a page navigation.
1689
- Clicking outside still closes it — the scrim covers the viewport and is
1690
- transparent, so only the click is caught. */
1691
- .dsm-xdpool-dockscrim{position:fixed;inset:0;z-index:60;display:flex;align-items:stretch;justify-content:flex-end;background:transparent}
1692
- .dsm-xdpool-dockpanel{--ease-instrument:cubic-bezier(.16,1,.3,1);display:flex;flex-direction:column;width:min(760px, 92vw);margin:14px;border:1px solid color-mix(in srgb, var(--dsw-alias-border-l2,#ffffff1f) 82%, transparent);border-radius:16px;background:color-mix(in srgb, var(--dsw-alias-bg-layer-1,#141517) 96%, transparent);box-shadow:0 20px 60px rgb(0 0 0 / 45%);overflow:hidden;animation:dsm-xdpool-dock-in .3s var(--ease-instrument)}
1693
- @keyframes dsm-xdpool-dock-in{from{opacity:0;transform:translateX(18px)}to{opacity:1;transform:none}}
1694
- .dsm-xdpool-dockhead{display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid color-mix(in srgb, var(--dsw-alias-border-l1,#ffffff14) 80%, transparent)}
1695
- .dsm-xdpool-docktitle{flex:1 1 auto;font-size:12px;line-height:17px;font-weight:640;color:var(--dsw-alias-label-primary,#e8e8ea)}
1696
- .dsm-xdpool-dockclose{appearance:none;flex:none;width:22px;height:22px;padding:0;border:1px solid transparent;border-radius:7px;background:transparent;color:var(--dsw-alias-label-tertiary,#8a90a0);font:inherit;font-size:15px;line-height:1;cursor:pointer;transition:color .16s ease,background .16s ease}
1697
- .dsm-xdpool-dockclose:hover{color:var(--dsw-alias-label-primary,#e8e8ea);background:color-mix(in srgb, var(--dsw-alias-label-primary,#e8e8ea) 9%, transparent)}
1698
- /* The card inside the panel was built for the settings dialog's width; scroll
1699
- the panel body, never the card, so its own sticky header keeps working. */
1700
- .dsm-xdpool-dockbody{flex:1 1 auto;min-height:0;overflow:auto;padding:14px}
1701
- @media (prefers-reduced-motion:reduce){
1702
- .dsm-xdpool-dock::after{display:none}
1703
- .dsm-xdpool-dockpanel{animation:none}
1704
- }
1705
-
1673
+ `.trim();
1674
+ /**
1675
+ * Frame for hosting the pool in the main column instead of the settings dialog.
1676
+ *
1677
+ * `.centerCol` is `overflow:hidden`, so this element is the scrollport; without
1678
+ * it the page clips at the fold. The horizontal inset is asymmetric on purpose —
1679
+ * the sidebar sits on the left of this column, so the left edge needs less air
1680
+ * than the right one to look centred.
1681
+ */
1682
+ const PAGE_CSS = `
1683
+ .dsm-xdpool-page{flex:1 1 auto;height:100%;min-height:0;overflow:auto;box-sizing:border-box;padding:20px 28px 34px}
1684
+ .dsm-xdpool-page > .dsm-xdpool-shell{margin:0 auto}
1685
+ @media (max-width:720px){.dsm-xdpool-page{padding:14px 16px 26px}}
1706
1686
  `.trim();
1707
1687
  //#endregion
1708
1688
  //#region src/client/avatar.ts
@@ -12358,7 +12338,7 @@ window.__ModuleLoader__.load({
12358
12338
  *
12359
12339
  * @module dsh-xdpool/version
12360
12340
  */
12361
- const POOL_VERSION = "2.4.4";
12341
+ const POOL_VERSION = "2.4.5";
12362
12342
  //#endregion
12363
12343
  //#region src/client/PoolCard.tsx
12364
12344
  /**
@@ -14732,15 +14712,33 @@ window.__ModuleLoader__.load({
14732
14712
  * `fill-rule="evenodd"` cuts the interior notches out of the silhouette so the
14733
14713
  * three layers stay distinguishable at that size.
14734
14714
  */
14715
+ /**
14716
+ * The mark's geometry, independent of any wrapper element.
14717
+ *
14718
+ * Kept separate from {@link POOL_NAV_ICON_SVG} because the two consumers need
14719
+ * different frames: the settings nav row masks a standalone document, while the
14720
+ * sidebar panel row renders paths into an `svg` the shell sizes for it (16px
14721
+ * wide, 18px in the collapsed rail). Sharing the paths — rather than the whole
14722
+ * document, parsed back out with a regex — is what keeps the two placements from
14723
+ * drifting apart when the artwork changes.
14724
+ */
14725
+ const POOL_NAV_ICON_PATHS = [{
14726
+ fill: "currentColor",
14727
+ fillRule: "evenodd",
14728
+ d: "M12 2.6a3.1 3.1 0 1 1 0 6.2 3.1 3.1 0 0 1 0-6.2Zm0 1.7a1.4 1.4 0 1 0 0 2.8 1.4 1.4 0 0 0 0-2.8ZM6.6 8.9a2.6 2.6 0 1 1 0 5.2 2.6 2.6 0 0 1 0-5.2Zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2ZM17.4 8.9a2.6 2.6 0 1 1 0 5.2 2.6 2.6 0 0 1 0-5.2Zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"
14729
+ }, {
14730
+ fill: "none",
14731
+ stroke: "currentColor",
14732
+ strokeWidth: 1.7,
14733
+ strokeLinecap: "round",
14734
+ strokeDasharray: "2.6 2.2",
14735
+ d: "M4.4 17.2a8.6 8.6 0 0 0 15.2 0"
14736
+ }];
14737
+ /** The same mark as a standalone SVG document, for `mask-image`. */
14735
14738
  const POOL_NAV_ICON_SVG = [
14736
14739
  "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\">",
14737
- "<path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"",
14738
- "M12 2.6a3.1 3.1 0 1 1 0 6.2 3.1 3.1 0 0 1 0-6.2Zm0 1.7a1.4 1.4 0 1 0 0 2.8 1.4 1.4 0 0 0 0-2.8Z",
14739
- "M6.6 8.9a2.6 2.6 0 1 1 0 5.2 2.6 2.6 0 0 1 0-5.2Zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
14740
- "M17.4 8.9a2.6 2.6 0 1 1 0 5.2 2.6 2.6 0 0 1 0-5.2Zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
14741
- "\"/>",
14742
- "<path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\"",
14743
- " d=\"M4.4 17.2a8.6 8.6 0 0 0 15.2 0\" stroke-dasharray=\"2.6 2.2\"/>",
14740
+ `<path fill="currentColor" fill-rule="evenodd" d="${POOL_NAV_ICON_PATHS[0].d}"/>`,
14741
+ `<path fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" d="${POOL_NAV_ICON_PATHS[1].d}" stroke-dasharray="2.6 2.2"/>`,
14744
14742
  "</svg>"
14745
14743
  ].join("");
14746
14744
  /**
@@ -14752,135 +14750,67 @@ window.__ModuleLoader__.load({
14752
14750
  */
14753
14751
  const POOL_NAV_ICON_MASK_URL = `data:image/svg+xml;utf8,${encodeURIComponent(POOL_NAV_ICON_SVG)}`;
14754
14752
  //#endregion
14755
- //#region src/client/dock.tsx
14753
+ //#region src/client/panel-icon.tsx
14756
14754
  /**
14757
- * The docked entry: a `sidebar.footer.action` button that opens the pool card in
14758
- * a `shell.overlay` panel.
14759
- *
14760
- * Why not link to the Settings page the card already lives on: the host's
14761
- * sidebar slot hands a registrant exactly `{ wide }` — no `openSettings`, no
14762
- * navigation seat. dsh-purge, dshmarket and everything else third-party solve it
14763
- * the same way, by owning the surface: a button that toggles an overlay of its
14764
- * own. Re-implementing the card for the overlay would mean two copies to keep in
14765
- * step, so the overlay renders the very same `PoolCard` the settings section
14766
- * renders, page state and all.
14767
- *
14768
- * The open flag lives at module scope rather than in React state because the
14769
- * button and the panel are two separate slot registrations with no common
14770
- * ancestor; module state plus a subscription is the only channel between them.
14755
+ * Render the pool's mark.
14756
+ *
14757
+ * Inlined rather than masked from a data URI because the shell gives the glyph an
14758
+ * 18px box in the collapsed rail: a `mask-size: 16px` silhouette would read as
14759
+ * smaller than the first-party icons beside it, and scaling the mask would blur
14760
+ * its 1.7px arc. Both placements share {@link POOL_NAV_ICON_PATHS}, so the artwork
14761
+ * cannot drift between the settings nav row and this one.
14771
14762
  */
14772
- /** Subscribers re-read {@link dockedOpen}; the setter notifies them. */
14773
- const listeners = /* @__PURE__ */ new Set();
14774
- let dockedOpen = false;
14775
- /** Read whether the docked panel is showing. */
14776
- function isDockedOpen() {
14777
- return dockedOpen;
14778
- }
14779
- /** Show or hide the docked panel, notifying every registrant. */
14780
- function setDockedOpen(open) {
14781
- if (dockedOpen === open) return;
14782
- dockedOpen = open;
14783
- for (const listener of [...listeners]) listener();
14784
- }
14785
- /** Subscribe to {@link setDockedOpen}. Returns the unsubscribe. */
14786
- function subscribeDockedOpen(listener) {
14787
- listeners.add(listener);
14788
- return () => {
14789
- listeners.delete(listener);
14790
- };
14791
- }
14792
- /** Subscribe a component to the open flag. */
14793
- function useDockedOpen() {
14794
- const [open, setOpen] = (0, react.useState)(isDockedOpen);
14795
- (0, react.useEffect)(() => {
14796
- setOpen(isDockedOpen());
14797
- return subscribeDockedOpen(() => {
14798
- setOpen(isDockedOpen());
14799
- });
14800
- }, []);
14801
- return open;
14763
+ function PanelIcon({ size }) {
14764
+ const edge = typeof size === "number" && size > 0 ? size : 16;
14765
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
14766
+ xmlns: "http://www.w3.org/2000/svg",
14767
+ viewBox: "0 0 24 24",
14768
+ width: edge,
14769
+ height: edge,
14770
+ "aria-hidden": "true",
14771
+ focusable: "false",
14772
+ children: POOL_NAV_ICON_PATHS.map((shape, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { ...shape }, index))
14773
+ });
14802
14774
  }
14775
+ //#endregion
14776
+ //#region src/client/page.tsx
14803
14777
  /**
14804
- * The sidebar button.
14778
+ * The wrapper that makes the pool usable as a *sidebar panel*.
14779
+ *
14780
+ * Two problems it solves, both invisible from inside `PoolShell`:
14781
+ *
14782
+ * 1. Scrolling. The settings dialog owns a scroll container, so a page dropped
14783
+ * into `settings.plugin.item` can be as tall as it likes. The main column
14784
+ * cannot: `@deepseek-ai/dsh-client-ui-layout` styles `.centerCol` as
14785
+ * `display:flex; flex-direction:column; overflow:hidden`, and the slot is
14786
+ * rendered into that box with no scrolling of its own. Without a scrollport
14787
+ * here the page simply clips — which is what happens if you register
14788
+ * `PoolCard` into the `main` slot directly. The reference community plugin
14789
+ * solves it the same way, with `height:100%; min-height:0; overflow:auto` on
14790
+ * its page root.
14791
+ *
14792
+ * 2. Insets. The settings dialog supplies padding around the card; the main
14793
+ * column supplies none, so the same markup arrives flush against the window
14794
+ * edge. The padding below restores the frame the dialog used to provide.
14805
14795
  *
14806
- * Renders a glyph and, only when the rail is wide, the product name: a label in
14807
- * the collapsed 56px rail is clipped mid-word, which reads as a layout bug.
14796
+ * `PAGE_CSS` is injected here rather than living in `SHARED_CARD_CSS` because
14797
+ * the stylesheets in `PoolShell` are rendered *inside* the shell — this wrapper
14798
+ * is its parent, so nothing from that subtree could style it.
14799
+ *
14800
+ * @module dsh-xdpool/client/page
14808
14801
  */
14809
- function SidebarEntry({ wide }) {
14810
- const open = useDockedOpen();
14811
- const label = "XD Pool";
14812
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
14813
- type: "button",
14814
- className: `dsm-xdpool-dock${open ? " dsm-xdpool-dock-on" : ""}`,
14815
- title: open ? "收起 XD Pool" : "打开 XD Pool",
14816
- "aria-expanded": open,
14817
- "aria-label": label,
14818
- style: { "--dsm-xdpool-dock-glyph": `url("${POOL_NAV_ICON_MASK_URL}")` },
14819
- onClick: () => {
14820
- setDockedOpen(!isDockedOpen());
14821
- },
14822
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14823
- className: "dsm-xdpool-dockicon",
14824
- "aria-hidden": "true"
14825
- }), wide === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14826
- className: "dsm-xdpool-docklabel",
14827
- children: label
14828
- }) : null]
14829
- });
14830
- }
14831
14802
  /**
14832
- * The panel, rendered into `shell.overlay`.
14803
+ * `PoolCard`, sized and scrolled for the main column.
14833
14804
  *
14834
- * `children` is the card itself, passed in rather than imported so this module
14835
- * stays free of the card's dependency graph (the overlay must not drag the
14836
- * settings scope in with it when the host line has no scope to hand out).
14805
+ * Kept as a separate component instead of an option on `PoolCard` so the
14806
+ * settings registration stays exactly what it was: the dialog already provides
14807
+ * both the scrollport and the insets, and adding either again would double
14808
+ * them.
14837
14809
  */
14838
- function DockedPanel({ children }) {
14839
- const open = useDockedOpen();
14840
- (0, react.useEffect)(() => {
14841
- if (!open) return;
14842
- const onKeyDown = (event) => {
14843
- if (event.key === "Escape") setDockedOpen(false);
14844
- };
14845
- window.addEventListener("keydown", onKeyDown);
14846
- return () => {
14847
- window.removeEventListener("keydown", onKeyDown);
14848
- };
14849
- }, [open]);
14850
- if (!open) return null;
14851
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14852
- className: "dsm-xdpool-dockscrim",
14853
- role: "presentation",
14854
- onClick: () => {
14855
- setDockedOpen(false);
14856
- },
14857
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14858
- className: "dsm-xdpool-dockpanel",
14859
- role: "dialog",
14860
- "aria-modal": "false",
14861
- "aria-label": "XD Pool",
14862
- onClick: (event) => {
14863
- event.stopPropagation();
14864
- },
14865
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14866
- className: "dsm-xdpool-dockhead",
14867
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14868
- className: "dsm-xdpool-docktitle",
14869
- children: "XD Pool"
14870
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
14871
- type: "button",
14872
- className: "dsm-xdpool-dockclose",
14873
- "aria-label": "关闭",
14874
- onClick: () => {
14875
- setDockedOpen(false);
14876
- },
14877
- children: "×"
14878
- })]
14879
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14880
- className: "dsm-xdpool-dockbody",
14881
- children
14882
- })]
14883
- })
14810
+ function PoolPage(props) {
14811
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14812
+ className: "dsm-xdpool-page",
14813
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: PAGE_CSS }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PoolCard, { ...props })]
14884
14814
  });
14885
14815
  }
14886
14816
  //#endregion
@@ -15961,22 +15891,22 @@ window.__ModuleLoader__.load({
15961
15891
  }, PoolCard));
15962
15892
  installNavIcon(ctx, () => t("row.navLabel"));
15963
15893
  try {
15964
- ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
15965
- name: "sidebar.footer.action",
15966
- id: "xdpool-dock",
15967
- order: 60,
15968
- label: "XD Pool"
15969
- }, SidebarEntry));
15970
- ctx.slots.inject("shell.overlay", () => ctx.slots.register({
15971
- name: "shell.overlay",
15972
- id: "xdpool-dock-panel",
15973
- order: 60
15974
- }, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DockedPanel, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PoolCard, { ...settingsScope === void 0 ? { t } : {
15975
- t,
15976
- settingsScope
15977
- } }) })));
15894
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
15895
+ name: "sidebar.panellist",
15896
+ id: "xdpool",
15897
+ order: 20,
15898
+ label: () => t("row.navLabel")
15899
+ }, PanelIcon));
15900
+ ctx.slots.inject("main", () => ctx.slots.register({
15901
+ name: "main",
15902
+ key: "xdpool",
15903
+ inject: () => settingsScope === void 0 ? { t } : {
15904
+ t,
15905
+ settingsScope
15906
+ }
15907
+ }, PoolPage));
15978
15908
  } catch (error) {
15979
- console.error("[dsh-xdpool] docked entry unavailable (settings page unaffected):", error);
15909
+ console.error("[dsh-xdpool] sidebar entry unavailable (settings page unaffected):", error);
15980
15910
  }
15981
15911
  } catch (error) {
15982
15912
  console.error("[dsh-xdpool] client page failed to load (host provider unaffected):", error);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-xdpool",
3
3
  "displayName": "DSH XD Pool",
4
4
  "description": "One DSH model pool for every free quota you have: merge all locally signed-in WorkBuddy accounts with auto-failover, sign in to 13 vendors by QR/web, aggregate 76 vendors' free tiers, plus a usage dashboard, model routing, a local OpenAI-compatible gateway and a points-automation suite.",
5
- "version": "2.4.4",
5
+ "version": "2.4.5",
6
6
  "license": "MIT",
7
7
  "author": "XDTrees",
8
8
  "homepage": "https://github.com/XDTrees/dsh-xdpool#readme",