Commit Graph
374 Commits
Author SHA1 Message Date
HuanLinOTO e9a488024c merge #336: add unrun devDependency for tsdown config loading 2026-08-23 20:19:20 +08:00
HuanLinOTO ed3083d133 merge #338: 移除公开版 cordis peer,迁移类型基底到 @deepseek-ai/cordis
# Conflicts:
#	README.md
#	README_EN.md
#	package.json
2026-08-23 20:18:55 +08:00
HuanLinOTO 3445331494 merge #278: panel host clip viewport, hidden panel no longer causes bidirectional overflow 2026-08-23 20:17:58 +08:00
HuanLinOTO 60b9c867d8 merge #342: fs-search skip node_modules and other noise dirs 2026-08-23 20:17:22 +08:00
HuanLinOTO c88eccfadb merge #330: drop panel host below DSH cordis dynamic-plugin layer 2026-08-23 20:17:16 +08:00
HuanLinOTOandxxxg0001 5c88279d7a merge #285: forward-port linked worktrees to RC8
Co-authored-by: xxxg0001 <xxxg0001@users.noreply.github.com>
2026-08-23 20:17:14 +08:00
HuanLinOTO 23a72a5f64 merge #310: keep conversation composer in viewport under layout push 2026-08-23 20:16:59 +08:00
HuanLinOTO ba83dd33f7 merge #341: fix(mermaid): suppress global error rendering 2026-08-23 20:16:33 +08:00
多玩幻灵qwq d6d8db0416 Merge pull request #339 from HuanLinOTO/feat/better-locale-ja
feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 19 语言覆盖(ja/de/fr/...)
2026-08-23 19:52:15 +08:00
HuanLinOTO 4af8d3121a fix: devDependency 改用 npm registry 版本,去掉本机 link 路径 2026-08-23 19:42:24 +08:00
HuanLinOTO f58e361cbe fix(i18n): guard ctx.get call so absent better-locale is truly no-op
Sidebar.tsx read ctx.get('betterLocale') unconditionally. In real
cordis hosts ctx.get exists and returns undefined when better-locale
is absent — but the unit-test mocks build a minimal ctx without a
get method, throwing 'TypeError: ctx.get is not a function' inside
<Sidebar> render and taking down 7 tests
(bottom-auto-terminal + sidebar-crash suites).

Add a typeof guard so the optional contract holds even when ctx.get
is missing. The activation-order-safe ctx.effect path in index.tsx
is unaffected (real cordis Context, runs once).
2026-08-23 16:56:07 +08:00
HuanLinOTO 7f8f95a59b feat(i18n): 覆盖支持扩展到 19 语言(de/fr/pt/ko/ar/hi/id/tr/vi/th/ru/it/nl/sv/pl/zh-HK/zh-TW/zh-MO) 2026-08-23 14:57:48 +08:00
Zzy dab6e795a8 Merge upstream main into fix/composer-clipped-by-layout-push 2026-08-23 11:19:36 +08:00
kelvinwww 4882a5c90f docs: 补充文件 API 路径安全说明 2026-08-23 10:42:36 +08:00
kelvinwww e9ce58afea fix: 限制文件 API 访问会话工作区 2026-08-23 10:28:05 +08:00
lx b87f263c09 fix(build): make cleanup command cross-platform 2026-08-23 03:26:25 +08:00
leokuang d0f194b67a fix(explorer): 文件名搜索跳过 node_modules 等噪声目录
右侧文件窗口的全局文件名搜索会深度优先遍历整个工作区,原先只跳过
.git。含大型 node_modules 的仓库会先把 10 万条访问预算耗尽,导致
docs/ 等后序目录里的真实文件搜不到。

搜索改为跳过依赖、包管理存储和常见构建缓存目录(不匹配也不下降),
并补回归测试钉住 node_modules / dist 内的命中不会泄漏。
2026-08-23 02:50:37 +08:00
lx 57a4ad82e1 merge main into fix/multi-repository-git-selector 2026-08-23 02:50:34 +08:00
HuanLinOTO 16a809594c fix(i18n): better-locale 接入改为 activation-order-safe
ctx.get('betterLocale') 是非响应式读取(cordis 只对声明的 inject
依赖重新求值)。如果 better-locale 在 better-sidebar 之后激活,
apply 顶层的 ctx.get 返回 undefined,attachBetterLocale(undefined)
使整个覆盖接入变成 no-op,即使用户后来在设置页选了日语,
better-sidebar 的 t() 也不查询 override store。

改为在 ctx.effect 中订阅 ctx.locale.subscribe:better-locale 在
激活时(若有持久化的 override)和用户切换 override 时都会 bump
locale revision,subscribe 回调触发 sync(),重新读取 ctx.get
('betterLocale')。store 可用后 attachBetterLocale(store) + register
ja dict;store 不可用时 detach。

这解决了插件激活顺序不确定的问题:无论 better-locale 先于还是后于
better-sidebar 激活,override 接入都能正确建立。
2026-08-23 02:31:30 +08:00
zhangqianfeng a75928daf1 fix(mermaid): suppress global error rendering 2026-08-23 01:50:07 +08:00
HuanLinOTO 38dc807d15 refactor(i18n): 覆盖只在 DSH active=en 时生效(借用英文槽位)
better-locale 的覆盖本质上是借用 DSH 的英文槽位来渲染第三语言:
- store.getOverride(dshActive, ns, key) 改为只在 dshActive === 'en'
  时返回覆盖文本;DSH 在 zh 下覆盖完全惰性(保持原生 zh)
- 新增 store.isOverrideActive(dshActive):返回 active !== undefined
  && dshActive === 'en',供 better-sidebar 的 isZh() 判断
- isZh() 改用 isOverrideActive(dshActive) 判断(仅在 DSH=en 且有
  override 时返回 false),zh 下返回 true
- LanguageSwitcher 接收 dshActive prop,选了非 native 且 DSH 非 en
  时显示「请将 DSH 切换到英文以查看 [语言名]」提示
- 新增 switchToEnHint copy key(zh + en)
- 测试:better-locale +3 store tests(en-only / inert on zh /
  isOverrideActive),+1 patch test(inert on zh);better-sidebar
  +1 locales test(override inert while DSH on zh),FakeBetterLocale
  加 isOverrideActive 方法
2026-08-23 01:42:01 +08:00
Fisfzy c381557a1e feat(catalog): add ego-browser to built-in tab catalog
Register ego-browser as a recommended TAB plugin in the built-in catalog
(Side card settings → 侧边栏内容 → add-tab modal). Entry follows the
PluginEntry contract: unique id @dsh-external/ego-browser, GitHub URL,
localized description (zh+en added to locales.ts), and an install script
that starts at cd ~/.dsh and uses the dsh plugin CLI. The plugin registers
a sidebar tab for the agent browser as an optional peer of better-sidebar
(tab when present, floating bubble when not).
2026-08-23 01:24:20 +08:00
HuanLinOTO 7ceee777b4 feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 ja 覆盖
新增 ja 词典(src/client/locales-ja.ts,326 keys,与 zh key-set-equal),
通过可选 peer @huanlin/dsh-plugin-better-locale 接入第三语言覆盖:

- locales.ts: 新增 attachBetterLocale(store) + ja dict export;t() 先查
  store.getOverride(active, 'betterSidebar', key),命中则返回覆盖文本,
  否则走原 zh/en 链;isZh() 在 override 激活时返回 false
- client/index.tsx: apply 中 ctx.get('betterLocale') 取得 override store
  (optional,未安装时为 undefined),attachBetterLocale 注入 t();同时
  register('betterSidebar', { ja }) 把 ja 词典注册进 better-locale 的
  store,让外部 ctx.locale.lookup 调用者也能拿到 ja 文本
- Sidebar.tsx: 新增 betterLocaleActive uSES 订阅 store.subscribe,覆盖
  切换时全树重渲染(DSH locale 的 active 字段不变,既有 localeRevision
  uSES 不会触发)
- package.json: @huanlin/dsh-plugin-better-locale 加为 optional peer
  + devDep link
- tests/locales.spec.ts: 新增 7 个 better-locale override 测试(命中/
  fallback/detach/interpolate/ja dict 完整性 smoke check)

未安装 better-locale 时 ctx.get 返回 undefined,整段为 no-op,zh/en
行为完全不变。
2026-08-23 00:52:20 +08:00
Menghuan1918 ddb66c9c08 fix(market): 移除公开版 cordis peer 并迁移类型基底到 @deepseek-ai/cordis
DSH 市场(dsh-community-market)预览硬拒依赖字段出现 'cordis'
(dependencies/peerDependencies/optionalDependencies 任一字段、
optional 均无效),导致 dshfind/1024Store 目录拿不到
repository_backlink 验证目标,点击安装报
"This catalog item has no verified install target"。

- package.json: 删除 cordis peer + peerDependenciesMeta;版本 0.15.2
- context-types.ts: Context 基底改 @deepseek-ai/cordis,结构服务面改
  交集(避免 DS host/client 对同一成员类型不同导致的 TS2717 merge
  冲突),仅保留 betterSidebar 的 module augmentation;删除
  declare module 'cordis'
- client/index.tsx: 4 处 effect 体失败分支改返回 no-op disposer
  (vendored cordis effect 契约拒绝 undefined 形状)
- 新增 tests/market-manifest.spec.ts:无 cordis 依赖字段、打包产物
  无生命周期脚本、bundle patch 安全路径、仓库身份、精确稳定版本
- 消费者检查扩展:@deepseek-ai/cordis 路径的 Context augmentation
  断言;文档示例同步迁移
2026-08-23 00:26:39 +08:00
Menghuan1918 d9b8f15d9e chore(release): 0.15.2
- bump version: package.json / dsh.plugin.json / SIDEBAR_SERVICE_VERSION
- README / README_EN: feature tour converted to a table (two screenshots
  per row, saves space); add v0.15.2 changelog (file-tree open-in-app
  menu #334, tab context menu #331, diff collapse by default #270, prune
  empty restored panes #268, hide git windows on Windows #301, untracked
  files inside new folders #242, toggle/drag per-frame re-render perf
  #315); community QR codes landed in #325
v0.15.2
2026-08-22 23:30:19 +08:00
Atlas Zeng 22a5d372be feat(editor): 文件树「在应用中打开」子菜单(外部编辑器/图钉固定/SSH 远端/自定义编辑器) (#334)
* feat(editor): file tree 'open with' submenu (external editors, pin, SSH-remote, custom editors)

The file tree right-click menu gains an 'Open with >' submenu with the
resource manager (reveal in file manager), VS Code, Cursor and Zed, plus
user-defined custom editors (name + URL template + VSCode-family flag).
Each submenu row carries a pushpin that promotes the target to a direct
top-level menu item.

A configurable SSH host (settings: Files card gear) switches VSCode-family
openers to <scheme>://vscode-remote/ssh-remote+<host>/<path> and hides
host-local targets (file manager / Zed / non-VSCode custom editors).

External opens run through the new fenced /sidebar/api/open.external route
which spawns the platform opener with an argv array (no shell); the client
never navigates raw scheme URLs itself. The settings seam's render panel now
appends after the declarative rows instead of replacing them.

* feat(editor): polish open-with menu UI — brand icons, pushpin, chevron, row alignment

- Submenu rows: the custom label is now a block-level flex row filling the
  primitives itemLabel, so the pushpin docks at the row's far right (matching
  the trailing check of every other menu); menu row icons normalized to 16px
  (DSH native menus use 16, ours were 14).
- The submenu parent row gains a right-aligned chevron (the primitives Menu
  renders no arrow for submenu parents).
- Icons via react-icons: codicons VscPin/VscPinned pushpins (replacing the
  hand-drawn pin), VscFile/VscFolder/VscFolderOpened tree glyphs (replacing
  the </> file mark), VscLinkExternal for the open-with parent and the
  browser tab's open-external button (globally), SiCursor/SiZedindustries
  brand silhouettes; the VS Code mark is inlined from simple-icons@11.0.0
  (CC0) as IconVscode16 since later simple-icons releases dropped it. All
  monochrome currentColor to follow the active skin.
- tsdown: pin react-icons si/vsc to their ESM entries via resolve.alias —
  the package's exports map lists require before import, so the shared
  conditionNames picked the unshakeable CJS entry and bloated the core
  bundle by ~6.4 MB; tree-shaken it now adds only ~12 KB.
2026-08-22 23:19:17 +08:00
wangqiongkakaandwangqiong f36afc0180 feat: collapse diff files by default (#270)
* feat: collapse diff files by default

* feat: expand source diffs by default

---------

Co-authored-by: wangqiong <wangqiong@yy.com>
2026-08-22 23:13:35 +08:00
wangqiongkakaandwangqiong 38a3d94410 fix: prune empty restored panes (#268)
Co-authored-by: wangqiong <wangqiong@yy.com>
2026-08-22 23:11:57 +08:00
Xinchen Lee 71c5413e5c fix(git): hide spawned git windows on Windows (#301) 2026-08-22 23:11:27 +08:00
SKXandMenghuan1918 e867e27106 fix(git): 切换未跟踪文件显示模式以支持新文件夹内文件差异 (#242)
* fix(git): 切换未跟踪文件显示模式以支持新文件夹内文件差异
当使用 `git status --untracked-files=normal` 时,新文件夹会被折叠为单条 `?? dir/` 条目,点击后因无法读取目录(`fs.read` 报错 "is a directory")而无法加载差异。切换至 `--untracked-files=all` 使新文件夹下的每个文件独立显示为一行,并可正常查看差异,与 VSCode 默认行为一致。同时添加回归测试以验证 porcelain 输出格式。

* test(git): 补真实 git 集成测试钉住 status() 的 --untracked-files=all(review feedback)

---------

Co-authored-by: Menghuan1918 <menghuan2003@outlook.com>
2026-08-22 23:09:58 +08:00
MilchstraBe-Endless10 656f9fba3c fix: add unrun devDependency for tsdown config loading
tsdown 0.22 resolves its config file through unrun; without it declared,
git-hosted installs (which run `pnpm install` + prepare inside the fetched
source) fail with "Failed to import module unrun". Declare it as a dev
dependency so the prepare step can load tsdown.config.ts.
2026-08-22 22:54:29 +08:00
Atlas Zeng 4148130aac perf(sidebar): 消除开关/拖拽的每帧 React 重渲染
- 根因:开关过渡期 ResizeObserver→setCenterRect 每帧重渲染整棵 Sidebar;TabContent 无 memo;内部 Divider/文件树 dock 每 pointermove 全量更新;拖拽残余为 `#root` 每帧 reflow(动画固有成本,A/B 实证非回归)。
- 修复:centerRect 改 ref + 底栏 DOM 直写(零 React 渲染);TabContent memo(显式比较器,locale/registry/paneId 保持新鲜);新增 frame-batcher 对 Divider/dock 拖拽按帧合并;拖拽期跳过无意义 locate。
- 验证:单测 76 文件 773 通过;`pnpm test:mount` 12/12(含新 toggle-layout e2e:开关过渡底栏逐帧 0.0px 贴合);4x CPU 节流 A/B:开关 >17ms 帧 collapse 19→6 / expand 24→4~6,p95 21ms→15ms;拖拽不变(非回归)。
- CR(@codex P1)已修复:`moveTab` 复用同一 tab 对象,比较器改显式比较 `paneId`(含回归单测)。
- 本地 DSH Web 实测通过后按用户要求合入。

fixes #315
2026-08-22 22:50:13 +08:00
Atlas Zeng 924f377fa1 Update README.md 2026-08-22 20:12:32 +08:00
Atlas Zeng d3c0f22345 feat(tab-bar): add right-click close context menu on tabs (#331)
Right-clicking a tab now takes over the browser menu and shows four
close operations scoped to the current pane:
- Close
- Close Other Tabs
- Close Tabs to the Left
- Close Tabs to the Right

Items gray out when nothing is eligible (single tab / strip ends).
Bulk closes reuse the per-tab onClose path, so descriptor onClose
callbacks and terminal pty release still run for every close. Labels
are i18n'd (zh/en).
2026-08-22 19:57:34 +08:00
ZgblKylin e24d9bf879 fix(sidebar): drop panel host below DSH cordis dynamic-plugin layer (z 40→25)
The ui-cordis CordisPanel is position:fixed at z-index 30; the unified
panel host at 40 made the bottom workbench cover the cordis inventory
and approval surfaces. Host now sits at 25 (above AppFrame overlay 20,
below cordis 30 and the 100+ float stack). Update skin contract docs
and add a mount e2e assertion for the layering band.
2026-08-22 19:10:02 +08:00
lx f5fd6fc01a docs: add multi-repository git screenshots 2026-08-22 18:19:23 +08:00
lx 78de79c9d6 fix: support selecting child git repositories 2026-08-22 18:13:50 +08:00
Atlas Zeng 67271e54c8 Clean up community section in README
Removed commented-out QR code sections for WeChat and QQ from the community section.
2026-08-22 18:00:24 +08:00
Atlas Zeng 9b4240449c Update README.md (#325) 2026-08-22 17:59:20 +08:00
Atlas Zeng ef8d3a9050 chore(release): 0.15.1 (#324)
* chore(release): 0.15.1

- bump version: package.json / dsh.plugin.json / SIDEBAR_SERVICE_VERSION
- README / README_EN: add v0.15.1 changelog (sidechat Codex-style transcript
  rework, terminal park-frame pty keep-alive, file-tree drag gating, README
  rewrite); plugin ecosystem 28+ with two new entries
  (dsh-better-sidebar-starter, my-dsh-plugins) and per-category <details>
  collapsible listings

* docs(readme): add community section placeholder for WeChat/QQ group QR codes
v0.15.1
2026-08-22 17:46:26 +08:00
Atlas Zeng 9a40164d1a fix(explorer): 非文件拖拽不再触发文件树上传遮罩 (#317)
拖动 Tab(重排/跨 pane split)经过资源管理器文件树时,FileTree 的
dragenter/dragover/drop 此前对任何拖拽都响应:显示上传遮罩并吞掉
事件,导致 pane 根的 Tab-drop 处理收不到、Tab 无法落下。

统一按 dataTransfer.types 含 'Files' 门控(与 Sidebar.tsx 面板宿主
shield 一致):非文件拖拽原样冒泡给 pane 根,OS 文件拖拽行为不变。

回归测试:应用内 Tab 拖拽不出现 uploadDropZone、不改 dropTarget、
不 preventDefault、不触发 onUploadRequest。
2026-08-22 17:22:10 +08:00
多玩幻灵qwq 29b8615bff fix(terminal): keep pty alive across conversation switches via park frame (#323)
Switching from session A to B unmounted A's TerminalView, whose socket
close the host treated as a transient drop and started the 30s reconnect-
grace countdown — leaving the user in B for >30s killed A's shell, so
switching back respawned a fresh one instead of reattaching.

Add a third control frame {type:'park'} distinct from refresh/crash
drops: the client sends it on unmount when the tab is still open in its
session's state but the active session has moved on; the host marks the
pty as parked and skips the grace countdown. Switching back (open()
cancels the parked state) or explicitly closing the tab (close frame's
scheduleClose clears it) resume the normal lifecycle. Agent terminals
skip park — their lifetime is already indefinite on a bare drop.
2026-08-22 17:21:27 +08:00
Lin 546afefb9b Merge branch 'main' into fix/markdown-preview-local-images 2026-08-22 15:37:55 +08:00
Atlas Zeng b7aab48deb feat(sidechat): Codex 风格转录重构 — 首问显示 / 注入拆分 / 折叠行 / 弹窗去抖 (#314)
* fix(sidechat): split context injection from the first user message

The first composer message rode the same user/message event as the side
boundary prompt (+ parked snapshot), and the transcript dropped that whole
row by its boundary prefix — swallowing the user's first question.

Deliver first contact as TWO messages: the boundary+snapshot rides
agent.inject (queued next-step context, claimed first at the opening step,
no wake) stamped with source kind 'plugin', and the question is the
follow-up that wakes the driver. The log records injection and question as
separate events; the transcript maps injections (plugin-sourced, or the
legacy boundary-prefix blob of pre-split threads) onto a collapsible
injection row and renders genuine user messages — the first one included —
as user rows. boundaryDelivered still recognizes the injection by its
unchanged boundary-prefix opening.

* feat(sidechat): codex-style collapsible rows and creating shimmer

Tool calls, thinking and context injections now share one quiet
single-line chrome (chevron + label + a one-line argument summary) that
expands into an indented body on a hairline thread — no cards or fills.
Streaming labels and the creating hero shimmer (shimmer = generating);
failed tools go danger; reduced-motion stills every loop.

* fix(sidebar): debounce subagent auto-open past the title frame

The host delivers a new child's origin and title in separate frames, so a
Side Chat thread's first visible frame lacks its 'Side: ' label and
tripped the 0→N auto-open — every thread creation popped the task page.
The trigger now rearms for 500ms and re-evaluates the original baseline
against the live snapshot, by which time the title filter recognizes the
thread. Genuine subagents still auto-open, half a second later.

* fix(sidechat): render legacy threads' first message as its own bubble

Threads created before the host split still log the boundary (+snapshot)
and the question as ONE 'user' message; the transcript mapped that row
wholly onto the injection row, so the user's first question only appeared
inside the expansion. The boundary prompt is a known constant, so the
legacy row now splits THERE: the injection row keeps the prompt, the
remainder renders as the user's real message (a pre-split snapshot, when
present, rides along — the common case is a pure question). Row React keys
become kind:seq so the split's two rows never collide.
2026-08-22 15:34:05 +08:00
Menghuan1918 7dbd9b75e2 docs(readme): simplify install to add -> approve-builds -> add
The node one-liner prewrite was overkill for the README. Plain double
'add' does NOT work (both runs exit 1 on ERR_PNPM_IGNORED_BUILDS,
verified in scratch); the working minimal flow is add (fails, dep
written) -> pnpm approve-builds --all (re-runs install scripts) ->
add (succeeds, bundle registered). Verified end-to-end in a scratch
profile.
2026-08-22 12:10:24 +08:00
Menghuan1918 cdda072973 docs(readme): make first-run install succeed (pre-write allowBuilds before dsh plugin add)
pnpm 11's strict-dep-builds blocks node-pty/protobufjs build scripts and
makes the FIRST 'dsh plugin add' exit non-zero (dsh aborts on
ERR_PNPM_IGNORED_BUILDS), forcing a second run after approve-builds.

The install block now pre-writes allowBuilds (node-pty/protobufjs) and
minimumReleaseAgeExclude (dsh-better-sidebar) into the profile's
pnpm-workspace.yaml with the same idempotent merge as scripts/install.sh
ensure_workspace_settings, so the first add succeeds. Verified in a
scratch DSH_HOME: preflight -> add exit 0 (node-pty postinstall ran),
rerun unchanged (no duplicate keys), second add exit 0; merge branch
tested when allowBuilds already exists. Paste-to-DSH prompt updated to
match.
2026-08-22 12:06:32 +08:00
Menghuan1918 646c91cf70 docs(readme): sync EN with uploaded screenshots, normalize image sizes, add Side Chat badge
- Mirror the zh feature tour (user-uploaded CDN images) into
  README_EN.md: real screenshots for explorer/editor, markdown,
  image, terminal, git, browser, tasks, side chat, settings, mobile;
  drop manual-shot placeholders (git-diff, bottom-panel) and the
  CodeMirror subsection (folded into the explorer section)
- Normalize image display size to width=880 (mobile 360) across both
  languages; remove the stale 'placeholders to come' note
- Add the missing Side Chat badge to the hero feature-chip row
  (zh + en)
- Mirror user's trim: 'Office' removed from the features bullet
  (Office preview is an ecosystem plugin), Git panel ecosystem
  sentence dropped
2026-08-22 02:03:25 +08:00
Atlas Zeng 55ad7eec8d Update README.md 2026-08-22 01:29:06 +08:00
Menghuan1918 d38a411f6d docs(readme): user-facing DSH compat badge, node-pty-safe install script, paste-to-DSH install prompt
- Drop peer-floor / CI-baseline jargon everywhere; supported DSH
  versions are stated only via the shields badge
- Install block: add 'pnpm approve-builds --all && pnpm rebuild
  node-pty' unconditionally so the terminal's prebuilt binary is
  always in place (pnpm 11 blocks build scripts by default)
- Add a copy-paste prompt block that lets a DSH session install the
  plugin itself (mirrors the manual script + node-pty step)
- Trim internal process detail from historical changelog bullets
  (v0.14.0 / v0.13.1), keep the user-facing symptoms and gates
2026-08-22 01:04:16 +08:00
Menghuan1918 940d0a8928 docs(readme): feature tour with screenshots, plugin ecosystem section, DSH compat badges
- Hero badge wall: npm version/downloads, CI, stars, DSH compat
  (0.1.0-rc.8 / 0.1.1-rc.1 / 0.1.1-rc.2), ecosystem topic badge
- New 🖼️ feature tour: one subsection per feature with real UI
  screenshots (10 captured from a scratch DSH instance; 4 slots left
  as commented placeholders for manual upload via GitHub web)
- New 🌐 plugin ecosystem section: 26 topic plugins in three grouped
  tables (tab / viewer / enhancement) with live star badges, the two
  extension points, and curation instructions
- Recent updates: keep v0.15.0 visible, collapse v0.12-v0.14 into
  <details>; add Releases link and per-version DSH support line
- Add TOC, contributing section, star history chart, contributors wall
- Bilingual README.md / README_EN.md kept in sync
2026-08-22 00:49:08 +08:00