@imjp/writenex-astro 0.1.0
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/README.md +539 -0
- package/dist/chunk-5PM6EQE5.js +151 -0
- package/dist/chunk-5PM6EQE5.js.map +1 -0
- package/dist/chunk-7XU5X6CW.js +1331 -0
- package/dist/chunk-7XU5X6CW.js.map +1 -0
- package/dist/chunk-AAOQHQPU.js +574 -0
- package/dist/chunk-AAOQHQPU.js.map +1 -0
- package/dist/chunk-CF2XXJFF.js +1410 -0
- package/dist/chunk-CF2XXJFF.js.map +1 -0
- package/dist/chunk-CRPZUUDU.js +52 -0
- package/dist/chunk-CRPZUUDU.js.map +1 -0
- package/dist/chunk-CYLDJ3HZ.js +310 -0
- package/dist/chunk-CYLDJ3HZ.js.map +1 -0
- package/dist/chunk-KIKIPIFA.js +1 -0
- package/dist/chunk-KIKIPIFA.js.map +1 -0
- package/dist/chunk-XNTQTTJU.js +145 -0
- package/dist/chunk-XNTQTTJU.js.map +1 -0
- package/dist/client/index.css +2 -0
- package/dist/client/index.css.map +1 -0
- package/dist/client/index.js +375 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/styles.css +584 -0
- package/dist/client/variables.css +304 -0
- package/dist/config/index.d.ts +54 -0
- package/dist/config/index.js +38 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config-BmEdBDo_.d.ts +220 -0
- package/dist/content-BWR52vD-.d.ts +64 -0
- package/dist/discovery/index.d.ts +310 -0
- package/dist/discovery/index.js +38 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/errors-C0iYiDTv.d.ts +107 -0
- package/dist/filesystem/index.d.ts +1292 -0
- package/dist/filesystem/index.js +203 -0
- package/dist/filesystem/index.js.map +1 -0
- package/dist/image-FP7w5ZIs.d.ts +47 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +151 -0
- package/dist/index.js.map +1 -0
- package/dist/loader-55LWCXHA.js +12 -0
- package/dist/loader-55LWCXHA.js.map +1 -0
- package/dist/loader-CrdnaAWR.d.ts +327 -0
- package/dist/server/index.d.ts +357 -0
- package/dist/server/index.js +37 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +94 -0
- package/src/client/App.tsx +900 -0
- package/src/client/components/ConfigPanel/ConfigPanel.css +553 -0
- package/src/client/components/ConfigPanel/ConfigPanel.tsx +396 -0
- package/src/client/components/ConfigPanel/index.ts +6 -0
- package/src/client/components/CreateContentModal/CreateContentModal.css +327 -0
- package/src/client/components/CreateContentModal/CreateContentModal.tsx +216 -0
- package/src/client/components/CreateContentModal/index.ts +7 -0
- package/src/client/components/Editor/Editor.css +885 -0
- package/src/client/components/Editor/Editor.tsx +484 -0
- package/src/client/components/Editor/ImageDialog.css +344 -0
- package/src/client/components/Editor/ImageDialog.tsx +367 -0
- package/src/client/components/Editor/LinkDialog.css +326 -0
- package/src/client/components/Editor/LinkDialog.tsx +332 -0
- package/src/client/components/Editor/index.ts +6 -0
- package/src/client/components/FrontmatterForm/FrontmatterForm.css +468 -0
- package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +914 -0
- package/src/client/components/FrontmatterForm/index.ts +7 -0
- package/src/client/components/Header/Header.css +300 -0
- package/src/client/components/Header/Header.tsx +300 -0
- package/src/client/components/Header/index.ts +7 -0
- package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.css +239 -0
- package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.tsx +151 -0
- package/src/client/components/KeyboardShortcuts/index.ts +6 -0
- package/src/client/components/LazyEditor.tsx +75 -0
- package/src/client/components/LiveRegion/LiveRegion.css +19 -0
- package/src/client/components/LiveRegion/LiveRegion.tsx +60 -0
- package/src/client/components/LiveRegion/index.ts +7 -0
- package/src/client/components/SearchReplace/SearchReplacePanel.css +300 -0
- package/src/client/components/SearchReplace/SearchReplacePanel.tsx +332 -0
- package/src/client/components/SearchReplace/index.ts +7 -0
- package/src/client/components/SelectCollectionModal/SelectCollectionModal.css +308 -0
- package/src/client/components/SelectCollectionModal/SelectCollectionModal.tsx +223 -0
- package/src/client/components/SelectCollectionModal/index.ts +7 -0
- package/src/client/components/Sidebar/Sidebar.css +570 -0
- package/src/client/components/Sidebar/Sidebar.tsx +617 -0
- package/src/client/components/Sidebar/index.ts +7 -0
- package/src/client/components/SkipLink/SkipLink.css +51 -0
- package/src/client/components/SkipLink/SkipLink.tsx +67 -0
- package/src/client/components/SkipLink/index.ts +7 -0
- package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.css +233 -0
- package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.tsx +160 -0
- package/src/client/components/UnsavedChangesModal/index.ts +1 -0
- package/src/client/components/VersionHistory/DiffViewer.css +430 -0
- package/src/client/components/VersionHistory/DiffViewer.tsx +383 -0
- package/src/client/components/VersionHistory/VersionActions.css +318 -0
- package/src/client/components/VersionHistory/VersionActions.tsx +277 -0
- package/src/client/components/VersionHistory/VersionHistoryPanel.css +369 -0
- package/src/client/components/VersionHistory/VersionHistoryPanel.tsx +469 -0
- package/src/client/components/VersionHistory/index.ts +9 -0
- package/src/client/context/ApiContext.tsx +154 -0
- package/src/client/context/ThemeContext.tsx +172 -0
- package/src/client/hooks/useAnnounce.ts +201 -0
- package/src/client/hooks/useApi.ts +374 -0
- package/src/client/hooks/useArrowNavigation.ts +286 -0
- package/src/client/hooks/useAutosave.ts +241 -0
- package/src/client/hooks/useFocusTrap.ts +178 -0
- package/src/client/hooks/useKeyboardShortcuts.ts +203 -0
- package/src/client/hooks/useSearch.ts +206 -0
- package/src/client/hooks/useVersionHistory.ts +451 -0
- package/src/client/index.tsx +70 -0
- package/src/client/styles.css +584 -0
- package/src/client/utils/focus.ts +57 -0
- package/src/client/utils/openInEditor.ts +130 -0
- package/src/client/variables.css +304 -0
- package/src/config/defaults.ts +109 -0
- package/src/config/index.ts +32 -0
- package/src/config/loader.ts +174 -0
- package/src/config/schema.ts +161 -0
- package/src/core/constants.ts +39 -0
- package/src/core/errors.ts +739 -0
- package/src/core/index.ts +11 -0
- package/src/discovery/collections.ts +216 -0
- package/src/discovery/index.ts +33 -0
- package/src/discovery/patterns.ts +702 -0
- package/src/discovery/schema.ts +453 -0
- package/src/filesystem/images.ts +798 -0
- package/src/filesystem/index.ts +107 -0
- package/src/filesystem/reader.ts +452 -0
- package/src/filesystem/version-config.ts +390 -0
- package/src/filesystem/versions.ts +1339 -0
- package/src/filesystem/watcher.ts +226 -0
- package/src/filesystem/writer.ts +540 -0
- package/src/index.ts +61 -0
- package/src/integration.ts +228 -0
- package/src/server/assets.ts +254 -0
- package/src/server/cache.ts +355 -0
- package/src/server/index.ts +33 -0
- package/src/server/middleware.ts +209 -0
- package/src/server/routes.ts +1428 -0
- package/src/types/api.ts +61 -0
- package/src/types/config.ts +134 -0
- package/src/types/content.ts +64 -0
- package/src/types/image.ts +48 -0
- package/src/types/index.ts +58 -0
- package/src/types/version.ts +117 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configuration panel modal
|
|
3
|
+
*
|
|
4
|
+
* Modal component displaying Writenex Astro configuration settings,
|
|
5
|
+
* including image settings, editor settings, and discovered collections.
|
|
6
|
+
* Includes focus trap for accessibility compliance.
|
|
7
|
+
*
|
|
8
|
+
* @module @writenex/astro/client/components/ConfigPanel
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
12
|
+
import {
|
|
13
|
+
X,
|
|
14
|
+
Settings,
|
|
15
|
+
Folder,
|
|
16
|
+
Image,
|
|
17
|
+
Info,
|
|
18
|
+
ExternalLink,
|
|
19
|
+
Copy,
|
|
20
|
+
Check,
|
|
21
|
+
ChevronDown,
|
|
22
|
+
} from "lucide-react";
|
|
23
|
+
import { useFocusTrap } from "../../hooks/useFocusTrap";
|
|
24
|
+
import type { Collection, WritenexClientConfig } from "../../hooks/useApi";
|
|
25
|
+
import { useSharedApi } from "../../context/ApiContext";
|
|
26
|
+
import {
|
|
27
|
+
openInEditor,
|
|
28
|
+
getAvailableEditors,
|
|
29
|
+
getPreferredEditor,
|
|
30
|
+
setPreferredEditor,
|
|
31
|
+
type EditorType,
|
|
32
|
+
} from "../../utils/openInEditor";
|
|
33
|
+
import "./ConfigPanel.css";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Props for the ConfigPanel component
|
|
37
|
+
*/
|
|
38
|
+
interface ConfigPanelProps {
|
|
39
|
+
/** Current configuration */
|
|
40
|
+
config: WritenexClientConfig | null;
|
|
41
|
+
/** Discovered collections */
|
|
42
|
+
collections: Collection[];
|
|
43
|
+
/** Whether the modal is open */
|
|
44
|
+
isOpen: boolean;
|
|
45
|
+
/** Callback to close the modal */
|
|
46
|
+
onClose: () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configuration panel modal component
|
|
51
|
+
*
|
|
52
|
+
* @component
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* <ConfigPanel
|
|
56
|
+
* config={config}
|
|
57
|
+
* collections={collections}
|
|
58
|
+
* isOpen={showConfig}
|
|
59
|
+
* onClose={() => setShowConfig(false)}
|
|
60
|
+
* />
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function ConfigPanel({
|
|
64
|
+
config,
|
|
65
|
+
collections,
|
|
66
|
+
isOpen,
|
|
67
|
+
onClose,
|
|
68
|
+
}: ConfigPanelProps): React.ReactElement | null {
|
|
69
|
+
const api = useSharedApi();
|
|
70
|
+
const triggerRef = useRef<HTMLElement | null>(null);
|
|
71
|
+
const [configPath, setConfigPath] = useState<string | null>(null);
|
|
72
|
+
const [hasConfigFile, setHasConfigFile] = useState(false);
|
|
73
|
+
const [copied, setCopied] = useState(false);
|
|
74
|
+
const [selectedEditor, setSelectedEditor] =
|
|
75
|
+
useState<EditorType>(getPreferredEditor());
|
|
76
|
+
const [showEditorDropdown, setShowEditorDropdown] = useState(false);
|
|
77
|
+
|
|
78
|
+
// Fetch config path when modal opens
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (isOpen) {
|
|
81
|
+
api
|
|
82
|
+
.getConfigPath()
|
|
83
|
+
.then((data) => {
|
|
84
|
+
setConfigPath(data.configPath);
|
|
85
|
+
setHasConfigFile(data.hasConfigFile);
|
|
86
|
+
})
|
|
87
|
+
.catch(() => {
|
|
88
|
+
setConfigPath(null);
|
|
89
|
+
setHasConfigFile(false);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}, [isOpen, api]);
|
|
93
|
+
|
|
94
|
+
// Store the trigger element when modal opens
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (isOpen) {
|
|
97
|
+
triggerRef.current = document.activeElement as HTMLElement;
|
|
98
|
+
}
|
|
99
|
+
}, [isOpen]);
|
|
100
|
+
|
|
101
|
+
// Focus trap for accessibility
|
|
102
|
+
const { containerRef } = useFocusTrap({
|
|
103
|
+
enabled: isOpen,
|
|
104
|
+
onEscape: onClose,
|
|
105
|
+
returnFocusTo: triggerRef.current,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const handleOpenInEditor = useCallback(() => {
|
|
109
|
+
if (configPath) {
|
|
110
|
+
openInEditor(configPath, selectedEditor);
|
|
111
|
+
setPreferredEditor(selectedEditor);
|
|
112
|
+
}
|
|
113
|
+
}, [configPath, selectedEditor]);
|
|
114
|
+
|
|
115
|
+
const handleCopyPath = useCallback(() => {
|
|
116
|
+
if (configPath) {
|
|
117
|
+
navigator.clipboard.writeText(configPath).then(() => {
|
|
118
|
+
setCopied(true);
|
|
119
|
+
setTimeout(() => setCopied(false), 2000);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}, [configPath]);
|
|
123
|
+
|
|
124
|
+
const handleEditorSelect = useCallback((editor: EditorType) => {
|
|
125
|
+
setSelectedEditor(editor);
|
|
126
|
+
setPreferredEditor(editor);
|
|
127
|
+
setShowEditorDropdown(false);
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
130
|
+
if (!isOpen) return null;
|
|
131
|
+
|
|
132
|
+
const handleOverlayClick = (e: React.MouseEvent) => {
|
|
133
|
+
if (e.target === e.currentTarget) onClose();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div className="wn-config-overlay" onClick={handleOverlayClick}>
|
|
138
|
+
<div
|
|
139
|
+
ref={containerRef}
|
|
140
|
+
className="wn-config-modal"
|
|
141
|
+
role="dialog"
|
|
142
|
+
aria-modal="true"
|
|
143
|
+
aria-labelledby="config-panel-title"
|
|
144
|
+
>
|
|
145
|
+
{/* Header */}
|
|
146
|
+
<div className="wn-config-header">
|
|
147
|
+
<h2 id="config-panel-title" className="wn-config-title">
|
|
148
|
+
<Settings size={16} />
|
|
149
|
+
Configuration
|
|
150
|
+
</h2>
|
|
151
|
+
<button
|
|
152
|
+
className="wn-config-close"
|
|
153
|
+
onClick={onClose}
|
|
154
|
+
title="Close"
|
|
155
|
+
aria-label="Close configuration panel"
|
|
156
|
+
>
|
|
157
|
+
<X size={16} />
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
{/* Content */}
|
|
162
|
+
<div className="wn-config-content">
|
|
163
|
+
{/* Configuration File - Primary action at top */}
|
|
164
|
+
<section className="wn-config-section">
|
|
165
|
+
<div className="wn-config-help">
|
|
166
|
+
<h3 className="wn-config-help-title">Configuration File</h3>
|
|
167
|
+
{hasConfigFile && configPath ? (
|
|
168
|
+
<>
|
|
169
|
+
<p className="wn-config-help-text">
|
|
170
|
+
Configuration loaded from{" "}
|
|
171
|
+
<code className="wn-config-help-code">
|
|
172
|
+
{configPath.split("/").pop()}
|
|
173
|
+
</code>
|
|
174
|
+
</p>
|
|
175
|
+
<div className="wn-config-actions">
|
|
176
|
+
<div className="wn-config-editor-select">
|
|
177
|
+
<button
|
|
178
|
+
className="wn-config-btn wn-config-btn--primary"
|
|
179
|
+
onClick={handleOpenInEditor}
|
|
180
|
+
title={`Open in ${getAvailableEditors().find((e) => e.name === selectedEditor)?.displayName}`}
|
|
181
|
+
>
|
|
182
|
+
<ExternalLink size={14} />
|
|
183
|
+
Open in Editor
|
|
184
|
+
</button>
|
|
185
|
+
<button
|
|
186
|
+
className="wn-config-btn wn-config-btn--dropdown"
|
|
187
|
+
onClick={() =>
|
|
188
|
+
setShowEditorDropdown(!showEditorDropdown)
|
|
189
|
+
}
|
|
190
|
+
aria-label="Select editor"
|
|
191
|
+
aria-expanded={showEditorDropdown}
|
|
192
|
+
>
|
|
193
|
+
<ChevronDown size={14} />
|
|
194
|
+
</button>
|
|
195
|
+
{showEditorDropdown && (
|
|
196
|
+
<div className="wn-config-dropdown">
|
|
197
|
+
{getAvailableEditors().map((editor) => (
|
|
198
|
+
<button
|
|
199
|
+
key={editor.name}
|
|
200
|
+
className={`wn-config-dropdown-item ${selectedEditor === editor.name ? "wn-config-dropdown-item--active" : ""}`}
|
|
201
|
+
onClick={() =>
|
|
202
|
+
handleEditorSelect(editor.name as EditorType)
|
|
203
|
+
}
|
|
204
|
+
>
|
|
205
|
+
{editor.displayName}
|
|
206
|
+
</button>
|
|
207
|
+
))}
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
</div>
|
|
211
|
+
<button
|
|
212
|
+
className="wn-config-btn wn-config-btn--secondary"
|
|
213
|
+
onClick={handleCopyPath}
|
|
214
|
+
title="Copy file path"
|
|
215
|
+
>
|
|
216
|
+
{copied ? <Check size={14} /> : <Copy size={14} />}
|
|
217
|
+
{copied ? "Copied" : "Copy Path"}
|
|
218
|
+
</button>
|
|
219
|
+
</div>
|
|
220
|
+
</>
|
|
221
|
+
) : (
|
|
222
|
+
<p className="wn-config-help-text">
|
|
223
|
+
No configuration file found. Using default settings.
|
|
224
|
+
</p>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
</section>
|
|
228
|
+
|
|
229
|
+
{/* Image Settings */}
|
|
230
|
+
<section className="wn-config-section">
|
|
231
|
+
<h3 className="wn-config-section-title">
|
|
232
|
+
<Image size={14} />
|
|
233
|
+
Image Settings
|
|
234
|
+
</h3>
|
|
235
|
+
<div className="wn-config-items">
|
|
236
|
+
<ConfigItem
|
|
237
|
+
label="Strategy"
|
|
238
|
+
value={config?.images?.strategy ?? "colocated"}
|
|
239
|
+
valueClass={getStrategyClass(config?.images?.strategy)}
|
|
240
|
+
description={getStrategyDescription(config?.images?.strategy)}
|
|
241
|
+
/>
|
|
242
|
+
{config?.images?.publicPath && (
|
|
243
|
+
<ConfigItem
|
|
244
|
+
label="Public Path"
|
|
245
|
+
value={config.images.publicPath}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
{config?.images?.storagePath && (
|
|
249
|
+
<ConfigItem
|
|
250
|
+
label="Storage Path"
|
|
251
|
+
value={config.images.storagePath}
|
|
252
|
+
/>
|
|
253
|
+
)}
|
|
254
|
+
</div>
|
|
255
|
+
</section>
|
|
256
|
+
|
|
257
|
+
{/* Editor Settings */}
|
|
258
|
+
<section className="wn-config-section">
|
|
259
|
+
<h3 className="wn-config-section-title">
|
|
260
|
+
<Info size={14} />
|
|
261
|
+
Editor Settings
|
|
262
|
+
</h3>
|
|
263
|
+
<div className="wn-config-items">
|
|
264
|
+
<ConfigItem
|
|
265
|
+
label="Autosave"
|
|
266
|
+
value={
|
|
267
|
+
config?.editor?.autosave !== false ? "Enabled" : "Disabled"
|
|
268
|
+
}
|
|
269
|
+
valueClass={
|
|
270
|
+
config?.editor?.autosave !== false
|
|
271
|
+
? "wn-config-item-value--emerald"
|
|
272
|
+
: "wn-config-item-value--muted"
|
|
273
|
+
}
|
|
274
|
+
/>
|
|
275
|
+
<ConfigItem
|
|
276
|
+
label="Interval"
|
|
277
|
+
value={`${(config?.editor?.autosaveInterval ?? 3000) / 1000}s`}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
</section>
|
|
281
|
+
|
|
282
|
+
{/* Collections */}
|
|
283
|
+
<section className="wn-config-section">
|
|
284
|
+
<h3 className="wn-config-section-title">
|
|
285
|
+
<Folder size={14} />
|
|
286
|
+
Collections ({collections.length})
|
|
287
|
+
</h3>
|
|
288
|
+
{collections.length === 0 ? (
|
|
289
|
+
<p className="wn-config-empty">No collections discovered</p>
|
|
290
|
+
) : (
|
|
291
|
+
<div className="wn-config-collections">
|
|
292
|
+
{collections.map((col) => (
|
|
293
|
+
<CollectionCard key={col.name} collection={col} />
|
|
294
|
+
))}
|
|
295
|
+
</div>
|
|
296
|
+
)}
|
|
297
|
+
</section>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get CSS class for strategy value
|
|
306
|
+
*/
|
|
307
|
+
function getStrategyClass(strategy?: string): string {
|
|
308
|
+
switch (strategy) {
|
|
309
|
+
case "colocated":
|
|
310
|
+
return "wn-config-item-value--violet";
|
|
311
|
+
case "public":
|
|
312
|
+
return "wn-config-item-value--emerald";
|
|
313
|
+
default:
|
|
314
|
+
return "wn-config-item-value--amber";
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get description for strategy value
|
|
320
|
+
*/
|
|
321
|
+
function getStrategyDescription(strategy?: string): string {
|
|
322
|
+
switch (strategy) {
|
|
323
|
+
case "colocated":
|
|
324
|
+
return "Images stored alongside content files";
|
|
325
|
+
case "public":
|
|
326
|
+
return "Images stored in public folder";
|
|
327
|
+
default:
|
|
328
|
+
return "Custom storage path";
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Config item component
|
|
334
|
+
*/
|
|
335
|
+
function ConfigItem({
|
|
336
|
+
label,
|
|
337
|
+
value,
|
|
338
|
+
valueClass = "",
|
|
339
|
+
description,
|
|
340
|
+
}: {
|
|
341
|
+
label: string;
|
|
342
|
+
value: string;
|
|
343
|
+
valueClass?: string;
|
|
344
|
+
description?: string;
|
|
345
|
+
}): React.ReactElement {
|
|
346
|
+
return (
|
|
347
|
+
<div className="wn-config-item">
|
|
348
|
+
<span className="wn-config-item-label">{label}</span>
|
|
349
|
+
<div>
|
|
350
|
+
<span className={`wn-config-item-value ${valueClass}`}>{value}</span>
|
|
351
|
+
{description && (
|
|
352
|
+
<p className="wn-config-item-description">{description}</p>
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Collection card component
|
|
361
|
+
*/
|
|
362
|
+
function CollectionCard({
|
|
363
|
+
collection,
|
|
364
|
+
}: {
|
|
365
|
+
collection: Collection;
|
|
366
|
+
}): React.ReactElement {
|
|
367
|
+
return (
|
|
368
|
+
<div className="wn-config-collection">
|
|
369
|
+
<div className="wn-config-collection-header">
|
|
370
|
+
<span className="wn-config-collection-name">{collection.name}</span>
|
|
371
|
+
<span className="wn-config-collection-count">
|
|
372
|
+
{collection.count} items
|
|
373
|
+
</span>
|
|
374
|
+
</div>
|
|
375
|
+
<div className="wn-config-collection-details">
|
|
376
|
+
<p className="wn-config-collection-detail">
|
|
377
|
+
<span>Path:</span> {collection.path}
|
|
378
|
+
</p>
|
|
379
|
+
<p className="wn-config-collection-detail">
|
|
380
|
+
<span>Pattern:</span> {collection.filePattern}
|
|
381
|
+
</p>
|
|
382
|
+
{collection.schema && (
|
|
383
|
+
<p className="wn-config-collection-detail wn-config-collection-detail--blue">
|
|
384
|
+
<span>Schema:</span> {Object.keys(collection.schema).length} fields
|
|
385
|
+
detected
|
|
386
|
+
</p>
|
|
387
|
+
)}
|
|
388
|
+
{collection.previewUrl && (
|
|
389
|
+
<p className="wn-config-collection-detail wn-config-collection-detail--violet">
|
|
390
|
+
<span>Preview:</span> {collection.previewUrl}
|
|
391
|
+
</p>
|
|
392
|
+
)}
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
);
|
|
396
|
+
}
|