@butlerw/vellum 0.1.16 → 0.1.17

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 (2) hide show
  1. package/dist/index.mjs +216 -146
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -17,7 +17,7 @@ import OpenAI from 'openai';
17
17
  import { ThinkingLevel, GoogleGenAI } from '@google/genai';
18
18
  import { Writable, Readable } from 'stream';
19
19
  import * as fs30 from 'fs';
20
- import fs30__default, { existsSync, stat as stat$1, unwatchFile, watchFile, promises, readFileSync, writeFileSync, watch as watch$1, mkdirSync, readdirSync, statSync, copyFileSync, rmSync, createWriteStream } from 'fs';
20
+ import fs30__default, { existsSync, stat as stat$1, unwatchFile, watchFile, promises, mkdirSync, readFileSync, writeFileSync, watch as watch$1, readdirSync, statSync, copyFileSync, rmSync, createWriteStream } from 'fs';
21
21
  import * as os16 from 'os';
22
22
  import os16__default, { type as type$1, homedir, platform, tmpdir, arch, hostname, release } from 'os';
23
23
  import * as util from 'util';
@@ -35,7 +35,7 @@ import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/webso
35
35
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
36
36
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
37
37
  import { Command } from 'commander';
38
- import { Text, Box, useInput, Static, render, measureElement, useApp, useStdout, useStdin } from 'ink';
38
+ import { Text, Box, useInput, render, measureElement, useApp, useStdout, useStdin } from 'ink';
39
39
  import React6, { createContext, memo, useMemo, useState, useRef, useEffect, useCallback, forwardRef, startTransition, useContext, useImperativeHandle, useId, useReducer, useLayoutEffect, Component } from 'react';
40
40
  import { confirm, select, input } from '@inquirer/prompts';
41
41
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -19513,9 +19513,9 @@ var init_handler = __esm({
19513
19513
  if (this.fsw.closed) {
19514
19514
  return;
19515
19515
  }
19516
- const dirname51 = sp2.dirname(file);
19516
+ const dirname53 = sp2.dirname(file);
19517
19517
  const basename26 = sp2.basename(file);
19518
- const parent = this.fsw._getWatchedDir(dirname51);
19518
+ const parent = this.fsw._getWatchedDir(dirname53);
19519
19519
  let prevStats = stats;
19520
19520
  if (parent.has(basename26))
19521
19521
  return;
@@ -19542,7 +19542,7 @@ var init_handler = __esm({
19542
19542
  prevStats = newStats2;
19543
19543
  }
19544
19544
  } catch (error2) {
19545
- this.fsw._remove(dirname51, basename26);
19545
+ this.fsw._remove(dirname53, basename26);
19546
19546
  }
19547
19547
  } else if (parent.has(basename26)) {
19548
19548
  const at = newStats.atimeMs;
@@ -167829,9 +167829,9 @@ var NodeFsHandler2 = class {
167829
167829
  if (this.fsw.closed) {
167830
167830
  return;
167831
167831
  }
167832
- const dirname51 = sp2.dirname(file);
167832
+ const dirname53 = sp2.dirname(file);
167833
167833
  const basename26 = sp2.basename(file);
167834
- const parent = this.fsw._getWatchedDir(dirname51);
167834
+ const parent = this.fsw._getWatchedDir(dirname53);
167835
167835
  let prevStats = stats;
167836
167836
  if (parent.has(basename26))
167837
167837
  return;
@@ -167858,7 +167858,7 @@ var NodeFsHandler2 = class {
167858
167858
  prevStats = newStats2;
167859
167859
  }
167860
167860
  } catch (error2) {
167861
- this.fsw._remove(dirname51, basename26);
167861
+ this.fsw._remove(dirname53, basename26);
167862
167862
  }
167863
167863
  } else if (parent.has(basename26)) {
167864
167864
  const at = newStats.atimeMs;
@@ -172704,8 +172704,10 @@ var EN_TRANSLATIONS = {
172704
172704
  thinking: "Thinking...",
172705
172705
  generating: "Generating...",
172706
172706
  user: "You",
172707
- assistant: "Assistant",
172707
+ assistant: "Vellum",
172708
172708
  system: "System",
172709
+ tool: "Tool",
172710
+ unknown: "Unknown",
172709
172711
  error: "Error",
172710
172712
  empty: "No messages yet",
172711
172713
  copied: "Copied to clipboard",
@@ -172899,8 +172901,10 @@ var ZH_TRANSLATIONS = {
172899
172901
  thinking: "\u601D\u8003\u4E2D...",
172900
172902
  generating: "\u751F\u6210\u4E2D...",
172901
172903
  user: "\u4F60",
172902
- assistant: "\u52A9\u624B",
172904
+ assistant: "Vellum",
172903
172905
  system: "\u7CFB\u7EDF",
172906
+ tool: "\u5DE5\u5177",
172907
+ unknown: "\u672A\u77E5",
172904
172908
  error: "\u9519\u8BEF",
172905
172909
  empty: "\u6682\u65E0\u6D88\u606F",
172906
172910
  copied: "\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
@@ -189538,7 +189542,7 @@ init_esm_shims();
189538
189542
 
189539
189543
  // src/version.ts
189540
189544
  init_esm_shims();
189541
- var version = "0.1.16" ;
189545
+ var version = "0.1.17" ;
189542
189546
 
189543
189547
  // src/tui/components/Banner/ShimmerText.tsx
189544
189548
  init_esm_shims();
@@ -191786,9 +191790,72 @@ init_esm_shims();
191786
191790
 
191787
191791
  // src/tui/hooks/useInputHistory.ts
191788
191792
  init_esm_shims();
191793
+ var VELLUM_DIR2 = join(homedir(), ".vellum");
191794
+ var HISTORY_FILE = join(VELLUM_DIR2, "input-history.json");
191795
+ function isLocalStorageAvailable() {
191796
+ try {
191797
+ if (typeof globalThis.localStorage === "undefined") {
191798
+ return false;
191799
+ }
191800
+ const testKey = "__vellum_storage_test__";
191801
+ globalThis.localStorage.setItem(testKey, "test");
191802
+ globalThis.localStorage.removeItem(testKey);
191803
+ return true;
191804
+ } catch {
191805
+ return false;
191806
+ }
191807
+ }
191808
+ var cachedBackend = null;
191809
+ function getStorageBackend() {
191810
+ if (cachedBackend !== null) {
191811
+ return cachedBackend;
191812
+ }
191813
+ if (isLocalStorageAvailable()) {
191814
+ cachedBackend = "localStorage";
191815
+ return cachedBackend;
191816
+ }
191817
+ try {
191818
+ if (!existsSync(VELLUM_DIR2)) {
191819
+ mkdirSync(VELLUM_DIR2, { recursive: true });
191820
+ }
191821
+ cachedBackend = "file";
191822
+ return cachedBackend;
191823
+ } catch {
191824
+ cachedBackend = "none";
191825
+ return cachedBackend;
191826
+ }
191827
+ }
191828
+ function readHistoryFile() {
191829
+ try {
191830
+ if (!existsSync(HISTORY_FILE)) {
191831
+ return {};
191832
+ }
191833
+ const content = readFileSync(HISTORY_FILE, "utf-8");
191834
+ const parsed = JSON.parse(content);
191835
+ if (typeof parsed === "object" && parsed !== null) {
191836
+ return parsed;
191837
+ }
191838
+ return {};
191839
+ } catch {
191840
+ return {};
191841
+ }
191842
+ }
191843
+ function writeHistoryFile(data) {
191844
+ try {
191845
+ const dir = dirname(HISTORY_FILE);
191846
+ if (!existsSync(dir)) {
191847
+ mkdirSync(dir, { recursive: true });
191848
+ }
191849
+ writeFileSync(HISTORY_FILE, JSON.stringify(data, null, 2), "utf-8");
191850
+ return true;
191851
+ } catch {
191852
+ return false;
191853
+ }
191854
+ }
191789
191855
  function loadFromStorage(key) {
191856
+ const backend = getStorageBackend();
191790
191857
  try {
191791
- if (typeof globalThis.localStorage !== "undefined") {
191858
+ if (backend === "localStorage") {
191792
191859
  const stored = globalThis.localStorage.getItem(key);
191793
191860
  if (stored) {
191794
191861
  const parsed = JSON.parse(stored);
@@ -191796,15 +191863,26 @@ function loadFromStorage(key) {
191796
191863
  return parsed.filter((item) => typeof item === "string");
191797
191864
  }
191798
191865
  }
191866
+ } else if (backend === "file") {
191867
+ const historyData = readHistoryFile();
191868
+ const entries = historyData[key];
191869
+ if (Array.isArray(entries)) {
191870
+ return entries.filter((item) => typeof item === "string");
191871
+ }
191799
191872
  }
191800
191873
  } catch {
191801
191874
  }
191802
191875
  return [];
191803
191876
  }
191804
191877
  function saveToStorage(key, history2) {
191878
+ const backend = getStorageBackend();
191805
191879
  try {
191806
- if (typeof globalThis.localStorage !== "undefined") {
191880
+ if (backend === "localStorage") {
191807
191881
  globalThis.localStorage.setItem(key, JSON.stringify(history2));
191882
+ } else if (backend === "file") {
191883
+ const historyData = readHistoryFile();
191884
+ historyData[key] = history2;
191885
+ writeHistoryFile(historyData);
191808
191886
  }
191809
191887
  } catch {
191810
191888
  }
@@ -199975,33 +200053,52 @@ var DEFAULT_STATIC_MULTIPLIER = 1.2;
199975
200053
  var DEFAULT_VIRTUAL_MULTIPLIER = 5;
199976
200054
  var DEFAULT_MIN_WINDOW_SIZE = 10;
199977
200055
  var DEFAULT_MAX_WINDOW_SIZE_RATIO = 0.8;
200056
+ var DEFAULT_MESSAGE_COUNT_THRESHOLD = 50;
200057
+ var DEFAULT_MESSAGE_COUNT_HYSTERESIS = 10;
199978
200058
  function useModeController(input3) {
199979
- const { availableHeight, totalContentHeight, config = {} } = input3;
200059
+ const { availableHeight, totalContentHeight, config = {}, messageCount = 0 } = input3;
199980
200060
  const {
199981
200061
  staticMultiplier = DEFAULT_STATIC_MULTIPLIER,
199982
200062
  virtualMultiplier = DEFAULT_VIRTUAL_MULTIPLIER,
199983
200063
  forceMode,
199984
200064
  minWindowSize = DEFAULT_MIN_WINDOW_SIZE,
199985
- maxWindowSizeRatio = DEFAULT_MAX_WINDOW_SIZE_RATIO
200065
+ maxWindowSizeRatio = DEFAULT_MAX_WINDOW_SIZE_RATIO,
200066
+ messageCountThreshold = DEFAULT_MESSAGE_COUNT_THRESHOLD,
200067
+ messageCountHysteresis = DEFAULT_MESSAGE_COUNT_HYSTERESIS
199986
200068
  } = config;
200069
+ const wasVirtualizedByCountRef = useRef(false);
199987
200070
  return useMemo(() => {
199988
200071
  const staticThreshold = Math.max(1, availableHeight * staticMultiplier);
199989
200072
  const virtualThreshold = Math.max(1, availableHeight * virtualMultiplier);
199990
200073
  const windowSize = Math.max(minWindowSize, Math.floor(availableHeight * maxWindowSizeRatio));
199991
200074
  const isAutoMode = !forceMode;
199992
200075
  if (forceMode) {
200076
+ wasVirtualizedByCountRef.current = false;
199993
200077
  return {
199994
200078
  mode: forceMode,
199995
200079
  windowSize,
199996
200080
  modeReason: "forced",
199997
200081
  staticThreshold,
199998
200082
  virtualThreshold,
199999
- isAutoMode
200083
+ isAutoMode,
200084
+ messageCountThreshold,
200085
+ virtualizedByMessageCount: false
200000
200086
  };
200001
200087
  }
200088
+ let virtualizedByMessageCount = false;
200089
+ const exitThreshold = messageCountThreshold - messageCountHysteresis;
200090
+ if (wasVirtualizedByCountRef.current) {
200091
+ virtualizedByMessageCount = messageCount >= exitThreshold;
200092
+ } else {
200093
+ virtualizedByMessageCount = messageCount >= messageCountThreshold;
200094
+ }
200095
+ wasVirtualizedByCountRef.current = virtualizedByMessageCount;
200002
200096
  let mode;
200003
200097
  let modeReason;
200004
- if (totalContentHeight <= staticThreshold) {
200098
+ if (virtualizedByMessageCount) {
200099
+ mode = "virtualized";
200100
+ modeReason = "content-very-large";
200101
+ } else if (totalContentHeight <= staticThreshold) {
200005
200102
  mode = "static";
200006
200103
  modeReason = "content-fits";
200007
200104
  } else if (totalContentHeight <= virtualThreshold) {
@@ -200017,7 +200114,9 @@ function useModeController(input3) {
200017
200114
  modeReason,
200018
200115
  staticThreshold,
200019
200116
  virtualThreshold,
200020
- isAutoMode
200117
+ isAutoMode,
200118
+ messageCountThreshold,
200119
+ virtualizedByMessageCount
200021
200120
  };
200022
200121
  }, [
200023
200122
  availableHeight,
@@ -200026,7 +200125,10 @@ function useModeController(input3) {
200026
200125
  virtualMultiplier,
200027
200126
  forceMode,
200028
200127
  minWindowSize,
200029
- maxWindowSizeRatio
200128
+ maxWindowSizeRatio,
200129
+ messageCount,
200130
+ messageCountThreshold,
200131
+ messageCountHysteresis
200030
200132
  ]);
200031
200133
  }
200032
200134
 
@@ -200753,18 +200855,18 @@ function getRoleIcon(role) {
200753
200855
  return icons2.info;
200754
200856
  }
200755
200857
  }
200756
- function getRoleLabel(role) {
200858
+ function getRoleLabel(role, t) {
200757
200859
  switch (role) {
200758
200860
  case "user":
200759
- return "You";
200861
+ return t("messages.user");
200760
200862
  case "assistant":
200761
- return "Vellum";
200863
+ return t("messages.assistant");
200762
200864
  case "system":
200763
- return "System";
200865
+ return t("messages.system");
200764
200866
  case "tool":
200765
- return "Tool";
200867
+ return t("messages.tool");
200766
200868
  default:
200767
- return "Unknown";
200869
+ return t("messages.unknown");
200768
200870
  }
200769
200871
  }
200770
200872
  function estimateMessageHeightLegacy(message2, width, includeToolCalls) {
@@ -200898,8 +201000,9 @@ var MessageItem = memo(function MessageItem2({
200898
201000
  thinkingToggleHint,
200899
201001
  onThinkingHeightChange
200900
201002
  }) {
201003
+ const { t } = useTUITranslation();
200901
201004
  const icon = getRoleIcon(message2.role);
200902
- const label = getRoleLabel(message2.role);
201005
+ const label = getRoleLabel(message2.role, t);
200903
201006
  const timestamp2 = formatTimestamp2(message2.timestamp);
200904
201007
  const hasThinking = message2.thinking && message2.thinking.length > 0;
200905
201008
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -201006,10 +201109,18 @@ var MessageList = memo(function MessageList2({
201006
201109
  0
201007
201110
  );
201008
201111
  }, [messages, estimatedContentWidth]);
201009
- const { mode, windowSize, modeReason, staticThreshold, virtualThreshold } = useModeController({
201112
+ const {
201113
+ mode,
201114
+ windowSize,
201115
+ modeReason,
201116
+ staticThreshold,
201117
+ virtualThreshold,
201118
+ virtualizedByMessageCount
201119
+ } = useModeController({
201010
201120
  availableHeight,
201011
201121
  totalContentHeight,
201012
- config: modeConfig
201122
+ config: modeConfig,
201123
+ messageCount: messages.length
201013
201124
  });
201014
201125
  const toolGroupCallIds = useMemo(() => {
201015
201126
  const ids = /* @__PURE__ */ new Set();
@@ -201042,6 +201153,7 @@ var MessageList = memo(function MessageList2({
201042
201153
  );
201043
201154
  const isStaticOutputMode = process.env.VELLUM_STATIC_OUTPUT === "1";
201044
201155
  const shouldConstrainHeight = !isStaticOutputMode && (useAltBuffer || (process.stdout.isTTY ?? false));
201156
+ const allowStaticRendering = false;
201045
201157
  const virtualizedListRef = useRef(null);
201046
201158
  const expectedScrollTopRef = useRef(null);
201047
201159
  const isApplyingControllerScrollRef = useRef(false);
@@ -201085,13 +201197,13 @@ var MessageList = memo(function MessageList2({
201085
201197
  const base = computedMaxHeight ?? availableHeight ?? 20;
201086
201198
  return Math.max(MIN_SCROLL_VIEWPORT, Math.max(0, base - thinkingIndicatorHeight));
201087
201199
  }, [computedMaxHeight, availableHeight, thinkingIndicatorHeight]);
201088
- const hasActiveStreamingThinking = useMemo(() => {
201200
+ useMemo(() => {
201089
201201
  if (pendingMessage?.thinking && pendingMessage.isStreaming && !pendingMessage.isThinkingComplete) {
201090
201202
  return true;
201091
201203
  }
201092
201204
  return false;
201093
201205
  }, [pendingMessage?.thinking, pendingMessage?.isStreaming, pendingMessage?.isThinkingComplete]);
201094
- const useStaticRendering = historyMessages !== void 0 && !computedMaxHeight && !hasActiveStreamingThinking && (mode === "static" || !adaptive);
201206
+ const useStaticRendering = allowStaticRendering;
201095
201207
  const prevMessageLengthRef = useRef(messages.length);
201096
201208
  const [scrollState, scrollActions] = useScrollController({
201097
201209
  viewportHeight: scrollViewportHeight,
@@ -201143,6 +201255,7 @@ var MessageList = memo(function MessageList2({
201143
201255
  useStaticRendering,
201144
201256
  useVirtualizedListInternal,
201145
201257
  messageCount: messages.length,
201258
+ virtualizedByMessageCount,
201146
201259
  adaptive
201147
201260
  });
201148
201261
  }
@@ -201158,6 +201271,7 @@ var MessageList = memo(function MessageList2({
201158
201271
  useStaticRendering,
201159
201272
  useVirtualizedListInternal,
201160
201273
  messages.length,
201274
+ virtualizedByMessageCount,
201161
201275
  adaptive
201162
201276
  ]);
201163
201277
  const [scrollOffset, setScrollOffset] = useState(0);
@@ -201535,7 +201649,7 @@ var MessageList = memo(function MessageList2({
201535
201649
  ]
201536
201650
  );
201537
201651
  const keyExtractor = useCallback((item) => item.id, []);
201538
- const pendingThinkingToggle = pendingMessage ? getThinkingToggleInfo(pendingMessage) : null;
201652
+ pendingMessage ? getThinkingToggleInfo(pendingMessage) : null;
201539
201653
  const handleStickingChange = useCallback(
201540
201654
  (isSticking) => {
201541
201655
  setUserScrolledUp(!isSticking);
@@ -201758,89 +201872,6 @@ var MessageList = memo(function MessageList2({
201758
201872
  !enableScroll && userScrolledUp && autoScroll && /* @__PURE__ */ jsx(Box, { justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: mutedColor, italic: true, children: "Auto-scroll paused (scroll to bottom to resume)" }) })
201759
201873
  ] });
201760
201874
  }
201761
- if (useStaticRendering && historyMessages) {
201762
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
201763
- !enableScroll && showScrollUp && /* @__PURE__ */ jsx(Box, { justifyContent: "center", borderBottom: true, borderColor, children: /* @__PURE__ */ jsxs(Text, { color: mutedColor, children: [
201764
- "\u2191 ",
201765
- scrollOffset,
201766
- " more above \u2191"
201767
- ] }) }),
201768
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
201769
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
201770
- /* @__PURE__ */ jsx(Static, { items: historyMessages, children: (message2) => {
201771
- const thinkingToggle = getThinkingToggleInfo(message2);
201772
- return /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(
201773
- MessageItem,
201774
- {
201775
- message: message2,
201776
- roleColor: roleColors[message2.role],
201777
- mutedColor,
201778
- accentColor,
201779
- thinkingColor,
201780
- successColor,
201781
- errorColor,
201782
- showToolCalls: shouldRenderInlineToolCalls(message2),
201783
- thinkingDisplayMode,
201784
- thinkingToggleSignal: thinkingToggle.signal,
201785
- thinkingToggleHint: thinkingToggle.hint
201786
- }
201787
- ) }, message2.id);
201788
- } }),
201789
- pendingMessage && /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(
201790
- MessageItem,
201791
- {
201792
- message: pendingMessage,
201793
- roleColor: roleColors[pendingMessage.role],
201794
- mutedColor,
201795
- accentColor,
201796
- thinkingColor,
201797
- successColor,
201798
- errorColor,
201799
- showToolCalls: shouldRenderInlineToolCalls(pendingMessage),
201800
- thinkingDisplayMode,
201801
- thinkingToggleSignal: pendingThinkingToggle?.signal,
201802
- thinkingToggleHint: pendingThinkingToggle?.hint
201803
- }
201804
- ) })
201805
- ] }),
201806
- enableScroll && /* @__PURE__ */ jsx(
201807
- ScrollIndicator,
201808
- {
201809
- totalHeight: scrollState.totalHeight,
201810
- offsetFromBottom: scrollState.offsetFromBottom,
201811
- viewportHeight: scrollState.viewportHeight,
201812
- thumbColor: animatedThumbColor,
201813
- trackColor: animatedTrackColor
201814
- }
201815
- )
201816
- ] }),
201817
- showThinkingIndicator && /* @__PURE__ */ jsx(ThinkingIndicator, {}),
201818
- messageSeparation.streamingMessages.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: messageSeparation.streamingMessages.map((msg) => /* @__PURE__ */ jsx(
201819
- StreamingMessageItem,
201820
- {
201821
- id: msg.id,
201822
- content: msg.content,
201823
- meta: msg.meta,
201824
- onComplete: messageSeparation.markComplete
201825
- },
201826
- msg.id
201827
- )) }),
201828
- bannerState.isVisible && /* @__PURE__ */ jsx(
201829
- NewMessagesBanner,
201830
- {
201831
- unreadCount: scrollState.newMessageCount,
201832
- isAtBottom: isAtBottomForBanner,
201833
- onJumpToBottom: scrollActions.scrollToBottom
201834
- }
201835
- ),
201836
- !enableScroll && showScrollDown && /* @__PURE__ */ jsx(Box, { justifyContent: "center", borderTop: true, borderColor, children: /* @__PURE__ */ jsxs(Text, { color: mutedColor, children: [
201837
- "\u2193 ",
201838
- messages.length - scrollOffset - (computedMaxHeight ?? 0),
201839
- " more below \u2193"
201840
- ] }) }),
201841
- !enableScroll && userScrolledUp && autoScroll && /* @__PURE__ */ jsx(Box, { justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: mutedColor, italic: true, children: "Auto-scroll paused (scroll to bottom to resume)" }) })
201842
- ] });
201843
- }
201844
201875
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, justifyContent: "flex-end", children: [
201845
201876
  !enableScroll && showScrollUp && /* @__PURE__ */ jsx(Box, { justifyContent: "center", borderBottom: true, borderColor, children: /* @__PURE__ */ jsxs(Text, { color: mutedColor, children: [
201846
201877
  "\u2191 ",
@@ -208218,9 +208249,6 @@ function useProviderStatus(options = {}) {
208218
208249
  };
208219
208250
  }
208220
208251
 
208221
- // src/tui/hooks/useScrollEventBatcher.ts
208222
- init_esm_shims();
208223
-
208224
208252
  // src/tui/hooks/useSmoothScroll.ts
208225
208253
  init_esm_shims();
208226
208254
 
@@ -214321,6 +214349,48 @@ function renderSidebarContent({
214321
214349
  /* @__PURE__ */ jsx(SystemStatusPanel, { compact: false, persistence })
214322
214350
  ] });
214323
214351
  }
214352
+ function useOverlayPositioning() {
214353
+ const { width, isNarrow } = useTerminalDimensions();
214354
+ if (isNarrow) {
214355
+ return {
214356
+ margins: {
214357
+ standard: { top: 2, left: 1 },
214358
+ compact: { top: 1, left: 1 },
214359
+ loading: { top: 2, left: 1 }
214360
+ },
214361
+ loadingMinWidth: Math.min(36, width - 4),
214362
+ // Leave 4 cols for border+padding
214363
+ isNarrow: true,
214364
+ width
214365
+ };
214366
+ }
214367
+ if (width <= 120) {
214368
+ const t = (width - 80) / 40;
214369
+ const standardLeft = Math.round(2 + t * 8);
214370
+ const compactLeft = Math.round(1 + t * 4);
214371
+ const loadingLeft = Math.round(2 + t * 6);
214372
+ return {
214373
+ margins: {
214374
+ standard: { top: Math.round(3 + t * 2), left: standardLeft },
214375
+ compact: { top: Math.round(2 + t * 1), left: compactLeft },
214376
+ loading: { top: Math.round(2 + t * 2), left: loadingLeft }
214377
+ },
214378
+ loadingMinWidth: Math.min(40, width - standardLeft * 2 - 4),
214379
+ isNarrow: false,
214380
+ width
214381
+ };
214382
+ }
214383
+ return {
214384
+ margins: {
214385
+ standard: { top: 5, left: 10 },
214386
+ compact: { top: 3, left: 5 },
214387
+ loading: { top: 4, left: 8 }
214388
+ },
214389
+ loadingMinWidth: 40,
214390
+ isNarrow: false,
214391
+ width
214392
+ };
214393
+ }
214324
214394
  function AppOverlays({
214325
214395
  activeApproval,
214326
214396
  activeRiskLevel,
@@ -214356,6 +214426,7 @@ function AppOverlays({
214356
214426
  themeContext,
214357
214427
  updateAvailable
214358
214428
  }) {
214429
+ const { margins, loadingMinWidth, isNarrow } = useOverlayPositioning();
214359
214430
  return /* @__PURE__ */ jsxs(Fragment, { children: [
214360
214431
  updateAvailable && /* @__PURE__ */ jsx(
214361
214432
  UpdateBanner,
@@ -214371,18 +214442,18 @@ function AppOverlays({
214371
214442
  Box,
214372
214443
  {
214373
214444
  position: "absolute",
214374
- marginTop: 5,
214375
- marginLeft: 10,
214445
+ marginTop: margins.standard.top,
214446
+ marginLeft: margins.standard.left,
214376
214447
  borderStyle: "round",
214377
214448
  borderColor: themeContext.theme.colors.info,
214378
- padding: 1,
214449
+ padding: isNarrow ? 0 : 1,
214379
214450
  children: /* @__PURE__ */ jsx(
214380
214451
  ModeSelector,
214381
214452
  {
214382
214453
  currentMode: currentMode2,
214383
214454
  onSelect: handleModeSelect,
214384
214455
  isActive: showModeSelector,
214385
- showDescriptions: true
214456
+ showDescriptions: !isNarrow
214386
214457
  }
214387
214458
  )
214388
214459
  }
@@ -214391,11 +214462,11 @@ function AppOverlays({
214391
214462
  Box,
214392
214463
  {
214393
214464
  position: "absolute",
214394
- marginTop: 3,
214395
- marginLeft: 5,
214465
+ marginTop: margins.compact.top,
214466
+ marginLeft: margins.compact.left,
214396
214467
  borderStyle: "round",
214397
214468
  borderColor: themeContext.theme.colors.primary,
214398
- padding: 1,
214469
+ padding: isNarrow ? 0 : 1,
214399
214470
  children: /* @__PURE__ */ jsx(
214400
214471
  SessionPicker,
214401
214472
  {
@@ -214413,11 +214484,11 @@ function AppOverlays({
214413
214484
  Box,
214414
214485
  {
214415
214486
  position: "absolute",
214416
- marginTop: 5,
214417
- marginLeft: 10,
214487
+ marginTop: margins.standard.top,
214488
+ marginLeft: margins.standard.left,
214418
214489
  borderStyle: "double",
214419
214490
  borderColor: themeContext.theme.colors.warning,
214420
- padding: 1,
214491
+ padding: isNarrow ? 0 : 1,
214421
214492
  children: /* @__PURE__ */ jsx(
214422
214493
  PermissionDialog,
214423
214494
  {
@@ -214435,11 +214506,11 @@ function AppOverlays({
214435
214506
  Box,
214436
214507
  {
214437
214508
  position: "absolute",
214438
- marginTop: 5,
214439
- marginLeft: 10,
214509
+ marginTop: margins.standard.top,
214510
+ marginLeft: margins.standard.left,
214440
214511
  borderStyle: "round",
214441
214512
  borderColor: themeContext.theme.colors.success,
214442
- padding: 1,
214513
+ padding: isNarrow ? 0 : 1,
214443
214514
  children: /* @__PURE__ */ jsx(
214444
214515
  ModelSelector,
214445
214516
  {
@@ -214447,7 +214518,7 @@ function AppOverlays({
214447
214518
  currentProvider,
214448
214519
  onSelect: handleModelSelect,
214449
214520
  isActive: showModelSelector,
214450
- showDetails: true
214521
+ showDetails: !isNarrow
214451
214522
  }
214452
214523
  )
214453
214524
  }
@@ -214456,13 +214527,13 @@ function AppOverlays({
214456
214527
  Box,
214457
214528
  {
214458
214529
  position: "absolute",
214459
- marginTop: 4,
214460
- marginLeft: 8,
214530
+ marginTop: margins.loading.top,
214531
+ marginLeft: margins.loading.left,
214461
214532
  borderStyle: "round",
214462
214533
  borderColor: themeContext.theme.colors.warning,
214463
- padding: 1,
214534
+ padding: isNarrow ? 0 : 1,
214464
214535
  flexDirection: "column",
214465
- minWidth: 40,
214536
+ minWidth: loadingMinWidth,
214466
214537
  children: [
214467
214538
  /* @__PURE__ */ jsx(LoadingIndicator, { message: pendingOperation.message }),
214468
214539
  pendingOperation.cancel && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press Esc to cancel" })
@@ -214473,11 +214544,11 @@ function AppOverlays({
214473
214544
  Box,
214474
214545
  {
214475
214546
  position: "absolute",
214476
- marginTop: 5,
214477
- marginLeft: 10,
214547
+ marginTop: margins.standard.top,
214548
+ marginLeft: margins.standard.left,
214478
214549
  borderStyle: "round",
214479
214550
  borderColor: themeContext.theme.colors.info,
214480
- padding: 1,
214551
+ padding: isNarrow ? 0 : 1,
214481
214552
  children: /* @__PURE__ */ jsx(
214482
214553
  HotkeyHelpModal,
214483
214554
  {
@@ -214492,11 +214563,11 @@ function AppOverlays({
214492
214563
  Box,
214493
214564
  {
214494
214565
  position: "absolute",
214495
- marginTop: 3,
214496
- marginLeft: 5,
214566
+ marginTop: margins.compact.top,
214567
+ marginLeft: margins.compact.left,
214497
214568
  borderStyle: "double",
214498
214569
  borderColor: themeContext.theme.colors.warning,
214499
- padding: 1,
214570
+ padding: isNarrow ? 0 : 1,
214500
214571
  children: /* @__PURE__ */ jsx(
214501
214572
  ApprovalQueue,
214502
214573
  {
@@ -214514,11 +214585,11 @@ function AppOverlays({
214514
214585
  Box,
214515
214586
  {
214516
214587
  position: "absolute",
214517
- marginTop: 3,
214518
- marginLeft: 5,
214588
+ marginTop: margins.compact.top,
214589
+ marginLeft: margins.compact.left,
214519
214590
  borderStyle: "double",
214520
214591
  borderColor: themeContext.theme.colors.info,
214521
- padding: 1,
214592
+ padding: isNarrow ? 0 : 1,
214522
214593
  children: /* @__PURE__ */ jsx(
214523
214594
  CheckpointDiffView,
214524
214595
  {
@@ -214784,10 +214855,9 @@ function AppContentView({
214784
214855
  MessageList,
214785
214856
  {
214786
214857
  messages,
214787
- historyMessages: messages.filter((m) => !m.isStreaming),
214788
214858
  pendingMessage,
214789
214859
  isLoading,
214790
- useVirtualizedList: true,
214860
+ adaptive: true,
214791
214861
  estimatedItemHeight: 4,
214792
214862
  scrollKeyMode: commandInputFocused ? "page" : "all",
214793
214863
  forceFollowOnInput: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@butlerw/vellum",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Next-generation AI coding assistant CLI",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -57,9 +57,9 @@
57
57
  "tsx": "^4.21.0",
58
58
  "typescript": "^5.9.3",
59
59
  "@vellum/core": "0.1.14",
60
- "@vellum/lsp": "0.1.0",
61
60
  "@vellum/mcp": "0.1.2",
62
61
  "@vellum/plugin": "0.1.2",
62
+ "@vellum/lsp": "0.1.0",
63
63
  "@vellum/provider": "0.1.0",
64
64
  "@vellum/sandbox": "0.1.0",
65
65
  "@vellum/shared": "0.1.0"