skydive-cli 0.2.0-beta.427 → 0.2.0-beta.455

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.
package/dist/js/bin.mjs CHANGED
@@ -20,7 +20,7 @@ import semver from "semver";
20
20
 
21
21
  //#region package.json
22
22
  var name = "skydive-cli";
23
- var version = "0.2.0-beta.427";
23
+ var version = "0.2.0-beta.455";
24
24
 
25
25
  //#endregion
26
26
  //#region src/types.ts
@@ -1200,7 +1200,7 @@ const importCommand = {
1200
1200
  process.exit(1);
1201
1201
  }
1202
1202
  }
1203
- const { runChat } = await import("./boot-BiMPCqRg.mjs");
1203
+ const { runChat } = await import("./boot-SxcKHfwY.mjs");
1204
1204
  await runChat({
1205
1205
  appUrl,
1206
1206
  sessionToken: session.value.sessionToken,
@@ -1511,7 +1511,7 @@ const chatCommand = {
1511
1511
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
1512
1512
  process.exit(1);
1513
1513
  }
1514
- const { runChat } = await import("./boot-BiMPCqRg.mjs");
1514
+ const { runChat } = await import("./boot-SxcKHfwY.mjs");
1515
1515
  await runChat({
1516
1516
  appUrl,
1517
1517
  sessionToken: session.value.sessionToken,
@@ -1810,7 +1810,7 @@ const switchCommand = {
1810
1810
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
1811
1811
  process.exit(1);
1812
1812
  }
1813
- const { runWorkspacePicker } = await import("./boot-BiMPCqRg.mjs");
1813
+ const { runWorkspacePicker } = await import("./boot-SxcKHfwY.mjs");
1814
1814
  await runWorkspacePicker(session);
1815
1815
  return;
1816
1816
  }
@@ -4357,6 +4357,10 @@ const keybindGroups = [
4357
4357
  keys: "1 / 2",
4358
4358
  action: "switch Changes / Files"
4359
4359
  },
4360
+ {
4361
+ keys: "\\",
4362
+ action: "toggle side / stacked layout"
4363
+ },
4360
4364
  {
4361
4365
  keys: "↵ / o / l",
4362
4366
  action: "focus selected file preview"
@@ -6747,7 +6751,7 @@ function gcOnce() {
6747
6751
  gcStarted = true;
6748
6752
  store.gc();
6749
6753
  }
6750
- function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHeight, active, shellLayout, tab, onTabChange, selectedChangeFile, onSelectedChangeFileChange, selectedWorkspaceFile, onSelectedWorkspaceFileChange, navigatorSize, onNavigatorResize, onInsertToComposer, onRequestChatFocus, onClose }) {
6754
+ function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHeight, active, shellLayout, tab, onTabChange, selectedChangeFile, onSelectedChangeFileChange, selectedWorkspaceFile, onSelectedWorkspaceFileChange, navigatorSize, onNavigatorResize, onToggleOrientation, onInsertToComposer, onRequestChatFocus, onClose }) {
6751
6755
  const width = viewportWidth;
6752
6756
  const height = viewportHeight;
6753
6757
  const shellHeight = Math.max(6, height - 2);
@@ -6898,6 +6902,10 @@ function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHei
6898
6902
  onTabChange("files");
6899
6903
  return;
6900
6904
  }
6905
+ if (key.name === "\\") {
6906
+ if (onToggleOrientation() === "unavailable") setStatus("no room to change layout in this terminal");
6907
+ return;
6908
+ }
6901
6909
  if (key.name === "s") {
6902
6910
  if (comments.length === 0) {
6903
6911
  setStatus("no pending comments");
@@ -7288,18 +7296,93 @@ function PaneRowView({ row, width, isCursor, inSelection, isAnchor, isCommented
7288
7296
  //#endregion
7289
7297
  //#region src/chat/tui/file-pane-layout.ts
7290
7298
  /**
7299
+ * Terminal cells are about twice as tall as they are wide, so a raw
7300
+ * cols-vs-rows compare would call almost any terminal "wide". Scale rows by
7301
+ * this factor to compare the *visual* aspect: a square-looking terminal has
7302
+ * `width ≈ height * CELL_ASPECT`.
7303
+ */
7304
+ const CELL_ASPECT = 2;
7305
+ /**
7306
+ * The automatic split follows the terminal's visual aspect: wider than tall
7307
+ * places review beside the chat (a vertical divider), taller than wide stacks
7308
+ * it underneath (a horizontal divider). This is the responsive default when
7309
+ * the user hasn't pinned an orientation.
7310
+ */
7311
+ function autoPrefersSide({ width, height }) {
7312
+ return width >= height * CELL_ASPECT;
7313
+ }
7314
+ /**
7315
+ * Bare-minimum room to render a split at all (as opposed to the higher
7316
+ * comfort thresholds the automatic choice uses). A user pin is honored down
7317
+ * to these: chat + review each need a usable strip, but we don't insist on
7318
+ * the roomier auto breakpoints. Mirrors the clamps in clampSidePaneWidth /
7319
+ * clampBelowPaneHeight so a pinned split never renders narrower than those.
7320
+ */
7321
+ function canRenderSide({ width, height }) {
7322
+ return width >= 76 && height >= 8;
7323
+ }
7324
+ function canRenderBelow({ width, height }) {
7325
+ return height >= 24 && width >= 24;
7326
+ }
7327
+ /**
7291
7328
  * Responsive placement for the file pane.
7292
7329
  *
7293
- * Wide terminals keep the conversation readable and place review beside it.
7294
- * Tall terminals without enough horizontal room stack review underneath.
7295
- * Cramped terminals give review the full body rather than rendering two
7296
- * unusably small surfaces.
7330
+ * With no user pin the split follows the terminal's visual aspect (see
7331
+ * autoPrefersSide): wider than tall -> side, taller than wide -> stacked.
7332
+ * The preferred split still has to be renderable; if it isn't we use the
7333
+ * other, and cramped terminals that can render neither get the full body.
7334
+ *
7335
+ * A user `orientation` pin always wins as long as the terminal can render
7336
+ * that split at all (the lower canRender* bar) — so you get the layout you
7337
+ * asked for even on a smallish terminal. Only when the pinned split
7338
+ * genuinely can't fit do we fall back: to the other split if it can render,
7339
+ * else to `fullscreen`.
7297
7340
  */
7298
- function filePanePlacement({ width, height }) {
7299
- if (width >= 140 && height >= 24) return "side";
7300
- if (width >= 50 && height >= 40) return "below";
7341
+ function filePanePlacement({ width, height, orientation }) {
7342
+ const renderSide = canRenderSide({
7343
+ width,
7344
+ height
7345
+ });
7346
+ const renderBelow = canRenderBelow({
7347
+ width,
7348
+ height
7349
+ });
7350
+ if (orientation === "side") {
7351
+ if (renderSide) return "side";
7352
+ if (renderBelow) return "below";
7353
+ return "fullscreen";
7354
+ }
7355
+ if (orientation === "below") {
7356
+ if (renderBelow) return "below";
7357
+ if (renderSide) return "side";
7358
+ return "fullscreen";
7359
+ }
7360
+ if (autoPrefersSide({
7361
+ width,
7362
+ height
7363
+ })) {
7364
+ if (renderSide) return "side";
7365
+ if (renderBelow) return "below";
7366
+ return "fullscreen";
7367
+ }
7368
+ if (renderBelow) return "below";
7369
+ if (renderSide) return "side";
7301
7370
  return "fullscreen";
7302
7371
  }
7372
+ /**
7373
+ * Whether toggling the orientation would actually change anything: both
7374
+ * splits must be renderable. When only one (or neither) can render, the
7375
+ * caller surfaces a hint instead of a silent no-op.
7376
+ */
7377
+ function canToggleOrientation({ width, height }) {
7378
+ return canRenderSide({
7379
+ width,
7380
+ height
7381
+ }) && canRenderBelow({
7382
+ width,
7383
+ height
7384
+ });
7385
+ }
7303
7386
  /** Keep a dragged side pane wide enough to use and leave the chat usable. */
7304
7387
  function clampSidePaneWidth(width, bodyWidth) {
7305
7388
  return Math.max(44, Math.min(bodyWidth - 30, width));
@@ -7827,6 +7910,7 @@ function ChatScreen({ agent, conversation }) {
7827
7910
  side: null,
7828
7911
  stacked: null
7829
7912
  });
7913
+ const [reviewOrientation, setReviewOrientation] = useState(null);
7830
7914
  const [reviewWorkspaceFile, setReviewWorkspaceFile] = useState(null);
7831
7915
  useEffect(() => {
7832
7916
  setReviewChangeFile(null);
@@ -7882,7 +7966,8 @@ function ChatScreen({ agent, conversation }) {
7882
7966
  const bodyHeight = Math.max(1, height - 3);
7883
7967
  const reviewPlacement = filePanePlacement({
7884
7968
  width,
7885
- height
7969
+ height,
7970
+ orientation: reviewOrientation
7886
7971
  });
7887
7972
  const reviewWidth = reviewPlacement === "side" ? clampSidePaneWidth(reviewSizePref.side ?? sidePaneWidth(bodyWidth), bodyWidth) : bodyWidth;
7888
7973
  const reviewHeight = reviewPlacement === "below" ? clampBelowPaneHeight(reviewSizePref.below ?? belowPaneHeight(bodyHeight), bodyHeight) : bodyHeight;
@@ -7925,6 +8010,7 @@ function ChatScreen({ agent, conversation }) {
7925
8010
  helpOpen,
7926
8011
  reviewOpen,
7927
8012
  reviewFocused,
8013
+ reviewPlacement,
7928
8014
  credPromptOpen
7929
8015
  ]);
7930
8016
  useEffect(() => {
@@ -9019,6 +9105,14 @@ function ChatScreen({ agent, conversation }) {
9019
9105
  ...prev,
9020
9106
  side: size
9021
9107
  }),
9108
+ onToggleOrientation: () => {
9109
+ if (!canToggleOrientation({
9110
+ width,
9111
+ height
9112
+ })) return "unavailable";
9113
+ setReviewOrientation(reviewPlacement === "below" ? "side" : "below");
9114
+ return "toggled";
9115
+ },
9022
9116
  onInsertToComposer: (review) => {
9023
9117
  const next = [inputRef.current.trimEnd(), review].filter((part) => part.length > 0).join("\n\n");
9024
9118
  setInput(next);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.2.0-beta.427",
3
+ "version": "0.2.0-beta.455",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",