goblin-malin 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-ZQ7B4MTU.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-FLO2QNLY.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,18 @@ 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(PrimaryModeTabBar, {
9011
9261
  width,
9012
9262
  splitPos: splitPositions[0] ?? 0
9013
9263
  }));
@@ -9015,7 +9265,7 @@ var Toolbar = /* @__PURE__ */ __name(({ buttons, width, flows, onFlowChange, flo
9015
9265
  var PrimaryModeTabBar = /* @__PURE__ */ __name(({ width, splitPos }) => {
9016
9266
  const { focusState } = useFocusContext();
9017
9267
  const { primaryMode } = focusState.secondaryPanel;
9018
- return /* @__PURE__ */ React31.createElement(TabBar, {
9268
+ return /* @__PURE__ */ React32.createElement(TabBar, {
9019
9269
  width,
9020
9270
  tabs: [
9021
9271
  {
@@ -9033,12 +9283,12 @@ var PrimaryModeTabBar = /* @__PURE__ */ __name(({ width, splitPos }) => {
9033
9283
  }, "PrimaryModeTabBar");
9034
9284
 
9035
9285
  // src/components/TaskListPanel/TaskListPanel.tsx
9036
- import React35, { useMemo as useMemo4 } from "react";
9037
- import { Box as Box21, Text as Text30 } from "ink";
9286
+ import React36, { useMemo as useMemo4 } from "react";
9287
+ import { Box as Box22, Text as Text31 } from "ink";
9038
9288
 
9039
9289
  // 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";
9290
+ import React33, { useEffect as useEffect15, useState as useState15 } from "react";
9291
+ import { Box as Box19, Text as Text28 } from "ink";
9042
9292
  var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9043
9293
  const theme = useTheme();
9044
9294
  const { focusState } = useFocusContext();
@@ -9046,8 +9296,8 @@ var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9046
9296
  const selectedIndex = focusState.taskList.selectedTaskIndex;
9047
9297
  const multiCount = focusState.taskList.selectedTaskIds.size;
9048
9298
  const selectedTask = tasks[selectedIndex];
9049
- const [, setTaskVersion] = useState14(0);
9050
- useEffect14(() => {
9299
+ const [, setTaskVersion] = useState15(0);
9300
+ useEffect15(() => {
9051
9301
  if (!selectedTask) return;
9052
9302
  return selectedTask.subscribe(() => setTaskVersion((v) => v + 1));
9053
9303
  }, [
@@ -9065,7 +9315,7 @@ var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9065
9315
  return true;
9066
9316
  }), "filterRow");
9067
9317
  if (!isTaskListActive) return null;
9068
- return /* @__PURE__ */ React32.createElement(Box18, {
9318
+ return /* @__PURE__ */ React33.createElement(Box19, {
9069
9319
  paddingX: 1,
9070
9320
  height: rowCount,
9071
9321
  overflow: "hidden",
@@ -9074,56 +9324,56 @@ var ActionBar = /* @__PURE__ */ __name(({ tasks, flow }) => {
9074
9324
  alignItems: "flex-start"
9075
9325
  }, bar ? bar.rows.map((row, i) => {
9076
9326
  const visible = filterRow(row);
9077
- return /* @__PURE__ */ React32.createElement(Box18, {
9327
+ return /* @__PURE__ */ React33.createElement(Box19, {
9078
9328
  key: i,
9079
9329
  height: 1,
9080
9330
  flexDirection: "row",
9081
9331
  alignItems: "flex-start",
9082
9332
  flexShrink: 0
9083
- }, row.text && /* @__PURE__ */ React32.createElement(Box18, {
9333
+ }, row.text && /* @__PURE__ */ React33.createElement(Box19, {
9084
9334
  marginRight: 1,
9085
9335
  flexShrink: 0
9086
- }, /* @__PURE__ */ React32.createElement(Text27, {
9336
+ }, /* @__PURE__ */ React33.createElement(Text28, {
9087
9337
  color: row.textColor ?? theme.text.primary,
9088
9338
  bold: true
9089
- }, row.text)), /* @__PURE__ */ React32.createElement(Box18, {
9339
+ }, row.text)), /* @__PURE__ */ React33.createElement(Box19, {
9090
9340
  marginRight: 1,
9091
9341
  flexShrink: 0
9092
- }, /* @__PURE__ */ React32.createElement(Text27, {
9342
+ }, /* @__PURE__ */ React33.createElement(Text28, {
9093
9343
  color: theme.text.hint
9094
- }, "\u203A")), visible.length > 0 ? visible.map((action, j) => /* @__PURE__ */ React32.createElement(Hint, {
9344
+ }, "\u203A")), visible.length > 0 ? visible.map((action, j) => /* @__PURE__ */ React33.createElement(Hint, {
9095
9345
  key: j,
9096
9346
  label: action.label,
9097
9347
  shortcut: getShortcutLiteral(action.shortcuts)
9098
- })) : /* @__PURE__ */ React32.createElement(Text27, {
9348
+ })) : /* @__PURE__ */ React33.createElement(Text28, {
9099
9349
  color: theme.text.hint,
9100
9350
  italic: true
9101
9351
  }, "\u2014"));
9102
- }) : /* @__PURE__ */ React32.createElement(Box18, {
9352
+ }) : /* @__PURE__ */ React33.createElement(Box19, {
9103
9353
  height: 1,
9104
9354
  flexDirection: "row",
9105
9355
  alignItems: "flex-start"
9106
- }, /* @__PURE__ */ React32.createElement(Text27, {
9356
+ }, /* @__PURE__ */ React33.createElement(Text28, {
9107
9357
  color: theme.text.hint,
9108
9358
  italic: true
9109
9359
  }, "No contextual actions available")));
9110
9360
  }, "ActionBar");
9111
9361
 
9112
9362
  // 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";
9363
+ import React34, { useReducer as useReducer2, useEffect as useEffect16 } from "react";
9364
+ import { Box as Box20, Text as Text29 } from "ink";
9115
9365
  var HintChip = /* @__PURE__ */ __name(({ entry, dim }) => {
9116
9366
  const theme = useTheme();
9117
- return /* @__PURE__ */ React33.createElement(Box19, {
9367
+ return /* @__PURE__ */ React34.createElement(Box20, {
9118
9368
  marginRight: 1,
9119
9369
  flexShrink: 0
9120
- }, /* @__PURE__ */ React33.createElement(Text28, {
9370
+ }, /* @__PURE__ */ React34.createElement(Text29, {
9121
9371
  color: theme.text.active,
9122
9372
  dimColor: dim,
9123
9373
  bold: true
9124
9374
  }, "[", getShortcutLiteral([
9125
9375
  entry.shortcut
9126
- ]), "]"), /* @__PURE__ */ React33.createElement(Text28, {
9376
+ ]), "]"), /* @__PURE__ */ React34.createElement(Text29, {
9127
9377
  color: theme.text.hint,
9128
9378
  dimColor: dim
9129
9379
  }, " ", entry.label));
@@ -9131,25 +9381,25 @@ var HintChip = /* @__PURE__ */ __name(({ entry, dim }) => {
9131
9381
  var HintLine = /* @__PURE__ */ __name(({ line, contextShortcuts, dim, width }) => {
9132
9382
  const theme = useTheme();
9133
9383
  const matchedShortcuts = line.shortcutIds.map((sid) => contextShortcuts.find((s) => s.id === sid)).filter((s) => s !== void 0);
9134
- return /* @__PURE__ */ React33.createElement(Box19, {
9384
+ return /* @__PURE__ */ React34.createElement(Box20, {
9135
9385
  flexDirection: "row",
9136
9386
  width,
9137
9387
  overflow: "hidden",
9138
9388
  flexShrink: 0
9139
- }, /* @__PURE__ */ React33.createElement(Box19, {
9389
+ }, /* @__PURE__ */ React34.createElement(Box20, {
9140
9390
  marginRight: 1,
9141
9391
  flexShrink: 0
9142
- }, line.left.type === "node" ? line.left.renderNode(dim ?? false) : /* @__PURE__ */ React33.createElement(Text28, {
9392
+ }, line.left.type === "node" ? line.left.renderNode(dim ?? false) : /* @__PURE__ */ React34.createElement(Text29, {
9143
9393
  color: line.left.color ?? theme.text.active,
9144
9394
  dimColor: dim,
9145
9395
  bold: line.left.bold ?? true
9146
- }, line.left.value)), /* @__PURE__ */ React33.createElement(Box19, {
9396
+ }, line.left.value)), /* @__PURE__ */ React34.createElement(Box20, {
9147
9397
  marginRight: 1,
9148
9398
  flexShrink: 0
9149
- }, /* @__PURE__ */ React33.createElement(Text28, {
9399
+ }, /* @__PURE__ */ React34.createElement(Text29, {
9150
9400
  color: theme.text.active,
9151
9401
  dimColor: dim
9152
- }, "\u203A")), matchedShortcuts.map((entry) => /* @__PURE__ */ React33.createElement(HintChip, {
9402
+ }, "\u203A")), matchedShortcuts.map((entry) => /* @__PURE__ */ React34.createElement(HintChip, {
9153
9403
  key: entry.id,
9154
9404
  entry,
9155
9405
  dim
@@ -9157,13 +9407,13 @@ var HintLine = /* @__PURE__ */ __name(({ line, contextShortcuts, dim, width }) =
9157
9407
  }, "HintLine");
9158
9408
  var DynamicHintBar = /* @__PURE__ */ __name(({ width, isActive = true }) => {
9159
9409
  const [, forceUpdate] = useReducer2((x) => x + 1, 0);
9160
- useEffect15(() => {
9410
+ useEffect16(() => {
9161
9411
  return shortcutRegistry.subscribe(forceUpdate);
9162
9412
  }, []);
9163
9413
  if (!isActive) return null;
9164
9414
  const contexts = shortcutRegistry.getActiveHintContexts();
9165
9415
  if (contexts.length === 0) return null;
9166
- return /* @__PURE__ */ React33.createElement(Box19, {
9416
+ return /* @__PURE__ */ React34.createElement(Box20, {
9167
9417
  flexDirection: "column",
9168
9418
  width,
9169
9419
  overflow: "hidden",
@@ -9171,7 +9421,7 @@ var DynamicHintBar = /* @__PURE__ */ __name(({ width, isActive = true }) => {
9171
9421
  alignItems: "flex-start",
9172
9422
  justifyContent: "flex-start",
9173
9423
  flexShrink: 0
9174
- }, contexts.map((ctx) => ctx.lines.map((line) => /* @__PURE__ */ React33.createElement(HintLine, {
9424
+ }, contexts.map((ctx) => ctx.lines.map((line) => /* @__PURE__ */ React34.createElement(HintLine, {
9175
9425
  key: `${ctx.contextId}-${line.id}`,
9176
9426
  line,
9177
9427
  contextShortcuts: ctx.shortcuts,
@@ -9181,14 +9431,14 @@ var DynamicHintBar = /* @__PURE__ */ __name(({ width, isActive = true }) => {
9181
9431
  }, "DynamicHintBar");
9182
9432
 
9183
9433
  // src/components/TaskListPanel/TaskRow.tsx
9184
- import React34 from "react";
9185
- import { Box as Box20, Text as Text29 } from "ink";
9434
+ import React35 from "react";
9435
+ import { Box as Box21, Text as Text30 } from "ink";
9186
9436
 
9187
9437
  // src/hooks/useTask.ts
9188
- import { useEffect as useEffect16, useState as useState15 } from "react";
9438
+ import { useEffect as useEffect17, useState as useState16 } from "react";
9189
9439
  var useTask = /* @__PURE__ */ __name((task) => {
9190
- const [taskSnapshot, setTaskSnapshot] = useState15(() => task.get());
9191
- useEffect16(() => {
9440
+ const [taskSnapshot, setTaskSnapshot] = useState16(() => task.get());
9441
+ useEffect17(() => {
9192
9442
  const unsubscribe = task.subscribe((updatedTask) => {
9193
9443
  setTaskSnapshot(updatedTask.get());
9194
9444
  });
@@ -9200,26 +9450,26 @@ var useTask = /* @__PURE__ */ __name((task) => {
9200
9450
  }, "useTask");
9201
9451
 
9202
9452
  // src/components/TaskListPanel/TaskRow.tsx
9203
- var TaskRow = /* @__PURE__ */ React34.memo(/* @__PURE__ */ __name(function TaskRow2({ taskReference, isActive, isHighlighted, isMultiSelected, selectedColumnIndex, columns, flow }) {
9453
+ var TaskRow = /* @__PURE__ */ React35.memo(/* @__PURE__ */ __name(function TaskRow2({ taskReference, isActive, isHighlighted, isMultiSelected, selectedColumnIndex, columns, flow }) {
9204
9454
  const theme = useTheme();
9205
9455
  const task = useTask(taskReference);
9206
9456
  const isActiveIndicator = isActive ? "\u261B" : " ";
9207
9457
  const indicator = isActiveIndicator + (isMultiSelected ? "\u2713" : " ");
9208
9458
  const backgroundColor = isActive ? theme.ui.rowActiveDimmedBackground : isHighlighted ? theme.ui.rowBackground : void 0;
9209
- return /* @__PURE__ */ React34.createElement(Box20, {
9459
+ return /* @__PURE__ */ React35.createElement(Box21, {
9210
9460
  key: task.id,
9211
9461
  paddingX: 1,
9212
9462
  overflowY: "hidden",
9213
9463
  backgroundColor
9214
- }, /* @__PURE__ */ React34.createElement(Box20, {
9464
+ }, /* @__PURE__ */ React35.createElement(Box21, {
9215
9465
  width: 2,
9216
9466
  flexShrink: 0
9217
- }, /* @__PURE__ */ React34.createElement(Text29, {
9467
+ }, /* @__PURE__ */ React35.createElement(Text30, {
9218
9468
  color: isActive ? "white" : isMultiSelected ? "cyan" : "white"
9219
9469
  }, indicator)), columns.map((column, index) => {
9220
9470
  const CellComponent = column.component;
9221
9471
  const isCellActive = isActive && selectedColumnIndex === index;
9222
- return /* @__PURE__ */ React34.createElement(Box20, {
9472
+ return /* @__PURE__ */ React35.createElement(Box21, {
9223
9473
  key: `${CellComponent.name}-${column.label}-${index}`,
9224
9474
  width: column.width,
9225
9475
  minWidth: column.width,
@@ -9230,7 +9480,7 @@ var TaskRow = /* @__PURE__ */ React34.memo(/* @__PURE__ */ __name(function TaskR
9230
9480
  paddingX: 1,
9231
9481
  backgroundColor: isCellActive ? theme.ui.rowActiveBackground : void 0,
9232
9482
  overflow: "hidden"
9233
- }, /* @__PURE__ */ React34.createElement(CellComponent, {
9483
+ }, /* @__PURE__ */ React35.createElement(CellComponent, {
9234
9484
  task,
9235
9485
  taskReference,
9236
9486
  width: column.width,
@@ -9661,12 +9911,6 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9661
9911
  newAdjacentWidths[c.id] = c.width + shares[i];
9662
9912
  });
9663
9913
  }
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
9914
  const fixedWidth = calculatedColumns.filter((_, i) => columns[i]?.resizable === false).reduce((s, c) => s + c.width, 0);
9671
9915
  const resizableWidth = Math.max(1, availableWidth - fixedWidth);
9672
9916
  const allRatios = {};
@@ -9677,7 +9921,7 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9677
9921
  });
9678
9922
  flow.setColumnRatios(allRatios);
9679
9923
  });
9680
- return /* @__PURE__ */ React35.createElement(Box21, {
9924
+ return /* @__PURE__ */ React36.createElement(Box22, {
9681
9925
  borderStyle: "single",
9682
9926
  borderColor: theme.ui.border,
9683
9927
  borderBackgroundColor: theme.ui.background,
@@ -9687,19 +9931,19 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9687
9931
  borderBottom: false,
9688
9932
  height: fullHeight,
9689
9933
  flexGrow: 1
9690
- }, /* @__PURE__ */ React35.createElement(Box21, {
9934
+ }, /* @__PURE__ */ React36.createElement(Box22, {
9691
9935
  flexDirection: "row",
9692
9936
  paddingX: 1,
9693
9937
  height: 1,
9694
9938
  overflow: "hidden",
9695
9939
  flexShrink: 0
9696
- }, /* @__PURE__ */ React35.createElement(Box21, {
9940
+ }, /* @__PURE__ */ React36.createElement(Box22, {
9697
9941
  width: 2,
9698
9942
  height: 1,
9699
9943
  flexShrink: 0
9700
9944
  }), calculatedColumns.map((column, index) => {
9701
9945
  const isActive = isWindowActive && focusState.taskList.isHeaderFocused && focusState.taskList.selectedColumnIndex === index;
9702
- return /* @__PURE__ */ React35.createElement(Box21, {
9946
+ return /* @__PURE__ */ React36.createElement(Box22, {
9703
9947
  key: `header-${column.label}-${index}`,
9704
9948
  width: column.width,
9705
9949
  minWidth: column.width,
@@ -9709,19 +9953,19 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9709
9953
  overflow: "hidden",
9710
9954
  flexShrink: 0,
9711
9955
  backgroundColor: isActive ? theme.ui.rowActiveBackground : void 0
9712
- }, /* @__PURE__ */ React35.createElement(Text30, {
9956
+ }, /* @__PURE__ */ React36.createElement(Text31, {
9713
9957
  bold: true,
9714
9958
  color: isActive ? "white" : column.color || "cyan"
9715
9959
  }, column.label));
9716
- })), /* @__PURE__ */ React35.createElement(Box21, {
9960
+ })), /* @__PURE__ */ React36.createElement(Box22, {
9717
9961
  flexDirection: "column",
9718
9962
  height,
9719
9963
  overflow: "hidden",
9720
9964
  flexGrow: 1
9721
- }, tasks.length === 0 ? /* @__PURE__ */ React35.createElement(Box21, {
9965
+ }, tasks.length === 0 ? /* @__PURE__ */ React36.createElement(Box22, {
9722
9966
  paddingX: 4,
9723
9967
  overflow: "hidden"
9724
- }, /* @__PURE__ */ React35.createElement(Text30, {
9968
+ }, /* @__PURE__ */ React36.createElement(Text31, {
9725
9969
  italic: true,
9726
9970
  color: "gray"
9727
9971
  }, "Press Ctrl+V to import music URLs")) : tasks.slice(offset, offset + height).map((task, index) => {
@@ -9730,7 +9974,7 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9730
9974
  const isRowActive = isWindowActive && !focusState.taskList.isHeaderFocused && selectedIndex === visibleIndex;
9731
9975
  const selectedColumnIndex = isRowActive ? focusState.taskList.selectedColumnIndex : -1;
9732
9976
  const isMultiSelected = focusState.taskList.selectedTaskIds.has(task.getId());
9733
- return /* @__PURE__ */ React35.createElement(TaskRow, {
9977
+ return /* @__PURE__ */ React36.createElement(TaskRow, {
9734
9978
  key: task.getId(),
9735
9979
  taskReference: task,
9736
9980
  isHighlighted: isRowHighlighted,
@@ -9740,22 +9984,22 @@ var TaskListPanel = /* @__PURE__ */ __name(({ columns, tasks, width, flow }) =>
9740
9984
  columns: calculatedColumns,
9741
9985
  flow
9742
9986
  });
9743
- })), isWindowActive && focusState.taskList.isHeaderFocused ? /* @__PURE__ */ React35.createElement(DynamicHintBar, {
9987
+ })), isWindowActive && focusState.taskList.isHeaderFocused ? /* @__PURE__ */ React36.createElement(DynamicHintBar, {
9744
9988
  width: width - 2,
9745
9989
  isActive: true
9746
- }) : /* @__PURE__ */ React35.createElement(ActionBar, {
9990
+ }) : /* @__PURE__ */ React36.createElement(ActionBar, {
9747
9991
  tasks,
9748
9992
  flow
9749
9993
  }));
9750
9994
  }, "TaskListPanel");
9751
9995
 
9752
9996
  // src/components/SecondaryPanel/SecondaryPanel.tsx
9753
- import React57 from "react";
9754
- import { Box as Box41 } from "ink";
9997
+ import React58 from "react";
9998
+ import { Box as Box42 } from "ink";
9755
9999
 
9756
10000
  // 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";
10001
+ import React37, { useEffect as useEffect18, useState as useState17 } from "react";
10002
+ import { Box as Box23, Text as Text32 } from "ink";
9759
10003
  import { inspect } from "util";
9760
10004
  function formatDetails(details) {
9761
10005
  if (!details || Object.keys(details).length === 0) {
@@ -9785,9 +10029,9 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9785
10029
  const { focusState } = useFocusContext();
9786
10030
  const height = heightProp ?? focusState.layout.secondaryPanelHeight;
9787
10031
  const selectedTask = focusState.activeWindow === "taskList" ? tasks?.[focusState.taskList.selectedTaskIndex] : null;
9788
- const [logs, setLogs] = useState16([]);
9789
- const [scrollOffset, setScrollOffset] = useState16(0);
9790
- useEffect17(() => {
10032
+ const [logs, setLogs] = useState17([]);
10033
+ const [scrollOffset, setScrollOffset] = useState17(0);
10034
+ useEffect18(() => {
9791
10035
  const unsubscribe = inkTransport.subscribe((incomingLogs) => {
9792
10036
  const normalized = incomingLogs;
9793
10037
  setLogs((prevLogs) => [
@@ -9798,8 +10042,8 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9798
10042
  return unsubscribe;
9799
10043
  }, []);
9800
10044
  const isActive = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab === "logs";
9801
- const [prevSelectedTask, setPrevSelectedTask] = useState16(selectedTask);
9802
- const [prevIsActive, setPrevIsActive] = useState16(isActive);
10045
+ const [prevSelectedTask, setPrevSelectedTask] = useState17(selectedTask);
10046
+ const [prevIsActive, setPrevIsActive] = useState17(isActive);
9803
10047
  if (prevSelectedTask !== selectedTask) {
9804
10048
  setPrevSelectedTask(selectedTask);
9805
10049
  setScrollOffset(0);
@@ -9856,7 +10100,7 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9856
10100
  const logRows = logRowsNoTop - (showTopIndicator ? 1 : 0);
9857
10101
  const visibleStart = Math.max(0, (visibleEnd ?? filteredLogs.length) - logRows);
9858
10102
  const visibleLogs = filteredLogs.slice(visibleStart, visibleEnd);
9859
- return /* @__PURE__ */ React36.createElement(Box22, {
10103
+ return /* @__PURE__ */ React37.createElement(Box23, {
9860
10104
  flexDirection: "row",
9861
10105
  borderStyle: "single",
9862
10106
  borderColor: theme.ui.border,
@@ -9866,7 +10110,7 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9866
10110
  height,
9867
10111
  flexGrow: 1,
9868
10112
  overflow: "hidden"
9869
- }, /* @__PURE__ */ React36.createElement(Box22, {
10113
+ }, /* @__PURE__ */ React37.createElement(Box23, {
9870
10114
  flexDirection: "column",
9871
10115
  alignSelf: "flex-end",
9872
10116
  alignContent: "flex-end",
@@ -9874,33 +10118,33 @@ var LogPanel = /* @__PURE__ */ __name(({ tasks, height: heightProp }) => {
9874
10118
  overflow: "hidden",
9875
10119
  flexGrow: 1,
9876
10120
  flexShrink: 0
9877
- }, showTopIndicator && /* @__PURE__ */ React36.createElement(Box22, {
10121
+ }, showTopIndicator && /* @__PURE__ */ React37.createElement(Box23, {
9878
10122
  paddingX: 1,
9879
10123
  flexShrink: 0
9880
- }, /* @__PURE__ */ React36.createElement(Text31, {
10124
+ }, /* @__PURE__ */ React37.createElement(Text32, {
9881
10125
  color: theme.ui.border,
9882
10126
  dimColor: true
9883
- }, "\u2191 ", visibleStart, " more above")), visibleLogs.map((log) => /* @__PURE__ */ React36.createElement(Box22, {
10127
+ }, "\u2191 ", visibleStart, " more above")), visibleLogs.map((log) => /* @__PURE__ */ React37.createElement(Box23, {
9884
10128
  key: log.id,
9885
10129
  paddingX: 1,
9886
10130
  height: 1,
9887
10131
  overflow: "hidden",
9888
10132
  flexGrow: 1,
9889
10133
  flexShrink: 0
9890
- }, /* @__PURE__ */ React36.createElement(Text31, {
10134
+ }, /* @__PURE__ */ React37.createElement(Text32, {
9891
10135
  wrap: "truncate-end"
9892
- }, getLogString(log)))), showBottomIndicator && /* @__PURE__ */ React36.createElement(Box22, {
10136
+ }, getLogString(log)))), showBottomIndicator && /* @__PURE__ */ React37.createElement(Box23, {
9893
10137
  paddingX: 1,
9894
10138
  flexShrink: 0
9895
- }, /* @__PURE__ */ React36.createElement(Text31, {
10139
+ }, /* @__PURE__ */ React37.createElement(Text32, {
9896
10140
  color: theme.ui.border,
9897
10141
  dimColor: true
9898
10142
  }, "\u2193 ", clampedOffset, " more below"))));
9899
10143
  }, "LogPanel");
9900
10144
 
9901
10145
  // src/components/SecondaryPanel/MetadataPanel/MetadataPanel.tsx
9902
- import React45, { useState as useState18, useEffect as useEffect18 } from "react";
9903
- import { Box as Box30 } from "ink";
10146
+ import React46, { useState as useState19, useEffect as useEffect19 } from "react";
10147
+ import { Box as Box31 } from "ink";
9904
10148
 
9905
10149
  // src/flows/musicDownloadFlow/utils/metadataFields.ts
9906
10150
  function formatDuration(ms) {
@@ -10018,13 +10262,13 @@ var FIELDS = [
10018
10262
  var navigableFields = FIELDS.filter((f) => f.editable);
10019
10263
 
10020
10264
  // src/components/SecondaryPanel/MetadataPanel/MetadataSourceList.tsx
10021
- import React42 from "react";
10022
- import { Box as Box27, Text as Text36 } from "ink";
10265
+ import React43 from "react";
10266
+ import { Box as Box28, Text as Text37 } from "ink";
10023
10267
  import Spinner2 from "ink-spinner";
10024
10268
 
10025
10269
  // src/components/SecondaryPanel/MetadataPanel/useSourceListInput.ts
10026
- import React37 from "react";
10027
- import open2 from "open";
10270
+ import React38 from "react";
10271
+ import open3 from "open";
10028
10272
  import clipboard2 from "clipboardy";
10029
10273
  function toOpenableUri2(url) {
10030
10274
  const m = url.match(/open\.spotify\.com\/(track|album|artist|playlist)\/([A-Za-z0-9]+)/);
@@ -10101,7 +10345,7 @@ function useSourceListInput({ groups, sortedGroups, cursor, isActive, onCursorCh
10101
10345
  id: "sourceList.line.uri",
10102
10346
  left: {
10103
10347
  type: "node",
10104
- renderNode: /* @__PURE__ */ __name((dimmed) => React37.createElement(Uri, {
10348
+ renderNode: /* @__PURE__ */ __name((dimmed) => React38.createElement(Uri, {
10105
10349
  uri,
10106
10350
  platform: focusedResult.metadata.platform,
10107
10351
  dimmed,
@@ -10329,7 +10573,7 @@ function useSourceListInput({ groups, sortedGroups, cursor, isActive, onCursorCh
10329
10573
  handler: /* @__PURE__ */ __name(() => {
10330
10574
  if (cursor.type !== "result") return;
10331
10575
  const url = sortedGroups[cursor.groupIndex]?.results[cursor.resultIndex]?.metadata.url;
10332
- if (url) open2(toOpenableUri2(url)).catch(() => {
10576
+ if (url) open3(toOpenableUri2(url)).catch(() => {
10333
10577
  });
10334
10578
  }, "handler")
10335
10579
  },
@@ -10446,8 +10690,8 @@ function useSourceListInput({ groups, sortedGroups, cursor, isActive, onCursorCh
10446
10690
  __name(useSourceListInput, "useSourceListInput");
10447
10691
 
10448
10692
  // src/components/SecondaryPanel/MetadataPanel/MetadataGroupHeader.tsx
10449
- import React38 from "react";
10450
- import { Box as Box23, Text as Text32 } from "ink";
10693
+ import React39 from "react";
10694
+ import { Box as Box24, Text as Text33 } from "ink";
10451
10695
  var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive }) => {
10452
10696
  const theme = useTheme();
10453
10697
  const display = providerDisplayRegistry.get(group.serviceKey);
@@ -10458,7 +10702,7 @@ var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive
10458
10702
  const fetchedByLabel = isFallback ? providerDisplayRegistry.get(primaryResult.metadata.fetchedBy).label : void 0;
10459
10703
  const countLabel = `(${group.results.length} result${group.results.length !== 1 ? "s" : ""})`;
10460
10704
  const noteText = isFallback ? `\u2139 ${display.label} is not available but rudimentary metadata were fetched from ${fetchedByLabel}` : void 0;
10461
- return /* @__PURE__ */ React38.createElement(Box23, {
10705
+ return /* @__PURE__ */ React39.createElement(Box24, {
10462
10706
  flexDirection: "row",
10463
10707
  flexGrow: 1,
10464
10708
  flexShrink: 0,
@@ -10466,29 +10710,29 @@ var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive
10466
10710
  overflow: "hidden",
10467
10711
  backgroundColor: bg,
10468
10712
  alignItems: "flex-start"
10469
- }, /* @__PURE__ */ React38.createElement(Box23, {
10713
+ }, /* @__PURE__ */ React39.createElement(Box24, {
10470
10714
  width: 3,
10471
10715
  minWidth: 3,
10472
10716
  flexShrink: 0
10473
- }, /* @__PURE__ */ React38.createElement(Text32, {
10717
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10474
10718
  color: isSelected && isActive ? theme.text.active : theme.text.secondary,
10475
10719
  wrap: "truncate-end"
10476
- }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React38.createElement(Box23, {
10720
+ }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React39.createElement(Box24, {
10477
10721
  flexShrink: 0
10478
- }, /* @__PURE__ */ React38.createElement(Text32, {
10722
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10479
10723
  color: display.color,
10480
10724
  wrap: "truncate-end",
10481
10725
  bold: true
10482
- }, "\u25CF " + display.label)), /* @__PURE__ */ React38.createElement(Box23, {
10726
+ }, "\u25CF " + display.label)), /* @__PURE__ */ React39.createElement(Box24, {
10483
10727
  flexShrink: 0
10484
- }, /* @__PURE__ */ React38.createElement(Text32, {
10728
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10485
10729
  color: theme.text.secondary,
10486
10730
  wrap: "truncate-end"
10487
- }, " " + countLabel)), noteText && /* @__PURE__ */ React38.createElement(Box23, {
10731
+ }, " " + countLabel)), noteText && /* @__PURE__ */ React39.createElement(Box24, {
10488
10732
  flexGrow: 1,
10489
10733
  paddingLeft: 3,
10490
10734
  flexShrink: 0
10491
- }, /* @__PURE__ */ React38.createElement(Text32, {
10735
+ }, /* @__PURE__ */ React39.createElement(Text33, {
10492
10736
  color: theme.palette.blue,
10493
10737
  italic: true,
10494
10738
  wrap: "truncate-end"
@@ -10496,12 +10740,12 @@ var MetadataGroupHeader = /* @__PURE__ */ __name(({ group, isSelected, isActive
10496
10740
  }, "MetadataGroupHeader");
10497
10741
 
10498
10742
  // src/components/SecondaryPanel/MetadataPanel/MetadataResultRow.tsx
10499
- import React40 from "react";
10500
- import { Box as Box25, Text as Text34 } from "ink";
10743
+ import React41 from "react";
10744
+ import { Box as Box26, Text as Text35 } from "ink";
10501
10745
 
10502
10746
  // src/components/SecondaryPanel/MetadataPanel/DiscoverySourceLine.tsx
10503
- import React39 from "react";
10504
- import { Box as Box24, Text as Text33 } from "ink";
10747
+ import React40 from "react";
10748
+ import { Box as Box25, Text as Text34 } from "ink";
10505
10749
  function formatSearchKeys(keys) {
10506
10750
  return keys.map((k) => {
10507
10751
  switch (k) {
@@ -10526,37 +10770,37 @@ var DiscoverySourceLine = /* @__PURE__ */ __name(({ source, dimmed }) => {
10526
10770
  const discovererDisplay = providerDisplayRegistry.get(source.discoveredBy);
10527
10771
  const fromUriPlatform = source.fromUri.split("::")[0]?.toLowerCase() ?? "";
10528
10772
  const keysLabel = formatSearchKeys(source.searchKeys);
10529
- return /* @__PURE__ */ React39.createElement(Box24, {
10773
+ return /* @__PURE__ */ React40.createElement(Box25, {
10530
10774
  flexDirection: "row",
10531
10775
  overflow: "hidden",
10532
10776
  marginLeft: 14,
10533
10777
  flexShrink: 0,
10534
10778
  height: 1
10535
- }, /* @__PURE__ */ React39.createElement(Box24, {
10779
+ }, /* @__PURE__ */ React40.createElement(Box25, {
10536
10780
  flexShrink: 0
10537
- }, /* @__PURE__ */ React39.createElement(Text33, {
10781
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10538
10782
  color: theme.text.secondary,
10539
10783
  dimColor: dimmed,
10540
10784
  wrap: "truncate-end"
10541
- }, "\u2514\u2500 found by ")), /* @__PURE__ */ React39.createElement(Box24, {
10785
+ }, "\u2514\u2500 found by ")), /* @__PURE__ */ React40.createElement(Box25, {
10542
10786
  flexShrink: 0
10543
- }, /* @__PURE__ */ React39.createElement(Text33, {
10787
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10544
10788
  color: discovererDisplay.color,
10545
10789
  dimColor: dimmed,
10546
10790
  wrap: "truncate-end"
10547
- }, discovererDisplay.label)), /* @__PURE__ */ React39.createElement(Box24, {
10791
+ }, discovererDisplay.label)), /* @__PURE__ */ React40.createElement(Box25, {
10548
10792
  flexShrink: 0
10549
- }, /* @__PURE__ */ React39.createElement(Text33, {
10793
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10550
10794
  color: theme.text.secondary,
10551
10795
  dimColor: dimmed,
10552
10796
  wrap: "truncate-end"
10553
- }, " using ")), /* @__PURE__ */ React39.createElement(Uri, {
10797
+ }, " using ")), /* @__PURE__ */ React40.createElement(Uri, {
10554
10798
  uri: source.fromUri,
10555
10799
  platform: fromUriPlatform,
10556
10800
  dimmed
10557
- }), /* @__PURE__ */ React39.createElement(Box24, {
10801
+ }), /* @__PURE__ */ React40.createElement(Box25, {
10558
10802
  flexShrink: 0
10559
- }, /* @__PURE__ */ React39.createElement(Text33, {
10803
+ }, /* @__PURE__ */ React40.createElement(Text34, {
10560
10804
  color: theme.text.secondary,
10561
10805
  dimColor: dimmed,
10562
10806
  wrap: "truncate-end"
@@ -10618,12 +10862,12 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10618
10862
  const bg = isSelected ? focusColorBg : void 0;
10619
10863
  const uri = m.uri ?? `${m.platform.toUpperCase()}::TRACK::${m.id}`;
10620
10864
  const sourceLines = showDiscoverySources && result.discoverySources.length > 0 ? result.discoverySources : [];
10621
- return /* @__PURE__ */ React40.createElement(Box25, {
10865
+ return /* @__PURE__ */ React41.createElement(Box26, {
10622
10866
  flexDirection: "column",
10623
10867
  width,
10624
10868
  overflow: "hidden",
10625
10869
  flexShrink: 0
10626
- }, /* @__PURE__ */ React40.createElement(Box25, {
10870
+ }, /* @__PURE__ */ React41.createElement(Box26, {
10627
10871
  flexDirection: "row",
10628
10872
  width,
10629
10873
  minWidth: width,
@@ -10633,52 +10877,52 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10633
10877
  flexWrap: "nowrap",
10634
10878
  alignItems: "flex-start",
10635
10879
  flexShrink: 0
10636
- }, /* @__PURE__ */ React40.createElement(Box25, {
10880
+ }, /* @__PURE__ */ React41.createElement(Box26, {
10637
10881
  width: 3,
10638
10882
  minWidth: 3,
10639
10883
  flexShrink: 0,
10640
10884
  marginRight: 2
10641
- }, /* @__PURE__ */ React40.createElement(Text34, null, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React40.createElement(Box25, {
10885
+ }, /* @__PURE__ */ React41.createElement(Text35, null, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React41.createElement(Box26, {
10642
10886
  width: 2,
10643
10887
  minWidth: 2,
10644
10888
  paddingRight: 1,
10645
10889
  flexShrink: 0
10646
- }, /* @__PURE__ */ React40.createElement(Text34, {
10890
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10647
10891
  dimColor: isDimmed,
10648
10892
  color: statusColor
10649
- }, statusIcon)), /* @__PURE__ */ React40.createElement(Box25, {
10893
+ }, statusIcon)), /* @__PURE__ */ React41.createElement(Box26, {
10650
10894
  width: 7,
10651
10895
  minWidth: 7,
10652
10896
  paddingRight: 1,
10653
10897
  flexShrink: 0
10654
- }, /* @__PURE__ */ React40.createElement(Text34, {
10898
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10655
10899
  color: badge.color,
10656
10900
  dimColor: isDimmed,
10657
10901
  strikethrough: isDimmed
10658
- }, badgeText)), /* @__PURE__ */ React40.createElement(Box25, {
10902
+ }, badgeText)), /* @__PURE__ */ React41.createElement(Box26, {
10659
10903
  flexShrink: 0
10660
- }, /* @__PURE__ */ React40.createElement(Uri, {
10904
+ }, /* @__PURE__ */ React41.createElement(Uri, {
10661
10905
  uri,
10662
10906
  platform: m.platform,
10663
10907
  fetchState: result.fetchState,
10664
10908
  dimmed: isDimmed,
10665
10909
  fetchedBy: m.fetchedBy
10666
- })), result.fetchState === "error" && result.fetchError && /* @__PURE__ */ React40.createElement(Box25, {
10910
+ })), result.fetchState === "error" && result.fetchError && /* @__PURE__ */ React41.createElement(Box26, {
10667
10911
  paddingLeft: 1,
10668
10912
  flexShrink: 0
10669
- }, /* @__PURE__ */ React40.createElement(Text34, {
10913
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10670
10914
  color: theme.status.error,
10671
10915
  wrap: "truncate-end"
10672
- }, result.fetchError)), /* @__PURE__ */ React40.createElement(Box25, {
10916
+ }, result.fetchError)), /* @__PURE__ */ React41.createElement(Box26, {
10673
10917
  flexGrow: 1,
10674
10918
  overflow: "hidden",
10675
10919
  paddingLeft: 1
10676
- }, /* @__PURE__ */ React40.createElement(Text34, {
10920
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10677
10921
  color: theme.text.primary,
10678
10922
  dimColor: isDimmed,
10679
10923
  strikethrough: isDimmed,
10680
10924
  wrap: "truncate-end"
10681
- }, suffix)), isSelected && /* @__PURE__ */ React40.createElement(Box25, {
10925
+ }, suffix)), isSelected && /* @__PURE__ */ React41.createElement(Box26, {
10682
10926
  flexDirection: "row",
10683
10927
  height: 1,
10684
10928
  width: 5,
@@ -10686,10 +10930,10 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10686
10930
  paddingLeft: 1,
10687
10931
  paddingRight: 1,
10688
10932
  flexShrink: 0
10689
- }, /* @__PURE__ */ React40.createElement(Text34, {
10933
+ }, /* @__PURE__ */ React41.createElement(Text35, {
10690
10934
  color: theme.text.secondary,
10691
10935
  dimColor: isDimmed
10692
- }, ">>>"))), sourceLines.map((src, i) => /* @__PURE__ */ React40.createElement(DiscoverySourceLine, {
10936
+ }, ">>>"))), sourceLines.map((src, i) => /* @__PURE__ */ React41.createElement(DiscoverySourceLine, {
10693
10937
  key: i,
10694
10938
  source: src,
10695
10939
  dimmed: isDimmed
@@ -10697,8 +10941,8 @@ var MetadataResultRow = /* @__PURE__ */ __name(({ result, serviceKey: _serviceKe
10697
10941
  }, "MetadataResultRow");
10698
10942
 
10699
10943
  // src/components/SecondaryPanel/MetadataPanel/MetadataCompiledRow.tsx
10700
- import React41 from "react";
10701
- import { Box as Box26, Text as Text35 } from "ink";
10944
+ import React42 from "react";
10945
+ import { Box as Box27, Text as Text36 } from "ink";
10702
10946
 
10703
10947
  // src/components/SecondaryPanel/utils.ts
10704
10948
  function formatDuration3(ms) {
@@ -10722,7 +10966,7 @@ var MetadataCompiledRow = /* @__PURE__ */ __name(({ compiled, overrideCount, isS
10722
10966
  const durationPart = duration ? ` (${duration})` : "";
10723
10967
  const trackInfo = artist ? `${artist} - ${title}${durationPart}` : `${title}${durationPart}`;
10724
10968
  const suffixText = overrideCount > 0 ? ` (${overrideCount} edit${overrideCount === 1 ? "" : "s"})` : "";
10725
- return /* @__PURE__ */ React41.createElement(Box26, {
10969
+ return /* @__PURE__ */ React42.createElement(Box27, {
10726
10970
  flexDirection: "row",
10727
10971
  height: 1,
10728
10972
  flexShrink: 0,
@@ -10730,36 +10974,36 @@ var MetadataCompiledRow = /* @__PURE__ */ __name(({ compiled, overrideCount, isS
10730
10974
  minWidth: width,
10731
10975
  overflow: "hidden",
10732
10976
  backgroundColor: bg
10733
- }, /* @__PURE__ */ React41.createElement(Box26, {
10977
+ }, /* @__PURE__ */ React42.createElement(Box27, {
10734
10978
  width: 3,
10735
10979
  minWidth: 3
10736
- }, /* @__PURE__ */ React41.createElement(Text35, {
10980
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10737
10981
  color: isSelected && isActive ? theme.text.active : theme.text.secondary
10738
- }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React41.createElement(Box26, {
10982
+ }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React42.createElement(Box27, {
10739
10983
  width: COMPILED_PREFIX.length,
10740
10984
  minWidth: COMPILED_PREFIX.length
10741
- }, /* @__PURE__ */ React41.createElement(Text35, {
10985
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10742
10986
  color: theme.action.primary
10743
- }, COMPILED_PREFIX)), /* @__PURE__ */ React41.createElement(Box26, {
10987
+ }, COMPILED_PREFIX)), /* @__PURE__ */ React42.createElement(Box27, {
10744
10988
  flexGrow: 1,
10745
10989
  flexDirection: "row"
10746
- }, /* @__PURE__ */ React41.createElement(Text35, {
10990
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10747
10991
  color: theme.text.primary,
10748
10992
  wrap: "truncate-end"
10749
- }, trackInfo)), overrideCount > 0 && /* @__PURE__ */ React41.createElement(Box26, {
10993
+ }, trackInfo)), overrideCount > 0 && /* @__PURE__ */ React42.createElement(Box27, {
10750
10994
  flexShrink: 0
10751
- }, /* @__PURE__ */ React41.createElement(Text35, {
10995
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10752
10996
  color: theme.text.secondary,
10753
10997
  italic: true,
10754
10998
  wrap: "truncate-end"
10755
- }, suffixText)), isSelected && /* @__PURE__ */ React41.createElement(Box26, {
10999
+ }, suffixText)), isSelected && /* @__PURE__ */ React42.createElement(Box27, {
10756
11000
  flexDirection: "row",
10757
11001
  height: 1,
10758
11002
  width: 5,
10759
11003
  minWidth: 5,
10760
11004
  paddingLeft: 1,
10761
11005
  paddingRight: 1
10762
- }, /* @__PURE__ */ React41.createElement(Text35, {
11006
+ }, /* @__PURE__ */ React42.createElement(Text36, {
10763
11007
  color: theme.text.secondary
10764
11008
  }, ">>>")));
10765
11009
  }, "MetadataCompiledRow");
@@ -10788,26 +11032,26 @@ var MetadataSourceList = /* @__PURE__ */ __name(({ groups, compiled, overrides,
10788
11032
  onRefetchResult
10789
11033
  });
10790
11034
  const overrideCount = Object.keys(overrides).filter((k) => overrides[k] !== void 0).length;
10791
- return /* @__PURE__ */ React42.createElement(Box27, {
11035
+ return /* @__PURE__ */ React43.createElement(Box28, {
10792
11036
  flexDirection: "column",
10793
11037
  width,
10794
11038
  height,
10795
11039
  overflow: "hidden"
10796
- }, /* @__PURE__ */ React42.createElement(Box27, {
11040
+ }, /* @__PURE__ */ React43.createElement(Box28, {
10797
11041
  flexDirection: "row",
10798
11042
  height: 1,
10799
11043
  marginBottom: 1,
10800
11044
  overflow: "hidden"
10801
- }, /* @__PURE__ */ React42.createElement(Text36, {
11045
+ }, /* @__PURE__ */ React43.createElement(Text37, {
10802
11046
  color: "gray",
10803
11047
  italic: true,
10804
11048
  wrap: "truncate-end"
10805
- }, " " + HEADER_LINE)), /* @__PURE__ */ React42.createElement(Box27, {
11049
+ }, " " + HEADER_LINE)), /* @__PURE__ */ React43.createElement(Box28, {
10806
11050
  flexDirection: "column",
10807
11051
  flexGrow: 1,
10808
11052
  gap: 1,
10809
11053
  overflow: "hidden"
10810
- }, /* @__PURE__ */ React42.createElement(MetadataCompiledRow, {
11054
+ }, /* @__PURE__ */ React43.createElement(MetadataCompiledRow, {
10811
11055
  compiled,
10812
11056
  overrideCount,
10813
11057
  isSelected: cursor.type === "compiled",
@@ -10817,15 +11061,15 @@ var MetadataSourceList = /* @__PURE__ */ __name(({ groups, compiled, overrides,
10817
11061
  const sortedResults = [
10818
11062
  ...group.results
10819
11063
  ].sort((a, b) => a.rank - b.rank);
10820
- return /* @__PURE__ */ React42.createElement(Box27, {
11064
+ return /* @__PURE__ */ React43.createElement(Box28, {
10821
11065
  key: group.serviceKey,
10822
11066
  flexDirection: "column",
10823
11067
  overflow: "hidden"
10824
- }, /* @__PURE__ */ React42.createElement(MetadataGroupHeader, {
11068
+ }, /* @__PURE__ */ React43.createElement(MetadataGroupHeader, {
10825
11069
  group,
10826
11070
  isSelected: cursor.type === "group" && cursor.groupIndex === gIdx,
10827
11071
  isActive
10828
- }), sortedResults.map((result, rIdx) => /* @__PURE__ */ React42.createElement(MetadataResultRow, {
11072
+ }), sortedResults.map((result, rIdx) => /* @__PURE__ */ React43.createElement(MetadataResultRow, {
10829
11073
  key: rIdx,
10830
11074
  result,
10831
11075
  serviceKey: group.serviceKey,
@@ -10834,32 +11078,32 @@ var MetadataSourceList = /* @__PURE__ */ __name(({ groups, compiled, overrides,
10834
11078
  width,
10835
11079
  showDiscoverySources
10836
11080
  })));
10837
- }), (isFetchingPrimarySource || isDiscovering) && /* @__PURE__ */ React42.createElement(Box27, {
11081
+ }), (isFetchingPrimarySource || isDiscovering) && /* @__PURE__ */ React43.createElement(Box28, {
10838
11082
  flexDirection: "row",
10839
11083
  height: 1,
10840
11084
  paddingLeft: 3,
10841
11085
  flexShrink: 0
10842
- }, /* @__PURE__ */ React42.createElement(Box27, {
11086
+ }, /* @__PURE__ */ React43.createElement(Box28, {
10843
11087
  flexDirection: "row",
10844
11088
  paddingRight: 1,
10845
11089
  flexShrink: 0
10846
- }, /* @__PURE__ */ React42.createElement(Text36, {
11090
+ }, /* @__PURE__ */ React43.createElement(Text37, {
10847
11091
  color: theme.text.secondary
10848
- }, /* @__PURE__ */ React42.createElement(Spinner2, {
11092
+ }, /* @__PURE__ */ React43.createElement(Spinner2, {
10849
11093
  type: "dots"
10850
- }))), /* @__PURE__ */ React42.createElement(Text36, {
11094
+ }))), /* @__PURE__ */ React43.createElement(Text37, {
10851
11095
  color: theme.text.secondary
10852
11096
  }, getLoadingText(isFetchingPrimarySource, isDiscovering)))));
10853
11097
  }, "MetadataSourceList");
10854
11098
 
10855
11099
  // src/components/SecondaryPanel/MetadataPanel/MetadataDetailPanel.tsx
10856
- import React44, { useState as useState17 } from "react";
10857
- import { Box as Box29, Text as Text38 } from "ink";
11100
+ import React45, { useState as useState18 } from "react";
11101
+ import { Box as Box30, Text as Text39 } from "ink";
10858
11102
  import clipboard3 from "clipboardy";
10859
11103
 
10860
11104
  // src/components/SecondaryPanel/MetadataPanel/FieldRow.tsx
10861
- import React43 from "react";
10862
- import { Box as Box28, Text as Text37 } from "ink";
11105
+ import React44 from "react";
11106
+ import { Box as Box29, Text as Text38 } from "ink";
10863
11107
  import TextInput6 from "ink-text-input";
10864
11108
  function getPlatformColor(apiProvider) {
10865
11109
  return providerDisplayRegistry.get(apiProvider).colorSubtle;
@@ -10889,45 +11133,45 @@ var LABEL_W = 10;
10889
11133
  var FieldRow = /* @__PURE__ */ __name(({ field, isFocused, isCompiled, isEditing, value, attribution, hasOverride, editValue, editError, onEditValueChange, onEditSubmit }) => {
10890
11134
  const theme = useTheme();
10891
11135
  const badge = attributionBadge(attribution, theme);
10892
- return /* @__PURE__ */ React43.createElement(Box28, {
11136
+ return /* @__PURE__ */ React44.createElement(Box29, {
10893
11137
  flexDirection: "row",
10894
11138
  paddingX: 1,
10895
11139
  flexShrink: 0,
10896
11140
  flexGrow: 1
10897
- }, /* @__PURE__ */ React43.createElement(Box28, {
11141
+ }, /* @__PURE__ */ React44.createElement(Box29, {
10898
11142
  width: 2,
10899
11143
  minWidth: 2,
10900
11144
  flexShrink: 0
10901
- }, isFocused && /* @__PURE__ */ React43.createElement(Text37, {
11145
+ }, isFocused && /* @__PURE__ */ React44.createElement(Text38, {
10902
11146
  color: theme.text.active
10903
- }, "\u261B")), /* @__PURE__ */ React43.createElement(Box28, {
11147
+ }, "\u261B")), /* @__PURE__ */ React44.createElement(Box29, {
10904
11148
  width: LABEL_W,
10905
11149
  flexShrink: 0
10906
- }, /* @__PURE__ */ React43.createElement(Text37, {
11150
+ }, /* @__PURE__ */ React44.createElement(Text38, {
10907
11151
  color: isFocused ? theme.field.selected : theme.ui.border,
10908
11152
  bold: true
10909
- }, field.label.toUpperCase().padEnd(LABEL_W))), isEditing ? /* @__PURE__ */ React43.createElement(Box28, {
11153
+ }, field.label.toUpperCase().padEnd(LABEL_W))), isEditing ? /* @__PURE__ */ React44.createElement(Box29, {
10910
11154
  flexWrap: "wrap",
10911
11155
  flexDirection: "row",
10912
11156
  flexGrow: 1
10913
- }, /* @__PURE__ */ React43.createElement(TextInput6, {
11157
+ }, /* @__PURE__ */ React44.createElement(TextInput6, {
10914
11158
  value: editValue,
10915
11159
  onChange: onEditValueChange,
10916
11160
  onSubmit: onEditSubmit
10917
- }), editError && /* @__PURE__ */ React43.createElement(Text37, {
11161
+ }), editError && /* @__PURE__ */ React44.createElement(Text38, {
10918
11162
  color: theme.field.error
10919
- }, " \u2717 invalid")) : /* @__PURE__ */ React43.createElement(Box28, {
11163
+ }, " \u2717 invalid")) : /* @__PURE__ */ React44.createElement(Box29, {
10920
11164
  flexGrow: 1
10921
- }, /* @__PURE__ */ React43.createElement(Text37, {
11165
+ }, /* @__PURE__ */ React44.createElement(Text38, {
10922
11166
  color: value === "\u2014" ? theme.field.missing : hasOverride ? theme.field.overridden : theme.field.normal,
10923
11167
  underline: isFocused,
10924
11168
  dimColor: value === "\u2014",
10925
11169
  wrap: "truncate-end"
10926
- }, value)), isCompiled && !isEditing && !editError && /* @__PURE__ */ React43.createElement(Box28, {
11170
+ }, value)), isCompiled && !isEditing && !editError && /* @__PURE__ */ React44.createElement(Box29, {
10927
11171
  minWidth: badge.text.length + 1,
10928
11172
  paddingLeft: 1,
10929
11173
  flexShrink: 0
10930
- }, /* @__PURE__ */ React43.createElement(Text37, {
11174
+ }, /* @__PURE__ */ React44.createElement(Text38, {
10931
11175
  color: badge.color,
10932
11176
  dimColor: true,
10933
11177
  italic: badge.italic
@@ -10942,9 +11186,9 @@ __name(getPlatformBrightColor, "getPlatformBrightColor");
10942
11186
  var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides, selectedFieldIndex, isActive, width, onOverrideChange, onInnerFocusSwitch }) => {
10943
11187
  const theme = useTheme();
10944
11188
  const { setIsEditingField } = useFocusContext();
10945
- const [editingField, setEditingField] = useState17(null);
10946
- const [editValue, setEditValue] = useState17("");
10947
- const [editError, setEditError] = useState17(false);
11189
+ const [editingField, setEditingField] = useState18(null);
11190
+ const [editValue, setEditValue] = useState18("");
11191
+ const [editError, setEditError] = useState18(false);
10948
11192
  function startEditing(fieldKey, initialValue) {
10949
11193
  setEditValue(initialValue);
10950
11194
  setEditError(false);
@@ -11085,25 +11329,25 @@ var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides,
11085
11329
  const borderLeft = `\u250C${"\u2500".repeat(leftD)} `;
11086
11330
  const borderRight = ` ${"\u2500".repeat(rightD)}\u2510`;
11087
11331
  const platformColor = isCompiled ? theme.text.secondary : getPlatformBrightColor(source.metadata.apiProvider);
11088
- return /* @__PURE__ */ React44.createElement(Box29, {
11332
+ return /* @__PURE__ */ React45.createElement(Box30, {
11089
11333
  flexDirection: "column",
11090
11334
  width,
11091
11335
  flexGrow: 1,
11092
11336
  overflow: "hidden"
11093
- }, /* @__PURE__ */ React44.createElement(Box29, {
11337
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11094
11338
  flexDirection: "column",
11095
11339
  height: 1,
11096
11340
  flexShrink: 0,
11097
11341
  overflow: "hidden"
11098
- }, /* @__PURE__ */ React44.createElement(Box29, {
11342
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11099
11343
  flexDirection: "row"
11100
- }, /* @__PURE__ */ React44.createElement(Text38, {
11344
+ }, /* @__PURE__ */ React45.createElement(Text39, {
11101
11345
  color: theme.text.secondary
11102
- }, borderLeft), /* @__PURE__ */ React44.createElement(Text38, {
11346
+ }, borderLeft), /* @__PURE__ */ React45.createElement(Text39, {
11103
11347
  color: platformColor
11104
- }, headerLabel), /* @__PURE__ */ React44.createElement(Text38, {
11348
+ }, headerLabel), /* @__PURE__ */ React45.createElement(Text39, {
11105
11349
  color: theme.text.secondary
11106
- }, borderRight))), /* @__PURE__ */ React44.createElement(Box29, {
11350
+ }, borderRight))), /* @__PURE__ */ React45.createElement(Box30, {
11107
11351
  flexDirection: "column",
11108
11352
  flexGrow: 1,
11109
11353
  overflow: "hidden",
@@ -11111,11 +11355,11 @@ var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides,
11111
11355
  borderColor: theme.text.secondary,
11112
11356
  borderBackgroundColor: theme.ui.background,
11113
11357
  borderTop: false
11114
- }, /* @__PURE__ */ React44.createElement(Box29, {
11358
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11115
11359
  flexDirection: "column",
11116
11360
  flexGrow: 1,
11117
11361
  overflow: "hidden"
11118
- }, /* @__PURE__ */ React44.createElement(Box29, {
11362
+ }, /* @__PURE__ */ React45.createElement(Box30, {
11119
11363
  flexDirection: "column",
11120
11364
  flexShrink: 0
11121
11365
  }, FIELDS.map((field) => {
@@ -11124,7 +11368,7 @@ var MetadataDetailPanel = /* @__PURE__ */ __name(({ source, compiled, overrides,
11124
11368
  const attr = isCompiled ? compiled.attribution[field.key] : void 0;
11125
11369
  const isEditing = editingField === field.key;
11126
11370
  const hasOverride = isCompiled && overrides[field.key] !== void 0;
11127
- return /* @__PURE__ */ React44.createElement(FieldRow, {
11371
+ return /* @__PURE__ */ React45.createElement(FieldRow, {
11128
11372
  key: field.key,
11129
11373
  field,
11130
11374
  isFocused,
@@ -11149,13 +11393,13 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11149
11393
  const { cursor, showDiscoverySources, innerFocus, selectedFieldIndex } = sourcesPanel;
11150
11394
  const isPanelActive = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab === "metadataSources";
11151
11395
  const typedTask = selectedTask;
11152
- const [snapshot, setSnapshot] = useState18(() => typedTask?.get());
11153
- const [prevTypedTask, setPrevTypedTask] = useState18(typedTask);
11396
+ const [snapshot, setSnapshot] = useState19(() => typedTask?.get());
11397
+ const [prevTypedTask, setPrevTypedTask] = useState19(typedTask);
11154
11398
  if (prevTypedTask !== typedTask) {
11155
11399
  setPrevTypedTask(typedTask);
11156
11400
  setSnapshot(typedTask?.get());
11157
11401
  }
11158
- useEffect18(() => {
11402
+ useEffect19(() => {
11159
11403
  if (!typedTask) return;
11160
11404
  return typedTask.subscribe((t) => {
11161
11405
  setSnapshot(t.get());
@@ -11166,7 +11410,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11166
11410
  const groups = snapshot?.attributes?.metadataGroups ?? [];
11167
11411
  const overrides = snapshot?.attributes?.metadataOverride ?? {};
11168
11412
  const compiled = computeCompiledMetadata(groups, overrides);
11169
- const [splitRatio, setSplitRatio] = useState18(0.6);
11413
+ const [splitRatio, setSplitRatio] = useState19(0.6);
11170
11414
  const leftWidth = Math.floor(width * splitRatio) - 4;
11171
11415
  const rightWidth = width - leftWidth - 4;
11172
11416
  useShortcuts({
@@ -11282,7 +11526,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11282
11526
  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
11527
  const hintBarHeight = cursor.type === "result" ? 3 : 2;
11284
11528
  const listHeight = height - hintBarHeight;
11285
- return /* @__PURE__ */ React45.createElement(Box30, {
11529
+ return /* @__PURE__ */ React46.createElement(Box31, {
11286
11530
  flexDirection: "column",
11287
11531
  height,
11288
11532
  overflow: "hidden",
@@ -11292,11 +11536,11 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11292
11536
  borderTop: false,
11293
11537
  borderBottom: false,
11294
11538
  paddingRight: 1
11295
- }, /* @__PURE__ */ React45.createElement(Box30, {
11539
+ }, /* @__PURE__ */ React46.createElement(Box31, {
11296
11540
  flexDirection: "row",
11297
11541
  flexGrow: 1,
11298
11542
  overflow: "hidden"
11299
- }, /* @__PURE__ */ React45.createElement(MetadataSourceList, {
11543
+ }, /* @__PURE__ */ React46.createElement(MetadataSourceList, {
11300
11544
  groups,
11301
11545
  compiled,
11302
11546
  overrides,
@@ -11312,7 +11556,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11312
11556
  onRefetchResult: handleRefetchResult,
11313
11557
  isFetchingPrimarySource: snapshot?.attributes?.primaryMetadataInProgress ?? false,
11314
11558
  isDiscovering: snapshot?.attributes?.metadataDiscoveringInProgress ?? false
11315
- }), /* @__PURE__ */ React45.createElement(MetadataDetailPanel, {
11559
+ }), /* @__PURE__ */ React46.createElement(MetadataDetailPanel, {
11316
11560
  source: selectedResult ?? "compiled",
11317
11561
  compiled,
11318
11562
  overrides,
@@ -11322,30 +11566,30 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
11322
11566
  height: listHeight,
11323
11567
  onOverrideChange: handleOverrideChange,
11324
11568
  onInnerFocusSwitch: /* @__PURE__ */ __name(() => setSourcesInnerFocus("list"), "onInnerFocusSwitch")
11325
- })), /* @__PURE__ */ React45.createElement(DynamicHintBar, {
11569
+ })), /* @__PURE__ */ React46.createElement(DynamicHintBar, {
11326
11570
  width: width - 2,
11327
11571
  isActive: isPanelActive
11328
11572
  }));
11329
11573
  }, "MetadataPanel");
11330
11574
 
11331
11575
  // src/components/SecondaryPanel/DownloadPanel/DownloadPanel.tsx
11332
- import React56, { useState as useState20, useEffect as useEffect20 } from "react";
11333
- import { Box as Box40 } from "ink";
11576
+ import React57, { useState as useState21, useEffect as useEffect21 } from "react";
11577
+ import { Box as Box41 } from "ink";
11334
11578
  import fs14 from "fs";
11335
11579
  import path14 from "path";
11336
11580
 
11337
11581
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/DownloadSourceTree.tsx
11338
- import React50 from "react";
11339
- import { Box as Box34, Text as Text43 } from "ink";
11582
+ import React51 from "react";
11583
+ import { Box as Box35, Text as Text44 } from "ink";
11340
11584
 
11341
11585
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/ProviderHeader.tsx
11342
- import React46 from "react";
11343
- import { Box as Box31, Text as Text39 } from "ink";
11586
+ import React47 from "react";
11587
+ import { Box as Box32, Text as Text40 } from "ink";
11344
11588
  function ProviderHeader({ label, color, addMargin }) {
11345
- return /* @__PURE__ */ React46.createElement(Box31, {
11589
+ return /* @__PURE__ */ React47.createElement(Box32, {
11346
11590
  paddingLeft: 1,
11347
11591
  marginTop: addMargin ? 1 : 0
11348
- }, /* @__PURE__ */ React46.createElement(Text39, {
11592
+ }, /* @__PURE__ */ React47.createElement(Text40, {
11349
11593
  color,
11350
11594
  bold: true
11351
11595
  }, label));
@@ -11353,8 +11597,8 @@ function ProviderHeader({ label, color, addMargin }) {
11353
11597
  __name(ProviderHeader, "ProviderHeader");
11354
11598
 
11355
11599
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/MetadataHeader.tsx
11356
- import React47 from "react";
11357
- import { Box as Box32, Text as Text40 } from "ink";
11600
+ import React48 from "react";
11601
+ import { Box as Box33, Text as Text41 } from "ink";
11358
11602
  function MetadataHeader({ source }) {
11359
11603
  const theme = useTheme();
11360
11604
  const m = source.track;
@@ -11362,23 +11606,23 @@ function MetadataHeader({ source }) {
11362
11606
  const title = m.trackName ?? "";
11363
11607
  const info = artist ? `${artist} - ${title}` : title;
11364
11608
  const uri = m.uri ?? `${m.platform.toUpperCase()}::TRACK::${m.id}`;
11365
- return /* @__PURE__ */ React47.createElement(Box32, {
11609
+ return /* @__PURE__ */ React48.createElement(Box33, {
11366
11610
  marginLeft: 2,
11367
11611
  flexDirection: "row",
11368
11612
  overflow: "hidden",
11369
11613
  flexShrink: 0
11370
- }, /* @__PURE__ */ React47.createElement(Box32, {
11614
+ }, /* @__PURE__ */ React48.createElement(Box33, {
11371
11615
  paddingRight: 1,
11372
11616
  flexDirection: "row",
11373
11617
  flexShrink: 0
11374
- }, /* @__PURE__ */ React47.createElement(Text40, {
11618
+ }, /* @__PURE__ */ React48.createElement(Text41, {
11375
11619
  color: theme.text.secondary
11376
- }, "\u2514\u2500 used")), /* @__PURE__ */ React47.createElement(Box32, {
11620
+ }, "\u2514\u2500 used")), /* @__PURE__ */ React48.createElement(Box33, {
11377
11621
  flexShrink: 0
11378
- }, /* @__PURE__ */ React47.createElement(Uri, {
11622
+ }, /* @__PURE__ */ React48.createElement(Uri, {
11379
11623
  uri,
11380
11624
  platform: m.platform
11381
- })), /* @__PURE__ */ React47.createElement(Text40, {
11625
+ })), /* @__PURE__ */ React48.createElement(Text41, {
11382
11626
  color: theme.text.primary,
11383
11627
  wrap: "truncate-end"
11384
11628
  }, ` (${info})`));
@@ -11386,8 +11630,8 @@ function MetadataHeader({ source }) {
11386
11630
  __name(MetadataHeader, "MetadataHeader");
11387
11631
 
11388
11632
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/SourceFileRow.tsx
11389
- import React49 from "react";
11390
- import { Box as Box33, Text as Text42 } from "ink";
11633
+ import React50 from "react";
11634
+ import { Box as Box34, Text as Text43 } from "ink";
11391
11635
 
11392
11636
  // src/components/SecondaryPanel/DownloadPanel/utils.ts
11393
11637
  function getProviderColor(provider) {
@@ -11412,32 +11656,32 @@ function tagValue(tags, key) {
11412
11656
  __name(tagValue, "tagValue");
11413
11657
 
11414
11658
  // src/components/SecondaryPanel/DownloadPanel/StateBadge.tsx
11415
- import React48 from "react";
11416
- import { Text as Text41 } from "ink";
11659
+ import React49 from "react";
11660
+ import { Text as Text42 } from "ink";
11417
11661
  function StateBadge({ source }) {
11418
11662
  const theme = useTheme();
11419
- if (source.savedFile) return /* @__PURE__ */ React48.createElement(Text41, {
11663
+ if (source.savedFile) return /* @__PURE__ */ React49.createElement(Text42, {
11420
11664
  color: theme.status.locked
11421
11665
  }, "\u25CF SAVED");
11422
11666
  switch (source.state) {
11423
11667
  case "downloading":
11424
- return /* @__PURE__ */ React48.createElement(Text41, {
11668
+ return /* @__PURE__ */ React49.createElement(Text42, {
11425
11669
  color: theme.status.downloading
11426
11670
  }, "DOWNLOADING");
11427
11671
  case "downloaded":
11428
- return /* @__PURE__ */ React48.createElement(Text41, {
11672
+ return /* @__PURE__ */ React49.createElement(Text42, {
11429
11673
  color: theme.status.success
11430
11674
  }, "DOWNLOADED");
11431
11675
  case "failed":
11432
- return /* @__PURE__ */ React48.createElement(Text41, {
11676
+ return /* @__PURE__ */ React49.createElement(Text42, {
11433
11677
  color: theme.status.error
11434
11678
  }, "FAILED");
11435
11679
  case "searching":
11436
- return /* @__PURE__ */ React48.createElement(Text41, {
11680
+ return /* @__PURE__ */ React49.createElement(Text42, {
11437
11681
  color: theme.status.downloading
11438
11682
  }, "SEARCHING");
11439
11683
  default:
11440
- return /* @__PURE__ */ React48.createElement(Text41, {
11684
+ return /* @__PURE__ */ React49.createElement(Text42, {
11441
11685
  color: theme.status.skipped
11442
11686
  }, "PENDING");
11443
11687
  }
@@ -11451,7 +11695,7 @@ function SourceFileRow({ source, sourceIndex, isSelected, isActive, width }) {
11451
11695
  const localFile = source.localFile;
11452
11696
  const filename = localFile ? `${localFile.name}.${localFile.extension}` : null;
11453
11697
  const sizeText = source.fileInfo ? formatBytes(source.fileInfo.sizeBytes) : "";
11454
- return /* @__PURE__ */ React49.createElement(Box33, {
11698
+ return /* @__PURE__ */ React50.createElement(Box34, {
11455
11699
  flexDirection: "row",
11456
11700
  width,
11457
11701
  minWidth: width,
@@ -11459,48 +11703,48 @@ function SourceFileRow({ source, sourceIndex, isSelected, isActive, width }) {
11459
11703
  overflow: "hidden",
11460
11704
  backgroundColor: bg,
11461
11705
  paddingLeft: 1
11462
- }, /* @__PURE__ */ React49.createElement(Box33, {
11706
+ }, /* @__PURE__ */ React50.createElement(Box34, {
11463
11707
  width: 3,
11464
11708
  minWidth: 3,
11465
11709
  flexShrink: 0
11466
- }, /* @__PURE__ */ React49.createElement(Text42, {
11710
+ }, /* @__PURE__ */ React50.createElement(Text43, {
11467
11711
  color: theme.ui.focusIndicator
11468
- }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React49.createElement(Box33, {
11712
+ }, isSelected && isActive ? "\u261B " : " ")), /* @__PURE__ */ React50.createElement(Box34, {
11469
11713
  width: 2,
11470
11714
  minWidth: 2,
11471
11715
  flexShrink: 0
11472
- }, source.isRejected ? /* @__PURE__ */ React49.createElement(Text42, {
11716
+ }, source.isRejected ? /* @__PURE__ */ React50.createElement(Text43, {
11473
11717
  color: theme.status.error
11474
- }, "\u2718 ") : source.selected ? /* @__PURE__ */ React49.createElement(Text42, {
11718
+ }, "\u2718 ") : source.selected ? /* @__PURE__ */ React50.createElement(Text43, {
11475
11719
  color: theme.ui.selection
11476
- }, "\u2713 ") : /* @__PURE__ */ React49.createElement(Text42, null, " ")), /* @__PURE__ */ React49.createElement(Box33, {
11720
+ }, "\u2713 ") : /* @__PURE__ */ React50.createElement(Text43, null, " ")), /* @__PURE__ */ React50.createElement(Box34, {
11477
11721
  minWidth: 3,
11478
11722
  flexShrink: 0
11479
- }, /* @__PURE__ */ React49.createElement(Text42, {
11723
+ }, /* @__PURE__ */ React50.createElement(Text43, {
11480
11724
  color: theme.text.secondary,
11481
11725
  dimColor: source.isRejected,
11482
11726
  strikethrough: source.isRejected
11483
- }, `${sourceIndex + 1}. `)), /* @__PURE__ */ React49.createElement(Box33, {
11727
+ }, `${sourceIndex + 1}. `)), /* @__PURE__ */ React50.createElement(Box34, {
11484
11728
  flexGrow: 1,
11485
11729
  overflow: "hidden"
11486
- }, filename ? /* @__PURE__ */ React49.createElement(Text42, {
11730
+ }, filename ? /* @__PURE__ */ React50.createElement(Text43, {
11487
11731
  color: theme.text.primary,
11488
11732
  wrap: "truncate-end",
11489
11733
  dimColor: source.isRejected,
11490
11734
  strikethrough: source.isRejected
11491
- }, filename) : /* @__PURE__ */ React49.createElement(StateBadge, {
11735
+ }, filename) : /* @__PURE__ */ React50.createElement(StateBadge, {
11492
11736
  source
11493
- })), sizeText !== "" && /* @__PURE__ */ React49.createElement(Box33, {
11737
+ })), sizeText !== "" && /* @__PURE__ */ React50.createElement(Box34, {
11494
11738
  minWidth: sizeText.length + 1,
11495
11739
  paddingLeft: 1,
11496
11740
  flexShrink: 0
11497
- }, /* @__PURE__ */ React49.createElement(Text42, {
11741
+ }, /* @__PURE__ */ React50.createElement(Text43, {
11498
11742
  color: theme.text.secondary
11499
- }, sizeText)), /* @__PURE__ */ React49.createElement(Box33, {
11743
+ }, sizeText)), /* @__PURE__ */ React50.createElement(Box34, {
11500
11744
  paddingLeft: 1,
11501
11745
  paddingRight: 1,
11502
11746
  flexShrink: 0
11503
- }, /* @__PURE__ */ React49.createElement(StateBadge, {
11747
+ }, /* @__PURE__ */ React50.createElement(StateBadge, {
11504
11748
  source
11505
11749
  })));
11506
11750
  }
@@ -11653,19 +11897,19 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11653
11897
  ]
11654
11898
  });
11655
11899
  if (sources.length === 0) {
11656
- return /* @__PURE__ */ React50.createElement(Box34, {
11900
+ return /* @__PURE__ */ React51.createElement(Box35, {
11657
11901
  flexDirection: "column",
11658
11902
  width,
11659
11903
  height,
11660
11904
  overflow: "hidden",
11661
11905
  paddingX: 1,
11662
11906
  paddingY: 1
11663
- }, /* @__PURE__ */ React50.createElement(Text43, {
11907
+ }, /* @__PURE__ */ React51.createElement(Text44, {
11664
11908
  color: theme.text.secondary,
11665
11909
  dimColor: true
11666
11910
  }, "No download sources yet"));
11667
11911
  }
11668
- return /* @__PURE__ */ React50.createElement(Box34, {
11912
+ return /* @__PURE__ */ React51.createElement(Box35, {
11669
11913
  flexDirection: "column",
11670
11914
  width,
11671
11915
  height,
@@ -11673,7 +11917,7 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11673
11917
  }, treeItems.map((item, i) => {
11674
11918
  if (item.type === "provider-header") {
11675
11919
  const addMargin = treeItems.slice(0, i).some((t) => t.type === "provider-header");
11676
- return /* @__PURE__ */ React50.createElement(ProviderHeader, {
11920
+ return /* @__PURE__ */ React51.createElement(ProviderHeader, {
11677
11921
  key: `ph-${i}`,
11678
11922
  label: item.label,
11679
11923
  color: item.color,
@@ -11681,12 +11925,12 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11681
11925
  });
11682
11926
  }
11683
11927
  if (item.type === "metadata-header") {
11684
- return /* @__PURE__ */ React50.createElement(MetadataHeader, {
11928
+ return /* @__PURE__ */ React51.createElement(MetadataHeader, {
11685
11929
  key: `mh-${i}`,
11686
11930
  source: item.source
11687
11931
  });
11688
11932
  }
11689
- return /* @__PURE__ */ React50.createElement(SourceFileRow, {
11933
+ return /* @__PURE__ */ React51.createElement(SourceFileRow, {
11690
11934
  key: `fr-${i}`,
11691
11935
  source: item.source,
11692
11936
  sourceIndex: item.sourceIndex,
@@ -11698,14 +11942,14 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
11698
11942
  }, "DownloadSourceTree");
11699
11943
 
11700
11944
  // 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";
11945
+ import React56, { useState as useState20, useEffect as useEffect20 } from "react";
11946
+ import { Box as Box40, Text as Text49 } from "ink";
11703
11947
  import * as path13 from "path";
11704
11948
  import * as fs13 from "fs";
11705
11949
 
11706
11950
  // src/components/SecondaryPanel/DownloadPanel/PlaybackBar.tsx
11707
- import React51 from "react";
11708
- import { Box as Box35, Text as Text44 } from "ink";
11951
+ import React52 from "react";
11952
+ import { Box as Box36, Text as Text45 } from "ink";
11709
11953
  function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11710
11954
  const theme = useTheme();
11711
11955
  const active = isPlaying || isPaused;
@@ -11715,7 +11959,7 @@ function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11715
11959
  const ratio = active && durationMs > 0 ? Math.min(1, positionMs / durationMs) : 0;
11716
11960
  const filled = Math.floor(ratio * barW);
11717
11961
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barW - filled);
11718
- return /* @__PURE__ */ React51.createElement(Box35, {
11962
+ return /* @__PURE__ */ React52.createElement(Box36, {
11719
11963
  flexDirection: "column",
11720
11964
  minHeight: 1,
11721
11965
  height: 1,
@@ -11724,17 +11968,17 @@ function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11724
11968
  marginBottom: 1,
11725
11969
  marginX: 1,
11726
11970
  backgroundColor: theme.ui.background
11727
- }, /* @__PURE__ */ React51.createElement(Box35, {
11971
+ }, /* @__PURE__ */ React52.createElement(Box36, {
11728
11972
  flexDirection: "row",
11729
11973
  flexGrow: 1,
11730
11974
  overflow: "hidden"
11731
- }, /* @__PURE__ */ React51.createElement(Text44, {
11975
+ }, /* @__PURE__ */ React52.createElement(Text45, {
11732
11976
  color: isPlaying ? theme.ui.progressFill : theme.text.secondary,
11733
11977
  dimColor: !active
11734
- }, isPaused ? "\u23F8 " : "\u25B6 "), /* @__PURE__ */ React51.createElement(Text44, {
11978
+ }, isPaused ? "\u23F8 " : "\u25B6 "), /* @__PURE__ */ React52.createElement(Text45, {
11735
11979
  color: isPlaying ? theme.ui.progressFill : theme.text.secondary,
11736
11980
  dimColor: !active
11737
- }, bar), /* @__PURE__ */ React51.createElement(Text44, {
11981
+ }, bar), /* @__PURE__ */ React52.createElement(Text45, {
11738
11982
  color: theme.text.secondary,
11739
11983
  dimColor: !active
11740
11984
  }, timeStr)));
@@ -11742,55 +11986,55 @@ function PlaybackBar({ positionMs, durationMs, width, isPlaying, isPaused }) {
11742
11986
  __name(PlaybackBar, "PlaybackBar");
11743
11987
 
11744
11988
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DiffView.tsx
11745
- import React53 from "react";
11989
+ import React54 from "react";
11746
11990
  import * as path12 from "path";
11747
- import { Box as Box37, Text as Text46 } from "ink";
11991
+ import { Box as Box38, Text as Text47 } from "ink";
11748
11992
 
11749
11993
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DiffRow.tsx
11750
- import React52 from "react";
11751
- import { Box as Box36, Text as Text45 } from "ink";
11994
+ import React53 from "react";
11995
+ import { Box as Box37, Text as Text46 } from "ink";
11752
11996
  var LABEL_W2 = 11;
11753
11997
  function DiffRow({ label, left, right }) {
11754
11998
  const theme = useTheme();
11755
11999
  const changed = left !== right;
11756
- return /* @__PURE__ */ React52.createElement(Box36, {
12000
+ return /* @__PURE__ */ React53.createElement(Box37, {
11757
12001
  flexDirection: "row",
11758
12002
  paddingX: 1,
11759
12003
  height: 1,
11760
12004
  overflow: "hidden"
11761
- }, /* @__PURE__ */ React52.createElement(Box36, {
12005
+ }, /* @__PURE__ */ React53.createElement(Box37, {
11762
12006
  width: LABEL_W2,
11763
12007
  minWidth: LABEL_W2,
11764
12008
  flexShrink: 0
11765
- }, /* @__PURE__ */ React52.createElement(Text45, {
12009
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11766
12010
  color: theme.diff.changed,
11767
12011
  bold: true,
11768
12012
  wrap: "truncate-end"
11769
- }, label.toUpperCase().padEnd(LABEL_W2))), /* @__PURE__ */ React52.createElement(Box36, {
12013
+ }, label.toUpperCase().padEnd(LABEL_W2))), /* @__PURE__ */ React53.createElement(Box37, {
11770
12014
  flexGrow: 1,
11771
12015
  overflow: "hidden"
11772
- }, /* @__PURE__ */ React52.createElement(Text45, {
12016
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11773
12017
  color: changed ? theme.diff.base : theme.text.primary,
11774
12018
  dimColor: !changed,
11775
12019
  wrap: "truncate-end"
11776
- }, left)), /* @__PURE__ */ React52.createElement(Box36, {
12020
+ }, left)), /* @__PURE__ */ React53.createElement(Box37, {
11777
12021
  width: 1,
11778
12022
  minWidth: 1,
11779
12023
  flexShrink: 0
11780
- }, /* @__PURE__ */ React52.createElement(Text45, {
12024
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11781
12025
  color: theme.diff.base
11782
- }, "\u2502")), /* @__PURE__ */ React52.createElement(Box36, {
12026
+ }, "\u2502")), /* @__PURE__ */ React53.createElement(Box37, {
11783
12027
  flexGrow: 1,
11784
12028
  overflow: "hidden",
11785
12029
  paddingLeft: 1
11786
- }, /* @__PURE__ */ React52.createElement(Text45, {
12030
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11787
12031
  color: changed ? theme.diff.modified : theme.text.primary,
11788
12032
  wrap: "truncate-end"
11789
- }, right)), changed && /* @__PURE__ */ React52.createElement(Box36, {
12033
+ }, right)), changed && /* @__PURE__ */ React53.createElement(Box37, {
11790
12034
  width: 2,
11791
12035
  minWidth: 2,
11792
12036
  flexShrink: 0
11793
- }, /* @__PURE__ */ React52.createElement(Text45, {
12037
+ }, /* @__PURE__ */ React53.createElement(Text46, {
11794
12038
  color: theme.diff.modified
11795
12039
  }, " \u2190")));
11796
12040
  }
@@ -11826,27 +12070,27 @@ function DiffView({ savedSource, pendingSource, compiled, outputDir, diffKind, i
11826
12070
  const innerW = width - 2;
11827
12071
  const oldProviderColor = getProviderColor(savedSource.provider);
11828
12072
  const newProviderColor = getProviderColor(pendingSource.provider);
11829
- return /* @__PURE__ */ React53.createElement(Box37, {
12073
+ return /* @__PURE__ */ React54.createElement(Box38, {
11830
12074
  flexDirection: "column",
11831
12075
  width,
11832
12076
  height,
11833
12077
  overflow: "hidden"
11834
- }, /* @__PURE__ */ React53.createElement(Box37, {
12078
+ }, /* @__PURE__ */ React54.createElement(Box38, {
11835
12079
  flexDirection: "row",
11836
12080
  paddingX: 1,
11837
12081
  height: 1,
11838
12082
  flexShrink: 0
11839
- }, /* @__PURE__ */ React53.createElement(Text46, {
12083
+ }, /* @__PURE__ */ React54.createElement(Text47, {
11840
12084
  color: theme.diff.base
11841
- }, "\u250C\u2500\u2500 "), /* @__PURE__ */ React53.createElement(Text46, {
12085
+ }, "\u250C\u2500\u2500 "), /* @__PURE__ */ React54.createElement(Text47, {
11842
12086
  color: oldProviderColor
11843
- }, oldProvider), /* @__PURE__ */ React53.createElement(Text46, {
12087
+ }, oldProvider), /* @__PURE__ */ React54.createElement(Text47, {
11844
12088
  color: theme.diff.base
11845
- }, " \u2192 "), /* @__PURE__ */ React53.createElement(Text46, {
12089
+ }, " \u2192 "), /* @__PURE__ */ React54.createElement(Text47, {
11846
12090
  color: newProviderColor
11847
- }, newProvider), /* @__PURE__ */ React53.createElement(Text46, {
12091
+ }, newProvider), /* @__PURE__ */ React54.createElement(Text47, {
11848
12092
  color: theme.diff.base
11849
- }, "\u2500".repeat(Math.max(0, innerW - oldProvider.length - newProvider.length - 10)), "\u2510")), /* @__PURE__ */ React53.createElement(Box37, {
12093
+ }, "\u2500".repeat(Math.max(0, innerW - oldProvider.length - newProvider.length - 10)), "\u2510")), /* @__PURE__ */ React54.createElement(Box38, {
11850
12094
  flexDirection: "column",
11851
12095
  flexGrow: 1,
11852
12096
  overflow: "hidden",
@@ -11854,86 +12098,86 @@ function DiffView({ savedSource, pendingSource, compiled, outputDir, diffKind, i
11854
12098
  borderColor: theme.diff.base,
11855
12099
  borderBackgroundColor: theme.ui.background,
11856
12100
  borderTop: false
11857
- }, /* @__PURE__ */ React53.createElement(Box37, {
12101
+ }, /* @__PURE__ */ React54.createElement(Box38, {
11858
12102
  paddingX: 1,
11859
12103
  height: 1,
11860
12104
  flexShrink: 0
11861
- }, /* @__PURE__ */ React53.createElement(Text46, {
12105
+ }, /* @__PURE__ */ React54.createElement(Text47, {
11862
12106
  color: theme.diff.base
11863
- }, "\u2500\u2500 File ", "\u2500".repeat(Math.max(0, innerW - 9)))), /* @__PURE__ */ React53.createElement(DiffRow, {
12107
+ }, "\u2500\u2500 File ", "\u2500".repeat(Math.max(0, innerW - 9)))), /* @__PURE__ */ React54.createElement(DiffRow, {
11864
12108
  label: "Provider",
11865
12109
  left: oldProvider,
11866
12110
  right: newProvider
11867
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12111
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11868
12112
  label: "Filename",
11869
12113
  left: oldFilename,
11870
12114
  right: newFilename
11871
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12115
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11872
12116
  label: "Directory",
11873
12117
  left: oldDir,
11874
12118
  right: newDir
11875
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12119
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11876
12120
  label: "Size",
11877
12121
  left: oldSize,
11878
12122
  right: newSize
11879
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12123
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11880
12124
  label: "Duration",
11881
12125
  left: oldDuration,
11882
12126
  right: newDuration
11883
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12127
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11884
12128
  label: "Saved",
11885
12129
  left: oldSavedAt,
11886
12130
  right: "(new)"
11887
- }), /* @__PURE__ */ React53.createElement(Box37, {
12131
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11888
12132
  height: 1,
11889
12133
  flexShrink: 0
11890
- }), /* @__PURE__ */ React53.createElement(Box37, {
12134
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11891
12135
  paddingX: 1,
11892
12136
  height: 1,
11893
12137
  flexShrink: 0
11894
- }, /* @__PURE__ */ React53.createElement(Text46, {
12138
+ }, /* @__PURE__ */ React54.createElement(Text47, {
11895
12139
  color: theme.diff.base
11896
- }, "\u2500\u2500 Metadata ", "\u2500".repeat(Math.max(0, innerW - 13)))), /* @__PURE__ */ React53.createElement(DiffRow, {
12140
+ }, "\u2500\u2500 Metadata ", "\u2500".repeat(Math.max(0, innerW - 13)))), /* @__PURE__ */ React54.createElement(DiffRow, {
11897
12141
  label: "Title",
11898
12142
  left: title,
11899
12143
  right: title
11900
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12144
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11901
12145
  label: "Artists",
11902
12146
  left: artists,
11903
12147
  right: artists
11904
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12148
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11905
12149
  label: "Album",
11906
12150
  left: album,
11907
12151
  right: album
11908
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12152
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11909
12153
  label: "Year",
11910
12154
  left: year,
11911
12155
  right: year
11912
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12156
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11913
12157
  label: "BPM",
11914
12158
  left: bpm,
11915
12159
  right: bpm
11916
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12160
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11917
12161
  label: "Key",
11918
12162
  left: key,
11919
12163
  right: key
11920
- }), /* @__PURE__ */ React53.createElement(DiffRow, {
12164
+ }), /* @__PURE__ */ React54.createElement(DiffRow, {
11921
12165
  label: "Genres",
11922
12166
  left: genres,
11923
12167
  right: genres
11924
- }), /* @__PURE__ */ React53.createElement(Box37, {
12168
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11925
12169
  flexGrow: 1
11926
- }), /* @__PURE__ */ React53.createElement(Box37, {
12170
+ }), /* @__PURE__ */ React54.createElement(Box38, {
11927
12171
  flexDirection: "row",
11928
12172
  paddingX: 1,
11929
12173
  height: 1,
11930
12174
  overflow: "hidden",
11931
12175
  flexShrink: 0
11932
- }, /* @__PURE__ */ React53.createElement(Hint, {
12176
+ }, /* @__PURE__ */ React54.createElement(Hint, {
11933
12177
  label: diffKind === "metadata-change" ? "Save changes" : "Switch source",
11934
12178
  shortcut: "Enter",
11935
12179
  dim: !isActive
11936
- }), /* @__PURE__ */ React53.createElement(Hint, {
12180
+ }), /* @__PURE__ */ React54.createElement(Hint, {
11937
12181
  label: "Cancel",
11938
12182
  shortcut: "Esc",
11939
12183
  dim: !isActive
@@ -11942,28 +12186,28 @@ function DiffView({ savedSource, pendingSource, compiled, outputDir, diffKind, i
11942
12186
  __name(DiffView, "DiffView");
11943
12187
 
11944
12188
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceDetail/DetailRow.tsx
11945
- import React54 from "react";
11946
- import { Box as Box38, Text as Text47 } from "ink";
12189
+ import React55 from "react";
12190
+ import { Box as Box39, Text as Text48 } from "ink";
11947
12191
  var LABEL_W3 = 11;
11948
12192
  function DetailRow({ label, value, valueColor, dim }) {
11949
12193
  const theme = useTheme();
11950
- return /* @__PURE__ */ React54.createElement(Box38, {
12194
+ return /* @__PURE__ */ React55.createElement(Box39, {
11951
12195
  flexDirection: "row",
11952
12196
  paddingX: 1,
11953
12197
  height: 1,
11954
12198
  flexShrink: 0
11955
- }, /* @__PURE__ */ React54.createElement(Box38, {
12199
+ }, /* @__PURE__ */ React55.createElement(Box39, {
11956
12200
  width: LABEL_W3,
11957
12201
  minWidth: LABEL_W3,
11958
12202
  flexShrink: 0,
11959
12203
  marginRight: 1
11960
- }, /* @__PURE__ */ React54.createElement(Text47, {
12204
+ }, /* @__PURE__ */ React55.createElement(Text48, {
11961
12205
  color: theme.ui.border,
11962
12206
  bold: true,
11963
12207
  wrap: "truncate-end"
11964
- }, label.toUpperCase().padEnd(LABEL_W3))), /* @__PURE__ */ React54.createElement(Box38, {
12208
+ }, label.toUpperCase().padEnd(LABEL_W3))), /* @__PURE__ */ React55.createElement(Box39, {
11965
12209
  flexGrow: 1
11966
- }, /* @__PURE__ */ React54.createElement(Text47, {
12210
+ }, /* @__PURE__ */ React55.createElement(Text48, {
11967
12211
  color: valueColor ?? theme.text.primary,
11968
12212
  dimColor: dim || value === "\u2014",
11969
12213
  wrap: "truncate-end"
@@ -11986,23 +12230,23 @@ function SectionDivider({ label, width }) {
11986
12230
  const theme = useTheme();
11987
12231
  const innerW = width - 2;
11988
12232
  const dashes = Math.max(0, innerW - label.length - 3);
11989
- return /* @__PURE__ */ React55.createElement(Box39, {
12233
+ return /* @__PURE__ */ React56.createElement(Box40, {
11990
12234
  paddingX: 1,
11991
12235
  height: 1,
11992
12236
  flexDirection: "column"
11993
- }, /* @__PURE__ */ React55.createElement(Text48, {
12237
+ }, /* @__PURE__ */ React56.createElement(Text49, {
11994
12238
  color: theme.text.secondary
11995
12239
  }, "\u2500\u2500 ", label, " " + "\u2500".repeat(dashes)));
11996
12240
  }
11997
12241
  __name(SectionDivider, "SectionDivider");
11998
12242
  var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compiled, outputDir, isDiffMode, diffKind, pendingSource, isActive, isSaving, width, height, onInnerFocusSwitch, onConfirmDiff, onCancelDiff, onRelocateFile, onSave }) => {
11999
12243
  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);
12244
+ const [isPlaying, setIsPlaying] = useState20(false);
12245
+ const [isPaused, setIsPaused] = useState20(false);
12246
+ const [positionMs, setPositionMs] = useState20(0);
12247
+ const [durationMs, setDurationMs] = useState20(0);
12004
12248
  const currentFilePath = source?.localFile?.path ?? null;
12005
- useEffect19(() => {
12249
+ useEffect20(() => {
12006
12250
  const player = getInstance();
12007
12251
  function syncFromStatus(status) {
12008
12252
  const isThisFile = status.filePath === currentFilePath;
@@ -12041,7 +12285,7 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12041
12285
  }, [
12042
12286
  currentFilePath
12043
12287
  ]);
12044
- useEffect19(() => {
12288
+ useEffect20(() => {
12045
12289
  return () => {
12046
12290
  const player = getInstance();
12047
12291
  if (player.getStatus().filePath === currentFilePath) {
@@ -12198,7 +12442,7 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12198
12442
  ]
12199
12443
  });
12200
12444
  if (isDiffMode && savedSource && pendingSource) {
12201
- return /* @__PURE__ */ React55.createElement(DiffView, {
12445
+ return /* @__PURE__ */ React56.createElement(DiffView, {
12202
12446
  savedSource,
12203
12447
  pendingSource,
12204
12448
  compiled,
@@ -12238,25 +12482,25 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12238
12482
  trackType,
12239
12483
  source.track.id
12240
12484
  ].filter(Boolean);
12241
- return /* @__PURE__ */ React55.createElement(Box39, {
12485
+ return /* @__PURE__ */ React56.createElement(Box40, {
12242
12486
  flexDirection: "column",
12243
12487
  width,
12244
12488
  height,
12245
12489
  overflow: "hidden"
12246
- }, /* @__PURE__ */ React55.createElement(Box39, {
12490
+ }, /* @__PURE__ */ React56.createElement(Box40, {
12247
12491
  flexDirection: "column",
12248
12492
  height: 1,
12249
12493
  flexShrink: 0,
12250
12494
  overflow: "hidden"
12251
- }, /* @__PURE__ */ React55.createElement(Box39, {
12495
+ }, /* @__PURE__ */ React56.createElement(Box40, {
12252
12496
  flexDirection: "row"
12253
- }, /* @__PURE__ */ React55.createElement(Text48, {
12497
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12254
12498
  color: borderColor
12255
- }, borderLeft), /* @__PURE__ */ React55.createElement(Text48, {
12499
+ }, borderLeft), /* @__PURE__ */ React56.createElement(Text49, {
12256
12500
  color: headerColor
12257
- }, headerLabel), /* @__PURE__ */ React55.createElement(Text48, {
12501
+ }, headerLabel), /* @__PURE__ */ React56.createElement(Text49, {
12258
12502
  color: borderColor
12259
- }, borderRight))), /* @__PURE__ */ React55.createElement(Box39, {
12503
+ }, borderRight))), /* @__PURE__ */ React56.createElement(Box40, {
12260
12504
  flexDirection: "column",
12261
12505
  flexGrow: 1,
12262
12506
  overflow: "hidden",
@@ -12264,120 +12508,120 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
12264
12508
  borderColor,
12265
12509
  borderBackgroundColor: theme.ui.background,
12266
12510
  borderTop: false
12267
- }, /* @__PURE__ */ React55.createElement(Box39, {
12511
+ }, /* @__PURE__ */ React56.createElement(Box40, {
12268
12512
  flexDirection: "column",
12269
12513
  flexGrow: 1,
12270
12514
  overflow: "hidden"
12271
- }, outputFileExists && /* @__PURE__ */ React55.createElement(Box39, {
12515
+ }, outputFileExists && /* @__PURE__ */ React56.createElement(Box40, {
12272
12516
  paddingX: 1,
12273
12517
  paddingBottom: 1,
12274
12518
  flexShrink: 0
12275
- }, /* @__PURE__ */ React55.createElement(Text48, {
12519
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12276
12520
  color: theme.status.warning,
12277
12521
  bold: true
12278
- }, "\u25B3 Warning: A file already exists! Please check before overwriting the file.")), canPlay && /* @__PURE__ */ React55.createElement(PlaybackBar, {
12522
+ }, "\u25B3 Warning: A file already exists! Please check before overwriting the file.")), canPlay && /* @__PURE__ */ React56.createElement(PlaybackBar, {
12279
12523
  positionMs,
12280
12524
  durationMs: displayDurationMs,
12281
12525
  width: innerW,
12282
12526
  isPlaying,
12283
12527
  isPaused
12284
- }), /* @__PURE__ */ React55.createElement(Box39, {
12528
+ }), /* @__PURE__ */ React56.createElement(Box40, {
12285
12529
  paddingX: 1,
12286
12530
  paddingBottom: 1,
12287
12531
  height: 1,
12288
12532
  flexShrink: 0,
12289
12533
  flexDirection: "row",
12290
12534
  overflow: "hidden"
12291
- }, /* @__PURE__ */ React55.createElement(Text48, {
12535
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12292
12536
  color: theme.text.secondary
12293
- }, "SOURCE "), sourceParts.map((part, idx) => /* @__PURE__ */ React55.createElement(React55.Fragment, {
12537
+ }, "SOURCE "), sourceParts.map((part, idx) => /* @__PURE__ */ React56.createElement(React56.Fragment, {
12294
12538
  key: idx
12295
- }, idx > 0 && /* @__PURE__ */ React55.createElement(Text48, {
12539
+ }, idx > 0 && /* @__PURE__ */ React56.createElement(Text49, {
12296
12540
  color: theme.text.secondary
12297
- }, " > "), /* @__PURE__ */ React55.createElement(Text48, {
12541
+ }, " > "), /* @__PURE__ */ React56.createElement(Text49, {
12298
12542
  color: dlProvider.color
12299
- }, part)))), fileNotFound ? /* @__PURE__ */ React55.createElement(Box39, {
12543
+ }, part)))), fileNotFound ? /* @__PURE__ */ React56.createElement(Box40, {
12300
12544
  flexDirection: "column",
12301
12545
  paddingX: 1,
12302
12546
  paddingY: 1
12303
- }, /* @__PURE__ */ React55.createElement(Text48, {
12547
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12304
12548
  color: theme.status.warning
12305
- }, "\u26A0 File not found"), /* @__PURE__ */ React55.createElement(Text48, {
12549
+ }, " \u25B3 File not found"), /* @__PURE__ */ React56.createElement(Text49, {
12306
12550
  color: theme.text.secondary,
12307
12551
  dimColor: true,
12308
12552
  wrap: "truncate-end"
12309
- }, "Last known: ", source.localFile?.path ?? "\u2014"), /* @__PURE__ */ React55.createElement(Box39, {
12553
+ }, "Last known: ", source.localFile?.path ?? "\u2014"), /* @__PURE__ */ React56.createElement(Box40, {
12310
12554
  marginTop: 1
12311
- }, /* @__PURE__ */ React55.createElement(Text48, {
12555
+ }, /* @__PURE__ */ React56.createElement(Text49, {
12312
12556
  color: theme.text.active,
12313
12557
  bold: true
12314
- }, "[Ctrl+F]"), /* @__PURE__ */ React55.createElement(Text48, {
12558
+ }, "[Ctrl+F]"), /* @__PURE__ */ React56.createElement(Text49, {
12315
12559
  color: theme.text.hint
12316
- }, " Relocate file"))) : /* @__PURE__ */ React55.createElement(Box39, {
12560
+ }, " Relocate file"))) : /* @__PURE__ */ React56.createElement(Box40, {
12317
12561
  flexDirection: "column",
12318
12562
  flexShrink: 0
12319
- }, compiled && /* @__PURE__ */ React55.createElement(React55.Fragment, null, /* @__PURE__ */ React55.createElement(DetailRow, {
12563
+ }, compiled && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(DetailRow, {
12320
12564
  label: "Title",
12321
12565
  value: compiled.trackName || "\u2014"
12322
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12566
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12323
12567
  label: "Artists",
12324
12568
  value: compiled.artists.map((a) => a.name).join(", ") || "\u2014"
12325
- }), compiled.album && /* @__PURE__ */ React55.createElement(DetailRow, {
12569
+ }), compiled.album && /* @__PURE__ */ React56.createElement(DetailRow, {
12326
12570
  label: "Album",
12327
12571
  value: compiled.album.albumName
12328
- }), compiled.year != null && /* @__PURE__ */ React55.createElement(DetailRow, {
12572
+ }), compiled.year != null && /* @__PURE__ */ React56.createElement(DetailRow, {
12329
12573
  label: "Year",
12330
12574
  value: String(compiled.year)
12331
- }), compiled.bpm != null && /* @__PURE__ */ React55.createElement(DetailRow, {
12575
+ }), compiled.bpm != null && /* @__PURE__ */ React56.createElement(DetailRow, {
12332
12576
  label: "BPM",
12333
12577
  value: String(compiled.bpm)
12334
- }), compiled.key && /* @__PURE__ */ React55.createElement(DetailRow, {
12578
+ }), compiled.key && /* @__PURE__ */ React56.createElement(DetailRow, {
12335
12579
  label: "Key",
12336
12580
  value: compiled.key
12337
- }), compiled.genres && compiled.genres.length > 0 && /* @__PURE__ */ React55.createElement(DetailRow, {
12581
+ }), compiled.genres && compiled.genres.length > 0 && /* @__PURE__ */ React56.createElement(DetailRow, {
12338
12582
  label: "Genres",
12339
12583
  value: compiled.genres.join(", ")
12340
- })), /* @__PURE__ */ React55.createElement(DetailRow, {
12584
+ })), /* @__PURE__ */ React56.createElement(DetailRow, {
12341
12585
  label: "File",
12342
12586
  value: filename
12343
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12587
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12344
12588
  label: "Format",
12345
12589
  value: formatLabel
12346
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12590
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12347
12591
  label: "Size",
12348
12592
  value: sizeText
12349
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12593
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12350
12594
  label: "Duration",
12351
12595
  value: formatDuration3(sourceDurationMs)
12352
- }), compiled && /* @__PURE__ */ React55.createElement(DetailRow, {
12596
+ }), compiled && /* @__PURE__ */ React56.createElement(DetailRow, {
12353
12597
  label: "Output",
12354
12598
  value: path13.join(outputDir, computeOutputFilename(compiled)),
12355
12599
  valueColor: theme.text.secondary
12356
- })), !fileNotFound && /* @__PURE__ */ React55.createElement(React55.Fragment, null, /* @__PURE__ */ React55.createElement(Box39, {
12600
+ })), !fileNotFound && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Box40, {
12357
12601
  height: 1,
12358
12602
  flexDirection: "column"
12359
- }), /* @__PURE__ */ React55.createElement(SectionDivider, {
12603
+ }), /* @__PURE__ */ React56.createElement(SectionDivider, {
12360
12604
  label: "Embedded Tags",
12361
12605
  width: innerW
12362
- }), PRIORITY_TAGS.map((tag) => /* @__PURE__ */ React55.createElement(DetailRow, {
12606
+ }), PRIORITY_TAGS.map((tag) => /* @__PURE__ */ React56.createElement(DetailRow, {
12363
12607
  key: tag,
12364
12608
  label: tag,
12365
12609
  value: tagValue(embeddedTags, tag)
12366
- })), otherTagKeys.map((tag) => /* @__PURE__ */ React55.createElement(DetailRow, {
12610
+ })), otherTagKeys.map((tag) => /* @__PURE__ */ React56.createElement(DetailRow, {
12367
12611
  key: tag,
12368
12612
  label: tag,
12369
12613
  value: tagValue(embeddedTags, tag)
12370
- }))), source.savedFile && /* @__PURE__ */ React55.createElement(Box39, {
12614
+ }))), source.savedFile && /* @__PURE__ */ React56.createElement(Box40, {
12371
12615
  flexDirection: "column",
12372
12616
  overflow: "hidden",
12373
12617
  marginTop: 1
12374
- }, /* @__PURE__ */ React55.createElement(SectionDivider, {
12618
+ }, /* @__PURE__ */ React56.createElement(SectionDivider, {
12375
12619
  label: "Saved",
12376
12620
  width: innerW
12377
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12621
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12378
12622
  label: "Path",
12379
12623
  value: source.savedFile.path
12380
- }), /* @__PURE__ */ React55.createElement(DetailRow, {
12624
+ }), /* @__PURE__ */ React56.createElement(DetailRow, {
12381
12625
  label: "Saved at",
12382
12626
  value: formatDate(source.savedFile.savedAt)
12383
12627
  })))));
@@ -12390,16 +12634,16 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12390
12634
  const { focusState, setSourcesInnerFocus } = useFocusContext();
12391
12635
  const { sourcesPanel } = focusState.secondaryPanel;
12392
12636
  const { innerFocus } = sourcesPanel;
12393
- const [selectedSourceIndex, setSelectedSourceIndex] = useState20(0);
12637
+ const [selectedSourceIndex, setSelectedSourceIndex] = useState21(0);
12394
12638
  const isPanelActive = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab === "downloadSources";
12395
12639
  const typedTask = selectedTask;
12396
- const [snapshot, setSnapshot] = useState20(() => typedTask?.get());
12397
- const [prevTypedTask, setPrevTypedTask] = useState20(typedTask);
12640
+ const [snapshot, setSnapshot] = useState21(() => typedTask?.get());
12641
+ const [prevTypedTask, setPrevTypedTask] = useState21(typedTask);
12398
12642
  if (prevTypedTask !== typedTask) {
12399
12643
  setPrevTypedTask(typedTask);
12400
12644
  setSnapshot(typedTask?.get());
12401
12645
  }
12402
- useEffect20(() => {
12646
+ useEffect21(() => {
12403
12647
  if (!typedTask) return;
12404
12648
  return typedTask.subscribe((t) => {
12405
12649
  setSnapshot(t.get());
@@ -12412,12 +12656,12 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12412
12656
  const compiled = snapshot?.attributes ? computeCompiledMetadata(snapshot.attributes.metadataGroups, snapshot.attributes.metadataOverride) : null;
12413
12657
  const { outputDir } = getSaveSettings();
12414
12658
  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);
12659
+ const [splitRatio, setSplitRatio] = useState21(0.6);
12416
12660
  const leftWidth = Math.floor(width * splitRatio) - 4;
12417
12661
  const rightWidth = width - leftWidth - 4;
12418
12662
  const listHeight = height - HINT_BAR_HEIGHT;
12419
- const [isDiffMode, setIsDiffMode] = useState20(false);
12420
- const [pendingSourceIndex, setPendingSourceIndex] = useState20(null);
12663
+ const [isDiffMode, setIsDiffMode] = useState21(false);
12664
+ const [pendingSourceIndex, setPendingSourceIndex] = useState21(null);
12421
12665
  const previewFilename = compiled ? computeOutputFilename(compiled) : null;
12422
12666
  const savedFilename = savedSource?.savedFile ? path14.basename(savedSource.savedFile.path) : null;
12423
12667
  const hasMetadataChange = previewFilename != null && savedFilename != null && previewFilename !== savedFilename;
@@ -12531,7 +12775,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12531
12775
  }
12532
12776
  __name(handleRelocateFile, "handleRelocateFile");
12533
12777
  const selectedSource = downloadNavIndex >= 0 ? downloadSources[downloadNavIndex] ?? null : null;
12534
- return /* @__PURE__ */ React56.createElement(Box40, {
12778
+ return /* @__PURE__ */ React57.createElement(Box41, {
12535
12779
  flexDirection: "column",
12536
12780
  height,
12537
12781
  overflow: "hidden",
@@ -12541,11 +12785,11 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12541
12785
  borderTop: false,
12542
12786
  borderBottom: false,
12543
12787
  paddingRight: 1
12544
- }, /* @__PURE__ */ React56.createElement(Box40, {
12788
+ }, /* @__PURE__ */ React57.createElement(Box41, {
12545
12789
  flexDirection: "row",
12546
12790
  flexGrow: 1,
12547
12791
  overflow: "hidden"
12548
- }, /* @__PURE__ */ React56.createElement(DownloadSourceTree, {
12792
+ }, /* @__PURE__ */ React57.createElement(DownloadSourceTree, {
12549
12793
  sources: downloadSources,
12550
12794
  selectedSourceIndex: downloadNavIndex,
12551
12795
  isActive: isPanelActive && innerFocus === "list",
@@ -12555,7 +12799,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12555
12799
  onRequestSelect: handleRequestSelect,
12556
12800
  onRejectSource: handleRejectSource,
12557
12801
  onInnerFocusSwitch: /* @__PURE__ */ __name(() => setSourcesInnerFocus("detail"), "onInnerFocusSwitch")
12558
- }), /* @__PURE__ */ React56.createElement(DownloadSourceDetail, {
12802
+ }), /* @__PURE__ */ React57.createElement(DownloadSourceDetail, {
12559
12803
  source: selectedSource,
12560
12804
  savedSource,
12561
12805
  compiled,
@@ -12572,7 +12816,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
12572
12816
  onCancelDiff: handleCancelDiff,
12573
12817
  onRelocateFile: handleRelocateFile,
12574
12818
  onSave: handleSave
12575
- })), /* @__PURE__ */ React56.createElement(DynamicHintBar, {
12819
+ })), /* @__PURE__ */ React57.createElement(DynamicHintBar, {
12576
12820
  width: width - 2,
12577
12821
  isActive: isPanelActive && innerFocus === "list"
12578
12822
  }));
@@ -12586,11 +12830,11 @@ var SecondaryPanel = /* @__PURE__ */ __name(({ tasks, width }) => {
12586
12830
  const contentHeight = Math.max(1, height - 1);
12587
12831
  const selectedTask = tasks[focusState.taskList.selectedTaskIndex] ?? null;
12588
12832
  const activeTabKey = subTab === "metadataSources" ? "3" : subTab === "downloadSources" ? "4" : "5";
12589
- return /* @__PURE__ */ React57.createElement(Box41, {
12833
+ return /* @__PURE__ */ React58.createElement(Box42, {
12590
12834
  flexDirection: "column",
12591
12835
  height,
12592
12836
  overflow: "hidden"
12593
- }, /* @__PURE__ */ React57.createElement(TabBar, {
12837
+ }, /* @__PURE__ */ React58.createElement(TabBar, {
12594
12838
  width,
12595
12839
  tabs: [
12596
12840
  {
@@ -12607,26 +12851,26 @@ var SecondaryPanel = /* @__PURE__ */ __name(({ tasks, width }) => {
12607
12851
  }
12608
12852
  ],
12609
12853
  activeTabKey
12610
- }), subTab === "metadataSources" && /* @__PURE__ */ React57.createElement(MetadataPanel, {
12854
+ }), subTab === "metadataSources" && /* @__PURE__ */ React58.createElement(MetadataPanel, {
12611
12855
  selectedTask,
12612
12856
  width,
12613
12857
  height: contentHeight
12614
- }), subTab === "downloadSources" && /* @__PURE__ */ React57.createElement(DownloadPanel, {
12858
+ }), subTab === "downloadSources" && /* @__PURE__ */ React58.createElement(DownloadPanel, {
12615
12859
  selectedTask,
12616
12860
  width,
12617
12861
  height: contentHeight
12618
- }), subTab === "logs" && /* @__PURE__ */ React57.createElement(LogPanel, {
12862
+ }), subTab === "logs" && /* @__PURE__ */ React58.createElement(LogPanel, {
12619
12863
  tasks,
12620
12864
  height: contentHeight
12621
12865
  }));
12622
12866
  }, "SecondaryPanel");
12623
12867
 
12624
12868
  // src/components/ShortcutDispatcher.tsx
12625
- import React59 from "react";
12869
+ import React60 from "react";
12626
12870
  import { useInput } from "ink";
12627
12871
 
12628
12872
  // src/contexts/ImportActionsContext.tsx
12629
- import React58, { createContext as createContext4, useContext as useContext4 } from "react";
12873
+ import React59, { createContext as createContext4, useContext as useContext4 } from "react";
12630
12874
  var ImportActionsContext = /* @__PURE__ */ createContext4(null);
12631
12875
  var useImportActions = /* @__PURE__ */ __name(() => {
12632
12876
  const ctx = useContext4(ImportActionsContext);
@@ -12634,7 +12878,7 @@ var useImportActions = /* @__PURE__ */ __name(() => {
12634
12878
  return ctx;
12635
12879
  }, "useImportActions");
12636
12880
  var ImportActionsProvider = /* @__PURE__ */ __name(({ children, openImportFlow }) => {
12637
- return /* @__PURE__ */ React58.createElement(ImportActionsContext.Provider, {
12881
+ return /* @__PURE__ */ React59.createElement(ImportActionsContext.Provider, {
12638
12882
  value: {
12639
12883
  openImportFlow
12640
12884
  }
@@ -12655,11 +12899,11 @@ var ShortcutDispatcher = /* @__PURE__ */ __name(() => {
12655
12899
  }, "ShortcutDispatcher");
12656
12900
 
12657
12901
  // src/components/InputRouter.tsx
12658
- import React60 from "react";
12902
+ import React61 from "react";
12659
12903
  var InputRouter = /* @__PURE__ */ __name(({ tasks, flow }) => {
12660
12904
  const { focusState, handleTabPress, switchMode, setPrimaryMode, setSecondaryTab } = useFocusContext();
12661
12905
  const { openImportFlow } = useImportActions();
12662
- const isMainScreen = focusState.activeWindow !== "importModal" && focusState.activeWindow !== "settingsModal" && focusState.activeWindow !== "setupWizardModal" && focusState.activeWindow !== "welcomeModal";
12906
+ const isMainScreen = focusState.activeWindow !== "importModal" && focusState.activeWindow !== "settingsModal" && focusState.activeWindow !== "setupWizardModal" && focusState.activeWindow !== "welcomeModal" && focusState.activeWindow !== "updateModal";
12663
12907
  const isDetailFocused = focusState.activeWindow === "secondaryPanel" && focusState.secondaryPanel.subTab !== "logs" && focusState.secondaryPanel.sourcesPanel.innerFocus === "detail";
12664
12908
  useShortcuts({
12665
12909
  id: "global",
@@ -12741,7 +12985,7 @@ var InputRouter = /* @__PURE__ */ __name(({ tasks, flow }) => {
12741
12985
  }, "InputRouter");
12742
12986
 
12743
12987
  // src/components/ImportModal/useImportFlow.ts
12744
- import { useState as useState21, useCallback as useCallback5 } from "react";
12988
+ import { useState as useState22, useCallback as useCallback6 } from "react";
12745
12989
 
12746
12990
  // src/components/ImportModal/clipboard.ts
12747
12991
  import { execSync } from "child_process";
@@ -12780,8 +13024,8 @@ __name(detectUrls, "detectUrls");
12780
13024
  // src/components/ImportModal/useImportFlow.ts
12781
13025
  function useImportFlow(currentFlow) {
12782
13026
  const { focusState } = useFocusContext();
12783
- const [pendingImport, setPendingImport] = useState21(null);
12784
- const openImportFlow = useCallback5((text) => {
13027
+ const [pendingImport, setPendingImport] = useState22(null);
13028
+ const openImportFlow = useCallback6((text) => {
12785
13029
  if (focusState.activeWindow === "prompt") return;
12786
13030
  const handle = /* @__PURE__ */ __name((raw) => {
12787
13031
  const urls = detectUrls(raw);
@@ -12819,7 +13063,7 @@ function useImportFlow(currentFlow) {
12819
13063
  }, [
12820
13064
  focusState.activeWindow
12821
13065
  ]);
12822
- const handleImportConfirm = useCallback5(({ fetchMetadata, download }) => {
13066
+ const handleImportConfirm = useCallback6(({ fetchMetadata, download }) => {
12823
13067
  if (!pendingImport || !currentFlow) {
12824
13068
  setPendingImport(null);
12825
13069
  return;
@@ -12835,7 +13079,7 @@ function useImportFlow(currentFlow) {
12835
13079
  pendingImport,
12836
13080
  currentFlow
12837
13081
  ]);
12838
- const handleImportCancel = useCallback5(() => {
13082
+ const handleImportCancel = useCallback6(() => {
12839
13083
  setPendingImport(null);
12840
13084
  }, []);
12841
13085
  return {
@@ -12849,84 +13093,186 @@ function useImportFlow(currentFlow) {
12849
13093
  __name(useImportFlow, "useImportFlow");
12850
13094
 
12851
13095
  // src/components/AppInner.tsx
12852
- var AppInner = /* @__PURE__ */ __name(({ tasks, filteredTasks, toolbarButtons, columns, currentFlow, orchestrator, setActiveFlowId, terminalHeight, terminalWidth }) => {
13096
+ var AppInner = /* @__PURE__ */ __name(({ tasks, filteredTasks, toolbarButtons, columns, currentFlow, orchestrator, setActiveFlowId, terminalHeight, terminalWidth, updateInfo }) => {
12853
13097
  const theme = useTheme();
12854
13098
  const { pendingImport, openImportFlow, handleImportConfirm, handleImportCancel } = useImportFlow(currentFlow);
12855
- return /* @__PURE__ */ React61.createElement(ImportActionsProvider, {
13099
+ return /* @__PURE__ */ React62.createElement(ImportActionsProvider, {
12856
13100
  openImportFlow
12857
- }, /* @__PURE__ */ React61.createElement(ShortcutDispatcher, null), /* @__PURE__ */ React61.createElement(InputRouter, {
13101
+ }, /* @__PURE__ */ React62.createElement(ShortcutDispatcher, null), /* @__PURE__ */ React62.createElement(InputRouter, {
12858
13102
  tasks: filteredTasks,
12859
13103
  flow: currentFlow
12860
- }), /* @__PURE__ */ React61.createElement(Box42, {
13104
+ }), /* @__PURE__ */ React62.createElement(Box43, {
12861
13105
  flexDirection: "column",
12862
13106
  height: terminalHeight,
12863
13107
  backgroundColor: theme.ui.background
12864
- }, currentFlow && /* @__PURE__ */ React61.createElement(Toolbar, {
13108
+ }, currentFlow && /* @__PURE__ */ React62.createElement(Toolbar, {
12865
13109
  buttons: toolbarButtons,
12866
13110
  width: terminalWidth,
12867
13111
  flows: orchestrator.getAllFlows(),
12868
13112
  onFlowChange: setActiveFlowId,
12869
13113
  flow: currentFlow,
12870
- orchestrator
12871
- }), currentFlow && /* @__PURE__ */ React61.createElement(TaskListPanel, {
13114
+ orchestrator,
13115
+ updateInfo
13116
+ }), currentFlow && /* @__PURE__ */ React62.createElement(TaskListPanel, {
12872
13117
  columns,
12873
13118
  tasks: filteredTasks,
12874
13119
  width: terminalWidth,
12875
13120
  flow: currentFlow
12876
- }), /* @__PURE__ */ React61.createElement(SecondaryPanel, {
13121
+ }), /* @__PURE__ */ React62.createElement(SecondaryPanel, {
12877
13122
  tasks: filteredTasks,
12878
13123
  width: terminalWidth,
12879
13124
  flow: currentFlow
12880
- }), /* @__PURE__ */ React61.createElement(Separator, {
13125
+ }), /* @__PURE__ */ React62.createElement(Separator, {
12881
13126
  width: terminalWidth
12882
- }), /* @__PURE__ */ React61.createElement(Footer, null), /* @__PURE__ */ React61.createElement(PromptModal, {
13127
+ }), /* @__PURE__ */ React62.createElement(Footer, null), /* @__PURE__ */ React62.createElement(PromptModal, {
12883
13128
  tasks,
12884
13129
  terminalHeight,
12885
13130
  terminalWidth
12886
- }), /* @__PURE__ */ React61.createElement(ImportModal, {
13131
+ }), /* @__PURE__ */ React62.createElement(ImportModal, {
12887
13132
  pendingImport,
12888
13133
  terminalHeight,
12889
13134
  terminalWidth,
12890
13135
  onConfirm: handleImportConfirm,
12891
13136
  onCancel: handleImportCancel
12892
- }), /* @__PURE__ */ React61.createElement(SettingsModal, {
13137
+ }), /* @__PURE__ */ React62.createElement(SettingsModal, {
12893
13138
  terminalHeight,
12894
13139
  terminalWidth,
12895
13140
  currentFlow
12896
- }), /* @__PURE__ */ React61.createElement(SetupWizardModal, {
13141
+ }), /* @__PURE__ */ React62.createElement(SetupWizardModal, {
12897
13142
  tasks,
12898
13143
  terminalHeight,
12899
13144
  terminalWidth
12900
- }), /* @__PURE__ */ React61.createElement(WelcomeModal, {
13145
+ }), /* @__PURE__ */ React62.createElement(WelcomeModal, {
13146
+ terminalHeight,
13147
+ terminalWidth
13148
+ }), updateInfo && /* @__PURE__ */ React62.createElement(UpdateModal, {
13149
+ latestVersion: updateInfo.latestVersion,
13150
+ releaseUrl: updateInfo.releaseUrl,
12901
13151
  terminalHeight,
12902
13152
  terminalWidth
12903
13153
  })));
12904
13154
  }, "AppInner");
12905
13155
 
13156
+ // src/components/Toolbar/useSettingsButton.ts
13157
+ var useSettingsButton = /* @__PURE__ */ __name(() => {
13158
+ const theme = useTheme();
13159
+ const { switchWindow } = useFocusContext();
13160
+ return {
13161
+ label: "Settings",
13162
+ icon: "\u26ED",
13163
+ color: theme.action.neutral,
13164
+ enabled: true,
13165
+ onPress: /* @__PURE__ */ __name(() => switchWindow("settingsModal"), "onPress")
13166
+ };
13167
+ }, "useSettingsButton");
13168
+
13169
+ // src/components/Toolbar/useExitButton.ts
13170
+ var LEAVE_ALT_SCREEN_COMMAND = "\x1B[?1049l";
13171
+ var useExitButton = /* @__PURE__ */ __name(() => {
13172
+ const theme = useTheme();
13173
+ return {
13174
+ label: "Exit",
13175
+ icon: "\u21B3",
13176
+ color: theme.action.destructive,
13177
+ enabled: true,
13178
+ onPress: /* @__PURE__ */ __name(() => {
13179
+ process.stdout.write(LEAVE_ALT_SCREEN_COMMAND);
13180
+ console.info("Saving cache before exit\u2026");
13181
+ cache.save();
13182
+ process.exit(0);
13183
+ }, "onPress")
13184
+ };
13185
+ }, "useExitButton");
13186
+
13187
+ // src/updater/updateChecker.ts
13188
+ function isNewer(local, remote) {
13189
+ const parse = /* @__PURE__ */ __name((v) => v.replace(/^v/, "").split(".").map(Number), "parse");
13190
+ const [lMaj = 0, lMin = 0, lPatch = 0] = parse(local);
13191
+ const [rMaj = 0, rMin = 0, rPatch = 0] = parse(remote);
13192
+ if (rMaj !== lMaj) return rMaj > lMaj;
13193
+ if (rMin !== lMin) return rMin > lMin;
13194
+ return rPatch > lPatch;
13195
+ }
13196
+ __name(isNewer, "isNewer");
13197
+ async function checkForUpdate() {
13198
+ try {
13199
+ const res = await fetch("https://api.github.com/repos/Tetraxel/goblin-malin/releases/latest", {
13200
+ headers: {
13201
+ "User-Agent": "goblin-malin-updater"
13202
+ },
13203
+ signal: AbortSignal.timeout(8e3)
13204
+ });
13205
+ if (!res.ok) return null;
13206
+ const data = await res.json();
13207
+ const exeAsset = IS_SEA ? data.assets.find((a) => a.name.endsWith(".exe")) : void 0;
13208
+ return {
13209
+ hasUpdate: isNewer(APP_VERSION, data.tag_name),
13210
+ latestVersion: data.tag_name.replace(/^v/, ""),
13211
+ releaseUrl: data.html_url,
13212
+ downloadUrl: exeAsset?.browser_download_url ?? null
13213
+ };
13214
+ } catch {
13215
+ return null;
13216
+ }
13217
+ }
13218
+ __name(checkForUpdate, "checkForUpdate");
13219
+
12906
13220
  // src/components/App.tsx
12907
13221
  var App = /* @__PURE__ */ __name(() => {
12908
- useEffect21(() => {
13222
+ useEffect22(() => {
12909
13223
  const initWav = getAssetPath("sounds", "init.wav");
12910
13224
  const player = getInstance();
12911
13225
  player.setVolume(50).then(() => player.play(initWav)).catch(() => {
12912
13226
  });
12913
13227
  }, []);
12914
- const [tasks, setTasks] = useState22([]);
13228
+ const [tasks, setTasks] = useState23([]);
12915
13229
  const { height: terminalHeight, width: terminalWidth } = useScreenSize();
12916
13230
  const orchestrator = FlowOrchestrator.getInstance();
12917
- const [activeFlowId, setActiveFlowId] = useState22(() => {
13231
+ const [activeFlowId, setActiveFlowId] = useState23(() => {
12918
13232
  orchestrator.registerFlow(MusicDownloadFlow, true);
12919
13233
  return orchestrator.getEnabledFlows()?.[0].id;
12920
13234
  });
12921
- const [toolbarButtons, setToolbarButtons] = useState22([]);
12922
- const [columns, setColumns] = useState22([]);
13235
+ const [toolbarButtons, setToolbarButtons] = useState23([]);
13236
+ const [columns, setColumns] = useState23([]);
12923
13237
  const currentFlow = activeFlowId ? orchestrator.getFlow(activeFlowId) : void 0;
12924
- useEffect21(() => {
13238
+ const [updateInfo, setUpdateInfo] = useState23(null);
13239
+ const [pendingUpdate, setPendingUpdate] = useState23(null);
13240
+ const isOrchestratorIdle = useCallback7((orch) => orch.getTasksInProgress().length === 0, []);
13241
+ useEffect22(() => {
13242
+ const settings = SettingsStore.getInstance().getAppSettings();
13243
+ if (!settings.general.checkForUpdates) return;
13244
+ checkForUpdate().then((info) => {
13245
+ if (!info?.hasUpdate) return;
13246
+ if (isOrchestratorIdle(orchestrator)) {
13247
+ setUpdateInfo(info);
13248
+ } else {
13249
+ setPendingUpdate(info);
13250
+ }
13251
+ });
13252
+ }, []);
13253
+ useEffect22(() => {
13254
+ if (!pendingUpdate) return;
13255
+ return orchestrator.subscribe((orch) => {
13256
+ if (isOrchestratorIdle(orch)) {
13257
+ setUpdateInfo(pendingUpdate);
13258
+ setPendingUpdate(null);
13259
+ }
13260
+ });
13261
+ }, [
13262
+ pendingUpdate,
13263
+ orchestrator,
13264
+ isOrchestratorIdle
13265
+ ]);
13266
+ useEffect22(() => {
12925
13267
  if (!currentFlow) return;
12926
13268
  globalLogger.debug(`Active flow changed: ${currentFlow?.displayName}`);
12927
13269
  const unsubscribe = currentFlow.subscribe((_updatedFlow) => {
12928
- globalLogger.debug(`flow state changed, updating UI...`);
12929
- setToolbarButtons(currentFlow.getToolbarButtons() ?? []);
13270
+ const buttons = [
13271
+ ...currentFlow.getToolbarButtons() ?? [],
13272
+ useSettingsButton,
13273
+ useExitButton
13274
+ ];
13275
+ setToolbarButtons(buttons);
12930
13276
  setColumns(currentFlow.getColumns() ?? []);
12931
13277
  });
12932
13278
  return unsubscribe;
@@ -12934,7 +13280,7 @@ var App = /* @__PURE__ */ __name(() => {
12934
13280
  currentFlow,
12935
13281
  currentFlow?.id
12936
13282
  ]);
12937
- useEffect21(() => {
13283
+ useEffect22(() => {
12938
13284
  const unsubscribe = orchestrator.subscribe((orchestrator2) => {
12939
13285
  setTasks(orchestrator2.getTasks());
12940
13286
  });
@@ -12947,11 +13293,11 @@ var App = /* @__PURE__ */ __name(() => {
12947
13293
  tasks,
12948
13294
  activeFlowId
12949
13295
  ]);
12950
- return /* @__PURE__ */ React62.createElement(ThemeProvider, null, /* @__PURE__ */ React62.createElement(ShortcutRegistryProvider, null, /* @__PURE__ */ React62.createElement(FocusProvider, {
12951
- toolbarButtonCount: toolbarButtons.length,
13296
+ return /* @__PURE__ */ React63.createElement(ThemeProvider, null, /* @__PURE__ */ React63.createElement(ShortcutRegistryProvider, null, /* @__PURE__ */ React63.createElement(FocusProvider, {
13297
+ toolbarButtonCount: toolbarButtons.length + (updateInfo ? 1 : 0),
12952
13298
  taskCount: filteredTasks.length,
12953
13299
  taskColumnCount: columns.length
12954
- }, /* @__PURE__ */ React62.createElement(ToolbarActionsProvider, null, /* @__PURE__ */ React62.createElement(AppInner, {
13300
+ }, /* @__PURE__ */ React63.createElement(ToolbarActionsProvider, null, /* @__PURE__ */ React63.createElement(AppInner, {
12955
13301
  tasks,
12956
13302
  filteredTasks,
12957
13303
  toolbarButtons,
@@ -12960,17 +13306,18 @@ var App = /* @__PURE__ */ __name(() => {
12960
13306
  orchestrator,
12961
13307
  setActiveFlowId,
12962
13308
  terminalHeight,
12963
- terminalWidth
13309
+ terminalWidth,
13310
+ updateInfo
12964
13311
  })))));
12965
13312
  }, "App");
12966
13313
 
12967
13314
  // src/components/FullScreenBox.tsx
12968
- import React63 from "react";
12969
- import { Box as Box43 } from "ink";
13315
+ import React64 from "react";
13316
+ import { Box as Box44 } from "ink";
12970
13317
  import { forwardRef } from "react";
12971
13318
  var FullScreenBox = /* @__PURE__ */ forwardRef(/* @__PURE__ */ __name(function FullScreenBox2(props, ref) {
12972
13319
  const { height, width } = useScreenSize();
12973
- return /* @__PURE__ */ React63.createElement(Box43, {
13320
+ return /* @__PURE__ */ React64.createElement(Box44, {
12974
13321
  ref,
12975
13322
  height,
12976
13323
  width,
@@ -13003,7 +13350,7 @@ var withFullScreen = /* @__PURE__ */ __name((node, options) => {
13003
13350
  return {
13004
13351
  instance,
13005
13352
  start: /* @__PURE__ */ __name(async () => {
13006
- instance.rerender(/* @__PURE__ */ React64.createElement(FullScreenBox, null, node));
13353
+ instance.rerender(/* @__PURE__ */ React65.createElement(FullScreenBox, null, node));
13007
13354
  }, "start"),
13008
13355
  waitUntilExit
13009
13356
  };
@@ -13026,7 +13373,7 @@ function start() {
13026
13373
  } catch {
13027
13374
  }
13028
13375
  process2.stdout.write("\x1B[?1049h");
13029
- const instance = render(/* @__PURE__ */ React64.createElement(App, null), {
13376
+ const instance = render(/* @__PURE__ */ React65.createElement(App, null), {
13030
13377
  patchConsole: true,
13031
13378
  maxFps: 60,
13032
13379
  exitOnCtrlC: false