@michengai/dsh-agency-agents 0.1.33 → 0.1.34

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/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ The five most recent published versions are listed below.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.1.34 - 2026-09-08
10
+
11
+ - Fixed the composer Expert picker so its scrollable menu opens within the available viewport space instead of being clipped when many experts are enabled.
12
+ - Kept the initial enabled-first ordering in Expert Settings stable while toggling individual experts, preventing cards from jumping during bulk setup.
13
+
9
14
  ## 0.1.33 - 2026-09-07
10
15
 
11
16
  - Added independent in-product update checks with automatic updates when a verified DSH update service is available and a profile-specific manual fallback otherwise.
@@ -6,6 +6,11 @@
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## 0.1.34 - 2026-09-08
10
+
11
+ - 修复输入框「专家」选择器在启用较多专家时超出视口而被截断的问题,菜单现在会在可用空间内展开并滚动。
12
+ - 设置页首次进入后保持已启用优先的卡片顺序,连续启停专家不再让卡片跳动。
13
+
9
14
  ## 0.1.33 - 2026-09-07
10
15
 
11
16
  - 新增独立的插件内检查更新能力:仅在已验证的 DSH 更新服务可用时自动更新,其余环境提供与 Profile 对应的手工更新命令。
package/lib/client.js CHANGED
@@ -7690,6 +7690,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7690
7690
  else inactive.push(expert);
7691
7691
  return [...active, ...inactive];
7692
7692
  }
7693
+ /** 按首次进入设置页时保存的顺序排列;不在单项启停后重新排序。 */
7694
+ function sortExpertsByOrder(list, order) {
7695
+ const positions = new Map(order.map((slug, index) => [slug, index]));
7696
+ return list.slice().sort((left, right) => {
7697
+ return (positions.get(left.slug) ?? Number.MAX_SAFE_INTEGER) - (positions.get(right.slug) ?? Number.MAX_SAFE_INTEGER);
7698
+ });
7699
+ }
7693
7700
  const DIVISION_COUNTS = Object.fromEntries(DIVISION_ORDER.map((division) => [division, EXPERTS.filter((expert) => expert.division === division).length]));
7694
7701
  /** 根据 slug 稳定分配复用头像;空头像池安全回退为第 0 项。 */
7695
7702
  function expertAvatarIndex(slug, avatarCount) {
@@ -7828,7 +7835,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7828
7835
  const MENU_NAME_OVERRIDE = DIVISION_ORDER.map((division) => inputTriggerSourceId(division)).map((name) => `[role="listbox"] div[data-source=${JSON.stringify(name)}] ~ button`).map((selector) => `${selector} span:last-child`).join(",");
7829
7836
  /** Windows 优先使用彩色 emoji 字体,名称中的普通文字由后续字体安全回退。 */
7830
7837
  const EXPERT_MENU_NAME_STYLE = "flex:1 1 auto;max-width:none;min-width:0;font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",sans-serif!important;font-variant-emoji:emoji!important";
7831
- const COMPOSER_CSS = ".aag-btn-wrap{position:relative;order:1;margin-right:-8px}.aag-btn{display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 4px 0 8px;border:none;border-radius:24px;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;font-weight:500;cursor:pointer}.aag-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.aag-menu{position:absolute;bottom:calc(100% + 4px);left:0;box-sizing:border-box;padding:4px;display:flex;flex-direction:column;gap:0;width:300px;max-width:360px;max-height:calc(100vh - 24px);overflow-y:auto;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);z-index:10000}.aag-menu-title{padding:8px 10px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.aag-menu-item{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:none;border-radius:10px;background:transparent;cursor:pointer;text-align:left;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);box-sizing:border-box}.aag-menu-item:hover{background:var(--dsw-alias-interactive-bg-hover)}.aag-emoji{flex:0 0 auto;font-size:16px}.aag-menu-empty{padding:8px 10px;color:var(--dsw-alias-label-secondary);font-size:13px}[data-composer-card] :has(> button[aria-haspopup=\"listbox\"]) > :nth-child(2){order:2}";
7838
+ const COMPOSER_CSS = ".aag-btn-wrap{position:relative;order:1;margin-right:-8px}.aag-btn{display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 4px 0 8px;border:none;border-radius:24px;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;font-weight:500;cursor:pointer}.aag-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.aag-menu{position:absolute;bottom:calc(100% + 4px);left:0;box-sizing:border-box;padding:4px;display:flex;flex-direction:column;gap:0;width:300px;max-width:360px;max-height:calc(100dvh - 24px);overflow-y:auto;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);z-index:10000}.aag-menu[data-placement=\"below\"]{top:calc(100% + 4px);bottom:auto}.aag-menu-title{padding:8px 10px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-tertiary)}.aag-menu-item{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:none;border-radius:10px;background:transparent;cursor:pointer;text-align:left;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);box-sizing:border-box}.aag-menu-item:hover{background:var(--dsw-alias-interactive-bg-hover)}.aag-emoji{flex:0 0 auto;font-size:16px}.aag-menu-empty{padding:8px 10px;color:var(--dsw-alias-label-secondary);font-size:13px}[data-composer-card] :has(> button[aria-haspopup=\"listbox\"]) > :nth-child(2){order:2}";
7832
7839
  const SETTINGS_CSS = `
7833
7840
  .aag-section{box-sizing:border-box;display:flex;min-width:0;max-width:760px;width:100%;margin:0 auto;flex-direction:column;gap:16px;padding:0 0 32px;color:var(--dsw-alias-label-primary)}
7834
7841
  .aag-toolbar{display:flex;align-items:flex-start;gap:16px;padding-bottom:12px}
@@ -8047,6 +8054,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8047
8054
  function resolveExpertToolbarClick(enabledCount) {
8048
8055
  return enabledCount === 0 ? "settings" : "menu";
8049
8056
  }
8057
+ /**
8058
+ * 让浮层始终留在可视区域内。输入框通常贴近底部,因此默认向上展开;
8059
+ * 当上方空间更小时,改为向下展开并将列表限制在实际可滚动的高度内。
8060
+ */
8061
+ function resolveExpertMenuPosition(trigger, viewportHeight) {
8062
+ const gap = 4;
8063
+ const viewportInset = 12;
8064
+ const above = Math.max(0, Math.floor(trigger.top - gap - viewportInset));
8065
+ const below = Math.max(0, Math.floor(viewportHeight - trigger.bottom - gap - viewportInset));
8066
+ return above >= below ? {
8067
+ placement: "above",
8068
+ maxHeight: above
8069
+ } : {
8070
+ placement: "below",
8071
+ maxHeight: below
8072
+ };
8073
+ }
8050
8074
  const SETTINGS_TRIGGER_LABELS = /* @__PURE__ */ new Set(["设置", "Settings"]);
8051
8075
  const COMPOSER_TRIGGER_SCOPE = "[data-composer-card], .aag-btn-wrap";
8052
8076
  function isSettingsTriggerLabel(label) {
@@ -8146,6 +8170,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8146
8170
  const [open, setOpen] = react.default.useState(false);
8147
8171
  const [enabled, setEnabled] = react.default.useState(/* @__PURE__ */ new Set());
8148
8172
  const [insertError, setInsertError] = react.default.useState(null);
8173
+ const [menuPosition, setMenuPosition] = react.default.useState();
8174
+ const rootRef = react.default.useRef(null);
8175
+ react.default.useLayoutEffect(() => {
8176
+ if (!open) return;
8177
+ const updatePosition = () => {
8178
+ const root = rootRef.current;
8179
+ if (root === null) return;
8180
+ const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
8181
+ const next = resolveExpertMenuPosition(root.getBoundingClientRect(), viewportHeight);
8182
+ setMenuPosition((current) => current?.placement === next.placement && current.maxHeight === next.maxHeight ? current : next);
8183
+ };
8184
+ updatePosition();
8185
+ window.addEventListener("resize", updatePosition);
8186
+ window.addEventListener("scroll", updatePosition, true);
8187
+ const visualViewport = window.visualViewport;
8188
+ visualViewport?.addEventListener("resize", updatePosition);
8189
+ visualViewport?.addEventListener("scroll", updatePosition);
8190
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(updatePosition);
8191
+ if (rootRef.current !== null) observer?.observe(rootRef.current);
8192
+ return () => {
8193
+ window.removeEventListener("resize", updatePosition);
8194
+ window.removeEventListener("scroll", updatePosition, true);
8195
+ visualViewport?.removeEventListener("resize", updatePosition);
8196
+ visualViewport?.removeEventListener("scroll", updatePosition);
8197
+ observer?.disconnect();
8198
+ };
8199
+ }, [open]);
8149
8200
  react.default.useEffect(() => {
8150
8201
  if (!open) return;
8151
8202
  const onPointerDown = (ev) => {
@@ -8181,14 +8232,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8181
8232
  setOpen(false);
8182
8233
  };
8183
8234
  const groups = groupByDivision(EXPERTS.filter((e) => enabled.has(e.slug)), props.getActive());
8184
- const menu = open ? react.default.createElement("div", { className: "aag-menu" }, insertError === null ? null : react.default.createElement("div", {
8235
+ const menu = open ? react.default.createElement("div", {
8236
+ className: "aag-menu",
8237
+ "data-placement": menuPosition?.placement,
8238
+ style: menuPosition === void 0 ? void 0 : { maxHeight: `${menuPosition.maxHeight}px` }
8239
+ }, insertError === null ? null : react.default.createElement("div", {
8185
8240
  className: "aag-error",
8186
8241
  role: "alert"
8187
8242
  }, insertError), groups.length === 0 ? react.default.createElement("div", { className: "aag-menu-empty" }, props.t("menu.empty")) : groups.map((g) => menuGroup(g, pick, props.t, props.getActive))) : null;
8188
- return react.default.createElement("div", { className: "aag-btn-wrap" }, react.default.createElement("button", {
8243
+ return react.default.createElement("div", {
8244
+ className: "aag-btn-wrap",
8245
+ ref: rootRef
8246
+ }, react.default.createElement("button", {
8189
8247
  type: "button",
8190
8248
  className: "aag-btn",
8191
8249
  title: props.t("button.title"),
8250
+ "aria-expanded": open,
8192
8251
  onMouseDown: keepComposerFocus,
8193
8252
  onClick
8194
8253
  }, expertIcon(), react.default.createElement("span", null, props.t("settings.nav"))), menu);
@@ -8349,6 +8408,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8349
8408
  }
8350
8409
  function ExpertCardsSettings(props) {
8351
8410
  const [state, setState] = react.default.useState(null);
8411
+ const [initialOrder, setInitialOrder] = react.default.useState(null);
8352
8412
  const [error, setError] = react.default.useState(null);
8353
8413
  const [query, setQuery] = react.default.useState("");
8354
8414
  const [division, setDivision] = react.default.useState("");
@@ -8361,6 +8421,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8361
8421
  const load = react.default.useCallback(() => {
8362
8422
  readEnabled(props.remote).then((current) => {
8363
8423
  setState(current);
8424
+ setInitialOrder((order) => order ?? sortExpertsByEnabled(EXPERTS, current.enabled).map((expert) => expert.slug));
8364
8425
  setError(null);
8365
8426
  props.onEnabledChange?.(current.enabled);
8366
8427
  }).catch((err) => {
@@ -8372,6 +8433,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8372
8433
  readEnabled(props.remote).then((current) => {
8373
8434
  if (!alive) return;
8374
8435
  setState(current);
8436
+ setInitialOrder((order) => order ?? sortExpertsByEnabled(EXPERTS, current.enabled).map((expert) => expert.slug));
8375
8437
  props.onEnabledChange?.(current.enabled);
8376
8438
  }).catch((err) => {
8377
8439
  if (alive) setError(err instanceof Error ? err.message : String(err));
@@ -8459,10 +8521,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8459
8521
  className: "aag-note"
8460
8522
  }, props.t("settings.loading")));
8461
8523
  else {
8462
- const filtered = sortExpertsByEnabled(filterExperts(EXPERTS, {
8524
+ const filtered = filterExperts(initialOrder === null ? sortExpertsByEnabled(EXPERTS, state.enabled) : sortExpertsByOrder(EXPERTS, initialOrder), {
8463
8525
  query,
8464
8526
  division
8465
- }), state.enabled);
8527
+ });
8466
8528
  const enabledCount = [...state.enabled].filter((slug) => EXPERTS.some((expert) => expert.slug === slug)).length;
8467
8529
  const total = EXPERTS.length;
8468
8530
  const hasFilter = normalizeExpertQuery(query) !== "" || division !== "";
@@ -8784,9 +8846,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8784
8846
  exports.normalizeExpertQuery = normalizeExpertQuery;
8785
8847
  exports.openAgentSettings = openAgentSettings;
8786
8848
  exports.pickHostSettingsTrigger = pickHostSettingsTrigger;
8849
+ exports.resolveExpertMenuPosition = resolveExpertMenuPosition;
8787
8850
  exports.resolveExpertToolbarClick = resolveExpertToolbarClick;
8788
8851
  exports.resolveReferenceInsertionTarget = resolveReferenceInsertionTarget;
8789
8852
  exports.sortExpertsByEnabled = sortExpertsByEnabled;
8853
+ exports.sortExpertsByOrder = sortExpertsByOrder;
8790
8854
  exports.writeErrorKey = writeErrorKey;
8791
8855
  exports.writeErrorMessage = writeErrorMessage;
8792
8856
  return module.exports;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@michengai/dsh-agency-agents",
3
3
  "description": "Agency Experts — a summonable domain-expert roster for DSH (expert mode)",
4
- "version": "0.1.33",
4
+ "version": "0.1.34",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.22.0",
7
7
  "engines": {