open-nami 1.2.6 → 1.2.7

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.
Files changed (2) hide show
  1. package/dist/cli.js +116 -25
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ var require_package = __commonJS({
9
9
  "package.json"(exports, module) {
10
10
  module.exports = {
11
11
  name: "open-nami",
12
- version: "1.2.6",
12
+ version: "1.2.7",
13
13
  description: "Open Nami: terminal-first bulk social media profile downloader for Instagram, TikTok, Facebook & X.",
14
14
  type: "module",
15
15
  bin: {
@@ -5352,9 +5352,9 @@ function startClipboardWatcher(options = {}) {
5352
5352
  try {
5353
5353
  const res = await dispatch({
5354
5354
  profile: parsed,
5355
- choice,
5356
- outputDir: options.outputDir,
5357
- writeMetadata: options.writeMetadata,
5355
+ choice: options.getChoice ? options.getChoice() : options.choice || "all",
5356
+ outputDir: resolveDefaultDownloadDir(parsed.platform, parsed.username, options.outputDir),
5357
+ writeMetadata: options.getWriteMetadata ? options.getWriteMetadata() : options.writeMetadata ?? false,
5358
5358
  signal: options.signal,
5359
5359
  onProgress: (p) => {
5360
5360
  if (p.statusText) {
@@ -5444,16 +5444,19 @@ function revealInFileManager(targetPath) {
5444
5444
 
5445
5445
  // src/components/views/watcher-view.tsx
5446
5446
  import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
5447
+ var CHOICES = ["all", "photos", "videos", "stories", "highlights"];
5447
5448
  function WatcherView({
5448
5449
  onBack,
5449
5450
  outputDir,
5450
5451
  theme,
5451
5452
  gapComponent: Gap2,
5452
5453
  defaultChoice = "all",
5453
- defaultWriteMetadata = false
5454
+ defaultWriteMetadata = false,
5455
+ onRegisterClickTargets
5454
5456
  }) {
5455
5457
  const [mediaChoice, setMediaChoice] = useState7(defaultChoice);
5456
5458
  const [writeMetadata, setWriteMetadata] = useState7(defaultWriteMetadata);
5459
+ const [focusedIndex, setFocusedIndex] = useState7(0);
5457
5460
  const [history, setHistory] = useState7([]);
5458
5461
  const [activeJob, setActiveJob] = useState7(null);
5459
5462
  const [stats, setStats] = useState7({ detected: 0, downloaded: 0, skipped: 0 });
@@ -5471,8 +5474,10 @@ function WatcherView({
5471
5474
  useEffect6(() => {
5472
5475
  const watcher = startClipboardWatcher({
5473
5476
  choice: mediaChoiceRef.current,
5477
+ getChoice: () => mediaChoiceRef.current,
5474
5478
  outputDir,
5475
5479
  writeMetadata: writeMetadataRef.current,
5480
+ getWriteMetadata: () => writeMetadataRef.current,
5476
5481
  onDetected: (profile) => {
5477
5482
  setStats((prev) => ({ ...prev, detected: prev.detected + 1 }));
5478
5483
  const newItem = {
@@ -5528,9 +5533,9 @@ function WatcherView({
5528
5533
  }, [outputDir]);
5529
5534
  const cycleChoice = useCallback3(() => {
5530
5535
  setMediaChoice((prev) => {
5531
- if (prev === "all") return "photos";
5532
- if (prev === "photos") return "videos";
5533
- return "all";
5536
+ const idx = CHOICES.indexOf(prev);
5537
+ const nextChoice = CHOICES[(idx + 1) % CHOICES.length] || "all";
5538
+ return nextChoice;
5534
5539
  });
5535
5540
  }, []);
5536
5541
  const toggleMetadata = useCallback3(() => {
@@ -5540,20 +5545,68 @@ function WatcherView({
5540
5545
  const target = outputDir || resolveDefaultDownloadDir("instagram", "watcher");
5541
5546
  revealInFileManager(target);
5542
5547
  }, [outputDir]);
5548
+ useEffect6(() => {
5549
+ if (!onRegisterClickTargets) return;
5550
+ const targets = [
5551
+ { match: "Target:", padX: 1, action: () => {
5552
+ setFocusedIndex(0);
5553
+ cycleChoice();
5554
+ } },
5555
+ { match: "Sidecars:", padX: 1, action: () => {
5556
+ setFocusedIndex(1);
5557
+ toggleMetadata();
5558
+ } },
5559
+ { match: "Open Folder", padX: 1, action: () => {
5560
+ setFocusedIndex(2);
5561
+ openFolder();
5562
+ } },
5563
+ { match: "c target", padX: 1, action: () => {
5564
+ setFocusedIndex(0);
5565
+ cycleChoice();
5566
+ } },
5567
+ { match: "m sidecar", padX: 1, action: () => {
5568
+ setFocusedIndex(1);
5569
+ toggleMetadata();
5570
+ } },
5571
+ { match: "o open dir", padX: 1, action: () => {
5572
+ setFocusedIndex(2);
5573
+ openFolder();
5574
+ } },
5575
+ { match: "esc back", padX: 1, action: onBack }
5576
+ ];
5577
+ onRegisterClickTargets(targets);
5578
+ }, [onRegisterClickTargets, cycleChoice, toggleMetadata, openFolder, onBack, mediaChoice, writeMetadata]);
5543
5579
  useInput4((input, key) => {
5544
5580
  if (key.escape) {
5545
5581
  onBack();
5546
5582
  return;
5547
5583
  }
5584
+ if (key.leftArrow) {
5585
+ setFocusedIndex((prev) => prev === 0 ? 2 : prev - 1);
5586
+ return;
5587
+ }
5588
+ if (key.rightArrow || key.tab) {
5589
+ setFocusedIndex((prev) => prev === 2 ? 0 : prev + 1);
5590
+ return;
5591
+ }
5592
+ if (key.return || input === " ") {
5593
+ if (focusedIndex === 0) cycleChoice();
5594
+ else if (focusedIndex === 1) toggleMetadata();
5595
+ else if (focusedIndex === 2) openFolder();
5596
+ return;
5597
+ }
5548
5598
  if (input === "c" || input === "C") {
5599
+ setFocusedIndex(0);
5549
5600
  cycleChoice();
5550
5601
  return;
5551
5602
  }
5552
5603
  if (input === "m" || input === "M") {
5604
+ setFocusedIndex(1);
5553
5605
  toggleMetadata();
5554
5606
  return;
5555
5607
  }
5556
5608
  if (input === "o" || input === "O") {
5609
+ setFocusedIndex(2);
5557
5610
  openFolder();
5558
5611
  return;
5559
5612
  }
@@ -5569,8 +5622,10 @@ function WatcherView({
5569
5622
  ] }),
5570
5623
  /* @__PURE__ */ jsx16(ProgressBar, { indeterminate: true, width: 58, tick: pulseTick }),
5571
5624
  /* @__PURE__ */ jsx16(Gap2, {}),
5572
- /* @__PURE__ */ jsx16(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: "Listening for Instagram, TikTok, Facebook, or X profile links in clipboard." }),
5573
- /* @__PURE__ */ jsx16(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: "Simply copy any profile link in your browser and Open Nami will download it!" }),
5625
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
5626
+ /* @__PURE__ */ jsx16(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: "Auto-monitoring clipboard for profile links (Instagram, TikTok, FB, X)." }),
5627
+ /* @__PURE__ */ jsx16(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: "Copy any profile link in your browser: Open Nami downloads it in background." })
5628
+ ] }),
5574
5629
  /* @__PURE__ */ jsx16(Gap2, {}),
5575
5630
  /* @__PURE__ */ jsx16(
5576
5631
  Box13,
@@ -5606,15 +5661,42 @@ function WatcherView({
5606
5661
  ] }) : null,
5607
5662
  /* @__PURE__ */ jsx16(Gap2, {}),
5608
5663
  /* @__PURE__ */ jsxs13(Box13, { justifyContent: "space-between", children: [
5609
- /* @__PURE__ */ jsxs13(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: [
5610
- "[c] Target: ",
5611
- /* @__PURE__ */ jsx16(Text15, { color: theme.primary, bold: true, children: mediaChoice.toUpperCase() })
5612
- ] }),
5613
- /* @__PURE__ */ jsxs13(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: [
5614
- "[m] Sidecars: ",
5615
- /* @__PURE__ */ jsx16(Text15, { color: theme.primary, bold: true, children: writeMetadata ? "ON" : "OFF" })
5616
- ] }),
5617
- /* @__PURE__ */ jsx16(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: "[o] Open Folder" })
5664
+ /* @__PURE__ */ jsx16(
5665
+ Box13,
5666
+ {
5667
+ borderStyle: "round",
5668
+ borderColor: focusedIndex === 0 ? theme.primary : theme.gray,
5669
+ borderDimColor: focusedIndex !== 0,
5670
+ paddingX: 1,
5671
+ children: /* @__PURE__ */ jsxs13(Text15, { color: focusedIndex === 0 ? theme.primary : theme.gray, bold: focusedIndex === 0, children: [
5672
+ "[c] Target: ",
5673
+ /* @__PURE__ */ jsx16(Text15, { color: theme.primary, bold: true, children: mediaChoice === "all" ? "EVERYTHING" : mediaChoice.toUpperCase() })
5674
+ ] })
5675
+ }
5676
+ ),
5677
+ /* @__PURE__ */ jsx16(
5678
+ Box13,
5679
+ {
5680
+ borderStyle: "round",
5681
+ borderColor: focusedIndex === 1 ? theme.primary : theme.gray,
5682
+ borderDimColor: focusedIndex !== 1,
5683
+ paddingX: 1,
5684
+ children: /* @__PURE__ */ jsxs13(Text15, { color: focusedIndex === 1 ? theme.primary : theme.gray, bold: focusedIndex === 1, children: [
5685
+ "[m] Sidecars: ",
5686
+ /* @__PURE__ */ jsx16(Text15, { color: theme.primary, bold: true, children: writeMetadata ? "ON" : "OFF" })
5687
+ ] })
5688
+ }
5689
+ ),
5690
+ /* @__PURE__ */ jsx16(
5691
+ Box13,
5692
+ {
5693
+ borderStyle: "round",
5694
+ borderColor: focusedIndex === 2 ? theme.primary : theme.gray,
5695
+ borderDimColor: focusedIndex !== 2,
5696
+ paddingX: 1,
5697
+ children: /* @__PURE__ */ jsx16(Text15, { color: focusedIndex === 2 ? theme.primary : theme.gray, bold: focusedIndex === 2, children: "[o] Open Folder" })
5698
+ }
5699
+ )
5618
5700
  ] }),
5619
5701
  /* @__PURE__ */ jsx16(Box13, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs13(Text15, { color: theme.gray, dimColor: theme.dimSecondary, children: [
5620
5702
  "Session Stats: ",
@@ -5756,7 +5838,7 @@ async function runBatchQueue(options) {
5756
5838
  // src/components/views/batch-view.tsx
5757
5839
  import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
5758
5840
  var PLATFORMS = ["instagram", "tiktok", "facebook", "x"];
5759
- var CHOICES = ["all", "photos", "videos", "stories", "highlights"];
5841
+ var CHOICES2 = ["all", "photos", "videos", "stories", "highlights"];
5760
5842
  var DATE_PRESETS2 = ["all", "24h", "7d", "30d", "2024", "2023", "2022", "2021", "custom"];
5761
5843
  var LIMIT_PRESETS2 = [void 0, 10, 24, 50, 100, "custom"];
5762
5844
  function BatchView({
@@ -5798,8 +5880,8 @@ function BatchView({
5798
5880
  }, []);
5799
5881
  const cycleChoice = useCallback4(() => {
5800
5882
  setChoice((prev) => {
5801
- const idx = CHOICES.indexOf(prev);
5802
- return CHOICES[(idx + 1) % CHOICES.length];
5883
+ const idx = CHOICES2.indexOf(prev);
5884
+ return CHOICES2[(idx + 1) % CHOICES2.length];
5803
5885
  });
5804
5886
  }, []);
5805
5887
  const cycleLimit = useCallback4(() => {
@@ -6793,6 +6875,7 @@ function InnerApp({
6793
6875
  const [clipboardUrl, setClipboardUrl] = useState9(void 0);
6794
6876
  const [settingsFocus, setSettingsFocus] = useState9("cookies");
6795
6877
  const [batchClickTargets, setBatchClickTargets] = useState9([]);
6878
+ const [watcherClickTargets, setWatcherClickTargets] = useState9([]);
6796
6879
  const columns = stdout?.columns && stdout.columns > 0 ? stdout.columns : 80;
6797
6880
  const boxWidth = Math.max(14, Math.min(64, columns - 6));
6798
6881
  const contentWidth = Math.max(10, Math.min(columns - 4, 78));
@@ -6944,8 +7027,11 @@ function InnerApp({
6944
7027
  if (key.ctrl && (input === "w" || input === "W")) {
6945
7028
  if (phase.name === "watcher") {
6946
7029
  setPhase({ name: "input" });
6947
- } else if (phase.name === "input") {
7030
+ return;
7031
+ }
7032
+ if (phase.name === "input" && urlInput.length === 0) {
6948
7033
  setPhase({ name: "watcher" });
7034
+ return;
6949
7035
  }
6950
7036
  return;
6951
7037
  }
@@ -7063,6 +7149,9 @@ function InnerApp({
7063
7149
  if (phase.name === "batch") {
7064
7150
  targets.push(...batchClickTargets);
7065
7151
  }
7152
+ if (phase.name === "watcher") {
7153
+ targets.push(...watcherClickTargets);
7154
+ }
7066
7155
  for (const [key, label] of hints) {
7067
7156
  const action = hintAction(key);
7068
7157
  if (action) targets.push({ match: `${key} ${label}`, action });
@@ -7083,7 +7172,8 @@ function InnerApp({
7083
7172
  handlePlatformSelect,
7084
7173
  handleModeSelect,
7085
7174
  resetToInput,
7086
- batchClickTargets
7175
+ batchClickTargets,
7176
+ watcherClickTargets
7087
7177
  ]);
7088
7178
  useMouseClick(
7089
7179
  (x, y) => {
@@ -7210,7 +7300,8 @@ function InnerApp({
7210
7300
  theme,
7211
7301
  gapComponent: Gap,
7212
7302
  defaultChoice: initialChoice,
7213
- defaultWriteMetadata: effectiveWriteMetadata
7303
+ defaultWriteMetadata: effectiveWriteMetadata,
7304
+ onRegisterClickTargets: setWatcherClickTargets
7214
7305
  }
7215
7306
  ),
7216
7307
  phase.name === "batch" && /* @__PURE__ */ jsx18(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-nami",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "Open Nami: terminal-first bulk social media profile downloader for Instagram, TikTok, Facebook & X.",
5
5
  "type": "module",
6
6
  "bin": {