@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,226 @@
1
+ /**
2
+ * @fileoverview File watcher for detecting external changes
3
+ *
4
+ * This module provides file watching capabilities to detect when
5
+ * content files are modified outside of the Writenex editor
6
+ * (e.g., in VS Code or another editor).
7
+ *
8
+ * @module @writenex/astro/filesystem/watcher
9
+ */
10
+
11
+ import { watch, type FSWatcher } from "chokidar";
12
+ import { stat } from "node:fs/promises";
13
+ import { join } from "node:path";
14
+
15
+ /**
16
+ * File change event types
17
+ */
18
+ export type FileChangeType = "add" | "change" | "unlink";
19
+
20
+ /**
21
+ * File change event
22
+ */
23
+ export interface FileChangeEvent {
24
+ type: FileChangeType;
25
+ path: string;
26
+ collection: string;
27
+ }
28
+
29
+ /**
30
+ * Watcher options
31
+ */
32
+ export interface WatcherOptions {
33
+ /** Callback when a file changes */
34
+ onChange?: (event: FileChangeEvent) => void;
35
+ /** Debounce delay in milliseconds */
36
+ debounceMs?: number;
37
+ /** Patterns to ignore */
38
+ ignored?: string[];
39
+ }
40
+
41
+ /**
42
+ * Content file watcher
43
+ *
44
+ * Watches the src/content directory for changes and emits events
45
+ * when files are added, modified, or deleted.
46
+ */
47
+ export class ContentWatcher {
48
+ private watcher: FSWatcher | null = null;
49
+ private projectRoot: string;
50
+ private contentDir: string;
51
+ private options: WatcherOptions;
52
+ private debounceTimers: Map<string, NodeJS.Timeout> = new Map();
53
+
54
+ constructor(
55
+ projectRoot: string,
56
+ contentDir: string = "src/content",
57
+ options: WatcherOptions = {}
58
+ ) {
59
+ this.projectRoot = projectRoot;
60
+ this.contentDir = contentDir;
61
+ this.options = {
62
+ debounceMs: 100,
63
+ ignored: ["**/node_modules/**", "**/.git/**"],
64
+ ...options,
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Start watching for file changes
70
+ */
71
+ start(): void {
72
+ if (this.watcher) {
73
+ return; // Already watching
74
+ }
75
+
76
+ const watchPath = join(this.projectRoot, this.contentDir);
77
+
78
+ this.watcher = watch(watchPath, {
79
+ ignored: this.options.ignored,
80
+ persistent: true,
81
+ ignoreInitial: true,
82
+ awaitWriteFinish: {
83
+ stabilityThreshold: 100,
84
+ pollInterval: 50,
85
+ },
86
+ });
87
+
88
+ this.watcher
89
+ .on("add", (path) => this.handleChange("add", path))
90
+ .on("change", (path) => this.handleChange("change", path))
91
+ .on("unlink", (path) => this.handleChange("unlink", path))
92
+ .on("error", (error) => {
93
+ console.error("[writenex] Watcher error:", error);
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Stop watching for file changes
99
+ */
100
+ async stop(): Promise<void> {
101
+ if (this.watcher) {
102
+ await this.watcher.close();
103
+ this.watcher = null;
104
+ }
105
+
106
+ // Clear all debounce timers
107
+ for (const timer of this.debounceTimers.values()) {
108
+ clearTimeout(timer);
109
+ }
110
+ this.debounceTimers.clear();
111
+ }
112
+
113
+ /**
114
+ * Handle a file change event
115
+ */
116
+ private handleChange(type: FileChangeType, filePath: string): void {
117
+ // Only handle markdown files
118
+ if (!filePath.endsWith(".md") && !filePath.endsWith(".mdx")) {
119
+ return;
120
+ }
121
+
122
+ // Debounce rapid changes
123
+ const existingTimer = this.debounceTimers.get(filePath);
124
+ if (existingTimer) {
125
+ clearTimeout(existingTimer);
126
+ }
127
+
128
+ const timer = setTimeout(() => {
129
+ this.debounceTimers.delete(filePath);
130
+ this.emitChange(type, filePath);
131
+ }, this.options.debounceMs);
132
+
133
+ this.debounceTimers.set(filePath, timer);
134
+ }
135
+
136
+ /**
137
+ * Emit a file change event
138
+ */
139
+ private emitChange(type: FileChangeType, filePath: string): void {
140
+ if (!this.options.onChange) {
141
+ return;
142
+ }
143
+
144
+ // Extract collection name from path
145
+ const contentPath = join(this.projectRoot, this.contentDir);
146
+ const relativePath = filePath
147
+ .replace(contentPath, "")
148
+ .replace(/^[/\\]/, "");
149
+ const parts = relativePath.split(/[/\\]/);
150
+ const collection = parts[0] ?? "";
151
+
152
+ this.options.onChange({
153
+ type,
154
+ path: filePath,
155
+ collection,
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Check if the watcher is running
161
+ */
162
+ isWatching(): boolean {
163
+ return this.watcher !== null;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Track file modification times for conflict detection
169
+ */
170
+ export class FileModificationTracker {
171
+ private mtimes: Map<string, number> = new Map();
172
+
173
+ /**
174
+ * Record the current modification time of a file
175
+ */
176
+ async track(filePath: string): Promise<void> {
177
+ try {
178
+ const stats = await stat(filePath);
179
+ this.mtimes.set(filePath, stats.mtimeMs);
180
+ } catch {
181
+ // File might not exist yet
182
+ this.mtimes.delete(filePath);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Check if a file has been modified externally
188
+ */
189
+ async hasExternalChanges(filePath: string): Promise<boolean> {
190
+ const lastKnown = this.mtimes.get(filePath);
191
+ if (lastKnown === undefined) {
192
+ return false; // Not tracked, assume no changes
193
+ }
194
+
195
+ try {
196
+ const stats = await stat(filePath);
197
+ return stats.mtimeMs > lastKnown;
198
+ } catch {
199
+ return true; // File might have been deleted
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Clear tracking for a file
205
+ */
206
+ untrack(filePath: string): void {
207
+ this.mtimes.delete(filePath);
208
+ }
209
+
210
+ /**
211
+ * Clear all tracking
212
+ */
213
+ clear(): void {
214
+ this.mtimes.clear();
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Create a content watcher instance
220
+ */
221
+ export function createContentWatcher(
222
+ projectRoot: string,
223
+ options?: WatcherOptions
224
+ ): ContentWatcher {
225
+ return new ContentWatcher(projectRoot, "src/content", options);
226
+ }