minecodex 2.0.34 → 2.0.35

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.
@@ -1032,10 +1032,59 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
1032
1032
  record.motionTimer = setTimeout(onFinish, 320);
1033
1033
  }
1034
1034
 
1035
+ function prepareSelectedModelReveal(record) {
1036
+ const { menu } = record;
1037
+ const interaction = new AbortController();
1038
+ let frame = null;
1039
+ const cancel = () => {
1040
+ interaction.abort();
1041
+ if (frame !== null) cancelAnimationFrame(frame);
1042
+ frame = null;
1043
+ };
1044
+ for (const type of ["wheel", "touchstart", "pointerdown", "keydown", "input", "click"]) {
1045
+ document.addEventListener(type, cancel, { capture: true, passive: true, signal: interaction.signal });
1046
+ }
1047
+ menu.scrollTop = 0;
1048
+ return {
1049
+ cancel,
1050
+ finishOpening() {
1051
+ if (interaction.signal.aborted || !menu.isConnected) return;
1052
+ // 先呈现列表顶部,再在下一帧检查选中项;sticky 搜索栏不算可见内容区。
1053
+ frame = requestAnimationFrame(() => {
1054
+ const selected = menu.querySelector("[data-codex-model-slider-selected]");
1055
+ if (!selected) return cancel();
1056
+ const zoom = menu.currentCSSZoom || 1;
1057
+ const bounds = menu.getBoundingClientRect();
1058
+ const top = menu.querySelector("[data-codex-model-slider-search-header]").getBoundingClientRect().bottom;
1059
+ const bottom = bounds.bottom - (parseFloat(getComputedStyle(menu).paddingBottom) || 0) * zoom;
1060
+ const item = selected.getBoundingClientRect();
1061
+ if (item.top >= top && item.bottom <= bottom) return cancel();
1062
+ const from = menu.scrollTop;
1063
+ const to = Math.max(0, Math.min(menu.scrollHeight - menu.clientHeight,
1064
+ from + ((item.top + item.bottom - top - bottom) / 2) / zoom));
1065
+ if (matchMedia("(prefers-reduced-motion: reduce)").matches) {
1066
+ menu.scrollTop = to;
1067
+ return cancel();
1068
+ }
1069
+ const start = performance.now();
1070
+ const scroll = now => {
1071
+ if (!menu.isConnected) return cancel();
1072
+ const progress = Math.min(1, (now - start) / 400);
1073
+ menu.scrollTop = from + (to - from) * (1 - (1 - progress) ** 3);
1074
+ if (progress < 1) frame = requestAnimationFrame(scroll);
1075
+ else cancel();
1076
+ };
1077
+ frame = requestAnimationFrame(scroll);
1078
+ });
1079
+ },
1080
+ };
1081
+ }
1082
+
1035
1083
  function closeControllerModelMenu({ immediate = false } = {}) {
1036
1084
  const record = modelSelectorControllerMenu;
1037
1085
  if (!record) return false;
1038
1086
  if (record.closing && !immediate) return true;
1087
+ record.reveal?.cancel();
1039
1088
  const placeholder = document.querySelector(
1040
1089
  '[data-codex-intelligence-trigger] [class*="_ModelPickerTriggerPlaceholder_"]',
1041
1090
  );
@@ -1249,12 +1298,14 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
1249
1298
  search.focus({ preventScroll: true });
1250
1299
  const record = modelSelectorControllerMenu;
1251
1300
  record.listHeight = Number.parseFloat(getComputedStyle(menu).height);
1301
+ record.reveal = prepareSelectedModelReveal(record);
1252
1302
  animateControllerModelMenu(record, true, () => {
1253
1303
  if (modelSelectorControllerMenu !== record || record.closing) return;
1254
1304
  record.animations.forEach((animation) => animation.cancel());
1255
1305
  record.animations = [];
1256
1306
  menu.style.overflowY = "auto";
1257
1307
  sizeModelMenuViewport(menu, selector.maxVisibleItems);
1308
+ record.reveal.finishOpening();
1258
1309
  });
1259
1310
  }
1260
1311
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecodex",
3
- "version": "2.0.34",
3
+ "version": "2.0.35",
4
4
  "description": "Lightweight, local-first plugins for the Codex desktop app.",
5
5
  "license": "MIT",
6
6
  "type": "module",