@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,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
|
+
}
|