skills/linjector-luau-script-ide/SKILL.md
Open-source Luau/Lua script IDE and executor built as a Windows Forms C# application with Monaco editor integration
npx skillsauth add aradotso/trending-skills linjector-luau-script-ideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Skill by ara.so — Daily 2026 Skills collection.
LInjector is an open-source Luau/Lua script IDE and executor built as a Windows Forms application in C# (.NET Framework 4.8, 32-bit). It features the Monaco editor (the same editor powering VS Code) with Luau syntax highlighting, an optimized multi-tab system, and DLL injection capabilities targeting the Roblox UWP client.
Important Note (2026): The UWP Version is patched due to Hyperion/Byfron anti-cheat. You can still build, modify, and extend LInjector by providing your own working DLL or injection method.
LInjector/
├── LInjector.sln # Visual Studio solution
├── LInjector/
│ ├── LInjector.csproj # Project file (.NET 4.8, x86)
│ ├── Forms/ # Windows Forms UI
│ │ ├── MainForm.cs # Main application window
│ │ └── *.cs # Other form files
│ ├── Classes/ # Core logic classes
│ │ ├── Injector.cs # DLL injection logic
│ │ ├── TabSystem.cs # Optimized tab management
│ │ └── *.cs
│ ├── Monaco/ # Monaco editor web assets
│ │ ├── index.html # Editor host page
│ │ └── *.js # Monaco JS files
│ └── Resources/ # Embedded resources
# 1. Clone the repository
git clone https://github.com/WeritoP/LInjector-FORKED-
cd LInjector-FORKED-
# 2. Open in Visual Studio 2022
start LInjector.sln
# 3. Set build configuration in VS:
# Configuration: Release
# Platform: x86
# 4. Build via keyboard shortcut
# CTRL + SHIFT + B
# OR via CLI with MSBuild
msbuild LInjector.sln /p:Configuration=Release /p:Platform=x86
Important: Always compile before editing Forms. If you open a Form before compiling, Visual Studio's designer will fail. If this happens, restart Visual Studio.
LInjector embeds Monaco in a WebBrowser or WebView2 control. The editor is hosted in a local HTML file.
// In your Form class — get script content from Monaco
private string GetEditorContent()
{
// Execute JavaScript to retrieve editor value
object result = webBrowser.Document.InvokeScript(
"eval",
new object[] { "monaco.editor.getModels()[0].getValue()" }
);
return result?.ToString() ?? string.Empty;
}
// Set content in Monaco editor
private void SetEditorContent(string script)
{
string escaped = script
.Replace("\\", "\\\\")
.Replace("'", "\\'")
.Replace("\n", "\\n")
.Replace("\r", "\\r");
webBrowser.Document.InvokeScript(
"eval",
new object[] { $"monaco.editor.getModels()[0].setValue('{escaped}')" }
);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
html, body, #container {
width: 100%; height: 100%;
margin: 0; padding: 0;
overflow: hidden;
background: #1e1e1e;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="monaco/min/vs/loader.js"></script>
<script>
require.config({ paths: { 'vs': 'monaco/min/vs' } });
require(['vs/editor/editor.main'], function () {
window.editor = monaco.editor.create(
document.getElementById('container'), {
value: '-- LInjector Script\n',
language: 'lua',
theme: 'vs-dark',
fontSize: 14,
automaticLayout: true,
minimap: { enabled: false },
scrollBeyondLastLine: false
}
);
});
// Helper functions callable from C#
function getScript() {
return window.editor.getValue();
}
function setScript(val) {
window.editor.setValue(val);
}
function clearScript() {
window.editor.setValue('');
}
</script>
</body>
</html>
LInjector's tab system is optimized to cap RAM usage at ~1 GB even with 40+ tabs open.
// TabSystem.cs — Example pattern for managing script tabs
public class ScriptTab
{
public string Name { get; set; }
public string Content { get; set; }
public TabPage TabPage { get; set; }
public ScriptTab(string name, string content = "")
{
Name = name;
Content = content;
}
}
public class TabManager
{
private readonly List<ScriptTab> _tabs = new List<ScriptTab>();
private readonly TabControl _tabControl;
private int _activeIndex = 0;
public TabManager(TabControl tabControl)
{
_tabControl = tabControl;
}
// Add a new script tab
public void AddTab(string name = null)
{
int tabNumber = _tabs.Count + 1;
string tabName = name ?? $"Script {tabNumber}";
var tab = new ScriptTab(tabName);
var tabPage = new TabPage(tabName);
tab.TabPage = tabPage;
_tabs.Add(tab);
_tabControl.TabPages.Add(tabPage);
_tabControl.SelectedTab = tabPage;
}
// Remove tab by index
public void RemoveTab(int index)
{
if (index < 0 || index >= _tabs.Count) return;
_tabControl.TabPages.Remove(_tabs[index].TabPage);
_tabs.RemoveAt(index);
}
// Save current tab content before switching
public void SaveCurrentTabContent(string content)
{
if (_activeIndex >= 0 && _activeIndex < _tabs.Count)
_tabs[_activeIndex].Content = content;
}
// Get content for a tab
public string GetTabContent(int index)
{
if (index < 0 || index >= _tabs.Count) return string.Empty;
return _tabs[index].Content;
}
}
Since the original injection target is patched, here is the pattern used to plug in your own DLL:
// Classes/Injector.cs — Pattern for DLL injection
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
public class Injector
{
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(int access, bool inherit, int processId);
[DllImport("kernel32.dll")]
private static extern IntPtr VirtualAllocEx(
IntPtr hProcess, IntPtr lpAddress,
uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
private static extern bool WriteProcessMemory(
IntPtr hProcess, IntPtr lpBaseAddress,
byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateRemoteThread(
IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize,
IntPtr lpStartAddress, IntPtr lpParameter,
uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
private const int PROCESS_ALL_ACCESS = 0x1F0FFF;
private const uint MEM_COMMIT = 0x1000;
private const uint MEM_RESERVE = 0x2000;
private const uint PAGE_READWRITE = 0x04;
/// <summary>
/// Inject a DLL into a target process by name.
/// Returns true on success.
/// </summary>
public static bool InjectDll(string processName, string dllPath)
{
if (!File.Exists(dllPath))
throw new FileNotFoundException($"DLL not found: {dllPath}");
Process[] procs = Process.GetProcessesByName(processName);
if (procs.Length == 0)
throw new Exception($"Process '{processName}' not found.");
Process target = procs[0];
IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, target.Id);
if (hProcess == IntPtr.Zero)
throw new Exception("Failed to open process.");
byte[] dllBytes = System.Text.Encoding.ASCII.GetBytes(dllPath + "\0");
IntPtr allocMem = VirtualAllocEx(
hProcess, IntPtr.Zero, (uint)dllBytes.Length,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (allocMem == IntPtr.Zero)
throw new Exception("Memory allocation failed.");
WriteProcessMemory(hProcess, allocMem, dllBytes,
(uint)dllBytes.Length, out _);
IntPtr loadLibAddr = GetProcAddress(
GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr thread = CreateRemoteThread(
hProcess, IntPtr.Zero, 0,
loadLibAddr, allocMem, 0, IntPtr.Zero);
return thread != IntPtr.Zero;
}
}
// In MainForm.cs — Inject button click handler
private async void btnInject_Click(object sender, EventArgs e)
{
string dllPath = Path.Combine(
Application.StartupPath, "Modules", "your_dll.dll");
try
{
bool success = await Task.Run(() =>
Injector.InjectDll("RobloxPlayerBeta", dllPath));
lblStatus.Text = success
? "✔ Injected successfully"
: "✘ Injection failed";
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}", "LInjector",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Execute the current script via the injected DLL's named pipe or HTTP endpoint
private async void btnExecute_Click(object sender, EventArgs e)
{
string script = GetEditorContent();
if (string.IsNullOrWhiteSpace(script)) return;
try
{
await ExecuteScriptAsync(script);
lblStatus.Text = "Script executed.";
}
catch (Exception ex)
{
lblStatus.Text = $"Error: {ex.Message}";
}
}
private async Task ExecuteScriptAsync(string script)
{
// Example: communicate with injected DLL via named pipe
using var pipe = new System.IO.Pipes.NamedPipeClientStream(
".", "LInjectorPipe",
System.IO.Pipes.PipeDirection.Out);
await pipe.ConnectAsync(timeoutMilliseconds: 3000);
byte[] data = System.Text.Encoding.UTF8.GetBytes(script);
await pipe.WriteAsync(data, 0, data.Length);
}
// Save script to file
private void SaveScript(string content, string filePath = null)
{
if (filePath == null)
{
using var sfd = new SaveFileDialog
{
Filter = "Lua Files (*.lua)|*.lua|Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
DefaultExt = "lua",
FileName = "script"
};
if (sfd.ShowDialog() != DialogResult.OK) return;
filePath = sfd.FileName;
}
File.WriteAllText(filePath, content, System.Text.Encoding.UTF8);
}
// Load script from file
private string LoadScript()
{
using var ofd = new OpenFileDialog
{
Filter = "Lua Files (*.lua)|*.lua|Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
};
if (ofd.ShowDialog() != DialogResult.OK) return null;
return File.ReadAllText(ofd.FileName, System.Text.Encoding.UTF8);
}
LInjector uses app settings for persistent configuration:
// Reading/writing settings (App.config / user settings)
// In Settings.settings, add keys: Theme, FontSize, AutoInject
// Read
string theme = Properties.Settings.Default.Theme ?? "vs-dark";
int fontSize = Properties.Settings.Default.FontSize > 0
? Properties.Settings.Default.FontSize : 14;
// Write and save
Properties.Settings.Default.Theme = "vs-dark";
Properties.Settings.Default.FontSize = 16;
Properties.Settings.Default.Save();
// Apply theme to Monaco from C#
private void ApplyEditorTheme(string theme)
{
webBrowser.Document.InvokeScript(
"eval",
new object[] { $"monaco.editor.setTheme('{theme}')" }
);
}
// Replace "RobloxPlayerBeta" with your target process name
private const string TARGET_PROCESS = "RobloxPlayerBeta";
// or for UWP:
// private const string TARGET_PROCESS = "Windows10Universal";
// In Monaco/index.html — register Luau-specific completions
monaco.languages.registerCompletionItemProvider('lua', {
provideCompletionItems: function(model, position) {
return {
suggestions: [
{
label: 'game',
kind: monaco.languages.CompletionItemKind.Variable,
insertText: 'game',
documentation: 'The root DataModel instance'
},
{
label: 'workspace',
kind: monaco.languages.CompletionItemKind.Variable,
insertText: 'workspace',
documentation: 'Alias for game.Workspace'
},
{
label: 'loadstring',
kind: monaco.languages.CompletionItemKind.Function,
insertText: 'loadstring(${1:source})()',
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Load and execute a string as Lua code'
}
]
};
}
});
| Problem | Cause | Fix |
|---|---|---|
| Forms designer blank/broken | Compiled before opening form | Compile first (CTRL+SHIFT+B), then restart VS |
| msbuild not found | MSBuild not in PATH | Use VS Developer Command Prompt |
| Injection returns false | Process not found or access denied | Run as Administrator; verify process name |
| Monaco editor blank | WebBrowser control needs IE11 emulation | Add registry key for IE emulation (see below) |
| DLL not found error | DLL missing from Modules/ folder | Place your DLL in the output Modules/ directory |
Add this to your app startup (requires running as admin once, or via installer):
// Force IE11 rendering mode for WebBrowser control
private static void SetWebBrowserEmulation()
{
string appName = Path.GetFileName(Application.ExecutablePath);
using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
@"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
writable: true);
key?.SetValue(appName, 11001, Microsoft.Win32.RegistryValueKind.DWord);
}
// Call SetWebBrowserEmulation() in Program.cs before Application.Run()
public static bool IsProcessRunning(string name)
=> Process.GetProcessesByName(name).Length > 0;
// Usage
if (!IsProcessRunning("RobloxPlayerBeta"))
{
MessageBox.Show("Please launch Roblox before injecting.",
"LInjector", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
development
```markdown --- name: compose-performance-skills description: Install and use the skydoves/compose-performance-skills agent skill library to diagnose and fix Jetpack Compose performance issues including stability, recomposition, lazy layouts, modifiers, side effects, and build configuration. triggers: - "my composable recomposes too often" - "LazyColumn drops frames during scroll" - "diagnose Compose stability issues" - "fix unnecessary recomposition in Jetpack Compose" - "optimize Com
development
Headless iOS Simulator manager with host-side HID input injection, 60fps streaming, and device farm web UI for iOS 26
development
```markdown --- name: claude-code-game-studios description: Turn Claude Code into a full 49-agent game dev studio with 72 workflow skills, automated hooks, and a real studio hierarchy for Godot, Unity, and Unreal projects. triggers: - "set up claude code game studios" - "use ai agents for game development" - "set up game dev studio with claude" - "add game studio agents to my project" - "how do I use claude code for game dev" - "set up godot unity unreal ai workflow" - "49 agents g
development
```markdown --- name: xq-py-quantum-vm description: Python implementation of the Quip Network's quantum virtual machine (xqvm) triggers: - quantum virtual machine python - xqvm quip network - quantum circuit simulation python - xq-py quantum vm - quip network quantum python - simulate quantum gates python - quantum vm xqvm - xqvm-py quantum circuit --- # xq-py Quantum Virtual Machine > Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection. `xqvm-py` is a Python impl