-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Nano #333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Nano #333
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ import { FaGithub } from "react-icons/fa" | |||||||||||||||||||||||||||||
| import { Toaster, toast } from "sonner" | ||||||||||||||||||||||||||||||
| import { ButtonWithTooltip } from "@/components/button-with-tooltip" | ||||||||||||||||||||||||||||||
| import { ChatInput } from "@/components/chat-input" | ||||||||||||||||||||||||||||||
| import { ImageGenerationConfig } from "@/components/image-generation-config" | ||||||||||||||||||||||||||||||
| import { ResetWarningModal } from "@/components/reset-warning-modal" | ||||||||||||||||||||||||||||||
| import { SettingsDialog } from "@/components/settings-dialog" | ||||||||||||||||||||||||||||||
| import { useDiagram } from "@/contexts/diagram-context" | ||||||||||||||||||||||||||||||
|
|
@@ -34,6 +35,10 @@ const STORAGE_MESSAGES_KEY = "next-ai-draw-io-messages" | |||||||||||||||||||||||||||||
| const STORAGE_XML_SNAPSHOTS_KEY = "next-ai-draw-io-xml-snapshots" | ||||||||||||||||||||||||||||||
| const STORAGE_SESSION_ID_KEY = "next-ai-draw-io-session-id" | ||||||||||||||||||||||||||||||
| export const STORAGE_DIAGRAM_XML_KEY = "next-ai-draw-io-diagram-xml" | ||||||||||||||||||||||||||||||
| const STORAGE_IMAGE_GENERATION_ENABLED_KEY = | ||||||||||||||||||||||||||||||
| "next-ai-draw-io-image-generation-enabled" | ||||||||||||||||||||||||||||||
| const STORAGE_IMAGE_RESOLUTION_KEY = "next-ai-draw-io-image-resolution" | ||||||||||||||||||||||||||||||
| const STORAGE_IMAGE_ASPECT_RATIO_KEY = "next-ai-draw-io-image-aspect-ratio" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // sessionStorage keys | ||||||||||||||||||||||||||||||
| const SESSION_STORAGE_INPUT_KEY = "next-ai-draw-io-input" | ||||||||||||||||||||||||||||||
|
|
@@ -150,12 +155,39 @@ export default function ChatPanel({ | |||||||||||||||||||||||||||||
| const [showNewChatDialog, setShowNewChatDialog] = useState(false) | ||||||||||||||||||||||||||||||
| const [minimalStyle, setMinimalStyle] = useState(false) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Image generation configuration states | ||||||||||||||||||||||||||||||
| const [imageGenerationEnabled, setImageGenerationEnabled] = useState(false) | ||||||||||||||||||||||||||||||
| const [imageResolution, setImageResolution] = useState("1K") | ||||||||||||||||||||||||||||||
| const [imageAspectRatio, setImageAspectRatio] = useState("1:1") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Restore input from sessionStorage on mount (when ChatPanel remounts due to key change) | ||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||
| const savedInput = sessionStorage.getItem(SESSION_STORAGE_INPUT_KEY) | ||||||||||||||||||||||||||||||
| if (savedInput) { | ||||||||||||||||||||||||||||||
| setInput(savedInput) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Restore image generation config from localStorage | ||||||||||||||||||||||||||||||
| const savedImageEnabled = localStorage.getItem( | ||||||||||||||||||||||||||||||
| STORAGE_IMAGE_GENERATION_ENABLED_KEY, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| if (savedImageEnabled !== null) { | ||||||||||||||||||||||||||||||
| setImageGenerationEnabled(savedImageEnabled === "true") | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const savedResolution = localStorage.getItem( | ||||||||||||||||||||||||||||||
| STORAGE_IMAGE_RESOLUTION_KEY, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| if (savedResolution) { | ||||||||||||||||||||||||||||||
| setImageResolution(savedResolution) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const savedAspectRatio = localStorage.getItem( | ||||||||||||||||||||||||||||||
| STORAGE_IMAGE_ASPECT_RATIO_KEY, | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| if (savedAspectRatio) { | ||||||||||||||||||||||||||||||
| setImageAspectRatio(savedAspectRatio) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }, []) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Check config on mount | ||||||||||||||||||||||||||||||
|
|
@@ -241,6 +273,40 @@ export default function ChatPanel({ | |||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (toolCall.toolName === "display_image") { | ||||||||||||||||||||||||||||||
| const { imageData, description } = toolCall.input as { | ||||||||||||||||||||||||||||||
| imageData: string | ||||||||||||||||||||||||||||||
| description?: string | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Create an mxCell with the image | ||||||||||||||||||||||||||||||
| const imageId = `img-${Date.now()}` | ||||||||||||||||||||||||||||||
| const imageXml = `<mxCell id="${imageId}" value="${description || "AI生成图片"}" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;aspect=fixed;imageAspect=0;image=data:image/png;base64,${imageData};" vertex="1" parent="1"> | ||||||||||||||||||||||||||||||
| <mxGeometry x="50" y="50" width="400" height="400" as="geometry"/> | ||||||||||||||||||||||||||||||
| </mxCell>` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const validatedXml = validateAndFixXml(imageXml) | ||||||||||||||||||||||||||||||
| onDisplayChart(wrapWithMxFile(validatedXml), true) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| addToolOutput({ | ||||||||||||||||||||||||||||||
| tool: "display_image", | ||||||||||||||||||||||||||||||
| toolCallId: toolCall.toolCallId, | ||||||||||||||||||||||||||||||
| state: "output-available", | ||||||||||||||||||||||||||||||
| output: "图片已成功显示在画布上。", | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||
| console.error("[display_image] Error:", error) | ||||||||||||||||||||||||||||||
| addToolOutput({ | ||||||||||||||||||||||||||||||
| tool: "display_image", | ||||||||||||||||||||||||||||||
| toolCallId: toolCall.toolCallId, | ||||||||||||||||||||||||||||||
| state: "output-error", | ||||||||||||||||||||||||||||||
| errorText: `显示图片失败: ${error instanceof Error ? error.message : String(error)}`, | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (toolCall.toolName === "display_diagram") { | ||||||||||||||||||||||||||||||
| const { xml } = toolCall.input as { xml: string } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
@@ -619,6 +685,49 @@ Continue from EXACTLY where you stopped.`, | |||||||||||||||||||||||||||||
| // DEBUG: Log finish reason to diagnose truncation | ||||||||||||||||||||||||||||||
| console.log("[onFinish] finishReason:", metadata?.finishReason) | ||||||||||||||||||||||||||||||
| console.log("[onFinish] metadata:", metadata) | ||||||||||||||||||||||||||||||
| console.log("[onFinish] message parts:", message?.parts) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Check if image generation mode produced an image | ||||||||||||||||||||||||||||||
| if (imageGenerationEnabled && message?.parts) { | ||||||||||||||||||||||||||||||
| for (const part of message.parts) { | ||||||||||||||||||||||||||||||
| // Check for image data in the part | ||||||||||||||||||||||||||||||
| if (part.type === "image" || (part as any).image) { | ||||||||||||||||||||||||||||||
| console.log("[onFinish] Found image in response:", part) | ||||||||||||||||||||||||||||||
| // Extract base64 image data | ||||||||||||||||||||||||||||||
| const imageUrl = | ||||||||||||||||||||||||||||||
| (part as any).image || (part as any).url | ||||||||||||||||||||||||||||||
|
Comment on lines
+694
to
+698
|
||||||||||||||||||||||||||||||
| if (imageUrl && typeof imageUrl === "string") { | ||||||||||||||||||||||||||||||
| // Remove data URL prefix if present | ||||||||||||||||||||||||||||||
| const base64Data = imageUrl.replace( | ||||||||||||||||||||||||||||||
| /^data:image\/[^;]+;base64,/, | ||||||||||||||||||||||||||||||
| "", | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+700
to
+705
|
||||||||||||||||||||||||||||||
| // Remove data URL prefix if present | |
| const base64Data = imageUrl.replace( | |
| /^data:image\/[^;]+;base64,/, | |
| "", | |
| ) |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardcoded dimensions "width='600' height='600'" may not be appropriate for all aspect ratios. Consider calculating dimensions based on the configured imageAspectRatio to ensure the image displays with correct proportions.
| const imageXml = `<mxCell id="${imageId}" value="AI生成图片" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;aspect=fixed;imageAspect=0;image=${imageUrl};" vertex="1" parent="1"> | |
| <mxGeometry x="50" y="50" width="600" height="600" as="geometry"/> | |
| const baseWidth = 600 | |
| const aspectRatio = | |
| typeof imageAspectRatio === "number" && | |
| imageAspectRatio > 0 | |
| ? imageAspectRatio | |
| : 1 | |
| const imageWidth = baseWidth | |
| const imageHeight = Math.round( | |
| baseWidth / aspectRatio, | |
| ) | |
| const imageXml = `<mxCell id="${imageId}" value="AI生成图片" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;aspect=fixed;imageAspect=0;image=${imageUrl};" vertex="1" parent="1"> | |
| <mxGeometry x="50" y="50" width="${imageWidth}" height="${imageHeight}" as="geometry"/> |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,132 @@ | ||||
| "use client" | ||||
|
|
||||
| import { ImageIcon } from "lucide-react" | ||||
|
||||
| import { ImageIcon } from "lucide-react" |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The height property is incorrectly set to "4" when it should be "h-7" or similar valid Tailwind class. The value "4" without a unit prefix will not work as expected for the button height.
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The height property is incorrectly set to "4" when it should be "h-7" or similar valid Tailwind class. The value "4" without a unit prefix will not work as expected for the button height.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same hardcoded dimensions issue exists here. The width and height should be calculated based on the configured aspect ratio to ensure proper image display.