dsh-edit-turn 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -250,6 +250,12 @@ ln -s /path/to/dsh-install/node_modules ./node_modules
250
250
 
251
251
  ### 更新日志
252
252
 
253
+ **0.3.3** —— 编辑器锚定到刚按下的铅笔,位置不再跑到屏幕另一端。
254
+
255
+ - 回复的编辑按钮在 turn-tail 操作条里,而编辑器的锚点行可能是比屏幕还高的回答行;0.3.2 会把它钳到视口顶部,按钮可达但离点击处很远。现在把锚点记为按下的按钮本身:优先贴在按钮下方,放不下就贴在按钮上方,再按视口钳制。
256
+ - 长草稿仍由编辑器自身滚动、footer sticky,保存/取消始终可见。
257
+ - **测试**:`npm test` **213/213**、`npm run verify:contract` **96** 项、`npm run verify:client` **184** 项。
258
+
253
259
  **0.3.2** —— 修复编辑器在长回答或页面底部时位置错误、按钮点不到的问题。
254
260
 
255
261
  - **视口钳制**:编辑框优先放在行下方;放不下就翻到行上方;行比屏幕高、底部在视口外时钉在视口底部,不再跟着行跑到屏幕外。
package/lib/client.js CHANGED
@@ -42,7 +42,7 @@ window.__ModuleLoader__.load({
42
42
  const RERUN_PREFIX = '/dsh-rerun-turn'
43
43
 
44
44
  /** Keep in sync with package.json and lib/index.js. */
45
- const PLUGIN_VERSION = '0.3.2'
45
+ const PLUGIN_VERSION = '0.3.3'
46
46
 
47
47
  // --- copy -----------------------------------------------------------------
48
48
 
@@ -364,6 +364,7 @@ window.__ModuleLoader__.load({
364
364
  this.editorElement = null
365
365
  this.editorBox = null
366
366
  this.editorRow = null
367
+ this.editorAnchor = null
367
368
  this.editorFocus = false
368
369
  this.editorDraft = ''
369
370
  // The block list being edited: what the carrier will be rebuilt from.
@@ -563,6 +564,9 @@ window.__ModuleLoader__.load({
563
564
  }
564
565
  : target
565
566
  this.editor = normalized
567
+ this.editorAnchor = target.anchor !== undefined && target.anchor !== null && target.anchor.isConnected === true
568
+ ? target.anchor
569
+ : null
566
570
  this.editorBlocks = replyBlocks
567
571
  this.editorBlockIndex = chosen === undefined ? null : chosen.index
568
572
  this.editorBlockKind = chosen === undefined ? null : chosen.kind
@@ -644,6 +648,7 @@ window.__ModuleLoader__.load({
644
648
  this.editorElement = null
645
649
  this.editorBox = null
646
650
  this.editorRow = null
651
+ this.editorAnchor = null
647
652
  this.editorFocus = false
648
653
  this.editor = null
649
654
  this.editorDraft = ''
@@ -1197,9 +1202,9 @@ window.__ModuleLoader__.load({
1197
1202
  * at all and reads exactly like a dead pencil.
1198
1203
  */
1199
1204
  function wireAction(button) {
1200
- pressable(button, () => {
1205
+ pressable(button, (event) => {
1201
1206
  const activate = button[ACTIVATE]
1202
- if (typeof activate === 'function') activate()
1207
+ if (typeof activate === 'function') activate(event)
1203
1208
  })
1204
1209
  }
1205
1210
 
@@ -1815,7 +1820,14 @@ window.__ModuleLoader__.load({
1815
1820
  } else if (button.dataset !== undefined && button.dataset.dshetFallback !== undefined) {
1816
1821
  delete button.dataset.dshetFallback
1817
1822
  }
1818
- button[ACTIVATE] = () => controller.open(entry.target)
1823
+ button[ACTIVATE] = (event) => controller.open({
1824
+ ...entry.target,
1825
+ // The editor belongs beside the pencil the user just pressed, not at the
1826
+ // top of a row that may be taller than the screen.
1827
+ anchor: event !== undefined && event !== null && event.currentTarget !== undefined
1828
+ ? event.currentTarget
1829
+ : button,
1830
+ })
1819
1831
  liveHosts.add(host)
1820
1832
  if (button.getAttribute('aria-label') !== label) {
1821
1833
  button.setAttribute('aria-label', label)
@@ -1961,10 +1973,16 @@ window.__ModuleLoader__.load({
1961
1973
  // past the bottom edge where nothing could reach it. Flip above the row when
1962
1974
  // there is room, otherwise pin the box to the bottom of the viewport and let
1963
1975
  // its own scrollbar carry the content; the footer stays sticky either way.
1964
- function placeEditor(row, box) {
1976
+ function placeEditor(row, box, anchor = null) {
1965
1977
  const layer = ensureEditorLayer()
1966
1978
  if (box.parentElement !== layer) layer.appendChild(box)
1967
1979
  const rect = row.getBoundingClientRect()
1980
+ let anchorRect = null
1981
+ if (anchor !== null && anchor !== undefined && anchor.isConnected === true && typeof anchor.getBoundingClientRect === 'function') {
1982
+ const candidate = anchor.getBoundingClientRect()
1983
+ if (candidate.width > 0 && candidate.height > 0) anchorRect = candidate
1984
+ }
1985
+ const vertical = anchorRect === null ? rect : anchorRect
1968
1986
  const viewport = viewportSize()
1969
1987
  const margin = 8
1970
1988
  const width = viewport.width > 0
@@ -1973,11 +1991,11 @@ window.__ModuleLoader__.load({
1973
1991
  box.style.width = `${Math.round(width)}px`
1974
1992
  const measured = box.getBoundingClientRect()
1975
1993
  const height = (measured && measured.height) || box.offsetHeight || 0
1976
- let top = rect.bottom + 6
1994
+ let top = vertical.bottom + 6
1977
1995
  if (viewport.height > 0 && height > 0) {
1978
1996
  const maxTop = Math.max(margin, viewport.height - height - margin)
1979
1997
  if (top + height > viewport.height - margin) {
1980
- const above = rect.top - height - 6
1998
+ const above = vertical.top - height - 6
1981
1999
  top = above >= margin ? above : maxTop
1982
2000
  }
1983
2001
  // A row taller than the screen has its bottom above the viewport after
@@ -2019,7 +2037,7 @@ window.__ModuleLoader__.load({
2019
2037
  pressed = true
2020
2038
  event.preventDefault()
2021
2039
  event.stopPropagation()
2022
- action()
2040
+ action(event)
2023
2041
  }
2024
2042
  button.onclick = (event) => {
2025
2043
  event.preventDefault()
@@ -2028,7 +2046,7 @@ window.__ModuleLoader__.load({
2028
2046
  pressed = false
2029
2047
  return
2030
2048
  }
2031
- action()
2049
+ action(event)
2032
2050
  }
2033
2051
  }
2034
2052
 
@@ -2270,7 +2288,7 @@ window.__ModuleLoader__.load({
2270
2288
  ? controller.editorBox
2271
2289
  : null
2272
2290
  if (existing && existing.dataset.dshetFor === marker) {
2273
- placeEditor(row, existing)
2291
+ placeEditor(row, existing, controller.editorAnchor)
2274
2292
  return
2275
2293
  }
2276
2294
 
@@ -2337,13 +2355,13 @@ window.__ModuleLoader__.load({
2337
2355
  fitEditorHeight(area)
2338
2356
  const row = controller.editorRow
2339
2357
  const box = controller.editorBox
2340
- if (row !== null && box !== null && box.isConnected === true) placeEditor(row, box)
2358
+ if (row !== null && box !== null && box.isConnected === true) placeEditor(row, box, controller.editorAnchor)
2341
2359
  })
2342
2360
  area.addEventListener('compositionend', () => {
2343
2361
  fitEditorHeight(area)
2344
2362
  const row = controller.editorRow
2345
2363
  const box = controller.editorBox
2346
- if (row !== null && box !== null && box.isConnected === true) placeEditor(row, box)
2364
+ if (row !== null && box !== null && box.isConnected === true) placeEditor(row, box, controller.editorAnchor)
2347
2365
  })
2348
2366
  area.addEventListener('focus', () => {
2349
2367
  controller.editorFocus = true
@@ -2458,7 +2476,7 @@ window.__ModuleLoader__.load({
2458
2476
  controller.editorRow = row
2459
2477
  controller.editorElement = area
2460
2478
  fitEditorHeight(area)
2461
- placeEditor(row, box)
2479
+ placeEditor(row, box, controller.editorAnchor)
2462
2480
  if (!view.confirming && !view.pending && (fresh || wasFocused)) {
2463
2481
  try {
2464
2482
  area.focus()
@@ -2932,6 +2950,7 @@ window.__ModuleLoader__.load({
2932
2950
  event.preventDefault()
2933
2951
  event.stopPropagation()
2934
2952
  controller.open({
2953
+ anchor: event.currentTarget,
2935
2954
  seq: entry.seq,
2936
2955
  mode: 'reply',
2937
2956
  turn: entry.turn,
@@ -2985,7 +3004,7 @@ window.__ModuleLoader__.load({
2985
3004
  const keepPlaced = () => {
2986
3005
  const row = controller.editorRow
2987
3006
  const box = controller.editorBox
2988
- if (row !== null && box !== null && box.isConnected === true) placeEditor(row, box)
3007
+ if (row !== null && box !== null && box.isConnected === true) placeEditor(row, box, controller.editorAnchor)
2989
3008
  }
2990
3009
  document.addEventListener('pointerdown', onPointerDown, true)
2991
3010
  // Scroll events do not bubble; capture reaches the inner scroller too.
package/lib/index.js CHANGED
@@ -80,7 +80,7 @@ export const PLUGIN_ID = 'dsh-edit-turn'
80
80
  export const name = PLUGIN_ID
81
81
 
82
82
  /** Keep in sync with package.json and lib/client.js. */
83
- export const PLUGIN_VERSION = '0.3.2'
83
+ export const PLUGIN_VERSION = '0.3.3'
84
84
 
85
85
  // A ring of the last few requests, answering "did the browser half even ask, and
86
86
  // what did it get?" without needing its console. Read-only diagnostics: the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-edit-turn",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Edit any message in DeepSeek Harness in place: a prompt is replaced where it stands; a model reply block (answer text or reasoning) is patched through a plugin record and a message projection, so the turn stays where it is, later turns are untouched and the log grows by one small record. Falls back to the replay splice on hosts without the projection API. The append-only session log is never rewritten.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",