mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-01 17:10:24 +08:00
Compare commits
12 Commits
fix/electr
...
features/v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
011b883cdb | ||
|
|
fd9302e736 | ||
|
|
fa06c61538 | ||
|
|
60994d281e | ||
|
|
6640272f90 | ||
|
|
86560e0fc0 | ||
|
|
bb6c7c11ce | ||
|
|
6b0296a97f | ||
|
|
85513b8c00 | ||
|
|
24270e2622 | ||
|
|
07c9e7d758 | ||
|
|
ee4c0149f1 |
21
.github/workflows/electron-release.yml
vendored
21
.github/workflows/electron-release.yml
vendored
@@ -34,15 +34,6 @@ jobs:
|
||||
node-version: 24
|
||||
cache: "npm"
|
||||
|
||||
- name: Download draw.io static files for offline use
|
||||
run: |
|
||||
rm -rf public/drawio
|
||||
git clone --depth 1 --branch v29.3.5 https://github.com/jgraph/drawio.git /tmp/drawio
|
||||
mkdir -p public/drawio
|
||||
cp -r /tmp/drawio/src/main/webapp/* public/drawio/
|
||||
rm -rf public/drawio/WEB-INF
|
||||
rm -rf public/drawio/META-INF
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
@@ -66,16 +57,6 @@ jobs:
|
||||
node-version: 24
|
||||
cache: "npm"
|
||||
|
||||
- name: Download draw.io static files for offline use
|
||||
shell: bash
|
||||
run: |
|
||||
rm -rf public/drawio
|
||||
git clone --depth 1 --branch v29.3.5 https://github.com/jgraph/drawio.git /tmp/drawio
|
||||
mkdir -p public/drawio
|
||||
cp -r /tmp/drawio/src/main/webapp/* public/drawio/
|
||||
rm -rf public/drawio/WEB-INF
|
||||
rm -rf public/drawio/META-INF
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
@@ -99,7 +80,7 @@ jobs:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: '880a211d-2cd3-4e7b-8d04-3d1f8eb39df5'
|
||||
project-slug: 'next-ai-draw-io'
|
||||
signing-policy-slug: 'release-signing'
|
||||
signing-policy-slug: 'test-signing'
|
||||
artifact-configuration-slug: 'windows-exe'
|
||||
github-artifact-id: ${{ steps.upload-unsigned.outputs.artifact-id }}
|
||||
wait-for-completion: true
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -56,8 +56,6 @@ push-via-ec2.sh
|
||||
/dist-electron/
|
||||
/release/
|
||||
/electron-standalone/
|
||||
# Draw.io static files (downloaded during CI build)
|
||||
public/drawio/
|
||||
*.dmg
|
||||
*.exe
|
||||
*.AppImage
|
||||
|
||||
@@ -11,16 +11,13 @@ import {
|
||||
} from "@/components/ui/resizable"
|
||||
import { useDiagram } from "@/contexts/diagram-context"
|
||||
import { i18n, type Locale } from "@/lib/i18n/config"
|
||||
import { isIndexedDBUsable } from "@/lib/session-storage"
|
||||
|
||||
const drawioBaseUrl =
|
||||
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net"
|
||||
|
||||
export default function Home() {
|
||||
const {
|
||||
drawioRef,
|
||||
handleDiagramExport,
|
||||
handleDiagramAutoSave,
|
||||
onDrawioLoad,
|
||||
resetDrawioReady,
|
||||
} = useDiagram()
|
||||
const { drawioRef, handleDiagramExport, onDrawioLoad, resetDrawioReady } =
|
||||
useDiagram()
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
// Extract current language from pathname (e.g., "/zh/about" → "zh")
|
||||
@@ -31,11 +28,6 @@ export default function Home() {
|
||||
const [darkMode, setDarkMode] = useState(false)
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
const [isDrawioReady, setIsDrawioReady] = useState(false)
|
||||
const [isElectron, setIsElectron] = useState(false)
|
||||
const [canPersist, setCanPersist] = useState(false)
|
||||
const [drawioBaseUrl, setDrawioBaseUrl] = useState(
|
||||
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net",
|
||||
)
|
||||
|
||||
const chatPanelRef = useRef<ImperativePanelHandle>(null)
|
||||
const isMobileRef = useRef(false)
|
||||
@@ -72,21 +64,6 @@ export default function Home() {
|
||||
document.documentElement.classList.toggle("dark", prefersDark)
|
||||
}
|
||||
|
||||
// Detect Electron and use bundled draw.io files for offline use
|
||||
// Note: react-drawio uses `new URL(baseUrl)` so we need absolute URL
|
||||
// Include /index.html because Next.js doesn't auto-serve index.html for directories
|
||||
const electronDetected =
|
||||
!process.env.NEXT_PUBLIC_DRAWIO_BASE_URL &&
|
||||
!!(window as unknown as { electronAPI?: unknown }).electronAPI
|
||||
if (electronDetected) {
|
||||
setIsElectron(true)
|
||||
setDrawioBaseUrl(`${window.location.origin}/drawio/index.html`)
|
||||
}
|
||||
|
||||
void (async () => {
|
||||
const usable = await isIndexedDBUsable()
|
||||
setCanPersist(usable)
|
||||
})()
|
||||
setIsLoaded(true)
|
||||
}, [pathname, router])
|
||||
|
||||
@@ -95,17 +72,6 @@ export default function Home() {
|
||||
onDrawioLoad()
|
||||
}, [onDrawioLoad])
|
||||
|
||||
const handleDrawioAutoSave = useCallback(
|
||||
(data: { xml?: string }) => {
|
||||
handleDiagramAutoSave(data)
|
||||
// Only suppress modified state when persistence is available
|
||||
if (canPersist) {
|
||||
drawioRef.current?.status({ message: "", modified: false })
|
||||
}
|
||||
},
|
||||
[canPersist, drawioRef, handleDiagramAutoSave],
|
||||
)
|
||||
|
||||
const handleDarkModeChange = () => {
|
||||
const newValue = !darkMode
|
||||
setDarkMode(newValue)
|
||||
@@ -194,36 +160,20 @@ export default function Home() {
|
||||
className={`h-full w-full ${isDrawioReady ? "" : "invisible absolute inset-0"}`}
|
||||
>
|
||||
<DrawIoEmbed
|
||||
key={`${drawioUi}-${darkMode}-${currentLang}-${isElectron}`}
|
||||
key={`${drawioUi}-${darkMode}-${currentLang}`}
|
||||
ref={drawioRef}
|
||||
autosave
|
||||
onAutoSave={handleDrawioAutoSave}
|
||||
onExport={handleDiagramExport}
|
||||
onLoad={handleDrawioLoad}
|
||||
baseUrl={drawioBaseUrl}
|
||||
configuration={
|
||||
canPersist
|
||||
? { confirmExit: false }
|
||||
: undefined
|
||||
}
|
||||
urlParameters={{
|
||||
ui: drawioUi,
|
||||
spin: false,
|
||||
libraries: false,
|
||||
// Disable modified tracking only when persistence is available
|
||||
...(canPersist && {
|
||||
modified: false,
|
||||
keepmodified: false,
|
||||
}),
|
||||
saveAndExit: false,
|
||||
noSaveBtn: true,
|
||||
noExitBtn: true,
|
||||
dark: darkMode,
|
||||
lang: currentLang,
|
||||
// Enable offline mode in Electron to disable external service calls
|
||||
...(isElectron && {
|
||||
offline: true,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -118,10 +118,7 @@ async function handleChatRequest(req: Request): Promise<Response> {
|
||||
// === SERVER-SIDE QUOTA CHECK START ===
|
||||
// Quota is opt-in: only enabled when DYNAMODB_QUOTA_TABLE env var is set
|
||||
const hasOwnApiKey = !!(
|
||||
req.headers.get("x-ai-provider") &&
|
||||
(req.headers.get("x-ai-api-key") ||
|
||||
req.headers.get("x-aws-access-key-id") ||
|
||||
req.headers.get("x-vertex-api-key"))
|
||||
req.headers.get("x-ai-provider") && req.headers.get("x-ai-api-key")
|
||||
)
|
||||
|
||||
// Skip quota check if: quota disabled, user has own API key, or is anonymous
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Copy,
|
||||
FileCode,
|
||||
FileText,
|
||||
Link,
|
||||
Pencil,
|
||||
RotateCcw,
|
||||
ThumbsDown,
|
||||
@@ -60,20 +59,20 @@ function getCompleteOperations(
|
||||
|
||||
import { useDiagram } from "@/contexts/diagram-context"
|
||||
|
||||
// Helper to split text content into regular text and file/URL sections (PDF, text files, or URLs)
|
||||
// Helper to split text content into regular text and file sections (PDF or text files)
|
||||
interface TextSection {
|
||||
type: "text" | "file" | "url"
|
||||
type: "text" | "file"
|
||||
content: string
|
||||
filename?: string
|
||||
charCount?: number
|
||||
fileType?: "pdf" | "text" | "url"
|
||||
fileType?: "pdf" | "text"
|
||||
}
|
||||
|
||||
function splitTextIntoFileSections(text: string): TextSection[] {
|
||||
const sections: TextSection[] = []
|
||||
// Match [PDF: filename], [File: filename], or [URL: url] patterns
|
||||
// Match [PDF: filename] or [File: filename] patterns
|
||||
const filePattern =
|
||||
/\[(PDF|File|URL):\s*([^\]]+)\]\n([\s\S]*?)(?=\n\n\[(PDF|File|URL):|$)/g
|
||||
/\[(PDF|File):\s*([^\]]+)\]\n([\s\S]*?)(?=\n\n\[(PDF|File):|$)/g
|
||||
let lastIndex = 0
|
||||
let match
|
||||
|
||||
@@ -84,34 +83,28 @@ function splitTextIntoFileSections(text: string): TextSection[] {
|
||||
sections.push({ type: "text", content: beforeText })
|
||||
}
|
||||
|
||||
// Add file/url section
|
||||
const sectionType = match[1].toLowerCase()
|
||||
const fileType =
|
||||
sectionType === "pdf"
|
||||
? "pdf"
|
||||
: sectionType === "url"
|
||||
? "url"
|
||||
: "text"
|
||||
// Add file section
|
||||
const fileType = match[1].toLowerCase() === "pdf" ? "pdf" : "text"
|
||||
const filename = match[2].trim()
|
||||
const content = match[3].trim()
|
||||
const fileContent = match[3].trim()
|
||||
sections.push({
|
||||
type: sectionType === "url" ? "url" : "file",
|
||||
content: content,
|
||||
type: "file",
|
||||
content: fileContent,
|
||||
filename,
|
||||
charCount: content.length,
|
||||
charCount: fileContent.length,
|
||||
fileType,
|
||||
})
|
||||
|
||||
lastIndex = match.index + match[0].length
|
||||
}
|
||||
|
||||
// Add remaining text after last section
|
||||
// Add remaining text after last file section
|
||||
const remainingText = text.slice(lastIndex).trim()
|
||||
if (remainingText) {
|
||||
sections.push({ type: "text", content: remainingText })
|
||||
}
|
||||
|
||||
// If no file/url sections found, return original text
|
||||
// If no file sections found, return original text
|
||||
if (sections.length === 0) {
|
||||
sections.push({ type: "text", content: text })
|
||||
}
|
||||
@@ -130,8 +123,8 @@ const getMessageTextContent = (message: UIMessage): string => {
|
||||
// Get only the user's original text, excluding appended file content
|
||||
const getUserOriginalText = (message: UIMessage): string => {
|
||||
const fullText = getMessageTextContent(message)
|
||||
// Strip out [PDF: ...], [File: ...], and [URL: ...] sections that were appended
|
||||
const filePattern = /\n\n\[(PDF|File|URL):\s*[^\]]+\]\n[\s\S]*$/
|
||||
// Strip out [PDF: ...] and [File: ...] sections that were appended
|
||||
const filePattern = /\n\n\[(PDF|File):\s*[^\]]+\]\n[\s\S]*$/
|
||||
return fullText.replace(filePattern, "").trim()
|
||||
}
|
||||
|
||||
@@ -1091,14 +1084,12 @@ export function ChatMessageDisplay({
|
||||
) => {
|
||||
if (
|
||||
section.type ===
|
||||
"file" ||
|
||||
section.type ===
|
||||
"url"
|
||||
"file"
|
||||
) {
|
||||
const sectionKey = `${message.id}-${section.type}-${partIndex}-${sectionIndex}`
|
||||
const pdfKey = `${message.id}-file-${partIndex}-${sectionIndex}`
|
||||
const isExpanded =
|
||||
expandedPdfSections[
|
||||
sectionKey
|
||||
pdfKey
|
||||
] ??
|
||||
false
|
||||
const charDisplay =
|
||||
@@ -1107,27 +1098,10 @@ export function ChatMessageDisplay({
|
||||
1000
|
||||
? `${(section.charCount / 1000).toFixed(1)}k`
|
||||
: section.charCount
|
||||
|
||||
// Icon selector
|
||||
const Icon =
|
||||
section.fileType ===
|
||||
"pdf"
|
||||
? FileText
|
||||
: section.fileType ===
|
||||
"url"
|
||||
? Link
|
||||
: FileCode
|
||||
|
||||
const iconColor =
|
||||
section.fileType ===
|
||||
"pdf"
|
||||
? "text-red-500"
|
||||
: "text-blue-700"
|
||||
|
||||
return (
|
||||
<div
|
||||
key={
|
||||
sectionKey
|
||||
pdfKey
|
||||
}
|
||||
className="rounded-lg border border-border/60 bg-muted/30 overflow-hidden"
|
||||
>
|
||||
@@ -1142,7 +1116,7 @@ export function ChatMessageDisplay({
|
||||
prev,
|
||||
) => ({
|
||||
...prev,
|
||||
[sectionKey]:
|
||||
[pdfKey]:
|
||||
!isExpanded,
|
||||
}),
|
||||
)
|
||||
@@ -1150,10 +1124,13 @@ export function ChatMessageDisplay({
|
||||
className="w-full flex items-center justify-between px-3 py-2 hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon
|
||||
className={`h-4 w-4 ${iconColor}`}
|
||||
/>
|
||||
<span className="text-xs font-medium truncate max-w-[200px]">
|
||||
{section.fileType ===
|
||||
"pdf" ? (
|
||||
<FileText className="h-4 w-4 text-red-500" />
|
||||
) : (
|
||||
<FileCode className="h-4 w-4 text-blue-500" />
|
||||
)}
|
||||
<span className="text-xs font-medium">
|
||||
{
|
||||
section.filename
|
||||
}
|
||||
|
||||
@@ -137,22 +137,26 @@ export default function ChatPanel({
|
||||
const onFetchChart = (saveToHistory = true) => {
|
||||
return Promise.race([
|
||||
new Promise<string>((resolve) => {
|
||||
resolverRef.current = resolve
|
||||
if (resolverRef && "current" in resolverRef) {
|
||||
resolverRef.current = resolve
|
||||
}
|
||||
if (saveToHistory) {
|
||||
onExport()
|
||||
} else {
|
||||
handleExportWithoutHistory()
|
||||
}
|
||||
}),
|
||||
new Promise<string>((_, reject) => {
|
||||
const currentResolver = resolverRef.current
|
||||
setTimeout(() => {
|
||||
if (resolverRef.current === currentResolver) {
|
||||
resolverRef.current = null
|
||||
}
|
||||
reject(new Error("Chart export timed out after 10 seconds"))
|
||||
}, 10000)
|
||||
}),
|
||||
new Promise<string>((_, reject) =>
|
||||
setTimeout(
|
||||
() =>
|
||||
reject(
|
||||
new Error(
|
||||
"Chart export timed out after 10 seconds",
|
||||
),
|
||||
),
|
||||
10000,
|
||||
),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -673,7 +677,7 @@ export default function ChatPanel({
|
||||
// Debounce: save after 1 second of no changes
|
||||
localStorageDebounceRef.current = setTimeout(async () => {
|
||||
try {
|
||||
if (messages.length > 0 || hasDiagramNow) {
|
||||
if (messages.length > 0) {
|
||||
const sessionData = await buildSessionData({
|
||||
// Only capture thumbnail if there was a diagram AND this isn't a no-diagram session
|
||||
withThumbnail: hasDiagramNow && !isNodiagramSession,
|
||||
@@ -695,7 +699,6 @@ export default function ChatPanel({
|
||||
}
|
||||
}
|
||||
}, [
|
||||
chartXML,
|
||||
messages,
|
||||
status,
|
||||
sessionIsAvailable,
|
||||
@@ -726,8 +729,7 @@ export default function ChatPanel({
|
||||
const handleVisibilityChange = async () => {
|
||||
if (
|
||||
document.visibilityState === "hidden" &&
|
||||
(messagesRef.current.length > 0 ||
|
||||
isRealDiagram(chartXMLRef.current))
|
||||
messagesRef.current.length > 0
|
||||
) {
|
||||
try {
|
||||
// Attempt to save session - browser may not wait for completion
|
||||
|
||||
@@ -443,12 +443,12 @@ export function ModelConfigDialog({
|
||||
}}
|
||||
className={cn(
|
||||
"group flex items-center gap-3 px-3 py-2.5 rounded-xl w-full",
|
||||
"text-left text-sm transition-all duration-150 border border-transparent",
|
||||
"text-left text-sm transition-all duration-150",
|
||||
"hover:bg-interactive-hover",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
selectedProviderId ===
|
||||
provider.id &&
|
||||
"bg-surface-0 shadow-sm border-border-subtle",
|
||||
"bg-surface-0 shadow-sm ring-1 ring-border-subtle",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -997,19 +997,10 @@ export function ModelConfigDialog({
|
||||
className="text-xs font-medium flex items-center gap-1.5"
|
||||
>
|
||||
<Link2 className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
{formatMessage(
|
||||
dict.modelConfig
|
||||
.baseUrlWithExample,
|
||||
{
|
||||
example:
|
||||
PROVIDER_INFO[
|
||||
selectedProvider
|
||||
.provider
|
||||
]
|
||||
.defaultBaseUrl ||
|
||||
"https://api.example.com/v1",
|
||||
},
|
||||
)}
|
||||
Base URL{" "}
|
||||
<span className="text-muted-foreground font-normal">
|
||||
(optional)
|
||||
</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="vertex-base-url"
|
||||
@@ -1213,19 +1204,17 @@ export function ModelConfigDialog({
|
||||
className="text-xs font-medium flex items-center gap-1.5"
|
||||
>
|
||||
<Link2 className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
{formatMessage(
|
||||
{
|
||||
dict.modelConfig
|
||||
.baseUrlWithExample,
|
||||
.baseUrl
|
||||
}
|
||||
<span className="text-muted-foreground font-normal">
|
||||
{
|
||||
example:
|
||||
PROVIDER_INFO[
|
||||
selectedProvider
|
||||
.provider
|
||||
]
|
||||
.defaultBaseUrl ||
|
||||
"https://api.example.com/v1",
|
||||
},
|
||||
)}
|
||||
dict
|
||||
.modelConfig
|
||||
.optional
|
||||
}
|
||||
</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="base-url"
|
||||
|
||||
@@ -455,7 +455,7 @@ function SettingsContent({
|
||||
>
|
||||
<SelectTrigger
|
||||
id="send-shortcut-select"
|
||||
className="w-auto h-9 rounded-xl"
|
||||
className="w-[170px] h-9 rounded-xl"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
|
||||
@@ -20,10 +20,9 @@ interface DiagramContextType {
|
||||
loadDiagram: (chart: string, skipValidation?: boolean) => string | null
|
||||
handleExport: () => void
|
||||
handleExportWithoutHistory: () => void
|
||||
resolverRef: React.MutableRefObject<((value: string) => void) | null>
|
||||
drawioRef: React.MutableRefObject<DrawIoEmbedRef | null>
|
||||
resolverRef: React.Ref<((value: string) => void) | null>
|
||||
drawioRef: React.Ref<DrawIoEmbedRef | null>
|
||||
handleDiagramExport: (data: any) => void
|
||||
handleDiagramAutoSave: (data: { xml?: string }) => void
|
||||
clearDiagram: () => void
|
||||
saveDiagramToFile: (
|
||||
filename: string,
|
||||
@@ -268,11 +267,6 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleDiagramAutoSave = (data: { xml?: string }) => {
|
||||
if (!data?.xml) return
|
||||
setChartXML(data.xml)
|
||||
}
|
||||
|
||||
const clearDiagram = () => {
|
||||
const emptyDiagram = `<mxfile><diagram name="Page-1" id="page-1"><mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/></root></mxGraphModel></diagram></mxfile>`
|
||||
// Skip validation for trusted internal template (loadDiagram also sets chartXML)
|
||||
@@ -397,7 +391,6 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
|
||||
resolverRef,
|
||||
drawioRef,
|
||||
handleDiagramExport,
|
||||
handleDiagramAutoSave,
|
||||
clearDiagram,
|
||||
saveDiagramToFile,
|
||||
getThumbnailSvg,
|
||||
|
||||
@@ -37,11 +37,10 @@ mac:
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
# Disable electron-builder's signing - we use custom ad-hoc signing in afterPack
|
||||
# to properly sign nested bundles with --deep flag for bundled draw.io files
|
||||
identity: null
|
||||
hardenedRuntime: false
|
||||
hardenedRuntime: true
|
||||
gatekeeperAssess: false
|
||||
entitlements: resources/entitlements.mac.plist
|
||||
entitlementsInherit: resources/entitlements.mac.plist
|
||||
|
||||
dmg:
|
||||
contents:
|
||||
|
||||
@@ -302,7 +302,6 @@
|
||||
"enterSecretKey": "Enter your secret access key",
|
||||
"baseUrl": "Base URL",
|
||||
"optional": "(optional)",
|
||||
"baseUrlWithExample": "Base URL (optional, e.g. {example})",
|
||||
"customEndpoint": "Custom endpoint URL",
|
||||
"models": "Models",
|
||||
"customModelId": "Custom model ID...",
|
||||
|
||||
@@ -302,7 +302,6 @@
|
||||
"enterSecretKey": "シークレットアクセスキーを入力",
|
||||
"baseUrl": "ベース URL",
|
||||
"optional": "(オプション)",
|
||||
"baseUrlWithExample": "ベース URL(オプション、例: {example})",
|
||||
"customEndpoint": "カスタムエンドポイント URL",
|
||||
"models": "モデル",
|
||||
"customModelId": "カスタムモデル ID...",
|
||||
|
||||
@@ -302,7 +302,6 @@
|
||||
"enterSecretKey": "输入您的 Secret Key",
|
||||
"baseUrl": "基础 URL",
|
||||
"optional": "(可选)",
|
||||
"baseUrlWithExample": "基础 URL(可选,例如 {example})",
|
||||
"customEndpoint": "自定义端点 URL",
|
||||
"models": "模型",
|
||||
"customModelId": "自定义模型 ID...",
|
||||
|
||||
@@ -75,18 +75,6 @@ export function isIndexedDBAvailable(): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if IndexedDB is actually usable (not just present).
|
||||
export async function isIndexedDBUsable(): Promise<boolean> {
|
||||
if (!isIndexedDBAvailable()) return false
|
||||
try {
|
||||
const db = await getDB()
|
||||
db.close()
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// CRUD Operations
|
||||
export async function getAllSessionMetadata(): Promise<SessionMetadata[]> {
|
||||
if (!isIndexedDBAvailable()) return []
|
||||
|
||||
@@ -83,36 +83,21 @@ export const PROVIDER_INFO: Record<
|
||||
ProviderName,
|
||||
{ label: string; defaultBaseUrl?: string }
|
||||
> = {
|
||||
openai: {
|
||||
label: "OpenAI",
|
||||
defaultBaseUrl: "https://api.openai.com/v1",
|
||||
},
|
||||
openai: { label: "OpenAI" },
|
||||
anthropic: {
|
||||
label: "Anthropic",
|
||||
defaultBaseUrl: "https://api.anthropic.com/v1",
|
||||
},
|
||||
google: {
|
||||
label: "Google",
|
||||
defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
||||
},
|
||||
google: { label: "Google" },
|
||||
vertexai: { label: "Google Vertex AI" },
|
||||
azure: {
|
||||
label: "Azure OpenAI",
|
||||
defaultBaseUrl: "https://your-resource.openai.azure.com/openai",
|
||||
},
|
||||
azure: { label: "Azure OpenAI" },
|
||||
bedrock: { label: "Amazon Bedrock" },
|
||||
ollama: {
|
||||
label: "Ollama",
|
||||
defaultBaseUrl: "http://localhost:11434",
|
||||
},
|
||||
openrouter: {
|
||||
label: "OpenRouter",
|
||||
defaultBaseUrl: "https://openrouter.ai/api/v1",
|
||||
},
|
||||
deepseek: {
|
||||
label: "DeepSeek",
|
||||
defaultBaseUrl: "https://api.deepseek.com/v1",
|
||||
},
|
||||
openrouter: { label: "OpenRouter" },
|
||||
deepseek: { label: "DeepSeek" },
|
||||
siliconflow: {
|
||||
label: "SiliconFlow",
|
||||
defaultBaseUrl: "https://api.siliconflow.cn/v1",
|
||||
@@ -121,10 +106,7 @@ export const PROVIDER_INFO: Record<
|
||||
label: "SGLang",
|
||||
defaultBaseUrl: "http://127.0.0.1:8000/v1",
|
||||
},
|
||||
gateway: {
|
||||
label: "AI Gateway",
|
||||
defaultBaseUrl: "https://ai-gateway.vercel.sh/v1/ai",
|
||||
},
|
||||
gateway: { label: "AI Gateway" },
|
||||
edgeone: { label: "EdgeOne Pages" },
|
||||
doubao: {
|
||||
label: "Doubao (ByteDance)",
|
||||
|
||||
616
package-lock.json
generated
616
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "next-ai-draw-io",
|
||||
"version": "0.4.12-beta.5",
|
||||
"version": "0.4.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "next-ai-draw-io",
|
||||
"version": "0.4.12-beta.5",
|
||||
"version": "0.4.10",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^4.0.1",
|
||||
@@ -28,7 +28,6 @@
|
||||
"@next/third-parties": "^16.0.6",
|
||||
"@opennextjs/cloudflare": "1.14.8",
|
||||
"@openrouter/ai-sdk-provider": "^1.5.4",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.209.0",
|
||||
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
@@ -106,7 +105,7 @@
|
||||
"vite-tsconfig-paths": "^6.0.3",
|
||||
"vitest": "^4.0.16",
|
||||
"wait-on": "^9.0.3",
|
||||
"wrangler": "^4.60.0"
|
||||
"wrangler": "4.58.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "^4.1.18",
|
||||
@@ -6128,22 +6127,37 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/kv-asset-handler": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz",
|
||||
"integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.1.tgz",
|
||||
"integrity": "sha512-Nu8ahitGFFJztxUml9oD/DLb7Z28C8cd8F46IVQ7y5Btz575pvMY8AqZsXkX7Gds29eCKdMgIHjIvzskHgPSFg==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"mime": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/kv-asset-handler/node_modules/mime": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/unenv-preset": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.11.0.tgz",
|
||||
"integrity": "sha512-z3hxFajL765VniNPGV0JRStZolNz63gU3B3AktwoGdDlnQvz5nP+Ah4RL04PONlZQjwmDdGHowEStJ94+RsaJg==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.8.0.tgz",
|
||||
"integrity": "sha512-oIAu6EdQ4zJuPwwKr9odIEqd8AV96z1aqi3RBEA4iKaJ+Vd3fvuI6m5EDC7/QCv+oaPIhy1SkYBYxmD09N+oZg==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"unenv": "2.0.0-rc.24",
|
||||
"workerd": "^1.20260115.0"
|
||||
"workerd": "^1.20251202.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"workerd": {
|
||||
@@ -6152,9 +6166,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-darwin-64": {
|
||||
"version": "1.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260120.0.tgz",
|
||||
"integrity": "sha512-JLHx3p5dpwz4wjVSis45YNReftttnI3ndhdMh5BUbbpdreN/g0jgxNt5Qp9tDFqEKl++N63qv+hxJiIIvSLR+Q==",
|
||||
"version": "1.20260107.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260107.1.tgz",
|
||||
"integrity": "sha512-Srwe/IukVppkMU2qTndkFaKCmZBI7CnZoq4Y0U0gD/8158VGzMREHTqCii4IcCeHifwrtDqTWu8EcA1VBKI4mg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -6168,9 +6182,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-darwin-arm64": {
|
||||
"version": "1.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260120.0.tgz",
|
||||
"integrity": "sha512-1Md2tCRhZjwajsZNOiBeOVGiS3zbpLPzUDjHr4+XGTXWOA6FzzwScJwQZLa0Doc28Cp4Nr1n7xGL0Dwiz1XuOA==",
|
||||
"version": "1.20260107.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260107.1.tgz",
|
||||
"integrity": "sha512-aAYwU7zXW+UZFh/a4vHP5cs1ulTOcDRLzwU9547yKad06RlZ6ioRm7ovjdYvdqdmbI8mPd99v4LN9gMmecazQw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -6184,9 +6198,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-linux-64": {
|
||||
"version": "1.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260120.0.tgz",
|
||||
"integrity": "sha512-O0mIfJfvU7F8N5siCoRDaVDuI12wkz2xlG4zK6/Ct7U9c9FiE0ViXNFWXFQm5PPj+qbkNRyhjUwhP+GCKTk5EQ==",
|
||||
"version": "1.20260107.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260107.1.tgz",
|
||||
"integrity": "sha512-Wh7xWtFOkk6WY3CXe3lSqZ1anMkFcwy+qOGIjtmvQ/3nCOaG34vKNwPIE9iwryPupqkSuDmEqkosI1UUnSTh1A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -6200,9 +6214,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-linux-arm64": {
|
||||
"version": "1.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260120.0.tgz",
|
||||
"integrity": "sha512-aRHO/7bjxVpjZEmVVcpmhbzpN6ITbFCxuLLZSW0H9O0C0w40cDCClWSi19T87Ax/PQcYjFNT22pTewKsupkckA==",
|
||||
"version": "1.20260107.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260107.1.tgz",
|
||||
"integrity": "sha512-NI0/5rdssdZZKYHxNG4umTmMzODByq86vSCEk8u4HQbGhRCQo7rV1eXn84ntSBdyWBzWdYGISCbeZMsgfIjSTg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -6216,9 +6230,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workerd-windows-64": {
|
||||
"version": "1.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260120.0.tgz",
|
||||
"integrity": "sha512-ASZIz1E8sqZQqQCgcfY1PJbBpUDrxPt8NZ+lqNil0qxnO4qX38hbCsdDF2/TDAuq0Txh7nu8ztgTelfNDlb4EA==",
|
||||
"version": "1.20260107.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260107.1.tgz",
|
||||
"integrity": "sha512-gmBMqs606Gd/IhBEBPSL/hJAqy2L8IyPUjKtoqd/Ccy7GQxbSc0rYlRkxbQ9YzmqnuhrTVYvXuLscyWrpmAJkw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7832,6 +7846,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -11969,9 +11984,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@speed-highlight/core": {
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz",
|
||||
"integrity": "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==",
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.12.tgz",
|
||||
"integrity": "sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
@@ -13532,6 +13547,15 @@
|
||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
@@ -15029,6 +15053,19 @@
|
||||
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -15047,6 +15084,16 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
@@ -17126,6 +17173,18 @@
|
||||
"which": "bin/which"
|
||||
}
|
||||
},
|
||||
"node_modules/exit-hook": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz",
|
||||
"integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||
@@ -18005,6 +18064,12 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/global-agent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz",
|
||||
@@ -18798,6 +18863,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
|
||||
"integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-async-function": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
|
||||
@@ -21212,15 +21283,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare": {
|
||||
"version": "4.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260120.0.tgz",
|
||||
"integrity": "sha512-XXZyE2pDKMtP5OLuv0LPHEAzIYhov4jrYjcqrhhqtxGGtXneWOHvXIPo+eV8sqwqWd3R7j4DlEKcyb+87BR49Q==",
|
||||
"version": "4.20260107.0",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260107.0.tgz",
|
||||
"integrity": "sha512-X93sXczqbBq9ixoM6jnesmdTqp+4baVC/aM/DuPpRS0LK0XtcqaO75qPzNEvDEzBAHxwMAWRIum/9hg32YB8iA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "0.8.1",
|
||||
"sharp": "^0.34.5",
|
||||
"undici": "7.18.2",
|
||||
"workerd": "1.20260120.0",
|
||||
"acorn": "8.14.0",
|
||||
"acorn-walk": "8.3.2",
|
||||
"exit-hook": "2.2.1",
|
||||
"glob-to-regexp": "0.4.1",
|
||||
"sharp": "^0.33.5",
|
||||
"stoppable": "1.1.0",
|
||||
"undici": "7.14.0",
|
||||
"workerd": "1.20260107.1",
|
||||
"ws": "8.18.0",
|
||||
"youch": "4.1.0-beta.10",
|
||||
"zod": "^3.25.76"
|
||||
@@ -21232,6 +21308,418 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
||||
"integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
|
||||
"integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
|
||||
"integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
|
||||
"integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
|
||||
"integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
|
||||
"integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
|
||||
"integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
|
||||
"integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
|
||||
"integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
|
||||
"integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
|
||||
"integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
|
||||
"integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
|
||||
"integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
|
||||
"integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
|
||||
"integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/sharp": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
|
||||
"integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.3",
|
||||
"semver": "^7.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.33.5",
|
||||
"@img/sharp-darwin-x64": "0.33.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.0.5",
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.0.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.4",
|
||||
"@img/sharp-linux-arm": "0.33.5",
|
||||
"@img/sharp-linux-arm64": "0.33.5",
|
||||
"@img/sharp-linux-s390x": "0.33.5",
|
||||
"@img/sharp-linux-x64": "0.33.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.33.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.33.5",
|
||||
"@img/sharp-wasm32": "0.33.5",
|
||||
"@img/sharp-win32-ia32": "0.33.5",
|
||||
"@img/sharp-win32-x64": "0.33.5"
|
||||
}
|
||||
},
|
||||
"node_modules/miniflare/node_modules/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
@@ -23923,6 +24411,7 @@
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
@@ -24152,6 +24641,15 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
|
||||
"integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
@@ -24359,6 +24857,16 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/stoppable": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
|
||||
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -25532,9 +26040,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz",
|
||||
"integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==",
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz",
|
||||
"integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
@@ -26356,9 +26864,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/workerd": {
|
||||
"version": "1.20260120.0",
|
||||
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260120.0.tgz",
|
||||
"integrity": "sha512-R6X/VQOkwLTBGLp4VRUwLQZZVxZ9T9J8pGiJ6GQUMaRkY7TVWrCSkVfoNMM1/YyFsY5UYhhPoQe5IehnhZ3Pdw==",
|
||||
"version": "1.20260107.1",
|
||||
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260107.1.tgz",
|
||||
"integrity": "sha512-4ylAQJDdJZdMAUl2SbJgTa77YHpa88l6qmhiuCLNactP933+rifs7I0w1DslhUIFgydArUX5dNLAZnZhT7Bh7g==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
@@ -26369,28 +26877,28 @@
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cloudflare/workerd-darwin-64": "1.20260120.0",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20260120.0",
|
||||
"@cloudflare/workerd-linux-64": "1.20260120.0",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20260120.0",
|
||||
"@cloudflare/workerd-windows-64": "1.20260120.0"
|
||||
"@cloudflare/workerd-darwin-64": "1.20260107.1",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20260107.1",
|
||||
"@cloudflare/workerd-linux-64": "1.20260107.1",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20260107.1",
|
||||
"@cloudflare/workerd-windows-64": "1.20260107.1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler": {
|
||||
"version": "4.60.0",
|
||||
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.60.0.tgz",
|
||||
"integrity": "sha512-n4kibm/xY0Qd5G2K/CbAQeVeOIlwPNVglmFjlDRCCYk3hZh8IggO/rg8AXt/vByK2Sxsugl5Z7yvgWxrUbmS6g==",
|
||||
"version": "4.58.0",
|
||||
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.58.0.tgz",
|
||||
"integrity": "sha512-Jm6EYtlt8iUcznOCPSMYC54DYkwrMNESzbH0Vh3GFHv/7XVw5gBC13YJAB+nWMRGJ+6B2dMzy/NVQS4ONL51Pw==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@cloudflare/kv-asset-handler": "0.4.2",
|
||||
"@cloudflare/unenv-preset": "2.11.0",
|
||||
"@cloudflare/kv-asset-handler": "0.4.1",
|
||||
"@cloudflare/unenv-preset": "2.8.0",
|
||||
"blake3-wasm": "2.1.5",
|
||||
"esbuild": "0.27.0",
|
||||
"miniflare": "4.20260120.0",
|
||||
"miniflare": "4.20260107.0",
|
||||
"path-to-regexp": "6.3.0",
|
||||
"unenv": "2.0.0-rc.24",
|
||||
"workerd": "1.20260120.0"
|
||||
"workerd": "1.20260107.1"
|
||||
},
|
||||
"bin": {
|
||||
"wrangler": "bin/wrangler.js",
|
||||
@@ -26403,7 +26911,7 @@
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20260120.0"
|
||||
"@cloudflare/workers-types": "^4.20260107.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cloudflare/workers-types": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-ai-draw-io",
|
||||
"version": "0.4.12",
|
||||
"version": "0.4.11",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"main": "dist-electron/main/index.js",
|
||||
@@ -139,7 +139,7 @@
|
||||
"vite-tsconfig-paths": "^6.0.3",
|
||||
"vitest": "^4.0.16",
|
||||
"wait-on": "^9.0.3",
|
||||
"wrangler": "^4.60.0"
|
||||
"wrangler": "4.58.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@openrouter/ai-sdk-provider": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-ai-drawio/mcp-server",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.13",
|
||||
"description": "MCP server for Next AI Draw.io - AI-powered diagram generation with real-time browser preview",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -44,17 +44,6 @@ function isLikelyMcpSessionId(sessionId: string): boolean {
|
||||
return sessionId.startsWith("mcp-") && sessionId.length <= 128
|
||||
}
|
||||
|
||||
// Find the most recent active session (for auto-redirect when no sessionId provided)
|
||||
function getMostRecentSessionId(): string | null {
|
||||
let mostRecent: { id: string; lastUpdated: Date } | null = null
|
||||
for (const [sessionId, state] of stateStore) {
|
||||
if (!mostRecent || state.lastUpdated > mostRecent.lastUpdated) {
|
||||
mostRecent = { id: sessionId, lastUpdated: state.lastUpdated }
|
||||
}
|
||||
}
|
||||
return mostRecent?.id || null
|
||||
}
|
||||
|
||||
function ensureSessionStateInitialized(sessionId: string): void {
|
||||
if (!sessionId) return
|
||||
if (!isLikelyMcpSessionId(sessionId)) return
|
||||
@@ -206,17 +195,6 @@ function handleRequest(
|
||||
|
||||
if (url.pathname === "/" || url.pathname === "/index.html") {
|
||||
const sessionId = url.searchParams.get("mcp") || ""
|
||||
|
||||
// Auto-redirect to most recent session if no sessionId provided
|
||||
if (!sessionId) {
|
||||
const recentSessionId = getMostRecentSessionId()
|
||||
if (recentSessionId) {
|
||||
res.writeHead(302, { Location: `/?mcp=${recentSessionId}` })
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ensureSessionStateInitialized(sessionId)
|
||||
|
||||
res.writeHead(200, { "Content-Type": "text/html" })
|
||||
@@ -397,202 +375,85 @@ function getHtmlPage(sessionId: string): string {
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Next AI Draw.io</title>
|
||||
<title>Draw.io MCP</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&display=swap');
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html, body { width: 100%; height: 100%; overflow: hidden; }
|
||||
#container { width: 100%; height: 100%; display: flex; flex-direction: column; }
|
||||
#header {
|
||||
padding: 0 20px; height: 52px;
|
||||
background: linear-gradient(to bottom, #ffffff, #fafbfc);
|
||||
border-bottom: 1px solid #e8ecf0;
|
||||
font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
|
||||
padding: 8px 16px; background: #1a1a2e; color: #eee;
|
||||
font-family: system-ui, sans-serif; font-size: 14px;
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||
position: relative; z-index: 10;
|
||||
}
|
||||
#header .brand {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
#header .logo {
|
||||
width: 28px; height: 28px; border-radius: 6px;
|
||||
background: #18181b;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
#header .logo img { width: 20px; height: 20px; filter: brightness(0) invert(1); }
|
||||
#header .title {
|
||||
font-size: 15px; font-weight: 600; color: #1a1a2e;
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
#header .session {
|
||||
font-size: 11px; color: #8b95a5; font-weight: 400;
|
||||
background: #f1f3f9; padding: 3px 8px; border-radius: 4px;
|
||||
margin-left: 12px; font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
#header .right { display: flex; align-items: center; gap: 12px; }
|
||||
#save-btn {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 7px 14px; border-radius: 8px; font-size: 13px;
|
||||
background: linear-gradient(to bottom, #18181b, #27272a);
|
||||
color: white; border: none; cursor: pointer;
|
||||
font-weight: 500; font-family: inherit;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.1);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
#save-btn svg { width: 14px; height: 14px; }
|
||||
#save-btn:hover {
|
||||
background: linear-gradient(to bottom, #27272a, #3f3f46);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 3px 8px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.1);
|
||||
}
|
||||
#save-btn:active { transform: translateY(0); }
|
||||
#save-btn:disabled, #history-btn:disabled {
|
||||
background: #e5e7eb; color: #9ca3af;
|
||||
cursor: not-allowed; transform: none; box-shadow: none;
|
||||
}
|
||||
#history-btn {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 7px 14px; border-radius: 8px; font-size: 13px;
|
||||
background: #f4f4f5; color: #3f3f46; border: 1px solid #e4e4e7;
|
||||
cursor: pointer; font-weight: 500; font-family: inherit;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
#history-btn svg { width: 14px; height: 14px; }
|
||||
#history-btn:hover {
|
||||
background: #e4e4e7; border-color: #d4d4d8;
|
||||
}
|
||||
#header .session { color: #888; font-size: 12px; }
|
||||
#header .status { font-size: 12px; }
|
||||
#header .status.connected { color: #4ade80; }
|
||||
#header .status.disconnected { color: #f87171; }
|
||||
#drawio { flex: 1; border: none; }
|
||||
#history-modal, #save-modal {
|
||||
display: none; position: fixed; inset: 0;
|
||||
background: rgba(0,0,0,0.4); backdrop-filter: blur(4px);
|
||||
z-index: 2000; align-items: center; justify-content: center;
|
||||
}
|
||||
#history-modal.open, #save-modal.open { display: flex; }
|
||||
.modal-content {
|
||||
background: white; border-radius: 16px;
|
||||
width: 90%; max-width: 480px; max-height: 70vh;
|
||||
display: flex; flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
|
||||
font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
|
||||
animation: modalIn 0.2s ease-out;
|
||||
}
|
||||
@keyframes modalIn {
|
||||
from { opacity: 0; transform: scale(0.95) translateY(-10px); }
|
||||
to { opacity: 1; transform: scale(1) translateY(0); }
|
||||
}
|
||||
.modal-header {
|
||||
padding: 20px 24px 16px; border-bottom: 1px solid #f1f3f5;
|
||||
}
|
||||
.modal-header h2 {
|
||||
font-size: 17px; font-weight: 600; margin: 0; color: #18181b;
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
.modal-body { flex: 1; overflow-y: auto; padding: 20px 24px; }
|
||||
.modal-footer {
|
||||
padding: 16px 24px; border-top: 1px solid #f1f3f5;
|
||||
display: flex; gap: 10px; justify-content: flex-end;
|
||||
}
|
||||
.history-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
||||
.history-item {
|
||||
border: 2px solid #e4e4e7; border-radius: 10px; padding: 10px;
|
||||
cursor: pointer; text-align: center; transition: all 0.15s ease;
|
||||
background: #fafafa;
|
||||
}
|
||||
.history-item:hover { border-color: #a1a1aa; background: white; }
|
||||
.history-item.selected {
|
||||
border-color: #18181b; background: white;
|
||||
box-shadow: 0 0 0 3px rgba(24,24,27,0.1);
|
||||
}
|
||||
.history-item .thumb {
|
||||
aspect-ratio: 4/3; background: #f4f4f5; border-radius: 6px;
|
||||
#history-btn {
|
||||
position: fixed; bottom: 24px; right: 24px;
|
||||
width: 48px; height: 48px; border-radius: 50%;
|
||||
background: #3b82f6; color: white; border: none; cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-bottom: 6px; overflow: hidden;
|
||||
z-index: 1000;
|
||||
}
|
||||
#history-btn:hover { background: #2563eb; }
|
||||
#history-btn:disabled { background: #6b7280; cursor: not-allowed; }
|
||||
#history-btn svg { width: 24px; height: 24px; }
|
||||
#history-modal {
|
||||
display: none; position: fixed; inset: 0;
|
||||
background: rgba(0,0,0,0.5); z-index: 2000;
|
||||
align-items: center; justify-content: center;
|
||||
}
|
||||
#history-modal.open { display: flex; }
|
||||
.modal-content {
|
||||
background: white; border-radius: 12px;
|
||||
width: 90%; max-width: 500px; max-height: 70vh;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.modal-header { padding: 16px; border-bottom: 1px solid #e5e7eb; }
|
||||
.modal-header h2 { font-size: 18px; margin: 0; }
|
||||
.modal-body { flex: 1; overflow-y: auto; padding: 16px; }
|
||||
.modal-footer { padding: 12px 16px; border-top: 1px solid #e5e7eb; display: flex; gap: 8px; justify-content: flex-end; }
|
||||
.history-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
|
||||
.history-item {
|
||||
border: 2px solid #e5e7eb; border-radius: 8px; padding: 8px;
|
||||
cursor: pointer; text-align: center;
|
||||
}
|
||||
.history-item:hover { border-color: #3b82f6; }
|
||||
.history-item.selected { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.3); }
|
||||
.history-item .thumb {
|
||||
aspect-ratio: 4/3; background: #f3f4f6; border-radius: 4px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-bottom: 4px; overflow: hidden;
|
||||
}
|
||||
.history-item .thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
|
||||
.history-item .label { font-size: 11px; color: #71717a; font-weight: 500; }
|
||||
.btn {
|
||||
padding: 9px 18px; border-radius: 8px; font-size: 13px;
|
||||
cursor: pointer; border: none; font-weight: 500;
|
||||
font-family: inherit; transition: all 0.15s ease;
|
||||
}
|
||||
.btn-primary {
|
||||
background: linear-gradient(to bottom, #18181b, #27272a);
|
||||
color: white;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.1);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(to bottom, #27272a, #3f3f46);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.btn-primary:disabled {
|
||||
background: #e4e4e7; color: #a1a1aa;
|
||||
cursor: not-allowed; transform: none; box-shadow: none;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: #f4f4f5; color: #3f3f46; border: 1px solid #e4e4e7;
|
||||
}
|
||||
.btn-secondary:hover { background: #e4e4e7; }
|
||||
.empty { text-align: center; padding: 40px; color: #71717a; font-size: 14px; }
|
||||
.form-group { margin-bottom: 18px; }
|
||||
.form-group label {
|
||||
display: block; font-size: 13px; font-weight: 500;
|
||||
margin-bottom: 8px; color: #3f3f46;
|
||||
}
|
||||
.form-group select, .form-group input {
|
||||
width: 100%; padding: 10px 14px; border: 1px solid #e4e4e7;
|
||||
border-radius: 8px; font-size: 14px; outline: none;
|
||||
font-family: inherit; background: white;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.form-group select:focus, .form-group input:focus {
|
||||
border-color: #18181b;
|
||||
box-shadow: 0 0 0 3px rgba(24,24,27,0.08);
|
||||
}
|
||||
.filename-group { display: flex; }
|
||||
.filename-group input { border-radius: 8px 0 0 8px; border-right: none; }
|
||||
.filename-group .ext {
|
||||
padding: 10px 14px; background: #f4f4f5; border: 1px solid #e4e4e7;
|
||||
border-radius: 0 8px 8px 0; font-size: 13px; color: #71717a;
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
.history-item .label { font-size: 12px; color: #666; }
|
||||
.btn { padding: 8px 16px; border-radius: 6px; font-size: 14px; cursor: pointer; border: none; }
|
||||
.btn-primary { background: #3b82f6; color: white; }
|
||||
.btn-primary:disabled { background: #93c5fd; cursor: not-allowed; }
|
||||
.btn-secondary { background: #f3f4f6; color: #374151; }
|
||||
.empty { text-align: center; padding: 40px; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="header">
|
||||
<div class="brand">
|
||||
<div class="logo">
|
||||
<svg viewBox="0 0 1536 1536" fill="#ffffff">
|
||||
<g transform="translate(0,1536) scale(0.1,-0.1)">
|
||||
<path d="M2765 14404 c-100 -29 -181 -58 -225 -82 -227 -125 -359 -296 -431 -560 -19 -70 -19 -108 -19 -1175 0 -1068 1 -1104 20 -1172 58 -206 159 -356 319 -474 71 -53 199 -121 226 -121 9 0 26 -5 38 -12 12 -6 62 -19 112 -29 85 -17 207 -18 2219 -19 1172 0 2133 -3 2138 -8 4 -4 7 -246 6 -538 l-3 -529 -2330 -5 c-2506 -6 -2373 -3 -2470 -54 -61 -31 -150 -113 -194 -178 -87 -128 -82 -77 -90 -1025 l-6 -838 -360 -6 c-292 -4 -368 -8 -405 -21 -194 -68 -303 -177 -373 -372 l-22 -61 1 -2887 c1 -2716 2 -2890 18 -2935 56 -153 161 -276 286 -334 126 -59 0 -54 1400 -54 1394 0 1290 -4 1410 53 95 45 198 148 242 241 62 133 58 -93 58 3026 0 2992 1 2883 -40 2990 -59 156 -183 272 -360 337 -25 9 -146 14 -440 18 l-405 5 0 540 0 540 2020 3 c1111 1 2030 0 2043 -3 l22 -5 -2 -538 -3 -537 -380 -6 c-312 -4 -388 -8 -426 -21 -195 -68 -326 -204 -383 -399 -15 -51 -16 -295 -16 -2921 0 -2778 1 -2867 19 -2920 36 -104 72 -167 134 -230 75 -78 115 -105 222 -151 l50 -22 1219 -3 c672 -1 1255 1 1300 6 109 12 217 63 298 140 73 69 107 118 144 208 l29 69 3 2880 c2 2687 1 2884 -15 2945 -48 183 -188 332 -373 398 -37 13 -114 17 -430 21 l-385 6 -3 534 c-2 421 0 536 10 543 7 4 925 8 2039 8 1718 0 2028 -2 2038 -14 8 -10 11 -154 11 -531 -1 -284 -4 -523 -7 -531 -4 -12 -69 -14 -392 -14 -354 0 -391 -2 -448 -20 -168 -52 -282 -148 -353 -295 -22 -45 -40 -91 -40 -103 0 -11 -5 -33 -10 -47 -7 -18 -10 -988 -10 -2875 0 -2393 2 -2858 14 -2902 43 -167 148 -298 293 -369 57 -27 107 -44 151 -50 88 -11 2429 -11 2508 0 210 31 416 238 445 450 6 39 8 1245 7 2926 -3 2713 -4 2862 -21 2900 -41 93 -74 150 -110 191 -46 52 -149 134 -169 134 -8 0 -19 5 -24 10 -6 6 -42 19 -80 30 -63 18 -100 20 -415 20 -307 0 -348 2 -353 16 -3 9 -6 390 -6 848 0 797 -1 834 -19 886 -31 87 -50 118 -111 183 -66 70 -141 119 -221 144 -50 16 -228 18 -2389 23 l-2335 5 0 535 0 535 2165 5 c1191 3 2170 8 2176 12 6 4 35 12 65 17 201 35 435 198 539 376 55 93 82 153 110 245 19 63 20 94 20 1167 0 1047 -1 1106 -19 1180 -70 290 -275 523 -539 613 -160 54 232 50 -5028 49 -4182 0 -4856 -2 -4899 -15z"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="title">Next AI Draw.io</span>
|
||||
${sessionId ? `<span class="session">${sessionId.slice(-8)}</span>` : ""}
|
||||
</div>
|
||||
<div class="right">
|
||||
<button id="history-btn" title="History" ${sessionId ? "" : "disabled"}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
History
|
||||
</button>
|
||||
<button id="save-btn" ${sessionId ? "" : "disabled"}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="7 10 12 15 17 10"></polyline>
|
||||
<line x1="12" y1="15" x2="12" y2="3"></line>
|
||||
</svg>
|
||||
Download
|
||||
</button>
|
||||
<div>
|
||||
<strong>Draw.io MCP</strong>
|
||||
<span class="session">${sessionId ? `Session: ${sessionId}` : "No session"}</span>
|
||||
</div>
|
||||
<div id="status" class="status disconnected">Connecting...</div>
|
||||
</div>
|
||||
<iframe id="drawio" src="${normalizeUrl(DRAWIO_BASE_URL)}/?embed=1&proto=json&spin=1&libraries=1&noSaveBtn=1&noExitBtn=1&saveAndExit=0"></iframe>
|
||||
<iframe id="drawio" src="${normalizeUrl(DRAWIO_BASE_URL)}/?embed=1&proto=json&spin=1&libraries=1"></iframe>
|
||||
</div>
|
||||
<button id="history-btn" title="History" ${sessionId ? "" : "disabled"}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="history-modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2>History</h2></div>
|
||||
@@ -606,35 +467,10 @@ function getHtmlPage(sessionId: string): string {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="save-modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h2>Download Diagram</h2></div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Format</label>
|
||||
<select id="save-format">
|
||||
<option value="drawio">Draw.io (.drawio)</option>
|
||||
<option value="png">PNG Image (.png)</option>
|
||||
<option value="svg">SVG Vector (.svg)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Filename</label>
|
||||
<div class="filename-group">
|
||||
<input type="text" id="save-filename" value="diagram" placeholder="Enter filename">
|
||||
<span class="ext" id="save-ext">.drawio</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" id="save-cancel-btn">Cancel</button>
|
||||
<button class="btn btn-primary" id="save-confirm-btn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const sessionId = "${sessionId}";
|
||||
const iframe = document.getElementById('drawio');
|
||||
const statusEl = document.getElementById('status');
|
||||
let currentVersion = 0, isReady = false, pendingXml = null, lastXml = null;
|
||||
let pendingSvgExport = null;
|
||||
let pendingAiSvg = false;
|
||||
@@ -645,6 +481,8 @@ function getHtmlPage(sessionId: string): string {
|
||||
const msg = JSON.parse(e.data);
|
||||
if (msg.event === 'init') {
|
||||
isReady = true;
|
||||
statusEl.textContent = 'Ready';
|
||||
statusEl.className = 'status connected';
|
||||
if (pendingXml) { loadDiagram(pendingXml); pendingXml = null; }
|
||||
} else if ((msg.event === 'save' || msg.event === 'autosave') && msg.xml && msg.xml !== lastXml) {
|
||||
// Request SVG export, then push state with SVG
|
||||
@@ -653,23 +491,6 @@ function getHtmlPage(sessionId: string): string {
|
||||
// Fallback if export doesn't respond
|
||||
setTimeout(() => { if (pendingSvgExport === msg.xml) { pushState(msg.xml, ''); pendingSvgExport = null; } }, 2000);
|
||||
} else if (msg.event === 'export' && msg.data) {
|
||||
// Handle file download export (PNG/SVG only, drawio uses lastXml directly)
|
||||
if (pendingDownload && (pendingDownload.format === 'png' || pendingDownload.format === 'svg')) {
|
||||
const dl = pendingDownload;
|
||||
pendingDownload = null;
|
||||
let dataUrl = msg.data;
|
||||
if (!dataUrl.startsWith('data:')) {
|
||||
const mime = dl.format === 'png' ? 'image/png' : 'image/svg+xml';
|
||||
dataUrl = 'data:' + mime + ';base64,' + btoa(unescape(encodeURIComponent(msg.data)));
|
||||
}
|
||||
const a = document.createElement('a');
|
||||
a.href = dataUrl; a.download = dl.filename;
|
||||
document.body.appendChild(a); a.click(); document.body.removeChild(a);
|
||||
saveModal.classList.remove('open');
|
||||
saveConfirmBtn.disabled = false;
|
||||
saveConfirmBtn.textContent = 'Save';
|
||||
return;
|
||||
}
|
||||
// Handle sync export (XML format) - server requested fresh state
|
||||
if (pendingSyncExport && !msg.data.startsWith('data:') && !msg.data.startsWith('<svg')) {
|
||||
pendingSyncExport = false;
|
||||
@@ -742,64 +563,6 @@ function getHtmlPage(sessionId: string): string {
|
||||
|
||||
if (sessionId) { poll(); setInterval(poll, 2000); }
|
||||
|
||||
// Save modal
|
||||
const saveBtn = document.getElementById('save-btn');
|
||||
const saveModal = document.getElementById('save-modal');
|
||||
const saveFormat = document.getElementById('save-format');
|
||||
const saveFilename = document.getElementById('save-filename');
|
||||
const saveExt = document.getElementById('save-ext');
|
||||
const saveCancelBtn = document.getElementById('save-cancel-btn');
|
||||
const saveConfirmBtn = document.getElementById('save-confirm-btn');
|
||||
let pendingDownload = null;
|
||||
|
||||
const extMap = { drawio: '.drawio', png: '.png', svg: '.svg' };
|
||||
|
||||
saveBtn.onclick = () => {
|
||||
if (!sessionId || !isReady) return;
|
||||
saveModal.classList.add('open');
|
||||
saveFilename.focus();
|
||||
saveFilename.select();
|
||||
};
|
||||
|
||||
saveFormat.onchange = () => {
|
||||
saveExt.textContent = extMap[saveFormat.value] || '.drawio';
|
||||
};
|
||||
|
||||
saveCancelBtn.onclick = () => { saveModal.classList.remove('open'); };
|
||||
saveModal.onclick = (e) => { if (e.target === saveModal) saveCancelBtn.onclick(); };
|
||||
|
||||
saveConfirmBtn.onclick = () => {
|
||||
const format = saveFormat.value;
|
||||
const filename = (saveFilename.value.trim() || 'diagram') + extMap[format];
|
||||
saveConfirmBtn.disabled = true;
|
||||
saveConfirmBtn.textContent = 'Exporting...';
|
||||
|
||||
if (format === 'drawio') {
|
||||
// Use lastXml directly instead of requesting export (avoids race with SVG exports)
|
||||
let xmlData = lastXml || '';
|
||||
if (xmlData && !xmlData.includes('<mxfile')) {
|
||||
xmlData = '<mxfile host="mcp"><diagram name="Page-1">' + xmlData + '</diagram></mxfile>';
|
||||
}
|
||||
const blob = new Blob([xmlData], { type: 'application/xml' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url; a.download = filename;
|
||||
document.body.appendChild(a); a.click(); document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
saveModal.classList.remove('open');
|
||||
saveConfirmBtn.disabled = false;
|
||||
saveConfirmBtn.textContent = 'Save';
|
||||
} else if (format === 'png') {
|
||||
pendingDownload = { format: 'png', filename };
|
||||
iframe.contentWindow.postMessage(JSON.stringify({ action: 'export', format: 'png', scale: 2 }), '*');
|
||||
setTimeout(() => { saveConfirmBtn.disabled = false; saveConfirmBtn.textContent = 'Save'; pendingDownload = null; }, 5000);
|
||||
} else if (format === 'svg') {
|
||||
pendingDownload = { format: 'svg', filename };
|
||||
iframe.contentWindow.postMessage(JSON.stringify({ action: 'export', format: 'svg' }), '*');
|
||||
setTimeout(() => { saveConfirmBtn.disabled = false; saveConfirmBtn.textContent = 'Save'; pendingDownload = null; }, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
// History UI
|
||||
const historyBtn = document.getElementById('history-btn');
|
||||
const historyModal = document.getElementById('history-modal');
|
||||
|
||||
1
proxy.ts
1
proxy.ts
@@ -31,7 +31,6 @@ export function proxy(request: NextRequest) {
|
||||
if (
|
||||
pathname.startsWith("/api/") ||
|
||||
pathname.startsWith("/_next/") ||
|
||||
pathname.startsWith("/drawio") ||
|
||||
pathname.includes("/favicon") ||
|
||||
/\.(.*)$/.test(pathname)
|
||||
) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* electron-builder afterPack hook
|
||||
* Copies node_modules to the standalone directory in the packaged app
|
||||
* and ad-hoc signs macOS apps for offline draw.io bundle compatibility
|
||||
*/
|
||||
|
||||
const {
|
||||
@@ -13,7 +12,6 @@ const {
|
||||
statSync,
|
||||
} = require("fs")
|
||||
const path = require("path")
|
||||
const { execSync } = require("child_process")
|
||||
|
||||
/**
|
||||
* Copy directory recursively, converting symlinks to regular files/directories.
|
||||
@@ -82,22 +80,4 @@ module.exports = async (context) => {
|
||||
"Ensure 'npm run electron:prepare' was run before building.",
|
||||
)
|
||||
}
|
||||
|
||||
// Ad-hoc sign macOS apps to fix signature issues with bundled draw.io files
|
||||
if (context.packager.platform.name === "mac") {
|
||||
const appPath = path.join(
|
||||
appOutDir,
|
||||
`${context.packager.appInfo.productFilename}.app`,
|
||||
)
|
||||
console.log(`[afterPack] Ad-hoc signing macOS app: ${appPath}`)
|
||||
try {
|
||||
execSync(`codesign --force --deep --sign - "${appPath}"`, {
|
||||
stdio: "inherit",
|
||||
})
|
||||
console.log("[afterPack] Ad-hoc signing completed successfully")
|
||||
} catch (error) {
|
||||
console.error("[afterPack] Ad-hoc signing failed:", error.message)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user