sidekick-docker 0.1.2 → 0.1.3

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.
@@ -15141,7 +15141,7 @@ var require_load_balancer_child_handler = __commonJS({
15141
15141
  createSubchannel(subchannelAddress, subchannelArgs) {
15142
15142
  return this.parent.channelControlHelper.createSubchannel(subchannelAddress, subchannelArgs);
15143
15143
  }
15144
- updateState(connectivityState, picker, errorMessage) {
15144
+ updateState(connectivityState, picker, errorMessage5) {
15145
15145
  var _a;
15146
15146
  if (this.calledByPendingChild()) {
15147
15147
  if (connectivityState === connectivity_state_1.ConnectivityState.CONNECTING) {
@@ -15153,7 +15153,7 @@ var require_load_balancer_child_handler = __commonJS({
15153
15153
  } else if (!this.calledByCurrentChild()) {
15154
15154
  return;
15155
15155
  }
15156
- this.parent.channelControlHelper.updateState(connectivityState, picker, errorMessage);
15156
+ this.parent.channelControlHelper.updateState(connectivityState, picker, errorMessage5);
15157
15157
  }
15158
15158
  requestReresolution() {
15159
15159
  var _a;
@@ -15379,11 +15379,11 @@ var require_resolving_load_balancer = __commonJS({
15379
15379
  this.updateResolution();
15380
15380
  }
15381
15381
  },
15382
- updateState: (newState, picker, errorMessage) => {
15382
+ updateState: (newState, picker, errorMessage5) => {
15383
15383
  this.latestChildState = newState;
15384
15384
  this.latestChildPicker = picker;
15385
- this.latestChildErrorMessage = errorMessage;
15386
- this.updateState(newState, picker, errorMessage);
15385
+ this.latestChildErrorMessage = errorMessage5;
15386
+ this.updateState(newState, picker, errorMessage5);
15387
15387
  },
15388
15388
  addChannelzChild: channelControlHelper.addChannelzChild.bind(channelControlHelper),
15389
15389
  removeChannelzChild: channelControlHelper.removeChannelzChild.bind(channelControlHelper)
@@ -15447,13 +15447,13 @@ var require_resolving_load_balancer = __commonJS({
15447
15447
  }
15448
15448
  this.backoffTimeout.runOnce();
15449
15449
  }
15450
- updateState(connectivityState, picker, errorMessage) {
15450
+ updateState(connectivityState, picker, errorMessage5) {
15451
15451
  trace((0, uri_parser_1.uriToString)(this.target) + " " + connectivity_state_1.ConnectivityState[this.currentState] + " -> " + connectivity_state_1.ConnectivityState[connectivityState]);
15452
15452
  if (connectivityState === connectivity_state_1.ConnectivityState.IDLE) {
15453
15453
  picker = new picker_1.QueuePicker(this, picker);
15454
15454
  }
15455
15455
  this.currentState = connectivityState;
15456
- this.channelControlHelper.updateState(connectivityState, picker, errorMessage);
15456
+ this.channelControlHelper.updateState(connectivityState, picker, errorMessage5);
15457
15457
  }
15458
15458
  handleResolutionFailure(error) {
15459
15459
  if (this.latestChildState === connectivity_state_1.ConnectivityState.IDLE) {
@@ -28353,13 +28353,13 @@ var require_subchannel = __commonJS({
28353
28353
  * @param newState The state to transition to
28354
28354
  * @returns True if the state changed, false otherwise
28355
28355
  */
28356
- transitionToState(oldStates, newState, errorMessage) {
28356
+ transitionToState(oldStates, newState, errorMessage5) {
28357
28357
  var _a, _b;
28358
28358
  if (oldStates.indexOf(this.connectivityState) === -1) {
28359
28359
  return false;
28360
28360
  }
28361
- if (errorMessage) {
28362
- this.trace(connectivity_state_1.ConnectivityState[this.connectivityState] + " -> " + connectivity_state_1.ConnectivityState[newState] + ' with error "' + errorMessage + '"');
28361
+ if (errorMessage5) {
28362
+ this.trace(connectivity_state_1.ConnectivityState[this.connectivityState] + " -> " + connectivity_state_1.ConnectivityState[newState] + ' with error "' + errorMessage5 + '"');
28363
28363
  } else {
28364
28364
  this.trace(connectivity_state_1.ConnectivityState[this.connectivityState] + " -> " + connectivity_state_1.ConnectivityState[newState]);
28365
28365
  }
@@ -28400,7 +28400,7 @@ var require_subchannel = __commonJS({
28400
28400
  throw new Error(`Invalid state: unknown ConnectivityState ${newState}`);
28401
28401
  }
28402
28402
  for (const listener of this.stateListeners) {
28403
- listener(this, previousState, newState, this.keepaliveTime, errorMessage);
28403
+ listener(this, previousState, newState, this.keepaliveTime, errorMessage5);
28404
28404
  }
28405
28405
  return true;
28406
28406
  }
@@ -29969,18 +29969,18 @@ var require_transport = __commonJS({
29969
29969
  setImmediate(() => {
29970
29970
  if (!reportedError) {
29971
29971
  reportedError = true;
29972
- reject(`${errorMessage.trim()} (${(/* @__PURE__ */ new Date()).toISOString()})`);
29972
+ reject(`${errorMessage5.trim()} (${(/* @__PURE__ */ new Date()).toISOString()})`);
29973
29973
  }
29974
29974
  });
29975
29975
  };
29976
29976
  const errorHandler = (error) => {
29977
29977
  var _a2;
29978
29978
  (_a2 = this.session) === null || _a2 === void 0 ? void 0 : _a2.destroy();
29979
- errorMessage = error.message;
29980
- this.trace("connection failed with error " + errorMessage);
29979
+ errorMessage5 = error.message;
29980
+ this.trace("connection failed with error " + errorMessage5);
29981
29981
  if (!reportedError) {
29982
29982
  reportedError = true;
29983
- reject(`${errorMessage} (${(/* @__PURE__ */ new Date()).toISOString()})`);
29983
+ reject(`${errorMessage5} (${(/* @__PURE__ */ new Date()).toISOString()})`);
29984
29984
  }
29985
29985
  };
29986
29986
  const sessionOptions = {
@@ -30001,7 +30001,7 @@ var require_transport = __commonJS({
30001
30001
  const defaultWin = (_h = (_g = (_f = http2.getDefaultSettings) === null || _f === void 0 ? void 0 : _f.call(http2)) === null || _g === void 0 ? void 0 : _g.initialWindowSize) !== null && _h !== void 0 ? _h : 65535;
30002
30002
  const connWin = options["grpc-node.flow_control_window"];
30003
30003
  this.session = session;
30004
- let errorMessage = "Failed to connect";
30004
+ let errorMessage5 = "Failed to connect";
30005
30005
  let reportedError = false;
30006
30006
  session.unref();
30007
30007
  session.once("remoteSettings", () => {
@@ -35224,8 +35224,8 @@ var require_load_balancer_pick_first = __commonJS({
35224
35224
  this.currentState = connectivity_state_1.ConnectivityState.IDLE;
35225
35225
  this.currentSubchannelIndex = 0;
35226
35226
  this.currentPick = null;
35227
- this.subchannelStateListener = (subchannel, previousState, newState, keepaliveTime, errorMessage) => {
35228
- this.onSubchannelStateUpdate(subchannel, previousState, newState, errorMessage);
35227
+ this.subchannelStateListener = (subchannel, previousState, newState, keepaliveTime, errorMessage5) => {
35228
+ this.onSubchannelStateUpdate(subchannel, previousState, newState, errorMessage5);
35229
35229
  };
35230
35230
  this.pickedSubchannelHealthListener = () => this.calculateAndReportNewState();
35231
35231
  this.stickyTransientFailureMode = false;
@@ -35248,26 +35248,26 @@ var require_load_balancer_pick_first = __commonJS({
35248
35248
  var _a;
35249
35249
  if (this.currentPick) {
35250
35250
  if (this.reportHealthStatus && !this.currentPick.isHealthy()) {
35251
- const errorMessage = `Picked subchannel ${this.currentPick.getAddress()} is unhealthy`;
35251
+ const errorMessage5 = `Picked subchannel ${this.currentPick.getAddress()} is unhealthy`;
35252
35252
  this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({
35253
- details: errorMessage
35254
- }), errorMessage);
35253
+ details: errorMessage5
35254
+ }), errorMessage5);
35255
35255
  } else {
35256
35256
  this.updateState(connectivity_state_1.ConnectivityState.READY, new PickFirstPicker(this.currentPick), null);
35257
35257
  }
35258
35258
  } else if (((_a = this.latestAddressList) === null || _a === void 0 ? void 0 : _a.length) === 0) {
35259
- const errorMessage = `No connection established. Last error: ${this.lastError}. Resolution note: ${this.latestResolutionNote}`;
35259
+ const errorMessage5 = `No connection established. Last error: ${this.lastError}. Resolution note: ${this.latestResolutionNote}`;
35260
35260
  this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({
35261
- details: errorMessage
35262
- }), errorMessage);
35261
+ details: errorMessage5
35262
+ }), errorMessage5);
35263
35263
  } else if (this.children.length === 0) {
35264
35264
  this.updateState(connectivity_state_1.ConnectivityState.IDLE, new picker_1.QueuePicker(this), null);
35265
35265
  } else {
35266
35266
  if (this.stickyTransientFailureMode) {
35267
- const errorMessage = `No connection established. Last error: ${this.lastError}. Resolution note: ${this.latestResolutionNote}`;
35267
+ const errorMessage5 = `No connection established. Last error: ${this.lastError}. Resolution note: ${this.latestResolutionNote}`;
35268
35268
  this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({
35269
- details: errorMessage
35270
- }), errorMessage);
35269
+ details: errorMessage5
35270
+ }), errorMessage5);
35271
35271
  } else {
35272
35272
  this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this), null);
35273
35273
  }
@@ -35301,7 +35301,7 @@ var require_load_balancer_pick_first = __commonJS({
35301
35301
  this.currentPick = null;
35302
35302
  }
35303
35303
  }
35304
- onSubchannelStateUpdate(subchannel, previousState, newState, errorMessage) {
35304
+ onSubchannelStateUpdate(subchannel, previousState, newState, errorMessage5) {
35305
35305
  var _a;
35306
35306
  if ((_a = this.currentPick) === null || _a === void 0 ? void 0 : _a.realSubchannelEquals(subchannel)) {
35307
35307
  if (newState !== connectivity_state_1.ConnectivityState.READY) {
@@ -35317,8 +35317,8 @@ var require_load_balancer_pick_first = __commonJS({
35317
35317
  }
35318
35318
  if (newState === connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) {
35319
35319
  child.hasReportedTransientFailure = true;
35320
- if (errorMessage) {
35321
- this.lastError = errorMessage;
35320
+ if (errorMessage5) {
35321
+ this.lastError = errorMessage5;
35322
35322
  }
35323
35323
  this.maybeEnterStickyTransientFailureMode();
35324
35324
  if (index === this.currentSubchannelIndex) {
@@ -35383,10 +35383,10 @@ var require_load_balancer_pick_first = __commonJS({
35383
35383
  clearTimeout(this.connectionDelayTimeout);
35384
35384
  this.calculateAndReportNewState();
35385
35385
  }
35386
- updateState(newState, picker, errorMessage) {
35386
+ updateState(newState, picker, errorMessage5) {
35387
35387
  trace(connectivity_state_1.ConnectivityState[this.currentState] + " -> " + connectivity_state_1.ConnectivityState[newState]);
35388
35388
  this.currentState = newState;
35389
- this.channelControlHelper.updateState(newState, picker, errorMessage);
35389
+ this.channelControlHelper.updateState(newState, picker, errorMessage5);
35390
35390
  }
35391
35391
  resetSubchannelList() {
35392
35392
  for (const child of this.children) {
@@ -35479,10 +35479,10 @@ var require_load_balancer_pick_first = __commonJS({
35479
35479
  this.resolutionNote = resolutionNote;
35480
35480
  this.latestState = connectivity_state_1.ConnectivityState.IDLE;
35481
35481
  const childChannelControlHelper = (0, load_balancer_1.createChildChannelControlHelper)(channelControlHelper, {
35482
- updateState: (connectivityState, picker, errorMessage) => {
35482
+ updateState: (connectivityState, picker, errorMessage5) => {
35483
35483
  this.latestState = connectivityState;
35484
35484
  this.latestPicker = picker;
35485
- channelControlHelper.updateState(connectivityState, picker, errorMessage);
35485
+ channelControlHelper.updateState(connectivityState, picker, errorMessage5);
35486
35486
  }
35487
35487
  });
35488
35488
  this.pickFirstBalancer = new PickFirstLoadBalancer(childChannelControlHelper);
@@ -35981,12 +35981,12 @@ var require_load_balancer_round_robin = __commonJS({
35981
35981
  this.updatesPaused = false;
35982
35982
  this.lastError = null;
35983
35983
  this.childChannelControlHelper = (0, load_balancer_1.createChildChannelControlHelper)(channelControlHelper, {
35984
- updateState: (connectivityState, picker, errorMessage) => {
35984
+ updateState: (connectivityState, picker, errorMessage5) => {
35985
35985
  if (this.currentState === connectivity_state_1.ConnectivityState.READY && connectivityState !== connectivity_state_1.ConnectivityState.READY) {
35986
35986
  this.channelControlHelper.requestReresolution();
35987
35987
  }
35988
- if (errorMessage) {
35989
- this.lastError = errorMessage;
35988
+ if (errorMessage5) {
35989
+ this.lastError = errorMessage5;
35990
35990
  }
35991
35991
  this.calculateAndUpdateState();
35992
35992
  }
@@ -36016,10 +36016,10 @@ var require_load_balancer_round_robin = __commonJS({
36016
36016
  } else if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.CONNECTING) > 0) {
36017
36017
  this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this), null);
36018
36018
  } else if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) > 0) {
36019
- const errorMessage = `round_robin: No connection established. Last error: ${this.lastError}`;
36019
+ const errorMessage5 = `round_robin: No connection established. Last error: ${this.lastError}`;
36020
36020
  this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({
36021
- details: errorMessage
36022
- }), errorMessage);
36021
+ details: errorMessage5
36022
+ }), errorMessage5);
36023
36023
  } else {
36024
36024
  this.updateState(connectivity_state_1.ConnectivityState.IDLE, new picker_1.QueuePicker(this), null);
36025
36025
  }
@@ -36029,7 +36029,7 @@ var require_load_balancer_round_robin = __commonJS({
36029
36029
  }
36030
36030
  }
36031
36031
  }
36032
- updateState(newState, picker, errorMessage) {
36032
+ updateState(newState, picker, errorMessage5) {
36033
36033
  trace(connectivity_state_1.ConnectivityState[this.currentState] + " -> " + connectivity_state_1.ConnectivityState[newState]);
36034
36034
  if (newState === connectivity_state_1.ConnectivityState.READY) {
36035
36035
  this.currentReadyPicker = picker;
@@ -36037,7 +36037,7 @@ var require_load_balancer_round_robin = __commonJS({
36037
36037
  this.currentReadyPicker = null;
36038
36038
  }
36039
36039
  this.currentState = newState;
36040
- this.channelControlHelper.updateState(newState, picker, errorMessage);
36040
+ this.channelControlHelper.updateState(newState, picker, errorMessage5);
36041
36041
  }
36042
36042
  resetSubchannelList() {
36043
36043
  for (const child of this.children) {
@@ -36059,8 +36059,8 @@ var require_load_balancer_round_robin = __commonJS({
36059
36059
  const endpointList = rotateArray(maybeEndpointList.value, startIndex);
36060
36060
  this.resetSubchannelList();
36061
36061
  if (endpointList.length === 0) {
36062
- const errorMessage = `No addresses resolved. Resolution note: ${resolutionNote}`;
36063
- this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({ details: errorMessage }), errorMessage);
36062
+ const errorMessage5 = `No addresses resolved. Resolution note: ${resolutionNote}`;
36063
+ this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({ details: errorMessage5 }), errorMessage5);
36064
36064
  }
36065
36065
  trace("Connect to endpoint list " + endpointList.map(subchannel_address_1.endpointToString));
36066
36066
  this.updatesPaused = true;
@@ -36347,11 +36347,11 @@ var require_load_balancer_outlier_detection = __commonJS({
36347
36347
  mapEntry === null || mapEntry === void 0 ? void 0 : mapEntry.subchannelWrappers.push(subchannelWrapper);
36348
36348
  return subchannelWrapper;
36349
36349
  },
36350
- updateState: (connectivityState, picker, errorMessage) => {
36350
+ updateState: (connectivityState, picker, errorMessage5) => {
36351
36351
  if (connectivityState === connectivity_state_1.ConnectivityState.READY) {
36352
- channelControlHelper.updateState(connectivityState, new OutlierDetectionPicker(picker, this.isCountingEnabled()), errorMessage);
36352
+ channelControlHelper.updateState(connectivityState, new OutlierDetectionPicker(picker, this.isCountingEnabled()), errorMessage5);
36353
36353
  } else {
36354
- channelControlHelper.updateState(connectivityState, picker, errorMessage);
36354
+ channelControlHelper.updateState(connectivityState, picker, errorMessage5);
36355
36355
  }
36356
36356
  }
36357
36357
  }));
@@ -36918,10 +36918,10 @@ var require_load_balancer_weighted_round_robin = __commonJS({
36918
36918
  } else if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.CONNECTING) > 0) {
36919
36919
  this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this), null);
36920
36920
  } else if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) > 0) {
36921
- const errorMessage = `weighted_round_robin: No connection established. Last error: ${this.lastError}`;
36921
+ const errorMessage5 = `weighted_round_robin: No connection established. Last error: ${this.lastError}`;
36922
36922
  this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({
36923
- details: errorMessage
36924
- }), errorMessage);
36923
+ details: errorMessage5
36924
+ }), errorMessage5);
36925
36925
  } else {
36926
36926
  this.updateState(connectivity_state_1.ConnectivityState.IDLE, new picker_1.QueuePicker(this), null);
36927
36927
  }
@@ -36931,10 +36931,10 @@ var require_load_balancer_weighted_round_robin = __commonJS({
36931
36931
  }
36932
36932
  }
36933
36933
  }
36934
- updateState(newState, picker, errorMessage) {
36934
+ updateState(newState, picker, errorMessage5) {
36935
36935
  trace(connectivity_state_1.ConnectivityState[this.currentState] + " -> " + connectivity_state_1.ConnectivityState[newState]);
36936
36936
  this.currentState = newState;
36937
- this.channelControlHelper.updateState(newState, picker, errorMessage);
36937
+ this.channelControlHelper.updateState(newState, picker, errorMessage5);
36938
36938
  }
36939
36939
  updateAddressList(maybeEndpointList, lbConfig, options, resolutionNote) {
36940
36940
  var _a, _b;
@@ -36948,8 +36948,8 @@ var require_load_balancer_weighted_round_robin = __commonJS({
36948
36948
  return true;
36949
36949
  }
36950
36950
  if (maybeEndpointList.value.length === 0) {
36951
- const errorMessage = `No addresses resolved. Resolution note: ${resolutionNote}`;
36952
- this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({ details: errorMessage }), errorMessage);
36951
+ const errorMessage5 = `No addresses resolved. Resolution note: ${resolutionNote}`;
36952
+ this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({ details: errorMessage5 }), errorMessage5);
36953
36953
  return false;
36954
36954
  }
36955
36955
  trace("Connect to endpoint list " + maybeEndpointList.value.map(subchannel_address_1.endpointToString));
@@ -36964,15 +36964,15 @@ var require_load_balancer_weighted_round_robin = __commonJS({
36964
36964
  if (!entry) {
36965
36965
  entry = {
36966
36966
  child: new load_balancer_pick_first_1.LeafLoadBalancer(endpoint, (0, load_balancer_1.createChildChannelControlHelper)(this.channelControlHelper, {
36967
- updateState: (connectivityState, picker, errorMessage) => {
36967
+ updateState: (connectivityState, picker, errorMessage5) => {
36968
36968
  if (this.currentState === connectivity_state_1.ConnectivityState.READY && connectivityState !== connectivity_state_1.ConnectivityState.READY) {
36969
36969
  this.channelControlHelper.requestReresolution();
36970
36970
  }
36971
36971
  if (connectivityState === connectivity_state_1.ConnectivityState.READY) {
36972
36972
  entry.nonEmptySince = null;
36973
36973
  }
36974
- if (errorMessage) {
36975
- this.lastError = errorMessage;
36974
+ if (errorMessage5) {
36975
+ this.lastError = errorMessage5;
36976
36976
  }
36977
36977
  this.calculateAndUpdateState();
36978
36978
  },
@@ -39740,9 +39740,15 @@ var require_DockerClient = __commonJS({
39740
39740
  const result = await this.docker.pruneNetworks();
39741
39741
  return { networksDeleted: result.NetworksDeleted || [] };
39742
39742
  }
39743
- async *streamEvents(filters) {
39743
+ async *streamEvents(filters, signal) {
39744
39744
  const stream = await this.docker.getEvents({ filters });
39745
+ if (signal) {
39746
+ const onAbort = () => stream.destroy?.();
39747
+ signal.addEventListener("abort", onAbort, { once: true });
39748
+ }
39745
39749
  for await (const chunk of stream) {
39750
+ if (signal?.aborted)
39751
+ break;
39746
39752
  const lines = chunk.toString("utf8").split("\n").filter(Boolean);
39747
39753
  for (const line of lines) {
39748
39754
  let raw;
@@ -40087,7 +40093,7 @@ var require_StatsCollector = __commonJS({
40087
40093
  Object.defineProperty(exports2, "__esModule", { value: true });
40088
40094
  exports2.StatsCollector = void 0;
40089
40095
  var DEFAULT_MAX_SAMPLES = 60;
40090
- var StatsCollector2 = class {
40096
+ var StatsCollector3 = class {
40091
40097
  histories = /* @__PURE__ */ new Map();
40092
40098
  maxSamples;
40093
40099
  constructor(maxSamples = DEFAULT_MAX_SAMPLES) {
@@ -40132,7 +40138,7 @@ var require_StatsCollector = __commonJS({
40132
40138
  this.histories.clear();
40133
40139
  }
40134
40140
  };
40135
- exports2.StatsCollector = StatsCollector2;
40141
+ exports2.StatsCollector = StatsCollector3;
40136
40142
  }
40137
40143
  });
40138
40144
 
@@ -40605,19 +40611,63 @@ var require_LogTemplateEngine = __commonJS({
40605
40611
  }
40606
40612
  });
40607
40613
 
40614
+ // ../sidekick-docker-shared/dist/reconnect.js
40615
+ var require_reconnect = __commonJS({
40616
+ "../sidekick-docker-shared/dist/reconnect.js"(exports2) {
40617
+ "use strict";
40618
+ Object.defineProperty(exports2, "__esModule", { value: true });
40619
+ exports2.ReconnectScheduler = exports2.MAX_RECONNECT_ATTEMPTS = exports2.MAX_RECONNECT_DELAY = exports2.INITIAL_RECONNECT_DELAY = void 0;
40620
+ exports2.INITIAL_RECONNECT_DELAY = 2e3;
40621
+ exports2.MAX_RECONNECT_DELAY = 3e4;
40622
+ exports2.MAX_RECONNECT_ATTEMPTS = 10;
40623
+ var ReconnectScheduler4 = class {
40624
+ timer = null;
40625
+ attempts = 0;
40626
+ delay = exports2.INITIAL_RECONNECT_DELAY;
40627
+ /** Schedule a reconnect callback with exponential backoff. Returns false if max attempts reached. */
40628
+ schedule(callback) {
40629
+ if (this.attempts >= exports2.MAX_RECONNECT_ATTEMPTS) {
40630
+ return false;
40631
+ }
40632
+ this.clear();
40633
+ this.timer = setTimeout(() => {
40634
+ this.timer = null;
40635
+ this.attempts++;
40636
+ this.delay = Math.min(this.delay * 2, exports2.MAX_RECONNECT_DELAY);
40637
+ callback();
40638
+ }, this.delay);
40639
+ return true;
40640
+ }
40641
+ /** Clear any pending reconnect timer. */
40642
+ clear() {
40643
+ if (this.timer) {
40644
+ clearTimeout(this.timer);
40645
+ this.timer = null;
40646
+ }
40647
+ }
40648
+ /** Reset backoff state (call after a successful stream). */
40649
+ reset() {
40650
+ this.attempts = 0;
40651
+ this.delay = exports2.INITIAL_RECONNECT_DELAY;
40652
+ }
40653
+ };
40654
+ exports2.ReconnectScheduler = ReconnectScheduler4;
40655
+ }
40656
+ });
40657
+
40608
40658
  // ../sidekick-docker-shared/dist/events/EventWatcher.js
40609
40659
  var require_EventWatcher = __commonJS({
40610
40660
  "../sidekick-docker-shared/dist/events/EventWatcher.js"(exports2) {
40611
40661
  "use strict";
40612
40662
  Object.defineProperty(exports2, "__esModule", { value: true });
40613
40663
  exports2.EventWatcher = void 0;
40664
+ var reconnect_1 = require_reconnect();
40614
40665
  var EventWatcher2 = class {
40615
40666
  client;
40616
40667
  callbacks;
40617
40668
  running = false;
40618
40669
  abortController = null;
40619
- reconnectDelay = 1e3;
40620
- maxReconnectDelay = 3e4;
40670
+ reconnectDelay = reconnect_1.INITIAL_RECONNECT_DELAY;
40621
40671
  constructor(client, callbacks) {
40622
40672
  this.client = client;
40623
40673
  this.callbacks = callbacks;
@@ -40626,7 +40676,7 @@ var require_EventWatcher = __commonJS({
40626
40676
  if (this.running)
40627
40677
  return;
40628
40678
  this.running = true;
40629
- this.reconnectDelay = 1e3;
40679
+ this.reconnectDelay = reconnect_1.INITIAL_RECONNECT_DELAY;
40630
40680
  this.watch();
40631
40681
  }
40632
40682
  stop() {
@@ -40641,12 +40691,12 @@ var require_EventWatcher = __commonJS({
40641
40691
  while (this.running) {
40642
40692
  try {
40643
40693
  this.abortController = new AbortController();
40644
- for await (const event of this.client.streamEvents()) {
40694
+ for await (const event of this.client.streamEvents(void 0, this.abortController.signal)) {
40645
40695
  if (!this.running)
40646
40696
  break;
40647
40697
  this.callbacks.onEvent(event);
40648
40698
  }
40649
- this.reconnectDelay = 1e3;
40699
+ this.reconnectDelay = reconnect_1.INITIAL_RECONNECT_DELAY;
40650
40700
  } catch (err) {
40651
40701
  if (!this.running)
40652
40702
  break;
@@ -40655,15 +40705,22 @@ var require_EventWatcher = __commonJS({
40655
40705
  if (!this.running)
40656
40706
  break;
40657
40707
  this.callbacks.onReconnect?.();
40658
- await sleep(this.reconnectDelay);
40659
- this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
40660
- }
40708
+ await this.cancellableSleep(this.reconnectDelay);
40709
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, reconnect_1.MAX_RECONNECT_DELAY);
40710
+ }
40711
+ }
40712
+ /** Sleep that resolves early when stop() is called. */
40713
+ cancellableSleep(ms) {
40714
+ return new Promise((resolve) => {
40715
+ const timer = setTimeout(resolve, ms);
40716
+ this.abortController?.signal.addEventListener("abort", () => {
40717
+ clearTimeout(timer);
40718
+ resolve();
40719
+ }, { once: true });
40720
+ });
40661
40721
  }
40662
40722
  };
40663
40723
  exports2.EventWatcher = EventWatcher2;
40664
- function sleep(ms) {
40665
- return new Promise((resolve) => setTimeout(resolve, ms));
40666
- }
40667
40724
  }
40668
40725
  });
40669
40726
 
@@ -40748,6 +40805,18 @@ var require_formatters = __commonJS({
40748
40805
  }
40749
40806
  });
40750
40807
 
40808
+ // ../sidekick-docker-shared/dist/errors.js
40809
+ var require_errors2 = __commonJS({
40810
+ "../sidekick-docker-shared/dist/errors.js"(exports2) {
40811
+ "use strict";
40812
+ Object.defineProperty(exports2, "__esModule", { value: true });
40813
+ exports2.errorMessage = errorMessage5;
40814
+ function errorMessage5(err) {
40815
+ return err instanceof Error ? err.message : String(err);
40816
+ }
40817
+ }
40818
+ });
40819
+
40751
40820
  // ../sidekick-docker-shared/dist/branding.js
40752
40821
  var require_branding = __commonJS({
40753
40822
  "../sidekick-docker-shared/dist/branding.js"(exports2) {
@@ -41472,7 +41541,7 @@ var require_dist = __commonJS({
41472
41541
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
41473
41542
  };
41474
41543
  Object.defineProperty(exports2, "__esModule", { value: true });
41475
- exports2.getRandomPhrase = exports2.BRAND_COLOR_ANSI_RESET = exports2.BRAND_COLOR_ANSI = exports2.BRAND_COLOR_HEX = exports2.BRAND_TAGLINE = exports2.BRAND_INLINE = exports2.stateColor = exports2.truncate = exports2.stateIcon = exports2.formatPorts = exports2.formatMemory = exports2.formatCpu = exports2.formatBytes = exports2.EventWatcher = exports2.LogTemplateEngine = exports2.LogSeverityTimeSeries = exports2.parseLine = exports2.detectFormat = exports2.detectSeverity = exports2.LogAnalytics = exports2.filterLine = exports2.fuzzyMatch = exports2.exactMatch = exports2.tokenizeLogLine = exports2.StatsCollector = exports2.ComposeFileReader = exports2.ComposeClient = exports2.ComposeDetector = exports2.DockerClient = void 0;
41544
+ exports2.getRandomPhrase = exports2.BRAND_COLOR_ANSI_RESET = exports2.BRAND_COLOR_ANSI = exports2.BRAND_COLOR_HEX = exports2.BRAND_TAGLINE = exports2.BRAND_INLINE = exports2.MAX_LOG_LINES = exports2.errorMessage = exports2.MAX_RECONNECT_ATTEMPTS = exports2.MAX_RECONNECT_DELAY = exports2.INITIAL_RECONNECT_DELAY = exports2.ReconnectScheduler = exports2.stateColor = exports2.truncate = exports2.stateIcon = exports2.formatPorts = exports2.formatMemory = exports2.formatCpu = exports2.formatBytes = exports2.EventWatcher = exports2.LogTemplateEngine = exports2.LogSeverityTimeSeries = exports2.parseLine = exports2.detectFormat = exports2.detectSeverity = exports2.LogAnalytics = exports2.filterLine = exports2.fuzzyMatch = exports2.exactMatch = exports2.tokenizeLogLine = exports2.StatsCollector = exports2.ComposeFileReader = exports2.ComposeClient = exports2.ComposeDetector = exports2.DockerClient = void 0;
41476
41545
  __exportStar(require_types(), exports2);
41477
41546
  var DockerClient_1 = require_DockerClient();
41478
41547
  Object.defineProperty(exports2, "DockerClient", { enumerable: true, get: function() {
@@ -41556,6 +41625,24 @@ var require_dist = __commonJS({
41556
41625
  Object.defineProperty(exports2, "stateColor", { enumerable: true, get: function() {
41557
41626
  return formatters_1.stateColor;
41558
41627
  } });
41628
+ var reconnect_1 = require_reconnect();
41629
+ Object.defineProperty(exports2, "ReconnectScheduler", { enumerable: true, get: function() {
41630
+ return reconnect_1.ReconnectScheduler;
41631
+ } });
41632
+ Object.defineProperty(exports2, "INITIAL_RECONNECT_DELAY", { enumerable: true, get: function() {
41633
+ return reconnect_1.INITIAL_RECONNECT_DELAY;
41634
+ } });
41635
+ Object.defineProperty(exports2, "MAX_RECONNECT_DELAY", { enumerable: true, get: function() {
41636
+ return reconnect_1.MAX_RECONNECT_DELAY;
41637
+ } });
41638
+ Object.defineProperty(exports2, "MAX_RECONNECT_ATTEMPTS", { enumerable: true, get: function() {
41639
+ return reconnect_1.MAX_RECONNECT_ATTEMPTS;
41640
+ } });
41641
+ var errors_1 = require_errors2();
41642
+ Object.defineProperty(exports2, "errorMessage", { enumerable: true, get: function() {
41643
+ return errors_1.errorMessage;
41644
+ } });
41645
+ exports2.MAX_LOG_LINES = 1e3;
41559
41646
  var branding_1 = require_branding();
41560
41647
  Object.defineProperty(exports2, "BRAND_INLINE", { enumerable: true, get: function() {
41561
41648
  return branding_1.BRAND_INLINE;
@@ -77874,8 +77961,8 @@ var {
77874
77961
  } = import_index.default;
77875
77962
 
77876
77963
  // src/commands/dashboard.ts
77877
- var import_react36 = __toESM(require_react(), 1);
77878
- var import_sidekick_docker_shared10 = __toESM(require_dist(), 1);
77964
+ var import_react37 = __toESM(require_react(), 1);
77965
+ var import_sidekick_docker_shared13 = __toESM(require_dist(), 1);
77879
77966
  import { spawnSync } from "child_process";
77880
77967
 
77881
77968
  // src/dashboard/DockerState.ts
@@ -77931,8 +78018,7 @@ var DockerState = class {
77931
78018
  case "image":
77932
78019
  case "volume":
77933
78020
  case "network":
77934
- this.refresh().catch(() => {
77935
- });
78021
+ this.refresh().catch((e) => console.debug("refresh failed:", e));
77936
78022
  break;
77937
78023
  }
77938
78024
  }
@@ -77944,11 +78030,10 @@ var DockerState = class {
77944
78030
  case "unpause": {
77945
78031
  const existing = this.containers.find((c) => c.id === resourceId);
77946
78032
  if (existing) {
77947
- existing.state = type === "unpause" ? "running" : "running";
78033
+ existing.state = "running";
77948
78034
  existing.status = "Up just now";
77949
78035
  }
77950
- this.refresh().catch(() => {
77951
- });
78036
+ this.refresh().catch((e) => console.debug("refresh failed:", e));
77952
78037
  break;
77953
78038
  }
77954
78039
  case "stop":
@@ -77972,13 +78057,11 @@ var DockerState = class {
77972
78057
  this.statsCollector.remove(resourceId);
77973
78058
  break;
77974
78059
  case "create":
77975
- this.refresh().catch(() => {
77976
- });
78060
+ this.refresh().catch((e) => console.debug("refresh failed:", e));
77977
78061
  break;
77978
78062
  default:
77979
78063
  if (name) {
77980
- this.refresh().catch(() => {
77981
- });
78064
+ this.refresh().catch((e) => console.debug("refresh failed:", e));
77982
78065
  }
77983
78066
  break;
77984
78067
  }
@@ -77989,7 +78072,7 @@ var DockerState = class {
77989
78072
  }
77990
78073
  appendLog(entry) {
77991
78074
  this.selectedLogs.push(entry);
77992
- if (this.selectedLogs.length > 1e3) {
78075
+ if (this.selectedLogs.length > import_sidekick_docker_shared.MAX_LOG_LINES) {
77993
78076
  this.selectedLogs.shift();
77994
78077
  }
77995
78078
  }
@@ -77998,7 +78081,7 @@ var DockerState = class {
77998
78081
  }
77999
78082
  appendComposeLog(entry) {
78000
78083
  this.selectedComposeLogs.push(entry);
78001
- if (this.selectedComposeLogs.length > 1e3) {
78084
+ if (this.selectedComposeLogs.length > import_sidekick_docker_shared.MAX_LOG_LINES) {
78002
78085
  this.selectedComposeLogs.shift();
78003
78086
  }
78004
78087
  }
@@ -78039,6 +78122,11 @@ var DockerState = class {
78039
78122
  // src/dashboard/panels/ContainersPanel.ts
78040
78123
  var import_sidekick_docker_shared4 = __toESM(require_dist(), 1);
78041
78124
 
78125
+ // src/dashboard/panels/types.ts
78126
+ var defaultOnError = (msg) => {
78127
+ console.debug(msg);
78128
+ };
78129
+
78042
78130
  // src/formatters.ts
78043
78131
  var import_sidekick_docker_shared2 = __toESM(require_dist(), 1);
78044
78132
  var import_sidekick_docker_shared3 = __toESM(require_dist(), 1);
@@ -78226,10 +78314,12 @@ var ContainersPanel = class {
78226
78314
  shortcutKey = 1;
78227
78315
  client;
78228
78316
  onAction;
78317
+ onError;
78229
78318
  onExec;
78230
- constructor(client, onAction) {
78319
+ constructor(client, onAction, onError) {
78231
78320
  this.client = client;
78232
78321
  this.onAction = onAction;
78322
+ this.onError = onError ?? defaultOnError;
78233
78323
  }
78234
78324
  setOnExec(handler) {
78235
78325
  this.onExec = handler;
@@ -78385,8 +78475,7 @@ var ContainersPanel = class {
78385
78475
  label: "Start",
78386
78476
  handler: (item) => {
78387
78477
  const c = item.data;
78388
- this.client.startContainer(c.id).then(() => this.onAction()).catch(() => {
78389
- });
78478
+ this.client.startContainer(c.id).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78390
78479
  },
78391
78480
  condition: (item) => item.data.state !== "running"
78392
78481
  },
@@ -78395,8 +78484,7 @@ var ContainersPanel = class {
78395
78484
  label: "Stop",
78396
78485
  handler: (item) => {
78397
78486
  const c = item.data;
78398
- this.client.stopContainer(c.id).then(() => this.onAction()).catch(() => {
78399
- });
78487
+ this.client.stopContainer(c.id).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78400
78488
  },
78401
78489
  condition: (item) => item.data.state === "running"
78402
78490
  },
@@ -78405,8 +78493,7 @@ var ContainersPanel = class {
78405
78493
  label: "Restart",
78406
78494
  handler: (item) => {
78407
78495
  const c = item.data;
78408
- this.client.restartContainer(c.id).then(() => this.onAction()).catch(() => {
78409
- });
78496
+ this.client.restartContainer(c.id).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78410
78497
  },
78411
78498
  condition: (item) => item.data.state === "running"
78412
78499
  },
@@ -78417,8 +78504,7 @@ var ContainersPanel = class {
78417
78504
  confirmMessage: "Remove this container?",
78418
78505
  handler: (item) => {
78419
78506
  const c = item.data;
78420
- this.client.removeContainer(c.id, true).then(() => this.onAction()).catch(() => {
78421
- });
78507
+ this.client.removeContainer(c.id, true).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78422
78508
  }
78423
78509
  },
78424
78510
  {
@@ -78439,16 +78525,21 @@ var ContainersPanel = class {
78439
78525
  };
78440
78526
 
78441
78527
  // src/dashboard/panels/ServicesPanel.ts
78528
+ function getProjectName(d) {
78529
+ return d.type === "project" ? d.project.name : d.service.projectName;
78530
+ }
78442
78531
  var ServicesPanel = class {
78443
78532
  id = "services";
78444
78533
  title = "Services";
78445
78534
  shortcutKey = 2;
78446
78535
  composeClient;
78447
78536
  onAction;
78537
+ onError;
78448
78538
  cwd;
78449
- constructor(composeClient, onAction, cwd2) {
78539
+ constructor(composeClient, onAction, cwd2, onError) {
78450
78540
  this.composeClient = composeClient;
78451
78541
  this.onAction = onAction;
78542
+ this.onError = onError ?? defaultOnError;
78452
78543
  this.cwd = cwd2;
78453
78544
  }
78454
78545
  detailTabs = [
@@ -78533,9 +78624,7 @@ var ServicesPanel = class {
78533
78624
  label: "Up",
78534
78625
  handler: (item) => {
78535
78626
  const d = item.data;
78536
- const projectName = d.type === "project" ? d.project.name : d.service.projectName;
78537
- this.composeClient.up(projectName, this.cwd).then(() => this.onAction()).catch(() => {
78538
- });
78627
+ this.composeClient.up(getProjectName(d), this.cwd).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78539
78628
  },
78540
78629
  condition: (item) => item.data !== null
78541
78630
  },
@@ -78546,9 +78635,7 @@ var ServicesPanel = class {
78546
78635
  confirmMessage: "Bring down this compose project?",
78547
78636
  handler: (item) => {
78548
78637
  const d = item.data;
78549
- const projectName = d.type === "project" ? d.project.name : d.service.projectName;
78550
- this.composeClient.down(projectName, this.cwd).then(() => this.onAction()).catch(() => {
78551
- });
78638
+ this.composeClient.down(getProjectName(d), this.cwd).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78552
78639
  },
78553
78640
  condition: (item) => item.data !== null
78554
78641
  },
@@ -78558,11 +78645,9 @@ var ServicesPanel = class {
78558
78645
  handler: (item) => {
78559
78646
  const d = item.data;
78560
78647
  if (d.type === "service") {
78561
- this.composeClient.restart(d.service.projectName, d.service.name, this.cwd).then(() => this.onAction()).catch(() => {
78562
- });
78648
+ this.composeClient.restart(d.service.projectName, d.service.name, this.cwd).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78563
78649
  } else {
78564
- this.composeClient.restart(d.project.name, void 0, this.cwd).then(() => this.onAction()).catch(() => {
78565
- });
78650
+ this.composeClient.restart(d.project.name, void 0, this.cwd).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78566
78651
  }
78567
78652
  },
78568
78653
  condition: (item) => item.data !== null
@@ -78573,11 +78658,9 @@ var ServicesPanel = class {
78573
78658
  handler: (item) => {
78574
78659
  const d = item.data;
78575
78660
  if (d.type === "service") {
78576
- this.composeClient.stop(d.service.projectName, d.service.name, this.cwd).then(() => this.onAction()).catch(() => {
78577
- });
78661
+ this.composeClient.stop(d.service.projectName, d.service.name, this.cwd).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78578
78662
  } else {
78579
- this.composeClient.stop(d.project.name, void 0, this.cwd).then(() => this.onAction()).catch(() => {
78580
- });
78663
+ this.composeClient.stop(d.project.name, void 0, this.cwd).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78581
78664
  }
78582
78665
  },
78583
78666
  condition: (item) => item.data !== null
@@ -78599,9 +78682,11 @@ var ImagesPanel = class {
78599
78682
  shortcutKey = 3;
78600
78683
  client;
78601
78684
  onAction;
78602
- constructor(client, onAction) {
78685
+ onError;
78686
+ constructor(client, onAction, onError) {
78603
78687
  this.client = client;
78604
78688
  this.onAction = onAction;
78689
+ this.onError = onError ?? defaultOnError;
78605
78690
  }
78606
78691
  detailTabs = [
78607
78692
  {
@@ -78642,8 +78727,7 @@ var ImagesPanel = class {
78642
78727
  confirmMessage: "Remove this image?",
78643
78728
  handler: (item) => {
78644
78729
  const img = item.data;
78645
- this.client.removeImage(img.id).then(() => this.onAction()).catch(() => {
78646
- });
78730
+ this.client.removeImage(img.id).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78647
78731
  }
78648
78732
  },
78649
78733
  {
@@ -78652,8 +78736,7 @@ var ImagesPanel = class {
78652
78736
  confirm: true,
78653
78737
  confirmMessage: "Prune all dangling images?",
78654
78738
  handler: () => {
78655
- this.client.pruneImages().then(() => this.onAction()).catch(() => {
78656
- });
78739
+ this.client.pruneImages().then(() => this.onAction()).catch((e) => this.onError(String(e)));
78657
78740
  }
78658
78741
  }
78659
78742
  ];
@@ -78671,9 +78754,11 @@ var VolumesPanel = class {
78671
78754
  shortcutKey = 4;
78672
78755
  client;
78673
78756
  onAction;
78674
- constructor(client, onAction) {
78757
+ onError;
78758
+ constructor(client, onAction, onError) {
78675
78759
  this.client = client;
78676
78760
  this.onAction = onAction;
78761
+ this.onError = onError ?? defaultOnError;
78677
78762
  }
78678
78763
  detailTabs = [
78679
78764
  {
@@ -78713,8 +78798,7 @@ var VolumesPanel = class {
78713
78798
  confirmMessage: "Remove this volume?",
78714
78799
  handler: (item) => {
78715
78800
  const vol = item.data;
78716
- this.client.removeVolume(vol.name).then(() => this.onAction()).catch(() => {
78717
- });
78801
+ this.client.removeVolume(vol.name).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78718
78802
  },
78719
78803
  condition: (item) => !item.data.isInUse
78720
78804
  },
@@ -78724,8 +78808,7 @@ var VolumesPanel = class {
78724
78808
  confirm: true,
78725
78809
  confirmMessage: "Prune all unused volumes?",
78726
78810
  handler: () => {
78727
- this.client.pruneVolumes().then(() => this.onAction()).catch(() => {
78728
- });
78811
+ this.client.pruneVolumes().then(() => this.onAction()).catch((e) => this.onError(String(e)));
78729
78812
  }
78730
78813
  }
78731
78814
  ];
@@ -78743,9 +78826,11 @@ var NetworksPanel = class {
78743
78826
  shortcutKey = 5;
78744
78827
  client;
78745
78828
  onAction;
78746
- constructor(client, onAction) {
78829
+ onError;
78830
+ constructor(client, onAction, onError) {
78747
78831
  this.client = client;
78748
78832
  this.onAction = onAction;
78833
+ this.onError = onError ?? defaultOnError;
78749
78834
  }
78750
78835
  detailTabs = [
78751
78836
  {
@@ -78795,8 +78880,7 @@ var NetworksPanel = class {
78795
78880
  confirmMessage: "Remove this network?",
78796
78881
  handler: (item) => {
78797
78882
  const net = item.data;
78798
- this.client.removeNetwork(net.id).then(() => this.onAction()).catch(() => {
78799
- });
78883
+ this.client.removeNetwork(net.id).then(() => this.onAction()).catch((e) => this.onError(String(e)));
78800
78884
  },
78801
78885
  condition: (item) => {
78802
78886
  const net = item.data;
@@ -78809,8 +78893,7 @@ var NetworksPanel = class {
78809
78893
  confirm: true,
78810
78894
  confirmMessage: "Prune all unused networks?",
78811
78895
  handler: () => {
78812
- this.client.pruneNetworks().then(() => this.onAction()).catch(() => {
78813
- });
78896
+ this.client.pruneNetworks().then(() => this.onAction()).catch((e) => this.onError(String(e)));
78814
78897
  }
78815
78898
  }
78816
78899
  ];
@@ -78823,13 +78906,13 @@ var NetworksPanel = class {
78823
78906
 
78824
78907
  // src/dashboard/LogStreamManager.ts
78825
78908
  var import_sidekick_docker_shared5 = __toESM(require_dist(), 1);
78826
- var MAX_LOG_LINES = 1e3;
78827
78909
  var LogStreamManager = class {
78828
78910
  client;
78829
78911
  currentContainerId = null;
78830
78912
  logs = [];
78831
78913
  aborted = false;
78832
78914
  streamPromise = null;
78915
+ reconnect = new import_sidekick_docker_shared5.ReconnectScheduler();
78833
78916
  onChange;
78834
78917
  analytics = new import_sidekick_docker_shared5.LogAnalytics();
78835
78918
  timeSeries = new import_sidekick_docker_shared5.LogSeverityTimeSeries();
@@ -78849,6 +78932,7 @@ var LogStreamManager = class {
78849
78932
  this.templateEngine.reset();
78850
78933
  if (!containerId) return;
78851
78934
  this.aborted = false;
78935
+ this.reconnect.reset();
78852
78936
  this.streamPromise = this.streamLogs(containerId);
78853
78937
  }
78854
78938
  async streamLogs(containerId) {
@@ -78862,18 +78946,31 @@ var LogStreamManager = class {
78862
78946
  const severity = this.analytics.push(entry.message);
78863
78947
  this.timeSeries.push(severity);
78864
78948
  this.templateEngine.push(entry.message);
78865
- if (this.logs.length > MAX_LOG_LINES) {
78949
+ if (this.logs.length > import_sidekick_docker_shared5.MAX_LOG_LINES) {
78866
78950
  this.logs.shift();
78867
78951
  }
78868
78952
  this.onChange();
78869
78953
  }
78870
- } catch {
78954
+ this.reconnect.reset();
78955
+ } catch (err) {
78956
+ console.debug("log stream error:", (0, import_sidekick_docker_shared5.errorMessage)(err));
78957
+ }
78958
+ if (!this.aborted && this.currentContainerId === containerId) {
78959
+ const scheduled = this.reconnect.schedule(() => {
78960
+ if (!this.aborted && this.currentContainerId === containerId) {
78961
+ this.streamPromise = this.streamLogs(containerId);
78962
+ }
78963
+ });
78964
+ if (!scheduled) {
78965
+ console.debug(`log stream: gave up reconnecting for ${containerId}`);
78966
+ }
78871
78967
  }
78872
78968
  }
78873
78969
  stop() {
78874
78970
  this.aborted = true;
78875
78971
  this.currentContainerId = null;
78876
78972
  this.streamPromise = null;
78973
+ this.reconnect.clear();
78877
78974
  }
78878
78975
  getLogs() {
78879
78976
  return this.logs;
@@ -78896,12 +78993,14 @@ var LogStreamManager = class {
78896
78993
  };
78897
78994
 
78898
78995
  // src/dashboard/StatsStreamManager.ts
78996
+ var import_sidekick_docker_shared6 = __toESM(require_dist(), 1);
78899
78997
  var StatsStreamManager = class {
78900
78998
  client;
78901
78999
  collector;
78902
79000
  currentContainerId = null;
78903
79001
  aborted = false;
78904
79002
  streamPromise = null;
79003
+ reconnect = new import_sidekick_docker_shared6.ReconnectScheduler();
78905
79004
  onChange;
78906
79005
  loadingInterval = null;
78907
79006
  constructor(client, collector, onChange) {
@@ -78916,6 +79015,7 @@ var StatsStreamManager = class {
78916
79015
  this.currentContainerId = containerId;
78917
79016
  if (!containerId) return;
78918
79017
  this.aborted = false;
79018
+ this.reconnect.reset();
78919
79019
  this.loadingInterval = setInterval(() => this.onChange(), 200);
78920
79020
  this.streamPromise = this.streamStats(containerId);
78921
79021
  }
@@ -78933,12 +79033,26 @@ var StatsStreamManager = class {
78933
79033
  this.clearLoadingInterval();
78934
79034
  this.onChange();
78935
79035
  }
78936
- } catch {
79036
+ this.reconnect.reset();
79037
+ } catch (err) {
79038
+ console.debug("stats stream error:", (0, import_sidekick_docker_shared6.errorMessage)(err));
79039
+ }
79040
+ if (!this.aborted && this.currentContainerId === containerId) {
79041
+ const scheduled = this.reconnect.schedule(() => {
79042
+ if (!this.aborted && this.currentContainerId === containerId) {
79043
+ this.loadingInterval = setInterval(() => this.onChange(), 200);
79044
+ this.streamPromise = this.streamStats(containerId);
79045
+ }
79046
+ });
79047
+ if (!scheduled) {
79048
+ console.debug(`stats stream: gave up reconnecting for ${containerId}`);
79049
+ }
78937
79050
  }
78938
79051
  }
78939
79052
  stop() {
78940
79053
  this.aborted = true;
78941
79054
  this.clearLoadingInterval();
79055
+ this.reconnect.clear();
78942
79056
  this.currentContainerId = null;
78943
79057
  this.streamPromise = null;
78944
79058
  }
@@ -78950,18 +79064,18 @@ var StatsStreamManager = class {
78950
79064
  }
78951
79065
  dispose() {
78952
79066
  this.stop();
78953
- this.clearLoadingInterval();
78954
79067
  }
78955
79068
  };
78956
79069
 
78957
79070
  // src/dashboard/ComposeLogStreamManager.ts
78958
- var MAX_LOG_LINES2 = 1e3;
79071
+ var import_sidekick_docker_shared7 = __toESM(require_dist(), 1);
78959
79072
  var ComposeLogStreamManager = class {
78960
79073
  composeClient;
78961
79074
  currentProject = null;
78962
79075
  currentService = null;
78963
79076
  logs = [];
78964
79077
  aborted = false;
79078
+ reconnect = new import_sidekick_docker_shared7.ReconnectScheduler();
78965
79079
  onChange;
78966
79080
  constructor(composeClient, onChange) {
78967
79081
  this.composeClient = composeClient;
@@ -78975,6 +79089,7 @@ var ComposeLogStreamManager = class {
78975
79089
  this.logs = [];
78976
79090
  if (!project) return;
78977
79091
  this.aborted = false;
79092
+ this.reconnect.reset();
78978
79093
  this.streamLogs(project, service);
78979
79094
  }
78980
79095
  async streamLogs(project, service) {
@@ -78982,18 +79097,31 @@ var ComposeLogStreamManager = class {
78982
79097
  for await (const entry of this.composeClient.streamLogs(project, service ?? void 0)) {
78983
79098
  if (this.aborted || this.currentProject !== project) break;
78984
79099
  this.logs.push(entry);
78985
- if (this.logs.length > MAX_LOG_LINES2) {
79100
+ if (this.logs.length > import_sidekick_docker_shared7.MAX_LOG_LINES) {
78986
79101
  this.logs.shift();
78987
79102
  }
78988
79103
  this.onChange();
78989
79104
  }
78990
- } catch {
79105
+ this.reconnect.reset();
79106
+ } catch (err) {
79107
+ console.debug("compose log stream error:", (0, import_sidekick_docker_shared7.errorMessage)(err));
79108
+ }
79109
+ if (!this.aborted && this.currentProject === project) {
79110
+ const scheduled = this.reconnect.schedule(() => {
79111
+ if (!this.aborted && this.currentProject === project) {
79112
+ this.streamLogs(project, service);
79113
+ }
79114
+ });
79115
+ if (!scheduled) {
79116
+ console.debug(`compose log stream: gave up reconnecting for ${project}`);
79117
+ }
78991
79118
  }
78992
79119
  }
78993
79120
  stop() {
78994
79121
  this.aborted = true;
78995
79122
  this.currentProject = null;
78996
79123
  this.currentService = null;
79124
+ this.reconnect.clear();
78997
79125
  }
78998
79126
  getLogs() {
78999
79127
  return this.logs;
@@ -79004,7 +79132,7 @@ var ComposeLogStreamManager = class {
79004
79132
  };
79005
79133
 
79006
79134
  // src/dashboard/ink/Dashboard.tsx
79007
- var import_react35 = __toESM(require_react(), 1);
79135
+ var import_react36 = __toESM(require_react(), 1);
79008
79136
  await init_build2();
79009
79137
 
79010
79138
  // src/dashboard/ink/useTerminalSize.ts
@@ -79077,6 +79205,324 @@ function useWindowedScroll({ totalItems, viewportHeight }) {
79077
79205
  };
79078
79206
  }
79079
79207
 
79208
+ // src/dashboard/ink/useKeyboardHandler.ts
79209
+ await init_build2();
79210
+ function executeAction(action, item, dispatch, addToast) {
79211
+ if (action.confirm) {
79212
+ dispatch({ type: "SET_CONFIRM", action: () => {
79213
+ action.handler(item);
79214
+ addToast(action.label, "info");
79215
+ }, message: action.confirmMessage || "Are you sure?" });
79216
+ } else {
79217
+ action.handler(item);
79218
+ addToast(action.label, "info");
79219
+ }
79220
+ }
79221
+ function handleFilterInput(input, key, opts) {
79222
+ const { currentValue, setAction, clearToast, dispatch, addToast, onTab } = opts;
79223
+ if (key.escape) {
79224
+ if (currentValue && clearToast) addToast(clearToast, "info");
79225
+ dispatch({ type: setAction, value: "" });
79226
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79227
+ return;
79228
+ }
79229
+ if (key.return) {
79230
+ if (setAction === "SET_FILTER" && currentValue) addToast(`Filter: "${currentValue}"`, "info");
79231
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79232
+ return;
79233
+ }
79234
+ if (key.tab && onTab) {
79235
+ onTab();
79236
+ return;
79237
+ }
79238
+ if (key.backspace || key.delete) {
79239
+ dispatch({ type: setAction, value: currentValue.slice(0, -1) });
79240
+ return;
79241
+ }
79242
+ if (input && !key.ctrl && !key.meta) {
79243
+ dispatch({ type: setAction, value: currentValue + input });
79244
+ }
79245
+ }
79246
+ function useKeyboardHandler(ctx) {
79247
+ const { exit } = use_app_default();
79248
+ const { state, dispatch, panels, panel, selectedItem, contextActions, clampedSelection, currentItems, detailLines, detailViewportHeight, detailTabs, tabIdx, panelActions, sideScroll, addToast, rotatePhrase } = ctx;
79249
+ use_input_default((input, key) => {
79250
+ if (state.overlay === "exec") return;
79251
+ rotatePhrase();
79252
+ if (input === "q" || key.ctrl && input === "c") {
79253
+ if (state.overlay) {
79254
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79255
+ return;
79256
+ }
79257
+ exit();
79258
+ return;
79259
+ }
79260
+ if (state.overlay === "confirm") {
79261
+ if (input === "y" || input === "Y") {
79262
+ state.confirmAction?.();
79263
+ dispatch({ type: "SET_CONFIRM", action: null, message: "" });
79264
+ return;
79265
+ }
79266
+ if (input === "n" || input === "N" || key.escape) {
79267
+ dispatch({ type: "SET_CONFIRM", action: null, message: "" });
79268
+ return;
79269
+ }
79270
+ return;
79271
+ }
79272
+ if (state.overlay === "filter") {
79273
+ handleFilterInput(input, key, {
79274
+ currentValue: state.filterString,
79275
+ setAction: "SET_FILTER",
79276
+ clearToast: "Filter cleared",
79277
+ dispatch,
79278
+ addToast
79279
+ });
79280
+ return;
79281
+ }
79282
+ if (state.overlay === "log-filter") {
79283
+ handleFilterInput(input, key, {
79284
+ currentValue: state.logFilterString,
79285
+ setAction: "SET_LOG_FILTER",
79286
+ clearToast: "Log filter cleared",
79287
+ dispatch,
79288
+ addToast,
79289
+ onTab: () => dispatch({ type: "TOGGLE_LOG_FILTER_MODE" })
79290
+ });
79291
+ return;
79292
+ }
79293
+ if (state.overlay === "context-menu") {
79294
+ if (key.escape) {
79295
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79296
+ return;
79297
+ }
79298
+ if (input === "j" || key.downArrow) {
79299
+ dispatch({ type: "CONTEXT_MENU_NAV", delta: 1, itemCount: contextActions.length });
79300
+ return;
79301
+ }
79302
+ if (input === "k" || key.upArrow) {
79303
+ dispatch({ type: "CONTEXT_MENU_NAV", delta: -1, itemCount: contextActions.length });
79304
+ return;
79305
+ }
79306
+ if (key.return) {
79307
+ const action = contextActions[state.contextMenuIndex];
79308
+ if (action && selectedItem) {
79309
+ executeAction(action, selectedItem, dispatch, addToast);
79310
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79311
+ }
79312
+ return;
79313
+ }
79314
+ const match = contextActions.find((a) => a.key === input);
79315
+ if (match && selectedItem) {
79316
+ executeAction(match, selectedItem, dispatch, addToast);
79317
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79318
+ }
79319
+ return;
79320
+ }
79321
+ if (state.overlay === "help") {
79322
+ if (key.escape || input === "?") {
79323
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79324
+ }
79325
+ return;
79326
+ }
79327
+ if (state.overlay === "version") {
79328
+ if (key.escape || input === "V") {
79329
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79330
+ }
79331
+ return;
79332
+ }
79333
+ if (key.escape) {
79334
+ if (state.filterString) {
79335
+ dispatch({ type: "SET_FILTER", value: "" });
79336
+ return;
79337
+ }
79338
+ if (state.focusTarget === "detail") {
79339
+ dispatch({ type: "SET_FOCUS", target: "side" });
79340
+ return;
79341
+ }
79342
+ return;
79343
+ }
79344
+ if (input === "?") {
79345
+ dispatch({ type: "SET_OVERLAY", overlay: "help" });
79346
+ return;
79347
+ }
79348
+ if (input === "V") {
79349
+ dispatch({ type: "SET_OVERLAY", overlay: "version" });
79350
+ return;
79351
+ }
79352
+ const num = parseInt(input, 10);
79353
+ if (num >= 1 && num <= panels.length) {
79354
+ panels[state.activePanelIndex]?.onDeactivate?.();
79355
+ dispatch({ type: "SWITCH_PANEL", index: num - 1 });
79356
+ panels[num - 1]?.onActivate?.();
79357
+ return;
79358
+ }
79359
+ if (key.tab) {
79360
+ dispatch({ type: "TOGGLE_FOCUS" });
79361
+ return;
79362
+ }
79363
+ if (input === "z") {
79364
+ dispatch({ type: "CYCLE_LAYOUT" });
79365
+ addToast(`Layout: ${state.layoutMode === "normal" ? "Expanded" : "Normal"}`, "info");
79366
+ return;
79367
+ }
79368
+ if (input === "/") {
79369
+ dispatch({ type: "SET_OVERLAY", overlay: "filter" });
79370
+ return;
79371
+ }
79372
+ if (input === "f") {
79373
+ if (panel.id === "containers" && tabIdx === 0) {
79374
+ dispatch({ type: "SET_OVERLAY", overlay: "log-filter" });
79375
+ return;
79376
+ }
79377
+ }
79378
+ if (input === "x") {
79379
+ if (selectedItem && panelActions.length > 0) {
79380
+ dispatch({ type: "SET_OVERLAY", overlay: "context-menu" });
79381
+ }
79382
+ return;
79383
+ }
79384
+ if (input === "[") {
79385
+ dispatch({ type: "CYCLE_DETAIL_TAB", direction: -1, tabCount: detailTabs.length });
79386
+ return;
79387
+ }
79388
+ if (input === "]") {
79389
+ dispatch({ type: "CYCLE_DETAIL_TAB", direction: 1, tabCount: detailTabs.length });
79390
+ return;
79391
+ }
79392
+ if (state.focusTarget === "side") {
79393
+ if (input === "j" || key.downArrow) {
79394
+ if (clampedSelection < currentItems.length - 1) {
79395
+ dispatch({ type: "SELECT_ITEM", index: clampedSelection + 1 });
79396
+ sideScroll.selectNext();
79397
+ }
79398
+ return;
79399
+ }
79400
+ if (input === "k" || key.upArrow) {
79401
+ if (clampedSelection > 0) {
79402
+ dispatch({ type: "SELECT_ITEM", index: clampedSelection - 1 });
79403
+ sideScroll.selectPrev();
79404
+ }
79405
+ return;
79406
+ }
79407
+ if (input === "g") {
79408
+ dispatch({ type: "SELECT_ITEM", index: 0 });
79409
+ sideScroll.selectFirst();
79410
+ return;
79411
+ }
79412
+ if (input === "G") {
79413
+ dispatch({ type: "SELECT_ITEM", index: Math.max(0, currentItems.length - 1) });
79414
+ sideScroll.selectLast();
79415
+ return;
79416
+ }
79417
+ if (key.return) {
79418
+ dispatch({ type: "SET_FOCUS", target: "detail" });
79419
+ return;
79420
+ }
79421
+ }
79422
+ if (state.focusTarget === "detail") {
79423
+ if (input === "j" || key.downArrow) {
79424
+ dispatch({ type: "SCROLL_DETAIL_DELTA", delta: 1, totalLines: detailLines.length, viewportHeight: detailViewportHeight });
79425
+ return;
79426
+ }
79427
+ if (input === "k" || key.upArrow) {
79428
+ dispatch({ type: "SCROLL_DETAIL_DELTA", delta: -1, totalLines: detailLines.length, viewportHeight: detailViewportHeight });
79429
+ return;
79430
+ }
79431
+ if (input === "h" || key.leftArrow) {
79432
+ dispatch({ type: "SET_FOCUS", target: "side" });
79433
+ return;
79434
+ }
79435
+ if (input === "g") {
79436
+ dispatch({ type: "SCROLL_DETAIL", offset: 0 });
79437
+ return;
79438
+ }
79439
+ if (input === "G") {
79440
+ dispatch({ type: "SCROLL_DETAIL", offset: Math.max(0, detailLines.length - detailViewportHeight) });
79441
+ return;
79442
+ }
79443
+ }
79444
+ if (selectedItem) {
79445
+ const actionMatch = panelActions.find((a) => a.key === input && (!a.condition || a.condition(selectedItem)));
79446
+ if (actionMatch) {
79447
+ executeAction(actionMatch, selectedItem, dispatch, addToast);
79448
+ }
79449
+ }
79450
+ });
79451
+ }
79452
+
79453
+ // src/dashboard/ink/useMouseHandler.ts
79454
+ var import_react31 = __toESM(require_react(), 1);
79455
+ function useMouseHandler(ctx) {
79456
+ const { state, dispatch, panels, panelCounts, currentItems, clampedSelection, sideWidth, sideScroll, detailLines, detailViewportHeight, detailTabs, rows, rotatePhrase } = ctx;
79457
+ return (0, import_react31.useCallback)((event) => {
79458
+ rotatePhrase();
79459
+ if (state.overlay === "filter") return;
79460
+ if (state.overlay) {
79461
+ if (event.type === "click") {
79462
+ dispatch({ type: "SET_OVERLAY", overlay: null });
79463
+ }
79464
+ return;
79465
+ }
79466
+ const { x, y } = event;
79467
+ if (event.type === "scroll") {
79468
+ if (x < sideWidth && sideWidth > 0) {
79469
+ const delta = event.scrollDirection === "down" ? 3 : -3;
79470
+ dispatch({ type: "SCROLL_SIDE", delta, itemCount: currentItems.length });
79471
+ const newIdx = Math.max(0, Math.min(clampedSelection + delta, currentItems.length - 1));
79472
+ sideScroll.setSelected(newIdx);
79473
+ } else {
79474
+ const delta = event.scrollDirection === "down" ? 3 : -3;
79475
+ dispatch({ type: "SCROLL_DETAIL_DELTA", delta, totalLines: detailLines.length, viewportHeight: detailViewportHeight });
79476
+ }
79477
+ return;
79478
+ }
79479
+ if (event.type !== "click" || event.button !== "left") return;
79480
+ if (y === 0) {
79481
+ let col = 0;
79482
+ for (let i = 0; i < panels.length; i++) {
79483
+ const count = panelCounts[i];
79484
+ let countLen = 0;
79485
+ if (count) {
79486
+ countLen = count.running !== void 0 ? ` ${count.running}/${count.total}`.length : ` ${count.total}`.length;
79487
+ }
79488
+ const tabWidth = String(panels[i].shortcutKey).length + panels[i].title.length + 3 + countLen + 1;
79489
+ if (x >= col && x < col + tabWidth) {
79490
+ panels[state.activePanelIndex]?.onDeactivate?.();
79491
+ dispatch({ type: "SWITCH_PANEL", index: i });
79492
+ panels[i]?.onActivate?.();
79493
+ return;
79494
+ }
79495
+ col += tabWidth;
79496
+ }
79497
+ return;
79498
+ }
79499
+ if (y >= rows - 1) return;
79500
+ if (x < sideWidth && sideWidth > 0) {
79501
+ dispatch({ type: "SET_FOCUS", target: "side" });
79502
+ const hasScrollUp = sideScroll.scrollOffset > 0;
79503
+ const itemRow = y - 2 - (hasScrollUp ? 1 : 0);
79504
+ const itemIndex = sideScroll.scrollOffset + itemRow;
79505
+ if (itemIndex >= 0 && itemIndex < currentItems.length) {
79506
+ dispatch({ type: "SELECT_ITEM", index: itemIndex });
79507
+ sideScroll.setSelected(itemIndex);
79508
+ }
79509
+ } else {
79510
+ dispatch({ type: "SET_FOCUS", target: "detail" });
79511
+ if (y === 1 && detailTabs.length > 1) {
79512
+ let col = sideWidth;
79513
+ for (let i = 0; i < detailTabs.length; i++) {
79514
+ const tabWidth = detailTabs[i].label.length + 3;
79515
+ if (x >= col && x < col + tabWidth) {
79516
+ dispatch({ type: "SET_DETAIL_TAB", index: i });
79517
+ return;
79518
+ }
79519
+ col += tabWidth;
79520
+ }
79521
+ }
79522
+ }
79523
+ }, [state.overlay, state.activePanelIndex, sideWidth, currentItems.length, clampedSelection, sideScroll, detailLines.length, detailViewportHeight, panels, panelCounts, detailTabs, rows, rotatePhrase, dispatch]);
79524
+ }
79525
+
79080
79526
  // src/dashboard/ink/TabBar.tsx
79081
79527
  await init_build2();
79082
79528
  var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
@@ -79086,8 +79532,14 @@ function TabBar({ panels, activeIndex, layoutMode, phrase, panelCounts }) {
79086
79532
  const isActive = i === activeIndex;
79087
79533
  const count = panelCounts?.[i];
79088
79534
  let countStr = "";
79535
+ let countColor;
79089
79536
  if (count) {
79090
79537
  countStr = count.running !== void 0 ? ` ${count.running}/${count.total}` : ` ${count.total}`;
79538
+ if (count.running !== void 0) {
79539
+ if (count.running === count.total && count.total > 0) countColor = "green";
79540
+ else if (count.running > 0) countColor = "yellow";
79541
+ else countColor = void 0;
79542
+ }
79091
79543
  }
79092
79544
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { marginRight: 1, children: [
79093
79545
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -79102,10 +79554,10 @@ function TabBar({ panels, activeIndex, layoutMode, phrase, panelCounts }) {
79102
79554
  countStr && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
79103
79555
  Text,
79104
79556
  {
79105
- color: isActive ? "#2B4C7E" : "gray",
79557
+ color: isActive ? countColor || "#2B4C7E" : countColor || "gray",
79106
79558
  bold: isActive,
79107
79559
  inverse: isActive,
79108
- dimColor: !isActive,
79560
+ dimColor: !isActive && !countColor,
79109
79561
  children: `${countStr} `
79110
79562
  }
79111
79563
  ),
@@ -79165,11 +79617,15 @@ function SideList({ items, selectedIndex, scrollOffset, focused, width, viewport
79165
79617
  const rightLabel = item.rightLabel || "";
79166
79618
  const rightLen = rightLabel.length;
79167
79619
  const leftWidth = innerWidth - rightLen - (rightLen ? 1 : 0);
79168
- const leftText = `${prefix}${icon}${rest}`;
79169
- const paddedLeft = leftText.length < leftWidth ? leftText + " ".repeat(leftWidth - leftText.length) : leftText.substring(0, leftWidth);
79170
79620
  if (isSelected && focused) {
79171
- const fullLine = rightLen ? ` ${paddedLeft} ${rightLabel}` : ` ${paddedLeft}`;
79172
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "cyan", bold: true, inverse: true, wrap: "truncate", children: fullLine.padEnd(innerWidth) }) }, item.id);
79621
+ const restText = rest.length < leftWidth - prefix.length - 1 ? rest + " ".repeat(leftWidth - prefix.length - 1 - rest.length) : rest.substring(0, leftWidth - prefix.length - 1);
79622
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
79623
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#2B4C7E", bold: true, inverse: true, wrap: "truncate", children: ` ${prefix}` }),
79624
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: item.iconColor || "#2B4C7E", bold: true, inverse: true, children: icon }),
79625
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#2B4C7E", bold: true, inverse: true, wrap: "truncate", children: restText }),
79626
+ rightLen > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#2B4C7E", bold: true, inverse: true, children: ` ${rightLabel}` }),
79627
+ !rightLen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#2B4C7E", bold: true, inverse: true, children: " " })
79628
+ ] }, item.id);
79173
79629
  }
79174
79630
  const iconColor = item.iconColor || (isSelected ? "#2B4C7E" : "white");
79175
79631
  const textColor = isSelected ? "#2B4C7E" : "white";
@@ -79181,13 +79637,17 @@ function SideList({ items, selectedIndex, scrollOffset, focused, width, viewport
79181
79637
  ] }, item.id);
79182
79638
  }),
79183
79639
  hasScrollDown && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: ` \u25BC (${belowCount} more)` }),
79184
- items.length === 0 && filterString && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
79185
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: ` No matches for "${filterString}"` }),
79186
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: " Press Esc to clear" })
79640
+ items.length === 0 && filterString && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingTop: 1, children: [
79641
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "yellow", children: ` \u2717 No matches for "${filterString}"` }),
79642
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", dimColor: true, children: " Press Esc to clear filter" })
79187
79643
  ] }),
79188
- items.length === 0 && !filterString && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
79189
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: ` No ${panelTitle.toLowerCase()} found` }),
79190
- panelId && EMPTY_HINTS[panelId]?.map((hint, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: ` ${hint}` }, i))
79644
+ items.length === 0 && !filterString && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingTop: 1, children: [
79645
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: ` \u2500 No ${panelTitle.toLowerCase()} found` }),
79646
+ panelId && EMPTY_HINTS[panelId] && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
79647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: "" }),
79648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", dimColor: true, children: ` ${EMPTY_HINTS[panelId][0]}` }),
79649
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "#2B4C7E", bold: true, children: ` ${EMPTY_HINTS[panelId][1]}` })
79650
+ ] })
79191
79651
  ] })
79192
79652
  ] });
79193
79653
  }
@@ -79197,7 +79657,7 @@ await init_build2();
79197
79657
  var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
79198
79658
  function DetailTabBar({ tabs, activeIndex }) {
79199
79659
  if (tabs.length <= 1) {
79200
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, {});
79660
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { children: tabs.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", dimColor: true, children: ` ${tabs[0].label}` }) });
79201
79661
  }
79202
79662
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
79203
79663
  tabs.map((tab2, i) => {
@@ -79213,7 +79673,7 @@ function DetailTabBar({ tabs, activeIndex }) {
79213
79673
  ) }, tab2.label);
79214
79674
  }),
79215
79675
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { flexGrow: 1 }),
79216
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", children: "[ ] switch" })
79676
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", dimColor: true, children: "[/] cycle tabs" })
79217
79677
  ] });
79218
79678
  }
79219
79679
 
@@ -79235,9 +79695,9 @@ function DetailPane({ content, scrollOffset, viewportHeight, focused }) {
79235
79695
  }
79236
79696
 
79237
79697
  // src/dashboard/ink/StatusBar.tsx
79238
- var import_react31 = __toESM(require_react(), 1);
79698
+ var import_react32 = __toESM(require_react(), 1);
79239
79699
  await init_build2();
79240
- var import_sidekick_docker_shared6 = __toESM(require_dist(), 1);
79700
+ var import_sidekick_docker_shared8 = __toESM(require_dist(), 1);
79241
79701
  var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
79242
79702
  function formatAgo(date) {
79243
79703
  const secs = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -79246,72 +79706,89 @@ function formatAgo(date) {
79246
79706
  const mins = Math.floor(secs / 60);
79247
79707
  return { text: `${mins}m ago`, stale: mins >= 1 };
79248
79708
  }
79249
- function StatusBar({ daemonConnected, focusTarget, panelHints, panelActionHints, filterString, containerCount, runningCount, version: version2, matchCount, totalCount, lastRefresh }) {
79250
- const [, setTick] = (0, import_react31.useState)(0);
79251
- (0, import_react31.useEffect)(() => {
79709
+ var SEP2 = "\u2502";
79710
+ function StatusBar({ daemonConnected, focusTarget, panelActionHints, filterString, containerCount, runningCount, version: version2, matchCount, totalCount, lastRefresh }) {
79711
+ const [, setTick] = (0, import_react32.useState)(0);
79712
+ (0, import_react32.useEffect)(() => {
79252
79713
  const timer = setInterval(() => setTick((t) => t + 1), 5e3);
79253
79714
  return () => clearInterval(timer);
79254
79715
  }, []);
79255
79716
  const ago = lastRefresh ? formatAgo(lastRefresh) : null;
79256
79717
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { children: [
79257
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: true, color: "magenta", children: ` ${import_sidekick_docker_shared6.BRAND_INLINE}` }),
79258
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", children: ` ${import_sidekick_docker_shared6.BRAND_TAGLINE} v${version2}` }),
79259
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", children: " " }),
79718
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: true, color: "magenta", children: ` \u26A1 ${import_sidekick_docker_shared8.BRAND_INLINE}` }),
79719
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", dimColor: true, children: ` ${import_sidekick_docker_shared8.BRAND_TAGLINE} v${version2}` }),
79720
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", dimColor: true, children: ` ${SEP2} ` }),
79260
79721
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: daemonConnected ? "green" : "red", children: daemonConnected ? `\u25CF ${runningCount ?? 0}/${containerCount ?? 0}` : "\u25CB disconnected" }),
79261
- ago && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: ago.stale ? "yellow" : "gray", children: ` \u21BB ${ago.text}` }),
79262
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", children: " " }),
79263
- panelActionHints ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", children: `${panelActionHints} ` }) : null,
79264
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "gray", children: [
79265
- panelHints,
79266
- focusTarget === "side" ? "j/k nav Tab focus " : "j/k scroll Tab focus ",
79267
- "/ filter ? help q quit"
79722
+ ago && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: ago.stale ? "yellow" : "gray", dimColor: !ago.stale, children: ` \u21BB ${ago.text}` }),
79723
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", dimColor: true, children: ` ${SEP2} ` }),
79724
+ panelActionHints.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
79725
+ panelActionHints.map((hint, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react32.default.Fragment, { children: [
79726
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }),
79727
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: hint.destructive ? "red" : "#2B4C7E", children: `${hint.key}:${hint.label}` })
79728
+ ] }, hint.key)),
79729
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", dimColor: true, children: ` ${SEP2} ` })
79268
79730
  ] }),
79269
- filterString ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "yellow", children: ` Filter: "${filterString}"${matchCount !== void 0 && totalCount !== void 0 ? ` (${matchCount} of ${totalCount})` : ""}` }) : null
79731
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: focusTarget === "side" ? "#2B4C7E" : "gray", bold: focusTarget === "side", children: "\u25C0" }),
79732
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", dimColor: true, children: "/" }),
79733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: focusTarget === "detail" ? "#2B4C7E" : "gray", bold: focusTarget === "detail", children: "\u25B6" }),
79734
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "gray", dimColor: true, children: " j/k Tab / ? q" }),
79735
+ filterString ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "yellow", bold: true, children: ` \u25C9 "${filterString}"${matchCount !== void 0 && totalCount !== void 0 ? ` ${matchCount}/${totalCount}` : ""}` }) : null
79270
79736
  ] });
79271
79737
  }
79272
79738
 
79273
79739
  // src/dashboard/ink/HelpOverlay.tsx
79274
79740
  await init_build2();
79275
- var import_sidekick_docker_shared7 = __toESM(require_dist(), 1);
79741
+ var import_sidekick_docker_shared9 = __toESM(require_dist(), 1);
79276
79742
  var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
79277
79743
  var GLOBAL_BINDINGS = [
79278
79744
  { key: "1-5", label: "Switch panel" },
79279
79745
  { key: "j/k", label: "Navigate / scroll" },
79280
- { key: "g/G", label: "First / last item" },
79281
- { key: "Tab", label: "Toggle focus (side/detail)" },
79282
- { key: "[/]", label: "Prev / next detail tab" },
79283
- { key: "z", label: "Cycle layout mode" },
79746
+ { key: "g/G", label: "Jump to first / last" },
79747
+ { key: "Tab", label: "Toggle focus" },
79748
+ { key: "[/]", label: "Cycle detail tabs" },
79749
+ { key: "z", label: "Toggle expanded layout" },
79284
79750
  { key: "/", label: "Filter items" },
79285
- { key: "x", label: "Context menu (actions)" },
79751
+ { key: "x", label: "Actions menu" },
79286
79752
  { key: "V", label: "Version info" },
79287
- { key: "?", label: "Toggle help" },
79753
+ { key: "?", label: "This help" },
79288
79754
  { key: "q", label: "Quit" }
79289
79755
  ];
79756
+ function KeyBadge({ k }) {
79757
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "white", backgroundColor: "#2B4C7E", bold: true, children: ` ${k} ` });
79758
+ }
79290
79759
  function HelpOverlay({ panels, activePanelIndex, version: version2 }) {
79291
79760
  const panel = panels[activePanelIndex];
79292
79761
  const actions = panel.getActions();
79293
79762
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
79294
79763
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { children: [
79295
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "magenta", children: `${import_sidekick_docker_shared7.BRAND_INLINE} ${import_sidekick_docker_shared7.BRAND_TAGLINE}` }),
79296
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", children: ` v${version2}` })
79764
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "magenta", children: `\u26A1 ${import_sidekick_docker_shared9.BRAND_INLINE} ${import_sidekick_docker_shared9.BRAND_TAGLINE}` }),
79765
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", dimColor: true, children: ` v${version2}` })
79766
+ ] }),
79767
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "" }),
79768
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { children: [
79769
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "yellow", children: "\u2500\u2500 Navigation " }),
79770
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", dimColor: true, children: "\u2500".repeat(30) })
79297
79771
  ] }),
79298
79772
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "" }),
79299
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "yellow", children: "Navigation" }),
79300
- GLOBAL_BINDINGS.map((b) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
79301
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "#2B4C7E", children: ` ${b.key.padEnd(8)}` }),
79302
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: b.label })
79773
+ GLOBAL_BINDINGS.map((b) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { children: [
79774
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { width: 10, justifyContent: "flex-end", marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyBadge, { k: b.key }) }),
79775
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", children: b.label })
79303
79776
  ] }, b.key)),
79304
79777
  actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
79305
79778
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "" }),
79306
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "yellow", children: `${panel.title} Actions` }),
79307
- actions.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
79308
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "#2B4C7E", children: ` ${a.key.padEnd(8)}` }),
79309
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: a.label }),
79310
- a.confirm && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "red", children: " (requires confirmation)" })
79779
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { children: [
79780
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: "yellow", children: `\u2500\u2500 ${panel.title} Actions ` }),
79781
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", dimColor: true, children: "\u2500".repeat(24) })
79782
+ ] }),
79783
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "" }),
79784
+ actions.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { children: [
79785
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { width: 10, justifyContent: "flex-end", marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyBadge, { k: a.key }) }),
79786
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: a.confirm ? "red" : "gray", children: a.label }),
79787
+ a.confirm && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "red", dimColor: true, children: " \u26A0" })
79311
79788
  ] }, a.key))
79312
79789
  ] }),
79313
79790
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: "" }),
79314
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", children: "Press ? or Esc to close" })
79791
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: "gray", dimColor: true, children: "Press ? or Esc to close" })
79315
79792
  ] });
79316
79793
  }
79317
79794
 
@@ -79321,8 +79798,9 @@ var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
79321
79798
  function FilterOverlay({ filterString, matchCount, totalCount, panelTitle }) {
79322
79799
  const countInfo = matchCount !== void 0 && totalCount !== void 0 && panelTitle ? ` ${matchCount} of ${totalCount} ${panelTitle.toLowerCase()}` : "";
79323
79800
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { position: "absolute", marginTop: 1, marginLeft: 1, children: [
79324
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { backgroundColor: "gray", color: "white", children: ` / ${filterString}\u2588 ` }),
79325
- countInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "gray", children: countInfo })
79801
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { backgroundColor: "#2B4C7E", color: "white", bold: true, children: ` \u2315 ${filterString}\u2588 ` }),
79802
+ countInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "gray", dimColor: true, children: countInfo }),
79803
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "gray", dimColor: true, children: " Enter: apply Esc: clear" })
79326
79804
  ] });
79327
79805
  }
79328
79806
 
@@ -79338,23 +79816,37 @@ function ContextMenuOverlay({ actions, selectedIndex }) {
79338
79816
  marginLeft: 2,
79339
79817
  flexDirection: "column",
79340
79818
  borderStyle: "single",
79341
- borderColor: "cyan",
79819
+ borderColor: "#2B4C7E",
79342
79820
  paddingX: 1,
79343
79821
  children: [
79344
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { bold: true, color: "cyan", children: "Actions" }),
79822
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { bold: true, color: "#2B4C7E", children: "\u2630 Actions" }),
79345
79823
  actions.map((action, i) => {
79346
79824
  const isSelected = i === selectedIndex;
79347
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
79348
- Text,
79349
- {
79350
- color: isSelected ? "#2B4C7E" : "white",
79351
- bold: isSelected,
79352
- inverse: isSelected,
79353
- children: ` ${action.key} ${action.label} `
79354
- }
79355
- ) }, action.key);
79825
+ const isDanger = !!action.confirm;
79826
+ const color = isSelected ? isDanger ? "red" : "#2B4C7E" : isDanger ? "red" : "white";
79827
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { children: [
79828
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
79829
+ Text,
79830
+ {
79831
+ color,
79832
+ bold: isSelected,
79833
+ inverse: isSelected,
79834
+ children: ` ${action.key} `
79835
+ }
79836
+ ),
79837
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
79838
+ Text,
79839
+ {
79840
+ color,
79841
+ bold: isSelected,
79842
+ inverse: isSelected,
79843
+ children: `${action.label}${isDanger ? " \u26A0" : ""} `
79844
+ }
79845
+ )
79846
+ ] }, action.key);
79356
79847
  }),
79357
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "gray", children: "Esc to close" })
79848
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "" }),
79849
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "gray", dimColor: true, children: "j/k select Enter run Esc close" })
79358
79850
  ]
79359
79851
  }
79360
79852
  );
@@ -79373,16 +79865,22 @@ function ConfirmOverlay({ message }) {
79373
79865
  flexDirection: "column",
79374
79866
  borderStyle: "double",
79375
79867
  borderColor: "red",
79376
- paddingX: 1,
79868
+ paddingX: 2,
79869
+ paddingY: 1,
79377
79870
  children: [
79378
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { bold: true, color: "red", children: " Confirm " }),
79871
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
79872
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "red", bold: true, children: "\u26A0 " }),
79873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { bold: true, color: "red", children: "Confirm Action" })
79874
+ ] }),
79875
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "" }),
79379
79876
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: ` ${message}` }),
79877
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "gray", dimColor: true, children: " This cannot be undone." }),
79380
79878
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "" }),
79381
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
79382
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "green", children: " y " }),
79383
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "Yes " }),
79384
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "red", children: " n " }),
79385
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "No" })
79879
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
79880
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { backgroundColor: "green", color: "white", bold: true, children: " y Yes " }),
79881
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: " " }),
79882
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { backgroundColor: "red", color: "white", bold: true, children: " n No " }),
79883
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "gray", dimColor: true, children: " or Esc to cancel" })
79386
79884
  ] })
79387
79885
  ]
79388
79886
  }
@@ -79390,7 +79888,7 @@ function ConfirmOverlay({ message }) {
79390
79888
  }
79391
79889
 
79392
79890
  // src/dashboard/ink/ToastNotification.tsx
79393
- var import_react32 = __toESM(require_react(), 1);
79891
+ var import_react33 = __toESM(require_react(), 1);
79394
79892
  await init_build2();
79395
79893
  var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
79396
79894
  var SEVERITY_COLORS2 = {
@@ -79400,27 +79898,43 @@ var SEVERITY_COLORS2 = {
79400
79898
  };
79401
79899
  var SPINNER_FRAMES = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827";
79402
79900
  function ToastNotification({ toast }) {
79403
- const [frame, setFrame] = (0, import_react32.useState)(0);
79404
- (0, import_react32.useEffect)(() => {
79901
+ const [frame, setFrame] = (0, import_react33.useState)(0);
79902
+ (0, import_react33.useEffect)(() => {
79405
79903
  if (toast.severity !== "info") return;
79406
79904
  const timer = setInterval(() => {
79407
79905
  setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
79408
79906
  }, 100);
79409
79907
  return () => clearInterval(timer);
79410
79908
  }, [toast.id, toast.severity]);
79411
- const spinner = toast.severity === "info" ? `${SPINNER_FRAMES[frame]} ` : "";
79412
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { position: "absolute", marginTop: 0, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: SEVERITY_COLORS2[toast.severity] || "white", children: ` ${spinner}${toast.message} ` }) });
79909
+ const SEVERITY_ICONS = {
79910
+ error: "\u2717",
79911
+ // ✗
79912
+ warning: "\u26A0",
79913
+ // ⚠
79914
+ info: SPINNER_FRAMES[frame]
79915
+ };
79916
+ const icon = SEVERITY_ICONS[toast.severity] || "";
79917
+ const textColor = toast.severity === "warning" ? "black" : "white";
79918
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { position: "absolute", marginTop: 0, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { backgroundColor: SEVERITY_COLORS2[toast.severity] || "white", color: textColor, bold: true, children: ` ${icon} ${toast.message} ` }) });
79413
79919
  }
79414
79920
 
79415
79921
  // src/dashboard/ink/TooSmallOverlay.tsx
79416
79922
  await init_build2();
79923
+ var import_sidekick_docker_shared10 = __toESM(require_dist(), 1);
79417
79924
  var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
79418
79925
  function TooSmallOverlay({ columns, rows }) {
79926
+ const needWidth = Math.max(0, 60 - columns);
79927
+ const needHeight = Math.max(0, 15 - rows);
79928
+ const hints = [];
79929
+ if (needWidth > 0) hints.push(`${needWidth} col${needWidth > 1 ? "s" : ""} wider`);
79930
+ if (needHeight > 0) hints.push(`${needHeight} row${needHeight > 1 ? "s" : ""} taller`);
79419
79931
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", justifyContent: "center", alignItems: "center", height: rows, width: columns, children: [
79932
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, color: "magenta", children: `\u26A1 ${import_sidekick_docker_shared10.BRAND_INLINE}` }),
79933
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
79420
79934
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "yellow", bold: true, children: "Terminal too small" }),
79421
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "gray", children: `Current: ${columns}x${rows}` }),
79422
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "gray", children: "Minimum: 60x15" }),
79423
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "gray", children: "Please resize your terminal." })
79935
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "gray", children: `${columns}\xD7${rows} \u2192 need ${hints.join(" and ")}` }),
79936
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
79937
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "gray", dimColor: true, children: "Resize to at least 60\xD715 to continue." })
79424
79938
  ] });
79425
79939
  }
79426
79940
 
@@ -79509,7 +80023,7 @@ function parseMouseEvent(data) {
79509
80023
  }
79510
80024
 
79511
80025
  // src/dashboard/ink/mouse/MouseProvider.tsx
79512
- var import_react33 = __toESM(require_react(), 1);
80026
+ var import_react34 = __toESM(require_react(), 1);
79513
80027
  await init_build2();
79514
80028
  var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
79515
80029
  function InputSink() {
@@ -79518,7 +80032,7 @@ function InputSink() {
79518
80032
  return null;
79519
80033
  }
79520
80034
  function MouseProvider({ onMouse, children }) {
79521
- (0, import_react33.useEffect)(() => {
80035
+ (0, import_react34.useEffect)(() => {
79522
80036
  enableMouse();
79523
80037
  const handler = (data) => {
79524
80038
  const event = parseMouseEvent(data);
@@ -79547,34 +80061,34 @@ var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
79547
80061
  function LogFilterOverlay({ filterString, filterMode }) {
79548
80062
  const modeLabel = filterMode === "exact" ? "exact" : "fuzzy";
79549
80063
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { position: "absolute", marginTop: 1, marginLeft: 1, children: [
79550
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { backgroundColor: "blue", color: "white", children: ` Log filter (${modeLabel}): ${filterString}\u2588 ` }),
80064
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { backgroundColor: "#2B4C7E", color: "white", bold: true, children: ` Log filter (${modeLabel}): ${filterString}\u2588 ` }),
79551
80065
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: "gray", children: " Tab: toggle mode Enter: apply Esc: clear" })
79552
80066
  ] });
79553
80067
  }
79554
80068
 
79555
80069
  // src/dashboard/ink/VersionOverlay.tsx
79556
- var import_react34 = __toESM(require_react(), 1);
80070
+ var import_react35 = __toESM(require_react(), 1);
79557
80071
  await init_build2();
79558
- var import_sidekick_docker_shared8 = __toESM(require_dist(), 1);
80072
+ var import_sidekick_docker_shared11 = __toESM(require_dist(), 1);
79559
80073
  var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
79560
80074
  function VersionOverlay({ version: version2 }) {
79561
- const phrase = import_react34.default.useMemo(() => (0, import_sidekick_docker_shared8.getRandomPhrase)(), []);
80075
+ const phrase = import_react35.default.useMemo(() => (0, import_sidekick_docker_shared11.getRandomPhrase)(), []);
79562
80076
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
79563
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { bold: true, color: "magenta", children: import_sidekick_docker_shared8.BRAND_INLINE }),
79564
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text, { color: "gray", children: [
79565
- import_sidekick_docker_shared8.BRAND_TAGLINE,
79566
- " v",
79567
- version2
79568
- ] }),
80077
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { bold: true, color: "magenta", children: `\u26A1 ${import_sidekick_docker_shared11.BRAND_INLINE}` }),
80078
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "#2B4C7E", bold: true, children: `${import_sidekick_docker_shared11.BRAND_TAGLINE} v${version2}` }),
80079
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: "" }),
80080
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "gray", dimColor: true, children: "\u2500".repeat(40) }),
80081
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: "" }),
80082
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "gray", italic: true, children: ` "${phrase}"` }),
79569
80083
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: "" }),
79570
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "gray", italic: true, children: `"${phrase}"` }),
80084
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "gray", dimColor: true, children: "\u2500".repeat(40) }),
79571
80085
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { children: "" }),
79572
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "gray", children: "Press V or Esc to close" })
80086
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: "gray", dimColor: true, children: "Press V or Esc to close" })
79573
80087
  ] });
79574
80088
  }
79575
80089
 
79576
80090
  // src/dashboard/ink/Dashboard.tsx
79577
- var import_sidekick_docker_shared9 = __toESM(require_dist(), 1);
80091
+ var import_sidekick_docker_shared12 = __toESM(require_dist(), 1);
79578
80092
 
79579
80093
  // src/dashboard/ExecManager.ts
79580
80094
  var ExecManager = class {
@@ -79635,6 +80149,8 @@ var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
79635
80149
  var SIDE_PANEL_WIDTH = 28;
79636
80150
  var MIN_SCREEN_WIDTH = 60;
79637
80151
  var MIN_SCREEN_HEIGHT = 15;
80152
+ var RESERVED_UI_ROWS = 5;
80153
+ var TOAST_DURATIONS = { error: 4e3, warning: 3e3, info: 2e3 };
79638
80154
  function reducer(state, action) {
79639
80155
  switch (action.type) {
79640
80156
  case "SWITCH_PANEL":
@@ -79739,25 +80255,24 @@ var initialState = {
79739
80255
  logFilterMode: "exact"
79740
80256
  };
79741
80257
  function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecFallback }) {
79742
- const [state, dispatch] = (0, import_react35.useReducer)(reducer, initialState);
79743
- const { exit } = use_app_default();
80258
+ const [state, dispatch] = (0, import_react36.useReducer)(reducer, initialState);
79744
80259
  const { columns, rows } = useTerminalSize();
79745
- const toastIdRef = (0, import_react35.useRef)(0);
79746
- const execManagerRef = (0, import_react35.useRef)(null);
79747
- const [phrase, setPhrase] = import_react35.default.useState(() => (0, import_sidekick_docker_shared9.getRandomPhrase)());
79748
- const phraseTimerRef = (0, import_react35.useRef)(null);
79749
- const rotatePhrase = (0, import_react35.useCallback)(() => {
79750
- setPhrase((0, import_sidekick_docker_shared9.getRandomPhrase)());
80260
+ const toastIdRef = (0, import_react36.useRef)(0);
80261
+ const execManagerRef = (0, import_react36.useRef)(null);
80262
+ const [phrase, setPhrase] = import_react36.default.useState(() => (0, import_sidekick_docker_shared12.getRandomPhrase)());
80263
+ const phraseTimerRef = (0, import_react36.useRef)(null);
80264
+ const rotatePhrase = (0, import_react36.useCallback)(() => {
80265
+ setPhrase((0, import_sidekick_docker_shared12.getRandomPhrase)());
79751
80266
  if (phraseTimerRef.current) clearTimeout(phraseTimerRef.current);
79752
80267
  phraseTimerRef.current = setTimeout(rotatePhrase, 7e3);
79753
80268
  }, []);
79754
- (0, import_react35.useEffect)(() => {
80269
+ (0, import_react36.useEffect)(() => {
79755
80270
  phraseTimerRef.current = setTimeout(rotatePhrase, 7e3);
79756
80271
  return () => {
79757
80272
  if (phraseTimerRef.current) clearTimeout(phraseTimerRef.current);
79758
80273
  };
79759
80274
  }, [rotatePhrase]);
79760
- (0, import_react35.useEffect)(() => {
80275
+ (0, import_react36.useEffect)(() => {
79761
80276
  if (!execTriggerRef) return;
79762
80277
  execTriggerRef.current = (containerId, containerName) => {
79763
80278
  const manager = new ExecManager();
@@ -79792,7 +80307,7 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
79792
80307
  execTriggerRef.current = null;
79793
80308
  };
79794
80309
  }, [execTriggerRef, onExecFallback, columns, rows]);
79795
- (0, import_react35.useEffect)(() => {
80310
+ (0, import_react36.useEffect)(() => {
79796
80311
  if (state.overlay !== "exec") return;
79797
80312
  const handler = (data) => {
79798
80313
  if (data.length === 1 && data[0] === 29) {
@@ -79809,22 +80324,23 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
79809
80324
  process.stdin.removeListener("data", handler);
79810
80325
  };
79811
80326
  }, [state.overlay]);
79812
- (0, import_react35.useEffect)(() => {
80327
+ (0, import_react36.useEffect)(() => {
79813
80328
  if (state.overlay === "exec" && execManagerRef.current) {
79814
80329
  execManagerRef.current.resize(columns, rows);
79815
80330
  }
79816
80331
  }, [columns, rows, state.overlay]);
79817
- const addToast = (0, import_react35.useCallback)((message, severity) => {
79818
- const durations = { error: 4e3, warning: 3e3, info: 2e3 };
80332
+ const addToast = (0, import_react36.useCallback)((message, severity) => {
79819
80333
  const id = ++toastIdRef.current;
79820
- dispatch({ type: "ADD_TOAST", toast: { id, message, severity, expiresAt: Date.now() + durations[severity] } });
79821
- setTimeout(() => dispatch({ type: "REMOVE_TOAST", id }), durations[severity]);
80334
+ dispatch({ type: "ADD_TOAST", toast: { id, message, severity, expiresAt: Date.now() + TOAST_DURATIONS[severity] } });
80335
+ setTimeout(() => dispatch({ type: "REMOVE_TOAST", id }), TOAST_DURATIONS[severity]);
79822
80336
  }, []);
79823
80337
  const panel = panels[state.activePanelIndex];
79824
80338
  const tooSmall = columns < MIN_SCREEN_WIDTH || rows < MIN_SCREEN_HEIGHT;
79825
80339
  const sideWidth = state.layoutMode === "expanded" ? 0 : SIDE_PANEL_WIDTH;
79826
- const getFilteredItems = (0, import_react35.useCallback)(() => {
79827
- let items = panel.getItems(metrics);
80340
+ const allItems = panel.getItems(metrics);
80341
+ const totalItemCount = allItems.length;
80342
+ const currentItems = (() => {
80343
+ let items = allItems;
79828
80344
  if (state.filterString) {
79829
80345
  const f = state.filterString.toLowerCase();
79830
80346
  items = items.filter((it) => {
@@ -79834,21 +80350,20 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
79834
80350
  }
79835
80351
  items.sort((a, b) => a.sortKey - b.sortKey);
79836
80352
  return items;
79837
- }, [panel, metrics, state.filterString]);
79838
- const currentItems = getFilteredItems();
80353
+ })();
79839
80354
  const clampedSelection = Math.min(state.selectedItemIndex, Math.max(0, currentItems.length - 1));
79840
80355
  if (clampedSelection !== state.selectedItemIndex && currentItems.length > 0) {
79841
80356
  dispatch({ type: "SELECT_ITEM", index: clampedSelection });
79842
80357
  }
79843
- const sideViewportHeight = Math.max(1, rows - 5);
80358
+ const sideViewportHeight = Math.max(1, rows - RESERVED_UI_ROWS);
79844
80359
  const sideScroll = useWindowedScroll({ totalItems: currentItems.length, viewportHeight: sideViewportHeight });
79845
- (0, import_react35.useEffect)(() => {
80360
+ (0, import_react36.useEffect)(() => {
79846
80361
  if (sideScroll.selectedIndex !== state.selectedItemIndex) {
79847
80362
  sideScroll.setSelected(state.selectedItemIndex);
79848
80363
  }
79849
80364
  }, [state.selectedItemIndex]);
79850
80365
  const selectedItem = currentItems[clampedSelection];
79851
- (0, import_react35.useEffect)(() => {
80366
+ (0, import_react36.useEffect)(() => {
79852
80367
  onSelectionChange?.(panel.id, selectedItem?.id ?? null);
79853
80368
  }, [panel.id, selectedItem?.id]);
79854
80369
  const detailTabs = panel.detailTabs;
@@ -79865,362 +80380,67 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
79865
80380
  detailContent = "(no item selected)";
79866
80381
  }
79867
80382
  const detailLines = detailContent.split("\n");
79868
- const detailViewportHeight = Math.max(1, rows - 5);
80383
+ const detailViewportHeight = Math.max(1, rows - RESERVED_UI_ROWS);
79869
80384
  const activeTab = detailTabs[tabIdx];
79870
80385
  const shouldAutoScroll = activeTab?.autoScrollBottom ?? false;
79871
- (0, import_react35.useEffect)(() => {
80386
+ (0, import_react36.useEffect)(() => {
79872
80387
  if (shouldAutoScroll && detailLines.length > detailViewportHeight) {
79873
80388
  dispatch({ type: "SCROLL_DETAIL", offset: detailLines.length - detailViewportHeight });
79874
80389
  }
79875
80390
  }, [shouldAutoScroll, detailLines.length, detailViewportHeight]);
79876
- const getContextActions = (0, import_react35.useCallback)(() => {
79877
- if (!selectedItem) return [];
79878
- return panel.getActions().filter((a) => !a.condition || a.condition(selectedItem));
79879
- }, [panel, selectedItem]);
79880
- const contextActions = state.overlay === "context-menu" ? getContextActions() : [];
79881
- const handleMouse = (0, import_react35.useCallback)((event) => {
79882
- rotatePhrase();
79883
- if (state.overlay === "filter") return;
79884
- if (state.overlay) {
79885
- if (event.type === "click") {
79886
- dispatch({ type: "SET_OVERLAY", overlay: null });
79887
- }
79888
- return;
79889
- }
79890
- const { x, y } = event;
79891
- if (event.type === "scroll") {
79892
- if (x < sideWidth && sideWidth > 0) {
79893
- const delta = event.scrollDirection === "down" ? 3 : -3;
79894
- dispatch({ type: "SCROLL_SIDE", delta, itemCount: currentItems.length });
79895
- const newIdx = Math.max(0, Math.min(clampedSelection + delta, currentItems.length - 1));
79896
- sideScroll.setSelected(newIdx);
79897
- } else {
79898
- const delta = event.scrollDirection === "down" ? 3 : -3;
79899
- dispatch({ type: "SCROLL_DETAIL_DELTA", delta, totalLines: detailLines.length, viewportHeight: detailViewportHeight });
79900
- }
79901
- return;
79902
- }
79903
- if (event.type !== "click" || event.button !== "left") return;
79904
- if (y === 0) {
79905
- let col = 0;
79906
- for (let i = 0; i < panels.length; i++) {
79907
- const count = panelCounts[i];
79908
- let countLen = 0;
79909
- if (count) {
79910
- countLen = count.running !== void 0 ? ` ${count.running}/${count.total}`.length : ` ${count.total}`.length;
79911
- }
79912
- const tabWidth = String(panels[i].shortcutKey).length + panels[i].title.length + 3 + countLen + 1;
79913
- if (x >= col && x < col + tabWidth) {
79914
- panels[state.activePanelIndex]?.onDeactivate?.();
79915
- dispatch({ type: "SWITCH_PANEL", index: i });
79916
- panels[i]?.onActivate?.();
79917
- return;
79918
- }
79919
- col += tabWidth;
79920
- }
79921
- return;
79922
- }
79923
- if (y >= rows - 1) return;
79924
- if (x < sideWidth && sideWidth > 0) {
79925
- dispatch({ type: "SET_FOCUS", target: "side" });
79926
- const hasScrollUp = sideScroll.scrollOffset > 0;
79927
- const itemRow = y - 2 - (hasScrollUp ? 1 : 0);
79928
- const itemIndex = sideScroll.scrollOffset + itemRow;
79929
- if (itemIndex >= 0 && itemIndex < currentItems.length) {
79930
- dispatch({ type: "SELECT_ITEM", index: itemIndex });
79931
- sideScroll.setSelected(itemIndex);
79932
- }
79933
- } else {
79934
- dispatch({ type: "SET_FOCUS", target: "detail" });
79935
- if (y === 1 && detailTabs.length > 1) {
79936
- let col = sideWidth;
79937
- for (let i = 0; i < detailTabs.length; i++) {
79938
- const tabWidth = detailTabs[i].label.length + 3;
79939
- if (x >= col && x < col + tabWidth) {
79940
- dispatch({ type: "SET_DETAIL_TAB", index: i });
79941
- return;
79942
- }
79943
- col += tabWidth;
79944
- }
79945
- }
79946
- }
79947
- }, [state.overlay, state.activePanelIndex, sideWidth, currentItems.length, clampedSelection, sideScroll, detailLines.length, detailViewportHeight, panels, detailTabs, rows, rotatePhrase]);
79948
- use_input_default((input, key) => {
79949
- if (state.overlay === "exec") return;
79950
- rotatePhrase();
79951
- if (input === "q" || key.ctrl && input === "c") {
79952
- if (state.overlay) {
79953
- dispatch({ type: "SET_OVERLAY", overlay: null });
79954
- return;
79955
- }
79956
- exit();
79957
- return;
79958
- }
79959
- if (state.overlay === "confirm") {
79960
- if (input === "y" || input === "Y") {
79961
- state.confirmAction?.();
79962
- dispatch({ type: "SET_CONFIRM", action: null, message: "" });
79963
- return;
79964
- }
79965
- if (input === "n" || input === "N" || key.escape) {
79966
- dispatch({ type: "SET_CONFIRM", action: null, message: "" });
79967
- return;
79968
- }
79969
- return;
79970
- }
79971
- if (state.overlay === "filter") {
79972
- if (key.escape) {
79973
- if (state.filterString) addToast("Filter cleared", "info");
79974
- dispatch({ type: "SET_FILTER", value: "" });
79975
- dispatch({ type: "SET_OVERLAY", overlay: null });
79976
- return;
79977
- }
79978
- if (key.return) {
79979
- if (state.filterString) addToast(`Filter: "${state.filterString}"`, "info");
79980
- dispatch({ type: "SET_OVERLAY", overlay: null });
79981
- return;
79982
- }
79983
- if (key.backspace || key.delete) {
79984
- dispatch({ type: "SET_FILTER", value: state.filterString.slice(0, -1) });
79985
- return;
79986
- }
79987
- if (input && !key.ctrl && !key.meta) {
79988
- dispatch({ type: "SET_FILTER", value: state.filterString + input });
79989
- return;
79990
- }
79991
- return;
79992
- }
79993
- if (state.overlay === "log-filter") {
79994
- if (key.escape) {
79995
- if (state.logFilterString) addToast("Log filter cleared", "info");
79996
- dispatch({ type: "SET_LOG_FILTER", value: "" });
79997
- dispatch({ type: "SET_OVERLAY", overlay: null });
79998
- return;
79999
- }
80000
- if (key.return) {
80001
- dispatch({ type: "SET_OVERLAY", overlay: null });
80002
- return;
80003
- }
80004
- if (key.tab) {
80005
- dispatch({ type: "TOGGLE_LOG_FILTER_MODE" });
80006
- return;
80007
- }
80008
- if (key.backspace || key.delete) {
80009
- dispatch({ type: "SET_LOG_FILTER", value: state.logFilterString.slice(0, -1) });
80010
- return;
80011
- }
80012
- if (input && !key.ctrl && !key.meta) {
80013
- dispatch({ type: "SET_LOG_FILTER", value: state.logFilterString + input });
80014
- return;
80015
- }
80016
- return;
80017
- }
80018
- if (state.overlay === "context-menu") {
80019
- if (key.escape) {
80020
- dispatch({ type: "SET_OVERLAY", overlay: null });
80021
- return;
80022
- }
80023
- if (input === "j" || key.downArrow) {
80024
- dispatch({ type: "CONTEXT_MENU_NAV", delta: 1, itemCount: contextActions.length });
80025
- return;
80026
- }
80027
- if (input === "k" || key.upArrow) {
80028
- dispatch({ type: "CONTEXT_MENU_NAV", delta: -1, itemCount: contextActions.length });
80029
- return;
80030
- }
80031
- if (key.return) {
80032
- const action = contextActions[state.contextMenuIndex];
80033
- if (action && selectedItem) {
80034
- if (action.confirm) {
80035
- dispatch({ type: "SET_CONFIRM", action: () => {
80036
- action.handler(selectedItem);
80037
- addToast(action.label, "info");
80038
- }, message: action.confirmMessage || "Are you sure?" });
80039
- } else {
80040
- action.handler(selectedItem);
80041
- addToast(action.label, "info");
80042
- }
80043
- dispatch({ type: "SET_OVERLAY", overlay: null });
80044
- }
80045
- return;
80046
- }
80047
- const match = contextActions.find((a) => a.key === input);
80048
- if (match && selectedItem) {
80049
- if (match.confirm) {
80050
- dispatch({ type: "SET_CONFIRM", action: () => {
80051
- match.handler(selectedItem);
80052
- addToast(match.label, "info");
80053
- }, message: match.confirmMessage || "Are you sure?" });
80054
- } else {
80055
- match.handler(selectedItem);
80056
- addToast(match.label, "info");
80057
- }
80058
- dispatch({ type: "SET_OVERLAY", overlay: null });
80059
- }
80060
- return;
80061
- }
80062
- if (state.overlay === "help") {
80063
- if (key.escape || input === "?") {
80064
- dispatch({ type: "SET_OVERLAY", overlay: null });
80065
- }
80066
- return;
80067
- }
80068
- if (state.overlay === "version") {
80069
- if (key.escape || input === "V") {
80070
- dispatch({ type: "SET_OVERLAY", overlay: null });
80071
- }
80072
- return;
80073
- }
80074
- if (key.escape) {
80075
- if (state.filterString) {
80076
- dispatch({ type: "SET_FILTER", value: "" });
80077
- return;
80078
- }
80079
- if (state.focusTarget === "detail") {
80080
- dispatch({ type: "SET_FOCUS", target: "side" });
80081
- return;
80082
- }
80083
- return;
80084
- }
80085
- if (input === "?") {
80086
- dispatch({ type: "SET_OVERLAY", overlay: "help" });
80087
- return;
80088
- }
80089
- if (input === "V") {
80090
- dispatch({ type: "SET_OVERLAY", overlay: "version" });
80091
- return;
80092
- }
80093
- const num = parseInt(input, 10);
80094
- if (num >= 1 && num <= panels.length) {
80095
- panels[state.activePanelIndex]?.onDeactivate?.();
80096
- dispatch({ type: "SWITCH_PANEL", index: num - 1 });
80097
- panels[num - 1]?.onActivate?.();
80098
- return;
80099
- }
80100
- if (key.tab) {
80101
- dispatch({ type: "TOGGLE_FOCUS" });
80102
- return;
80103
- }
80104
- if (input === "z") {
80105
- dispatch({ type: "CYCLE_LAYOUT" });
80106
- addToast(`Layout: ${state.layoutMode === "normal" ? "Expanded" : "Normal"}`, "info");
80107
- return;
80108
- }
80109
- if (input === "/") {
80110
- dispatch({ type: "SET_OVERLAY", overlay: "filter" });
80111
- return;
80112
- }
80113
- if (input === "f") {
80114
- if (panel.id === "containers" && tabIdx === 0) {
80115
- dispatch({ type: "SET_OVERLAY", overlay: "log-filter" });
80116
- return;
80117
- }
80118
- }
80119
- if (input === "x") {
80120
- if (selectedItem && panel.getActions().length > 0) {
80121
- dispatch({ type: "SET_OVERLAY", overlay: "context-menu" });
80122
- }
80123
- return;
80124
- }
80125
- if (input === "[") {
80126
- dispatch({ type: "CYCLE_DETAIL_TAB", direction: -1, tabCount: detailTabs.length });
80127
- return;
80128
- }
80129
- if (input === "]") {
80130
- dispatch({ type: "CYCLE_DETAIL_TAB", direction: 1, tabCount: detailTabs.length });
80131
- return;
80132
- }
80133
- if (state.focusTarget === "side") {
80134
- if (input === "j" || key.downArrow) {
80135
- if (clampedSelection < currentItems.length - 1) {
80136
- dispatch({ type: "SELECT_ITEM", index: clampedSelection + 1 });
80137
- sideScroll.selectNext();
80138
- }
80139
- return;
80140
- }
80141
- if (input === "k" || key.upArrow) {
80142
- if (clampedSelection > 0) {
80143
- dispatch({ type: "SELECT_ITEM", index: clampedSelection - 1 });
80144
- sideScroll.selectPrev();
80145
- }
80146
- return;
80147
- }
80148
- if (input === "g") {
80149
- dispatch({ type: "SELECT_ITEM", index: 0 });
80150
- sideScroll.selectFirst();
80151
- return;
80152
- }
80153
- if (input === "G") {
80154
- dispatch({ type: "SELECT_ITEM", index: Math.max(0, currentItems.length - 1) });
80155
- sideScroll.selectLast();
80156
- return;
80157
- }
80158
- if (key.return) {
80159
- dispatch({ type: "SET_FOCUS", target: "detail" });
80160
- return;
80161
- }
80162
- }
80163
- if (state.focusTarget === "detail") {
80164
- if (input === "j" || key.downArrow) {
80165
- dispatch({ type: "SCROLL_DETAIL_DELTA", delta: 1, totalLines: detailLines.length, viewportHeight: detailViewportHeight });
80166
- return;
80167
- }
80168
- if (input === "k" || key.upArrow) {
80169
- dispatch({ type: "SCROLL_DETAIL_DELTA", delta: -1, totalLines: detailLines.length, viewportHeight: detailViewportHeight });
80170
- return;
80171
- }
80172
- if (input === "h" || key.leftArrow) {
80173
- dispatch({ type: "SET_FOCUS", target: "side" });
80174
- return;
80175
- }
80176
- if (input === "g") {
80177
- dispatch({ type: "SCROLL_DETAIL", offset: 0 });
80178
- return;
80179
- }
80180
- if (input === "G") {
80181
- dispatch({ type: "SCROLL_DETAIL", offset: Math.max(0, detailLines.length - detailViewportHeight) });
80182
- return;
80183
- }
80184
- }
80185
- if (selectedItem) {
80186
- const actions = panel.getActions();
80187
- const actionMatch = actions.find((a) => a.key === input && (!a.condition || a.condition(selectedItem)));
80188
- if (actionMatch) {
80189
- if (actionMatch.confirm) {
80190
- dispatch({ type: "SET_CONFIRM", action: () => {
80191
- actionMatch.handler(selectedItem);
80192
- addToast(actionMatch.label, "info");
80193
- }, message: actionMatch.confirmMessage || "Are you sure?" });
80194
- } else {
80195
- actionMatch.handler(selectedItem);
80196
- addToast(actionMatch.label, "info");
80197
- }
80198
- }
80199
- }
80200
- });
80201
- if (tooSmall) {
80202
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TooSmallOverlay, { columns, rows });
80203
- }
80391
+ const panelActions = panel.getActions();
80392
+ const applicableActions = selectedItem ? panelActions.filter((a) => !a.condition || a.condition(selectedItem)) : [];
80393
+ const contextActions = state.overlay === "context-menu" ? applicableActions : [];
80204
80394
  const runningCount = metrics.containers.filter((c) => c.state === "running").length;
80205
80395
  const panelCounts = panels.map((p) => {
80206
- const items = p.getItems(metrics);
80207
80396
  if (p.id === "containers") {
80208
- const running = items.filter((it) => {
80209
- const d = it.data;
80210
- return d?.state === "running";
80211
- }).length;
80212
- return { total: items.length, running };
80397
+ return { total: metrics.containers.length, running: runningCount };
80213
80398
  }
80214
80399
  if (p.id === "services") {
80215
- const meaningful = items.filter((it) => it.data !== null).length;
80400
+ const meaningful = metrics.composeProjects.reduce((sum, proj) => sum + proj.services.length, 0);
80216
80401
  return { total: meaningful };
80217
80402
  }
80403
+ const items = p === panel ? allItems : p.getItems(metrics);
80218
80404
  return { total: items.length };
80219
80405
  });
80406
+ const handleMouse = useMouseHandler({
80407
+ state,
80408
+ dispatch,
80409
+ panels,
80410
+ panelCounts,
80411
+ currentItems,
80412
+ clampedSelection,
80413
+ sideWidth,
80414
+ sideScroll,
80415
+ detailLines,
80416
+ detailViewportHeight,
80417
+ detailTabs,
80418
+ rows,
80419
+ rotatePhrase
80420
+ });
80421
+ useKeyboardHandler({
80422
+ state,
80423
+ dispatch,
80424
+ panels,
80425
+ panel,
80426
+ selectedItem,
80427
+ contextActions,
80428
+ clampedSelection,
80429
+ currentItems,
80430
+ detailLines,
80431
+ detailViewportHeight,
80432
+ detailTabs,
80433
+ tabIdx,
80434
+ panelActions,
80435
+ sideScroll,
80436
+ addToast,
80437
+ rotatePhrase
80438
+ });
80439
+ if (tooSmall) {
80440
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TooSmallOverlay, { columns, rows });
80441
+ }
80220
80442
  const showNormalLayout = state.overlay !== "help" && state.overlay !== "exec" && state.overlay !== "version";
80221
- const allItems = panel.getItems(metrics);
80222
- const totalItemCount = allItems.length;
80223
- const panelActionHints = selectedItem ? panel.getActions().filter((a) => !a.condition || a.condition(selectedItem)).map((a) => `${a.key}:${a.label}`).join(" ") : "";
80443
+ const panelActionHints = applicableActions.map((a) => ({ key: a.key, label: a.label, destructive: !!a.confirm }));
80224
80444
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MouseProvider, { onMouse: handleMouse, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Box_default, { flexDirection: "column", height: rows, width: columns, children: [
80225
80445
  showNormalLayout && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TabBar, { panels, activeIndex: state.activePanelIndex, layoutMode: state.layoutMode, phrase, panelCounts }),
80226
80446
  showNormalLayout && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Box_default, { flexGrow: 1, flexDirection: "row", children: [
@@ -80253,8 +80473,8 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
80253
80473
  )
80254
80474
  ] })
80255
80475
  ] }),
80256
- state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.1.2" }),
80257
- state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VersionOverlay, { version: "0.1.2" }),
80476
+ state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.1.3" }),
80477
+ state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VersionOverlay, { version: "0.1.3" }),
80258
80478
  state.overlay === "exec" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
80259
80479
  ExecOverlay,
80260
80480
  {
@@ -80267,12 +80487,11 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
80267
80487
  {
80268
80488
  daemonConnected: metrics.daemonConnected,
80269
80489
  focusTarget: state.focusTarget,
80270
- panelHints: "",
80271
80490
  panelActionHints,
80272
80491
  filterString: state.filterString,
80273
80492
  containerCount: metrics.containers.length,
80274
80493
  runningCount,
80275
- version: "0.1.2",
80494
+ version: "0.1.3",
80276
80495
  matchCount: state.filterString ? currentItems.length : void 0,
80277
80496
  totalCount: state.filterString ? totalItemCount : void 0,
80278
80497
  lastRefresh: metrics.lastRefresh
@@ -80314,7 +80533,7 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
80314
80533
  async function dashboardAction(_opts, cmd) {
80315
80534
  const globalOpts = cmd.parent?.opts() ?? cmd.opts();
80316
80535
  const socketPath = globalOpts.socket;
80317
- const client = new import_sidekick_docker_shared10.DockerClient(socketPath ? { socketPath } : void 0);
80536
+ const client = new import_sidekick_docker_shared13.DockerClient(socketPath ? { socketPath } : void 0);
80318
80537
  const ok = await client.ping();
80319
80538
  if (!ok) {
80320
80539
  console.error("Error: Cannot connect to Docker daemon. Is Docker running?");
@@ -80323,10 +80542,12 @@ async function dashboardAction(_opts, cmd) {
80323
80542
  const cwd2 = process.cwd();
80324
80543
  const state = new DockerState(client, cwd2);
80325
80544
  await state.refresh();
80326
- const composeClient = new import_sidekick_docker_shared10.ComposeClient();
80545
+ const composeClient = new import_sidekick_docker_shared13.ComposeClient();
80327
80546
  const onAction = () => {
80328
- state.refresh().then(() => scheduleRender()).catch(() => {
80329
- });
80547
+ state.refresh().then(() => scheduleRender()).catch((e) => console.debug("refresh failed:", e));
80548
+ };
80549
+ const onError = (msg) => {
80550
+ console.debug("panel action failed:", msg);
80330
80551
  };
80331
80552
  const logManager = new LogStreamManager(client, () => {
80332
80553
  state.setSelectedLogs(logManager.getLogs());
@@ -80353,8 +80574,7 @@ async function dashboardAction(_opts, cmd) {
80353
80574
  client.inspectContainer(itemId).then((info) => {
80354
80575
  state.setInspectedEnv(itemId, info.Config.Env || []);
80355
80576
  scheduleRender();
80356
- }).catch(() => {
80357
- });
80577
+ }).catch((e) => console.debug("inspectContainer failed:", e));
80358
80578
  }
80359
80579
  } else if (panelId === "services" && itemId) {
80360
80580
  logManager.select(null);
@@ -80372,24 +80592,24 @@ async function dashboardAction(_opts, cmd) {
80372
80592
  }
80373
80593
  };
80374
80594
  const panels = [
80375
- new ContainersPanel(client, onAction),
80376
- new ServicesPanel(composeClient, onAction, cwd2),
80377
- new ImagesPanel(client, onAction),
80378
- new VolumesPanel(client, onAction),
80379
- new NetworksPanel(client, onAction)
80595
+ new ContainersPanel(client, onAction, onError),
80596
+ new ServicesPanel(composeClient, onAction, cwd2, onError),
80597
+ new ImagesPanel(client, onAction, onError),
80598
+ new VolumesPanel(client, onAction, onError),
80599
+ new NetworksPanel(client, onAction, onError)
80380
80600
  ];
80381
- const watcher = new import_sidekick_docker_shared10.EventWatcher(client, {
80601
+ const watcher = new import_sidekick_docker_shared13.EventWatcher(client, {
80382
80602
  onEvent: (event) => {
80383
80603
  state.processEvent(event);
80384
80604
  scheduleRender();
80385
80605
  },
80386
- onError: () => {
80606
+ onError: (err) => {
80607
+ console.debug("event watcher error:", err.message);
80387
80608
  }
80388
80609
  });
80389
80610
  watcher.start();
80390
80611
  const refreshInterval = setInterval(() => {
80391
- state.refresh().then(() => scheduleRender()).catch(() => {
80392
- });
80612
+ state.refresh().then(() => scheduleRender()).catch((e) => console.debug("periodic refresh failed:", e));
80393
80613
  }, 3e4);
80394
80614
  const execTriggerRef = { current: null };
80395
80615
  const onExecFallback = (containerId) => {
@@ -80400,7 +80620,7 @@ async function dashboardAction(_opts, cmd) {
80400
80620
  stdio: "inherit"
80401
80621
  });
80402
80622
  instance = render2(
80403
- import_react36.default.createElement(Dashboard, {
80623
+ import_react37.default.createElement(Dashboard, {
80404
80624
  panels,
80405
80625
  metrics: getEnrichedMetrics(),
80406
80626
  onSelectionChange,
@@ -80412,7 +80632,7 @@ async function dashboardAction(_opts, cmd) {
80412
80632
  };
80413
80633
  const { render: render2 } = await init_build2().then(() => build_exports);
80414
80634
  let instance = render2(
80415
- import_react36.default.createElement(Dashboard, {
80635
+ import_react37.default.createElement(Dashboard, {
80416
80636
  panels,
80417
80637
  metrics: getEnrichedMetrics(),
80418
80638
  onSelectionChange,
@@ -80443,7 +80663,7 @@ async function dashboardAction(_opts, cmd) {
80443
80663
  renderTimer = setTimeout(() => {
80444
80664
  renderTimer = null;
80445
80665
  instance.rerender(
80446
- import_react36.default.createElement(Dashboard, {
80666
+ import_react37.default.createElement(Dashboard, {
80447
80667
  panels,
80448
80668
  metrics: getEnrichedMetrics(),
80449
80669
  onSelectionChange,
@@ -80493,9 +80713,9 @@ async function dashboardAction(_opts, cmd) {
80493
80713
  }
80494
80714
 
80495
80715
  // src/commands/ps.ts
80496
- var import_sidekick_docker_shared11 = __toESM(require_dist(), 1);
80716
+ var import_sidekick_docker_shared14 = __toESM(require_dist(), 1);
80497
80717
  async function psAction(opts) {
80498
- const client = new import_sidekick_docker_shared11.DockerClient();
80718
+ const client = new import_sidekick_docker_shared14.DockerClient();
80499
80719
  const ok = await client.ping();
80500
80720
  if (!ok) {
80501
80721
  console.error("Error: Cannot connect to Docker daemon. Is Docker running?");
@@ -80522,9 +80742,9 @@ async function psAction(opts) {
80522
80742
  }
80523
80743
 
80524
80744
  // src/commands/logs.ts
80525
- var import_sidekick_docker_shared12 = __toESM(require_dist(), 1);
80745
+ var import_sidekick_docker_shared15 = __toESM(require_dist(), 1);
80526
80746
  async function logsAction(container, opts) {
80527
- const client = new import_sidekick_docker_shared12.DockerClient();
80747
+ const client = new import_sidekick_docker_shared15.DockerClient();
80528
80748
  const ok = await client.ping();
80529
80749
  if (!ok) {
80530
80750
  console.error("Error: Cannot connect to Docker daemon. Is Docker running?");
@@ -80541,7 +80761,7 @@ async function logsAction(container, opts) {
80541
80761
  console.log(`${prefix}${ts} ${entry.message}${reset}`);
80542
80762
  }
80543
80763
  } catch (err) {
80544
- const msg = err instanceof Error ? err.message : String(err);
80764
+ const msg = (0, import_sidekick_docker_shared15.errorMessage)(err);
80545
80765
  if (msg.includes("no such container") || msg.includes("No such container")) {
80546
80766
  console.error(`Error: Container "${container}" not found.`);
80547
80767
  } else {
@@ -80553,7 +80773,7 @@ async function logsAction(container, opts) {
80553
80773
 
80554
80774
  // src/cli.ts
80555
80775
  var program2 = new Command();
80556
- program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.1.2").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
80776
+ program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.1.3").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
80557
80777
  await dashboardAction(_opts, cmd);
80558
80778
  });
80559
80779
  program2.command("ps").description("List containers").option("-a, --all", "Show all containers (default: running only)", false).action(async (opts) => {