Ode Script Editor: A simple embedded script editor entirely in Roblox

Installation:

Ode Script Editor is a simple and embedded Luau source-code editor in Roblox that can run both in-game and contained in a plugin. It includes some QoL features such as syntax highlighting and auto-indentation.

Here is a very simple snippet that demonstrates its usage. It works almost entirely out of the box with no tinkering required!

local OdeSE = require(path.to.OdeScriptEditor)
local FrameInstance: Frame = path.to.FrameInstance

local scriptEditor = OdeSE.Embed(FrameInstance)

odecode

To see it in action, check out my plugin Exestack!

Wait, how do I use this? [DOCUMENTATION]


Ode Script Editor requires minimal setup, only needing a GuiObject (preferably a Frame) and a single function to attach the editor. However, to actually use it for something useful needs some more work. Ode provides a simple API under the ScriptEditor class created by .Embed().

ScriptEditor OdeScriptEditor.Embed(Gui: GuiBase2d)

Creates a ScriptEditor class.


nil ScriptEditor:LoadStringAsync(str: string, lineNumber: number?)

Loads a string and jumps to the specified line number or 1 if none is given.


nil ScriptEditor:LoadScriptAsync(scriptObject: LuaSourceContainer, lineNumber: number)

Loads a script and jumps to the specified line number or 1 if none is given. Ode does not save changes if you use this! It needs to be done in conjunction with .OnEdit.


nil ScriptEditor:Unload()

Unloads the script editor’s source code text.


nil ScriptEditor:ReadOnly(allowEditing: boolean?)

Sets the script editor to read only. If allowEditing is set to true then it re-enables editing.


nil ScriptEditor:ApplyTheme(odeThemeData: OdeThemeData)

Applies a theme to Ode. Read the source code for more information.


nil ScriptEditor:ApplyStudioTheme(studio: Studio)

Applies a studio theme to Ode.


nil ScriptEditor:Destroy()

Destroys the script editor.


Event ScriptEditor.OnEdit (source: string)

An event that fires whenever an edit has been made.


number ScriptEditor.LineFocused

The line that is first displayed.


string ScriptEditor.RawSource

The raw source code the editor is using.


Frame ScriptEditor.Background

The GUI container for the script editor


boolean ScriptEditor.AutoWrapEnabled

Whether or not auto wrapping is enabled.


number ScriptEditor.VisibleLines

An internal value used to describe the number of lines visible in the editor.


LuaTable ScriptEditor.SourceData

An internal data type used to represent source code.


OdeThemeData ScriptEditor.Theme

An internal table for Ode’s theme data. To change this use the :ApplyTheme method given. For more information check out the GitHub repo.

Any other features?


I’m quite busy as of right now and can’t find the time to develop much. However, you are welcome to help in the repo!

27 Likes

Is there anyway to run the script?

1 Like

This sounds pretty awesome! I’m thinking of utilizing this to have an AI write its own code like in that Stanford paper on minecraft.
For something like that you would need to prompt an AI model to write code. ChatGPT is the best API for this. You can send a system message to only respond in code format then recieve the string from the API and execute the string using

LoadString(response.response_text). But you would

function GPT4(inputText)
-- Import the HttpService
-- Define the API URL
	local API_URL =""
-- Define the headers with your authorization key
local headers = {
	["Authorization"] = Bearerkey
}

-- Define a function to query the API with a payload
local function query(payload)
	-- Encode the payload as a JSON string
	local jsonPayload = HttpService:JSONEncode(payload)
	-- Send a POST request to the API URL with the headers and the payload
	local response = HttpService:PostAsync(API_URL, jsonPayload, Enum.HttpContentType.ApplicationJson, false, headers)
	-- Decode the response as a JSON table
	local jsonResponse = HttpService:JSONDecode(response)
	-- Return the JSON table
	return jsonResponse
end
	
-- Define your input text
	
-- Query the API with your input text as the inputs field
	local output = query({
["system_message"] ="You are a Lua coding assistant in the context of ROBLOX Studio. Only provide your response in code block format and only provide explanations in comments."		
["inputs"] = chatmodule.randomizeString(inputText),
	})
	
	-- or
	local generatedText = output[1]["generated_text"]
--print(output)
	-- Print the string
	return generatedText
-- Print the output

end

You can then save this string if it passes into a function key array. You can then use a function like this to input the prompt and store the generated command into an array. This is just concept. I haven’t done it myself but I’ve looked into it. :slight_smile: I’m working on a library of action functions right now, but a more advanced version of this concept here would be good for creating self learning AIs that write their own code using a LLM.

---example of table function
local Labeldb={
	["search query"]=wikidb,
}
--example of writing a input into an existing table
--- and receiving api input to store the function into an array
function WriteData(playerinput)
local savedfunction=GPT4(playerinput)
Labeldb[playerinput]= function() return savedfunction:LoadString() end --This way the recieved variable is stored and the function is executed with the
Labeldb[playerinput]()
end

Edit: I just realized you wanted to run the script you wrote in Ode. Sorry but this resource doesn’t come with a compiler/interpreter of any kind. That’s something you have to do yourself.

ORIGINAL REPLY:


It’s already explained in the original post with my simple code snippet but I’m gonna guide you through the process since it seems you’re struggling a bit.


  1. Install the module script. This can be done with the GitHub repo using plugins like Rojo or installing the one provided in the Creator Marketplace.
  2. Create a new Roblox place or use an old one.
  3. Once you’ve installed it, place it inside ReplicatedStorage.
  4. Create a ScreenGui and a Frame instance. I recommend resizing the Frame to take up more space.
  5. Create a local script inside of the ScreenGui and write this inside:
local OdeSE = require(game.ReplicatedStorage.OdeScriptEditor)
local FrameInstance = script.Parent.Frame

local scriptEditor = OdeSE.Embed(FrameInstance)
  1. You’re done!

I can’t actually test this since I’m not on my computer right now but it should work. If you want an additional example you can use the uncopylocked Roblox place I listed to see how the code works.

welp is there anyone i could get all the text the player written then run it with load string or something?

Yes. I just forgot to include it in the documentation.

odeScriptEditor.OnEdit:Connect(function(source)
    --prints luau code the user wrote
    print(source)
end)

or

local source = odeScriptEditor.RawSource

As for running the code, use loadstring or this: vLua: Loadstring reimplemented in Lua

Version 1.0.1

  • Added some new builtin identifiers (Font, SharedTable, buffer, etc.).
  • Partial syntax highlighting support for string interpolation.

Version 1.0.2

  • Fixed a bug introduced with Roblox’s TextBox Scrolling update where moving your cursor could cause a desync between the text and syntax highlighting.
  • Fixed a bug where line numbers would not properly update.
1 Like

Hello! I looked through the source code and it seems odd when the letter “y” is used, it gets offsetted weirdly and ruins all proceeding highlighting, is there a fix for this?

Awesome project nonetheless!

I was not able to replicate your bug. Can you show me an example?

Screenshot 2024-05-26 091118

It was on me because of another font :sob:

encountered a bug when pasting:

also when doing ctrl+a it only selects whats shown of the scrollbar container,

I was unable to replicate this issue. Did you grab the 1.0.2 version on the Creator Marketplace, because that is a little outdated compared to the GitHub repo, and there copy-pasting works fine from testing.


This is somewhat intentional. Ode does not load all the Lua code at once but instead only shows what should be visible to the user. It is done this way so Ode can handle larger scripts longer than a few hundred lines. This is a consequence of that.

Nice, I suggest you look at my editor (plugin). It uses a different syntax highlighter. The one made by boatbomber can’t handle large lines of code but mine can, and it’s open source. I’ve also got somewhat functional autocompletion if that interests you. It’s able to run code, output, etc - basically a buffed up version of what you have.

But I think your idea of a minimalistic editor is nice. I would try replacing your syntax highlighter with mine and see if it’s better, and try adding autocomplete.

3 Likes

I remember seeing your plugin a while back in Cool Creations/Creations Feedback before I even open sourced Ode Script Editor. I was honestly very impressed with what you had accomplished!

Looking at the code for your lexer it would be difficult to implement as Ode expects for the lexer to give a third value specifying where in the code it found a particular token. I already had to modify boatbomber’s lexer to do this :sob:.

I can’t find the time to dedicate myself to this, but it’s definitely in the list of things I want to do though!

Oh thank you, I appreciate it. It’s a shame it didn’t get a lot of attention as if it did it would motivate me to increase its potential into actual software. Seeing occasional projects like this that make some sort of ide or editor motivates me. I was actually planning to make something VERY similar to yours, but can run sandboxed code in its own actors or threads, kill running threads in basically a small box (like inCommand).

Could you elaborate on this? Boatbomber’s scans the string for tokens anyway. You don’t really need to tamper my lexer to work with yours in my opinion. You only really need it for autocomplete and syntax highlighting.

This is unreliable, exposing your access token grants many vulnerabilities to your AI account. I wouldn’t trust making a plugin that includes AI because you need some sort of auth token to send the requests.

I didn’t tell you to make a plugin and post your auth token. As you can see I did not post mine. Not that I don’t agree that it shouldn’t be done.

1 Like

Oh my apologies. Sorry.