@nuasite/cms 0.13.1 → 0.14.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/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "directory": "packages/astro-cms"
15
15
  },
16
16
  "license": "Apache-2.0",
17
- "version": "0.13.1",
17
+ "version": "0.14.0",
18
18
  "module": "src/index.ts",
19
19
  "types": "src/index.ts",
20
20
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@astrojs/compiler": "^2.13.1",
29
+ "@astrojs/compiler": "^3.0.0",
30
30
  "@babel/parser": "^7.29.0",
31
31
  "node-html-parser": "^7.1.0",
32
32
  "yaml": "^2.8.2"
@@ -39,12 +39,12 @@
39
39
  "@milkdown/preset-gfm": "^7.19.0",
40
40
  "@milkdown/prose": "^7.19.0",
41
41
  "@milkdown/utils": "^7.19.0",
42
- "@preact/signals": "^2.8.1",
42
+ "@preact/signals": "^2.8.2",
43
43
  "@tailwindcss/vite": "^4.2.1",
44
44
  "@types/bun": "1.3.10",
45
45
  "clsx": "^2.1.1",
46
- "marked": "^17.0.3",
47
- "preact": "^10.28.4",
46
+ "marked": "^17.0.4",
47
+ "preact": "^10.29.0",
48
48
  "prosemirror-commands": "^1.7.1",
49
49
  "prosemirror-inputrules": "^1.5.1",
50
50
  "prosemirror-keymap": "^1.2.3",
@@ -57,7 +57,7 @@
57
57
  "tailwind-merge": "^3.5.0"
58
58
  },
59
59
  "peerDependencies": {
60
- "astro": "^5.17",
60
+ "astro": "^6.0.2",
61
61
  "typescript": "^5",
62
62
  "vite": "^7.3.1",
63
63
  "@aws-sdk/client-s3": "^3.0.0"
@@ -4,7 +4,14 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
4
4
  import path from 'node:path'
5
5
  import { scanCollections } from './collection-scanner'
6
6
  import { getProjectRoot } from './config'
7
- import { buildMapPattern, detectArrayPattern, extractArrayElementProps, handleAddArrayItem, handleRemoveArrayItem, parseInlineArrayName } from './handlers/array-ops'
7
+ import {
8
+ buildMapPattern,
9
+ detectArrayPattern,
10
+ extractArrayElementProps,
11
+ handleAddArrayItem,
12
+ handleRemoveArrayItem,
13
+ parseInlineArrayName,
14
+ } from './handlers/array-ops'
8
15
  import {
9
16
  extractPropsFromSource,
10
17
  findComponentInvocationLine,
@@ -1,5 +1,5 @@
1
- import { useMemo } from 'preact/hooks'
2
1
  import { signal } from '@preact/signals'
2
+ import { useMemo } from 'preact/hooks'
3
3
  import { deleteMarkdownPage } from '../markdown-api'
4
4
  import {
5
5
  closeCollectionsBrowser,
@@ -144,66 +144,68 @@ export function CollectionsBrowser() {
144
144
  )}
145
145
  {entries.map((entry) => (
146
146
  <div key={entry.slug} class="relative" data-cms-ui>
147
- {confirmDeleteSlug.value === entry.slug ? (
148
- <div class="flex items-center gap-2 px-4 py-3 bg-red-500/10 border border-red-500/20 rounded-cms-lg" data-cms-ui>
149
- <div class="flex-1 min-w-0 text-sm text-white/70">
150
- Delete "{entry.title || entry.slug}"?
151
- </div>
152
- <button
153
- type="button"
154
- onClick={() => handleConfirmDelete(entry.slug, entry.sourcePath)}
155
- disabled={deletingEntry.value === entry.slug}
156
- class="px-3 py-1 text-xs font-medium text-white bg-red-600 hover:bg-red-700 rounded-cms-pill transition-colors disabled:opacity-50"
157
- data-cms-ui
158
- >
159
- {deletingEntry.value === entry.slug ? 'Deleting...' : 'Delete'}
160
- </button>
161
- <button
162
- type="button"
163
- onClick={handleCancelDelete}
164
- class="px-3 py-1 text-xs font-medium text-white/60 hover:text-white bg-white/10 hover:bg-white/20 rounded-cms-pill transition-colors"
165
- data-cms-ui
166
- >
167
- Cancel
168
- </button>
169
- </div>
170
- ) : (
171
- <button
172
- type="button"
173
- onClick={() => handleEntryClick(entry.slug, entry.sourcePath, entry.pathname)}
174
- class="w-full flex items-center gap-3 px-4 py-3 hover:bg-white/10 rounded-cms-lg transition-colors text-left group"
175
- data-cms-ui
176
- >
177
- <div class="flex-1 min-w-0">
178
- <div class={`font-medium truncate ${entry.draft ? 'text-white/40' : 'text-white'}`}>
179
- {entry.title || entry.slug}
147
+ {confirmDeleteSlug.value === entry.slug
148
+ ? (
149
+ <div class="flex items-center gap-2 px-4 py-3 bg-red-500/10 border border-red-500/20 rounded-cms-lg" data-cms-ui>
150
+ <div class="flex-1 min-w-0 text-sm text-white/70">
151
+ Delete "{entry.title || entry.slug}"?
180
152
  </div>
181
- {entry.title && <div class="text-white/30 text-xs truncate">{entry.slug}</div>}
153
+ <button
154
+ type="button"
155
+ onClick={() => handleConfirmDelete(entry.slug, entry.sourcePath)}
156
+ disabled={deletingEntry.value === entry.slug}
157
+ class="px-3 py-1 text-xs font-medium text-white bg-red-600 hover:bg-red-700 rounded-cms-pill transition-colors disabled:opacity-50"
158
+ data-cms-ui
159
+ >
160
+ {deletingEntry.value === entry.slug ? 'Deleting...' : 'Delete'}
161
+ </button>
162
+ <button
163
+ type="button"
164
+ onClick={handleCancelDelete}
165
+ class="px-3 py-1 text-xs font-medium text-white/60 hover:text-white bg-white/10 hover:bg-white/20 rounded-cms-pill transition-colors"
166
+ data-cms-ui
167
+ >
168
+ Cancel
169
+ </button>
182
170
  </div>
183
- {entry.draft && (
184
- <span class="shrink-0 px-2 py-0.5 text-xs font-medium text-amber-400/80 bg-amber-400/10 rounded-full border border-amber-400/20">
185
- Draft
186
- </span>
187
- )}
171
+ )
172
+ : (
188
173
  <button
189
174
  type="button"
190
- onClick={(e) => handleDeleteClick(e, entry.slug)}
191
- class="shrink-0 p-1 text-white/0 group-hover:text-white/30 hover:!text-red-400 rounded transition-colors"
192
- title="Delete entry"
175
+ onClick={() => handleEntryClick(entry.slug, entry.sourcePath, entry.pathname)}
176
+ class="w-full flex items-center gap-3 px-4 py-3 hover:bg-white/10 rounded-cms-lg transition-colors text-left group"
193
177
  data-cms-ui
194
178
  >
195
- <TrashIcon />
179
+ <div class="flex-1 min-w-0">
180
+ <div class={`font-medium truncate ${entry.draft ? 'text-white/40' : 'text-white'}`}>
181
+ {entry.title || entry.slug}
182
+ </div>
183
+ {entry.title && <div class="text-white/30 text-xs truncate">{entry.slug}</div>}
184
+ </div>
185
+ {entry.draft && (
186
+ <span class="shrink-0 px-2 py-0.5 text-xs font-medium text-amber-400/80 bg-amber-400/10 rounded-full border border-amber-400/20">
187
+ Draft
188
+ </span>
189
+ )}
190
+ <button
191
+ type="button"
192
+ onClick={(e) => handleDeleteClick(e, entry.slug)}
193
+ class="shrink-0 p-1 text-white/0 group-hover:text-white/30 hover:!text-red-400 rounded transition-colors"
194
+ title="Delete entry"
195
+ data-cms-ui
196
+ >
197
+ <TrashIcon />
198
+ </button>
199
+ <svg
200
+ class="w-4 h-4 text-white/20 group-hover:text-white/40 shrink-0 transition-colors"
201
+ fill="none"
202
+ stroke="currentColor"
203
+ viewBox="0 0 24 24"
204
+ >
205
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
206
+ </svg>
196
207
  </button>
197
- <svg
198
- class="w-4 h-4 text-white/20 group-hover:text-white/40 shrink-0 transition-colors"
199
- fill="none"
200
- stroke="currentColor"
201
- viewBox="0 0 24 24"
202
- >
203
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
204
- </svg>
205
- </button>
206
- )}
208
+ )}
207
209
  </div>
208
210
  ))}
209
211
  </div>
@@ -333,7 +335,12 @@ function BackArrowIcon() {
333
335
  function TrashIcon() {
334
336
  return (
335
337
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
336
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
338
+ <path
339
+ stroke-linecap="round"
340
+ stroke-linejoin="round"
341
+ stroke-width="2"
342
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
343
+ />
337
344
  </svg>
338
345
  )
339
346
  }
@@ -152,7 +152,7 @@ function applyChanges(
152
152
  return { newContent, appliedCount, failedChanges }
153
153
  }
154
154
 
155
- function applyImageChange(
155
+ export function applyImageChange(
156
156
  content: string,
157
157
  change: ChangePayload,
158
158
  ): { success: true; content: string } | { success: false; error: string } {
@@ -709,7 +709,7 @@ function resolveCmsPlaceholders(text: string, manifest: CmsManifest): string {
709
709
  * Handles balanced braces for nested expressions.
710
710
  * Returns the match with index and length, or null if not found.
711
711
  */
712
- function findExpressionSrcAttribute(text: string): { index: number; length: number } | null {
712
+ export function findExpressionSrcAttribute(text: string): { index: number; length: number } | null {
713
713
  // Find 'src=' followed by '{'
714
714
  const srcExprStart = /src\s*=\s*\{/
715
715
  const match = text.match(srcExprStart)
@@ -47,14 +47,14 @@ export function createVitePlugin(context: VitePluginContext): Plugin[] {
47
47
  // processes them. We use prependListener so our handler runs first.
48
48
  const watcher = server.watcher
49
49
  const origEmit = watcher.emit.bind(watcher)
50
- watcher.emit = function (event: string, filePath: string, ...args: any[]) {
50
+ watcher.emit = ((event: string, filePath: string, ...args: any[]) => {
51
51
  if ((event === 'unlink' || event === 'unlinkDir') && expectedDeletions.has(filePath)) {
52
52
  expectedDeletions.delete(filePath)
53
53
  // Swallow the event — don't let Vite/Astro see it
54
54
  return true
55
55
  }
56
56
  return origEmit(event, filePath, ...args)
57
- } as typeof watcher.emit
57
+ }) as typeof watcher.emit
58
58
  },
59
59
  }
60
60