cvc-tui 0.4.0 → 0.4.2

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.
Files changed (133) hide show
  1. package/dist/entry.js +71148 -61
  2. package/package.json +2 -2
  3. package/dist/app/completion.js +0 -102
  4. package/dist/app/createGatewayEventHandler.js +0 -508
  5. package/dist/app/createSlashHandler.js +0 -101
  6. package/dist/app/delegationStore.js +0 -51
  7. package/dist/app/gatewayContext.js +0 -17
  8. package/dist/app/historyStore.js +0 -123
  9. package/dist/app/inputBuffer.js +0 -120
  10. package/dist/app/inputSelectionStore.js +0 -8
  11. package/dist/app/inputStore.js +0 -28
  12. package/dist/app/interfaces.js +0 -6
  13. package/dist/app/overlayStore.js +0 -40
  14. package/dist/app/promptStore.js +0 -44
  15. package/dist/app/queueStore.js +0 -25
  16. package/dist/app/scroll.js +0 -44
  17. package/dist/app/setupHandoff.js +0 -28
  18. package/dist/app/slash/commands/core.js +0 -479
  19. package/dist/app/slash/commands/debug.js +0 -44
  20. package/dist/app/slash/commands/ops.js +0 -498
  21. package/dist/app/slash/commands/session.js +0 -431
  22. package/dist/app/slash/commands/setup.js +0 -20
  23. package/dist/app/slash/commands/toggles.js +0 -40
  24. package/dist/app/slash/registry.js +0 -18
  25. package/dist/app/slash/types.js +0 -1
  26. package/dist/app/spawnHistoryStore.js +0 -105
  27. package/dist/app/turnController.js +0 -650
  28. package/dist/app/turnStore.js +0 -48
  29. package/dist/app/uiStore.js +0 -36
  30. package/dist/app/useComposerState.js +0 -265
  31. package/dist/app/useConfigSync.js +0 -144
  32. package/dist/app/useInputHandlers.js +0 -403
  33. package/dist/app/useLongRunToolCharms.js +0 -50
  34. package/dist/app/useMainApp.js +0 -629
  35. package/dist/app/useSessionLifecycle.js +0 -175
  36. package/dist/app/useSubmission.js +0 -287
  37. package/dist/app.js +0 -15
  38. package/dist/banner.js +0 -57
  39. package/dist/components/agentsOverlay.js +0 -474
  40. package/dist/components/appChrome.js +0 -252
  41. package/dist/components/appLayout.js +0 -121
  42. package/dist/components/appOverlays.js +0 -65
  43. package/dist/components/branding.js +0 -97
  44. package/dist/components/fpsOverlay.js +0 -22
  45. package/dist/components/helpHint.js +0 -21
  46. package/dist/components/markdown.js +0 -501
  47. package/dist/components/maskedPrompt.js +0 -12
  48. package/dist/components/messageLine.js +0 -82
  49. package/dist/components/modelPicker.js +0 -254
  50. package/dist/components/overlayControls.js +0 -30
  51. package/dist/components/overlays/confirmPrompt.js +0 -25
  52. package/dist/components/overlays/helpOverlay.js +0 -76
  53. package/dist/components/overlays/historySearch.js +0 -49
  54. package/dist/components/overlays/modelPicker.js +0 -60
  55. package/dist/components/overlays/overlayUtils.js +0 -19
  56. package/dist/components/overlays/secretPrompt.js +0 -36
  57. package/dist/components/overlays/sessionPicker.js +0 -93
  58. package/dist/components/overlays/skillsHub.js +0 -71
  59. package/dist/components/prompts.js +0 -95
  60. package/dist/components/queuedMessages.js +0 -24
  61. package/dist/components/sessionPicker.js +0 -130
  62. package/dist/components/skillsHub.js +0 -165
  63. package/dist/components/streamingAssistant.js +0 -35
  64. package/dist/components/streamingMarkdown.js +0 -144
  65. package/dist/components/textInput.js +0 -794
  66. package/dist/components/themed.js +0 -12
  67. package/dist/components/thinking.js +0 -496
  68. package/dist/components/todoPanel.js +0 -40
  69. package/dist/components/transcript.js +0 -22
  70. package/dist/config/env.js +0 -18
  71. package/dist/config/limits.js +0 -22
  72. package/dist/config/timing.js +0 -18
  73. package/dist/content/charms.js +0 -5
  74. package/dist/content/faces.js +0 -21
  75. package/dist/content/fortunes.js +0 -29
  76. package/dist/content/hotkeys.js +0 -38
  77. package/dist/content/placeholders.js +0 -15
  78. package/dist/content/setup.js +0 -14
  79. package/dist/content/verbs.js +0 -41
  80. package/dist/domain/details.js +0 -53
  81. package/dist/domain/messages.js +0 -63
  82. package/dist/domain/paths.js +0 -16
  83. package/dist/domain/providers.js +0 -11
  84. package/dist/domain/roles.js +0 -6
  85. package/dist/domain/slash.js +0 -11
  86. package/dist/domain/usage.js +0 -1
  87. package/dist/domain/viewport.js +0 -33
  88. package/dist/gateway/client.js +0 -312
  89. package/dist/gatewayClient.js +0 -574
  90. package/dist/gatewayTypes.js +0 -1
  91. package/dist/hooks/useCompletion.js +0 -86
  92. package/dist/hooks/useGitBranch.js +0 -58
  93. package/dist/hooks/useInputHistory.js +0 -12
  94. package/dist/hooks/useQueue.js +0 -57
  95. package/dist/hooks/useVirtualHistory.js +0 -401
  96. package/dist/lib/circularBuffer.js +0 -43
  97. package/dist/lib/clipboard.js +0 -126
  98. package/dist/lib/editor.js +0 -41
  99. package/dist/lib/editor.test.js +0 -58
  100. package/dist/lib/emoji.js +0 -49
  101. package/dist/lib/externalCli.js +0 -11
  102. package/dist/lib/forceTruecolor.js +0 -26
  103. package/dist/lib/fpsStore.js +0 -36
  104. package/dist/lib/gracefulExit.js +0 -29
  105. package/dist/lib/history.js +0 -69
  106. package/dist/lib/inputMetrics.js +0 -143
  107. package/dist/lib/liveProgress.js +0 -51
  108. package/dist/lib/liveProgress.test.js +0 -89
  109. package/dist/lib/mathUnicode.js +0 -685
  110. package/dist/lib/memory.js +0 -123
  111. package/dist/lib/memoryMonitor.js +0 -76
  112. package/dist/lib/messages.js +0 -3
  113. package/dist/lib/messages.test.js +0 -25
  114. package/dist/lib/osc52.js +0 -53
  115. package/dist/lib/perfPane.js +0 -94
  116. package/dist/lib/platform.js +0 -312
  117. package/dist/lib/precisionWheel.js +0 -25
  118. package/dist/lib/reasoning.js +0 -39
  119. package/dist/lib/rpc.js +0 -26
  120. package/dist/lib/subagentTree.js +0 -287
  121. package/dist/lib/syntax.js +0 -89
  122. package/dist/lib/terminalModes.js +0 -46
  123. package/dist/lib/terminalParity.js +0 -48
  124. package/dist/lib/terminalSetup.js +0 -321
  125. package/dist/lib/text.js +0 -203
  126. package/dist/lib/text.test.js +0 -18
  127. package/dist/lib/todo.js +0 -2
  128. package/dist/lib/todo.test.js +0 -22
  129. package/dist/lib/viewportStore.js +0 -82
  130. package/dist/lib/virtualHeights.js +0 -61
  131. package/dist/lib/wheelAccel.js +0 -143
  132. package/dist/theme.js +0 -398
  133. package/dist/types.js +0 -1
@@ -1,21 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- export const FACES = [
6
- '(。•́︿•̀。)',
7
- '(◔_◔)',
8
- '(¬‿¬)',
9
- '( •_•)>⌐■-■',
10
- '(⌐■_■)',
11
- '(´・_・`)',
12
- '◉_◉',
13
- '(°ロ°)',
14
- '( ˘⌣˘)♡',
15
- 'ヽ(>∀<☆)☆',
16
- '٩(๑❛ᴗ❛๑)۶',
17
- '(⊙_⊙)',
18
- '(¬_¬)',
19
- '( ͡° ͜ʖ ͡°)',
20
- 'ಠ_ಠ'
21
- ];
@@ -1,29 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- const FORTUNES = [
6
- 'you are one clean refactor away from clarity',
7
- 'a tiny rename today prevents a huge bug tomorrow',
8
- 'your next commit message will be immaculate',
9
- 'the edge case you are ignoring is already solved in your head',
10
- 'minimal diff, maximal calm',
11
- 'today favors bold deletions over new abstractions',
12
- 'the right helper is already in your codebase',
13
- 'you will ship before overthinking catches up',
14
- 'tests are about to save your future self',
15
- 'your instincts are correctly suspicious of that one branch'
16
- ];
17
- const LEGENDARY = [
18
- 'legendary drop: one-line fix, first try',
19
- 'legendary drop: every flaky test passes cleanly',
20
- 'legendary drop: your diff teaches by itself'
21
- ];
22
- const hash = (s) => [...s].reduce((h, c) => Math.imul(h ^ c.charCodeAt(0), 16777619), 2166136261) >>> 0;
23
- const fromScore = (n) => {
24
- const rare = n % 20 === 0;
25
- const bag = rare ? LEGENDARY : FORTUNES;
26
- return `${rare ? '🌟' : '🔮'} ${bag[n % bag.length]}`;
27
- };
28
- export const randomFortune = () => fromScore(Math.floor(Math.random() * 0x7fffffff));
29
- export const dailyFortune = (seed) => fromScore(hash(`${seed || 'anon'}|${new Date().toDateString()}`));
@@ -1,38 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- import { isMac, isRemoteShell } from '../lib/platform.js';
6
- const action = isMac ? 'Cmd' : 'Ctrl';
7
- const paste = isMac ? 'Cmd' : 'Alt';
8
- const copyHotkeys = isMac
9
- ? [
10
- ['Cmd+C', 'copy selection'],
11
- ['Ctrl+C', 'interrupt / clear draft / exit']
12
- ]
13
- : isRemoteShell()
14
- ? [
15
- ['Cmd+C', 'copy selection when forwarded by the terminal'],
16
- ['Ctrl+C', 'copy selection / interrupt / clear draft / exit']
17
- ]
18
- : [['Ctrl+C', 'copy selection / interrupt / clear draft / exit']];
19
- export const HOTKEYS = [
20
- ...copyHotkeys,
21
- [action + '+D', 'exit'],
22
- [action + '+G / Alt+G', 'open $EDITOR (Alt+G fallback for VSCode/Cursor)'],
23
- [action + '+L', 'redraw / repaint'],
24
- [paste + '+V / /paste', 'paste text; /paste attaches clipboard image'],
25
- ['Tab', 'apply completion'],
26
- ['↑/↓', 'completions / queue edit / history'],
27
- ['Ctrl+X', 'delete the queued message you’re editing (Esc cancels edit)'],
28
- [action + '+A/E', 'home / end of line'],
29
- [action + '+Z / ' + action + '+Y', 'undo / redo input edits'],
30
- [action + '+W', 'delete word'],
31
- [action + '+U/K', 'delete to start / end'],
32
- [action + '+←/→', 'jump word'],
33
- ['Home/End', 'start / end of line'],
34
- ['Shift+Enter / Alt+Enter', 'insert newline'],
35
- ['\\+Enter', 'multi-line continuation (fallback)'],
36
- ['!<cmd>', 'run a shell command (e.g. !ls, !git status)'],
37
- ['{!<cmd>}', 'interpolate shell output inline (e.g. "branch is {!git branch --show-current}")']
38
- ];
@@ -1,15 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- import { pick } from '../lib/text.js';
6
- export const PLACEHOLDERS = [
7
- 'Ask me anything…',
8
- 'Try "explain this codebase"',
9
- 'Try "write a test for…"',
10
- 'Try "refactor the auth module"',
11
- 'Try "/help" for commands',
12
- 'Try "fix the lint errors"',
13
- 'Try "how does the config loader work?"'
14
- ];
15
- export const PLACEHOLDER = pick(PLACEHOLDERS);
@@ -1,14 +0,0 @@
1
- export const SETUP_REQUIRED_TITLE = 'Setup Required';
2
- export const buildSetupRequiredSections = () => [
3
- {
4
- text: 'Hermes needs a model provider before the TUI can start a session.'
5
- },
6
- {
7
- rows: [
8
- ['/model', 'configure provider + model in-place'],
9
- ['/setup', 'run full first-time setup wizard in-place'],
10
- ['Ctrl+C', 'exit and run `hermes setup` manually']
11
- ],
12
- title: 'Actions'
13
- }
14
- ];
@@ -1,41 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- export const TOOL_VERBS = {
6
- browser: 'browsing',
7
- clarify: 'asking',
8
- create_file: 'creating',
9
- delegate_task: 'delegating',
10
- delete_file: 'deleting',
11
- execute_code: 'executing',
12
- image_generate: 'generating',
13
- list_files: 'listing',
14
- memory: 'remembering',
15
- patch: 'patching',
16
- read_file: 'reading',
17
- run_command: 'running',
18
- search_code: 'searching',
19
- search_files: 'searching',
20
- terminal: 'terminal',
21
- web_extract: 'extracting',
22
- web_search: 'searching',
23
- write_file: 'writing'
24
- };
25
- export const VERBS = [
26
- 'pondering',
27
- 'contemplating',
28
- 'musing',
29
- 'cogitating',
30
- 'ruminating',
31
- 'deliberating',
32
- 'mulling',
33
- 'reflecting',
34
- 'processing',
35
- 'reasoning',
36
- 'analyzing',
37
- 'computing',
38
- 'synthesizing',
39
- 'formulating',
40
- 'brainstorming'
41
- ];
@@ -1,53 +0,0 @@
1
- const MODES = ['hidden', 'collapsed', 'expanded'];
2
- export const SECTION_NAMES = ['thinking', 'tools', 'subagents', 'activity'];
3
- // Out-of-the-box per-section defaults — applied when the user hasn't pinned
4
- // an explicit override and layered ABOVE the global details_mode:
5
- //
6
- // - thinking / tools: expanded — stream open so the turn reads like a
7
- // live transcript (reasoning + tool calls side by side) instead of a
8
- // wall of chevrons the user has to click every turn.
9
- // - activity: hidden — ambient meta (gateway hints, terminal-parity
10
- // nudges, background notifications) is noise for typical use. Tool
11
- // failures still render inline on the failing tool row, and ambient
12
- // errors/warnings surface via the floating-alert backstop when every
13
- // panel resolves to hidden.
14
- // - subagents: not set — falls through to the global details_mode so
15
- // Spawn trees stay under a chevron until a delegation actually happens.
16
- //
17
- // Opt out of any of these with `display.sections.<name>` in config.yaml
18
- // or at runtime via `/details <name> collapsed|hidden`.
19
- const SECTION_DEFAULTS = {
20
- thinking: 'expanded',
21
- tools: 'expanded',
22
- activity: 'hidden'
23
- };
24
- const THINKING_FALLBACK = {
25
- collapsed: 'collapsed',
26
- full: 'expanded',
27
- truncated: 'collapsed'
28
- };
29
- const norm = (v) => String(v ?? '')
30
- .trim()
31
- .toLowerCase();
32
- export const parseDetailsMode = (v) => MODES.find(m => m === norm(v)) ?? null;
33
- export const isSectionName = (v) => typeof v === 'string' && SECTION_NAMES.includes(v);
34
- export const resolveDetailsMode = (d) => parseDetailsMode(d?.details_mode) ?? THINKING_FALLBACK[norm(d?.thinking_mode)] ?? 'collapsed';
35
- // Build SectionVisibility from a free-form blob. Unknown section names and
36
- // invalid modes are dropped silently — partial overrides are intentional, so
37
- // missing keys fall through to SECTION_DEFAULTS / global at lookup time.
38
- export const resolveSections = (raw) => raw && typeof raw === 'object' && !Array.isArray(raw)
39
- ? Object.fromEntries(Object.entries(raw)
40
- .map(([k, v]) => [k, parseDetailsMode(v)])
41
- .filter(([k, m]) => !!m && isSectionName(k)))
42
- : {};
43
- // Effective mode for one section: explicit override → global command mode →
44
- // built-in live-stream defaults → global config mode.
45
- //
46
- // The `commandOverride` flag is set for in-session `/details <mode>` changes.
47
- // That command should immediately apply to every section, including sections
48
- // with built-in defaults like thinking/tools=expanded and activity=hidden. On
49
- // startup/config sync we keep those defaults layered above the persisted global
50
- // config so the TUI still opens live reasoning/tools by default unless the user
51
- // pins explicit per-section overrides.
52
- export const sectionMode = (name, global, sections, commandOverride = false) => sections?.[name] ?? (commandOverride ? global : (SECTION_DEFAULTS[name] ?? global));
53
- export const nextDetailsMode = (m) => MODES[(MODES.indexOf(m) + 1) % MODES.length];
@@ -1,63 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- import { LONG_MSG } from '../config/limits.js';
6
- import { buildToolTrailLine, fmtK } from '../lib/text.js';
7
- export const introMsg = (info) => ({ info, kind: 'intro', role: 'system', text: '' });
8
- export const imageTokenMeta = (info) => {
9
- const { width, height, token_estimate: t } = info ?? {};
10
- return [width && height ? `${width}x${height}` : '', (t ?? 0) > 0 ? `~${fmtK(t)} tok` : '']
11
- .filter(Boolean)
12
- .join(' · ');
13
- };
14
- export const attachedImageNotice = (info) => {
15
- const meta = imageTokenMeta(info);
16
- const label = info?.name ? `📎 Attached image: ${info.name}` : '📎 Attached image';
17
- return `${label}${meta ? ` · ${meta}` : ''}`;
18
- };
19
- export const userDisplay = (text) => {
20
- if (text.length <= LONG_MSG) {
21
- return text;
22
- }
23
- const first = text.split('\n')[0]?.trim() ?? '';
24
- const words = first.split(/\s+/).filter(Boolean);
25
- const prefix = (words.length > 1 ? words.slice(0, 4).join(' ') : first).slice(0, 80);
26
- return `${prefix || '(message)'} [long message]`;
27
- };
28
- export const toTranscriptMessages = (rows) => {
29
- if (!Array.isArray(rows)) {
30
- return [];
31
- }
32
- const out = [];
33
- let pending = [];
34
- for (const row of rows) {
35
- if (!row || typeof row !== 'object') {
36
- continue;
37
- }
38
- const { context, name, role, text } = row;
39
- if (role === 'tool') {
40
- pending.push(buildToolTrailLine(name ?? 'tool', context ?? ''));
41
- continue;
42
- }
43
- if (typeof text !== 'string' || !text.trim()) {
44
- continue;
45
- }
46
- if (role === 'assistant') {
47
- out.push({ role, text, ...(pending.length && { tools: pending }) });
48
- pending = [];
49
- }
50
- else if (role === 'user' || role === 'system') {
51
- out.push({ role, text });
52
- pending = [];
53
- }
54
- }
55
- return out;
56
- };
57
- export const fmtDuration = (ms) => {
58
- const t = Math.max(0, Math.floor(ms / 1000));
59
- const h = Math.floor(t / 3600);
60
- const m = Math.floor((t % 3600) / 60);
61
- const s = t % 60;
62
- return h > 0 ? `${h}h ${m}m` : m > 0 ? `${m}m ${s}s` : `${s}s`;
63
- };
@@ -1,16 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- export const shortCwd = (cwd, max = 28) => {
6
- const h = process.env.HOME;
7
- const p = h && cwd.startsWith(h) ? `~${cwd.slice(h.length)}` : cwd;
8
- return p.length <= max ? p : `…${p.slice(-(max - 1))}`;
9
- };
10
- export const fmtCwdBranch = (cwd, branch, max = 40) => {
11
- if (!branch) {
12
- return shortCwd(cwd, max);
13
- }
14
- const tag = ` (${branch.length > 16 ? `…${branch.slice(-15)}` : branch})`;
15
- return `${shortCwd(cwd, Math.max(8, max - tag.length))}${tag}`;
16
- };
@@ -1,11 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- export const providerDisplayNames = (providers) => {
6
- const counts = new Map();
7
- for (const p of providers) {
8
- counts.set(p.name, (counts.get(p.name) ?? 0) + 1);
9
- }
10
- return providers.map(p => (counts.get(p.name) ?? 0) > 1 && p.slug && p.slug !== p.name ? `${p.name} (${p.slug})` : p.name);
11
- };
@@ -1,6 +0,0 @@
1
- export const ROLE = {
2
- assistant: t => ({ body: t.color.text, glyph: t.brand.tool, prefix: t.color.border }),
3
- system: t => ({ body: '', glyph: '·', prefix: t.color.muted }),
4
- tool: t => ({ body: t.color.muted, glyph: '⚡', prefix: t.color.muted }),
5
- user: t => ({ body: t.color.label, glyph: t.brand.prompt, prefix: t.color.label })
6
- };
@@ -1,11 +0,0 @@
1
- // @ts-nocheck
2
- // SPDX-License-Identifier: MIT
3
- // Ported from CVC Agent (https://github.com/NousResearch/cvc)
4
- // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
5
- /** Appended to `/model` args from the TUI picker for session scope; stripped in `session` slash before `config.set`. */
6
- export const TUI_SESSION_MODEL_FLAG = '--tui-session';
7
- export const looksLikeSlashCommand = (text) => /^\/[^\s/]*(?:\s|$)/.test(text);
8
- export const parseSlashCommand = (cmd) => {
9
- const [name = '', ...rest] = cmd.slice(1).split(/\s+/);
10
- return { arg: rest.join(' '), cmd, name: name.toLowerCase() };
11
- };
@@ -1 +0,0 @@
1
- export const ZERO = { calls: 0, input: 0, output: 0, total: 0 };
@@ -1,33 +0,0 @@
1
- import { userDisplay } from './messages.js';
2
- const upperBound = (offsets, target) => {
3
- let lo = 0;
4
- let hi = offsets.length;
5
- while (lo < hi) {
6
- const mid = (lo + hi) >> 1;
7
- offsets[mid] <= target ? (lo = mid + 1) : (hi = mid);
8
- }
9
- return lo;
10
- };
11
- export const stickyPromptFromViewport = (messages, offsets, top, bottom, sticky) => {
12
- if (sticky || !messages.length) {
13
- return '';
14
- }
15
- const first = Math.max(0, upperBound(offsets, top) - 1);
16
- const last = Math.max(first, upperBound(offsets, bottom) - 1);
17
- const visibleStart = Math.min(messages.length, first);
18
- const visibleEnd = Math.min(messages.length - 1, last);
19
- for (let i = visibleStart; i <= visibleEnd; i++) {
20
- if (messages[i]?.role === 'user') {
21
- return '';
22
- }
23
- }
24
- for (let i = Math.min(messages.length - 1, visibleStart - 1); i >= 0; i--) {
25
- if (messages[i]?.role !== 'user') {
26
- continue;
27
- }
28
- return (offsets[i + 1] ?? (offsets[i] ?? 0) + 1) <= top
29
- ? userDisplay(messages[i].text.trim()).replace(/\s+/g, ' ').trim()
30
- : '';
31
- }
32
- return '';
33
- };