@djangocfg/ui-tools 2.1.270 → 2.1.272

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.
package/dist/index.cjs CHANGED
@@ -6,7 +6,7 @@ require('./chunk-F2N7P5XU.cjs');
6
6
  var chunkIHAY6FO6_cjs = require('./chunk-IHAY6FO6.cjs');
7
7
  var chunk77HQWEQ6_cjs = require('./chunk-77HQWEQ6.cjs');
8
8
  var chunkF2CMIIOH_cjs = require('./chunk-F2CMIIOH.cjs');
9
- var chunkVAL2LCQD_cjs = require('./chunk-VAL2LCQD.cjs');
9
+ var chunkWM4RT5KX_cjs = require('./chunk-WM4RT5KX.cjs');
10
10
  var chunk33AMWFBZ_cjs = require('./chunk-33AMWFBZ.cjs');
11
11
  require('./chunk-2SMCH62O.cjs');
12
12
  var chunkL37FZYJU_cjs = require('./chunk-L37FZYJU.cjs');
@@ -372,7 +372,7 @@ var CodeBlock = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ code, language, isUs
372
372
  }
373
373
  ),
374
374
  /* @__PURE__ */ jsxRuntime.jsx(
375
- chunkVAL2LCQD_cjs.PrettyCode_default,
375
+ chunkWM4RT5KX_cjs.PrettyCode_default,
376
376
  {
377
377
  data: code,
378
378
  language,
@@ -735,14 +735,14 @@ function OpenapiLoadingFallback() {
735
735
  }
736
736
  chunkWGEGR3DF_cjs.__name(OpenapiLoadingFallback, "OpenapiLoadingFallback");
737
737
  var LazyPlaygroundLayout = createLazyComponent(
738
- () => import('./PlaygroundLayout-LIAN63CZ.cjs').then((mod) => ({ default: mod.PlaygroundLayout })),
738
+ () => import('./PlaygroundLayout-ZO2LO7M5.cjs').then((mod) => ({ default: mod.PlaygroundLayout })),
739
739
  {
740
740
  displayName: "LazyPlaygroundLayout",
741
741
  fallback: /* @__PURE__ */ jsxRuntime.jsx(OpenapiLoadingFallback, {})
742
742
  }
743
743
  );
744
744
  var LazyOpenapiViewer = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ config }) => {
745
- return /* @__PURE__ */ jsxRuntime.jsx(chunkVAL2LCQD_cjs.PlaygroundProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(LazyPlaygroundLayout, {}) });
745
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkWM4RT5KX_cjs.PlaygroundProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(LazyPlaygroundLayout, {}) });
746
746
  }, "LazyOpenapiViewer");
747
747
  LazyOpenapiViewer.displayName = "LazyOpenapiViewer";
748
748
  var LazyJsonSchemaForm = createLazyComponent(
@@ -890,11 +890,11 @@ function LottiePlayer(props) {
890
890
  }
891
891
  chunkWGEGR3DF_cjs.__name(LottiePlayer, "LottiePlayer");
892
892
  var PlaygroundLayout = React3.lazy(
893
- () => import('./PlaygroundLayout-LIAN63CZ.cjs').then((mod) => ({ default: mod.PlaygroundLayout }))
893
+ () => import('./PlaygroundLayout-ZO2LO7M5.cjs').then((mod) => ({ default: mod.PlaygroundLayout }))
894
894
  );
895
895
  var LoadingFallback9 = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground", children: "Loading API Playground..." }) }), "LoadingFallback");
896
896
  var Playground = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(({ config }) => {
897
- return /* @__PURE__ */ jsxRuntime.jsx(chunkVAL2LCQD_cjs.PlaygroundProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback9, {}), children: /* @__PURE__ */ jsxRuntime.jsx(PlaygroundLayout, {}) }) });
897
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkWM4RT5KX_cjs.PlaygroundProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(React3.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingFallback9, {}), children: /* @__PURE__ */ jsxRuntime.jsx(PlaygroundLayout, {}) }) });
898
898
  }, "Playground");
899
899
  var OpenapiViewer_default = Playground;
900
900
  var CronSchedulerClient = React3.lazy(() => import('./CronScheduler.client-A4GO6YBY.cjs'));
@@ -2151,7 +2151,7 @@ Object.defineProperty(exports, "useCronWeekDays", {
2151
2151
  });
2152
2152
  Object.defineProperty(exports, "PrettyCode", {
2153
2153
  enumerable: true,
2154
- get: function () { return chunkVAL2LCQD_cjs.PrettyCode_default; }
2154
+ get: function () { return chunkWM4RT5KX_cjs.PrettyCode_default; }
2155
2155
  });
2156
2156
  Object.defineProperty(exports, "JsonTree", {
2157
2157
  enumerable: true,
package/dist/index.mjs CHANGED
@@ -4,8 +4,8 @@ import './chunk-JWB2EWQO.mjs';
4
4
  export { ImageViewer } from './chunk-GGKGH5PM.mjs';
5
5
  export { generateContentKey, useAudioCache, useBlobUrlCleanup, useImageCache, useMediaCacheStore, useVideoCache, useVideoPlayerSettings } from './chunk-5LBDYFWH.mjs';
6
6
  export { CronSchedulerProvider, CustomInput, DayChips, MonthDayGrid, SchedulePreview, ScheduleTypeSelector, TimeSelector, buildCron, humanizeCron, isValidCron, parseCron, useCronCustom, useCronMonthDays, useCronPreview, useCronScheduler, useCronSchedulerContext, useCronTime, useCronType, useCronWeekDays } from './chunk-PZKAH7WQ.mjs';
7
- import { PlaygroundProvider, PrettyCode_default } from './chunk-FX3GCEUL.mjs';
8
- export { PrettyCode_default as PrettyCode } from './chunk-FX3GCEUL.mjs';
7
+ import { PlaygroundProvider, PrettyCode_default } from './chunk-QZ55LYK2.mjs';
8
+ export { PrettyCode_default as PrettyCode } from './chunk-QZ55LYK2.mjs';
9
9
  export { JsonTree_default as JsonTree } from './chunk-LFWQ36LJ.mjs';
10
10
  import './chunk-SSUOENAZ.mjs';
11
11
  export { ArrayFieldItemTemplate, ArrayFieldTemplate, BaseInputTemplate, CheckboxWidget, ColorWidget, ErrorListTemplate, FieldTemplate, JsonSchemaForm, NumberWidget, ObjectFieldTemplate, SelectWidget, SliderWidget, SwitchWidget, TextWidget, getRequiredFields, hasRequiredFields, mergeDefaults, normalizeFormData, safeJsonParse, safeJsonStringify, validateRequiredFields, validateSchema } from './chunk-JUGQNNDC.mjs';
@@ -708,7 +708,7 @@ function OpenapiLoadingFallback() {
708
708
  }
709
709
  __name(OpenapiLoadingFallback, "OpenapiLoadingFallback");
710
710
  var LazyPlaygroundLayout = createLazyComponent(
711
- () => import('./PlaygroundLayout-FRKIMYVN.mjs').then((mod) => ({ default: mod.PlaygroundLayout })),
711
+ () => import('./PlaygroundLayout-G325I6HM.mjs').then((mod) => ({ default: mod.PlaygroundLayout })),
712
712
  {
713
713
  displayName: "LazyPlaygroundLayout",
714
714
  fallback: /* @__PURE__ */ jsx(OpenapiLoadingFallback, {})
@@ -863,7 +863,7 @@ function LottiePlayer(props) {
863
863
  }
864
864
  __name(LottiePlayer, "LottiePlayer");
865
865
  var PlaygroundLayout = lazy(
866
- () => import('./PlaygroundLayout-FRKIMYVN.mjs').then((mod) => ({ default: mod.PlaygroundLayout }))
866
+ () => import('./PlaygroundLayout-G325I6HM.mjs').then((mod) => ({ default: mod.PlaygroundLayout }))
867
867
  );
868
868
  var LoadingFallback9 = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: "Loading API Playground..." }) }), "LoadingFallback");
869
869
  var Playground = /* @__PURE__ */ __name(({ config }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-tools",
3
- "version": "2.1.270",
3
+ "version": "2.1.272",
4
4
  "description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
5
5
  "keywords": [
6
6
  "ui-tools",
@@ -90,8 +90,8 @@
90
90
  "check": "tsc --noEmit"
91
91
  },
92
92
  "peerDependencies": {
93
- "@djangocfg/i18n": "^2.1.270",
94
- "@djangocfg/ui-core": "^2.1.270",
93
+ "@djangocfg/i18n": "^2.1.272",
94
+ "@djangocfg/ui-core": "^2.1.272",
95
95
  "consola": "^3.4.2",
96
96
  "lucide-react": "^0.545.0",
97
97
  "react": "^19.1.0",
@@ -133,10 +133,10 @@
133
133
  "@maplibre/maplibre-gl-geocoder": "^1.7.0"
134
134
  },
135
135
  "devDependencies": {
136
- "@djangocfg/i18n": "^2.1.270",
136
+ "@djangocfg/i18n": "^2.1.272",
137
137
  "@djangocfg/playground": "workspace:*",
138
- "@djangocfg/typescript-config": "^2.1.270",
139
- "@djangocfg/ui-core": "^2.1.270",
138
+ "@djangocfg/typescript-config": "^2.1.272",
139
+ "@djangocfg/ui-core": "^2.1.272",
140
140
  "@types/mapbox__mapbox-gl-draw": "^1.4.8",
141
141
  "@types/node": "^24.7.2",
142
142
  "@types/react": "^19.1.0",
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { ChevronRight, Filter, Search } from 'lucide-react';
4
- import React, { useMemo } from 'react';
4
+ import React, { useEffect, useMemo, useState } from 'react';
5
5
 
6
6
  import {
7
7
  Combobox,
@@ -71,6 +71,13 @@ export function EndpointList() {
71
71
  const { endpoints, categories, loading, error, schemas, currentSchema, setCurrentSchema } =
72
72
  useOpenApiSchema({ schemas: config.schemas, defaultSchemaId: config.defaultSchemaId });
73
73
 
74
+ // ── Debounced search ──────────────────────────────────────────────────────
75
+ const [debouncedSearch, setDebouncedSearch] = useState(state.searchTerm);
76
+ useEffect(() => {
77
+ const id = setTimeout(() => setDebouncedSearch(state.searchTerm), 150);
78
+ return () => clearTimeout(id);
79
+ }, [state.searchTerm]);
80
+
74
81
  // ── Data ──────────────────────────────────────────────────────────────────
75
82
  const schemaOptions = useMemo(
76
83
  () => schemas.map((s) => ({ value: s.id, label: s.name })),
@@ -82,8 +89,8 @@ export function EndpointList() {
82
89
  if (state.selectedCategory !== 'All') {
83
90
  list = list.filter((e) => e.category === state.selectedCategory);
84
91
  }
85
- if (state.searchTerm) {
86
- const q = state.searchTerm.toLowerCase();
92
+ if (debouncedSearch) {
93
+ const q = debouncedSearch.toLowerCase();
87
94
  list = list.filter((e) =>
88
95
  e.name.toLowerCase().includes(q) ||
89
96
  e.description.toLowerCase().includes(q) ||
@@ -91,7 +98,7 @@ export function EndpointList() {
91
98
  );
92
99
  }
93
100
  return list;
94
- }, [endpoints, state.selectedCategory, state.searchTerm, state.selectedVersion]);
101
+ }, [endpoints, state.selectedCategory, debouncedSearch, state.selectedVersion]);
95
102
 
96
103
  // ── Derived ───────────────────────────────────────────────────────────────
97
104
  const isFiltered = state.selectedCategory !== 'All';
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { Key, Loader2, Send, Terminal } from 'lucide-react';
3
+ import { Key, Loader2, Send, Sparkles, Terminal } from 'lucide-react';
4
4
  import React, { useMemo } from 'react';
5
5
 
6
6
  import {
@@ -123,7 +123,18 @@ export function RequestPanel() {
123
123
  <div className="shrink-0 border-b px-4 py-3 bg-muted/20 space-y-1.5">
124
124
  <div className="flex items-center gap-2">
125
125
  <MethodBadge method={ep.method} />
126
- <span className="font-mono text-xs text-foreground/70 truncate min-w-0">{epPath}</span>
126
+ <span className="font-mono text-xs text-foreground/70 truncate min-w-0 flex-1">{epPath}</span>
127
+ <Button
128
+ onClick={sendRequest}
129
+ disabled={isSendDisabled}
130
+ size="sm"
131
+ className="shrink-0 gap-1.5 h-7 text-xs px-3"
132
+ >
133
+ {state.loading
134
+ ? <><Loader2 className="h-3 w-3 animate-spin" /> Sending…</>
135
+ : <><Send className="h-3 w-3" /> Send</>
136
+ }
137
+ </Button>
127
138
  </div>
128
139
  {urlChanged && (
129
140
  <div className="font-mono text-[10px] text-muted-foreground/50 break-all leading-snug pl-0.5">
@@ -141,10 +152,26 @@ export function RequestPanel() {
141
152
  {/* Body */}
142
153
  {hasBody && (
143
154
  <div className="space-y-1.5">
144
- <div className="flex items-baseline gap-2">
145
- <SectionLabel>Body</SectionLabel>
146
- {bodyType && (
147
- <span className="text-[10px] text-muted-foreground/40 font-mono">{bodyType}</span>
155
+ <div className="flex items-center justify-between gap-2">
156
+ <div className="flex items-baseline gap-2">
157
+ <SectionLabel>Body</SectionLabel>
158
+ {bodyType && (
159
+ <span className="text-[10px] text-muted-foreground/40 font-mono">{bodyType}</span>
160
+ )}
161
+ </div>
162
+ {isJsonValid && state.requestBody && (
163
+ <button
164
+ type="button"
165
+ onClick={() => {
166
+ try {
167
+ setRequestBody(JSON.stringify(JSON.parse(state.requestBody), null, 2));
168
+ } catch {}
169
+ }}
170
+ className="inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors"
171
+ >
172
+ <Sparkles className="h-2.5 w-2.5" />
173
+ Format
174
+ </button>
148
175
  )}
149
176
  </div>
150
177
  <Textarea
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { Loader2, Send, Terminal } from 'lucide-react';
3
+ import { Loader2, Send, Terminal, WifiOff } from 'lucide-react';
4
4
  import { useMemo } from 'react';
5
5
 
6
6
  import { CopyButton } from '@djangocfg/ui-core/components';
@@ -50,6 +50,7 @@ export function ResponsePanel() {
50
50
 
51
51
  // ── Derived ───────────────────────────────────────────────────────────────
52
52
  const sizeKb = rawText ? `${(rawText.length / 1024).toFixed(1)} KB` : '';
53
+ const duration = response?.duration != null ? `${response.duration}ms` : '';
53
54
  const hasError = Boolean(response?.error);
54
55
  const hasStatus = response?.status != null;
55
56
  const hasCopy = Boolean(rawText);
@@ -67,6 +68,17 @@ export function ResponsePanel() {
67
68
  if (!selectedEndpoint) return <EmptyState icon={Terminal} text="Response will appear here" />;
68
69
  if (!response) return <EmptyState icon={Send} text='Press "Send Request" to see the response' />;
69
70
 
71
+ // Pure network error (no HTTP response at all — CORS, offline, timeout)
72
+ if (hasError && !hasStatus) {
73
+ return (
74
+ <EmptyState
75
+ icon={WifiOff}
76
+ text={response.error!}
77
+ className="text-destructive [&_svg]:text-destructive"
78
+ />
79
+ );
80
+ }
81
+
70
82
  // ── Render ────────────────────────────────────────────────────────────────
71
83
  return (
72
84
  <>
@@ -80,6 +92,9 @@ export function ResponsePanel() {
80
92
  {sizeKb && (
81
93
  <span className="text-[10px] text-muted-foreground/50 tabular-nums shrink-0">{sizeKb}</span>
82
94
  )}
95
+ {duration && (
96
+ <span className="text-[10px] text-muted-foreground/50 tabular-nums shrink-0">{duration}</span>
97
+ )}
83
98
  </div>
84
99
  {hasCopy && (
85
100
  <CopyButton value={rawText} variant="ghost" size="sm" className="h-6 px-2 text-[10px] text-muted-foreground shrink-0">
@@ -88,7 +103,7 @@ export function ResponsePanel() {
88
103
  )}
89
104
  </div>
90
105
 
91
- {/* Network/request error */}
106
+ {/* HTTP-level error body (4xx/5xx — has status but also error flag) */}
92
107
  {hasError && (
93
108
  <div className="shrink-0 mx-4 mt-3 rounded border border-destructive/20 bg-destructive/5 px-3 py-2">
94
109
  <p className="text-xs text-destructive">{response.error}</p>