@djangocfg/ui-tools 2.1.285 → 2.1.287

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 (79) hide show
  1. package/dist/DocsLayout-BCVU6TTX.cjs +2027 -0
  2. package/dist/DocsLayout-BCVU6TTX.cjs.map +1 -0
  3. package/dist/DocsLayout-ERETJLLV.mjs +2020 -0
  4. package/dist/DocsLayout-ERETJLLV.mjs.map +1 -0
  5. package/dist/{PlaygroundLayout-O52C6HK5.css → DocsLayout-MBFIB4NO.css} +1 -1
  6. package/dist/{PrettyCode.client-SGDGQTYT.cjs → PrettyCode.client-5GABIN2I.cjs} +57 -35
  7. package/dist/PrettyCode.client-5GABIN2I.cjs.map +1 -0
  8. package/dist/{PrettyCode.client-DW5LTG47.mjs → PrettyCode.client-IZTXXYHG.mjs} +57 -35
  9. package/dist/PrettyCode.client-IZTXXYHG.mjs.map +1 -0
  10. package/dist/chunk-IULI4XII.cjs +1129 -0
  11. package/dist/chunk-IULI4XII.cjs.map +1 -0
  12. package/dist/chunk-VZGQC3NG.mjs +1100 -0
  13. package/dist/chunk-VZGQC3NG.mjs.map +1 -0
  14. package/dist/index.cjs +88 -552
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +18 -6
  17. package/dist/index.d.ts +18 -6
  18. package/dist/index.mjs +25 -496
  19. package/dist/index.mjs.map +1 -1
  20. package/package.json +6 -6
  21. package/src/tools/OpenapiViewer/.claude/.sidecar/activity.jsonl +6 -0
  22. package/src/tools/OpenapiViewer/.claude/.sidecar/history/2026-04-22.md +35 -0
  23. package/src/tools/OpenapiViewer/.claude/.sidecar/map_cache.json +30 -0
  24. package/src/tools/OpenapiViewer/.claude/.sidecar/review.md +35 -0
  25. package/src/tools/OpenapiViewer/.claude/.sidecar/scan.log +3 -0
  26. package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-001.md +18 -0
  27. package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-002.md +18 -0
  28. package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-003.md +18 -0
  29. package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-004.md +18 -0
  30. package/src/tools/OpenapiViewer/.claude/.sidecar/tasks/T-005.md +18 -0
  31. package/src/tools/OpenapiViewer/.claude/.sidecar/usage.json +5 -0
  32. package/src/tools/OpenapiViewer/.claude/project-map.md +23 -0
  33. package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +28 -2
  34. package/src/tools/OpenapiViewer/README.md +104 -51
  35. package/src/tools/OpenapiViewer/components/DocsLayout/ApiIntroSection.tsx +64 -0
  36. package/src/tools/OpenapiViewer/components/DocsLayout/DocsView.tsx +137 -0
  37. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc.tsx +268 -0
  38. package/src/tools/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +139 -0
  39. package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar.tsx +211 -0
  40. package/src/tools/OpenapiViewer/components/DocsLayout/SlideInPlayground.tsx +101 -0
  41. package/src/tools/OpenapiViewer/components/DocsLayout/TryItSheet.tsx +57 -0
  42. package/src/tools/OpenapiViewer/components/DocsLayout/anchor.ts +11 -0
  43. package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +71 -0
  44. package/src/tools/OpenapiViewer/components/DocsLayout/index.tsx +166 -0
  45. package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +121 -0
  46. package/src/tools/OpenapiViewer/components/DocsLayout/sidebarLabel.ts +60 -0
  47. package/src/tools/OpenapiViewer/components/index.ts +5 -2
  48. package/src/tools/OpenapiViewer/components/shared/BodyFormEditor.tsx +422 -0
  49. package/src/tools/OpenapiViewer/components/shared/EndpointDraftSync.tsx +108 -0
  50. package/src/tools/OpenapiViewer/components/shared/EndpointResetButton.tsx +50 -0
  51. package/src/tools/OpenapiViewer/components/{PlaygroundLayout → shared}/RequestPanel.tsx +174 -87
  52. package/src/tools/OpenapiViewer/components/shared/SendButton.tsx +91 -0
  53. package/src/tools/OpenapiViewer/components/{PlaygroundLayout → shared}/ui.tsx +5 -4
  54. package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +82 -8
  55. package/src/tools/OpenapiViewer/hooks/useEndpointDraft.ts +142 -0
  56. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +126 -13
  57. package/src/tools/OpenapiViewer/index.tsx +3 -7
  58. package/src/tools/OpenapiViewer/lazy.tsx +6 -27
  59. package/src/tools/OpenapiViewer/types.ts +44 -0
  60. package/src/tools/OpenapiViewer/utils/formatters.ts +2 -23
  61. package/src/tools/OpenapiViewer/utils/index.ts +3 -1
  62. package/src/tools/OpenapiViewer/utils/schemaExport.ts +206 -0
  63. package/src/tools/OpenapiViewer/utils/url.ts +202 -0
  64. package/src/tools/PrettyCode/PrettyCode.client.tsx +42 -8
  65. package/src/tools/PrettyCode/index.tsx +6 -0
  66. package/dist/PlaygroundLayout-DHUATCHB.cjs +0 -798
  67. package/dist/PlaygroundLayout-DHUATCHB.cjs.map +0 -1
  68. package/dist/PlaygroundLayout-NONWOVQR.mjs +0 -791
  69. package/dist/PlaygroundLayout-NONWOVQR.mjs.map +0 -1
  70. package/dist/PrettyCode.client-DW5LTG47.mjs.map +0 -1
  71. package/dist/PrettyCode.client-SGDGQTYT.cjs.map +0 -1
  72. package/dist/chunk-5FKE7OME.cjs +0 -369
  73. package/dist/chunk-5FKE7OME.cjs.map +0 -1
  74. package/dist/chunk-BKWDHJKF.mjs +0 -356
  75. package/dist/chunk-BKWDHJKF.mjs.map +0 -1
  76. package/src/tools/OpenapiViewer/components/PlaygroundLayout/EndpointList.tsx +0 -228
  77. package/src/tools/OpenapiViewer/components/PlaygroundLayout/index.tsx +0 -107
  78. /package/dist/{PlaygroundLayout-O52C6HK5.css.map → DocsLayout-MBFIB4NO.css.map} +0 -0
  79. /package/src/tools/OpenapiViewer/components/{PlaygroundLayout → shared}/ResponsePanel.tsx +0 -0
@@ -1,228 +0,0 @@
1
- 'use client';
2
-
3
- import { ChevronRight, Filter, Search } from 'lucide-react';
4
- import React, { useEffect, useMemo, useState } from 'react';
5
-
6
- import {
7
- Combobox,
8
- DownloadButton,
9
- DropdownMenu,
10
- DropdownMenuContent,
11
- DropdownMenuItem,
12
- DropdownMenuTrigger,
13
- Input,
14
- Skeleton,
15
- } from '@djangocfg/ui-core/components';
16
- import { cn } from '@djangocfg/ui-core/lib';
17
-
18
- import { usePlaygroundContext } from '../../context/PlaygroundContext';
19
- import useOpenApiSchema from '../../hooks/useOpenApiSchema';
20
- import { deduplicateEndpoints } from '../../utils/versionManager';
21
- import { MethodBadge, ScrollArea, relativePath } from './ui';
22
-
23
- // ─── Endpoint row ─────────────────────────────────────────────────────────────
24
-
25
- function EndpointRow({
26
- method,
27
- path,
28
- description,
29
- isActive,
30
- onClick,
31
- }: {
32
- method: string;
33
- path: string;
34
- description: string;
35
- isActive: boolean;
36
- onClick: () => void;
37
- }) {
38
- const displayPath = relativePath(path);
39
- const rowCls = cn(
40
- 'group w-full text-left flex items-start gap-2.5 px-3 py-2.5 transition-colors hover:bg-muted/40',
41
- isActive && 'bg-primary/[0.06] hover:bg-primary/[0.09]',
42
- );
43
- const arrowCls = cn(
44
- 'h-3.5 w-3.5 shrink-0 mt-px transition-opacity',
45
- isActive ? 'text-primary opacity-100' : 'opacity-0 group-hover:opacity-30',
46
- );
47
-
48
- return (
49
- <button className={rowCls} onClick={onClick}>
50
- <MethodBadge method={method} />
51
- <div className="flex-1 min-w-0">
52
- <p className="font-mono text-[11px] text-foreground/75 truncate leading-tight">
53
- {displayPath}
54
- </p>
55
- {description && (
56
- <p className="text-[10px] text-muted-foreground/60 truncate leading-tight mt-0.5">
57
- {description}
58
- </p>
59
- )}
60
- </div>
61
- <ChevronRight className={arrowCls} />
62
- </button>
63
- );
64
- }
65
-
66
- // ─── EndpointList ─────────────────────────────────────────────────────────────
67
-
68
- export function EndpointList() {
69
- const { state, config, setSelectedEndpoint, setSelectedCategory, setSearchTerm } =
70
- usePlaygroundContext();
71
- const { endpoints, categories, loading, error, schemas, currentSchema, setCurrentSchema } =
72
- useOpenApiSchema({ schemas: config.schemas, defaultSchemaId: config.defaultSchemaId });
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
-
81
- // ── Data ──────────────────────────────────────────────────────────────────
82
- const schemaOptions = useMemo(
83
- () => schemas.map((s) => ({ value: s.id, label: s.name })),
84
- [schemas],
85
- );
86
-
87
- const filtered = useMemo(() => {
88
- let list = deduplicateEndpoints(endpoints, state.selectedVersion);
89
- if (state.selectedCategory !== 'All') {
90
- list = list.filter((e) => e.category === state.selectedCategory);
91
- }
92
- if (debouncedSearch) {
93
- const q = debouncedSearch.toLowerCase();
94
- list = list.filter((e) =>
95
- e.name.toLowerCase().includes(q) ||
96
- e.description.toLowerCase().includes(q) ||
97
- e.path.toLowerCase().includes(q),
98
- );
99
- }
100
- return list;
101
- }, [endpoints, state.selectedCategory, debouncedSearch, state.selectedVersion]);
102
-
103
- // ── Derived ───────────────────────────────────────────────────────────────
104
- const isFiltered = state.selectedCategory !== 'All';
105
- const hasCategories = categories.length > 0;
106
- const hasMultipleSchemas = schemas.length > 1;
107
- const endpointLabel = `${filtered.length} endpoint${filtered.length !== 1 ? 's' : ''}`;
108
- const downloadFilename = currentSchema ? `${currentSchema.id}-openapi.json` : 'openapi.json';
109
-
110
- // ── Early returns ─────────────────────────────────────────────────────────
111
- if (loading) {
112
- return (
113
- <div className="p-3 space-y-1.5">
114
- {Array.from({ length: 12 }).map((_, i) => (
115
- <Skeleton key={i} className="h-10 w-full rounded" />
116
- ))}
117
- </div>
118
- );
119
- }
120
-
121
- if (error) {
122
- return (
123
- <div className="p-4">
124
- <p className="text-xs text-destructive">Failed to load schema: {error}</p>
125
- </div>
126
- );
127
- }
128
-
129
- // ── Render ────────────────────────────────────────────────────────────────
130
- return (
131
- <>
132
- {/* Toolbar */}
133
- <div className="shrink-0 border-b px-2.5 py-2 space-y-2">
134
- {hasMultipleSchemas && (
135
- <Combobox
136
- options={schemaOptions}
137
- value={currentSchema?.id ?? ''}
138
- onValueChange={(id) => id && setCurrentSchema(id)}
139
- placeholder="Select API"
140
- searchPlaceholder="Search APIs…"
141
- emptyText="No APIs found"
142
- className="w-full h-8 text-xs"
143
- />
144
- )}
145
-
146
- <div className="flex gap-1.5">
147
- <div className="relative flex-1 min-w-0">
148
- <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground/50 pointer-events-none" />
149
- <Input
150
- placeholder="Search endpoints…"
151
- value={state.searchTerm}
152
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchTerm(e.target.value)}
153
- className="pl-8 h-8 text-xs"
154
- />
155
- </div>
156
-
157
- {hasCategories && (
158
- <DropdownMenu>
159
- <DropdownMenuTrigger asChild>
160
- <button className={cn(
161
- 'relative shrink-0 flex items-center justify-center h-8 w-8 rounded-md border transition-colors',
162
- isFiltered
163
- ? 'border-primary bg-primary/10 text-primary'
164
- : 'border-input bg-background text-muted-foreground hover:text-foreground hover:bg-muted/50',
165
- )}>
166
- <Filter className="h-3.5 w-3.5" />
167
- {isFiltered && (
168
- <span className="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-primary" />
169
- )}
170
- </button>
171
- </DropdownMenuTrigger>
172
- <DropdownMenuContent align="end" className="min-w-[160px] max-h-72 overflow-y-auto">
173
- {['All', ...categories].map((c) => (
174
- <DropdownMenuItem
175
- key={c}
176
- onClick={() => setSelectedCategory(c)}
177
- className={cn('text-xs', state.selectedCategory === c && 'bg-accent font-medium')}
178
- >
179
- {c}
180
- </DropdownMenuItem>
181
- ))}
182
- </DropdownMenuContent>
183
- </DropdownMenu>
184
- )}
185
- </div>
186
- </div>
187
-
188
- {/* Meta row */}
189
- <div className="shrink-0 flex items-center justify-between px-3 py-1 border-b bg-muted/20">
190
- <span className="text-[10px] text-muted-foreground/50 tabular-nums">{endpointLabel}</span>
191
- {currentSchema && (
192
- <DownloadButton
193
- url={currentSchema.url}
194
- filename={downloadFilename}
195
- variant="ghost"
196
- size="sm"
197
- className="h-6 px-2 text-[10px] text-muted-foreground/50 hover:text-foreground"
198
- >
199
- JSON
200
- </DownloadButton>
201
- )}
202
- </div>
203
-
204
- {/* List */}
205
- <ScrollArea>
206
- {filtered.length === 0 ? (
207
- <div className="py-10 text-center text-xs text-muted-foreground">No endpoints found</div>
208
- ) : (
209
- <div className="divide-y divide-border/40">
210
- {filtered.map((ep) => (
211
- <EndpointRow
212
- key={`${ep.method}-${ep.path}`}
213
- method={ep.method}
214
- path={ep.path}
215
- description={ep.description}
216
- isActive={
217
- state.selectedEndpoint?.path === ep.path &&
218
- state.selectedEndpoint?.method === ep.method
219
- }
220
- onClick={() => setSelectedEndpoint(ep)}
221
- />
222
- ))}
223
- </div>
224
- )}
225
- </ScrollArea>
226
- </>
227
- );
228
- }
@@ -1,107 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import { cn } from '@djangocfg/ui-core/lib';
6
-
7
- import { useMobile } from '../../hooks/useMobile';
8
- import { usePlaygroundContext } from '../../context/PlaygroundContext';
9
- import { EndpointList } from './EndpointList';
10
- import { RequestPanel } from './RequestPanel';
11
- import { ResponsePanel } from './ResponsePanel';
12
- import { Panel, PanelHeader } from './ui';
13
-
14
- // ─── Mobile tab layout ────────────────────────────────────────────────────────
15
-
16
- type MobileTab = 'endpoints' | 'request' | 'response';
17
-
18
- const MOBILE_TABS: { id: MobileTab; label: string }[] = [
19
- { id: 'endpoints', label: 'Endpoints' },
20
- { id: 'request', label: 'Request' },
21
- { id: 'response', label: 'Response' },
22
- ];
23
-
24
- function MobileView() {
25
- const { state } = usePlaygroundContext();
26
- const [tab, setTab] = React.useState<MobileTab>('endpoints');
27
-
28
- React.useEffect(() => {
29
- if (state.selectedEndpoint) setTab('request');
30
- }, [state.selectedEndpoint?.path, state.selectedEndpoint?.method]);
31
-
32
- React.useEffect(() => {
33
- if (state.response && !state.loading) setTab('response');
34
- }, [state.response, state.loading]);
35
-
36
- const hasResponse = Boolean(state.response) && !state.loading;
37
-
38
- return (
39
- <Panel className="h-full">
40
- <div className="shrink-0 flex border-b">
41
- {MOBILE_TABS.map((t) => {
42
- const isActive = tab === t.id;
43
- const showDot = t.id === 'response' && hasResponse;
44
- return (
45
- <button
46
- key={t.id}
47
- onClick={() => setTab(t.id)}
48
- className={cn(
49
- 'flex-1 py-2.5 text-xs font-medium transition-colors border-b-2 -mb-px relative',
50
- isActive
51
- ? 'border-primary text-foreground'
52
- : 'border-transparent text-muted-foreground hover:text-foreground',
53
- )}
54
- >
55
- {t.label}
56
- {showDot && (
57
- <span className="absolute top-2 right-[calc(50%-16px)] h-1.5 w-1.5 rounded-full bg-primary" />
58
- )}
59
- </button>
60
- );
61
- })}
62
- </div>
63
-
64
- <Panel className="flex-1">
65
- {tab === 'endpoints' && <EndpointList />}
66
- {tab === 'request' && <RequestPanel />}
67
- {tab === 'response' && <ResponsePanel />}
68
- </Panel>
69
- </Panel>
70
- );
71
- }
72
-
73
- // ─── Desktop 3-column layout ─────────────────────────────────────────────────
74
-
75
- function DesktopView() {
76
- return (
77
- <div className="grid grid-cols-[260px_1fr_1fr] divide-x h-full min-h-0 overflow-hidden">
78
- <Panel>
79
- <PanelHeader title="Endpoints" />
80
- <EndpointList />
81
- </Panel>
82
- <Panel>
83
- <PanelHeader title="Request" />
84
- <RequestPanel />
85
- </Panel>
86
- <Panel>
87
- <PanelHeader title="Response" />
88
- <ResponsePanel />
89
- </Panel>
90
- </div>
91
- );
92
- }
93
-
94
- // ─── Root ─────────────────────────────────────────────────────────────────────
95
-
96
- export const PlaygroundLayout: React.FC = () => {
97
- const { isMobile } = useMobile();
98
-
99
- return (
100
- <div
101
- className="flex flex-col overflow-hidden"
102
- style={{ height: 'calc(100dvh - var(--navbar-height, 64px))' }}
103
- >
104
- {isMobile ? <MobileView /> : <DesktopView />}
105
- </div>
106
- );
107
- };