@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.
Files changed (141) hide show
  1. package/README.md +539 -0
  2. package/dist/chunk-5PM6EQE5.js +151 -0
  3. package/dist/chunk-5PM6EQE5.js.map +1 -0
  4. package/dist/chunk-7XU5X6CW.js +1331 -0
  5. package/dist/chunk-7XU5X6CW.js.map +1 -0
  6. package/dist/chunk-AAOQHQPU.js +574 -0
  7. package/dist/chunk-AAOQHQPU.js.map +1 -0
  8. package/dist/chunk-CF2XXJFF.js +1410 -0
  9. package/dist/chunk-CF2XXJFF.js.map +1 -0
  10. package/dist/chunk-CRPZUUDU.js +52 -0
  11. package/dist/chunk-CRPZUUDU.js.map +1 -0
  12. package/dist/chunk-CYLDJ3HZ.js +310 -0
  13. package/dist/chunk-CYLDJ3HZ.js.map +1 -0
  14. package/dist/chunk-KIKIPIFA.js +1 -0
  15. package/dist/chunk-KIKIPIFA.js.map +1 -0
  16. package/dist/chunk-XNTQTTJU.js +145 -0
  17. package/dist/chunk-XNTQTTJU.js.map +1 -0
  18. package/dist/client/index.css +2 -0
  19. package/dist/client/index.css.map +1 -0
  20. package/dist/client/index.js +375 -0
  21. package/dist/client/index.js.map +1 -0
  22. package/dist/client/styles.css +584 -0
  23. package/dist/client/variables.css +304 -0
  24. package/dist/config/index.d.ts +54 -0
  25. package/dist/config/index.js +38 -0
  26. package/dist/config/index.js.map +1 -0
  27. package/dist/config-BmEdBDo_.d.ts +220 -0
  28. package/dist/content-BWR52vD-.d.ts +64 -0
  29. package/dist/discovery/index.d.ts +310 -0
  30. package/dist/discovery/index.js +38 -0
  31. package/dist/discovery/index.js.map +1 -0
  32. package/dist/errors-C0iYiDTv.d.ts +107 -0
  33. package/dist/filesystem/index.d.ts +1292 -0
  34. package/dist/filesystem/index.js +203 -0
  35. package/dist/filesystem/index.js.map +1 -0
  36. package/dist/image-FP7w5ZIs.d.ts +47 -0
  37. package/dist/index.d.ts +64 -0
  38. package/dist/index.js +151 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/loader-55LWCXHA.js +12 -0
  41. package/dist/loader-55LWCXHA.js.map +1 -0
  42. package/dist/loader-CrdnaAWR.d.ts +327 -0
  43. package/dist/server/index.d.ts +357 -0
  44. package/dist/server/index.js +37 -0
  45. package/dist/server/index.js.map +1 -0
  46. package/package.json +94 -0
  47. package/src/client/App.tsx +900 -0
  48. package/src/client/components/ConfigPanel/ConfigPanel.css +553 -0
  49. package/src/client/components/ConfigPanel/ConfigPanel.tsx +396 -0
  50. package/src/client/components/ConfigPanel/index.ts +6 -0
  51. package/src/client/components/CreateContentModal/CreateContentModal.css +327 -0
  52. package/src/client/components/CreateContentModal/CreateContentModal.tsx +216 -0
  53. package/src/client/components/CreateContentModal/index.ts +7 -0
  54. package/src/client/components/Editor/Editor.css +885 -0
  55. package/src/client/components/Editor/Editor.tsx +484 -0
  56. package/src/client/components/Editor/ImageDialog.css +344 -0
  57. package/src/client/components/Editor/ImageDialog.tsx +367 -0
  58. package/src/client/components/Editor/LinkDialog.css +326 -0
  59. package/src/client/components/Editor/LinkDialog.tsx +332 -0
  60. package/src/client/components/Editor/index.ts +6 -0
  61. package/src/client/components/FrontmatterForm/FrontmatterForm.css +468 -0
  62. package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +914 -0
  63. package/src/client/components/FrontmatterForm/index.ts +7 -0
  64. package/src/client/components/Header/Header.css +300 -0
  65. package/src/client/components/Header/Header.tsx +300 -0
  66. package/src/client/components/Header/index.ts +7 -0
  67. package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.css +239 -0
  68. package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.tsx +151 -0
  69. package/src/client/components/KeyboardShortcuts/index.ts +6 -0
  70. package/src/client/components/LazyEditor.tsx +75 -0
  71. package/src/client/components/LiveRegion/LiveRegion.css +19 -0
  72. package/src/client/components/LiveRegion/LiveRegion.tsx +60 -0
  73. package/src/client/components/LiveRegion/index.ts +7 -0
  74. package/src/client/components/SearchReplace/SearchReplacePanel.css +300 -0
  75. package/src/client/components/SearchReplace/SearchReplacePanel.tsx +332 -0
  76. package/src/client/components/SearchReplace/index.ts +7 -0
  77. package/src/client/components/SelectCollectionModal/SelectCollectionModal.css +308 -0
  78. package/src/client/components/SelectCollectionModal/SelectCollectionModal.tsx +223 -0
  79. package/src/client/components/SelectCollectionModal/index.ts +7 -0
  80. package/src/client/components/Sidebar/Sidebar.css +570 -0
  81. package/src/client/components/Sidebar/Sidebar.tsx +617 -0
  82. package/src/client/components/Sidebar/index.ts +7 -0
  83. package/src/client/components/SkipLink/SkipLink.css +51 -0
  84. package/src/client/components/SkipLink/SkipLink.tsx +67 -0
  85. package/src/client/components/SkipLink/index.ts +7 -0
  86. package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.css +233 -0
  87. package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.tsx +160 -0
  88. package/src/client/components/UnsavedChangesModal/index.ts +1 -0
  89. package/src/client/components/VersionHistory/DiffViewer.css +430 -0
  90. package/src/client/components/VersionHistory/DiffViewer.tsx +383 -0
  91. package/src/client/components/VersionHistory/VersionActions.css +318 -0
  92. package/src/client/components/VersionHistory/VersionActions.tsx +277 -0
  93. package/src/client/components/VersionHistory/VersionHistoryPanel.css +369 -0
  94. package/src/client/components/VersionHistory/VersionHistoryPanel.tsx +469 -0
  95. package/src/client/components/VersionHistory/index.ts +9 -0
  96. package/src/client/context/ApiContext.tsx +154 -0
  97. package/src/client/context/ThemeContext.tsx +172 -0
  98. package/src/client/hooks/useAnnounce.ts +201 -0
  99. package/src/client/hooks/useApi.ts +374 -0
  100. package/src/client/hooks/useArrowNavigation.ts +286 -0
  101. package/src/client/hooks/useAutosave.ts +241 -0
  102. package/src/client/hooks/useFocusTrap.ts +178 -0
  103. package/src/client/hooks/useKeyboardShortcuts.ts +203 -0
  104. package/src/client/hooks/useSearch.ts +206 -0
  105. package/src/client/hooks/useVersionHistory.ts +451 -0
  106. package/src/client/index.tsx +70 -0
  107. package/src/client/styles.css +584 -0
  108. package/src/client/utils/focus.ts +57 -0
  109. package/src/client/utils/openInEditor.ts +130 -0
  110. package/src/client/variables.css +304 -0
  111. package/src/config/defaults.ts +109 -0
  112. package/src/config/index.ts +32 -0
  113. package/src/config/loader.ts +174 -0
  114. package/src/config/schema.ts +161 -0
  115. package/src/core/constants.ts +39 -0
  116. package/src/core/errors.ts +739 -0
  117. package/src/core/index.ts +11 -0
  118. package/src/discovery/collections.ts +216 -0
  119. package/src/discovery/index.ts +33 -0
  120. package/src/discovery/patterns.ts +702 -0
  121. package/src/discovery/schema.ts +453 -0
  122. package/src/filesystem/images.ts +798 -0
  123. package/src/filesystem/index.ts +107 -0
  124. package/src/filesystem/reader.ts +452 -0
  125. package/src/filesystem/version-config.ts +390 -0
  126. package/src/filesystem/versions.ts +1339 -0
  127. package/src/filesystem/watcher.ts +226 -0
  128. package/src/filesystem/writer.ts +540 -0
  129. package/src/index.ts +61 -0
  130. package/src/integration.ts +228 -0
  131. package/src/server/assets.ts +254 -0
  132. package/src/server/cache.ts +355 -0
  133. package/src/server/index.ts +33 -0
  134. package/src/server/middleware.ts +209 -0
  135. package/src/server/routes.ts +1428 -0
  136. package/src/types/api.ts +61 -0
  137. package/src/types/config.ts +134 -0
  138. package/src/types/content.ts +64 -0
  139. package/src/types/image.ts +48 -0
  140. package/src/types/index.ts +58 -0
  141. package/src/types/version.ts +117 -0
@@ -0,0 +1,327 @@
1
+ /**
2
+ * @fileoverview CreateContentModal styles
3
+ *
4
+ * Modal styling for create new content dialog.
5
+ */
6
+
7
+ /* ============================================================================
8
+ MODAL OVERLAY
9
+ ============================================================================ */
10
+
11
+ .wn-create-modal-overlay {
12
+ position: fixed;
13
+ inset: 0;
14
+ z-index: var(--wn-z-modal);
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ background-color: var(--wn-backdrop);
19
+ animation: wn-create-modal-fadeIn var(--wn-transition-fast) ease-out;
20
+ }
21
+
22
+ @keyframes wn-create-modal-fadeIn {
23
+ from {
24
+ opacity: 0;
25
+ }
26
+ to {
27
+ opacity: 1;
28
+ }
29
+ }
30
+
31
+ /* ============================================================================
32
+ MODAL CONTAINER
33
+ ============================================================================ */
34
+
35
+ .wn-create-modal {
36
+ width: 100%;
37
+ max-width: var(--wn-modal-md);
38
+ overflow: hidden;
39
+ border-radius: var(--wn-radius-lg);
40
+ border: 1px solid var(--wn-zinc-700);
41
+ background-color: var(--wn-zinc-900);
42
+ animation: wn-create-modal-zoomIn var(--wn-transition-fast) ease-out;
43
+ }
44
+
45
+ @keyframes wn-create-modal-zoomIn {
46
+ from {
47
+ opacity: 0;
48
+ transform: scale(0.95) translateY(-10px);
49
+ }
50
+ to {
51
+ opacity: 1;
52
+ transform: scale(1) translateY(0);
53
+ }
54
+ }
55
+
56
+ /* ============================================================================
57
+ MODAL HEADER
58
+ ============================================================================ */
59
+
60
+ .wn-create-modal-header {
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: space-between;
64
+ padding: var(--wn-space-5) var(--wn-space-6);
65
+ border-bottom: 1px solid var(--wn-zinc-700);
66
+ }
67
+
68
+ .wn-create-modal-header-content {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--wn-space-4);
72
+ }
73
+
74
+ .wn-create-modal-icon {
75
+ color: var(--wn-brand-500);
76
+ }
77
+
78
+ .wn-create-modal-title {
79
+ font-size: var(--wn-font-md);
80
+ font-weight: 600;
81
+ color: var(--wn-zinc-50);
82
+ margin: 0;
83
+ }
84
+
85
+ .wn-create-modal-close {
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ width: var(--wn-icon-btn-md);
90
+ height: var(--wn-icon-btn-md);
91
+ padding: 0;
92
+ border: none;
93
+ border-radius: var(--wn-radius-md);
94
+ background-color: transparent;
95
+ color: var(--wn-zinc-400);
96
+ cursor: pointer;
97
+ transition:
98
+ background-color var(--wn-transition-fast),
99
+ color var(--wn-transition-fast);
100
+ }
101
+
102
+ .wn-create-modal-close:hover:not(:disabled) {
103
+ background-color: var(--wn-overlay-10);
104
+ color: var(--wn-zinc-50);
105
+ }
106
+
107
+ .wn-create-modal-close:disabled {
108
+ opacity: 0.5;
109
+ cursor: not-allowed;
110
+ }
111
+
112
+ /* ============================================================================
113
+ MODAL BODY
114
+ ============================================================================ */
115
+
116
+ .wn-create-modal-body {
117
+ padding: var(--wn-space-6);
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: var(--wn-space-5);
121
+ }
122
+
123
+ /* Collection indicator */
124
+ .wn-create-modal-collection {
125
+ font-size: var(--wn-font-xs);
126
+ color: var(--wn-zinc-500);
127
+ }
128
+
129
+ .wn-create-modal-collection-name {
130
+ color: var(--wn-zinc-400);
131
+ font-weight: 500;
132
+ }
133
+
134
+ /* Form field */
135
+ .wn-create-modal-field {
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: var(--wn-space-3);
139
+ }
140
+
141
+ .wn-create-modal-label {
142
+ font-size: var(--wn-font-base);
143
+ font-weight: 500;
144
+ color: var(--wn-zinc-400);
145
+ }
146
+
147
+ .wn-create-modal-input {
148
+ width: 100%;
149
+ padding: var(--wn-space-3) var(--wn-space-4);
150
+ border-radius: var(--wn-radius-md);
151
+ border: 1px solid var(--wn-zinc-700);
152
+ background-color: var(--wn-zinc-950);
153
+ font-size: var(--wn-font-base);
154
+ color: var(--wn-zinc-50);
155
+ outline: none;
156
+ transition:
157
+ border-color var(--wn-transition-fast),
158
+ box-shadow var(--wn-transition-fast);
159
+ }
160
+
161
+ .wn-create-modal-input::placeholder {
162
+ color: var(--wn-zinc-600);
163
+ }
164
+
165
+ .wn-create-modal-input:focus {
166
+ border-color: var(--wn-brand-500);
167
+ box-shadow: 0 0 0 2px var(--wn-brand-alpha-20);
168
+ }
169
+
170
+ .wn-create-modal-input:disabled {
171
+ opacity: 0.5;
172
+ cursor: not-allowed;
173
+ }
174
+
175
+ /* Slug preview */
176
+ .wn-create-modal-slug {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: var(--wn-space-3);
180
+ padding: var(--wn-space-3) var(--wn-space-4);
181
+ border-radius: var(--wn-radius-md);
182
+ background-color: var(--wn-zinc-950);
183
+ border: 1px solid var(--wn-zinc-800);
184
+ }
185
+
186
+ .wn-create-modal-slug-label {
187
+ font-size: var(--wn-font-xs);
188
+ color: var(--wn-zinc-500);
189
+ }
190
+
191
+ .wn-create-modal-slug-value {
192
+ font-family: ui-monospace, monospace;
193
+ font-size: var(--wn-font-xs);
194
+ color: var(--wn-brand-400);
195
+ background: none;
196
+ padding: 0;
197
+ }
198
+
199
+ /* ============================================================================
200
+ MODAL FOOTER
201
+ ============================================================================ */
202
+
203
+ .wn-create-modal-footer {
204
+ display: flex;
205
+ justify-content: flex-end;
206
+ gap: var(--wn-space-4);
207
+ padding-top: var(--wn-space-3);
208
+ }
209
+
210
+ /* Buttons */
211
+ .wn-create-modal-btn {
212
+ display: inline-flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ gap: var(--wn-space-3);
216
+ padding: var(--wn-space-3) var(--wn-space-5);
217
+ border-radius: var(--wn-radius-md);
218
+ font-size: var(--wn-font-base);
219
+ font-weight: 500;
220
+ cursor: pointer;
221
+ transition:
222
+ background-color var(--wn-transition-fast),
223
+ color var(--wn-transition-fast),
224
+ border-color var(--wn-transition-fast);
225
+ }
226
+
227
+ .wn-create-modal-btn:disabled {
228
+ opacity: 0.5;
229
+ cursor: not-allowed;
230
+ }
231
+
232
+ .wn-create-modal-btn--secondary {
233
+ background-color: transparent;
234
+ border: 1px solid var(--wn-zinc-700);
235
+ color: var(--wn-zinc-400);
236
+ }
237
+
238
+ .wn-create-modal-btn--secondary:hover:not(:disabled) {
239
+ background-color: var(--wn-zinc-800);
240
+ color: var(--wn-zinc-50);
241
+ }
242
+
243
+ .wn-create-modal-btn--primary {
244
+ background-color: var(--wn-brand-500);
245
+ border: 1px solid var(--wn-brand-500);
246
+ color: white;
247
+ }
248
+
249
+ .wn-create-modal-btn--primary:hover:not(:disabled) {
250
+ background-color: var(--wn-brand-600);
251
+ border-color: var(--wn-brand-600);
252
+ }
253
+
254
+ /* ============================================================================
255
+ LIGHT MODE OVERRIDES
256
+ ============================================================================ */
257
+
258
+ .wn-light .wn-create-modal-overlay {
259
+ background-color: var(--wn-backdrop-light);
260
+ }
261
+
262
+ .wn-light .wn-create-modal {
263
+ border-color: var(--wn-zinc-200);
264
+ background-color: #fff;
265
+ }
266
+
267
+ .wn-light .wn-create-modal-header {
268
+ border-bottom-color: var(--wn-zinc-200);
269
+ }
270
+
271
+ .wn-light .wn-create-modal-title {
272
+ color: var(--wn-zinc-900);
273
+ }
274
+
275
+ .wn-light .wn-create-modal-close {
276
+ color: var(--wn-zinc-500);
277
+ }
278
+
279
+ .wn-light .wn-create-modal-close:hover:not(:disabled) {
280
+ background-color: var(--wn-overlay-light-5);
281
+ color: var(--wn-zinc-900);
282
+ }
283
+
284
+ .wn-light .wn-create-modal-collection {
285
+ color: var(--wn-zinc-500);
286
+ }
287
+
288
+ .wn-light .wn-create-modal-collection-name {
289
+ color: var(--wn-zinc-600);
290
+ }
291
+
292
+ .wn-light .wn-create-modal-label {
293
+ color: var(--wn-zinc-600);
294
+ }
295
+
296
+ .wn-light .wn-create-modal-input {
297
+ border-color: var(--wn-zinc-200);
298
+ background-color: var(--wn-zinc-50);
299
+ color: var(--wn-zinc-900);
300
+ }
301
+
302
+ .wn-light .wn-create-modal-input::placeholder {
303
+ color: var(--wn-zinc-400);
304
+ }
305
+
306
+ .wn-light .wn-create-modal-slug {
307
+ background-color: var(--wn-zinc-50);
308
+ border-color: var(--wn-zinc-200);
309
+ }
310
+
311
+ .wn-light .wn-create-modal-slug-label {
312
+ color: var(--wn-zinc-500);
313
+ }
314
+
315
+ .wn-light .wn-create-modal-slug-value {
316
+ color: var(--wn-brand-600);
317
+ }
318
+
319
+ .wn-light .wn-create-modal-btn--secondary {
320
+ border-color: var(--wn-zinc-200);
321
+ color: var(--wn-zinc-500);
322
+ }
323
+
324
+ .wn-light .wn-create-modal-btn--secondary:hover:not(:disabled) {
325
+ background-color: var(--wn-zinc-100);
326
+ color: var(--wn-zinc-900);
327
+ }
@@ -0,0 +1,216 @@
1
+ /**
2
+ * @fileoverview Create content modal component
3
+ *
4
+ * Modal dialog for creating new content with title input and slug preview.
5
+ * Replaces the native window.prompt for a better user experience.
6
+ * Includes focus trap for accessibility compliance.
7
+ *
8
+ * @module @writenex/astro/client/components/CreateContentModal
9
+ */
10
+
11
+ import { useCallback, useEffect, useRef, useState } from "react";
12
+ import { X, FileText } from "lucide-react";
13
+ import { useFocusTrap } from "../../hooks/useFocusTrap";
14
+ import "./CreateContentModal.css";
15
+
16
+ /**
17
+ * Props for CreateContentModal component
18
+ */
19
+ interface CreateContentModalProps {
20
+ /** Whether the modal is open */
21
+ isOpen: boolean;
22
+ /** Callback to close the modal */
23
+ onClose: () => void;
24
+ /** Callback when content is created */
25
+ onCreate: (title: string) => void;
26
+ /** Collection name for display */
27
+ collectionName: string;
28
+ /** Whether creation is in progress */
29
+ isCreating?: boolean;
30
+ }
31
+
32
+ /**
33
+ * Generate a URL-safe slug from a title
34
+ *
35
+ * @param title - The title to slugify
36
+ * @returns URL-safe slug
37
+ */
38
+ function generateSlug(title: string): string {
39
+ return title
40
+ .toLowerCase()
41
+ .trim()
42
+ .replace(/[^\w\s-]/g, "")
43
+ .replace(/[\s_-]+/g, "-")
44
+ .replace(/^-+|-+$/g, "");
45
+ }
46
+
47
+ /**
48
+ * Modal dialog for creating new content
49
+ *
50
+ * Features:
51
+ * - Title input with auto-focus
52
+ * - Real-time slug preview
53
+ * - Keyboard navigation (Enter to create, Escape to close)
54
+ * - Loading state during creation
55
+ *
56
+ * @component
57
+ * @example
58
+ * ```tsx
59
+ * <CreateContentModal
60
+ * isOpen={showCreateModal}
61
+ * onClose={() => setShowCreateModal(false)}
62
+ * onCreate={handleCreate}
63
+ * collectionName="blog"
64
+ * />
65
+ * ```
66
+ */
67
+ export function CreateContentModal({
68
+ isOpen,
69
+ onClose,
70
+ onCreate,
71
+ collectionName,
72
+ isCreating = false,
73
+ }: CreateContentModalProps): React.ReactElement | null {
74
+ const [title, setTitle] = useState("");
75
+ const inputRef = useRef<HTMLInputElement>(null);
76
+ const triggerRef = useRef<HTMLElement | null>(null);
77
+
78
+ // Store the trigger element when modal opens
79
+ useEffect(() => {
80
+ if (isOpen) {
81
+ triggerRef.current = document.activeElement as HTMLElement;
82
+ }
83
+ }, [isOpen]);
84
+
85
+ // Focus trap for accessibility
86
+ const { containerRef } = useFocusTrap({
87
+ enabled: isOpen,
88
+ onEscape: isCreating ? undefined : onClose,
89
+ returnFocusTo: triggerRef.current,
90
+ });
91
+
92
+ // Reset title and focus input when modal opens
93
+ useEffect(() => {
94
+ if (isOpen) {
95
+ setTitle("");
96
+ // Small delay to ensure modal is rendered and focus trap is active
97
+ setTimeout(() => {
98
+ inputRef.current?.focus();
99
+ }, 50);
100
+ }
101
+ }, [isOpen]);
102
+
103
+ const handleSubmit = useCallback(
104
+ (e: React.FormEvent) => {
105
+ e.preventDefault();
106
+ const trimmedTitle = title.trim();
107
+ if (trimmedTitle && !isCreating) {
108
+ onCreate(trimmedTitle);
109
+ }
110
+ },
111
+ [title, isCreating, onCreate]
112
+ );
113
+
114
+ const handleOverlayClick = useCallback(
115
+ (e: React.MouseEvent) => {
116
+ if (e.target === e.currentTarget && !isCreating) {
117
+ onClose();
118
+ }
119
+ },
120
+ [onClose, isCreating]
121
+ );
122
+
123
+ const slug = generateSlug(title);
124
+ const isValid = title.trim().length > 0;
125
+
126
+ if (!isOpen) return null;
127
+
128
+ return (
129
+ <div
130
+ className="wn-create-modal-overlay"
131
+ onClick={handleOverlayClick}
132
+ role="presentation"
133
+ >
134
+ <div
135
+ ref={containerRef}
136
+ className="wn-create-modal"
137
+ role="dialog"
138
+ aria-modal="true"
139
+ aria-labelledby="create-modal-title"
140
+ >
141
+ {/* Header */}
142
+ <div className="wn-create-modal-header">
143
+ <div className="wn-create-modal-header-content">
144
+ <FileText size={18} className="wn-create-modal-icon" />
145
+ <h2 id="create-modal-title" className="wn-create-modal-title">
146
+ Create New Content
147
+ </h2>
148
+ </div>
149
+ <button
150
+ className="wn-create-modal-close"
151
+ onClick={onClose}
152
+ disabled={isCreating}
153
+ title="Close (Esc)"
154
+ aria-label="Close modal"
155
+ >
156
+ <X size={16} />
157
+ </button>
158
+ </div>
159
+
160
+ {/* Body */}
161
+ <form onSubmit={handleSubmit} className="wn-create-modal-body">
162
+ <div className="wn-create-modal-collection">
163
+ Creating in{" "}
164
+ <span className="wn-create-modal-collection-name">
165
+ {collectionName}
166
+ </span>
167
+ </div>
168
+
169
+ <div className="wn-create-modal-field">
170
+ <label htmlFor="content-title" className="wn-create-modal-label">
171
+ Title
172
+ </label>
173
+ <input
174
+ ref={inputRef}
175
+ id="content-title"
176
+ type="text"
177
+ className="wn-create-modal-input"
178
+ placeholder="Enter content title..."
179
+ value={title}
180
+ onChange={(e) => setTitle(e.target.value)}
181
+ disabled={isCreating}
182
+ autoComplete="off"
183
+ spellCheck="false"
184
+ />
185
+ </div>
186
+
187
+ {slug && (
188
+ <div className="wn-create-modal-slug">
189
+ <span className="wn-create-modal-slug-label">Slug:</span>
190
+ <code className="wn-create-modal-slug-value">{slug}</code>
191
+ </div>
192
+ )}
193
+
194
+ {/* Footer */}
195
+ <div className="wn-create-modal-footer">
196
+ <button
197
+ type="button"
198
+ className="wn-create-modal-btn wn-create-modal-btn--secondary"
199
+ onClick={onClose}
200
+ disabled={isCreating}
201
+ >
202
+ Cancel
203
+ </button>
204
+ <button
205
+ type="submit"
206
+ className="wn-create-modal-btn wn-create-modal-btn--primary"
207
+ disabled={!isValid || isCreating}
208
+ >
209
+ {isCreating ? "Creating..." : "Create"}
210
+ </button>
211
+ </div>
212
+ </form>
213
+ </div>
214
+ </div>
215
+ );
216
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview CreateContentModal component exports
3
+ *
4
+ * @module @writenex/astro/client/components/CreateContentModal
5
+ */
6
+
7
+ export { CreateContentModal } from "./CreateContentModal";