goblin-malin 0.1.2 → 0.1.4

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.
@@ -1,4 +1,5 @@
1
1
  import {
2
+ APP_VERSION,
2
3
  DEFAULT_APP_DATA_DIR,
3
4
  SETTINGS_PATH,
4
5
  SettingsStore,
@@ -10,15 +11,15 @@ import {
10
11
  getCacheDir,
11
12
  globalLogger,
12
13
  inkTransport
13
- } from "./chunk-ZZ35YTR2.js";
14
+ } from "./chunk-PYRJ4FSU.js";
14
15
 
15
16
  // src/index.tsx
16
- import React64 from "react";
17
+ import React65 from "react";
17
18
  import { render } from "ink";
18
19
  import process2 from "process";
19
20
 
20
21
  // src/components/App.tsx
21
- import React62, { useEffect as useEffect21, useMemo as useMemo5, useState as useState22 } from "react";
22
+ import React63, { useCallback as useCallback7, useEffect as useEffect22, useMemo as useMemo5, useState as useState23 } from "react";
22
23
 
23
24
  // src/hooks/useScreenSize.ts
24
25
  import { useStdout } from "ink";
@@ -404,6 +405,14 @@ var useFocusManager = /* @__PURE__ */ __name(({ toolbarButtonCount, taskCount, t
404
405
  isEditingField: editing
405
406
  }));
406
407
  }, []);
408
+ const openUpdateModal = useCallback2(() => {
409
+ setFocusState((prev) => ({
410
+ ...prev,
411
+ previousWindow: prev.activeWindow,
412
+ activeWindow: "updateModal",
413
+ returningFromWindow: void 0
414
+ }));
415
+ }, []);
407
416
  const openWizard = useCallback2((config, onDisable) => {
408
417
  setFocusState((prev) => ({
409
418
  ...prev,
@@ -445,7 +454,8 @@ var useFocusManager = /* @__PURE__ */ __name(({ toolbarButtonCount, taskCount, t
445
454
  setSourcesInnerFocus,
446
455
  setDetailFieldIndex: setSourceDetailFieldIndex,
447
456
  setIsEditingField,
448
- openWizard
457
+ openWizard,
458
+ openUpdateModal
449
459
  };
450
460
  }, "useFocusManager");
451
461
 
@@ -1688,24 +1698,6 @@ var ServiceRegistry = class {
1688
1698
  }
1689
1699
  };
1690
1700
 
1691
- // src/flows/musicDownloadFlow/toolbar/useExitButton.ts
1692
- var LEAVE_ALT_SCREEN_COMMAND = "\x1B[?1049l";
1693
- var useExitButton = /* @__PURE__ */ __name(() => {
1694
- const theme = useTheme();
1695
- return {
1696
- label: "Exit",
1697
- icon: "\u21B3",
1698
- color: theme.action.destructive,
1699
- enabled: true,
1700
- onPress: /* @__PURE__ */ __name(() => {
1701
- process.stdout.write(LEAVE_ALT_SCREEN_COMMAND);
1702
- console.info("Saving cache before exit\u2026");
1703
- cache.save();
1704
- process.exit(0);
1705
- }, "onPress")
1706
- };
1707
- }, "useExitButton");
1708
-
1709
1701
  // src/flows/musicDownloadFlow/toolbar/useRunAllButton.ts
1710
1702
  import { useEffect as useEffect3, useState as useState4 } from "react";
1711
1703
  var useRunAllButton = /* @__PURE__ */ __name(({ flow, orchestrator }) => {
@@ -1752,19 +1744,6 @@ var useRunAllButton = /* @__PURE__ */ __name(({ flow, orchestrator }) => {
1752
1744
  };
1753
1745
  }, "useRunAllButton");
1754
1746
 
1755
- // src/flows/musicDownloadFlow/toolbar/useSettingsButton.ts
1756
- var useSettingsButton = /* @__PURE__ */ __name(() => {
1757
- const theme = useTheme();
1758
- const { switchWindow } = useFocusContext();
1759
- return {
1760
- label: "Settings",
1761
- icon: "\u26ED",
1762
- color: theme.action.neutral,
1763
- enabled: true,
1764
- onPress: /* @__PURE__ */ __name(() => switchWindow("settingsModal"), "onPress")
1765
- };
1766
- }, "useSettingsButton");
1767
-
1768
1747
  // src/flows/musicDownloadFlow/columns/UrlCell.tsx
1769
1748
  import React6 from "react";
1770
1749
  import { Text as Text2 } from "ink";
@@ -4645,7 +4624,7 @@ var DownloadTask = class extends Task {
4645
4624
  }
4646
4625
  if (existingSavedPath) {
4647
4626
  if (existingSavedPath !== outputPath) {
4648
- const { moveFile } = await import("./metadata-AGDUAOW7.js");
4627
+ const { moveFile } = await import("./metadata-6BFFVJQ7.js");
4649
4628
  await moveFile(existingSavedPath, outputPath);
4650
4629
  }
4651
4630
  outputCreated = true;
@@ -4980,7 +4959,6 @@ function toOpenableUri(url) {
4980
4959
  }
4981
4960
  __name(toOpenableUri, "toOpenableUri");
4982
4961
  var GenericProviderCell = /* @__PURE__ */ __name(() => null, "GenericProviderCell");
4983
- var DEFAULT_TEST_URL = "https://open.spotify.com/track/4v7kKFlEDmpVToHOICsXaM";
4984
4962
  var MusicDownloadFlow = class _MusicDownloadFlow extends FlowBase {
4985
4963
  static {
4986
4964
  __name(this, "MusicDownloadFlow");
@@ -5042,14 +5020,6 @@ var MusicDownloadFlow = class _MusicDownloadFlow extends FlowBase {
5042
5020
  this.discoveryServiceRegistry.register("songlink", SonglinkService);
5043
5021
  this.downloadServiceRegistry.register("ytdlp", YtDlpService);
5044
5022
  SettingsStore.getInstance().onSettingsChanged(() => this.notifyTaskSubscribers());
5045
- const defaultTasks = this.createTasksFromUrls([
5046
- DEFAULT_TEST_URL
5047
- ], {
5048
- toTag: true,
5049
- toDownload: true
5050
- });
5051
- this.orchestrator.addTasks(defaultTasks);
5052
- this.logger.info(`Imported default test URL: ${DEFAULT_TEST_URL}`);
5053
5023
  }
5054
5024
  // ── FlowBase overrides ───────────────────────────────────────────────────
5055
5025
  getDisplayMode() {
@@ -5112,9 +5082,7 @@ var MusicDownloadFlow = class _MusicDownloadFlow extends FlowBase {
5112
5082
  }
5113
5083
  getToolbarButtons() {
5114
5084
  return [
5115
- useRunAllButton,
5116
- useSettingsButton,
5117
- useExitButton
5085
+ useRunAllButton
5118
5086
  ];
5119
5087
  }
5120
5088
  getContextualActionBar(task, attributes) {
@@ -6023,8 +5991,8 @@ function getInstance() {
6023
5991
  __name(getInstance, "getInstance");
6024
5992
 
6025
5993
  // src/components/AppInner.tsx
6026
- import React61 from "react";
6027
- import { Box as Box42 } from "ink";
5994
+ import React62 from "react";
5995
+ import { Box as Box43 } from "ink";
6028
5996
 
6029
5997
  // src/components/Footer.tsx
6030
5998
  import React16, { useEffect as useEffect5, useState as useState6 } from "react";
@@ -6553,33 +6521,44 @@ function buildGlobalSettingsItems(settings, onChange) {
6553
6521
  {
6554
6522
  kind: "checkbox",
6555
6523
  indent: 0,
6556
- label: "Re-open last session on start-up",
6557
- get: /* @__PURE__ */ __name(() => settings.general.reopenLastSession, "get"),
6524
+ label: "Show welcome tutorial on start-up",
6525
+ get: /* @__PURE__ */ __name(() => settings.general.showWelcomeTutorial, "get"),
6558
6526
  set: /* @__PURE__ */ __name((v) => onChange({
6559
6527
  general: {
6560
- reopenLastSession: v
6528
+ showWelcomeTutorial: v
6561
6529
  }
6562
6530
  }), "set")
6563
6531
  },
6564
6532
  {
6565
6533
  kind: "checkbox",
6566
6534
  indent: 0,
6567
- label: "Enable animations",
6568
- get: /* @__PURE__ */ __name(() => settings.general.animationsEnabled, "get"),
6535
+ label: "Check for updates on start-up",
6536
+ get: /* @__PURE__ */ __name(() => settings.general.checkForUpdates, "get"),
6569
6537
  set: /* @__PURE__ */ __name((v) => onChange({
6570
6538
  general: {
6571
- animationsEnabled: v
6539
+ checkForUpdates: v
6572
6540
  }
6573
6541
  }), "set")
6574
6542
  },
6575
6543
  {
6576
6544
  kind: "checkbox",
6577
6545
  indent: 0,
6578
- label: "Show welcome tutorial on start-up",
6579
- get: /* @__PURE__ */ __name(() => settings.general.showWelcomeTutorial, "get"),
6546
+ label: "Re-open last session on start-up",
6547
+ get: /* @__PURE__ */ __name(() => settings.general.reopenLastSession, "get"),
6580
6548
  set: /* @__PURE__ */ __name((v) => onChange({
6581
6549
  general: {
6582
- showWelcomeTutorial: v
6550
+ reopenLastSession: v
6551
+ }
6552
+ }), "set")
6553
+ },
6554
+ {
6555
+ kind: "checkbox",
6556
+ indent: 0,
6557
+ label: "Enable animations",
6558
+ get: /* @__PURE__ */ __name(() => settings.general.animationsEnabled, "get"),
6559
+ set: /* @__PURE__ */ __name((v) => onChange({
6560
+ general: {
6561
+ animationsEnabled: v
6583
6562
  }
6584
6563
  }), "set")
6585
6564
  },
@@ -6849,7 +6828,7 @@ var SettingsItemRow = /* @__PURE__ */ __name(({ item, isSelected, isEditing, edi
6849
6828
  }, item.label));
6850
6829
  case "providerHeader": {
6851
6830
  const PROVIDER_INDENT = 2;
6852
- const WARNING = " \u26A0 Missing credentials";
6831
+ const WARNING = " \u25B3 Missing credentials";
6853
6832
  const warningLen = item.missingCredentials ? WARNING.length : 0;
6854
6833
  const padLen = Math.max(0, innerWidth - item.label.length - PROVIDER_INDENT - 4 - warningLen);
6855
6834
  return /* @__PURE__ */ React22.createElement(Box9, {
@@ -6858,7 +6837,7 @@ var SettingsItemRow = /* @__PURE__ */ __name(({ item, isSelected, isEditing, edi
6858
6837
  }, /* @__PURE__ */ React22.createElement(Text18, {
6859
6838
  color: item.color,
6860
6839
  bold: true
6861
- }, item.label), item.missingCredentials && /* @__PURE__ */ React22.createElement(Text18, {
6840
+ }, item.label + " "), item.missingCredentials && /* @__PURE__ */ React22.createElement(Text18, {
6862
6841
  color: "yellow"
6863
6842
  }, WARNING), /* @__PURE__ */ React22.createElement(Text18, {
6864
6843
  dimColor: true
@@ -6871,7 +6850,7 @@ var SettingsItemRow = /* @__PURE__ */ __name(({ item, isSelected, isEditing, edi
6871
6850
  }, /* @__PURE__ */ React22.createElement(Text18, {
6872
6851
  color: isSelected ? theme.ui.focusIndicator : void 0,
6873
6852
  bold: isSelected
6874
- }, cursor, item.get() ? "\u2611" : "\u2610", " " + item.label));
6853
+ }, cursor, item.get() ? "\u2714" : "\u2610", " " + item.label));
6875
6854
  case "textInput": {
6876
6855
  const prefix = cursor + item.label + ": ";
6877
6856
  return /* @__PURE__ */ React22.createElement(Box9, {
@@ -7823,7 +7802,6 @@ var SetupWizardModal = /* @__PURE__ */ __name(({ tasks, terminalHeight, terminal
7823
7802
  import React27, { useCallback as useCallback4, useEffect as useEffect13, useState as useState13 } from "react";
7824
7803
  import { Box as Box13, Text as Text22 } from "ink";
7825
7804
  import Gradient from "ink-gradient";
7826
- import BigText from "ink-big-text";
7827
7805
 
7828
7806
  // src/components/WithBackground.tsx
7829
7807
  import React26 from "react";
@@ -7934,6 +7912,7 @@ var WelcomeModal = /* @__PURE__ */ __name(({ terminalHeight, terminalWidth }) =>
7934
7912
  borderBackgroundColor: theme.ui.background,
7935
7913
  paddingX: 2,
7936
7914
  paddingY: 1,
7915
+ flexShrink: 0,
7937
7916
  width: modalWidth,
7938
7917
  backgroundColor: theme.ui.background
7939
7918
  }, /* @__PURE__ */ React27.createElement(Box13, {
@@ -7943,27 +7922,21 @@ var WelcomeModal = /* @__PURE__ */ __name(({ terminalHeight, terminalWidth }) =>
7943
7922
  }, /* @__PURE__ */ React27.createElement(Text22, {
7944
7923
  bold: true,
7945
7924
  color: theme.ui.modalBorder
7946
- }, "Welcome to "), hasSmallScreen && /* @__PURE__ */ React27.createElement(WithBackground, {
7925
+ }, "Welcome to "), /* @__PURE__ */ React27.createElement(WithBackground, {
7947
7926
  color: theme.ui.background
7948
7927
  }, /* @__PURE__ */ React27.createElement(Gradient, {
7949
7928
  name: "rainbow"
7950
7929
  }, /* @__PURE__ */ React27.createElement(Text22, {
7951
7930
  bold: true
7952
- }, "Goblin Malin \u{1F609}")))), !hasSmallScreen && /* @__PURE__ */ React27.createElement(Box13, {
7953
- overflow: "hidden"
7954
- }, /* @__PURE__ */ React27.createElement(WithBackground, {
7955
- color: theme.ui.background
7956
- }, /* @__PURE__ */ React27.createElement(Gradient, {
7957
- name: "rainbow"
7958
- }, /* @__PURE__ */ React27.createElement(BigText, {
7959
- text: "Goblin Malin"
7960
- })))), /* @__PURE__ */ React27.createElement(Box13, {
7961
- flexDirection: "column"
7931
+ }, "Goblin Malin \u{1F609}")))), /* @__PURE__ */ React27.createElement(Box13, {
7932
+ flexDirection: "column",
7933
+ flexShrink: 0
7962
7934
  }, /* @__PURE__ */ React27.createElement(Text22, null, "Paste a Spotify or YouTube link \u2014 Goblin Malin handles the rest."), /* @__PURE__ */ React27.createElement(Text22, {
7963
7935
  dimColor: true
7964
7936
  }, "Here's how it works:")), /* @__PURE__ */ React27.createElement(Box13, {
7965
7937
  flexDirection: "column",
7966
- marginTop: 1
7938
+ marginTop: 1,
7939
+ flexShrink: 0
7967
7940
  }, STEPS.map((step, i) => /* @__PURE__ */ React27.createElement(Box13, {
7968
7941
  key: i,
7969
7942
  flexDirection: "row"
@@ -7971,16 +7944,19 @@ var WelcomeModal = /* @__PURE__ */ __name(({ terminalHeight, terminalWidth }) =>
7971
7944
  color: theme.text.muted
7972
7945
  }, ` ${i + 1}. `), /* @__PURE__ */ React27.createElement(Text22, null, step)))), /* @__PURE__ */ React27.createElement(Box13, {
7973
7946
  flexDirection: "column",
7974
- marginTop: 1
7947
+ marginTop: 1,
7948
+ flexShrink: 0
7975
7949
  }, /* @__PURE__ */ React27.createElement(Text22, {
7976
7950
  bold: true,
7977
7951
  color: theme.text.secondary
7978
7952
  }, "Useful shortcuts:"), SHORTCUTS.map(([shortcut, desc]) => /* @__PURE__ */ React27.createElement(Box13, {
7979
7953
  key: shortcut,
7980
- flexDirection: "row"
7954
+ flexDirection: "row",
7955
+ flexShrink: 0
7981
7956
  }, /* @__PURE__ */ React27.createElement(Box13, {
7982
7957
  width: 12,
7983
- minWidth: 12
7958
+ minWidth: 12,
7959
+ flexShrink: 0
7984
7960
  }, /* @__PURE__ */ React27.createElement(Text22, {
7985
7961
  bold: true,
7986
7962
  color: theme.text.active
@@ -7989,14 +7965,16 @@ var WelcomeModal = /* @__PURE__ */ __name(({ terminalHeight, terminalWidth }) =>
7989
7965
  }, desc)))), /* @__PURE__ */ React27.createElement(Box13, {
7990
7966
  marginTop: 1,
7991
7967
  flexDirection: "row",
7992
- alignItems: "center"
7968
+ alignItems: "center",
7969
+ flexShrink: 0
7993
7970
  }, /* @__PURE__ */ React27.createElement(Text22, {
7994
7971
  color: dontShowAgain ? theme.text.active : theme.text.muted
7995
7972
  }, dontShowAgain ? "[x]" : "[ ]"), /* @__PURE__ */ React27.createElement(Text22, {
7996
7973
  dimColor: true
7997
7974
  }, " Don't show this again")), /* @__PURE__ */ React27.createElement(Box13, {
7998
7975
  marginTop: 1,
7999
- flexDirection: "row"
7976
+ flexDirection: "row",
7977
+ flexShrink: 0
8000
7978
  }, /* @__PURE__ */ React27.createElement(Hint, {
8001
7979
  label: "Toggle",
8002
7980
  shortcut: "Enter"
@@ -8006,11 +7984,276 @@ var WelcomeModal = /* @__PURE__ */ __name(({ terminalHeight, terminalWidth }) =>
8006
7984
  }))));
8007
7985
  }, "WelcomeModal");
8008
7986
 
7987
+ // src/components/UpdateModal/UpdateModal.tsx
7988
+ import React28, { useCallback as useCallback5, useEffect as useEffect14, useRef as useRef3, useState as useState14 } from "react";
7989
+ import { Box as Box14, Text as Text23 } from "ink";
7990
+ import { spawn as spawn3, exec as exec2 } from "child_process";
7991
+ import open2 from "open";
7992
+
7993
+ // src/updater/installSource.ts
7994
+ import { createRequire } from "module";
7995
+ import { readFileSync } from "fs";
7996
+ import { join as join9 } from "path";
7997
+ var _require = createRequire(import.meta.url);
7998
+ var IS_SEA = (() => {
7999
+ try {
8000
+ const sea = _require("node:sea");
8001
+ return sea.isSea();
8002
+ } catch {
8003
+ return false;
8004
+ }
8005
+ })();
8006
+ function getInstaller() {
8007
+ try {
8008
+ const raw = readFileSync(join9(DEFAULT_APP_DATA_DIR, "install.json"), "utf8");
8009
+ const installer2 = JSON.parse(raw).installer;
8010
+ if (installer2 === "yarn" || installer2 === "pnpm" || installer2 === "npm") return installer2;
8011
+ return "unknown";
8012
+ } catch {
8013
+ return "npm";
8014
+ }
8015
+ }
8016
+ __name(getInstaller, "getInstaller");
8017
+ function getUpdateCommand() {
8018
+ switch (getInstaller()) {
8019
+ case "yarn":
8020
+ return "yarn global add goblin-malin";
8021
+ case "pnpm":
8022
+ return "pnpm add -g goblin-malin";
8023
+ default:
8024
+ return "npm install -g goblin-malin";
8025
+ }
8026
+ }
8027
+ __name(getUpdateCommand, "getUpdateCommand");
8028
+
8029
+ // src/components/UpdateModal/UpdateModal.tsx
8030
+ var installer = getInstaller();
8031
+ var updateCommand = getUpdateCommand();
8032
+ var UpdateModal = /* @__PURE__ */ __name(({ latestVersion, releaseUrl, terminalHeight, terminalWidth }) => {
8033
+ const theme = useTheme();
8034
+ const { focusState, switchBack } = useFocusContext();
8035
+ const isActive = focusState.activeWindow === "updateModal";
8036
+ const mountedRef = useRef3(true);
8037
+ useEffect14(() => {
8038
+ return () => {
8039
+ mountedRef.current = false;
8040
+ };
8041
+ }, []);
8042
+ const [selectedIndex, setSelectedIndex] = useState14(0);
8043
+ const [pkgStatus, setPkgStatus] = useState14("idle");
8044
+ const [pkgOutput, setPkgOutput] = useState14([]);
8045
+ const handleClose = useCallback5(() => {
8046
+ switchBack();
8047
+ }, [
8048
+ switchBack
8049
+ ]);
8050
+ const handleConfirm = useCallback5(() => {
8051
+ if (IS_SEA) {
8052
+ const cmd = process.platform === "win32" ? `start "" "${releaseUrl}"` : process.platform === "darwin" ? `open "${releaseUrl}"` : `xdg-open "${releaseUrl}"`;
8053
+ exec2(cmd);
8054
+ switchBack();
8055
+ return;
8056
+ }
8057
+ if (pkgStatus === "done") {
8058
+ process.exit(0);
8059
+ }
8060
+ if (pkgStatus === "error") {
8061
+ switchBack();
8062
+ return;
8063
+ }
8064
+ if (pkgStatus === "running") return;
8065
+ if (selectedIndex === 1) {
8066
+ void open2(releaseUrl);
8067
+ switchBack();
8068
+ return;
8069
+ }
8070
+ setPkgStatus("running");
8071
+ const [bin, ...args] = updateCommand.split(" ");
8072
+ const proc = spawn3(bin, args, {
8073
+ stdio: "pipe",
8074
+ shell: true
8075
+ });
8076
+ const addLines = /* @__PURE__ */ __name((chunk) => {
8077
+ const lines = chunk.toString().split("\n").filter(Boolean);
8078
+ setPkgOutput((prev) => [
8079
+ ...prev,
8080
+ ...lines
8081
+ ].slice(-12));
8082
+ }, "addLines");
8083
+ proc.stdout?.on("data", addLines);
8084
+ proc.stderr?.on("data", addLines);
8085
+ proc.on("exit", (code) => {
8086
+ if (!mountedRef.current) return;
8087
+ setPkgStatus(code === 0 ? "done" : "error");
8088
+ });
8089
+ proc.on("error", (err) => {
8090
+ if (!mountedRef.current) return;
8091
+ setPkgOutput((prev) => [
8092
+ ...prev,
8093
+ err.message
8094
+ ]);
8095
+ setPkgStatus("error");
8096
+ });
8097
+ }, [
8098
+ pkgStatus,
8099
+ selectedIndex,
8100
+ releaseUrl,
8101
+ switchBack
8102
+ ]);
8103
+ useShortcuts({
8104
+ id: "updateModal",
8105
+ isActive,
8106
+ exclusive: true,
8107
+ priority: 300,
8108
+ shortcuts: [
8109
+ {
8110
+ id: "updateModal.close",
8111
+ defaultShortcut: {
8112
+ key: "escape"
8113
+ },
8114
+ label: "Dismiss",
8115
+ handler: handleClose
8116
+ },
8117
+ {
8118
+ id: "updateModal.confirm",
8119
+ defaultShortcut: {
8120
+ key: "return"
8121
+ },
8122
+ label: "Confirm",
8123
+ handler: handleConfirm
8124
+ },
8125
+ {
8126
+ id: "updateModal.up",
8127
+ defaultShortcut: {
8128
+ key: "upArrow"
8129
+ },
8130
+ label: "",
8131
+ handler: /* @__PURE__ */ __name(() => {
8132
+ if (!IS_SEA && pkgStatus === "idle") setSelectedIndex(0);
8133
+ }, "handler")
8134
+ },
8135
+ {
8136
+ id: "updateModal.down",
8137
+ defaultShortcut: {
8138
+ key: "downArrow"
8139
+ },
8140
+ label: "",
8141
+ handler: /* @__PURE__ */ __name(() => {
8142
+ if (!IS_SEA && pkgStatus === "idle") setSelectedIndex(1);
8143
+ }, "handler")
8144
+ }
8145
+ ]
8146
+ });
8147
+ if (!isActive) return null;
8148
+ const modalWidth = Math.min(62, terminalWidth - 8);
8149
+ const isIdle = IS_SEA || pkgStatus === "idle";
8150
+ const isInProgress = !IS_SEA && pkgStatus === "running";
8151
+ return /* @__PURE__ */ React28.createElement(Box14, {
8152
+ position: "absolute",
8153
+ width: "100%",
8154
+ height: terminalHeight,
8155
+ flexDirection: "column",
8156
+ justifyContent: "center",
8157
+ alignItems: "center"
8158
+ }, /* @__PURE__ */ React28.createElement(Box14, {
8159
+ flexDirection: "column",
8160
+ borderStyle: "round",
8161
+ borderColor: theme.status.success,
8162
+ paddingX: 2,
8163
+ paddingY: 1,
8164
+ width: modalWidth,
8165
+ backgroundColor: theme.ui.background
8166
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8167
+ bold: true,
8168
+ color: theme.status.success
8169
+ }, "Update Available"), /* @__PURE__ */ React28.createElement(Box14, {
8170
+ flexDirection: "column",
8171
+ marginTop: 1
8172
+ }, /* @__PURE__ */ React28.createElement(Box14, {
8173
+ flexDirection: "row"
8174
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8175
+ color: theme.text.muted
8176
+ }, "Current version "), /* @__PURE__ */ React28.createElement(Text23, null, APP_VERSION)), /* @__PURE__ */ React28.createElement(Box14, {
8177
+ flexDirection: "row"
8178
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8179
+ color: theme.text.muted
8180
+ }, "New version "), /* @__PURE__ */ React28.createElement(Text23, {
8181
+ bold: true,
8182
+ color: theme.status.success
8183
+ }, latestVersion))), isIdle && /* @__PURE__ */ React28.createElement(Box14, {
8184
+ flexDirection: "column",
8185
+ marginTop: 1
8186
+ }, IS_SEA ? /* @__PURE__ */ React28.createElement(Box14, {
8187
+ flexDirection: "row",
8188
+ flexShrink: 0
8189
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8190
+ color: theme.ui.focusIndicator
8191
+ }, "\u261B Open release page")) : /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(Box14, {
8192
+ flexDirection: "row",
8193
+ flexShrink: 0
8194
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8195
+ color: selectedIndex === 0 ? theme.ui.focusIndicator : theme.text.primary
8196
+ }, selectedIndex === 0 ? "\u261B " : " ", "Update with "), /* @__PURE__ */ React28.createElement(Text23, {
8197
+ color: selectedIndex === 0 ? theme.ui.focusIndicator : theme.action.primary,
8198
+ bold: true
8199
+ }, updateCommand)), /* @__PURE__ */ React28.createElement(Box14, {
8200
+ flexDirection: "row",
8201
+ flexShrink: 0
8202
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8203
+ color: selectedIndex === 1 ? theme.ui.focusIndicator : theme.text.primary
8204
+ }, selectedIndex === 1 ? "\u261B " : " ", "Open release page")))), !IS_SEA && pkgStatus === "running" && /* @__PURE__ */ React28.createElement(Box14, {
8205
+ flexDirection: "column",
8206
+ marginTop: 1
8207
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8208
+ color: theme.ui.focusIndicator
8209
+ }, "Updating via ", installer, "..."), pkgOutput.map((line, i) => /* @__PURE__ */ React28.createElement(Text23, {
8210
+ key: i,
8211
+ dimColor: true,
8212
+ wrap: "truncate"
8213
+ }, line))), !IS_SEA && pkgStatus === "done" && /* @__PURE__ */ React28.createElement(Box14, {
8214
+ flexDirection: "column",
8215
+ marginTop: 1
8216
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8217
+ color: theme.status.success
8218
+ }, "Updated successfully."), /* @__PURE__ */ React28.createElement(Text23, {
8219
+ dimColor: true
8220
+ }, "Restart to use v", latestVersion, ".")), !IS_SEA && pkgStatus === "error" && /* @__PURE__ */ React28.createElement(Box14, {
8221
+ flexDirection: "column",
8222
+ marginTop: 1
8223
+ }, /* @__PURE__ */ React28.createElement(Text23, {
8224
+ color: theme.status.error
8225
+ }, "Update failed."), pkgOutput.slice(-3).map((line, i) => /* @__PURE__ */ React28.createElement(Text23, {
8226
+ key: i,
8227
+ color: theme.text.muted,
8228
+ wrap: "truncate"
8229
+ }, line))), /* @__PURE__ */ React28.createElement(Box14, {
8230
+ marginTop: 1,
8231
+ flexDirection: "row"
8232
+ }, isIdle && /* @__PURE__ */ React28.createElement(React28.Fragment, null, !IS_SEA && /* @__PURE__ */ React28.createElement(Hint, {
8233
+ label: "Select",
8234
+ shortcut: "\u2191\u2193"
8235
+ }), /* @__PURE__ */ React28.createElement(Hint, {
8236
+ label: "Confirm",
8237
+ shortcut: "Enter"
8238
+ }), /* @__PURE__ */ React28.createElement(Hint, {
8239
+ label: "Dismiss",
8240
+ shortcut: "Esc"
8241
+ })), isInProgress && /* @__PURE__ */ React28.createElement(Text23, {
8242
+ dimColor: true
8243
+ }, "Please wait..."), !isIdle && !isInProgress && /* @__PURE__ */ React28.createElement(React28.Fragment, null, pkgStatus === "done" && /* @__PURE__ */ React28.createElement(Hint, {
8244
+ label: "Restart",
8245
+ shortcut: "Enter"
8246
+ }), /* @__PURE__ */ React28.createElement(Hint, {
8247
+ label: "Close",
8248
+ shortcut: "Esc"
8249
+ })))));
8250
+ }, "UpdateModal");
8251
+
8009
8252
  // src/components/Toolbar/Toolbar.tsx
8010
- import React31 from "react";
8011
- import { Box as Box17, Text as Text26 } from "ink";
8253
+ import React32 from "react";
8254
+ import { Box as Box18, Text as Text27 } from "ink";
8012
8255
 
8013
- // node_modules/ansi-regex/index.js
8256
+ // node_modules/string-width/node_modules/ansi-regex/index.js
8014
8257
  function ansiRegex({ onlyFirst = false } = {}) {
8015
8258
  const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
8016
8259
  const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
@@ -8020,7 +8263,7 @@ function ansiRegex({ onlyFirst = false } = {}) {
8020
8263
  }
8021
8264
  __name(ansiRegex, "ansiRegex");
8022
8265
 
8023
- // node_modules/strip-ansi/index.js
8266
+ // node_modules/string-width/node_modules/strip-ansi/index.js
8024
8267
  var regex = ansiRegex();
8025
8268
  function stripAnsi(string) {
8026
8269
  if (typeof string !== "string") {
@@ -8872,8 +9115,8 @@ function stringWidth(input, options = {}) {
8872
9115
  __name(stringWidth, "stringWidth");
8873
9116
 
8874
9117
  // src/components/Toolbar/ToolbarButtonInvoker.tsx
8875
- import { Box as Box14, Text as Text23 } from "ink";
8876
- import React28, { useLayoutEffect } from "react";
9118
+ import { Box as Box15, Text as Text24 } from "ink";
9119
+ import React29, { useLayoutEffect } from "react";
8877
9120
  var ToolbarButtonInvoker = /* @__PURE__ */ __name(({ hook, isSelected, index, flow, orchestrator }) => {
8878
9121
  const actionsRef = useToolbarActionsRef();
8879
9122
  const { enabled, label, icon, color, bold, italic, onPress } = hook({
@@ -8885,12 +9128,12 @@ var ToolbarButtonInvoker = /* @__PURE__ */ __name(({ hook, isSelected, index, fl
8885
9128
  actionsRef.current[index] = onPress;
8886
9129
  });
8887
9130
  if (!enabled) return null;
8888
- return /* @__PURE__ */ React28.createElement(Box14, {
9131
+ return /* @__PURE__ */ React29.createElement(Box15, {
8889
9132
  key: index,
8890
9133
  paddingX: 1,
8891
9134
  backgroundColor: isSelected ? color : void 0,
8892
9135
  overflow: "hidden"
8893
- }, /* @__PURE__ */ React28.createElement(Text23, {
9136
+ }, /* @__PURE__ */ React29.createElement(Text24, {
8894
9137
  color: isSelected ? "white" : color,
8895
9138
  bold,
8896
9139
  italic,
@@ -8898,19 +9141,6 @@ var ToolbarButtonInvoker = /* @__PURE__ */ __name(({ hook, isSelected, index, fl
8898
9141
  }, `${icon} ${label}`));
8899
9142
  }, "ToolbarButtonInvoker");
8900
9143
 
8901
- // src/components/Toolbar/FlowSelector.tsx
8902
- import React29 from "react";
8903
- import { Box as Box15, Text as Text24 } from "ink";
8904
- var FlowSelector = /* @__PURE__ */ __name(({ flows, currentFlow }) => {
8905
- return /* @__PURE__ */ React29.createElement(Box15, null, flows.map((flow) => /* @__PURE__ */ React29.createElement(Box15, {
8906
- key: flow.id,
8907
- marginRight: 2
8908
- }, /* @__PURE__ */ React29.createElement(Text24, {
8909
- color: flow.id === currentFlow.id ? "green" : "gray",
8910
- bold: flow.id === currentFlow.id
8911
- }, flow.displayName))));
8912
- }, "FlowSelector");
8913
-
8914
9144
  // src/components/TabBar.tsx
8915
9145
  import React30 from "react";
8916
9146
  import { Box as Box16, Text as Text25 } from "ink";
@@ -8946,8 +9176,28 @@ var TabBar = /* @__PURE__ */ __name(({ width, tabs, activeTabKey, splitPos }) =>
8946
9176
  }, " \u2500\u2524"));
8947
9177
  }, "TabBar");
8948
9178
 
9179
+ // src/components/Toolbar/UpdateBadge.tsx
9180
+ import React31, { useLayoutEffect as useLayoutEffect2 } from "react";
9181
+ import { Box as Box17, Text as Text26 } from "ink";
9182
+ var UpdateBadge = /* @__PURE__ */ __name(({ version, isSelected, index }) => {
9183
+ const theme = useTheme();
9184
+ const { openUpdateModal } = useFocusContext();
9185
+ const actionsRef = useToolbarActionsRef();
9186
+ useLayoutEffect2(() => {
9187
+ actionsRef.current[index] = openUpdateModal;
9188
+ });
9189
+ return /* @__PURE__ */ React31.createElement(Box17, {
9190
+ paddingX: 1,
9191
+ backgroundColor: isSelected ? theme.status.success : void 0,
9192
+ flexShrink: 0
9193
+ }, /* @__PURE__ */ React31.createElement(Text26, {
9194
+ color: isSelected ? "black" : theme.status.success,
9195
+ bold: true
9196
+ }, "Update available v", version));
9197
+ }, "UpdateBadge");
9198
+
8949
9199
  // src/components/Toolbar/Toolbar.tsx
8950
- var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flow, orchestrator }) => {
9200
+ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flow, orchestrator, updateInfo }) => {
8951
9201
  const theme = useTheme();
8952
9202
  const { focusState } = useFocusContext();
8953
9203
  const isActive = focusState.activeWindow === "toolbar";
@@ -8957,11 +9207,11 @@ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flo
8957
9207
  const splitPositions = [
8958
9208
  nameWidth + 3
8959
9209
  ];
8960
- return /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(Separator, {
9210
+ return /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(Separator, {
8961
9211
  width,
8962
9212
  type: "top",
8963
9213
  splitPositions
8964
- }), /* @__PURE__ */ React31.createElement(Box17, {
9214
+ }), /* @__PURE__ */ React32.createElement(Box18, {
8965
9215
  borderStyle: "single",
8966
9216
  borderColor: theme.ui.border,
8967
9217
  borderBackgroundColor: theme.ui.background,
@@ -8971,7 +9221,7 @@ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flo
8971
9221
  overflow: "hidden",
8972
9222
  display: "flex",
8973
9223
  height
8974
- }, /* @__PURE__ */ React31.createElement(Box17, {
9224
+ }, /* @__PURE__ */ React32.createElement(Box18, {
8975
9225
  borderStyle: "single",
8976
9226
  borderColor: theme.ui.border,
8977
9227
  borderBackgroundColor: theme.ui.background,
@@ -8983,12 +9233,12 @@ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flo
8983
9233
  minWidth: nameWidth + 2,
8984
9234
  height,
8985
9235
  overflow: "hidden"
8986
- }, /* @__PURE__ */ React31.createElement(Text26, {
9236
+ }, /* @__PURE__ */ React32.createElement(Text27, {
8987
9237
  color: theme.action.primary,
8988
9238
  bold: true
8989
9239
  }, name)), buttons.map((hook, index) => {
8990
9240
  const isSelected = isActive && focusState.toolbar.selectedButtonIndex === index;
8991
- return /* @__PURE__ */ React31.createElement(ToolbarButtonInvoker, {
9241
+ return /* @__PURE__ */ React32.createElement(ToolbarButtonInvoker, {
8992
9242
  key: index,
8993
9243
  hook,
8994
9244
  isSelected,
@@ -8996,18 +9246,20 @@ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flo
8996
9246
  flow,
8997
9247
  orchestrator
8998
9248
  });
8999
- }), /* @__PURE__ */ React31.createElement(Box17, {
9249
+ }), /* @__PURE__ */ React32.createElement(Box18, {
9000
9250
  flexDirection: "row",
9001
9251
  display: "flex",
9002
9252
  flexGrow: 1,
9003
9253
  justifyContent: "flex-end",
9004
9254
  gap: 1,
9005
9255
  height
9006
- }, /* @__PURE__ */ React31.createElement(FlowSelector, {
9007
- flows,
9008
- currentFlow: flow,
9009
- onFlowChange
9010
- }))), /* @__PURE__ */ React31.createElement(PrimaryModeTabBar, {
9256
+ }, updateInfo ? /* @__PURE__ */ React32.createElement(UpdateBadge, {
9257
+ version: updateInfo.latestVersion,
9258
+ isSelected: isActive && focusState.toolbar.selectedButtonIndex === buttons.length,
9259
+ index: buttons.length
9260
+ }) : /* @__PURE__ */ React32.createElement(Text27, {
9261
+ dimColor: true
9262
+ }, "v", APP_VERSION))), /* @__PURE__ */ React32.createElement(PrimaryModeTabBar, {
9011
9263
  width,
9012
9264
  splitPos: splitPositions[0] ?? 0
9013
9265
  }));
@@ -9015,7 +9267,7 @@ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flo
9015
9267
  var PrimaryModeTabBar = /* @__PURE__ */ __name(({ width, splitPos }) => {
9016
9268
  const { focusState } = useFocusContext();
9017
9269
  const { primaryMode } = focusState.secondaryPanel;
9018
- return /* @__PURE__ */ React31.createElement(TabBar, {
9270
+ return /* @__PURE__ */ React32.createElement(TabBar, {
9019
9271
  width,
9020
9272
  tabs: [
9021
9273
  {
@@ -9033,12 +9285,12 @@ var PrimaryModeTabBar = /* @__PURE__ */ __name(({ width, splitPos }) => {
9033
9285
  }, "PrimaryModeTabBar");
9034
9286
 
9035
9287
  // src/components/TaskListPanel/TaskListPanel.tsx
9036
- import React35, { useMemo as useMemo4 } from "react";
9037
- import { Box as Box21, Text as Text30 } from "ink";
9288
+ import React36, { useMemo as useMemo4 } from "react";
9289
+ import { Box as Box22, Text as Text31 } from "ink";
9038
9290
 
9039
9291
  // src/components/TaskListPanel/ActionBar.tsx
9040
- import React32, { useEffect as useEffect14, useState as useState14 } from "react";
9041
- import { Box as Box18, Text as Text27 } from "ink";
9292
+ import React33, { useEffect as useEffect15, useState as useState15 } from "react";
9293
+ import { Box as Box19, Text as Text28 } from "ink";
9042
9294
  var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9043
9295
  const theme = useTheme();
9044
9296
  const { focusState } = useFocusContext();
@@ -9046,8 +9298,8 @@ var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9046
9298
  const selectedIndex = focusState.taskList.selectedTaskIndex;
9047
9299
  const multiCount = focusState.taskList.selectedTaskIds.size;
9048
9300
  const selectedTask = tasks[selectedIndex];
9049
- const [, setTaskVersion] = useState14(0);
9050
- useEffect14(() => {
9301
+ const [, setTaskVersion] = useState15(0);
9302
+ useEffect15(() => {
9051
9303
  if (!selectedTask) return;
9052
9304
  return selectedTask.subscribe(() => setTaskVersion((v) => v + 1));
9053
9305
  }, [
@@ -9065,7 +9317,7 @@ var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9065
9317
  return true;
9066
9318
  }), "filterRow");
9067
9319
  if (!isTaskListActive) return null;
9068
- return /* @__PURE__ */ React32.createElement(Box18, {
9320
+ return /* @__PURE__ */ React33.createElement(Box19, {
9069
9321
  paddingX: 1,
9070
9322
  height: rowCount,
9071
9323
  overflow: "hidden",
@@ -9074,56 +9326,56 @@ var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9074
9326
  alignItems: "flex-start"
9075
9327
  }, bar ? bar.rows.map((row, i) => {
9076
9328
  const visible = filterRow(row);
9077
- return /* @__PURE__ */ React32.createElement(Box18, {
9329
+ return /* @__PURE__ */ React33.createElement(Box19, {
9078
9330
  key: i,
9079
9331
  height: 1,
9080
9332
  flexDirection: "row",
9081
9333
  alignItems: "flex-start",
9082
9334
  flexShrink: 0
9083
- }, row.text && /* @__PURE__ */ React32.createElement(Box18, {
9335
+ }, row.text && /* @__PURE__ */ React33.createElement(Box19, {
9084
9336
  marginRight: 1,
9085
9337
  flexShrink: 0
9086
- }, /* @__PURE__ */ React32.createElement(Text27, {
9338
+ }, /* @__PURE__ */ React33.createElement(Text28, {
9087
9339
  color: row.textColor ?? theme.text.primary,
9088
9340
  bold: true
9089
- }, row.text)), /* @__PURE__ */ React32.createElement(Box18, {
9341
+ }, row.text)), /* @__PURE__ */ React33.createElement(Box19, {
9090
9342
  marginRight: 1,
9091
9343
  flexShrink: 0
9092
- }, /* @__PURE__ */ React32.createElement(Text27, {
9344
+ }, /* @__PURE__ */ React33.createElement(Text28, {
9093
9345
  color: theme.text.hint
9094
- }, "\u203A")), visible.length > 0 ? visible.map((action, j) => /* @__PURE__ */ React32.createElement(Hint, {
9346
+ }, "\u203A")), visible.length > 0 ? visible.map((action, j) => /* @__PURE__ */ React33.createElement(Hint, {
9095
9347
  key: j,
9096
9348
  label: action.label,
9097
9349
  shortcut: getShortcutLiteral(action.shortcuts)
9098
- })) : /* @__PURE__ */ React32.createElement(Text27, {
9350
+ })) : /* @__PURE__ */ React33.createElement(Text28, {
9099
9351
  color: theme.text.hint,
9100
9352
  italic: true
9101
9353
  }, "\u2014"));
9102
- }) : /* @__PURE__ */ React32.createElement(Box18, {
9354
+ }) : /* @__PURE__ */ React33.createElement(Box19, {
9103
9355
  height: 1,
9104
9356
  flexDirection: "row",
9105
9357
  alignItems: "flex-start"
9106
- }, /* @__PURE__ */ React32.createElement(Text27, {
9358
+ }, /* @__PURE__ */ React33.createElement(Text28, {
9107
9359
  color: theme.text.hint,
9108
9360
  italic: true
9109
9361
  }, "No contextual actions available")));
9110
9362
  }, "ActionBar");
9111
9363
 
9112
9364
  // src/components/DynamicHintBar/DynamicHintBar.tsx
9113
- import React33, { useReducer as useReducer2, useEffect as useEffect15 } from "react";
9114
- import { Box as Box19, Text as Text28 } from "ink";
9365
+ import React34, { useReducer as useReducer2, useEffect as useEffect16 } from "react";
9366
+ import { Box as Box20, Text as Text29 } from "ink";
9115
9367
  var HintChip = /* @__PURE__ */ __name(({ entry, dim }) => {
9116
9368
  const theme = useTheme();
9117
- return /* @__PURE__ */ React33.createElement(Box19, {
9369
+ return /* @__PURE__ */ React34.createElement(Box20, {
9118
9370
  marginRight: 1,
9119
9371
  flexShrink: 0
9120
- }, /* @__PURE__ */ React33.createElement(Text28, {
9372
+ }, /* @__PURE__ */ React34.createElement(Text29, {
9121
9373
  color: theme.text.active,
9122
9374
  dimColor: dim,
9123
9375
  bold: true
9124
9376
  }, "[", getShortcutLiteral([
9125
9377
  entry.shortcut
9126
- ]), "]"), /* @__PURE__ */ React33.createElement(Text28, {
9378
+ ]), "]"), /* @__PURE__ */ React34.createElement(Text29, {
9127
9379
  color: theme.text.hint,
9128
9380
  dimColor: dim
9129
9381
  }, " ", entry.label));
@@ -9131,25 +9383,25 @@ var HintChip = /* @__PURE__ */ __name(({ entry, dim }) => {
9131
9383
  var HintLine = /* @__PURE__ */ __name(({ line, contextShortcuts, dim, width }) => {
9132
9384
  const theme = useTheme();
9133
9385
  const matchedShortcuts = line.shortcutIds.map((sid) => contextShortcuts.find((s) => s.id === sid)).filter((s) => s !== void 0);
9134
- return /* @__PURE__ */ React33.createElement(Box19, {
9386
+ return /* @__PURE__ */ React34.createElement(Box20, {
9135
9387
  flexDirection: "row",
9136
9388
  width,
9137
9389
  overflow: "hidden",
9138
9390
  flexShrink: 0
9139
- }, /* @__PURE__ */ React33.createElement(Box19, {
9391
+ }, /* @__PURE__ */ React34.createElement(Box20, {
9140
9392
  marginRight: 1,
9141
9393
  flexShrink: 0
9142
- }, line.left.type === "node" ? line.left.renderNode(dim ?? false) : /* @__PURE__ */ React33.createElement(Text28, {
9394
+ }, line.left.type === "node" ? line.left.renderNode(dim ?? false) : /* @__PURE__ */ React34.createElement(Text29, {
9143
9395
  color: line.left.color ?? theme.text.active,
9144
9396
  dimColor: dim,
9145
9397
  bold: line.left.bold ?? true
9146
- }, line.left.value)), /* @__PURE__ */ React33.createElement(Box19, {
9398
+ }, line.left.value)), /* @__PURE__ */ React34.createElement(Box20, {
9147
9399
  marginRight: 1,
9148
9400
  flexShrink: 0
9149
- }, /* @__PURE__ */ React33.createElement(Text28, {
9401
+ }, /* @__PURE__ */ React34.createElement(Text29, {
9150
9402
  color: theme.text.active,
9151
9403
  dimColor: dim
9152
- }, "\u203A")), matchedShortcuts.map((entry) => /* @__PURE__ */ React33.createElement(HintChip, {
9404
+ }, "\u203A")), matchedShortcuts.map((entry) => /* @__PURE__ */ React34.createElement(HintChip, {
9153
9405
  key: entry.id,
9154
9406
  entry,
9155
9407
  dim
@@ -9157,13 +9409,13 @@ var HintLine = /* @__PURE__ */ __name(({ line, contextShortcuts, dim, width }) =
9157
9409
  }, "HintLine");
9158
9410
  var DynamicHintBar = /* @__PURE__ */ __name(({ width, isActive = true }) => {
9159
9411
  const [, forceUpdate] = useReducer2((x) => x + 1, 0);
9160
- useEffect15(() => {
9412
+ useEffect16(() => {
9161
9413
  return shortcutRegistry.subscribe(forceUpdate);
9162
9414
  }, []);
9163
9415
  if (!isActive) return null;
9164
9416
  const contexts = shortcutRegistry.getActiveHintContexts();
9165
9417
  if (contexts.length === 0) return null;
9166
- return /* @__PURE__ */ React33.createElement(Box19, {
9418
+ return /* @__PURE__ */ React34.createElement(Box20, {
9167
9419
  flexDirection: "column",
9168
9420
  width,
9169
9421
  overflow: "hidden",
@@ -9171,7 +9423,7 @@ var DynamicHintBar = /* @__PURE__ */ __name(({ width, isActive = true }) => {
9171
9423
  alignItems: "flex-start",
9172
9424
  justifyContent: "flex-start",
9173
9425
  flexShrink: 0
9174
- }, contexts.map((ctx) => ctx.lines.map((line) => /* @__PURE__ */ React33.createElement(HintLine, {
9426
+ }, contexts.map((ctx) => ctx.lines.map((line) => /* @__PURE__ */ React34.createElement(HintLine, {
9175
9427
  key: `${ctx.contextId}-${line.id}`,
9176
9428
  line,
9177
9429
  contextShortcuts: ctx.shortcuts,
@@ -9181,14 +9433,14 @@ var DynamicHintBar = /* @__PURE__ */ __name(({ width, isActive = true }) => {
9181
9433
  }, "DynamicHintBar");
9182
9434
 
9183
9435
  // src/components/TaskListPanel/TaskRow.tsx
9184
- import React34 from "react";
9185
- import { Box as Box20, Text as Text29 } from "ink";
9436
+ import React35 from "react";
9437
+ import { Box as Box21, Text as Text30 } from "ink";
9186
9438
 
9187
9439
  // src/hooks/useTask.ts
9188
- import { useEffect as useEffect16, useState as useState15 } from "react";
9440
+ import { useEffect as useEffect17, useState as useState16 } from "react";
9189
9441
  var useTask = /* @__PURE__ */ __name((task) => {
9190
- const [taskSnapshot, setTaskSnapshot] = useState15(() => task.get());
9191
- useEffect16(() => {
9442
+ const [taskSnapshot, setTaskSnapshot] = useState16(() => task.get());
9443
+ useEffect17(() => {
9192
9444
  const unsubscribe = task.subscribe((updatedTask) => {
9193
9445
  setTaskSnapshot(updatedTask.get());
9194
9446
  });
@@ -9200,26 +9452,26 @@ var useTask = /* @__PURE__ */ __name((task) => {
9200
9452
  }, "useTask");
9201
9453
 
9202
9454
  // src/components/TaskListPanel/TaskRow.tsx
9203
- var TaskRow = /* @__PURE__ */ React34.memo(/* @__PURE__ */ __name(function TaskRow2({ taskReference, isActive, isHighlighted, isMultiSelected, selectedColumnIndex, columns, flow }) {
9455
+ var TaskRow = /* @__PURE__ */ React35.memo(/* @__PURE__ */ __name(function TaskRow2({ taskReference, isActive, isHighlighted, isMultiSelected, selectedColumnIndex, columns, flow }) {
9204
9456
  const theme = useTheme();
9205
9457
  const task = useTask(taskReference);
9206
9458
  const isActiveIndicator = isActive ? "\u261B" : " ";
9207
9459
  const indicator = isActiveIndicator + (isMultiSelected ? "\u2713" : " ");
9208
9460
  const backgroundColor = isActive ? theme.ui.rowActiveDimmedBackground : isHighlighted ? theme.ui.rowBackground : void 0;
9209
- return /* @__PURE__ */ React34.createElement(Box20, {
9461
+ return /* @__PURE__ */ React35.createElement(Box21, {
9210
9462
  key: task.id,
9211
9463
  paddingX: 1,
9212
9464
  overflowY: "hidden",
9213
9465
  backgroundColor
9214
- }, /* @__PURE__ */ React34.createElement(Box20, {
9466
+ }, /* @__PURE__ */ React35.createElement(Box21, {
9215
9467
  width: 2,
9216
9468
  flexShrink: 0
9217
- }, /* @__PURE__ */ React34.createElement(Text29, {
9469
+ }, /* @__PURE__ */ React35.createElement(Text30, {
9218
9470
  color: isActive ? "white" : isMultiSelected ? "cyan" : "white"
9219
9471
  }, indicator)), columns.map((column, index) => {
9220
9472
  const CellComponent = column.component;
9221
9473
  const isCellActive = isActive && selectedColumnIndex === index;
9222
- return /* @__PURE__ */ React34.createElement(Box20, {
9474
+ return /* @__PURE__ */ React35.createElement(Box21, {
9223
9475
  key: `${CellComponent.name}-${column.label}-${index}`,
9224
9476
  width: column.width,
9225
9477
  minWidth: column.width,
@@ -9230,7 +9482,7 @@ var TaskRow = /* @__PURE__ */ React34.memo(/* @__PURE__ */ __name(function TaskR
9230
9482
  paddingX: 1,
9231
9483
  backgroundColor: isCellActive ? theme.ui.rowActiveBackground : void 0,
9232
9484
  overflow: "hidden"
9233
- }, /* @__PURE__ */ React34.createElement(CellComponent, {
9485
+ }, /* @__PURE__ */ React35.createElement(CellComponent, {
9234
9486
  task,
9235
9487
  taskReference,
9236
9488
  width: column.width,
@@ -9661,12 +9913,6 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9661
9913
  newAdjacentWidths[c.id] = c.width + shares[i];
9662
9914
  });
9663
9915
  }
9664
- const deltaLog = calculatedColumns.filter((_, i) => columns[i]?.resizable !== false).map((c) => {
9665
- const newW = c.id === col.id ? newSelectedWidth : newAdjacentWidths[c.id] ?? c.width;
9666
- const delta = newW - c.width;
9667
- return `${c.id}:${delta > 0 ? "+" : ""}${delta}`;
9668
- }).join(" | ");
9669
- globalLogger.info(`resize ${direction}: ${deltaLog}`);
9670
9916
  const fixedWidth = calculatedColumns.filter((_, i) => columns[i]?.resizable === false).reduce((s, c) => s + c.width, 0);
9671
9917
  const resizableWidth = Math.max(1, availableWidth - fixedWidth);
9672
9918
  const allRatios = {};
@@ -9677,7 +9923,7 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9677
9923
  });
9678
9924
  flow.setColumnRatios(allRatios);
9679
9925
  });
9680
- return /* @__PURE__ */ React35.createElement(Box21, {
9926
+ return /* @__PURE__ */ React36.createElement(Box22, {
9681
9927
  borderStyle: "single",
9682
9928
  borderColor: theme.ui.border,
9683
9929
  borderBackgroundColor: theme.ui.background,
@@ -9687,19 +9933,19 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9687
9933
  borderBottom: false,
9688
9934
  height: fullHeight,
9689
9935
  flexGrow: 1
9690
- }, /* @__PURE__ */ React35.createElement(Box21, {
9936
+ }, /* @__PURE__ */ React36.createElement(Box22, {
9691
9937
  flexDirection: "row",
9692
9938
  paddingX: 1,
9693
9939
  height: 1,
9694
9940
  overflow: "hidden",
9695
9941
  flexShrink: 0
9696
- }, /* @__PURE__ */ React35.createElement(Box21, {
9942
+ }, /* @__PURE__ */ React36.createElement(Box22, {
9697
9943
  width: 2,
9698
9944
  height: 1,
9699
9945
  flexShrink: 0
9700
9946
  }), calculatedColumns.map((column, index) => {
9701
9947
  const isActive = isWindowActive && focusState.taskList.isHeaderFocused && focusState.taskList.selectedColumnIndex === index;
9702
- return /* @__PURE__ */ React35.createElement(Box21, {
9948
+ return /* @__PURE__ */ React36.createElement(Box22, {
9703
9949
  key: `header-${column.label}-${index}`,
9704
9950
  width: column.width,
9705
9951
  minWidth: column.width,
@@ -9709,19 +9955,19 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9709
9955
  overflow: "hidden",
9710
9956
  flexShrink: 0,
9711
9957
  backgroundColor: isActive ? theme.ui.rowActiveBackground : void 0
9712
- }, /* @__PURE__ */ React35.createElement(Text30, {
9958
+ }, /* @__PURE__ */ React36.createElement(Text31, {
9713
9959
  bold: true,
9714
9960
  color: isActive ? "white" : column.color || "cyan"
9715
9961
  }, column.label));
9716
- })), /* @__PURE__ */ React35.createElement(Box21, {
9962
+ })), /* @__PURE__ */ React36.createElement(Box22, {
9717
9963
  flexDirection: "column",
9718
9964
  height,
9719
9965
  overflow: "hidden",
9720
9966
  flexGrow: 1
9721
- }, tasks.length === 0 ? /* @__PURE__ */ React35.createElement(Box21, {
9967
+ }, tasks.length === 0 ? /* @__PURE__ */ React36.createElement(Box22, {
9722
9968
  paddingX: 4,
9723
9969
  overflow: "hidden"
9724
- }, /* @__PURE__ */ React35.createElement(Text30, {
9970
+ }, /* @__PURE__ */ React36.createElement(Text31, {
9725
9971
  italic: true,
9726
9972
  color: "gray"
9727
9973
  }, "Press Ctrl+V to import music URLs")) : tasks.slice(offset, offset + height).map((task, index) => {
@@ -9730,7 +9976,7 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9730
9976
  const isRowActive = isWindowActive && !focusState.taskList.isHeaderFocused && selectedIndex === visibleIndex;
9731
9977
  const selectedColumnIndex = isRowActive ? focusState.taskList.selectedColumnIndex : -1;
9732
9978
  const isMultiSelected = focusState.taskList.selectedTaskIds.has(task.getId());
9733
- return /* @__PURE__ */ React35.createElement(TaskRow, {
9979
+ return /* @__PURE__ */ React36.createElement(TaskRow, {
9734
9980
  key: task.getId(),
9735
9981
  taskReference: task,
9736
9982
  isHighlighted: isRowHighlighted,
@@ -9740,22 +9986,22 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9740
9986
  columns: calculatedColumns,
9741
9987
  flow
9742
9988
  });
9743
- })), isWindowActive && focusState.taskList.isHeaderFocused ? /* @__PURE__ */ React35.createElement(DynamicHintBar, {
9989
+ })), isWindowActive && focusState.taskList.isHeaderFocused ? /* @__PURE__ */ React36.createElement(DynamicHintBar, {
9744
9990
  width: width - 2,
9745
9991
  isActive: true
9746
- }) : /* @__PURE__ */ React35.createElement(ActionBar, {
9992
+ }) : /* @__PURE__ */ React36.createElement(ActionBar, {
9747
9993
  tasks,
9748
9994
  flow
9749
9995
  }));
9750
9996
  }, "TaskListPanel");
9751
9997
 
9752
9998
  // src/components/SecondaryPanel/SecondaryPanel.tsx
9753
- import React57 from "react";
9754
- import { Box as Box41 } from "ink";
9999
+ import React58 from "react";
10000
+ import { Box as Box42 } from "ink";
9755
10001
 
9756
10002
  // src/components/SecondaryPanel/LogPanel.tsx
9757
- import React36, { useEffect as useEffect17, useState as useState16 } from "react";
9758
- import { Box as Box22, Text as Text31 } from "ink";
10003
+ import React37, { useEffect as useEffect18, useState as useState17 } from "react";
10004
+ import { Box as Box23, Text as Text32 } from "ink";
9759
10005
  import { inspect } from "util";
9760
10006
  function formatDetails(details) {
9761
10007
  if (!details || Object.keys(details).length === 0) {
@@ -9785,9 +10031,9 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9785
10031
  const { focusState } = useFocusContext();
9786
10032
  const height = heightProp ?? focusState.layout.secondaryPanelHeight;
9787
10033
  const selectedTask = focusState.activeWindow === "taskList" ? tasks?.[focusState.taskList.selectedTaskIndex] : null;
9788
- const [logs, setLogs] = useState16([]);
9789
- const [scrollOffset, setScrollOffset] = useState16(0);
9790
- useEffect17(() => {
10034
+ const [logs, setLogs] = useState17([]);
10035
+ const [scrollOffset, setScrollOffset] = useState17(0);
10036
+ useEffect18(() => {
9791
10037
  const unsubscribe = inkTransport.subscribe((incomingLogs) => {
9792
10038
  const normalized = incomingLogs;
9793
10039
  setLogs((prevLogs) => [
@@ -9798,8 +10044,8 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9798
10044
  return unsubscribe;
9799
10045
  }, []);
9800
10046
  const isActive = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab === "logs";
9801
- const [prevSelectedTask, setPrevSelectedTask] = useState16(selectedTask);
9802
- const [prevIsActive, setPrevIsActive] = useState16(isActive);
10047
+ const [prevSelectedTask, setPrevSelectedTask] = useState17(selectedTask);
10048
+ const [prevIsActive, setPrevIsActive] = useState17(isActive);
9803
10049
  if (prevSelectedTask !== selectedTask) {
9804
10050
  setPrevSelectedTask(selectedTask);
9805
10051
  setScrollOffset(0);
@@ -9856,7 +10102,7 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9856
10102
  const logRows = logRowsNoTop - (showTopIndicator ? 1 : 0);
9857
10103
  const visibleStart = Math.max(0, (visibleEnd ?? filteredLogs.length) - logRows);
9858
10104
  const visibleLogs = filteredLogs.slice(visibleStart, visibleEnd);
9859
- return /* @__PURE__ */ React36.createElement(Box22, {
10105
+ return /* @__PURE__ */ React37.createElement(Box23, {
9860
10106
  flexDirection: "row",
9861
10107
  borderStyle: "single",
9862
10108
  borderColor: theme.ui.border,
@@ -9866,7 +10112,7 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9866
10112
  height,
9867
10113
  flexGrow: 1,
9868
10114
  overflow: "hidden"
9869
- }, /* @__PURE__ */ React36.createElement(Box22, {
10115
+ }, /* @__PURE__ */ React37.createElement(Box23, {
9870
10116
  flexDirection: "column",
9871
10117
  alignSelf: "flex-end",
9872
10118
  alignContent: "flex-end",
@@ -9874,33 +10120,33 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9874
10120
  overflow: "hidden",
9875
10121
  flexGrow: 1,
9876
10122
  flexShrink: 0
9877
- }, showTopIndicator && /* @__PURE__ */ React36.createElement(Box22, {
10123
+ }, showTopIndicator && /* @__PURE__ */ React37.createElement(Box23, {
9878
10124
  paddingX: 1,
9879
10125
  flexShrink: 0
9880
- }, /* @__PURE__ */ React36.createElement(Text31, {
10126
+ }, /* @__PURE__ */ React37.createElement(Text32, {
9881
10127
  color: theme.ui.border,
9882
10128
  dimColor: true
9883
- }, "\u2191 ", visibleStart, " more above")), visibleLogs.map((log) => /* @__PURE__ */ React36.createElement(Box22, {
10129
+ }, "\u2191 ", visibleStart, " more above")), visibleLogs.map((log) => /* @__PURE__ */ React37.createElement(Box23, {
9884
10130
  key: log.id,
9885
10131
  paddingX: 1,
9886
10132
  height: 1,
9887
10133
  overflow: "hidden",
9888
10134
  flexGrow: 1,
9889
10135
  flexShrink: 0
9890
- }, /* @__PURE__ */ React36.createElement(Text31, {
10136
+ }, /* @__PURE__ */ React37.createElement(Text32, {
9891
10137
  wrap: "truncate-end"
9892
- }, getLogString(log)))), showBottomIndicator && /* @__PURE__ */ React36.createElement(Box22, {
10138
+ }, getLogString(log)))), showBottomIndicator && /* @__PURE__ */ React37.createElement(Box23, {
9893
10139
  paddingX: 1,
9894
10140
  flexShrink: 0
9895
- }, /* @__PURE__ */ React36.createElement(Text31, {
10141
+ }, /* @__PURE__ */ React37.createElement(Text32, {
9896
10142
  color: theme.ui.border,
9897
10143
  dimColor: true
9898
10144
  }, "\u2193 ", clampedOffset, " more below"))));
9899
10145
  }, "LogPanel");
9900
10146
 
9901
10147
  // src/components/SecondaryPanel/MetadataPanel/MetadataPanel.tsx
9902
- import React45, { useState as useState18, useEffect as useEffect18 } from "react";
9903
- import { Box as Box30 } from "ink";
10148
+ import React46, { useState as useState19, useEffect as useEffect19 } from "react";
10149
+ import { Box as Box31 } from "ink";
9904
10150
 
9905
10151
  // src/flows/musicDownloadFlow/utils/metadataFields.ts
9906
10152
  function formatDuration(ms) {
@@ -10018,13 +10264,13 @@ var FIELDS = [
10018
10264
  var navigableFields = FIELDS.filter((f) => f.editable);
10019
10265
 
10020
10266
  // src/components/SecondaryPanel/MetadataPanel/MetadataSourceList.tsx
10021
- import React42 from "react";
10022
- import { Box as Box27, Text as Text36 } from "ink";
10267
+ import React43 from "react";
10268
+ import { Box as Box28, Text as Text37 } from "ink";
10023
10269
  import Spinner2 from "ink-spinner";
10024
10270
 
10025
10271
  // src/components/SecondaryPanel/MetadataPanel/useSourceListInput.ts
10026
- import React37 from "react";
10027
- import open2 from "open";
10272
+ import React38 from "react";
10273
+ import open3 from "open";
10028
10274
  import clipboard2 from "clipboardy";
10029
10275
  function toOpenableUri2(url) {
10030
10276
  const m = url.match(/open\.spotify\.com\/(track|album|artist|playlist)\/([A-Za-z0-9]+)/);
@@ -10101,7 +10347,7 @@ function useSourceListInput({ groups, sortedGroups, cursor, isActive, onCursorCh
10101
10347
  id: "sourceList.line.uri",
10102
10348
  left: {
10103
10349
  type: "node",
10104
- renderNode: /* @__PURE__ */ __name((dimmed) => React37.createElement(Uri, {
10350
+ renderNode: /* @__PURE__ */ __name((dimmed) => React38.createElement(Uri, {
10105
10351
  uri,
10106
10352
  platform: focusedResult.metadata.platform,
10107
10353
  dimmed,
@@ -10329,7 +10575,7 @@ function useSourceListInput({ groups, sortedGroups, cursor, isActive, onCursorCh
10329
10575
  handler: /* @__PURE__ */ __name(() => {
10330
10576
  if (cursor.type !== "result") return;
10331
10577
  const url = sortedGroups[cursor.groupIndex]?.results[cursor.resultIndex]?.metadata.url;
10332
- if (url) open2(toOpenableUri2(url)).catch(() => {
10578
+ if (url) open3(toOpenableUri2(url)).catch(() => {
10333
10579
  });
10334
10580
  }, "handler")
10335
10581
  },
@@ -10446,8 +10692,8 @@ function useSourceListInput({ groups, sortedGroups, cursor, isActive, onCursorCh
10446
10692
  __name(useSourceListInput, "useSourceListInput");
10447
10693
 
10448
10694
  // src/components/SecondaryPanel/MetadataPanel/MetadataGroupHeader.tsx
10449
- import React38 from "react";
10450
- import { Box as Box23, Text as Text32 } from "ink";
10695
+ import React39 from "react";
10696
+ import { Box as Box24, Text as Text33 } from "ink";
10451
10697
  var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive }) => {
10452
10698
  const theme = useTheme();
10453
10699
  const display = providerDisplayRegistry.get(group.serviceKey);
@@ -10458,7 +10704,7 @@ var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive
10458
10704
  const fetchedByLabel = isFallback ? providerDisplayRegistry.get(primaryResult.metadata.fetchedBy).label : void 0;
10459
10705
  const countLabel = `(${group.results.length} result${group.results.length !== 1 ? "s" : ""})`;
10460
10706
  const noteText = isFallback ? `\u2139 ${display.label} is not available but rudimentary metadata were fetched from ${fetchedByLabel}` : void 0;
10461
- return /* @__PURE__ */ React38.createElement(Box23, {
10707
+ return /* @__PURE__ */ React39.createElement(Box24, {
10462
10708
  flexDirection: "row",
10463
10709
  flexGrow: 1,
10464
10710
  flexShrink: 0,
@@ -10466,29 +10712,29 @@ var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive
10466
10712
  overflow: "hidden",
10467
10713
  backgroundColor: bg,
10468
10714
  alignItems: "flex-start"
10469
- }, /* @__PURE__ */ React38.createElement(Box23, {
10715
+ }, /* @__PURE__ */ React39.createElement(Box24, {
10470
10716
  width: 3,
10471
10717
  minWidth: 3,
10472
10718
  flexShrink: 0
10473
- }, /* @__PURE__ */ React38.createElement(Text32, {
10719
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10474
10720
  color: isSelected && isActive ? theme.text.active : theme.text.secondary,
10475
10721
  wrap: "truncate-end"
10476
- }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React38.createElement(Box23, {
10722
+ }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React39.createElement(Box24, {
10477
10723
  flexShrink: 0
10478
- }, /* @__PURE__ */ React38.createElement(Text32, {
10724
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10479
10725
  color: display.color,
10480
10726
  wrap: "truncate-end",
10481
10727
  bold: true
10482
- }, "\u25CF " + display.label)), /* @__PURE__ */ React38.createElement(Box23, {
10728
+ }, "\u25CF " + display.label)), /* @__PURE__ */ React39.createElement(Box24, {
10483
10729
  flexShrink: 0
10484
- }, /* @__PURE__ */ React38.createElement(Text32, {
10730
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10485
10731
  color: theme.text.secondary,
10486
10732
  wrap: "truncate-end"
10487
- }, " " + countLabel)), noteText && /* @__PURE__ */ React38.createElement(Box23, {
10733
+ }, " " + countLabel)), noteText && /* @__PURE__ */ React39.createElement(Box24, {
10488
10734
  flexGrow: 1,
10489
10735
  paddingLeft: 3,
10490
10736
  flexShrink: 0
10491
- }, /* @__PURE__ */ React38.createElement(Text32, {
10737
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10492
10738
  color: theme.palette.blue,
10493
10739
  italic: true,
10494
10740
  wrap: "truncate-end"
@@ -10496,12 +10742,12 @@ var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive
10496
10742
  }, "MetadataGroupHeader");
10497
10743
 
10498
10744
  // src/components/SecondaryPanel/MetadataPanel/MetadataResultRow.tsx
10499
- import React40 from "react";
10500
- import { Box as Box25, Text as Text34 } from "ink";
10745
+ import React41 from "react";
10746
+ import { Box as Box26, Text as Text35 } from "ink";
10501
10747
 
10502
10748
  // src/components/SecondaryPanel/MetadataPanel/DiscoverySourceLine.tsx
10503
- import React39 from "react";
10504
- import { Box as Box24, Text as Text33 } from "ink";
10749
+ import React40 from "react";
10750
+ import { Box as Box25, Text as Text34 } from "ink";
10505
10751
  function formatSearchKeys(keys) {
10506
10752
  return keys.map((k) => {
10507
10753
  switch (k) {
@@ -10526,37 +10772,37 @@ var DiscoverySourceLine = /* @__PURE__ */ __name(({ source, dimmed }) => {
10526
10772
  const discovererDisplay = providerDisplayRegistry.get(source.discoveredBy);
10527
10773
  const fromUriPlatform = source.fromUri.split("::")[0]?.toLowerCase() ?? "";
10528
10774
  const keysLabel = formatSearchKeys(source.searchKeys);
10529
- return /* @__PURE__ */ React39.createElement(Box24, {
10775
+ return /* @__PURE__ */ React40.createElement(Box25, {
10530
10776
  flexDirection: "row",
10531
10777
  overflow: "hidden",
10532
10778
  marginLeft: 14,
10533
10779
  flexShrink: 0,
10534
10780
  height: 1
10535
- }, /* @__PURE__ */ React39.createElement(Box24, {
10781
+ }, /* @__PURE__ */ React40.createElement(Box25, {
10536
10782
  flexShrink: 0
10537
- }, /* @__PURE__ */ React39.createElement(Text33, {
10783
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10538
10784
  color: theme.text.secondary,
10539
10785
  dimColor: dimmed,
10540
10786
  wrap: "truncate-end"
10541
- }, "\u2514\u2500 found by ")), /* @__PURE__ */ React39.createElement(Box24, {
10787
+ }, "\u2514\u2500 found by ")), /* @__PURE__ */ React40.createElement(Box25, {
10542
10788
  flexShrink: 0
10543
- }, /* @__PURE__ */ React39.createElement(Text33, {
10789
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10544
10790
  color: discovererDisplay.color,
10545
10791
  dimColor: dimmed,
10546
10792
  wrap: "truncate-end"
10547
- }, discovererDisplay.label)), /* @__PURE__ */ React39.createElement(Box24, {
10793
+ }, discovererDisplay.label)), /* @__PURE__ */ React40.createElement(Box25, {
10548
10794
  flexShrink: 0
10549
- }, /* @__PURE__ */ React39.createElement(Text33, {
10795
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10550
10796
  color: theme.text.secondary,
10551
10797
  dimColor: dimmed,
10552
10798
  wrap: "truncate-end"
10553
- }, " using ")), /* @__PURE__ */ React39.createElement(Uri, {
10799
+ }, " using ")), /* @__PURE__ */ React40.createElement(Uri, {
10554
10800
  uri: source.fromUri,
10555
10801
  platform: fromUriPlatform,
10556
10802
  dimmed
10557
- }), /* @__PURE__ */ React39.createElement(Box24, {
10803
+ }), /* @__PURE__ */ React40.createElement(Box25, {
10558
10804
  flexShrink: 0
10559
- }, /* @__PURE__ */ React39.createElement(Text33, {
10805
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10560
10806
  color: theme.text.secondary,
10561
10807
  dimColor: dimmed,
10562
10808
  wrap: "truncate-end"
@@ -10618,12 +10864,12 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10618
10864
  const bg = isSelected ? focusColorBg : void 0;
10619
10865
  const uri = m.uri ?? `${m.platform.toUpperCase()}::TRACK::${m.id}`;
10620
10866
  const sourceLines = showDiscoverySources && result.discoverySources.length > 0 ? result.discoverySources : [];
10621
- return /* @__PURE__ */ React40.createElement(Box25, {
10867
+ return /* @__PURE__ */ React41.createElement(Box26, {
10622
10868
  flexDirection: "column",
10623
10869
  width,
10624
10870
  overflow: "hidden",
10625
10871
  flexShrink: 0
10626
- }, /* @__PURE__ */ React40.createElement(Box25, {
10872
+ }, /* @__PURE__ */ React41.createElement(Box26, {
10627
10873
  flexDirection: "row",
10628
10874
  width,
10629
10875
  minWidth: width,
@@ -10633,52 +10879,52 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10633
10879
  flexWrap: "nowrap",
10634
10880
  alignItems: "flex-start",
10635
10881
  flexShrink: 0
10636
- }, /* @__PURE__ */ React40.createElement(Box25, {
10882
+ }, /* @__PURE__ */ React41.createElement(Box26, {
10637
10883
  width: 3,
10638
10884
  minWidth: 3,
10639
10885
  flexShrink: 0,
10640
10886
  marginRight: 2
10641
- }, /* @__PURE__ */ React40.createElement(Text34, null, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React40.createElement(Box25, {
10887
+ }, /* @__PURE__ */ React41.createElement(Text35, null, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React41.createElement(Box26, {
10642
10888
  width: 2,
10643
10889
  minWidth: 2,
10644
10890
  paddingRight: 1,
10645
10891
  flexShrink: 0
10646
- }, /* @__PURE__ */ React40.createElement(Text34, {
10892
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10647
10893
  dimColor: isDimmed,
10648
10894
  color: statusColor
10649
- }, statusIcon)), /* @__PURE__ */ React40.createElement(Box25, {
10895
+ }, statusIcon)), /* @__PURE__ */ React41.createElement(Box26, {
10650
10896
  width: 7,
10651
10897
  minWidth: 7,
10652
10898
  paddingRight: 1,
10653
10899
  flexShrink: 0
10654
- }, /* @__PURE__ */ React40.createElement(Text34, {
10900
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10655
10901
  color: badge.color,
10656
10902
  dimColor: isDimmed,
10657
10903
  strikethrough: isDimmed
10658
- }, badgeText)), /* @__PURE__ */ React40.createElement(Box25, {
10904
+ }, badgeText)), /* @__PURE__ */ React41.createElement(Box26, {
10659
10905
  flexShrink: 0
10660
- }, /* @__PURE__ */ React40.createElement(Uri, {
10906
+ }, /* @__PURE__ */ React41.createElement(Uri, {
10661
10907
  uri,
10662
10908
  platform: m.platform,
10663
10909
  fetchState: result.fetchState,
10664
10910
  dimmed: isDimmed,
10665
10911
  fetchedBy: m.fetchedBy
10666
- })), result.fetchState === "error" && result.fetchError && /* @__PURE__ */ React40.createElement(Box25, {
10912
+ })), result.fetchState === "error" && result.fetchError && /* @__PURE__ */ React41.createElement(Box26, {
10667
10913
  paddingLeft: 1,
10668
10914
  flexShrink: 0
10669
- }, /* @__PURE__ */ React40.createElement(Text34, {
10915
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10670
10916
  color: theme.status.error,
10671
10917
  wrap: "truncate-end"
10672
- }, result.fetchError)), /* @__PURE__ */ React40.createElement(Box25, {
10918
+ }, result.fetchError)), /* @__PURE__ */ React41.createElement(Box26, {
10673
10919
  flexGrow: 1,
10674
10920
  overflow: "hidden",
10675
10921
  paddingLeft: 1
10676
- }, /* @__PURE__ */ React40.createElement(Text34, {
10922
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10677
10923
  color: theme.text.primary,
10678
10924
  dimColor: isDimmed,
10679
10925
  strikethrough: isDimmed,
10680
10926
  wrap: "truncate-end"
10681
- }, suffix)), isSelected && /* @__PURE__ */ React40.createElement(Box25, {
10927
+ }, suffix)), isSelected && /* @__PURE__ */ React41.createElement(Box26, {
10682
10928
  flexDirection: "row",
10683
10929
  height: 1,
10684
10930
  width: 5,
@@ -10686,10 +10932,10 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10686
10932
  paddingLeft: 1,
10687
10933
  paddingRight: 1,
10688
10934
  flexShrink: 0
10689
- }, /* @__PURE__ */ React40.createElement(Text34, {
10935
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10690
10936
  color: theme.text.secondary,
10691
10937
  dimColor: isDimmed
10692
- }, ">>>"))), sourceLines.map((src, i) => /* @__PURE__ */ React40.createElement(DiscoverySourceLine, {
10938
+ }, ">>>"))), sourceLines.map((src, i) => /* @__PURE__ */ React41.createElement(DiscoverySourceLine, {
10693
10939
  key: i,
10694
10940
  source: src,
10695
10941
  dimmed: isDimmed
@@ -10697,8 +10943,8 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10697
10943
  }, "MetadataResultRow");
10698
10944
 
10699
10945
  // src/components/SecondaryPanel/MetadataPanel/MetadataCompiledRow.tsx
10700
- import React41 from "react";
10701
- import { Box as Box26, Text as Text35 } from "ink";
10946
+ import React42 from "react";
10947
+ import { Box as Box27, Text as Text36 } from "ink";
10702
10948
 
10703
10949
  // src/components/SecondaryPanel/utils.ts
10704
10950
  function formatDuration3(ms) {
@@ -10722,7 +10968,7 @@ var MetadataCompiledRow = /* @__PURE__ */ __name(({ compiled, overrideCount, isS
10722
10968
  const durationPart = duration ? ` (${duration})` : "";
10723
10969
  const trackInfo = artist ? `${artist} - ${title}${durationPart}` : `${title}${durationPart}`;
10724
10970
  const suffixText = overrideCount > 0 ? ` (${overrideCount} edit${overrideCount === 1 ? "" : "s"})` : "";
10725
- return /* @__PURE__ */ React41.createElement(Box26, {
10971
+ return /* @__PURE__ */ React42.createElement(Box27, {
10726
10972
  flexDirection: "row",
10727
10973
  height: 1,
10728
10974
  flexShrink: 0,
@@ -10730,36 +10976,36 @@ var MetadataCompiledRow = /* @__PURE__ */ __name(({ compiled, overrideCount, isS
10730
10976
  minWidth: width,
10731
10977
  overflow: "hidden",
10732
10978
  backgroundColor: bg
10733
- }, /* @__PURE__ */ React41.createElement(Box26, {
10979
+ }, /* @__PURE__ */ React42.createElement(Box27, {
10734
10980
  width: 3,
10735
10981
  minWidth: 3
10736
- }, /* @__PURE__ */ React41.createElement(Text35, {
10982
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10737
10983
  color: isSelected && isActive ? theme.text.active : theme.text.secondary
10738
- }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React41.createElement(Box26, {
10984
+ }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React42.createElement(Box27, {
10739
10985
  width: COMPILED_PREFIX.length,
10740
10986
  minWidth: COMPILED_PREFIX.length
10741
- }, /* @__PURE__ */ React41.createElement(Text35, {
10987
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10742
10988
  color: theme.action.primary
10743
- }, COMPILED_PREFIX)), /* @__PURE__ */ React41.createElement(Box26, {
10989
+ }, COMPILED_PREFIX)), /* @__PURE__ */ React42.createElement(Box27, {
10744
10990
  flexGrow: 1,
10745
10991
  flexDirection: "row"
10746
- }, /* @__PURE__ */ React41.createElement(Text35, {
10992
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10747
10993
  color: theme.text.primary,
10748
10994
  wrap: "truncate-end"
10749
- }, trackInfo)), overrideCount > 0 && /* @__PURE__ */ React41.createElement(Box26, {
10995
+ }, trackInfo)), overrideCount > 0 && /* @__PURE__ */ React42.createElement(Box27, {
10750
10996
  flexShrink: 0
10751
- }, /* @__PURE__ */ React41.createElement(Text35, {
10997
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10752
10998
  color: theme.text.secondary,
10753
10999
  italic: true,
10754
11000
  wrap: "truncate-end"
10755
- }, suffixText)), isSelected && /* @__PURE__ */ React41.createElement(Box26, {
11001
+ }, suffixText)), isSelected && /* @__PURE__ */ React42.createElement(Box27, {
10756
11002
  flexDirection: "row",
10757
11003
  height: 1,
10758
11004
  width: 5,
10759
11005
  minWidth: 5,
10760
11006
  paddingLeft: 1,
10761
11007
  paddingRight: 1
10762
- }, /* @__PURE__ */ React41.createElement(Text35, {
11008
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10763
11009
  color: theme.text.secondary
10764
11010
  }, ">>>")));
10765
11011
  }, "MetadataCompiledRow");
@@ -10788,26 +11034,26 @@ var MetadataSourceList = /* @__PURE__ */ __name(({ groups, compiled, overrides,
10788
11034
  onRefetchResult
10789
11035
  });
10790
11036
  const overrideCount = Object.keys(overrides).filter((k) => overrides[k] !== void 0).length;
10791
- return /* @__PURE__ */ React42.createElement(Box27, {
11037
+ return /* @__PURE__ */ React43.createElement(Box28, {
10792
11038
  flexDirection: "column",
10793
11039
  width,
10794
11040
  height,
10795
11041
  overflow: "hidden"
10796
- }, /* @__PURE__ */ React42.createElement(Box27, {
11042
+ }, /* @__PURE__ */ React43.createElement(Box28, {
10797
11043
  flexDirection: "row",
10798
11044
  height: 1,
10799
11045
  marginBottom: 1,
10800
11046
  overflow: "hidden"
10801
- }, /* @__PURE__ */ React42.createElement(Text36, {
11047
+ }, /* @__PURE__ */ React43.createElement(Text37, {
10802
11048
  color: "gray",
10803
11049
  italic: true,
10804
11050
  wrap: "truncate-end"
10805
- }, " " + HEADER_LINE)), /* @__PURE__ */ React42.createElement(Box27, {
11051
+ }, " " + HEADER_LINE)), /* @__PURE__ */ React43.createElement(Box28, {
10806
11052
  flexDirection: "column",
10807
11053
  flexGrow: 1,
10808
11054
  gap: 1,
10809
11055
  overflow: "hidden"
10810
- }, /* @__PURE__ */ React42.createElement(MetadataCompiledRow, {
11056
+ }, /* @__PURE__ */ React43.createElement(MetadataCompiledRow, {
10811
11057
  compiled,
10812
11058
  overrideCount,
10813
11059
  isSelected: cursor.type === "compiled",
@@ -10817,15 +11063,15 @@ var MetadataSourceList = /* @__PURE__ */ __name(({ groups, compiled, overrides,
10817
11063
  const sortedResults = [
10818
11064
  ...group.results
10819
11065
  ].sort((a, b) => a.rank - b.rank);
10820
- return /* @__PURE__ */ React42.createElement(Box27, {
11066
+ return /* @__PURE__ */ React43.createElement(Box28, {
10821
11067
  key: group.serviceKey,
10822
11068
  flexDirection: "column",
10823
11069
  overflow: "hidden"
10824
- }, /* @__PURE__ */ React42.createElement(MetadataGroupHeader, {
11070
+ }, /* @__PURE__ */ React43.createElement(MetadataGroupHeader, {
10825
11071
  group,
10826
11072
  isSelected: cursor.type === "group" && cursor.groupIndex === gIdx,
10827
11073
  isActive
10828
- }), sortedResults.map((result, rIdx) => /* @__PURE__ */ React42.createElement(MetadataResultRow, {
11074
+ }), sortedResults.map((result, rIdx) => /* @__PURE__ */ React43.createElement(MetadataResultRow, {
10829
11075
  key: rIdx,
10830
11076
  result,
10831
11077
  serviceKey: group.serviceKey,
@@ -10834,32 +11080,32 @@ var MetadataSourceList = /* @__PURE__ */ __name(({ groups, compiled, overrides,
10834
11080
  width,
10835
11081
  showDiscoverySources
10836
11082
  })));
10837
- }), (isFetchingPrimarySource || isDiscovering) && /* @__PURE__ */ React42.createElement(Box27, {
11083
+ }), (isFetchingPrimarySource || isDiscovering) && /* @__PURE__ */ React43.createElement(Box28, {
10838
11084
  flexDirection: "row",
10839
11085
  height: 1,
10840
11086
  paddingLeft: 3,
10841
11087
  flexShrink: 0
10842
- }, /* @__PURE__ */ React42.createElement(Box27, {
11088
+ }, /* @__PURE__ */ React43.createElement(Box28, {
10843
11089
  flexDirection: "row",
10844
11090
  paddingRight: 1,
10845
11091
  flexShrink: 0
10846
- }, /* @__PURE__ */ React42.createElement(Text36, {
11092
+ }, /* @__PURE__ */ React43.createElement(Text37, {
10847
11093
  color: theme.text.secondary
10848
- }, /* @__PURE__ */ React42.createElement(Spinner2, {
11094
+ }, /* @__PURE__ */ React43.createElement(Spinner2, {
10849
11095
  type: "dots"
10850
- }))), /* @__PURE__ */ React42.createElement(Text36, {
11096
+ }))), /* @__PURE__ */ React43.createElement(Text37, {
10851
11097
  color: theme.text.secondary
10852
11098
  }, getLoadingText(isFetchingPrimarySource, isDiscovering)))));
10853
11099
  }, "MetadataSourceList");
10854
11100
 
10855
11101
  // src/components/SecondaryPanel/MetadataPanel/MetadataDetailPanel.tsx
10856
- import React44, { useState as useState17 } from "react";
10857
- import { Box as Box29, Text as Text38 } from "ink";
11102
+ import React45, { useState as useState18 } from "react";
11103
+ import { Box as Box30, Text as Text39 } from "ink";
10858
11104
  import clipboard3 from "clipboardy";
10859
11105
 
10860
11106
  // src/components/SecondaryPanel/MetadataPanel/FieldRow.tsx
10861
- import React43 from "react";
10862
- import { Box as Box28, Text as Text37 } from "ink";
11107
+ import React44 from "react";
11108
+ import { Box as Box29, Text as Text38 } from "ink";
10863
11109
  import TextInput6 from "ink-text-input";
10864
11110
  function getPlatformColor(apiProvider) {
10865
11111
  return providerDisplayRegistry.get(apiProvider).colorSubtle;
@@ -10889,45 +11135,45 @@ var LABEL_W = 10;
10889
11135
  var FieldRow = /* @__PURE__ */ __name(({ field, isFocused, isCompiled, isEditing, value, attribution, hasOverride, editValue, editError, onEditValueChange, onEditSubmit }) => {
10890
11136
  const theme = useTheme();
10891
11137
  const badge = attributionBadge(attribution, theme);
10892
- return /* @__PURE__ */ React43.createElement(Box28, {
11138
+ return /* @__PURE__ */ React44.createElement(Box29, {
10893
11139
  flexDirection: "row",
10894
11140
  paddingX: 1,
10895
11141
  flexShrink: 0,
10896
11142
  flexGrow: 1
10897
- }, /* @__PURE__ */ React43.createElement(Box28, {
11143
+ }, /* @__PURE__ */ React44.createElement(Box29, {
10898
11144
  width: 2,
10899
11145
  minWidth: 2,
10900
11146
  flexShrink: 0
10901
- }, isFocused && /* @__PURE__ */ React43.createElement(Text37, {
11147
+ }, isFocused && /* @__PURE__ */ React44.createElement(Text38, {
10902
11148
  color: theme.text.active
10903
- }, "\u261B")), /* @__PURE__ */ React43.createElement(Box28, {
11149
+ }, "\u261B")), /* @__PURE__ */ React44.createElement(Box29, {
10904
11150
  width: LABEL_W,
10905
11151
  flexShrink: 0
10906
- }, /* @__PURE__ */ React43.createElement(Text37, {
11152
+ }, /* @__PURE__ */ React44.createElement(Text38, {
10907
11153
  color: isFocused ? theme.field.selected : theme.ui.border,
10908
11154
  bold: true
10909
- }, field.label.toUpperCase().padEnd(LABEL_W))), isEditing ? /* @__PURE__ */ React43.createElement(Box28, {
11155
+ }, field.label.toUpperCase().padEnd(LABEL_W))), isEditing ? /* @__PURE__ */ React44.createElement(Box29, {
10910
11156
  flexWrap: "wrap",
10911
11157
  flexDirection: "row",
10912
11158
  flexGrow: 1
10913
- }, /* @__PURE__ */ React43.createElement(TextInput6, {
11159
+ }, /* @__PURE__ */ React44.createElement(TextInput6, {
10914
11160
  value: editValue,
10915
11161
  onChange: onEditValueChange,
10916
11162
  onSubmit: onEditSubmit
10917
- }), editError && /* @__PURE__ */ React43.createElement(Text37, {
11163
+ }), editError && /* @__PURE__ */ React44.createElement(Text38, {
10918
11164
  color: theme.field.error
10919
- }, " \u2717 invalid")) : /* @__PURE__ */ React43.createElement(Box28, {
11165
+ }, " \u2717 invalid")) : /* @__PURE__ */ React44.createElement(Box29, {
10920
11166
  flexGrow: 1
10921
- }, /* @__PURE__ */ React43.createElement(Text37, {
11167
+ }, /* @__PURE__ */ React44.createElement(Text38, {
10922
11168
  color: value === "\u2014" ? theme.field.missing : hasOverride ? theme.field.overridden : theme.field.normal,
10923
11169
  underline: isFocused,
10924
11170
  dimColor: value === "\u2014",
10925
11171
  wrap: "truncate-end"
10926
- }, value)), isCompiled && !isEditing && !editError && /* @__PURE__ */ React43.createElement(Box28, {
11172
+ }, value)), isCompiled && !isEditing && !editError && /* @__PURE__ */ React44.createElement(Box29, {
10927
11173
  minWidth: badge.text.length + 1,
10928
11174
  paddingLeft: 1,
10929
11175
  flexShrink: 0
10930
- }, /* @__PURE__ */ React43.createElement(Text37, {
11176
+ }, /* @__PURE__ */ React44.createElement(Text38, {
10931
11177
  color: badge.color,
10932
11178
  dimColor: true,
10933
11179
  italic: badge.italic
@@ -10942,9 +11188,9 @@ __name(getPlatformBrightColor, "getPlatformBrightColor");
10942
11188
  var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides, selectedFieldIndex, isActive, width, onOverrideChange, onInnerFocusSwitch }) => {
10943
11189
  const theme = useTheme();
10944
11190
  const { setIsEditingField } = useFocusContext();
10945
- const [editingField, setEditingField] = useState17(null);
10946
- const [editValue, setEditValue] = useState17("");
10947
- const [editError, setEditError] = useState17(false);
11191
+ const [editingField, setEditingField] = useState18(null);
11192
+ const [editValue, setEditValue] = useState18("");
11193
+ const [editError, setEditError] = useState18(false);
10948
11194
  function startEditing(fieldKey, initialValue) {
10949
11195
  setEditValue(initialValue);
10950
11196
  setEditError(false);
@@ -11085,25 +11331,25 @@ var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides,
11085
11331
  const borderLeft = `\u250C${"\u2500".repeat(leftD)} `;
11086
11332
  const borderRight = ` ${"\u2500".repeat(rightD)}\u2510`;
11087
11333
  const platformColor = isCompiled ? theme.text.secondary : getPlatformBrightColor(source.metadata.apiProvider);
11088
- return /* @__PURE__ */ React44.createElement(Box29, {
11334
+ return /* @__PURE__ */ React45.createElement(Box30, {
11089
11335
  flexDirection: "column",
11090
11336
  width,
11091
11337
  flexGrow: 1,
11092
11338
  overflow: "hidden"
11093
- }, /* @__PURE__ */ React44.createElement(Box29, {
11339
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11094
11340
  flexDirection: "column",
11095
11341
  height: 1,
11096
11342
  flexShrink: 0,
11097
11343
  overflow: "hidden"
11098
- }, /* @__PURE__ */ React44.createElement(Box29, {
11344
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11099
11345
  flexDirection: "row"
11100
- }, /* @__PURE__ */ React44.createElement(Text38, {
11346
+ }, /* @__PURE__ */ React45.createElement(Text39, {
11101
11347
  color: theme.text.secondary
11102
- }, borderLeft), /* @__PURE__ */ React44.createElement(Text38, {
11348
+ }, borderLeft), /* @__PURE__ */ React45.createElement(Text39, {
11103
11349
  color: platformColor
11104
- }, headerLabel), /* @__PURE__ */ React44.createElement(Text38, {
11350
+ }, headerLabel), /* @__PURE__ */ React45.createElement(Text39, {
11105
11351
  color: theme.text.secondary
11106
- }, borderRight))), /* @__PURE__ */ React44.createElement(Box29, {
11352
+ }, borderRight))), /* @__PURE__ */ React45.createElement(Box30, {
11107
11353
  flexDirection: "column",
11108
11354
  flexGrow: 1,
11109
11355
  overflow: "hidden",
@@ -11111,11 +11357,11 @@ var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides,
11111
11357
  borderColor: theme.text.secondary,
11112
11358
  borderBackgroundColor: theme.ui.background,
11113
11359
  borderTop: false
11114
- }, /* @__PURE__ */ React44.createElement(Box29, {
11360
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11115
11361
  flexDirection: "column",
11116
11362
  flexGrow: 1,
11117
11363
  overflow: "hidden"
11118
- }, /* @__PURE__ */ React44.createElement(Box29, {
11364
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11119
11365
  flexDirection: "column",
11120
11366
  flexShrink: 0
11121
11367
  }, FIELDS.map((field) => {
@@ -11124,7 +11370,7 @@ var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides,
11124
11370
  const attr = isCompiled ? compiled.attribution[field.key] : void 0;
11125
11371
  const isEditing = editingField === field.key;
11126
11372
  const hasOverride = isCompiled && overrides[field.key] !== void 0;
11127
- return /* @__PURE__ */ React44.createElement(FieldRow, {
11373
+ return /* @__PURE__ */ React45.createElement(FieldRow, {
11128
11374
  key: field.key,
11129
11375
  field,
11130
11376
  isFocused,
@@ -11149,13 +11395,13 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11149
11395
  const { cursor, showDiscoverySources, innerFocus, selectedFieldIndex } = sourcesPanel;
11150
11396
  const isPanelActive = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab === "metadataSources";
11151
11397
  const typedTask = selectedTask;
11152
- const [snapshot, setSnapshot] = useState18(() => typedTask?.get());
11153
- const [prevTypedTask, setPrevTypedTask] = useState18(typedTask);
11398
+ const [snapshot, setSnapshot] = useState19(() => typedTask?.get());
11399
+ const [prevTypedTask, setPrevTypedTask] = useState19(typedTask);
11154
11400
  if (prevTypedTask !== typedTask) {
11155
11401
  setPrevTypedTask(typedTask);
11156
11402
  setSnapshot(typedTask?.get());
11157
11403
  }
11158
- useEffect18(() => {
11404
+ useEffect19(() => {
11159
11405
  if (!typedTask) return;
11160
11406
  return typedTask.subscribe((t) => {
11161
11407
  setSnapshot(t.get());
@@ -11166,7 +11412,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11166
11412
  const groups = snapshot?.attributes?.metadataGroups ?? [];
11167
11413
  const overrides = snapshot?.attributes?.metadataOverride ?? {};
11168
11414
  const compiled = computeCompiledMetadata(groups, overrides);
11169
- const [splitRatio, setSplitRatio] = useState18(0.6);
11415
+ const [splitRatio, setSplitRatio] = useState19(0.6);
11170
11416
  const leftWidth = Math.floor(width * splitRatio) - 4;
11171
11417
  const rightWidth = width - leftWidth - 4;
11172
11418
  useShortcuts({
@@ -11282,7 +11528,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11282
11528
  const selectedResult = cursor.type === "result" ? sortedGroups[cursor.groupIndex]?.results.sort((a, b) => a.rank - b.rank)[cursor.resultIndex] : cursor.type === "group" ? sortedGroups[cursor.groupIndex] ? pickGroupRepresentative(sortedGroups[cursor.groupIndex]) : void 0 : void 0;
11283
11529
  const hintBarHeight = cursor.type === "result" ? 3 : 2;
11284
11530
  const listHeight = height - hintBarHeight;
11285
- return /* @__PURE__ */ React45.createElement(Box30, {
11531
+ return /* @__PURE__ */ React46.createElement(Box31, {
11286
11532
  flexDirection: "column",
11287
11533
  height,
11288
11534
  overflow: "hidden",
@@ -11292,11 +11538,11 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11292
11538
  borderTop: false,
11293
11539
  borderBottom: false,
11294
11540
  paddingRight: 1
11295
- }, /* @__PURE__ */ React45.createElement(Box30, {
11541
+ }, /* @__PURE__ */ React46.createElement(Box31, {
11296
11542
  flexDirection: "row",
11297
11543
  flexGrow: 1,
11298
11544
  overflow: "hidden"
11299
- }, /* @__PURE__ */ React45.createElement(MetadataSourceList, {
11545
+ }, /* @__PURE__ */ React46.createElement(MetadataSourceList, {
11300
11546
  groups,
11301
11547
  compiled,
11302
11548
  overrides,
@@ -11312,7 +11558,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11312
11558
  onRefetchResult: handleRefetchResult,
11313
11559
  isFetchingPrimarySource: snapshot?.attributes?.primaryMetadataInProgress ?? false,
11314
11560
  isDiscovering: snapshot?.attributes?.metadataDiscoveringInProgress ?? false
11315
- }), /* @__PURE__ */ React45.createElement(MetadataDetailPanel, {
11561
+ }), /* @__PURE__ */ React46.createElement(MetadataDetailPanel, {
11316
11562
  source: selectedResult ?? "compiled",
11317
11563
  compiled,
11318
11564
  overrides,
@@ -11322,30 +11568,30 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11322
11568
  height: listHeight,
11323
11569
  onOverrideChange: handleOverrideChange,
11324
11570
  onInnerFocusSwitch: /* @__PURE__ */ __name(() => setSourcesInnerFocus("list"), "onInnerFocusSwitch")
11325
- })), /* @__PURE__ */ React45.createElement(DynamicHintBar, {
11571
+ })), /* @__PURE__ */ React46.createElement(DynamicHintBar, {
11326
11572
  width: width - 2,
11327
11573
  isActive: isPanelActive
11328
11574
  }));
11329
11575
  }, "MetadataPanel");
11330
11576
 
11331
11577
  // src/components/SecondaryPanel/DownloadPanel/DownloadPanel.tsx
11332
- import React56, { useState as useState20, useEffect as useEffect20 } from "react";
11333
- import { Box as Box40 } from "ink";
11578
+ import React57, { useState as useState21, useEffect as useEffect21 } from "react";
11579
+ import { Box as Box41 } from "ink";
11334
11580
  import fs14 from "fs";
11335
11581
  import path14 from "path";
11336
11582
 
11337
11583
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/DownloadSourceTree.tsx
11338
- import React50 from "react";
11339
- import { Box as Box34, Text as Text43 } from "ink";
11584
+ import React51 from "react";
11585
+ import { Box as Box35, Text as Text44 } from "ink";
11340
11586
 
11341
11587
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/ProviderHeader.tsx
11342
- import React46 from "react";
11343
- import { Box as Box31, Text as Text39 } from "ink";
11588
+ import React47 from "react";
11589
+ import { Box as Box32, Text as Text40 } from "ink";
11344
11590
  function ProviderHeader({ label, color, addMargin }) {
11345
- return /* @__PURE__ */ React46.createElement(Box31, {
11591
+ return /* @__PURE__ */ React47.createElement(Box32, {
11346
11592
  paddingLeft: 1,
11347
11593
  marginTop: addMargin ? 1 : 0
11348
- }, /* @__PURE__ */ React46.createElement(Text39, {
11594
+ }, /* @__PURE__ */ React47.createElement(Text40, {
11349
11595
  color,
11350
11596
  bold: true
11351
11597
  }, label));
@@ -11353,8 +11599,8 @@ function ProviderHeader({ label, color, addMargin }) {
11353
11599
  __name(ProviderHeader, "ProviderHeader");
11354
11600
 
11355
11601
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/MetadataHeader.tsx
11356
- import React47 from "react";
11357
- import { Box as Box32, Text as Text40 } from "ink";
11602
+ import React48 from "react";
11603
+ import { Box as Box33, Text as Text41 } from "ink";
11358
11604
  function MetadataHeader({ source }) {
11359
11605
  const theme = useTheme();
11360
11606
  const m = source.track;
@@ -11362,23 +11608,23 @@ function MetadataHeader({ source }) {
11362
11608
  const title = m.trackName ?? "";
11363
11609
  const info = artist ? `${artist} - ${title}` : title;
11364
11610
  const uri = m.uri ?? `${m.platform.toUpperCase()}::TRACK::${m.id}`;
11365
- return /* @__PURE__ */ React47.createElement(Box32, {
11611
+ return /* @__PURE__ */ React48.createElement(Box33, {
11366
11612
  marginLeft: 2,
11367
11613
  flexDirection: "row",
11368
11614
  overflow: "hidden",
11369
11615
  flexShrink: 0
11370
- }, /* @__PURE__ */ React47.createElement(Box32, {
11616
+ }, /* @__PURE__ */ React48.createElement(Box33, {
11371
11617
  paddingRight: 1,
11372
11618
  flexDirection: "row",
11373
11619
  flexShrink: 0
11374
- }, /* @__PURE__ */ React47.createElement(Text40, {
11620
+ }, /* @__PURE__ */ React48.createElement(Text41, {
11375
11621
  color: theme.text.secondary
11376
- }, "\u2514\u2500 used")), /* @__PURE__ */ React47.createElement(Box32, {
11622
+ }, "\u2514\u2500 used")), /* @__PURE__ */ React48.createElement(Box33, {
11377
11623
  flexShrink: 0
11378
- }, /* @__PURE__ */ React47.createElement(Uri, {
11624
+ }, /* @__PURE__ */ React48.createElement(Uri, {
11379
11625
  uri,
11380
11626
  platform: m.platform
11381
- })), /* @__PURE__ */ React47.createElement(Text40, {
11627
+ })), /* @__PURE__ */ React48.createElement(Text41, {
11382
11628
  color: theme.text.primary,
11383
11629
  wrap: "truncate-end"
11384
11630
  }, ` (${info})`));
@@ -11386,8 +11632,8 @@ function MetadataHeader({ source }) {
11386
11632
  __name(MetadataHeader, "MetadataHeader");
11387
11633
 
11388
11634
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/SourceFileRow.tsx
11389
- import React49 from "react";
11390
- import { Box as Box33, Text as Text42 } from "ink";
11635
+ import React50 from "react";
11636
+ import { Box as Box34, Text as Text43 } from "ink";
11391
11637
 
11392
11638
  // src/components/SecondaryPanel/DownloadPanel/utils.ts
11393
11639
  function getProviderColor(provider) {
@@ -11412,32 +11658,32 @@ function tagValue(tags, key) {
11412
11658
  __name(tagValue, "tagValue");
11413
11659
 
11414
11660
  // src/components/SecondaryPanel/DownloadPanel/StateBadge.tsx
11415
- import React48 from "react";
11416
- import { Text as Text41 } from "ink";
11661
+ import React49 from "react";
11662
+ import { Text as Text42 } from "ink";
11417
11663
  function StateBadge({ source }) {
11418
11664
  const theme = useTheme();
11419
- if (source.savedFile) return /* @__PURE__ */ React48.createElement(Text41, {
11665
+ if (source.savedFile) return /* @__PURE__ */ React49.createElement(Text42, {
11420
11666
  color: theme.status.locked
11421
11667
  }, "\u25CF SAVED");
11422
11668
  switch (source.state) {
11423
11669
  case "downloading":
11424
- return /* @__PURE__ */ React48.createElement(Text41, {
11670
+ return /* @__PURE__ */ React49.createElement(Text42, {
11425
11671
  color: theme.status.downloading
11426
11672
  }, "DOWNLOADING");
11427
11673
  case "downloaded":
11428
- return /* @__PURE__ */ React48.createElement(Text41, {
11674
+ return /* @__PURE__ */ React49.createElement(Text42, {
11429
11675
  color: theme.status.success
11430
11676
  }, "DOWNLOADED");
11431
11677
  case "failed":
11432
- return /* @__PURE__ */ React48.createElement(Text41, {
11678
+ return /* @__PURE__ */ React49.createElement(Text42, {
11433
11679
  color: theme.status.error
11434
11680
  }, "FAILED");
11435
11681
  case "searching":
11436
- return /* @__PURE__ */ React48.createElement(Text41, {
11682
+ return /* @__PURE__ */ React49.createElement(Text42, {
11437
11683
  color: theme.status.downloading
11438
11684
  }, "SEARCHING");
11439
11685
  default:
11440
- return /* @__PURE__ */ React48.createElement(Text41, {
11686
+ return /* @__PURE__ */ React49.createElement(Text42, {
11441
11687
  color: theme.status.skipped
11442
11688
  }, "PENDING");
11443
11689
  }
@@ -11451,7 +11697,7 @@ function SourceFileRow({ source, sourceIndex, isSelected, isActive, width }) {
11451
11697
  const localFile = source.localFile;
11452
11698
  const filename = localFile ? `${localFile.name}.${localFile.extension}` : null;
11453
11699
  const sizeText = source.fileInfo ? formatBytes(source.fileInfo.sizeBytes) : "";
11454
- return /* @__PURE__ */ React49.createElement(Box33, {
11700
+ return /* @__PURE__ */ React50.createElement(Box34, {
11455
11701
  flexDirection: "row",
11456
11702
  width,
11457
11703
  minWidth: width,
@@ -11459,48 +11705,48 @@ function SourceFileRow({ source, sourceIndex, isSelected, isActive, width }) {
11459
11705
  overflow: "hidden",
11460
11706
  backgroundColor: bg,
11461
11707
  paddingLeft: 1
11462
- }, /* @__PURE__ */ React49.createElement(Box33, {
11708
+ }, /* @__PURE__ */ React50.createElement(Box34, {
11463
11709
  width: 3,
11464
11710
  minWidth: 3,
11465
11711
  flexShrink: 0
11466
- }, /* @__PURE__ */ React49.createElement(Text42, {
11712
+ }, /* @__PURE__ */ React50.createElement(Text43, {
11467
11713
  color: theme.ui.focusIndicator
11468
- }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React49.createElement(Box33, {
11714
+ }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React50.createElement(Box34, {
11469
11715
  width: 2,
11470
11716
  minWidth: 2,
11471
11717
  flexShrink: 0
11472
- }, source.isRejected ? /* @__PURE__ */ React49.createElement(Text42, {
11718
+ }, source.isRejected ? /* @__PURE__ */ React50.createElement(Text43, {
11473
11719
  color: theme.status.error
11474
- }, "\u2718 ") : source.selected ? /* @__PURE__ */ React49.createElement(Text42, {
11720
+ }, "\u2718 ") : source.selected ? /* @__PURE__ */ React50.createElement(Text43, {
11475
11721
  color: theme.ui.selection
11476
- }, "\u2713 ") : /* @__PURE__ */ React49.createElement(Text42, null, " ")), /* @__PURE__ */ React49.createElement(Box33, {
11722
+ }, "\u2713 ") : /* @__PURE__ */ React50.createElement(Text43, null, " ")), /* @__PURE__ */ React50.createElement(Box34, {
11477
11723
  minWidth: 3,
11478
11724
  flexShrink: 0
11479
- }, /* @__PURE__ */ React49.createElement(Text42, {
11725
+ }, /* @__PURE__ */ React50.createElement(Text43, {
11480
11726
  color: theme.text.secondary,
11481
11727
  dimColor: source.isRejected,
11482
11728
  strikethrough: source.isRejected
11483
- }, `${sourceIndex + 1}. `)), /* @__PURE__ */ React49.createElement(Box33, {
11729
+ }, `${sourceIndex + 1}. `)), /* @__PURE__ */ React50.createElement(Box34, {
11484
11730
  flexGrow: 1,
11485
11731
  overflow: "hidden"
11486
- }, filename ? /* @__PURE__ */ React49.createElement(Text42, {
11732
+ }, filename ? /* @__PURE__ */ React50.createElement(Text43, {
11487
11733
  color: theme.text.primary,
11488
11734
  wrap: "truncate-end",
11489
11735
  dimColor: source.isRejected,
11490
11736
  strikethrough: source.isRejected
11491
- }, filename) : /* @__PURE__ */ React49.createElement(StateBadge, {
11737
+ }, filename) : /* @__PURE__ */ React50.createElement(StateBadge, {
11492
11738
  source
11493
- })), sizeText !== "" && /* @__PURE__ */ React49.createElement(Box33, {
11739
+ })), sizeText !== "" && /* @__PURE__ */ React50.createElement(Box34, {
11494
11740
  minWidth: sizeText.length + 1,
11495
11741
  paddingLeft: 1,
11496
11742
  flexShrink: 0
11497
- }, /* @__PURE__ */ React49.createElement(Text42, {
11743
+ }, /* @__PURE__ */ React50.createElement(Text43, {
11498
11744
  color: theme.text.secondary
11499
- }, sizeText)), /* @__PURE__ */ React49.createElement(Box33, {
11745
+ }, sizeText)), /* @__PURE__ */ React50.createElement(Box34, {
11500
11746
  paddingLeft: 1,
11501
11747
  paddingRight: 1,
11502
11748
  flexShrink: 0
11503
- }, /* @__PURE__ */ React49.createElement(StateBadge, {
11749
+ }, /* @__PURE__ */ React50.createElement(StateBadge, {
11504
11750
  source
11505
11751
  })));
11506
11752
  }
@@ -11653,19 +11899,19 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11653
11899
  ]
11654
11900
  });
11655
11901
  if (sources.length === 0) {
11656
- return /* @__PURE__ */ React50.createElement(Box34, {
11902
+ return /* @__PURE__ */ React51.createElement(Box35, {
11657
11903
  flexDirection: "column",
11658
11904
  width,
11659
11905
  height,
11660
11906
  overflow: "hidden",
11661
11907
  paddingX: 1,
11662
11908
  paddingY: 1
11663
- }, /* @__PURE__ */ React50.createElement(Text43, {
11909
+ }, /* @__PURE__ */ React51.createElement(Text44, {
11664
11910
  color: theme.text.secondary,
11665
11911
  dimColor: true
11666
11912
  }, "No download sources yet"));
11667
11913
  }
11668
- return /* @__PURE__ */ React50.createElement(Box34, {
11914
+ return /* @__PURE__ */ React51.createElement(Box35, {
11669
11915
  flexDirection: "column",
11670
11916
  width,
11671
11917
  height,
@@ -11673,7 +11919,7 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11673
11919
  }, treeItems.map((item, i) => {
11674
11920
  if (item.type === "provider-header") {
11675
11921
  const addMargin = treeItems.slice(0, i).some((t) => t.type === "provider-header");
11676
- return /* @__PURE__ */ React50.createElement(ProviderHeader, {
11922
+ return /* @__PURE__ */ React51.createElement(ProviderHeader, {
11677
11923
  key: `ph-${i}`,
11678
11924
  label: item.label,
11679
11925
  color: item.color,
@@ -11681,12 +11927,12 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11681
11927
  });
11682
11928
  }
11683
11929
  if (item.type === "metadata-header") {
11684
- return /* @__PURE__ */ React50.createElement(MetadataHeader, {
11930
+ return /* @__PURE__ */ React51.createElement(MetadataHeader, {
11685
11931
  key: `mh-${i}`,
11686
11932
  source: item.source
11687
11933
  });
11688
11934
  }
11689
- return /* @__PURE__ */ React50.createElement(SourceFileRow, {
11935
+ return /* @__PURE__ */ React51.createElement(SourceFileRow, {
11690
11936
  key: `fr-${i}`,
11691
11937
  source: item.source,
11692
11938
  sourceIndex: item.sourceIndex,
@@ -11698,14 +11944,14 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11698
11944
  }, "DownloadSourceTree");
11699
11945
 
11700
11946
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DownloadSourceDetail.tsx
11701
- import React55, { useState as useState19, useEffect as useEffect19 } from "react";
11702
- import { Box as Box39, Text as Text48 } from "ink";
11947
+ import React56, { useState as useState20, useEffect as useEffect20 } from "react";
11948
+ import { Box as Box40, Text as Text49 } from "ink";
11703
11949
  import * as path13 from "path";
11704
11950
  import * as fs13 from "fs";
11705
11951
 
11706
11952
  // src/components/SecondaryPanel/DownloadPanel/PlaybackBar.tsx
11707
- import React51 from "react";
11708
- import { Box as Box35, Text as Text44 } from "ink";
11953
+ import React52 from "react";
11954
+ import { Box as Box36, Text as Text45 } from "ink";
11709
11955
  function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11710
11956
  const theme = useTheme();
11711
11957
  const active = isPlaying || isPaused;
@@ -11715,7 +11961,7 @@ function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11715
11961
  const ratio = active && durationMs > 0 ? Math.min(1, positionMs / durationMs) : 0;
11716
11962
  const filled = Math.floor(ratio * barW);
11717
11963
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barW - filled);
11718
- return /* @__PURE__ */ React51.createElement(Box35, {
11964
+ return /* @__PURE__ */ React52.createElement(Box36, {
11719
11965
  flexDirection: "column",
11720
11966
  minHeight: 1,
11721
11967
  height: 1,
@@ -11724,17 +11970,17 @@ function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11724
11970
  marginBottom: 1,
11725
11971
  marginX: 1,
11726
11972
  backgroundColor: theme.ui.background
11727
- }, /* @__PURE__ */ React51.createElement(Box35, {
11973
+ }, /* @__PURE__ */ React52.createElement(Box36, {
11728
11974
  flexDirection: "row",
11729
11975
  flexGrow: 1,
11730
11976
  overflow: "hidden"
11731
- }, /* @__PURE__ */ React51.createElement(Text44, {
11977
+ }, /* @__PURE__ */ React52.createElement(Text45, {
11732
11978
  color: isPlaying ? theme.ui.progressFill : theme.text.secondary,
11733
11979
  dimColor: !active
11734
- }, isPaused ? "\u23F8 " : "\u25B6 "), /* @__PURE__ */ React51.createElement(Text44, {
11980
+ }, isPaused ? "\u23F8 " : "\u25B6 "), /* @__PURE__ */ React52.createElement(Text45, {
11735
11981
  color: isPlaying ? theme.ui.progressFill : theme.text.secondary,
11736
11982
  dimColor: !active
11737
- }, bar), /* @__PURE__ */ React51.createElement(Text44, {
11983
+ }, bar), /* @__PURE__ */ React52.createElement(Text45, {
11738
11984
  color: theme.text.secondary,
11739
11985
  dimColor: !active
11740
11986
  }, timeStr)));
@@ -11742,55 +11988,55 @@ function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11742
11988
  __name(PlaybackBar, "PlaybackBar");
11743
11989
 
11744
11990
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DiffView.tsx
11745
- import React53 from "react";
11991
+ import React54 from "react";
11746
11992
  import * as path12 from "path";
11747
- import { Box as Box37, Text as Text46 } from "ink";
11993
+ import { Box as Box38, Text as Text47 } from "ink";
11748
11994
 
11749
11995
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DiffRow.tsx
11750
- import React52 from "react";
11751
- import { Box as Box36, Text as Text45 } from "ink";
11996
+ import React53 from "react";
11997
+ import { Box as Box37, Text as Text46 } from "ink";
11752
11998
  var LABEL_W2 = 11;
11753
11999
  function DiffRow({ label, left, right }) {
11754
12000
  const theme = useTheme();
11755
12001
  const changed = left !== right;
11756
- return /* @__PURE__ */ React52.createElement(Box36, {
12002
+ return /* @__PURE__ */ React53.createElement(Box37, {
11757
12003
  flexDirection: "row",
11758
12004
  paddingX: 1,
11759
12005
  height: 1,
11760
12006
  overflow: "hidden"
11761
- }, /* @__PURE__ */ React52.createElement(Box36, {
12007
+ }, /* @__PURE__ */ React53.createElement(Box37, {
11762
12008
  width: LABEL_W2,
11763
12009
  minWidth: LABEL_W2,
11764
12010
  flexShrink: 0
11765
- }, /* @__PURE__ */ React52.createElement(Text45, {
12011
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11766
12012
  color: theme.diff.changed,
11767
12013
  bold: true,
11768
12014
  wrap: "truncate-end"
11769
- }, label.toUpperCase().padEnd(LABEL_W2))), /* @__PURE__ */ React52.createElement(Box36, {
12015
+ }, label.toUpperCase().padEnd(LABEL_W2))), /* @__PURE__ */ React53.createElement(Box37, {
11770
12016
  flexGrow: 1,
11771
12017
  overflow: "hidden"
11772
- }, /* @__PURE__ */ React52.createElement(Text45, {
12018
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11773
12019
  color: changed ? theme.diff.base : theme.text.primary,
11774
12020
  dimColor: !changed,
11775
12021
  wrap: "truncate-end"
11776
- }, left)), /* @__PURE__ */ React52.createElement(Box36, {
12022
+ }, left)), /* @__PURE__ */ React53.createElement(Box37, {
11777
12023
  width: 1,
11778
12024
  minWidth: 1,
11779
12025
  flexShrink: 0
11780
- }, /* @__PURE__ */ React52.createElement(Text45, {
12026
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11781
12027
  color: theme.diff.base
11782
- }, "\u2502")), /* @__PURE__ */ React52.createElement(Box36, {
12028
+ }, "\u2502")), /* @__PURE__ */ React53.createElement(Box37, {
11783
12029
  flexGrow: 1,
11784
12030
  overflow: "hidden",
11785
12031
  paddingLeft: 1
11786
- }, /* @__PURE__ */ React52.createElement(Text45, {
12032
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11787
12033
  color: changed ? theme.diff.modified : theme.text.primary,
11788
12034
  wrap: "truncate-end"
11789
- }, right)), changed && /* @__PURE__ */ React52.createElement(Box36, {
12035
+ }, right)), changed && /* @__PURE__ */ React53.createElement(Box37, {
11790
12036
  width: 2,
11791
12037
  minWidth: 2,
11792
12038
  flexShrink: 0
11793
- }, /* @__PURE__ */ React52.createElement(Text45, {
12039
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11794
12040
  color: theme.diff.modified
11795
12041
  }, " \u2190")));
11796
12042
  }
@@ -11826,27 +12072,27 @@ function DiffView({ savedSource, pendingSource, compiled, outputDir, diffKind, i
11826
12072
  const innerW = width - 2;
11827
12073
  const oldProviderColor = getProviderColor(savedSource.provider);
11828
12074
  const newProviderColor = getProviderColor(pendingSource.provider);
11829
- return /* @__PURE__ */ React53.createElement(Box37, {
12075
+ return /* @__PURE__ */ React54.createElement(Box38, {
11830
12076
  flexDirection: "column",
11831
12077
  width,
11832
12078
  height,
11833
12079
  overflow: "hidden"
11834
- }, /* @__PURE__ */ React53.createElement(Box37, {
12080
+ }, /* @__PURE__ */ React54.createElement(Box38, {
11835
12081
  flexDirection: "row",
11836
12082
  paddingX: 1,
11837
12083
  height: 1,
11838
12084
  flexShrink: 0
11839
- }, /* @__PURE__ */ React53.createElement(Text46, {
12085
+ }, /* @__PURE__ */ React54.createElement(Text47, {
11840
12086
  color: theme.diff.base
11841
- }, "\u250C\u2500\u2500 "), /* @__PURE__ */ React53.createElement(Text46, {
12087
+ }, "\u250C\u2500\u2500 "), /* @__PURE__ */ React54.createElement(Text47, {
11842
12088
  color: oldProviderColor
11843
- }, oldProvider), /* @__PURE__ */ React53.createElement(Text46, {
12089
+ }, oldProvider), /* @__PURE__ */ React54.createElement(Text47, {
11844
12090
  color: theme.diff.base
11845
- }, " \u2192 "), /* @__PURE__ */ React53.createElement(Text46, {
12091
+ }, " \u2192 "), /* @__PURE__ */ React54.createElement(Text47, {
11846
12092
  color: newProviderColor
11847
- }, newProvider), /* @__PURE__ */ React53.createElement(Text46, {
12093
+ }, newProvider), /* @__PURE__ */ React54.createElement(Text47, {
11848
12094
  color: theme.diff.base
11849
- }, "\u2500".repeat(Math.max(0, innerW - oldProvider.length - newProvider.length - 10)), "\u2510")), /* @__PURE__ */ React53.createElement(Box37, {
12095
+ }, "\u2500".repeat(Math.max(0, innerW - oldProvider.length - newProvider.length - 10)), "\u2510")), /* @__PURE__ */ React54.createElement(Box38, {
11850
12096
  flexDirection: "column",
11851
12097
  flexGrow: 1,
11852
12098
  overflow: "hidden",
@@ -11854,86 +12100,86 @@ function DiffView({ savedSource, pendingSource, compiled, outputDir, diffKind, i
11854
12100
  borderColor: theme.diff.base,
11855
12101
  borderBackgroundColor: theme.ui.background,
11856
12102
  borderTop: false
11857
- }, /* @__PURE__ */ React53.createElement(Box37, {
12103
+ }, /* @__PURE__ */ React54.createElement(Box38, {
11858
12104
  paddingX: 1,
11859
12105
  height: 1,
11860
12106
  flexShrink: 0
11861
- }, /* @__PURE__ */ React53.createElement(Text46, {
12107
+ }, /* @__PURE__ */ React54.createElement(Text47, {
11862
12108
  color: theme.diff.base
11863
- }, "\u2500\u2500 File ", "\u2500".repeat(Math.max(0, innerW - 9)))), /* @__PURE__ */ React53.createElement(DiffRow, {
12109
+ }, "\u2500\u2500 File ", "\u2500".repeat(Math.max(0, innerW - 9)))), /* @__PURE__ */ React54.createElement(DiffRow, {
11864
12110
  label: "Provider",
11865
12111
  left: oldProvider,
11866
12112
  right: newProvider
11867
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12113
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11868
12114
  label: "Filename",
11869
12115
  left: oldFilename,
11870
12116
  right: newFilename
11871
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12117
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11872
12118
  label: "Directory",
11873
12119
  left: oldDir,
11874
12120
  right: newDir
11875
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12121
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11876
12122
  label: "Size",
11877
12123
  left: oldSize,
11878
12124
  right: newSize
11879
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12125
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11880
12126
  label: "Duration",
11881
12127
  left: oldDuration,
11882
12128
  right: newDuration
11883
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12129
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11884
12130
  label: "Saved",
11885
12131
  left: oldSavedAt,
11886
12132
  right: "(new)"
11887
- }), /* @__PURE__ */ React53.createElement(Box37, {
12133
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11888
12134
  height: 1,
11889
12135
  flexShrink: 0
11890
- }), /* @__PURE__ */ React53.createElement(Box37, {
12136
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11891
12137
  paddingX: 1,
11892
12138
  height: 1,
11893
12139
  flexShrink: 0
11894
- }, /* @__PURE__ */ React53.createElement(Text46, {
12140
+ }, /* @__PURE__ */ React54.createElement(Text47, {
11895
12141
  color: theme.diff.base
11896
- }, "\u2500\u2500 Metadata ", "\u2500".repeat(Math.max(0, innerW - 13)))), /* @__PURE__ */ React53.createElement(DiffRow, {
12142
+ }, "\u2500\u2500 Metadata ", "\u2500".repeat(Math.max(0, innerW - 13)))), /* @__PURE__ */ React54.createElement(DiffRow, {
11897
12143
  label: "Title",
11898
12144
  left: title,
11899
12145
  right: title
11900
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12146
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11901
12147
  label: "Artists",
11902
12148
  left: artists,
11903
12149
  right: artists
11904
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12150
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11905
12151
  label: "Album",
11906
12152
  left: album,
11907
12153
  right: album
11908
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12154
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11909
12155
  label: "Year",
11910
12156
  left: year,
11911
12157
  right: year
11912
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12158
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11913
12159
  label: "BPM",
11914
12160
  left: bpm,
11915
12161
  right: bpm
11916
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12162
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11917
12163
  label: "Key",
11918
12164
  left: key,
11919
12165
  right: key
11920
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12166
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11921
12167
  label: "Genres",
11922
12168
  left: genres,
11923
12169
  right: genres
11924
- }), /* @__PURE__ */ React53.createElement(Box37, {
12170
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11925
12171
  flexGrow: 1
11926
- }), /* @__PURE__ */ React53.createElement(Box37, {
12172
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11927
12173
  flexDirection: "row",
11928
12174
  paddingX: 1,
11929
12175
  height: 1,
11930
12176
  overflow: "hidden",
11931
12177
  flexShrink: 0
11932
- }, /* @__PURE__ */ React53.createElement(Hint, {
12178
+ }, /* @__PURE__ */ React54.createElement(Hint, {
11933
12179
  label: diffKind === "metadata-change" ? "Save changes" : "Switch source",
11934
12180
  shortcut: "Enter",
11935
12181
  dim: !isActive
11936
- }), /* @__PURE__ */ React53.createElement(Hint, {
12182
+ }), /* @__PURE__ */ React54.createElement(Hint, {
11937
12183
  label: "Cancel",
11938
12184
  shortcut: "Esc",
11939
12185
  dim: !isActive
@@ -11942,28 +12188,28 @@ function DiffView({ savedSource, pendingSource, compiled, outputDir, diffKind, i
11942
12188
  __name(DiffView, "DiffView");
11943
12189
 
11944
12190
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DetailRow.tsx
11945
- import React54 from "react";
11946
- import { Box as Box38, Text as Text47 } from "ink";
12191
+ import React55 from "react";
12192
+ import { Box as Box39, Text as Text48 } from "ink";
11947
12193
  var LABEL_W3 = 11;
11948
12194
  function DetailRow({ label, value, valueColor, dim }) {
11949
12195
  const theme = useTheme();
11950
- return /* @__PURE__ */ React54.createElement(Box38, {
12196
+ return /* @__PURE__ */ React55.createElement(Box39, {
11951
12197
  flexDirection: "row",
11952
12198
  paddingX: 1,
11953
12199
  height: 1,
11954
12200
  flexShrink: 0
11955
- }, /* @__PURE__ */ React54.createElement(Box38, {
12201
+ }, /* @__PURE__ */ React55.createElement(Box39, {
11956
12202
  width: LABEL_W3,
11957
12203
  minWidth: LABEL_W3,
11958
12204
  flexShrink: 0,
11959
12205
  marginRight: 1
11960
- }, /* @__PURE__ */ React54.createElement(Text47, {
12206
+ }, /* @__PURE__ */ React55.createElement(Text48, {
11961
12207
  color: theme.ui.border,
11962
12208
  bold: true,
11963
12209
  wrap: "truncate-end"
11964
- }, label.toUpperCase().padEnd(LABEL_W3))), /* @__PURE__ */ React54.createElement(Box38, {
12210
+ }, label.toUpperCase().padEnd(LABEL_W3))), /* @__PURE__ */ React55.createElement(Box39, {
11965
12211
  flexGrow: 1
11966
- }, /* @__PURE__ */ React54.createElement(Text47, {
12212
+ }, /* @__PURE__ */ React55.createElement(Text48, {
11967
12213
  color: valueColor ?? theme.text.primary,
11968
12214
  dimColor: dim || value === "\u2014",
11969
12215
  wrap: "truncate-end"
@@ -11986,23 +12232,23 @@ function SectionDivider({ label, width }) {
11986
12232
  const theme = useTheme();
11987
12233
  const innerW = width - 2;
11988
12234
  const dashes = Math.max(0, innerW - label.length - 3);
11989
- return /* @__PURE__ */ React55.createElement(Box39, {
12235
+ return /* @__PURE__ */ React56.createElement(Box40, {
11990
12236
  paddingX: 1,
11991
12237
  height: 1,
11992
12238
  flexDirection: "column"
11993
- }, /* @__PURE__ */ React55.createElement(Text48, {
12239
+ }, /* @__PURE__ */ React56.createElement(Text49, {
11994
12240
  color: theme.text.secondary
11995
12241
  }, "\u2500\u2500 ", label, " " + "\u2500".repeat(dashes)));
11996
12242
  }
11997
12243
  __name(SectionDivider, "SectionDivider");
11998
12244
  var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compiled, outputDir, isDiffMode, diffKind, pendingSource, isActive, isSaving, width, height, onInnerFocusSwitch, onConfirmDiff, onCancelDiff, onRelocateFile, onSave }) => {
11999
12245
  const theme = useTheme();
12000
- const [isPlaying, setIsPlaying] = useState19(false);
12001
- const [isPaused, setIsPaused] = useState19(false);
12002
- const [positionMs, setPositionMs] = useState19(0);
12003
- const [durationMs, setDurationMs] = useState19(0);
12246
+ const [isPlaying, setIsPlaying] = useState20(false);
12247
+ const [isPaused, setIsPaused] = useState20(false);
12248
+ const [positionMs, setPositionMs] = useState20(0);
12249
+ const [durationMs, setDurationMs] = useState20(0);
12004
12250
  const currentFilePath = source?.localFile?.path ?? null;
12005
- useEffect19(() => {
12251
+ useEffect20(() => {
12006
12252
  const player = getInstance();
12007
12253
  function syncFromStatus(status) {
12008
12254
  const isThisFile = status.filePath === currentFilePath;
@@ -12041,7 +12287,7 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12041
12287
  }, [
12042
12288
  currentFilePath
12043
12289
  ]);
12044
- useEffect19(() => {
12290
+ useEffect20(() => {
12045
12291
  return () => {
12046
12292
  const player = getInstance();
12047
12293
  if (player.getStatus().filePath === currentFilePath) {
@@ -12198,7 +12444,7 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12198
12444
  ]
12199
12445
  });
12200
12446
  if (isDiffMode && savedSource && pendingSource) {
12201
- return /* @__PURE__ */ React55.createElement(DiffView, {
12447
+ return /* @__PURE__ */ React56.createElement(DiffView, {
12202
12448
  savedSource,
12203
12449
  pendingSource,
12204
12450
  compiled,
@@ -12238,25 +12484,25 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12238
12484
  trackType,
12239
12485
  source.track.id
12240
12486
  ].filter(Boolean);
12241
- return /* @__PURE__ */ React55.createElement(Box39, {
12487
+ return /* @__PURE__ */ React56.createElement(Box40, {
12242
12488
  flexDirection: "column",
12243
12489
  width,
12244
12490
  height,
12245
12491
  overflow: "hidden"
12246
- }, /* @__PURE__ */ React55.createElement(Box39, {
12492
+ }, /* @__PURE__ */ React56.createElement(Box40, {
12247
12493
  flexDirection: "column",
12248
12494
  height: 1,
12249
12495
  flexShrink: 0,
12250
12496
  overflow: "hidden"
12251
- }, /* @__PURE__ */ React55.createElement(Box39, {
12497
+ }, /* @__PURE__ */ React56.createElement(Box40, {
12252
12498
  flexDirection: "row"
12253
- }, /* @__PURE__ */ React55.createElement(Text48, {
12499
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12254
12500
  color: borderColor
12255
- }, borderLeft), /* @__PURE__ */ React55.createElement(Text48, {
12501
+ }, borderLeft), /* @__PURE__ */ React56.createElement(Text49, {
12256
12502
  color: headerColor
12257
- }, headerLabel), /* @__PURE__ */ React55.createElement(Text48, {
12503
+ }, headerLabel), /* @__PURE__ */ React56.createElement(Text49, {
12258
12504
  color: borderColor
12259
- }, borderRight))), /* @__PURE__ */ React55.createElement(Box39, {
12505
+ }, borderRight))), /* @__PURE__ */ React56.createElement(Box40, {
12260
12506
  flexDirection: "column",
12261
12507
  flexGrow: 1,
12262
12508
  overflow: "hidden",
@@ -12264,120 +12510,120 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12264
12510
  borderColor,
12265
12511
  borderBackgroundColor: theme.ui.background,
12266
12512
  borderTop: false
12267
- }, /* @__PURE__ */ React55.createElement(Box39, {
12513
+ }, /* @__PURE__ */ React56.createElement(Box40, {
12268
12514
  flexDirection: "column",
12269
12515
  flexGrow: 1,
12270
12516
  overflow: "hidden"
12271
- }, outputFileExists && /* @__PURE__ */ React55.createElement(Box39, {
12517
+ }, outputFileExists && /* @__PURE__ */ React56.createElement(Box40, {
12272
12518
  paddingX: 1,
12273
12519
  paddingBottom: 1,
12274
12520
  flexShrink: 0
12275
- }, /* @__PURE__ */ React55.createElement(Text48, {
12521
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12276
12522
  color: theme.status.warning,
12277
12523
  bold: true
12278
- }, "\u25B3 Warning: A file already exists! Please check before overwriting the file.")), canPlay && /* @__PURE__ */ React55.createElement(PlaybackBar, {
12524
+ }, "\u25B3 Warning: A file already exists! Please check before overwriting the file.")), canPlay && /* @__PURE__ */ React56.createElement(PlaybackBar, {
12279
12525
  positionMs,
12280
12526
  durationMs: displayDurationMs,
12281
12527
  width: innerW,
12282
12528
  isPlaying,
12283
12529
  isPaused
12284
- }), /* @__PURE__ */ React55.createElement(Box39, {
12530
+ }), /* @__PURE__ */ React56.createElement(Box40, {
12285
12531
  paddingX: 1,
12286
12532
  paddingBottom: 1,
12287
12533
  height: 1,
12288
12534
  flexShrink: 0,
12289
12535
  flexDirection: "row",
12290
12536
  overflow: "hidden"
12291
- }, /* @__PURE__ */ React55.createElement(Text48, {
12537
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12292
12538
  color: theme.text.secondary
12293
- }, "SOURCE "), sourceParts.map((part, idx) => /* @__PURE__ */ React55.createElement(React55.Fragment, {
12539
+ }, "SOURCE "), sourceParts.map((part, idx) => /* @__PURE__ */ React56.createElement(React56.Fragment, {
12294
12540
  key: idx
12295
- }, idx > 0 && /* @__PURE__ */ React55.createElement(Text48, {
12541
+ }, idx > 0 && /* @__PURE__ */ React56.createElement(Text49, {
12296
12542
  color: theme.text.secondary
12297
- }, " > "), /* @__PURE__ */ React55.createElement(Text48, {
12543
+ }, " > "), /* @__PURE__ */ React56.createElement(Text49, {
12298
12544
  color: dlProvider.color
12299
- }, part)))), fileNotFound ? /* @__PURE__ */ React55.createElement(Box39, {
12545
+ }, part)))), fileNotFound ? /* @__PURE__ */ React56.createElement(Box40, {
12300
12546
  flexDirection: "column",
12301
12547
  paddingX: 1,
12302
12548
  paddingY: 1
12303
- }, /* @__PURE__ */ React55.createElement(Text48, {
12549
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12304
12550
  color: theme.status.warning
12305
- }, "\u26A0 File not found"), /* @__PURE__ */ React55.createElement(Text48, {
12551
+ }, " \u25B3 File not found"), /* @__PURE__ */ React56.createElement(Text49, {
12306
12552
  color: theme.text.secondary,
12307
12553
  dimColor: true,
12308
12554
  wrap: "truncate-end"
12309
- }, "Last known: ", source.localFile?.path ?? "\u2014"), /* @__PURE__ */ React55.createElement(Box39, {
12555
+ }, "Last known: ", source.localFile?.path ?? "\u2014"), /* @__PURE__ */ React56.createElement(Box40, {
12310
12556
  marginTop: 1
12311
- }, /* @__PURE__ */ React55.createElement(Text48, {
12557
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12312
12558
  color: theme.text.active,
12313
12559
  bold: true
12314
- }, "[Ctrl+F]"), /* @__PURE__ */ React55.createElement(Text48, {
12560
+ }, "[Ctrl+F]"), /* @__PURE__ */ React56.createElement(Text49, {
12315
12561
  color: theme.text.hint
12316
- }, " Relocate file"))) : /* @__PURE__ */ React55.createElement(Box39, {
12562
+ }, " Relocate file"))) : /* @__PURE__ */ React56.createElement(Box40, {
12317
12563
  flexDirection: "column",
12318
12564
  flexShrink: 0
12319
- }, compiled && /* @__PURE__ */ React55.createElement(React55.Fragment, null, /* @__PURE__ */ React55.createElement(DetailRow, {
12565
+ }, compiled && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(DetailRow, {
12320
12566
  label: "Title",
12321
12567
  value: compiled.trackName || "\u2014"
12322
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12568
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12323
12569
  label: "Artists",
12324
12570
  value: compiled.artists.map((a) => a.name).join(", ") || "\u2014"
12325
- }), compiled.album && /* @__PURE__ */ React55.createElement(DetailRow, {
12571
+ }), compiled.album && /* @__PURE__ */ React56.createElement(DetailRow, {
12326
12572
  label: "Album",
12327
12573
  value: compiled.album.albumName
12328
- }), compiled.year != null && /* @__PURE__ */ React55.createElement(DetailRow, {
12574
+ }), compiled.year != null && /* @__PURE__ */ React56.createElement(DetailRow, {
12329
12575
  label: "Year",
12330
12576
  value: String(compiled.year)
12331
- }), compiled.bpm != null && /* @__PURE__ */ React55.createElement(DetailRow, {
12577
+ }), compiled.bpm != null && /* @__PURE__ */ React56.createElement(DetailRow, {
12332
12578
  label: "BPM",
12333
12579
  value: String(compiled.bpm)
12334
- }), compiled.key && /* @__PURE__ */ React55.createElement(DetailRow, {
12580
+ }), compiled.key && /* @__PURE__ */ React56.createElement(DetailRow, {
12335
12581
  label: "Key",
12336
12582
  value: compiled.key
12337
- }), compiled.genres && compiled.genres.length > 0 && /* @__PURE__ */ React55.createElement(DetailRow, {
12583
+ }), compiled.genres && compiled.genres.length > 0 && /* @__PURE__ */ React56.createElement(DetailRow, {
12338
12584
  label: "Genres",
12339
12585
  value: compiled.genres.join(", ")
12340
- })), /* @__PURE__ */ React55.createElement(DetailRow, {
12586
+ })), /* @__PURE__ */ React56.createElement(DetailRow, {
12341
12587
  label: "File",
12342
12588
  value: filename
12343
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12589
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12344
12590
  label: "Format",
12345
12591
  value: formatLabel
12346
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12592
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12347
12593
  label: "Size",
12348
12594
  value: sizeText
12349
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12595
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12350
12596
  label: "Duration",
12351
12597
  value: formatDuration3(sourceDurationMs)
12352
- }), compiled && /* @__PURE__ */ React55.createElement(DetailRow, {
12598
+ }), compiled && /* @__PURE__ */ React56.createElement(DetailRow, {
12353
12599
  label: "Output",
12354
12600
  value: path13.join(outputDir, computeOutputFilename(compiled)),
12355
12601
  valueColor: theme.text.secondary
12356
- })), !fileNotFound && /* @__PURE__ */ React55.createElement(React55.Fragment, null, /* @__PURE__ */ React55.createElement(Box39, {
12602
+ })), !fileNotFound && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Box40, {
12357
12603
  height: 1,
12358
12604
  flexDirection: "column"
12359
- }), /* @__PURE__ */ React55.createElement(SectionDivider, {
12605
+ }), /* @__PURE__ */ React56.createElement(SectionDivider, {
12360
12606
  label: "Embedded Tags",
12361
12607
  width: innerW
12362
- }), PRIORITY_TAGS.map((tag) => /* @__PURE__ */ React55.createElement(DetailRow, {
12608
+ }), PRIORITY_TAGS.map((tag) => /* @__PURE__ */ React56.createElement(DetailRow, {
12363
12609
  key: tag,
12364
12610
  label: tag,
12365
12611
  value: tagValue(embeddedTags, tag)
12366
- })), otherTagKeys.map((tag) => /* @__PURE__ */ React55.createElement(DetailRow, {
12612
+ })), otherTagKeys.map((tag) => /* @__PURE__ */ React56.createElement(DetailRow, {
12367
12613
  key: tag,
12368
12614
  label: tag,
12369
12615
  value: tagValue(embeddedTags, tag)
12370
- }))), source.savedFile && /* @__PURE__ */ React55.createElement(Box39, {
12616
+ }))), source.savedFile && /* @__PURE__ */ React56.createElement(Box40, {
12371
12617
  flexDirection: "column",
12372
12618
  overflow: "hidden",
12373
12619
  marginTop: 1
12374
- }, /* @__PURE__ */ React55.createElement(SectionDivider, {
12620
+ }, /* @__PURE__ */ React56.createElement(SectionDivider, {
12375
12621
  label: "Saved",
12376
12622
  width: innerW
12377
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12623
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12378
12624
  label: "Path",
12379
12625
  value: source.savedFile.path
12380
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12626
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12381
12627
  label: "Saved at",
12382
12628
  value: formatDate(source.savedFile.savedAt)
12383
12629
  })))));
@@ -12390,16 +12636,16 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12390
12636
  const { focusState, setSourcesInnerFocus } = useFocusContext();
12391
12637
  const { sourcesPanel } = focusState.secondaryPanel;
12392
12638
  const { innerFocus } = sourcesPanel;
12393
- const [selectedSourceIndex, setSelectedSourceIndex] = useState20(0);
12639
+ const [selectedSourceIndex, setSelectedSourceIndex] = useState21(0);
12394
12640
  const isPanelActive = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab === "downloadSources";
12395
12641
  const typedTask = selectedTask;
12396
- const [snapshot, setSnapshot] = useState20(() => typedTask?.get());
12397
- const [prevTypedTask, setPrevTypedTask] = useState20(typedTask);
12642
+ const [snapshot, setSnapshot] = useState21(() => typedTask?.get());
12643
+ const [prevTypedTask, setPrevTypedTask] = useState21(typedTask);
12398
12644
  if (prevTypedTask !== typedTask) {
12399
12645
  setPrevTypedTask(typedTask);
12400
12646
  setSnapshot(typedTask?.get());
12401
12647
  }
12402
- useEffect20(() => {
12648
+ useEffect21(() => {
12403
12649
  if (!typedTask) return;
12404
12650
  return typedTask.subscribe((t) => {
12405
12651
  setSnapshot(t.get());
@@ -12412,12 +12658,12 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12412
12658
  const compiled = snapshot?.attributes ? computeCompiledMetadata(snapshot.attributes.metadataGroups, snapshot.attributes.metadataOverride) : null;
12413
12659
  const { outputDir } = getSaveSettings();
12414
12660
  const downloadNavIndex = downloadSources.length === 0 ? -1 : Math.max(0, Math.min(selectedSourceIndex < 0 ? 0 : selectedSourceIndex, downloadSources.length - 1));
12415
- const [splitRatio, setSplitRatio] = useState20(0.6);
12661
+ const [splitRatio, setSplitRatio] = useState21(0.6);
12416
12662
  const leftWidth = Math.floor(width * splitRatio) - 4;
12417
12663
  const rightWidth = width - leftWidth - 4;
12418
12664
  const listHeight = height - HINT_BAR_HEIGHT;
12419
- const [isDiffMode, setIsDiffMode] = useState20(false);
12420
- const [pendingSourceIndex, setPendingSourceIndex] = useState20(null);
12665
+ const [isDiffMode, setIsDiffMode] = useState21(false);
12666
+ const [pendingSourceIndex, setPendingSourceIndex] = useState21(null);
12421
12667
  const previewFilename = compiled ? computeOutputFilename(compiled) : null;
12422
12668
  const savedFilename = savedSource?.savedFile ? path14.basename(savedSource.savedFile.path) : null;
12423
12669
  const hasMetadataChange = previewFilename != null && savedFilename != null && previewFilename !== savedFilename;
@@ -12531,7 +12777,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12531
12777
  }
12532
12778
  __name(handleRelocateFile, "handleRelocateFile");
12533
12779
  const selectedSource = downloadNavIndex >= 0 ? downloadSources[downloadNavIndex] ?? null : null;
12534
- return /* @__PURE__ */ React56.createElement(Box40, {
12780
+ return /* @__PURE__ */ React57.createElement(Box41, {
12535
12781
  flexDirection: "column",
12536
12782
  height,
12537
12783
  overflow: "hidden",
@@ -12541,11 +12787,11 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12541
12787
  borderTop: false,
12542
12788
  borderBottom: false,
12543
12789
  paddingRight: 1
12544
- }, /* @__PURE__ */ React56.createElement(Box40, {
12790
+ }, /* @__PURE__ */ React57.createElement(Box41, {
12545
12791
  flexDirection: "row",
12546
12792
  flexGrow: 1,
12547
12793
  overflow: "hidden"
12548
- }, /* @__PURE__ */ React56.createElement(DownloadSourceTree, {
12794
+ }, /* @__PURE__ */ React57.createElement(DownloadSourceTree, {
12549
12795
  sources: downloadSources,
12550
12796
  selectedSourceIndex: downloadNavIndex,
12551
12797
  isActive: isPanelActive && innerFocus === "list",
@@ -12555,7 +12801,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12555
12801
  onRequestSelect: handleRequestSelect,
12556
12802
  onRejectSource: handleRejectSource,
12557
12803
  onInnerFocusSwitch: /* @__PURE__ */ __name(() => setSourcesInnerFocus("detail"), "onInnerFocusSwitch")
12558
- }), /* @__PURE__ */ React56.createElement(DownloadSourceDetail, {
12804
+ }), /* @__PURE__ */ React57.createElement(DownloadSourceDetail, {
12559
12805
  source: selectedSource,
12560
12806
  savedSource,
12561
12807
  compiled,
@@ -12572,7 +12818,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12572
12818
  onCancelDiff: handleCancelDiff,
12573
12819
  onRelocateFile: handleRelocateFile,
12574
12820
  onSave: handleSave
12575
- })), /* @__PURE__ */ React56.createElement(DynamicHintBar, {
12821
+ })), /* @__PURE__ */ React57.createElement(DynamicHintBar, {
12576
12822
  width: width - 2,
12577
12823
  isActive: isPanelActive && innerFocus === "list"
12578
12824
  }));
@@ -12586,11 +12832,11 @@ var SecondaryPanel = /* @__PURE__ */ __name(({ tasks, width }) => {
12586
12832
  const contentHeight = Math.max(1, height - 1);
12587
12833
  const selectedTask = tasks[focusState.taskList.selectedTaskIndex] ?? null;
12588
12834
  const activeTabKey = subTab === "metadataSources" ? "3" : subTab === "downloadSources" ? "4" : "5";
12589
- return /* @__PURE__ */ React57.createElement(Box41, {
12835
+ return /* @__PURE__ */ React58.createElement(Box42, {
12590
12836
  flexDirection: "column",
12591
12837
  height,
12592
12838
  overflow: "hidden"
12593
- }, /* @__PURE__ */ React57.createElement(TabBar, {
12839
+ }, /* @__PURE__ */ React58.createElement(TabBar, {
12594
12840
  width,
12595
12841
  tabs: [
12596
12842
  {
@@ -12607,26 +12853,26 @@ var SecondaryPanel = /* @__PURE__ */ __name(({ tasks, width }) => {
12607
12853
  }
12608
12854
  ],
12609
12855
  activeTabKey
12610
- }), subTab === "metadataSources" && /* @__PURE__ */ React57.createElement(MetadataPanel, {
12856
+ }), subTab === "metadataSources" && /* @__PURE__ */ React58.createElement(MetadataPanel, {
12611
12857
  selectedTask,
12612
12858
  width,
12613
12859
  height: contentHeight
12614
- }), subTab === "downloadSources" && /* @__PURE__ */ React57.createElement(DownloadPanel, {
12860
+ }), subTab === "downloadSources" && /* @__PURE__ */ React58.createElement(DownloadPanel, {
12615
12861
  selectedTask,
12616
12862
  width,
12617
12863
  height: contentHeight
12618
- }), subTab === "logs" && /* @__PURE__ */ React57.createElement(LogPanel, {
12864
+ }), subTab === "logs" && /* @__PURE__ */ React58.createElement(LogPanel, {
12619
12865
  tasks,
12620
12866
  height: contentHeight
12621
12867
  }));
12622
12868
  }, "SecondaryPanel");
12623
12869
 
12624
12870
  // src/components/ShortcutDispatcher.tsx
12625
- import React59 from "react";
12871
+ import React60 from "react";
12626
12872
  import { useInput } from "ink";
12627
12873
 
12628
12874
  // src/contexts/ImportActionsContext.tsx
12629
- import React58, { createContext as createContext4, useContext as useContext4 } from "react";
12875
+ import React59, { createContext as createContext4, useContext as useContext4 } from "react";
12630
12876
  var ImportActionsContext = /* @__PURE__ */ createContext4(null);
12631
12877
  var useImportActions = /* @__PURE__ */ __name(() => {
12632
12878
  const ctx = useContext4(ImportActionsContext);
@@ -12634,7 +12880,7 @@ var useImportActions = /* @__PURE__ */ __name(() => {
12634
12880
  return ctx;
12635
12881
  }, "useImportActions");
12636
12882
  var ImportActionsProvider = /* @__PURE__ */ __name(({ children, openImportFlow }) => {
12637
- return /* @__PURE__ */ React58.createElement(ImportActionsContext.Provider, {
12883
+ return /* @__PURE__ */ React59.createElement(ImportActionsContext.Provider, {
12638
12884
  value: {
12639
12885
  openImportFlow
12640
12886
  }
@@ -12655,11 +12901,11 @@ var ShortcutDispatcher = /* @__PURE__ */ __name(() => {
12655
12901
  }, "ShortcutDispatcher");
12656
12902
 
12657
12903
  // src/components/InputRouter.tsx
12658
- import React60 from "react";
12904
+ import React61 from "react";
12659
12905
  var InputRouter = /* @__PURE__ */ __name(({ tasks, flow }) => {
12660
12906
  const { focusState, handleTabPress, switchMode, setPrimaryMode, setSecondaryTab } = useFocusContext();
12661
12907
  const { openImportFlow } = useImportActions();
12662
- const isMainScreen = focusState.activeWindow !== "importModal" && focusState.activeWindow !== "settingsModal" && focusState.activeWindow !== "setupWizardModal" && focusState.activeWindow !== "welcomeModal";
12908
+ const isMainScreen = focusState.activeWindow !== "importModal" && focusState.activeWindow !== "settingsModal" && focusState.activeWindow !== "setupWizardModal" && focusState.activeWindow !== "welcomeModal" && focusState.activeWindow !== "updateModal";
12663
12909
  const isDetailFocused = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab !== "logs" && focusState.secondaryPanel.sourcesPanel.innerFocus === "detail";
12664
12910
  useShortcuts({
12665
12911
  id: "global",
@@ -12741,7 +12987,7 @@ var InputRouter = /* @__PURE__ */ __name(({ tasks, flow }) => {
12741
12987
  }, "InputRouter");
12742
12988
 
12743
12989
  // src/components/ImportModal/useImportFlow.ts
12744
- import { useState as useState21, useCallback as useCallback5 } from "react";
12990
+ import { useState as useState22, useCallback as useCallback6 } from "react";
12745
12991
 
12746
12992
  // src/components/ImportModal/clipboard.ts
12747
12993
  import { execSync } from "child_process";
@@ -12780,8 +13026,8 @@ __name(detectUrls, "detectUrls");
12780
13026
  // src/components/ImportModal/useImportFlow.ts
12781
13027
  function useImportFlow(currentFlow) {
12782
13028
  const { focusState } = useFocusContext();
12783
- const [pendingImport, setPendingImport] = useState21(null);
12784
- const openImportFlow = useCallback5((text) => {
13029
+ const [pendingImport, setPendingImport] = useState22(null);
13030
+ const openImportFlow = useCallback6((text) => {
12785
13031
  if (focusState.activeWindow === "prompt") return;
12786
13032
  const handle = /* @__PURE__ */ __name((raw) => {
12787
13033
  const urls = detectUrls(raw);
@@ -12819,7 +13065,7 @@ function useImportFlow(currentFlow) {
12819
13065
  }, [
12820
13066
  focusState.activeWindow
12821
13067
  ]);
12822
- const handleImportConfirm = useCallback5(({ fetchMetadata, download }) => {
13068
+ const handleImportConfirm = useCallback6(({ fetchMetadata, download }) => {
12823
13069
  if (!pendingImport || !currentFlow) {
12824
13070
  setPendingImport(null);
12825
13071
  return;
@@ -12835,7 +13081,7 @@ function useImportFlow(currentFlow) {
12835
13081
  pendingImport,
12836
13082
  currentFlow
12837
13083
  ]);
12838
- const handleImportCancel = useCallback5(() => {
13084
+ const handleImportCancel = useCallback6(() => {
12839
13085
  setPendingImport(null);
12840
13086
  }, []);
12841
13087
  return {
@@ -12849,84 +13095,186 @@ function useImportFlow(currentFlow) {
12849
13095
  __name(useImportFlow, "useImportFlow");
12850
13096
 
12851
13097
  // src/components/AppInner.tsx
12852
- var AppInner = /* @__PURE__ */ __name(({ tasks, filteredTasks, toolbarButtons, columns, currentFlow, orchestrator, setActiveFlowId, terminalHeight, terminalWidth }) => {
13098
+ var AppInner = /* @__PURE__ */ __name(({ tasks, filteredTasks, toolbarButtons, columns, currentFlow, orchestrator, setActiveFlowId, terminalHeight, terminalWidth, updateInfo }) => {
12853
13099
  const theme = useTheme();
12854
13100
  const { pendingImport, openImportFlow, handleImportConfirm, handleImportCancel } = useImportFlow(currentFlow);
12855
- return /* @__PURE__ */ React61.createElement(ImportActionsProvider, {
13101
+ return /* @__PURE__ */ React62.createElement(ImportActionsProvider, {
12856
13102
  openImportFlow
12857
- }, /* @__PURE__ */ React61.createElement(ShortcutDispatcher, null), /* @__PURE__ */ React61.createElement(InputRouter, {
13103
+ }, /* @__PURE__ */ React62.createElement(ShortcutDispatcher, null), /* @__PURE__ */ React62.createElement(InputRouter, {
12858
13104
  tasks: filteredTasks,
12859
13105
  flow: currentFlow
12860
- }), /* @__PURE__ */ React61.createElement(Box42, {
13106
+ }), /* @__PURE__ */ React62.createElement(Box43, {
12861
13107
  flexDirection: "column",
12862
13108
  height: terminalHeight,
12863
13109
  backgroundColor: theme.ui.background
12864
- }, currentFlow && /* @__PURE__ */ React61.createElement(Toolbar, {
13110
+ }, currentFlow && /* @__PURE__ */ React62.createElement(Toolbar, {
12865
13111
  buttons: toolbarButtons,
12866
13112
  width: terminalWidth,
12867
13113
  flows: orchestrator.getAllFlows(),
12868
13114
  onFlowChange: setActiveFlowId,
12869
13115
  flow: currentFlow,
12870
- orchestrator
12871
- }), currentFlow && /* @__PURE__ */ React61.createElement(TaskListPanel, {
13116
+ orchestrator,
13117
+ updateInfo
13118
+ }), currentFlow && /* @__PURE__ */ React62.createElement(TaskListPanel, {
12872
13119
  columns,
12873
13120
  tasks: filteredTasks,
12874
13121
  width: terminalWidth,
12875
13122
  flow: currentFlow
12876
- }), /* @__PURE__ */ React61.createElement(SecondaryPanel, {
13123
+ }), /* @__PURE__ */ React62.createElement(SecondaryPanel, {
12877
13124
  tasks: filteredTasks,
12878
13125
  width: terminalWidth,
12879
13126
  flow: currentFlow
12880
- }), /* @__PURE__ */ React61.createElement(Separator, {
13127
+ }), /* @__PURE__ */ React62.createElement(Separator, {
12881
13128
  width: terminalWidth
12882
- }), /* @__PURE__ */ React61.createElement(Footer, null), /* @__PURE__ */ React61.createElement(PromptModal, {
13129
+ }), /* @__PURE__ */ React62.createElement(Footer, null), /* @__PURE__ */ React62.createElement(PromptModal, {
12883
13130
  tasks,
12884
13131
  terminalHeight,
12885
13132
  terminalWidth
12886
- }), /* @__PURE__ */ React61.createElement(ImportModal, {
13133
+ }), /* @__PURE__ */ React62.createElement(ImportModal, {
12887
13134
  pendingImport,
12888
13135
  terminalHeight,
12889
13136
  terminalWidth,
12890
13137
  onConfirm: handleImportConfirm,
12891
13138
  onCancel: handleImportCancel
12892
- }), /* @__PURE__ */ React61.createElement(SettingsModal, {
13139
+ }), /* @__PURE__ */ React62.createElement(SettingsModal, {
12893
13140
  terminalHeight,
12894
13141
  terminalWidth,
12895
13142
  currentFlow
12896
- }), /* @__PURE__ */ React61.createElement(SetupWizardModal, {
13143
+ }), /* @__PURE__ */ React62.createElement(SetupWizardModal, {
12897
13144
  tasks,
12898
13145
  terminalHeight,
12899
13146
  terminalWidth
12900
- }), /* @__PURE__ */ React61.createElement(WelcomeModal, {
13147
+ }), /* @__PURE__ */ React62.createElement(WelcomeModal, {
13148
+ terminalHeight,
13149
+ terminalWidth
13150
+ }), updateInfo && /* @__PURE__ */ React62.createElement(UpdateModal, {
13151
+ latestVersion: updateInfo.latestVersion,
13152
+ releaseUrl: updateInfo.releaseUrl,
12901
13153
  terminalHeight,
12902
13154
  terminalWidth
12903
13155
  })));
12904
13156
  }, "AppInner");
12905
13157
 
13158
+ // src/components/Toolbar/useSettingsButton.ts
13159
+ var useSettingsButton = /* @__PURE__ */ __name(() => {
13160
+ const theme = useTheme();
13161
+ const { switchWindow } = useFocusContext();
13162
+ return {
13163
+ label: "Settings",
13164
+ icon: "\u26ED",
13165
+ color: theme.action.neutral,
13166
+ enabled: true,
13167
+ onPress: /* @__PURE__ */ __name(() => switchWindow("settingsModal"), "onPress")
13168
+ };
13169
+ }, "useSettingsButton");
13170
+
13171
+ // src/components/Toolbar/useExitButton.ts
13172
+ var LEAVE_ALT_SCREEN_COMMAND = "\x1B[?1049l";
13173
+ var useExitButton = /* @__PURE__ */ __name(() => {
13174
+ const theme = useTheme();
13175
+ return {
13176
+ label: "Exit",
13177
+ icon: "\u21B3",
13178
+ color: theme.action.destructive,
13179
+ enabled: true,
13180
+ onPress: /* @__PURE__ */ __name(() => {
13181
+ process.stdout.write(LEAVE_ALT_SCREEN_COMMAND);
13182
+ console.info("Saving cache before exit\u2026");
13183
+ cache.save();
13184
+ process.exit(0);
13185
+ }, "onPress")
13186
+ };
13187
+ }, "useExitButton");
13188
+
13189
+ // src/updater/updateChecker.ts
13190
+ function isNewer(local, remote) {
13191
+ const parse = /* @__PURE__ */ __name((v) => v.replace(/^v/, "").split(".").map(Number), "parse");
13192
+ const [lMaj = 0, lMin = 0, lPatch = 0] = parse(local);
13193
+ const [rMaj = 0, rMin = 0, rPatch = 0] = parse(remote);
13194
+ if (rMaj !== lMaj) return rMaj > lMaj;
13195
+ if (rMin !== lMin) return rMin > lMin;
13196
+ return rPatch > lPatch;
13197
+ }
13198
+ __name(isNewer, "isNewer");
13199
+ async function checkForUpdate() {
13200
+ try {
13201
+ const res = await fetch("https://api.github.com/repos/Tetraxel/goblin-malin/releases/latest", {
13202
+ headers: {
13203
+ "User-Agent": "goblin-malin-updater"
13204
+ },
13205
+ signal: AbortSignal.timeout(8e3)
13206
+ });
13207
+ if (!res.ok) return null;
13208
+ const data = await res.json();
13209
+ const exeAsset = IS_SEA ? data.assets.find((a) => a.name.endsWith(".exe")) : void 0;
13210
+ return {
13211
+ hasUpdate: isNewer(APP_VERSION, data.tag_name),
13212
+ latestVersion: data.tag_name.replace(/^v/, ""),
13213
+ releaseUrl: data.html_url,
13214
+ downloadUrl: exeAsset?.browser_download_url ?? null
13215
+ };
13216
+ } catch {
13217
+ return null;
13218
+ }
13219
+ }
13220
+ __name(checkForUpdate, "checkForUpdate");
13221
+
12906
13222
  // src/components/App.tsx
12907
13223
  var App = /* @__PURE__ */ __name(() => {
12908
- useEffect21(() => {
13224
+ useEffect22(() => {
12909
13225
  const initWav = getAssetPath("sounds", "init.wav");
12910
13226
  const player = getInstance();
12911
13227
  player.setVolume(50).then(() => player.play(initWav)).catch(() => {
12912
13228
  });
12913
13229
  }, []);
12914
- const [tasks, setTasks] = useState22([]);
13230
+ const [tasks, setTasks] = useState23([]);
12915
13231
  const { height: terminalHeight, width: terminalWidth } = useScreenSize();
12916
13232
  const orchestrator = FlowOrchestrator.getInstance();
12917
- const [activeFlowId, setActiveFlowId] = useState22(() => {
13233
+ const [activeFlowId, setActiveFlowId] = useState23(() => {
12918
13234
  orchestrator.registerFlow(MusicDownloadFlow, true);
12919
13235
  return orchestrator.getEnabledFlows()?.[0].id;
12920
13236
  });
12921
- const [toolbarButtons, setToolbarButtons] = useState22([]);
12922
- const [columns, setColumns] = useState22([]);
13237
+ const [toolbarButtons, setToolbarButtons] = useState23([]);
13238
+ const [columns, setColumns] = useState23([]);
12923
13239
  const currentFlow = activeFlowId ? orchestrator.getFlow(activeFlowId) : void 0;
12924
- useEffect21(() => {
13240
+ const [updateInfo, setUpdateInfo] = useState23(null);
13241
+ const [pendingUpdate, setPendingUpdate] = useState23(null);
13242
+ const isOrchestratorIdle = useCallback7((orch) => orch.getTasksInProgress().length === 0, []);
13243
+ useEffect22(() => {
13244
+ const settings = SettingsStore.getInstance().getAppSettings();
13245
+ if (!settings.general.checkForUpdates) return;
13246
+ checkForUpdate().then((info) => {
13247
+ if (!info?.hasUpdate) return;
13248
+ if (isOrchestratorIdle(orchestrator)) {
13249
+ setUpdateInfo(info);
13250
+ } else {
13251
+ setPendingUpdate(info);
13252
+ }
13253
+ });
13254
+ }, []);
13255
+ useEffect22(() => {
13256
+ if (!pendingUpdate) return;
13257
+ return orchestrator.subscribe((orch) => {
13258
+ if (isOrchestratorIdle(orch)) {
13259
+ setUpdateInfo(pendingUpdate);
13260
+ setPendingUpdate(null);
13261
+ }
13262
+ });
13263
+ }, [
13264
+ pendingUpdate,
13265
+ orchestrator,
13266
+ isOrchestratorIdle
13267
+ ]);
13268
+ useEffect22(() => {
12925
13269
  if (!currentFlow) return;
12926
13270
  globalLogger.debug(`Active flow changed: ${currentFlow?.displayName}`);
12927
13271
  const unsubscribe = currentFlow.subscribe((_updatedFlow) => {
12928
- globalLogger.debug(`flow state changed, updating UI...`);
12929
- setToolbarButtons(currentFlow.getToolbarButtons() ?? []);
13272
+ const buttons = [
13273
+ ...currentFlow.getToolbarButtons() ?? [],
13274
+ useSettingsButton,
13275
+ useExitButton
13276
+ ];
13277
+ setToolbarButtons(buttons);
12930
13278
  setColumns(currentFlow.getColumns() ?? []);
12931
13279
  });
12932
13280
  return unsubscribe;
@@ -12934,7 +13282,7 @@ var App = /* @__PURE__ */ __name(() => {
12934
13282
  currentFlow,
12935
13283
  currentFlow?.id
12936
13284
  ]);
12937
- useEffect21(() => {
13285
+ useEffect22(() => {
12938
13286
  const unsubscribe = orchestrator.subscribe((orchestrator2) => {
12939
13287
  setTasks(orchestrator2.getTasks());
12940
13288
  });
@@ -12947,11 +13295,11 @@ var App = /* @__PURE__ */ __name(() => {
12947
13295
  tasks,
12948
13296
  activeFlowId
12949
13297
  ]);
12950
- return /* @__PURE__ */ React62.createElement(ThemeProvider, null, /* @__PURE__ */ React62.createElement(ShortcutRegistryProvider, null, /* @__PURE__ */ React62.createElement(FocusProvider, {
12951
- toolbarButtonCount: toolbarButtons.length,
13298
+ return /* @__PURE__ */ React63.createElement(ThemeProvider, null, /* @__PURE__ */ React63.createElement(ShortcutRegistryProvider, null, /* @__PURE__ */ React63.createElement(FocusProvider, {
13299
+ toolbarButtonCount: toolbarButtons.length + (updateInfo ? 1 : 0),
12952
13300
  taskCount: filteredTasks.length,
12953
13301
  taskColumnCount: columns.length
12954
- }, /* @__PURE__ */ React62.createElement(ToolbarActionsProvider, null, /* @__PURE__ */ React62.createElement(AppInner, {
13302
+ }, /* @__PURE__ */ React63.createElement(ToolbarActionsProvider, null, /* @__PURE__ */ React63.createElement(AppInner, {
12955
13303
  tasks,
12956
13304
  filteredTasks,
12957
13305
  toolbarButtons,
@@ -12960,17 +13308,18 @@ var App = /* @__PURE__ */ __name(() => {
12960
13308
  orchestrator,
12961
13309
  setActiveFlowId,
12962
13310
  terminalHeight,
12963
- terminalWidth
13311
+ terminalWidth,
13312
+ updateInfo
12964
13313
  })))));
12965
13314
  }, "App");
12966
13315
 
12967
13316
  // src/components/FullScreenBox.tsx
12968
- import React63 from "react";
12969
- import { Box as Box43 } from "ink";
13317
+ import React64 from "react";
13318
+ import { Box as Box44 } from "ink";
12970
13319
  import { forwardRef } from "react";
12971
13320
  var FullScreenBox = /* @__PURE__ */ forwardRef(/* @__PURE__ */ __name(function FullScreenBox2(props, ref) {
12972
13321
  const { height, width } = useScreenSize();
12973
- return /* @__PURE__ */ React63.createElement(Box43, {
13322
+ return /* @__PURE__ */ React64.createElement(Box44, {
12974
13323
  ref,
12975
13324
  height,
12976
13325
  width,
@@ -13003,7 +13352,7 @@ var withFullScreen = /* @__PURE__ */ __name((node, options) => {
13003
13352
  return {
13004
13353
  instance,
13005
13354
  start: /* @__PURE__ */ __name(async () => {
13006
- instance.rerender(/* @__PURE__ */ React64.createElement(FullScreenBox, null, node));
13355
+ instance.rerender(/* @__PURE__ */ React65.createElement(FullScreenBox, null, node));
13007
13356
  }, "start"),
13008
13357
  waitUntilExit
13009
13358
  };
@@ -13026,7 +13375,7 @@ function start() {
13026
13375
  } catch {
13027
13376
  }
13028
13377
  process2.stdout.write("\x1B[?1049h");
13029
- const instance = render(/* @__PURE__ */ React64.createElement(App, null), {
13378
+ const instance = render(/* @__PURE__ */ React65.createElement(App, null), {
13030
13379
  patchConsole: true,
13031
13380
  maxFps: 60,
13032
13381
  exitOnCtrlC: false