# dsh-ui-hub [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Tests](https://github.com/Han-1413141/dsh-ui-hub/actions/workflows/test.yml/badge.svg)](https://github.com/Han-1413141/dsh-ui-hub/actions/workflows/test.yml) English | [δΈ­ζ–‡](README.md) **UI Hub** for DSH Desktop and Web discovers controls in UI slots and marked floating widgets. Search for a control, show or hide it, adjust its position, arrange floating widgets, and export your layout for later restoration. ![Cover: Whale Girl Β· UI Hub](docs/assets/whale-girl.png) ![UI Hub panel (Chinese)](docs/assets/screenshot-panel-collapsed.png) ![UI Hub panel (English)](docs/assets/screenshot-panel-en.png) ## Find controls and back up layouts - Search by control name, plugin or slot. Matches expand automatically; clearing the search restores the saved collapse state. - **Export layout** saves a JSON file. **Import layout** restores visibility, positions, sizes and group state. Invalid files leave the current layout intact. - Transfer this local file between Web and Desktop when controls have matching identities and structure. - Idle counters no longer rewrite the DOM continuously; re-enabling the plugin resumes normal updates. ## ✨ Features | Feature | Description | |---|---| | πŸ” Full discovery | Enumerates every UI inside the platform's `[data-slot]` anchors plus floating widgets outside slots (dsh-sticky-disclosure pills, dsh-mingli-chart views, ...); unknown widgets can be captured with Pick element | | πŸ—‚οΈ Official vs plugin | The panel is split into **Official UI** and **Plugin UI** categories (every row also carries an official/plugin tag) | | πŸ“ Collapsible groups | Two-level collapse (category β†’ slot group), **all collapsed by default** so only category/group names + counts show; expand step by step, state is remembered | | 🎚️ Per-widget control | Each UI root has its own switch; expand a root to toggle its inner **buttons / icons / charts / fields** individually | | πŸ“ Three position modes | **Default** (restore), **Nudge** (translate without leaving the layout), **Float** (fixed positioning with exact x/y) | | πŸ–±οΈ Direct drag mode | Enable Drag mode and **drag any UI directly to move it** (slot-mounted UIs use nudge and keep their layout; loose widgets float) or **drag its bottom-right grip to resize**; Esc exits | | πŸ›‘οΈ Collision avoidance | Off (report only) / Smart (clear overlaps) / Strict (any overlap); locked items stay put and push others away | | ✨ One-click arrange | Packs every floating UI into right-aligned vertical columns anchored to the conversation scrollport | | πŸ’Ύ Persistent | Toggles, positions, sizes, and collapse state live in `localStorage` and are restored across refreshes | | 🧹 Non-invasive & reversible | No plugin code is modified; only additive `data-uihub-*` attributes plus the hub's own `!important` stylesheet. Disposal removes every trace | ## Why DSH is a plugin ecosystem and every plugin adds a bit of chrome: header buttons, composer badges, sidebar cards, bottom-right pills, floating charts. Plugins do not know each other's geometry, so overlap is the default. UI Hub keeps one roster and one floor plan for all of them: 1. Discover every UI and group it by slot; 2. Toggle, move, and lock each item; 3. Separate floating widgets that overlap; 4. Arrange scattered widgets into one aligned column in a click. ## πŸ“Έ UI gallery Full captioned walkthrough: **[docs/GALLERY.md](docs/GALLERY.md)**. **30-second demo: default collapsed β†’ official UI expanded β†’ group expanded β†’ drag mode** ![Demo](docs/assets/demo.gif) | Screen | Caption | |---|---| | ![Collapsed panel](docs/assets/feature-panel-collapsed.png) | The panel opens with only the two collapsed categories: Official UI / Plugin UI | | ![Official UI expanded](docs/assets/feature-official-expanded.png) | Official UI expanded: slot groups appear, each still collapsed | | ![Group rows](docs/assets/feature-official-group-expanded.png) | One group expanded: per-row switches, official tag, position mode, `β‹―` inner elements | | ![Drag mode](docs/assets/feature-drag.png) | Drag mode: dashed outlines for direct dragging, bottom-right grips for resizing, Esc to exit | ## Usage 1. On Desktop, choose **Enable now** after installing, or reopen the app if installed from the terminal. On Web, restart `dsh web`. A **UI Hub** pill appears in the top-right corner (hotkey `Ctrl+Shift+U`, macOS `βŒ˜β‡§U`); click it to open the panel. 2. The panel opens with the two collapsed categories **Official UI / Plugin UI** (all collapsed by default). Click a category to reveal its slot groups, then click a group to list its UIs. 3. Each row: left switch shows/hides the item, `β‹―` expands its detail. 4. Detail pane: - **Position mode**: default / nudge / float; - **X/Y** in float mode, **DX/DY** in nudge mode; - **Drag to move**: a grab handle appears at the widget corner; - **Lock**: collision avoidance never moves this item; - **Inner elements**: toggle buttons, icons, charts, and fields separately. 5. Toolbar: - **Drag mode**: drag any UI directly to move it, drag its corner grip to resize, Esc to exit; - **Auto arrange**: packs all float-mode UIs into right-aligned columns; - **Pick element**: click any element on the page (even unmarked ones) to manage it; - **Reset all**: clear every toggle, position, and size. ### API ```js window.dshUiHub.exportLayout() // export a portable layout object window.dshUiHub.importLayout(data) // restore an object or JSON string; returns boolean window.dshUiHub.items() // [{ key, label, plugin, category, slot, on, mode, x, y, sw, sh, children: [...] }] window.dshUiHub.setConfig(key, { on: false }) window.dshUiHub.setConfig(key, { mode: "float", x: 300, y: 200 }) window.dshUiHub.setConfig(key, { sw: 360, sh: 240 }) // set size window.dshUiHub.setConfig("child:...", { on: false }) window.dshUiHub.arrange() // one-click auto arrange window.dshUiHub.collisionMode("strict") // off | smart | strict window.dshUiHub.dragMode(true) // enable/disable direct drag mode window.dshUiHub.open() / close() / reset() ``` ## Behavior notes - **Stable identity**: slot UIs are keyed `slot:@` and re-attach across React re-renders; floating widgets are keyed by their `data-*` marker; picked elements by a structural path hash. - **No style fights**: positions are enforced through `data-uihub-float` + CSS variables + `!important` rules, so other plugins' inline `left/top` writes cannot override them. Removing the attribute restores the plugin's own styles. - **Dispose restores everything**: all `data-uihub-*` attributes, CSS variables, stylesheet, and hub chrome are removed. - **Direct drag**: drag mode intercepts pointers only while enabled. Dragging a **slot-mounted UI uses nudge** (it stays inside the layout, so anchored popups follow it), while loose floating widgets move with float coordinates; the bottom-right grip resizes (float: resizes the floating box; default/nudge: pins an in-place size override). Ending a drag never re-fires the button's click; a plain click still reaches the plugin normally. Reset item restores the original; Esc exits. - **Floated popup follower**: when an explicitly floated UI opens a popup that still appears at the original spot, the hub shifts it with the independent CSS `translate` property (only popups anchored at the original spot; centered full-page dialogs are untouched). - **Locked items win**: arrange and avoidance skip locked items and route others around them. - Panel and handles use z-index 80/86: above plugin floats, below app dialogs (100+). ## Install **Desktop (DSH 0.2.0-rc.2):** open **Plugins β†’ Add plugin** in the sidebar, paste the following source, install it, then choose **Enable now**. Follow DSH's restart prompt if shown. ```text github:Han-1413141/dsh-ui-hub ``` Desktop includes Node and pnpm. For terminal installation, first install the bundled command through **Manage dsh command** in the application menu. Open Desktop once to initialize its profile, fully quit it, and run: ```bash dsh plugin --profile desktop add github:Han-1413141/dsh-ui-hub ``` Then reopen Desktop. **Web** uses a separate profile: ```bash dsh plugin --profile web add github:Han-1413141/dsh-ui-hub dsh web ``` The standalone CLI follows DSH's Node requirement: `^22.19.0 || >=24.0.0` for the version checked here. Desktop's bundled command needs no separate Node or pnpm installation. PowerShell installer (automatically selects `desktop` for the bundled command, otherwise `web`): ```powershell irm https://raw.githubusercontent.com/Han-1413141/dsh-ui-hub/main/install.ps1 | iex ``` To select a profile explicitly, download `install.ps1` and run `./install.ps1 -Profile desktop` or `-Profile web`. Without Git, use `https://github.com/Han-1413141/dsh-ui-hub/archive/refs/heads/main.tar.gz` as the source. ```bash # Replace desktop with web for a Web installation. dsh plugin --profile desktop update dsh-ui-hub dsh plugin --profile desktop remove dsh-ui-hub ``` Layout/shortcut preferences belong to the browser origin: Desktop and Web keep separate preferences. Compatibility details and verification limits are in [COMPATIBILITY.md](docs/COMPATIBILITY.md). ## Tests ```bash python -X utf8 test/verify.py python -X utf8 test/verify_compat.py # Playwright chromium over test/mock.html ``` Covers discovery, official/plugin classification, default-collapsed categories with stepwise expansion, root/child toggles, float positioning, arrange alignment, strict collision avoidance, panel, hotkey, pick mode, direct drag move + drag resize, persistence across reload, and teardown. ## Known limitations - **Float coordinate system**: float mode uses `position:fixed`. If a UI's ancestor carries a CSS `transform`, fixed positions resolve against that ancestor; use Nudge for such items. - **Platform DOM changes**: discovery relies on the public `[data-slot]` anchors and plugin `data-*` markers. If the platform renames them, items reappear under new keys (old configs stay local). - **Child identity**: inner elements are numbered in DOM order, so configs can follow a reordered neighbor after a plugin restructures its subtree. - **No forced reflow inside slots**: default/nudge modes respect the original slot layout; for a full-page rearrangement, switch items to Float and use Auto arrange. - Picked elements retain only their current-page DOM identity and need to be picked again after reload. Slot controls and marked plugin widgets can restore automatically.