@nuasite/cms 0.19.1 → 0.20.2

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 (39) hide show
  1. package/dist/editor.js +12615 -12689
  2. package/package.json +3 -3
  3. package/src/build-processor.ts +4 -4
  4. package/src/dev-middleware.ts +185 -189
  5. package/src/editor/api.ts +0 -251
  6. package/src/editor/components/fields.tsx +6 -6
  7. package/src/editor/components/markdown-editor-overlay.tsx +46 -70
  8. package/src/editor/components/markdown-inline-editor.tsx +34 -165
  9. package/src/editor/components/mdx-block-view.tsx +351 -47
  10. package/src/editor/components/mdx-component-picker.tsx +35 -11
  11. package/src/editor/components/media-library.tsx +1 -15
  12. package/src/editor/components/modal-shell.tsx +1 -1
  13. package/src/editor/components/toolbar.tsx +0 -75
  14. package/src/editor/constants.ts +0 -4
  15. package/src/editor/editor.ts +2 -192
  16. package/src/editor/hooks/index.ts +0 -3
  17. package/src/editor/hooks/useBlockEditorHandlers.ts +1 -8
  18. package/src/editor/hooks/useTooltipState.ts +1 -2
  19. package/src/editor/index.tsx +2 -18
  20. package/src/editor/milkdown-mdx-plugin.tsx +116 -19
  21. package/src/editor/milkdown-utils.ts +174 -0
  22. package/src/editor/post-message.ts +0 -6
  23. package/src/editor/signals.ts +0 -183
  24. package/src/editor/styles.css +0 -108
  25. package/src/editor/types.ts +0 -76
  26. package/src/html-processor.ts +9 -7
  27. package/src/source-finder/cache.ts +47 -0
  28. package/src/source-finder/collection-finder.ts +181 -0
  29. package/src/source-finder/index.ts +5 -2
  30. package/src/source-finder/search-index.ts +79 -0
  31. package/src/source-finder/snippet-utils.ts +36 -61
  32. package/src/types.ts +0 -4
  33. package/src/utils.ts +10 -0
  34. package/src/vite-plugin.ts +24 -4
  35. package/src/editor/ai.ts +0 -185
  36. package/src/editor/components/ai-chat.tsx +0 -631
  37. package/src/editor/components/ai-tooltip.tsx +0 -180
  38. package/src/editor/components/mdx-props-editor.tsx +0 -94
  39. package/src/editor/hooks/useAIHandlers.ts +0 -345
@@ -4,11 +4,8 @@ import { fetchManifest, getMarkdownContent } from './api'
4
4
  import type { ToastMessage, ToastType } from './components/toast/types'
5
5
  import { getConfig } from './config'
6
6
  import type {
7
- AIState,
8
- AIStatusType,
9
7
  AttributeEditorState,
10
8
  BlockEditorState,
11
- ChatMessage,
12
9
  CmsConfig,
13
10
  CmsManifest,
14
11
  CmsSettings,
@@ -20,13 +17,10 @@ import type {
20
17
  ConfirmDialogState,
21
18
  CreatePageState,
22
19
  DeletePageState,
23
- DeploymentState,
24
- DeploymentStatusType,
25
20
  EditorState,
26
21
  FieldDefinition,
27
22
  MarkdownEditorState,
28
23
  MarkdownPageEntry,
29
- MdxPropsEditorState,
30
24
  MediaItem,
31
25
  MediaLibraryState,
32
26
  PendingAttributeChange,
@@ -104,21 +98,6 @@ function createDirtyTracking<T extends { isDirty: boolean }>(
104
98
  }
105
99
 
106
100
  // Initial state factories
107
- function createInitialAIState(): AIState {
108
- return {
109
- isPromptVisible: false,
110
- isProcessing: false,
111
- targetElementId: null,
112
- streamingContent: null,
113
- error: null,
114
- isChatOpen: false,
115
- chatMessages: [],
116
- chatContextElementId: null,
117
- currentStatus: null,
118
- statusMessage: null,
119
- }
120
- }
121
-
122
101
  function createInitialBlockEditorState(): BlockEditorState {
123
102
  return {
124
103
  isOpen: false,
@@ -181,15 +160,6 @@ function createInitialCollectionsBrowserState(): CollectionsBrowserState {
181
160
  }
182
161
  }
183
162
 
184
- function createInitialDeploymentState(): DeploymentState {
185
- return {
186
- status: null,
187
- lastDeployedAt: null,
188
- isPolling: false,
189
- error: null,
190
- }
191
- }
192
-
193
163
  function createInitialColorEditorState(): ColorEditorState {
194
164
  return {
195
165
  isOpen: false,
@@ -297,22 +267,6 @@ const _pendingImageChangesHelpers = createMapHelpers(pendingImageChanges)
297
267
  const _pendingColorChangesHelpers = createMapHelpers(pendingColorChanges)
298
268
  const _pendingBgImageChangesHelpers = createMapHelpers(pendingBgImageChanges)
299
269
 
300
- // ============================================================================
301
- // AI State Signals
302
- // ============================================================================
303
-
304
- export const aiState = signal<AIState>(createInitialAIState())
305
-
306
- // Convenience computed signals for AI state
307
- export const isAIProcessing = computed(() => aiState.value.isProcessing)
308
- export const isChatOpen = computed(() => aiState.value.isChatOpen)
309
- export const chatMessages = computed(() => aiState.value.chatMessages)
310
- export const chatContextElementId = computed(
311
- () => aiState.value.chatContextElementId,
312
- )
313
- export const currentStatus = computed(() => aiState.value.currentStatus)
314
- export const statusMessage = computed(() => aiState.value.statusMessage)
315
-
316
270
  // ============================================================================
317
271
  // Block Editor State Signals
318
272
  // ============================================================================
@@ -346,24 +300,8 @@ export const isMarkdownPreview = signal(false)
346
300
  // MDX Component Block State Signals
347
301
  // ============================================================================
348
302
 
349
- export const mdxPropsEditorState = signal<MdxPropsEditorState>({
350
- isOpen: false,
351
- nodePos: null,
352
- componentName: null,
353
- props: {},
354
- cursorPos: null,
355
- })
356
-
357
303
  export const mdxComponentPickerOpen = signal(false)
358
304
 
359
- export function openMdxPropsEditor(nodePos: number, componentName: string, props: Record<string, string>, cursorPos: { x: number; y: number }): void {
360
- mdxPropsEditorState.value = { isOpen: true, nodePos, componentName, props, cursorPos }
361
- }
362
-
363
- export function closeMdxPropsEditor(): void {
364
- mdxPropsEditorState.value = { isOpen: false, nodePos: null, componentName: null, props: {}, cursorPos: null }
365
- }
366
-
367
305
  // ============================================================================
368
306
  // Reference Picker State Signals
369
307
  // ============================================================================
@@ -467,19 +405,6 @@ export const collectionsBrowserState = signal<CollectionsBrowserState>(
467
405
  export const isCollectionsBrowserOpen = computed(() => collectionsBrowserState.value.isOpen)
468
406
  export const selectedBrowserCollection = computed(() => collectionsBrowserState.value.selectedCollection)
469
407
 
470
- // ============================================================================
471
- // Deployment State Signals
472
- // ============================================================================
473
-
474
- export const deploymentState = signal<DeploymentState>(
475
- createInitialDeploymentState(),
476
- )
477
-
478
- // Convenience computed signals for deployment
479
- export const deploymentStatus = computed(() => deploymentState.value.status)
480
- export const isDeploymentPolling = computed(() => deploymentState.value.isPolling)
481
- export const lastDeployedAt = computed(() => deploymentState.value.lastDeployedAt)
482
-
483
408
  // ============================================================================
484
409
  // Redirect Countdown State
485
410
  // ============================================================================
@@ -788,83 +713,6 @@ export const deletePendingAttributeChange = _pendingAttributeChangesHelpers.dele
788
713
  export const clearPendingAttributeChanges = _pendingAttributeChangesHelpers.clear
789
714
  export const getPendingAttributeChange = _pendingAttributeChangesHelpers.get
790
715
 
791
- // ============================================================================
792
- // AI State Mutations
793
- // ============================================================================
794
-
795
- export function setAIPromptVisible(visible: boolean): void {
796
- aiState.value = { ...aiState.value, isPromptVisible: visible }
797
- }
798
-
799
- export function setAIProcessing(processing: boolean): void {
800
- aiState.value = { ...aiState.value, isProcessing: processing }
801
- }
802
-
803
- export function setAIStatus(status: AIStatusType, message?: string): void {
804
- aiState.value = {
805
- ...aiState.value,
806
- currentStatus: status,
807
- statusMessage: message ?? null,
808
- }
809
- }
810
-
811
- export function clearAIStatus(): void {
812
- aiState.value = {
813
- ...aiState.value,
814
- currentStatus: null,
815
- statusMessage: null,
816
- }
817
- }
818
-
819
- export function setAITargetElement(elementId: string | null): void {
820
- aiState.value = { ...aiState.value, targetElementId: elementId }
821
- }
822
-
823
- export function setAIStreamingContent(content: string | null): void {
824
- aiState.value = { ...aiState.value, streamingContent: content }
825
- }
826
-
827
- export function setAIError(error: string | null): void {
828
- aiState.value = { ...aiState.value, error: error }
829
- }
830
-
831
- export function resetAIState(): void {
832
- aiState.value = createInitialAIState()
833
- }
834
-
835
- export function setAIChatOpen(open: boolean): void {
836
- aiState.value = { ...aiState.value, isChatOpen: open }
837
- }
838
-
839
- export function addChatMessage(message: ChatMessage): void {
840
- aiState.value = {
841
- ...aiState.value,
842
- chatMessages: [...aiState.value.chatMessages, message],
843
- }
844
- }
845
-
846
- export function setChatMessages(messages: ChatMessage[]): void {
847
- aiState.value = {
848
- ...aiState.value,
849
- chatMessages: messages,
850
- }
851
- }
852
-
853
- export function updateChatMessage(messageId: string, content: string): void {
854
- aiState.value = {
855
- ...aiState.value,
856
- chatMessages: aiState.value.chatMessages.map((msg) => msg.id === messageId ? { ...msg, content } : msg),
857
- }
858
- }
859
-
860
- export function setChatContextElement(elementId: string | null): void {
861
- aiState.value = { ...aiState.value, chatContextElementId: elementId }
862
- }
863
-
864
- export function clearChatMessages(): void {
865
- aiState.value = { ...aiState.value, chatMessages: [] }
866
- }
867
-
868
716
  // ============================================================================
869
717
  // Block Editor State Mutations
870
718
  // ============================================================================
@@ -1266,34 +1114,6 @@ export async function openMarkdownEditorForEntry(
1266
1114
  }
1267
1115
  }
1268
1116
 
1269
- // ============================================================================
1270
- // Deployment State Mutations
1271
- // ============================================================================
1272
-
1273
- export function setDeploymentStatus(status: DeploymentStatusType | null): void {
1274
- deploymentState.value = { ...deploymentState.value, status }
1275
- }
1276
-
1277
- export function setDeploymentPolling(isPolling: boolean): void {
1278
- deploymentState.value = { ...deploymentState.value, isPolling }
1279
- }
1280
-
1281
- export function setLastDeployedAt(timestamp: string | null): void {
1282
- deploymentState.value = { ...deploymentState.value, lastDeployedAt: timestamp }
1283
- }
1284
-
1285
- export function setDeploymentError(error: string | null): void {
1286
- deploymentState.value = { ...deploymentState.value, error }
1287
- }
1288
-
1289
- export function updateDeploymentState(update: Partial<DeploymentState>): void {
1290
- deploymentState.value = { ...deploymentState.value, ...update }
1291
- }
1292
-
1293
- export function resetDeploymentState(): void {
1294
- deploymentState.value = createInitialDeploymentState()
1295
- }
1296
-
1297
1117
  // ============================================================================
1298
1118
  // Color Editor State Mutations
1299
1119
  // ============================================================================
@@ -1547,7 +1367,6 @@ export function getStateSnapshot(): EditorState {
1547
1367
  pendingComponentChanges: pendingComponentChanges.value,
1548
1368
  pendingInserts: pendingInserts.value,
1549
1369
  manifest: manifest.value,
1550
- ai: aiState.value,
1551
1370
  blockEditor: blockEditorState.value,
1552
1371
  }
1553
1372
  }
@@ -1575,7 +1394,6 @@ export function resetAllState(): void {
1575
1394
  pendingComponentChanges.value = new Map()
1576
1395
  pendingInserts.value = new Map()
1577
1396
  manifest.value = { entries: {}, components: {}, componentDefinitions: {} }
1578
- aiState.value = createInitialAIState()
1579
1397
  blockEditorState.value = createInitialBlockEditorState()
1580
1398
  markdownEditorState.value = createInitialMarkdownEditorState()
1581
1399
  mediaLibraryState.value = createInitialMediaLibraryState()
@@ -1583,7 +1401,6 @@ export function resetAllState(): void {
1583
1401
  deletePageState.value = createInitialDeletePageState()
1584
1402
  redirectsManagerState.value = createInitialRedirectsManagerState()
1585
1403
  collectionsBrowserState.value = createInitialCollectionsBrowserState()
1586
- deploymentState.value = createInitialDeploymentState()
1587
1404
  colorEditorState.value = createInitialColorEditorState()
1588
1405
  confirmDialogState.value = createInitialConfirmDialogState()
1589
1406
  seoEditorState.value = createInitialSeoEditorState()
@@ -87,114 +87,6 @@
87
87
  animation: spin 1s linear infinite;
88
88
  }
89
89
 
90
- /* Markdown styles for AI chat messages */
91
- .cms-markdown p {
92
- margin: 0 0 0.5em 0;
93
- }
94
-
95
- .cms-markdown p:last-child {
96
- margin-bottom: 0;
97
- }
98
-
99
- .cms-markdown strong {
100
- font-weight: 700;
101
- }
102
-
103
- .cms-markdown em {
104
- font-style: italic;
105
- }
106
-
107
- .cms-markdown code {
108
- background: rgba(255, 255, 255, 0.1);
109
- padding: 0.1em 0.3em;
110
- border-radius: 3px;
111
- font-family: ui-monospace, monospace;
112
- font-size: 0.9em;
113
- }
114
-
115
- .cms-markdown pre {
116
- background: rgba(0, 0, 0, 0.3);
117
- padding: 0.75em;
118
- border-radius: 4px;
119
- overflow-x: auto;
120
- margin: 0.5em 0;
121
- }
122
-
123
- .cms-markdown pre code {
124
- background: none;
125
- padding: 0;
126
- }
127
-
128
- .cms-markdown ul {
129
- margin: 0.5em 0;
130
- padding-left: 1.5em;
131
- list-style-type: disc;
132
- }
133
-
134
- .cms-markdown ol {
135
- margin: 0.5em 0;
136
- padding-left: 1.5em;
137
- list-style-type: decimal;
138
- }
139
-
140
- .cms-markdown ul ul {
141
- list-style-type: circle;
142
- }
143
-
144
- .cms-markdown ul ul ul {
145
- list-style-type: square;
146
- }
147
-
148
- .cms-markdown li {
149
- margin: 0.25em 0;
150
- display: list-item;
151
- }
152
-
153
- .cms-markdown h1, .cms-markdown h2, .cms-markdown h3,
154
- .cms-markdown h4, .cms-markdown h5, .cms-markdown h6 {
155
- margin: 0.75em 0 0.25em 0;
156
- font-weight: 600;
157
- line-height: 1.3;
158
- }
159
-
160
- .cms-markdown h1 {
161
- font-size: 1.4em;
162
- }
163
-
164
- .cms-markdown h2 {
165
- font-size: 1.2em;
166
- }
167
-
168
- .cms-markdown h3 {
169
- font-size: 1.1em;
170
- }
171
-
172
- .cms-markdown h1:first-child, .cms-markdown h2:first-child, .cms-markdown h3:first-child {
173
- margin-top: 0;
174
- }
175
-
176
- .cms-markdown hr {
177
- border: none;
178
- border-top: 1px solid rgba(255, 255, 255, 0.15);
179
- margin: 0.75em 0;
180
- }
181
-
182
- .cms-markdown a {
183
- color: #93c5fd;
184
- text-decoration: underline;
185
- }
186
-
187
- .cms-markdown a:hover {
188
- color: #bfdbfe;
189
- }
190
-
191
- .cms-markdown blockquote {
192
- border-left: 3px solid #93c5fd;
193
- margin: 0.5em 0;
194
- padding-left: 1em;
195
- color: rgba(255, 255, 255, 0.7);
196
- }
197
-
198
90
  /* Soft Design System Card Component */
199
91
  .cms-card {
200
92
  background: var(--color-cms-card);
@@ -240,35 +240,6 @@ export interface SaveBatchResponse {
240
240
  errors?: Array<{ cmsId: string; error: string }>
241
241
  }
242
242
 
243
- export interface ChatMessage {
244
- id: string
245
- role: 'user' | 'assistant'
246
- content: string
247
- elementId?: string
248
- timestamp: number
249
- }
250
-
251
- export type AIStatusType =
252
- | 'thinking'
253
- | 'coding'
254
- | 'building'
255
- | 'deploying'
256
- | 'complete'
257
- | null
258
-
259
- export interface AIState {
260
- isPromptVisible: boolean
261
- isProcessing: boolean
262
- targetElementId: string | null
263
- streamingContent: string | null
264
- error: string | null
265
- isChatOpen: boolean
266
- chatMessages: ChatMessage[]
267
- chatContextElementId: string | null
268
- currentStatus: AIStatusType
269
- statusMessage: string | null
270
- }
271
-
272
243
  export interface BlockEditorState {
273
244
  isOpen: boolean
274
245
  currentComponentId: string | null
@@ -322,7 +293,6 @@ export interface EditorState {
322
293
  pendingComponentChanges: Map<string, ComponentInstance>
323
294
  pendingInserts: Map<string, PendingComponentInsert>
324
295
  manifest: CmsManifest
325
- ai: AIState
326
296
  blockEditor: BlockEditorState
327
297
  }
328
298
 
@@ -481,42 +451,6 @@ export interface MediaUploadResponse {
481
451
  error?: string
482
452
  }
483
453
 
484
- // ============================================================================
485
- // Deployment Status Types
486
- // ============================================================================
487
-
488
- export type DeploymentStatusType =
489
- | 'pending'
490
- | 'queued'
491
- | 'running'
492
- | 'completed'
493
- | 'failed'
494
- | 'cancelled'
495
-
496
- export interface DeploymentStatusResponse {
497
- currentDeployment: {
498
- id: string
499
- status: DeploymentStatusType
500
- createdAt: string
501
- startedAt: string | null
502
- commitMessage: string | null
503
- } | null
504
- lastSuccessfulDeployment: {
505
- completedAt: string
506
- publishedUrl: string
507
- } | null
508
- pendingCount: number
509
- /** When false, deployment is not available (e.g. local dev) and polling should be skipped */
510
- deploymentEnabled?: boolean
511
- }
512
-
513
- export interface DeploymentState {
514
- status: DeploymentStatusType | null
515
- lastDeployedAt: string | null
516
- isPolling: boolean
517
- error: string | null
518
- }
519
-
520
454
  // ============================================================================
521
455
  // Confirm Dialog Types
522
456
  // ============================================================================
@@ -698,16 +632,6 @@ export type UndoAction =
698
632
  // MDX Component Block Types
699
633
  // ============================================================================
700
634
 
701
- export interface MdxPropsEditorState {
702
- isOpen: boolean
703
- /** ProseMirror document position of the node being edited */
704
- nodePos: number | null
705
- componentName: string | null
706
- props: Record<string, string>
707
- /** Position for the floating editor panel */
708
- cursorPos: { x: number; y: number } | null
709
- }
710
-
711
635
  declare global {
712
636
  interface Window {
713
637
  NuaCmsConfig?: Partial<CmsConfig>
@@ -1,6 +1,6 @@
1
1
  import { type HTMLElement as ParsedHTMLElement, parse } from 'node-html-parser'
2
2
  import { processSeoFromHtml } from './seo-processor'
3
- import { enhanceManifestWithSourceSnippets } from './source-finder'
3
+
4
4
  import { extractBackgroundImageClasses, extractColorClasses, extractTextStyleClasses } from './tailwind-colors'
5
5
  import type {
6
6
  Attribute,
@@ -90,6 +90,8 @@ export interface ProcessHtmlResult {
90
90
  collectionWrapperId?: string
91
91
  /** Extracted SEO data from the page */
92
92
  seo?: PageSeoData
93
+ /** Collection definitions passed through for deferred enhancement */
94
+ collectionDefinitions?: Record<string, CollectionDefinition>
93
95
  }
94
96
 
95
97
  /**
@@ -440,7 +442,10 @@ export async function processHtml(
440
442
  let foundWrapper = false
441
443
 
442
444
  // Strategy 1: Dev mode - look for source file attributes
445
+ const SKIP_WRAPPER_TAGS = new Set(['html', 'head', 'body', 'script', 'style', 'meta', 'link'])
443
446
  for (const node of allElements) {
447
+ const tag = node.tagName?.toLowerCase?.() ?? ''
448
+ if (SKIP_WRAPPER_TAGS.has(tag)) continue
444
449
  const sourceFile = node.getAttribute('data-astro-source-file')
445
450
  if (!sourceFile) continue
446
451
 
@@ -1023,10 +1028,6 @@ export async function processHtml(
1023
1028
  node.removeAttribute('data-astro-source-line')
1024
1029
  })
1025
1030
 
1026
- // Enhance manifest entries with actual source snippets from source files
1027
- // This allows the CMS to match and replace dynamic content in source files
1028
- const enhancedEntries = await enhanceManifestWithSourceSnippets(entries, collectionDefinitions)
1029
-
1030
1031
  // Get the current HTML for SEO processing
1031
1032
  let finalHtml = root.toString()
1032
1033
 
@@ -1048,7 +1049,7 @@ export async function processHtml(
1048
1049
 
1049
1050
  // If title was marked with CMS ID, add it to entries
1050
1051
  if (seoResult.titleId && seo.title) {
1051
- enhancedEntries[seoResult.titleId] = {
1052
+ entries[seoResult.titleId] = {
1052
1053
  id: seoResult.titleId,
1053
1054
  tag: 'title',
1054
1055
  text: seo.title.content,
@@ -1061,10 +1062,11 @@ export async function processHtml(
1061
1062
 
1062
1063
  return {
1063
1064
  html: finalHtml,
1064
- entries: enhancedEntries,
1065
+ entries,
1065
1066
  components,
1066
1067
  collectionWrapperId,
1067
1068
  seo,
1069
+ collectionDefinitions,
1068
1070
  }
1069
1071
  }
1070
1072
 
@@ -18,6 +18,9 @@ const textSearchIndex: SearchIndexEntry[] = []
18
18
  const imageSearchIndex: ImageIndexEntry[] = []
19
19
  let searchIndexInitialized = false
20
20
 
21
+ /** Files that changed since last indexing — tracked by Vite watcher */
22
+ const dirtyFiles = new Set<string>()
23
+
21
24
  // ============================================================================
22
25
  // Cache Access Functions
23
26
  // ============================================================================
@@ -58,6 +61,49 @@ export function addToImageSearchIndex(entry: ImageIndexEntry): void {
58
61
  imageSearchIndex.push(entry)
59
62
  }
60
63
 
64
+ // ============================================================================
65
+ // Dirty File Tracking (incremental re-indexing)
66
+ // ============================================================================
67
+
68
+ /**
69
+ * Mark a file as dirty so its index entries are refreshed on next page load.
70
+ * Called by the Vite file watcher when source files change.
71
+ * @param absPath - Absolute path to the changed file
72
+ */
73
+ export function markFileDirty(absPath: string): void {
74
+ dirtyFiles.add(absPath)
75
+ // Also evict the parsed file cache so it's re-read from disk
76
+ parsedFileCache.delete(absPath)
77
+ }
78
+
79
+ export function getDirtyFiles(): Set<string> {
80
+ return dirtyFiles
81
+ }
82
+
83
+ export function clearDirtyFiles(): void {
84
+ dirtyFiles.clear()
85
+ }
86
+
87
+ /**
88
+ * Remove all index entries for a specific file (by relative path).
89
+ * Used before re-indexing a changed file to avoid duplicates.
90
+ */
91
+ export function removeFileFromIndexes(relFile: string): void {
92
+ filterInPlace(textSearchIndex, (e) => e.file !== relFile)
93
+ filterInPlace(imageSearchIndex, (e) => e.file !== relFile)
94
+ }
95
+
96
+ /** Remove non-matching elements in-place (single pass, no per-element splice). */
97
+ function filterInPlace<T>(arr: T[], keep: (item: T) => boolean): void {
98
+ let write = 0
99
+ for (const item of arr) {
100
+ if (keep(item)) {
101
+ arr[write++] = item
102
+ }
103
+ }
104
+ arr.length = write
105
+ }
106
+
61
107
  // ============================================================================
62
108
  // Cache Clear Function
63
109
  // ============================================================================
@@ -69,6 +115,7 @@ export function clearSourceFinderCache(): void {
69
115
  parsedFileCache.clear()
70
116
  directoryCache.clear()
71
117
  markdownFileCache.clear()
118
+ dirtyFiles.clear()
72
119
  // Clear arrays in-place to avoid stale references from consumers
73
120
  textSearchIndex.length = 0
74
121
  imageSearchIndex.length = 0