@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,239 @@
1
+ /**
2
+ * @fileoverview KeyboardShortcuts modal styles
3
+ *
4
+ * Modal styling for keyboard shortcuts help panel.
5
+ */
6
+
7
+ /* ============================================================================
8
+ MODAL OVERLAY
9
+ ============================================================================ */
10
+
11
+ .wn-shortcuts-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-shortcuts-fadeIn var(--wn-transition-fast) ease-out;
20
+ }
21
+
22
+ @keyframes wn-shortcuts-fadeIn {
23
+ from {
24
+ opacity: 0;
25
+ }
26
+ to {
27
+ opacity: 1;
28
+ }
29
+ }
30
+
31
+ /* ============================================================================
32
+ MODAL CONTAINER
33
+ ============================================================================ */
34
+
35
+ .wn-shortcuts-modal {
36
+ width: 100%;
37
+ max-width: var(--wn-modal-sm);
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-shortcuts-zoomIn var(--wn-transition-fast) ease-out;
43
+ }
44
+
45
+ .wn-shortcuts-modal--large {
46
+ max-width: 32rem;
47
+ }
48
+
49
+ @keyframes wn-shortcuts-zoomIn {
50
+ from {
51
+ opacity: 0;
52
+ transform: scale(0.95);
53
+ }
54
+ to {
55
+ opacity: 1;
56
+ transform: scale(1);
57
+ }
58
+ }
59
+
60
+ /* ============================================================================
61
+ MODAL HEADER
62
+ ============================================================================ */
63
+
64
+ .wn-shortcuts-header {
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: space-between;
68
+ padding: var(--wn-space-4) var(--wn-space-5);
69
+ border-bottom: 1px solid var(--wn-zinc-700);
70
+ }
71
+
72
+ .wn-shortcuts-title {
73
+ font-size: var(--wn-font-base);
74
+ font-weight: 600;
75
+ color: var(--wn-zinc-50);
76
+ margin: 0;
77
+ }
78
+
79
+ .wn-shortcuts-close {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ width: var(--wn-icon-btn-md);
84
+ height: var(--wn-icon-btn-md);
85
+ padding: 0;
86
+ border: none;
87
+ border-radius: var(--wn-radius-md);
88
+ background-color: transparent;
89
+ color: var(--wn-zinc-400);
90
+ cursor: pointer;
91
+ transition:
92
+ background-color var(--wn-transition-fast),
93
+ color var(--wn-transition-fast);
94
+ }
95
+
96
+ .wn-shortcuts-close:hover {
97
+ background-color: var(--wn-overlay-10);
98
+ color: var(--wn-zinc-50);
99
+ }
100
+
101
+ /* ============================================================================
102
+ SHORTCUTS LIST
103
+ ============================================================================ */
104
+
105
+ .wn-shortcuts-list {
106
+ max-height: 60vh;
107
+ overflow-y: auto;
108
+ padding: var(--wn-space-3);
109
+ }
110
+
111
+ .wn-shortcuts-section {
112
+ margin-bottom: var(--wn-space-5);
113
+ }
114
+
115
+ .wn-shortcuts-section:last-child {
116
+ margin-bottom: 0;
117
+ }
118
+
119
+ .wn-shortcuts-category {
120
+ font-size: var(--wn-font-xs);
121
+ font-weight: 600;
122
+ text-transform: uppercase;
123
+ letter-spacing: 0.05em;
124
+ color: var(--wn-zinc-500);
125
+ margin: 0 0 var(--wn-space-3) var(--wn-space-4);
126
+ }
127
+
128
+ .wn-shortcuts-item {
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: space-between;
132
+ padding: var(--wn-space-3) var(--wn-space-4);
133
+ border-radius: var(--wn-radius-md);
134
+ transition: background-color var(--wn-transition-fast);
135
+ }
136
+
137
+ .wn-shortcuts-item:hover {
138
+ background-color: var(--wn-zinc-800);
139
+ }
140
+
141
+ .wn-shortcuts-item--disabled {
142
+ opacity: 0.4;
143
+ }
144
+
145
+ .wn-shortcuts-item--disabled:hover {
146
+ background-color: transparent;
147
+ }
148
+
149
+ .wn-shortcuts-label {
150
+ font-size: var(--wn-font-base);
151
+ color: var(--wn-zinc-400);
152
+ }
153
+
154
+ .wn-shortcuts-key {
155
+ padding: var(--wn-space-1) var(--wn-space-3);
156
+ border-radius: var(--wn-radius-sm);
157
+ border: 1px solid var(--wn-zinc-700);
158
+ background-color: var(--wn-zinc-950);
159
+ font-family: ui-monospace, monospace;
160
+ font-size: var(--wn-font-xs);
161
+ color: var(--wn-zinc-50);
162
+ }
163
+
164
+ /* ============================================================================
165
+ MODAL FOOTER
166
+ ============================================================================ */
167
+
168
+ .wn-shortcuts-footer {
169
+ padding: var(--wn-space-4) var(--wn-space-5);
170
+ border-top: 1px solid var(--wn-zinc-700);
171
+ text-align: center;
172
+ font-size: var(--wn-font-xs);
173
+ color: var(--wn-zinc-600);
174
+ }
175
+
176
+ .wn-shortcuts-footer kbd {
177
+ padding: var(--wn-space-1) var(--wn-space-2);
178
+ border-radius: var(--wn-radius-sm);
179
+ background-color: var(--wn-zinc-800);
180
+ color: var(--wn-zinc-400);
181
+ }
182
+
183
+ /* ============================================================================
184
+ LIGHT MODE OVERRIDES
185
+ ============================================================================ */
186
+
187
+ .wn-light .wn-shortcuts-overlay {
188
+ background-color: var(--wn-backdrop-light);
189
+ }
190
+
191
+ .wn-light .wn-shortcuts-modal {
192
+ border-color: var(--wn-zinc-200);
193
+ background-color: #fff;
194
+ }
195
+
196
+ .wn-light .wn-shortcuts-header {
197
+ border-bottom-color: var(--wn-zinc-200);
198
+ }
199
+
200
+ .wn-light .wn-shortcuts-title {
201
+ color: var(--wn-zinc-900);
202
+ }
203
+
204
+ .wn-light .wn-shortcuts-close {
205
+ color: var(--wn-zinc-500);
206
+ }
207
+
208
+ .wn-light .wn-shortcuts-close:hover {
209
+ background-color: var(--wn-overlay-light-5);
210
+ color: var(--wn-zinc-900);
211
+ }
212
+
213
+ .wn-light .wn-shortcuts-category {
214
+ color: var(--wn-zinc-400);
215
+ }
216
+
217
+ .wn-light .wn-shortcuts-item:hover {
218
+ background-color: var(--wn-zinc-100);
219
+ }
220
+
221
+ .wn-light .wn-shortcuts-label {
222
+ color: var(--wn-zinc-600);
223
+ }
224
+
225
+ .wn-light .wn-shortcuts-key {
226
+ border-color: var(--wn-zinc-200);
227
+ background-color: var(--wn-zinc-50);
228
+ color: var(--wn-zinc-900);
229
+ }
230
+
231
+ .wn-light .wn-shortcuts-footer {
232
+ border-top-color: var(--wn-zinc-200);
233
+ color: var(--wn-zinc-400);
234
+ }
235
+
236
+ .wn-light .wn-shortcuts-footer kbd {
237
+ background-color: var(--wn-zinc-100);
238
+ color: var(--wn-zinc-600);
239
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @fileoverview Keyboard shortcuts help modal component
3
+ *
4
+ * Displays a modal with all available keyboard shortcuts including
5
+ * application shortcuts and built-in editor formatting shortcuts.
6
+ * Includes focus trap for accessibility compliance.
7
+ *
8
+ * @module @writenex/astro/client/components/KeyboardShortcuts
9
+ */
10
+
11
+ import { useEffect, useRef } from "react";
12
+ import { X } from "lucide-react";
13
+ import { useFocusTrap } from "../../hooks/useFocusTrap";
14
+ import {
15
+ formatShortcut,
16
+ type ShortcutDefinition,
17
+ } from "../../hooks/useKeyboardShortcuts";
18
+ import "./KeyboardShortcuts.css";
19
+
20
+ /**
21
+ * Built-in editor shortcuts from MDXEditor/Lexical.
22
+ * These are always available when editing content.
23
+ */
24
+ const EDITOR_SHORTCUTS: {
25
+ category: string;
26
+ shortcuts: { label: string; keys: string }[];
27
+ }[] = [
28
+ {
29
+ category: "Formatting",
30
+ shortcuts: [
31
+ { label: "Bold", keys: "Ctrl+B" },
32
+ { label: "Italic", keys: "Ctrl+I" },
33
+ { label: "Underline", keys: "Ctrl+U" },
34
+ ],
35
+ },
36
+ {
37
+ category: "Actions",
38
+ shortcuts: [
39
+ { label: "Undo", keys: "Ctrl+Z" },
40
+ { label: "Redo", keys: "Ctrl+Shift+Z" },
41
+ ],
42
+ },
43
+ ];
44
+
45
+ /**
46
+ * Props for ShortcutsHelpModal component
47
+ */
48
+ interface ShortcutsHelpModalProps {
49
+ /** List of application shortcuts to display */
50
+ shortcuts: ShortcutDefinition[];
51
+ /** Callback to close the modal */
52
+ onClose: () => void;
53
+ }
54
+
55
+ /**
56
+ * Keyboard shortcuts help modal component
57
+ *
58
+ * @component
59
+ * @example
60
+ * ```tsx
61
+ * {showHelp && (
62
+ * <ShortcutsHelpModal shortcuts={shortcuts} onClose={closeHelp} />
63
+ * )}
64
+ * ```
65
+ */
66
+ export function ShortcutsHelpModal({
67
+ shortcuts,
68
+ onClose,
69
+ }: ShortcutsHelpModalProps): React.ReactElement {
70
+ const triggerRef = useRef<HTMLElement | null>(null);
71
+
72
+ // Store the trigger element when modal mounts
73
+ useEffect(() => {
74
+ triggerRef.current = document.activeElement as HTMLElement;
75
+ }, []);
76
+
77
+ // Focus trap for accessibility
78
+ const { containerRef } = useFocusTrap({
79
+ enabled: true,
80
+ onEscape: onClose,
81
+ returnFocusTo: triggerRef.current,
82
+ });
83
+
84
+ const handleOverlayClick = (e: React.MouseEvent) => {
85
+ if (e.target === e.currentTarget) onClose();
86
+ };
87
+
88
+ return (
89
+ <div className="wn-shortcuts-overlay" onClick={handleOverlayClick}>
90
+ <div
91
+ ref={containerRef}
92
+ className="wn-shortcuts-modal wn-shortcuts-modal--large"
93
+ role="dialog"
94
+ aria-modal="true"
95
+ aria-labelledby="shortcuts-title"
96
+ >
97
+ {/* Header */}
98
+ <div className="wn-shortcuts-header">
99
+ <h2 id="shortcuts-title" className="wn-shortcuts-title">
100
+ Keyboard Shortcuts
101
+ </h2>
102
+ <button
103
+ className="wn-shortcuts-close"
104
+ onClick={onClose}
105
+ title="Close (Esc)"
106
+ aria-label="Close shortcuts help"
107
+ >
108
+ <X size={16} />
109
+ </button>
110
+ </div>
111
+
112
+ {/* Shortcuts List */}
113
+ <div className="wn-shortcuts-list">
114
+ {/* Application Shortcuts */}
115
+ <div className="wn-shortcuts-section">
116
+ <h3 className="wn-shortcuts-category">Application</h3>
117
+ {shortcuts.map((shortcut) => (
118
+ <div
119
+ key={shortcut.key}
120
+ className={`wn-shortcuts-item ${shortcut.enabled === false ? "wn-shortcuts-item--disabled" : ""}`}
121
+ >
122
+ <span className="wn-shortcuts-label">{shortcut.label}</span>
123
+ <kbd className="wn-shortcuts-key">
124
+ {formatShortcut(shortcut)}
125
+ </kbd>
126
+ </div>
127
+ ))}
128
+ </div>
129
+
130
+ {/* Editor Shortcuts */}
131
+ {EDITOR_SHORTCUTS.map((group) => (
132
+ <div key={group.category} className="wn-shortcuts-section">
133
+ <h3 className="wn-shortcuts-category">{group.category}</h3>
134
+ {group.shortcuts.map((shortcut) => (
135
+ <div key={shortcut.label} className="wn-shortcuts-item">
136
+ <span className="wn-shortcuts-label">{shortcut.label}</span>
137
+ <kbd className="wn-shortcuts-key">{shortcut.keys}</kbd>
138
+ </div>
139
+ ))}
140
+ </div>
141
+ ))}
142
+ </div>
143
+
144
+ {/* Footer */}
145
+ <div className="wn-shortcuts-footer">
146
+ Press <kbd>Ctrl+/</kbd> to toggle this help
147
+ </div>
148
+ </div>
149
+ </div>
150
+ );
151
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview KeyboardShortcuts component exports
3
+ * @module @writenex/astro/client/components/KeyboardShortcuts
4
+ */
5
+
6
+ export { ShortcutsHelpModal } from "./KeyboardShortcuts";
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @fileoverview Lazy-loaded Editor component
3
+ *
4
+ * This module provides a lazy-loaded version of the MDXEditor component
5
+ * to improve initial bundle size and load time. The actual editor is
6
+ * loaded on-demand when the component mounts.
7
+ *
8
+ * @module @writenex/astro/client/components/LazyEditor
9
+ */
10
+
11
+ import { lazy, Suspense, memo } from "react";
12
+ import { EditorLoading } from "./Editor/Editor";
13
+
14
+ /**
15
+ * Props for LazyEditor - same as Editor
16
+ */
17
+ interface LazyEditorProps {
18
+ /** Initial markdown content */
19
+ initialContent: string;
20
+ /** Callback when content changes */
21
+ onChange: (markdown: string) => void;
22
+ /** Whether the editor is read-only */
23
+ readOnly?: boolean;
24
+ /** Placeholder text when empty */
25
+ placeholder?: string;
26
+ /** Handler for image uploads. Returns the image URL/path on success. */
27
+ onImageUpload?: (file: File) => Promise<string | null>;
28
+ /** Base path for API requests */
29
+ basePath?: string;
30
+ /** Current collection name (for image URL resolution) */
31
+ collection?: string;
32
+ /** Current content ID (for image URL resolution) */
33
+ contentId?: string;
34
+ /** Search query for highlighting */
35
+ searchQuery?: string;
36
+ /** Current search match index (1-based) */
37
+ searchActiveIndex?: number;
38
+ }
39
+
40
+ /**
41
+ * Lazy-loaded Editor component
42
+ *
43
+ * Uses React.lazy to defer loading the MDXEditor bundle until needed.
44
+ */
45
+ const LazyEditorComponent = lazy(() =>
46
+ import("./Editor/Editor").then((mod) => ({ default: mod.Editor }))
47
+ );
48
+
49
+ /**
50
+ * Editor wrapper with Suspense fallback
51
+ *
52
+ * This component wraps the lazy-loaded Editor with a Suspense boundary
53
+ * that shows a loading indicator while the editor bundle loads.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * <LazyEditor
58
+ * initialContent={markdown}
59
+ * onChange={handleChange}
60
+ * placeholder="Start writing..."
61
+ * />
62
+ * ```
63
+ */
64
+ export const LazyEditor = memo(function LazyEditor(
65
+ props: LazyEditorProps
66
+ ): React.ReactElement {
67
+ return (
68
+ <Suspense fallback={<EditorLoading />}>
69
+ <LazyEditorComponent {...props} />
70
+ </Suspense>
71
+ );
72
+ });
73
+
74
+ // Re-export EditorEmpty and EditorLoading for convenience
75
+ export { EditorEmpty, EditorLoading } from "./Editor/Editor";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @fileoverview Live Region component styles
3
+ *
4
+ * Styles for the live region component that is visually hidden
5
+ * but accessible to screen readers.
6
+ */
7
+
8
+ /* Live region - visually hidden but accessible */
9
+ .wn-live-region {
10
+ position: absolute;
11
+ width: 1px;
12
+ height: 1px;
13
+ padding: 0;
14
+ margin: -1px;
15
+ overflow: hidden;
16
+ clip: rect(0, 0, 0, 0);
17
+ white-space: nowrap;
18
+ border: 0;
19
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @fileoverview Live Region component for screen reader announcements
3
+ *
4
+ * A global live region component that announces dynamic content changes
5
+ * to screen readers. Integrates with the useAnnounce hook to provide
6
+ * a centralized announcement system.
7
+ *
8
+ * @module @writenex/astro/client/components/LiveRegion
9
+ * @see {@link useAnnounce} - Hook for triggering announcements
10
+ */
11
+
12
+ import type { AnnouncePoliteness } from "../../hooks/useAnnounce";
13
+ import "./LiveRegion.css";
14
+
15
+ /**
16
+ * Props for the LiveRegion component
17
+ */
18
+ export interface LiveRegionProps {
19
+ /** Current message to announce */
20
+ message: string;
21
+ /** Politeness level - polite waits, assertive interrupts */
22
+ politeness?: AnnouncePoliteness;
23
+ }
24
+
25
+ /**
26
+ * Live region component for screen reader announcements
27
+ *
28
+ * Renders an ARIA live region that announces messages to screen readers.
29
+ * The component is visually hidden but accessible to assistive technology.
30
+ *
31
+ * @component
32
+ * @example
33
+ * ```tsx
34
+ * function App() {
35
+ * const { currentMessage, currentPoliteness } = useAnnounce();
36
+ *
37
+ * return (
38
+ * <>
39
+ * <LiveRegion message={currentMessage} politeness={currentPoliteness} />
40
+ * {/* rest of app *\/}
41
+ * </>
42
+ * );
43
+ * }
44
+ * ```
45
+ */
46
+ export function LiveRegion({
47
+ message,
48
+ politeness = "polite",
49
+ }: LiveRegionProps): React.ReactElement {
50
+ return (
51
+ <div
52
+ role="status"
53
+ aria-live={politeness}
54
+ aria-atomic="true"
55
+ className="wn-live-region"
56
+ >
57
+ {message}
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview LiveRegion component exports
3
+ * @module @writenex/astro/client/components/LiveRegion
4
+ */
5
+
6
+ export { LiveRegion } from "./LiveRegion";
7
+ export type { LiveRegionProps } from "./LiveRegion";