@deepseek-harness-tui/dsh-tui 0.8.7 → 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 +29 -10
- 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 +26 -5
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/FileSuggestions.js +53 -16
- 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/LogoV2.d.ts +5 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +12 -3
- 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 +273 -50
- 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 +27 -6
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +432 -66
- package/lib/types/dsh-adapter/contract.d.ts +58 -18
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +124 -36
- 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 +10 -13
- 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 +194 -16
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +73 -10
- 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 +268 -52
- 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 +16 -3
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +144 -25
- package/lib/types/ink/selection.d.ts +29 -4
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +41 -5
- 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 +64 -10
- 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/fileSuggestions.d.ts +19 -0
- package/lib/types/utils/fileSuggestions.d.ts.map +1 -0
- package/lib/types/utils/fileSuggestions.js +40 -0
- package/lib/types/utils/mentions.d.ts.map +1 -1
- package/lib/types/utils/mentions.js +3 -1
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +1 -0
- package/package.json +69 -66
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline rail — the pure geometry model behind the fullscreen
|
|
3
|
+
* transcript's turn navigator (a port of Grok Build's timeline sidebar
|
|
4
|
+
* semantics onto the dsh Ink tree).
|
|
5
|
+
*
|
|
6
|
+
* The rail is a 2-column gutter REPLACING the classic scrollbar: one tick
|
|
7
|
+
* per user turn, where tick position encodes CONVERSATION ORDER, not
|
|
8
|
+
* scroll proportion. Geometry is computed into a single frozen structure
|
|
9
|
+
* consumed by both the renderer and mouse hit-testing, so what you see is
|
|
10
|
+
* always what you can click ("看得到但点不中" impossible by construction).
|
|
11
|
+
*
|
|
12
|
+
* Viewport semantics (all derived from the same turn-top list, mirroring
|
|
13
|
+
* grok-pager's ScrollbackState):
|
|
14
|
+
*
|
|
15
|
+
* - active: the LAST turn whose prompt top is at-or-above the viewport
|
|
16
|
+
* top (the turn whose content owns the top row — "the turn being
|
|
17
|
+
* read"). When pre-turn content (logo / loaded-context panel) owns the
|
|
18
|
+
* top, the FIRST turn stands in. Never the newest-turn clamp: its
|
|
19
|
+
* one-step-off-bottom highlight leap is exactly what this rule avoids.
|
|
20
|
+
* - up target: the last turn STRICTLY above the viewport top. From
|
|
21
|
+
* mid-turn it first aligns the current turn's own prompt, and it can
|
|
22
|
+
* never name a trailing turn no scroll could bring to the top
|
|
23
|
+
* (the stuck-▲ bug).
|
|
24
|
+
* - down target: the FIRST turn whose prompt top is strictly below the
|
|
25
|
+
* viewport top AND reachable (top ≤ maxScroll — the renderer clamps
|
|
26
|
+
* scrollTop to maxScroll, so a turn below it could never own the top
|
|
27
|
+
* row; naming it would make ▼ repeat itself forever).
|
|
28
|
+
*
|
|
29
|
+
* Not ported (deliberately): ratatui Buffer drawing and Rust state
|
|
30
|
+
* organization — algorithms only, per the reference report.
|
|
31
|
+
*/
|
|
32
|
+
/** Columns the rail reserves (widest tick glyph). */
|
|
33
|
+
export declare const RAIL_WIDTH = 2;
|
|
34
|
+
/** Terminals narrower than this hide the rail (transcript needs the cols). */
|
|
35
|
+
export declare const RAIL_MIN_TERMINAL_WIDTH = 60;
|
|
36
|
+
/** Fewer turns than this and the rail is noise. */
|
|
37
|
+
export declare const RAIL_MIN_TURNS = 2;
|
|
38
|
+
/** Stored preview cap (chars). Render paths re-truncate to card width. */
|
|
39
|
+
export declare const PREVIEW_MAX_CHARS = 120;
|
|
40
|
+
/** One user turn, as the rail sees it. Identity is the row id — never an
|
|
41
|
+
* array index (deletion / rewind / loadOlder would mis-target it). */
|
|
42
|
+
export interface TimelineTurn {
|
|
43
|
+
/** ChatRow id of the user message (jump/preview target). */
|
|
44
|
+
id: number;
|
|
45
|
+
/** Content-space top of the prompt TEXT (scrollTo(target) pins it to
|
|
46
|
+
* the viewport top). */
|
|
47
|
+
top: number;
|
|
48
|
+
/** First non-empty prompt line, char-capped (see clipPreview). */
|
|
49
|
+
preview: string;
|
|
50
|
+
/**
|
|
51
|
+
* True while the row sits BEFORE the fold window (older than the most
|
|
52
|
+
* recent MAX_RENDERED_ROWS rows): its top is unknown (unmounted since
|
|
53
|
+
* before the fold; `top` carries −1) and clicking the tick must first
|
|
54
|
+
* reveal the folded history (Chat: showAll + force-mount + seek) rather
|
|
55
|
+
* than scrollTo(−1). Rendering a tick for it is still correct — the
|
|
56
|
+
* turn EXISTS and is navigable, it is just folded away right now
|
|
57
|
+
* (drawing only window turns read as "the rail covers just 2-3 nodes"
|
|
58
|
+
* on tool-heavy sessions where 300 rows ≈ a handful of turns).
|
|
59
|
+
*/
|
|
60
|
+
folded?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/** The per-commit snapshot MessageList reports and the rail + sticky
|
|
63
|
+
* header consume — one source so the two can never disagree. */
|
|
64
|
+
export interface TimelineSnapshot {
|
|
65
|
+
/** Turns in conversation order. */
|
|
66
|
+
turns: ReadonlyArray<TimelineTurn>;
|
|
67
|
+
/** Turn owning the viewport top row; first turn while pre-turn content
|
|
68
|
+
* owns the top; null only when there are no turns. */
|
|
69
|
+
activeId: number | null;
|
|
70
|
+
/** ▲ target (strictly above the top), null at the first turn. */
|
|
71
|
+
upId: number | null;
|
|
72
|
+
/** ▼ target (below the top and reachable), null at the end. */
|
|
73
|
+
downId: number | null;
|
|
74
|
+
}
|
|
75
|
+
/** Per-render rail geometry: where the ticks and chevrons landed. */
|
|
76
|
+
export interface RailGeometry {
|
|
77
|
+
/** Turn-index window shown as ticks: [windowStart, windowEnd). */
|
|
78
|
+
windowStart: number;
|
|
79
|
+
windowEnd: number;
|
|
80
|
+
/** Row of the ▲ chevron (block-local, within the rail column). */
|
|
81
|
+
upRow: number;
|
|
82
|
+
/** Row of the first tick. */
|
|
83
|
+
tickTop: number;
|
|
84
|
+
/** Row of the ▼ chevron. */
|
|
85
|
+
downRow: number;
|
|
86
|
+
}
|
|
87
|
+
/** What a rail row is. */
|
|
88
|
+
export type RailHit = {
|
|
89
|
+
kind: 'up';
|
|
90
|
+
} | {
|
|
91
|
+
kind: 'down';
|
|
92
|
+
} | {
|
|
93
|
+
kind: 'tick';
|
|
94
|
+
index: number;
|
|
95
|
+
};
|
|
96
|
+
/** Single eligibility policy: setting, terminal width, turn count, and
|
|
97
|
+
* geometric feasibility in one place. */
|
|
98
|
+
export declare function railEligible(opts: {
|
|
99
|
+
turnCount: number;
|
|
100
|
+
terminalWidth: number;
|
|
101
|
+
viewportRows: number;
|
|
102
|
+
/** False when the content fits the viewport (inline mode / fresh
|
|
103
|
+
* session): nothing to navigate, the terminal's own scrollback owns
|
|
104
|
+
* the wheel. */
|
|
105
|
+
scrollable: boolean;
|
|
106
|
+
}): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Compute rail geometry, or null when the rail must not render.
|
|
109
|
+
*
|
|
110
|
+
* Windowing: when turns outnumber tick rows (viewport − 2 chevrons), slide
|
|
111
|
+
* a window around the ACTIVE turn; at the bottom prefer the tail so the
|
|
112
|
+
* newest ticks stay visible — but never exclude the active turn, or no
|
|
113
|
+
* tick would highlight. The chevron+tick block is vertically centered in
|
|
114
|
+
* the rail column.
|
|
115
|
+
*/
|
|
116
|
+
export declare function computeRailGeometry(turnCount: number, viewportRows: number, activeIndex: number | null, atBottom: boolean): RailGeometry | null;
|
|
117
|
+
/**
|
|
118
|
+
* Which rail interaction a block-local row lands on. The whole rail width
|
|
119
|
+
* is the hit target (no pixel-hunting the glyph).
|
|
120
|
+
*/
|
|
121
|
+
export declare function railHit(geo: RailGeometry, row: number): RailHit | null;
|
|
122
|
+
/**
|
|
123
|
+
* First non-empty line, char-capped with a `…` marker. Bounded work: the
|
|
124
|
+
* scan stops at the first non-empty line and slices at the cap, so a huge
|
|
125
|
+
* one-line prompt costs O(cap), not O(line length). Char cap (not display
|
|
126
|
+
* width) on purpose — this bounds the stored snapshot; the hover card
|
|
127
|
+
* re-wraps to its own width (wrapPreviewLines).
|
|
128
|
+
*/
|
|
129
|
+
export declare function clipPreview(text: string, maxChars?: number): string;
|
|
130
|
+
/**
|
|
131
|
+
* Wrap a preview to at most 2 lines by DISPLAY width (CJK-aware — a
|
|
132
|
+
* terminal cell column budget, not a char count). The last line is
|
|
133
|
+
* ellipsized when content remains. Char-by-char accumulation keeps
|
|
134
|
+
* surrogate pairs and (best-effort) combining sequences intact by never
|
|
135
|
+
* splitting below a low surrogate.
|
|
136
|
+
*/
|
|
137
|
+
export declare function wrapPreviewLines(preview: string, maxWidth: number): string[];
|
|
138
|
+
//# sourceMappingURL=timeline-rail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline-rail.d.ts","sourceRoot":"","sources":["../../../src/ink/timeline-rail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,qDAAqD;AACrD,eAAO,MAAM,UAAU,IAAI,CAAA;AAE3B,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC,mDAAmD;AACnD,eAAO,MAAM,cAAc,IAAI,CAAA;AAE/B,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAEpC;uEACuE;AACvE,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAA;IACV;6BACyB;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;iEACiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;IAClC;2DACuD;IACvD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,iEAAiE;IACjE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,qEAAqE;AACrE,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,0BAA0B;AAC1B,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GACd;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnC;0CAC0C;AAC1C,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB;;qBAEiB;IACjB,UAAU,EAAE,OAAO,CAAA;CACpB,GAAG,OAAO,CAOV;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,OAAO,GAChB,YAAY,GAAG,IAAI,CAuBrB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUtE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAoB,GAAG,MAAM,CAkB9E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B5E"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline rail — the pure geometry model behind the fullscreen
|
|
3
|
+
* transcript's turn navigator (a port of Grok Build's timeline sidebar
|
|
4
|
+
* semantics onto the dsh Ink tree).
|
|
5
|
+
*
|
|
6
|
+
* The rail is a 2-column gutter REPLACING the classic scrollbar: one tick
|
|
7
|
+
* per user turn, where tick position encodes CONVERSATION ORDER, not
|
|
8
|
+
* scroll proportion. Geometry is computed into a single frozen structure
|
|
9
|
+
* consumed by both the renderer and mouse hit-testing, so what you see is
|
|
10
|
+
* always what you can click ("看得到但点不中" impossible by construction).
|
|
11
|
+
*
|
|
12
|
+
* Viewport semantics (all derived from the same turn-top list, mirroring
|
|
13
|
+
* grok-pager's ScrollbackState):
|
|
14
|
+
*
|
|
15
|
+
* - active: the LAST turn whose prompt top is at-or-above the viewport
|
|
16
|
+
* top (the turn whose content owns the top row — "the turn being
|
|
17
|
+
* read"). When pre-turn content (logo / loaded-context panel) owns the
|
|
18
|
+
* top, the FIRST turn stands in. Never the newest-turn clamp: its
|
|
19
|
+
* one-step-off-bottom highlight leap is exactly what this rule avoids.
|
|
20
|
+
* - up target: the last turn STRICTLY above the viewport top. From
|
|
21
|
+
* mid-turn it first aligns the current turn's own prompt, and it can
|
|
22
|
+
* never name a trailing turn no scroll could bring to the top
|
|
23
|
+
* (the stuck-▲ bug).
|
|
24
|
+
* - down target: the FIRST turn whose prompt top is strictly below the
|
|
25
|
+
* viewport top AND reachable (top ≤ maxScroll — the renderer clamps
|
|
26
|
+
* scrollTop to maxScroll, so a turn below it could never own the top
|
|
27
|
+
* row; naming it would make ▼ repeat itself forever).
|
|
28
|
+
*
|
|
29
|
+
* Not ported (deliberately): ratatui Buffer drawing and Rust state
|
|
30
|
+
* organization — algorithms only, per the reference report.
|
|
31
|
+
*/
|
|
32
|
+
import { stringWidth } from './stringWidth.js';
|
|
33
|
+
/** Columns the rail reserves (widest tick glyph). */
|
|
34
|
+
export const RAIL_WIDTH = 2;
|
|
35
|
+
/** Terminals narrower than this hide the rail (transcript needs the cols). */
|
|
36
|
+
export const RAIL_MIN_TERMINAL_WIDTH = 60;
|
|
37
|
+
/** Fewer turns than this and the rail is noise. */
|
|
38
|
+
export const RAIL_MIN_TURNS = 2;
|
|
39
|
+
/** Stored preview cap (chars). Render paths re-truncate to card width. */
|
|
40
|
+
export const PREVIEW_MAX_CHARS = 120;
|
|
41
|
+
/** Single eligibility policy: setting, terminal width, turn count, and
|
|
42
|
+
* geometric feasibility in one place. */
|
|
43
|
+
export function railEligible(opts) {
|
|
44
|
+
return (opts.scrollable &&
|
|
45
|
+
opts.turnCount >= RAIL_MIN_TURNS &&
|
|
46
|
+
opts.terminalWidth >= RAIL_MIN_TERMINAL_WIDTH &&
|
|
47
|
+
opts.viewportRows >= 3);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compute rail geometry, or null when the rail must not render.
|
|
51
|
+
*
|
|
52
|
+
* Windowing: when turns outnumber tick rows (viewport − 2 chevrons), slide
|
|
53
|
+
* a window around the ACTIVE turn; at the bottom prefer the tail so the
|
|
54
|
+
* newest ticks stay visible — but never exclude the active turn, or no
|
|
55
|
+
* tick would highlight. The chevron+tick block is vertically centered in
|
|
56
|
+
* the rail column.
|
|
57
|
+
*/
|
|
58
|
+
export function computeRailGeometry(turnCount, viewportRows, activeIndex, atBottom) {
|
|
59
|
+
if (turnCount < RAIL_MIN_TURNS)
|
|
60
|
+
return null;
|
|
61
|
+
const maxTicks = viewportRows - 2;
|
|
62
|
+
if (maxTicks < 1)
|
|
63
|
+
return null;
|
|
64
|
+
let start = 0;
|
|
65
|
+
if (turnCount > maxTicks) {
|
|
66
|
+
const tailStart = turnCount - maxTicks;
|
|
67
|
+
const anchor = activeIndex ?? turnCount - 1;
|
|
68
|
+
start = atBottom
|
|
69
|
+
? Math.min(anchor, tailStart)
|
|
70
|
+
: Math.min(Math.max(0, anchor - Math.floor(maxTicks / 2)), tailStart);
|
|
71
|
+
}
|
|
72
|
+
const shown = Math.min(turnCount, maxTicks);
|
|
73
|
+
// Vertically center the ▲ + ticks + ▼ stack, like the web rail.
|
|
74
|
+
const blockTop = Math.max(0, Math.floor((viewportRows - (shown + 2)) / 2));
|
|
75
|
+
return {
|
|
76
|
+
windowStart: start,
|
|
77
|
+
windowEnd: start + shown,
|
|
78
|
+
upRow: blockTop,
|
|
79
|
+
tickTop: blockTop + 1,
|
|
80
|
+
downRow: blockTop + 1 + shown,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Which rail interaction a block-local row lands on. The whole rail width
|
|
85
|
+
* is the hit target (no pixel-hunting the glyph).
|
|
86
|
+
*/
|
|
87
|
+
export function railHit(geo, row) {
|
|
88
|
+
if (row === geo.upRow)
|
|
89
|
+
return { kind: 'up' };
|
|
90
|
+
if (row === geo.downRow)
|
|
91
|
+
return { kind: 'down' };
|
|
92
|
+
if (row >= geo.tickTop) {
|
|
93
|
+
const rel = row - geo.tickTop;
|
|
94
|
+
if (rel < geo.windowEnd - geo.windowStart) {
|
|
95
|
+
return { kind: 'tick', index: geo.windowStart + rel };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* First non-empty line, char-capped with a `…` marker. Bounded work: the
|
|
102
|
+
* scan stops at the first non-empty line and slices at the cap, so a huge
|
|
103
|
+
* one-line prompt costs O(cap), not O(line length). Char cap (not display
|
|
104
|
+
* width) on purpose — this bounds the stored snapshot; the hover card
|
|
105
|
+
* re-wraps to its own width (wrapPreviewLines).
|
|
106
|
+
*/
|
|
107
|
+
export function clipPreview(text, maxChars = PREVIEW_MAX_CHARS) {
|
|
108
|
+
let line = '';
|
|
109
|
+
let rest = text;
|
|
110
|
+
while (rest.length > 0) {
|
|
111
|
+
const nl = rest.indexOf('\n');
|
|
112
|
+
const head = nl === -1 ? rest : rest.slice(0, nl);
|
|
113
|
+
rest = nl === -1 ? '' : rest.slice(nl + 1);
|
|
114
|
+
const trimmed = head.trim();
|
|
115
|
+
if (trimmed.length > 0) {
|
|
116
|
+
line = trimmed;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (line.length <= maxChars)
|
|
121
|
+
return line;
|
|
122
|
+
// Keep the cap INCLUDING the ellipsis marker, like grok's preview. Slice
|
|
123
|
+
// by CODE POINTS, not UTF-16 units — a surrogate pair split mid-pair
|
|
124
|
+
// would emit a lone surrogate to the terminal.
|
|
125
|
+
return `${[...line].slice(0, maxChars - 1).join('')}…`;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Wrap a preview to at most 2 lines by DISPLAY width (CJK-aware — a
|
|
129
|
+
* terminal cell column budget, not a char count). The last line is
|
|
130
|
+
* ellipsized when content remains. Char-by-char accumulation keeps
|
|
131
|
+
* surrogate pairs and (best-effort) combining sequences intact by never
|
|
132
|
+
* splitting below a low surrogate.
|
|
133
|
+
*/
|
|
134
|
+
export function wrapPreviewLines(preview, maxWidth) {
|
|
135
|
+
const widthOf = stringWidth;
|
|
136
|
+
const lines = [];
|
|
137
|
+
let current = '';
|
|
138
|
+
let currentW = 0;
|
|
139
|
+
for (const ch of preview) {
|
|
140
|
+
const w = widthOf(ch);
|
|
141
|
+
if (w <= 0) {
|
|
142
|
+
// Zero-width (combining marks, ZWJ): attach for free.
|
|
143
|
+
current += ch;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (currentW + w > maxWidth) {
|
|
147
|
+
if (lines.length === 1) {
|
|
148
|
+
// Second line full → ellipsize in place and stop.
|
|
149
|
+
return [lines[0], ellipsize(current, maxWidth)];
|
|
150
|
+
}
|
|
151
|
+
lines.push(current);
|
|
152
|
+
current = ch;
|
|
153
|
+
currentW = w;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
current += ch;
|
|
157
|
+
currentW += w;
|
|
158
|
+
}
|
|
159
|
+
if (current.length > 0 || lines.length === 0)
|
|
160
|
+
lines.push(current);
|
|
161
|
+
if (lines.length > 2) {
|
|
162
|
+
// Cannot happen (we stop after the second), kept for exhaustiveness.
|
|
163
|
+
return [lines[0], ellipsize(lines[1], maxWidth)];
|
|
164
|
+
}
|
|
165
|
+
return lines.slice(0, 2);
|
|
166
|
+
}
|
|
167
|
+
function ellipsize(line, maxWidth) {
|
|
168
|
+
// Make room for '…' (1 col) then hard-cut by display width.
|
|
169
|
+
let out = '';
|
|
170
|
+
let w = 0;
|
|
171
|
+
for (const ch of line) {
|
|
172
|
+
const cw = stringWidth(ch);
|
|
173
|
+
if (cw > 0 && w + cw > maxWidth - 1)
|
|
174
|
+
break;
|
|
175
|
+
out += ch;
|
|
176
|
+
w += cw;
|
|
177
|
+
}
|
|
178
|
+
return `${out}…`;
|
|
179
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap-text.d.ts","sourceRoot":"","sources":["../../../src/ink/wrap-text.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"wrap-text.d.ts","sourceRoot":"","sources":["../../../src/ink/wrap-text.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAoFzC;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,GAC3B,MAAM,CAoCR"}
|
|
@@ -2,6 +2,48 @@ import sliceAnsi from '../utils/sliceAnsi.js';
|
|
|
2
2
|
import { stringWidth } from './stringWidth.js';
|
|
3
3
|
import { wrapAnsi } from './wrapAnsi.js';
|
|
4
4
|
const ELLIPSIS = '…';
|
|
5
|
+
// --- cross-mount wrap cache ------------------------------------------------
|
|
6
|
+
// wrapText is pure: (text, maxWidth, wrapType) deterministically maps to one
|
|
7
|
+
// output string. Yet before this cache every consumer re-wrapped from
|
|
8
|
+
// scratch:
|
|
9
|
+
//
|
|
10
|
+
// - dom.ts measureTextNode seeds its per-NODE incremental cache on first
|
|
11
|
+
// measure; virtualization unmounts scrolled-away rows, so scrolling back
|
|
12
|
+
// re-mounts the row's Text nodes and re-runs the FULL wrap (wrap-ansi
|
|
13
|
+
// tokenizes + string-width-measures every word — 100-300ms single-frame
|
|
14
|
+
// yoga spikes when a row of large settled text scrolls in, the dominant
|
|
15
|
+
// long-session scroll stall);
|
|
16
|
+
// - render-node-to-output.ts wraps every VISIBLE text node on every full
|
|
17
|
+
// paint pass (no per-node cache at all there).
|
|
18
|
+
//
|
|
19
|
+
// A content-addressed LRU here makes both paths O(1) for immutable settled
|
|
20
|
+
// text (Markdown memo keeps content identity stable for the whole session).
|
|
21
|
+
// The key embeds the raw text, so V8's content-hash Map lookup matches even
|
|
22
|
+
// a rebuilt string. Budget mirrors line-width-cache: whole-cache clear on
|
|
23
|
+
// overflow (repopulates within a frame or two).
|
|
24
|
+
const WRAP_CACHE_MAX_ENTRIES = 2048;
|
|
25
|
+
const WRAP_CACHE_MAX_CHARS = 2_000_000;
|
|
26
|
+
const wrapCache = new Map();
|
|
27
|
+
let wrapCacheChars = 0;
|
|
28
|
+
/** Values below this are not worth a cache entry (key building costs about
|
|
29
|
+
* as much as the wrap itself for trivial single-word strings). */
|
|
30
|
+
const WRAP_CACHE_MIN_LENGTH = 24;
|
|
31
|
+
function cachedWrap(text, maxWidth, wrapType, compute) {
|
|
32
|
+
if (text.length < WRAP_CACHE_MIN_LENGTH)
|
|
33
|
+
return compute();
|
|
34
|
+
const key = `${maxWidth}\u0000${wrapType}\u0000${text}`;
|
|
35
|
+
const hit = wrapCache.get(key);
|
|
36
|
+
if (hit !== undefined)
|
|
37
|
+
return hit;
|
|
38
|
+
const result = compute();
|
|
39
|
+
if (wrapCache.size >= WRAP_CACHE_MAX_ENTRIES || wrapCacheChars + text.length > WRAP_CACHE_MAX_CHARS) {
|
|
40
|
+
wrapCache.clear();
|
|
41
|
+
wrapCacheChars = 0;
|
|
42
|
+
}
|
|
43
|
+
wrapCache.set(key, result);
|
|
44
|
+
wrapCacheChars += text.length;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
5
47
|
// sliceAnsi may include a boundary-spanning wide char (e.g. CJK at position
|
|
6
48
|
// end-1 with width 2 overshoots by 1). Retry with a tighter bound once.
|
|
7
49
|
function sliceFit(text, start, end) {
|
|
@@ -36,16 +78,16 @@ function truncate(text, columns, position) {
|
|
|
36
78
|
*/
|
|
37
79
|
export default function wrapText(text, maxWidth, wrapType) {
|
|
38
80
|
if (wrapType === 'wrap') {
|
|
39
|
-
return wrapAnsi(text, maxWidth, {
|
|
81
|
+
return cachedWrap(text, maxWidth, wrapType, () => wrapAnsi(text, maxWidth, {
|
|
40
82
|
trim: false,
|
|
41
83
|
hard: true,
|
|
42
|
-
});
|
|
84
|
+
}));
|
|
43
85
|
}
|
|
44
86
|
if (wrapType === 'wrap-trim') {
|
|
45
|
-
return wrapAnsi(text, maxWidth, {
|
|
87
|
+
return cachedWrap(text, maxWidth, wrapType, () => wrapAnsi(text, maxWidth, {
|
|
46
88
|
trim: true,
|
|
47
89
|
hard: true,
|
|
48
|
-
});
|
|
90
|
+
}));
|
|
49
91
|
}
|
|
50
92
|
if (wrapType.startsWith('truncate')) {
|
|
51
93
|
let position = 'end';
|
|
@@ -55,7 +97,7 @@ export default function wrapText(text, maxWidth, wrapType) {
|
|
|
55
97
|
if (wrapType === 'truncate-start') {
|
|
56
98
|
position = 'start';
|
|
57
99
|
}
|
|
58
|
-
return truncate(text, maxWidth, position);
|
|
100
|
+
return cachedWrap(text, maxWidth, wrapType, () => truncate(text, maxWidth, position));
|
|
59
101
|
}
|
|
60
102
|
return text;
|
|
61
103
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAqB,KAAK,OAAO,EAAsE,MAAM,2BAA2B,CAAA;AAC/I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAqB,KAAK,OAAO,EAAsE,MAAM,2BAA2B,CAAA;AAC/I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AA8J3D,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,QAAQ,EACR,UAAkB,EAClB,cAAc,EAAE,kBAAkB,GACnC,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,oEAAoE;IACpE,eAAe,CAAC,EAAE,cAAc,CAAA;IAChC,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,4BAusFA"}
|