前言¶
DSH 的左側欄上半部分是一列「全局面板入口」:插件、插件市場、自動化任務、遠程窗口、任務看板、記憶系統。每裝一個插件,這裏就多一行。列表越長,會話歷史就被擠得越靠下,而會話歷史纔是日常真正在用的那塊。
dsh-entry-drawer 就是針對這個痛點的插件。維護者是 xingzhen199186,分類爲客戶端插件,協議 MIT,npm 上版本 0.1.0,倉庫星標爲 0。它不做任何新功能,只借平臺留給第三方插件的唯一座位 sidebar.panellist,在入口列表最前面放一個開關行,把其餘入口整塊收起,把空間還給會話歷史。
這是什麼¶
一句話定位:在 DSH 0.2.x 的側欄入口列表最前面加一行開關,點擊後隱藏除自己以外的所有入口,再點一次恢復。
瀏覽器端的能力只有這一條,但做得比較完整:收起狀態會被記住,重載頁面、重啓桌面端都保持上次的選擇;展開/收起都不會改動 DOM,收起通過一條 CSS 規則表達。
平臺側沒有被它修改。座位(sidebar.panellist)是界面上一個固定位置,插件只能往裏放自己的東西,改不了別人放的東西;殼層掌管這些行的盒子、行高和點擊行爲,平臺本身也沒有「把入口收成一組」的能力。
你會看到什麼¶
展開時(默認狀態),新增的一行位於左側欄入口列表第一行,就在會話歷史上方,內容是一對朝上的雙箭頭圖標加文字「入口收納」(英文界面顯示 Entry drawer),下方是原有的全部入口,一個不少。
點擊這一行:
- 其餘入口整塊隱藏,直接不佔位置,不是變灰;
- 會話歷史上移,多出幾行空間;
- 箭頭翻成朝下,開關行留在原位。
再點一次,全部入口回來,箭頭朝上。
收起狀態存兩份:瀏覽器 cookie(鍵 dsh-entry-drawer,一年有效)和 localStorage(鍵 dsh-entry-drawer:collapsed)。桌面端每次啓動會在 127.0.0.1 上換一個端口,而瀏覽器按「協議 + 主機 + 端口」分區存 localStorage,cookie 只認主機不認端口,所以跨端口重啓後依然記得。
一條已實測的行爲:2026-11-05 在桌面端 0.2.2-rc.1 上驗證過,先收起再展開正常;之後新裝的第三方插件多出來的入口也會被一併收進去,不需要重新配置。
安裝¶
前提:DSH 0.2.x,本機有可用的 Node.js 與 pnpm(dsh plugin 只是 pnpm 的轉發器)。三種安裝方式按場景選一種。
從 npm 安裝(推薦):
dsh plugin --profile web add dsh-entry-drawer
web 換成你實際在用的 profile 名。
跟最新代碼、或 npm 還沒同步到新版時,從 GitHub 安裝:
dsh plugin --profile web add github:xingzhen199186/dsh-entry-drawer
改代碼時想一邊改一邊看效果,用本地目錄安裝:
dsh plugin --profile web add ./dsh-entry-drawer
也可以在插件管理器裏選「從目錄安裝」並指向該文件夾。注意這種方式 profile 裏記的是目錄路徑,之後移動或刪除這個文件夾,插件就加載不起來了。
裝完必須重載:Web 端刷新頁面即可;桌面端建議直接重啓應用,因爲跑在瀏覽器裏的那一半(client.js)要等窗口重新加載纔會掛上,桌面端默認沒有客戶端熱更新。
另外,桌面端請優先在應用內安裝。桌面端的 profile 由應用自己託管,命令行寫入一般會被拒絕(提示該 profile 只由桌面應用管理),這不是權限問題。在桌面端「插件」頁面填包名、倉庫地址或本機目錄即可。
卸載:
dsh plugin --profile web remove dsh-entry-drawer
然後重載窗口。收起狀態不會跟着刪掉——它存在瀏覽器裏,想清掉見後面的常見問題。
它是怎麼做到的¶
一共三件事:佔一個座位、攔一次點擊、寫一條樣式規則,外加一個保險面板和一處存偏好的地方。
第一步,佔座位。客戶端半在側欄座位上註冊一行,order: -1000 讓它排在入口列表最前面:
ctx.slots.inject('sidebar.panellist', () =>
ctx.slots.register(
{ name: 'sidebar.panellist', id: 'entry-drawer', order: -1000, label },
FoldGlyph,
),
)
行裏畫出來的就是開關本身:一個帶 .dsh-entry-drawer-mark 標記的 span,裏面是雙箭頭圖標,文字標籤「入口收納」來自插件自帶的中英詞典。
第二步,攔點擊。行盒子和行點擊都歸殼層,插件改不了,於是在 document 上註冊一個捕獲階段的 click 監聽(捕獲階段比殼層自己的處理更早看到這次點擊)。判據很窄:點到的元素在某個 button 裏,且那個 button 含本插件的標記。命中就 preventDefault() 加 stopPropagation(),殼層「切換到某個面板」的邏輯不再執行,插件改爲翻轉收起狀態並把選擇存起來。整個插件只註冊一個監聽,隨插件一起掛上和回收,點側欄其他位置不受影響。
第三步,寫樣式。收起不改 DOM,由插件自己的一條 <style> 表達,收起時追加:
nav[aria-label="全局面板"] > *:not(:has(.dsh-entry-drawer-mark)) { display: none !important }
讀法:在無障礙名爲「全局面板」的那個 nav 裏,隱藏所有不帶本插件標記的直接子項。導航名優先取活的語言詞典(sidebar 命名空間的 panels.label),取不到時用已知的兩個名字「全局面板」和「Global panels」各生成一條規則,所以中英文界面都能命中;名字拼進選擇器前做過程序內轉義。每個名字都要單獨生成一條規則並寫全子項部分,不能合成一個逗號列表——否則前半段會把整個導航(連開關一起)隱藏掉。切換界面語言時樣式重算。
保險面板和狀態存儲:插件還在主內容區(main)註冊了一個同 id 的佔位面板,渲染結果爲空。正常路徑用不到它,因爲點擊已被攔下;若某條旁路仍然選中了這個面板,它會在掛載時翻轉收起狀態,然後跳回上一個看過的面板,而不是因爲沒有內容報錯。宿主半(index.js)是空實現,apply() 裏什麼都不做,留着只爲讓這個包在插件樹裏有一個可選中、可檢查、可卸載的入口——所有邏輯都在客戶端。
測試¶
沒有構建步驟,純 JS,客戶端半是 window.__ModuleLoader__.load({ id, factory }) 形式的模塊。測試不需要瀏覽器也不需要裝依賴,用 Node 內置的 assert、一個極小的假 React 與假 DOM 直接驅動 client.js:
node test/client.test.mjs
通過時輸出 client.test.mjs: all checks passed。覆蓋範圍:兩個座位的註冊契約、展開與收起兩種樣式表、點擊攔截(擋住殼層、翻轉狀態、寫入偏好)、點別處不受影響、活詞典優先、標籤轉義、無詞典與無存儲時的降級、樣式表和監聽器的歸屬與回收。
改完 client.js 要重載窗口才生效;桌面端請直接重啓,它默認不跟隨源碼熱更新。
已知限制¶
以下是實際情況,不是免責聲明。
- 依賴導航的無障礙名。樣式規則靠
nav[aria-label=...]定位,上游哪天改掉這個名字或不再用nav元素,插件會安靜地失效(收起不生效,也不誤藏別的東西)。修法是改client.js裏的KNOWN_NAV_LABELS。 - 點擊攔截在 React 之外。它靠「
button裏含本插件標記」這個結構判斷,上游若改了行結構,攔截會退化:點擊落回殼層並選中這一個面板,保險面板會跳回原面板,體感是「點了沒反應」。 - 收起是整組隱藏。官方的「插件」「自動化任務」「更多」會一起消失,因爲它們和第三方入口在同一個列表裏,做不到只藏第三方、留下官方。展開即恢復。
- 窄側欄下沒有可收的東西。Windows 桌面端把側欄收成窄條時,整塊入口列表本來就是隱藏的,插件不改變任何東西。
- 本地目錄安裝有脆弱點。用
add ./目錄裝時,profile 裏記的是目錄路徑,移動或刪除該文件夾客戶端半會加載失敗,長期用請改用 git 安裝,或別動這個目錄。 - 狀態按瀏覽器存。換瀏覽器、換主機名(例如從
127.0.0.1換成localhost)、清站點數據,都會回到展開。多個窗口共用同一份存儲,但插件不監聽storage事件,一個窗口切換後另一個窗口要重載纔跟上。
常見問題¶
點了這一行入口整塊不見了,怎麼找回來?再點同一行,這一行始終留着,它就是開關。
收起之後點不回來了?通常是上游改了行結構,這一下點擊落回了殼層。收起狀態記在 cookie 裏,刷新不會把它變回來,可在控制台執行 document.cookie = 'dsh-entry-drawer=0; path=/' 再刷新,或清掉這個站點的數據。
會影響別的側欄插件嗎?不會改它們的代碼或狀態。裝在同一列表裏的入口收起時會跟着一起藏起來,展開就回來;側欄其他區域和主內容區不受影響。
能只收第三方入口、留下官方入口嗎?不能。它們在同一個列表裏,從這個座位只能整組隱藏。
適用場景與注意¶
適合 DSH 0.2.x 的 Web 端與桌面端用戶:側欄裝了較多插件、會話歷史被入口列表擠得要靠滾動找的人。形態爲客戶端插件(dsh.client 聲明 platform: "web"),依賴 @deepseek-ai/dsh-client-ui-sidebar;沒有側欄的形態(例如 headless 命令行)不加載。
狀態存放在瀏覽器本地,不寫 profile、不碰任何配置文件——但插件是以當前 dsh 進程權限運行的。安裝前應檢查源碼與許可證(MIT,見倉庫 LICENSE)。
目錄頁與源碼:
- 目錄頁:https://www.skillhub.cn/plugins/xingzhen199186/dsh-entry-drawer
- GitHub:https://github.com/xingzhen199186/dsh-entry-drawer
生態背景:DSH 的理念是「一切皆插件」,插件生態由社區維護,目錄站點是獨立站點,與 DeepSeek / 幻方無官方從屬關係。