From fde963f82a2061b4bbfa83c3b936541d8213dcba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:28:51 +0000 Subject: [PATCH 1/7] Initial plan From a06048997b6dba074c7b9cae33398c69d859d918 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:37:13 +0000 Subject: [PATCH 2/7] feat: create ten pre-built widgets and implement gallery page - Created 10 beautiful pre-built widgets using basic components: 1. User Profile Card - profile with avatar, stats, and actions 2. Task List Item - task with priority badge and progress 3. Notification Card - activity notifications with timestamps 4. Weather Widget - current weather with forecast details 5. Stats Dashboard Card - metrics with trend indicators 6. Product Card - e-commerce product with image and details 7. Music Player - compact player with album art and controls 8. Social Media Post - post card with engagement metrics 9. Calendar Event - meeting/event card with participants 10. Email Preview - email card with sender info and actions - Implemented gallery page with: - Grid layout displaying all widgets - Interactive preview of each widget - Dialog showing widget's JSX template with syntax highlighting - "Use This Widget" button to create and edit widget in editor - Navigation integration with sidebar menu Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --- .../src/pages/gallery/WidgetGallery.tsx | 166 +++++++++++++++++- .../pages/gallery/widgets/calendar-event.ts | 70 ++++++++ .../pages/gallery/widgets/email-preview.ts | 64 +++++++ .../src/pages/gallery/widgets/index.ts | 30 ++++ .../src/pages/gallery/widgets/music-player.ts | 50 ++++++ .../gallery/widgets/notification-card.ts | 37 ++++ .../src/pages/gallery/widgets/product-card.ts | 51 ++++++ .../gallery/widgets/social-media-post.ts | 66 +++++++ .../pages/gallery/widgets/stats-dashboard.ts | 47 +++++ .../pages/gallery/widgets/task-list-item.ts | 57 ++++++ .../gallery/widgets/user-profile-card.ts | 61 +++++++ .../pages/gallery/widgets/weather-widget.ts | 56 ++++++ .../src/pages/layout/AppSidebar.tsx | 10 +- 13 files changed, 763 insertions(+), 2 deletions(-) create mode 100644 apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/email-preview.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/index.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/music-player.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/notification-card.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/product-card.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/stats-dashboard.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts create mode 100644 apps/widget-builder/src/pages/gallery/widgets/weather-widget.ts diff --git a/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx b/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx index 9a0463e..03682b6 100644 --- a/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx +++ b/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx @@ -1,3 +1,167 @@ +import { WidgetRenderer } from "@deer-flow/widget-renderer"; +import { Code2, Sparkles } from "lucide-react"; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; + +import { ErrorBoundary } from "@/components/ErrorBoundary"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { components } from "@/components/widget-components"; +import { useWidgets } from "@/hooks/use-widgets"; + +import { galleryWidgets } from "./widgets"; + export const WidgetGallery = () => { - return
Widget Gallery Page
; + const navigate = useNavigate(); + const { createWidget } = useWidgets(); + const [selectedWidget, setSelectedWidget] = useState(null); + const [isDialogOpen, setIsDialogOpen] = useState(false); + + const handleWidgetClick = (index: number) => { + setSelectedWidget(index); + setIsDialogOpen(true); + }; + + const handleUseWidget = () => { + if (selectedWidget === null) return; + + const widget = galleryWidgets[selectedWidget].widget; + const newWidget = createWidget(widget); + setIsDialogOpen(false); + navigate(`/editor/${newWidget.id}`); + }; + + return ( +
+ {/* Header */} +
+
+ +

Widget Gallery

+
+

+ {galleryWidgets.length} pre-built widgets ready to use +

+
+ + {/* Gallery Grid */} + +
+
+ {galleryWidgets.map((item, index) => ( +
handleWidgetClick(index)} + > +
+ {/* Widget Preview */} +
+
+ + + +
+
+ + {/* Widget Info */} +
+

{item.widget.name}

+

+ {item.widget.description} +

+
+ + {/* Hover Overlay */} +
+ +
+
+
+ ))} +
+
+
+ + {/* Widget Details Dialog */} + + + + + {selectedWidget !== null && galleryWidgets[selectedWidget].widget.name} + + + {selectedWidget !== null && galleryWidgets[selectedWidget].widget.description} + + + + +
+ {/* Widget Preview */} +
+
+ {selectedWidget !== null && ( + + + + )} +
+
+ + {/* JSX Template Code */} +
+

JSX Template

+
+ + {selectedWidget !== null ? galleryWidgets[selectedWidget].widget.template || "" : ""} + +
+
+
+
+ + + + + +
+
+
+ ); }; diff --git a/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts b/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts new file mode 100644 index 0000000..31adeae --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts @@ -0,0 +1,70 @@ +import { Widget } from "@deer-flow/widget"; + +export const calendarEvent: Omit = { + name: "Calendar Event", + description: "An event card showing meeting details and participants", + template: ` + + + + {data.month} + {data.day} + + + + + {data.title} + + 🕐 {data.time} + 📍 {data.location} + + + + + + + + {data.description} + + + Participants: + + {data.participants.map((participant, i) => ( + + ))} + + +{data.moreParticipants} + + + + + + + + +`, + states: [ + { + name: "Default", + data: { + month: "NOV", + day: "15", + title: "Team Sync Meeting", + time: "2:00 PM - 3:00 PM", + location: "Conference Room A", + description: "Weekly team sync to discuss project progress and upcoming milestones", + participants: [ + { avatar: "https://i.pravatar.cc/150?img=6" }, + { avatar: "https://i.pravatar.cc/150?img=7" }, + { avatar: "https://i.pravatar.cc/150?img=8" } + ], + moreParticipants: "5" + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts b/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts new file mode 100644 index 0000000..85a9604 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts @@ -0,0 +1,64 @@ +import { Widget } from "@deer-flow/widget"; + +export const emailPreview: Omit = { + name: "Email Preview", + description: "An email preview card with sender info and quick actions", + template: ` + + + + + {data.sender.name} + {data.unread && New} + + {data.sender.email} + + + {data.time} + + + + {data.subject} + + + + {data.preview} + + + {data.hasAttachment && ( + + 📎 + {data.attachmentCount} attachment{data.attachmentCount > 1 ? "s" : ""} + + )} + + + + + + + +`, + states: [ + { + name: "Default", + data: { + sender: { + name: "Emma Wilson", + email: "emma@company.com", + avatar: "https://i.pravatar.cc/150?img=9" + }, + subject: "Q4 Marketing Strategy Review", + preview: "Hi team, I've prepared the Q4 marketing strategy document. Please review the attached presentation and share your feedback before Friday's meeting...", + time: "10:30 AM", + unread: true, + hasAttachment: true, + attachmentCount: 2 + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/index.ts b/apps/widget-builder/src/pages/gallery/widgets/index.ts new file mode 100644 index 0000000..874fcb2 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/index.ts @@ -0,0 +1,30 @@ +import { Widget } from "@deer-flow/widget"; + +import { calendarEvent } from "./calendar-event"; +import { emailPreview } from "./email-preview"; +import { musicPlayer } from "./music-player"; +import { notificationCard } from "./notification-card"; +import { productCard } from "./product-card"; +import { socialMediaPost } from "./social-media-post"; +import { statsDashboard } from "./stats-dashboard"; +import { taskListItem } from "./task-list-item"; +import { userProfileCard } from "./user-profile-card"; +import { weatherWidget } from "./weather-widget"; + +export interface GalleryWidget { + widget: Omit; + thumbnail?: string; +} + +export const galleryWidgets: GalleryWidget[] = [ + { widget: userProfileCard }, + { widget: taskListItem }, + { widget: notificationCard }, + { widget: weatherWidget }, + { widget: statsDashboard }, + { widget: productCard }, + { widget: musicPlayer }, + { widget: socialMediaPost }, + { widget: calendarEvent }, + { widget: emailPreview }, +]; diff --git a/apps/widget-builder/src/pages/gallery/widgets/music-player.ts b/apps/widget-builder/src/pages/gallery/widgets/music-player.ts new file mode 100644 index 0000000..a3bde62 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/music-player.ts @@ -0,0 +1,50 @@ +import { Widget } from "@deer-flow/widget"; + +export const musicPlayer: Omit = { + name: "Music Player", + description: "A compact music player widget with controls", + template: ` + + + + {data.title} + {data.artist} + {data.album} + + + + + + + {data.currentTime} + {data.duration} + + + + + + + + +`, + states: [ + { + name: "Default", + data: { + albumArt: "https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=400", + title: "Midnight Dreams", + artist: "Luna Rivers", + album: "Neon Nights", + currentTime: "2:34", + duration: "4:12", + progress: 62, + playing: true + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/notification-card.ts b/apps/widget-builder/src/pages/gallery/widgets/notification-card.ts new file mode 100644 index 0000000..bd62340 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/notification-card.ts @@ -0,0 +1,37 @@ +import { Widget } from "@deer-flow/widget"; + +export const notificationCard: Omit = { + name: "Notification Card", + description: "A notification card showing activity updates with timestamp", + template: ` + + + {data.icon} + + + + + {data.title} + + {data.type} + + + {data.message} + {data.timestamp} + + +`, + states: [ + { + name: "Default", + data: { + icon: "✓", + title: "Deployment Successful", + message: "Your application has been deployed to production", + type: "success", + timestamp: "2 minutes ago" + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/product-card.ts b/apps/widget-builder/src/pages/gallery/widgets/product-card.ts new file mode 100644 index 0000000..09493e4 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/product-card.ts @@ -0,0 +1,51 @@ +import { Widget } from "@deer-flow/widget"; + +export const productCard: Omit = { + name: "Product Card", + description: "An e-commerce product card with image, details, and actions", + template: ` + + + + + {data.category} + + + {data.rating} + + + + + {data.name} + {data.description} + + + + + {data.price} + {data.oldPrice && {data.oldPrice}} + + + + +`, + states: [ + { + name: "Default", + data: { + image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400", + name: "Wireless Headphones", + description: "Premium noise-cancelling headphones with 30h battery life", + category: "Audio", + price: "$299", + oldPrice: "$399", + rating: "4.8" + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts b/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts new file mode 100644 index 0000000..ff630e5 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts @@ -0,0 +1,66 @@ +import { Widget } from "@deer-flow/widget"; + +export const socialMediaPost: Omit = { + name: "Social Media Post", + description: "A social media post card with engagement metrics", + template: ` + + + + {data.author.name} + {data.timestamp} + + + + + + + {data.content} + + + {data.image && } + + + + + + ❤️ + {data.likes} + + + 💬 + {data.comments} + + + ↗️ + {data.shares} + + +`, + states: [ + { + name: "Default", + data: { + author: { + name: "Alex Johnson", + avatar: "https://i.pravatar.cc/150?img=5" + }, + timestamp: "2 hours ago", + content: "Just launched our new product! Excited to share this journey with you all. 🚀", + image: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=600", + likes: "234", + comments: "45", + shares: "12" + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/stats-dashboard.ts b/apps/widget-builder/src/pages/gallery/widgets/stats-dashboard.ts new file mode 100644 index 0000000..74746f3 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/stats-dashboard.ts @@ -0,0 +1,47 @@ +import { Widget } from "@deer-flow/widget"; + +export const statsDashboard: Omit = { + name: "Stats Dashboard Card", + description: "A statistics dashboard card with metrics and trend indicators", + template: ` + + + Total Revenue + {data.value} + + + {data.trend === "up" ? "↑" : "↓"} {data.change} + + + + + + + + This Month + {data.thisMonth} + +{data.monthGrowth}% from last month + + + + Goal + {data.goal} + + + +`, + states: [ + { + name: "Default", + data: { + value: "$45,231", + trend: "up", + change: "12.5%", + thisMonth: "$12,450", + monthGrowth: "18", + goal: "$15,000", + progress: 83 + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts b/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts new file mode 100644 index 0000000..5f45295 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts @@ -0,0 +1,57 @@ +import { Widget } from "@deer-flow/widget"; + +export const taskListItem: Omit = { + name: "Task List Item", + description: "An elegant task list item with priority badge and due date", + template: ` + + + + {data.priority} + + + + + {data.title} + {data.description} + + + + + + Due Date + {data.dueDate} + + + + + + + + {data.assignee.name} + + + {data.progress}% + +`, + states: [ + { + name: "Default", + data: { + title: "Redesign Landing Page", + description: "Update the homepage with new branding and animations", + priority: "high", + dueDate: "Nov 12", + progress: 65, + assignee: { + name: "Mike Chen", + avatar: "https://i.pravatar.cc/150?img=3" + } + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts b/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts new file mode 100644 index 0000000..9a7cf21 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts @@ -0,0 +1,61 @@ +import { Widget } from "@deer-flow/widget"; + +export const userProfileCard: Omit = { + name: "User Profile Card", + description: "A beautiful user profile card with avatar, name, and social stats", + template: ` + + + + {data.name} + {data.role} + {data.location} + + + + + + + + {data.stats.followers} + Followers + + + {data.stats.following} + Following + + + {data.stats.posts} + Posts + + + + + + + + + +`, + states: [ + { + name: "Default", + data: { + avatar: "https://i.pravatar.cc/150?img=1", + name: "Sarah Anderson", + role: "Product Designer", + location: "San Francisco, CA", + stats: { + followers: "2.4K", + following: "486", + posts: "127" + } + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/gallery/widgets/weather-widget.ts b/apps/widget-builder/src/pages/gallery/widgets/weather-widget.ts new file mode 100644 index 0000000..14cefc3 --- /dev/null +++ b/apps/widget-builder/src/pages/gallery/widgets/weather-widget.ts @@ -0,0 +1,56 @@ +import { Widget } from "@deer-flow/widget"; + +export const weatherWidget: Omit = { + name: "Weather Widget", + description: "Current weather conditions with forecast", + template: ` + + + + {data.location} + {data.date} + + {data.icon} + + + + {data.temperature} + °C + + + {data.condition} + + + + + + Humidity + {data.humidity}% + + + Wind + {data.wind} km/h + + + Feels Like + {data.feelsLike}° + + + +`, + states: [ + { + name: "Default", + data: { + location: "San Francisco", + date: "Monday, Nov 7", + icon: "☀️", + temperature: "22", + condition: "Sunny", + humidity: "65", + wind: "12", + feelsLike: "24" + } + } + ] +}; diff --git a/apps/widget-builder/src/pages/layout/AppSidebar.tsx b/apps/widget-builder/src/pages/layout/AppSidebar.tsx index 42e9ea2..6591d37 100644 --- a/apps/widget-builder/src/pages/layout/AppSidebar.tsx +++ b/apps/widget-builder/src/pages/layout/AppSidebar.tsx @@ -1,4 +1,4 @@ -import { LayoutTemplate, PlusCircleIcon, Package } from "lucide-react"; +import { LayoutTemplate, PlusCircleIcon, Package, Sparkles } from "lucide-react"; import { Link } from "react-router-dom"; import { @@ -51,6 +51,14 @@ export function AppSidebar() { + + + + + Gallery + + + From 20254aee35ea2733e8ab9a46fff9d2fa7f3236e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:44:12 +0000 Subject: [PATCH 3/7] refactor: improve code quality based on review feedback - Extract selectedWidgetData to reduce duplication in dialog rendering - Use computed variable instead of repeated null checks and array access - Improve code readability and maintainability Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --- .../src/pages/gallery/WidgetGallery.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx b/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx index 03682b6..71be9c9 100644 --- a/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx +++ b/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx @@ -41,6 +41,8 @@ export const WidgetGallery = () => { navigate(`/editor/${newWidget.id}`); }; + const selectedWidgetData = selectedWidget !== null ? galleryWidgets[selectedWidget] : null; + return (
{/* Header */} @@ -105,12 +107,8 @@ export const WidgetGallery = () => { - - {selectedWidget !== null && galleryWidgets[selectedWidget].widget.name} - - - {selectedWidget !== null && galleryWidgets[selectedWidget].widget.description} - + {selectedWidgetData?.widget.name} + {selectedWidgetData?.widget.description} @@ -118,13 +116,13 @@ export const WidgetGallery = () => { {/* Widget Preview */}
- {selectedWidget !== null && ( + {selectedWidgetData && ( )} @@ -144,7 +142,7 @@ export const WidgetGallery = () => { fontSize: "0.875rem", }} > - {selectedWidget !== null ? galleryWidgets[selectedWidget].widget.template || "" : ""} + {selectedWidgetData?.widget.template || ""}
From ab14c6ed1d90c38d8ebea37c88b44b3a91d0366c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 01:25:21 +0000 Subject: [PATCH 4/7] feat: add template prop support to WidgetRenderer - Parse template string to schema when schema is not provided - Allows rendering widgets directly from JSX template strings - Enables gallery to use template prop instead of pre-parsing schemas Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --- .../widget-renderer/src/WidgetRenderer.tsx | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/packages/widget-renderer/src/WidgetRenderer.tsx b/packages/widget-renderer/src/WidgetRenderer.tsx index 3429a3f..4058085 100644 --- a/packages/widget-renderer/src/WidgetRenderer.tsx +++ b/packages/widget-renderer/src/WidgetRenderer.tsx @@ -1,4 +1,4 @@ -import { JSXElementSchema, executeExpression } from "@deer-flow/widget"; +import { JSXElementSchema, executeExpression, parseJSXTemplate } from "@deer-flow/widget"; import React from "react"; import { ComponentType } from "react"; @@ -6,6 +6,7 @@ export type WidgetRendererProps = { schema?: JSXElementSchema; components: Record>; data?: Record; + template?: string; }; // Helper to resolve expression values from data context @@ -22,42 +23,54 @@ function resolveValue(value: JSXElementSchema["value"], data: Record{content}; } case "expression": { - const content = resolveValue(schema.value, data ?? {}); + const content = resolveValue(effectiveSchema.value, data ?? {}); return <>{content}; } case "element": { - if (!schema.name) { - console.warn("Element schema missing 'name' property:", schema); + if (!effectiveSchema.name) { + console.warn("Element schema missing 'name' property:", effectiveSchema); return null; } // Resolve component from the map, or fall back to a string for native HTML elements - const Component = components[schema.name] || schema.name; + const Component = components[effectiveSchema.name] || effectiveSchema.name; const props: Record = {}; - if (schema.props) { - for (const key in schema.props) { - if (Object.prototype.hasOwnProperty.call(schema.props, key)) { + if (effectiveSchema.props) { + for (const key in effectiveSchema.props) { + if (Object.prototype.hasOwnProperty.call(effectiveSchema.props, key)) { // Resolve prop values, which might be expressions - props[key] = resolveValue(schema.props[key] as JSXElementSchema["value"], data ?? {}); + props[key] = resolveValue(effectiveSchema.props[key] as JSXElementSchema["value"], data ?? {}); } } } // Recursively render children - const children = schema.children - ? schema.children.map((child, index) => ( + const children = effectiveSchema.children + ? effectiveSchema.children.map((child, index) => ( )) : undefined; @@ -66,7 +79,7 @@ export function WidgetRenderer({ schema, components, data }: WidgetRendererProps } default: - console.warn("Unknown schema node type:", schema.type); + console.warn("Unknown schema node type:", effectiveSchema.type); return null; } } From 1a34d1a42030f0bfcd5404ff74ea89772f27d5cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 01:44:38 +0000 Subject: [PATCH 5/7] perf: optimize template parsing with useRef and useEffect - Cache parsed schema in useRef to avoid re-parsing on every render - Only parse template when it changes using useEffect - Track last template value to detect changes - Improves performance by preventing unnecessary parsing operations Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --- .../widget-renderer/src/WidgetRenderer.tsx | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/widget-renderer/src/WidgetRenderer.tsx b/packages/widget-renderer/src/WidgetRenderer.tsx index 4058085..ae77fe4 100644 --- a/packages/widget-renderer/src/WidgetRenderer.tsx +++ b/packages/widget-renderer/src/WidgetRenderer.tsx @@ -1,5 +1,5 @@ import { JSXElementSchema, executeExpression, parseJSXTemplate } from "@deer-flow/widget"; -import React from "react"; +import React, { useRef, useEffect } from "react"; import { ComponentType } from "react"; export type WidgetRendererProps = { @@ -24,17 +24,30 @@ function resolveValue(value: JSXElementSchema["value"], data: Record(null); + const lastTemplateRef = useRef(undefined); + + // Parse template only when it changes + useEffect(() => { + if (template && template !== lastTemplateRef.current) { + const parseResult = parseJSXTemplate(template); + if (parseResult.success && parseResult.schema) { + parsedSchemaRef.current = parseResult.schema; + } else { + console.warn("Failed to parse template:", parseResult.errors); + parsedSchemaRef.current = null; + } + lastTemplateRef.current = template; + } else if (!template && lastTemplateRef.current !== undefined) { + // Template was removed + parsedSchemaRef.current = null; + lastTemplateRef.current = undefined; } - } + }, [template]); + + // Determine effective schema + const effectiveSchema = schema || parsedSchemaRef.current; if (!effectiveSchema) { return null; From b370f1dfab2b370fba85b8b9107b2f6364145138 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 02:53:29 +0000 Subject: [PATCH 6/7] fix: update image URLs to use picsum.photos - Replace pravatar.cc URLs with picsum.photos/150/150?random=X - Replace unsplash URLs with picsum.photos/{width}/{height}?random=X - All avatar images now use 150x150 format - Album art and product images use appropriate dimensions - Social media post image uses 600x400 aspect ratio Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --- .../src/pages/gallery/widgets/calendar-event.ts | 6 +++--- .../src/pages/gallery/widgets/email-preview.ts | 2 +- .../src/pages/gallery/widgets/music-player.ts | 2 +- .../src/pages/gallery/widgets/product-card.ts | 2 +- .../src/pages/gallery/widgets/social-media-post.ts | 4 ++-- .../src/pages/gallery/widgets/task-list-item.ts | 2 +- .../src/pages/gallery/widgets/user-profile-card.ts | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts b/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts index 31adeae..e426a26 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/calendar-event.ts @@ -59,9 +59,9 @@ export const calendarEvent: Omit = { location: "Conference Room A", description: "Weekly team sync to discuss project progress and upcoming milestones", participants: [ - { avatar: "https://i.pravatar.cc/150?img=6" }, - { avatar: "https://i.pravatar.cc/150?img=7" }, - { avatar: "https://i.pravatar.cc/150?img=8" } + { avatar: "https://picsum.photos/150/150?random=6" }, + { avatar: "https://picsum.photos/150/150?random=7" }, + { avatar: "https://picsum.photos/150/150?random=8" } ], moreParticipants: "5" } diff --git a/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts b/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts index 85a9604..c9978d6 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/email-preview.ts @@ -50,7 +50,7 @@ export const emailPreview: Omit = { sender: { name: "Emma Wilson", email: "emma@company.com", - avatar: "https://i.pravatar.cc/150?img=9" + avatar: "https://picsum.photos/150/150?random=9" }, subject: "Q4 Marketing Strategy Review", preview: "Hi team, I've prepared the Q4 marketing strategy document. Please review the attached presentation and share your feedback before Friday's meeting...", diff --git a/apps/widget-builder/src/pages/gallery/widgets/music-player.ts b/apps/widget-builder/src/pages/gallery/widgets/music-player.ts index a3bde62..1cb2eee 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/music-player.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/music-player.ts @@ -36,7 +36,7 @@ export const musicPlayer: Omit = { { name: "Default", data: { - albumArt: "https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=400", + albumArt: "https://picsum.photos/400/400?random=20", title: "Midnight Dreams", artist: "Luna Rivers", album: "Neon Nights", diff --git a/apps/widget-builder/src/pages/gallery/widgets/product-card.ts b/apps/widget-builder/src/pages/gallery/widgets/product-card.ts index 09493e4..c1df075 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/product-card.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/product-card.ts @@ -38,7 +38,7 @@ export const productCard: Omit = { { name: "Default", data: { - image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400", + image: "https://picsum.photos/400/400?random=10", name: "Wireless Headphones", description: "Premium noise-cancelling headphones with 30h battery life", category: "Audio", diff --git a/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts b/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts index ff630e5..b47e6da 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/social-media-post.ts @@ -52,11 +52,11 @@ export const socialMediaPost: Omit = { data: { author: { name: "Alex Johnson", - avatar: "https://i.pravatar.cc/150?img=5" + avatar: "https://picsum.photos/150/150?random=5" }, timestamp: "2 hours ago", content: "Just launched our new product! Excited to share this journey with you all. 🚀", - image: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=600", + image: "https://picsum.photos/600/400?random=30", likes: "234", comments: "45", shares: "12" diff --git a/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts b/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts index 5f45295..71dd2cd 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/task-list-item.ts @@ -49,7 +49,7 @@ export const taskListItem: Omit = { progress: 65, assignee: { name: "Mike Chen", - avatar: "https://i.pravatar.cc/150?img=3" + avatar: "https://picsum.photos/150/150?random=3" } } } diff --git a/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts b/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts index 9a7cf21..7eb13f2 100644 --- a/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts +++ b/apps/widget-builder/src/pages/gallery/widgets/user-profile-card.ts @@ -46,7 +46,7 @@ export const userProfileCard: Omit = { { name: "Default", data: { - avatar: "https://i.pravatar.cc/150?img=1", + avatar: "https://picsum.photos/150/150?random=1", name: "Sarah Anderson", role: "Product Designer", location: "San Francisco, CA", From 58cf9eb1660d64cba957d6608855ebe7593356ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 02:58:20 +0000 Subject: [PATCH 7/7] feat: add keyboard accessibility to gallery widgets - Add role="button" to clickable gallery items - Add tabIndex={0} for keyboard navigation - Add onKeyDown handler for Enter and Space keys - Add aria-label for screen reader support - Add focus ring styling with focus:ring-2 focus:ring-primary - Improves accessibility for users who rely on keyboard navigation Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --- .../src/pages/gallery/WidgetGallery.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx b/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx index 71be9c9..961b1bd 100644 --- a/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx +++ b/apps/widget-builder/src/pages/gallery/WidgetGallery.tsx @@ -41,6 +41,13 @@ export const WidgetGallery = () => { navigate(`/editor/${newWidget.id}`); }; + const handleKeyDown = (event: React.KeyboardEvent, index: number) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + handleWidgetClick(index); + } + }; + const selectedWidgetData = selectedWidget !== null ? galleryWidgets[selectedWidget] : null; return ( @@ -63,8 +70,12 @@ export const WidgetGallery = () => { {galleryWidgets.map((item, index) => (
handleWidgetClick(index)} + onKeyDown={(e) => handleKeyDown(e, index)} + aria-label={`View ${item.widget.name} details`} >
{/* Widget Preview */}