diff --git a/README.md b/README.md index 41dfee1..5f5fd3b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ CodeForge 是一款轻量级、高性能的桌面代码执行器,专为开发 - **C** - **Clojure** - **C++** +- **CSS** - **Go** - **Groovy** - **HTML** @@ -40,6 +41,7 @@ CodeForge 是一款轻量级、高性能的桌面代码执行器,专为开发 - **Ruby** - **Rust** - **Shell** +- **SVG** - **Swift** - **TypeScript** - **TypeScript (Browser)** diff --git a/package.json b/package.json index 45d7ca0..5337648 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,14 @@ "dependencies": { "@babel/runtime": "^7.28.2", "@codemirror/lang-cpp": "^6.0.3", + "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-go": "^6.0.1", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-java": "^6.0.2", "@codemirror/lang-javascript": "^6.2.4", "@codemirror/lang-python": "^6.2.1", "@codemirror/lang-rust": "^6.0.2", + "@codemirror/lang-xml": "^6.1.0", "@codemirror/language": "^6.11.2", "@codemirror/legacy-modes": "^6.5.1", "@codemirror/state": "^6.5.2", diff --git a/public/icons/css.svg b/public/icons/css.svg new file mode 100644 index 0000000..b765c26 --- /dev/null +++ b/public/icons/css.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/public/icons/svg.svg b/public/icons/svg.svg new file mode 100644 index 0000000..75605fd --- /dev/null +++ b/public/icons/svg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src-tauri/src/examples/css.css b/src-tauri/src/examples/css.css new file mode 100644 index 0000000..96c30f2 --- /dev/null +++ b/src-tauri/src/examples/css.css @@ -0,0 +1,631 @@ +/* CSS 示例代码 - CodeForge 代码执行环境 */ + +/* ========================================= */ +/* CodeForge CSS */ +/* ========================================= */ + +/* 🎨 基础选择器示例 (Basic selectors) */ +body { + font-family: 'Arial', 'Helvetica', sans-serif; + margin: 0; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: #333; + line-height: 1.6; +} + +/* 📝 类选择器示例 (Class selectors) */ +.container { + max-width: 1200px; + margin: 0 auto; + background: rgba(255, 255, 255, 0.95); + border-radius: 15px; + padding: 30px; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); +} + +.header { + text-align: center; + margin-bottom: 40px; + position: relative; +} + +.header::before { + content: '🚀'; + font-size: 3em; + display: block; + margin-bottom: 10px; +} + +/* 🏷️ ID选择器示例 (ID selectors) */ +#main-title { + color: #2c3e50; + font-size: 2.5em; + font-weight: bold; + margin: 0; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1); + background: linear-gradient(45deg, #3498db, #9b59b6); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +#subtitle { + color: #7f8c8d; + font-size: 1.2em; + margin-top: 10px; + font-style: italic; +} + +/* 🔧 属性选择器示例 (Attribute selectors) */ +input[type="text"] { + width: 100%; + padding: 12px 15px; + border: 2px solid #ddd; + border-radius: 8px; + font-size: 16px; + transition: all 0.3s ease; + box-sizing: border-box; +} + +input[type="text"]:focus { + border-color: #3498db; + box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); + outline: none; + transform: translateY(-2px); +} + +button[disabled] { + opacity: 0.6; + cursor: not-allowed; + filter: grayscale(50%); +} + +/* 🎯 伪类选择器示例 (Pseudo-class selectors) */ +a:link { + color: #3498db; + text-decoration: none; +} + +a:visited { + color: #9b59b6; +} + +a:hover { + color: #e74c3c; + text-decoration: underline; + transform: scale(1.05); + display: inline-block; + transition: all 0.2s ease; +} + +a:active { + color: #c0392b; + transform: scale(0.98); +} + +/* 📋 列表样式示例 (List styles) */ +.feature-list { + list-style: none; + padding: 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin: 30px 0; +} + +.feature-list li { + background: linear-gradient(45deg, #ff6b6b, #ffa500); + color: white; + padding: 20px; + border-radius: 10px; + position: relative; + transform: translateY(0); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); +} + +.feature-list li:hover { + transform: translateY(-5px) scale(1.02); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2); +} + +.feature-list li::before { + content: '✨'; + position: absolute; + top: -10px; + right: -10px; + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; +} + +/* 📱 响应式设计示例 (Responsive design) */ +@media screen and (max-width: 768px) { + .container { + margin: 10px; + padding: 20px; + border-radius: 10px; + } + + #main-title { + font-size: 2em; + } + + .feature-list { + grid-template-columns: 1fr; + gap: 15px; + } + + .feature-list li { + padding: 15px; + } +} + +@media screen and (max-width: 480px) { + body { + padding: 10px; + } + + #main-title { + font-size: 1.8em; + } + + .container { + padding: 15px; + } +} + +/* 🎪 动画示例 (Animation examples) */ +@keyframes bounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateY(0); + } + 40% { + transform: translateY(-20px); + } + 60% { + transform: translateY(-10px); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translate3d(0, 40px, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.bounce-animation { + animation: bounce 2s infinite; +} + +.fade-in-animation { + animation: fadeInUp 0.8s ease-out; +} + +.rotate-animation { + animation: rotate 2s linear infinite; + display: inline-block; +} + +/* 🎨 CSS Grid 布局示例 (CSS Grid layout) */ +.grid-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + grid-gap: 20px; + margin: 30px 0; +} + +.grid-item { + background: linear-gradient(135deg, #667eea, #764ba2); + color: white; + padding: 25px; + border-radius: 12px; + text-align: center; + transition: all 0.3s ease; +} + +.grid-item:nth-child(odd) { + background: linear-gradient(135deg, #f093fb, #f5576c); +} + +.grid-item:nth-child(even) { + background: linear-gradient(135deg, #4facfe, #00f2fe); +} + +.grid-item:hover { + transform: translateY(-10px) rotate(2deg); + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); +} + +/* 🌟 Flexbox 布局示例 (Flexbox layout) */ +.flex-container { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 15px; + margin: 25px 0; + padding: 20px; + background: rgba(52, 152, 219, 0.1); + border-radius: 10px; + border: 2px dashed #3498db; +} + +.flex-item { + flex: 1; + min-width: 120px; + padding: 15px; + background: white; + border-radius: 8px; + text-align: center; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.flex-item:hover { + transform: scale(1.05); + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); +} + +/* 🎭 伪元素示例 (Pseudo-elements) */ +.quote { + position: relative; + font-style: italic; + font-size: 1.2em; + color: #2c3e50; + padding: 20px 40px; + margin: 30px 0; + background: rgba(236, 240, 241, 0.8); + border-left: 4px solid #3498db; + border-radius: 0 10px 10px 0; +} + +.quote::before { + content: '"'; + font-size: 4em; + color: #3498db; + position: absolute; + top: -10px; + left: 10px; + opacity: 0.3; +} + +.quote::after { + content: '"'; + font-size: 4em; + color: #3498db; + position: absolute; + bottom: -40px; + right: 10px; + opacity: 0.3; +} + +/* 🔧 CSS变量示例 (CSS variables) */ +:root { + --primary-color: #3498db; + --secondary-color: #2ecc71; + --danger-color: #e74c3c; + --warning-color: #f39c12; + --dark-color: #2c3e50; + --light-color: #ecf0f1; + --border-radius: 8px; + --box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + --transition: all 0.3s ease; +} + +.button { + display: inline-block; + padding: 12px 24px; + margin: 5px; + border: none; + border-radius: var(--border-radius); + font-size: 16px; + font-weight: bold; + text-decoration: none; + cursor: pointer; + transition: var(--transition); + box-shadow: var(--box-shadow); +} + +.button-primary { + background-color: var(--primary-color); + color: white; +} + +.button-primary:hover { + background-color: #2980b9; + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4); +} + +.button-success { + background-color: var(--secondary-color); + color: white; +} + +.button-success:hover { + background-color: #27ae60; + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(46, 204, 113, 0.4); +} + +.button-danger { + background-color: var(--danger-color); + color: white; +} + +.button-danger:hover { + background-color: #c0392b; + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(231, 76, 60, 0.4); +} + +/* 🌈 渐变和阴影示例 (Gradients and shadows) */ +.gradient-box { + background: linear-gradient(45deg, + #ff9a9e 0%, + #fecfef 50%, + #fecfef 100%); + padding: 30px; + border-radius: 15px; + text-align: center; + color: #333; + margin: 20px 0; + position: relative; + overflow: hidden; +} + +.gradient-box::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(255, 255, 255, 0.4), + transparent); + transition: left 0.5s; +} + +.gradient-box:hover::before { + left: 100%; +} + +/* 🎯 表单样式示例 (Form styles) */ +.form-group { + margin-bottom: 20px; + position: relative; +} + +.form-label { + display: block; + margin-bottom: 5px; + font-weight: bold; + color: var(--dark-color); +} + +.form-control { + width: 100%; + padding: 12px 15px; + border: 2px solid #ddd; + border-radius: var(--border-radius); + font-size: 16px; + transition: var(--transition); + box-sizing: border-box; +} + +.form-control:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); + outline: none; +} + +.form-control:invalid { + border-color: var(--danger-color); +} + +.form-control:valid { + border-color: var(--secondary-color); +} + +/* 💫 高级效果示例 (Advanced effects) */ +.glass-effect { + background: rgba(255, 255, 255, 0.2); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 15px; + padding: 30px; + color: #333; + box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37); +} + +.neon-text { + color: #fff; + text-align: center; + font-size: 2em; + font-weight: bold; + text-shadow: 0 0 5px #00ff00, + 0 0 10px #00ff00, + 0 0 15px #00ff00, + 0 0 20px #00ff00, + 0 0 35px #00ff00, + 0 0 40px #00ff00; + animation: flicker 2s infinite alternate; +} + +@keyframes flicker { + 0%, 18%, 22%, 25%, 53%, 57%, 100% { + text-shadow: 0 0 5px #00ff00, + 0 0 10px #00ff00, + 0 0 15px #00ff00, + 0 0 20px #00ff00, + 0 0 35px #00ff00, + 0 0 40px #00ff00; + } + 20%, 24%, 55% { + text-shadow: none; + } +} + +/* 🎪 3D效果示例 (3D effects) */ +.card-3d { + width: 300px; + height: 200px; + margin: 50px auto; + perspective: 1000px; +} + +.card-inner { + width: 100%; + height: 100%; + transition: transform 0.6s; + transform-style: preserve-3d; + cursor: pointer; +} + +.card-3d:hover .card-inner { + transform: rotateY(180deg); +} + +.card-front, .card-back { + position: absolute; + width: 100%; + height: 100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + border-radius: 15px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5em; + font-weight: bold; + color: white; +} + +.card-front { + background: linear-gradient(45deg, #667eea, #764ba2); +} + +.card-back { + background: linear-gradient(45deg, #f093fb, #f5576c); + transform: rotateY(180deg); +} + +/* 📱 现代布局示例 (Modern layout) */ +.modern-grid { + display: grid; + grid-template-areas: + "header header header" + "sidebar main aside" + "footer footer footer"; + grid-template-columns: 200px 1fr 200px; + grid-template-rows: auto 1fr auto; + min-height: 400px; + gap: 20px; + margin: 30px 0; +} + +.header-area { + grid-area: header; + background: linear-gradient(90deg, #667eea, #764ba2); + color: white; + padding: 20px; + border-radius: 10px; + text-align: center; +} + +.sidebar-area { + grid-area: sidebar; + background: #3498db; + color: white; + padding: 20px; + border-radius: 10px; +} + +.main-area { + grid-area: main; + background: #2ecc71; + color: white; + padding: 20px; + border-radius: 10px; +} + +.aside-area { + grid-area: aside; + background: #e74c3c; + color: white; + padding: 20px; + border-radius: 10px; +} + +.footer-area { + grid-area: footer; + background: linear-gradient(90deg, #f093fb, #f5576c); + color: white; + padding: 20px; + border-radius: 10px; + text-align: center; +} + +/* 🎨 CSS 完成标记 */ +.completion-banner { + text-align: center; + padding: 40px 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border-radius: 15px; + margin: 40px 0; + position: relative; + overflow: hidden; +} + +.completion-banner::before { + content: '🎉'; + font-size: 5em; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + opacity: 0.1; + animation: rotate 10s linear infinite; +} + +.completion-banner h2 { + margin: 0; + font-size: 2em; + margin-bottom: 10px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +.completion-banner p { + margin: 0; + font-size: 1.2em; + opacity: 0.9; +} + +/* 🚀 感谢使用 CodeForge! */ +/* Thank you for using CodeForge! */ \ No newline at end of file diff --git a/src-tauri/src/examples/svg.svg b/src-tauri/src/examples/svg.svg new file mode 100644 index 0000000..bc49f8a --- /dev/null +++ b/src-tauri/src/examples/svg.svg @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CodeForge SVG 示例 + + + Scalable Vector Graphics Demo + + + + + + + + + 圆形动画 + + + + + + + 旋转矩形 + + + + + + + 缩放星形 + + + + + + + 透明椭圆 + + + + + + + 路径动画 + + + + + + 数据可视化示例 + + + + + + + + + + + + + + + + + + + + + + + A + B + C + D + + + + + + 饼图示例 + + + + + + + + + + + + + + + + + + + + 统计 + + + + + + 图标示例 + + + + + + + + 心形 + + + + + + + + 旋转星 + + + + + + + + 设置 + + + + + + + + + + + + + 感谢使用 CodeForge SVG 示例! + + + + + + CodeForge - Scalable Vector Graphics Demo + + \ No newline at end of file diff --git a/src-tauri/src/plugins/css.rs b/src-tauri/src/plugins/css.rs new file mode 100644 index 0000000..6a889b0 --- /dev/null +++ b/src-tauri/src/plugins/css.rs @@ -0,0 +1,55 @@ +use super::{LanguagePlugin, PluginConfig}; +use std::vec; + +pub struct CssPlugin; + +impl LanguagePlugin for CssPlugin { + fn get_order(&self) -> i32 { + 21 + } + + fn get_language_name(&self) -> &'static str { + "CSS" + } + + fn get_language_key(&self) -> &'static str { + "css" + } + + fn get_file_extension(&self) -> String { + self.get_config() + .map(|config| config.extension.clone()) + .unwrap_or_else(|| "css".to_string()) + } + + fn get_version_args(&self) -> Vec<&'static str> { + vec!["--"] + } + + fn get_path_command(&self) -> String { + "--".to_string() + } + + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("css"), + before_compile: None, + extension: String::from("css"), + execute_home: None, + run_command: Some(String::from( + "echo ", + )), + after_compile: None, + template: Some(String::from("// 在这里输入 CSS 代码")), + timeout: Some(30), + console_type: Some(String::from("web")), + } + } + + fn get_default_command(&self) -> String { + self.get_config() + .and_then(|config| config.run_command.clone()) + .unwrap_or_else(|| "bash".to_string()) + } +} diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index 3398a59..47dc46e 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -3,6 +3,7 @@ use crate::plugins::applescript::AppleScriptPlugin; use crate::plugins::c::CPlugin; use crate::plugins::clojure::ClojurePlugin; use crate::plugins::cpp::CppPlugin; +use crate::plugins::css::CssPlugin; use crate::plugins::go::GoPlugin; use crate::plugins::groovy::GroovyPlugin; use crate::plugins::html::HtmlPlugin; @@ -18,6 +19,7 @@ use crate::plugins::ruby::RubyPlugin; use crate::plugins::rust::RustPlugin; use crate::plugins::scala::ScalaPlugin; use crate::plugins::shell::ShellPlugin; +use crate::plugins::svg::SvgPlugin; use crate::plugins::swift::SwiftPlugin; use crate::plugins::typescript::TypeScriptPlugin; use crate::plugins::typescript_browser::TypeScriptBrowserPlugin; @@ -50,6 +52,8 @@ impl PluginManager { plugins.insert("cpp".to_string(), Box::new(CppPlugin)); plugins.insert("groovy".to_string(), Box::new(GroovyPlugin)); plugins.insert("html".to_string(), Box::new(HtmlPlugin)); + plugins.insert("css".to_string(), Box::new(CssPlugin)); + plugins.insert("svg".to_string(), Box::new(SvgPlugin)); plugins.insert( "javascript-nodejs".to_string(), Box::new(JavaScriptNodeJsPlugin), diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index a700e5e..0bd60ef 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -373,6 +373,7 @@ pub mod applescript; pub mod c; pub mod clojure; pub mod cpp; +pub mod css; pub mod go; pub mod groovy; pub mod html; @@ -389,6 +390,7 @@ pub mod ruby; pub mod rust; pub mod scala; pub mod shell; +pub mod svg; pub mod swift; pub mod typescript; pub mod typescript_browser; diff --git a/src-tauri/src/plugins/svg.rs b/src-tauri/src/plugins/svg.rs new file mode 100644 index 0000000..17f84e5 --- /dev/null +++ b/src-tauri/src/plugins/svg.rs @@ -0,0 +1,55 @@ +use super::{LanguagePlugin, PluginConfig}; +use std::vec; + +pub struct SvgPlugin; + +impl LanguagePlugin for SvgPlugin { + fn get_order(&self) -> i32 { + 22 + } + + fn get_language_name(&self) -> &'static str { + "SVG" + } + + fn get_language_key(&self) -> &'static str { + "svg" + } + + fn get_file_extension(&self) -> String { + self.get_config() + .map(|config| config.extension.clone()) + .unwrap_or_else(|| "svg".to_string()) + } + + fn get_version_args(&self) -> Vec<&'static str> { + vec!["--"] + } + + fn get_path_command(&self) -> String { + "--".to_string() + } + + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("svg"), + before_compile: None, + extension: String::from("svg"), + execute_home: None, + run_command: Some(String::from("cat $filename")), + after_compile: None, + template: Some(String::from( + "\n", + )), + timeout: Some(30), + console_type: Some(String::from("web")), + } + } + + fn get_default_command(&self) -> String { + self.get_config() + .and_then(|config| config.run_command.clone()) + .unwrap_or_else(|| "cat".to_string()) + } +} diff --git a/src/components/WebOutput.vue b/src/components/WebOutput.vue index 40797df..d0aa096 100644 --- a/src/components/WebOutput.vue +++ b/src/components/WebOutput.vue @@ -95,6 +95,26 @@ const processWebContent = async (content: string) => { } } } + + // 处理 style 标签 + const styleMatches = content.match(/]*src="(file:\/\/[^"]*)"[^>]*><\/style>/gi) + if (styleMatches) { + for (const match of styleMatches) { + const pathMatch = match.match(/src="(file:\/\/[^"]*)"/) + if (pathMatch) { + const filePath = pathMatch[1].replace('file://', '') + try { + const fileContent = await readTextFile(filePath) + const inlineStyle = "" + result = result.replace(match, inlineStyle) + } + catch (error) { + console.error("读取CSS文件失败:", error) + result = result.replace(match, "") + } + } + } + } } catch (error) { console.error('无法导入 Tauri 文件系统插件:', error) diff --git a/src/composables/useCodeMirrorEditor.ts b/src/composables/useCodeMirrorEditor.ts index 9c0d438..3c02d7b 100644 --- a/src/composables/useCodeMirrorEditor.ts +++ b/src/composables/useCodeMirrorEditor.ts @@ -6,6 +6,8 @@ import {java} from '@codemirror/lang-java' import {rust} from '@codemirror/lang-rust' import {cpp} from '@codemirror/lang-cpp' import {html} from '@codemirror/lang-html' +import {css} from '@codemirror/lang-css' +import {xml} from '@codemirror/lang-xml' import {shell} from '@codemirror/legacy-modes/mode/shell' import {swift} from '@codemirror/legacy-modes/mode/swift' import {kotlin, scala} from '@codemirror/legacy-modes/mode/clike' @@ -200,6 +202,10 @@ export function useCodeMirrorEditor(props: Props) return StreamLanguage.define(groovy) case 'html': return html() + case 'css': + return css() + case 'svg': + return xml() default: return null }