From 0f9af851ae8408cba62e4feea96559c23e664b0d Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Fri, 14 Aug 2026 00:41:08 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=E8=81=8A=E5=A4=A9=E4=BA=A7=E5=87=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=A1=8C=E6=8B=A6=E6=88=AA=E6=94=B9=E7=94=A8?= =?UTF-8?q?=20slots.inject=20=E5=BB=B6=E8=BF=9F=E6=B3=A8=E5=86=8C=EF=BC=88?= =?UTF-8?q?issue=20#15=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit conversation.chat.turnTail 是宿主 ui-conversation 在 conversation.chat.node children 表中声明的子插槽,直接 slots.register 会早于声明而抛 "not declared (a parent entry's children table must declare it)", 导致拦截注册失败回退默认 deliverables 行为并弹诊断条。 改用 ctx.slots.inject 等待声明落账后再注册(与官方 dsh-client-ui-deliverables 同款写法):已声明则同步注册,未声明则 在声明提交时注册,声明撤销自动卸载、重声明重新注册,fiber 卸载 幂等清理。editor tab 禁用门控与 openInSidebar 座位不变。 新增 tests/turn-tail-intercept.spec.ts 覆盖已声明/待声明/提前销毁 三种注册时机、editor 禁用回退与 openInSidebar 座位接线。 --- src/client/intercept.tsx | 19 +++- tests/turn-tail-intercept.spec.ts | 181 ++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 tests/turn-tail-intercept.spec.ts diff --git a/src/client/intercept.tsx b/src/client/intercept.tsx index 867fc48..e8f3b70 100644 --- a/src/client/intercept.tsx +++ b/src/client/intercept.tsx @@ -57,9 +57,22 @@ export function SidebarProducedFiles(props: { ) } -/** Register the turn-tail interception (returns the disposer). */ +/** + * Register the turn-tail interception (returns the disposer). + * + * The slot is a CHILD slot the host's ui-conversation declares in its + * `conversation.chat.node` children table (kind: chain, scope: session). + * Registering it directly races the declaration — the ui-slots core's + * load-time validation throws "not declared (a parent entry's children + * table must declare it)" when the parent entry is not on the ledger yet. + * slots.inject waits for the declaration: the callback runs synchronously + * when the slot is already declared, otherwise it runs inside the declaring + * register() call once the declaration commits; declaration collapse + * disposes the entry and a later declaration re-registers it. This mirrors + * @deepseek-ai/dsh-client-ui-deliverables' registration of the same slot. + */ export function registerTurnTailInterception(ctx: Context, store: SidebarStore): () => void { - return ctx.slots.register({ + return ctx.slots.inject('conversation.chat.turnTail', () => ctx.slots.register({ name: 'conversation.chat.turnTail', // Decline the takeover while the editor tab type is disabled in the side // card settings: the produced-files row falls back to the default @@ -73,7 +86,7 @@ export function registerTurnTailInterception(ctx: Context, store: SidebarStore): inject: (sessionId: string) => ({ openInSidebar: (path: string) => { openSidebarFile(ctx, store, sessionId, path) }, }), - }, SidebarProducedFiles) + }, SidebarProducedFiles)) } /** diff --git a/tests/turn-tail-intercept.spec.ts b/tests/turn-tail-intercept.spec.ts new file mode 100644 index 0000000..8504fa1 --- /dev/null +++ b/tests/turn-tail-intercept.spec.ts @@ -0,0 +1,181 @@ +/** + * Turn-tail interception registration spec (issue #15): `registerTurnTailInterception` + * must go through `ctx.slots.inject` — the slot is a CHILD slot the host's + * ui-conversation declares in its `conversation.chat.node` children table, so a + * direct `slots.register` races the declaration and the ui-slots core throws + * "not declared (a parent entry's children table must declare it)". + * + * The fake `slots` mirrors SlotRegistry.inject's semantics: run the callback + * synchronously when the slot is already declared; otherwise wait and run it + * when the declaration commits; the returned disposer cancels a pending wait + * and disposes any active registration; the register disposer is idempotent. + */ +import { describe, expect, it, vi } from 'vitest' +import { createSidebarStore } from '../src/client/state.ts' +import { registerTurnTailInterception } from '../src/client/intercept.tsx' +import type { Context } from '../src/context-types.ts' + +interface RegisteredSlot { + options: Record + component: unknown +} + +/** + * A structural fake of the client slots service. `declared` selects the + * timing: already-on-ledger (callback runs synchronously) vs. pending + * (callback runs on `declare()`, unless the controller was disposed first). + */ +const fakeSlots = (declared: boolean) => { + const registered: RegisteredSlot[] = [] + const disposals: number[] = [] + const pendings: Array<() => void> = [] + return { + registered, + disposals, + pendings, + slots: { + register: (options: Record, component: unknown) => { + registered.push({ options, component }) + return () => { disposals.push(1) } + }, + inject: (key: string, callback: () => () => void) => { + if (key !== 'conversation.chat.turnTail') { + throw new Error(`unexpected injected key "${key}"`) + } + let active: (() => void) | undefined + let stopped = false + const run = (): void => { + if (stopped || active !== undefined) return + active = callback() + } + if (declared) run() + else pendings.push(run) + return () => { + stopped = true + active?.() + active = undefined + } + }, + }, + } +} + +/** A produced-files owner currency: one closing assistant seq + its nodes. */ +const producedOwner = (paths: string[]): unknown => ({ + nodes: [ + { kind: 'assistant', seq: 1, turn: 1 }, + ...paths.map(path => ({ + kind: 'tool-result', isError: false, callView: { card: 'diff', locations: [{ path }] }, + })), + { kind: 'assistant', seq: 2, turn: 1 }, + ], + seq: 2, +}) + +const emptyOwner = (): unknown => ({ nodes: [{ kind: 'assistant', seq: 1, turn: 1 }], seq: 1 }) + +/** The minimal client-context fake the registration (and its seats) touch. */ +const clientCtx = (slots: unknown): Context => ({ + slots, + sessions: { + list: { getSnapshot: () => ({ current: 's1', byId: { s1: { id: 's1', cwd: '/w', displayTitle: 's1' } } }) }, + }, + betterSidebar: { openTab: vi.fn() }, +} as unknown as Context) + +describe('turn-tail interception registration (issue #15)', () => { + it('registers through slots.inject and lands once the slot is already declared', () => { + const fake = fakeSlots(true) + const store = createSidebarStore() + const restore = registerTurnTailInterception(clientCtx(fake.slots), store) + + // Exactly one registration, with the takeover descriptor. + expect(fake.registered).toHaveLength(1) + const { options, component } = fake.registered[0]! + expect(options.name).toBe('conversation.chat.turnTail') + expect(options.priority).toBe(-1) + expect(options.registrant).toBe('dsh-better-sidebar') + expect(options.select).toBeTypeOf('function') + expect(options.inject).toBeTypeOf('function') + expect(component).toBeTypeOf('function') + + // Disposal removes the registration; the disposer is idempotent. + restore() + expect(fake.disposals).toHaveLength(1) + restore() + expect(fake.disposals).toHaveLength(1) + }) + + it('waits for the host declaration instead of throwing (the pre-fix race)', () => { + const fake = fakeSlots(false) + const store = createSidebarStore() + const restore = registerTurnTailInterception(clientCtx(fake.slots), store) + + // The slot is undeclared: nothing registered yet, no error. + expect(fake.registered).toHaveLength(0) + + // The host's ui-conversation commits the declaration → the entry lands. + expect(fake.pendings).toHaveLength(1) + fake.pendings[0]!() + expect(fake.registered).toHaveLength(1) + + // A later re-declaration does not double-register while the entry is live. + fake.pendings[0]!() + expect(fake.registered).toHaveLength(1) + + restore() + expect(fake.disposals).toHaveLength(1) + }) + + it('a disposal before the declaration cancels the wait permanently', () => { + const fake = fakeSlots(false) + const store = createSidebarStore() + const restore = registerTurnTailInterception(clientCtx(fake.slots), store) + restore() + + // The declaration arrives after the controller was disposed: ignored. + fake.pendings[0]!() + expect(fake.registered).toHaveLength(0) + }) + + it('declines the takeover while the editor tab is disabled in the settings', () => { + const fake = fakeSlots(true) + const store = createSidebarStore() + const restore = registerTurnTailInterception(clientCtx(fake.slots), store) + const select = fake.registered[0]!.options.select as (owner: unknown) => unknown + + // Enabled (default): a produced turn claims the chain; an empty one declines. + expect(select(producedOwner(['a.ts', 'b.ts']))).toEqual(['a.ts', 'b.ts']) + expect(select(emptyOwner())).toBeNull() + + // Editor tab disabled: even a produced turn falls back to the default + // deliverables row (chips that cannot open must not be offered). + store.setPrefs({ ...store.getPrefs(), tabsEnabled: { editor: false } }) + expect(select(producedOwner(['a.ts']))).toBeNull() + + restore() + }) + + it('wires the openInSidebar seat to the sidebar editor tab', () => { + const fake = fakeSlots(true) + const ctx = clientCtx(fake.slots) + const store = createSidebarStore() + const restore = registerTurnTailInterception(ctx, store) + const inject = fake.registered[0]!.options.inject as (sessionId: string) => { + openInSidebar: (path: string) => void + } + + // The seat hands the session-scoped opener to the chips row. + const seat = inject('s1') + expect(seat.openInSidebar).toBeTypeOf('function') + seat.openInSidebar('/w/src/a.ts') + expect(ctx.betterSidebar.openTab).toHaveBeenCalledWith({ + type: 'editor', + title: 'a.ts', + path: '/w/src/a.ts', + id: 'editor:/w/src/a.ts', + }) + + restore() + }) +}) From 8bf6d1d7bab1520e6f12bccafa0f0ce05e3bdd60 Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Fri, 14 Aug 2026 00:41:10 +0800 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20v0.10.3=20=E2=80=94=20=E5=8F=91?= =?UTF-8?q?=E5=B8=83=20turnTail=20=E6=8B=A6=E6=88=AA=20slots.inject=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=88issue=20#15=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsh.plugin.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsh.plugin.json b/dsh.plugin.json index 092de97..8eb53df 100644 --- a/dsh.plugin.json +++ b/dsh.plugin.json @@ -1,6 +1,6 @@ { "id": "dsh-external/dsh-better-sidebar", - "version": "0.10.2", + "version": "0.10.3", "main": "./lib/index.js", "description": "VSCode 风格右侧侧边栏:文件资源管理器 / 编辑器 / 终端 / Git / 浏览器,按会话隔离。暴露服务供其他插件注册侧边栏页面和文件预览器", "engines": { diff --git a/package.json b/package.json index 1b99b02..1f3b9d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dsh-better-sidebar", - "version": "0.10.2", + "version": "0.10.3", "description": "DSH web plugin: a VSCode-like right sidebar (explorer / editor / terminal / git / browser), isolated per conversation session. Exposes a service for other plugins to register sidebar tabs and file viewers.", "type": "module", "repository": { From e128c303bc882cb07e7174081d6d556d744b8424 Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Fri, 14 Aug 2026 00:48:44 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Windows=20Powe?= =?UTF-8?q?rShell=20=E4=B8=80=E9=94=AE=E5=AE=89=E8=A3=85=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=20install.ps1=EF=BC=8CREADME=20=E5=AE=89=E8=A3=85=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=B8=A4=E5=B9=B3=E5=8F=B0=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/install.ps1:官方 CLI 方式一键安装(PowerShell 5.1+/pwsh), 与 install.sh 对齐:预写 allowBuilds + minimumReleaseAgeExclude(幂等)、 执行 dsh plugin add、幂等清理旧手动挂载行;支持 -Version/-Restart/-DryRun。 - scripts/install.sh:加固 — -h/--help、Windows Git Bash 下 DSH_HOME 回退 $USERPROFILE、npm/pnpm view 双通道解析最新版本、失败提示细化、挂载行 清理改为逐块扫描(删除注释块跟随)。 - README.md:安装章节改为「① 前置 → ② 对应平台一键命令 → ③ 重启硬刷新」, macOS/Linux 与 Windows PowerShell 双命令示例。 - package.json:files 列表纳入 scripts/install.ps1。 --- README.md | 99 ++++++++++++++---- package.json | 1 + scripts/install.ps1 | 242 ++++++++++++++++++++++++++++++++++++++++++++ scripts/install.sh | 176 +++++++++++++++++++++++--------- 4 files changed, 451 insertions(+), 67 deletions(-) create mode 100644 scripts/install.ps1 diff --git a/README.md b/README.md index f211723..83186d0 100644 --- a/README.md +++ b/README.md @@ -35,35 +35,86 @@ https://github.com/user-attachments/assets/23187822-047e-45cc-b480-fe997bd55b86 ## 🚀 安装 -前置:已安装 DSH(`dsh web` 可运行),Node.js ≥ 20、pnpm ≥ 10。插件已发布到 npm:**`dsh-better-sidebar@0.10.2`**(`@deepseek-ai/*` peer 依赖对齐宿主实际版本 `^0.1.0-rc.6` / `@deepseek-ai/cordis@^4.0.1`,同版本单实例)。包内声明了 `dsh.bundle.patch`(随包发布的 `cordis.patch.yml`),安装后由官方 CLI 自动挂载,不修改 DSH 源码。 +装好只需三步:**① 满足前置 → ② 跑一条对应平台的一键命令 → ③ 重启 DSH 并硬刷新**。 + +### 前置要求 + +- 已安装 **DSH**,且 `dsh web` 能正常启动(首次运行会初始化 `~/.dsh/profiles/web` 这个 profile)。 +- **Node.js ≥ 20**(DSH 运行本身就需要)。 +- **pnpm ≥ 10**(装依赖用;pnpm 11 也可,脚本会自动处理它的新策略)。 +- 插件已发布到 npm:**`dsh-better-sidebar@0.10.2`**。包内声明了 `dsh.bundle.patch`(随包发布的 `cordis.patch.yml`),官方 CLI 装完即自动挂载,**不修改 DSH 源码**。 ### 一键安装(推荐) +**macOS / Linux**(Windows 装了 Git Bash 或 WSL 也可用): + ```sh curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -# 指定版本: curl -fsSL <同上> | bash -s 0.10.2 -# 装完自动重启 DSH: curl -fsSL <同上> | bash -s -- --restart ``` -脚本自动完成:`pnpm add` 登记依赖到 `~/.dsh/profiles/web/package.json` → 预写 `allowBuilds`(node-pty/protobufjs,规避 pnpm 11 的构建脚本拦截)→ 识别包内 `dsh.bundle.patch`,把插件自动加进 `dsh.profile.bundles` 挂载 → 幂等清理旧的手动挂载行(防双挂载)。版本默认最新(`latest`),可传参;`--dry-run` 只预览不落盘。 +**Windows(PowerShell 5.1+ / pwsh)**: -### 手动安装(npx dsh + 命令链) +```powershell +irm https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1 | iex +``` -等价于上面脚本的一连串 bash 命令(全新 profile 首次装可能因构建脚本被拦,链里 `||` 分支会自动批准并重试一次): +**指定版本 / 装完自动重启**(两平台参数一致): ```sh -cd ~/.dsh/profiles/web \ - && npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar \ - || (pnpm approve-builds --all \ - && npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar) -# 固定版本:把上面的 dsh-better-sidebar 换成 dsh-better-sidebar@0.10.2 +# macOS / Linux +curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -s 0.10.2 --restart + +# Windows PowerShell +& ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.2 -Restart ``` -不带 `@<版本>` 时,pnpm 解析 npm 的 `latest` 标签(最新发布版)。`dsh plugin add` 完成:`pnpm add` 登记依赖 → 识别 `dsh.bundle.patch` 自动注册到 `dsh.profile.bundles` → 下次启动自动挂载(无需手写 `cordis.patch.yml`)。 +脚本会自动完成 4 件事(全部幂等,可安全重复执行): -> ⚠️ pnpm 11 的两道供应链策略,命令链无法完全规避: -> - `strict-dep-builds` 拦截 node-pty/protobufjs 的构建脚本:首次装报 `Ignored build scripts`(包其实已装上、node-pty 预编译产物可直接用),`pnpm approve-builds --all` 一次即可。 -> - `minimumReleaseAge` 拒绝发布 <24h 的新版本:这是 pnpm 的策略、命令链无法完全规避,仅因插件刚发布不久才会触发,**24 小时后自动消失**(触发时重跑一次,pnpm 会自动补 `minimumReleaseAgeExclude`)。 +1. 预写 `allowBuilds`(node-pty / protobufjs),规避 pnpm 11 的构建脚本拦截; +2. 预写 `minimumReleaseAgeExclude`,放行「发布不足 24 小时」的新版本; +3. 执行 `dsh plugin --profile web add dsh-better-sidebar`:登记依赖 → 识别 `dsh.bundle.patch` → 自动注册进 `dsh.profile.bundles` 挂载; +4. 清理旧版残留的手动挂载行,避免「双挂载」(页面出现两个侧边栏)。 + +不确定的话,可先加 `--dry-run`(PowerShell 用 `-DryRun`)预览步骤再真正执行。`curl | bash` / `irm | iex` 会执行远程代码——脚本已随仓库开源(`scripts/install.sh` / `scripts/install.ps1`),可先下载审阅。 + +### 手动安装(逐步命令) + +适合想看清每一步的用户,与一键脚本等价。**第 ③ 步可重复执行;①② 只需做一次。** + +**macOS / Linux(bash)**: + +```sh +cd ~/.dsh/profiles/web + +# ① 放行 node-pty / protobufjs 的构建脚本(pnpm 11 默认拦截;pnpm 10 可跳过) +pnpm approve-builds --all + +# ② 放行「发布不足 24h」的新版本(装老版本可跳过;若已有该键,把下面那行并入其下即可) +cat >> pnpm-workspace.yaml <<'EOF' +minimumReleaseAgeExclude: + - dsh-better-sidebar +EOF + +# ③ 安装并自动挂载(不带 @版本 = npm 的 latest;固定版本写 dsh-better-sidebar@0.10.2) +npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar +``` + +**Windows(PowerShell)**: + +```powershell +cd ~\.dsh\profiles\web + +# ① 放行构建脚本 +pnpm approve-builds --all + +# ② 放行新版本(一次性;若已有该键,把 - dsh-better-sidebar 并入其下即可) +Add-Content -Path pnpm-workspace.yaml -Value "`nminimumReleaseAgeExclude:`n - dsh-better-sidebar" + +# ③ 安装并自动挂载 +npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar +``` + +`dsh plugin add` 会:`pnpm add` 登记依赖 → 识别包内 `dsh.bundle.patch` → 自动注册到 `dsh.profile.bundles` → 下次启动自动挂载(**无需手写 `cordis.patch.yml`**)。 ### 更新 @@ -71,7 +122,18 @@ cd ~/.dsh/profiles/web \ dsh plugin --profile web add dsh-better-sidebar ``` -或重跑一次一键脚本;也可手动把 `~/.dsh/profiles/web/package.json` 版本号改新版后 `pnpm install`。随后重启 DSH 并硬刷新(Cmd/Ctrl+Shift+R)。 +或重跑一次一键脚本;也可把 `~/.dsh/profiles/web/package.json` 里的版本号改高后 `pnpm install`。改完**重启 DSH 并硬刷新**(Cmd/Ctrl+Shift+R)。 + +### 常见问题 + +| 现象 | 原因与解决 | +|---|---| +| 报 `Ignored build scripts` | pnpm 11 拦截构建脚本。跑 `pnpm approve-builds --all`(一键脚本已自动处理)。 | +| 报 `minimum release age` / 版本不足 24h | 装的版本发布不足 24 小时。等 24h 或重跑一次(pnpm 会自动补 `minimumReleaseAgeExclude`);一键脚本已自动处理。 | +| 报「找不到 profile 目录」 | 先跑一次 `dsh web`,让它初始化 `~/.dsh/profiles/web`。 | +| 页面出现**两个侧边栏** | 双挂载:`~/.dsh/profiles/web/cordis.patch.yml` 还留着旧的手动挂载行,删掉那段 `- insert: ... better-sidebar ...`(一键脚本会自动清)。 | +| Windows 下终端无法使用 | `node-pty` 依赖预编译二进制;若当前 Node 版本没有对应产物,需装编译工具链(VS Build Tools)。主流 Node 版本一般已有预编译。 | +| Windows 没有 bash / curl | 直接用 PowerShell 一键命令;或安装 Git Bash / WSL 再跑 bash 命令。 |
从源码安装 / 开发(可选,替代 npm 方式) @@ -82,7 +144,10 @@ dsh plugin --profile web add dsh-better-sidebar 1. git clone https://github.com/omdsh-dev/DSH-better-sidebar.git ~/Code/DSH-better-sidebar cd ~/Code/DSH-better-sidebar && pnpm install && pnpm build 2. ~/.dsh/profiles/web/package.json 的 dependencies 写 "dsh-better-sidebar": "link:<克隆目录绝对路径>" -3. ~/.dsh/profiles/web/cordis.patch.yml 追加挂载行(同上) +3. ~/.dsh/profiles/web/cordis.patch.yml 追加挂载行: + - insert: + - id: better-sidebar + name: 'dsh-better-sidebar' 4. 在 ~/.dsh/profiles/web 执行 pnpm install 5. 重启 DSH 并硬刷新 ``` diff --git a/package.json b/package.json index 1f3b9d1..7e76dca 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "lib/types/**/*.d.ts", "src", "scripts/install.sh", + "scripts/install.ps1", "cordis.patch.yml", "README.md", "README_EN.md", diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 0000000..ab083c0 --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,242 @@ +# ============================================================================= +# dsh-better-sidebar 一键安装脚本(官方 CLI 方式,Windows PowerShell 5.1+ / pwsh) +# +# 通过 DSH 官方插件命令安装 npm 包并自动挂载: +# dsh plugin --profile web add dsh-better-sidebar@ +# +# 包内声明了 dsh.bundle.patch(cordis.patch.yml):CLI 的 bundle 协调会把它 +# 自动加进 profile 的 dsh.profile.bundles,下次启动即挂载——无需手动写 +# cordis.patch.yml 挂载行。符合仓库硬约束:不修改 DSH 源码,插件永远作为 +# 独立包被 profile 引用。 +# +# 用法(任选其一): +# # 默认最新版 +# irm https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1 | iex +# # 指定版本 / 装完重启 +# & ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.2 -Restart +# # 本地保存后运行 +# powershell -ExecutionPolicy Bypass -File install.ps1 -Version 0.10.2 -DryRun +# +# 参数: +# -Version npm 版本号/范围,缺省 latest(自动解析为最新)。 +# -Restart 装完后尝试 `pm2 restart dsh-web`(无 pm2 时仅提示)。会断开当前页面会话。 +# -DryRun 只打印将要执行的操作,不写任何文件。 +# +# 环境变量(均可省略): +# DSH_HOME 默认 %USERPROFILE%\.dsh +# REGISTRY 默认 https://registry.npmjs.org +# DSH_CMD 默认优先 PATH 上的 dsh,缺省回退 npx -y --package @deepseek-ai/dsh +# +# 说明: +# - pnpm 11 的 strict-dep-builds 会拦截 node-pty/protobufjs 的构建脚本并使 +# `dsh plugin add` 非零退出。脚本会先把这两个构建许可写进 profile 的 +# pnpm-workspace.yaml(幂等),保证 CLI 一步成功。 +# - pnpm 11 的 minimumReleaseAge 会拒绝发布 <24h 的新版本。脚本会预写 +# minimumReleaseAgeExclude(幂等),放行本插件,避免"重跑一次才成功"。 +# - 老版本(<0.10.2)用手动挂载行,bundle 通道激活后需移除,否则双挂载。 +# 脚本会幂等移除 better-sidebar 挂载行。 +# ============================================================================= +param( + [string]$Version = '', + [switch]$Restart, + [switch]$DryRun +) + +$PKG = 'dsh-better-sidebar' +$REGISTRY = if ($env:REGISTRY) { $env:REGISTRY } else { 'https://registry.npmjs.org' } + +# DSH_HOME:DSH_HOME 环境变量 > %USERPROFILE% > $HOME +if ($env:DSH_HOME) { + $DSH_HOME = $env:DSH_HOME +} elseif ($env:USERPROFILE) { + $DSH_HOME = Join-Path $env:USERPROFILE '.dsh' +} else { + $DSH_HOME = Join-Path $HOME '.dsh' +} +$PROFILE_DIR = Join-Path $DSH_HOME 'profiles\web' +$WS_YML = Join-Path $PROFILE_DIR 'pnpm-workspace.yaml' +$PATCH_YML = Join-Path $PROFILE_DIR 'cordis.patch.yml' + +function Say([string]$m) { Write-Host "[install] $m" -ForegroundColor Green } +function Warn([string]$m) { Write-Host "[warn] $m" -ForegroundColor Yellow } +function Die([string]$m) { Write-Host "[error] $m" -ForegroundColor Red; exit 1 } + +# 解析版本 -> npm spec("x.y.z" / "^x.y.z" / latest) +function Resolve-Spec { + param([string]$Given) + if ([string]::IsNullOrWhiteSpace($Given) -or $Given -eq 'latest') { + $v = $null + foreach ($tool in @('npm', 'pnpm')) { + if (Get-Command $tool -ErrorAction SilentlyContinue) { + $v = (& $tool view $PKG version "--registry=$REGISTRY" 2>$null | Select-Object -Last 1) + if ($v) { break } + } + } + if ($v) { return ([string]$v).Trim() } + Warn '无法联网解析最新版本(npm/pnpm 查询失败),回退为 latest,由 pnpm 直接解析。' + Warn '若已知版本号,可显式传入:-Version 0.10.2' + return 'latest' + } + return $Given +} + +# 组装 dsh CLI:优先 PATH 上的 dsh,缺省 npx 拉官方包 +function Get-DshCli { + if ($env:DSH_CMD) { return $env:DSH_CMD } + if (Get-Command dsh -ErrorAction SilentlyContinue) { return 'dsh' } + if (Get-Command npx -ErrorAction SilentlyContinue) { return 'npx' } + return $null +} + +# 前置校验 +if (-not (Get-Command node -ErrorAction SilentlyContinue)) { + Die '未找到 node(DSH 运行需要 Node.js >= 20),请先安装 Node.js 并加入 PATH。' +} +if (-not (Test-Path $PROFILE_DIR)) { + Die "找不到 profile 目录:$PROFILE_DIR(请先安装并运行过一次 dsh web)" +} +if (-not (Test-Path $WS_YML)) { + Die "找不到 $WS_YML(请先初始化 web profile)" +} + +$SPEC = Resolve-Spec $Version +$CLI = Get-DshCli +if (-not $CLI) { + Die '未找到 dsh 或 npx。请先安装 DSH(并确保 Node/npm 可用),或用 DSH_CMD 指定。' +} +Say "目标:$CLI plugin --profile web add $PKG@$SPEC(profile: $PROFILE_DIR)" + +if ($DryRun) { + Say "[dry-run] 步骤 1:确保 $WS_YML 含 allowBuilds(node-pty/protobufjs: true)与 minimumReleaseAgeExclude($PKG)" + Say "[dry-run] 步骤 2:执行 $CLI plugin --profile web add $PKG@$SPEC(安装 + bundle 自动注册)" + Say "[dry-run] 步骤 3:校验 dsh.profile.bundles 含 $PKG" + Say "[dry-run] 步骤 4:幂等移除 $PATCH_YML 里旧的 better-sidebar 手动挂载行(避免双挂载)" + if ($Restart) { Say '[dry-run] 步骤 5:pm2 restart dsh-web' } else { Say '[dry-run] 步骤 5:提示用户手动重启 DSH' } + exit 0 +} + +# 步骤 1:预写 workspace 设置(幂等),保证 pnpm 不拦截构建、放行本插件新版本 +$wsScript = @' +const fs = require("fs"); +const p = process.argv[1]; +let t = fs.readFileSync(p, "utf8"); +const before = t; +t = t.replace(/^(\s*)(node-pty|protobufjs):.*$/gm, "$1$2: true"); +if (!/^\s*allowBuilds:\s*$/m.test(t)) { + t += "\nallowBuilds:\n node-pty: true\n protobufjs: true\n"; +} else { + for (const k of ["node-pty", "protobufjs"]) { + if (!new RegExp("^\\s*" + k + ":\\s*true\\s*$", "m").test(t)) { + t = t.replace(/^(\s*allowBuilds:\s*)$/m, "$1\n " + k + ": true"); + } + } +} +if (!/^\s*-\s+dsh-better-sidebar\s*$/m.test(t)) { + if (/^\s*minimumReleaseAgeExclude:\s*$/m.test(t)) { + t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-better-sidebar"); + } else { + t += "\nminimumReleaseAgeExclude:\n - dsh-better-sidebar\n"; + } +} +if (t !== before) fs.writeFileSync(p, t); +console.log(t === before ? "unchanged" : "updated"); +'@ +$wsOut = node -e $wsScript "$WS_YML" 2>&1 +$wsCode = $LASTEXITCODE +$wsResult = (($wsOut | Out-String)).Trim() +if ($wsCode -ne 0) { Die "处理 $WS_YML 失败(node 退出码 $wsCode):$wsResult" } +if ($wsResult -eq 'updated') { + Say "已确保 $WS_YML:allowBuilds(node-pty/protobufjs: true)+ minimumReleaseAgeExclude($PKG)" +} else { + Say 'workspace 设置已就绪,跳过' +} + +# 步骤 2:官方 CLI 安装 + bundle 自动注册(含挂载) +if ($CLI -eq 'dsh') { + $cliArgs = @('plugin', '--profile', 'web', 'add', "$PKG@$SPEC") +} else { + $cliArgs = @('-y', '--package', '@deepseek-ai/dsh', 'dsh', 'plugin', '--profile', 'web', 'add', "$PKG@$SPEC") +} +Say "执行 $CLI plugin --profile web add $PKG@$SPEC ..." +$addOut = & $CLI @cliArgs 2>&1 +$addCode = $LASTEXITCODE +$addOut | ForEach-Object { $_ } +if ($addCode -ne 0) { + Warn 'dsh plugin add 失败。已预写 allowBuilds 与 minimumReleaseAgeExclude,仍失败的可能原因:' + Warn ' - 网络/登录问题:npm registry 不可达或需要登录。' + Warn " - 依赖安装冲突:可手动重试 cd $PROFILE_DIR; pnpm install。" + exit 1 +} + +# 步骤 3:校验 bundle 已注册(挂载生效的判据) +$pkgJson = Get-Content -Raw (Join-Path $PROFILE_DIR 'package.json') | ConvertFrom-Json +$bundles = $pkgJson.dsh.profile.bundles +if ($bundles -notcontains $PKG) { + Warn 'dsh-better-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册。' + Warn "若上面的 pnpm 输出提示 ignored build scripts,请确认 $WS_YML 的 allowBuilds 后重跑本脚本。" + exit 1 +} +Say "bundle 已注册:dsh.profile.bundles 包含 $PKG(下次启动自动挂载)" + +# 步骤 4:幂等移除旧的 manual 挂载行(避免与 bundle 双挂载) +$mountScript = @' +const fs = require("fs"); +const p = process.argv[1]; +const lines = fs.readFileSync(p, "utf8").split("\n"); +const out = []; +let i = 0; +let removed = false; +while (i < lines.length) { + const line = lines[i]; + if (/^[ \t]*- insert:\s*$/.test(line)) { + const block = [line]; + let j = i + 1; + while (j < lines.length && lines[j].trim() !== "" && !/^-\s/.test(lines[j])) { + block.push(lines[j]); + j++; + } + if (block.some((l) => /id:\s*better-sidebar\b/.test(l))) { + while (out.length && /^[ \t]*#/.test(out[out.length - 1])) out.pop(); + i = j; + removed = true; + continue; + } + } + out.push(line); + i++; +} +if (!removed) { + console.log("none"); +} else { + const t = out.join("\n").replace(/\n{3,}/g, "\n\n"); + fs.writeFileSync(p, t); + console.log("removed"); +} +'@ +$mountOut = node -e $mountScript "$PATCH_YML" 2>&1 +$mountCode = $LASTEXITCODE +$mountResult = (($mountOut | Out-String)).Trim() +if ($mountCode -ne 0) { Die "处理 $PATCH_YML 失败(node 退出码 $mountCode):$mountResult" } +if ($mountResult -eq 'removed') { + Say "已从 $PATCH_YML 移除旧的 better-sidebar 手动挂载行(bundle 通道接管挂载)" +} else { + Say '无旧手动挂载行,跳过' +} + +Say "安装完成:$PKG@$SPEC" + +# 步骤 5:重启提示 +if ($Restart) { + if (Get-Command pm2 -ErrorAction SilentlyContinue) { + Say '重启 dsh-web(pm2)...' + pm2 restart dsh-web + if ($LASTEXITCODE -ne 0) { Warn 'pm2 restart 失败,请手动重启 DSH' } + } else { + Warn '未找到 pm2,请手动重启 DSH(如:pm2 restart dsh-web 或 dsh web)' + } +} else { + Say '下一步:重启 DSH 并硬刷新(Ctrl+Shift+R / Cmd+Shift+R)使新副本生效。' + if (Get-Command pm2 -ErrorAction SilentlyContinue) { + Say '本机可用:pm2 restart dsh-web(会短暂断开当前页面会话)' + } +} diff --git a/scripts/install.sh b/scripts/install.sh index ec27d9a..979c7df 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # ============================================================================= -# dsh-better-sidebar 一键安装脚本(官方 CLI 方式) +# dsh-better-sidebar 一键安装脚本(官方 CLI 方式,macOS / Linux / Windows Git Bash) # # 通过 DSH 官方插件命令安装 npm 包并自动挂载: # dsh plugin --profile web add dsh-better-sidebar@ @@ -13,14 +13,15 @@ # 用法: # bash scripts/install.sh [版本] [--restart] [--dry-run] # -# 版本 npm 版本号/范围,缺省为 latest(自动解析为 ^<最新>)。 -# 示例:0.10.2、^0.10.2、~0.10.2、latest +# 版本 npm 版本号/范围,缺省为 latest(自动解析为 ^<最新>)。 +# 示例:0.10.2、^0.10.2、~0.10.2、latest # --restart 装完后尝试 `pm2 restart dsh-web`(无 pm2 时仅打印提示)。 # 注意:重启会断开当前 DSH 页面会话,默认不自动重启。 # --dry-run 只打印将要执行的操作,不写任何文件。 +# -h/--help 打印本帮助。 # -# 环境: -# DSH_HOME 默认 ~/.dsh +# 环境(均可省略,脚本会自动探测): +# DSH_HOME 默认 ~/.dsh(Windows Git Bash 下回退 $USERPROFILE/.dsh) # REGISTRY 默认 https://registry.npmjs.org(发布源;装依赖仍走 pnpm 配置) # DSH_CMD 默认优先用 PATH 上的 `dsh`,缺省回退 npx -y --package @deepseek-ai/dsh # @@ -28,6 +29,8 @@ # - pnpm 11 的 strict-dep-builds 会拦截 node-pty/protobufjs 的构建脚本并使 # `dsh plugin add` 非零退出(bundle 协调因此被跳过)。脚本会先把这两个 # 构建许可写进 profile 的 pnpm-workspace.yaml(幂等),保证 CLI 一步成功。 +# - pnpm 11 的 minimumReleaseAge 会拒绝发布 <24h 的新版本。脚本会预写 +# minimumReleaseAgeExclude(幂等),放行本插件,避免"重跑一次才成功"。 # - 老版本(<0.10.2)用手动挂载行,bundle 通道激活后需移除,否则双挂载 # (Node 半挂两次、页面两个侧边栏)。脚本会幂等移除 better-sidebar 挂载行。 # - 回滚:dsh plugin --profile web remove dsh-better-sidebar,或把 profile 依赖 @@ -35,7 +38,25 @@ # ============================================================================= set -euo pipefail -DSH_HOME="${DSH_HOME:-$HOME/.dsh}" +# 帮助请求优先处理 +for arg in "$@"; do + if [ "$arg" = "-h" ] || [ "$arg" = "--help" ]; then + cat <<'EOF' +dsh-better-sidebar 一键安装脚本 + +用法:bash scripts/install.sh [版本] [--restart] [--dry-run] + + 版本 npm 版本号/范围,缺省 latest(自动解析为最新)。示例:0.10.2、^0.10.2、latest + --restart 装完后尝试 `pm2 restart dsh-web`(无 pm2 时仅提示) + --dry-run 只打印将要执行的操作,不写任何文件 + +环境变量(可省略):DSH_HOME(默认 ~/.dsh)、REGISTRY(npm 源)、DSH_CMD(dsh 命令) +EOF + exit 0 + fi +done + +DSH_HOME="${DSH_HOME:-${HOME:-${USERPROFILE:-}}/.dsh}" PROFILE_DIR="$DSH_HOME/profiles/web" WS_YML="$PROFILE_DIR/pnpm-workspace.yaml" PATCH_YML="$PROFILE_DIR/cordis.patch.yml" @@ -50,7 +71,8 @@ for arg in "$@"; do case "$arg" in --restart) RESTART=true ;; --dry-run) DRY_RUN=true ;; - -*) echo "未知参数: $arg" >&2; exit 2 ;; + -h|--help) : ;; # 已在上面处理 + -*) echo "未知参数: ${arg}(用 -h 查看用法)" >&2; exit 2 ;; *) VERSION_SPEC="$arg" ;; esac done @@ -59,15 +81,25 @@ say() { printf '\033[32m[install]\033[0m %s\n' "$*"; } warn() { printf '\033[33m[warn]\033[0m %s\n' "$*" >&2; } die() { printf '\033[31m[error]\033[0m %s\n' "$*" >&2; exit 1; } -# 解析用户给的版本 -> CLI 要用的 npm spec("x.y.z" / "^x.y.z") +# 解析用户给的版本 -> CLI 要用的 npm spec("x.y.z" / "^x.y.z" / latest) resolve_spec() { local given="${1:-latest}" case "$given" in latest) - local v - v="$(npm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" \ - || die "无法从 $REGISTRY 解析 $PKG 的最新版本(请检查网络/登录)。可显式传版本号:bash scripts/install.sh 0.10.2" - printf '%s' "$v" + local v="" + if command -v npm >/dev/null 2>&1; then + v="$(npm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" || v="" + fi + if [ -z "$v" ] && command -v pnpm >/dev/null 2>&1; then + v="$(pnpm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" || v="" + fi + if [ -n "$v" ]; then + printf '%s' "$v" + else + warn "无法联网解析最新版本(npm/pnpm 查询失败),回退为 latest,由 pnpm 直接解析。" + warn "若已知版本号,可显式传入:bash scripts/install.sh 0.10.2" + printf 'latest' + fi ;; *) printf '%s' "$given" ;; esac @@ -77,11 +109,16 @@ resolve_spec() { dsh_cli() { if command -v "$DSH_CMD" >/dev/null 2>&1; then printf '%s' "$DSH_CMD" - else + elif command -v npx >/dev/null 2>&1; then printf 'npx -y --package @deepseek-ai/dsh dsh' + else + die "未找到 dsh 或 npx。请先安装 DSH(并确保 Node/npm 可用),或用 DSH_CMD 指定 dsh 路径。" fi } +# 前置校验 +command -v node >/dev/null 2>&1 || die "未找到 node(DSH 运行需要 Node.js ≥ 20),请先安装 Node.js 并加入 PATH。" + [ -d "$PROFILE_DIR" ] || die "找不到 profile 目录:${PROFILE_DIR}(请先安装并运行过一次 dsh web)" [ -f "$WS_YML" ] || die "找不到 ${WS_YML}(请先初始化 web profile)" @@ -90,35 +127,52 @@ CLI="$(dsh_cli)" say "目标:$CLI plugin --profile web add $PKG@${SPEC}(profile: ${PROFILE_DIR})" if [ "$DRY_RUN" = true ]; then - say "[dry-run] 步骤 1:确保 $WS_YML 含 allowBuilds(node-pty/protobufjs: true)" + say "[dry-run] 步骤 1:确保 $WS_YML 含 allowBuilds(node-pty/protobufjs: true)与 minimumReleaseAgeExclude(${PKG})" say "[dry-run] 步骤 2:执行 $CLI plugin --profile web add $PKG@${SPEC}(安装 + bundle 自动注册)" - say "[dry-run] 步骤 3:幂等移除 $PATCH_YML 里旧的 better-sidebar 手动挂载行(避免双挂载)" - if [ "$RESTART" = true ]; then say "[dry-run] 步骤 4:pm2 restart dsh-web"; else say "[dry-run] 步骤 4:提示用户手动重启 DSH"; fi + say "[dry-run] 步骤 3:校验 dsh.profile.bundles 含 $PKG" + say "[dry-run] 步骤 4:幂等移除 $PATCH_YML 里旧的 better-sidebar 手动挂载行(避免双挂载)" + if [ "$RESTART" = true ]; then say "[dry-run] 步骤 5:pm2 restart dsh-web"; else say "[dry-run] 步骤 5:提示用户手动重启 DSH"; fi exit 0 fi -# 步骤 1:预写 allowBuilds(幂等),保证 pnpm 不为 node-pty/protobufjs 拦截构建 -if ! grep -qE '^\s*node-pty: true\s*$' "$WS_YML"; then - # 清除 pnpm 自动写入的占位值("set this to true or false")后写入 true - node -e ' - const fs = require("fs"); - const p = process.argv[1]; - let t = fs.readFileSync(p, "utf8"); - t = t.replace(/node-pty:.*/g, "node-pty: true"); - t = t.replace(/protobufjs:.*/g, "protobufjs: true"); - if (!t.includes("allowBuilds:")) t += "\nallowBuilds:\n node-pty: true\n protobufjs: true\n"; - fs.writeFileSync(p, t); - ' "$WS_YML" - say "已确保 $WS_YML 的 allowBuilds(node-pty/protobufjs: true)" -else - say "allowBuilds 已就绪,跳过" -fi +# 步骤 1:预写 workspace 设置(幂等),保证 pnpm 不拦截构建、不放行旧版本策略 +WS_RESULT="$(node -e ' +const fs = require("fs"); +const p = process.argv[1]; +let t = fs.readFileSync(p, "utf8"); +const before = t; +// allowBuilds:把 node-pty/protobufjs 归位为 true +t = t.replace(/^(\s*)(node-pty|protobufjs):.*$/gm, "$1$2: true"); +if (!/^\s*allowBuilds:\s*$/m.test(t)) { + t += "\nallowBuilds:\n node-pty: true\n protobufjs: true\n"; +} else { + for (const k of ["node-pty", "protobufjs"]) { + if (!new RegExp("^\\s*" + k + ":\\s*true\\s*$", "m").test(t)) { + t = t.replace(/^(\s*allowBuilds:\s*)$/m, "$1\n " + k + ": true"); + } + } +} +// minimumReleaseAgeExclude:放行本插件(版本无关),避免 <24h 新版本被拒 +if (!/^\s*-\s+dsh-better-sidebar\s*$/m.test(t)) { + if (/^\s*minimumReleaseAgeExclude:\s*$/m.test(t)) { + t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-better-sidebar"); + } else { + t += "\nminimumReleaseAgeExclude:\n - dsh-better-sidebar\n"; + } +} +if (t !== before) fs.writeFileSync(p, t); +console.log(t === before ? "unchanged" : "updated"); +' "$WS_YML")" +[ "$WS_RESULT" = "updated" ] \ + && say "已确保 $WS_YML:allowBuilds(node-pty/protobufjs: true)+ minimumReleaseAgeExclude(${PKG})" \ + || say "workspace 设置已就绪,跳过" # 步骤 2:官方 CLI 安装 + bundle 自动注册(含挂载) say "执行 $CLI plugin --profile web add $PKG@$SPEC ..." if ! $CLI plugin --profile web add "$PKG@$SPEC" 2>&1 | tail -n +1; then - warn "dsh plugin add 失败。可能原因:minimumReleaseAge(发布 <24h,pnpm 通常自动写入排除并重试)" - warn "或 allowBuilds 未生效。可手动重试:cd $PROFILE_DIR && pnpm install" + warn "dsh plugin add 失败。已预写 allowBuilds 与 minimumReleaseAgeExclude,仍失败的可能原因:" + warn " - 网络/登录问题:npm registry 不可达或需要登录。" + warn " - 依赖安装冲突:可手动重试 cd $PROFILE_DIR && pnpm install。" exit 1 fi @@ -133,24 +187,46 @@ if ! node -e ' warn "若上面的 pnpm 输出提示 ignored build scripts,请确认 $WS_YML 的 allowBuilds 后重跑本脚本。" exit 1 fi -say "bundle 已注册:dsh.profile.bundles 包含 $PKG(下次启动自动挂载)" +say "bundle 已注册:dsh.profile.bundles 包含 ${PKG}(下次启动自动挂载)" # 步骤 3:幂等移除旧的 manual 挂载行(避免与 bundle 双挂载) -if grep -qE '^\s*- id: better-sidebar\b' "$PATCH_YML"; then - node -e ' - const fs = require("fs"); - const p = process.argv[1]; - let t = fs.readFileSync(p, "utf8"); - // 移除 better-sidebar 的 insert 块(含其前的注释行) - t = t.replace(/(?:^[ \t]*#[^\n]*\n)*[ \t]*- insert:\n[ \t]+- id: better-sidebar\n[ \t]+name: '"'"'dsh-better-sidebar'"'"'\n?/g, ""); - // 清理可能留下的空行堆积 - t = t.replace(/\n{3,}/g, "\n\n"); - fs.writeFileSync(p, t); - ' "$PATCH_YML" - say "已从 $PATCH_YML 移除旧的 better-sidebar 手动挂载行(bundle 通道接管挂载)" -else - say "无旧手动挂载行,跳过" -fi +MOUNT_RESULT="$(node -e ' +const fs = require("fs"); +const p = process.argv[1]; +const lines = fs.readFileSync(p, "utf8").split("\n"); +const out = []; +let i = 0; +let removed = false; +while (i < lines.length) { + const line = lines[i]; + if (/^[ \t]*- insert:\s*$/.test(line)) { + const block = [line]; + let j = i + 1; + while (j < lines.length && lines[j].trim() !== "" && !/^-\s/.test(lines[j])) { + block.push(lines[j]); + j++; + } + if (block.some((l) => /id:\s*better-sidebar\b/.test(l))) { + while (out.length && /^[ \t]*#/.test(out[out.length - 1])) out.pop(); + i = j; + removed = true; + continue; + } + } + out.push(line); + i++; +} +if (!removed) { + console.log("none"); +} else { + const t = out.join("\n").replace(/\n{3,}/g, "\n\n"); + fs.writeFileSync(p, t); + console.log("removed"); +} +' "$PATCH_YML")" +[ "$MOUNT_RESULT" = "removed" ] \ + && say "已从 $PATCH_YML 移除旧的 better-sidebar 手动挂载行(bundle 通道接管挂载)" \ + || say "无旧手动挂载行,跳过" say "安装完成:$PKG@$SPEC" From 3cbf43854b240a11748dcad4bf5a43d98a527719 Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Fri, 14 Aug 2026 00:49:01 +0800 Subject: [PATCH 4/5] =?UTF-8?q?docs:=20README=5FEN=20=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E7=AB=A0=E8=8A=82=EF=BC=88PowerShell=20?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E5=91=BD=E4=BB=A4=20+=20=E5=88=86=E6=AD=A5?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=20+=20=E6=95=85=E9=9A=9C=E6=8E=92=E6=9F=A5?= =?UTF-8?q?=E8=A1=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_EN.md | 99 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/README_EN.md b/README_EN.md index ba68d07..1f29e5a 100644 --- a/README_EN.md +++ b/README_EN.md @@ -35,35 +35,86 @@ https://github.com/user-attachments/assets/23187822-047e-45cc-b480-fe997bd55b86 ## 🚀 Installation -Prerequisites: DSH installed (`dsh web` works), Node.js ≥ 20, pnpm ≥ 10. The plugin is published to npm as **`dsh-better-sidebar@0.10.2`** (`@deepseek-ai/*` peers aligned with the host's actual versions `^0.1.0-rc.6` / `@deepseek-ai/cordis@^4.0.1`, single instance). The package declares `dsh.bundle.patch` (the shipped `cordis.patch.yml`), so the official CLI mounts it after install — the DSH source is never modified. +Installation is three steps: **① meet the prerequisites → ② run one command for your OS → ③ restart DSH and hard-refresh**. + +### Prerequisites + +- **DSH** installed and `dsh web` boots (the first run initializes the `~/.dsh/profiles/web` profile). +- **Node.js ≥ 20** (DSH itself requires it). +- **pnpm ≥ 10** (pnpm 11 also works — the installer handles its new policies automatically). +- The plugin is published to npm as **`dsh-better-sidebar@0.10.2`**. It declares `dsh.bundle.patch` (the shipped `cordis.patch.yml`), so the official CLI mounts it after install — the DSH source is never modified. ### One-click install (recommended) +**macOS / Linux** (also works in Git Bash / WSL on Windows): + ```sh curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -# Pick a version: curl -fsSL | bash -s 0.10.2 -# Auto-restart DSH: curl -fsSL | bash -s -- --restart ``` -The script: runs `pnpm add` to register the dependency in `~/.dsh/profiles/web/package.json`, pre-writes `allowBuilds` (node-pty/protobufjs, to dodge pnpm 11's build-script block), detects the package's `dsh.bundle.patch`, auto-appends the plugin to `dsh.profile.bundles`, and idempotently cleans up the old hand-written mount line (to avoid double-mounting). Version defaults to latest (`latest`); `--dry-run` previews without writing. +**Windows (PowerShell 5.1+ / pwsh)**: -### Manual install (npx dsh + command chain) +```powershell +irm https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1 | iex +``` -The same steps as a chain of bash commands (a fresh profile may hit the build-script block once; the `||` branch approves and retries): +**Pin a version / auto-restart after install** (same options on both platforms): ```sh -cd ~/.dsh/profiles/web \ - && npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar \ - || (pnpm approve-builds --all \ - && npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar) -# Pin a version: replace dsh-better-sidebar with dsh-better-sidebar@0.10.2 +# macOS / Linux +curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -s 0.10.2 --restart + +# Windows PowerShell +& ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.2 -Restart ``` -Without `@`, pnpm resolves npm's `latest` tag (the newest release). `dsh plugin add` runs `pnpm add` to register the dependency, detects `dsh.bundle.patch`, auto-appends the plugin to `dsh.profile.bundles`, and the next boot mounts it — no hand-written `cordis.patch.yml` mount line. +The script does four things, all idempotent (safe to re-run): -> ⚠️ pnpm 11's two supply-chain policies, which no command chain can fully dodge: -> - `strict-dep-builds` blocks node-pty/protobufjs build scripts: the first install reports `Ignored build scripts` (the package is already installed and node-pty's prebuilds work as-is); run `pnpm approve-builds --all` once. -> - `minimumReleaseAge` rejects releases younger than 24h: this is a pnpm policy that no command chain can fully avoid — it only triggers because the plugin was just published, and **it disappears on its own after 24 hours** (if it fires, just re-run once; pnpm auto-appends a `minimumReleaseAgeExclude` entry). +1. Pre-writes `allowBuilds` (node-pty / protobufjs) to dodge pnpm 11's build-script block; +2. Pre-writes `minimumReleaseAgeExclude` to allow versions younger than 24 hours; +3. Runs `dsh plugin --profile web add dsh-better-sidebar`: registers the dependency → detects `dsh.bundle.patch` → auto-appends the plugin to `dsh.profile.bundles`; +4. Removes any leftover hand-written mount line to avoid double-mounting (two sidebars on the page). + +Not sure? Add `--dry-run` (`-DryRun` in PowerShell) to preview before committing. `curl | bash` / `irm | iex` executes remote code — the scripts are open source in the repo (`scripts/install.sh` / `scripts/install.ps1`); download and review them first if you prefer. + +### Manual install (step by step) + +For users who want to see every step; equivalent to the one-click script. **Step ③ is repeatable; ①② only need to run once.** + +**macOS / Linux (bash)**: + +```sh +cd ~/.dsh/profiles/web + +# ① Allow node-pty / protobufjs build scripts (pnpm 11 blocks them by default; skip on pnpm 10) +pnpm approve-builds --all + +# ② Allow versions published less than 24h ago (skip for older releases; if the key already exists, merge the line under it instead) +cat >> pnpm-workspace.yaml <<'EOF' +minimumReleaseAgeExclude: + - dsh-better-sidebar +EOF + +# ③ Install and auto-mount (no @version = npm's latest; pin with dsh-better-sidebar@0.10.2) +npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar +``` + +**Windows (PowerShell)**: + +```powershell +cd ~\.dsh\profiles\web + +# ① Allow build scripts +pnpm approve-builds --all + +# ② Allow fresh releases (once; if the key already exists, merge - dsh-better-sidebar under it instead) +Add-Content -Path pnpm-workspace.yaml -Value "`nminimumReleaseAgeExclude:`n - dsh-better-sidebar" + +# ③ Install and auto-mount +npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar +``` + +`dsh plugin add` runs `pnpm add` to register the dependency, detects `dsh.bundle.patch`, auto-appends the plugin to `dsh.profile.bundles`, and the next boot mounts it — **no hand-written `cordis.patch.yml` mount line**. ### Updating @@ -71,7 +122,18 @@ Without `@`, pnpm resolves npm's `latest` tag (the newest release). `ds dsh plugin --profile web add dsh-better-sidebar ``` -or re-run the one-click script; or bump the version range in `~/.dsh/profiles/web/package.json` (e.g. `"^0.10.2"`) and run `pnpm install`. Then restart DSH and hard-refresh (Cmd/Ctrl+Shift+R). +or re-run the one-click script; or bump the version in `~/.dsh/profiles/web/package.json` (e.g. `"^0.10.2"`) and run `pnpm install`. Then restart DSH and hard-refresh (Cmd/Ctrl+Shift+R). + +### Troubleshooting + +| Symptom | Cause & fix | +|---|---| +| `Ignored build scripts` | pnpm 11 blocked build scripts. Run `pnpm approve-builds --all` (the one-click script handles it). | +| `minimum release age` / version `< 24h` | The release is younger than 24 hours. Wait, or re-run once (pnpm auto-adds `minimumReleaseAgeExclude`); the one-click script handles it. | +| "profile directory not found" | Run `dsh web` once so it initializes `~/.dsh/profiles/web`. | +| Two sidebars on the page | Double-mount: `~/.dsh/profiles/web/cordis.patch.yml` still has the old hand-written `- insert: ... better-sidebar ...` line — delete it (the one-click script cleans it). | +| Terminal fails on Windows | `node-pty` relies on prebuilt binaries; if none match your Node version, install a build toolchain (VS Build Tools). Mainstream Node versions are usually covered. | +| No bash / curl on Windows | Use the PowerShell one-click command, or install Git Bash / WSL and run the bash commands. |
Install from source / develop (optional — alternative to the npm flow) @@ -82,7 +144,10 @@ To debug local changes or track the dev branch, point the dependency at a local 1. git clone https://github.com/omdsh-dev/DSH-better-sidebar.git ~/Code/DSH-better-sidebar cd ~/Code/DSH-better-sidebar && pnpm install && pnpm build 2. In ~/.dsh/profiles/web/package.json dependencies write "dsh-better-sidebar": "link:" -3. Append the mount line to ~/.dsh/profiles/web/cordis.patch.yml (same as above) +3. Append this mount line to ~/.dsh/profiles/web/cordis.patch.yml: + - insert: + - id: better-sidebar + name: 'dsh-better-sidebar' 4. Run pnpm install in ~/.dsh/profiles/web 5. Restart DSH and hard-refresh ``` From 97e52b650e8444fdfbf7049216f416693318a09a Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Fri, 14 Aug 2026 00:59:02 +0800 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20=E5=AE=89=E8=A3=85=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E6=8A=98=E5=8F=A0=E6=8A=80=E6=9C=AF=E7=BB=86=E8=8A=82?= =?UTF-8?q?=E4=B8=BA=E5=8F=AF=E6=8A=98=E5=8F=A0=E5=9D=97=EF=BC=8C=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=BC=95=E7=94=A8=E6=9B=B4=E6=96=B0=E4=B8=BA=200.10.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 66 ++++++++++++++++++++++++++++++---------------------- README_EN.md | 66 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 83186d0..2ab6547 100644 --- a/README.md +++ b/README.md @@ -35,18 +35,9 @@ https://github.com/user-attachments/assets/23187822-047e-45cc-b480-fe997bd55b86 ## 🚀 安装 -装好只需三步:**① 满足前置 → ② 跑一条对应平台的一键命令 → ③ 重启 DSH 并硬刷新**。 +**前置**:已装好 DSH(`dsh web` 能正常运行),Node.js ≥ 20、pnpm ≥ 10。 -### 前置要求 - -- 已安装 **DSH**,且 `dsh web` 能正常启动(首次运行会初始化 `~/.dsh/profiles/web` 这个 profile)。 -- **Node.js ≥ 20**(DSH 运行本身就需要)。 -- **pnpm ≥ 10**(装依赖用;pnpm 11 也可,脚本会自动处理它的新策略)。 -- 插件已发布到 npm:**`dsh-better-sidebar@0.10.2`**。包内声明了 `dsh.bundle.patch`(随包发布的 `cordis.patch.yml`),官方 CLI 装完即自动挂载,**不修改 DSH 源码**。 - -### 一键安装(推荐) - -**macOS / Linux**(Windows 装了 Git Bash 或 WSL 也可用): +**macOS / Linux**(Windows 装了 Git Bash 或 WSL 也可): ```sh curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash @@ -58,28 +49,27 @@ curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/s irm https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1 | iex ``` -**指定版本 / 装完自动重启**(两平台参数一致): +装完**重启 DSH 并硬刷新**(Cmd/Ctrl+Shift+R)即可看到侧边栏。 + +
+指定版本 / 装完自动重启(可选) ```sh # macOS / Linux -curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -s 0.10.2 --restart +curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -s 0.10.3 --restart # Windows PowerShell -& ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.2 -Restart +& ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.3 -Restart ``` -脚本会自动完成 4 件事(全部幂等,可安全重复执行): +不确定的话,可先加 `--dry-run`(PowerShell 用 `-DryRun`)预览步骤再执行。 -1. 预写 `allowBuilds`(node-pty / protobufjs),规避 pnpm 11 的构建脚本拦截; -2. 预写 `minimumReleaseAgeExclude`,放行「发布不足 24 小时」的新版本; -3. 执行 `dsh plugin --profile web add dsh-better-sidebar`:登记依赖 → 识别 `dsh.bundle.patch` → 自动注册进 `dsh.profile.bundles` 挂载; -4. 清理旧版残留的手动挂载行,避免「双挂载」(页面出现两个侧边栏)。 +
-不确定的话,可先加 `--dry-run`(PowerShell 用 `-DryRun`)预览步骤再真正执行。`curl | bash` / `irm | iex` 会执行远程代码——脚本已随仓库开源(`scripts/install.sh` / `scripts/install.ps1`),可先下载审阅。 +
+手动安装(逐步命令,想看清每一步) -### 手动安装(逐步命令) - -适合想看清每一步的用户,与一键脚本等价。**第 ③ 步可重复执行;①② 只需做一次。** +与一键脚本等价。**第 ③ 步可重复执行;①② 只需做一次。** **macOS / Linux(bash)**: @@ -95,7 +85,7 @@ minimumReleaseAgeExclude: - dsh-better-sidebar EOF -# ③ 安装并自动挂载(不带 @版本 = npm 的 latest;固定版本写 dsh-better-sidebar@0.10.2) +# ③ 安装并自动挂载(不带 @版本 = npm 的 latest;固定版本写 dsh-better-sidebar@0.10.3) npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar ``` @@ -114,9 +104,24 @@ Add-Content -Path pnpm-workspace.yaml -Value "`nminimumReleaseAgeExclude:`n - d npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar ``` -`dsh plugin add` 会:`pnpm add` 登记依赖 → 识别包内 `dsh.bundle.patch` → 自动注册到 `dsh.profile.bundles` → 下次启动自动挂载(**无需手写 `cordis.patch.yml`**)。 +
-### 更新 +
+脚本内部做了什么(技术细节) + +一键脚本自动完成 4 件事(全部幂等,可安全重复执行): + +1. 预写 `allowBuilds`(node-pty / protobufjs),规避 pnpm 11 的构建脚本拦截; +2. 预写 `minimumReleaseAgeExclude`,放行「发布不足 24 小时」的新版本; +3. 执行 `dsh plugin --profile web add dsh-better-sidebar`:登记依赖 → 识别包内 `dsh.bundle.patch` → 自动注册进 `dsh.profile.bundles` 挂载; +4. 清理旧版残留的手动挂载行,避免「双挂载」(页面出现两个侧边栏)。 + +`curl | bash` / `irm | iex` 会执行远程代码——脚本已随仓库开源(`scripts/install.sh` / `scripts/install.ps1`),可先下载审阅。插件以 npm 包 `dsh-better-sidebar@0.10.3` 发布,通过 `dsh.bundle.patch`(随包的 `cordis.patch.yml`)由官方 CLI 自动挂载,**不修改 DSH 源码**。 + +
+ +
+更新 ```sh dsh plugin --profile web add dsh-better-sidebar @@ -124,7 +129,10 @@ dsh plugin --profile web add dsh-better-sidebar 或重跑一次一键脚本;也可把 `~/.dsh/profiles/web/package.json` 里的版本号改高后 `pnpm install`。改完**重启 DSH 并硬刷新**(Cmd/Ctrl+Shift+R)。 -### 常见问题 +
+ +
+常见问题 | 现象 | 原因与解决 | |---|---| @@ -135,6 +143,8 @@ dsh plugin --profile web add dsh-better-sidebar | Windows 下终端无法使用 | `node-pty` 依赖预编译二进制;若当前 Node 版本没有对应产物,需装编译工具链(VS Build Tools)。主流 Node 版本一般已有预编译。 | | Windows 没有 bash / curl | 直接用 PowerShell 一键命令;或安装 Git Bash / WSL 再跑 bash 命令。 | +
+
从源码安装 / 开发(可选,替代 npm 方式) @@ -152,7 +162,7 @@ dsh plugin --profile web add dsh-better-sidebar 5. 重启 DSH 并硬刷新 ``` -更新:`git pull && pnpm install && pnpm build` → 重启 DSH(仅 client 改动可硬刷新)。切回 npm 通道时,把依赖改回 `"dsh-better-sidebar": "^0.10.2"` 再 `pnpm install`。 +更新:`git pull && pnpm install && pnpm build` → 重启 DSH(仅 client 改动可硬刷新)。切回 npm 通道时,把依赖改回 `"dsh-better-sidebar": "^0.10.3"` 再 `pnpm install`。
diff --git a/README_EN.md b/README_EN.md index 1f29e5a..5f43479 100644 --- a/README_EN.md +++ b/README_EN.md @@ -35,16 +35,7 @@ https://github.com/user-attachments/assets/23187822-047e-45cc-b480-fe997bd55b86 ## 🚀 Installation -Installation is three steps: **① meet the prerequisites → ② run one command for your OS → ③ restart DSH and hard-refresh**. - -### Prerequisites - -- **DSH** installed and `dsh web` boots (the first run initializes the `~/.dsh/profiles/web` profile). -- **Node.js ≥ 20** (DSH itself requires it). -- **pnpm ≥ 10** (pnpm 11 also works — the installer handles its new policies automatically). -- The plugin is published to npm as **`dsh-better-sidebar@0.10.2`**. It declares `dsh.bundle.patch` (the shipped `cordis.patch.yml`), so the official CLI mounts it after install — the DSH source is never modified. - -### One-click install (recommended) +**Prerequisites**: DSH installed (`dsh web` boots), Node.js ≥ 20, pnpm ≥ 10. **macOS / Linux** (also works in Git Bash / WSL on Windows): @@ -58,28 +49,27 @@ curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/s irm https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1 | iex ``` -**Pin a version / auto-restart after install** (same options on both platforms): +Then **restart DSH and hard-refresh** (Cmd/Ctrl+Shift+R) to see the sidebar. + +
+Pin a version / auto-restart (optional) ```sh # macOS / Linux -curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -s 0.10.2 --restart +curl -fsSL https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.sh | bash -s 0.10.3 --restart # Windows PowerShell -& ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.2 -Restart +& ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.3 -Restart ``` -The script does four things, all idempotent (safe to re-run): +Not sure? Add `--dry-run` (`-DryRun` in PowerShell) to preview before running. -1. Pre-writes `allowBuilds` (node-pty / protobufjs) to dodge pnpm 11's build-script block; -2. Pre-writes `minimumReleaseAgeExclude` to allow versions younger than 24 hours; -3. Runs `dsh plugin --profile web add dsh-better-sidebar`: registers the dependency → detects `dsh.bundle.patch` → auto-appends the plugin to `dsh.profile.bundles`; -4. Removes any leftover hand-written mount line to avoid double-mounting (two sidebars on the page). +
-Not sure? Add `--dry-run` (`-DryRun` in PowerShell) to preview before committing. `curl | bash` / `irm | iex` executes remote code — the scripts are open source in the repo (`scripts/install.sh` / `scripts/install.ps1`); download and review them first if you prefer. +
+Manual install (step by step) -### Manual install (step by step) - -For users who want to see every step; equivalent to the one-click script. **Step ③ is repeatable; ①② only need to run once.** +Equivalent to the one-click script. **Step ③ is repeatable; ①② only need to run once.** **macOS / Linux (bash)**: @@ -95,7 +85,7 @@ minimumReleaseAgeExclude: - dsh-better-sidebar EOF -# ③ Install and auto-mount (no @version = npm's latest; pin with dsh-better-sidebar@0.10.2) +# ③ Install and auto-mount (no @version = npm's latest; pin with dsh-better-sidebar@0.10.3) npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar ``` @@ -114,17 +104,35 @@ Add-Content -Path pnpm-workspace.yaml -Value "`nminimumReleaseAgeExclude:`n - d npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar ``` -`dsh plugin add` runs `pnpm add` to register the dependency, detects `dsh.bundle.patch`, auto-appends the plugin to `dsh.profile.bundles`, and the next boot mounts it — **no hand-written `cordis.patch.yml` mount line**. +
-### Updating +
+What the script does (technical details) + +The one-click script does four things, all idempotent (safe to re-run): + +1. Pre-writes `allowBuilds` (node-pty / protobufjs) to dodge pnpm 11's build-script block; +2. Pre-writes `minimumReleaseAgeExclude` to allow versions younger than 24 hours; +3. Runs `dsh plugin --profile web add dsh-better-sidebar`: registers the dependency → detects `dsh.bundle.patch` → auto-appends the plugin to `dsh.profile.bundles`; +4. Removes any leftover hand-written mount line to avoid double-mounting (two sidebars on the page). + +`curl | bash` / `irm | iex` executes remote code — the scripts are open source in the repo (`scripts/install.sh` / `scripts/install.ps1`); download and review them first if you prefer. The plugin ships as npm package `dsh-better-sidebar@0.10.3` and mounts via `dsh.bundle.patch` (the shipped `cordis.patch.yml`), so the DSH source is never modified. + +
+ +
+Updating ```sh dsh plugin --profile web add dsh-better-sidebar ``` -or re-run the one-click script; or bump the version in `~/.dsh/profiles/web/package.json` (e.g. `"^0.10.2"`) and run `pnpm install`. Then restart DSH and hard-refresh (Cmd/Ctrl+Shift+R). +or re-run the one-click script; or bump the version in `~/.dsh/profiles/web/package.json` (e.g. `"^0.10.3"`) and run `pnpm install`. Then restart DSH and hard-refresh (Cmd/Ctrl+Shift+R). -### Troubleshooting +
+ +
+Troubleshooting | Symptom | Cause & fix | |---|---| @@ -135,6 +143,8 @@ or re-run the one-click script; or bump the version in `~/.dsh/profiles/web/pack | Terminal fails on Windows | `node-pty` relies on prebuilt binaries; if none match your Node version, install a build toolchain (VS Build Tools). Mainstream Node versions are usually covered. | | No bash / curl on Windows | Use the PowerShell one-click command, or install Git Bash / WSL and run the bash commands. | +
+
Install from source / develop (optional — alternative to the npm flow) @@ -152,7 +162,7 @@ To debug local changes or track the dev branch, point the dependency at a local 5. Restart DSH and hard-refresh ``` -Update: `git pull && pnpm install && pnpm build` → restart DSH (client-only changes can just hard-refresh). To switch back to the npm channel, restore `"dsh-better-sidebar": "^0.10.2"` and re-run `pnpm install`. +Update: `git pull && pnpm install && pnpm build` → restart DSH (client-only changes can just hard-refresh). To switch back to the npm channel, restore `"dsh-better-sidebar": "^0.10.3"` and re-run `pnpm install`.