这是一个示例卡片,将被导出为图片。
112 |您可以在卡片中添加任何HTML内容,包括文本、图像、表格等。
113 |卡片的样式和内容将会被完整地保存到图片中。
114 |{element};
13 | }
14 |
15 | if (leaf.italic) {
16 | element = {element};
17 | }
18 |
19 | if (leaf.underline) {
20 | element = {element};
21 | }
22 |
23 | const style: React.CSSProperties = {};
24 |
25 | if (leaf.color) {
26 | style.color = leaf.color;
27 | }
28 |
29 | if (leaf.fontSize) {
30 | style.fontSize = leaf.fontSize;
31 | }
32 |
33 | if (leaf.backgroundColor) {
34 | style.backgroundColor = leaf.backgroundColor;
35 | }
36 |
37 | return (
38 |
39 | {element}
40 |
41 | );
42 | };
--------------------------------------------------------------------------------
/smart-card-react/src/modules/html-editor/components/ToolbarButton.tsx:
--------------------------------------------------------------------------------
1 | import React, { PropsWithChildren } from 'react';
2 |
3 | interface ToolbarButtonProps {
4 | active?: boolean;
5 | onMouseDown?: (event: React.MouseEvent这是一个基于Slate.js的可视化HTML编辑器,支持文本格式化、颜色调整和元素尺寸设置。
16 | 17 |{html}
24 | 17 | {children} 18 |19 | ); 20 | case 'bulleted-list': 21 | return ( 22 |
72 | {children} 73 |
74 | ); 75 | } 76 | }; -------------------------------------------------------------------------------- /smart-card-react/src/components/ui/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Slot } from '@radix-ui/react-slot'; 3 | import { cva, type VariantProps } from 'class-variance-authority'; 4 | import { cn } from '@/lib/utils'; 5 | import { Loader2 } from 'lucide-react'; 6 | 7 | const buttonVariants = cva( 8 | 'inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', 9 | { 10 | variants: { 11 | variant: { 12 | primary: 'bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500', 13 | secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus-visible:ring-gray-500', 14 | outline: 'border border-gray-300 bg-white text-gray-900 hover:bg-gray-50 focus-visible:ring-gray-500', 15 | ghost: 'text-gray-900 hover:bg-gray-100 focus-visible:ring-gray-500', 16 | }, 17 | size: { 18 | sm: 'px-3 py-1.5 text-sm rounded-md', 19 | md: 'px-4 py-2 text-sm rounded-md', 20 | lg: 'px-6 py-3 text-base rounded-lg', 21 | }, 22 | fullWidth: { 23 | true: 'w-full', 24 | } 25 | }, 26 | defaultVariants: { 27 | variant: 'primary', 28 | size: 'md', 29 | }, 30 | } 31 | ); 32 | 33 | export interface ButtonProps 34 | extends React.ButtonHTMLAttributes{error}
36 | )} 37 | {helperText && !error && ( 38 |{helperText}
39 | )} 40 |{error}
76 | )} 77 | {helperText && !error && ( 78 |{helperText}
79 | )} 80 |
47 | <style> 标签和 style 属性。<script> 标签将不会被执行。
15 |
16 |
17 |
18 | 加入智能卡片工坊,开启您的创作之旅。
51 |107 | 已经有账户了?{' '} 108 | 109 | 前往登录 110 | 111 |
112 |
59 | 使用固定密码登录:
60 | 邮箱: {MAGIC_EMAIL}
61 | 密码: {MAGIC_PASSWORD}
62 |
109 | 还没有账户?{' '} 110 | 111 | 立即注册 112 | 113 |
114 |${text}`;
127 | if (child.color) text = `${text}`;
128 | if (child.fontSize) text = `${text}`;
129 | return text;
130 | }).join('');
131 | return `${textContent}
`; 132 | } 133 | return ''; 134 | }).join(''); 135 | }; -------------------------------------------------------------------------------- /tools/selenium2img.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.options import Options 3 | import time 4 | import os 5 | from .card_extractor import extract_card_from_image 6 | 7 | def html_to_image(html_path, output_path, width=393, height=None): 8 | """ 9 | Renders HTML file to an image using Selenium and Chrome, emulating a mobile device. 10 | 11 | Parameters: 12 | html_path: Path to HTML file or URL 13 | output_path: Path to save the output image 14 | width: Width of the viewport in pixels (default: 393px - iPhone 15 width) 15 | height: Height of the viewport in pixels (None for auto, dynamically calculated) 16 | """ 17 | # Configure Chrome options 18 | chrome_options = Options() 19 | chrome_options.add_argument("--headless") # Run in headless mode 20 | chrome_options.add_argument("--disable-gpu") 21 | # Remove specific window size and user agent as mobile emulation handles this 22 | # chrome_options.add_argument(f"--window-size={width},{height or 852}") 23 | # chrome_options.add_argument("--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1") 24 | chrome_options.add_argument("--hide-scrollbars") 25 | chrome_options.add_argument("--no-sandbox") 26 | chrome_options.add_argument("--disable-dev-shm-usage") 27 | chrome_options.add_argument("--force-device-scale-factor=2") # Keep high DPI 28 | 29 | # Define mobile emulation settings for iPhone 15 30 | mobile_emulation = { 31 | "deviceMetrics": { 32 | "width": width, 33 | "height": height or 852, # Use a default height if not dynamic 34 | "pixelRatio": 3.0 # iPhone 15 has a 3x pixel ratio 35 | }, 36 | "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1" 37 | } 38 | chrome_options.add_experimental_option("mobileEmulation", mobile_emulation) 39 | 40 | try: 41 | # Initialize the driver with mobile emulation options 42 | driver = webdriver.Chrome(options=chrome_options) 43 | 44 | # Convert to absolute path if it's a local file 45 | if not html_path.startswith('http'): 46 | html_path = 'file://' + os.path.abspath(html_path) 47 | 48 | # Load the page 49 | driver.get(html_path) 50 | 51 | # Wait for page rendering and any JavaScript execution 52 | time.sleep(2) 53 | 54 | # Dynamically calculate height if needed 55 | if height is None: 56 | # Calculate the full page height 57 | calculated_height = driver.execute_script(""" 58 | return Math.max( 59 | document.body.scrollHeight, 60 | document.documentElement.scrollHeight, 61 | document.body.offsetHeight, 62 | document.documentElement.offsetHeight, 63 | document.body.clientHeight, 64 | document.documentElement.clientHeight 65 | ); 66 | """) 67 | print(f"自适应内容高度: {calculated_height}像素") 68 | 69 | # Update mobile emulation height and re-initialize driver for correct height 70 | # Note: Re-initializing is necessary because mobileEmulation height is set at launch 71 | driver.quit() # Close the current driver 72 | mobile_emulation["deviceMetrics"]["height"] = calculated_height 73 | chrome_options.add_experimental_option("mobileEmulation", mobile_emulation) 74 | driver = webdriver.Chrome(options=chrome_options) # Re-launch with correct height 75 | driver.get(html_path) # Reload page 76 | time.sleep(1) # Wait for reload 77 | 78 | # Capture screenshot 79 | driver.save_screenshot(output_path) 80 | print(f"Image saved to {output_path}") 81 | return True 82 | 83 | except Exception as e: 84 | print(f"Error converting HTML to image: {e}") 85 | return False 86 | 87 | finally: 88 | if 'driver' in locals(): 89 | driver.quit() 90 | 91 | # Only run example code when this file is executed directly, not when imported 92 | if __name__ == "__main__": 93 | # Example usage 94 | html_to_image('./96a32822-aebe-4f5a-a2c6-2bed830af5f9.html', 'output.png') 95 | # Uncommenting the below line will run the card extraction too 96 | extract_card_from_image('output.png', 'card.png') -------------------------------------------------------------------------------- /smart-card-react/src/services/api.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance, AxiosResponse } from 'axios'; 2 | import { 3 | AIGenerationRequest, 4 | AIGenerationResponse, 5 | WebScrapingRequest, 6 | WebScrapingResponse, 7 | ImageExportOptions, 8 | ImageExportResponse, 9 | Card 10 | } from '@/types'; 11 | 12 | class ApiService { 13 | private apiClient: AxiosInstance; 14 | 15 | constructor() { 16 | this.apiClient = axios.create({ 17 | baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000', 18 | timeout: 30000, 19 | headers: { 20 | 'Content-Type': 'application/json', 21 | }, 22 | }); 23 | 24 | // 请求拦截器 25 | this.apiClient.interceptors.request.use( 26 | (config) => { 27 | console.log('API Request:', config.method?.toUpperCase(), config.url); 28 | return config; 29 | }, 30 | (error) => { 31 | return Promise.reject(error); 32 | } 33 | ); 34 | 35 | // 响应拦截器 36 | this.apiClient.interceptors.response.use( 37 | (response: AxiosResponse) => { 38 | console.log('API Response:', response.status, response.config.url); 39 | return response; 40 | }, 41 | (error) => { 42 | console.error('API Error:', error.response?.data || error.message); 43 | return Promise.reject(error); 44 | } 45 | ); 46 | } 47 | 48 | // AI 内容生成 49 | async generateContent(request: AIGenerationRequest): Promise这个演示展示了如何使用html2image.js将HTML元素导出为图片:
100 |这是一个示例卡片,将被导出为图片。
112 |您可以在卡片中添加任何HTML内容,包括文本、图像、表格等。
113 |卡片的样式和内容将会被完整地保存到图片中。
114 |{msg.text}
102 |正在思考...
111 |