@davmash/bygone 0.8.13 → 0.8.14

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.
@@ -3158,7 +3158,31 @@ var require_menuUtils = __commonJS({
3158
3158
  canRemovePanel: isMultiDiff && (session2?.multi?.files?.length || 0) > 1
3159
3159
  };
3160
3160
  }
3161
- module2.exports = { getMenuCapabilities: getMenuCapabilities2 };
3161
+ async function collectComparisonSelection2(choosePaths, confirmSelection) {
3162
+ const paths = [];
3163
+ while (true) {
3164
+ const selectedPaths = await choosePaths(paths.length);
3165
+ if (!Array.isArray(selectedPaths) || selectedPaths.length === 0) {
3166
+ return [];
3167
+ }
3168
+ for (const selectedPath of selectedPaths) {
3169
+ if (typeof selectedPath === "string" && !paths.includes(selectedPath)) {
3170
+ paths.push(selectedPath);
3171
+ }
3172
+ }
3173
+ if (paths.length < 2) {
3174
+ continue;
3175
+ }
3176
+ const decision = await confirmSelection([...paths]);
3177
+ if (decision === "compare") {
3178
+ return paths;
3179
+ }
3180
+ if (decision !== "add") {
3181
+ return [];
3182
+ }
3183
+ }
3184
+ }
3185
+ module2.exports = { collectComparisonSelection: collectComparisonSelection2, getMenuCapabilities: getMenuCapabilities2 };
3162
3186
  }
3163
3187
  });
3164
3188
 
@@ -10577,7 +10601,7 @@ var require_package = __commonJS({
10577
10601
  displayName: "Bygone",
10578
10602
  desktopName: "com.davidmashburn.bygone.desktop",
10579
10603
  description: "See how change happened. Visual diff and file history for VS Code.",
10580
- version: "0.8.13",
10604
+ version: "0.8.14",
10581
10605
  author: "David Mashburn",
10582
10606
  license: "MIT",
10583
10607
  private: false,
@@ -23811,7 +23835,7 @@ var { searchFileHistory: searchFileHistory2 } = (init_gitHistorySearch(), __toCo
23811
23835
  var { classifyAuthoredTourPaths: classifyAuthoredTourPaths2, discoverAuthoredTourDocument: discoverAuthoredTourDocument2 } = (init_tourDocument(), __toCommonJS(tourDocument_exports));
23812
23836
  var { materializeBranchReviewTrees: materializeBranchReviewTrees2, materializeGitTree: materializeGitTree2, resolveBranchReviewRange: resolveBranchReviewRange2, resolveReviewPathPair: resolveReviewPathPair2 } = (init_gitComparison(), __toCommonJS(gitComparison_exports));
23813
23837
  var { buildDirectoryNavigationState } = require_navigationUtils();
23814
- var { getMenuCapabilities } = require_menuUtils();
23838
+ var { collectComparisonSelection, getMenuCapabilities } = require_menuUtils();
23815
23839
  var {
23816
23840
  cloneSessionSource,
23817
23841
  createBranchReviewSource,
@@ -23891,6 +23915,8 @@ var wordWrapEnabled = false;
23891
23915
  var wordWrapAvailable = false;
23892
23916
  var repositorySearch = null;
23893
23917
  var lastRepositoryReplacement = null;
23918
+ var pendingLaunchPrompt = null;
23919
+ var nextLaunchPromptId = 1;
23894
23920
  var appIsQuitting = false;
23895
23921
  var restoringWindowState = false;
23896
23922
  if (!singleInstanceLock) {
@@ -24321,40 +24347,52 @@ ${HELP_URL}`,
24321
24347
  {
24322
24348
  label: "File",
24323
24349
  submenu: [
24350
+ {
24351
+ label: "New Blank Comparison",
24352
+ accelerator: "CmdOrCtrl+N",
24353
+ click: () => runMenuAction("open a blank comparison", openBlankDiff)
24354
+ },
24355
+ { type: "separator" },
24324
24356
  {
24325
24357
  label: "Compare Files\u2026",
24326
24358
  accelerator: "CmdOrCtrl+O",
24327
- click: () => {
24328
- void openCompareFilesDialog();
24329
- }
24359
+ click: () => runMenuAction("compare files", openCompareFilesDialog)
24330
24360
  },
24331
24361
  {
24332
24362
  label: "Compare Directories\u2026",
24333
24363
  accelerator: "CmdOrCtrl+Shift+O",
24334
- click: () => {
24335
- void openCompareDirectoriesDialog();
24336
- }
24364
+ click: () => runMenuAction("compare directories", openCompareDirectoriesDialog)
24337
24365
  },
24366
+ { type: "separator" },
24338
24367
  {
24339
- label: "Add Panel to Left\u2026",
24340
- enabled: canAddPanel,
24341
- click: () => {
24342
- void addPanelFromMenu("left");
24343
- }
24368
+ label: "View File or Directory History\u2026",
24369
+ accelerator: "CmdOrCtrl+Shift+H",
24370
+ click: () => runMenuAction("open Git history", openHistoryDialog)
24344
24371
  },
24345
24372
  {
24346
- label: "Add Panel to Right\u2026",
24347
- enabled: canAddPanel,
24348
- click: () => {
24349
- void addPanelFromMenu("right");
24350
- }
24373
+ label: "Open Authored Tour\u2026",
24374
+ click: () => runMenuAction("open an authored tour", openAuthoredTourDialog)
24351
24375
  },
24376
+ { type: "separator" },
24352
24377
  {
24353
- label: "Remove Active Panel",
24354
- enabled: canRemovePanel,
24355
- click: () => {
24356
- void removeActivePanelFromMenu();
24357
- }
24378
+ label: "Comparison Panels",
24379
+ submenu: [
24380
+ {
24381
+ label: "Add Panel to Left\u2026",
24382
+ enabled: canAddPanel,
24383
+ click: () => runMenuAction("add a panel", () => addPanelFromMenu("left"))
24384
+ },
24385
+ {
24386
+ label: "Add Panel to Right\u2026",
24387
+ enabled: canAddPanel,
24388
+ click: () => runMenuAction("add a panel", () => addPanelFromMenu("right"))
24389
+ },
24390
+ {
24391
+ label: "Remove Active Panel",
24392
+ enabled: canRemovePanel,
24393
+ click: () => runMenuAction("remove the active panel", removeActivePanelFromMenu)
24394
+ }
24395
+ ]
24358
24396
  },
24359
24397
  { type: "separator" },
24360
24398
  ...fileActionItems,
@@ -24416,9 +24454,7 @@ ${HELP_URL}`,
24416
24454
  {
24417
24455
  label: "Search in Files\u2026",
24418
24456
  accelerator: "CmdOrCtrl+Alt+F",
24419
- click: () => {
24420
- void openRepositorySearchDialog();
24421
- }
24457
+ click: () => runMenuAction("search in files", openRepositorySearchDialog)
24422
24458
  },
24423
24459
  { type: "separator" },
24424
24460
  {
@@ -24462,35 +24498,13 @@ ${HELP_URL}`,
24462
24498
  label: "Git",
24463
24499
  submenu: [
24464
24500
  {
24465
- label: "View File or Directory History\u2026",
24466
- accelerator: "CmdOrCtrl+Shift+H",
24467
- click: () => {
24468
- void openHistoryDialog();
24469
- }
24501
+ label: "Compare Revisions\u2026",
24502
+ click: () => runMenuAction("compare Git revisions", openGitRefsDialog)
24470
24503
  },
24471
24504
  {
24472
- label: "Explore Current Branch Change",
24505
+ label: "Review Branch Change\u2026",
24473
24506
  accelerator: "CmdOrCtrl+Shift+R",
24474
- click: () => {
24475
- void openBranchReviewDialog();
24476
- }
24477
- },
24478
- { type: "separator" },
24479
- {
24480
- label: "Older Commit",
24481
- accelerator: "Alt+Left",
24482
- enabled: isHistory,
24483
- click: () => {
24484
- void navigateHistory("back");
24485
- }
24486
- },
24487
- {
24488
- label: "Newer Commit",
24489
- accelerator: "Alt+Right",
24490
- enabled: isHistory,
24491
- click: () => {
24492
- void navigateHistory("forward");
24493
- }
24507
+ click: () => runMenuAction("review a branch change", openBranchReviewDialog)
24494
24508
  }
24495
24509
  ]
24496
24510
  },
@@ -24499,15 +24513,11 @@ ${HELP_URL}`,
24499
24513
  submenu: [
24500
24514
  {
24501
24515
  label: "Present Current Branch",
24502
- click: () => {
24503
- void presentCurrentBranch();
24504
- }
24516
+ click: () => runMenuAction("present the current branch", presentCurrentBranch)
24505
24517
  },
24506
24518
  {
24507
- label: "Open Authored Tour\u2026",
24508
- click: () => {
24509
- void openAuthoredTourDialog().catch((error) => showError(`Could not open Bygone presentation: ${getErrorMessage(error)}`));
24510
- }
24519
+ label: "Present Branch or Ref\u2026",
24520
+ click: () => runMenuAction("present a branch or ref", openPresentBranchDialog)
24511
24521
  },
24512
24522
  { type: "separator" },
24513
24523
  {
@@ -24540,6 +24550,23 @@ ${HELP_URL}`,
24540
24550
  click: () => {
24541
24551
  void returnToDirectoryView();
24542
24552
  }
24553
+ },
24554
+ { type: "separator" },
24555
+ {
24556
+ label: "Older Commit",
24557
+ accelerator: "Alt+Left",
24558
+ enabled: isHistory,
24559
+ click: () => {
24560
+ void navigateHistory("back");
24561
+ }
24562
+ },
24563
+ {
24564
+ label: "Newer Commit",
24565
+ accelerator: "Alt+Right",
24566
+ enabled: isHistory,
24567
+ click: () => {
24568
+ void navigateHistory("forward");
24569
+ }
24543
24570
  }
24544
24571
  ]
24545
24572
  },
@@ -24642,9 +24669,38 @@ ${HELP_URL}`,
24642
24669
  ];
24643
24670
  Menu.setApplicationMenu(Menu.buildFromTemplate(template));
24644
24671
  }
24672
+ function runMenuAction(description, action) {
24673
+ void Promise.resolve().then(action).catch((error) => showError(`Could not ${description}: ${getErrorMessage(error)}`));
24674
+ }
24645
24675
  async function presentCurrentBranch() {
24646
- const cwd = getSourceRepoRoot(session.source) || process.cwd();
24647
- await openTourPresentation([], cwd);
24676
+ const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to present");
24677
+ if (repoRoot) {
24678
+ await openTourPresentation([], repoRoot);
24679
+ }
24680
+ }
24681
+ async function openPresentBranchDialog() {
24682
+ const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to present");
24683
+ if (!repoRoot) {
24684
+ return;
24685
+ }
24686
+ openLaunchPrompt({
24687
+ kind: "present-branch",
24688
+ title: "Present Branch or Ref",
24689
+ description: `Repository: ${repoRoot}`,
24690
+ submitLabel: "Present",
24691
+ fields: [
24692
+ { name: "head", label: "Head branch or ref", value: "HEAD", required: true },
24693
+ { name: "base", label: "Base branch or ref (optional)", value: "" }
24694
+ ]
24695
+ }, async (values) => {
24696
+ const head = values.head.trim() || "HEAD";
24697
+ const base = values.base.trim();
24698
+ const args = [head];
24699
+ if (base) {
24700
+ args.push(tokensFor("base")[0], base);
24701
+ }
24702
+ await openTourPresentation(args, repoRoot);
24703
+ });
24648
24704
  }
24649
24705
  async function showRepositorySearchStatus() {
24650
24706
  const capability = detectRipgrepCapability2();
@@ -25253,6 +25309,24 @@ async function handleRendererMessage(message) {
25253
25309
  await refreshSession();
25254
25310
  return;
25255
25311
  }
25312
+ if (message.type === "submitLaunchPrompt" && Number.isInteger(message.requestId) && message.requestId === pendingLaunchPrompt?.requestId && message.values && typeof message.values === "object" && !Array.isArray(message.values)) {
25313
+ const prompt = pendingLaunchPrompt;
25314
+ pendingLaunchPrompt = null;
25315
+ const values = Object.fromEntries(prompt.fieldNames.map((fieldName) => [
25316
+ fieldName,
25317
+ typeof message.values[fieldName] === "string" ? message.values[fieldName] : ""
25318
+ ]));
25319
+ try {
25320
+ await prompt.submit(values);
25321
+ } catch (error) {
25322
+ await showError(`Could not ${prompt.description}: ${getErrorMessage(error)}`);
25323
+ }
25324
+ return;
25325
+ }
25326
+ if (message.type === "cancelLaunchPrompt" && Number.isInteger(message.requestId) && message.requestId === pendingLaunchPrompt?.requestId) {
25327
+ pendingLaunchPrompt = null;
25328
+ return;
25329
+ }
25256
25330
  if (message.type === "searchChangeSet" && Number.isInteger(message.requestId)) {
25257
25331
  searchCurrentChangeSet(message);
25258
25332
  return;
@@ -25406,25 +25480,116 @@ async function handleRendererMessage(message) {
25406
25480
  }
25407
25481
  }
25408
25482
  async function openCompareFilesDialog() {
25409
- if (!mainWindow) {
25483
+ const filePaths = await collectComparisonPaths("file");
25484
+ if (filePaths.length < 2) {
25410
25485
  return;
25411
25486
  }
25412
- const result = await dialog.showOpenDialog(mainWindow, {
25413
- title: "Select two files to compare",
25414
- properties: ["openFile", "multiSelections"]
25487
+ if (filePaths.length === 2) {
25488
+ await openDiff(filePaths[0], filePaths[1]);
25489
+ } else {
25490
+ await openMultiDiff(filePaths);
25491
+ }
25492
+ }
25493
+ async function collectComparisonPaths(kind) {
25494
+ ensureExploreWindow();
25495
+ const isDirectory = kind === "directory";
25496
+ const noun = isDirectory ? "directory" : "file";
25497
+ return collectComparisonSelection(async (selectedCount) => {
25498
+ const result = await dialog.showOpenDialog(mainWindow, {
25499
+ title: selectedCount === 0 ? `Select ${noun}s to compare` : `Add ${noun}${selectedCount === 1 ? "" : "s"} to the comparison`,
25500
+ properties: [isDirectory ? "openDirectory" : "openFile", "multiSelections"]
25501
+ });
25502
+ if (result.canceled) {
25503
+ return null;
25504
+ }
25505
+ return result.filePaths.map((selectedPath) => path8.resolve(selectedPath));
25506
+ }, async (paths) => {
25507
+ const choice = await dialog.showMessageBox(mainWindow, {
25508
+ type: "question",
25509
+ buttons: [`Compare ${paths.length}`, "Add More\u2026", "Cancel"],
25510
+ defaultId: 0,
25511
+ cancelId: 2,
25512
+ message: `Compare ${paths.length} selected ${noun}${paths.length === 1 ? "" : "s"}?`,
25513
+ detail: paths.join("\n")
25514
+ });
25515
+ if (choice.response === 0) {
25516
+ return "compare";
25517
+ }
25518
+ return choice.response === 1 ? "add" : "cancel";
25519
+ });
25520
+ }
25521
+ function ensureExploreWindow() {
25522
+ ensureMainWindow();
25523
+ if (!mainWindow.isVisible()) {
25524
+ mainWindow.show();
25525
+ }
25526
+ mainWindow.focus();
25527
+ }
25528
+ function openLaunchPrompt(config, submit) {
25529
+ ensureExploreWindow();
25530
+ const requestId = nextLaunchPromptId++;
25531
+ pendingLaunchPrompt = {
25532
+ requestId,
25533
+ fieldNames: config.fields.map((field) => field.name),
25534
+ description: config.failureDescription || config.title.toLowerCase(),
25535
+ submit
25536
+ };
25537
+ postToRenderer({
25538
+ type: "openLaunchPrompt",
25539
+ requestId,
25540
+ ...config
25415
25541
  });
25416
- if (result.canceled || result.filePaths.length < 2) {
25417
- if (!result.canceled) {
25418
- await showInfo("Select at least two files to compare.");
25542
+ }
25543
+ async function resolveOrChooseGitRepository(title) {
25544
+ const sourcePath = session.source?.repoRoot || session.source?.path || session.source?.paths?.[0];
25545
+ const sourceCandidate = sourcePath && getPathKind(sourcePath) === "file" ? path8.dirname(sourcePath) : sourcePath;
25546
+ const candidates = [sourceCandidate, launchArguments.cwd, process.cwd()].filter((candidate, index, all) => candidate && all.indexOf(candidate) === index);
25547
+ for (const candidate of candidates) {
25548
+ try {
25549
+ return fs7.realpathSync(runGit(["rev-parse", "--show-toplevel"], candidate));
25550
+ } catch {
25419
25551
  }
25420
- return;
25421
25552
  }
25422
- if (result.filePaths.length === 2) {
25423
- await openDiff(result.filePaths[0], result.filePaths[1]);
25424
- } else {
25425
- await openMultiDiff(result.filePaths);
25553
+ ensureExploreWindow();
25554
+ const result = await dialog.showOpenDialog(mainWindow, {
25555
+ title,
25556
+ properties: ["openDirectory"]
25557
+ });
25558
+ if (result.canceled || result.filePaths.length === 0) {
25559
+ return null;
25560
+ }
25561
+ try {
25562
+ return fs7.realpathSync(runGit(["rev-parse", "--show-toplevel"], result.filePaths[0]));
25563
+ } catch (error) {
25564
+ await showError(`The selected directory is not inside a Git repository: ${getErrorMessage(error)}`);
25565
+ return null;
25426
25566
  }
25427
25567
  }
25568
+ async function openGitRefsDialog() {
25569
+ const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to compare");
25570
+ if (!repoRoot) {
25571
+ return;
25572
+ }
25573
+ openLaunchPrompt({
25574
+ kind: "git-refs",
25575
+ title: "Compare Git Revisions",
25576
+ description: `Repository: ${repoRoot}`,
25577
+ submitLabel: "Compare",
25578
+ fields: [{
25579
+ name: "refs",
25580
+ label: "Revisions (two or more, separated by spaces)",
25581
+ value: "HEAD~1 HEAD",
25582
+ required: true
25583
+ }]
25584
+ }, async (values) => {
25585
+ const refs = values.refs.trim().split(/\s+/).filter(Boolean);
25586
+ if (refs.length < 2) {
25587
+ await showInfo("Enter at least two Git revisions to compare.");
25588
+ return;
25589
+ }
25590
+ await openGitRefs(repoRoot, refs);
25591
+ });
25592
+ }
25428
25593
  async function openDroppedFiles(paths) {
25429
25594
  const normalizedPaths = paths.filter((candidate) => typeof candidate === "string" && candidate.length > 0).map((candidate) => path8.resolve(candidate)).filter((candidate, index, all) => all.indexOf(candidate) === index);
25430
25595
  const tourSelection = classifyAuthoredTourPaths2(normalizedPaths);
@@ -25488,37 +25653,29 @@ async function openHistoryDialog() {
25488
25653
  await openHistory(targetPath, historyIncludeStagedPreference);
25489
25654
  }
25490
25655
  async function openBranchReviewDialog() {
25491
- let reviewRoot = launchArguments.cwd || process.cwd();
25492
- try {
25493
- runGit(["rev-parse", "--show-toplevel"], reviewRoot);
25494
- } catch {
25495
- if (!mainWindow) {
25496
- return;
25497
- }
25498
- const result = await dialog.showOpenDialog(mainWindow, {
25499
- title: "Select a Git repository to review",
25500
- properties: ["openDirectory"]
25501
- });
25502
- if (result.canceled || result.filePaths.length === 0) {
25503
- return;
25504
- }
25505
- reviewRoot = result.filePaths[0];
25506
- }
25507
- await openGitBranchReview(reviewRoot, "HEAD");
25508
- }
25509
- async function openCompareDirectoriesDialog() {
25510
- if (!mainWindow) {
25656
+ const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to review");
25657
+ if (!repoRoot) {
25511
25658
  return;
25512
25659
  }
25513
- const result = await dialog.showOpenDialog(mainWindow, {
25514
- title: "Select directories to compare",
25515
- properties: ["openDirectory", "multiSelections"]
25660
+ openLaunchPrompt({
25661
+ kind: "review-branch",
25662
+ title: "Review Branch Change",
25663
+ description: `Repository: ${repoRoot}`,
25664
+ submitLabel: "Review",
25665
+ fields: [
25666
+ { name: "head", label: "Head branch or ref", value: "HEAD", required: true },
25667
+ { name: "base", label: "Base branch or ref (optional)", value: "" }
25668
+ ]
25669
+ }, async (values) => {
25670
+ await openGitBranchReview(repoRoot, values.head.trim() || "HEAD", values.base.trim() || void 0);
25516
25671
  });
25517
- if (result.canceled || result.filePaths.length < 2) {
25518
- if (!result.canceled) await showInfo("Select at least two directories to compare.");
25672
+ }
25673
+ async function openCompareDirectoriesDialog() {
25674
+ const directoryPaths = await collectComparisonPaths("directory");
25675
+ if (directoryPaths.length < 2) {
25519
25676
  return;
25520
25677
  }
25521
- await openDirectories(result.filePaths);
25678
+ await openDirectories(directoryPaths);
25522
25679
  }
25523
25680
  async function openDirectories(dirs, options = {}) {
25524
25681
  const resolvedDirs = dirs.map((dir) => path8.resolve(dir)).filter((dir, index, all) => all.indexOf(dir) === index);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davmash/bygone",
3
- "version": "0.8.13",
3
+ "version": "0.8.14",
4
4
  "description": "See how change happened. Visual diff and file history.",
5
5
  "author": "David Mashburn",
6
6
  "license": "MIT",