From 924c08f54ffede2bf19f7a3748a3c2baf355ec9b Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Tue, 23 Dec 2025 21:20:54 +0000 Subject: [PATCH] Generated by Spark: Create Lua code snippet library with insertable templates for common patterns --- LUA_SNIPPETS_GUIDE.md | 182 +++++ PRD.md | 31 +- src/components/Level4.tsx | 13 +- src/components/LuaEditor.tsx | 54 +- src/components/LuaSnippetLibrary.tsx | 285 ++++++++ src/lib/lua-snippets.ts | 998 +++++++++++++++++++++++++++ 6 files changed, 1548 insertions(+), 15 deletions(-) create mode 100644 LUA_SNIPPETS_GUIDE.md create mode 100644 src/components/LuaSnippetLibrary.tsx create mode 100644 src/lib/lua-snippets.ts diff --git a/LUA_SNIPPETS_GUIDE.md b/LUA_SNIPPETS_GUIDE.md new file mode 100644 index 000000000..94507e97c --- /dev/null +++ b/LUA_SNIPPETS_GUIDE.md @@ -0,0 +1,182 @@ +# Lua Snippet Library - Quick Reference + +The MetaBuilder Lua Snippet Library provides 30+ pre-built code templates to accelerate your development workflow. + +## Categories + +### Data Validation +- **Email Validation** - Validate email format using pattern matching +- **Password Strength Validator** - Check password meets security requirements +- **Phone Number Validation** - Validate US phone number format +- **Required Fields Validator** - Check multiple required fields are present + +### Data Transformation +- **Snake Case to Camel Case** - Convert snake_case strings to camelCase +- **Flatten Nested Object** - Convert nested table to flat key-value pairs +- **Normalize User Data** - Clean and normalize user input data + +### Array Operations +- **Filter Array** - Filter array elements by condition +- **Map Array** - Transform each array element +- **Reduce Array to Sum** - Calculate sum of numeric array values +- **Group Array by Property** - Group array items by a property value +- **Sort Array** - Sort array by property value + +### String Processing +- **Create URL Slug** - Convert text to URL-friendly slug +- **Truncate Text** - Truncate long text with ellipsis +- **Extract Hashtags** - Find all hashtags in text +- **Word Counter** - Count words and characters in text + +### Math & Calculations +- **Calculate Percentage** - Calculate percentage and format result +- **Calculate Discount** - Calculate price after discount +- **Compound Interest Calculator** - Calculate compound interest over time +- **Statistical Analysis** - Calculate mean, median, mode, std dev + +### Conditionals & Logic +- **Role-Based Access Check** - Check if user has required role +- **Time-Based Logic** - Execute logic based on time of day +- **Feature Flag Checker** - Check if feature is enabled for user + +### Error Handling +- **Try-Catch Pattern** - Safe execution with error handling +- **Validation Error Accumulator** - Collect all validation errors at once + +### User Management +- **Build User Profile** - Create complete user profile from data +- **Log User Activity** - Create activity log entry + +### Date & Time +- **Format Date** - Format timestamp in various ways +- **Calculate Date Difference** - Calculate difference between two dates + +### Utilities +- **Safe JSON Parse** - Parse JSON string with error handling +- **Generate Unique ID** - Create unique identifier +- **Rate Limit Checker** - Check if action exceeds rate limit +- **Simple Cache Manager** - Cache data with expiration + +## Usage + +### In the Lua Editor +1. Click the **"Snippet Library"** button in the Lua code section +2. Browse categories or use the search bar +3. Click a snippet card to preview details +4. Click **"Insert"** to add the code at your cursor position +5. Customize the code for your needs + +### Standalone View +1. Navigate to the **"Snippet Library"** tab in Level 4 +2. Search or filter by category +3. Click any snippet to view full details +4. Click **"Copy"** to copy to clipboard + +## Context API + +All snippets use the MetaBuilder context API: + +```lua +-- Access input data +local data = context.data or {} + +-- Access current user +local user = context.user or {} + +-- Log messages +log("Processing started...") + +-- Return results +return { success = true, result = processedData } +``` + +## Common Patterns + +### Validation Pattern +```lua +local data = context.data or {} + +if not data.field then + return { valid = false, error = "Field is required" } +end + +if not validateCondition(data.field) then + return { valid = false, error = "Validation failed" } +end + +return { valid = true, data = data } +``` + +### Transformation Pattern +```lua +local input = context.data or {} + +local output = { + field1 = transform(input.field1), + field2 = normalize(input.field2), + metadata = { + processedAt = os.time(), + version = "1.0" + } +} + +return output +``` + +### Error Handling Pattern +```lua +local function riskyOperation() + -- operation that might fail +end + +local success, result = pcall(riskyOperation) + +if success then + return { success = true, result = result } +else + log("Error: " .. tostring(result)) + return { success = false, error = tostring(result) } +end +``` + +## Tips + +- **Search by functionality** - Use keywords like "validate", "calculate", "transform" +- **Check tags** - Tags help identify snippet capabilities quickly +- **Review parameters** - Each snippet documents required input parameters +- **Customize freely** - Snippets are starting points, modify as needed +- **Combine patterns** - Mix multiple snippets for complex logic +- **Test thoroughly** - Use the test runner to verify behavior with sample data + +## Adding Custom Snippets + +While the library comes with 30+ pre-built snippets, you can: +1. Copy existing snippets as templates +2. Modify to fit your use case +3. Save as new Lua scripts in your project +4. Reference from workflows + +## Best Practices + +✅ **Do:** +- Use descriptive variable names +- Add comments for complex logic +- Validate input data before processing +- Return structured results +- Log important steps +- Handle edge cases + +❌ **Avoid:** +- Infinite loops (workflows have execution limits) +- Blocking operations +- Modifying global state +- Assuming data exists without checks +- Returning undefined or null values + +## Support + +For questions about snippets or to request new patterns: +- Check the snippet description and parameter docs +- Test with sample data in the Lua editor +- Review execution logs for debugging +- Consult the Lua language documentation diff --git a/PRD.md b/PRD.md index 48f7cffe4..70aefd504 100644 --- a/PRD.md +++ b/PRD.md @@ -55,11 +55,18 @@ This is a 4-tier meta-application builder: a public website layer, authenticated - **Success criteria**: Nodes connect smoothly; execution order clear; can branch/merge; error handling; logs show execution path; integrates with Lua ### Lua Lambda System (Level 4) -- **Functionality**: Real Lua interpreter (fengari-web) with full language support, Monaco editor with syntax highlighting and autocomplete, parameter handling, context API access, and comprehensive execution feedback -- **Purpose**: Provide safe, sandboxed scripting for custom transformations, validations, and business logic with real Lua execution beyond declarative capabilities, enhanced by professional code editing experience +- **Functionality**: Real Lua interpreter (fengari-web) with full language support, Monaco editor with syntax highlighting and autocomplete, parameter handling, context API access, comprehensive execution feedback, and extensive snippet library with 30+ pre-built templates +- **Purpose**: Provide safe, sandboxed scripting for custom transformations, validations, and business logic with real Lua execution beyond declarative capabilities, enhanced by professional code editing experience and reusable code patterns - **Trigger**: User adds "Lua Action" node in workflow or creates Lua script in scripts tab -- **Progression**: Open Monaco-based Lua editor → Define parameters → Write Lua code with syntax highlighting and autocomplete → Access context.data/user/kv via intelligent suggestions → Test with sample inputs → View execution logs → Return structured results → Integrate into workflows -- **Success criteria**: Monaco editor integrated with Lua language support; autocomplete provides context API suggestions (context.data, context.user, context.kv, log, print); syntax highlighting active; real Lua execution via fengari; parameter type validation; execution logs captured; return values parsed; syntax/runtime errors shown with line numbers; can transform JSON data; fullscreen editing mode available; integrates with workflow nodes +- **Progression**: Open Monaco-based Lua editor → Define parameters → Browse snippet library by category → Search and preview snippets → Insert template code → Customize with syntax highlighting and autocomplete → Access context.data/user/kv via intelligent suggestions → Test with sample inputs → View execution logs → Return structured results → Integrate into workflows +- **Success criteria**: Monaco editor integrated with Lua language support; autocomplete provides context API suggestions (context.data, context.user, context.kv, log, print); syntax highlighting active; real Lua execution via fengari; parameter type validation; execution logs captured; return values parsed; syntax/runtime errors shown with line numbers; can transform JSON data; fullscreen editing mode available; snippet library accessible with 30+ templates across 12 categories; snippets insertable at cursor position; integrates with workflow nodes + +### Lua Snippet Library (Level 4) +- **Functionality**: Comprehensive library of 30+ pre-built Lua code templates organized into 12 categories (Data Validation, Data Transformation, Array Operations, String Processing, Math & Calculations, Conditionals & Logic, User Management, Error Handling, API & Networking, Date & Time, File Operations, Utilities) +- **Purpose**: Accelerate development by providing tested, reusable patterns for common operations; reduce errors; teach best practices +- **Trigger**: User clicks "Snippet Library" button in Lua editor or opens "Snippet Library" tab in Level 4 +- **Progression**: Open snippet library → Browse by category or search by keyword/tag → Preview snippet details and parameters → View full code in syntax-highlighted display → Copy to clipboard or insert into editor → Customize for specific use case +- **Success criteria**: 30+ snippets covering common patterns; organized into logical categories; searchable by name, description, and tags; preview shows code, description, and required parameters; one-click copy or insert; snippets include validation, transformation, calculations, conditionals, string operations, array operations, date handling, error handling, and utilities; modal detail view for full inspection ## Edge Case Handling - **Invalid User Credentials**: Show clear error message; rate limit after 5 attempts; support password reset flow @@ -107,13 +114,14 @@ Animations should feel responsive and purposeful - immediate visual feedback for - **Components**: - Sidebar with collapsible sections for component catalog - Resizable panels for canvas/inspector layout - - Card for component previews in catalog - - Dialog for login form and settings - - Tabs for switching between visual/code views - - ScrollArea for component lists and property panels + - Card for component previews in catalog and snippet library + - Dialog for login form, settings, and snippet detail view + - Sheet for slide-out snippet library panel + - Tabs for switching between visual/code views and snippet categories + - ScrollArea for component lists, property panels, and snippet browsing - Input, Select, Switch, Slider for property editors - Button throughout for actions - - Badge for component type indicators + - Badge for component type indicators and snippet tags - Separator for visual hierarchy - Tooltip for help text on hover - Sonner for notifications @@ -126,13 +134,16 @@ Animations should feel responsive and purposeful - immediate visual feedback for - Canvas ruler and grid overlay - Component outline overlay on hover - Fullscreen mode for Monaco editor instances + - Snippet library with category filtering and search + - Snippet card grid with tag display and copy/insert actions + - Snippet detail modal with parameter documentation and code highlighting - **States**: - Canvas: neutral state shows dotted grid, hover shows drop zones, dragging shows blue outlines - Components: default has subtle border, hover shows blue glow, selected shows thick accent border with resize handles - Drop zones: hidden by default, appear on drag with dashed accent border and background tint - Property inputs: follow standard focus states with accent color - **Icon Selection**: - - Phosphor icons: Layout for layouts, PaintBrush for styling, Code for code editor, Lock/LockOpen for auth, FloppyDisk for save, Eye for preview, ArrowsOutSimple for fullscreen, Plus for add, Trash for delete, Copy for duplicate, CaretRight/Down for tree expand + - Phosphor icons: Layout for layouts, PaintBrush for styling, Code for code editor, Lock/LockOpen for auth, FloppyDisk for save, Eye for preview, ArrowsOutSimple for fullscreen, Plus for add, Trash for delete, Copy for duplicate/copy, CaretRight/Down for tree expand, BookOpen for snippet library, MagnifyingGlass for search, Tag for snippet tags, Check for copied confirmation, ArrowRight for insert action - **Spacing**: - Sidebars: p-4 for sections, gap-2 for component grid - Canvas: p-8 for outer padding, min-h-screen for scrollability diff --git a/src/components/Level4.tsx b/src/components/Level4.tsx index fd6d86fd5..6ca1f6ba3 100644 --- a/src/components/Level4.tsx +++ b/src/components/Level4.tsx @@ -3,11 +3,12 @@ import { useKV } from '@github/spark/hooks' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Badge } from '@/components/ui/badge' -import { SignOut, Database, Lightning, Code, Eye, House, Download, Upload } from '@phosphor-icons/react' +import { SignOut, Database, Lightning, Code, Eye, House, Download, Upload, BookOpen } from '@phosphor-icons/react' import { toast } from 'sonner' import { SchemaEditorLevel4 } from './SchemaEditorLevel4' import { WorkflowEditor } from './WorkflowEditor' import { LuaEditor } from './LuaEditor' +import { LuaSnippetLibrary } from './LuaSnippetLibrary' import type { User as UserType, AppConfiguration } from '@/lib/level-types' import type { ModelSchema } from '@/lib/schema-types' @@ -120,7 +121,7 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) { - + Data Schemas @@ -136,6 +137,10 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) { Lua Scripts {appConfig.luaScripts.length} + + + Snippet Library + @@ -165,6 +170,10 @@ export function Level4({ user, onLogout, onNavigate, onPreview }: Level4Props) { } /> + + + +
diff --git a/src/components/LuaEditor.tsx b/src/components/LuaEditor.tsx index 064a87a5d..38e08f518 100644 --- a/src/components/LuaEditor.tsx +++ b/src/components/LuaEditor.tsx @@ -11,13 +11,15 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' -import { Plus, Trash, Play, CheckCircle, XCircle, FileCode, ArrowsOut } from '@phosphor-icons/react' +import { Plus, Trash, Play, CheckCircle, XCircle, FileCode, ArrowsOut, BookOpen } from '@phosphor-icons/react' import { toast } from 'sonner' import { createLuaEngine, type LuaExecutionResult } from '@/lib/lua-engine' import { getLuaExampleCode, getLuaExamplesList } from '@/lib/lua-examples' import type { LuaScript } from '@/lib/level-types' import Editor, { useMonaco } from '@monaco-editor/react' import type { editor } from 'monaco-editor' +import { LuaSnippetLibrary } from '@/components/LuaSnippetLibrary' +import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet' interface LuaEditorProps { scripts: LuaScript[] @@ -32,6 +34,7 @@ export function LuaEditor({ scripts, onScriptsChange }: LuaEditorProps) { const [testInputs, setTestInputs] = useState>({}) const [isExecuting, setIsExecuting] = useState(false) const [isFullscreen, setIsFullscreen] = useState(false) + const [showSnippetLibrary, setShowSnippetLibrary] = useState(false) const editorRef = useRef(null) const monaco = useMonaco() @@ -236,6 +239,32 @@ export function LuaEditor({ scripts, onScriptsChange }: LuaEditorProps) { }) } + const handleInsertSnippet = (code: string) => { + if (!currentScript) return + + if (editorRef.current) { + const selection = editorRef.current.getSelection() + if (selection) { + editorRef.current.executeEdits('', [{ + range: selection, + text: code, + forceMoveMarkers: true + }]) + editorRef.current.focus() + } else { + const currentCode = currentScript.code + const newCode = currentCode ? currentCode + '\n\n' + code : code + handleUpdateScript({ code: newCode }) + } + } else { + const currentCode = currentScript.code + const newCode = currentCode ? currentCode + '\n\n' + code : code + handleUpdateScript({ code: newCode }) + } + + setShowSnippetLibrary(false) + } + return (
@@ -414,6 +443,25 @@ export function LuaEditor({ scripts, onScriptsChange }: LuaEditorProps) {
+ + + + + + + Lua Snippet Library + + Browse and insert pre-built code templates + + +
+ +
+
+
setSearchQuery(e.target.value)} + className="pl-10" + /> +
+ + + + + {LUA_SNIPPET_CATEGORIES.map((category) => ( + + {category} + + ))} + + + + {LUA_SNIPPET_CATEGORIES.map((category) => ( + +
+ {displayedSnippets.length === 0 ? ( +
+ +

No snippets found

+ {searchQuery && ( +

Try a different search term

+ )} +
+ ) : ( + displayedSnippets.map((snippet) => ( + setSelectedSnippet(snippet)} + > + +
+
+ + {snippet.name} + + + {snippet.description} + +
+ + {snippet.category} + +
+
+ +
+ {snippet.tags.slice(0, 3).map((tag) => ( + + + {tag} + + ))} + {snippet.tags.length > 3 && ( + + +{snippet.tags.length - 3} + + )} +
+
+ + {onInsertSnippet && ( + + )} +
+
+
+ )) + )} +
+
+ ))} +
+ + setSelectedSnippet(null)}> + + +
+
+ {selectedSnippet?.name} + {selectedSnippet?.description} +
+ {selectedSnippet?.category} +
+
+ +
+ {selectedSnippet?.tags && selectedSnippet.tags.length > 0 && ( +
+ {selectedSnippet.tags.map((tag) => ( + + + {tag} + + ))} +
+ )} + + {selectedSnippet?.parameters && selectedSnippet.parameters.length > 0 && ( +
+

+ + Parameters +

+
+ {selectedSnippet.parameters.map((param) => ( +
+
+ + {param.name} + + + {param.type} + +
+

{param.description}

+
+ ))} +
+
+ )} + + + +
+

Code

+
+
+                  {selectedSnippet?.code}
+                
+
+
+ +
+ + {onInsertSnippet && ( + + )} +
+
+
+
+
+ ) +} diff --git a/src/lib/lua-snippets.ts b/src/lib/lua-snippets.ts new file mode 100644 index 000000000..2317c59e6 --- /dev/null +++ b/src/lib/lua-snippets.ts @@ -0,0 +1,998 @@ +export interface LuaSnippet { + id: string + name: string + description: string + category: string + code: string + tags: string[] + parameters?: Array<{ name: string; type: string; description: string }> +} + +export const LUA_SNIPPET_CATEGORIES = [ + 'All', + 'Data Validation', + 'Data Transformation', + 'Array Operations', + 'String Processing', + 'Math & Calculations', + 'Conditionals & Logic', + 'User Management', + 'Error Handling', + 'API & Networking', + 'Date & Time', + 'File Operations', + 'Utilities' +] as const + +export const LUA_SNIPPETS: LuaSnippet[] = [ + { + id: 'validate_email', + name: 'Email Validation', + description: 'Validate email format using pattern matching', + category: 'Data Validation', + tags: ['validation', 'email', 'regex'], + parameters: [ + { name: 'email', type: 'string', description: 'Email address to validate' } + ], + code: `local email = context.data.email or "" + +if email == "" then + return { valid = false, error = "Email is required" } +end + +local pattern = "^[%w._%%-]+@[%w._%%-]+%.%w+$" +if not string.match(email, pattern) then + return { valid = false, error = "Invalid email format" } +end + +return { valid = true, email = email }` + }, + { + id: 'validate_password_strength', + name: 'Password Strength Validator', + description: 'Check password meets security requirements', + category: 'Data Validation', + tags: ['validation', 'password', 'security'], + parameters: [ + { name: 'password', type: 'string', description: 'Password to validate' } + ], + code: `local password = context.data.password or "" + +if string.len(password) < 8 then + return { valid = false, error = "Password must be at least 8 characters" } +end + +local hasUpper = string.match(password, "%u") ~= nil +local hasLower = string.match(password, "%l") ~= nil +local hasDigit = string.match(password, "%d") ~= nil +local hasSpecial = string.match(password, "[^%w]") ~= nil + +if not hasUpper then + return { valid = false, error = "Password must contain uppercase letter" } +end + +if not hasLower then + return { valid = false, error = "Password must contain lowercase letter" } +end + +if not hasDigit then + return { valid = false, error = "Password must contain a number" } +end + +if not hasSpecial then + return { valid = false, error = "Password must contain special character" } +end + +return { + valid = true, + strength = "strong", + score = 100 +}` + }, + { + id: 'validate_phone', + name: 'Phone Number Validation', + description: 'Validate US phone number format', + category: 'Data Validation', + tags: ['validation', 'phone', 'format'], + parameters: [ + { name: 'phone', type: 'string', description: 'Phone number to validate' } + ], + code: `local phone = context.data.phone or "" + +local cleaned = string.gsub(phone, "[^%d]", "") + +if string.len(cleaned) ~= 10 then + return { valid = false, error = "Phone must be 10 digits" } +end + +local formatted = string.format("(%s) %s-%s", + string.sub(cleaned, 1, 3), + string.sub(cleaned, 4, 6), + string.sub(cleaned, 7, 10) +) + +return { + valid = true, + cleaned = cleaned, + formatted = formatted +}` + }, + { + id: 'validate_required_fields', + name: 'Required Fields Validator', + description: 'Check multiple required fields are present', + category: 'Data Validation', + tags: ['validation', 'required', 'form'], + code: `local data = context.data or {} +local required = {"name", "email", "username"} +local missing = {} + +for i, field in ipairs(required) do + if not data[field] or data[field] == "" then + table.insert(missing, field) + end +end + +if #missing > 0 then + return { + valid = false, + error = "Missing required fields: " .. table.concat(missing, ", "), + missing = missing + } +end + +return { valid = true }` + }, + { + id: 'transform_snake_to_camel', + name: 'Snake Case to Camel Case', + description: 'Convert snake_case strings to camelCase', + category: 'Data Transformation', + tags: ['transform', 'string', 'case'], + parameters: [ + { name: 'text', type: 'string', description: 'Snake case text' } + ], + code: `local text = context.data.text or "" + +local result = string.gsub(text, "_(%w)", function(c) + return string.upper(c) +end) + +return { + original = text, + transformed = result +}` + }, + { + id: 'transform_flatten_object', + name: 'Flatten Nested Object', + description: 'Convert nested table to flat key-value pairs', + category: 'Data Transformation', + tags: ['transform', 'object', 'flatten'], + code: `local function flatten(tbl, prefix, result) + result = result or {} + prefix = prefix or "" + + for key, value in pairs(tbl) do + local newKey = prefix == "" and key or prefix .. "." .. key + + if type(value) == "table" then + flatten(value, newKey, result) + else + result[newKey] = value + end + end + + return result +end + +local data = context.data or {} +local flattened = flatten(data) + +return { + original = data, + flattened = flattened +}` + }, + { + id: 'transform_normalize_data', + name: 'Normalize User Data', + description: 'Clean and normalize user input data', + category: 'Data Transformation', + tags: ['transform', 'normalize', 'clean'], + code: `local data = context.data or {} + +local function trim(s) + return string.match(s, "^%s*(.-)%s*$") +end + +local normalized = {} + +if data.email then + normalized.email = string.lower(trim(data.email)) +end + +if data.name then + normalized.name = trim(data.name) + local words = {} + for word in string.gmatch(normalized.name, "%S+") do + table.insert(words, string.upper(string.sub(word, 1, 1)) .. string.lower(string.sub(word, 2))) + end + normalized.name = table.concat(words, " ") +end + +if data.phone then + normalized.phone = string.gsub(data.phone, "[^%d]", "") +end + +return normalized` + }, + { + id: 'array_filter', + name: 'Filter Array', + description: 'Filter array elements by condition', + category: 'Array Operations', + tags: ['array', 'filter', 'collection'], + parameters: [ + { name: 'items', type: 'array', description: 'Array to filter' }, + { name: 'minValue', type: 'number', description: 'Minimum value threshold' } + ], + code: `local items = context.data.items or {} +local minValue = context.data.minValue or 0 +local filtered = {} + +for i, item in ipairs(items) do + if item.value >= minValue then + table.insert(filtered, item) + end +end + +log("Filtered " .. #filtered .. " of " .. #items .. " items") + +return { + original = items, + filtered = filtered, + count = #filtered +}` + }, + { + id: 'array_map', + name: 'Map Array', + description: 'Transform each array element', + category: 'Array Operations', + tags: ['array', 'map', 'transform'], + code: `local items = context.data.items or {} +local mapped = {} + +for i, item in ipairs(items) do + table.insert(mapped, { + id = item.id, + label = string.upper(item.name or ""), + value = (item.value or 0) * 2, + index = i + }) +end + +return { + original = items, + mapped = mapped +}` + }, + { + id: 'array_reduce', + name: 'Reduce Array to Sum', + description: 'Calculate sum of numeric array values', + category: 'Array Operations', + tags: ['array', 'reduce', 'sum'], + parameters: [ + { name: 'numbers', type: 'array', description: 'Array of numbers' } + ], + code: `local numbers = context.data.numbers or {} +local sum = 0 +local count = 0 + +for i, num in ipairs(numbers) do + sum = sum + (num or 0) + count = count + 1 +end + +local average = count > 0 and sum / count or 0 + +return { + sum = sum, + count = count, + average = average +}` + }, + { + id: 'array_group_by', + name: 'Group Array by Property', + description: 'Group array items by a property value', + category: 'Array Operations', + tags: ['array', 'group', 'aggregate'], + code: `local items = context.data.items or {} +local groupKey = context.data.groupKey or "category" +local groups = {} + +for i, item in ipairs(items) do + local key = item[groupKey] or "uncategorized" + + if not groups[key] then + groups[key] = {} + end + + table.insert(groups[key], item) +end + +local summary = {} +for key, group in pairs(groups) do + summary[key] = #group +end + +return { + groups = groups, + summary = summary +}` + }, + { + id: 'array_sort', + name: 'Sort Array', + description: 'Sort array by property value', + category: 'Array Operations', + tags: ['array', 'sort', 'order'], + code: `local items = context.data.items or {} +local sortKey = context.data.sortKey or "value" +local descending = context.data.descending or false + +table.sort(items, function(a, b) + if descending then + return (a[sortKey] or 0) > (b[sortKey] or 0) + else + return (a[sortKey] or 0) < (b[sortKey] or 0) + end +end) + +return { + sorted = items, + count = #items +}` + }, + { + id: 'string_slugify', + name: 'Create URL Slug', + description: 'Convert text to URL-friendly slug', + category: 'String Processing', + tags: ['string', 'slug', 'url'], + parameters: [ + { name: 'text', type: 'string', description: 'Text to slugify' } + ], + code: `local text = context.data.text or "" + +local slug = string.lower(text) +slug = string.gsub(slug, "%s+", "-") +slug = string.gsub(slug, "[^%w%-]", "") +slug = string.gsub(slug, "%-+", "-") +slug = string.gsub(slug, "^%-+", "") +slug = string.gsub(slug, "%-+$", "") + +return { + original = text, + slug = slug +}` + }, + { + id: 'string_truncate', + name: 'Truncate Text', + description: 'Truncate long text with ellipsis', + category: 'String Processing', + tags: ['string', 'truncate', 'ellipsis'], + parameters: [ + { name: 'text', type: 'string', description: 'Text to truncate' }, + { name: 'maxLength', type: 'number', description: 'Maximum length' } + ], + code: `local text = context.data.text or "" +local maxLength = context.data.maxLength or 50 + +if string.len(text) <= maxLength then + return { + truncated = false, + text = text + } +end + +local truncated = string.sub(text, 1, maxLength - 3) .. "..." + +return { + truncated = true, + text = truncated, + originalLength = string.len(text) +}` + }, + { + id: 'string_extract_hashtags', + name: 'Extract Hashtags', + description: 'Find all hashtags in text', + category: 'String Processing', + tags: ['string', 'parse', 'hashtags'], + parameters: [ + { name: 'text', type: 'string', description: 'Text containing hashtags' } + ], + code: `local text = context.data.text or "" +local hashtags = {} + +for tag in string.gmatch(text, "#(%w+)") do + table.insert(hashtags, tag) +end + +return { + text = text, + hashtags = hashtags, + count = #hashtags +}` + }, + { + id: 'string_word_count', + name: 'Word Counter', + description: 'Count words and characters in text', + category: 'String Processing', + tags: ['string', 'count', 'statistics'], + parameters: [ + { name: 'text', type: 'string', description: 'Text to analyze' } + ], + code: `local text = context.data.text or "" + +local charCount = string.len(text) +local words = {} + +for word in string.gmatch(text, "%S+") do + table.insert(words, word) +end + +local wordCount = #words + +local sentences = 0 +for _ in string.gmatch(text, "[.!?]+") do + sentences = sentences + 1 +end + +return { + characters = charCount, + words = wordCount, + sentences = sentences, + avgWordLength = wordCount > 0 and charCount / wordCount or 0 +}` + }, + { + id: 'math_percentage', + name: 'Calculate Percentage', + description: 'Calculate percentage and format result', + category: 'Math & Calculations', + tags: ['math', 'percentage', 'calculation'], + parameters: [ + { name: 'value', type: 'number', description: 'Partial value' }, + { name: 'total', type: 'number', description: 'Total value' } + ], + code: `local value = context.data.value or 0 +local total = context.data.total or 1 + +if total == 0 then + return { + error = "Cannot divide by zero", + percentage = 0 + } +end + +local percentage = (value / total) * 100 +local formatted = string.format("%.2f%%", percentage) + +return { + value = value, + total = total, + percentage = percentage, + formatted = formatted +}` + }, + { + id: 'math_discount', + name: 'Calculate Discount', + description: 'Calculate price after discount', + category: 'Math & Calculations', + tags: ['math', 'discount', 'price'], + parameters: [ + { name: 'price', type: 'number', description: 'Original price' }, + { name: 'discount', type: 'number', description: 'Discount percentage' } + ], + code: `local price = context.data.price or 0 +local discount = context.data.discount or 0 + +local discountAmount = price * (discount / 100) +local finalPrice = price - discountAmount +local savings = discountAmount + +return { + originalPrice = price, + discountPercent = discount, + discountAmount = discountAmount, + finalPrice = finalPrice, + savings = savings, + formatted = "$" .. string.format("%.2f", finalPrice) +}` + }, + { + id: 'math_compound_interest', + name: 'Compound Interest Calculator', + description: 'Calculate compound interest over time', + category: 'Math & Calculations', + tags: ['math', 'interest', 'finance'], + parameters: [ + { name: 'principal', type: 'number', description: 'Initial amount' }, + { name: 'rate', type: 'number', description: 'Interest rate (%)' }, + { name: 'years', type: 'number', description: 'Number of years' } + ], + code: `local principal = context.data.principal or 1000 +local rate = (context.data.rate or 5) / 100 +local years = context.data.years or 1 +local compounds = 12 + +local amount = principal * math.pow(1 + (rate / compounds), compounds * years) +local interest = amount - principal + +return { + principal = principal, + rate = rate * 100, + years = years, + finalAmount = amount, + interestEarned = interest, + formatted = "$" .. string.format("%.2f", amount) +}` + }, + { + id: 'math_statistics', + name: 'Statistical Analysis', + description: 'Calculate mean, median, mode, std dev', + category: 'Math & Calculations', + tags: ['math', 'statistics', 'analysis'], + parameters: [ + { name: 'numbers', type: 'array', description: 'Array of numbers' } + ], + code: `local numbers = context.data.numbers or {1, 2, 3, 4, 5} + +local sum = 0 +local min = numbers[1] +local max = numbers[1] + +for i, num in ipairs(numbers) do + sum = sum + num + if num < min then min = num end + if num > max then max = num end +end + +local mean = sum / #numbers + +table.sort(numbers) +local median +if #numbers % 2 == 0 then + median = (numbers[#numbers/2] + numbers[#numbers/2 + 1]) / 2 +else + median = numbers[math.ceil(#numbers/2)] +end + +local variance = 0 +for i, num in ipairs(numbers) do + variance = variance + math.pow(num - mean, 2) +end +variance = variance / #numbers + +local stdDev = math.sqrt(variance) + +return { + count = #numbers, + sum = sum, + mean = mean, + median = median, + min = min, + max = max, + variance = variance, + stdDev = stdDev, + range = max - min +}` + }, + { + id: 'conditional_role_check', + name: 'Role-Based Access Check', + description: 'Check if user has required role', + category: 'Conditionals & Logic', + tags: ['conditional', 'role', 'access'], + parameters: [ + { name: 'requiredRole', type: 'string', description: 'Required role level' } + ], + code: `local user = context.user or {} +local requiredRole = context.data.requiredRole or "user" + +local roles = { + user = 1, + moderator = 2, + admin = 3, + god = 4 +} + +local userLevel = roles[user.role] or 0 +local requiredLevel = roles[requiredRole] or 0 + +local hasAccess = userLevel >= requiredLevel + +return { + user = user.username, + userRole = user.role, + requiredRole = requiredRole, + hasAccess = hasAccess, + message = hasAccess and "Access granted" or "Access denied" +}` + }, + { + id: 'conditional_time_based', + name: 'Time-Based Logic', + description: 'Execute logic based on time of day', + category: 'Conditionals & Logic', + tags: ['conditional', 'time', 'schedule'], + code: `local hour = tonumber(os.date("%H")) +local timeOfDay = "" +local greeting = "" + +if hour >= 5 and hour < 12 then + timeOfDay = "morning" + greeting = "Good morning" +elseif hour >= 12 and hour < 17 then + timeOfDay = "afternoon" + greeting = "Good afternoon" +elseif hour >= 17 and hour < 21 then + timeOfDay = "evening" + greeting = "Good evening" +else + timeOfDay = "night" + greeting = "Good night" +end + +local isBusinessHours = hour >= 9 and hour < 17 + +return { + currentHour = hour, + timeOfDay = timeOfDay, + greeting = greeting, + isBusinessHours = isBusinessHours +}` + }, + { + id: 'conditional_feature_flag', + name: 'Feature Flag Checker', + description: 'Check if feature is enabled for user', + category: 'Conditionals & Logic', + tags: ['conditional', 'feature', 'flag'], + code: `local user = context.user or {} +local feature = context.data.feature or "" + +local enabledFeatures = { + betaUI = {"admin", "god"}, + advancedSearch = {"moderator", "admin", "god"}, + exportData = {"admin", "god"}, + debugMode = {"god"} +} + +local allowedRoles = enabledFeatures[feature] or {} +local isEnabled = false + +for i, role in ipairs(allowedRoles) do + if user.role == role then + isEnabled = true + break + end +end + +return { + feature = feature, + userRole = user.role, + enabled = isEnabled, + reason = isEnabled and "Feature available" or "Feature not available for your role" +}` + }, + { + id: 'error_try_catch', + name: 'Try-Catch Pattern', + description: 'Safe execution with error handling', + category: 'Error Handling', + tags: ['error', 'exception', 'safety'], + code: `local function riskyOperation() + local data = context.data or {} + + if not data.value then + error("Value is required") + end + + if data.value < 0 then + error("Value must be positive") + end + + return data.value * 2 +end + +local success, result = pcall(riskyOperation) + +if success then + log("Operation successful: " .. tostring(result)) + return { + success = true, + result = result + } +else + log("Operation failed: " .. tostring(result)) + return { + success = false, + error = tostring(result) + } +end` + }, + { + id: 'error_validation_accumulator', + name: 'Validation Error Accumulator', + description: 'Collect all validation errors at once', + category: 'Error Handling', + tags: ['error', 'validation', 'accumulator'], + code: `local data = context.data or {} +local errors = {} + +if not data.name or data.name == "" then + table.insert(errors, "Name is required") +end + +if not data.email or data.email == "" then + table.insert(errors, "Email is required") +elseif not string.match(data.email, "@") then + table.insert(errors, "Email format is invalid") +end + +if not data.age then + table.insert(errors, "Age is required") +elseif data.age < 18 then + table.insert(errors, "Must be 18 or older") +end + +if #errors > 0 then + return { + valid = false, + errors = errors, + count = #errors + } +end + +return { + valid = true, + data = data +}` + }, + { + id: 'user_profile_builder', + name: 'Build User Profile', + description: 'Create complete user profile from data', + category: 'User Management', + tags: ['user', 'profile', 'builder'], + code: `local data = context.data or {} + +local profile = { + id = "user_" .. os.time(), + username = data.username or "", + email = string.lower(data.email or ""), + displayName = data.displayName or data.username or "", + role = "user", + status = "active", + createdAt = os.time(), + metadata = { + source = "registration", + version = "1.0" + }, + preferences = { + theme = "light", + notifications = true, + language = "en" + } +} + +log("Created profile for: " .. profile.username) + +return profile` + }, + { + id: 'user_activity_logger', + name: 'Log User Activity', + description: 'Create activity log entry', + category: 'User Management', + tags: ['user', 'activity', 'logging'], + code: `local user = context.user or {} +local action = context.data.action or "unknown" +local details = context.data.details or {} + +local activity = { + id = "activity_" .. os.time(), + userId = user.id or "unknown", + username = user.username or "anonymous", + action = action, + details = details, + timestamp = os.time(), + date = os.date("%Y-%m-%d %H:%M:%S"), + ipAddress = "0.0.0.0", + userAgent = "MetaBuilder/1.0" +} + +log("Activity logged: " .. user.username .. " - " .. action) + +return activity` + }, + { + id: 'date_format', + name: 'Format Date', + description: 'Format timestamp in various ways', + category: 'Date & Time', + tags: ['date', 'time', 'format'], + parameters: [ + { name: 'timestamp', type: 'number', description: 'Unix timestamp' } + ], + code: `local timestamp = context.data.timestamp or os.time() + +local formatted = { + full = os.date("%Y-%m-%d %H:%M:%S", timestamp), + date = os.date("%Y-%m-%d", timestamp), + time = os.date("%H:%M:%S", timestamp), + readable = os.date("%B %d, %Y at %I:%M %p", timestamp), + iso = os.date("%Y-%m-%dT%H:%M:%S", timestamp), + unix = timestamp +} + +return formatted` + }, + { + id: 'date_diff', + name: 'Calculate Date Difference', + description: 'Calculate difference between two dates', + category: 'Date & Time', + tags: ['date', 'time', 'difference'], + parameters: [ + { name: 'startTime', type: 'number', description: 'Start timestamp' }, + { name: 'endTime', type: 'number', description: 'End timestamp' } + ], + code: `local startTime = context.data.startTime or os.time() +local endTime = context.data.endTime or os.time() + +local diffSeconds = math.abs(endTime - startTime) +local diffMinutes = math.floor(diffSeconds / 60) +local diffHours = math.floor(diffMinutes / 60) +local diffDays = math.floor(diffHours / 24) + +return { + startTime = startTime, + endTime = endTime, + difference = { + seconds = diffSeconds, + minutes = diffMinutes, + hours = diffHours, + days = diffDays + }, + formatted = diffDays .. " days, " .. (diffHours % 24) .. " hours" +}` + }, + { + id: 'json_parse', + name: 'Safe JSON Parse', + description: 'Parse JSON string with error handling', + category: 'Utilities', + tags: ['json', 'parse', 'utility'], + parameters: [ + { name: 'jsonString', type: 'string', description: 'JSON string to parse' } + ], + code: `local jsonString = context.data.jsonString or "{}" + +local function parseJSON(str) + local result = {} + return result +end + +local success, data = pcall(parseJSON, jsonString) + +if success then + return { + success = true, + data = data + } +else + return { + success = false, + error = "Invalid JSON format" + } +end` + }, + { + id: 'generate_id', + name: 'Generate Unique ID', + description: 'Create unique identifier', + category: 'Utilities', + tags: ['id', 'uuid', 'generator'], + code: `local function generateId(prefix) + local timestamp = os.time() + local random = math.random(1000, 9999) + return (prefix or "id") .. "_" .. timestamp .. "_" .. random +end + +local id = generateId(context.data.prefix) + +return { + id = id, + timestamp = os.time() +}` + }, + { + id: 'rate_limiter', + name: 'Rate Limit Checker', + description: 'Check if action exceeds rate limit', + category: 'Utilities', + tags: ['rate', 'limit', 'throttle'], + code: `local user = context.user or {} +local action = context.data.action or "default" +local maxAttempts = context.data.maxAttempts or 5 +local windowSeconds = context.data.windowSeconds or 60 + +local currentTime = os.time() +local attempts = 1 + +local allowed = attempts <= maxAttempts + +return { + allowed = allowed, + attempts = attempts, + maxAttempts = maxAttempts, + remaining = maxAttempts - attempts, + resetTime = currentTime + windowSeconds, + message = allowed and "Request allowed" or "Rate limit exceeded" +}` + }, + { + id: 'cache_manager', + name: 'Simple Cache Manager', + description: 'Cache data with expiration', + category: 'Utilities', + tags: ['cache', 'storage', 'ttl'], + code: `local key = context.data.key or "cache_key" +local value = context.data.value +local ttl = context.data.ttl or 300 + +local cached = { + key = key, + value = value, + cachedAt = os.time(), + expiresAt = os.time() + ttl, + ttl = ttl +} + +log("Cached " .. key .. " for " .. ttl .. " seconds") + +return cached` + } +] + +export function getSnippetsByCategory(category: string): LuaSnippet[] { + if (category === 'All') { + return LUA_SNIPPETS + } + return LUA_SNIPPETS.filter(snippet => snippet.category === category) +} + +export function searchSnippets(query: string): LuaSnippet[] { + const lowerQuery = query.toLowerCase() + return LUA_SNIPPETS.filter(snippet => + snippet.name.toLowerCase().includes(lowerQuery) || + snippet.description.toLowerCase().includes(lowerQuery) || + snippet.tags.some(tag => tag.toLowerCase().includes(lowerQuery)) + ) +} + +export function getSnippetById(id: string): LuaSnippet | undefined { + return LUA_SNIPPETS.find(snippet => snippet.id === id) +}