@alpaca-editor/core 1.0.3978 → 1.0.3980
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/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/config/config.js +18 -2
- package/dist/config/config.js.map +1 -1
- package/dist/editor/AspectRatioSelector.d.ts +13 -0
- package/dist/editor/AspectRatioSelector.js +71 -0
- package/dist/editor/AspectRatioSelector.js.map +1 -0
- package/dist/editor/ConfirmationDialog.js +4 -5
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/PictureCropper.d.ts +1 -1
- package/dist/editor/PictureCropper.js +466 -113
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/Terminal.js +5 -4
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AiTerminal.js +20 -2
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/client/EditorClient.js +14 -3
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +3 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +13 -5
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/media-selector/Preview.js +1 -1
- package/dist/editor/media-selector/Preview.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +48 -24
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +3 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +6 -6
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +26 -4
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.d.ts +2 -1
- package/dist/editor/page-viewer/EditorForm.js +16 -2
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/EditorFormPopup.d.ts +11 -0
- package/dist/editor/page-viewer/EditorFormPopup.js +41 -0
- package/dist/editor/page-viewer/EditorFormPopup.js.map +1 -0
- package/dist/editor/page-viewer/PageViewer.js +4 -6
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/services/contextService.d.ts +26 -0
- package/dist/editor/services/contextService.js +102 -0
- package/dist/editor/services/contextService.js.map +1 -0
- package/dist/editor/sidebar/Completions.d.ts +1 -0
- package/dist/editor/sidebar/Completions.js +54 -0
- package/dist/editor/sidebar/Completions.js.map +1 -0
- package/dist/editor/sidebar/Validation.js +3 -3
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +17 -3
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +2 -1
- package/dist/editor/ui/SimpleTabs.js +2 -2
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +134 -30
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/ui/switch.tsx +1 -1
- package/src/config/config.tsx +18 -1
- package/src/editor/AspectRatioSelector.tsx +146 -0
- package/src/editor/ConfirmationDialog.tsx +36 -45
- package/src/editor/PictureCropper.tsx +724 -233
- package/src/editor/Terminal.tsx +9 -8
- package/src/editor/ai/AiTerminal.tsx +58 -15
- package/src/editor/client/EditorClient.tsx +26 -1
- package/src/editor/client/editContext.ts +7 -0
- package/src/editor/commands/componentCommands.tsx +14 -9
- package/src/editor/media-selector/Preview.tsx +7 -5
- package/src/editor/page-editor-chrome/FrameMenu.tsx +70 -15
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +9 -3
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +31 -5
- package/src/editor/page-viewer/EditorForm.tsx +21 -1
- package/src/editor/page-viewer/EditorFormPopup.tsx +104 -0
- package/src/editor/page-viewer/PageViewer.tsx +3 -11
- package/src/editor/services/contextService.ts +146 -0
- package/src/editor/sidebar/Completions.tsx +160 -0
- package/src/editor/sidebar/Validation.tsx +9 -10
- package/src/editor/ui/PerfectTree.tsx +19 -3
- package/src/editor/ui/SimpleTabs.tsx +4 -1
- package/src/revision.ts +2 -2
- package/src/types.ts +1 -0
- package/dist/editor/menubar/BrowseHistory.d.ts +0 -6
- package/dist/editor/menubar/BrowseHistory.js +0 -11
- package/dist/editor/menubar/BrowseHistory.js.map +0 -1
- package/src/editor/menubar/BrowseHistory.tsx +0 -28
|
@@ -4,7 +4,7 @@ import { PageViewerFrame } from "./PageViewerFrame";
|
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
5
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
6
6
|
import { useEditContext } from "../client/editContext";
|
|
7
|
-
import {
|
|
7
|
+
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
8
8
|
import { cn } from "../../lib/utils";
|
|
9
9
|
import { Splitter, SplitterPanel } from "../ui/Splitter";
|
|
10
10
|
|
|
@@ -31,10 +31,8 @@ export function PageViewer({
|
|
|
31
31
|
if (!pageViewContext) return null;
|
|
32
32
|
|
|
33
33
|
useEffect(() => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
}, [editContext?.insertMode]);
|
|
34
|
+
setFormEditorCollapsed(false);
|
|
35
|
+
}, [editContext?.insertMode, editContext?.activeEditorTab]);
|
|
38
36
|
|
|
39
37
|
useEffect(() => {
|
|
40
38
|
if (
|
|
@@ -98,12 +96,6 @@ export function PageViewer({
|
|
|
98
96
|
{editContext?.statusMessage}
|
|
99
97
|
</div>
|
|
100
98
|
<div className="flex gap-1">
|
|
101
|
-
<SimpleIconButton
|
|
102
|
-
icon=<Ellipsis size={12} />
|
|
103
|
-
selected={editContext?.enableCompletions}
|
|
104
|
-
label="Enable Completions"
|
|
105
|
-
onClick={() => editContext?.setEnableCompletions((c) => !c)}
|
|
106
|
-
/>
|
|
107
99
|
<SimpleIconButton
|
|
108
100
|
icon="pi pi-step-forward"
|
|
109
101
|
selected={followEdits}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { EditContextType } from "../client/editContext";
|
|
2
|
+
import { executePrompt } from "./aiService";
|
|
3
|
+
import { PageViewContext } from "../page-viewer/pageViewContext";
|
|
4
|
+
|
|
5
|
+
export interface PageContext {
|
|
6
|
+
abstract: string;
|
|
7
|
+
pageTitle: string;
|
|
8
|
+
pageType: string;
|
|
9
|
+
lastGenerated: Date;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Cache for generated contexts to avoid repeated API calls
|
|
13
|
+
const contextCache = new Map<string, PageContext>();
|
|
14
|
+
const CONTEXT_CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generates a concise page abstract for use in completion prompts
|
|
18
|
+
* @param editContext - The current edit context
|
|
19
|
+
* @param pageViewContext - The page view context
|
|
20
|
+
* @returns Promise<PageContext> - The generated page context
|
|
21
|
+
*/
|
|
22
|
+
export async function generatePageContext(
|
|
23
|
+
editContext: EditContextType,
|
|
24
|
+
pageViewContext: PageViewContext,
|
|
25
|
+
): Promise<PageContext | null> {
|
|
26
|
+
if (!editContext.contentEditorItem) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Only generate context if completions are enabled
|
|
31
|
+
if (!editContext.enableCompletions) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cacheKey = `${editContext.contentEditorItem.id}-${editContext.contentEditorItem.language}-${editContext.contentEditorItem.version}`;
|
|
36
|
+
|
|
37
|
+
// Check cache first
|
|
38
|
+
const cached = contextCache.get(cacheKey);
|
|
39
|
+
if (
|
|
40
|
+
cached &&
|
|
41
|
+
Date.now() - cached.lastGenerated.getTime() < CONTEXT_CACHE_DURATION
|
|
42
|
+
) {
|
|
43
|
+
return cached;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Generate abstract using AI with get-content function
|
|
48
|
+
const messages = [
|
|
49
|
+
{
|
|
50
|
+
name: "system",
|
|
51
|
+
role: "system",
|
|
52
|
+
content:
|
|
53
|
+
"You are a content summarization tool. You have access to a get-content function that can extract text content from the current page. Use this function to get the page content, then create a concise abstract in exactly 100 tokens or less. Focus on the main topics, purpose, and key information. Be specific and informative. Your summary will be fed into a text completion prompt, so make it as concise as possible.",
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const result = await executePrompt(
|
|
58
|
+
messages,
|
|
59
|
+
editContext,
|
|
60
|
+
() => ({
|
|
61
|
+
endpoint: "/alpaca/editor/ai/prompt",
|
|
62
|
+
promptData: {
|
|
63
|
+
itemid: editContext.contentEditorItem!.id,
|
|
64
|
+
language: editContext.contentEditorItem!.language,
|
|
65
|
+
version: editContext.contentEditorItem!.version,
|
|
66
|
+
},
|
|
67
|
+
allowedFunctions: ["get-content"],
|
|
68
|
+
}),
|
|
69
|
+
{
|
|
70
|
+
profile: "context-generation",
|
|
71
|
+
addContextContent: false,
|
|
72
|
+
addAllContent: false,
|
|
73
|
+
},
|
|
74
|
+
undefined,
|
|
75
|
+
"gpt-4o-mini", // Use a fast, efficient model for context generation
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
console.log("result", result);
|
|
79
|
+
|
|
80
|
+
const abstract =
|
|
81
|
+
result?.messages?.[result?.messages.length - 1]?.content ||
|
|
82
|
+
"This page contains content that can be edited and enhanced with AI assistance.";
|
|
83
|
+
|
|
84
|
+
const pageContext: PageContext = {
|
|
85
|
+
abstract: abstract.substring(0, 500), // Ensure we don't exceed reasonable length
|
|
86
|
+
pageTitle: editContext.contentEditorItem.name || "Page",
|
|
87
|
+
pageType: editContext.contentEditorItem.templateName || "Content",
|
|
88
|
+
lastGenerated: new Date(),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Cache the result
|
|
92
|
+
contextCache.set(cacheKey, pageContext);
|
|
93
|
+
|
|
94
|
+
return pageContext;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("Error generating page context:", error);
|
|
97
|
+
|
|
98
|
+
// Return a fallback context
|
|
99
|
+
const fallbackContext: PageContext = {
|
|
100
|
+
abstract:
|
|
101
|
+
"This page contains content that can be edited and enhanced with AI assistance.",
|
|
102
|
+
pageTitle: editContext.contentEditorItem.name || "Page",
|
|
103
|
+
pageType: editContext.contentEditorItem.templateName || "Content",
|
|
104
|
+
lastGenerated: new Date(),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return fallbackContext;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Clears the context cache for a specific item or all items
|
|
113
|
+
* @param itemKey - Optional specific item key to clear, if not provided clears all
|
|
114
|
+
*/
|
|
115
|
+
export function clearContextCache(itemKey?: string): void {
|
|
116
|
+
if (itemKey) {
|
|
117
|
+
contextCache.delete(itemKey);
|
|
118
|
+
} else {
|
|
119
|
+
contextCache.clear();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets a cached context without regenerating it
|
|
125
|
+
* @param editContext - The current edit context
|
|
126
|
+
* @returns PageContext | null - The cached context if available
|
|
127
|
+
*/
|
|
128
|
+
export function getCachedContext(
|
|
129
|
+
editContext: EditContextType,
|
|
130
|
+
): PageContext | null {
|
|
131
|
+
if (!editContext.contentEditorItem) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const cacheKey = `${editContext.contentEditorItem.id}-${editContext.contentEditorItem.language}-${editContext.contentEditorItem.version}`;
|
|
136
|
+
const cached = contextCache.get(cacheKey);
|
|
137
|
+
|
|
138
|
+
if (
|
|
139
|
+
cached &&
|
|
140
|
+
Date.now() - cached.lastGenerated.getTime() < CONTEXT_CACHE_DURATION
|
|
141
|
+
) {
|
|
142
|
+
return cached;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { useEditContext } from "../client/editContext";
|
|
3
|
+
import { Switch } from "../../components/ui/switch";
|
|
4
|
+
import { Button } from "../../components/ui/button";
|
|
5
|
+
import { Badge } from "../../components/ui/badge";
|
|
6
|
+
import { Loader2, RefreshCw, FileText } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
generatePageContext,
|
|
10
|
+
getCachedContext,
|
|
11
|
+
clearContextCache,
|
|
12
|
+
PageContext,
|
|
13
|
+
} from "../services/contextService";
|
|
14
|
+
|
|
15
|
+
export function Completions() {
|
|
16
|
+
const editContext = useEditContext();
|
|
17
|
+
const [pageContext, setPageContext] = useState<PageContext | null>(null);
|
|
18
|
+
const [isGeneratingContext, setIsGeneratingContext] = useState(false);
|
|
19
|
+
|
|
20
|
+
// Load page context when component mounts or item changes
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (editContext) {
|
|
23
|
+
const cached = getCachedContext(editContext);
|
|
24
|
+
if (cached) {
|
|
25
|
+
setPageContext(cached);
|
|
26
|
+
} else {
|
|
27
|
+
loadPageContext();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}, [
|
|
31
|
+
editContext?.contentEditorItem?.id,
|
|
32
|
+
editContext?.contentEditorItem?.language,
|
|
33
|
+
editContext?.contentEditorItem?.version,
|
|
34
|
+
editContext?.enableCompletions,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const loadPageContext = async () => {
|
|
38
|
+
if (!editContext || !editContext.pageView) return;
|
|
39
|
+
|
|
40
|
+
setIsGeneratingContext(true);
|
|
41
|
+
try {
|
|
42
|
+
const context = await generatePageContext(
|
|
43
|
+
editContext,
|
|
44
|
+
editContext.pageView,
|
|
45
|
+
);
|
|
46
|
+
setPageContext(context);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("Failed to load page context:", error);
|
|
49
|
+
} finally {
|
|
50
|
+
setIsGeneratingContext(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const refreshPageContext = async () => {
|
|
55
|
+
if (!editContext?.contentEditorItem) return;
|
|
56
|
+
|
|
57
|
+
const cacheKey = `${editContext.contentEditorItem.id}-${editContext.contentEditorItem.language}-${editContext.contentEditorItem.version}`;
|
|
58
|
+
clearContextCache(cacheKey);
|
|
59
|
+
await loadPageContext();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex h-full flex-col space-y-4 p-4">
|
|
64
|
+
{/* Settings Section */}
|
|
65
|
+
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
|
66
|
+
<h3 className="mb-3 text-sm font-medium">Completion Settings</h3>
|
|
67
|
+
<div className="space-y-3">
|
|
68
|
+
<div className="flex items-center justify-between">
|
|
69
|
+
<div className="space-y-0.5">
|
|
70
|
+
<label className="text-sm font-medium">Enable Completions</label>
|
|
71
|
+
<p className="text-xs text-gray-600">
|
|
72
|
+
Allow AI to suggest completions while typing
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
<Switch
|
|
76
|
+
checked={editContext?.enableCompletions || false}
|
|
77
|
+
onCheckedChange={(checked: boolean) =>
|
|
78
|
+
editContext?.setEnableCompletions(checked)
|
|
79
|
+
}
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{/* Page Context Section */}
|
|
86
|
+
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
|
87
|
+
<div className="mb-3 flex items-center justify-between">
|
|
88
|
+
<h3 className="text-sm font-medium">Page Context</h3>
|
|
89
|
+
<Button
|
|
90
|
+
variant="ghost"
|
|
91
|
+
size="sm"
|
|
92
|
+
onClick={refreshPageContext}
|
|
93
|
+
disabled={isGeneratingContext}
|
|
94
|
+
>
|
|
95
|
+
{isGeneratingContext ? (
|
|
96
|
+
<Loader2 className="h-3 w-3 animate-spin" />
|
|
97
|
+
) : (
|
|
98
|
+
<RefreshCw className="h-3 w-3 stroke-1" />
|
|
99
|
+
)}
|
|
100
|
+
</Button>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{isGeneratingContext ? (
|
|
104
|
+
<div className="flex items-center gap-2 text-gray-500">
|
|
105
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
106
|
+
<span className="text-sm">Generating context...</span>
|
|
107
|
+
</div>
|
|
108
|
+
) : pageContext ? (
|
|
109
|
+
<div className="space-y-2">
|
|
110
|
+
<div className="flex items-center gap-2">
|
|
111
|
+
<FileText className="h-4 w-4 stroke-1 text-blue-500" />
|
|
112
|
+
<span className="text-sm font-medium">
|
|
113
|
+
{pageContext.pageTitle}
|
|
114
|
+
</span>
|
|
115
|
+
<Badge variant="outline" className="text-xs">
|
|
116
|
+
{pageContext.pageType}
|
|
117
|
+
</Badge>
|
|
118
|
+
</div>
|
|
119
|
+
<p className="text-xs leading-relaxed text-gray-600">
|
|
120
|
+
{pageContext.abstract}
|
|
121
|
+
</p>
|
|
122
|
+
<div className="text-xs text-gray-400">
|
|
123
|
+
Generated: {pageContext.lastGenerated.toLocaleTimeString()}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
) : (
|
|
127
|
+
<div className="py-4 text-center text-gray-500">
|
|
128
|
+
<FileText className="mx-auto mb-2 h-6 w-6 opacity-50" />
|
|
129
|
+
<p className="text-sm">No context available</p>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{/* Keyboard Shortcuts */}
|
|
135
|
+
<div className="rounded-lg border bg-white p-4 shadow-sm">
|
|
136
|
+
<h3 className="mb-3 text-sm font-medium">Keyboard Shortcuts</h3>
|
|
137
|
+
<div className="space-y-1">
|
|
138
|
+
<div className="flex justify-between text-xs">
|
|
139
|
+
<span>Apply completion</span>
|
|
140
|
+
<kbd className="rounded bg-gray-100 px-2 py-1 text-gray-600">
|
|
141
|
+
Tab
|
|
142
|
+
</kbd>
|
|
143
|
+
</div>
|
|
144
|
+
<div className="flex justify-between text-xs">
|
|
145
|
+
<span>Request completion</span>
|
|
146
|
+
<kbd className="rounded bg-gray-100 px-2 py-1 text-gray-600">
|
|
147
|
+
Ctrl+Space
|
|
148
|
+
</kbd>
|
|
149
|
+
</div>
|
|
150
|
+
<div className="flex justify-between text-xs">
|
|
151
|
+
<span>Dismiss completion</span>
|
|
152
|
+
<kbd className="rounded bg-gray-100 px-2 py-1 text-gray-600">
|
|
153
|
+
Esc
|
|
154
|
+
</kbd>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
@@ -15,7 +15,7 @@ export function Validation() {
|
|
|
15
15
|
...y,
|
|
16
16
|
language: x.item.language,
|
|
17
17
|
version: x.item.version,
|
|
18
|
-
}))
|
|
18
|
+
})),
|
|
19
19
|
);
|
|
20
20
|
|
|
21
21
|
if (!validationResults) return null;
|
|
@@ -26,7 +26,7 @@ export function Validation() {
|
|
|
26
26
|
validationResult.language +
|
|
27
27
|
validationResult.version;
|
|
28
28
|
return (
|
|
29
|
-
<div className="flex gap-2
|
|
29
|
+
<div className="flex items-center gap-2">
|
|
30
30
|
<span
|
|
31
31
|
className={`name-tooltip-${id} break-all`}
|
|
32
32
|
style={{ position: "relative" }}
|
|
@@ -55,7 +55,7 @@ export function Validation() {
|
|
|
55
55
|
|
|
56
56
|
if (validationResults.length === 0) {
|
|
57
57
|
return (
|
|
58
|
-
<div className="text-gray-
|
|
58
|
+
<div className="text-gray-2 p-4 text-center text-xs">
|
|
59
59
|
No validation results
|
|
60
60
|
</div>
|
|
61
61
|
);
|
|
@@ -64,7 +64,7 @@ export function Validation() {
|
|
|
64
64
|
return (
|
|
65
65
|
<div>
|
|
66
66
|
{editContext?.validating && (
|
|
67
|
-
<div className="text-center
|
|
67
|
+
<div className="p-3 text-center">
|
|
68
68
|
<ProgressSpinner style={{ width: "20px", height: "20px" }} />
|
|
69
69
|
</div>
|
|
70
70
|
)}
|
|
@@ -78,21 +78,20 @@ export function Validation() {
|
|
|
78
78
|
language: data.item.language,
|
|
79
79
|
version: data.item.version,
|
|
80
80
|
};
|
|
81
|
-
const item =
|
|
82
|
-
itemDescriptor
|
|
83
|
-
);
|
|
81
|
+
const item =
|
|
82
|
+
await editContext?.itemsRepository.getItem(itemDescriptor);
|
|
84
83
|
if (!item || !editContext) return;
|
|
85
84
|
editContext.setCenterPanelView(
|
|
86
85
|
editContext.configuration.editor.views.find(
|
|
87
|
-
(x) => x.name === "content-editor"
|
|
88
|
-
)?.defaultCenterPanelView
|
|
86
|
+
(x) => x.name === "content-editor",
|
|
87
|
+
)?.defaultCenterPanelView,
|
|
89
88
|
);
|
|
90
89
|
editContext.setFocusedField(
|
|
91
90
|
{
|
|
92
91
|
item: itemDescriptor,
|
|
93
92
|
fieldId: data.item.fieldId,
|
|
94
93
|
},
|
|
95
|
-
true
|
|
94
|
+
true,
|
|
96
95
|
);
|
|
97
96
|
}}
|
|
98
97
|
></SimpleTable>
|
|
@@ -495,7 +495,7 @@ const NodeContent = memo(
|
|
|
495
495
|
const renderToggle = () => {
|
|
496
496
|
if (node.hasChildren && node.children === null) {
|
|
497
497
|
return (
|
|
498
|
-
<div className="flex h-[
|
|
498
|
+
<div className="flex h-[23x] w-[24px] items-center justify-center">
|
|
499
499
|
<ProgressSpinner
|
|
500
500
|
style={{ width: "16px", height: "16px" }}
|
|
501
501
|
className="text-gray-500"
|
|
@@ -511,7 +511,7 @@ const NodeContent = memo(
|
|
|
511
511
|
isExpanded ? "rotate-90" : "rotate-0"
|
|
512
512
|
}`}
|
|
513
513
|
>
|
|
514
|
-
<ChevronRight strokeWidth={1} className="h-
|
|
514
|
+
<ChevronRight strokeWidth={1} className="h-5.5 w-5.5" />
|
|
515
515
|
</span>
|
|
516
516
|
);
|
|
517
517
|
};
|
|
@@ -588,6 +588,22 @@ export const PerfectTree = <T,>({
|
|
|
588
588
|
// When toggling a node, notify parent and trigger external lazy load if needed.
|
|
589
589
|
const handleToggle = useCallback(
|
|
590
590
|
(node: TreeNode<T>) => {
|
|
591
|
+
const isCurrentlyExpanded = expandedKeys.includes(node.key);
|
|
592
|
+
|
|
593
|
+
// If the node is being expanded (not collapsed), clear the search filter and select the node
|
|
594
|
+
if (!isCurrentlyExpanded) {
|
|
595
|
+
setSearchTerm("");
|
|
596
|
+
// Clear any pending search timeout
|
|
597
|
+
if (searchTimeoutRef.current) {
|
|
598
|
+
clearTimeout(searchTimeoutRef.current);
|
|
599
|
+
searchTimeoutRef.current = null;
|
|
600
|
+
}
|
|
601
|
+
// Select the node being expanded for quick navigation
|
|
602
|
+
if (onSelect) {
|
|
603
|
+
onSelect(node.key, {} as React.MouseEvent);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
591
607
|
if (onToggleExpand) {
|
|
592
608
|
onToggleExpand(node.key);
|
|
593
609
|
}
|
|
@@ -597,7 +613,7 @@ export const PerfectTree = <T,>({
|
|
|
597
613
|
onLazyLoad(node);
|
|
598
614
|
}
|
|
599
615
|
},
|
|
600
|
-
[onToggleExpand, onLazyLoad],
|
|
616
|
+
[onToggleExpand, onLazyLoad, expandedKeys, onSelect],
|
|
601
617
|
);
|
|
602
618
|
|
|
603
619
|
const handleSelect = useCallback(
|
|
@@ -13,11 +13,13 @@ export function SimpleTabs({
|
|
|
13
13
|
setActiveTab,
|
|
14
14
|
activeTab,
|
|
15
15
|
className,
|
|
16
|
+
tabClassName,
|
|
16
17
|
}: {
|
|
17
18
|
tabs: Tab[];
|
|
18
19
|
setActiveTab: (index: number) => void;
|
|
19
20
|
activeTab: number;
|
|
20
21
|
className?: string;
|
|
22
|
+
tabClassName?: string;
|
|
21
23
|
}) {
|
|
22
24
|
if (activeTab > tabs.length - 1) {
|
|
23
25
|
activeTab = 0;
|
|
@@ -36,7 +38,8 @@ export function SimpleTabs({
|
|
|
36
38
|
<button
|
|
37
39
|
id={tab.id}
|
|
38
40
|
className={twMerge(
|
|
39
|
-
"
|
|
41
|
+
"min-w-0 flex-shrink cursor-pointer items-center px-3 pb-2 whitespace-nowrap",
|
|
42
|
+
tabClassName,
|
|
40
43
|
activeTab === index ? "active-tab" : "",
|
|
41
44
|
)}
|
|
42
45
|
key={tab.id}
|
package/src/revision.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = "1.0.
|
|
2
|
-
export const buildDate = "2025-07-
|
|
1
|
+
export const version = "1.0.3980";
|
|
2
|
+
export const buildDate = "2025-07-07 01:46:37";
|
package/src/types.ts
CHANGED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { ItemDescriptor } from "../pageModel";
|
|
2
|
-
import { ResultItem } from "../ui/ItemSearch";
|
|
3
|
-
export declare function BrowseHistory({ setHoveredItem, itemSelected, }: {
|
|
4
|
-
setHoveredItem: (item: ResultItem | undefined) => void;
|
|
5
|
-
itemSelected: (item: ItemDescriptor) => void;
|
|
6
|
-
}): import("react/jsx-runtime").JSX.Element | undefined;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEditContext } from "../client/editContext";
|
|
3
|
-
import { ItemList } from "../ui/ItemList";
|
|
4
|
-
export function BrowseHistory({ setHoveredItem, itemSelected, }) {
|
|
5
|
-
const editContext = useEditContext();
|
|
6
|
-
if (!editContext)
|
|
7
|
-
return;
|
|
8
|
-
const history = editContext.browseHistory;
|
|
9
|
-
return (_jsx(ItemList, { items: history, onItemHover: setHoveredItem, onItemClick: itemSelected, maxItems: 20 }));
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=BrowseHistory.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BrowseHistory.js","sourceRoot":"","sources":["../../../src/editor/menubar/BrowseHistory.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,EAC5B,cAAc,EACd,YAAY,GAIb;IACC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,IAAI,CAAC,WAAW;QAAE,OAAO;IAEzB,MAAM,OAAO,GAAG,WAAW,CAAC,aAAa,CAAC;IAE1C,OAAO,CACL,KAAC,QAAQ,IACP,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,cAAc,EAC3B,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,EAAE,GACZ,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useEditContext } from "../client/editContext";
|
|
2
|
-
|
|
3
|
-
import { ItemDescriptor } from "../pageModel";
|
|
4
|
-
import { ResultItem } from "../ui/ItemSearch";
|
|
5
|
-
import { ItemList } from "../ui/ItemList";
|
|
6
|
-
|
|
7
|
-
export function BrowseHistory({
|
|
8
|
-
setHoveredItem,
|
|
9
|
-
itemSelected,
|
|
10
|
-
}: {
|
|
11
|
-
setHoveredItem: (item: ResultItem | undefined) => void;
|
|
12
|
-
itemSelected: (item: ItemDescriptor) => void;
|
|
13
|
-
}) {
|
|
14
|
-
const editContext = useEditContext();
|
|
15
|
-
|
|
16
|
-
if (!editContext) return;
|
|
17
|
-
|
|
18
|
-
const history = editContext.browseHistory;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<ItemList
|
|
22
|
-
items={history}
|
|
23
|
-
onItemHover={setHoveredItem}
|
|
24
|
-
onItemClick={itemSelected}
|
|
25
|
-
maxItems={20}
|
|
26
|
-
/>
|
|
27
|
-
);
|
|
28
|
-
}
|