claude-threads 0.48.14 → 0.48.16

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 (3) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/index.js +740 -132
  3. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.48.16] - 2026-01-09
11
+
12
+ ### Added
13
+ - **Scrollable logs panel** - Logs are now scrollable with keyboard navigation (↑↓ arrows, g/G for top/bottom). Press `[l]` to focus logs panel for scrolling (#146)
14
+ - **Section headings** - Added clear section headings (Platforms, Logs, Threads) with counts
15
+ - **Numbered platforms and threads** - Each platform and thread now shows its number for quick reference
16
+ - **Clear screen at startup** - Terminal clears at startup for a clean UI
17
+
18
+ ### Changed
19
+ - **StatusLine pinned to bottom** - Status bar now stays at the bottom of the terminal
20
+ - **Dynamic log height** - Logs section uses available terminal space
21
+
22
+ ## [0.48.15] - 2026-01-09
23
+
24
+ ### Fixed
25
+ - **Clean screen on normal shutdown** - Screen now clears on Ctrl+C / quit, not just on update restart (#145)
26
+
10
27
  ## [0.48.14] - 2026-01-09
11
28
 
12
29
  ### Fixed
package/dist/index.js CHANGED
@@ -42039,6 +42039,233 @@ var require_cjs = __commonJS((exports, module) => {
42039
42039
  module.exports = deepmerge_1;
42040
42040
  });
42041
42041
 
42042
+ // node_modules/react/cjs/react-jsx-runtime.development.js
42043
+ var require_react_jsx_runtime_development = __commonJS((exports) => {
42044
+ var React28 = __toESM(require_react());
42045
+ (function() {
42046
+ function getComponentNameFromType(type) {
42047
+ if (type == null)
42048
+ return null;
42049
+ if (typeof type === "function")
42050
+ return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null;
42051
+ if (typeof type === "string")
42052
+ return type;
42053
+ switch (type) {
42054
+ case REACT_FRAGMENT_TYPE:
42055
+ return "Fragment";
42056
+ case REACT_PROFILER_TYPE:
42057
+ return "Profiler";
42058
+ case REACT_STRICT_MODE_TYPE:
42059
+ return "StrictMode";
42060
+ case REACT_SUSPENSE_TYPE:
42061
+ return "Suspense";
42062
+ case REACT_SUSPENSE_LIST_TYPE:
42063
+ return "SuspenseList";
42064
+ case REACT_ACTIVITY_TYPE:
42065
+ return "Activity";
42066
+ }
42067
+ if (typeof type === "object")
42068
+ switch (typeof type.tag === "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), type.$$typeof) {
42069
+ case REACT_PORTAL_TYPE:
42070
+ return "Portal";
42071
+ case REACT_CONTEXT_TYPE:
42072
+ return type.displayName || "Context";
42073
+ case REACT_CONSUMER_TYPE:
42074
+ return (type._context.displayName || "Context") + ".Consumer";
42075
+ case REACT_FORWARD_REF_TYPE:
42076
+ var innerType = type.render;
42077
+ type = type.displayName;
42078
+ type || (type = innerType.displayName || innerType.name || "", type = type !== "" ? "ForwardRef(" + type + ")" : "ForwardRef");
42079
+ return type;
42080
+ case REACT_MEMO_TYPE:
42081
+ return innerType = type.displayName || null, innerType !== null ? innerType : getComponentNameFromType(type.type) || "Memo";
42082
+ case REACT_LAZY_TYPE:
42083
+ innerType = type._payload;
42084
+ type = type._init;
42085
+ try {
42086
+ return getComponentNameFromType(type(innerType));
42087
+ } catch (x) {}
42088
+ }
42089
+ return null;
42090
+ }
42091
+ function testStringCoercion(value) {
42092
+ return "" + value;
42093
+ }
42094
+ function checkKeyStringCoercion(value) {
42095
+ try {
42096
+ testStringCoercion(value);
42097
+ var JSCompiler_inline_result = false;
42098
+ } catch (e) {
42099
+ JSCompiler_inline_result = true;
42100
+ }
42101
+ if (JSCompiler_inline_result) {
42102
+ JSCompiler_inline_result = console;
42103
+ var JSCompiler_temp_const = JSCompiler_inline_result.error;
42104
+ var JSCompiler_inline_result$jscomp$0 = typeof Symbol === "function" && Symbol.toStringTag && value[Symbol.toStringTag] || value.constructor.name || "Object";
42105
+ JSCompiler_temp_const.call(JSCompiler_inline_result, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", JSCompiler_inline_result$jscomp$0);
42106
+ return testStringCoercion(value);
42107
+ }
42108
+ }
42109
+ function getTaskName(type) {
42110
+ if (type === REACT_FRAGMENT_TYPE)
42111
+ return "<>";
42112
+ if (typeof type === "object" && type !== null && type.$$typeof === REACT_LAZY_TYPE)
42113
+ return "<...>";
42114
+ try {
42115
+ var name = getComponentNameFromType(type);
42116
+ return name ? "<" + name + ">" : "<...>";
42117
+ } catch (x) {
42118
+ return "<...>";
42119
+ }
42120
+ }
42121
+ function getOwner() {
42122
+ var dispatcher = ReactSharedInternals.A;
42123
+ return dispatcher === null ? null : dispatcher.getOwner();
42124
+ }
42125
+ function UnknownOwner() {
42126
+ return Error("react-stack-top-frame");
42127
+ }
42128
+ function hasValidKey(config) {
42129
+ if (hasOwnProperty.call(config, "key")) {
42130
+ var getter = Object.getOwnPropertyDescriptor(config, "key").get;
42131
+ if (getter && getter.isReactWarning)
42132
+ return false;
42133
+ }
42134
+ return config.key !== undefined;
42135
+ }
42136
+ function defineKeyPropWarningGetter(props, displayName) {
42137
+ function warnAboutAccessingKey() {
42138
+ specialPropKeyWarningShown || (specialPropKeyWarningShown = true, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", displayName));
42139
+ }
42140
+ warnAboutAccessingKey.isReactWarning = true;
42141
+ Object.defineProperty(props, "key", {
42142
+ get: warnAboutAccessingKey,
42143
+ configurable: true
42144
+ });
42145
+ }
42146
+ function elementRefGetterWithDeprecationWarning() {
42147
+ var componentName = getComponentNameFromType(this.type);
42148
+ didWarnAboutElementRef[componentName] || (didWarnAboutElementRef[componentName] = true, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."));
42149
+ componentName = this.props.ref;
42150
+ return componentName !== undefined ? componentName : null;
42151
+ }
42152
+ function ReactElement(type, key, props, owner, debugStack, debugTask) {
42153
+ var refProp = props.ref;
42154
+ type = {
42155
+ $$typeof: REACT_ELEMENT_TYPE,
42156
+ type,
42157
+ key,
42158
+ props,
42159
+ _owner: owner
42160
+ };
42161
+ (refProp !== undefined ? refProp : null) !== null ? Object.defineProperty(type, "ref", {
42162
+ enumerable: false,
42163
+ get: elementRefGetterWithDeprecationWarning
42164
+ }) : Object.defineProperty(type, "ref", { enumerable: false, value: null });
42165
+ type._store = {};
42166
+ Object.defineProperty(type._store, "validated", {
42167
+ configurable: false,
42168
+ enumerable: false,
42169
+ writable: true,
42170
+ value: 0
42171
+ });
42172
+ Object.defineProperty(type, "_debugInfo", {
42173
+ configurable: false,
42174
+ enumerable: false,
42175
+ writable: true,
42176
+ value: null
42177
+ });
42178
+ Object.defineProperty(type, "_debugStack", {
42179
+ configurable: false,
42180
+ enumerable: false,
42181
+ writable: true,
42182
+ value: debugStack
42183
+ });
42184
+ Object.defineProperty(type, "_debugTask", {
42185
+ configurable: false,
42186
+ enumerable: false,
42187
+ writable: true,
42188
+ value: debugTask
42189
+ });
42190
+ Object.freeze && (Object.freeze(type.props), Object.freeze(type));
42191
+ return type;
42192
+ }
42193
+ function jsxDEVImpl(type, config, maybeKey, isStaticChildren, debugStack, debugTask) {
42194
+ var children = config.children;
42195
+ if (children !== undefined)
42196
+ if (isStaticChildren)
42197
+ if (isArrayImpl(children)) {
42198
+ for (isStaticChildren = 0;isStaticChildren < children.length; isStaticChildren++)
42199
+ validateChildKeys(children[isStaticChildren]);
42200
+ Object.freeze && Object.freeze(children);
42201
+ } else
42202
+ console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
42203
+ else
42204
+ validateChildKeys(children);
42205
+ if (hasOwnProperty.call(config, "key")) {
42206
+ children = getComponentNameFromType(type);
42207
+ var keys = Object.keys(config).filter(function(k) {
42208
+ return k !== "key";
42209
+ });
42210
+ isStaticChildren = 0 < keys.length ? "{key: someKey, " + keys.join(": ..., ") + ": ...}" : "{key: someKey}";
42211
+ didWarnAboutKeySpread[children + isStaticChildren] || (keys = 0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}", console.error(`A props object containing a "key" prop is being spread into JSX:
42212
+ let props = %s;
42213
+ <%s {...props} />
42214
+ React keys must be passed directly to JSX without using spread:
42215
+ let props = %s;
42216
+ <%s key={someKey} {...props} />`, isStaticChildren, children, keys, children), didWarnAboutKeySpread[children + isStaticChildren] = true);
42217
+ }
42218
+ children = null;
42219
+ maybeKey !== undefined && (checkKeyStringCoercion(maybeKey), children = "" + maybeKey);
42220
+ hasValidKey(config) && (checkKeyStringCoercion(config.key), children = "" + config.key);
42221
+ if ("key" in config) {
42222
+ maybeKey = {};
42223
+ for (var propName in config)
42224
+ propName !== "key" && (maybeKey[propName] = config[propName]);
42225
+ } else
42226
+ maybeKey = config;
42227
+ children && defineKeyPropWarningGetter(maybeKey, typeof type === "function" ? type.displayName || type.name || "Unknown" : type);
42228
+ return ReactElement(type, children, maybeKey, getOwner(), debugStack, debugTask);
42229
+ }
42230
+ function validateChildKeys(node) {
42231
+ isValidElement2(node) ? node._store && (node._store.validated = 1) : typeof node === "object" && node !== null && node.$$typeof === REACT_LAZY_TYPE && (node._payload.status === "fulfilled" ? isValidElement2(node._payload.value) && node._payload.value._store && (node._payload.value._store.validated = 1) : node._store && (node._store.validated = 1));
42232
+ }
42233
+ function isValidElement2(object) {
42234
+ return typeof object === "object" && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
42235
+ }
42236
+ var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React28.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function() {
42237
+ return null;
42238
+ };
42239
+ React28 = {
42240
+ react_stack_bottom_frame: function(callStackForError) {
42241
+ return callStackForError();
42242
+ }
42243
+ };
42244
+ var specialPropKeyWarningShown;
42245
+ var didWarnAboutElementRef = {};
42246
+ var unknownOwnerDebugStack = React28.react_stack_bottom_frame.bind(React28, UnknownOwner)();
42247
+ var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
42248
+ var didWarnAboutKeySpread = {};
42249
+ exports.Fragment = REACT_FRAGMENT_TYPE;
42250
+ exports.jsx = function(type, config, maybeKey) {
42251
+ var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
42252
+ return jsxDEVImpl(type, config, maybeKey, false, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask);
42253
+ };
42254
+ exports.jsxs = function(type, config, maybeKey) {
42255
+ var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
42256
+ return jsxDEVImpl(type, config, maybeKey, true, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask);
42257
+ };
42258
+ })();
42259
+ });
42260
+
42261
+ // node_modules/react/jsx-runtime.js
42262
+ var require_jsx_runtime = __commonJS((exports, module) => {
42263
+ var react_jsx_runtime_development = __toESM(require_react_jsx_runtime_development());
42264
+ if (false) {} else {
42265
+ module.exports = react_jsx_runtime_development;
42266
+ }
42267
+ });
42268
+
42042
42269
  // node_modules/commander/esm.mjs
42043
42270
  var import__ = __toESM(require_commander(), 1);
42044
42271
  var {
@@ -54471,7 +54698,7 @@ class SessionManager extends EventEmitter4 {
54471
54698
  }
54472
54699
  }
54473
54700
  // src/ui/index.ts
54474
- var import_react59 = __toESM(require_react(), 1);
54701
+ var import_react62 = __toESM(require_react(), 1);
54475
54702
 
54476
54703
  // node_modules/ink/build/render.js
54477
54704
  import { Stream } from "stream";
@@ -59583,25 +59810,6 @@ var getInstance = (stdout, createInstance2) => {
59583
59810
  };
59584
59811
  // node_modules/ink/build/components/Static.js
59585
59812
  var import_react14 = __toESM(require_react(), 1);
59586
- function Static(props) {
59587
- const { items, children: render2, style: customStyle } = props;
59588
- const [index, setIndex] = import_react14.useState(0);
59589
- const itemsToRender = import_react14.useMemo(() => {
59590
- return items.slice(index);
59591
- }, [items, index]);
59592
- import_react14.useLayoutEffect(() => {
59593
- setIndex(items.length);
59594
- }, [items.length]);
59595
- const children = itemsToRender.map((item, itemIndex) => {
59596
- return render2(item, index + itemIndex);
59597
- });
59598
- const style = import_react14.useMemo(() => ({
59599
- position: "absolute",
59600
- flexDirection: "column",
59601
- ...customStyle
59602
- }), [customStyle]);
59603
- return import_react14.default.createElement("ink-box", { internal_static: true, style }, children);
59604
- }
59605
59813
  // node_modules/ink/build/components/Transform.js
59606
59814
  var import_react15 = __toESM(require_react(), 1);
59607
59815
  // node_modules/ink/build/components/Newline.js
@@ -59871,8 +60079,14 @@ var import_react23 = __toESM(require_react(), 1);
59871
60079
  var import_react24 = __toESM(require_react(), 1);
59872
60080
  // node_modules/ink/build/hooks/use-is-screen-reader-enabled.js
59873
60081
  var import_react25 = __toESM(require_react(), 1);
60082
+ // node_modules/ink/build/measure-element.js
60083
+ var measureElement = (node) => ({
60084
+ width: node.yogaNode?.getComputedWidth() ?? 0,
60085
+ height: node.yogaNode?.getComputedHeight() ?? 0
60086
+ });
60087
+ var measure_element_default = measureElement;
59874
60088
  // src/ui/App.tsx
59875
- var import_react58 = __toESM(require_react(), 1);
60089
+ var import_react61 = __toESM(require_react(), 1);
59876
60090
 
59877
60091
  // src/ui/components/Header.tsx
59878
60092
  var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
@@ -62990,9 +63204,16 @@ function Platforms({ platforms }) {
62990
63204
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
62991
63205
  flexDirection: "column",
62992
63206
  marginTop: 1,
62993
- children: Array.from(platforms.values()).map((platform2) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
63207
+ children: Array.from(platforms.values()).map((platform2, index) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
62994
63208
  gap: 1,
62995
63209
  children: [
63210
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
63211
+ dimColor: true,
63212
+ children: [
63213
+ index + 1,
63214
+ "."
63215
+ ]
63216
+ }, undefined, true, undefined, this),
62996
63217
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
62997
63218
  children: getPlatformIcon(platform2.platformType || "mattermost")
62998
63219
  }, undefined, false, undefined, this),
@@ -63111,7 +63332,7 @@ function getStatusIndicator2(status) {
63111
63332
  }
63112
63333
  }
63113
63334
  function CollapsibleSession(props) {
63114
- const { session, logs, expanded } = props;
63335
+ const { session, logs, expanded, sessionNumber } = props;
63115
63336
  const { icon, color } = getStatusIndicator2(session.status);
63116
63337
  const platformIcon = getPlatformIcon(session.platformType || "mattermost");
63117
63338
  const arrow = expanded ? "\u25BC" : "\u25B6";
@@ -63125,6 +63346,13 @@ function CollapsibleSession(props) {
63125
63346
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
63126
63347
  gap: 1,
63127
63348
  children: [
63349
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
63350
+ dimColor: true,
63351
+ children: [
63352
+ sessionNumber,
63353
+ "."
63354
+ ]
63355
+ }, undefined, true, undefined, this),
63128
63356
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
63129
63357
  children: platformIcon
63130
63358
  }, undefined, false, undefined, this),
@@ -63389,6 +63617,12 @@ function StatusLine({
63389
63617
  label: "eep-alive",
63390
63618
  enabled: toggles.keepAliveEnabled
63391
63619
  }, undefined, false, undefined, this),
63620
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(ToggleKey, {
63621
+ keyChar: "l",
63622
+ label: "ogs",
63623
+ enabled: toggles.logsFocused,
63624
+ color: toggles.logsFocused ? "cyan" : "gray"
63625
+ }, undefined, false, undefined, this),
63392
63626
  /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
63393
63627
  gap: 0,
63394
63628
  children: [
@@ -63446,6 +63680,282 @@ function StatusLine({
63446
63680
  ]
63447
63681
  }, undefined, true, undefined, this);
63448
63682
  }
63683
+ // src/ui/components/LogPanel.tsx
63684
+ var import_react59 = __toESM(require_react(), 1);
63685
+
63686
+ // node_modules/ink-scroll-view/dist/index.js
63687
+ var import_react57 = __toESM(require_react(), 1);
63688
+ var import_react58 = __toESM(require_react(), 1);
63689
+ var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
63690
+ var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
63691
+ var MeasurableItem = ({
63692
+ children,
63693
+ onMeasure,
63694
+ index,
63695
+ width,
63696
+ measureKey
63697
+ }) => {
63698
+ const ref = import_react58.useRef(null);
63699
+ import_react58.useLayoutEffect(() => {
63700
+ if (ref.current) {
63701
+ const { height } = measure_element_default(ref.current);
63702
+ onMeasure(index, height);
63703
+ }
63704
+ }, [index, onMeasure, width, measureKey, children]);
63705
+ return /* @__PURE__ */ import_jsx_runtime.jsx(Box_default, { ref, flexShrink: 0, width: "100%", flexDirection: "column", children });
63706
+ };
63707
+ function useStateRef(initialValue) {
63708
+ const [state, setStateInternal] = import_react58.useState(initialValue);
63709
+ const ref = import_react58.useRef(initialValue);
63710
+ const setState = import_react58.useCallback((update) => {
63711
+ const nextValue = typeof update === "function" ? update(ref.current) : update;
63712
+ ref.current = nextValue;
63713
+ setStateInternal(nextValue);
63714
+ }, []);
63715
+ const getState = import_react58.useCallback(() => ref.current, []);
63716
+ return [state, setState, getState];
63717
+ }
63718
+ var ControlledScrollView = import_react58.forwardRef(({
63719
+ scrollOffset,
63720
+ onViewportSizeChange,
63721
+ onContentHeightChange,
63722
+ onItemHeightChange,
63723
+ debug = false,
63724
+ children,
63725
+ ...boxProps
63726
+ }, ref) => {
63727
+ const [viewportSize, setViewportSize, getViewportSize] = useStateRef({
63728
+ height: 0,
63729
+ width: 0
63730
+ });
63731
+ const [contentHeight, setContentHeight, getContentHeight] = useStateRef(0);
63732
+ const [itemMeasureKeys, setItemMeasureKeys] = import_react58.useState({});
63733
+ const viewportRef = import_react58.useRef(null);
63734
+ const contentRef = import_react58.useRef(null);
63735
+ const prevContentHeightRef = import_react58.useRef(0);
63736
+ import_react58.useLayoutEffect(() => {
63737
+ if (contentHeight !== prevContentHeightRef.current) {
63738
+ onContentHeightChange?.(contentHeight, prevContentHeightRef.current);
63739
+ prevContentHeightRef.current = contentHeight;
63740
+ }
63741
+ }, [contentHeight, onContentHeightChange]);
63742
+ const itemHeightsRef = import_react58.useRef({});
63743
+ const itemKeysRef = import_react58.useRef([]);
63744
+ const itemOffsetsRef = import_react58.useRef([]);
63745
+ const firstInvalidOffsetIndexRef = import_react58.useRef(0);
63746
+ const handleItemMeasure = import_react58.useCallback((index, height) => {
63747
+ const key = itemKeysRef.current[index] || index;
63748
+ if (itemHeightsRef.current[key] !== height) {
63749
+ const previousHeight = itemHeightsRef.current[key] || 0;
63750
+ itemHeightsRef.current = {
63751
+ ...itemHeightsRef.current,
63752
+ [key]: height
63753
+ };
63754
+ let newTotalHeight = 0;
63755
+ for (const itemKey of itemKeysRef.current) {
63756
+ newTotalHeight += itemHeightsRef.current[itemKey] || 0;
63757
+ }
63758
+ const currentHeight = getContentHeight();
63759
+ if (newTotalHeight !== currentHeight) {
63760
+ setContentHeight(newTotalHeight);
63761
+ }
63762
+ onItemHeightChange?.(index, height, previousHeight);
63763
+ firstInvalidOffsetIndexRef.current = Math.min(firstInvalidOffsetIndexRef.current, index + 1);
63764
+ }
63765
+ }, [
63766
+ onItemHeightChange,
63767
+ onContentHeightChange,
63768
+ getContentHeight,
63769
+ setContentHeight
63770
+ ]);
63771
+ const measureViewport = import_react58.useCallback(() => {
63772
+ if (viewportRef.current) {
63773
+ const { width, height } = measure_element_default(viewportRef.current);
63774
+ const currentSize = getViewportSize();
63775
+ if (width !== currentSize.width || height !== currentSize.height) {
63776
+ onViewportSizeChange?.({ width, height }, currentSize);
63777
+ setViewportSize({ width, height });
63778
+ }
63779
+ }
63780
+ }, [viewportRef, onViewportSizeChange, getViewportSize, setViewportSize]);
63781
+ import_react58.useLayoutEffect(() => {
63782
+ measureViewport();
63783
+ });
63784
+ const prevChildrenRef = import_react58.useRef(null);
63785
+ if (prevChildrenRef.current !== children) {
63786
+ prevChildrenRef.current = children;
63787
+ const newItemKeys = [];
63788
+ const newItemHeights = {};
63789
+ import_react58.Children.forEach(children, (child, index) => {
63790
+ if (!child)
63791
+ return;
63792
+ const key = import_react58.isValidElement(child) ? child.key : null;
63793
+ const effectiveKey = key !== null ? key : index;
63794
+ newItemKeys[index] = effectiveKey;
63795
+ const itemHeight = itemHeightsRef.current[effectiveKey] || 0;
63796
+ newItemHeights[effectiveKey] = itemHeight;
63797
+ });
63798
+ itemHeightsRef.current = newItemHeights;
63799
+ itemKeysRef.current = newItemKeys;
63800
+ itemOffsetsRef.current = new Array(newItemKeys.length).fill(0);
63801
+ firstInvalidOffsetIndexRef.current = 0;
63802
+ let newTotalHeight = 0;
63803
+ newItemKeys.forEach((itemKey) => {
63804
+ newTotalHeight += newItemHeights[itemKey] || 0;
63805
+ });
63806
+ const currentHeight = getContentHeight();
63807
+ if (newTotalHeight !== currentHeight) {
63808
+ setContentHeight(newTotalHeight);
63809
+ }
63810
+ }
63811
+ import_react58.useImperativeHandle(ref, () => ({
63812
+ getContentHeight,
63813
+ getViewportHeight: () => getViewportSize().height,
63814
+ getBottomOffset: () => Math.max(0, getContentHeight() - getViewportSize().height),
63815
+ getItemHeight: (index) => {
63816
+ const key = itemKeysRef.current[index] || index;
63817
+ return itemHeightsRef.current[key] || 0;
63818
+ },
63819
+ remeasure: measureViewport,
63820
+ remeasureItem: (index) => setItemMeasureKeys((prev) => ({
63821
+ ...prev,
63822
+ [index]: (prev[index] || 0) + 1
63823
+ })),
63824
+ getItemPosition: (index) => {
63825
+ if (index < 0 || index >= itemKeysRef.current.length) {
63826
+ return null;
63827
+ }
63828
+ if (index >= firstInvalidOffsetIndexRef.current) {
63829
+ let currentOffset = 0;
63830
+ let startIndex = 0;
63831
+ if (firstInvalidOffsetIndexRef.current > 0) {
63832
+ startIndex = firstInvalidOffsetIndexRef.current;
63833
+ const prevIndex = startIndex - 1;
63834
+ const prevKey = itemKeysRef.current[prevIndex] || prevIndex;
63835
+ const prevHeight = itemHeightsRef.current[prevKey] || 0;
63836
+ currentOffset = (itemOffsetsRef.current[prevIndex] ?? 0) + prevHeight;
63837
+ }
63838
+ for (let i = startIndex;i <= index; i++) {
63839
+ itemOffsetsRef.current[i] = currentOffset;
63840
+ const key2 = itemKeysRef.current[i] || i;
63841
+ const height2 = itemHeightsRef.current[key2] || 0;
63842
+ currentOffset += height2;
63843
+ }
63844
+ firstInvalidOffsetIndexRef.current = index + 1;
63845
+ }
63846
+ const top = itemOffsetsRef.current[index] ?? 0;
63847
+ const key = itemKeysRef.current[index] || index;
63848
+ const height = itemHeightsRef.current[key] || 0;
63849
+ return { top, height };
63850
+ }
63851
+ }), []);
63852
+ return /* @__PURE__ */ import_jsx_runtime.jsx(Box_default, { ...boxProps, children: /* @__PURE__ */ import_jsx_runtime.jsx(Box_default, { ref: viewportRef, width: "100%", children: /* @__PURE__ */ import_jsx_runtime.jsx(Box_default, { overflow: debug ? undefined : "hidden", width: "100%", children: /* @__PURE__ */ import_jsx_runtime.jsx(Box_default, {
63853
+ ref: contentRef,
63854
+ width: "100%",
63855
+ flexDirection: "column",
63856
+ marginTop: -scrollOffset,
63857
+ children: import_react58.Children.map(children, (child, index) => {
63858
+ if (!child)
63859
+ return null;
63860
+ return /* @__PURE__ */ import_jsx_runtime.jsx(MeasurableItem, {
63861
+ index,
63862
+ width: viewportSize.width,
63863
+ onMeasure: handleItemMeasure,
63864
+ measureKey: itemMeasureKeys[index],
63865
+ children: child
63866
+ }, import_react58.isValidElement(child) ? child.key || index : index);
63867
+ })
63868
+ }) }) }) });
63869
+ });
63870
+ function useStateRef2(initialValue) {
63871
+ const [state, setStateInternal] = import_react57.useState(initialValue);
63872
+ const ref = import_react57.useRef(initialValue);
63873
+ const setState = import_react57.useCallback((update) => {
63874
+ const nextValue = typeof update === "function" ? update(ref.current) : update;
63875
+ ref.current = nextValue;
63876
+ setStateInternal(nextValue);
63877
+ }, []);
63878
+ const getState = import_react57.useCallback(() => ref.current, []);
63879
+ return [state, setState, getState];
63880
+ }
63881
+ var ScrollView = import_react57.forwardRef(({
63882
+ onScroll,
63883
+ onViewportSizeChange,
63884
+ onContentHeightChange,
63885
+ onItemHeightChange,
63886
+ debug = false,
63887
+ children,
63888
+ ...boxProps
63889
+ }, ref) => {
63890
+ const [scrollOffset, setScrollOffset, getScrollOffset] = useStateRef2(0);
63891
+ const innerRef = import_react57.useRef(null);
63892
+ const contentHeightRef = import_react57.useRef(0);
63893
+ const handleContentHeightChange = import_react57.useCallback((height, previousHeight) => {
63894
+ contentHeightRef.current = height;
63895
+ onContentHeightChange?.(height, previousHeight);
63896
+ if (getScrollOffset() > height) {
63897
+ setScrollOffset(height);
63898
+ onScroll?.(height);
63899
+ }
63900
+ }, [onContentHeightChange, onScroll, getScrollOffset, setScrollOffset]);
63901
+ const getBottomOffset = import_react57.useCallback(() => Math.max(0, contentHeightRef.current - (innerRef.current?.getViewportHeight() || 0)), []);
63902
+ import_react57.useImperativeHandle(ref, () => ({
63903
+ scrollTo: (offset) => {
63904
+ if (typeof offset !== "number" || isNaN(offset)) {
63905
+ return;
63906
+ }
63907
+ const currentContentHeight = contentHeightRef.current;
63908
+ const newScrollTop = Math.max(0, Math.min(offset, currentContentHeight));
63909
+ if (newScrollTop !== getScrollOffset()) {
63910
+ setScrollOffset(newScrollTop);
63911
+ onScroll?.(newScrollTop);
63912
+ }
63913
+ },
63914
+ scrollBy: (delta) => {
63915
+ if (typeof delta !== "number" || isNaN(delta)) {
63916
+ return;
63917
+ }
63918
+ const currentContentHeight = contentHeightRef.current;
63919
+ const newScrollTop = Math.max(0, Math.min(getScrollOffset() + delta, currentContentHeight));
63920
+ if (newScrollTop !== getScrollOffset()) {
63921
+ setScrollOffset(newScrollTop);
63922
+ onScroll?.(newScrollTop);
63923
+ }
63924
+ },
63925
+ scrollToTop: () => {
63926
+ if (getScrollOffset() !== 0) {
63927
+ setScrollOffset(0);
63928
+ onScroll?.(0);
63929
+ }
63930
+ },
63931
+ scrollToBottom: () => {
63932
+ const bottomOffset = getBottomOffset();
63933
+ if (getScrollOffset() !== bottomOffset) {
63934
+ setScrollOffset(bottomOffset);
63935
+ onScroll?.(bottomOffset);
63936
+ }
63937
+ },
63938
+ getScrollOffset,
63939
+ getContentHeight: () => contentHeightRef.current,
63940
+ getViewportHeight: () => innerRef.current?.getViewportHeight() || 0,
63941
+ getBottomOffset,
63942
+ getItemHeight: (index) => innerRef.current?.getItemHeight(index) || 0,
63943
+ getItemPosition: (index) => innerRef.current?.getItemPosition(index) || null,
63944
+ remeasure: () => innerRef.current?.remeasure(),
63945
+ remeasureItem: (index) => innerRef.current?.remeasureItem(index)
63946
+ }), [onScroll, getBottomOffset, getScrollOffset, setScrollOffset]);
63947
+ return /* @__PURE__ */ import_jsx_runtime2.jsx(ControlledScrollView, {
63948
+ ref: innerRef,
63949
+ scrollOffset,
63950
+ onViewportSizeChange,
63951
+ onContentHeightChange: handleContentHeightChange,
63952
+ onItemHeightChange,
63953
+ debug,
63954
+ children,
63955
+ ...boxProps
63956
+ });
63957
+ });
63958
+
63449
63959
  // src/ui/components/LogPanel.tsx
63450
63960
  var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
63451
63961
  function getLevelColor(level) {
@@ -63464,34 +63974,70 @@ var COMPONENT_WIDTH2 = 10;
63464
63974
  function padComponent2(name) {
63465
63975
  return name.padEnd(COMPONENT_WIDTH2);
63466
63976
  }
63467
- function LogPanel({ logs, maxLines = 10 }) {
63977
+ function LogPanel({ logs, maxLines = 10, focused = false }) {
63978
+ const scrollRef = import_react59.default.useRef(null);
63979
+ const { stdout } = use_stdout_default();
63468
63980
  const isDebug = process.env.DEBUG === "1";
63469
- const displayLogs = logs.filter((log19) => isDebug || log19.level !== "debug").slice(-maxLines);
63470
- if (displayLogs.length === 0) {
63981
+ const displayLogs = logs.filter((log19) => isDebug || log19.level !== "debug");
63982
+ const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
63983
+ import_react59.default.useEffect(() => {
63984
+ const handleResize = () => scrollRef.current?.remeasure();
63985
+ stdout?.on("resize", handleResize);
63986
+ return () => {
63987
+ stdout?.off("resize", handleResize);
63988
+ };
63989
+ }, [stdout]);
63990
+ import_react59.default.useEffect(() => {
63991
+ if (!focused && scrollRef.current) {
63992
+ scrollRef.current.scrollToBottom();
63993
+ }
63994
+ }, [displayLogs.length, focused]);
63995
+ use_input_default((input, key) => {
63996
+ if (!focused)
63997
+ return;
63998
+ if (key.upArrow) {
63999
+ scrollRef.current?.scrollBy(-1);
64000
+ } else if (key.downArrow) {
64001
+ scrollRef.current?.scrollBy(1);
64002
+ } else if (key.pageUp) {
64003
+ scrollRef.current?.scrollBy(-5);
64004
+ } else if (key.pageDown) {
64005
+ scrollRef.current?.scrollBy(5);
64006
+ } else if (input === "g") {
64007
+ scrollRef.current?.scrollToTop();
64008
+ } else if (input === "G") {
64009
+ scrollRef.current?.scrollToBottom();
64010
+ }
64011
+ }, { isActive: focused });
64012
+ if (visibleLogs.length === 0) {
63471
64013
  return null;
63472
64014
  }
63473
64015
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
63474
64016
  flexDirection: "column",
63475
- marginTop: 1,
63476
- children: displayLogs.map((log19) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
63477
- children: [
63478
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
63479
- dimColor: true,
63480
- children: [
63481
- "[",
63482
- padComponent2(log19.component),
63483
- "]"
63484
- ]
63485
- }, undefined, true, undefined, this),
63486
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
63487
- color: getLevelColor(log19.level),
63488
- children: [
63489
- " ",
63490
- log19.message
63491
- ]
63492
- }, undefined, true, undefined, this)
63493
- ]
63494
- }, log19.id, true, undefined, this))
64017
+ height: maxLines,
64018
+ overflow: "hidden",
64019
+ children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(ScrollView, {
64020
+ ref: scrollRef,
64021
+ children: visibleLogs.map((log19) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
64022
+ children: [
64023
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
64024
+ dimColor: true,
64025
+ children: [
64026
+ "[",
64027
+ padComponent2(log19.component),
64028
+ "]"
64029
+ ]
64030
+ }, undefined, true, undefined, this),
64031
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
64032
+ color: getLevelColor(log19.level),
64033
+ children: [
64034
+ " ",
64035
+ log19.message
64036
+ ]
64037
+ }, undefined, true, undefined, this)
64038
+ ]
64039
+ }, log19.id, true, undefined, this))
64040
+ }, undefined, false, undefined, this)
63495
64041
  }, undefined, false, undefined, this);
63496
64042
  }
63497
64043
  // src/ui/components/Modal.tsx
@@ -63676,10 +64222,10 @@ function UpdateModal({ state }) {
63676
64222
  }, undefined, true, undefined, this);
63677
64223
  }
63678
64224
  // src/ui/hooks/useAppState.ts
63679
- var import_react57 = __toESM(require_react(), 1);
64225
+ var import_react60 = __toESM(require_react(), 1);
63680
64226
  var logIdCounter = 0;
63681
64227
  function useAppState(initialConfig) {
63682
- const [state, setState] = import_react57.useState({
64228
+ const [state, setState] = import_react60.useState({
63683
64229
  config: initialConfig,
63684
64230
  platforms: new Map,
63685
64231
  sessions: new Map,
@@ -63688,13 +64234,13 @@ function useAppState(initialConfig) {
63688
64234
  ready: false,
63689
64235
  shuttingDown: false
63690
64236
  });
63691
- const setReady = import_react57.useCallback(() => {
64237
+ const setReady = import_react60.useCallback(() => {
63692
64238
  setState((prev) => ({ ...prev, ready: true }));
63693
64239
  }, []);
63694
- const setShuttingDown2 = import_react57.useCallback(() => {
64240
+ const setShuttingDown2 = import_react60.useCallback(() => {
63695
64241
  setState((prev) => ({ ...prev, shuttingDown: true }));
63696
64242
  }, []);
63697
- const addSession = import_react57.useCallback((session) => {
64243
+ const addSession = import_react60.useCallback((session) => {
63698
64244
  setState((prev) => {
63699
64245
  const sessions = new Map(prev.sessions);
63700
64246
  sessions.set(session.id, session);
@@ -63705,7 +64251,7 @@ function useAppState(initialConfig) {
63705
64251
  return { ...prev, sessions, expandedSessions };
63706
64252
  });
63707
64253
  }, []);
63708
- const updateSession = import_react57.useCallback((sessionId, updates) => {
64254
+ const updateSession = import_react60.useCallback((sessionId, updates) => {
63709
64255
  setState((prev) => {
63710
64256
  const sessions = new Map(prev.sessions);
63711
64257
  const session = sessions.get(sessionId);
@@ -63715,7 +64261,7 @@ function useAppState(initialConfig) {
63715
64261
  return { ...prev, sessions };
63716
64262
  });
63717
64263
  }, []);
63718
- const removeSession = import_react57.useCallback((sessionId) => {
64264
+ const removeSession = import_react60.useCallback((sessionId) => {
63719
64265
  setState((prev) => {
63720
64266
  const sessions = new Map(prev.sessions);
63721
64267
  sessions.delete(sessionId);
@@ -63724,7 +64270,7 @@ function useAppState(initialConfig) {
63724
64270
  return { ...prev, sessions, expandedSessions };
63725
64271
  });
63726
64272
  }, []);
63727
- const addLog = import_react57.useCallback((entry) => {
64273
+ const addLog = import_react60.useCallback((entry) => {
63728
64274
  const newEntry = {
63729
64275
  ...entry,
63730
64276
  id: `log-${++logIdCounter}`,
@@ -63735,7 +64281,7 @@ function useAppState(initialConfig) {
63735
64281
  return { ...prev, logs };
63736
64282
  });
63737
64283
  }, []);
63738
- const toggleSession = import_react57.useCallback((sessionId) => {
64284
+ const toggleSession = import_react60.useCallback((sessionId) => {
63739
64285
  setState((prev) => {
63740
64286
  const expandedSessions = new Set(prev.expandedSessions);
63741
64287
  if (expandedSessions.has(sessionId)) {
@@ -63746,7 +64292,7 @@ function useAppState(initialConfig) {
63746
64292
  return { ...prev, expandedSessions };
63747
64293
  });
63748
64294
  }, []);
63749
- const setPlatformStatus = import_react57.useCallback((platformId, status) => {
64295
+ const setPlatformStatus = import_react60.useCallback((platformId, status) => {
63750
64296
  setState((prev) => {
63751
64297
  const platforms = new Map(prev.platforms);
63752
64298
  const current = platforms.get(platformId) || {
@@ -63763,13 +64309,13 @@ function useAppState(initialConfig) {
63763
64309
  return { ...prev, platforms };
63764
64310
  });
63765
64311
  }, []);
63766
- const getLogsForSession = import_react57.useCallback((sessionId) => {
64312
+ const getLogsForSession = import_react60.useCallback((sessionId) => {
63767
64313
  return state.logs.filter((log19) => log19.sessionId === sessionId);
63768
64314
  }, [state.logs]);
63769
- const getGlobalLogs = import_react57.useCallback(() => {
64315
+ const getGlobalLogs = import_react60.useCallback(() => {
63770
64316
  return state.logs.filter((log19) => !log19.sessionId);
63771
64317
  }, [state.logs]);
63772
- const togglePlatformEnabled = import_react57.useCallback((platformId) => {
64318
+ const togglePlatformEnabled = import_react60.useCallback((platformId) => {
63773
64319
  let newEnabled = false;
63774
64320
  setState((prev) => {
63775
64321
  const platforms = new Map(prev.platforms);
@@ -63821,13 +64367,21 @@ function useKeyboard({
63821
64367
  onChromeToggle,
63822
64368
  onKeepAliveToggle,
63823
64369
  onUpdateModalToggle,
64370
+ onLogsFocusToggle,
63824
64371
  onForceUpdate,
63825
- updateModalVisible
64372
+ updateModalVisible,
64373
+ logsFocused
63826
64374
  }) {
63827
64375
  use_input_default((input, key) => {
63828
- if (key.escape && updateModalVisible) {
63829
- onUpdateModalToggle?.();
63830
- return;
64376
+ if (key.escape) {
64377
+ if (updateModalVisible) {
64378
+ onUpdateModalToggle?.();
64379
+ return;
64380
+ }
64381
+ if (logsFocused) {
64382
+ onLogsFocusToggle?.();
64383
+ return;
64384
+ }
63831
64385
  }
63832
64386
  if (input === "\x03" || input === "c" && key.ctrl) {
63833
64387
  if (onQuit) {
@@ -63870,6 +64424,9 @@ function useKeyboard({
63870
64424
  case "u":
63871
64425
  onUpdateModalToggle?.();
63872
64426
  break;
64427
+ case "l":
64428
+ onLogsFocusToggle?.();
64429
+ break;
63873
64430
  case "q":
63874
64431
  onQuit?.();
63875
64432
  break;
@@ -63879,6 +64436,14 @@ function useKeyboard({
63879
64436
 
63880
64437
  // src/ui/App.tsx
63881
64438
  var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
64439
+ var SEPARATOR = "\u2500".repeat(50);
64440
+ var Separator = import_react61.default.memo(() => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64441
+ marginTop: 1,
64442
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64443
+ dimColor: true,
64444
+ children: SEPARATOR
64445
+ }, undefined, false, undefined, this)
64446
+ }, undefined, false, undefined, this));
63882
64447
  function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks }) {
63883
64448
  const {
63884
64449
  state,
@@ -63894,19 +64459,22 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
63894
64459
  getLogsForSession,
63895
64460
  getGlobalLogs
63896
64461
  } = useAppState(config);
63897
- const [resizeCount, setResizeCount] = import_react58.default.useState(0);
63898
- const [toggles, setToggles] = import_react58.default.useState({
64462
+ const [resizeCount, setResizeCount] = import_react61.default.useState(0);
64463
+ const { stdout } = use_stdout_default();
64464
+ const terminalRows = stdout?.rows ?? 24;
64465
+ const [toggles, setToggles] = import_react61.default.useState({
63899
64466
  debugMode: process.env.DEBUG === "1",
63900
64467
  skipPermissions: config.skipPermissions,
63901
64468
  chromeEnabled: config.chromeEnabled,
63902
64469
  keepAliveEnabled: config.keepAliveEnabled,
63903
- updateModalVisible: false
64470
+ updateModalVisible: false,
64471
+ logsFocused: false
63904
64472
  });
63905
- const [updateState, setUpdateState] = import_react58.default.useState({
64473
+ const [updateState, setUpdateState] = import_react61.default.useState({
63906
64474
  status: "idle",
63907
64475
  currentVersion: config.version
63908
64476
  });
63909
- const handleDebugToggle = import_react58.default.useCallback(() => {
64477
+ const handleDebugToggle = import_react61.default.useCallback(() => {
63910
64478
  setToggles((prev) => {
63911
64479
  const newValue = !prev.debugMode;
63912
64480
  process.env.DEBUG = newValue ? "1" : "";
@@ -63914,36 +64482,39 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
63914
64482
  return { ...prev, debugMode: newValue };
63915
64483
  });
63916
64484
  }, [toggleCallbacks]);
63917
- const handlePermissionsToggle = import_react58.default.useCallback(() => {
64485
+ const handlePermissionsToggle = import_react61.default.useCallback(() => {
63918
64486
  setToggles((prev) => {
63919
64487
  const newValue = !prev.skipPermissions;
63920
64488
  toggleCallbacks?.onPermissionsToggle?.(newValue);
63921
64489
  return { ...prev, skipPermissions: newValue };
63922
64490
  });
63923
64491
  }, [toggleCallbacks]);
63924
- const handleChromeToggle = import_react58.default.useCallback(() => {
64492
+ const handleChromeToggle = import_react61.default.useCallback(() => {
63925
64493
  setToggles((prev) => {
63926
64494
  const newValue = !prev.chromeEnabled;
63927
64495
  toggleCallbacks?.onChromeToggle?.(newValue);
63928
64496
  return { ...prev, chromeEnabled: newValue };
63929
64497
  });
63930
64498
  }, [toggleCallbacks]);
63931
- const handleKeepAliveToggle = import_react58.default.useCallback(() => {
64499
+ const handleKeepAliveToggle = import_react61.default.useCallback(() => {
63932
64500
  setToggles((prev) => {
63933
64501
  const newValue = !prev.keepAliveEnabled;
63934
64502
  toggleCallbacks?.onKeepAliveToggle?.(newValue);
63935
64503
  return { ...prev, keepAliveEnabled: newValue };
63936
64504
  });
63937
64505
  }, [toggleCallbacks]);
63938
- const handleUpdateModalToggle = import_react58.default.useCallback(() => {
64506
+ const handleUpdateModalToggle = import_react61.default.useCallback(() => {
63939
64507
  setToggles((prev) => ({ ...prev, updateModalVisible: !prev.updateModalVisible }));
63940
64508
  }, []);
63941
- const handlePlatformToggle = import_react58.default.useCallback((platformId) => {
64509
+ const handleLogsFocusToggle = import_react61.default.useCallback(() => {
64510
+ setToggles((prev) => ({ ...prev, logsFocused: !prev.logsFocused }));
64511
+ }, []);
64512
+ const handlePlatformToggle = import_react61.default.useCallback((platformId) => {
63942
64513
  const newEnabled = togglePlatformEnabled(platformId);
63943
64514
  toggleCallbacks?.onPlatformToggle?.(platformId, newEnabled);
63944
64515
  }, [togglePlatformEnabled, toggleCallbacks]);
63945
- const getToggles = import_react58.default.useCallback(() => toggles, [toggles]);
63946
- import_react58.default.useEffect(() => {
64516
+ const getToggles = import_react61.default.useCallback(() => toggles, [toggles]);
64517
+ import_react61.default.useEffect(() => {
63947
64518
  onStateReady({
63948
64519
  setReady,
63949
64520
  setShuttingDown: setShuttingDown2,
@@ -63956,7 +64527,7 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
63956
64527
  getToggles
63957
64528
  });
63958
64529
  }, [onStateReady, setReady, setShuttingDown2, addSession, updateSession, removeSession, addLog, setPlatformStatus, setUpdateState, getToggles]);
63959
- import_react58.default.useEffect(() => {
64530
+ import_react61.default.useEffect(() => {
63960
64531
  if (onResizeReady) {
63961
64532
  onResizeReady(() => setResizeCount((c) => c + 1));
63962
64533
  }
@@ -63974,82 +64545,108 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
63974
64545
  onChromeToggle: handleChromeToggle,
63975
64546
  onKeepAliveToggle: handleKeepAliveToggle,
63976
64547
  onUpdateModalToggle: handleUpdateModalToggle,
64548
+ onLogsFocusToggle: handleLogsFocusToggle,
63977
64549
  onForceUpdate: toggleCallbacks?.onForceUpdate,
63978
- updateModalVisible: toggles.updateModalVisible
64550
+ updateModalVisible: toggles.updateModalVisible,
64551
+ logsFocused: toggles.logsFocused
63979
64552
  });
63980
- const staticContent = import_react58.default.useMemo(() => [
63981
- { id: `header-${resizeCount}`, element: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Header, {
63982
- version: config.version
63983
- }, undefined, false, undefined, this) },
63984
- { id: `config-${resizeCount}`, element: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ConfigSummary, {
63985
- config
63986
- }, undefined, false, undefined, this) }
63987
- ], [config, resizeCount]);
63988
64553
  const globalLogs = getGlobalLogs();
63989
64554
  const hasLogs = globalLogs.length > 0;
63990
64555
  const hasSessions = state.sessions.size > 0;
64556
+ const platformCount = Math.max(1, state.platforms.size);
64557
+ const sessionCount = Math.max(1, state.sessions.size);
64558
+ const fixedOverhead = 5 + 4 + 3 + platformCount + 3 + 3 + sessionCount + 3;
64559
+ const logHeight = Math.max(3, terminalRows - fixedOverhead);
63991
64560
  return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
63992
64561
  flexDirection: "column",
64562
+ height: terminalRows,
63993
64563
  children: [
63994
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Static, {
63995
- items: staticContent,
63996
- children: (item) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
63997
- children: item.element
63998
- }, item.id, false, undefined, this)
64564
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Header, {
64565
+ version: config.version
63999
64566
  }, undefined, false, undefined, this),
64567
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ConfigSummary, {
64568
+ config
64569
+ }, undefined, false, undefined, this),
64570
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Separator, {}, undefined, false, undefined, this),
64571
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64572
+ children: [
64573
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64574
+ dimColor: true,
64575
+ bold: true,
64576
+ children: "Platforms"
64577
+ }, undefined, false, undefined, this),
64578
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64579
+ dimColor: true,
64580
+ children: [
64581
+ " (",
64582
+ state.platforms.size,
64583
+ ")"
64584
+ ]
64585
+ }, undefined, true, undefined, this)
64586
+ ]
64587
+ }, undefined, true, undefined, this),
64000
64588
  /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Platforms, {
64001
64589
  platforms: state.platforms
64002
64590
  }, undefined, false, undefined, this),
64003
- hasLogs && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(jsx_dev_runtime11.Fragment, {
64591
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Separator, {}, undefined, false, undefined, this),
64592
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64004
64593
  children: [
64005
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64006
- marginTop: 1,
64007
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64008
- dimColor: true,
64009
- children: "\u2500".repeat(50)
64010
- }, undefined, false, undefined, this)
64594
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64595
+ dimColor: true,
64596
+ bold: toggles.logsFocused,
64597
+ color: toggles.logsFocused ? "cyan" : undefined,
64598
+ children: "Logs"
64011
64599
  }, undefined, false, undefined, this),
64012
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(LogPanel, {
64013
- logs: globalLogs,
64014
- maxLines: 10
64600
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64601
+ dimColor: true,
64602
+ children: [
64603
+ " (",
64604
+ globalLogs.length,
64605
+ ")"
64606
+ ]
64607
+ }, undefined, true, undefined, this),
64608
+ toggles.logsFocused && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64609
+ dimColor: true,
64610
+ children: " - \u2191\u2193 scroll, g/G top/bottom, [l] unfocus"
64015
64611
  }, undefined, false, undefined, this)
64016
64612
  ]
64017
64613
  }, undefined, true, undefined, this),
64018
- hasSessions && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(jsx_dev_runtime11.Fragment, {
64614
+ hasLogs ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(LogPanel, {
64615
+ logs: globalLogs,
64616
+ maxLines: logHeight,
64617
+ focused: toggles.logsFocused
64618
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64619
+ dimColor: true,
64620
+ italic: true,
64621
+ children: " No logs yet"
64622
+ }, undefined, false, undefined, this),
64623
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Separator, {}, undefined, false, undefined, this),
64624
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64019
64625
  children: [
64020
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64021
- marginTop: 1,
64022
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64023
- dimColor: true,
64024
- children: "\u2500".repeat(50)
64025
- }, undefined, false, undefined, this)
64026
- }, undefined, false, undefined, this),
64027
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64028
- marginTop: 0,
64029
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64030
- dimColor: true,
64031
- children: [
64032
- "Sessions (",
64033
- state.sessions.size,
64034
- ")"
64035
- ]
64036
- }, undefined, true, undefined, this)
64626
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64627
+ dimColor: true,
64628
+ bold: true,
64629
+ children: "Threads"
64037
64630
  }, undefined, false, undefined, this),
64038
- Array.from(state.sessions.entries()).map(([id, session], index) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(CollapsibleSession, {
64039
- session,
64040
- logs: getLogsForSession(id),
64041
- expanded: state.expandedSessions.has(id),
64042
- sessionNumber: index + 1
64043
- }, id, false, undefined, this))
64631
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64632
+ dimColor: true,
64633
+ children: [
64634
+ " (",
64635
+ state.sessions.size,
64636
+ ")"
64637
+ ]
64638
+ }, undefined, true, undefined, this)
64044
64639
  ]
64045
64640
  }, undefined, true, undefined, this),
64046
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(StatusLine, {
64047
- ready: state.ready,
64048
- shuttingDown: state.shuttingDown,
64049
- sessionCount: state.sessions.size,
64050
- toggles,
64051
- platforms: state.platforms,
64052
- updateState
64641
+ hasSessions ? Array.from(state.sessions.entries()).map(([id, session], index) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(CollapsibleSession, {
64642
+ session,
64643
+ logs: getLogsForSession(id),
64644
+ expanded: state.expandedSessions.has(id),
64645
+ sessionNumber: index + 1
64646
+ }, id, false, undefined, this)) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
64647
+ dimColor: true,
64648
+ italic: true,
64649
+ children: " No active threads"
64053
64650
  }, undefined, false, undefined, this),
64054
64651
  toggles.updateModalVisible && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
64055
64652
  marginTop: 1,
@@ -64057,6 +64654,14 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
64057
64654
  children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(UpdateModal, {
64058
64655
  state: updateState
64059
64656
  }, undefined, false, undefined, this)
64657
+ }, undefined, false, undefined, this),
64658
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(StatusLine, {
64659
+ ready: state.ready,
64660
+ shuttingDown: state.shuttingDown,
64661
+ sessionCount: state.sessions.size,
64662
+ toggles,
64663
+ platforms: state.platforms,
64664
+ updateState
64060
64665
  }, undefined, false, undefined, this)
64061
64666
  ]
64062
64667
  }, undefined, true, undefined, this);
@@ -64073,7 +64678,7 @@ async function startUI(options) {
64073
64678
  resolveHandlers = resolve6;
64074
64679
  });
64075
64680
  let onResize = null;
64076
- const { waitUntilExit } = render_default(import_react59.default.createElement(App2, {
64681
+ const { waitUntilExit } = render_default(import_react62.default.createElement(App2, {
64077
64682
  config,
64078
64683
  onStateReady: (handlers2) => resolveHandlers(handlers2),
64079
64684
  onResizeReady: (handler) => {
@@ -65064,6 +65669,7 @@ function hasRequiredCliArgs(args) {
65064
65669
  return !!(args.url && args.token && args.channel);
65065
65670
  }
65066
65671
  async function main() {
65672
+ process.stdout.write("\x1B[2J\x1B[H");
65067
65673
  const shouldUseAutoRestart = async () => {
65068
65674
  if (opts.autoRestart === false)
65069
65675
  return false;
@@ -65397,6 +66003,8 @@ async function main() {
65397
66003
  for (const client of platforms.values()) {
65398
66004
  client.disconnect();
65399
66005
  }
66006
+ process.stdout.write("\x1B[2J\x1B[H");
66007
+ process.stdout.write("\x1B[?25h");
65400
66008
  };
65401
66009
  triggerShutdown = () => {
65402
66010
  shutdown("Ctrl+C").finally(() => process.exit(0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "0.48.14",
3
+ "version": "0.48.16",
4
4
  "description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -67,6 +67,7 @@
67
67
  "commander": "^14.0.2",
68
68
  "diff": "^8.0.2",
69
69
  "ink": "^6.6.0",
70
+ "ink-scroll-view": "^0.3.5",
70
71
  "prompts": "^2.4.2",
71
72
  "react": "^19.2.3",
72
73
  "react-devtools-core": "^7.0.1",