@nextsparkjs/core 0.1.0-beta.181 → 0.1.0-beta.183

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 (41) hide show
  1. package/dist/components/api/keys/CreateApiKeyDialog.d.ts.map +1 -1
  2. package/dist/components/api/keys/CreateApiKeyDialog.js +20 -4
  3. package/dist/components/devtools/api-explorer/ApiEndpointsSidebar.d.ts.map +1 -1
  4. package/dist/components/devtools/api-explorer/ApiEndpointsSidebar.js +4 -1
  5. package/dist/components/devtools/api-explorer/ApiExplorer.d.ts.map +1 -1
  6. package/dist/components/devtools/api-explorer/ApiExplorer.js +92 -48
  7. package/dist/components/devtools/api-explorer/ApiRequestPanel.d.ts.map +1 -1
  8. package/dist/components/devtools/api-explorer/ApiRequestPanel.js +36 -6
  9. package/dist/components/devtools/api-explorer/JsonViewer.d.ts.map +1 -1
  10. package/dist/components/devtools/api-explorer/JsonViewer.js +13 -8
  11. package/dist/components/devtools/api-explorer/PanelDivider.d.ts +26 -0
  12. package/dist/components/devtools/api-explorer/PanelDivider.d.ts.map +1 -0
  13. package/dist/components/devtools/api-explorer/PanelDivider.js +102 -0
  14. package/dist/components/devtools/api-explorer/PresetsTab.d.ts.map +1 -1
  15. package/dist/components/devtools/api-explorer/PresetsTab.js +35 -2
  16. package/dist/components/devtools/api-explorer/preset-placeholders.d.ts +52 -0
  17. package/dist/components/devtools/api-explorer/preset-placeholders.d.ts.map +1 -0
  18. package/dist/components/devtools/api-explorer/preset-placeholders.js +62 -0
  19. package/dist/lib/api/keys.d.ts +15 -0
  20. package/dist/lib/api/keys.d.ts.map +1 -1
  21. package/dist/lib/api/keys.js +15 -1
  22. package/dist/lib/config/app.config.d.ts.map +1 -1
  23. package/dist/lib/config/app.config.js +7 -0
  24. package/dist/lib/config/config-types.d.ts +12 -0
  25. package/dist/lib/config/config-types.d.ts.map +1 -1
  26. package/dist/lib/selectors/core-selectors.d.ts +3 -0
  27. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  28. package/dist/lib/selectors/domains/devtools.selectors.d.ts +3 -0
  29. package/dist/lib/selectors/domains/devtools.selectors.d.ts.map +1 -1
  30. package/dist/lib/selectors/domains/devtools.selectors.js +5 -2
  31. package/dist/lib/selectors/selectors.d.ts +6 -0
  32. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  33. package/dist/styles/classes.json +10 -5
  34. package/dist/styles/ui.css +1 -1
  35. package/dist/templates/app/devtools/api/layout.tsx +11 -32
  36. package/dist/templates/app/devtools/layout.tsx +3 -1
  37. package/dist/types/api-presets.d.ts +19 -1
  38. package/dist/types/api-presets.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/templates/app/devtools/api/layout.tsx +11 -32
  41. package/templates/app/devtools/layout.tsx +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CreateApiKeyDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/api/keys/CreateApiKeyDialog.tsx"],"names":[],"mappings":"AA+BA,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC3G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CAgRvF"}
1
+ {"version":3,"file":"CreateApiKeyDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/api/keys/CreateApiKeyDialog.tsx"],"names":[],"mappings":"AA+BA,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC3G;AAuBD,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CAkRvF"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
3
+ import { useMemo, useState } from "react";
4
4
  import { useMutation } from "@tanstack/react-query";
5
5
  import { Button } from "../../ui/button.js";
6
6
  import { Input } from "../../ui/input.js";
@@ -26,11 +26,27 @@ import { Badge } from "../../ui/badge.js";
26
26
  import { Separator } from "../../ui/separator.js";
27
27
  import { AlertTriangle, Info } from "lucide-react";
28
28
  import { toast } from "sonner";
29
- import { API_SCOPES, SCOPE_CATEGORIES } from "../../../lib/api/keys.js";
29
+ import { SCOPE_CATEGORIES, getApiScopes, getAppApiScopes } from "../../../lib/api/keys.js";
30
30
  import { sel } from "../../../lib/test/index.js";
31
+ function useScopeCatalogue() {
32
+ return useMemo(() => {
33
+ const appScopes = Object.keys(getAppApiScopes());
34
+ if (appScopes.length === 0) return SCOPE_CATEGORIES;
35
+ return {
36
+ ...SCOPE_CATEGORIES,
37
+ app: {
38
+ name: "Aplicaci\xF3n",
39
+ description: "Scopes que esta aplicaci\xF3n define para sus propias rutas",
40
+ scopes: appScopes
41
+ }
42
+ };
43
+ }, []);
44
+ }
31
45
  function CreateApiKeyDialog({ open, onClose, onSuccess }) {
32
46
  const [name, setName] = useState("");
33
47
  const [selectedScopes, setSelectedScopes] = useState([]);
48
+ const scopeCatalogue = useScopeCatalogue();
49
+ const scopeLabels = useMemo(() => getApiScopes(), []);
34
50
  const [expiresAt, setExpiresAt] = useState("");
35
51
  const [expiryOption, setExpiryOption] = useState("never");
36
52
  const createApiKey = useMutation({
@@ -168,7 +184,7 @@ function CreateApiKeyDialog({ open, onClose, onSuccess }) {
168
184
  /* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }),
169
185
  /* @__PURE__ */ jsx(AlertDescription, { children: "Solo puedes asignar permisos que tu rol actual permite. Los permisos no se pueden cambiar despu\xE9s de crear la API key." })
170
186
  ] }),
171
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: Object.entries(SCOPE_CATEGORIES).map(([categoryKey, category]) => {
187
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: Object.entries(scopeCatalogue).map(([categoryKey, category]) => {
172
188
  const isFullySelected = isCategoryFullySelected(category.scopes);
173
189
  const isPartiallySelected = isCategoryPartiallySelected(category.scopes);
174
190
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
@@ -208,7 +224,7 @@ function CreateApiKeyDialog({ open, onClose, onSuccess }) {
208
224
  ),
209
225
  /* @__PURE__ */ jsxs(Label, { htmlFor: scope, className: "text-sm", children: [
210
226
  /* @__PURE__ */ jsx("code", { className: "text-xs bg-muted px-1 py-0.5 rounded mr-2", children: scope }),
211
- API_SCOPES[scope]
227
+ scopeLabels[scope]
212
228
  ] })
213
229
  ] }, scope)) }),
214
230
  categoryKey !== "system" && /* @__PURE__ */ jsx(Separator, {})
@@ -1 +1 @@
1
- {"version":3,"file":"ApiEndpointsSidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiEndpointsSidebar.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AA4CrD,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;CACnB;AAED,UAAU,wBAAwB;IAChC,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAA;IAC9C,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACzC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAClF,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GACjB,EAAE,wBAAwB,2CAkQ1B"}
1
+ {"version":3,"file":"ApiEndpointsSidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiEndpointsSidebar.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AA4CrD,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;CACnB;AAED,UAAU,wBAAwB;IAChC,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAA;IAC9C,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACzC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAClF,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GACjB,EAAE,wBAAwB,2CAyQ1B"}
@@ -50,7 +50,10 @@ function ApiEndpointsSidebar({
50
50
  });
51
51
  const [expandedPrefixes, setExpandedPrefixes] = useState(() => {
52
52
  const saved = loadState("sidebar:expandedPrefixes");
53
- return new Set(saved ?? []);
53
+ if (saved) return new Set(saved);
54
+ return new Set(
55
+ groupByPathPrefix(routes.theme ?? []).map((group) => `theme:${group.prefix}`)
56
+ );
54
57
  });
55
58
  useEffect(() => {
56
59
  saveState("sidebar:expandedCategories", Array.from(expandedCategories));
@@ -1 +1 @@
1
- {"version":3,"file":"ApiExplorer.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiExplorer.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAqB5F,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAA;IAC9C,eAAe,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC1D;AAwBD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,gBAAgB,2CAibxE"}
1
+ {"version":3,"file":"ApiExplorer.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiExplorer.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AA2B5F,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAA;IAC9C,eAAe,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC1D;AAwBD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,gBAAgB,2CA4dxE"}
@@ -8,12 +8,17 @@ import { sel } from "../../../lib/test/index.js";
8
8
  import { ApiEndpointsSidebar } from "./ApiEndpointsSidebar.js";
9
9
  import { ApiRequestPanel } from "./ApiRequestPanel.js";
10
10
  import { ApiResponsePanel } from "./ApiResponsePanel.js";
11
+ import { PanelDivider } from "./PanelDivider.js";
11
12
  import { useApiRequest } from "../api-tester/hooks/useApiRequest.js";
12
13
  import { useApiExplorerNavigation } from "../../../hooks/useApiExplorerNavigation.js";
13
14
  import { extractPathParams, buildUrl } from "../api-tester/utils/url-builder.js";
14
15
  import { ApiPresetsService } from "../../../lib/services/api-presets.service.js";
15
16
  import { ApiDocsService } from "../../../lib/services/api-docs.service.js";
16
17
  import { loadState, saveState, draftKey } from "./explorer-storage.js";
18
+ import {
19
+ createPresetPlaceholderValues,
20
+ resolvePresetPlaceholders
21
+ } from "./preset-placeholders.js";
17
22
  function findRouteByPath(routes, path) {
18
23
  for (const category of Object.keys(routes)) {
19
24
  const found = routes[category].find((r) => r.path === path);
@@ -50,6 +55,24 @@ function ApiExplorer({ routes, initialEndpoint }) {
50
55
  });
51
56
  const [body, setBody] = useState("");
52
57
  const [actAsUserId, setActAsUserId] = useState(() => loadState("actAsUserId") ?? "");
58
+ const [splitPercent, setSplitPercent] = useState(55);
59
+ const splitRowRef = useRef(null);
60
+ useEffect(() => {
61
+ const saved = loadState("splitPercent");
62
+ if (typeof saved === "number") setSplitPercent(saved);
63
+ }, []);
64
+ const handleSplitChange = useCallback((percent) => {
65
+ setSplitPercent(percent);
66
+ saveState("splitPercent", percent);
67
+ }, []);
68
+ const [isSideBySide, setIsSideBySide] = useState(false);
69
+ useEffect(() => {
70
+ const query = window.matchMedia("(min-width: 1024px)");
71
+ const sync = () => setIsSideBySide(query.matches);
72
+ sync();
73
+ query.addEventListener("change", sync);
74
+ return () => query.removeEventListener("change", sync);
75
+ }, []);
53
76
  const [tabsModifiedByPreset, setTabsModifiedByPreset] = useState(
54
77
  () => /* @__PURE__ */ new Set()
55
78
  );
@@ -126,7 +149,16 @@ function ApiExplorer({ routes, initialEndpoint }) {
126
149
  apiKey: authType === "apiKey" ? apiKey : void 0
127
150
  });
128
151
  }, [selectedEndpoint, pathParams, queryParams, headers, method, body, authType, apiKey, bypassMode, selectedTeamId, actAsUserId, execute]);
129
- const handleApplyPreset = useCallback((preset) => {
152
+ const handleApplyPreset = useCallback((rawPreset) => {
153
+ const values = createPresetPlaceholderValues();
154
+ const preset = {
155
+ ...rawPreset,
156
+ pathParams: resolvePresetPlaceholders(rawPreset.pathParams, values),
157
+ params: resolvePresetPlaceholders(rawPreset.params, values),
158
+ headers: resolvePresetPlaceholders(rawPreset.headers, values),
159
+ payload: resolvePresetPlaceholders(rawPreset.payload, values),
160
+ sessionConfig: resolvePresetPlaceholders(rawPreset.sessionConfig, values)
161
+ };
130
162
  const modifiedTabs = /* @__PURE__ */ new Set();
131
163
  if (preset.method) {
132
164
  setMethod(preset.method);
@@ -172,11 +204,7 @@ function ApiExplorer({ routes, initialEndpoint }) {
172
204
  setBypassMode(preset.sessionConfig.crossTeam);
173
205
  }
174
206
  if (preset.sessionConfig.teamId) {
175
- let teamId = preset.sessionConfig.teamId;
176
- if (teamId === "{{FIRST_TEAM_ID}}" && typeof window !== "undefined") {
177
- teamId = localStorage.getItem("firstTeamId") || localStorage.getItem("activeTeamId") || "";
178
- }
179
- setSelectedTeamId(teamId);
207
+ setSelectedTeamId(preset.sessionConfig.teamId);
180
208
  }
181
209
  if (preset.sessionConfig.authType) {
182
210
  setAuthType(preset.sessionConfig.authType);
@@ -258,7 +286,7 @@ function ApiExplorer({ routes, initialEndpoint }) {
258
286
  }
259
287
  }, [routes, initialEndpoint, urlPath, urlMethod, navigateToEndpoint, applyEndpointState]);
260
288
  const totalRoutes = Object.values(routes).reduce((sum, arr) => sum + arr.length, 0);
261
- return /* @__PURE__ */ jsxs("div", { className: "flex h-[calc(100vh-4rem)] w-full overflow-hidden", "data-cy": sel("devtools.apiExplorer.container"), children: [
289
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full overflow-hidden", "data-cy": sel("devtools.apiExplorer.container"), children: [
262
290
  /* @__PURE__ */ jsxs("div", { className: "lg:hidden fixed top-0 left-0 right-0 z-50 h-14 bg-background border-b flex items-center px-4 gap-3", children: [
263
291
  /* @__PURE__ */ jsx(
264
292
  Button,
@@ -307,49 +335,65 @@ function ApiExplorer({ routes, initialEndpoint }) {
307
335
  )
308
336
  }
309
337
  ),
310
- /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col lg:flex-row min-w-0 pt-14 lg:pt-0", children: selectedEndpoint ? /* @__PURE__ */ jsxs(Fragment, { children: [
311
- /* @__PURE__ */ jsx("div", { className: "flex-1 lg:flex-[1.2] min-h-0 border-b lg:border-b-0 lg:border-r overflow-hidden", children: /* @__PURE__ */ jsx(
312
- ApiRequestPanel,
338
+ /* @__PURE__ */ jsx("div", { ref: splitRowRef, className: "flex-1 flex flex-col lg:flex-row min-w-0 pt-14 lg:pt-0", children: selectedEndpoint ? /* @__PURE__ */ jsxs(Fragment, { children: [
339
+ /* @__PURE__ */ jsx(
340
+ "div",
313
341
  {
314
- route: selectedEndpoint.route,
315
- basePath: selectedEndpoint.path,
316
- method,
317
- pathParams,
318
- queryParams,
319
- headers,
320
- authType,
321
- apiKey,
322
- bypassMode,
323
- selectedTeamId,
324
- actAsUserId,
325
- body,
326
- status,
327
- onMethodChange: setMethod,
328
- onPathParamsChange: setPathParams,
329
- onQueryParamsChange: setQueryParams,
330
- onHeadersChange: setHeaders,
331
- onAuthTypeChange: setAuthType,
332
- onApiKeyChange: setApiKey,
333
- onBypassModeChange: setBypassMode,
334
- onTeamChange: setSelectedTeamId,
335
- onActAsUserChange: setActAsUserId,
336
- onBodyChange: setBody,
337
- onSend: handleSend,
338
- onCancel: cancel,
339
- endpointPresets,
340
- endpointDoc,
341
- onApplyPreset: handleApplyPreset,
342
- tabsModifiedByPreset,
343
- onClearPresetIndicator: (tab) => {
344
- setTabsModifiedByPreset((prev) => {
345
- const next = new Set(prev);
346
- next.delete(tab);
347
- return next;
348
- });
349
- }
342
+ className: "flex-1 min-h-0 min-w-0 border-b lg:border-b-0 overflow-hidden",
343
+ style: isSideBySide ? { flex: `0 0 ${splitPercent}%` } : void 0,
344
+ "data-split-panel": "request",
345
+ children: /* @__PURE__ */ jsx(
346
+ ApiRequestPanel,
347
+ {
348
+ route: selectedEndpoint.route,
349
+ basePath: selectedEndpoint.path,
350
+ method,
351
+ pathParams,
352
+ queryParams,
353
+ headers,
354
+ authType,
355
+ apiKey,
356
+ bypassMode,
357
+ selectedTeamId,
358
+ actAsUserId,
359
+ body,
360
+ status,
361
+ onMethodChange: setMethod,
362
+ onPathParamsChange: setPathParams,
363
+ onQueryParamsChange: setQueryParams,
364
+ onHeadersChange: setHeaders,
365
+ onAuthTypeChange: setAuthType,
366
+ onApiKeyChange: setApiKey,
367
+ onBypassModeChange: setBypassMode,
368
+ onTeamChange: setSelectedTeamId,
369
+ onActAsUserChange: setActAsUserId,
370
+ onBodyChange: setBody,
371
+ onSend: handleSend,
372
+ onCancel: cancel,
373
+ endpointPresets,
374
+ endpointDoc,
375
+ onApplyPreset: handleApplyPreset,
376
+ tabsModifiedByPreset,
377
+ onClearPresetIndicator: (tab) => {
378
+ setTabsModifiedByPreset((prev) => {
379
+ const next = new Set(prev);
380
+ next.delete(tab);
381
+ return next;
382
+ });
383
+ }
384
+ }
385
+ )
350
386
  }
351
- ) }),
352
- /* @__PURE__ */ jsx("div", { className: "h-[40vh] lg:h-auto lg:flex-1 min-h-[200px] overflow-hidden", children: /* @__PURE__ */ jsx(
387
+ ),
388
+ /* @__PURE__ */ jsx(
389
+ PanelDivider,
390
+ {
391
+ value: splitPercent,
392
+ onChange: handleSplitChange,
393
+ containerRef: splitRowRef
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsx("div", { className: "h-[40vh] lg:h-auto lg:flex-1 min-h-[200px] min-w-0 overflow-hidden", children: /* @__PURE__ */ jsx(
353
397
  ApiResponsePanel,
354
398
  {
355
399
  status,
@@ -1 +1 @@
1
- {"version":3,"file":"ApiRequestPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiRequestPanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAa/E,UAAU,oBAAoB;IAC5B,KAAK,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,UAAU,CAAA;IAClB,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC5C,kBAAkB,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IACjD,mBAAmB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IACrD,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IAClD,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAA;IAEpB,eAAe,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC3C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAE3C,oBAAoB,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAAA;IACzD,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAA;CACtE;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,cAAc,EACd,WAAW,EACX,IAAI,EACJ,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,EAAE,oBAAoB,2CAgOtB"}
1
+ {"version":3,"file":"ApiRequestPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiRequestPanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAc/E,UAAU,oBAAoB;IAC5B,KAAK,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,UAAU,CAAA;IAClB,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC5C,kBAAkB,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IACjD,mBAAmB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IACrD,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IAClD,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAA;IAEpB,eAAe,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC3C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAE3C,oBAAoB,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAAA;IACzD,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAA;CACtE;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,cAAc,EACd,WAAW,EACX,IAAI,EACJ,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,EAAE,oBAAoB,2CAiQtB"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useMemo } from "react";
4
- import { Send, X } from "lucide-react";
3
+ import { useEffect, useMemo, useState } from "react";
4
+ import { ChevronRight, Send, X } from "lucide-react";
5
5
  import { Button } from "../../ui/button.js";
6
6
  import { Badge } from "../../ui/badge.js";
7
7
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/tabs.js";
@@ -17,6 +17,7 @@ import { PayloadEditor } from "../api-tester/PayloadEditor.js";
17
17
  import { buildUrl, validatePathParams, validateJsonBody } from "../api-tester/utils/url-builder.js";
18
18
  import { PresetsTab } from "./PresetsTab.js";
19
19
  import { ApiDocsModal } from "./ApiDocsModal.js";
20
+ import { loadState, saveState } from "./explorer-storage.js";
20
21
  const methodColors = {
21
22
  GET: "bg-emerald-500/20 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",
22
23
  POST: "bg-blue-500/20 text-blue-600 dark:text-blue-400 border-blue-500/30",
@@ -68,6 +69,20 @@ function ApiRequestPanel({
68
69
  return pathErrors.length === 0 && !bodyError;
69
70
  }, [pathParams, method, body]);
70
71
  const showPayloadEditor = ["POST", "PATCH", "PUT"].includes(method);
72
+ const [sessionOpen, setSessionOpen] = useState(true);
73
+ useEffect(() => {
74
+ setSessionOpen(loadState("sessionOpen") ?? true);
75
+ }, []);
76
+ useEffect(() => {
77
+ saveState("sessionOpen", sessionOpen);
78
+ }, [sessionOpen]);
79
+ const sessionSummary = useMemo(() => {
80
+ const parts = [authType === "apiKey" ? "API key" : "session"];
81
+ if (bypassMode) parts.push("cross-team");
82
+ if (selectedTeamId) parts.push(selectedTeamId);
83
+ if (actAsUserId.trim()) parts.push(`as ${actAsUserId.trim()}`);
84
+ return parts.join(" \xB7 ");
85
+ }, [authType, bypassMode, selectedTeamId, actAsUserId]);
71
86
  const handlePathParamsChange = (params) => {
72
87
  onPathParamsChange(params);
73
88
  onClearPresetIndicator == null ? void 0 : onClearPresetIndicator("params");
@@ -127,7 +142,22 @@ function ApiRequestPanel({
127
142
  /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground uppercase tracking-wide mb-1", children: t("urlPreview") }),
128
143
  /* @__PURE__ */ jsx("code", { className: "font-mono text-xs break-all text-foreground", children: previewUrl })
129
144
  ] }),
130
- /* @__PURE__ */ jsx(
145
+ /* @__PURE__ */ jsxs(
146
+ "button",
147
+ {
148
+ type: "button",
149
+ onClick: () => setSessionOpen((open) => !open),
150
+ className: "flex w-full items-center gap-2 pt-2 border-t text-left text-xs text-muted-foreground hover:text-foreground",
151
+ "aria-expanded": sessionOpen,
152
+ "data-cy": sel("devtools.apiExplorer.request.toggleSession"),
153
+ children: [
154
+ /* @__PURE__ */ jsx(ChevronRight, { className: cn("h-3.5 w-3.5 transition-transform", sessionOpen && "rotate-90") }),
155
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Session" }),
156
+ !sessionOpen && /* @__PURE__ */ jsx("span", { className: "truncate", children: sessionSummary })
157
+ ]
158
+ }
159
+ ),
160
+ /* @__PURE__ */ jsx("div", { className: cn(!sessionOpen && "hidden"), children: /* @__PURE__ */ jsx(
131
161
  AuthSelector,
132
162
  {
133
163
  authType,
@@ -141,9 +171,9 @@ function ApiRequestPanel({
141
171
  onTeamChange,
142
172
  onActAsUserChange
143
173
  }
144
- )
174
+ ) })
145
175
  ] }),
146
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 flex flex-col", children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "params", className: "flex-1 flex flex-col", children: [
176
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 flex flex-col", children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "params", className: "flex-1 flex flex-col min-h-0", children: [
147
177
  /* @__PURE__ */ jsx("div", { className: "border-b px-4", children: /* @__PURE__ */ jsxs(TabsList, { className: "h-9 bg-transparent p-0 border-0", children: [
148
178
  /* @__PURE__ */ jsxs(
149
179
  TabsTrigger,
@@ -196,7 +226,7 @@ function ApiRequestPanel({
196
226
  }
197
227
  )
198
228
  ] }) }),
199
- /* @__PURE__ */ jsxs(ScrollArea, { className: "flex-1", children: [
229
+ /* @__PURE__ */ jsxs(ScrollArea, { className: "flex-1 min-h-0", children: [
200
230
  /* @__PURE__ */ jsxs(TabsContent, { value: "params", className: "m-0 p-4 space-y-4", children: [
201
231
  pathParams.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
202
232
  /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium mb-2", children: "Path Parameters" }),
@@ -1 +1 @@
1
- {"version":3,"file":"JsonViewer.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/JsonViewer.tsx"],"names":[],"mappings":"AAIA,OAAO,qCAAqC,CAAA;AAM5C,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE,CAAA;AACpD,KAAK,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;AAE1C,UAAU,eAAe;IACvB,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,eAAmB,EAAE,EAAE,eAAe,2CAqBnF;AAED,UAAU,2BAA2B;IACnC,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,WAAW,CAAA;IAC7B,mCAAmC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,SAAS,EACT,eAAmB,EACnB,YAAmB,GACpB,EAAE,2BAA2B,2CA+E7B"}
1
+ {"version":3,"file":"JsonViewer.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/JsonViewer.tsx"],"names":[],"mappings":"AAIA,OAAO,qCAAqC,CAAA;AAM5C,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE,CAAA;AACpD,KAAK,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;AAE1C,UAAU,eAAe;IACvB,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,eAAmB,EAAE,EAAE,eAAe,2CA4BnF;AAED,UAAU,2BAA2B;IACnC,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,WAAW,CAAA;IAC7B,mCAAmC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,SAAS,EACT,eAAmB,EACnB,YAAmB,GACpB,EAAE,2BAA2B,2CAoF7B"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
3
+ import { useCallback, useMemo, useState } from "react";
4
4
  import { JsonView, allExpanded, collapseAllNested, defaultStyles, darkStyles } from "react-json-view-lite";
5
5
  import "react-json-view-lite/dist/index.css";
6
6
  import { useTheme } from "next-themes";
@@ -10,7 +10,10 @@ import { Button } from "../../ui/button.js";
10
10
  function JsonViewer({ data, className, initialExpanded = 2 }) {
11
11
  const { resolvedTheme } = useTheme();
12
12
  const isDark = resolvedTheme === "dark";
13
- const shouldExpandNode = initialExpanded === "all" ? allExpanded : initialExpanded === "none" ? collapseAllNested : (level) => level < initialExpanded;
13
+ const shouldExpandNode = useMemo(
14
+ () => initialExpanded === "all" ? allExpanded : initialExpanded === "none" ? collapseAllNested : (level) => level < initialExpanded,
15
+ [initialExpanded]
16
+ );
14
17
  return /* @__PURE__ */ jsx("div", { className: cn("font-mono text-sm", className), children: /* @__PURE__ */ jsx(
15
18
  JsonView,
16
19
  {
@@ -29,19 +32,22 @@ function JsonViewerWithControls({
29
32
  const { resolvedTheme } = useTheme();
30
33
  const isDark = resolvedTheme === "dark";
31
34
  const [expandState, setExpandState] = useState(initialExpanded);
32
- const [viewKey, setViewKey] = useState(0);
35
+ const [applyNonce, setApplyNonce] = useState(0);
33
36
  const handleExpandAll = useCallback(() => {
34
37
  setExpandState("all");
35
- setViewKey((k) => k + 1);
38
+ setApplyNonce((n) => n + 1);
36
39
  }, []);
37
40
  const handleCollapseAll = useCallback(() => {
38
41
  setExpandState("none");
39
- setViewKey((k) => k + 1);
42
+ setApplyNonce((n) => n + 1);
40
43
  }, []);
41
44
  const handleCopy = useCallback(() => {
42
45
  navigator.clipboard.writeText(JSON.stringify(data, null, 2));
43
46
  }, [data]);
44
- const shouldExpandNode = expandState === "all" ? allExpanded : expandState === "none" ? collapseAllNested : (level) => level < expandState;
47
+ const shouldExpandNode = useMemo(
48
+ () => expandState === "all" ? allExpanded : expandState === "none" ? collapseAllNested : (level) => level < expandState,
49
+ [expandState, applyNonce]
50
+ );
45
51
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
46
52
  showControls && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 mb-2", "data-cy": "json-viewer-controls", children: [
47
53
  /* @__PURE__ */ jsxs(
@@ -94,8 +100,7 @@ function JsonViewerWithControls({
94
100
  data,
95
101
  shouldExpandNode,
96
102
  style: isDark ? darkStyles : defaultStyles
97
- },
98
- viewKey
103
+ }
99
104
  ) })
100
105
  ] });
101
106
  }
@@ -0,0 +1,26 @@
1
+ interface PanelDividerProps {
2
+ /** Current split, as the percentage of the container the LEFT panel takes. */
3
+ value: number;
4
+ onChange: (percent: number) => void;
5
+ /** The element the percentage is measured against. */
6
+ containerRef: React.RefObject<HTMLElement | null>;
7
+ min?: number;
8
+ max?: number;
9
+ label?: string;
10
+ }
11
+ /**
12
+ * Drag handle between two horizontally-split panels.
13
+ *
14
+ * Hand-rolled rather than pulled from a panel library: it needs one number, and a devtools
15
+ * screen is not worth a dependency in every app the framework ships.
16
+ *
17
+ * It reports a PERCENTAGE, not pixels, so a split chosen on a wide monitor still looks
18
+ * deliberate on a laptop. Pointer capture means a fast drag that outruns the cursor keeps
19
+ * working instead of being dropped the moment the pointer leaves the 6px strip.
20
+ *
21
+ * It is also a real `separator` widget: focusable, moved with the arrow keys, and it reports
22
+ * its position — dragging cannot be the only way to reach a layout.
23
+ */
24
+ export declare function PanelDivider({ value, onChange, containerRef, min, max, label, }: PanelDividerProps): import("react/jsx-runtime").JSX.Element;
25
+ export {};
26
+ //# sourceMappingURL=PanelDivider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PanelDivider.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/PanelDivider.tsx"],"names":[],"mappings":"AAMA,UAAU,iBAAiB;IACzB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,sDAAsD;IACtD,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IACjD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,GAAQ,EACR,GAAQ,EACR,KAAuB,GACxB,EAAE,iBAAiB,2CAiGnB"}
@@ -0,0 +1,102 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef } from "react";
4
+ import { cn } from "../../../lib/utils/index.js";
5
+ import { sel } from "../../../lib/test/index.js";
6
+ function PanelDivider({
7
+ value,
8
+ onChange,
9
+ containerRef,
10
+ min = 25,
11
+ max = 75,
12
+ label = "Resize panels"
13
+ }) {
14
+ const draggingRef = useRef(false);
15
+ const clamp = useCallback((percent) => Math.min(max, Math.max(min, percent)), [min, max]);
16
+ const percentFromClientX = useCallback(
17
+ (clientX) => {
18
+ const container = containerRef.current;
19
+ if (!container) return null;
20
+ const rect = container.getBoundingClientRect();
21
+ if (rect.width === 0) return null;
22
+ return clamp((clientX - rect.left) / rect.width * 100);
23
+ },
24
+ [containerRef, clamp]
25
+ );
26
+ const handlePointerDown = useCallback((event) => {
27
+ draggingRef.current = true;
28
+ event.currentTarget.setPointerCapture(event.pointerId);
29
+ document.body.style.userSelect = "none";
30
+ document.body.style.cursor = "col-resize";
31
+ }, []);
32
+ const handlePointerMove = useCallback(
33
+ (event) => {
34
+ if (!draggingRef.current) return;
35
+ const next = percentFromClientX(event.clientX);
36
+ if (next !== null) onChange(next);
37
+ },
38
+ [percentFromClientX, onChange]
39
+ );
40
+ const endDrag = useCallback((event) => {
41
+ if (!draggingRef.current) return;
42
+ draggingRef.current = false;
43
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
44
+ event.currentTarget.releasePointerCapture(event.pointerId);
45
+ }
46
+ document.body.style.userSelect = "";
47
+ document.body.style.cursor = "";
48
+ }, []);
49
+ useEffect(
50
+ () => () => {
51
+ document.body.style.userSelect = "";
52
+ document.body.style.cursor = "";
53
+ },
54
+ []
55
+ );
56
+ const handleKeyDown = useCallback(
57
+ (event) => {
58
+ const step = event.shiftKey ? 10 : 2;
59
+ if (event.key === "ArrowLeft") {
60
+ event.preventDefault();
61
+ onChange(clamp(value - step));
62
+ } else if (event.key === "ArrowRight") {
63
+ event.preventDefault();
64
+ onChange(clamp(value + step));
65
+ } else if (event.key === "Home") {
66
+ event.preventDefault();
67
+ onChange(min);
68
+ } else if (event.key === "End") {
69
+ event.preventDefault();
70
+ onChange(max);
71
+ }
72
+ },
73
+ [value, onChange, clamp, min, max]
74
+ );
75
+ return /* @__PURE__ */ jsx(
76
+ "div",
77
+ {
78
+ role: "separator",
79
+ "aria-orientation": "vertical",
80
+ "aria-label": label,
81
+ "aria-valuenow": Math.round(value),
82
+ "aria-valuemin": min,
83
+ "aria-valuemax": max,
84
+ tabIndex: 0,
85
+ onPointerDown: handlePointerDown,
86
+ onPointerMove: handlePointerMove,
87
+ onPointerUp: endDrag,
88
+ onPointerCancel: endDrag,
89
+ onKeyDown: handleKeyDown,
90
+ className: cn(
91
+ "hidden lg:flex shrink-0 w-1.5 cursor-col-resize items-center justify-center",
92
+ "bg-border/40 hover:bg-primary/60 focus-visible:bg-primary/60 transition-colors",
93
+ "focus-visible:outline-none"
94
+ ),
95
+ "data-cy": sel("devtools.apiExplorer.panelDivider"),
96
+ children: /* @__PURE__ */ jsx("div", { className: "h-8 w-0.5 rounded-full bg-foreground/25" })
97
+ }
98
+ );
99
+ }
100
+ export {
101
+ PanelDivider
102
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"PresetsTab.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/PresetsTab.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD,UAAU,eAAe;IACvB,eAAe,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC1C,aAAa,EAAE,UAAU,CAAA;IACzB,aAAa,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CAC3C;AAED,wBAAgB,UAAU,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,eAAe,2CA8H5F"}
1
+ {"version":3,"file":"PresetsTab.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/PresetsTab.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AA0CrD,UAAU,eAAe;IACvB,eAAe,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC1C,aAAa,EAAE,UAAU,CAAA;IACzB,aAAa,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CAC3C;AAED,wBAAgB,UAAU,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,eAAe,2CAgI5F"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { Play, Eye, BookOpen } from "lucide-react";
3
+ import { useEffect, useState } from "react";
4
+ import { Play, Eye, BookOpen, RefreshCw } from "lucide-react";
5
5
  import { Button } from "../../ui/button.js";
6
6
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../ui/card.js";
7
7
  import { Badge } from "../../ui/badge.js";
@@ -15,6 +15,38 @@ import {
15
15
  } from "../../ui/dialog.js";
16
16
  import { ScrollArea } from "../../ui/scroll-area.js";
17
17
  import { sel } from "../../../lib/test/index.js";
18
+ import { readPresetRunToken, regeneratePresetRunToken } from "./preset-placeholders.js";
19
+ function RunTokenBar({ presets }) {
20
+ const [token, setToken] = useState(null);
21
+ useEffect(() => {
22
+ setToken(readPresetRunToken());
23
+ }, []);
24
+ const usesRunToken = presets.some((preset) => JSON.stringify(preset).includes("{{RUN}}"));
25
+ if (!usesRunToken || !token) return null;
26
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border bg-muted/40 px-3 py-2", children: [
27
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Run" }),
28
+ /* @__PURE__ */ jsx("code", { className: "font-mono text-xs text-foreground", children: token }),
29
+ /* @__PURE__ */ jsxs(
30
+ Button,
31
+ {
32
+ variant: "ghost",
33
+ size: "sm",
34
+ className: "h-6 px-2",
35
+ onClick: () => setToken(regeneratePresetRunToken()),
36
+ "data-cy": sel("devtools.apiExplorer.presets.regenerateRunToken"),
37
+ children: [
38
+ /* @__PURE__ */ jsx(RefreshCw, { className: "h-3 w-3 mr-1" }),
39
+ "New run"
40
+ ]
41
+ }
42
+ ),
43
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
44
+ "Every ",
45
+ "{{RUN}}",
46
+ " in these presets resolves to this. Start a new run to send values nothing has used before."
47
+ ] })
48
+ ] });
49
+ }
18
50
  function PresetsTab({ endpointPresets, currentMethod, onApplyPreset }) {
19
51
  const [selectedPreset, setSelectedPreset] = useState(null);
20
52
  const [isDetailOpen, setIsDetailOpen] = useState(false);
@@ -44,6 +76,7 @@ function PresetsTab({ endpointPresets, currentMethod, onApplyPreset }) {
44
76
  };
45
77
  return /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", "data-cy": sel("devtools.apiExplorer.presets.tab"), children: [
46
78
  endpointPresets.summary && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: endpointPresets.summary }),
79
+ /* @__PURE__ */ jsx(RunTokenBar, { presets: endpointPresets.presets }),
47
80
  filteredPresets.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground p-4 bg-muted/30 rounded-lg", children: [
48
81
  /* @__PURE__ */ jsxs("p", { children: [
49
82
  "No presets for ",