@deepseek-harness-tui/dsh-tui 0.8.8 → 0.9.0
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 +27 -8
- package/bin/dsh-tui.js +1 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +4 -2
- package/lib/types/components/ActivityPicker.d.ts +4 -1
- package/lib/types/components/ActivityPicker.d.ts.map +1 -1
- package/lib/types/components/ActivityPicker.js +2 -2
- package/lib/types/components/BtwPanel.d.ts.map +1 -1
- package/lib/types/components/BtwPanel.js +1 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts +2 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -1
- package/lib/types/components/Chat/SubagentMessage.js +11 -3
- package/lib/types/components/CommandSuggestions.d.ts +21 -4
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +41 -14
- package/lib/types/components/EffortSlider.d.ts +4 -1
- package/lib/types/components/EffortSlider.d.ts.map +1 -1
- package/lib/types/components/EffortSlider.js +4 -2
- package/lib/types/components/ExtensionDialog.js +6 -2
- package/lib/types/components/FileSuggestions.d.ts +24 -7
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/FileSuggestions.js +39 -12
- package/lib/types/components/GoalTodoPanel.d.ts +26 -2
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +86 -17
- package/lib/types/components/HelpMenu.d.ts +7 -1
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +12 -4
- package/lib/types/components/HistorySearchDialog.d.ts +4 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +2 -2
- package/lib/types/components/LangPicker.d.ts +14 -0
- package/lib/types/components/LangPicker.d.ts.map +1 -0
- package/lib/types/components/LangPicker.js +17 -0
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +3 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +12 -3
- package/lib/types/components/MessageList.d.ts +43 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +553 -41
- package/lib/types/components/ModelPicker.d.ts +4 -1
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +2 -2
- package/lib/types/components/PermissionsPicker.d.ts +21 -0
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
- package/lib/types/components/PermissionsPicker.js +27 -0
- package/lib/types/components/PlanPicker.d.ts +15 -0
- package/lib/types/components/PlanPicker.d.ts.map +1 -0
- package/lib/types/components/PlanPicker.js +18 -0
- package/lib/types/components/PresetPicker.d.ts +4 -1
- package/lib/types/components/PresetPicker.d.ts.map +1 -1
- package/lib/types/components/PresetPicker.js +2 -2
- package/lib/types/components/PromptInput.d.ts +4 -3
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +248 -29
- package/lib/types/components/RewindPicker.d.ts +12 -1
- package/lib/types/components/RewindPicker.d.ts.map +1 -1
- package/lib/types/components/RewindPicker.js +6 -4
- package/lib/types/components/ScrollbarGutter.d.ts +28 -0
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
- package/lib/types/components/ScrollbarGutter.js +68 -0
- package/lib/types/components/Select.d.ts +8 -1
- package/lib/types/components/Select.d.ts.map +1 -1
- package/lib/types/components/Select.js +4 -2
- package/lib/types/components/SkillsPicker.d.ts +4 -1
- package/lib/types/components/SkillsPicker.d.ts.map +1 -1
- package/lib/types/components/SkillsPicker.js +2 -2
- package/lib/types/components/SubagentCard.d.ts +2 -1
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +6 -1
- package/lib/types/components/SubagentDashboard.d.ts +4 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +9 -1
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +5 -4
- package/lib/types/components/SuggestionCard.d.ts +63 -0
- package/lib/types/components/SuggestionCard.d.ts.map +1 -0
- package/lib/types/components/SuggestionCard.js +77 -0
- package/lib/types/components/ThemePicker.d.ts +4 -1
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +2 -2
- package/lib/types/components/ThinkingToggle.d.ts +4 -1
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +2 -2
- package/lib/types/components/TimelineRail.d.ts +53 -0
- package/lib/types/components/TimelineRail.d.ts.map +1 -0
- package/lib/types/components/TimelineRail.js +190 -0
- package/lib/types/components/WorkspaceFlowPicker.d.ts +6 -1
- package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
- package/lib/types/components/WorkspaceFlowPicker.js +4 -2
- package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
- package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
- package/lib/types/components/WorkspaceMenuPicker.js +19 -0
- package/lib/types/components/WorkspacePicker.d.ts +4 -1
- package/lib/types/components/WorkspacePicker.d.ts.map +1 -1
- package/lib/types/components/WorkspacePicker.js +2 -2
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
- package/lib/types/components/approvals/ApprovalPanel.js +5 -1
- package/lib/types/components/design-system/ListItem.d.ts +8 -1
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +8 -3
- package/lib/types/components/messages/AssistantTextMessage.d.ts +5 -2
- package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantTextMessage.js +6 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +10 -4
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +4 -2
- package/lib/types/components/messages/UserPromptMessage.d.ts +2 -1
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +34 -2
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +13 -2
- package/lib/types/components/sessions/SessionListRow.d.ts +4 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +4 -2
- package/lib/types/components/trajectory/HotspotView.d.ts +7 -1
- package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -1
- package/lib/types/components/trajectory/HotspotView.js +8 -2
- package/lib/types/components/trajectory/Ledger.d.ts +9 -1
- package/lib/types/components/trajectory/Ledger.d.ts.map +1 -1
- package/lib/types/components/trajectory/Ledger.js +23 -5
- package/lib/types/components/trajectory/WaveBand.d.ts +10 -1
- package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -1
- package/lib/types/components/trajectory/WaveBand.js +7 -2
- package/lib/types/dsh-adapter/channel.d.ts +16 -5
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +332 -29
- package/lib/types/dsh-adapter/index.d.ts +10 -2
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +4 -1
- package/lib/types/dsh-adapter/plugin.d.ts +0 -9
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +136 -6
- package/lib/types/i18n.d.ts +176 -15
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +51 -8
- package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -1
- package/lib/types/ink/components/AlternateScreen.js +13 -1
- package/lib/types/ink/components/App.d.ts +12 -1
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +101 -4
- package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
- package/lib/types/ink/components/ClockContext.js +2 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +170 -91
- package/lib/types/ink/constants.d.ts +9 -0
- package/lib/types/ink/constants.d.ts.map +1 -1
- package/lib/types/ink/constants.js +9 -0
- package/lib/types/ink/events/click-event.d.ts +7 -16
- package/lib/types/ink/events/click-event.d.ts.map +1 -1
- package/lib/types/ink/events/click-event.js +16 -19
- package/lib/types/ink/events/event-handlers.d.ts +11 -1
- package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
- package/lib/types/ink/events/event-handlers.js +2 -0
- package/lib/types/ink/events/input-event.d.ts +9 -0
- package/lib/types/ink/events/input-event.d.ts.map +1 -1
- package/lib/types/ink/events/input-event.js +4 -0
- package/lib/types/ink/events/pointer-event.d.ts +61 -0
- package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
- package/lib/types/ink/events/pointer-event.js +61 -0
- package/lib/types/ink/events/wheel-event.d.ts +17 -0
- package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
- package/lib/types/ink/events/wheel-event.js +20 -0
- package/lib/types/ink/geometry-trace.d.ts +51 -0
- package/lib/types/ink/geometry-trace.d.ts.map +1 -0
- package/lib/types/ink/geometry-trace.js +83 -0
- package/lib/types/ink/hit-test.d.ts +56 -1
- package/lib/types/ink/hit-test.d.ts.map +1 -1
- package/lib/types/ink/hit-test.js +146 -8
- package/lib/types/ink/ink.d.ts +58 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +230 -29
- package/lib/types/ink/node-cache.d.ts +5 -0
- package/lib/types/ink/node-cache.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.d.ts +8 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +101 -12
- package/lib/types/ink/render-node-to-output.d.ts +10 -0
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +111 -18
- package/lib/types/ink/timeline-rail.d.ts +138 -0
- package/lib/types/ink/timeline-rail.d.ts.map +1 -0
- package/lib/types/ink/timeline-rail.js +179 -0
- package/lib/types/ink/wrap-text.d.ts.map +1 -1
- package/lib/types/ink/wrap-text.js +47 -5
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +819 -337
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +56 -29
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +64 -22
- package/lib/types/screens/StatusLine.d.ts +2 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +17 -1
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
- package/lib/types/screens/TrajectoryScene.js +81 -14
- package/lib/types/screens/chatOverlay.d.ts +221 -0
- package/lib/types/screens/chatOverlay.d.ts.map +1 -0
- package/lib/types/screens/chatOverlay.js +114 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +42 -6
- package/lib/types/tuiDisplayPrefs.d.ts +15 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +9 -0
- package/lib/types/utils/debug.d.ts +10 -0
- package/lib/types/utils/debug.d.ts.map +1 -1
- package/lib/types/utils/debug.js +24 -0
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { nodeCache } from '../node-cache.js';
|
|
2
|
+
import { TerminalEvent } from './terminal-event.js';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for pointer-derived terminal events (click, wheel, hover).
|
|
5
|
+
*
|
|
6
|
+
* Carries the raw SGR/X10 button byte so handlers can read modifier flags
|
|
7
|
+
* (`shift`/`alt`/`ctrl`) without re-parsing the escape sequence, plus
|
|
8
|
+
* per-handler local coordinates: `_prepareForTarget` recomputes
|
|
9
|
+
* `localCol`/`localRow` from the nodeCache rect before each handler fires,
|
|
10
|
+
* so an onClick on a container sees coordinates relative to that container,
|
|
11
|
+
* not to the child the pointer actually landed on.
|
|
12
|
+
*
|
|
13
|
+
* `meta` is deliberately always false: xterm.js drops metaKey before SGR
|
|
14
|
+
* encoding (the SGR bit we call "meta" is wired to alt), so no pointer
|
|
15
|
+
* protocol can distinguish it reliably.
|
|
16
|
+
*/
|
|
17
|
+
export class PointerEvent extends TerminalEvent {
|
|
18
|
+
/** 0-indexed screen column of the pointer. */
|
|
19
|
+
col;
|
|
20
|
+
/** 0-indexed screen row of the pointer. */
|
|
21
|
+
row;
|
|
22
|
+
/** Raw protocol button byte (SGR encoding). */
|
|
23
|
+
button;
|
|
24
|
+
/** Which pointer action produced this event. */
|
|
25
|
+
action;
|
|
26
|
+
/** Shift held (SGR button bit 0x04). */
|
|
27
|
+
shift;
|
|
28
|
+
/** Alt/Option held (SGR button bit 0x08). */
|
|
29
|
+
alt;
|
|
30
|
+
/** Ctrl held (SGR button bit 0x10). */
|
|
31
|
+
ctrl;
|
|
32
|
+
/** Always false — see class doc. */
|
|
33
|
+
meta = false;
|
|
34
|
+
/** Column relative to the current handler's node (set during dispatch). */
|
|
35
|
+
localCol = 0;
|
|
36
|
+
/** Row relative to the current handler's node (set during dispatch). */
|
|
37
|
+
localRow = 0;
|
|
38
|
+
constructor(type, col, row, init) {
|
|
39
|
+
super(type, {
|
|
40
|
+
bubbles: init?.bubbles ?? true,
|
|
41
|
+
cancelable: init?.cancelable ?? true,
|
|
42
|
+
});
|
|
43
|
+
this.col = col;
|
|
44
|
+
this.row = row;
|
|
45
|
+
this.button = init?.button ?? 0;
|
|
46
|
+
this.action = init?.action ?? 'move';
|
|
47
|
+
this.shift = (this.button & 0x04) !== 0;
|
|
48
|
+
this.alt = (this.button & 0x08) !== 0;
|
|
49
|
+
this.ctrl = (this.button & 0x10) !== 0;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Refresh localCol/localRow for the node whose handler is about to run.
|
|
53
|
+
* Nodes without a cached rect (not rendered this frame) get 0,0 — handlers
|
|
54
|
+
* should prefer absolute col/row when that distinction matters.
|
|
55
|
+
*/
|
|
56
|
+
_prepareForTarget(target) {
|
|
57
|
+
const rect = nodeCache.get(target);
|
|
58
|
+
this.localCol = rect ? this.col - rect.x : 0;
|
|
59
|
+
this.localRow = rect ? this.row - rect.y : 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PointerEvent, type PointerEventInit } from './pointer-event.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mouse wheel event dispatched to the ScrollBox under the pointer.
|
|
4
|
+
*
|
|
5
|
+
* `deltaY`/`deltaX` are in terminal rows/columns per wheel notch (positive =
|
|
6
|
+
* content moves up / right, i.e. scroll down / scroll right), matching
|
|
7
|
+
* ScrollBox.scrollBy's sign convention. Coordinates identify where the wheel
|
|
8
|
+
* event occurred so routing can hit-test the deepest scroll container.
|
|
9
|
+
*/
|
|
10
|
+
export declare class WheelEvent extends PointerEvent {
|
|
11
|
+
/** Rows to scroll (positive = scroll down). */
|
|
12
|
+
readonly deltaY: number;
|
|
13
|
+
/** Columns to scroll (positive = scroll right; currently informational). */
|
|
14
|
+
readonly deltaX: number;
|
|
15
|
+
constructor(col: number, row: number, deltaY: number, deltaX?: number, init?: PointerEventInit);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=wheel-event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wheel-event.d.ts","sourceRoot":"","sources":["../../../../src/ink/events/wheel-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAExE;;;;;;;GAOG;AACH,qBAAa,UAAW,SAAQ,YAAY;IAC1C,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAGrB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,SAAI,EACV,IAAI,CAAC,EAAE,gBAAgB;CAM1B"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PointerEvent } from './pointer-event.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mouse wheel event dispatched to the ScrollBox under the pointer.
|
|
4
|
+
*
|
|
5
|
+
* `deltaY`/`deltaX` are in terminal rows/columns per wheel notch (positive =
|
|
6
|
+
* content moves up / right, i.e. scroll down / scroll right), matching
|
|
7
|
+
* ScrollBox.scrollBy's sign convention. Coordinates identify where the wheel
|
|
8
|
+
* event occurred so routing can hit-test the deepest scroll container.
|
|
9
|
+
*/
|
|
10
|
+
export class WheelEvent extends PointerEvent {
|
|
11
|
+
/** Rows to scroll (positive = scroll down). */
|
|
12
|
+
deltaY;
|
|
13
|
+
/** Columns to scroll (positive = scroll right; currently informational). */
|
|
14
|
+
deltaX;
|
|
15
|
+
constructor(col, row, deltaY, deltaX = 0, init) {
|
|
16
|
+
super('wheel', col, row, { ...init, action: 'move' });
|
|
17
|
+
this.deltaY = deltaY;
|
|
18
|
+
this.deltaX = deltaX;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type FrameCause = 'react-commit' | 'animation' | 'scroll' | 'scroll-drain' | 'measure' | 'resize' | 'reanchor' | 'immediate';
|
|
2
|
+
export declare const GEOMETRY_TRACE_ENABLED: boolean;
|
|
3
|
+
export type ScrollGeometryNote = {
|
|
4
|
+
sticky: boolean;
|
|
5
|
+
shrunk: boolean;
|
|
6
|
+
grew: boolean;
|
|
7
|
+
atBottom: boolean;
|
|
8
|
+
/** scrollTop captured before at-bottom follow (the user's logical view). */
|
|
9
|
+
scrollTopBeforeFollow: number;
|
|
10
|
+
/** scrollTop after pendingScrollDelta drain, before clamps. */
|
|
11
|
+
cur: number;
|
|
12
|
+
/** scrollTop written back to the DOM node (post follow/drain, pre virtual clamp). */
|
|
13
|
+
scrollTop: number;
|
|
14
|
+
/** What actually gets painted this frame (post cMin/cMax virtual clamp). */
|
|
15
|
+
renderScrollTop: number;
|
|
16
|
+
scrollHeight: number;
|
|
17
|
+
prevScrollHeight: number;
|
|
18
|
+
innerHeight: number;
|
|
19
|
+
maxScroll: number;
|
|
20
|
+
prevMaxScroll: number;
|
|
21
|
+
clampMin: number | null;
|
|
22
|
+
clampMax: number | null;
|
|
23
|
+
};
|
|
24
|
+
export type ListGeometryNote = {
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
topPad: number;
|
|
28
|
+
bottomPad: number;
|
|
29
|
+
/** Estimated total content height from cached row heights. */
|
|
30
|
+
total: number;
|
|
31
|
+
base: number;
|
|
32
|
+
sticky: boolean;
|
|
33
|
+
pending: number;
|
|
34
|
+
viewport: number;
|
|
35
|
+
termRows: number;
|
|
36
|
+
columns: number;
|
|
37
|
+
rowCount: number;
|
|
38
|
+
};
|
|
39
|
+
/** Tag the origin of the next painted frame. First note in the window wins. */
|
|
40
|
+
export declare function noteFrameCause(cause: FrameCause): void;
|
|
41
|
+
/** Renderer-side note, one per ScrollBox encountered during the paint. */
|
|
42
|
+
export declare function noteScrollGeometry(note: ScrollGeometryNote): void;
|
|
43
|
+
/** React-side note from MessageList's virtualization pass (latest wins). */
|
|
44
|
+
export declare function noteListGeometry(note: ListGeometryNote): void;
|
|
45
|
+
/** Scalar forensics (PromptInput content rows, …). Latest wins. */
|
|
46
|
+
export declare function noteAuxNumber(key: string, value: number): void;
|
|
47
|
+
/** Paint begins: just tag the frame id — inter-frame notes belong to it. */
|
|
48
|
+
export declare function beginGeometryFrame(frame: number): void;
|
|
49
|
+
/** Paint ends: flush one JSON line and reset the inter-frame window. */
|
|
50
|
+
export declare function endGeometryFrame(durationMs: number): void;
|
|
51
|
+
//# sourceMappingURL=geometry-trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry-trace.d.ts","sourceRoot":"","sources":["../../../src/ink/geometry-trace.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,WAAW,GACX,QAAQ,GACR,cAAc,GACd,SAAS,GACT,QAAQ,GACR,UAAU,GACV,WAAW,CAAA;AAEf,eAAO,MAAM,sBAAsB,SACQ,CAAA;AAI3C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,4EAA4E;IAC5E,qBAAqB,EAAE,MAAM,CAAA;IAC7B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAA;IACX,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAkBD,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAGtD;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAGjE;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAG7D;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAG9D;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGtD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAmBzD"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geometry forensics — per-frame scroll-geometry trace (issues #421/#433).
|
|
3
|
+
*
|
|
4
|
+
* `DSH_TUI_GEOMETRY_TRACE=<file>` appends one JSON line per painted frame:
|
|
5
|
+
* the frame's trigger cause, every ScrollBox's resolved geometry (scrollTop
|
|
6
|
+
* before/after follow + drain + virtual clamp, scrollHeight/maxScroll
|
|
7
|
+
* prev/cur, sticky/shrunk/grew flags, clamp bounds), the MessageList
|
|
8
|
+
* virtualization window, and aux counters (e.g. PromptInput content rows).
|
|
9
|
+
*
|
|
10
|
+
* Zero overhead when the env is unset: every entry point returns on a
|
|
11
|
+
* module-const boolean, so the disabled path is a single predictable branch.
|
|
12
|
+
*
|
|
13
|
+
* Ordering contract: causes and React-side notes (list geometry, aux) land
|
|
14
|
+
* BETWEEN frames — at event/commit time, before the throttled paint. The
|
|
15
|
+
* renderer adds scroll geometry during the paint; endGeometryFrame flushes
|
|
16
|
+
* and resets. First-noted cause wins (the origin of the frame chain); no
|
|
17
|
+
* note at paint time means a plain reconciler commit.
|
|
18
|
+
*/
|
|
19
|
+
import { appendFileSync } from 'node:fs';
|
|
20
|
+
export const GEOMETRY_TRACE_ENABLED = process.env.DSH_TUI_GEOMETRY_TRACE !== undefined &&
|
|
21
|
+
process.env.DSH_TUI_GEOMETRY_TRACE !== '';
|
|
22
|
+
const TRACE_PATH = process.env.DSH_TUI_GEOMETRY_TRACE ?? '';
|
|
23
|
+
let state = {
|
|
24
|
+
frame: -1,
|
|
25
|
+
cause: null,
|
|
26
|
+
scroll: [],
|
|
27
|
+
list: null,
|
|
28
|
+
aux: {}
|
|
29
|
+
};
|
|
30
|
+
/** Tag the origin of the next painted frame. First note in the window wins. */
|
|
31
|
+
export function noteFrameCause(cause) {
|
|
32
|
+
if (!GEOMETRY_TRACE_ENABLED)
|
|
33
|
+
return;
|
|
34
|
+
if (state.cause === null)
|
|
35
|
+
state.cause = cause;
|
|
36
|
+
}
|
|
37
|
+
/** Renderer-side note, one per ScrollBox encountered during the paint. */
|
|
38
|
+
export function noteScrollGeometry(note) {
|
|
39
|
+
if (!GEOMETRY_TRACE_ENABLED)
|
|
40
|
+
return;
|
|
41
|
+
state.scroll.push(note);
|
|
42
|
+
}
|
|
43
|
+
/** React-side note from MessageList's virtualization pass (latest wins). */
|
|
44
|
+
export function noteListGeometry(note) {
|
|
45
|
+
if (!GEOMETRY_TRACE_ENABLED)
|
|
46
|
+
return;
|
|
47
|
+
state.list = note;
|
|
48
|
+
}
|
|
49
|
+
/** Scalar forensics (PromptInput content rows, …). Latest wins. */
|
|
50
|
+
export function noteAuxNumber(key, value) {
|
|
51
|
+
if (!GEOMETRY_TRACE_ENABLED)
|
|
52
|
+
return;
|
|
53
|
+
state.aux[key] = value;
|
|
54
|
+
}
|
|
55
|
+
/** Paint begins: just tag the frame id — inter-frame notes belong to it. */
|
|
56
|
+
export function beginGeometryFrame(frame) {
|
|
57
|
+
if (!GEOMETRY_TRACE_ENABLED)
|
|
58
|
+
return;
|
|
59
|
+
state.frame = frame;
|
|
60
|
+
}
|
|
61
|
+
/** Paint ends: flush one JSON line and reset the inter-frame window. */
|
|
62
|
+
export function endGeometryFrame(durationMs) {
|
|
63
|
+
if (!GEOMETRY_TRACE_ENABLED)
|
|
64
|
+
return;
|
|
65
|
+
if (state.frame >= 0) {
|
|
66
|
+
const line = JSON.stringify({
|
|
67
|
+
t: new Date().toISOString(),
|
|
68
|
+
frame: state.frame,
|
|
69
|
+
cause: state.cause ?? 'react-commit',
|
|
70
|
+
ms: Math.round(durationMs * 10) / 10,
|
|
71
|
+
scroll: state.scroll,
|
|
72
|
+
list: state.list,
|
|
73
|
+
aux: state.aux
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
appendFileSync(TRACE_PATH, line + '\n');
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Unwritable path etc. — forensics must never break rendering.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
state = { frame: -1, cause: null, scroll: [], list: null, aux: {} };
|
|
83
|
+
}
|
|
@@ -16,18 +16,69 @@ import type { DOMElement } from './dom.js';
|
|
|
16
16
|
* @returns the deepest element whose rect contains (col, row), or null.
|
|
17
17
|
*/
|
|
18
18
|
export declare function hitTest(node: DOMElement, col: number, row: number): DOMElement | null;
|
|
19
|
+
/**
|
|
20
|
+
* hitTest, but overlay-aware: absolute-positioned nodes can paint OUTSIDE
|
|
21
|
+
* every ancestor's rect (OverlayAbove's `bottom:'100%'` pickers float over
|
|
22
|
+
* the transcript), so the plain containment recursion never reaches them —
|
|
23
|
+
* a click on the picker lands in the ScrollBox's rows instead. Check the
|
|
24
|
+
* frame's absolute hit list FIRST, in reverse paint order (later = visually
|
|
25
|
+
* on top), then fall back to the in-flow tree.
|
|
26
|
+
* @param root - the tree root for the in-flow fallback.
|
|
27
|
+
* @param col - the screen column to test.
|
|
28
|
+
* @param row - the screen row to test.
|
|
29
|
+
* @returns the deepest element at (col, row), or null.
|
|
30
|
+
*/
|
|
31
|
+
export declare function hitTestWithOverlays(root: DOMElement, col: number, row: number): DOMElement | null;
|
|
19
32
|
/**
|
|
20
33
|
* Hit-test the root at (col, row) and bubble a ClickEvent from the deepest
|
|
21
34
|
* containing node up through parentNode. Only nodes with an onClick handler
|
|
22
35
|
* fire. Stops when a handler calls stopImmediatePropagation(). Returns
|
|
23
36
|
* true if at least one onClick handler fired.
|
|
37
|
+
*
|
|
38
|
+
* Each handler call is isolated: a throwing handler is logged and the
|
|
39
|
+
* bubbling continues, so one broken onClick cannot swallow the click from
|
|
40
|
+
* its ancestors or the rest of the input batch.
|
|
41
|
+
*
|
|
24
42
|
* @param root - the tree root to hit-test.
|
|
25
43
|
* @param col - the screen column of the click.
|
|
26
44
|
* @param row - the screen row of the click.
|
|
27
45
|
* @param cellIsBlank - whether the clicked cell is blank, reported on the event.
|
|
46
|
+
* @param button - raw SGR release byte (carries shift/alt/ctrl modifier bits).
|
|
28
47
|
* @returns true when at least one onClick handler fired.
|
|
29
48
|
*/
|
|
30
|
-
export declare function dispatchClick(root: DOMElement, col: number, row: number, cellIsBlank?: boolean): boolean;
|
|
49
|
+
export declare function dispatchClick(root: DOMElement, col: number, row: number, cellIsBlank?: boolean, button?: number): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Fire onMouseLeave on every tracked hover node, then empty the set.
|
|
52
|
+
*
|
|
53
|
+
* The pointer-state resets (alt-screen swap, terminal resize) drop the hover
|
|
54
|
+
* set because the geometry it was computed against is gone. But the React
|
|
55
|
+
* components behind those nodes still hold their `hovered=true` state: a bare
|
|
56
|
+
* `set.clear()` never tells them the pointer "left", so every row crossed
|
|
57
|
+
* before the reset keeps its highlight forever — the next motion event only
|
|
58
|
+
* fires enter on the new row (the old nodes are no longer in the set, so no
|
|
59
|
+
* diff ever produces their leave). Firing leave first is what makes the
|
|
60
|
+
* reset invisible to the rows.
|
|
61
|
+
*
|
|
62
|
+
* @param hovered - the tracked hover set; emptied in place.
|
|
63
|
+
* @param col - pointer column for the synthetic leave events (-1 = unknown).
|
|
64
|
+
* @param row - pointer row for the synthetic leave events.
|
|
65
|
+
*/
|
|
66
|
+
export declare function clearHovered(hovered: Set<DOMElement>, col?: number, row?: number): void;
|
|
67
|
+
/**
|
|
68
|
+
* Route a wheel event to the ScrollBox (or any onWheel handler) under the
|
|
69
|
+
* pointer. Dispatches through the shared Dispatcher at continuous priority
|
|
70
|
+
* so the event bubbles from the deepest hit node and React schedules any
|
|
71
|
+
* state updates at the right lane.
|
|
72
|
+
*
|
|
73
|
+
* @param root - the tree root to hit-test.
|
|
74
|
+
* @param col - the screen column of the wheel event.
|
|
75
|
+
* @param row - the screen row of the wheel event.
|
|
76
|
+
* @param deltaY - rows to scroll (positive = scroll down).
|
|
77
|
+
* @param deltaX - columns to scroll (positive = right; informational).
|
|
78
|
+
* @param button - raw SGR byte (carries modifier bits).
|
|
79
|
+
* @returns true when an onWheel handler existed under the pointer.
|
|
80
|
+
*/
|
|
81
|
+
export declare function dispatchWheel(root: DOMElement, col: number, row: number, deltaY: number, deltaX?: number, button?: number): boolean;
|
|
31
82
|
/**
|
|
32
83
|
* Fire onMouseEnter/onMouseLeave as the pointer moves. Like DOM
|
|
33
84
|
* mouseenter/mouseleave: does NOT bubble — moving between children does
|
|
@@ -35,6 +86,10 @@ export declare function dispatchClick(root: DOMElement, col: number, row: number
|
|
|
35
86
|
* ancestor with a hover handler; diffs against the previous hovered set;
|
|
36
87
|
* fires leave on the nodes exited, enter on the nodes entered.
|
|
37
88
|
*
|
|
89
|
+
* Handlers receive a PointerEvent ('hover') with the pointer position;
|
|
90
|
+
* existing `() => void` handlers simply ignore the argument. Each call is
|
|
91
|
+
* isolated so one throwing handler cannot break the rest of the diff.
|
|
92
|
+
*
|
|
38
93
|
* Mutates `hovered` in place so the caller (App instance) can hold it
|
|
39
94
|
* across calls. Clears the set when the hit is null (cursor moved into a
|
|
40
95
|
* non-rendered gap or off the root rect).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hit-test.d.ts","sourceRoot":"","sources":["../../../src/ink/hit-test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"hit-test.d.ts","sourceRoot":"","sources":["../../../src/ink/hit-test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAU1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,UAAU,GAAG,IAAI,CAmBnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,UAAU,GAAG,IAAI,CAkBnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,WAAW,UAAQ,EACnB,MAAM,SAAI,GACT,OAAO,CA2CT;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,EACxB,GAAG,SAAK,EACR,GAAG,SAAK,GACP,IAAI,CAiBN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,SAAI,EACV,MAAM,SAAI,GACT,OAAO,CAcT;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,GACvB,IAAI,CAwCN"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ClickEvent } from './events/click-event.js';
|
|
2
|
+
import { PointerEvent } from './events/pointer-event.js';
|
|
3
|
+
import { WheelEvent } from './events/wheel-event.js';
|
|
4
|
+
import { logError } from '../utils/log.js';
|
|
2
5
|
import { nodeCache } from './node-cache.js';
|
|
6
|
+
import { getAbsoluteHitList } from './render-node-to-output.js';
|
|
7
|
+
import { dispatcher } from './reconciler.js';
|
|
3
8
|
/**
|
|
4
9
|
* Find the deepest DOM element whose rendered rect contains (col, row).
|
|
5
10
|
*
|
|
@@ -37,19 +42,55 @@ export function hitTest(node, col, row) {
|
|
|
37
42
|
}
|
|
38
43
|
return node;
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* hitTest, but overlay-aware: absolute-positioned nodes can paint OUTSIDE
|
|
47
|
+
* every ancestor's rect (OverlayAbove's `bottom:'100%'` pickers float over
|
|
48
|
+
* the transcript), so the plain containment recursion never reaches them —
|
|
49
|
+
* a click on the picker lands in the ScrollBox's rows instead. Check the
|
|
50
|
+
* frame's absolute hit list FIRST, in reverse paint order (later = visually
|
|
51
|
+
* on top), then fall back to the in-flow tree.
|
|
52
|
+
* @param root - the tree root for the in-flow fallback.
|
|
53
|
+
* @param col - the screen column to test.
|
|
54
|
+
* @param row - the screen row to test.
|
|
55
|
+
* @returns the deepest element at (col, row), or null.
|
|
56
|
+
*/
|
|
57
|
+
export function hitTestWithOverlays(root, col, row) {
|
|
58
|
+
const overlays = getAbsoluteHitList();
|
|
59
|
+
for (let i = overlays.length - 1; i >= 0; i--) {
|
|
60
|
+
const { node, rect } = overlays[i];
|
|
61
|
+
if (col >= rect.x &&
|
|
62
|
+
col < rect.x + rect.width &&
|
|
63
|
+
row >= rect.y &&
|
|
64
|
+
row < rect.y + rect.height) {
|
|
65
|
+
// hitTest re-checks containment via nodeCache and descends normally;
|
|
66
|
+
// nested absolutes deeper inside are themselves in the list and were
|
|
67
|
+
// already offered (later paint order) above.
|
|
68
|
+
const hit = hitTest(node, col, row);
|
|
69
|
+
if (hit)
|
|
70
|
+
return hit;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return hitTest(root, col, row);
|
|
74
|
+
}
|
|
40
75
|
/**
|
|
41
76
|
* Hit-test the root at (col, row) and bubble a ClickEvent from the deepest
|
|
42
77
|
* containing node up through parentNode. Only nodes with an onClick handler
|
|
43
78
|
* fire. Stops when a handler calls stopImmediatePropagation(). Returns
|
|
44
79
|
* true if at least one onClick handler fired.
|
|
80
|
+
*
|
|
81
|
+
* Each handler call is isolated: a throwing handler is logged and the
|
|
82
|
+
* bubbling continues, so one broken onClick cannot swallow the click from
|
|
83
|
+
* its ancestors or the rest of the input batch.
|
|
84
|
+
*
|
|
45
85
|
* @param root - the tree root to hit-test.
|
|
46
86
|
* @param col - the screen column of the click.
|
|
47
87
|
* @param row - the screen row of the click.
|
|
48
88
|
* @param cellIsBlank - whether the clicked cell is blank, reported on the event.
|
|
89
|
+
* @param button - raw SGR release byte (carries shift/alt/ctrl modifier bits).
|
|
49
90
|
* @returns true when at least one onClick handler fired.
|
|
50
91
|
*/
|
|
51
|
-
export function dispatchClick(root, col, row, cellIsBlank = false) {
|
|
52
|
-
let target =
|
|
92
|
+
export function dispatchClick(root, col, row, cellIsBlank = false, button = 0) {
|
|
93
|
+
let target = hitTestWithOverlays(root, col, row) ?? undefined;
|
|
53
94
|
if (!target)
|
|
54
95
|
return false;
|
|
55
96
|
// Click-to-focus: find the closest focusable ancestor and focus it.
|
|
@@ -64,7 +105,7 @@ export function dispatchClick(root, col, row, cellIsBlank = false) {
|
|
|
64
105
|
focusTarget = focusTarget.parentNode;
|
|
65
106
|
}
|
|
66
107
|
}
|
|
67
|
-
const event = new ClickEvent(col, row, cellIsBlank);
|
|
108
|
+
const event = new ClickEvent(col, row, cellIsBlank, { button });
|
|
68
109
|
let handled = false;
|
|
69
110
|
while (target) {
|
|
70
111
|
const handler = target._eventHandlers?.onClick;
|
|
@@ -75,7 +116,16 @@ export function dispatchClick(root, col, row, cellIsBlank = false) {
|
|
|
75
116
|
event.localCol = col - rect.x;
|
|
76
117
|
event.localRow = row - rect.y;
|
|
77
118
|
}
|
|
78
|
-
|
|
119
|
+
else {
|
|
120
|
+
event.localCol = 0;
|
|
121
|
+
event.localRow = 0;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
handler(event);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
logError(error);
|
|
128
|
+
}
|
|
79
129
|
if (event.didStopImmediatePropagation())
|
|
80
130
|
return true;
|
|
81
131
|
}
|
|
@@ -83,6 +133,73 @@ export function dispatchClick(root, col, row, cellIsBlank = false) {
|
|
|
83
133
|
}
|
|
84
134
|
return handled;
|
|
85
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Fire onMouseLeave on every tracked hover node, then empty the set.
|
|
138
|
+
*
|
|
139
|
+
* The pointer-state resets (alt-screen swap, terminal resize) drop the hover
|
|
140
|
+
* set because the geometry it was computed against is gone. But the React
|
|
141
|
+
* components behind those nodes still hold their `hovered=true` state: a bare
|
|
142
|
+
* `set.clear()` never tells them the pointer "left", so every row crossed
|
|
143
|
+
* before the reset keeps its highlight forever — the next motion event only
|
|
144
|
+
* fires enter on the new row (the old nodes are no longer in the set, so no
|
|
145
|
+
* diff ever produces their leave). Firing leave first is what makes the
|
|
146
|
+
* reset invisible to the rows.
|
|
147
|
+
*
|
|
148
|
+
* @param hovered - the tracked hover set; emptied in place.
|
|
149
|
+
* @param col - pointer column for the synthetic leave events (-1 = unknown).
|
|
150
|
+
* @param row - pointer row for the synthetic leave events.
|
|
151
|
+
*/
|
|
152
|
+
export function clearHovered(hovered, col = -1, row = -1) {
|
|
153
|
+
for (const node of hovered) {
|
|
154
|
+
hovered.delete(node);
|
|
155
|
+
// Detached nodes (screen already swapped out from under us) can no
|
|
156
|
+
// longer re-render — skip them; their component state dies with the
|
|
157
|
+
// subtree anyway.
|
|
158
|
+
if (!node.parentNode)
|
|
159
|
+
continue;
|
|
160
|
+
const handler = node._eventHandlers
|
|
161
|
+
?.onMouseLeave;
|
|
162
|
+
if (handler) {
|
|
163
|
+
try {
|
|
164
|
+
handler(new PointerEvent('hover', col, row, { action: 'move' }));
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
logError(error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Route a wheel event to the ScrollBox (or any onWheel handler) under the
|
|
174
|
+
* pointer. Dispatches through the shared Dispatcher at continuous priority
|
|
175
|
+
* so the event bubbles from the deepest hit node and React schedules any
|
|
176
|
+
* state updates at the right lane.
|
|
177
|
+
*
|
|
178
|
+
* @param root - the tree root to hit-test.
|
|
179
|
+
* @param col - the screen column of the wheel event.
|
|
180
|
+
* @param row - the screen row of the wheel event.
|
|
181
|
+
* @param deltaY - rows to scroll (positive = scroll down).
|
|
182
|
+
* @param deltaX - columns to scroll (positive = right; informational).
|
|
183
|
+
* @param button - raw SGR byte (carries modifier bits).
|
|
184
|
+
* @returns true when an onWheel handler existed under the pointer.
|
|
185
|
+
*/
|
|
186
|
+
export function dispatchWheel(root, col, row, deltaY, deltaX = 0, button = 0) {
|
|
187
|
+
const target = hitTestWithOverlays(root, col, row);
|
|
188
|
+
if (!target)
|
|
189
|
+
return false;
|
|
190
|
+
// Does any ancestor (target inclusive) carry an onWheel handler?
|
|
191
|
+
let node = target;
|
|
192
|
+
let hasHandler = false;
|
|
193
|
+
while (node && !hasHandler) {
|
|
194
|
+
hasHandler = Boolean(node._eventHandlers?.onWheel);
|
|
195
|
+
node = node.parentNode;
|
|
196
|
+
}
|
|
197
|
+
if (!hasHandler)
|
|
198
|
+
return false;
|
|
199
|
+
const event = new WheelEvent(col, row, deltaY, deltaX, { button });
|
|
200
|
+
dispatcher.dispatchContinuous(target, event);
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
86
203
|
/**
|
|
87
204
|
* Fire onMouseEnter/onMouseLeave as the pointer moves. Like DOM
|
|
88
205
|
* mouseenter/mouseleave: does NOT bubble — moving between children does
|
|
@@ -90,6 +207,10 @@ export function dispatchClick(root, col, row, cellIsBlank = false) {
|
|
|
90
207
|
* ancestor with a hover handler; diffs against the previous hovered set;
|
|
91
208
|
* fires leave on the nodes exited, enter on the nodes entered.
|
|
92
209
|
*
|
|
210
|
+
* Handlers receive a PointerEvent ('hover') with the pointer position;
|
|
211
|
+
* existing `() => void` handlers simply ignore the argument. Each call is
|
|
212
|
+
* isolated so one throwing handler cannot break the rest of the diff.
|
|
213
|
+
*
|
|
93
214
|
* Mutates `hovered` in place so the caller (App instance) can hold it
|
|
94
215
|
* across calls. Clears the set when the hit is null (cursor moved into a
|
|
95
216
|
* non-rendered gap or off the root rect).
|
|
@@ -100,7 +221,7 @@ export function dispatchClick(root, col, row, cellIsBlank = false) {
|
|
|
100
221
|
*/
|
|
101
222
|
export function dispatchHover(root, col, row, hovered) {
|
|
102
223
|
const next = new Set();
|
|
103
|
-
let node =
|
|
224
|
+
let node = hitTestWithOverlays(root, col, row) ?? undefined;
|
|
104
225
|
while (node) {
|
|
105
226
|
const h = node._eventHandlers;
|
|
106
227
|
if (h?.onMouseEnter || h?.onMouseLeave)
|
|
@@ -112,15 +233,32 @@ export function dispatchHover(root, col, row, hovered) {
|
|
|
112
233
|
hovered.delete(old);
|
|
113
234
|
// Skip handlers on detached nodes (removed between mouse events)
|
|
114
235
|
if (old.parentNode) {
|
|
115
|
-
|
|
116
|
-
|
|
236
|
+
const handler = old._eventHandlers
|
|
237
|
+
?.onMouseLeave;
|
|
238
|
+
if (handler) {
|
|
239
|
+
try {
|
|
240
|
+
handler(new PointerEvent('hover', col, row, { action: 'move' }));
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
logError(error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
117
246
|
}
|
|
118
247
|
}
|
|
119
248
|
}
|
|
120
249
|
for (const n of next) {
|
|
121
250
|
if (!hovered.has(n)) {
|
|
122
251
|
hovered.add(n);
|
|
123
|
-
n._eventHandlers
|
|
252
|
+
const handler = n._eventHandlers
|
|
253
|
+
?.onMouseEnter;
|
|
254
|
+
if (handler) {
|
|
255
|
+
try {
|
|
256
|
+
handler(new PointerEvent('hover', col, row, { action: 'move' }));
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
logError(error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
124
262
|
}
|
|
125
263
|
}
|
|
126
264
|
}
|
package/lib/types/ink/ink.d.ts
CHANGED
|
@@ -99,6 +99,28 @@ export default class Ink {
|
|
|
99
99
|
reanchorViewport(): void;
|
|
100
100
|
/** Render synchronously and invalidate older trailing/drain callbacks. */
|
|
101
101
|
private renderNow;
|
|
102
|
+
/**
|
|
103
|
+
* Schedule the next scroll-drain frame — Grok Build's Presenter, ported.
|
|
104
|
+
* Grok keeps TWO cadence knobs (min_draw_ms for renders, scroll_ms for
|
|
105
|
+
* scroll); collapsing both to FRAME_INTERVAL_MS made big flicks steppy
|
|
106
|
+
* (the proportional step, 75%-of-remaining, × 4× the interval = chunky
|
|
107
|
+
* ramp and ~4× longer settle), so the drain keeps its own fast cadence:
|
|
108
|
+
*
|
|
109
|
+
* - CADENCE: quarter interval (~250fps). Drain frames are cheap
|
|
110
|
+
* (DECSTBM + ~10 patches); scroll throughput is unchanged.
|
|
111
|
+
* - IN-FLIGHT GATE: while stdout still holds unflushed bytes above
|
|
112
|
+
* PTY_BACKLOG_BYTES (slow ConPTY round trip, ssh link), queue nothing
|
|
113
|
+
* further — re-probe at quarter interval instead. Grok's equivalent
|
|
114
|
+
* (in_flight_target + writer ack) exists to keep latency bounded
|
|
115
|
+
* under exactly this backpressure; without it each stacked frame adds
|
|
116
|
+
* its full render+write to the input→paint latency, which reads as
|
|
117
|
+
* sticky, laggy scrolling on Windows terminals.
|
|
118
|
+
*
|
|
119
|
+
* The gate holds only DRAIN frames; React-driven renders (keystrokes,
|
|
120
|
+
* streaming) still render via the normal throttle — user-visible updates
|
|
121
|
+
* must never wait behind scroll output.
|
|
122
|
+
*/
|
|
123
|
+
private scheduleDrain;
|
|
102
124
|
onRender(): void;
|
|
103
125
|
pause(): void;
|
|
104
126
|
resume(): void;
|
|
@@ -193,6 +215,31 @@ export default class Ink {
|
|
|
193
215
|
detachStdinForHandoff(): void;
|
|
194
216
|
/** @see drainStdin */
|
|
195
217
|
drainStdin(): void;
|
|
218
|
+
/**
|
|
219
|
+
* Self-heal after a terminal-side mode reset. Windows conpty drops DEC
|
|
220
|
+
* private modes on DPI changes, window moves between monitors and renderer
|
|
221
|
+
* restarts — the user sees the app "exit fullscreen" with a dead mouse
|
|
222
|
+
* while altScreenActive still claims we are in alt. Two layers:
|
|
223
|
+
*
|
|
224
|
+
* 1. Blind, idempotent mouse-tracking re-assert (covers the mode reset
|
|
225
|
+
* without a round trip; ~30 bytes).
|
|
226
|
+
* 2. DECRQM probe of mode 1049. Re-entry (destructive: ERASE) happens
|
|
227
|
+
* ONLY on a positive "reset" answer, so iTerm2's
|
|
228
|
+
* enter-clears-when-already-in-alt quirk can never fire on a healthy
|
|
229
|
+
* screen, and terminals that ignore DECRQM stay inert.
|
|
230
|
+
*
|
|
231
|
+
* Called from every interaction dispatch (click/hover/wheel/key) plus the
|
|
232
|
+
* focus/resize/stdin-gap triggers. The 250ms throttle keeps the round
|
|
233
|
+
* trip bounded while active use is going on — a dropped 1049 heals on
|
|
234
|
+
* the FIRST interaction after the drop instead of waiting for a focus
|
|
235
|
+
* event or a >5s idle gap (mouse motion keeps lastStdinTime fresh, so
|
|
236
|
+
* the gap path never fires during active use — the exact pattern that
|
|
237
|
+
* left the app broken until the user gave up).
|
|
238
|
+
*/
|
|
239
|
+
private lastHealthProbeAt;
|
|
240
|
+
probeAltScreenHealth: () => void;
|
|
241
|
+
/** Refocus = first observable moment after a conpty-side mode reset. */
|
|
242
|
+
handleTerminalFocusProbe: (focused: boolean) => void;
|
|
196
243
|
/**
|
|
197
244
|
* Re-enter alt-screen, clear, home, re-enable mouse tracking, and reset
|
|
198
245
|
* frame buffers so the next render repaints from scratch. Self-heal for
|
|
@@ -312,8 +359,18 @@ export default class Ink {
|
|
|
312
359
|
* Returns true if a DOM handler consumed the click. Gated on
|
|
313
360
|
* altScreenActive — clicks only make sense with a fixed viewport where
|
|
314
361
|
* nodeCache rects map 1:1 to terminal cells (no scrollback offset).
|
|
362
|
+
* The button byte is the raw SGR release code; its modifier bits land
|
|
363
|
+
* on ClickEvent.shift/alt/ctrl.
|
|
364
|
+
*/
|
|
365
|
+
dispatchClick(col: number, row: number, button?: number): boolean;
|
|
366
|
+
/**
|
|
367
|
+
* Route a wheel event to the ScrollBox (any onWheel handler) under the
|
|
368
|
+
* pointer. Returns true when a handler consumed it, so App can skip the
|
|
369
|
+
* legacy global wheel-key path and exactly one layer scrolls. Gated on
|
|
370
|
+
* altScreenActive like dispatchClick — without mouse tracking there are
|
|
371
|
+
* no wheel coordinates to route by.
|
|
315
372
|
*/
|
|
316
|
-
|
|
373
|
+
dispatchWheelAt(col: number, row: number, deltaY: number, deltaX?: number, button?: number): boolean;
|
|
317
374
|
dispatchHover(col: number, row: number): void;
|
|
318
375
|
dispatchKeyboardEvent(parsedKey: ParsedKey): void;
|
|
319
376
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ink.d.ts","sourceRoot":"","sources":["../../../src/ink/ink.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAa9C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"ink.d.ts","sourceRoot":"","sources":["../../../src/ink/ink.tsx"],"names":[],"mappings":"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"}
|