chendefine/dsh-composer-markdown0

dsh-composer-markdown

DSH Web 插件,用于增强对话输入框的 Markdown 编辑体验。提供列表自动续行、层级缩进、行内代码样式及代码块围栏自动闭合等功能,保持发送文本的字面保真度。

AI 分析

核心用于提升 DSH 对话框中 Markdown 文本的输入效率。适合频繁编写格式化文本的用户,需配合 DSH 0.1.2-alpha.x 及 Node.js 环境使用。

包名
dsh-composer-markdown
版本
0.1.1
许可证
MIT
最近更新
2026年9月7日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:chendefine/dsh-composer-markdown

Usage

Every editing gesture rides on Shift+Enter; Enter = native submit, never intercepted; a Shift+Enter that matches no scenario = the native soft break. Tab / Shift+Tab are claimed only when the caret's line is a list item (the level ladder) — everywhere else they stay native (focus traversal and the like). Plain Backspace / Delete / arrow keys are claimed only when they hit exactly the boundaries below — everything else keeps the native per-character behavior.

Lists

OperationTriggerBehavior
Bullet/ordered continuationType content after a line-head - / * / N. (any indent depth), press Shift+EnterThe new line below is auto-seeded with the {indent}- / {n+1}. prefix (nesting level preserved), caret right after it; works on any visual line — paragraph heads, soft-broken lines, and multi-line-pasted lines alike
Split at caretShift+Enter with the caret mid-content of a list itemThe line is cut at the caret: everything after it moves down and becomes the new item's content, spliced after the new prefix (byte-faithful); a caret at the line start / inside the marker / at the line end keeps the append-below behavior
Mid-insert shift-downShift+Enter inserting a new item mid-groupThe new line takes n+1 and every member below in the same group shifts +1 (1. 2. 3. inserting between 1 and 2 → 1. 2. 3. 4.); the group stays continuous and duplicate-free; insert and shift share one undo step
Renumbering (the invariant)After any change (delete/insert a line, empty-item exit, paste, undo/redo)Every ordered group (consecutive ordered lines, one indent, outside fences) is always numbered 1. … n. from its first member: a deleted line closes the gap below; a group split by a plain/blank/bullet line restarts its tail at 1.; deleting the splitting line merges the two runs into one continuous count; deeper indents merely suspend the outer group (a nested sublist never interrupts the outer run — its count resumes when the outer indent returns), nested groups normalize independently. Deliberate trade: a group cannot hold a non-1 start or a manual gap — hand-typed digits snap back (continuity IS the contract)
Empty-item exitShift+Enter on an item that is only a prefixThe prefix is removed and the empty paragraph kept (an empty line on soft lines) — press twice to cleanly leave the list
Level ladder — sinkTab with the collapsed caret anywhere on a list item's line (mid-content included)The item and every deeper line below it (nested sublists, deeper-continuation text — the whole subtree, recursively) each gain 2 spaces of indent; same-level siblings and everything after them are untouched; the caret rides its in-line position; the whole subtree moves in one keystroke, one undo step; the reshuffled ordered groups normalize on the invariant right after (a 2. nested under a parent renumbers to that nested run's 1.). Sinking is capped (v2.9): an item may sit at most ONE level below its parent — the nearest item line above (plain lines skipped, blanks/fences ending the search). An item already one level below its parent, and a list's FIRST item (nothing above to nest under), get a claimed-but-inert no-op on Tab (focus never jumps away mid-list-editing) — the ladder cannot descend without bound
Level ladder — lift / unlistSame positions, Shift+Tab — or Backspace with the collapsed caret right after the list atom (at the content head)A nested item (indent ≥ 1 space) → the item and its whole subtree each lose min(2, indent) spaces (odd hand-typed indents floor at 0); a top-level item → unlisted: the whole atom dies, the content stays as a plain-text line, the subtree still rises one level. The plain line an unlist leaves behind splits an ordered group — the same-indent members below restart at 1. per the invariant. Holding Tab sinks once and then holds at the cap; holding Shift+Tab climbs one rung per repeat — focus never escapes an item line on either key
Atomic atom deleteDelete with the collapsed caret right at a list item's line head (before the whole atom)The indent + marker atom goes in one stroke (␣␣1. — all of it), the line becomes top-level plain text, its own undo step; the indent can never be eaten character by character
Atomic arrow hop over atomsPlain ← / → with the collapsed caret at any list-atom boundary at the line head jumps clear over the whole ␣␣1. / ␣␣- (indent included) to the content's first character; right after the atom jumps back to the line head; a caret already inside the atom (including inside the indent spaces — a click, a vertical move) exits to the far edge — the keyboard never walks the atom interior. Shift+arrows stay native (a selection can still cover exactly the digit characters for manual renumbering)
Differentiated marker renderingAny visual line headed by - / * / N. (outside fences, empty prefixes included)Ordered digits switch to the code font; the bullet dash/star renders as (dot + space as one marker unit); the marker bytes are kept verbatim in the draft/copy/send text; a selection genuinely covering a marker character reveals the raw character
No resting inside an atomA collapsed caret landing inside a list atom (indent spaces included) via any path (↑/↓, mouse click)The caret is homed to the nearest atom edge (ties snap to the line head); the edges themselves (line head / content head) are legal rest points; selections are unaffected

Inline code

  • When a line holds a paired `non-empty` whose content stays on one line and whose inner-edge characters are non-blank (full-width space / NBSP included): the inner text gets the inline-code look (code font, light background, rounded corners — reusing the DSH theme tokens);
  • The paired backticks hide the instant the pair closes (invisible, zero width, still kept byte-for-byte in the draft and the sent text); they stay hidden while the caret travels inside the pair or brushes either side;
  • Only a selection genuinely covering one of the backtick characters (Shift+arrows, select-all) temporarily reveals the pair (select-what-you-see); it hides again once the selection collapses; a cross-paragraph selection (Ctrl+A) projects per paragraph — no misses at paragraph boundaries;
  • Non-matching pairs are fully inert (their backticks stay visible and never affect later pairing); ``-style multi-backtick delimiters are not recognized (naive left-to-right pairing).

Code blocks

OperationTriggerBehavior
Skeleton closingThe caret's line is exactly ``` (optionally with a language id ```ts) and the caret sits inside/after the marker run, Shift+EnterInserts the three-line skeleton ``` / empty / ```, caret at the head of the empty line; works when typed at the head of any line (soft-broken and pasted lines included); a caret before the marker run (bare ``` line, offset 0) is plain text and keeps the native soft break
Content-preserving flowType ``` at the head of a line that already has content (caret still right after it), Shift+EnterSplits after the ```: the marker line keeps ```, an empty line + closing skeleton is inserted, and everything after ``` on the original line moves below the closing marker (outside the block, byte-faithful); the language-id flow is unaffected (```ts at line end still yields the badged skeleton)
Paired renderingA paired ```…``` region in the draftThe whole region renders as one code block: code background + code font inside (the same theme tokens and geometry as the message-side CodeBlock); the first marker line is zero-height, the language id shows as a floating badge at the block's top-right; the ``` markers are fully imperceptible (invisible, never host the caret); an unclosed ``` is plain text, always — typing ``` mid-draft never swallows the content below into a block
One-keystroke unwrapBackspace / Delete at a block boundary (← at the first body line's head / the paragraph after the block; → at the paragraph before the block / the last body line's end)One keystroke removes the pair of ``` marker lines: the block un-renders, the body survives verbatim as plain paragraphs, the caret stays put — its own undo step
Leaving the block at the last body line's end (an empty paragraph grows below when the block ends the draft)The caret drops to the new paragraph below the block to continue typing plain text; ↑/↓ skip the marker lines at block boundaries
New paragraph inside a fenceShift+Enter inside a fence (the closing line included)Inserts a new paragraph (not a soft break), split at the caret; plain Enter inside a fence still submits

Coexistence with native behavior

  • Enter: keeps DSH's native "submit" and is never intercepted (on list lines and inside fences alike);
  • Ctrl/Cmd+Enter: keeps the native "accelerated submit";
  • Shift+Enter (no scenario matched): the native soft break ( );
  • Tab / Shift+Tab (not a list-item line, inside a fence, a range selection, or a modifier chord): fully native (browser focus traversal etc.), never intercepted; likewise yielded while a trigger menu is open; held repeats on a list-item line re-plan like any press (they never fall through to focus traversal);
  • While a / or @ trigger menu is open: the Enter family is fully yielded;
  • IME (e.g. Chinese input): keys during a composition trigger nothing (three-signal guard: isComposing / keyCode 229 / a 10 ms window after compositionend — the same guard the DSH input machine itself uses);
  • Slash-command claims, @ reference chips, image drag-drop/paste, busy/locked states, and Q&A/approval cards are unaffected;
  • Coexisting with other composer plugins: capture-phase first-come-first-served — a key already preventDefault-ed is yielded to; keys this plugin consumes get stopImmediatePropagation.