@qzsy/dsh-custom-pages
v0.1.2
Published
Custom page bookmarks for the DSH left sidebar: a hover flyout lists configured pages, each opens in an embedded web view, and a configuration panel manages titles and URLs.
Readme
@qzsy/dsh-custom-pages
A DeepSeek Harness Client plugin that adds a Custom pages entry to the left sidebar, a hover flyout of configured pages, an embedded web view for simple pages, the official right-Sidebar Browser for pages that need sign-in, and a configuration panel for titles, URLs, and per-page sign-in mode.
Install
dsh plugin add @qzsy/dsh-custom-pagesThe bundle patch inserts one row, custom-pages; no Host route, command, or
tool is registered.
What it does
Hover the 自定义页 / Custom pages row in the left sidebar (expanded, or the collapsed rail) and a frame-wide flyout opens beside it:
| Row | Behavior |
| --- | --- |
| Configured page (inline) | Selects the page in the main panel and renders its URL in a sandboxed embedded web view. The active page carries a brand-colored dot. |
| Configured page (sidebar browser) | Switches to the current conversation and opens the URL in the shipped right-Sidebar Browser (a native Electron <webview> on Desktop). Use this for sites that require sign-in. |
| 配置菜单 / Configure menu (always last) | Opens the same panel in its configuration view. |
The panel itself keeps a working address bar: a chip strip for every page, a reload button, open in browser, open in the sidebar browser, and a shortcut to the configuration view. The current URL is printed in the status bar with a hint for sites that refuse to be embedded. Choosing a sidebar-browser page shows a launcher card that explains the hand-off and offers both the sidebar Browser and the system browser.
The configuration view lists every page and supports the full CRUD loop:
- add a page with a title and URL;
- choose per page whether it opens inline or in the sidebar browser;
- edit the title, URL, and open mode inline (URLs are normalized on blur, and
missing schemes become
https://); - reorder with the up and down controls;
- delete a page, with a five-second Undo toast;
- clear every page with a two-step confirmation and the same undo.
Titles are capped at 80 characters, pages at 50. A URL must be HTTP(S), may not carry a username or password, and may not be the DSH application origin itself; invalid rows keep their text and explain the problem instead of silently disappearing.
Sign-in sites, cookies, and iframes
An embedded iframe is not a browser tab. Login-required sites commonly fail in the inline view for reasons outside this plugin:
X-Frame-Options: DENY/SAMEORIGINor a CSPframe-ancestorsdirective blocks the frame outright;- third-party cookie restrictions and
SameSite=Lax/Strictauth cookies are not sent in a cross-site iframe; - some login flows need top-level navigation or a popup that a sandboxed frame cannot provide.
For those pages enable 用右侧浏览器打开(支持登录) on the row (or while adding it). Selecting the page then switches to the current conversation and opens the URL in DSH's right-Sidebar Browser, which is a first-party browsing context. The sidebar Browser is optional: when the Browser tab type is not registered (the Web profile disables it by default), the plugin leaves the custom page launcher card with a retry action and a system-browser action instead of a blank frame.
Desktop Browser tabs keep cookies in the session's own partition; DSH documents that these cookies and Web storage do not survive an application restart. For a long-lived login, use the system browser or sign in again after restart. This plugin never reads or writes site cookies itself.
Where it plugs in
| Surface | Mechanism |
| --- | --- |
| Sidebar row | sidebar.panellist list registration with the shared id custom-pages. |
| Main panel | The root-scoped keyed main slot under the same id, so the shipped sidebar row click selects it. |
| Hover flyout | A shell.overlay occupant (frame-wide, click-through layer) positioned from the hovered row's rectangle. It is never clipped by the sidebar column. |
| Inline page | A sandboxed <iframe> with the same flag set as the shipped Browser tab. |
| Sign-in hand-off | After the conversation is mounted, ctx.sidebarRight.openTab('browser', { params: { url } }); an optional ctx.sidebarRightTabs.get('browser') check avoids switching away when the Browser type is absent. |
| Storage | localStorage key qzsy.custom-pages.v1 (origin-scoped) with title, URL, order, and open mode. Nothing is uploaded. |
The plugin requires the installed Client services slots, locale, and
layout; sidebarRight and sidebarRightTabs are optional and drive the
sign-in hand-off.
Security and privacy
- The configuration never leaves the browser. There is no Host route and no remote sync.
- The embedded page runs in a sandboxed iframe. It cannot read DSH DOM, storage, or API responses unless it is same-origin, which the URL validator refuses.
- Only HTTP(S) is accepted;
javascript:,data:,file:, and credentialed URLs are rejected. - Sites that send
X-Frame-Options, a restrictive CSP, or cross-site auth cookies that reject iframes may render blank or logged out. Use the per-page sidebar-browser mode; on Desktop it hands the URL to the official Electron webview instead of embedding it. - The plugin never handles credentials, cookies, or tokens itself. Sign-in stays inside the embedded carrier or the system browser.
Files
src/index.ts— empty Host half (the bundle row needs one).runtime/client.js— prebuilt DSH Client module: sidebar row, hover flyout, embedded web view, configuration editor, styles, and zh/en dictionaries.cordis.patch.yml— inserts thecustom-pagesbundle row.locale/en.json,locale/zh.json— Plugin Manager title and description.icon.svg— original artwork for the Plugin Manager row.dist/— Bun build output (bun run build), not committed.
