userfile
The files of the person at the screen, both ways. pick opens their own file picker (native OS dialog / Android / web browser) and brings the chosen file(s) into the engine; save puts a file the engine holds onto their device (a download in the browser).
require("@builtin::modules.api.engine.userfile") -- userfile (also available as global 'userfile')
The userfile namespace: 9 functions.
globals/userfile/pick
userfile.pick(opts: PickOpts?) -> PickResult
Open the user's system file picker and bring the chosen file(s)
into the engine. Yields until the user finishes (call from a coroutine /
task, like any task.await) and returns
{ cancelled, files = {{ name, mime, size, bytes?, vfsPath? }} }.
Without writeTo each file carries bytes (a binary-safe string);
with writeTo each carries vfsPath (read it with vfs.read).
Cancelling returns { cancelled = true, files = {} }; a genuine failure
(e.g. a lost browser user-activation gesture) raises an error.
Parameters
optsPickOpts(optional) — Picker options (optional): multiple, folder, title, filters, writeTo.
Returns PickResult — The decoded result table.
local r = userfile.pick({ filters = {{ name = "Images", extensions = {"png","jpg"} }} })
if not r.cancelled then vfs.write("/source/textures/wall.png", assert(r.files[1].bytes)) end
globals/userfile/pickFolder
userfile.pickFolder(opts: PickOpts?) -> PickResult
Convenience for userfile.pick({ folder = true }) — pick a whole
directory tree. Yields until the user finishes and returns the same
result table as pick. On the web this degrades to a multi-file selection.
Parameters
optsPickOpts(optional) — Picker options (optional);folderis forced true.
Returns PickResult — The decoded result table.
local r = userfile.pickFolder({ writeTo = "/source/imported/" })
globals/userfile/save
userfile.save(path: string, opts: SaveOpts?) -> (boolean, string, SaveReport?)
Save a file the engine holds onto the device of the person at the
screen: the way a person keeps a recording, a capture or an export.
The person decides where it lands. In the browser the file downloads at
once when the save rides a click or key press of the person at the
screen (call it from a button's click handler), and otherwise the page
shows them an offer they save with a click of their own, since a
browser starts a download only for a person who just acted. A native
engine opens the host's save dialog in their downloads folder, with the
name filled in, and writes the file where they choose. An engine with no
window has no person at its screen and refuses. The call waits for the
first answer, so run it where it can yield (task.spawn from a
callback). Returns true, a sentence saying where the file went, and the
report { state, detail, name, size, path } whose state
is "saved" (the file is on the device) or "offered" (it is in front
of the person, as the page's offer or the save dialog, and lands when
they act); or false and the reason the save was refused: no file at the
path, a folder, a name with a folder in it, no window, or a file too
large for the browser to hold (which the page also shows).
Parameters
pathstring— VFS path of the file to save.optsSaveOpts(optional) —{ name }, the file name the device gets (the file's own when omitted).
Returns (boolean, string, SaveReport?) — Whether the device took the file. Where it went, or why it was refused. The save report, when the device took the file.
local ok, where = userfile.save("/zero/runtime/recordings/take.mp4")
local ok, where, report = userfile.save("/zero/source/export/level.json", { name = "level-3.json" })
modules/userfile/pick
pick(opts: PickOpts?): PickResult
Open the user's system file picker and bring the chosen file(s)
into the engine. Yields until the user finishes (call from a coroutine /
task, like any task.await) and returns
{ cancelled, files = {{ name, mime, size, bytes?, vfsPath? }} }.
Without writeTo each file carries bytes (a binary-safe string);
with writeTo each carries vfsPath (read it with vfs.read).
Cancelling returns { cancelled = true, files = {} }; a genuine failure
(e.g. a lost browser user-activation gesture) raises an error.
Parameters
optsPickOpts?(optional) — Picker options (optional): multiple, folder, title, filters, writeTo.
local r = userfile.pick({ filters = {{ name = "Images", extensions = {"png","jpg"} }} })
if not r.cancelled then vfs.write("/source/textures/wall.png", assert(r.files[1].bytes)) end
modules/userfile/pickFolder
pickFolder(opts: PickOpts?): PickResult
Convenience for userfile.pick({ folder = true }) — pick a whole
directory tree. Yields until the user finishes and returns the same
result table as pick. On the web this degrades to a multi-file selection.
Parameters
optsPickOpts?(optional) — Picker options (optional);folderis forced true.
local r = userfile.pickFolder({ writeTo = "/source/imported/" })
modules/userfile/save
save(path: string, opts: SaveOpts?): (boolean, string, SaveReport?)
Save a file the engine holds onto the device of the person at the
screen: the way a person keeps a recording, a capture or an export.
The person decides where it lands. In the browser the file downloads at
once when the save rides a click or key press of the person at the
screen (call it from a button's click handler), and otherwise the page
shows them an offer they save with a click of their own, since a
browser starts a download only for a person who just acted. A native
engine opens the host's save dialog in their downloads folder, with the
name filled in, and writes the file where they choose. An engine with no
window has no person at its screen and refuses. The call waits for the
first answer, so run it where it can yield (task.spawn from a
callback). Returns true, a sentence saying where the file went, and the
report { state, detail, name, size, path } whose state
is "saved" (the file is on the device) or "offered" (it is in front
of the person, as the page's offer or the save dialog, and lands when
they act); or false and the reason the save was refused: no file at the
path, a folder, a name with a folder in it, no window, or a file too
large for the browser to hold (which the page also shows).
Parameters
pathstring— VFS path of the file to save.optsSaveOpts?(optional) —{ name }, the file name the device gets (the file's own when omitted).
local ok, where = userfile.save("/zero/runtime/recordings/take.mp4")
local ok, where, report = userfile.save("/zero/source/export/level.json", { name = "level-3.json" })
typed/builtin//modules/api/engine/userfile/userfile/pick
userfile.pick(opts: PickOpts?) -> PickResult
Open the user's system file picker and bring the chosen file(s)
into the engine. Yields until the user finishes (call from a coroutine /
task, like any task.await) and returns
{ cancelled, files = {{ name, mime, size, bytes?, vfsPath? }} }.
Without writeTo each file carries bytes (a binary-safe string);
with writeTo each carries vfsPath (read it with vfs.read).
Cancelling returns { cancelled = true, files = {} }; a genuine failure
(e.g. a lost browser user-activation gesture) raises an error.
Parameters
optsPickOpts(optional) — Picker options (optional): multiple, folder, title, filters, writeTo.
Returns PickResult — The decoded result table.
local r = userfile.pick({ filters = {{ name = "Images", extensions = {"png","jpg"} }} })
if not r.cancelled then vfs.write("/source/textures/wall.png", assert(r.files[1].bytes)) end
typed/builtin//modules/api/engine/userfile/userfile/pickFolder
userfile.pickFolder(opts: PickOpts?) -> PickResult
Convenience for userfile.pick({ folder = true }) — pick a whole
directory tree. Yields until the user finishes and returns the same
result table as pick. On the web this degrades to a multi-file selection.
Parameters
optsPickOpts(optional) — Picker options (optional);folderis forced true.
Returns PickResult — The decoded result table.
local r = userfile.pickFolder({ writeTo = "/source/imported/" })
typed/builtin//modules/api/engine/userfile/userfile/save
userfile.save(path: string, opts: SaveOpts?) -> (boolean, string, SaveReport?)
Save a file the engine holds onto the device of the person at the
screen: the way a person keeps a recording, a capture or an export.
The person decides where it lands. In the browser the file downloads at
once when the save rides a click or key press of the person at the
screen (call it from a button's click handler), and otherwise the page
shows them an offer they save with a click of their own, since a
browser starts a download only for a person who just acted. A native
engine opens the host's save dialog in their downloads folder, with the
name filled in, and writes the file where they choose. An engine with no
window has no person at its screen and refuses. The call waits for the
first answer, so run it where it can yield (task.spawn from a
callback). Returns true, a sentence saying where the file went, and the
report { state, detail, name, size, path } whose state
is "saved" (the file is on the device) or "offered" (it is in front
of the person, as the page's offer or the save dialog, and lands when
they act); or false and the reason the save was refused: no file at the
path, a folder, a name with a folder in it, no window, or a file too
large for the browser to hold (which the page also shows).
Parameters
pathstring— VFS path of the file to save.optsSaveOpts(optional) —{ name }, the file name the device gets (the file's own when omitted).
Returns (boolean, string, SaveReport?) — Whether the device took the file. Where it went, or why it was refused. The save report, when the device took the file.
local ok, where = userfile.save("/zero/runtime/recordings/take.mp4")
local ok, where, report = userfile.save("/zero/source/export/level.json", { name = "level-3.json" })