Skip to content
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

Add support for Roblox's internal Stories and Storybooks #290

Draft
wants to merge 87 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
f2eb567
Switch to Storyteller for all story handling
vocksel Oct 27, 2024
45b13ff
Bump Storyteller to 0.2.0
vocksel Oct 31, 2024
4073010
Update stories.spec to be more like the e2e tests in Storyteller
vocksel Oct 31, 2024
a6d84e0
Storyteller 0.2.1
vocksel Oct 31, 2024
0adbbd2
Fix analysis errors
vocksel Nov 1, 2024
c640e6d
Fix story names getting cut off
vocksel Nov 2, 2024
3967950
Get controls largely working
vocksel Nov 2, 2024
c35c588
Revert some minor changes
vocksel Nov 2, 2024
1af8cd8
Bump to Storyteller 0.3.0
vocksel Nov 3, 2024
92f432c
Implicitly install packages on first build
vocksel Nov 3, 2024
38ca781
Fix giant gray box in story preview area
vocksel Nov 3, 2024
7e612d3
Remove unused hook
vocksel Nov 3, 2024
d42689d
Bump Storyteller to 0.4.0
vocksel Nov 3, 2024
a20077b
Revert a story change
vocksel Nov 3, 2024
fcd2400
Use Storyteller type sdirectly
vocksel Nov 3, 2024
bd6c8f2
Fix analysis errors
vocksel Nov 3, 2024
2a7486f
Merge remote-tracking branch 'origin/main' into storyteller
vocksel Nov 3, 2024
5834eba
WIP changes for getting controls working consistently
vocksel Nov 5, 2024
50e78f5
Install local Storyteller for convenience
vocksel Nov 6, 2024
e72d9f3
Install local ModuleLoader
vocksel Nov 6, 2024
5956994
Don't need the `build` arg
vocksel Nov 7, 2024
d860d4d
Add support to build to engine
vocksel Nov 7, 2024
d4b3142
Arg can be nil
vocksel Nov 7, 2024
2f6c388
Don't build the packages, just link
vocksel Nov 8, 2024
e686723
Output to the right place
vocksel Nov 8, 2024
75eb005
Add padding to StoryError
vocksel Nov 11, 2024
230f457
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Nov 11, 2024
4e967be
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Nov 18, 2024
247da48
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Nov 21, 2024
096b81e
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Dec 5, 2024
0c69b5f
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Dec 11, 2024
9f4c68f
Add Storyteller dependency
vocksel Dec 12, 2024
0d3d197
Fix storybooks not appearing in tree view
vocksel Dec 12, 2024
8e99070
Don't worry about ModuleLoader for now
vocksel Dec 12, 2024
91fc19b
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Dec 12, 2024
af73515
Allow the plugin to be constructed by a wrapper
vocksel Dec 12, 2024
ac95f13
Support reloading with the wrapper
vocksel Dec 12, 2024
8eba4a3
Fix Flipbook crashing when attempting to open to a story in a restric…
vocksel Dec 13, 2024
5f08f2b
Fix infinite loop
vocksel Dec 16, 2024
0d1acbd
Manually build Plugin instances to handle reloading better
vocksel Dec 16, 2024
471b80f
Full error logging with line numbers
vocksel Dec 16, 2024
2a460ca
Last opened story gets remembered again
vocksel Dec 16, 2024
e167805
Fix dropdown controls not changing visually
vocksel Dec 16, 2024
829b34c
Display unavailable storybooks
vocksel Dec 16, 2024
eb0230d
Only show unavailable storybooks if there are any
vocksel Dec 16, 2024
dc72958
Add a button to create the first storybook, story, and component
vocksel Dec 17, 2024
08329ff
Story pinning almost works, gonna come back to this later
vocksel Dec 17, 2024
88e2d8e
Sync onboarding storybook to filesystem
vocksel Dec 17, 2024
ffc379d
Watch Packages folder for changes
vocksel Dec 17, 2024
ed52153
Install ModuleLoader from disk
vocksel Dec 17, 2024
58dd60e
Only allow pinning stories and storybooks for right now
vocksel Dec 17, 2024
9136d16
Remove todo
vocksel Dec 17, 2024
0d135a2
Add new icons
vocksel Dec 17, 2024
de6a3cc
Redo icons without dpi scaling
vocksel Dec 17, 2024
078849f
Star icon for pinned nodes
vocksel Dec 17, 2024
8bdce5e
Remove another todo
vocksel Dec 17, 2024
6614d87
Adjust star colors
vocksel Dec 17, 2024
85e877b
Remove engine building step
vocksel Dec 18, 2024
4d1e48f
Fix stories spec
vocksel Dec 18, 2024
72a81ef
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Dec 18, 2024
6ee4084
Make sure community Flipbook doesn't crash
vocksel Dec 18, 2024
47b69fe
Add back Packages watching
vocksel Dec 18, 2024
0105111
Also install Storyteller to CodeSamples to handle analysis errors
vocksel Dec 18, 2024
305b41d
Remove `loader` prop. Storyteller handles this for us now
vocksel Dec 18, 2024
6c3538b
Couple small fixes
vocksel Dec 18, 2024
8e4b58e
Only read from disk once for pinned instances
vocksel Dec 18, 2024
e370b09
Use LocalStorageContext for useLastOpenedStory
vocksel Dec 18, 2024
4c76445
Fix dropdown controls when the user passes a dictionary
vocksel Dec 18, 2024
b1ae555
Render pages for unavialable storybooks
vocksel Dec 18, 2024
f58271e
Flexy sidebar
vocksel Dec 18, 2024
7185df8
Fix stories not toggling
vocksel Dec 18, 2024
dba0401
Show more comprehensive details for storybook errors
vocksel Dec 19, 2024
1d024a8
Reset the unavailable story between nodes
vocksel Dec 19, 2024
eb82624
Display a highlight when hovering a drag handle
vocksel Dec 19, 2024
3e391ca
FileSyncService doesn't exist but InternalSyncService does
vocksel Dec 19, 2024
5823ece
Consolidate modules for Roblox internal work
vocksel Dec 19, 2024
8298b56
Merge remote-tracking branch 'origin/main' into roblox-internal
vocksel Dec 19, 2024
cb79320
Fix LocalStorageContext types and use a constant storage key
vocksel Dec 19, 2024
d3e143a
Fix unnecessary re-renders for pinned instances
vocksel Dec 19, 2024
914535b
When no story has been opened, first click won't actually open it
vocksel Dec 19, 2024
d130e11
Add untested code block highlighting
vocksel Dec 19, 2024
30f2f1a
Syntax highlighting works!
vocksel Dec 19, 2024
4f91e52
Better errors for stories
vocksel Dec 20, 2024
984e87e
Make sure we have access to services
vocksel Dec 20, 2024
72ed253
I think I was mistaken about the second case
vocksel Dec 20, 2024
6caae9e
Use a new function pair for saving and loading paths
vocksel Dec 20, 2024
d76e4aa
Add LuauPolyfill to fix build issues
vocksel Dec 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .lune/build.luau
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
local process = require("@lune/process")

local clean = require("./lib/clean")
local compile = require("./lib/compile")
local constants = require("./lib/constants")
local getPluginsPath = require("./lib/getPluginsPath")
local parseArgs = require("./lib/parseArgs")
local process = require("@lune/process")
local run = require("./lib/run")
local watch = require("./lib/watcher/watch")

Expand All @@ -29,6 +30,7 @@ if args.watch then
filePatterns = {
"src/.*%.luau",
"example/.*%.luau",
"Packages/.*%.luau",
},
onChanged = build,
})
Expand Down
2 changes: 1 addition & 1 deletion .lune/lib/parseArgs.luau
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local FLAG_PATTERN = "%-%-(%w+)"
local FLAG_ALL_IN_ONE_PATTERN = `{FLAG_PATTERN}=(%w+)`

local function parseArgs(args: { string })
local parsedArgs: { [string]: string | boolean | number } = {}
local parsedArgs: { [string]: string | boolean | number | nil } = {}

local skipNextToken = false

Expand Down
22 changes: 22 additions & 0 deletions .lune/wally-install.luau
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
local process = require("@lune/process")

local run = require("./lib/run")

local function installPackageFromDisk(packageName: string, packagePath: string, runOptions: { [string]: any }?)
local homePath = process.env.HOME
assert(homePath, "no $HOME env var")

local absPackagePath = run("realpath", { packagePath })

run("rm", { `Packages/{packageName}.lua` }, runOptions)
run("ln", { "-s", `{absPackagePath}/dist`, `Packages/{packageName}` }, runOptions)
end

do
run("wally", { "install" })

installPackageFromDisk("Storyteller", "../storyteller")
-- ModuleLoader is a dependency of Storyteller, make sure to install both.
installPackageFromDisk("ModuleLoader", "../module-loader")

run("rojo", { "sourcemap", "build.project.json", "-o", "sourcemap.json" })
run("wally-package-types", { "--sourcemap", "sourcemap.json", "Packages" })
end
Expand All @@ -10,6 +27,11 @@ do
run("wally", { "install" }, {
cwd = "code-samples",
})

installPackageFromDisk("Storyteller", "../storyteller", {
cwd = "code-samples",
})

run("rojo", { "sourcemap", "default.project.json", "-o", "sourcemap.json" }, {
cwd = "code-samples",
})
Expand Down
Binary file added img/Alert.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/Star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/StarFilled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 94 additions & 0 deletions src/Common/CodeBlock.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
local Highlighter = require("@pkg/Highlighter")
local React = require("@pkg/React")
local Sift = require("@pkg/Sift")

local SelectableTextLabel = require("@root/Forms/SelectableTextLabel")
local nextLayoutOrder = require("@root/Common/nextLayoutOrder")
local useTheme = require("@root/Common/useTheme")

local useMemo = React.useMemo

local function getLineNumbers(str: string): string
return Sift.List.reduce(str:split("\n"), function(accumulator, _item, index)
return if index == 1 then tostring(index) else `{accumulator}\n{index}`
end, "")
end

export type Props = {
source: string,
sourceColor: Color3?,
layoutOrder: number?,
}

local function CodeBlock(props: Props)
local theme = useTheme()

local sourceColor = useMemo(function()
return if props.sourceColor then props.sourceColor else theme.text
end, { props.sourceColor })

local source = useMemo(function()
if props.sourceColor then
return props.source
else
return table.concat(
Highlighter.buildRichTextLines({
src = props.source,
}),
"\n"
)
end
end, { props.source })

return React.createElement("Frame", {
AutomaticSize = Enum.AutomaticSize.XY,
BackgroundTransparency = 0.5,
BorderSizePixel = 0,
BackgroundColor3 = theme.sidebar,
LayoutOrder = props.layoutOrder,
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
Padding = theme.padding,
}),

BorderRadius = React.createElement("UICorner", {
CornerRadius = theme.corner,
}),

Padding = React.createElement("UIPadding", {
PaddingTop = theme.padding,
PaddingRight = theme.padding,
PaddingBottom = theme.padding,
PaddingLeft = theme.padding,
}),

LineNumbers = React.createElement("TextLabel", {
LayoutOrder = nextLayoutOrder(),
AutomaticSize = Enum.AutomaticSize.XY,
Text = getLineNumbers(source),
TextSize = theme.textSize,
LineHeight = 1,
BackgroundTransparency = 1,
Font = Enum.Font.RobotoMono,
TextColor3 = theme.textFaded,
TextXAlignment = Enum.TextXAlignment.Right,
}),

SourceCode = React.createElement(SelectableTextLabel, {
RichText = true,
LayoutOrder = nextLayoutOrder(),
Size = UDim2.fromScale(1, 0),
AutomaticSize = Enum.AutomaticSize.Y,
Text = source,
TextColor3 = sourceColor,
TextSize = theme.textSize,
TextWrapped = false,
LineHeight = 1,
Font = Enum.Font.RobotoMono,
}),
})
end

return CodeBlock
4 changes: 4 additions & 0 deletions src/Common/ContextProviders.luau
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local React = require("@pkg/React")
local TreeView = require("@root/TreeView")

local ContextStack = require("@root/Common/ContextStack")
local LocalStorageContext = require("@root/Plugin/LocalStorageContext")
local NavigationContext = require("@root/Navigation/NavigationContext")
local PluginContext = require("@root/Plugin/PluginContext")
local SettingsContext = require("@root/UserSettings/SettingsContext")
Expand All @@ -20,6 +21,9 @@ local function ContextProviders(props: Props)
React.createElement(NavigationContext.Provider, {
defaultScreen = "Home",
}),
React.createElement(LocalStorageContext.Provider, {
storageKey = "FlipbookInternal",
}),
React.createElement(SettingsContext.Provider),
React.createElement(TreeView.TreeViewProvider),
},
Expand Down
64 changes: 0 additions & 64 deletions src/Common/getInstanceFromFullName.luau

This file was deleted.

44 changes: 44 additions & 0 deletions src/Common/getInstanceFromPath.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
--[[
Gets an instance based off the result of getInstancePath.

The reason we use this over GetFullName is because it uses a dot (.) as the
path separator which makes it difficult to disambiguate instances stories
and test files (Foo.story and Foo.spec, respectively)

Returns `nil` if the instance is outside the DataModel or otherwise cannot
be found.
]]

local tryGetService = require("@root/RobloxInternal/tryGetService")

local PATH_SEPERATOR = "/"

local function getInstanceFromPath(path: string): Instance?
local parts = path:split(PATH_SEPERATOR)
local serviceName = parts[1]

if serviceName then
-- This function only works for instances in the DataModel. As such, the
-- first part of the path will always be a service, so if we can't find
-- one we exit out and return nil
local current = tryGetService(serviceName)

if current then
for i = 2, #parts do
local found = current:FindFirstChild(parts[i])

if found then
current = found
else
return nil
end
end

return current
end
end

return nil
end

return getInstanceFromPath
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
local JestGlobals = require("@pkg/JestGlobals")
local newFolder = require("@root/Testing/newFolder")

local getInstanceFromFullName = require("./getInstanceFromFullName")
local getInstanceFromPath = require("./getInstanceFromPath")
local getInstancePath = require("./getInstancePath")

local expect = JestGlobals.expect
local test = JestGlobals.test
Expand All @@ -18,7 +19,7 @@ afterEach(function()
end)

test("gets services", function()
local path = getInstanceFromFullName("ReplicatedStorage")
local path = getInstanceFromPath("ReplicatedStorage")
expect(path).toBe(ReplicatedStorage)
end)

Expand All @@ -32,8 +33,7 @@ test("works on nested instances", function()
})
folder.Parent = ReplicatedStorage

local path = getInstanceFromFullName(module:GetFullName())
expect(path).toBe(module)
expect(getInstanceFromPath(getInstancePath(module))).toBe(module)
end)

test("works with spec files", function()
Expand All @@ -46,7 +46,7 @@ test("works with spec files", function()
})
folder.Parent = ReplicatedStorage

local path = getInstanceFromFullName(module:GetFullName())
local path = getInstanceFromPath(module:GetFullName())
expect(path).toBe(module)
end)

Expand All @@ -61,18 +61,17 @@ test("finds spec files BEFORE the module it is associated with", function()
})
folder.Parent = ReplicatedStorage

local path = getInstanceFromFullName(module:GetFullName())
expect(path).toBe(module)
expect(getInstanceFromPath(getInstancePath(module))).toBe(module)
end)

test("returns nil if the first part of the path is not a service", function()
expect(getInstanceFromFullName("Part")).toBeUndefined()
expect(getInstanceFromPath("Part")).toBeUndefined()
end)

test("returns nil if the path does not exist", function()
expect(getInstanceFromFullName("Foo.story")).toBeUndefined()
expect(getInstanceFromFullName("Path.To.Foo.story")).toBeUndefined()
expect(getInstanceFromFullName("ReplicatedStorage.Foo.Bar.Baz")).toBeUndefined()
expect(getInstanceFromFullName("ReplicatedStorage.Sample.story")).toBeUndefined()
expect(getInstanceFromFullName("ReplicatedStorage.Sample.spec")).toBeUndefined()
expect(getInstanceFromPath("Foo.story")).toBeUndefined()
expect(getInstanceFromPath("Path/To/Foo.story")).toBeUndefined()
expect(getInstanceFromPath("ReplicatedStorage/Foo/Bar/Baz")).toBeUndefined()
expect(getInstanceFromPath("ReplicatedStorage/Sample.story")).toBeUndefined()
expect(getInstanceFromPath("ReplicatedStorage/Sample.spec")).toBeUndefined()
end)
18 changes: 18 additions & 0 deletions src/Common/getInstancePath.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local PATH_SEPARATOR = "/"

local function getInstancePath(instance: Instance, pathSeparator: string?): string
pathSeparator = if pathSeparator then pathSeparator else PATH_SEPARATOR
assert(pathSeparator, "Luau")

local path = {}
local current = instance

while current and current.Parent ~= nil do
table.insert(path, 1, current.Name)
current = current.Parent
end

return table.concat(path, "/")
end

return getInstancePath
Loading
Loading