dsh-answer-top: Automatically scroll to the beginning of the answer after the AI finishes.

Introduction

Running agents in DSH (DeepSeek Harness) usually results in long answers. When the answer ends, the page stays at the bottom: either manually scrolling up for a long time, or regenerating. The dsh-answer-top plugin solves this issue: after the AI finishes answering, it automatically scrolls the scroll container back to the first line of this round’s answer.

Below is an introduction to the plugin: who maintains it, what it does specifically after installation, how to install it, what switches can be configured, and in what scenarios not to rely on it.

What is this

dsh-answer-top is a client-side plugin for the DSH ecosystem, maintained by DSH-USER-GTB. It does not change DSH’s behavior; it only listens to the conversation DOM rendered by the DSH client and executes a scroll when detecting that “this round of answering has ended.”

One-sentence positioning: Answer ends → automatically scroll to the first line of this round’s answer, no need to scroll up every time.

It belongs to the category of client-side plugins: all logic is on the browser side (lib/client.js), the host-side lib/index.js is an empty plugin, and apply does nothing.

Core Features

Auto-rollback Trigger Conditions

The plugin listens to the DSH client’s DOM contract to determine if the current round of answering has ended:

  • MutationObserver listens for changes to the data-actions-reveal attribute on document.body, with an 800ms timer as a fallback (to check missed events or session switches).
  • The appearance of data-actions-reveal on the [data-turn-tail] element indicates that this round of answering has ended.
  • Find the first node of this round with [data-chat-flow-kind="assistant-step"] and scroll [data-conversation-scroll] to the top of it.

The DOM contract used is reverse-engineered from the DSH client:

[data-conversation-scroll]   Scroll container
[data-chat-turn="N"]         All flow nodes for round N
[data-chat-flow-kind]        Node type: user / assistant-step / tool-call / turn-process
[data-turn-tail]             Round end container
data-actions-reveal          Attribute exists = round has ended

Behavior in various scenarios:

Scenario Behavior
Answer longer than one screen Scroll to the start of the answer
Answer shorter than max(viewport 25%, 80px) Do not scroll (avoids page jumping; threshold is adjustable)
Historical message loading Do not scroll (only effective for the last round)
Turn number identification fails Do not scroll (does not fallback to the entire answer to avoid accidentally scrolling to the start of the conversation)
During stream output Do not scroll (waits until truly finished)
First entry into a session after startup Jump to the start of the last round of answers (only once for the entire page lifecycle, repeated when switching sessions)

Trigger Threshold (v0.12.0+)

The default behavior is “don’t scroll if the answer is short,” which is enforced by two rules taking the higher value: don’t scroll if the answer height is lower than max(viewport × 25%, 80px).

The threshold is adjusted in two sliders under DSH Settings → Left Navigation “Auto Rollback” → “Trigger Threshold”. Dragging applies changes immediately and saves them automatically:

Control Range Default Description
Minimum Answer Height 5% – 100% of viewport 25% Do not scroll if the answer is lower than “viewport × this ratio”
Absolute Lower Limit 0 – 200px 80px Regardless of window size, do not scroll if the answer is lower than this height

Settings are persisted in the browser’s localStorage under the dsh-answer-top:config key, with the following structure:

{
  "v": 1,
  "enabled": true,
  "sessions": {},
  "threshold": { "ratio": 0.25, "px": 80 }
}
Field Description
v Configuration version, still 1
enabled Master switch, default true
sessions Reserved empty object, version 1 is always empty
threshold.ratio Ratio of minimum answer height to viewport, 0.05 – 1, default 0.25
threshold.px Absolute lower limit in pixels, 0 – 200, default 80

Configurations written by old versions do not have a threshold field; when read, they are auto-filled with { ratio: 0.25, px: 80 } without overwriting the original storage, and only written back when the slider is dragged next time.

Master Switch

Enabled by default. After turning it off, it won’t scroll at all—not just a little less, but not at all. The switch is located in the “Auto Rollback” section of the DSH Settings page, but can also be executed in the browser console:

__dshAnswerTop.setEnabled(false)   // Turn off
__dshAnswerTop.setEnabled(true)    // Turn on
__dshAnswerTop.config()            // View current configuration

Changes take effect immediately without refreshing. You can also change the threshold in the console, but it won’t be persisted:

__dshAnswerTop.setThreshold(0.15, 40)

Manual Rollback Button (v0.12.0+)

A small “⤒ Rollback” button always resides in the dock below the input box (conversation.composer.dock):

  • Clicking immediately scrolls to the beginning of the last round of answers, even if auto-rollback is turned off or if a coexistence plugin conflict is detected;
  • The manual path bypasses the master switch, the 300ms interaction window, and the height threshold;
  • DOM marker: data-dsh-answer-top-manual="1", title="Rollback to answer start".

You can also manually trigger it once via the console:

__dshAnswerTop.forceScroll()

Coexistence with Similar Plugins

If you enable other plugins that also “auto-scroll/jump” simultaneously, having two of them active will cause conflicts. The plugin reads the module list from ctx.modules (pure memory, no loopback, no network access):

// v0.12.0 onwards, only dsh-chat-answer-anchor is considered a conflict
["dsh-chat-answer-anchor"]

After a match, only the auto path is disabled: no automatic scroll when the answer ends, and the settings page shows “Detected dsh-chat-answer-anchor, auto-rollback paused.” The manual button and command line forceScroll() are not affected. Auto-resume occurs after disabling the anchor. The probe performs a lightweight re-check every 800ms; if ctx.modules is unavailable or malformed, the previous conclusion is kept—better to stay still than to make a mistake. dsh-jump-button is no longer considered a conflict since v0.12.0.

View probe results:

__dshAnswerTop.info().coexistence     // { anchor, checked, probe }
__dshAnswerTop.diagnose().coexistence
__dshAnswerTop.moduleIds()            // Verify if target plugin id has /client suffix

Data and Privacy

The plugin does not go online or upload data. The source code contains no fetch / XMLHttpRequest / WebSocket / sendBeacon, and it does not send data to DSH, GitHub, or third parties.

It only writes two keys to local storage locally: dsh-answer-top:config (settings) and dsh-answer-top:events (sanitized event logs, max 200 items / 50KB, oldest are dropped when exceeded). Event logs only record boolean or enum fields (errorType, at, v, count, anchor, probe, etc.), not message body, session id, user input, file paths, URLs, page content, screenshots, or raw error text. Cleanup methods:

localStorage.removeItem('dsh-answer-top:events')    // Clear logs only
localStorage.removeItem('dsh-answer-top:config')    // Reset settings along with logs
location.reload()

Installation and Enabling

The plugin is installed via a link string (link). After installation, you need to restart DSH (bundle-layer plugin). In DSH, go to Settings → Plugins → Add Plugin and enter the link installation string for the local directory:

link:<your-plugin-directory>

Equivalent command line syntax:

dsh plugin --profile desktop add link:<your-plugin-directory>

Uninstall:

dsh plugin --profile desktop remove dsh-answer-top

Download method: Community directory page https://www.skillhub.cn/plugins/DSH-USER-GTB/dsh-answer-top and source code repository https://github.com/DSH-USER-GTB/dsh-answer-top. For local development, use the same link command; just keep the directory unchanged when re-adding.

The version in package.json is 0.12.1, license MIT. As declared in the package, dsh.client.inject is [], but the runtime code’s exports.inject in the client side must be declared as ["slots", "modules"] to access ctx.modules, otherwise the coexistence probe is unavailable.

Typical Usage

  1. Install the plugin and start DSH.
  2. Open any session and send a request that triggers a long answer.
  3. After the answer ends, the page automatically returns to the first line of this round’s answer; if the answer is shorter than max(viewport 25%, 80px), it stays at the end without moving.
  4. To temporarily adjust the threshold, execute __dshAnswerTop.setThreshold(0.15, 40) in the console (not persisted).
  5. For long-term adjustment, drag the two sliders under “Auto Rollback → Trigger Threshold” in the settings page.
  6. When auto-rollback is paused by a coexistence plugin, click the “⤒ Rollback” button below the input box to jump manually.

Applicable Scenarios and Notes

Suitable for: DSH conversation scenarios dominated by long answers, especially for users who have to read from the beginning every time an answer ends; and for users who want manual control with just one permanent button. The settings page switch and manual button are two independent slots; the manual button is not affected when switching the primary/secondary position of the switch.

Things to note:

  • The plugin runs with the current DSH process permissions; before installation, you should open the source code repository to verify the code and license (MIT).
  • Only effective for the last round; it won’t scroll when browsing historical messages.
  • Relies on DSH’s DOM contract. If it fails after upgrading DSH, it is likely because the attribute names changed—first confirm if the contract elements exist using the method below.
  • Since v0.12.x, scrolling is an instantaneous jump (directly assigning scrollTop), so smooth animations do not work on DSH’s new scroll containers.

Debugging

If it doesn’t work, verify the contract elements one by one in the browser console:

// Check if contract elements exist
document.querySelector('[data-conversation-scroll]')
document.querySelectorAll('[data-chat-turn]').length
document.querySelectorAll('[data-turn-tail]').length

// Check if the last round has ended
const tails = document.querySelectorAll('[data-turn-tail]');
tails[tails.length-1].hasAttribute('data-actions-reveal')

If the elements are there but it won’t scroll, check the console for errors. If the switch is not visible in the settings page, since v0.9.1, you can use one command to locate which layer the problem is in:

__dshAnswerTop.diagnose()
Output Meaning Next Step
injectCallbackRuns: 0 Slot hasn’t been declared, injection callback hasn’t run Confirm settings.section still exists in DSH version
injectCallbackRuns: 1, registerRuns: 0 Callback ran but registration failed Check registerError
slotEntryCount: 0 Registered but not in list Check registerError / if abdicated
slotEntryCount: 1, slotMounted: 0 Registered, just not the corresponding section open Open Settings → “Auto Rollback”
slotEntryCount: 1, slotMounted: 1 Everything normal Check if component is hidden by CSS

Note that teardown() unregisters the slot, and you must refresh the page afterwards to re-register.

Summary

dsh-answer-top is a single-purpose client-side plugin: auto-rollback, adjustable thresholds, manual button fallback, no internet access, and no sensitive data stored. Install and restart DSH to use; if DSH changes the DOM contract in the future and causes it to fail, first verify the existence of those contract elements in the console. Directory page: https://www.skillhub.cn/plugins/DSH-USER-GTB/dsh-answer-top ; Source code: https://github.com/DSH-USER-GTB/dsh-answer-top .

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye