@nuasite/cms 0.20.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.
- package/dist/editor.js +11246 -11441
- package/package.json +1 -1
- package/src/dev-middleware.ts +14 -4
- package/src/editor/api.ts +0 -251
- package/src/editor/components/markdown-editor-overlay.tsx +7 -26
- package/src/editor/components/toolbar.tsx +0 -75
- package/src/editor/constants.ts +0 -4
- package/src/editor/editor.ts +2 -192
- package/src/editor/hooks/index.ts +0 -3
- package/src/editor/hooks/useBlockEditorHandlers.ts +1 -8
- package/src/editor/hooks/useTooltipState.ts +1 -2
- package/src/editor/index.tsx +2 -18
- package/src/editor/post-message.ts +0 -6
- package/src/editor/signals.ts +0 -166
- package/src/editor/styles.css +0 -108
- package/src/editor/types.ts +0 -66
- package/src/types.ts +0 -4
- package/src/editor/ai.ts +0 -185
- package/src/editor/components/ai-chat.tsx +0 -631
- package/src/editor/components/ai-tooltip.tsx +0 -180
- package/src/editor/hooks/useAIHandlers.ts +0 -345
package/package.json
CHANGED
package/src/dev-middleware.ts
CHANGED
|
@@ -44,6 +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
50
|
}
|
|
48
51
|
|
|
49
52
|
/**
|
|
@@ -111,12 +114,19 @@ export function createDevMiddleware(
|
|
|
111
114
|
|
|
112
115
|
const route = url.replace('/_nua/cms/', '').split('?')[0]!
|
|
113
116
|
|
|
114
|
-
handleCmsApiRoute(route, req, res, manifestWriter, config.contentDir, options.mediaAdapter)
|
|
115
|
-
(
|
|
117
|
+
handleCmsApiRoute(route, req, res, manifestWriter, config.contentDir, options.mediaAdapter)
|
|
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' })
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
.catch((error) => {
|
|
116
127
|
console.error('[astro-cms] API error:', error)
|
|
117
128
|
sendError(res, 'Internal server error', 500)
|
|
118
|
-
}
|
|
119
|
-
)
|
|
129
|
+
})
|
|
120
130
|
})
|
|
121
131
|
}
|
|
122
132
|
|
package/src/editor/api.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { setAvailableTextStyles } from './text-styling'
|
|
|
4
4
|
import type {
|
|
5
5
|
CmsManifest,
|
|
6
6
|
ComponentInsertOperation,
|
|
7
|
-
DeploymentStatusResponse,
|
|
8
7
|
SaveBatchRequest,
|
|
9
8
|
SaveBatchResponse,
|
|
10
9
|
UpdateMarkdownPageRequest,
|
|
@@ -20,68 +19,6 @@ export interface GetMarkdownContentResponse {
|
|
|
20
19
|
filePath: string
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
/**
|
|
24
|
-
* Types for CMS AI chat communication (SSE events)
|
|
25
|
-
*/
|
|
26
|
-
export interface CmsAiChatRequest {
|
|
27
|
-
prompt: string
|
|
28
|
-
elementId?: string
|
|
29
|
-
currentContent?: string
|
|
30
|
-
pageUrl: string
|
|
31
|
-
/** Source file context for the element being edited */
|
|
32
|
-
context?: string
|
|
33
|
-
sessionId?: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type CmsAiEvent =
|
|
37
|
-
| CmsAiTokenEvent
|
|
38
|
-
| CmsAiStatusEvent
|
|
39
|
-
| CmsAiActionEvent
|
|
40
|
-
| CmsAiErrorEvent
|
|
41
|
-
| CmsAiDoneEvent
|
|
42
|
-
|
|
43
|
-
export interface CmsAiTokenEvent {
|
|
44
|
-
type: 'token'
|
|
45
|
-
token: string
|
|
46
|
-
fullText: string
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface CmsAiStatusEvent {
|
|
50
|
-
type: 'status'
|
|
51
|
-
status: 'thinking' | 'coding' | 'building' | 'deploying' | 'complete'
|
|
52
|
-
message?: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface CmsAiActionEvent {
|
|
56
|
-
type: 'action'
|
|
57
|
-
action: CmsAiAction
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export type CmsAiAction =
|
|
61
|
-
| { name: 'refresh' }
|
|
62
|
-
| { name: 'preview'; url: string }
|
|
63
|
-
| { name: 'commit'; sha: string; message: string }
|
|
64
|
-
| { name: 'apply-edit'; elementId: string; content: string; htmlContent?: string }
|
|
65
|
-
|
|
66
|
-
export interface CmsAiErrorEvent {
|
|
67
|
-
type: 'error'
|
|
68
|
-
error: string
|
|
69
|
-
code?: string
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface CmsAiDoneEvent {
|
|
73
|
-
type: 'done'
|
|
74
|
-
summary?: string
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface CmsAiStreamCallbacks {
|
|
78
|
-
onToken?: (token: string, fullText: string) => void
|
|
79
|
-
onStatus?: (status: CmsAiStatusEvent['status'], message?: string) => void
|
|
80
|
-
onAction?: (action: CmsAiAction) => void
|
|
81
|
-
onError?: (error: string, code?: string) => void
|
|
82
|
-
onDone?: (summary?: string) => void
|
|
83
|
-
}
|
|
84
|
-
|
|
85
22
|
/**
|
|
86
23
|
* Get the manifest URL for the current page
|
|
87
24
|
* For example: /about -> /about.json
|
|
@@ -325,194 +262,6 @@ export async function removeComponent(
|
|
|
325
262
|
return res.json()
|
|
326
263
|
}
|
|
327
264
|
|
|
328
|
-
/**
|
|
329
|
-
* Parse SSE data line to event
|
|
330
|
-
*/
|
|
331
|
-
export function parseSseEvent(data: string): CmsAiEvent | null {
|
|
332
|
-
if (data === '[DONE]') {
|
|
333
|
-
return { type: 'done' }
|
|
334
|
-
}
|
|
335
|
-
try {
|
|
336
|
-
return JSON.parse(data) as CmsAiEvent
|
|
337
|
-
} catch {
|
|
338
|
-
return null
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Stream AI chat response from the server using SSE
|
|
344
|
-
* Provides rich events including status updates, actions (refresh, preview), and streaming tokens
|
|
345
|
-
*/
|
|
346
|
-
export async function streamAiChat(
|
|
347
|
-
apiBase: string,
|
|
348
|
-
request: CmsAiChatRequest,
|
|
349
|
-
callbacks: CmsAiStreamCallbacks,
|
|
350
|
-
abortSignal?: AbortSignal,
|
|
351
|
-
): Promise<void> {
|
|
352
|
-
const controller = new AbortController()
|
|
353
|
-
const timeoutId = setTimeout(() => controller.abort(), API.AI_STREAM_TIMEOUT_MS)
|
|
354
|
-
|
|
355
|
-
// Allow external abort signal to also abort the request
|
|
356
|
-
if (abortSignal) {
|
|
357
|
-
abortSignal.addEventListener('abort', () => controller.abort())
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
let doneCalled = false
|
|
361
|
-
|
|
362
|
-
try {
|
|
363
|
-
const res = await fetch(`${apiBase}/ai/chat`, {
|
|
364
|
-
method: 'POST',
|
|
365
|
-
credentials: 'include',
|
|
366
|
-
headers: {
|
|
367
|
-
'Content-Type': 'application/json',
|
|
368
|
-
},
|
|
369
|
-
body: JSON.stringify(request),
|
|
370
|
-
signal: controller.signal,
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
if (!res.ok) {
|
|
374
|
-
const text = await res.text().catch(() => '')
|
|
375
|
-
callbacks.onError?.(text || `Request failed (${res.status})`, String(res.status))
|
|
376
|
-
return
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (!res.body) {
|
|
380
|
-
callbacks.onError?.('No response body')
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const reader = res.body.getReader()
|
|
385
|
-
const decoder = new TextDecoder()
|
|
386
|
-
let buffer = ''
|
|
387
|
-
|
|
388
|
-
while (true) {
|
|
389
|
-
const { done, value } = await reader.read()
|
|
390
|
-
|
|
391
|
-
if (done) {
|
|
392
|
-
break
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
buffer += decoder.decode(value, { stream: true })
|
|
396
|
-
const lines = buffer.split('\n')
|
|
397
|
-
|
|
398
|
-
// Keep the last incomplete line in the buffer
|
|
399
|
-
buffer = lines.pop() || ''
|
|
400
|
-
|
|
401
|
-
for (const line of lines) {
|
|
402
|
-
if (line.startsWith('data: ')) {
|
|
403
|
-
const data = line.slice(6).trim()
|
|
404
|
-
if (!data) continue
|
|
405
|
-
|
|
406
|
-
const event = parseSseEvent(data)
|
|
407
|
-
if (!event) continue
|
|
408
|
-
|
|
409
|
-
switch (event.type) {
|
|
410
|
-
case 'token':
|
|
411
|
-
callbacks.onToken?.(event.token, event.fullText)
|
|
412
|
-
break
|
|
413
|
-
case 'status':
|
|
414
|
-
callbacks.onStatus?.(event.status, event.message)
|
|
415
|
-
break
|
|
416
|
-
case 'action':
|
|
417
|
-
callbacks.onAction?.(event.action)
|
|
418
|
-
break
|
|
419
|
-
case 'error':
|
|
420
|
-
callbacks.onError?.(event.error, event.code)
|
|
421
|
-
break
|
|
422
|
-
case 'done':
|
|
423
|
-
if (!doneCalled) {
|
|
424
|
-
doneCalled = true
|
|
425
|
-
callbacks.onDone?.(event.summary)
|
|
426
|
-
}
|
|
427
|
-
return
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Process any remaining buffer
|
|
434
|
-
if (buffer.startsWith('data: ')) {
|
|
435
|
-
const data = buffer.slice(6).trim()
|
|
436
|
-
if (data) {
|
|
437
|
-
const event = parseSseEvent(data)
|
|
438
|
-
if (event?.type === 'done' && !doneCalled) {
|
|
439
|
-
doneCalled = true
|
|
440
|
-
callbacks.onDone?.(event.summary)
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (!doneCalled) {
|
|
446
|
-
doneCalled = true
|
|
447
|
-
callbacks.onDone?.()
|
|
448
|
-
}
|
|
449
|
-
} catch (error) {
|
|
450
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
451
|
-
callbacks.onError?.('Request timed out or was cancelled', 'TIMEOUT')
|
|
452
|
-
return
|
|
453
|
-
}
|
|
454
|
-
callbacks.onError?.(error instanceof Error ? error.message : 'Unknown error')
|
|
455
|
-
} finally {
|
|
456
|
-
clearTimeout(timeoutId)
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Chat message type from history endpoint
|
|
462
|
-
*/
|
|
463
|
-
export interface ChatHistoryMessage {
|
|
464
|
-
id: string
|
|
465
|
-
role: 'user' | 'assistant' | 'tool'
|
|
466
|
-
content: string | null
|
|
467
|
-
created_at: string
|
|
468
|
-
channel?: string
|
|
469
|
-
identifier?: string
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
export interface ChatHistoryResponse {
|
|
473
|
-
messages: ChatHistoryMessage[]
|
|
474
|
-
hasMore: boolean
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* Fetch AI chat history for the current project
|
|
479
|
-
*/
|
|
480
|
-
export async function getChatHistory(
|
|
481
|
-
apiBase: string,
|
|
482
|
-
limit = 50,
|
|
483
|
-
): Promise<ChatHistoryResponse> {
|
|
484
|
-
const res = await fetchWithTimeout(`${apiBase}/ai/chat/history?limit=${limit}`, {
|
|
485
|
-
method: 'GET',
|
|
486
|
-
credentials: 'include',
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
if (!res.ok) {
|
|
490
|
-
const text = await res.text().catch(() => '')
|
|
491
|
-
throw new Error(`Failed to fetch chat history (${res.status}): ${text || res.statusText}`)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return res.json()
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Fetch deployment status for the current project
|
|
499
|
-
*/
|
|
500
|
-
export async function getDeploymentStatus(
|
|
501
|
-
apiBase: string,
|
|
502
|
-
): Promise<DeploymentStatusResponse> {
|
|
503
|
-
const res = await fetchWithTimeout(`${apiBase}/deployment/status`, {
|
|
504
|
-
method: 'GET',
|
|
505
|
-
credentials: 'include',
|
|
506
|
-
})
|
|
507
|
-
|
|
508
|
-
if (!res.ok) {
|
|
509
|
-
const text = await res.text().catch(() => '')
|
|
510
|
-
throw new Error(`Failed to fetch deployment status (${res.status}): ${text || res.statusText}`)
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return res.json()
|
|
514
|
-
}
|
|
515
|
-
|
|
516
265
|
/**
|
|
517
266
|
* Fetch markdown content from a file
|
|
518
267
|
*/
|
|
@@ -3,7 +3,6 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
|
|
3
3
|
import { slugify } from '../../shared'
|
|
4
4
|
import { updateMarkdownPage } from '../api'
|
|
5
5
|
import { STORAGE_KEYS, Z_INDEX } from '../constants'
|
|
6
|
-
import { startDeploymentPolling } from '../editor'
|
|
7
6
|
import { createMarkdownPage } from '../markdown-api'
|
|
8
7
|
import {
|
|
9
8
|
config,
|
|
@@ -72,15 +71,6 @@ export function MarkdownEditorOverlay() {
|
|
|
72
71
|
}
|
|
73
72
|
}, [isCreateMode, slugManuallyEdited, page?.frontmatter.title, page?.frontmatter.name])
|
|
74
73
|
|
|
75
|
-
const handleDeploymentComplete = useCallback(
|
|
76
|
-
(status: 'completed' | 'failed' | 'timeout') => {
|
|
77
|
-
if (status === 'failed') {
|
|
78
|
-
showToast('Deployment failed', 'error')
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
[],
|
|
82
|
-
)
|
|
83
|
-
|
|
84
74
|
/** Find the [data-cms-markdown] wrapper element on the actual page (not CMS UI). */
|
|
85
75
|
const findMarkdownWrapper = useCallback((): HTMLElement | null => {
|
|
86
76
|
const SKIP_TAGS = new Set(['BODY', 'HTML', 'BUTTON', 'SPAN', 'A'])
|
|
@@ -133,14 +123,10 @@ export function MarkdownEditorOverlay() {
|
|
|
133
123
|
isMarkdownPreview.value = false
|
|
134
124
|
setIsSaving(false)
|
|
135
125
|
|
|
136
|
-
showToast('Content saved
|
|
126
|
+
showToast('Content saved', 'success')
|
|
137
127
|
// Clear pending entry navigation so editor doesn't auto-open after save
|
|
138
128
|
sessionStorage.removeItem(STORAGE_KEYS.PENDING_ENTRY_NAVIGATION)
|
|
139
|
-
// Close the editor immediately — the toolbar will show deploying state
|
|
140
129
|
resetMarkdownEditorState()
|
|
141
|
-
startDeploymentPolling(config.value, {
|
|
142
|
-
onComplete: handleDeploymentComplete,
|
|
143
|
-
})
|
|
144
130
|
} else {
|
|
145
131
|
showToast(result.error || 'Failed to save markdown', 'error')
|
|
146
132
|
setIsSaving(false)
|
|
@@ -151,7 +137,7 @@ export function MarkdownEditorOverlay() {
|
|
|
151
137
|
setIsSaving(false)
|
|
152
138
|
}
|
|
153
139
|
},
|
|
154
|
-
[isSaving, isPreview,
|
|
140
|
+
[isSaving, isPreview, findMarkdownWrapper],
|
|
155
141
|
)
|
|
156
142
|
|
|
157
143
|
const handleCreate = useCallback(async () => {
|
|
@@ -207,16 +193,11 @@ export function MarkdownEditorOverlay() {
|
|
|
207
193
|
}
|
|
208
194
|
}
|
|
209
195
|
|
|
210
|
-
showToast('Page created
|
|
196
|
+
showToast('Page created', 'success')
|
|
211
197
|
resetMarkdownEditorState()
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (status === 'completed' && redirectUrl) {
|
|
216
|
-
startRedirectCountdown(redirectUrl, title.trim())
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
})
|
|
198
|
+
if (redirectUrl) {
|
|
199
|
+
startRedirectCountdown(redirectUrl, title.trim())
|
|
200
|
+
}
|
|
220
201
|
} else {
|
|
221
202
|
showToast(result.error || 'Failed to create page', 'error')
|
|
222
203
|
}
|
|
@@ -226,7 +207,7 @@ export function MarkdownEditorOverlay() {
|
|
|
226
207
|
} finally {
|
|
227
208
|
setIsSaving(false)
|
|
228
209
|
}
|
|
229
|
-
}, [isSaving
|
|
210
|
+
}, [isSaving])
|
|
230
211
|
|
|
231
212
|
const handlePreview = useCallback(() => {
|
|
232
213
|
const activeId = markdownEditorState.value.activeElementId
|
|
@@ -13,7 +13,6 @@ export interface ToolbarCallbacks {
|
|
|
13
13
|
onDiscard: () => void
|
|
14
14
|
onSelectElement?: () => void
|
|
15
15
|
onMediaLibrary?: () => void
|
|
16
|
-
onDismissDeployment?: () => void
|
|
17
16
|
onNavigateChange?: () => void
|
|
18
17
|
onEditContent?: () => void
|
|
19
18
|
onToggleHighlights?: () => void
|
|
@@ -39,80 +38,11 @@ const GridIcon = () => (
|
|
|
39
38
|
</svg>
|
|
40
39
|
)
|
|
41
40
|
|
|
42
|
-
const DeploymentStatusIndicator = ({ onDismiss }: { onDismiss?: () => void }) => {
|
|
43
|
-
const deploymentStatus = signals.deploymentStatus.value
|
|
44
|
-
const lastDeployedAt = signals.lastDeployedAt.value
|
|
45
|
-
|
|
46
|
-
if (!deploymentStatus) {
|
|
47
|
-
return null
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const isActive = deploymentStatus === 'pending' || deploymentStatus === 'queued' || deploymentStatus === 'running'
|
|
51
|
-
const isCompleted = deploymentStatus === 'completed'
|
|
52
|
-
const isFailed = deploymentStatus === 'failed'
|
|
53
|
-
|
|
54
|
-
if (!isActive && !isCompleted && !isFailed) {
|
|
55
|
-
return null
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const formatTimeAgo = (dateStr: string) => {
|
|
59
|
-
const date = new Date(dateStr)
|
|
60
|
-
const now = new Date()
|
|
61
|
-
const diffMs = now.getTime() - date.getTime()
|
|
62
|
-
const diffSec = Math.floor(diffMs / 1000)
|
|
63
|
-
const diffMin = Math.floor(diffSec / 60)
|
|
64
|
-
|
|
65
|
-
if (diffMin < 1) return 'just now'
|
|
66
|
-
if (diffMin === 1) return '1m ago'
|
|
67
|
-
if (diffMin < 60) return `${diffMin}m ago`
|
|
68
|
-
const diffHour = Math.floor(diffMin / 60)
|
|
69
|
-
if (diffHour === 1) return '1h ago'
|
|
70
|
-
return `${diffHour}h ago`
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<div
|
|
75
|
-
class={cn(
|
|
76
|
-
'flex items-center gap-1.5 sm:gap-2 px-3 py-2 sm:px-5 sm:py-2.5 text-sm font-medium rounded-cms-pill transition-all',
|
|
77
|
-
isActive && 'text-white/80',
|
|
78
|
-
isCompleted && 'bg-cms-primary text-cms-primary-text',
|
|
79
|
-
isFailed && 'bg-cms-error/20 text-cms-error cursor-pointer hover:bg-cms-error/30',
|
|
80
|
-
)}
|
|
81
|
-
onClick={isFailed ? onDismiss : undefined}
|
|
82
|
-
title={isFailed ? 'Click to dismiss' : undefined}
|
|
83
|
-
>
|
|
84
|
-
{isActive && (
|
|
85
|
-
<>
|
|
86
|
-
<span class="inline-block w-3.5 h-3.5 border-2 border-white/80 border-t-transparent rounded-full animate-spin" />
|
|
87
|
-
<span class="hidden sm:inline">Deploying</span>
|
|
88
|
-
</>
|
|
89
|
-
)}
|
|
90
|
-
{isCompleted && (
|
|
91
|
-
<>
|
|
92
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
93
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
|
94
|
-
</svg>
|
|
95
|
-
<span class="hidden sm:inline">Live{lastDeployedAt ? ` ${formatTimeAgo(lastDeployedAt)}` : ''}</span>
|
|
96
|
-
</>
|
|
97
|
-
)}
|
|
98
|
-
{isFailed && (
|
|
99
|
-
<>
|
|
100
|
-
<svg class="w-4 h-4 sm:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
101
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
102
|
-
</svg>
|
|
103
|
-
<span class="hidden sm:inline">Failed</span>
|
|
104
|
-
</>
|
|
105
|
-
)}
|
|
106
|
-
</div>
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
41
|
export const Toolbar = ({ callbacks, collectionDefinitions }: ToolbarProps) => {
|
|
111
42
|
const isEditing = signals.isEditing.value
|
|
112
43
|
const showingOriginal = signals.showingOriginal.value
|
|
113
44
|
const dirtyCount = signals.totalDirtyCount.value
|
|
114
45
|
const isSaving = signals.isSaving.value
|
|
115
|
-
const deploymentStatus = signals.deploymentStatus.value
|
|
116
46
|
const showEditableHighlights = signals.showEditableHighlights.value
|
|
117
47
|
const isPreviewingMarkdown = signals.isMarkdownPreview.value
|
|
118
48
|
const currentPageCollection = signals.currentPageCollection.value
|
|
@@ -123,8 +53,6 @@ export const Toolbar = ({ callbacks, collectionDefinitions }: ToolbarProps) => {
|
|
|
123
53
|
|
|
124
54
|
if (isPreviewingMarkdown) return null
|
|
125
55
|
|
|
126
|
-
const showDeploymentStatus = deploymentStatus !== null
|
|
127
|
-
|
|
128
56
|
const stopPropagation = (e: Event) => e.stopPropagation()
|
|
129
57
|
|
|
130
58
|
const handleDiscard = async () => {
|
|
@@ -298,9 +226,6 @@ export const Toolbar = ({ callbacks, collectionDefinitions }: ToolbarProps) => {
|
|
|
298
226
|
|
|
299
227
|
{/* Primary actions group */}
|
|
300
228
|
<div class="flex items-center gap-2 sm:gap-1.5">
|
|
301
|
-
{/* Deployment Status */}
|
|
302
|
-
{showDeploymentStatus && <DeploymentStatusIndicator onDismiss={callbacks.onDismissDeployment} />}
|
|
303
|
-
|
|
304
229
|
{/* Saving indicator */}
|
|
305
230
|
{isSaving && !showingOriginal && (
|
|
306
231
|
<div class="flex items-center gap-1.5 px-3 py-2 sm:px-5 sm:py-2.5 text-sm font-medium text-white/80">
|
package/src/editor/constants.ts
CHANGED
|
@@ -67,8 +67,6 @@ export const LAYOUT = {
|
|
|
67
67
|
BLOCK_EDITOR_WIDTH: 400,
|
|
68
68
|
/** Block editor approximate height for positioning */
|
|
69
69
|
BLOCK_EDITOR_HEIGHT: 500,
|
|
70
|
-
/** AI chat panel width */
|
|
71
|
-
AI_CHAT_WIDTH: 400,
|
|
72
70
|
} as const
|
|
73
71
|
|
|
74
72
|
/**
|
|
@@ -77,8 +75,6 @@ export const LAYOUT = {
|
|
|
77
75
|
export const API = {
|
|
78
76
|
/** Default request timeout in milliseconds */
|
|
79
77
|
REQUEST_TIMEOUT_MS: 30000,
|
|
80
|
-
/** AI streaming request timeout in milliseconds */
|
|
81
|
-
AI_STREAM_TIMEOUT_MS: 120000,
|
|
82
78
|
/** Maximum retry attempts for failed requests */
|
|
83
79
|
MAX_RETRIES: 3,
|
|
84
80
|
/** Base delay for exponential backoff (ms) */
|