-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Add roblox-creator-docs extension #20024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+4,599
−0
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
|
||
# Raycast specific files | ||
raycast-env.d.ts | ||
.raycast-swift-build | ||
.swiftpm | ||
compiled_raycast_swift | ||
|
||
# misc | ||
.DS_Store |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"printWidth": 120, | ||
"singleQuote": false | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Roblox Creator Docs Changelog | ||
|
||
## [Initial Version] - 2025-06-30 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Roblox Creator Docs | ||
|
||
Fast look-up for Roblox Creator Docs |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const { defineConfig } = require("eslint/config"); | ||
const raycastConfig = require("@raycast/eslint-config"); | ||
|
||
module.exports = defineConfig([ | ||
...raycastConfig, | ||
]); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
{ | ||
"$schema": "https://www.raycast.com/schemas/extension.json", | ||
"name": "roblox-creator-docs", | ||
"title": "Roblox Creator Docs", | ||
"description": "Fast look-up for Roblox Creator Docs", | ||
"icon": "extension-icon.png", | ||
"author": "ben_rowlands", | ||
"license": "MIT", | ||
"commands": [ | ||
{ | ||
"name": "creator-docs", | ||
"title": "Roblox Creator Docs", | ||
"subtitle": "Search Creator Docs", | ||
"description": "Browse and open Roblox Creator Docs in default browser", | ||
"mode": "view" | ||
} | ||
], | ||
"dependencies": { | ||
"@raycast/api": "^1.100.3", | ||
"@raycast/utils": "^1.17.0", | ||
"react": "^18.2.0", | ||
"node-fetch": "^2.6.7", | ||
"jszip": "^3.10.1", | ||
"js-yaml": "^4.1.0" | ||
}, | ||
"devDependencies": { | ||
"@raycast/eslint-config": "^2.0.4", | ||
"@types/node": "22.13.10", | ||
"@types/node-fetch": "^2.6.2", | ||
"@types/react": "19.0.10", | ||
"@types/js-yaml": "^4.0.5", | ||
"eslint": "^9.22.0", | ||
"prettier": "^3.5.3", | ||
"typescript": "^5.8.2" | ||
}, | ||
"scripts": { | ||
"build": "ray build", | ||
"dev": "node --max-old-space-size=4096 $(which ray) develop", | ||
"fix-lint": "ray lint --fix", | ||
"lint": "ray lint", | ||
"prepublishOnly": "echo \"\\n\\nIt seems like you are trying to publish the Raycast extension to npm.\\n\\nIf you did intend to publish it to npm, remove the \\`prepublishOnly\\` script and rerun \\`npm publish\\` again.\\nIf you wanted to publish it to the Raycast Store instead, use \\`npm run publish\\` instead.\\n\\n\" && exit 1", | ||
"publish": "npx @raycast/api@latest publish" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
import { Action, ActionPanel, List, open, showToast, Toast } from "@raycast/api"; | ||
import { useState, useEffect } from "react"; | ||
import RobloxDocsDataFetcher, { DocItem } from "./data-fetcher"; | ||
|
||
export default function Command() { | ||
const [searchText, setSearchText] = useState(""); | ||
const [filteredDocs, setFilteredDocs] = useState<DocItem[]>([]); | ||
const [allDocs, setAllDocs] = useState<DocItem[]>([]); | ||
const [isLoading, setIsLoading] = useState(true); | ||
|
||
// Performance optimization: limit displayed results | ||
const MAX_DISPLAYED_RESULTS = 50; | ||
|
||
// Initialize data fetcher | ||
const dataFetcher = new RobloxDocsDataFetcher(); | ||
|
||
useEffect(() => { | ||
loadDocsData(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (searchText.trim() === "") { | ||
// When no search term, show only first items to avoid memory issues | ||
setFilteredDocs(allDocs.slice(0, MAX_DISPLAYED_RESULTS)); | ||
} else { | ||
const searchLower = searchText.toLowerCase(); | ||
|
||
// Filter and score results for relevance | ||
const scoredResults = allDocs | ||
.map((doc) => { | ||
let score = 0; | ||
const titleLower = doc.title.toLowerCase(); | ||
const descLower = doc.description.toLowerCase(); | ||
|
||
// Exact title match gets highest score | ||
if (titleLower === searchLower) score += 100; | ||
// Title starts with search term | ||
else if (titleLower.startsWith(searchLower)) score += 50; | ||
// Title contains search term | ||
else if (titleLower.includes(searchLower)) score += 25; | ||
|
||
// Description matches | ||
if (descLower.includes(searchLower)) score += 10; | ||
|
||
// Keyword matches | ||
if (doc.keywords.some((keyword) => keyword.toLowerCase().includes(searchLower))) score += 15; | ||
|
||
// Category/type matches | ||
if (doc.category.toLowerCase().includes(searchLower)) score += 5; | ||
if (doc.type.toLowerCase().includes(searchLower)) score += 5; | ||
|
||
return { doc, score }; | ||
}) | ||
.filter((item) => item.score > 0) | ||
.sort((a, b) => b.score - a.score) | ||
.slice(0, MAX_DISPLAYED_RESULTS) | ||
.map((item) => item.doc); | ||
|
||
setFilteredDocs(scoredResults); | ||
} | ||
}, [searchText, allDocs, MAX_DISPLAYED_RESULTS]); | ||
|
||
const loadDocsData = async () => { | ||
try { | ||
setIsLoading(true); | ||
showToast({ | ||
style: Toast.Style.Animated, | ||
title: "Loading Roblox Creator Docs...", | ||
message: "Checking for updates...", | ||
}); | ||
|
||
const docs = await dataFetcher.fetchDocsData(); | ||
setAllDocs(docs); | ||
setFilteredDocs(docs.slice(0, MAX_DISPLAYED_RESULTS)); | ||
|
||
showToast({ | ||
style: Toast.Style.Success, | ||
title: "Docs Loaded Successfully", | ||
message: `Found ${docs.length} documentation pages`, | ||
}); | ||
} catch (error) { | ||
console.error("Error loading docs data:", error); | ||
showToast({ | ||
style: Toast.Style.Failure, | ||
title: "Failed to Load Docs", | ||
message: "Using fallback data. Check your internet connection.", | ||
}); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
const clearCacheAndRefresh = async () => { | ||
try { | ||
showToast({ | ||
style: Toast.Style.Animated, | ||
title: "Clearing Cache...", | ||
message: "Forcing fresh data fetch", | ||
}); | ||
|
||
// Clear the cache | ||
dataFetcher.clearCache(); | ||
|
||
// Reload data | ||
await loadDocsData(); | ||
} catch (error) { | ||
console.error("Error clearing cache:", error); | ||
showToast({ | ||
style: Toast.Style.Failure, | ||
title: "Failed to Clear Cache", | ||
message: "Please try again", | ||
}); | ||
} | ||
}; | ||
|
||
const getIconForType = (type: DocItem["type"], title?: string) => { | ||
// Apply smart icon detection for classes, references, and guides | ||
// since Roblox class docs can be categorized under any of these types | ||
if (title && (type === "class" || type === "reference" || type === "guide")) { | ||
const classIcon = getClassIcon(title); | ||
// Only use class icon if it's not the default, otherwise fall back to type icon | ||
if (classIcon !== "⚙️") { | ||
return classIcon; | ||
} | ||
} | ||
|
||
switch (type) { | ||
case "class": | ||
return "⚙️"; | ||
case "service": | ||
return "🔧"; | ||
case "tutorial": | ||
return "📚"; | ||
case "reference": | ||
return "📖"; | ||
case "guide": | ||
return "📝"; | ||
case "enum": | ||
return "🔢"; | ||
case "global": | ||
return "🌐"; | ||
case "property": | ||
return "🔶"; | ||
case "method": | ||
return "🔵"; | ||
case "event": | ||
return "⚡"; | ||
case "callback": | ||
return "🔄"; | ||
case "function": | ||
return "🟢"; | ||
default: | ||
return "📄"; | ||
} | ||
}; | ||
|
||
const getClassIcon = (className: string) => { | ||
const name = className.toLowerCase(); | ||
|
||
// Audio/Sound classes | ||
if (name.includes("audio") || name.includes("sound") || name.includes("music")) { | ||
return "🔊"; | ||
} | ||
|
||
// UI/GUI classes | ||
if ( | ||
name.includes("gui") || | ||
name.includes("frame") || | ||
name.includes("button") || | ||
name.includes("label") || | ||
name.includes("textbox") || | ||
name.includes("screen") | ||
) { | ||
return "🖥️"; | ||
} | ||
|
||
// Parts and physical objects | ||
if ( | ||
name.includes("part") || | ||
name.includes("mesh") || | ||
name.includes("union") || | ||
name.includes("wedge") || | ||
name.includes("cylinder") || | ||
name.includes("sphere") | ||
) { | ||
return "🧊"; | ||
} | ||
|
||
// Lighting and visual effects | ||
if ( | ||
name.includes("light") || | ||
name.includes("effect") || | ||
name.includes("beam") || | ||
name.includes("fire") || | ||
name.includes("smoke") || | ||
name.includes("sparkles") | ||
) { | ||
return "💡"; | ||
} | ||
|
||
// Animation classes | ||
if ( | ||
name.includes("animation") || | ||
name.includes("keyframe") || | ||
name.includes("pose") || | ||
name.includes("motor") || | ||
name.includes("joint") | ||
) { | ||
return "🎭"; | ||
} | ||
|
||
// Camera and rendering | ||
if (name.includes("camera") || name.includes("viewport")) { | ||
return "📷"; | ||
} | ||
|
||
// Player and character related | ||
if ( | ||
name.includes("player") || | ||
name.includes("humanoid") || | ||
name.includes("character") || | ||
name.includes("backpack") || | ||
name.includes("starter") | ||
) { | ||
return "👤"; | ||
} | ||
|
||
// Workspace and game structure | ||
if ( | ||
name.includes("workspace") || | ||
name.includes("folder") || | ||
name.includes("model") || | ||
name.includes("configuration") | ||
) { | ||
return "📁"; | ||
} | ||
|
||
// Physics and forces | ||
if ( | ||
name.includes("body") || | ||
name.includes("force") || | ||
name.includes("velocity") || | ||
name.includes("position") || | ||
name.includes("attachment") | ||
) { | ||
return "⚡"; | ||
} | ||
|
||
// Input and controls | ||
if ( | ||
name.includes("input") || | ||
name.includes("mouse") || | ||
name.includes("keyboard") || | ||
name.includes("touch") || | ||
name.includes("gamepad") | ||
) { | ||
return "🎮"; | ||
} | ||
|
||
// Network and communication | ||
if ( | ||
name.includes("remote") || | ||
name.includes("event") || | ||
name.includes("function") || | ||
name.includes("bindable") || | ||
name.includes("http") | ||
) { | ||
return "📡"; | ||
} | ||
|
||
// Script and programming | ||
if (name.includes("script") || name.includes("module") || name.includes("local")) { | ||
return "📜"; | ||
} | ||
|
||
// Default class icon | ||
return "⚙️"; | ||
}; | ||
|
||
return ( | ||
<List | ||
searchText={searchText} | ||
onSearchTextChange={setSearchText} | ||
searchBarPlaceholder="Search Roblox Creator Docs..." | ||
isLoading={isLoading} | ||
throttle | ||
> | ||
<List.Section | ||
title="Results" | ||
subtitle={ | ||
filteredDocs.length >= MAX_DISPLAYED_RESULTS | ||
? `${filteredDocs.length}+ docs (showing top ${MAX_DISPLAYED_RESULTS})` | ||
: `${filteredDocs.length} docs from ${allDocs.length} total` | ||
} | ||
> | ||
{filteredDocs.map((doc) => ( | ||
<List.Item | ||
key={doc.id} | ||
icon={getIconForType(doc.type, doc.title)} | ||
title={doc.title} | ||
subtitle={doc.category} | ||
accessories={[ | ||
{ text: doc.type }, | ||
{ text: doc.description.length > 50 ? doc.description.substring(0, 47) + "..." : doc.description }, | ||
]} | ||
actions={ | ||
<ActionPanel> | ||
<Action title="Open in Browser" onAction={() => open(doc.url)} icon="🌐" /> | ||
<Action.CopyToClipboard | ||
title="Copy URL" | ||
content={doc.url} | ||
shortcut={{ modifiers: ["cmd"], key: "c" }} | ||
icon="📋" | ||
/> | ||
<Action.CopyToClipboard | ||
title="Copy Title" | ||
content={doc.title} | ||
shortcut={{ modifiers: ["cmd", "shift"], key: "c" }} | ||
icon="📝" | ||
/> | ||
<Action | ||
title="Refresh Data" | ||
onAction={loadDocsData} | ||
shortcut={{ modifiers: ["cmd"], key: "r" }} | ||
icon="🔄" | ||
/> | ||
<Action | ||
title="Clear Cache & Refresh" | ||
onAction={clearCacheAndRefresh} | ||
shortcut={{ modifiers: ["cmd", "shift"], key: "r" }} | ||
icon="🗑️" | ||
/> | ||
</ActionPanel> | ||
} | ||
/> | ||
))} | ||
</List.Section> | ||
|
||
{filteredDocs.length === 0 && !isLoading && ( | ||
<List.EmptyView | ||
icon="../assets/no-results.png" | ||
title="No Results Found" | ||
description={`No documentation found for "${searchText}". Try a different search term.`} | ||
/> | ||
)} | ||
</List> | ||
); | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/tsconfig", | ||
"include": ["src/**/*", "raycast-env.d.ts"], | ||
"compilerOptions": { | ||
"lib": ["ES2023"], | ||
"module": "commonjs", | ||
"target": "ES2023", | ||
"strict": true, | ||
"isolatedModules": true, | ||
"esModuleInterop": true, | ||
"skipLibCheck": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"jsx": "react-jsx", | ||
"resolveJsonModule": true | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.