@trendr/core 0.4.11 → 0.4.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trendr/core",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "description": "direct-mode TUI renderer with JSX, signals, and per-cell diffing",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Pyers",
package/src/ansi.js CHANGED
@@ -19,6 +19,9 @@ export const sgrReset = `${ESC}0m`
19
19
  export const setTitle = (title) => `\x1b]2;${title}\x07`
20
20
  export const osc52Copy = (text) => `\x1b]52;c;${Buffer.from(text, 'utf8').toString('base64')}\x07`
21
21
 
22
+ export const beginSync = `${ESC}?2026h`
23
+ export const endSync = `${ESC}?2026l`
24
+
22
25
  export const enableMouse = `${ESC}?1003h${ESC}?1006h`
23
26
  export const disableMouse = `${ESC}?1003l${ESC}?1006l`
24
27
 
package/src/buffer.js CHANGED
@@ -159,5 +159,8 @@ export function blitRect(src, dst, x, y, w, h) {
159
159
  for (let col = x1; col < x2; col++) {
160
160
  dst.cells[base + col] = src.cells[base + col]
161
161
  }
162
+ // softWrap is per-row metadata set at text paint time; a blitted clean
163
+ // subtree must carry its wrap flags or selection re-joins lines wrongly
164
+ if (src.softWrap[row]) dst.softWrap[row] = 1
162
165
  }
163
166
  }
package/src/renderer.js CHANGED
@@ -1069,10 +1069,13 @@ export function mount(rootComponent, { stream, stdin, title, theme, onExit: onEx
1069
1069
 
1070
1070
  const { output, changed } = diff(prev, curr)
1071
1071
  if (changed > 0) {
1072
- out.write(ansi.hideCursor)
1072
+ // synchronized output (dec 2026): supporting terminals apply the whole
1073
+ // frame atomically instead of tearing mid-write; others ignore it
1074
+ out.write(ansi.beginSync + ansi.hideCursor)
1073
1075
  // diff() returns a view into a shared double buffer that gets reused two
1074
1076
  // frames later; write a copy so backpressured streams never see it mutate
1075
1077
  out.write(Buffer.from(output))
1078
+ out.write(ansi.endSync)
1076
1079
  }
1077
1080
 
1078
1081
  const now = performance.now()
@@ -1227,6 +1230,10 @@ export function mount(rootComponent, { stream, stdin, title, theme, onExit: onEx
1227
1230
  }
1228
1231
 
1229
1232
  ctx.repaint = repaint
1233
+ // gentle sibling of repaint: schedules a normal diffed frame with no
1234
+ // clear-screen; right for overlay state like selection that frame()
1235
+ // already applies each pass
1236
+ ctx.requestFrame = () => scheduler.forceFrame()
1230
1237
  ctx.getPaintBuffer = () => (inline ? lastInlineBuf : prev)
1231
1238
 
1232
1239
  return { unmount, repaint, setTheme, getBuffer: ctx.getPaintBuffer }
package/src/selection.js CHANGED
@@ -65,7 +65,7 @@ export function useSelection({ onCopy, copy = true } = {}) {
65
65
  state.dragging = false
66
66
  if (ctx.selection) {
67
67
  ctx.selection = null
68
- ctx.repaint()
68
+ ctx.requestFrame()
69
69
  }
70
70
  return
71
71
  }
@@ -73,7 +73,7 @@ export function useSelection({ onCopy, copy = true } = {}) {
73
73
  if (event.action === 'drag' && state.anchor) {
74
74
  state.dragging = true
75
75
  ctx.selection = normalize(state.anchor, { x: event.x, y: event.y })
76
- ctx.repaint()
76
+ ctx.requestFrame()
77
77
  return
78
78
  }
79
79
 
@@ -81,7 +81,7 @@ export function useSelection({ onCopy, copy = true } = {}) {
81
81
  if (state.dragging && ctx.selection) {
82
82
  const text = extractSelectionText(ctx.getPaintBuffer(), ctx.selection)
83
83
  ctx.selection = null
84
- ctx.repaint()
84
+ ctx.requestFrame()
85
85
  if (text) {
86
86
  if (copy) ctx.stream.write(osc52Copy(text))
87
87
  if (onCopy) onCopy(text)