sidekick-docker 0.2.0 → 0.2.1

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.
@@ -94732,9 +94732,7 @@ var ContainersPanel = class {
94732
94732
  if (c.state !== "running") return "Container is not running.";
94733
94733
  const latest = metrics.statsCollector.getLatest(c.id);
94734
94734
  if (!latest) {
94735
- const frames = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827";
94736
- const idx = Math.floor(Date.now() / 200) % frames.length;
94737
- return `${frames[idx]} Loading stats...`;
94735
+ return "Loading stats...";
94738
94736
  }
94739
94737
  const cpuSeries = metrics.statsCollector.getCpuSeries(c.id);
94740
94738
  const memSeries = metrics.statsCollector.getMemorySeries(c.id);
@@ -95533,7 +95531,6 @@ var LogStreamManager = class extends BaseStreamManager {
95533
95531
  var StatsStreamManager = class extends BaseStreamManager {
95534
95532
  client;
95535
95533
  collector;
95536
- loadingInterval = null;
95537
95534
  streamLabel = "stats";
95538
95535
  constructor(client, collector, onChange) {
95539
95536
  super(onChange);
@@ -95557,21 +95554,11 @@ var StatsStreamManager = class extends BaseStreamManager {
95557
95554
  }
95558
95555
  processItem(id, stats) {
95559
95556
  this.collector.push(id, stats);
95560
- this.clearLoadingInterval();
95561
95557
  }
95562
95558
  onClear() {
95563
95559
  }
95564
95560
  onBeforeStream() {
95565
- this.loadingInterval = setInterval(() => this.onChange(), 200);
95566
- }
95567
- onStop() {
95568
- this.clearLoadingInterval();
95569
- }
95570
- clearLoadingInterval() {
95571
- if (this.loadingInterval) {
95572
- clearInterval(this.loadingInterval);
95573
- this.loadingInterval = null;
95574
- }
95561
+ this.onChange();
95575
95562
  }
95576
95563
  getCollector() {
95577
95564
  return this.collector;
@@ -96229,8 +96216,7 @@ function DetailTabBar({ tabs, activeIndex }) {
96229
96216
  // src/dashboard/ink/DetailPane.tsx
96230
96217
  await init_build2();
96231
96218
  var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
96232
- function DetailPane({ content, scrollOffset, viewportHeight, focused }) {
96233
- const lines = content.split("\n");
96219
+ function DetailPane({ lines, scrollOffset, viewportHeight, focused }) {
96234
96220
  const visibleLines = lines.slice(scrollOffset, scrollOffset + viewportHeight);
96235
96221
  const hasScrollUp = scrollOffset > 0;
96236
96222
  const hasScrollDown = scrollOffset + viewportHeight < lines.length;
@@ -96259,7 +96245,7 @@ var SEP2 = "\u2502";
96259
96245
  function StatusBar({ daemonConnected, focusTarget, panelActionHints, filterString, containerCount, runningCount, version: version2, matchCount, totalCount, lastRefresh, contextHint }) {
96260
96246
  const [, setTick] = (0, import_react32.useState)(0);
96261
96247
  (0, import_react32.useEffect)(() => {
96262
- const timer = setInterval(() => setTick((t) => t + 1), 5e3);
96248
+ const timer = setInterval(() => setTick((t) => t + 1), 3e4);
96263
96249
  return () => clearInterval(timer);
96264
96250
  }, []);
96265
96251
  const ago = lastRefresh ? formatAgo(lastRefresh) : null;
@@ -96885,7 +96871,7 @@ var initialState = {
96885
96871
  sortReversed: false,
96886
96872
  sortMenuIndex: 0
96887
96873
  };
96888
- function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecFallback }) {
96874
+ function Dashboard({ panels, metrics, onViewStateChange, execTriggerRef, onExecFallback }) {
96889
96875
  const [state, dispatch] = (0, import_react36.useReducer)(reducer, initialState);
96890
96876
  const { columns, rows } = useTerminalSize();
96891
96877
  const toastIdRef = (0, import_react36.useRef)(0);
@@ -97047,11 +97033,16 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
97047
97033
  }
97048
97034
  }, [state.selectedItemIndex]);
97049
97035
  const selectedItem = currentItems[clampedSelection];
97050
- (0, import_react36.useEffect)(() => {
97051
- onSelectionChange?.(panel.id, selectedItem?.id ?? null);
97052
- }, [panel.id, selectedItem?.id]);
97053
97036
  const detailTabs = panel.detailTabs;
97054
97037
  const tabIdx = Math.min(state.detailTabIndex, detailTabs.length - 1);
97038
+ (0, import_react36.useEffect)(() => {
97039
+ onViewStateChange?.({
97040
+ panelId: panel.id,
97041
+ itemId: selectedItem?.id ?? null,
97042
+ detailTabIndex: tabIdx,
97043
+ sortField: state.sortField
97044
+ });
97045
+ }, [panel.id, selectedItem?.id, tabIdx, state.sortField]);
97055
97046
  const enrichedMetrics = {
97056
97047
  ...metrics,
97057
97048
  logFilterString: state.logFilterString,
@@ -97160,7 +97151,7 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
97160
97151
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
97161
97152
  DetailPane,
97162
97153
  {
97163
- content: detailContent,
97154
+ lines: detailLines,
97164
97155
  scrollOffset: state.detailScrollOffset,
97165
97156
  viewportHeight: detailViewportHeight,
97166
97157
  focused: state.focusTarget === "detail"
@@ -97168,8 +97159,8 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
97168
97159
  )
97169
97160
  ] })
97170
97161
  ] }),
97171
- state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.2.0" }),
97172
- state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VersionOverlay, { version: "0.2.0" }),
97162
+ state.overlay === "help" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(HelpOverlay, { panels, activePanelIndex: state.activePanelIndex, version: "0.2.1" }),
97163
+ state.overlay === "version" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VersionOverlay, { version: "0.2.1" }),
97173
97164
  state.overlay === "exec" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
97174
97165
  ExecOverlay,
97175
97166
  {
@@ -97186,7 +97177,7 @@ function Dashboard({ panels, metrics, onSelectionChange, execTriggerRef, onExecF
97186
97177
  filterString: state.filterString,
97187
97178
  containerCount: metrics.containers.length,
97188
97179
  runningCount,
97189
- version: "0.2.0",
97180
+ version: "0.2.1",
97190
97181
  matchCount: state.filterString ? currentItems.length : void 0,
97191
97182
  totalCount: state.filterString ? totalItemCount : void 0,
97192
97183
  lastRefresh: metrics.lastRefresh,
@@ -97247,27 +97238,61 @@ async function dashboardAction(_opts, cmd) {
97247
97238
  const onError = (msg) => {
97248
97239
  console.debug("panel action failed:", msg);
97249
97240
  };
97241
+ let logFlushTimer = null;
97242
+ let composeLogFlushTimer = null;
97250
97243
  const logManager = new LogStreamManager(client, () => {
97244
+ if (logFlushTimer) return;
97245
+ logFlushTimer = setTimeout(() => {
97246
+ logFlushTimer = null;
97247
+ state.setSelectedLogs(logManager.getLogs());
97248
+ logSeverityCounts = logManager.getSeverityCounts();
97249
+ scheduleRender();
97250
+ }, 100);
97251
+ });
97252
+ const flushLogsNow = () => {
97253
+ if (logFlushTimer) {
97254
+ clearTimeout(logFlushTimer);
97255
+ logFlushTimer = null;
97256
+ }
97251
97257
  state.setSelectedLogs(logManager.getLogs());
97252
97258
  logSeverityCounts = logManager.getSeverityCounts();
97253
97259
  scheduleRender();
97254
- });
97260
+ };
97255
97261
  let logSeverityCounts = logManager.getSeverityCounts();
97256
97262
  const statsManager = new StatsStreamManager(client, state.getStatsCollector(), () => {
97257
97263
  scheduleRender();
97258
97264
  });
97259
97265
  const composeLogManager = new ComposeLogStreamManager(composeClient, () => {
97266
+ if (composeLogFlushTimer) return;
97267
+ composeLogFlushTimer = setTimeout(() => {
97268
+ composeLogFlushTimer = null;
97269
+ state.clearComposeLogs();
97270
+ for (const entry of composeLogManager.getLogs()) {
97271
+ state.appendComposeLog(entry);
97272
+ }
97273
+ scheduleRender();
97274
+ }, 100);
97275
+ });
97276
+ const flushComposeLogsNow = () => {
97277
+ if (composeLogFlushTimer) {
97278
+ clearTimeout(composeLogFlushTimer);
97279
+ composeLogFlushTimer = null;
97280
+ }
97260
97281
  state.clearComposeLogs();
97261
97282
  for (const entry of composeLogManager.getLogs()) {
97262
97283
  state.appendComposeLog(entry);
97263
97284
  }
97264
97285
  scheduleRender();
97265
- });
97266
- const onSelectionChange = (panelId, itemId) => {
97286
+ };
97287
+ const onViewStateChange = (viewState) => {
97288
+ const { panelId, itemId, detailTabIndex, sortField } = viewState;
97289
+ const wantsLiveStats = sortField === "cpu" || sortField === "mem" || sortField === "net" || sortField === "io" || sortField === "pids";
97267
97290
  if (panelId === "containers") {
97268
- logManager.select(itemId);
97269
- statsManager.select(itemId);
97270
- composeLogManager.selectCompose(null, null);
97291
+ void logManager.select(detailTabIndex === 0 ? itemId : null);
97292
+ void statsManager.select(itemId && (detailTabIndex === 1 || wantsLiveStats) ? itemId : null);
97293
+ void composeLogManager.selectCompose(null, null);
97294
+ flushLogsNow();
97295
+ flushComposeLogsNow();
97271
97296
  if (itemId && !state.getInspectedEnv(itemId)) {
97272
97297
  client.getContainerEnv(itemId).then((env3) => {
97273
97298
  state.setInspectedEnv(itemId, env3);
@@ -97281,18 +97306,26 @@ async function dashboardAction(_opts, cmd) {
97281
97306
  }).catch((e) => console.debug("getContainerChanges failed:", e));
97282
97307
  }
97283
97308
  } else if (panelId === "services" && itemId) {
97284
- logManager.select(null);
97285
- statsManager.select(null);
97286
- const parts = itemId.split(":");
97287
- if (parts[0] === "project") {
97288
- composeLogManager.selectCompose(parts.slice(1).join(":"), null);
97289
- } else if (parts[0] === "service") {
97290
- composeLogManager.selectCompose(parts[1], parts.slice(2).join(":"));
97309
+ void logManager.select(null);
97310
+ void statsManager.select(null);
97311
+ if (detailTabIndex === 1) {
97312
+ const parts = itemId.split(":");
97313
+ if (parts[0] === "project") {
97314
+ void composeLogManager.selectCompose(parts.slice(1).join(":"), null);
97315
+ } else if (parts[0] === "service") {
97316
+ void composeLogManager.selectCompose(parts[1], parts.slice(2).join(":"));
97317
+ }
97318
+ } else {
97319
+ void composeLogManager.selectCompose(null, null);
97291
97320
  }
97321
+ flushLogsNow();
97322
+ flushComposeLogsNow();
97292
97323
  } else if (panelId === "images") {
97293
- logManager.select(null);
97294
- statsManager.select(null);
97295
- composeLogManager.selectCompose(null, null);
97324
+ void logManager.select(null);
97325
+ void statsManager.select(null);
97326
+ void composeLogManager.selectCompose(null, null);
97327
+ flushLogsNow();
97328
+ flushComposeLogsNow();
97296
97329
  if (itemId && !state.getImageLayers(itemId)) {
97297
97330
  client.getImageHistory(itemId).then((layers) => {
97298
97331
  state.setImageLayers(itemId, layers);
@@ -97300,9 +97333,11 @@ async function dashboardAction(_opts, cmd) {
97300
97333
  }).catch((e) => console.debug("getImageHistory failed:", e));
97301
97334
  }
97302
97335
  } else {
97303
- logManager.select(null);
97304
- statsManager.select(null);
97305
- composeLogManager.selectCompose(null, null);
97336
+ void logManager.select(null);
97337
+ void statsManager.select(null);
97338
+ void composeLogManager.selectCompose(null, null);
97339
+ flushLogsNow();
97340
+ flushComposeLogsNow();
97306
97341
  }
97307
97342
  };
97308
97343
  const panels = [
@@ -97337,7 +97372,7 @@ async function dashboardAction(_opts, cmd) {
97337
97372
  import_react37.default.createElement(Dashboard, {
97338
97373
  panels,
97339
97374
  metrics: getEnrichedMetrics(),
97340
- onSelectionChange,
97375
+ onViewStateChange,
97341
97376
  execTriggerRef,
97342
97377
  onExecFallback
97343
97378
  })
@@ -97349,7 +97384,7 @@ async function dashboardAction(_opts, cmd) {
97349
97384
  import_react37.default.createElement(Dashboard, {
97350
97385
  panels,
97351
97386
  metrics: getEnrichedMetrics(),
97352
- onSelectionChange,
97387
+ onViewStateChange,
97353
97388
  execTriggerRef,
97354
97389
  onExecFallback
97355
97390
  })
@@ -97383,7 +97418,7 @@ async function dashboardAction(_opts, cmd) {
97383
97418
  import_react37.default.createElement(Dashboard, {
97384
97419
  panels,
97385
97420
  metrics: getEnrichedMetrics(),
97386
- onSelectionChange,
97421
+ onViewStateChange,
97387
97422
  execTriggerRef,
97388
97423
  onExecFallback
97389
97424
  })
@@ -97406,6 +97441,14 @@ async function dashboardAction(_opts, cmd) {
97406
97441
  composeLogManager.dispose();
97407
97442
  } catch {
97408
97443
  }
97444
+ try {
97445
+ if (logFlushTimer) clearTimeout(logFlushTimer);
97446
+ } catch {
97447
+ }
97448
+ try {
97449
+ if (composeLogFlushTimer) clearTimeout(composeLogFlushTimer);
97450
+ } catch {
97451
+ }
97409
97452
  try {
97410
97453
  clearInterval(refreshInterval);
97411
97454
  } catch {
@@ -97490,7 +97533,7 @@ async function logsAction(container, opts) {
97490
97533
 
97491
97534
  // src/cli.ts
97492
97535
  var program2 = new Command();
97493
- program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.2.0").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
97536
+ program2.name("sidekick-docker").description("Docker management TUI dashboard").version("0.2.1").option("--socket <path>", "Docker socket path").action(async (_opts, cmd) => {
97494
97537
  await dashboardAction(_opts, cmd);
97495
97538
  });
97496
97539
  program2.command("ps").description("List containers").option("-a, --all", "Show all containers (default: running only)", false).action(async (opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-docker",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Docker management TUI dashboard",
5
5
  "author": "Cesar Andres Lopez <cesarandreslopez@gmail.com>",
6
6
  "repository": {