@michengai/dsh-codex-ui 0.2.60 → 0.2.61

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/dist/client.js CHANGED
@@ -1194,7 +1194,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1194
1194
  .dcu-wb-project-head:hover,.dcu-wb-project-head.dcu-wb-menu-open,.dcu-wb-session:hover,.dcu-wb-session.dcu-wb-selected,.dcu-wb-session.dcu-wb-menu-open{background:var(--dcu-sidebar-hover)}.dcu-wb-project-head+.dcu-wb-session,.dcu-wb-project-head+.dcu-wb-nochat{margin-top:4px}.dcu-wb-session+.dcu-wb-session{margin-top:2px}
1195
1195
  .dcu-wb-project-head[draggable=true],.dcu-wb-session[draggable=true]{cursor:grab}
1196
1196
  .dcu-wb-project-head[draggable=true]:active,.dcu-wb-session[draggable=true]:active{cursor:grabbing}
1197
- .dcu-wb-section,.dcu-wb-section:focus,.dcu-wb-section:focus-visible,.dcu-wb-project-head:focus,.dcu-wb-session:focus{outline:0}.dcu-wb-pin-end{position:relative;height:8px}.dcu-wb-project-head.dcu-wb-drop::before,.dcu-wb-session.dcu-wb-drop::before,.dcu-wb-pin-end.dcu-wb-drop::before,.dcu-wb-section[data-pin-over=true] .dcu-wb-section-head::after{content:"";position:absolute;left:8px;right:8px;top:-4px;height:8px;pointer-events:none;background:radial-gradient(circle at 4px 50%,var(--dsw-alias-state-business-primary) 3.25px,transparent 3.45px),linear-gradient(var(--dsw-alias-state-business-primary),var(--dsw-alias-state-business-primary)) 10px 50%/calc(100% - 10px) 2px no-repeat}.dcu-wb-project-head.dcu-wb-drop-after::before,.dcu-wb-session.dcu-wb-drop-after::before{top:auto;bottom:-4px}.dcu-wb-dragging{opacity:.28}.dcu-wb-drag-ghost{position:fixed;top:-120px;left:-240px;z-index:10040;max-width:220px;height:32px;padding:0 10px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:var(--dcu-sidebar-hover);color:var(--dcu-sidebar-primary);font:13px/32px var(--dcu-font,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}
1197
+ .dcu-wb-section,.dcu-wb-section:focus,.dcu-wb-section:focus-visible,.dcu-wb-project-head:focus,.dcu-wb-session:focus{outline:0}.dcu-wb-pin-end,.dcu-wb-pin-start{position:relative;height:8px}.dcu-wb-project-head.dcu-wb-drop::before,.dcu-wb-session.dcu-wb-drop::before,.dcu-wb-pin-end.dcu-wb-drop::before,.dcu-wb-pin-start.dcu-wb-drop::before{content:"";position:absolute;left:8px;right:8px;top:-4px;height:8px;pointer-events:none;background:radial-gradient(circle at 4px 50%,var(--dsw-alias-state-business-primary) 3.25px,transparent 3.45px),linear-gradient(var(--dsw-alias-state-business-primary),var(--dsw-alias-state-business-primary)) 10px 50%/calc(100% - 10px) 2px no-repeat}.dcu-wb-project-head.dcu-wb-drop-after::before,.dcu-wb-session.dcu-wb-drop-after::before{top:auto;bottom:-4px}.dcu-wb-dragging{opacity:.28}.dcu-wb-drag-ghost{position:fixed;top:-120px;left:-240px;z-index:10040;max-width:220px;height:32px;padding:0 10px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:var(--dcu-sidebar-hover);color:var(--dcu-sidebar-primary);font:13px/32px var(--dcu-font,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}
1198
1198
  .dcu-wb-folder{display:grid;place-items:center;flex:none;width:16px;height:20px;color:var(--dcu-sidebar-icon)}.dcu-wb-brand{display:block;width:16px;height:16px}
1199
1199
  .dcu-wb-project-current .dcu-wb-folder{color:var(--dsw-alias-state-business-primary)}
1200
1200
  .dcu-wb-project-title,.dcu-wb-session-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px}
@@ -1917,7 +1917,6 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1917
1917
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1918
1918
  className: "dcu-wb-section",
1919
1919
  "aria-label": t("workspace.pinned"),
1920
- "data-pin-over": pinDragActive && pinSlot === "header",
1921
1920
  onDragOver: (event) => {
1922
1921
  if (!pinDragActive && !isSidebarItemDrag(event.dataTransfer)) return;
1923
1922
  event.preventDefault();
@@ -1976,7 +1975,19 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1976
1975
  className: "dcu-wb-section-body",
1977
1976
  "data-open": sectionOpen("pinned"),
1978
1977
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1979
- pinSectionSessions.length === 0 && pinnedGroups.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1978
+ (pinDragActive || pinSlot === "header") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1979
+ className: `dcu-wb-pin-start${pinSlot === "header" ? " dcu-wb-drop" : ""}`,
1980
+ onDragOver: (event) => {
1981
+ if (!pinDragActive && !isSidebarItemDrag(event.dataTransfer)) return;
1982
+ event.preventDefault();
1983
+ event.stopPropagation();
1984
+ event.dataTransfer.dropEffect = "move";
1985
+ setPinSlot("header");
1986
+ setWorkspaceDropId(void 0);
1987
+ setWorkspaceDropAfter(false);
1988
+ }
1989
+ }),
1990
+ pinSectionSessions.length === 0 && pinnedGroups.length === 0 && pinSlot !== "header" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1980
1991
  className: "dcu-wb-empty",
1981
1992
  children: t("workspace.pinnedEmpty")
1982
1993
  }),
@@ -3722,6 +3733,10 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3722
3733
  //#endregion
3723
3734
  //#region src/dependencies.ts
3724
3735
  const MANAGED_DEPENDENCIES = [
3736
+ {
3737
+ id: "dsh",
3738
+ packageName: "@deepseek-ai/dsh"
3739
+ },
3725
3740
  {
3726
3741
  id: "ui",
3727
3742
  packageName: "@michengai/dsh-codex-ui"
@@ -3769,31 +3784,48 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3769
3784
  const [installing, setInstalling] = (0, react.useState)();
3770
3785
  const [message, setMessage] = (0, react.useState)();
3771
3786
  const alive = (0, react.useRef)(true);
3787
+ const root = (0, react.useRef)(null);
3788
+ const stateRef = (0, react.useRef)("loading");
3789
+ const requestId = (0, react.useRef)(0);
3790
+ stateRef.current = state;
3772
3791
  (0, react.useEffect)(() => () => {
3773
3792
  alive.current = false;
3774
3793
  }, []);
3775
3794
  const load = (0, react.useCallback)(async (signal) => {
3776
- setState("loading");
3795
+ const currentRequest = ++requestId.current;
3796
+ if (stateRef.current !== "ready") setState("loading");
3777
3797
  try {
3778
3798
  const response = await fetch(endpoint, {
3779
3799
  cache: "no-store",
3780
3800
  signal
3781
3801
  });
3782
3802
  const payload = await response.json();
3783
- if (signal?.aborted) return;
3803
+ if (signal?.aborted || currentRequest !== requestId.current) return;
3784
3804
  if (!response.ok || !Array.isArray(payload.dependencies) || !payload.dependencies.every(isDependencyStatus)) throw new Error();
3785
3805
  setDependencies(payload.dependencies);
3786
3806
  setState("ready");
3787
3807
  } catch (error) {
3788
- if (signal?.aborted || error instanceof DOMException && error.name === "AbortError") return;
3789
- setState("failed");
3808
+ if (signal?.aborted || error instanceof DOMException && error.name === "AbortError" || currentRequest !== requestId.current) return;
3809
+ if (stateRef.current !== "ready") setState("failed");
3790
3810
  }
3791
3811
  }, []);
3792
3812
  (0, react.useEffect)(() => {
3813
+ const node = root.current;
3793
3814
  const controller = new AbortController();
3794
- load(controller.signal);
3815
+ const refresh = () => {
3816
+ load(controller.signal);
3817
+ };
3818
+ refresh();
3819
+ if (node === null || typeof IntersectionObserver === "undefined") return () => {
3820
+ controller.abort();
3821
+ };
3822
+ const observer = new IntersectionObserver((entries) => {
3823
+ if (entries.some((entry) => entry.isIntersecting)) refresh();
3824
+ }, { threshold: .2 });
3825
+ observer.observe(node);
3795
3826
  return () => {
3796
3827
  controller.abort();
3828
+ observer.disconnect();
3797
3829
  };
3798
3830
  }, [load]);
3799
3831
  const install = async (id) => {
@@ -3822,6 +3854,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3822
3854
  };
3823
3855
  const title = (id) => t(`about.dependency.${id}`);
3824
3856
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3857
+ ref: root,
3825
3858
  className: "dcu-about",
3826
3859
  "aria-label": t("about.nav"),
3827
3860
  children: [
package/dist/index.mjs CHANGED
@@ -14,6 +14,10 @@ const SUITE_MEMBER_PACKAGES = [
14
14
  "@michengai/dsh-automation"
15
15
  ];
16
16
  const MANAGED_DEPENDENCIES = [
17
+ {
18
+ id: "dsh",
19
+ packageName: "@deepseek-ai/dsh"
20
+ },
17
21
  {
18
22
  id: "ui",
19
23
  packageName: "@michengai/dsh-codex-ui"
@@ -57,10 +61,10 @@ function profileDirectory() {
57
61
  async function declaredPluginNames() {
58
62
  try {
59
63
  const manifest = JSON.parse(await readFile(resolve(profileDirectory(), "package.json"), "utf8"));
60
- return Object.keys({
64
+ return [.../* @__PURE__ */ new Set([...Object.keys({
61
65
  ...manifest.devDependencies,
62
66
  ...manifest.dependencies
63
- });
67
+ }), ...manifest.dsh?.profile?.bundles ?? []])];
64
68
  } catch (error) {
65
69
  if (error.code === "ENOENT") return [];
66
70
  throw error;
@@ -76,31 +80,41 @@ function pluginsToRemoveBeforeInstall(declared, installing) {
76
80
  function resolveDshPluginTarget(installing, _declared = []) {
77
81
  return installing;
78
82
  }
83
+ function isOfficialRuntimePackage(packageName) {
84
+ return packageName === "@deepseek-ai/dsh" || packageName.startsWith("@deepseek-ai/dsh-");
85
+ }
86
+ function packageLookupRoots(packageName) {
87
+ if (isOfficialRuntimePackage(packageName) && process.env.DSH_RUNTIME_DIR !== void 0 && process.env.DSH_RUNTIME_DIR !== "") return [process.env.DSH_RUNTIME_DIR, profileDirectory()];
88
+ return [profileDirectory()];
89
+ }
79
90
  async function installedPackageVersion(packageName) {
80
- try {
81
- const manifest = JSON.parse(await readFile(resolve(profileDirectory(), "node_modules", ...packageName.split("/"), "package.json"), "utf8"));
82
- return typeof manifest.version === "string" ? manifest.version : void 0;
91
+ for (const root of packageLookupRoots(packageName)) try {
92
+ const manifest = JSON.parse(await readFile(resolve(root, "node_modules", ...packageName.split("/"), "package.json"), "utf8"));
93
+ if (typeof manifest.version === "string" && manifest.version !== "") return manifest.version;
83
94
  } catch (error) {
84
- if (error.code === "ENOENT") return void 0;
85
- throw error;
95
+ if (error.code !== "ENOENT") throw error;
86
96
  }
87
97
  }
88
- /** node_modules 实际版本为准;套件嵌套安装没有写进顶层 dependencies,也算已安装。 */
98
+ /** 磁盘有包且仍在 profile 声明里,才算已安装。卸载后残留的 node_modules 不算。 */
89
99
  function isManagedPackageInstalled(input) {
90
- return input.installedVersion !== void 0 && input.installedVersion !== "";
100
+ return input.declared && input.installedVersion !== void 0 && input.installedVersion !== "";
91
101
  }
92
102
  /** npm latest 查询缓存有效期:避免每次打开“关于”页都打 7 个 registry 请求。 */
93
103
  const LATEST_CACHE_TTL_MS = 3e5;
94
104
  const latestCache = /* @__PURE__ */ new Map();
95
- async function npmLatestVersion(packageName) {
96
- const hit = latestCache.get(packageName);
105
+ function cacheKey(packageName, tag) {
106
+ return packageName + "@" + tag;
107
+ }
108
+ async function npmTaggedVersion(packageName, tag) {
109
+ const key = cacheKey(packageName, tag);
110
+ const hit = latestCache.get(key);
97
111
  if (hit !== void 0 && Date.now() - hit.at < LATEST_CACHE_TTL_MS) return hit.version;
98
112
  try {
99
- const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, { signal: AbortSignal.timeout(5e3) });
113
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/${tag}`, { signal: AbortSignal.timeout(5e3) });
100
114
  if (!response.ok) return void 0;
101
115
  const manifest = await response.json();
102
116
  if (typeof manifest.version !== "string") return void 0;
103
- latestCache.set(packageName, {
117
+ latestCache.set(key, {
104
118
  version: manifest.version,
105
119
  at: Date.now()
106
120
  });
@@ -109,6 +123,10 @@ async function npmLatestVersion(packageName) {
109
123
  return;
110
124
  }
111
125
  }
126
+ async function npmLatestVersion(packageName) {
127
+ if (isOfficialRuntimePackage(packageName)) return await npmTaggedVersion(packageName, "next") ?? await npmTaggedVersion(packageName, "latest");
128
+ return npmTaggedVersion(packageName, "latest");
129
+ }
112
130
  function escapeRegExp(value) {
113
131
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
114
132
  }
@@ -154,17 +172,29 @@ function versionParts(version) {
154
172
  Number(match[3])
155
173
  ];
156
174
  }
175
+ function prereleaseRank(version) {
176
+ const match = /-rc\.(\d+)/i.exec(version);
177
+ return match === null ? Number.POSITIVE_INFINITY : Number(match[1]);
178
+ }
157
179
  function newerVersion(installed, latest) {
158
180
  const current = versionParts(installed);
159
181
  const candidate = versionParts(latest);
160
182
  if (current === void 0 || candidate === void 0) return false;
161
- return candidate[0] > current[0] || candidate[0] === current[0] && candidate[1] > current[1] || candidate[0] === current[0] && candidate[1] === current[1] && candidate[2] > current[2];
183
+ if (candidate[0] !== current[0]) return candidate[0] > current[0];
184
+ if (candidate[1] !== current[1]) return candidate[1] > current[1];
185
+ if (candidate[2] !== current[2]) return candidate[2] > current[2];
186
+ return prereleaseRank(latest) > prereleaseRank(installed);
162
187
  }
163
188
  /** 返回 Web profile 中固定管理插件的实际安装版本与 npm latest 状态。 */
164
189
  async function dependencyStatuses() {
190
+ const declaredNames = await declaredPluginNames();
165
191
  return Promise.all(MANAGED_DEPENDENCIES.map(async (dependency) => {
166
192
  const version = await installedPackageVersion(dependency.packageName);
167
- if (!isManagedPackageInstalled({ installedVersion: version })) return {
193
+ const declared = isOfficialRuntimePackage(dependency.packageName) || declaredNames.includes(dependency.packageName);
194
+ if (version === void 0 || !isManagedPackageInstalled({
195
+ installedVersion: version,
196
+ declared
197
+ })) return {
168
198
  ...dependency,
169
199
  installed: false,
170
200
  updateAvailable: false
@@ -303,7 +333,7 @@ async function installDependencyLocked(id) {
303
333
  const remove = pluginsToRemoveBeforeInstall(declared, target);
304
334
  if (remove.length > 0) await runDshPlugin(["remove", ...remove]);
305
335
  await ensureLatestReleaseAllowed(target, targetVersion);
306
- await recordDeclaredVersion(target, targetVersion);
336
+ if (!isOfficialRuntimePackage(target)) await recordDeclaredVersion(target, targetVersion);
307
337
  await recordPendingUpdate(target, targetVersion);
308
338
  if (requestDesktopHotUpdate()) return dependencyStatuses();
309
339
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-codex-ui",
3
- "version": "0.2.60",
3
+ "version": "0.2.61",
4
4
  "description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {