@trendr/core 0.4.10 → 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 +1 -1
- package/src/ansi.js +6 -0
- package/src/buffer.js +3 -0
- package/src/diff-view.js +1 -1
- package/src/dropdown.js +1 -1
- package/src/list.js +1 -1
- package/src/renderer.js +9 -1
- package/src/scroll-box.js +1 -1
- package/src/scrollable-text.js +1 -1
- package/src/selection.js +8 -6
- package/src/text-area.js +1 -1
package/package.json
CHANGED
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
|
|
|
@@ -28,6 +31,9 @@ export const ITALIC = 4
|
|
|
28
31
|
export const UNDERLINE = 8
|
|
29
32
|
export const INVERSE = 16
|
|
30
33
|
export const STRIKETHROUGH = 32
|
|
34
|
+
// never emitted as sgr: marks ui chrome cells (scrollbars, counters) that
|
|
35
|
+
// text selection should skip when extracting copy text
|
|
36
|
+
export const COPY_IGNORE = 64
|
|
31
37
|
|
|
32
38
|
const ATTR_CODES = [
|
|
33
39
|
[BOLD, '1'],
|
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/diff-view.js
CHANGED
|
@@ -236,7 +236,7 @@ export function Diff({
|
|
|
236
236
|
const isThumb = i >= thumbStart && i < thumbStart + thumbH
|
|
237
237
|
bar.push(jsx('text', {
|
|
238
238
|
key: i,
|
|
239
|
-
style: { color:
|
|
239
|
+
style: { color: palette.lineNo, dim: !isThumb, copyIgnore: true },
|
|
240
240
|
children: isThumb ? '█' : '│',
|
|
241
241
|
}))
|
|
242
242
|
}
|
package/src/dropdown.js
CHANGED
|
@@ -126,7 +126,7 @@ export function Dropdown({ items, cursor, scroll, visibleCount, width, onSubmit,
|
|
|
126
126
|
children: [
|
|
127
127
|
content,
|
|
128
128
|
jsx('text', {
|
|
129
|
-
style: { color: barIsThumb ? s.accent : muted, dim: !barIsThumb },
|
|
129
|
+
style: { color: barIsThumb ? s.accent : muted, dim: !barIsThumb, copyIgnore: true },
|
|
130
130
|
children: ' ' + (barIsThumb ? '█' : '│'),
|
|
131
131
|
}),
|
|
132
132
|
],
|
package/src/list.js
CHANGED
|
@@ -190,7 +190,7 @@ export function List({ items = [], selected: selectedProp, onSelect, onCursorCha
|
|
|
190
190
|
barChildren.push(
|
|
191
191
|
jsx('text', {
|
|
192
192
|
key: i,
|
|
193
|
-
style: { color: isThumb ? (focused ? accent : muted) : muted, dim: !isThumb },
|
|
193
|
+
style: { color: isThumb ? (focused ? accent : muted) : muted, dim: !isThumb, copyIgnore: true },
|
|
194
194
|
children: isThumb ? '\u2588' : '\u2502',
|
|
195
195
|
})
|
|
196
196
|
)
|
package/src/renderer.js
CHANGED
|
@@ -98,6 +98,7 @@ function resolveAttrs(style) {
|
|
|
98
98
|
if (style.underline) attrs |= ansi.UNDERLINE
|
|
99
99
|
if (style.inverse) attrs |= ansi.INVERSE
|
|
100
100
|
if (style.strikethrough) attrs |= ansi.STRIKETHROUGH
|
|
101
|
+
if (style.copyIgnore) attrs |= ansi.COPY_IGNORE
|
|
101
102
|
return attrs
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -1068,10 +1069,13 @@ export function mount(rootComponent, { stream, stdin, title, theme, onExit: onEx
|
|
|
1068
1069
|
|
|
1069
1070
|
const { output, changed } = diff(prev, curr)
|
|
1070
1071
|
if (changed > 0) {
|
|
1071
|
-
|
|
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)
|
|
1072
1075
|
// diff() returns a view into a shared double buffer that gets reused two
|
|
1073
1076
|
// frames later; write a copy so backpressured streams never see it mutate
|
|
1074
1077
|
out.write(Buffer.from(output))
|
|
1078
|
+
out.write(ansi.endSync)
|
|
1075
1079
|
}
|
|
1076
1080
|
|
|
1077
1081
|
const now = performance.now()
|
|
@@ -1226,6 +1230,10 @@ export function mount(rootComponent, { stream, stdin, title, theme, onExit: onEx
|
|
|
1226
1230
|
}
|
|
1227
1231
|
|
|
1228
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()
|
|
1229
1237
|
ctx.getPaintBuffer = () => (inline ? lastInlineBuf : prev)
|
|
1230
1238
|
|
|
1231
1239
|
return { unmount, repaint, setTheme, getBuffer: ctx.getPaintBuffer }
|
package/src/scroll-box.js
CHANGED
|
@@ -93,7 +93,7 @@ export function ScrollBox({ children, focused = true, scrollOffset: offsetProp,
|
|
|
93
93
|
barChildren.push(
|
|
94
94
|
jsx('text', {
|
|
95
95
|
key: i,
|
|
96
|
-
style: { color: isThumb ? (focused ? accent : muted) : muted, dim: !isThumb },
|
|
96
|
+
style: { color: isThumb ? (focused ? accent : muted) : muted, dim: !isThumb, copyIgnore: true },
|
|
97
97
|
children: isThumb ? thumbChar : trackChar,
|
|
98
98
|
})
|
|
99
99
|
)
|
package/src/scrollable-text.js
CHANGED
|
@@ -89,7 +89,7 @@ export function ScrollableText({ content = '', focused = true, scrollOffset: off
|
|
|
89
89
|
style: { flexDirection: 'row', height: 1 },
|
|
90
90
|
children: [
|
|
91
91
|
jsx('text', { style: { flexGrow: 1, ...textStyle }, children: line || ' ' }),
|
|
92
|
-
jsx('text', { style: { color: barColor, dim: !isThumb }, children: ' ' + barChar }),
|
|
92
|
+
jsx('text', { style: { color: barColor, dim: !isThumb, copyIgnore: true }, children: ' ' + barChar }),
|
|
93
93
|
],
|
|
94
94
|
})
|
|
95
95
|
})
|
package/src/selection.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMouse } from './hooks.js'
|
|
2
2
|
import { getContext, registerHook } from './renderer.js'
|
|
3
|
-
import { osc52Copy } from './ansi.js'
|
|
3
|
+
import { osc52Copy, COPY_IGNORE } from './ansi.js'
|
|
4
4
|
|
|
5
5
|
function normalize(a, b) {
|
|
6
6
|
const forward = a.y < b.y || (a.y === b.y && a.x <= b.x)
|
|
@@ -22,8 +22,10 @@ export function extractSelectionText(buf, sel) {
|
|
|
22
22
|
const to = y === sel.ey ? Math.min(sel.ex, buf.width - 1) : buf.width - 1
|
|
23
23
|
let text = ''
|
|
24
24
|
for (let x = 0; x <= to; x++) {
|
|
25
|
-
const
|
|
26
|
-
if (ch === '') continue
|
|
25
|
+
const cell = buf.cells[y * buf.width + x]
|
|
26
|
+
if (cell.ch === '') continue
|
|
27
|
+
// chrome cells (scrollbars, counters) never belong in copied text
|
|
28
|
+
const ch = cell.attrs & COPY_IGNORE ? ' ' : cell.ch
|
|
27
29
|
text += y === sel.sy && x < sel.sx ? ' ' : ch
|
|
28
30
|
}
|
|
29
31
|
rows.push({ text: text.replace(/\s+$/, ''), soft: !!buf.softWrap[y] })
|
|
@@ -63,7 +65,7 @@ export function useSelection({ onCopy, copy = true } = {}) {
|
|
|
63
65
|
state.dragging = false
|
|
64
66
|
if (ctx.selection) {
|
|
65
67
|
ctx.selection = null
|
|
66
|
-
ctx.
|
|
68
|
+
ctx.requestFrame()
|
|
67
69
|
}
|
|
68
70
|
return
|
|
69
71
|
}
|
|
@@ -71,7 +73,7 @@ export function useSelection({ onCopy, copy = true } = {}) {
|
|
|
71
73
|
if (event.action === 'drag' && state.anchor) {
|
|
72
74
|
state.dragging = true
|
|
73
75
|
ctx.selection = normalize(state.anchor, { x: event.x, y: event.y })
|
|
74
|
-
ctx.
|
|
76
|
+
ctx.requestFrame()
|
|
75
77
|
return
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -79,7 +81,7 @@ export function useSelection({ onCopy, copy = true } = {}) {
|
|
|
79
81
|
if (state.dragging && ctx.selection) {
|
|
80
82
|
const text = extractSelectionText(ctx.getPaintBuffer(), ctx.selection)
|
|
81
83
|
ctx.selection = null
|
|
82
|
-
ctx.
|
|
84
|
+
ctx.requestFrame()
|
|
83
85
|
if (text) {
|
|
84
86
|
if (copy) ctx.stream.write(osc52Copy(text))
|
|
85
87
|
if (onCopy) onCopy(text)
|
package/src/text-area.js
CHANGED
|
@@ -372,7 +372,7 @@ export function TextArea({ onSubmit, onCancel, onChange, onKeyDown, placeholder,
|
|
|
372
372
|
const counter = counterActive
|
|
373
373
|
? jsx('box', {
|
|
374
374
|
style: { flexDirection: 'row', height: 1 },
|
|
375
|
-
children: jsx('text', { style: { color: muted, dim: true }, children: `${displayPos.row + 1}/${lineMap.length}` }),
|
|
375
|
+
children: jsx('text', { style: { color: muted, dim: true, copyIgnore: true }, children: `${displayPos.row + 1}/${lineMap.length}` }),
|
|
376
376
|
})
|
|
377
377
|
: null
|
|
378
378
|
|