@nuasite/cms 0.20.2 → 0.20.5

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/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "directory": "packages/astro-cms"
15
15
  },
16
16
  "license": "Apache-2.0",
17
- "version": "0.20.2",
17
+ "version": "0.20.5",
18
18
  "module": "src/index.ts",
19
19
  "types": "src/index.ts",
20
20
  "type": "module",
@@ -44,9 +44,9 @@ interface ViteDevServerLike {
44
44
  on: (event: string, listener: (...args: any[]) => void) => any
45
45
  removeListener: (event: string, listener: (...args: any[]) => void) => any
46
46
  }
47
- ws?: {
48
- send: (payload: { type: string; path?: string }) => void
49
- }
47
+ environments?: Record<string, {
48
+ moduleGraph: { invalidateAll: () => void }
49
+ }>
50
50
  }
51
51
 
52
52
  /**
@@ -116,11 +116,14 @@ export function createDevMiddleware(
116
116
 
117
117
  handleCmsApiRoute(route, req, res, manifestWriter, config.contentDir, options.mediaAdapter)
118
118
  .then(() => {
119
- // Explicitly trigger full-reload after content-modifying routes.
120
- // In sandboxed environments (e.g. E2B), chokidar file watcher events
121
- // may not fire reliably, so we send the HMR event directly.
122
- if (req.method === 'POST' && server.ws) {
123
- server.ws.send({ type: 'full-reload' })
119
+ // Invalidate all Vite environment module caches after content-modifying
120
+ // routes so that a subsequent page reload serves fresh content.
121
+ // In sandboxed environments (e.g. E2B) chokidar doesn't detect file
122
+ // changes, leaving stale modules in Astro's SSR/prerender environments.
123
+ if (req.method === 'POST' && server.environments) {
124
+ for (const env of Object.values(server.environments)) {
125
+ env.moduleGraph.invalidateAll()
126
+ }
124
127
  }
125
128
  })
126
129
  .catch((error) => {
@@ -2,7 +2,7 @@ import { type Editor, editorViewCtx } from '@milkdown/core'
2
2
  import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
3
3
  import { slugify } from '../../shared'
4
4
  import { updateMarkdownPage } from '../api'
5
- import { STORAGE_KEYS, Z_INDEX } from '../constants'
5
+ import { schedulePageReload, STORAGE_KEYS, Z_INDEX } from '../constants'
6
6
  import { createMarkdownPage } from '../markdown-api'
7
7
  import {
8
8
  config,
@@ -127,6 +127,8 @@ export function MarkdownEditorOverlay() {
127
127
  // Clear pending entry navigation so editor doesn't auto-open after save
128
128
  sessionStorage.removeItem(STORAGE_KEYS.PENDING_ENTRY_NAVIGATION)
129
129
  resetMarkdownEditorState()
130
+
131
+ schedulePageReload()
130
132
  } else {
131
133
  showToast(result.error || 'Failed to save markdown', 'error')
132
134
  setIsSaving(false)
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'
2
- import { clampPanelPosition, Z_INDEX } from '../constants'
2
+ import { clampPanelPosition, schedulePageReload, Z_INDEX } from '../constants'
3
3
  import { updateMarkdownPage } from '../markdown-api'
4
4
  import { closeReferencePicker, config, manifest, referencePickerState, showToast } from '../signals'
5
5
 
@@ -57,6 +57,7 @@ export function ReferencePicker() {
57
57
  })
58
58
  if (result.success) {
59
59
  showToast('Reference updated', 'success')
60
+ schedulePageReload()
60
61
  } else {
61
62
  showToast(result.error || 'Failed to update reference', 'error')
62
63
  }
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useState } from 'preact/hooks'
2
2
  import { saveBatchChanges } from '../api'
3
+ import { schedulePageReload } from '../constants'
3
4
  import { isApplyingUndoRedo, recordChange } from '../history'
4
5
  import {
5
6
  clearPendingSeoChanges,
@@ -226,6 +227,7 @@ export function SeoEditor() {
226
227
  showToast(`Saved ${result.updated} SEO change(s) successfully!`, 'success')
227
228
  clearPendingSeoChanges()
228
229
  closeSeoEditor()
230
+ schedulePageReload()
229
231
  }
230
232
  } catch (error) {
231
233
  showToast(error instanceof Error ? error.message : 'Failed to save SEO changes', 'error')
@@ -43,8 +43,21 @@ export const TIMING = {
43
43
  PREVIEW_ERROR_DURATION_MS: 5000,
44
44
  /** Delay before focusing input after expansion (ms) */
45
45
  FOCUS_DELAY_MS: 50,
46
+ /** Delay before reloading the page after a content-modifying save (ms) */
47
+ RELOAD_DELAY_MS: 100,
48
+ /** Longer reload delay to allow collapse animation to play (ms) */
49
+ RELOAD_COLLAPSE_DELAY_MS: 300,
46
50
  } as const
47
51
 
52
+ /**
53
+ * Schedule a page reload after a content-modifying save.
54
+ * In normal dev, Vite HMR (via chokidar) usually reloads the page before this fires.
55
+ * In sandboxed environments (e.g. E2B) where HMR is unavailable, this ensures the page still refreshes.
56
+ */
57
+ export function schedulePageReload(delayMs: number = TIMING.RELOAD_DELAY_MS) {
58
+ setTimeout(() => location.reload(), delayMs)
59
+ }
60
+
48
61
  /**
49
62
  * Layout constants for UI positioning
50
63
  */
@@ -1,5 +1,5 @@
1
1
  import { fetchManifest, getMarkdownContent, saveBatchChanges } from './api'
2
- import { CSS, TIMING } from './constants'
2
+ import { CSS, schedulePageReload, TIMING } from './constants'
3
3
  import {
4
4
  cleanupHighlightSystem,
5
5
  disableAllInteractiveElements,
@@ -877,6 +877,9 @@ export async function saveAllChanges(
877
877
  }
878
878
 
879
879
  onStateChange?.()
880
+
881
+ schedulePageReload()
882
+
880
883
  return { success: true, updated: result.updated }
881
884
  } catch (err) {
882
885
  console.error('[CMS] Save failed:', err)
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useState } from 'preact/hooks'
2
+ import { schedulePageReload, TIMING } from '../constants'
2
3
  import { logDebug } from '../dom'
3
4
  import { getComponentInstances } from '../manifest'
4
5
  import * as signals from '../signals'
@@ -152,6 +153,7 @@ export function useBlockEditorHandlers({
152
153
  }
153
154
 
154
155
  showToast(`Item added ${position} current item`, 'success')
156
+ schedulePageReload()
155
157
  } else {
156
158
  // Standard component insertion
157
159
  const response = await fetch(`${config.apiBase}/insert-component`, {
@@ -176,6 +178,7 @@ export function useBlockEditorHandlers({
176
178
  }
177
179
 
178
180
  showToast(`${componentName} inserted ${position} component`, 'success')
181
+ schedulePageReload()
179
182
  }
180
183
  } catch (error) {
181
184
  console.error('[CMS] Failed to insert component:', error)
@@ -234,10 +237,11 @@ export function useBlockEditorHandlers({
234
237
 
235
238
  showToast(arrayMode ? 'Item removed' : 'Component removed', 'success')
236
239
 
237
- // Visually collapse and hide the component until HMR refreshes the page
240
+ // Visually collapse the component then reload to pick up file changes
238
241
  if (componentEl) {
239
242
  collapseElement(componentEl)
240
243
  }
244
+ schedulePageReload(TIMING.RELOAD_COLLAPSE_DELAY_MS)
241
245
  } catch (error) {
242
246
  console.error('[CMS] Failed to remove component:', error)
243
247
  showToast(arrayMode ? 'Failed to remove item' : 'Failed to remove component', 'error')