@testsmith/api-spector 0.1.4 → 0.1.6

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.
@@ -905,6 +905,7 @@ function checkDCE$1() {
905
905
  reactDom.exports = reactDom_production;
906
906
  }
907
907
  var reactDomExports = reactDom.exports;
908
+ const ReactDOM$2 = /* @__PURE__ */ getDefaultExportFromCjs(reactDomExports);
908
909
  /**
909
910
  * @license React
910
911
  * react-dom-client.production.js
@@ -13410,9 +13411,10 @@ const useStore = create()(
13410
13411
  activeCollectionId: null,
13411
13412
  activeEnvironmentId: localStorage.getItem("activeEnvironmentId") ?? null,
13412
13413
  globals: {},
13414
+ sessionVars: {},
13413
13415
  showGeneratorPanel: false,
13414
13416
  theme: localStorage.getItem("theme") ?? "dark",
13415
- zoom: Number(localStorage.getItem("zoom") ?? "1"),
13417
+ zoom: Number(localStorage.getItem("zoom") ?? "1.1"),
13416
13418
  history: [],
13417
13419
  sidebarTab: "collections",
13418
13420
  workspaceSettingsOpen: false,
@@ -13446,6 +13448,7 @@ const useStore = create()(
13446
13448
  s.activeTabId = null;
13447
13449
  s.activeCollectionId = null;
13448
13450
  s.globals = {};
13451
+ s.sessionVars = {};
13449
13452
  s.runnerResults = [];
13450
13453
  s.runnerRunning = false;
13451
13454
  s.runnerModal = { open: false, collectionId: null, folderId: null, filterTags: [] };
@@ -13639,6 +13642,12 @@ const useStore = create()(
13639
13642
  s.collections[id2].data.tls = tls;
13640
13643
  s.collections[id2].dirty = true;
13641
13644
  }),
13645
+ updateCollectionAuthAndHeaders: (id2, auth, headers) => set2((s) => {
13646
+ if (!s.collections[id2]) return;
13647
+ s.collections[id2].data.auth = auth;
13648
+ s.collections[id2].data.headers = headers;
13649
+ s.collections[id2].dirty = true;
13650
+ }),
13642
13651
  // ── Folder CRUD ───────────────────────────────────────────────────────────
13643
13652
  addFolder: (collectionId, parentFolderId, name2) => set2((s) => {
13644
13653
  const col = s.collections[collectionId]?.data;
@@ -13758,6 +13767,37 @@ const useStore = create()(
13758
13767
  s.tabs.push(tab);
13759
13768
  s.activeTabId = tab.id;
13760
13769
  }),
13770
+ moveRequest: (srcCollectionId, requestId, destCollectionId, destFolderId, destIndex) => set2((s) => {
13771
+ const srcCol = s.collections[srcCollectionId]?.data;
13772
+ const destCol = s.collections[destCollectionId]?.data;
13773
+ if (!srcCol || !destCol) return;
13774
+ const req = srcCol.requests[requestId];
13775
+ if (!req) return;
13776
+ const srcFolder = findFolderContaining(srcCol.rootFolder, requestId);
13777
+ const srcIdx = srcFolder ? srcFolder.requestIds.indexOf(requestId) : -1;
13778
+ removeFromFolder(srcCol.rootFolder, requestId);
13779
+ s.collections[srcCollectionId].dirty = true;
13780
+ if (srcCollectionId !== destCollectionId) {
13781
+ delete srcCol.requests[requestId];
13782
+ destCol.requests[requestId] = req;
13783
+ const tab = s.tabs.find((t2) => t2.requestId === requestId);
13784
+ if (tab) tab.collectionId = destCollectionId;
13785
+ }
13786
+ const destFolder = findFolder(destCol.rootFolder, destFolderId);
13787
+ if (destFolder) {
13788
+ if (destIndex !== void 0) {
13789
+ let adjusted = destIndex;
13790
+ if (srcCollectionId === destCollectionId && srcFolder?.id === destFolder.id && srcIdx < destIndex) {
13791
+ adjusted--;
13792
+ }
13793
+ adjusted = Math.max(0, Math.min(adjusted, destFolder.requestIds.length));
13794
+ destFolder.requestIds.splice(adjusted, 0, requestId);
13795
+ } else {
13796
+ destFolder.requestIds.push(requestId);
13797
+ }
13798
+ }
13799
+ s.collections[destCollectionId].dirty = true;
13800
+ }),
13761
13801
  // ── Tags ──────────────────────────────────────────────────────────────────
13762
13802
  updateFolderTags: (collectionId, folderId, tags2) => set2((s) => {
13763
13803
  const col = s.collections[collectionId]?.data;
@@ -13802,16 +13842,12 @@ const useStore = create()(
13802
13842
  const state = useStore.getState();
13803
13843
  const colEntry = Object.values(state.collections).find((c) => c.data.requests[requestId]);
13804
13844
  if (!colEntry) return { auth: null, headers: [] };
13845
+ let inheritedAuth = colEntry.data.auth && colEntry.data.auth.type !== "none" ? colEntry.data.auth : null;
13846
+ let inheritedHeaders = colEntry.data.headers?.filter((h) => h.enabled && h.key) ?? [];
13805
13847
  const path = findFolderPath(colEntry.data.rootFolder, requestId);
13806
- let inheritedAuth = null;
13807
- let inheritedHeaders = [];
13808
13848
  for (const folder of path) {
13809
- if (folder.auth && folder.auth.type !== "none") {
13810
- inheritedAuth = folder.auth;
13811
- }
13812
- if (folder.headers && folder.headers.length > 0) {
13813
- inheritedHeaders = [...inheritedHeaders, ...folder.headers];
13814
- }
13849
+ if (folder.auth && folder.auth.type !== "none") inheritedAuth = folder.auth;
13850
+ if (folder.headers?.length) inheritedHeaders = [...inheritedHeaders, ...folder.headers];
13815
13851
  }
13816
13852
  return { auth: inheritedAuth, headers: inheritedHeaders };
13817
13853
  },
@@ -13868,6 +13904,7 @@ const useStore = create()(
13868
13904
  }
13869
13905
  }
13870
13906
  s.globals = { ...s.globals, ...result.updatedGlobals };
13907
+ s.sessionVars = { ...s.sessionVars, ...result.updatedLocalVars };
13871
13908
  }),
13872
13909
  // ── Theme & zoom ──────────────────────────────────────────────────────────
13873
13910
  setTheme: (t2) => set2((s) => {
@@ -34505,18 +34542,35 @@ function mockScriptCompletionExtension() {
34505
34542
  ]
34506
34543
  });
34507
34544
  }
34545
+ const SCRIPT_SET_RE = /\bsp\.(?:variables|collectionVariables|environment|globals)\.set\(\s*["']([^"']+)["']/g;
34546
+ function extractScriptVarNames(script) {
34547
+ if (!script) return [];
34548
+ const names2 = [];
34549
+ let m;
34550
+ SCRIPT_SET_RE.lastIndex = 0;
34551
+ while ((m = SCRIPT_SET_RE.exec(script)) !== null) names2.push(m[1]);
34552
+ return names2;
34553
+ }
34508
34554
  function useVarNames() {
34509
34555
  const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
34510
34556
  const activeCollectionId = useStore((s) => s.activeCollectionId);
34511
34557
  const environments = useStore((s) => s.environments);
34512
34558
  const collections = useStore((s) => s.collections);
34513
34559
  const globals = useStore((s) => s.globals);
34560
+ const sessionVars = useStore((s) => s.sessionVars);
34514
34561
  return reactExports.useMemo(() => {
34515
34562
  const names2 = /* @__PURE__ */ new Set();
34516
34563
  Object.keys(globals).forEach((k) => names2.add(k));
34564
+ Object.keys(sessionVars).forEach((k) => names2.add(k));
34517
34565
  if (activeCollectionId) {
34518
- const colVars = collections[activeCollectionId]?.data.collectionVariables ?? {};
34519
- Object.keys(colVars).forEach((k) => names2.add(k));
34566
+ const col = collections[activeCollectionId]?.data;
34567
+ if (col) {
34568
+ Object.keys(col.collectionVariables ?? {}).forEach((k) => names2.add(k));
34569
+ for (const req of Object.values(col.requests)) {
34570
+ extractScriptVarNames(req.preRequestScript).forEach((k) => names2.add(k));
34571
+ extractScriptVarNames(req.postRequestScript).forEach((k) => names2.add(k));
34572
+ }
34573
+ }
34520
34574
  }
34521
34575
  if (activeEnvironmentId) {
34522
34576
  const envVars = environments[activeEnvironmentId]?.data.variables ?? [];
@@ -34528,7 +34582,8 @@ function useVarNames() {
34528
34582
  activeCollectionId,
34529
34583
  environments,
34530
34584
  collections,
34531
- globals
34585
+ globals,
34586
+ sessionVars
34532
34587
  ]);
34533
34588
  }
34534
34589
  function useVarValues() {
@@ -34566,24 +34621,80 @@ function parseVarTokens(str) {
34566
34621
  }
34567
34622
  return found;
34568
34623
  }
34569
- function VarInput({ value, onChange, className, wrapperClassName, ...rest }) {
34624
+ function PortalDropdown({
34625
+ pos,
34626
+ items: items2,
34627
+ activeIndex,
34628
+ mode,
34629
+ onPick
34630
+ }) {
34631
+ return ReactDOM$2.createPortal(
34632
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
34633
+ "ul",
34634
+ {
34635
+ style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.minWidth, zIndex: 9999 },
34636
+ className: "max-h-52 overflow-y-auto bg-surface-800 border border-surface-600 rounded shadow-xl text-xs",
34637
+ children: items2.map((s, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
34638
+ "button",
34639
+ {
34640
+ onMouseDown: (e) => {
34641
+ e.preventDefault();
34642
+ onPick(s);
34643
+ },
34644
+ className: `w-full text-left px-3 py-1.5 font-mono transition-colors ${i === activeIndex ? "bg-blue-600 text-white" : "hover:bg-surface-700"}`,
34645
+ children: mode === "var" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
34646
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "{{" }),
34647
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: s }),
34648
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "}}" })
34649
+ ] }) : s
34650
+ }
34651
+ ) }, s))
34652
+ }
34653
+ ),
34654
+ document.body
34655
+ );
34656
+ }
34657
+ function VarInput({ value, onChange, className, wrapperClassName, staticSuggestions, ...rest }) {
34570
34658
  const varNames = useVarNames();
34571
34659
  const varValues = useVarValues();
34572
34660
  const inputRef = reactExports.useRef(null);
34573
34661
  const [suggestions, setSuggestions] = reactExports.useState([]);
34662
+ const [suggestionMode, setSuggestionMode] = reactExports.useState("var");
34574
34663
  const [activeIndex, setActiveIndex] = reactExports.useState(-1);
34664
+ const [dropPos, setDropPos] = reactExports.useState(null);
34575
34665
  const [showPreview, setShowPreview] = reactExports.useState(false);
34666
+ reactExports.useEffect(() => {
34667
+ if (suggestions.length === 0) {
34668
+ setDropPos(null);
34669
+ return;
34670
+ }
34671
+ const el = inputRef.current;
34672
+ if (!el) return;
34673
+ const r = el.getBoundingClientRect();
34674
+ setDropPos({ top: r.bottom + 2, left: r.left, minWidth: r.width });
34675
+ }, [suggestions.length]);
34576
34676
  function detectQuery(val, cursor2) {
34577
34677
  const before = val.slice(0, cursor2);
34578
- const match = /\{\{(\w*)$/.exec(before);
34579
- if (!match) {
34678
+ const varMatch = /\{\{(\w*)$/.exec(before);
34679
+ if (varMatch) {
34680
+ const q = varMatch[1].toLowerCase();
34681
+ const filtered = varNames.filter((n) => n.toLowerCase().includes(q));
34682
+ setSuggestions(filtered);
34683
+ setSuggestionMode("var");
34684
+ setActiveIndex(-1);
34685
+ } else if (staticSuggestions) {
34686
+ const q = val.toLowerCase();
34687
+ const filtered = q ? staticSuggestions.filter((s) => s.toLowerCase().includes(q)).sort((a, b) => {
34688
+ const aP = a.toLowerCase().startsWith(q) ? 0 : 1;
34689
+ const bP = b.toLowerCase().startsWith(q) ? 0 : 1;
34690
+ return aP - bP;
34691
+ }).slice(0, 20) : staticSuggestions.slice(0, 20);
34692
+ setSuggestions(filtered);
34693
+ setSuggestionMode("static");
34694
+ setActiveIndex(-1);
34695
+ } else {
34580
34696
  setSuggestions([]);
34581
- return;
34582
34697
  }
34583
- const q = match[1].toLowerCase();
34584
- const filtered = varNames.filter((n) => n.toLowerCase().includes(q));
34585
- setSuggestions(filtered);
34586
- setActiveIndex(-1);
34587
34698
  }
34588
34699
  function handleChange(e) {
34589
34700
  const newVal = e.target.value;
@@ -34591,6 +34702,10 @@ function VarInput({ value, onChange, className, wrapperClassName, ...rest }) {
34591
34702
  onChange(newVal);
34592
34703
  detectQuery(newVal, cursor2);
34593
34704
  }
34705
+ function handleFocus(e) {
34706
+ detectQuery(value, e.target.selectionStart ?? value.length);
34707
+ rest.onFocus?.(e);
34708
+ }
34594
34709
  function handleKeyDown(e) {
34595
34710
  if (suggestions.length === 0) return;
34596
34711
  if (e.key === "ArrowDown") {
@@ -34606,21 +34721,28 @@ function VarInput({ value, onChange, className, wrapperClassName, ...rest }) {
34606
34721
  setSuggestions([]);
34607
34722
  }
34608
34723
  }
34609
- const apply = reactExports.useCallback((name2) => {
34724
+ const apply = reactExports.useCallback((suggestion) => {
34725
+ if (suggestionMode === "static") {
34726
+ onChange(suggestion);
34727
+ setSuggestions([]);
34728
+ return;
34729
+ }
34610
34730
  const el = inputRef.current;
34611
34731
  const cursor2 = el?.selectionStart ?? value.length;
34612
34732
  const before = value.slice(0, cursor2);
34613
34733
  const after = value.slice(cursor2);
34614
34734
  const match = /\{\{(\w*)$/.exec(before);
34615
34735
  if (!match) return;
34616
- const newVal = before.slice(0, match.index) + `{{${name2}}}` + after;
34617
- const newCursor = match.index + name2.length + 4;
34736
+ const newVal = before.slice(0, match.index) + `{{${suggestion}}}` + after;
34737
+ const newCursor = match.index + suggestion.length + 4;
34618
34738
  onChange(newVal);
34619
34739
  setSuggestions([]);
34620
34740
  requestAnimationFrame(() => el?.setSelectionRange(newCursor, newCursor));
34621
- }, [value, onChange]);
34622
- function handleBlur() {
34741
+ }, [suggestionMode, value, onChange]);
34742
+ function handleBlur(e) {
34623
34743
  setTimeout(() => setSuggestions([]), 150);
34744
+ setShowPreview(false);
34745
+ rest.onBlur?.(e);
34624
34746
  }
34625
34747
  const tokens = parseVarTokens(value);
34626
34748
  const hasVars = tokens.length > 0;
@@ -34628,6 +34750,7 @@ function VarInput({ value, onChange, className, wrapperClassName, ...rest }) {
34628
34750
  name: name2,
34629
34751
  resolved: varValues[name2] ?? null
34630
34752
  }));
34753
+ const { onFocus: _f, onBlur: _b, ...inputRest } = rest;
34631
34754
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
34632
34755
  "div",
34633
34756
  {
@@ -34642,26 +34765,22 @@ function VarInput({ value, onChange, className, wrapperClassName, ...rest }) {
34642
34765
  value,
34643
34766
  onChange: handleChange,
34644
34767
  onKeyDown: handleKeyDown,
34768
+ onFocus: handleFocus,
34645
34769
  onBlur: handleBlur,
34646
34770
  className: `w-full ${className ?? ""}`,
34647
- ...rest
34771
+ ...inputRest
34648
34772
  }
34649
34773
  ),
34650
- suggestions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "absolute left-0 top-full mt-0.5 z-50 min-w-[160px] max-h-44 overflow-y-auto bg-surface-800 border border-surface-600 rounded shadow-xl text-xs", children: suggestions.map((name2, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
34651
- "button",
34774
+ suggestions.length > 0 && dropPos && /* @__PURE__ */ jsxRuntimeExports.jsx(
34775
+ PortalDropdown,
34652
34776
  {
34653
- onMouseDown: (e) => {
34654
- e.preventDefault();
34655
- apply(name2);
34656
- },
34657
- className: `w-full text-left px-3 py-1.5 font-mono transition-colors ${i === activeIndex ? "bg-blue-600 text-white" : "hover:bg-surface-700"}`,
34658
- children: [
34659
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "{{" }),
34660
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: name2 }),
34661
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "}}" })
34662
- ]
34777
+ pos: dropPos,
34778
+ items: suggestions,
34779
+ activeIndex,
34780
+ mode: suggestionMode,
34781
+ onPick: apply
34663
34782
  }
34664
- ) }, name2)) }),
34783
+ ),
34665
34784
  showPreview && suggestions.length === 0 && previewItems.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 top-full mt-0.5 z-50 bg-surface-800 border border-surface-600 rounded shadow-xl text-xs min-w-[220px] max-w-[420px]", children: previewItems.map(({ name: name2, resolved }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline gap-2 px-3 py-1.5 border-b border-surface-700 last:border-0", children: [
34666
34785
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-blue-400 shrink-0", children: `{{${name2}}}` }),
34667
34786
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500 mx-0.5", children: "→" }),
@@ -34671,7 +34790,113 @@ function VarInput({ value, onChange, className, wrapperClassName, ...rest }) {
34671
34790
  }
34672
34791
  );
34673
34792
  }
34674
- function KVTable({ rows, onChange, keyPlaceholder = "Key", valuePlaceholder = "Value" }) {
34793
+ const HEADER_NAMES = [
34794
+ "Accept",
34795
+ "Authorization",
34796
+ "Content-Type",
34797
+ "Cache-Control",
34798
+ "Accept-Language",
34799
+ "Accept-Encoding",
34800
+ "Connection",
34801
+ "Content-Encoding",
34802
+ "Content-Length",
34803
+ "Cookie",
34804
+ "Host",
34805
+ "Origin",
34806
+ "Referer",
34807
+ "User-Agent",
34808
+ "X-Api-Key",
34809
+ "X-Auth-Token",
34810
+ "X-Correlation-ID",
34811
+ "X-Request-ID",
34812
+ "X-Forwarded-For",
34813
+ "X-Requested-With",
34814
+ "X-Tenant-ID",
34815
+ "If-Match",
34816
+ "If-None-Match",
34817
+ "If-Modified-Since",
34818
+ "If-Unmodified-Since",
34819
+ "If-Range",
34820
+ "Date",
34821
+ "Expect",
34822
+ "From",
34823
+ "Max-Forwards",
34824
+ "Pragma",
34825
+ "Proxy-Authorization",
34826
+ "Range",
34827
+ "TE",
34828
+ "Upgrade",
34829
+ "Via",
34830
+ "Warning"
34831
+ ];
34832
+ const HEADER_VALUE_SUGGESTIONS = {
34833
+ "accept": [
34834
+ "application/json",
34835
+ "application/xml",
34836
+ "text/html",
34837
+ "text/plain",
34838
+ "application/ld+json",
34839
+ "*/*"
34840
+ ],
34841
+ "content-type": [
34842
+ "application/json",
34843
+ "application/xml",
34844
+ "application/x-www-form-urlencoded",
34845
+ "multipart/form-data",
34846
+ "text/plain",
34847
+ "text/html",
34848
+ "text/xml",
34849
+ "application/octet-stream",
34850
+ "application/graphql",
34851
+ "application/ld+json"
34852
+ ],
34853
+ "cache-control": [
34854
+ "no-cache",
34855
+ "no-store",
34856
+ "max-age=0",
34857
+ "max-age=3600",
34858
+ "must-revalidate",
34859
+ "private",
34860
+ "public"
34861
+ ],
34862
+ "connection": [
34863
+ "keep-alive",
34864
+ "close",
34865
+ "upgrade"
34866
+ ],
34867
+ "accept-encoding": [
34868
+ "gzip",
34869
+ "deflate",
34870
+ "br",
34871
+ "gzip, deflate, br",
34872
+ "identity",
34873
+ "*"
34874
+ ],
34875
+ "accept-language": [
34876
+ "en-US",
34877
+ "en-US,en;q=0.9",
34878
+ "en",
34879
+ "*"
34880
+ ],
34881
+ "pragma": [
34882
+ "no-cache"
34883
+ ],
34884
+ "transfer-encoding": [
34885
+ "chunked",
34886
+ "gzip",
34887
+ "deflate",
34888
+ "compress",
34889
+ "identity"
34890
+ ],
34891
+ "upgrade": [
34892
+ "websocket",
34893
+ "HTTP/2.0"
34894
+ ]
34895
+ };
34896
+ function getValueSuggestions(headerName) {
34897
+ return HEADER_VALUE_SUGGESTIONS[headerName.toLowerCase()];
34898
+ }
34899
+ function KVTable({ rows, onChange, keyPlaceholder = "Key", valuePlaceholder = "Value", headerMode }) {
34675
34900
  const [descVisible, setDescVisible] = reactExports.useState(
34676
34901
  () => new Set(rows.map((r, i) => r.description ? i : -1).filter((i) => i >= 0))
34677
34902
  );
@@ -34725,7 +34950,8 @@ function KVTable({ rows, onChange, keyPlaceholder = "Key", valuePlaceholder = "V
34725
34950
  onChange: (v) => update(idx, { key: v }),
34726
34951
  placeholder: keyPlaceholder,
34727
34952
  wrapperClassName: "flex-1",
34728
- className: "bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono"
34953
+ className: "bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono",
34954
+ staticSuggestions: headerMode ? HEADER_NAMES : void 0
34729
34955
  }
34730
34956
  ),
34731
34957
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -34735,7 +34961,8 @@ function KVTable({ rows, onChange, keyPlaceholder = "Key", valuePlaceholder = "V
34735
34961
  onChange: (v) => update(idx, { value: v }),
34736
34962
  placeholder: valuePlaceholder,
34737
34963
  wrapperClassName: "flex-1",
34738
- className: "bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono"
34964
+ className: "bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono",
34965
+ staticSuggestions: headerMode ? getValueSuggestions(row.key) ?? void 0 : void 0
34739
34966
  }
34740
34967
  ),
34741
34968
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -34769,7 +34996,7 @@ function KVTable({ rows, onChange, keyPlaceholder = "Key", valuePlaceholder = "V
34769
34996
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: add2, className: "mt-1 text-surface-400 hover:text-white transition-colors", children: "+ Add" })
34770
34997
  ] });
34771
34998
  }
34772
- const AUTH_TYPES$1 = ["none", "bearer", "basic", "digest", "ntlm", "apikey"];
34999
+ const AUTH_TYPES$2 = ["none", "bearer", "basic", "digest", "ntlm", "apikey"];
34773
35000
  function FolderSettingsModal({ collectionId, folder, onClose }) {
34774
35001
  const updateFolder = useStore((s) => s.updateFolder);
34775
35002
  const [activeTab, setActiveTab] = reactExports.useState("auth");
@@ -34820,7 +35047,8 @@ function FolderSettingsModal({ collectionId, folder, onClose }) {
34820
35047
  rows: headers,
34821
35048
  onChange: setHeaders,
34822
35049
  keyPlaceholder: "Header-Name",
34823
- valuePlaceholder: "value"
35050
+ valuePlaceholder: "value",
35051
+ headerMode: true
34824
35052
  }
34825
35053
  )
34826
35054
  ] }),
@@ -34853,7 +35081,7 @@ function FolderAuthPanel({ auth, onChange }) {
34853
35081
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600", children: "Auth configured here is inherited by all requests in this folder unless the request overrides it with its own non-none auth type." }),
34854
35082
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
34855
35083
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-400", children: "Type:" }),
34856
- AUTH_TYPES$1.map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1 cursor-pointer", children: [
35084
+ AUTH_TYPES$2.map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1 cursor-pointer", children: [
34857
35085
  /* @__PURE__ */ jsxRuntimeExports.jsx(
34858
35086
  "input",
34859
35087
  {
@@ -34999,14 +35227,23 @@ function FolderAuthPanel({ auth, onChange }) {
34999
35227
  ] }) })
35000
35228
  ] });
35001
35229
  }
35230
+ const AUTH_TYPES$1 = ["none", "bearer", "basic", "digest", "ntlm", "apikey"];
35002
35231
  function CollectionSettingsModal({ collection, onClose }) {
35003
35232
  const updateCollectionTls = useStore((s) => s.updateCollectionTls);
35233
+ const updateCollectionAuthAndHeaders = useStore((s) => s.updateCollectionAuthAndHeaders);
35234
+ const [activeTab, setActiveTab] = reactExports.useState("auth");
35235
+ const [auth, setAuth] = reactExports.useState(collection.auth ?? { type: "none" });
35236
+ const [headers, setHeaders] = reactExports.useState(collection.headers ?? []);
35004
35237
  const existing = collection.tls;
35005
35238
  const [caCertPath, setCaCertPath] = reactExports.useState(existing?.caCertPath ?? "");
35006
35239
  const [clientCertPath, setClientCertPath] = reactExports.useState(existing?.clientCertPath ?? "");
35007
35240
  const [clientKeyPath, setClientKeyPath] = reactExports.useState(existing?.clientKeyPath ?? "");
35008
35241
  const [rejectUnauthorized, setRejectUnauthorized] = reactExports.useState(existing?.rejectUnauthorized !== false);
35242
+ function patchAuth(patch) {
35243
+ setAuth((prev) => ({ ...prev, ...patch }));
35244
+ }
35009
35245
  function save() {
35246
+ updateCollectionAuthAndHeaders(collection.id, auth, headers);
35010
35247
  const hasAny = caCertPath.trim() || clientCertPath.trim() || clientKeyPath.trim();
35011
35248
  updateCollectionTls(collection.id, hasAny ? {
35012
35249
  caCertPath: caCertPath.trim() || void 0,
@@ -35024,65 +35261,90 @@ function CollectionSettingsModal({ collection, onClose }) {
35024
35261
  children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
35025
35262
  "div",
35026
35263
  {
35027
- className: "w-[560px] bg-surface-900 border border-surface-800 rounded-lg shadow-2xl flex flex-col max-h-[80vh]",
35264
+ className: "w-[600px] bg-surface-900 border border-surface-800 rounded-lg shadow-2xl flex flex-col max-h-[80vh]",
35028
35265
  onClick: (e) => e.stopPropagation(),
35029
35266
  children: [
35030
35267
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-4 py-2.5 border-b border-surface-800 shrink-0", children: [
35031
35268
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
35032
35269
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-sm font-semibold", children: "Collection settings" }),
35033
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 mt-0.5", children: collection.name })
35270
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-surface-600 mt-0.5", children: [
35271
+ collection.name,
35272
+ " — auth and headers inherited by all requests in this collection"
35273
+ ] })
35034
35274
  ] }),
35035
35275
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "text-surface-400 hover:text-white text-lg leading-none", children: "×" })
35036
35276
  ] }),
35037
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 flex-1 overflow-y-auto text-xs flex flex-col gap-3", children: [
35038
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500", children: "TLS settings override the workspace-level configuration for every request in this collection. Leave all paths empty to inherit from the workspace." }),
35039
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35040
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "CA Certificate path" }),
35041
- /* @__PURE__ */ jsxRuntimeExports.jsx(
35042
- "input",
35043
- {
35044
- value: caCertPath,
35045
- onChange: (e) => setCaCertPath(e.target.value),
35046
- placeholder: "/path/to/ca.crt (leave empty to inherit)",
35047
- className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
35048
- }
35049
- )
35050
- ] }),
35051
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35052
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "Client certificate path" }),
35053
- /* @__PURE__ */ jsxRuntimeExports.jsx(
35054
- "input",
35055
- {
35056
- value: clientCertPath,
35057
- onChange: (e) => setClientCertPath(e.target.value),
35058
- placeholder: "/path/to/client.crt (leave empty to inherit)",
35059
- className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
35060
- }
35061
- )
35062
- ] }),
35063
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35064
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "Client key path" }),
35065
- /* @__PURE__ */ jsxRuntimeExports.jsx(
35066
- "input",
35067
- {
35068
- value: clientKeyPath,
35069
- onChange: (e) => setClientKeyPath(e.target.value),
35070
- placeholder: "/path/to/client.key (leave empty to inherit)",
35071
- className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
35072
- }
35073
- )
35074
- ] }),
35075
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
35076
- /* @__PURE__ */ jsxRuntimeExports.jsx(
35077
- "input",
35078
- {
35079
- type: "checkbox",
35080
- checked: rejectUnauthorized,
35081
- onChange: (e) => setRejectUnauthorized(e.target.checked),
35082
- className: "accent-blue-500"
35083
- }
35084
- ),
35085
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Reject unauthorized / self-signed certificates" })
35277
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b border-surface-800 px-4 shrink-0", children: ["auth", "headers", "tls"].map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsx(
35278
+ "button",
35279
+ {
35280
+ onClick: () => setActiveTab(t2),
35281
+ className: `px-3 py-1.5 text-xs transition-colors border-b-2 -mb-px capitalize ${activeTab === t2 ? "border-blue-500 text-white" : "border-transparent text-surface-400 hover:text-white"}`,
35282
+ children: t2 === "tls" ? "TLS" : t2
35283
+ },
35284
+ t2
35285
+ )) }),
35286
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 flex-1 overflow-y-auto text-xs", children: [
35287
+ activeTab === "auth" && /* @__PURE__ */ jsxRuntimeExports.jsx(CollectionAuthPanel, { auth, onChange: patchAuth }),
35288
+ activeTab === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsx(
35289
+ KVTable,
35290
+ {
35291
+ rows: headers,
35292
+ onChange: setHeaders,
35293
+ keyPlaceholder: "Header-Name",
35294
+ valuePlaceholder: "value",
35295
+ headerMode: true
35296
+ }
35297
+ ),
35298
+ activeTab === "tls" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [
35299
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500", children: "TLS settings override the workspace-level configuration for every request in this collection. Leave all paths empty to inherit from the workspace." }),
35300
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35301
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "CA Certificate path" }),
35302
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35303
+ "input",
35304
+ {
35305
+ value: caCertPath,
35306
+ onChange: (e) => setCaCertPath(e.target.value),
35307
+ placeholder: "/path/to/ca.crt (leave empty to inherit)",
35308
+ className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
35309
+ }
35310
+ )
35311
+ ] }),
35312
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35313
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "Client certificate path" }),
35314
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35315
+ "input",
35316
+ {
35317
+ value: clientCertPath,
35318
+ onChange: (e) => setClientCertPath(e.target.value),
35319
+ placeholder: "/path/to/client.crt (leave empty to inherit)",
35320
+ className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
35321
+ }
35322
+ )
35323
+ ] }),
35324
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35325
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "Client key path" }),
35326
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35327
+ "input",
35328
+ {
35329
+ value: clientKeyPath,
35330
+ onChange: (e) => setClientKeyPath(e.target.value),
35331
+ placeholder: "/path/to/client.key (leave empty to inherit)",
35332
+ className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
35333
+ }
35334
+ )
35335
+ ] }),
35336
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
35337
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35338
+ "input",
35339
+ {
35340
+ type: "checkbox",
35341
+ checked: rejectUnauthorized,
35342
+ onChange: (e) => setRejectUnauthorized(e.target.checked),
35343
+ className: "accent-blue-500"
35344
+ }
35345
+ ),
35346
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Reject unauthorized / self-signed certificates" })
35347
+ ] })
35086
35348
  ] })
35087
35349
  ] }),
35088
35350
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 px-4 py-3 border-t border-surface-800 shrink-0", children: [
@@ -35109,6 +35371,160 @@ function CollectionSettingsModal({ collection, onClose }) {
35109
35371
  }
35110
35372
  );
35111
35373
  }
35374
+ function CollectionAuthPanel({ auth, onChange }) {
35375
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [
35376
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600", children: "Auth configured here is inherited by all requests in this collection unless a folder or request overrides it with its own non-none auth type." }),
35377
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
35378
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-400", children: "Type:" }),
35379
+ AUTH_TYPES$1.map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1 cursor-pointer", children: [
35380
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35381
+ "input",
35382
+ {
35383
+ type: "radio",
35384
+ value: t2,
35385
+ checked: auth.type === t2,
35386
+ onChange: () => onChange({ type: t2 }),
35387
+ className: "accent-blue-500"
35388
+ }
35389
+ ),
35390
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: auth.type === t2 ? "text-white" : "text-surface-400", children: t2 })
35391
+ ] }, t2))
35392
+ ] }),
35393
+ auth.type === "bearer" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
35394
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Token" }),
35395
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35396
+ "input",
35397
+ {
35398
+ value: auth.token ?? "",
35399
+ onChange: (e) => onChange({ token: e.target.value }),
35400
+ placeholder: "Bearer token",
35401
+ className: "bg-surface-800 border border-surface-700 rounded px-2 py-1 font-mono focus:outline-none focus:border-blue-500"
35402
+ }
35403
+ )
35404
+ ] }),
35405
+ (auth.type === "basic" || auth.type === "digest") && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
35406
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35407
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Username" }),
35408
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35409
+ "input",
35410
+ {
35411
+ value: auth.username ?? "",
35412
+ onChange: (e) => onChange({ username: e.target.value }),
35413
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
35414
+ }
35415
+ )
35416
+ ] }),
35417
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35418
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Password" }),
35419
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35420
+ "input",
35421
+ {
35422
+ type: "password",
35423
+ value: auth.password ?? "",
35424
+ onChange: (e) => onChange({ password: e.target.value }),
35425
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 font-mono focus:outline-none focus:border-blue-500"
35426
+ }
35427
+ )
35428
+ ] })
35429
+ ] }),
35430
+ auth.type === "ntlm" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
35431
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
35432
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35433
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Username" }),
35434
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35435
+ "input",
35436
+ {
35437
+ value: auth.username ?? "",
35438
+ onChange: (e) => onChange({ username: e.target.value }),
35439
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
35440
+ }
35441
+ )
35442
+ ] }),
35443
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35444
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Password" }),
35445
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35446
+ "input",
35447
+ {
35448
+ type: "password",
35449
+ value: auth.password ?? "",
35450
+ onChange: (e) => onChange({ password: e.target.value }),
35451
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 font-mono focus:outline-none focus:border-blue-500"
35452
+ }
35453
+ )
35454
+ ] })
35455
+ ] }),
35456
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
35457
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35458
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Domain" }),
35459
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35460
+ "input",
35461
+ {
35462
+ value: auth.ntlmDomain ?? "",
35463
+ onChange: (e) => onChange({ ntlmDomain: e.target.value }),
35464
+ placeholder: "WORKGROUP",
35465
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
35466
+ }
35467
+ )
35468
+ ] }),
35469
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35470
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Workstation" }),
35471
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35472
+ "input",
35473
+ {
35474
+ value: auth.ntlmWorkstation ?? "",
35475
+ onChange: (e) => onChange({ ntlmWorkstation: e.target.value }),
35476
+ placeholder: "MACHINE",
35477
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
35478
+ }
35479
+ )
35480
+ ] })
35481
+ ] })
35482
+ ] }),
35483
+ auth.type === "apikey" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
35484
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
35485
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Key name" }),
35486
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35487
+ "input",
35488
+ {
35489
+ value: auth.apiKeyName ?? "X-API-Key",
35490
+ onChange: (e) => onChange({ apiKeyName: e.target.value }),
35491
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
35492
+ }
35493
+ )
35494
+ ] }),
35495
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
35496
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "In" }),
35497
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
35498
+ "select",
35499
+ {
35500
+ value: auth.apiKeyIn ?? "header",
35501
+ onChange: (e) => onChange({ apiKeyIn: e.target.value }),
35502
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500",
35503
+ children: [
35504
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "header", children: "Header" }),
35505
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "query", children: "Query" })
35506
+ ]
35507
+ }
35508
+ )
35509
+ ] }),
35510
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
35511
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Value" }),
35512
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
35513
+ "input",
35514
+ {
35515
+ value: auth.apiKeyValue ?? "",
35516
+ onChange: (e) => onChange({ apiKeyValue: e.target.value }),
35517
+ placeholder: "API key value",
35518
+ className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 font-mono focus:outline-none focus:border-blue-500"
35519
+ }
35520
+ )
35521
+ ] })
35522
+ ] }) })
35523
+ ] });
35524
+ }
35525
+ const DragCtx = reactExports.createContext({ dragging: null, setDragging: () => {
35526
+ }, onDrop: () => {
35527
+ } });
35112
35528
  function InlineEdit({
35113
35529
  value,
35114
35530
  onCommit,
@@ -35356,16 +35772,23 @@ function CollectionTree() {
35356
35772
  const updateRequestTags = useStore((s) => s.updateRequestTags);
35357
35773
  const updateRequest = useStore((s) => s.updateRequest);
35358
35774
  const openRunner = useStore((s) => s.openRunner);
35775
+ const moveRequest = useStore((s) => s.moveRequest);
35359
35776
  const colList = Object.values(collections);
35360
35777
  const [pendingConfirm, setPendingConfirm] = reactExports.useState(null);
35361
35778
  const [newRequestId, setNewRequestId] = reactExports.useState(null);
35779
+ const [dragging, setDragging] = reactExports.useState(null);
35362
35780
  function confirmThen(message, action) {
35363
35781
  setPendingConfirm({ message, onConfirm: () => {
35364
35782
  action();
35365
35783
  setPendingConfirm(null);
35366
35784
  } });
35367
35785
  }
35368
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 select-none", children: [
35786
+ function onDrop(destCollectionId, destFolderId, destIndex) {
35787
+ if (!dragging) return;
35788
+ moveRequest(dragging.collectionId, dragging.requestId, destCollectionId, destFolderId, destIndex);
35789
+ setDragging(null);
35790
+ }
35791
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(DragCtx.Provider, { value: { dragging, setDragging, onDrop }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 select-none", children: [
35369
35792
  pendingConfirm && /* @__PURE__ */ jsxRuntimeExports.jsx(
35370
35793
  ConfirmDialog,
35371
35794
  {
@@ -35410,7 +35833,7 @@ function CollectionTree() {
35410
35833
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "pt-1", children: "or import from Postman / OpenAPI above." })
35411
35834
  ] })
35412
35835
  ] })
35413
- ] });
35836
+ ] }) });
35414
35837
  }
35415
35838
  function CollectionNode({
35416
35839
  col,
@@ -35441,6 +35864,8 @@ function CollectionNode({
35441
35864
  const [renaming, setRenaming] = reactExports.useState(false);
35442
35865
  const [showSettings, setShowSettings] = reactExports.useState(false);
35443
35866
  const [expandCtrl, setExpandCtrl] = reactExports.useState({ value: true, seq: 0 });
35867
+ const [dropOver, setDropOver] = reactExports.useState(false);
35868
+ const dragCtx = reactExports.useContext(DragCtx);
35444
35869
  function expandAll() {
35445
35870
  setExpandCtrl((c) => ({ value: true, seq: c.seq + 1 }));
35446
35871
  }
@@ -35451,11 +35876,21 @@ function CollectionNode({
35451
35876
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
35452
35877
  "div",
35453
35878
  {
35454
- className: `group flex items-start gap-1 px-2 py-1.5 cursor-pointer hover:bg-surface-800 transition-colors ${isActive ? "text-[var(--text-primary)]" : "text-surface-400"}`,
35879
+ className: `group flex items-start gap-1 px-2 py-1.5 cursor-pointer hover:bg-surface-800 transition-colors ${isActive ? "text-[var(--text-primary)]" : "text-surface-400"} ${dropOver ? "outline outline-1 outline-blue-500 rounded" : ""}`,
35455
35880
  onClick: () => {
35456
35881
  onSelectCollection();
35457
35882
  setExpanded((e) => !e);
35458
35883
  },
35884
+ onDragOver: dragCtx.dragging ? (e) => {
35885
+ e.preventDefault();
35886
+ setDropOver(true);
35887
+ } : void 0,
35888
+ onDragLeave: () => setDropOver(false),
35889
+ onDrop: (e) => {
35890
+ e.preventDefault();
35891
+ setDropOver(false);
35892
+ dragCtx.onDrop(col.id, col.rootFolder.id);
35893
+ },
35459
35894
  children: [
35460
35895
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] w-3 text-center shrink-0 mt-0.5", children: expanded ? "▾" : "▸" }),
35461
35896
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0", children: renaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -35539,6 +35974,8 @@ function FolderRow({
35539
35974
  const [renaming, setRenaming] = reactExports.useState(false);
35540
35975
  const [showSettings, setShowSettings] = reactExports.useState(false);
35541
35976
  const [addingTag, setAddingTag] = reactExports.useState(false);
35977
+ const [dropOver, setDropOver] = reactExports.useState(false);
35978
+ const dragCtx = reactExports.useContext(DragCtx);
35542
35979
  const tags2 = folder.tags ?? [];
35543
35980
  const indent = depth * 12 + 8;
35544
35981
  const hasInheritedConfig = folder.auth && folder.auth.type !== "none" || folder.headers && folder.headers.length > 0;
@@ -35546,9 +35983,19 @@ function FolderRow({
35546
35983
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
35547
35984
  "div",
35548
35985
  {
35549
- className: "group flex items-start gap-1 py-1 hover:bg-surface-800 transition-colors cursor-pointer text-surface-400",
35986
+ className: `group flex items-start gap-1 py-1 hover:bg-surface-800 transition-colors cursor-pointer text-surface-400 ${dropOver ? "outline outline-1 outline-blue-500 rounded" : ""}`,
35550
35987
  style: { paddingLeft: indent },
35551
35988
  onClick: () => setExpanded((e) => !e),
35989
+ onDragOver: dragCtx.dragging ? (e) => {
35990
+ e.preventDefault();
35991
+ setDropOver(true);
35992
+ } : void 0,
35993
+ onDragLeave: () => setDropOver(false),
35994
+ onDrop: (e) => {
35995
+ e.preventDefault();
35996
+ setDropOver(false);
35997
+ dragCtx.onDrop(collectionId, folder.id);
35998
+ },
35552
35999
  children: [
35553
36000
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] w-3 text-center shrink-0 mt-0.5", children: expanded ? "▾" : "▸" }),
35554
36001
  /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, { className: `shrink-0 mt-0.5 ${hasInheritedConfig ? "text-blue-500" : "text-amber-600"}` }),
@@ -35668,13 +36115,16 @@ function FolderContents({
35668
36115
  },
35669
36116
  sub.id
35670
36117
  )),
35671
- folder.requestIds.map((reqId) => {
36118
+ folder.requestIds.map((reqId, reqIndex) => {
35672
36119
  const req = requests[reqId];
35673
36120
  if (!req) return null;
35674
36121
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
35675
36122
  RequestRow,
35676
36123
  {
35677
36124
  reqId: req.id,
36125
+ collectionId,
36126
+ folderId: folder.id,
36127
+ reqIndex,
35678
36128
  name: req.name,
35679
36129
  method: req.method,
35680
36130
  hookType: req.hookType,
@@ -35707,7 +36157,10 @@ const HOOK_COLORS = {
35707
36157
  afterAll: "bg-cyan-800 text-white"
35708
36158
  };
35709
36159
  function RequestRow({
35710
- reqId: _reqId,
36160
+ reqId,
36161
+ collectionId,
36162
+ folderId,
36163
+ reqIndex,
35711
36164
  name: name2,
35712
36165
  method,
35713
36166
  hookType,
@@ -35724,6 +36177,8 @@ function RequestRow({
35724
36177
  }) {
35725
36178
  const [renaming, setRenaming] = reactExports.useState(autoRename);
35726
36179
  const [addingTag, setAddingTag] = reactExports.useState(false);
36180
+ const [dropPos, setDropPos] = reactExports.useState(null);
36181
+ const dragCtx = reactExports.useContext(DragCtx);
35727
36182
  const hookMenuItems = [
35728
36183
  ...["beforeAll", "before", "after", "afterAll"].map((ht) => ({
35729
36184
  type: "item",
@@ -35731,55 +36186,84 @@ function RequestRow({
35731
36186
  onClick: () => onSetHookType(hookType === ht ? void 0 : ht)
35732
36187
  }))
35733
36188
  ];
35734
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
35735
- "div",
35736
- {
35737
- className: `group flex items-start gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-300 hover:bg-surface-800"}`,
35738
- style: { paddingLeft: indent },
35739
- onClick: onSelect,
35740
- onDoubleClick: () => setRenaming(true),
35741
- children: [
35742
- /* @__PURE__ */ jsxRuntimeExports.jsx(MethodBadge, { method, size: "xs" }),
35743
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
35744
- renaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(
35745
- InlineEdit,
35746
- {
35747
- value: name2,
35748
- onCommit: (v) => {
35749
- onRename(v);
35750
- setRenaming(false);
35751
- },
35752
- onCancel: () => setRenaming(false),
35753
- className: "w-full text-xs"
35754
- }
35755
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 min-w-0", children: [
35756
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs truncate", children: name2 }),
35757
- hookType && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `shrink-0 text-[9px] font-bold px-1 py-px rounded ${HOOK_COLORS[hookType]}`, children: HOOK_LABELS[hookType].toUpperCase() })
36189
+ function handleDragOver(e) {
36190
+ if (!dragCtx.dragging || dragCtx.dragging.requestId === reqId) return;
36191
+ e.preventDefault();
36192
+ const rect = e.currentTarget.getBoundingClientRect();
36193
+ setDropPos(e.clientY < rect.top + rect.height / 2 ? "before" : "after");
36194
+ }
36195
+ function handleDrop(e) {
36196
+ e.preventDefault();
36197
+ e.stopPropagation();
36198
+ const insertIndex = dropPos === "before" ? reqIndex : reqIndex + 1;
36199
+ dragCtx.onDrop(collectionId, folderId, insertIndex);
36200
+ setDropPos(null);
36201
+ }
36202
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
36203
+ dropPos === "before" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" }),
36204
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
36205
+ "div",
36206
+ {
36207
+ draggable: true,
36208
+ className: `group flex items-start gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-300 hover:bg-surface-800"}`,
36209
+ style: { paddingLeft: indent },
36210
+ onClick: onSelect,
36211
+ onDoubleClick: () => setRenaming(true),
36212
+ onDragStart: (e) => {
36213
+ e.dataTransfer.effectAllowed = "move";
36214
+ dragCtx.setDragging({ requestId: reqId, collectionId });
36215
+ },
36216
+ onDragEnd: () => {
36217
+ dragCtx.setDragging(null);
36218
+ setDropPos(null);
36219
+ },
36220
+ onDragOver: handleDragOver,
36221
+ onDragLeave: () => setDropPos(null),
36222
+ onDrop: handleDrop,
36223
+ children: [
36224
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MethodBadge, { method, size: "xs" }),
36225
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
36226
+ renaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(
36227
+ InlineEdit,
36228
+ {
36229
+ value: name2,
36230
+ onCommit: (v) => {
36231
+ onRename(v);
36232
+ setRenaming(false);
36233
+ },
36234
+ onCancel: () => setRenaming(false),
36235
+ className: "w-full text-xs"
36236
+ }
36237
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 min-w-0", children: [
36238
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs truncate", children: name2 }),
36239
+ hookType && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `shrink-0 text-[9px] font-bold px-1 py-px rounded ${HOOK_COLORS[hookType]}`, children: HOOK_LABELS[hookType].toUpperCase() })
36240
+ ] }),
36241
+ (tags2.length > 0 || addingTag) && /* @__PURE__ */ jsxRuntimeExports.jsx(
36242
+ TagChips,
36243
+ {
36244
+ tags: tags2,
36245
+ onRemove: (tag) => onUpdateTags(tags2.filter((t2) => t2 !== tag)),
36246
+ onAdd: (tag) => onUpdateTags([...tags2, tag]),
36247
+ forceAdding: addingTag,
36248
+ onDoneAdding: () => setAddingTag(false)
36249
+ }
36250
+ )
35758
36251
  ] }),
35759
- (tags2.length > 0 || addingTag) && /* @__PURE__ */ jsxRuntimeExports.jsx(
35760
- TagChips,
35761
- {
35762
- tags: tags2,
35763
- onRemove: (tag) => onUpdateTags(tags2.filter((t2) => t2 !== tag)),
35764
- onAdd: (tag) => onUpdateTags([...tags2, tag]),
35765
- forceAdding: addingTag,
35766
- onDoneAdding: () => setAddingTag(false)
35767
- }
35768
- )
35769
- ] }),
35770
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DotsBtn, { items: [
35771
- { type: "item", label: "Rename", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}), onClick: () => setRenaming(true) },
35772
- { type: "item", label: "Duplicate", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: onDuplicate },
35773
- { type: "item", label: "Add tag", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, {}), onClick: () => setAddingTag(true) },
35774
- { type: "separator" },
35775
- { type: "header", label: "Hook type" },
35776
- ...hookMenuItems,
35777
- { type: "separator" },
35778
- { type: "item", label: "Delete", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}), danger: true, onClick: onDelete }
35779
- ] }) })
35780
- ]
35781
- }
35782
- );
36252
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DotsBtn, { items: [
36253
+ { type: "item", label: "Rename", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}), onClick: () => setRenaming(true) },
36254
+ { type: "item", label: "Duplicate", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: onDuplicate },
36255
+ { type: "item", label: "Add tag", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, {}), onClick: () => setAddingTag(true) },
36256
+ { type: "separator" },
36257
+ { type: "header", label: "Hook type" },
36258
+ ...hookMenuItems,
36259
+ { type: "separator" },
36260
+ { type: "item", label: "Delete", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}), danger: true, onClick: onDelete }
36261
+ ] }) }),
36262
+ dropPos === "after" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" })
36263
+ ]
36264
+ }
36265
+ )
36266
+ ] });
35783
36267
  }
35784
36268
  function DotsHorizontalIcon() {
35785
36269
  return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3.5 h-3.5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6 10a2 2 0 11-4 0 2 2 0 014 0zm6 0a2 2 0 11-4 0 2 2 0 014 0zm6 0a2 2 0 11-4 0 2 2 0 014 0z" }) });
@@ -35841,7 +36325,8 @@ function HeadersTab({ request, onChange }) {
35841
36325
  rows: request.headers,
35842
36326
  onChange: (rows) => onChange({ headers: rows }),
35843
36327
  keyPlaceholder: "Header-Name",
35844
- valuePlaceholder: "value"
36328
+ valuePlaceholder: "value",
36329
+ headerMode: true
35845
36330
  }
35846
36331
  );
35847
36332
  }
@@ -42007,39 +42492,17 @@ function AuthTab({ request, onChange }) {
42007
42492
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: auth.type === t2 ? "text-white" : "text-surface-400", children: t2 })
42008
42493
  ] }, t2))
42009
42494
  ] }),
42010
- auth.type === "bearer" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
42011
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Token" }),
42012
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
42013
- /* @__PURE__ */ jsxRuntimeExports.jsx(
42014
- "input",
42015
- {
42016
- type: "password",
42017
- value: secretValue,
42018
- onChange: (e) => setSecretValue(e.target.value),
42019
- placeholder: auth.tokenSecretRef ? `Keychain ref: "${auth.tokenSecretRef}"` : "Paste token to store in keychain",
42020
- className: "flex-1 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono"
42021
- }
42022
- ),
42023
- /* @__PURE__ */ jsxRuntimeExports.jsx(
42024
- "input",
42025
- {
42026
- value: auth.tokenSecretRef ?? "API_TOKEN",
42027
- onChange: (e) => setAuth({ tokenSecretRef: e.target.value }),
42028
- placeholder: "Keychain key name",
42029
- className: "w-36 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
42030
- }
42031
- ),
42032
- /* @__PURE__ */ jsxRuntimeExports.jsx(
42033
- "button",
42034
- {
42035
- onClick: () => saveSecret(auth.tokenSecretRef ?? "API_TOKEN"),
42036
- className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
42037
- children: saved ? "✓" : "Save"
42038
- }
42039
- )
42040
- ] }),
42041
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-[10px]", children: "Token is stored in your OS keychain — never written to disk." })
42042
- ] }),
42495
+ auth.type === "bearer" && /* @__PURE__ */ jsxRuntimeExports.jsx(
42496
+ BearerPanel,
42497
+ {
42498
+ auth,
42499
+ secretValue,
42500
+ setSecretValue,
42501
+ saved,
42502
+ setAuth,
42503
+ saveSecret
42504
+ }
42505
+ ),
42043
42506
  auth.type === "basic" && /* @__PURE__ */ jsxRuntimeExports.jsx(
42044
42507
  BasicCredentialsFields,
42045
42508
  {
@@ -42431,6 +42894,80 @@ function BasicCredentialsFields({
42431
42894
  note && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[10px]", children: note })
42432
42895
  ] });
42433
42896
  }
42897
+ function BearerPanel({
42898
+ auth,
42899
+ secretValue,
42900
+ setSecretValue,
42901
+ saved,
42902
+ setAuth,
42903
+ saveSecret
42904
+ }) {
42905
+ const [keychainOpen, setKeychainOpen] = reactExports.useState(!!auth.tokenSecretRef);
42906
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
42907
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
42908
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-surface-400", children: [
42909
+ "Token",
42910
+ " ",
42911
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-500 text-[10px]", children: [
42912
+ "— supports ",
42913
+ "{{variables}}"
42914
+ ] })
42915
+ ] }),
42916
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
42917
+ VarInput,
42918
+ {
42919
+ value: auth.token ?? "",
42920
+ onChange: (v) => setAuth({ token: v }),
42921
+ placeholder: "{{token}} or paste a raw token",
42922
+ className: "bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono text-xs"
42923
+ }
42924
+ )
42925
+ ] }),
42926
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
42927
+ "button",
42928
+ {
42929
+ onClick: () => setKeychainOpen((o) => !o),
42930
+ className: "text-[10px] text-surface-500 hover:text-surface-300 text-left transition-colors w-fit",
42931
+ children: [
42932
+ keychainOpen ? "▾" : "▸",
42933
+ " Store in OS keychain instead"
42934
+ ]
42935
+ }
42936
+ ),
42937
+ keychainOpen && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 pl-3 border-l border-surface-800", children: [
42938
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500 text-[10px]", children: "Paste a raw token here to encrypt it in your OS keychain. Useful for static long-lived API tokens. Leave the value field above empty to use the keychain." }),
42939
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
42940
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
42941
+ "input",
42942
+ {
42943
+ type: "password",
42944
+ value: secretValue,
42945
+ onChange: (e) => setSecretValue(e.target.value),
42946
+ placeholder: auth.tokenSecretRef ? `Stored as "${auth.tokenSecretRef}"` : "Paste token",
42947
+ className: "flex-1 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono"
42948
+ }
42949
+ ),
42950
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
42951
+ "input",
42952
+ {
42953
+ value: auth.tokenSecretRef ?? "API_TOKEN",
42954
+ onChange: (e) => setAuth({ tokenSecretRef: e.target.value }),
42955
+ placeholder: "Keychain key",
42956
+ className: "w-32 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
42957
+ }
42958
+ ),
42959
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
42960
+ "button",
42961
+ {
42962
+ onClick: () => saveSecret(auth.tokenSecretRef ?? "API_TOKEN"),
42963
+ className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
42964
+ children: saved ? "✓" : "Save"
42965
+ }
42966
+ )
42967
+ ] })
42968
+ ] })
42969
+ ] });
42970
+ }
42434
42971
  const noSemi = 316, noSemiType = 317, incdec = 1, incdecPrefix = 2, questionDot = 3, JSXStartTag = 4, insertSemi = 318, spaces = 320, newline = 321, LineComment = 5, BlockComment = 6, Dialect_jsx = 0;
42435
42972
  const space = [
42436
42973
  9,
@@ -43012,16 +43549,22 @@ const SNIPPET_GROUPS = [
43012
43549
  group: "Variables — Set",
43013
43550
  items: [
43014
43551
  {
43015
- label: "Set variable",
43016
- code: `sp.variables.set("variable_name", "value");`
43552
+ label: "Save token from response (use in next requests)",
43553
+ code: `// Use collectionVariables so the value persists across requests
43554
+ const json = sp.response.json();
43555
+ sp.collectionVariables.set("token", json.access_token);`
43556
+ },
43557
+ {
43558
+ label: "Set collection variable (persists across requests)",
43559
+ code: `sp.collectionVariables.set("variable_name", "value");`
43017
43560
  },
43018
43561
  {
43019
- label: "Set environment variable",
43562
+ label: "Set environment variable (key must exist in env)",
43020
43563
  code: `sp.environment.set("variable_name", "value");`
43021
43564
  },
43022
43565
  {
43023
- label: "Set collection variable",
43024
- code: `sp.collectionVariables.set("variable_name", "value");`
43566
+ label: "Set variable (request-local only)",
43567
+ code: `sp.variables.set("variable_name", "value");`
43025
43568
  },
43026
43569
  {
43027
43570
  label: "Set global variable",
@@ -49775,7 +50318,11 @@ function RequestBuilder({ request }) {
49775
50318
  setTabResponse(activeTabId, null, null);
49776
50319
  try {
49777
50320
  const activeEnv = activeEnvironmentId ? environments[activeEnvironmentId]?.data ?? null : null;
49778
- const collectionVars = activeCollectionId ? collections[activeCollectionId]?.data.collectionVariables ?? {} : {};
50321
+ const sessionVars = useStore.getState().sessionVars;
50322
+ const collectionVars = {
50323
+ ...activeCollectionId ? collections[activeCollectionId]?.data.collectionVariables ?? {} : {},
50324
+ ...sessionVars
50325
+ };
49779
50326
  const inherited = useStore.getState().getInheritedAuthAndHeaders(request.id);
49780
50327
  const mergedAuth = request.auth.type !== "none" ? request.auth : inherited.auth ?? request.auth;
49781
50328
  const mergedHeaders = [
@@ -53381,7 +53928,7 @@ function RunnerModal() {
53381
53928
  {
53382
53929
  onClick: run,
53383
53930
  disabled: runnerRunning,
53384
- className: "px-4 py-1.5 text-xs bg-emerald-700 hover:bg-emerald-600 disabled:bg-surface-800 disabled:text-surface-400 rounded font-medium transition-colors flex items-center gap-1.5 whitespace-nowrap",
53931
+ className: "px-4 py-1.5 text-xs text-white bg-emerald-700 hover:bg-emerald-600 disabled:bg-surface-800 disabled:text-surface-400 rounded font-medium transition-colors flex items-center gap-1.5 whitespace-nowrap",
53385
53932
  children: [
53386
53933
  /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z", clipRule: "evenodd" }) }),
53387
53934
  runnerRunning ? "Running…" : "Run"
@@ -53489,7 +54036,7 @@ function RunnerModal() {
53489
54036
  },
53490
54037
  idx
53491
54038
  )) }) }) }),
53492
- summary && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-4 py-2 border-t border-surface-800 bg-surface-950/50 flex-shrink-0 text-xs", children: [
54039
+ summary && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-4 py-2 border-t border-surface-800 bg-surface-800/30 flex-shrink-0 text-xs", children: [
53493
54040
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 font-medium", children: [
53494
54041
  summary.passed,
53495
54042
  " passed"
@@ -56195,7 +56742,7 @@ function App() {
56195
56742
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
56196
56743
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
56197
56744
  "v",
56198
- "0.1.4"
56745
+ "0.1.6"
56199
56746
  ] }),
56200
56747
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
56201
56748
  /* @__PURE__ */ jsxRuntimeExports.jsx(