mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Add vscode-rojo submodule and update schemas for Atlas rebranding
- Added the vscode-rojo submodule to the project, linking to the UserGeneratedLLC repository. - Created a new plan for syncing vscode-rojo schemas, detailing updates to project, meta, and model schemas to align with the Rojo fork and rebrand as "Atlas". - Enhanced the Theme and DomLabel components to support subtree highlighting, improving user interaction in the PatchVisualizer. - Updated the PatchVisualizer to manage subtree highlight state and ancestor IDs for better visual feedback during selection.
This commit is contained in:
parent
aeec3a962b
commit
edfe15a143
6 changed files with 235 additions and 0 deletions
169
.cursor/plans/sync_vscode-rojo_schemas_6f3a7b26.plan.md
Normal file
169
.cursor/plans/sync_vscode-rojo_schemas_6f3a7b26.plan.md
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
---
|
||||||
|
name: Sync vscode-rojo schemas
|
||||||
|
overview: Update the vscode-rojo extension's schemas, file associations, TypeScript code, and branding to match this Rojo fork -- rebrand as "Atlas", add JSON5 support, new project fields, new file schemas, new middleware types, and syncback rules.
|
||||||
|
todos:
|
||||||
|
- id: update-project-schema
|
||||||
|
content: "Overhaul project.template.schema.json: make name optional, add all new fields with descriptions from Rust doc comments. Every field gets a concise description for VS Code tooltip intellisense."
|
||||||
|
status: completed
|
||||||
|
- id: create-meta-schema
|
||||||
|
content: Create meta.template.schema.json with documented fields. Descriptions sourced from meta_file.rs doc comments.
|
||||||
|
status: completed
|
||||||
|
- id: create-model-schema
|
||||||
|
content: Create model.template.schema.json with documented fields. Descriptions sourced from json_model.rs doc comments.
|
||||||
|
status: completed
|
||||||
|
- id: update-package-json
|
||||||
|
content: Rebrand package.json to Atlas (name, displayName, publisher, commands, config keys, activation events), add json5 file associations, new schema entries for meta/model, update GitHub URL
|
||||||
|
status: completed
|
||||||
|
- id: update-generate-plugin
|
||||||
|
content: Update GenerateSchemaPlugin.js to also generate meta.schema.json and model.schema.json, inject class names into model schema
|
||||||
|
status: completed
|
||||||
|
- id: rebrand-typescript
|
||||||
|
content: Rebrand all user-facing strings from 'Rojo' to 'Atlas' in TS files, update command IDs from vscode-rojo.* to vscode-atlas.*, update GitHub/docs links, add .project.json5 support to findProjectFiles and openMenu
|
||||||
|
status: completed
|
||||||
|
- id: update-readme
|
||||||
|
content: Rebrand README.md from Rojo to Atlas, update GitHub links, mention json5 support
|
||||||
|
status: completed
|
||||||
|
isProject: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Sync vscode-rojo Extension with Rojo Fork + Atlas Rebrand
|
||||||
|
|
||||||
|
## Three Concerns
|
||||||
|
|
||||||
|
1. **Schema sync** -- bring schemas up to date with all Rojo fork changes
|
||||||
|
2. **Schema documentation** -- every field gets a concise `description` from the Rust doc comments, so VS Code shows useful tooltips
|
||||||
|
3. **Atlas rebrand** -- rename all user-facing text, VS Code registration IDs, and links
|
||||||
|
|
||||||
|
**NOT in scope:** Renaming actual file paths, folders, TypeScript variable/type names, or dependencies. Only front-end text, VS Code command/config IDs, and links change.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schema Changes
|
||||||
|
|
||||||
|
### 1. Update `project.template.schema.json` -- Major overhaul
|
||||||
|
|
||||||
|
Source of truth: [src/project.rs](src/project.rs) lines 61-149 (Project), 372-435 (ProjectNode)
|
||||||
|
|
||||||
|
**Top-level changes:**
|
||||||
|
|
||||||
|
- `name` -- change from **required** to **optional**
|
||||||
|
- Add `$schema` (string), `blockedPlaceIds` (integer[]), `syncbackRules` (object), `syncRules` (SyncRule[]), `syncScriptsOnly` (boolean), `ignoreHiddenServices` (boolean)
|
||||||
|
- Fix `servePlaceIds` items to `integer`
|
||||||
|
- Remove `"required": ["name", "tree"]` -- only `tree` required
|
||||||
|
|
||||||
|
**Tree node additions:** `$id` (string), `$attributes` (object)
|
||||||
|
|
||||||
|
**SyncbackRules sub-schema** (from [src/syncback/mod.rs](src/syncback/mod.rs) lines 959-1015):
|
||||||
|
|
||||||
|
- `ignoreTrees`, `ignorePaths`, `ignoreClasses`: string[]
|
||||||
|
- `ignoreProperties`: { [className]: string[] }
|
||||||
|
- `syncCurrentCamera`, `syncUnscriptable`, `ignoreReferents`, `createIgnoreDirPaths`, `encodeWindowsInvalidChars`, `ignoreHiddenServices`, `warnDuplicateNames`: boolean
|
||||||
|
|
||||||
|
**SyncRule sub-schema** ([src/snapshot/metadata.rs](src/snapshot/metadata.rs) lines 294-313):
|
||||||
|
|
||||||
|
- `pattern` (string), `exclude` (string, optional), `use` (Middleware enum), `suffix` (string, optional)
|
||||||
|
|
||||||
|
**Middleware enum** (camelCase): `csv`, `jsonModel`, `json`, `serverScript`, `clientScript`, `moduleScript`, `pluginScript`, `localScript`, `legacyScript`, `project`, `rbxm`, `rbxmx`, `toml`, `text`, `yaml`, `ignore`
|
||||||
|
|
||||||
|
### 2. Create `schemas/meta.template.schema.json`
|
||||||
|
|
||||||
|
Source: [src/snapshot_middleware/meta_file.rs](src/snapshot_middleware/meta_file.rs). Combined schema for `*.meta.json5` and `init.meta.json5`.
|
||||||
|
|
||||||
|
All fields optional: `$schema`, `id`, `ignoreUnknownInstances`, `properties`, `attributes`, `className`
|
||||||
|
|
||||||
|
### 3. Create `schemas/model.template.schema.json`
|
||||||
|
|
||||||
|
Source: [src/snapshot_middleware/json_model.rs](src/snapshot_middleware/json_model.rs) lines 171-202.
|
||||||
|
|
||||||
|
`className` required. Optional: `$schema`, `name`, `id`, `children` (recursive), `properties`, `attributes`
|
||||||
|
|
||||||
|
### 4. Update `GenerateSchemaPlugin.js`
|
||||||
|
|
||||||
|
- Also read and process `meta.template.schema.json` and `model.template.schema.json`
|
||||||
|
- Inject class names enum into model schema's `className` field
|
||||||
|
- Write all three to `dist/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Atlas Rebrand
|
||||||
|
|
||||||
|
### 5. `package.json` changes
|
||||||
|
|
||||||
|
|
||||||
|
| Field | Old | New |
|
||||||
|
| ---------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `name` | `vscode-rojo` | `vscode-atlas` |
|
||||||
|
| `displayName` | `Rojo - Roblox Studio Sync` | `Atlas - Roblox Studio Sync` |
|
||||||
|
| `description` | `Rojo for VS Code` | `Atlas for VS Code` |
|
||||||
|
| `repository` | `rojo-rbx/vscode-rojo` | `UserGeneratedLLC/vscode-rojo` |
|
||||||
|
| `publisher` | `evaera` | TBD (keep or change) |
|
||||||
|
| command ID | `vscode-rojo.openMenu` | `vscode-atlas.openMenu` |
|
||||||
|
| command category | `Rojo` | `Atlas` |
|
||||||
|
| config title | `Rojo` | `Atlas` |
|
||||||
|
| config keys | `rojo.*` | `atlas.*` |
|
||||||
|
| activation | `onCommand:vscode-rojo.openMenu` | `onCommand:vscode-atlas.openMenu` |
|
||||||
|
| activation | `workspaceContains:*.project.json` | also add `*.project.json5` |
|
||||||
|
| jsonValidation | only `*.project.json/jsonc` | Replace with 3 `.json` entries only: `*.project.json`, `*.meta.json`, `*.model.json` (drop stale `.jsonc`; `.json5` handled by `json5.schemas`) |
|
||||||
|
|
||||||
|
|
||||||
|
**JSON5 schema registration via Better JSON5.** VS Code's built-in `jsonValidation` only works for `json`/`jsonc` language modes. Since we target the [Better JSON5](https://github.com/BlueGlassBlock/better-json5) extension (which has its own `json5.schemas` setting with full IntelliSense/validation), we register `.json5` schemas via `configurationDefaults` instead:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"configurationDefaults": {
|
||||||
|
"json5.schemas": [
|
||||||
|
{ "fileMatch": ["*.project.json5"], "url": "./dist/project.schema.json" },
|
||||||
|
{ "fileMatch": ["*.meta.json5"], "url": "./dist/meta.schema.json" },
|
||||||
|
{ "fileMatch": ["*.model.json5"], "url": "./dist/model.schema.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `jsonValidation` entries only cover the `.json` variants (which use VS Code's native JSON language service). No `jsonc` file association overrides needed.
|
||||||
|
|
||||||
|
**Bundle Better JSON5.** Add `extensionPack` to `package.json` so it installs alongside Atlas (users can disable independently):
|
||||||
|
|
||||||
|
```json
|
||||||
|
"extensionPack": [
|
||||||
|
"BlueGlassBlock.better-json5",
|
||||||
|
"JohnnyMorganz.luau-lsp",
|
||||||
|
"JohnnyMorganz.stylua"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Conflict check with original Rojo extension.** VS Code has no manifest-level incompatibility flag, so add a runtime check in `extension.ts` on activation: if `vscode.extensions.getExtension("evaera.vscode-rojo")` returns non-null, show a warning message asking the user to disable it (both extensions register the same commands/schemas and will conflict).
|
||||||
|
|
||||||
|
### 6. TypeScript string replacements (user-facing text only, not variable names)
|
||||||
|
|
||||||
|
**Command IDs** (in all TS files): `"vscode-rojo.*"` -> `"vscode-atlas.*"`
|
||||||
|
|
||||||
|
Affected files:
|
||||||
|
|
||||||
|
- `extension.ts` -- button command, console log, status messages
|
||||||
|
- `updateButton.ts` -- button commands
|
||||||
|
- `commands/openMenu.ts` -- registerCommand, executeCommand
|
||||||
|
- `commands/serveRecent.ts` -- registerCommand, executeCommand
|
||||||
|
- `commands/stopAll.ts` -- registerCommand
|
||||||
|
- `configuration.ts` -- config section name `"rojo"` -> `"atlas"`
|
||||||
|
|
||||||
|
**User-facing "Rojo" -> "Atlas"** in string literals:
|
||||||
|
|
||||||
|
- `extension.ts`: button text, info messages
|
||||||
|
- `openMenu.ts`: menu labels, error messages, info messages
|
||||||
|
- `serveProject.ts`: terminal title
|
||||||
|
- `buildProject.ts`: error/info messages
|
||||||
|
- `installPlugin.ts`: error/info messages
|
||||||
|
- `installRojo.ts`: success/error messages (note: the actual `rojo` CLI binary name stays the same -- only display text changes)
|
||||||
|
|
||||||
|
**Links:**
|
||||||
|
|
||||||
|
- `rojo.space/docs/v7/` -> keep as-is (still valid Rojo docs) OR update if there's a new docs URL
|
||||||
|
- `discord.gg/wH5ncNS` -> keep as-is (same community)
|
||||||
|
- `extension/evaera.vscode-rojo` -> update extension ID reference
|
||||||
|
|
||||||
|
`**default.project.json**` references -> `default.project.json5` (openMenu.ts lines 401-404)
|
||||||
|
|
||||||
|
`**findProjectFiles.ts**` line 77: also match `.project.json5`
|
||||||
|
|
||||||
|
### 7. README.md
|
||||||
|
|
||||||
|
Rebrand all "Rojo" display text to "Atlas", update GitHub link to `UserGeneratedLLC/vscode-rojo`, mention `.project.json5` support.
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -22,3 +22,6 @@
|
||||||
[submodule "plugin/Packages/msgpack-luau"]
|
[submodule "plugin/Packages/msgpack-luau"]
|
||||||
path = plugin/Packages/msgpack-luau
|
path = plugin/Packages/msgpack-luau
|
||||||
url = https://github.com/cipharius/msgpack-luau/
|
url = https://github.com/cipharius/msgpack-luau/
|
||||||
|
[submodule "vscode-rojo"]
|
||||||
|
path = vscode-rojo
|
||||||
|
url = https://github.com/UserGeneratedLLC/vscode-rojo.git
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,16 @@ function SelectionOption:render()
|
||||||
ZIndex = 10,
|
ZIndex = 10,
|
||||||
[Roact.Event.MouseEnter] = function()
|
[Roact.Event.MouseEnter] = function()
|
||||||
self:setState({ isHovered = true })
|
self:setState({ isHovered = true })
|
||||||
|
-- Trigger subtree highlight when this button would apply to children
|
||||||
|
if (isParentOnly or (isSelected and hasChildren)) and props.onSubtreeHoverStart then
|
||||||
|
props.onSubtreeHoverStart()
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
[Roact.Event.MouseLeave] = function()
|
[Roact.Event.MouseLeave] = function()
|
||||||
self:setState({ isHovered = false })
|
self:setState({ isHovered = false })
|
||||||
|
if props.onSubtreeHoverEnd then
|
||||||
|
props.onSubtreeHoverEnd()
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
[Roact.Event.Activated] = function()
|
[Roact.Event.Activated] = function()
|
||||||
if isParentOnly or (isSelected and hasChildren) then
|
if isParentOnly or (isSelected and hasChildren) then
|
||||||
|
|
@ -173,6 +180,8 @@ local function SelectionRadio(props)
|
||||||
layoutOrder = 1,
|
layoutOrder = 1,
|
||||||
hasChildren = hasChildren,
|
hasChildren = hasChildren,
|
||||||
isParentOnly = isParentOnly,
|
isParentOnly = isParentOnly,
|
||||||
|
onSubtreeHoverStart = props.onSubtreeHoverStart,
|
||||||
|
onSubtreeHoverEnd = props.onSubtreeHoverEnd,
|
||||||
onClick = function()
|
onClick = function()
|
||||||
if props.onSelectionChange then
|
if props.onSelectionChange then
|
||||||
props.onSelectionChange(props.nodeId, "pull")
|
props.onSelectionChange(props.nodeId, "pull")
|
||||||
|
|
@ -192,6 +201,8 @@ local function SelectionRadio(props)
|
||||||
layoutOrder = 2,
|
layoutOrder = 2,
|
||||||
hasChildren = hasChildren,
|
hasChildren = hasChildren,
|
||||||
isParentOnly = isParentOnly,
|
isParentOnly = isParentOnly,
|
||||||
|
onSubtreeHoverStart = props.onSubtreeHoverStart,
|
||||||
|
onSubtreeHoverEnd = props.onSubtreeHoverEnd,
|
||||||
onClick = function()
|
onClick = function()
|
||||||
if props.onSelectionChange then
|
if props.onSelectionChange then
|
||||||
props.onSelectionChange(props.nodeId, "ignore")
|
props.onSelectionChange(props.nodeId, "ignore")
|
||||||
|
|
@ -211,6 +222,8 @@ local function SelectionRadio(props)
|
||||||
layoutOrder = 3,
|
layoutOrder = 3,
|
||||||
hasChildren = hasChildren,
|
hasChildren = hasChildren,
|
||||||
isParentOnly = isParentOnly,
|
isParentOnly = isParentOnly,
|
||||||
|
onSubtreeHoverStart = props.onSubtreeHoverStart,
|
||||||
|
onSubtreeHoverEnd = props.onSubtreeHoverEnd,
|
||||||
onClick = function()
|
onClick = function()
|
||||||
if props.onSelectionChange then
|
if props.onSelectionChange then
|
||||||
props.onSelectionChange(props.nodeId, "push")
|
props.onSelectionChange(props.nodeId, "push")
|
||||||
|
|
@ -324,6 +337,17 @@ function DomLabel:render()
|
||||||
local props = self.props
|
local props = self.props
|
||||||
local depth = props.depth or 1
|
local depth = props.depth or 1
|
||||||
|
|
||||||
|
-- Derive subtree highlight state from binding (no re-renders needed)
|
||||||
|
local ancestorIds = props.ancestorIds
|
||||||
|
local isSubtreeHighlighted = if props.subtreeHighlightNodeId
|
||||||
|
then props.subtreeHighlightNodeId:map(function(highlightId)
|
||||||
|
if highlightId == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return ancestorIds[highlightId] == true
|
||||||
|
end)
|
||||||
|
else nil
|
||||||
|
|
||||||
return Theme.with(function(theme)
|
return Theme.with(function(theme)
|
||||||
local color = if props.isWarning
|
local color = if props.isWarning
|
||||||
then theme.Diff.Warning
|
then theme.Diff.Warning
|
||||||
|
|
@ -520,6 +544,17 @@ function DomLabel:render()
|
||||||
})
|
})
|
||||||
else nil,
|
else nil,
|
||||||
}),
|
}),
|
||||||
|
SubtreeHighlight = if isSubtreeHighlighted
|
||||||
|
then e("Frame", {
|
||||||
|
Size = UDim2.fromScale(1, 1),
|
||||||
|
BackgroundColor3 = theme.Diff.SubtreeHighlight,
|
||||||
|
BackgroundTransparency = isSubtreeHighlighted:map(function(highlighted)
|
||||||
|
return if highlighted then 0.92 else 1
|
||||||
|
end),
|
||||||
|
BorderSizePixel = 0,
|
||||||
|
ZIndex = 0,
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
SelectionRadio = e(SelectionRadio, {
|
SelectionRadio = e(SelectionRadio, {
|
||||||
-- Visible for nodes with patchType, or for parent-only nodes on hover
|
-- Visible for nodes with patchType, or for parent-only nodes on hover
|
||||||
visible = (props.patchType ~= nil and props.onSelectionChange ~= nil)
|
visible = (props.patchType ~= nil and props.onSelectionChange ~= nil)
|
||||||
|
|
@ -537,6 +572,16 @@ function DomLabel:render()
|
||||||
isParentOnly = props.patchType == nil,
|
isParentOnly = props.patchType == nil,
|
||||||
onSelectionChange = props.onSelectionChange,
|
onSelectionChange = props.onSelectionChange,
|
||||||
onSubtreeSelectionChange = props.onSubtreeSelectionChange,
|
onSubtreeSelectionChange = props.onSubtreeSelectionChange,
|
||||||
|
onSubtreeHoverStart = function()
|
||||||
|
if props.setSubtreeHighlightNodeId then
|
||||||
|
props.setSubtreeHighlightNodeId(props.nodeId)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
onSubtreeHoverEnd = function()
|
||||||
|
if props.setSubtreeHighlightNodeId then
|
||||||
|
props.setSubtreeHighlightNodeId(nil)
|
||||||
|
end
|
||||||
|
end,
|
||||||
}),
|
}),
|
||||||
LineGuides = e("Folder", nil, lineGuides),
|
LineGuides = e("Folder", nil, lineGuides),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ function PatchVisualizer:init()
|
||||||
-- Without this, every render creates new bindings starting at 24,
|
-- Without this, every render creates new bindings starting at 24,
|
||||||
-- causing expanded rows to visually glitch when unrelated props change.
|
-- causing expanded rows to visually glitch when unrelated props change.
|
||||||
self.nodeHeightBindings = {}
|
self.nodeHeightBindings = {}
|
||||||
|
|
||||||
|
-- Binding for subtree hover preview: stores the nodeId whose children should highlight
|
||||||
|
self.subtreeHighlightNodeId, self.setSubtreeHighlightNodeId = Roact.createBinding(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
function PatchVisualizer:willUnmount()
|
function PatchVisualizer:willUnmount()
|
||||||
|
|
@ -180,6 +183,15 @@ function PatchVisualizer:render()
|
||||||
local elementHeight = self.nodeHeightBindings[nodeId].binding
|
local elementHeight = self.nodeHeightBindings[nodeId].binding
|
||||||
local setElementHeight = self.nodeHeightBindings[nodeId].setBinding
|
local setElementHeight = self.nodeHeightBindings[nodeId].setBinding
|
||||||
|
|
||||||
|
-- Compute ancestor IDs for subtree hover highlight
|
||||||
|
local ancestorIds = {}
|
||||||
|
local currentParentId = node.parentId
|
||||||
|
while currentParentId and currentParentId ~= "ROOT" do
|
||||||
|
ancestorIds[currentParentId] = true
|
||||||
|
local parentNode = patchTree:getNode(currentParentId)
|
||||||
|
currentParentId = parentNode and parentNode.parentId
|
||||||
|
end
|
||||||
|
|
||||||
elementHeights[elementIndex] = elementHeight
|
elementHeights[elementIndex] = elementHeight
|
||||||
scrollElements[elementIndex] = e(DomLabel, {
|
scrollElements[elementIndex] = e(DomLabel, {
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
|
|
@ -206,6 +218,10 @@ function PatchVisualizer:render()
|
||||||
selection = self.props.selections and self.props.selections[node.id],
|
selection = self.props.selections and self.props.selections[node.id],
|
||||||
onSelectionChange = self.props.onSelectionChange,
|
onSelectionChange = self.props.onSelectionChange,
|
||||||
onSubtreeSelectionChange = self.props.onSubtreeSelectionChange,
|
onSubtreeSelectionChange = self.props.onSubtreeSelectionChange,
|
||||||
|
-- Subtree hover highlight props
|
||||||
|
ancestorIds = ancestorIds,
|
||||||
|
subtreeHighlightNodeId = self.subtreeHighlightNodeId,
|
||||||
|
setSubtreeHighlightNodeId = self.setSubtreeHighlightNodeId,
|
||||||
})
|
})
|
||||||
|
|
||||||
if isFinalChild then
|
if isFinalChild then
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ function StudioProvider:updateTheme()
|
||||||
Edit = Color3.fromRGB(255, 0, 255),
|
Edit = Color3.fromRGB(255, 0, 255),
|
||||||
|
|
||||||
Row = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText),
|
Row = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText),
|
||||||
|
SubtreeHighlight = if isDark then Color3.fromRGB(255, 255, 255) else Color3.fromRGB(0, 0, 0),
|
||||||
Warning = studioTheme:GetColor(Enum.StudioStyleGuideColor.WarningText),
|
Warning = studioTheme:GetColor(Enum.StudioStyleGuideColor.WarningText),
|
||||||
WhitespaceOnly = if isDark then Color3.fromRGB(140, 140, 140) else Color3.fromRGB(110, 110, 110),
|
WhitespaceOnly = if isDark then Color3.fromRGB(140, 140, 140) else Color3.fromRGB(110, 110, 110),
|
||||||
Changes = if isDark then Color3.fromRGB(100, 140, 220) else Color3.fromRGB(40, 100, 180),
|
Changes = if isDark then Color3.fromRGB(100, 140, 220) else Color3.fromRGB(40, 100, 180),
|
||||||
|
|
|
||||||
1
vscode-rojo
Submodule
1
vscode-rojo
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 67bb42ad6a5d019f260a88950b56c49deeec55a2
|
||||||
Loading…
Add table
Add a link
Reference in a new issue