@djangocfg/ui-nextjs 2.1.4 → 2.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.
- package/package.json +13 -12
- package/src/components/markdown/MarkdownMessage.tsx +11 -26
- package/src/components/sidebar.tsx +3 -1
- package/src/components/ssr-pagination.tsx +14 -30
- package/src/hooks/index.ts +7 -2
- package/src/hooks/useHotkey.ts +103 -0
- package/src/hooks/{useTheme.ts → useResolvedTheme.ts} +29 -18
- package/src/theme/ThemeProvider.tsx +55 -61
- package/src/theme/ThemeToggle.tsx +15 -11
- package/src/theme/index.ts +2 -1
- package/src/tools/JsonForm/JsonSchemaForm.tsx +7 -0
- package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +90 -14
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +218 -0
- package/src/tools/JsonForm/widgets/SliderWidget.tsx +147 -0
- package/src/tools/JsonForm/widgets/index.ts +2 -0
- package/src/tools/JsonTree/index.tsx +10 -20
- package/src/tools/Mermaid/Mermaid.client.tsx +2 -2
- package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +20 -21
- package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +7 -17
- package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +26 -26
- package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +0 -7
- package/src/tools/OpenapiViewer/types.ts +0 -1
- package/src/tools/PrettyCode/PrettyCode.client.tsx +10 -25
- package/src/tools/VideoPlayer/VideoPlayer.tsx +4 -4
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useCallback } from 'react';
|
|
4
|
-
import { Button, Card, CardContent, CardHeader, CardTitle, Input,
|
|
5
|
-
import {
|
|
4
|
+
import { Button, Card, CardContent, CardHeader, CardTitle, Input, Textarea, CopyButton } from '@djangocfg/ui-core/components';
|
|
5
|
+
import { Loader2, Key, Send } from 'lucide-react';
|
|
6
6
|
import { usePlaygroundContext } from '../context/PlaygroundContext';
|
|
7
|
-
import { findApiKeyById
|
|
7
|
+
import { findApiKeyById } from '../utils';
|
|
8
8
|
import { isValidJson, parseRequestHeaders } from '../utils';
|
|
9
9
|
import { RequestParametersForm } from './RequestParametersForm';
|
|
10
10
|
import { EndpointInfo } from './EndpointInfo';
|
|
@@ -14,16 +14,8 @@ export const RequestBuilder: React.FC = () => {
|
|
|
14
14
|
const {
|
|
15
15
|
state,
|
|
16
16
|
apiKeys,
|
|
17
|
-
apiKeysLoading,
|
|
18
|
-
setRequestUrl,
|
|
19
|
-
setRequestMethod,
|
|
20
|
-
setRequestHeaders,
|
|
21
17
|
setRequestBody,
|
|
22
|
-
setResponse,
|
|
23
|
-
setLoading,
|
|
24
|
-
setSelectedApiKey,
|
|
25
18
|
setManualApiToken,
|
|
26
|
-
copyToClipboard,
|
|
27
19
|
sendRequest
|
|
28
20
|
} = usePlaygroundContext();
|
|
29
21
|
|
|
@@ -143,15 +135,13 @@ export const RequestBuilder: React.FC = () => {
|
|
|
143
135
|
<CardHeader>
|
|
144
136
|
<div className="flex items-center justify-between">
|
|
145
137
|
<CardTitle className="text-sm text-foreground">cURL Command</CardTitle>
|
|
146
|
-
<
|
|
138
|
+
<CopyButton
|
|
139
|
+
value={curlCommand}
|
|
147
140
|
variant="outline"
|
|
148
141
|
size="sm"
|
|
149
|
-
onClick={() => copyToClipboard(curlCommand)}
|
|
150
|
-
disabled={!curlCommand}
|
|
151
142
|
>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
</Button>
|
|
143
|
+
Copy cURL
|
|
144
|
+
</CopyButton>
|
|
155
145
|
</div>
|
|
156
146
|
</CardHeader>
|
|
157
147
|
<CardContent>
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@djangocfg/ui-core/components';
|
|
5
|
-
import {
|
|
3
|
+
import React, { useMemo, useCallback } from 'react';
|
|
4
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, CopyButton } from '@djangocfg/ui-core/components';
|
|
5
|
+
import { XCircle, Download } from 'lucide-react';
|
|
6
6
|
import { usePlaygroundContext } from '../context/PlaygroundContext';
|
|
7
7
|
import { getStatusColor } from '../utils';
|
|
8
8
|
import JsonTree from '../../JsonTree';
|
|
9
9
|
|
|
10
10
|
export const ResponseViewer: React.FC = () => {
|
|
11
|
-
const { state
|
|
11
|
+
const { state } = usePlaygroundContext();
|
|
12
12
|
const { response } = state;
|
|
13
13
|
|
|
14
|
+
// Memoize response text for copy/download
|
|
15
|
+
const responseText = useMemo(() => {
|
|
16
|
+
if (!response?.data) return '';
|
|
17
|
+
return typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
|
|
18
|
+
}, [response?.data]);
|
|
19
|
+
|
|
20
|
+
const handleDownloadResponse = useCallback(() => {
|
|
21
|
+
if (!responseText) return;
|
|
22
|
+
const blob = new Blob([responseText], { type: 'application/json' });
|
|
23
|
+
const url = URL.createObjectURL(blob);
|
|
24
|
+
const a = document.createElement('a');
|
|
25
|
+
a.href = url;
|
|
26
|
+
a.download = 'response.json';
|
|
27
|
+
document.body.appendChild(a);
|
|
28
|
+
a.click();
|
|
29
|
+
document.body.removeChild(a);
|
|
30
|
+
URL.revokeObjectURL(url);
|
|
31
|
+
}, [responseText]);
|
|
32
|
+
|
|
14
33
|
if (!response) {
|
|
15
34
|
return (
|
|
16
35
|
<div className="space-y-4">
|
|
@@ -26,33 +45,14 @@ export const ResponseViewer: React.FC = () => {
|
|
|
26
45
|
);
|
|
27
46
|
}
|
|
28
47
|
|
|
29
|
-
const handleCopyResponse = () => {
|
|
30
|
-
const responseText = typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
|
|
31
|
-
copyToClipboard(responseText);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const handleDownloadResponse = () => {
|
|
35
|
-
const responseText = typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
|
|
36
|
-
const blob = new Blob([responseText], { type: 'application/json' });
|
|
37
|
-
const url = URL.createObjectURL(blob);
|
|
38
|
-
const a = document.createElement('a');
|
|
39
|
-
a.href = url;
|
|
40
|
-
a.download = 'response.json';
|
|
41
|
-
document.body.appendChild(a);
|
|
42
|
-
a.click();
|
|
43
|
-
document.body.removeChild(a);
|
|
44
|
-
URL.revokeObjectURL(url);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
48
|
return (
|
|
48
49
|
<div className="space-y-4">
|
|
49
50
|
<div className="flex items-center justify-between">
|
|
50
51
|
<h2 className="text-lg font-semibold text-foreground">Response</h2>
|
|
51
52
|
<div className="flex items-center space-x-2">
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
</Button>
|
|
53
|
+
<CopyButton value={responseText} variant="outline" size="sm">
|
|
54
|
+
Copy
|
|
55
|
+
</CopyButton>
|
|
56
56
|
<Button variant="outline" size="sm" onClick={handleDownloadResponse}>
|
|
57
57
|
<Download className="h-4 w-4" />
|
|
58
58
|
<span className="hidden sm:inline">Download</span>
|
|
@@ -5,7 +5,6 @@ import consola from 'consola';
|
|
|
5
5
|
import { type ApiEndpoint, type ApiResponse, type PlaygroundContextType, type PlaygroundState, type PlaygroundStep, type PlaygroundConfig } from '../types';
|
|
6
6
|
import { getDefaultVersion } from '../utils/versionManager';
|
|
7
7
|
import { parseRequestHeaders, substituteUrlParameters } from '../utils';
|
|
8
|
-
import { useCopy } from '@djangocfg/ui-core/hooks';
|
|
9
8
|
|
|
10
9
|
const createInitialState = (): PlaygroundState => ({
|
|
11
10
|
// Step management
|
|
@@ -59,11 +58,6 @@ export const PlaygroundProvider: React.FC<PlaygroundProviderProps> = ({ children
|
|
|
59
58
|
const apiKeys = React.useMemo(() => [], []);
|
|
60
59
|
const isLoadingApiKeys = false;
|
|
61
60
|
|
|
62
|
-
const { copyToClipboard } = useCopy({
|
|
63
|
-
successMessage: "cURL command copied to clipboard",
|
|
64
|
-
errorMessage: "Failed to copy cURL command"
|
|
65
|
-
});
|
|
66
|
-
|
|
67
61
|
const updateState = (updates: Partial<PlaygroundState>) => {
|
|
68
62
|
setState((prev) => ({ ...prev, ...updates }));
|
|
69
63
|
};
|
|
@@ -330,7 +324,6 @@ export const PlaygroundProvider: React.FC<PlaygroundProviderProps> = ({ children
|
|
|
330
324
|
|
|
331
325
|
// Actions
|
|
332
326
|
clearAll,
|
|
333
|
-
copyToClipboard,
|
|
334
327
|
sendRequest,
|
|
335
328
|
};
|
|
336
329
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Highlight, Language, themes } from 'prism-react-renderer';
|
|
4
|
-
import React
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { useTheme } from '../../hooks/useTheme';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { CopyButton } from '@djangocfg/ui-core/components';
|
|
6
|
+
import { useResolvedTheme } from '../../hooks/useResolvedTheme';
|
|
8
7
|
|
|
9
8
|
interface PrettyCodeProps {
|
|
10
9
|
data: string | object;
|
|
@@ -16,12 +15,7 @@ interface PrettyCodeProps {
|
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
const PrettyCode = ({ data, language, className, mode, inline = false, customBg }: PrettyCodeProps) => {
|
|
19
|
-
const detectedTheme =
|
|
20
|
-
const [copied, setCopied] = useState(false);
|
|
21
|
-
const { copyToClipboard } = useCopy({
|
|
22
|
-
successMessage: "Code copied to clipboard",
|
|
23
|
-
errorMessage: "Failed to copy code"
|
|
24
|
-
});
|
|
18
|
+
const detectedTheme = useResolvedTheme();
|
|
25
19
|
|
|
26
20
|
// Use provided mode or fall back to detected theme
|
|
27
21
|
const currentTheme = mode || detectedTheme;
|
|
@@ -32,15 +26,6 @@ const PrettyCode = ({ data, language, className, mode, inline = false, customBg
|
|
|
32
26
|
|
|
33
27
|
// Convert form object to JSON string with proper formatting
|
|
34
28
|
const contentJson = typeof data === 'string' ? data : JSON.stringify(data || {}, null, 2);
|
|
35
|
-
|
|
36
|
-
// Handle copy
|
|
37
|
-
const handleCopy = async () => {
|
|
38
|
-
const success = await copyToClipboard(contentJson);
|
|
39
|
-
if (success) {
|
|
40
|
-
setCopied(true);
|
|
41
|
-
setTimeout(() => setCopied(false), 2000);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
29
|
|
|
45
30
|
// Handle empty content
|
|
46
31
|
if (!contentJson || contentJson.trim() === '') {
|
|
@@ -173,13 +158,13 @@ const PrettyCode = ({ data, language, className, mode, inline = false, customBg
|
|
|
173
158
|
<span className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-background/80 text-muted-foreground border border-border/50 backdrop-blur-sm">
|
|
174
159
|
{displayLanguage}
|
|
175
160
|
</span>
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
|
|
161
|
+
<CopyButton
|
|
162
|
+
value={contentJson}
|
|
163
|
+
variant="ghost"
|
|
164
|
+
className="h-7 w-7 bg-background/80 border border-border/50 backdrop-blur-sm"
|
|
165
|
+
iconClassName="h-3.5 w-3.5"
|
|
179
166
|
title="Copy code"
|
|
180
|
-
|
|
181
|
-
{copied ? <Check className="h-3.5 w-3.5 text-green-500" /> : <Copy className="h-3.5 w-3.5" />}
|
|
182
|
-
</button>
|
|
167
|
+
/>
|
|
183
168
|
</div>
|
|
184
169
|
|
|
185
170
|
<div className="h-full overflow-auto">
|
|
@@ -194,20 +194,20 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
|
|
|
194
194
|
<MediaProvider />
|
|
195
195
|
|
|
196
196
|
{/* Poster with proper aspect ratio handling */}
|
|
197
|
-
{
|
|
197
|
+
{posterUrl && (
|
|
198
198
|
<Poster
|
|
199
199
|
className="vds-poster"
|
|
200
|
-
src={
|
|
200
|
+
src={posterUrl}
|
|
201
201
|
alt={source.title || 'Video poster'}
|
|
202
202
|
style={{ objectFit: 'cover' }}
|
|
203
203
|
/>
|
|
204
204
|
)}
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
{/* Use Vidstack's built-in default layout */}
|
|
207
207
|
{controls && (
|
|
208
208
|
<DefaultVideoLayout
|
|
209
209
|
icons={defaultLayoutIcons}
|
|
210
|
-
thumbnails={
|
|
210
|
+
thumbnails={posterUrl}
|
|
211
211
|
/>
|
|
212
212
|
)}
|
|
213
213
|
</MediaPlayer>
|