@dsh-xhl/dsh-file-explorer 0.1.6 → 0.1.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
@@ -8,7 +8,7 @@
8
8
  - **Hero / 新建会话页 ButtonGroup**:同一浮层位置由插件定位到会话列右上角(hero 阶段显示,有记录后由标题栏图标接管,无重复);hero 上为**左主按钮 + 右下拉**的组合:
9
9
  - **编辑器**(默认):点击打开文件预览 / 编辑弹窗(Ctrl+P 亦可)
10
10
  - **文件夹**:点击直接打开会话工作区所在的**系统文件夹**(Windows `explorer` / macOS `open` / Linux `xdg-open`)
11
- - **VSCode**:点击用 `code` 命令行在 **VS Code** 中打开会话工作区(自动探测安装位置 / PATH;未安装则该项自动隐藏)
11
+ - **VSCode**:点击用 `code` 命令行在 **VS Code** 中打开会话工作区(自动探测安装位置 / PATH;宿主明确报告未安装时该项自动隐藏,探测请求本身失败则保持可见并退避重试)
12
12
  - 选择记忆在 localStorage,点击后底部提示确认打开的路径;下拉选中某项会先切换默认打开方式,随后立即执行对应的打开动作
13
13
  - 纯插件实现,不改动框架代码(详见 `docs/hero-filex-button.md`)
14
14
  - **对话内点击文件 → 编辑器预览**:聊天里的文件路径链接(工具行、产物行、正文提及)默认用系统程序打开;插件拦截 `remote.session.openWorkspacePath` 与 `workspaces.openPath` 两条通道,改为在编辑器弹窗中预览(目录或 cwd 外的路径仍落回系统打开)
@@ -44,6 +44,11 @@ pnpm install
44
44
  pnpm build # lib/index.js(host)+ lib/client.js(client bundle,CodeMirror 内联)
45
45
  ```
46
46
 
47
+ > 构建采用**原地覆盖**(不再先删除 `lib/`)。宿主半在进程启动时载入内存,而 profile
48
+ > 若 link 到本工作区则会直读磁盘:构建中途清空目录会让 HMR 读到空 bundle,导致
49
+ > `/filex` 路由与客户端 import 瞬时失败(表现为 `unknown filex API method ...`)。
50
+ > 改动客户端后无需重启,宿主半的改动需重启 DSH 进程。
51
+
47
52
  ## 安装 / 挂载(desktop profile 示例)
48
53
 
49
54
  ```bash
package/cordis.patch.yml CHANGED
@@ -1,8 +1,8 @@
1
- # dsh-file-explorer bundle patch
2
- #
3
- # 通过 bundle 通道挂载:
4
- # dsh plugin --profile <name> add @dsh-xhl/dsh-file-explorer@<version>
5
- # 或手动把 '@dsh-xhl/dsh-file-explorer' 加进 profile 的 dsh.profile.bundles 并 link 依赖。
6
- - insert:
7
- - id: dsh-file-explorer
8
- name: '@dsh-xhl/dsh-file-explorer'
1
+ # dsh-file-explorer bundle patch
2
+ #
3
+ # 通过 bundle 通道挂载:
4
+ # dsh plugin --profile <name> add @dsh-xhl/dsh-file-explorer@<version>
5
+ # 或手动把 '@dsh-xhl/dsh-file-explorer' 加进 profile 的 dsh.profile.bundles 并 link 依赖。
6
+ - insert:
7
+ - id: dsh-file-explorer
8
+ name: '@dsh-xhl/dsh-file-explorer'
package/lib/client.js CHANGED
@@ -37014,21 +37014,37 @@ window.__ModuleLoader__.load({
37014
37014
  */
37015
37015
  const CSS = `
37016
37016
  /* ── header icon (utilities slot) ────────────────────────────────────── */
37017
- /* Same 28×28 round button as dsh-git-gui's header entry (.gg-header-btn):
37018
- the primitive sm button is already 28px tall with a 16px icon, so only
37019
- the width / padding / radius need pinning down. */
37020
- .filex-header-btn{width:28px;height:28px;padding:0;border-radius:14px}
37017
+ /* Same 28×28 header entry as dsh-git-gui's (.gg-header-btn) but with a
37018
+ modest rounded-rectangle corner: the primitive sm button is already 28px
37019
+ tall with a 16px icon, so only the width / padding / radius need pinning
37020
+ down. An 8px radius keeps it a rounded square rather than a full circle. */
37021
+ .filex-header-btn{width:28px;height:28px;padding:0;border-radius:8px}
37021
37022
 
37022
37023
  /* ── hero ButtonGroup (shell.overlay entry) ──────────────────────────── */
37023
37024
  /* The mode group shown only on the hero / new-session screen: the left
37024
37025
  button carries the remembered mode's icon, the right chevron the mode
37025
- dropdown. Outer corners reuse the in-chat header-button radius (14px on
37026
- a 28px-tall button = a fully round cap), so the group reads as one pill
37027
- with the same curve as the session header icon. */
37026
+ dropdown. Outer corners use the same rounded-rectangle radius as the
37027
+ header buttons (8px on a 28px-tall button), so the group reads as one
37028
+ pill consistent with the session header icons. */
37028
37029
  .filex-group{display:inline-flex;align-items:center;position:relative;height:28px}
37029
37030
  .filex-group .filex-group-main,.filex-group .filex-group-trigger{height:28px}
37030
- .filex-group .filex-group-main{border-radius:14px 0 0 14px;padding:0 9px}
37031
- .filex-group .filex-group-trigger{margin-left:-1px;border-radius:0 14px 14px 0;border-left:0;padding:0 7px}
37031
+ .filex-group .filex-group-main{border-radius:8px 0 0 8px;padding:0 9px}
37032
+ .filex-group .filex-group-trigger{margin-left:-1px;border-radius:0 8px 8px 0;border-left:0;padding:0 7px}
37033
+
37034
+ /* ── header corner seat (new runtime) ────────────────────────────────── */
37035
+ /* The corner occupant: the hero ButtonGroup directly left of the right-
37036
+ sidebar expand button in one flex row — the same corner seat the shell's
37037
+ expand button occupies alone once the header chrome is shown, now shared
37038
+ with the plugin's open-actions group. The seat's own margin and the
37039
+ header's padding position the row at the far right of the blank header. */
37040
+ .filex-corner{display:flex;align-items:center;gap:8px}
37041
+ /* Re-painted expand button, same footprint as the shell's ExpandButton
37042
+ (28×28, 15px mirrored panel icon) but with a rounded-rectangle corner to
37043
+ match the editor button beside it; visible only while the panel is
37044
+ collapsed. */
37045
+ .filex-corner-expand{width:28px;height:28px;color:var(--dsw-alias-label-secondary,#9ca3af);cursor:pointer;background:transparent;border:none;border-radius:8px;flex:none;justify-content:center;align-items:center;padding:6px;display:inline-flex}
37046
+ .filex-corner-expand:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(128,128,128,.15))}
37047
+ .filex-corner-expand-icon{transform:scaleX(-1)}
37032
37048
 
37033
37049
  /* ── hero floating utility (shell.overlay entry) ─────────────────────── */
37034
37050
  /* The ButtonGroup, pinned by the plugin to the conversation column's
@@ -38196,6 +38212,20 @@ window.__ModuleLoader__.load({
38196
38212
  };
38197
38213
  emit();
38198
38214
  }
38215
+ /**
38216
+ * Resolve the session's authoritative working directory from the host
38217
+ * (`session.cwd` reads the session header), falling back to the framework
38218
+ * session-list hint only when the host round-trip fails. This keeps
38219
+ * reveal / vscode pointing at the session's own workspace even when the
38220
+ * session-list snapshot carries no cwd for the bound session.
38221
+ */
38222
+ async function resolveCwdHint(sessionId, fallback) {
38223
+ try {
38224
+ const result = await api.sessionCwd({ sessionId });
38225
+ if (result.cwd !== "") return result.cwd;
38226
+ } catch {}
38227
+ return fallback;
38228
+ }
38199
38229
  /** Ask the host to reveal the session's working folder in the OS file manager. */
38200
38230
  async function openSystemFolder(sessionId, cwd) {
38201
38231
  const resolved = sessionId !== void 0 && sessionId !== "" ? sessionId : activeSessionId;
@@ -38204,7 +38234,8 @@ window.__ModuleLoader__.load({
38204
38234
  return;
38205
38235
  }
38206
38236
  try {
38207
- showNotice(`已打开文件夹:${(await api.fsReveal({ sessionId: resolved }, cwd)).cwd}`);
38237
+ const hint = await resolveCwdHint(resolved, cwd);
38238
+ showNotice(`已打开文件夹:${(await api.fsReveal({ sessionId: resolved }, hint)).cwd}`);
38208
38239
  } catch (error) {
38209
38240
  showNotice(`打开系统文件夹失败:${error instanceof Error ? error.message : String(error)}`);
38210
38241
  }
@@ -38217,7 +38248,8 @@ window.__ModuleLoader__.load({
38217
38248
  return;
38218
38249
  }
38219
38250
  try {
38220
- showNotice(`已在 VS Code 打开:${(await api.fsVscode({ sessionId: resolved }, cwd)).cwd}`);
38251
+ const hint = await resolveCwdHint(resolved, cwd);
38252
+ showNotice(`已在 VS Code 打开:${(await api.fsVscode({ sessionId: resolved }, hint)).cwd}`);
38221
38253
  } catch (error) {
38222
38254
  showNotice(`用 VS Code 打开失败:${error instanceof Error ? error.message : String(error)}`);
38223
38255
  }
@@ -38242,31 +38274,41 @@ window.__ModuleLoader__.load({
38242
38274
  } catch {}
38243
38275
  }
38244
38276
  /**
38245
- * Probe the host once per activation for VS Code availability and hide the
38246
- * VSCode option (falling back to the editor) when it is missing — the user
38247
- * explicitly wants the entry hidden rather than shown-but-broken.
38248
- */
38249
- function probeVscode() {
38277
+ * Probe the host for VS Code availability and hide the VSCode option when the
38278
+ * host positively reports it missing (a `{ vscode: false }` answer), so the
38279
+ * entry never shows as broken when the CLI is genuinely absent.
38280
+ *
38281
+ * A FAILED probe is not an answer: the host may simply be mid-reload (its
38282
+ * routes vanish for a moment while the bundle is rewritten), and latching
38283
+ * "unavailable" then would hide VSCode for the rest of the page's life over a
38284
+ * transient hiccup. Failures therefore keep the option visible and retry a few
38285
+ * times; picking it without a CLI still surfaces the host's own
38286
+ * 「未找到 VS Code 命令行工具」 error, which is the honest report.
38287
+ */
38288
+ function probeVscode(attempt = 0) {
38250
38289
  api.fsCapabilities().then((result) => {
38251
- store = {
38252
- ...store,
38253
- vscode: result.vscode
38254
- };
38255
- if (!result.vscode && store.mode === "vscode") store = {
38256
- ...store,
38257
- mode: "editor"
38258
- };
38259
- emit();
38290
+ if (result.vscode === false) {
38291
+ store = {
38292
+ ...store,
38293
+ vscode: false
38294
+ };
38295
+ if (store.mode === "vscode") store = {
38296
+ ...store,
38297
+ mode: "editor"
38298
+ };
38299
+ emit();
38300
+ return;
38301
+ }
38302
+ if (!store.vscode) {
38303
+ store = {
38304
+ ...store,
38305
+ vscode: true
38306
+ };
38307
+ emit();
38308
+ }
38260
38309
  }).catch(() => {
38261
- store = {
38262
- ...store,
38263
- vscode: false
38264
- };
38265
- if (store.mode === "vscode") store = {
38266
- ...store,
38267
- mode: "editor"
38268
- };
38269
- emit();
38310
+ if (attempt >= 4) return;
38311
+ window.setTimeout(() => probeVscode(attempt + 1), 1e3 * (attempt + 1));
38270
38312
  });
38271
38313
  }
38272
38314
  const MODE_ITEMS = [
@@ -38310,15 +38352,21 @@ window.__ModuleLoader__.load({
38310
38352
  }
38311
38353
  /**
38312
38354
  * The hero ButtonGroup: the left button carries the remembered mode's icon and
38313
- * runs its action, the right chevron opens the mode dropdown. Rendered only on
38314
- * the hero / new-session screen — the in-chat header keeps the single
38315
- * built-in-editor `HeaderIcon`.
38355
+ * runs its action, the right chevron opens the mode dropdown. Rendered on the
38356
+ * hero / new-session screen (both the corner seat on the new runtime and the
38357
+ * floating fallback) — the in-chat header keeps the single built-in-editor
38358
+ * `HeaderIcon`.
38359
+ *
38360
+ * `sessionId` is optional: the corner seat supplies it from the session
38361
+ * standard kit; the floating fallback (root-scoped overlay) resolves it from
38362
+ * `useSessions` → `state.current` instead.
38316
38363
  */
38317
38364
  function HeroGroup(props) {
38318
38365
  const state = (0, react.useSyncExternalStore)(subscribe, getSnapshot);
38319
38366
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
38320
38367
  const useSessions = props.useSessions ?? (() => void 0);
38321
- const sessionId = useSessions((s) => s.current);
38368
+ const current = useSessions((s) => s.current);
38369
+ const sessionId = props.sessionId ?? current;
38322
38370
  const sessionCwd = useSessions((s) => sessionId !== void 0 ? s.byId?.[sessionId]?.cwd : void 0);
38323
38371
  const mode = state.mode;
38324
38372
  const items = state.vscode ? MODE_ITEMS : MODE_ITEMS.filter((item) => item.id !== "vscode");
@@ -38379,6 +38427,103 @@ window.__ModuleLoader__.load({
38379
38427
  });
38380
38428
  }
38381
38429
  /**
38430
+ * Corner expand button re-painted beside the explorer ButtonGroup while this
38431
+ * plugin occupies the header's corner seat. Matches the shell's `ExpandButton`
38432
+ * (28×28, mirrored panel icon, tooltip); the toggle goes through the
38433
+ * cross-plugin `sidebarRight` face. Hides itself while the panel is open,
38434
+ * exactly like the shipped button — the DOM probe (`data-sidebar-right-open`)
38435
+ * is the same attribute the shell's panel root carries, so it tracks even
38436
+ * opens that did not come from this button.
38437
+ */
38438
+ function CornerExpandButton(props) {
38439
+ const [expanded, setExpanded] = (0, react.useState)(false);
38440
+ (0, react.useEffect)(() => {
38441
+ const probe = () => {
38442
+ setExpanded(document.querySelector("[data-sidebar-right-open]") !== null);
38443
+ };
38444
+ probe();
38445
+ const observer = new MutationObserver(probe);
38446
+ observer.observe(document.body, {
38447
+ childList: true,
38448
+ subtree: true,
38449
+ attributes: true,
38450
+ attributeFilter: ["data-sidebar-right-open"]
38451
+ });
38452
+ window.addEventListener("resize", probe);
38453
+ return () => {
38454
+ observer.disconnect();
38455
+ window.removeEventListener("resize", probe);
38456
+ };
38457
+ }, []);
38458
+ if (expanded) return null;
38459
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
38460
+ label: "展开右侧栏",
38461
+ side: "bottom",
38462
+ delayMs: 500,
38463
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
38464
+ type: "button",
38465
+ className: "filex-corner-expand",
38466
+ "aria-label": "展开右侧栏",
38467
+ "data-sidebar-right-expand": "",
38468
+ onClick: () => {
38469
+ props.sidebar.toggleExpanded();
38470
+ },
38471
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPanelLeftOutline16, { className: "filex-corner-expand-icon" })
38472
+ })
38473
+ });
38474
+ }
38475
+ /**
38476
+ * The header's corner seat while the new-session header chrome is blank: the
38477
+ * explorer ButtonGroup sits directly left of the expand button — the same
38478
+ * corner seat, one flex row, no floating layer, no measuring. In a session
38479
+ * with records the `utilities` seat renders (where the editor icon lives), so
38480
+ * only the expand button is re-painted here; the DOM probe keeps the pair
38481
+ * from ever duplicating.
38482
+ */
38483
+ function HeaderCornerGroup(props) {
38484
+ const [utilitiesPresent, setUtilitiesPresent] = (0, react.useState)(false);
38485
+ const [sidebar, setSidebar] = (0, react.useState)(void 0);
38486
+ (0, react.useEffect)(() => {
38487
+ let alive = true;
38488
+ let tries = 0;
38489
+ const read = () => {
38490
+ if (!alive) return;
38491
+ let value;
38492
+ try {
38493
+ value = ctxRef?.get?.("sidebarRight", false) ?? ctxRef?.sidebarRight;
38494
+ } catch {
38495
+ value = void 0;
38496
+ }
38497
+ if (value !== void 0) {
38498
+ setSidebar(value);
38499
+ return;
38500
+ }
38501
+ tries += 1;
38502
+ if (tries < 40) setTimeout(read, 250);
38503
+ };
38504
+ read();
38505
+ return () => {
38506
+ alive = false;
38507
+ };
38508
+ }, []);
38509
+ (0, react.useEffect)(() => {
38510
+ const probe = () => {
38511
+ setUtilitiesPresent(document.querySelector("[data-slot=\"conversation.session.header.utilities\"]") !== null);
38512
+ };
38513
+ probe();
38514
+ const observer = new MutationObserver(probe);
38515
+ observer.observe(document.body, {
38516
+ childList: true,
38517
+ subtree: true
38518
+ });
38519
+ return () => observer.disconnect();
38520
+ }, []);
38521
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
38522
+ className: "filex-corner",
38523
+ children: [!utilitiesPresent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeroGroup, { sessionId: props.sessionId }), sidebar !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CornerExpandButton, { sidebar })]
38524
+ });
38525
+ }
38526
+ /**
38382
38527
  * Hero / new-session floating utility: the hero ButtonGroup, rendered by the
38383
38528
  * plugin itself through the generic `shell.overlay` floating layer (no shell
38384
38529
  * change needed) and pinned to the conversation column's top-right corner —
@@ -38390,11 +38535,19 @@ window.__ModuleLoader__.load({
38390
38535
  * tracked automatically. Clicking behaves exactly like the in-chat icon: in a
38391
38536
  * blank-session hero the actions bind to that session, and with no session
38392
38537
  * at all they surface the no-session notice.
38538
+ *
38539
+ * On the new runtime the corner seat carries the same ButtonGroup, so this
38540
+ * fallback hides itself the moment `.filex-corner` is rendered — the two
38541
+ * paths never overlap.
38393
38542
  */
38394
38543
  function HeroFilexButton(props) {
38395
38544
  const [pos, setPos] = (0, react.useState)(null);
38396
38545
  (0, react.useEffect)(() => {
38397
38546
  const update = () => {
38547
+ if (document.querySelector(".filex-corner") !== null) {
38548
+ setPos(null);
38549
+ return;
38550
+ }
38398
38551
  const column = document.querySelector("[data-phase]");
38399
38552
  if (column === null || column.getAttribute("data-phase") !== "hero") {
38400
38553
  setPos(null);
@@ -38523,6 +38676,11 @@ window.__ModuleLoader__.load({
38523
38676
  heroFab();
38524
38677
  };
38525
38678
  });
38679
+ ctx.slots.inject("conversation.session.header.corner", () => ctx.slots.register({
38680
+ name: "conversation.session.header.corner",
38681
+ priority: -1,
38682
+ label: "文件预览 / 编辑(角落)"
38683
+ }, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeaderCornerGroup, { sessionId: props.sessionId })));
38526
38684
  probeVscode();
38527
38685
  ctx.effect(() => {
38528
38686
  const deps = {
package/lib/index.js CHANGED
@@ -134,11 +134,16 @@ function isWithin(parent, child) {
134
134
  if (c === p) return true;
135
135
  return c.startsWith(p + "\\") || c.startsWith(p + "/") || c.startsWith(p + "\\") || c.startsWith(p + "/");
136
136
  }
137
- /** Resolve a session's authoritative working directory (header cwd first, process cwd last). */
137
+ /**
138
+ * Resolve a session's authoritative working directory (header cwd first).
139
+ * A session with no workspace has no meaningful directory — falling back to
140
+ * the host process cwd (the DSH installation's home) would silently point
141
+ * file operations at the wrong tree, so this fails loudly instead.
142
+ */
138
143
  function sessionCwdOf(ctx, sessionId) {
139
144
  const headerCwd = ctx.sessions.get(sessionId)?.header.cwd;
140
145
  if (headerCwd !== void 0 && headerCwd !== "") return resolve(headerCwd);
141
- return process.cwd();
146
+ throw new FilexError("no-workspace", "该会话没有关联的工作区目录。请先为该会话选择一个工作区。", 400);
142
147
  }
143
148
  async function readJsonBody(req) {
144
149
  let raw = "";
@@ -334,15 +339,18 @@ function openInSystemFileManager(target) {
334
339
  }
335
340
  /**
336
341
  * Resolve the directory a reveal/open action should point at. Authoritative
337
- * source: the session's own header cwd. When it is absent (sessions created
338
- * without workspace metadata fall back to the host process cwd — usually
339
- * meaningless), accept the loopback-only client's session-list cwd hint so
340
- * the folder still opens where the user works.
342
+ * source: the session's own header cwd. When it is absent (a session created
343
+ * without workspace metadata), fall back to the loopback-only client's
344
+ * session-list cwd hint — and only then fail loudly, never to the host
345
+ * process cwd (which is the DSH installation's home directory and would make
346
+ * "open folder" appear to open the user's home).
341
347
  */
342
348
  function revealCwdOf(ctx, sessionId, rawHint) {
343
349
  const headerCwd = ctx.sessions.get(sessionId)?.header.cwd;
344
350
  const hint = typeof rawHint === "string" && rawHint !== "" ? rawHint : void 0;
345
- return headerCwd !== void 0 && headerCwd !== "" ? resolve(headerCwd) : hint !== void 0 && isAbsolute(hint) ? resolve(hint) : process.cwd();
351
+ if (headerCwd !== void 0 && headerCwd !== "") return resolve(headerCwd);
352
+ if (hint !== void 0 && isAbsolute(hint)) return resolve(hint);
353
+ throw new FilexError("no-workspace", "该会话没有关联的工作区目录,无法打开系统文件夹。请先为该会话选择一个工作区。", 400);
346
354
  }
347
355
  /**
348
356
  * Locate the VS Code CLI (`code`). Checks the standard install locations
@@ -111,11 +111,22 @@ export interface FilexSlotsService {
111
111
  id?: string;
112
112
  key?: string;
113
113
  order?: number;
114
+ priority?: number;
114
115
  label?: string | (() => string);
115
116
  inject?: (...args: unknown[]) => Record<string, unknown>;
116
117
  }, component: unknown): () => void;
117
118
  inject(key: string, callback: () => () => void): () => void;
118
119
  }
120
+ /**
121
+ * The cross-plugin right-sidebar face (`dsh-client-ui-sidebar-right` provides
122
+ * it as `ctx.sidebarRight`). Only the expand-toggle and expanded-state read
123
+ * are used here, to re-paint the header's corner expand button beside the
124
+ * explorer ButtonGroup while the plugin occupies the corner seat.
125
+ */
126
+ export interface FilexSidebarRight {
127
+ isExpanded(): boolean;
128
+ toggleExpanded(): void;
129
+ }
119
130
  declare module 'cordis' {
120
131
  interface Context {
121
132
  webServer: FilexWebServer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-xhl/dsh-file-explorer",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "DSH 外部编译型插件:工作区文件预览 / 编辑(CodeMirror 6 编辑器 + 文件树 + 全文搜索 + Markdown/HTML/图片/PDF 预览)。移植自同目录下的动态 Cordis 插件版本。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -41,7 +41,7 @@
41
41
  "README.md"
42
42
  ],
43
43
  "scripts": {
44
- "build": "node -e \"fs.rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsdown",
44
+ "build": "tsc -p tsconfig.build.json && tsdown",
45
45
  "typecheck": "tsc --noEmit",
46
46
  "bundle": "tsdown",
47
47
  "prepare": "tsdown"