1 line
No EOL
50 KiB
Text
1 line
No EOL
50 KiB
Text
{"protected":false,"mode":"f","metadata":{"name":"lazybox","author":"Luna Magdalena :3","version":[1],"dependencies":[],"paths":{"lazybox.js":"%B/lazybox.js","links/bg":"%B/bg","links/cat":"%B/cat","links/clear":"%B/clear","links/cp":"%B/cp","links/echo":"%B/echo","links/env":"%B/env","links/info":"%B/info","links/kill":"%B/kill","links/ln":"%B/ln","links/loader":"%B/loader","links/ls":"%B/ls","links/meta":"%B/meta","links/mkdir":"%B/mkdir","links/mv":"%B/mv","links/persist":"%B/persist","links/plist":"%B/plist","links/programs":"%B/programs","links/rm":"%B/rm","links/scroll":"%B/scroll","links/shutdown":"%B/shutdown","links/slog":"%B/slog","links/touch":"%B/touch","links/woman":"%B/woman","lazybox/bg":"%D/bg","lazybox/cat":"%D/cat","lazybox/clear":"%D/clear","lazybox/cp":"%D/cp","lazybox/echo":"%D/echo","lazybox/env":"%D/env","lazybox/info":"%D/info","lazybox/kill":"%D/kill","lazybox/ln":"%D/ln","lazybox/loader":"%D/loader","lazybox/ls":"%D/ls","lazybox/meta":"%D/meta","lazybox/mkdir":"%D/mkdir","lazybox/mv":"%D/mv","lazybox/persist":"%D/persist","lazybox/plist":"%D/plist","lazybox/programs":"%D/programs","lazybox/rm":"%D/rm","lazybox/scroll":"%D/scroll","lazybox/shutdown":"%D/shutdown","lazybox/slog":"%D/slog","lazybox/touch":"%D/touch","lazybox/woman":"%D/woman","lazybox/woman.template":"%D/woman.template"}},"content":"{\"links\":{\"protected\":false,\"metadata\":{},\"mode\":\"d\",\"content\":{\"bg\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"cat\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"clear\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"cp\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"echo\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"env\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"info\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"kill\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"ln\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"loader\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"ls\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"meta\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"mkdir\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"mv\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"persist\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"plist\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"programs\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"rm\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"scroll\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"shutdown\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"slog\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"touch\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"woman\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"},\"yarn\":{\"protected\":true,\"metadata\":{},\"mode\":\"l\",\"content\":\"./lazybox.js\"}}},\"lazybox\":{\"protected\":false,\"metadata\":{},\"mode\":\"d\",\"content\":{\"bg\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]bg\\u0011[s7000]\\n\\nbg is a utility for starting programs in the background\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tbg \\u0011[s6040]program\\u0011[s7000] [\\u0011[sE000]arguments\\u0011[s7000]]\"},\"cat\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]cat\\u0011[s7000]\\n\\ncat concatenates files, outputting their content to the next program or displaying it on screen\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tcat \\u0011[s6040]file\\u0011[s7000] [\\u0011[sE000]additional-files\\u0011[s7000]]\"},\"clear\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]clear\\u0011[s7000]\\n\\nclears the screen\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tclear\"},\"cp\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]cp\\u0011[s7000]\\n\\n\\t\\u0011[s4000]mv\\trm\\tln\\tmkdir\\ttouch\\u0011[s7000]\\n\\nattempts to copy the given node to the given destination\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tcp \\u0011[s6040]node\\u0011[s7000] \\u0011[s6040]destination\\u0011[s7000]\"},\"echo\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]echo\\u0011[s7000]\\n\\ndisplays or outputs the text content passed to it with a pipe or as command line arguments\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\techo [\\u0011[sE000]text\\u0011[s7000]]\"},\"env\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]env\\u0011[s7000]\\n\\nutility for manipulating environment variables\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tenv \\u0011[s6040]subcommand\\u0011[s7000] [\\u0011[sE000]arguments\\u0011[s7000]]\\n\\n\\u0011[s4020]Subcommands:\\u0011[s7000]\\n\\t\\u0011[s6010]get \\u0011[s2040]variable\\u0011[s7000]\\n\\t\\tprints the value of the given variable\\n\\t\\u0011[s6010]set \\u0011[s2040]variable\\u0011[s7000]\\n\\t\\tinteractively sets the value of the given variable. values are JSON parsed\\n\\t\\u0011[s6010]list\\u0011[s7000]\\n\\t\\tdisplays a list of all currently set environment variables and their values\\n\\t\\u0011[s6010]save \\u0011[s2040]file\\u0011[s7000]\\n\\t\\tattempts to save all current environment variables and their values to the given file\\n\\t\\u0011[s6010]load \\u0011[s2040]file\\u0011[s7000]\\n\\t\\tattempts to load environment variable values from the given file. file content must be a JSON object with each key being the name of a variable, as created by \\u0011[sE800] env save \\u0011[s7000]\"},\"info\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]info\\u0011[s7000]\\n\\ndisplays basic system info\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tinfo\"},\"kill\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]kill\\u0011[s7000]\\n\\n\\t\\u0011[s4000]plist\\u0011[s7000]\\n\\nkill quits the program with the provided id. running programs and their ids can be found with the \\u0011[sA000]plist\\u0011[s7000] utility\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tkill \\u0011[s6040]id\\u0011[s7000]\"},\"ln\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]ln\\u0011[s7000]\\n\\n\\t\\u0011[s4000]mv\\tcp\\trm\\tmkdir\\ttouch\\u0011[s7000]\\n\\nattempts to create a link from \\u0011[sA000]link-path\\u0011[s7000] to \\u0011[sA000]target\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tln \\u0011[s6040]target\\u0011[s7000] \\u0011[s6040]link-path\\u0011[s7000]\"},\"loader\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]*loader\\u0011[s7000]\\n\\n*loader is a file transfer utility. it can download and upload nodes between itty and the underlying device. it can also download nodes over https, provided CORS allows it\\n\\nnodes must be valid itty nodes. these can be created and extracted from outside itty by using the \\u0011[sA000]thread\\u0011[s7000] utility found in the itty repo\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tloader \\u0011[s6040]subcommand\\u0011[s7000] \\u0011[s6040]arguments\\u0011[s7000]\\n\\n\\u0011[s4020]Subcommands:\\u0011[s7000]\\n\\t\\u0011[s6010]download \\u0011[s2040]path\\u0011[s7000]\\n\\t\\tdownloads the node at \\u0011[sA000]path\\u0011[s7000] onto the device running itty\\n\\t\\u0011[s6010]upload \\u0011[s2040]path\\u0011[s7000]\\n\\t\\tdisplays a file picker and uploads the provided node, attempting to load it at \\u0011[sA000]path\\u0011[s7000]\\n\\t\\u0011[s6010]fetch \\u0011[s2040]path\\u0011[s7000] \\u0011[s2040]url\\u0011[s7000]\\n\\t\\tattempts to download the node at \\u0011[sA000]url\\u0011[s7000], and then attempts to load it at \\u0011[sA000]path\\u0011[s7000]\"},\"ls\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]ls\\u0011[s7000]\\n\\nls displays the contents of the given directory. if no directory is given, it displays the contents of the current one instead\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tls [\\u0011[sE000]flags\\u0011[s7000]] [\\u0011[sE000]directory\\u0011[s7000]]\\n\\n\\u0011[s4020]Flags:\\u0011[s7000]\\n\\t\\u0011[s3000]-a\\u0011[s7000] display hidden nodes\\n\\t\\u0011[s3000]-l\\u0011[s7000] use a list view\"},\"meta\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]meta\\u0011[s7000]\\n\\nmeta is a utility to manipulate file metadata\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tmeta \\u0011[s6040]subcommand\\u0011[s7000] \\u0011[s6040]arguments\\u0011[s7000]\\n\\n\\u0011[s4020]Subcommands:\\u0011[s7000]\\n\\t\\u0011[s6010]get \\u0011[s2040]file\\u0011[s7000] \\u0011[s2040]field\\u0011[s7000]\\n\\t\\tprints the value of \\u0011[sA000]field\\u0011[s7000] in \\u0011[sA000]file\\u0011[s7000]'s metadata\\n\\t\\u0011[s6010]set \\u0011[s2040]file\\u0011[s7000] \\u0011[s2040]field\\u0011[s7000]\\n\\t\\tattempts to interactively set the value of \\u0011[sA000]field\\u0011[s7000] in \\u0011[sA000]file\\u0011[s7000]'s metadata. values are JSON parsed\\n \\u0011[s6010]list \\u0011[s2040]file\\u0011[s7000]\\n\\t\\tdisplays a list of all fields and their values in \\u0011[sA000]file\\u0011[s7000]'s metadata\"},\"mkdir\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]mkdir\\u0011[s7000]\\n\\n\\t\\u0011[s4000]mv\\tcp\\trm\\tln\\ttouch\\u0011[s7000]\\n\\nattempts to create a directory at the given \\u0011[sA000]path\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tmkdir \\u0011[s6040]path\\u0011[s7000]\"},\"mv\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]mv\\u0011[s7000]\\n\\n\\t\\u0011[s4000]cp\\trm\\tln\\tmkdir\\ttouch\\u0011[s7000]\\n\\nattempts to move the node at \\u0011[sA000]origin\\u0011[s7000] to \\u0011[sA000]destination\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tmv \\u0011[s6040]origin\\u0011[s7000] \\u0011[s6040]destination\\u0011[s7000]\"},\"persist\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]persist\\u0011[s7000]\\n\\npersist is a utility to work with filesystem persistence\\n\\na positive persistence level means the given path and its children will be saved in persistent storage. a negative level means the given path and its children will never be saved in persistent storage\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tpersist \\u0011[s6040]subcommand\\u0011[s7000] \\u0011[s6040]arguments\\u0011[s7000]\\n\\n\\u0011[s4020]Subcommands:\\u0011[s7000]\\n\\t\\u0011[s6010]get \\u0011[s2040]path\\u0011[s7000]\\n\\t\\tget the persistence level of the given path\\n\\t\\u0011[s6010]set \\u0011[s2040]path\\u0011[s7000] \\u0011[s2040]level\\u0011[s7000]\\n\\t\\tset the persistence level of the given path to the provided level\\n\\t\\u0011[s6010]list\\u0011[s7000]\\n\\t\\tdisplay a list of all paths with non-zero persistence levels\\n\\t\\u0011[s6010]backup\\u0011[s7000]\\n\\t\\tcreate a backup of persistent storage and download it\\n\\t\\u0011[s6010]restore\\u0011[s7000]\\n\\t\\tdisplay a file picker and attempt to restore the uploaded persistent storage backup\\n\\t\\u0011[s6010]save\\u0011[s7000]\\n\\t\\ttrigger a filesystem save\"},\"plist\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]plist\\u0011[s7000]\\n\\n\\t\\u0011[s4000]kill\\u0011[s7000]\\n\\nplist will output a list of running programs, with each program on its own line\\n\\nthe number in square brackets is the program's id, which can be used to quit it with \\u0011[sA000]kill\\u0011[s7000]. the number in parentheses is the program's unique id. after these, follows the program's name\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tplist\"},\"programs\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]programs\\u0011[s7000]\\n\\ndisplays a list of all programs found in the \\u0011[sA000]path\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tprograms\"},\"rm\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]rm\\u0011[s7000]\\n\\n\\t\\u0011[s4000]mv\\tcp\\tln\\tmkdir\\ttouch\\u0011[s7000]\\n\\nattempts to remove the given node\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\trm \\u0011[s6040]node\\u0011[s7000]\"},\"scroll\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]scroll\\u0011[s7000]\\n\\nscroll will display any text piped to it, allowing the user to scroll the text to be able to read all its contents\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tscroll\\n\\n\\u0011[s5040]Binds\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s6020]Escape\\u0011[s7000] or \\u0011[s6020]q\\u0011[s7000]: quits the program\\n\\u0011[sC000]- \\u0011[s6020]Arrow up\\u0011[s7000] or \\u0011[s6020]k\\u0011[s7000]: scrolls up\\n\\u0011[sC000]- \\u0011[s6020]Arrow down\\u0011[s7000] or \\u0011[s6020]j\\u0011[s7000]: scrolls down\"},\"shutdown\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]shutdown\\u0011[s7000]\\n\\nshuts itty down. if shutdown functionality is disabled, restarts itty instead. this saves the filesystem and then reloads it\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tshutdown\"},\"slog\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]slog\\u0011[s7000]\\n\\nslog is a simple log utility. when called, it will parse text piped to it as JSON and interpret it as an error. if the error level is 0, it is logged as INFO. if the error level is non-zero, it is logged and also output to the developer console\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tslog\\n\\n\\u0011[s5040]Files\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s2040]/itty/slog\\u0011[s7000]: the log file slog writes to\"},\"touch\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]touch\\u0011[s7000]\\n\\n\\t\\u0011[s4000]mv\\tcp\\trm\\tln\\tmkdir\\u0011[s7000]\\n\\nif there is no node at the given \\u0011[sA000]path\\u0011[s7000], attempts to create an empty file\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\ttouch \\u0011[s6040]path\\u0011[s7000]\"},\"woman\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]woman\\u0011[s7000]\\n\\n\\t\\u0011[s4000]woman.template\\u0011[s7000]\\n\\nwoman is a simple utility for displaying documentation provided by itty packages\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\twoman \\u0011[s6040]page\\u0011[s7000]\\n\\n\\u0011[s5040]Binds\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s6020]Escape\\u0011[s7000] or \\u0011[s6020]q\\u0011[s7000]: quits the scroll view\\n\\u0011[sC000]- \\u0011[s6020]Arrow up\\u0011[s7000] or \\u0011[s6020]k\\u0011[s7000]: scrolls up\\n\\u0011[sC000]- \\u0011[s6020]Arrow down\\u0011[s7000] or \\u0011[s6020]j\\u0011[s7000]: scrolls down\\n\\n\\u0011[s5040]Environment\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s6020]dir_docs\\u0011[s7000] (\\u0011[sE000]string\\u0011[s7000]): the directory to search for documentation\\n\\n\\u0011[s5040]Woman page guidelines\\u0011[s7000]\\nwoman pages are plaintext with itty escape sequences used to add styling to text\\n\\nwoman pages should, for the most part, follow certain guidelines\\n\\nthese guidelines may be broken, if the writer considers this will make their documentation easier to understand\\n\\nto better understand the structure and style of woman pages, please see the page \\u0011[sA000]woman.template\\n\\n\\u0011[sC000]- \\u0011[s7000]they should be friendly and easy to understand\\n\\u0011[sC000]- \\u0011[s7000]they should all use the same style and formatting\\n\\u0011[sC000]- \\u0011[s7000]they shouldn't be too long. if a package needs extensive documentation, it can be split between multiple pages\\n\\u0011[sC000]- \\u0011[s7000]if a package has documentation, there must be a page by the most obvious name for the package\\n\\u0011[sC000]- \\u0011[s7000]additional page names must consist of this name, a period, and any name the writer desires for the page\\n\\u0011[sC000]- \\u0011[s7000]they should look good on displays of most sizes\\n\\u0011[sC000]- \\u0011[s7000]normal text should have style \\u0011[sA000]7000\\n\\u0011[sC000]- \\u0011[s7000]decorations should have style \\u0011[sA000]C000\\n\\u0011[sC000]- \\u0011[s7000]elements of a list should have style \\u0011[sA000]6020\\n\\u0011[sC000]- \\u0011[s7000]file paths should have style \\u0011[sA000]2040\\n\\u0011[sC000]- \\u0011[s7000]titles should have style \\u0011[sA000]5040\\n\\u0011[sC000]- \\u0011[s7000]subtitles should have style \\u0011[sA000]4020\\n\\u0011[sC000]- \\u0011[s7000]verbatim command and code examples should use style \\u0011[sA000]E800\\n\\u0011[sC000]- \\u0011[s7000]highlighted text should use styles \\u0011[sA000]A000\\u0011[s7000] or \\u0011[sA000]A020\\n\\n\\u0011[s4020]Structure:\\u0011[s7000]\\nwoman pages have the following sections, in this order:\\n\\u0011[sC000]- \\u0011[s6000]Title\\u0011[s7000]:\\n the title of the woman page, with style D050. mandatory\\n\\u0011[sC000]- \\u0011[s6000]Suggested pages\\u0011[s7000]:\\n other woman pages that may be of interest to the reader. starts with a tab, with additional tabs between pages. optional\\n\\u0011[sC000]- \\u0011[s6000]Introduction\\u0011[s7000]:\\n a little introduction about the program, package, or page. mandatory\\n\\u0011[sC000]- \\u0011[s6000]Usage\\u0011[s7000]:\\n contains a brief usage explanation, example command, and lists of subcommands, flags, and other arguments. mandatory if it applies\\n\\u0011[sC000]- \\u0011[s6000]Binds\\u0011[s7000]:\\n a list of keybinds if the program is interactive. mandatory if it applies\\n\\u0011[sC000]- \\u0011[s6000]Environment\\u0011[s7000]:\\n a list of environment variables the program reads or uses, and what they are for. mandatory if it applies\\n\\u0011[sC000]- \\u0011[s6000]Configuration\\u0011[s7000]:\\n an explanation of how to configure the program. mandatory if it applies\\n\\u0011[sC000]- \\u0011[s6000]Files\\u0011[s7000]:\\n a list of files the program writes or reads, and what they are for. mandatory if it applies\\n\\u0011[sC000]- \\u0011[s6000]Notes\\u0011[s7000]:\\n additional nodes regarding the page. optional\"},\"woman.template\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]Example Woman Page\\u0011[s7000]\\n\\n\\t\\u0011[s4000]woman\\u0011[s7000]\\n\\na template of a woman page, showing all sections and styles\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tcommand \\u0011[s6040]mandatory\\u0011[s7000] [\\u0011[sE000]optional\\u0011[s7000]]\\n\\n\\u0011[s4020]Subcommands:\\u0011[s7000]\\n\\t\\u0011[s6010]subcommand \\u0011[s2040]mandatory-argument\\u0011[s7000] [\\u0011[sE000]optional-argument\\u0011[s7000]]\\n\\t\\texplanation of what the subcommand does\\n\\t\\u0011[s6010]another-subcommand\\u0011[s7000]\\n\\t\\tanother explanation\\n\\n\\u0011[s4020]Flags:\\u0011[s7000]\\n\\t\\u0011[s3000]-f\\u0011[s7000] explanation of what the flag does\\n\\t\\u0011[s3000]-l\\u0011[s7000] another explanation\\n\\n\\u0011[s5040]Binds\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s6020]key\\u0011[s7000]: what the keybind does\\n\\n\\u0011[s5040]Custom\\u0011[s7000]\\nthis is a custom section of the page, it can contain whatever you want\\n\\nthis here is \\u0011[sA000]highlighted\\u0011[s7000]. so is \\u0011[sA020]this\\u0011[s7000].\\n\\n\\u0011[s4020]A subtitle:\\u0011[s7000]\\nhere is some text that is \\u0011[sE800] supposed to be code \\u0011[s7000]\\n\\n\\u0011[s5040]Environment\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s6020]environment_variable\\u0011[s7000] (\\u0011[sE000]type\\u0011[s7000]): what the variable is used for\\n\\n\\u0011[s5040]Configuration\\u0011[s7000]\\nexplanation of how to configure the program\\n\\u0011[sC000]- \\u0011[s6020]value\\u0011[s7000]: what the value does\\n\\n\\u0011[s5040]Files\\u0011[s7000]\\n\\u0011[sC000]- \\u0011[s2040]/path/to/file\\u0011[s7000]: what the file is used for\\n\\n\\u0011[s5040]Notes\\u0011[s7000]\\nspecial notes regarding the woman page\"},\"yarn\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"\\u0011[sD050]yarn\\u0011[s7000]\\n\\nyarn is a utility for creating and extracting archives called \\u0011[sA000]yarnballs\\n\\n\\u0011[s5040]Usage\\u0011[s7000]\\n\\tyarn \\u0011[s6040]subcommand\\u0011[s7000] \\u0011[s6040]arguments\\u0011[s7000]\\n\\n\\u0011[s4020]Subcommands:\\u0011[s7000]\\n\\t\\u0011[s6010]spool \\u0011[s2040]yarnball\\u0011[s7000] \\u0011[s2040]files\\u0011[s7000]\\n\\t\\tattempts to create a new yarnball, containing all the provided files\\n\\t\\u0011[s6010]unspool \\u0011[s2040]yarnball\\u0011[s7000] \\u0011[s2040]destination\\u0011[s7000]\\n\\t\\tattempts to extract the provided yarnball to the given destination\\n\\t\\u0011[s6010]metadata-from-json \\u0011[s2040]yarnball\\u0011[s7000] \\u0011[s2040]json-file\\u0011[s7000]\\n\\t\\tattempts to set the yarnball's metadata from the given JSON file. yarnball metadata can also be manipulated with the \\u0011[sA000]meta\\u0011[s7000] util\\n\\n\\u0011[s5040]Yarnball format\\u0011[s7000]\\nyarnballs are normal files whose content is stringified JSON\\n\\nthis JSON contains multiple entries. in each entry, the key is the filename of the contained node, and the content is an itty filesystem node\"}}},\"lazybox.js\":{\"protected\":true,\"metadata\":{\"runner\":[]},\"mode\":\"f\",\"content\":\"\\nconst subprogram = program.name.replace(/\\\\.[^\\\\.]*$/, \\\"\\\").replace(/^.*\\\\//, \\\"\\\")\\n\\nswitch (subprogram) {\\n case \\\"bg\\\":\\n bg()\\n break\\n case \\\"cat\\\":\\n cat()\\n break\\n case \\\"clear\\\":\\n clear()\\n break\\n case \\\"cp\\\":\\n cp()\\n break\\n case \\\"echo\\\":\\n echo()\\n break\\n case \\\"env\\\":\\n await env()\\n break\\n case \\\"info\\\":\\n info()\\n break\\n case \\\"kill\\\":\\n kill()\\n break\\n case \\\"loader\\\":\\n await loader()\\n break\\n case \\\"ls\\\":\\n ls()\\n break\\n case \\\"meta\\\":\\n await meta()\\n break\\n case \\\"mkdir\\\":\\n mkdir()\\n break\\n case \\\"mv\\\":\\n mv()\\n break\\n case \\\"plist\\\":\\n plist()\\n break\\n case \\\"programs\\\":\\n programs()\\n break\\n case \\\"rm\\\":\\n rm()\\n break\\n case \\\"scroll\\\":\\n await scroll()\\n break\\n case \\\"shutdown\\\":\\n await shutdown()\\n break\\n case \\\"slog\\\":\\n slog()\\n break\\n case \\\"touch\\\":\\n touch()\\n break\\n case \\\"yarn\\\":\\n await yarn()\\n break\\n case \\\"ln\\\":\\n ln()\\n break\\n case \\\"woman\\\":\\n await woman()\\n break\\n case \\\"persist\\\":\\n await persist()\\n break\\n default:\\n quit()\\n}\\n\\n// programs are their own functions and defined here\\n\\nfunction bg() {\\n if (!args[0]) {\\n display.print(\\\"No program specified\\\", 0x1000)\\n quit()\\n }\\n\\n const programName = args[0]\\n args.splice(0, 1)\\n\\n try {\\n exec.runProgram(programName, args)\\n } catch (e) {\\n if (e[0]) {\\n io.log(e)\\n }\\n }\\n\\n quit()\\n}\\n\\nfunction cat() {\\n program.autoOutput = false\\n\\n const baseStyle = itty.baseStyle\\n\\n for (let a of args) {\\n let file\\n try {\\n file = io.open(a)\\n } catch (e) {\\n display.print(\\\"Cannot open file\\\", 0x9000)\\n }\\n if (file) {\\n const lines = file.lines()\\n for (let line of lines) {\\n io.output(\\\"\\\\n\\\" + line)\\n display.print(line, itty.baseStyle, false)\\n }\\n }\\n }\\n \\n itty.baseStyle = baseStyle\\n\\n quit()\\n}\\n\\nfunction clear() {\\n program.enterFullscreen()\\n display.clear()\\n quit()\\n}\\n\\nfunction cp() {\\n if (!args[0] && !args[1]) {\\n itty.display.print(\\\"Not enough arguments\\\", 0xf000)\\n quit()\\n }\\n\\n try {\\n itty.fs.copyNode(args[0], args[1])\\n } catch (e) {\\n itty.display.print(e[1] || \\\"Error copying node\\\", 0x9000)\\n }\\n\\n quit()\\n}\\n\\nfunction echo() {\\n const baseStyle = itty.baseStyle\\n let echo = pipe\\n if (!pipe)\\n echo = args.join(\\\" \\\")\\n\\n display.print(echo, itty.baseStyle, false)\\n itty.baseStyle = baseStyle\\n quit()\\n}\\n\\nasync function env() {\\n const subcommand = args[0]\\n const env = args[1]\\n\\n const envtype = typeof itty.env[env]\\n let value\\n\\n let listing = false\\n let listIndex = 0\\n\\n switch(subcommand) {\\n case \\\"get\\\":\\n if (!env) {\\n display.print(\\\"No variable specified\\\")\\n quit()\\n }\\n display.print(JSON.stringify(itty.env[env]) || \\\"undefined\\\", getEnvStyle(envtype))\\n quit()\\n break\\n case \\\"set\\\":\\n if (!env) {\\n display.print(\\\"No variable specified\\\")\\n quit()\\n }\\n await parseValue()\\n if (itty.env[env] && typeof itty.env[env] !== typeof value) {\\n let r = await io.read(\\\"Mismatch between env type and provided value type. Continue? [y/N]: \\\")\\n if (r.length === 0 || r[0].toLowerCase() !== \\\"y\\\") {\\n display.print(\\\"Aborting...\\\")\\n quit()\\n }\\n }\\n itty.env[env] = value\\n quit()\\n break\\n case \\\"list\\\":\\n const base = itty.baseStyle.toString(16)\\n for (let e in itty.env) {\\n display.buffer(`\\\\x11[s${getEnvStyle(typeof itty.env[e]).toString(16)}]${e}`)\\n display.buffer(\\\"\\\\x11[sE000] = \\\")\\n display.print(`\\\\x11[s${base}]${JSON.stringify(itty.env[e])}`)\\n }\\n display.render()\\n quit()\\n break\\n case \\\"save\\\":\\n try {\\n envfile = io.open(args[1], \\\"w\\\")\\n } catch {\\n try {\\n fs.makeNode(args[1], \\\"f\\\")\\n envfile = io.open(args[1], \\\"w\\\")\\n } catch {\\n display.print(\\\"Error creating file\\\", 0x9000)\\n quit()\\n }\\n }\\n\\n try {\\n envfile.write(JSON.stringify(itty.env))\\n envfile.writeOut()\\n } catch {\\n display.print(\\\"Error writing to file\\\", 0x9000)\\n quit()\\n }\\n\\n quit()\\n break\\n case \\\"load\\\":\\n try {\\n envfile = io.open(args[1], \\\"r\\\")\\n } catch {\\n display.print(\\\"File does not exist\\\", 0x9000)\\n quit()\\n }\\n\\n try {\\n envfile = JSON.parse(envfile.read())\\n } catch {\\n display.print(\\\"Cannot parse JSON\\\", 0x9000)\\n quit()\\n }\\n\\n for (let e of Object.entries(envfile))\\n itty.env[e[0]] = e[1]\\n\\n quit()\\n }\\n\\n async function parseValue() {\\n let v = await io.read([[\\\"Value: \\\", 0xe000]])\\n\\n try {\\n v = JSON.parse(v)\\n } catch (e) {\\n try {\\n v = JSON.parse('\\\"' + v + '\\\"')\\n } catch (e) {\\n throw [1, \\\"Cannot parse value\\\"]\\n }\\n }\\n value = v\\n }\\n \\n function getEnvStyle(type) {\\n let style = 0xf000\\n switch(type) {\\n case \\\"object\\\":\\n style = 0x4000\\n break\\n case \\\"string\\\":\\n style = 0x6000\\n break\\n case \\\"number\\\":\\n style = 0x5000\\n break\\n case \\\"boolean\\\":\\n style = 0xa000\\n break\\n }\\n return style\\n }\\n\\n quit()\\n}\\n\\nfunction info() {\\n const baseStyle = itty.baseStyle\\n\\n style.setAttribute(1, {_link: \\\"https://codeberg.org/Magdalunaa/itty\\\", style: \\\"cursor: pointer; text-decoration-color: var(--itty-c-4)\\\"})\\n\\n display.print(`Itty version: v${itty.version[0]}.${itty.version[1]}${itty.version[2] ? \\\".\\\" + itty.version[2] : \\\"\\\"} ${itty.version[3] || \\\"\\\"}\\\\nRoot FS: ${itty.disk.version}`, 0x4000)\\n display.buffer(\\\"\\\\nDisplay dimensions: \\\", 0x7000)\\n display.buffer(itty.width + \\\"x\\\" + itty.height + \\\"\\\\n\\\\n\\\", 0x6000)\\n for (let i=0; i < 8; i++) {\\n display.buffer(\\\" \\\", 0x0100 * i)\\n }\\n itty.cursorY++\\n itty.cursorX = 0\\n for (let i=8; i < 16; i++) {\\n display.buffer(\\\" \\\", 0x0100 * i)\\n }\\n\\n display.print(\\\"\\\\n\\\\n\\\\x11[s4041]ITTY: Incredibly Tiny Terminal, Yay! \\\\x11[s7041]by \\\\x11[s6051]Luna Magdalena :3\\\", undefined, true)\\n\\n itty.baseStyle = baseStyle\\n\\n quit()\\n}\\n\\nfunction kill() {\\n const id = Number(args[0])\\n\\n if (id)\\n try {\\n exec.quit(id)\\n } catch {}\\n quit()\\n}\\n\\nasync function loader() {\\n if (!args[1]) {\\n display.print(\\\"Not enough arguments\\\")\\n quit()\\n }\\n\\n const command = args[0]\\n const mountpoint = args[1]\\n const node = fs.getNode(mountpoint)\\n let content\\n\\n switch(command) {\\n case \\\"save\\\":\\n case \\\"download\\\":\\n content = fs.export(node)\\n\\n const blob = new Blob([content], { type: \\\"text/plain\\\" })\\n const url = URL.createObjectURL(blob)\\n\\n const a = document.createElement(\\\"a\\\")\\n a.href = url\\n a.download = node.name\\n a.style.display = \\\"none\\\"\\n\\n document.body.appendChild(a)\\n a.click()\\n document.body.removeChild(a)\\n\\n URL.revokeObjectURL(url)\\n break\\n case \\\"load\\\":\\n case \\\"upload\\\":\\n content = await getFile()\\n await fs.import(content.text, mountpoint)\\n display.print(\\\"Disk loaded successfully\\\", 0x3000)\\n break\\n case \\\"fetch\\\":\\n try {\\n content = await fetch(args[2])\\n } catch {\\n display.print(\\\"Cannot fetch remote file\\\", 0x9000)\\n quit()\\n }\\n try {\\n content = await content.json()\\n } catch {\\n display.print(\\\"Cannot parse file JSON\\\", 0x9000)\\n quit()\\n }\\n await fs.import(content, mountpoint)\\n break\\n }\\n\\n quit()\\n\\n function getFile() {\\n return new Promise(resolve => {\\n const input = document.createElement(\\\"input\\\")\\n input.type = \\\"file\\\"\\n input.accept = \\\"*/*\\\"\\n input.style.display = \\\"none\\\"\\n\\n document.body.appendChild(input)\\n\\n input.onchange = async () => {\\n const file = input.files[0]\\n const text = await file.text();\\n document.body.removeChild(input)\\n resolve({ file, text })\\n }\\n\\n input.click()\\n })\\n }\\n}\\n\\nfunction ls() {\\n // get flags\\n let flags = {}\\n let dir = \\\".\\\"\\n for (let i=0; i < args.length; i++) {\\n if (args[i][0] === \\\"-\\\") {\\n if (args[i].indexOf(\\\"l\\\") !== -1)\\n flags.l = true\\n if (args[i].indexOf(\\\"a\\\") !== -1)\\n flags.a = true\\n } else {\\n dir = args[i]\\n break\\n }\\n }\\n\\n // get node\\n let node\\n try {\\n node = fs.getNode(dir)\\n } catch (e) {\\n display.print(\\\"Not a directory\\\")\\n quit()\\n }\\n\\n if (!node || node.mode !== \\\"d\\\") {\\n display.print(\\\"Not a directory\\\")\\n quit()\\n }\\n\\n let empty = true\\n for (let n of fs.listDir(node)) {\\n if (n.name[0] === \\\".\\\" && !flags.a)\\n continue\\n\\n empty = false\\n\\n let style = 0x7000\\n if (n.mode === \\\"d\\\")\\n style = 0x5010\\n else if (n.mode === \\\"l\\\")\\n style = 0x3040\\n\\n if (n.protected)\\n style += 0x0020\\n\\n if (itty.cursorX + n.name.length >= itty.width) {\\n itty.cursorX = 0\\n itty.cursorY++\\n }\\n\\n while (itty.cursorY >= itty.height - 1)\\n display.lift()\\n\\n if (!flags.l)\\n display.buffer(n.name + \\\"\\\\x11[s7000]\\\\t\\\", style)\\n else\\n display.print(`\\\\x11[sc000]${n.mode} \\\\x11[s${style.toString(16)}]${n.name}${n.mode === \\\"l\\\" ? \\\"\\\\x11[sf000] -> \\\\x11[sb000]\\\" + n.content : \\\"\\\"}${n.protected ? \\\"\\\\x11[sf000] (protected)\\\" : \\\"\\\"}`)\\n }\\n\\n if (empty)\\n display.print(\\\"Directory is empty\\\", 0xf000)\\n\\n display.render()\\n quit()\\n}\\n\\nasync function meta() {\\n const file = fs.getNode(args[1])\\n\\n if (!file) {\\n display.print(\\\"File not found\\\", 0x1000)\\n quit()\\n }\\n\\n const meta = file.metadata\\n\\n const subcommand = args[0]\\n const key = args[2]\\n\\n let value\\n\\n switch(subcommand) {\\n case \\\"get\\\":\\n if (!key) {\\n display.print(\\\"No variable specified\\\")\\n quit()\\n }\\n display.print(JSON.stringify(meta[key]) || \\\"undefined\\\", getStyle(typeof meta[key]))\\n quit()\\n break\\n case \\\"set\\\":\\n if (!key) {\\n display.print(\\\"No variable specified\\\")\\n quit()\\n }\\n await parseValue()\\n try {\\n fs.setMetadata(file, key, value)\\n } catch (e) {\\n display.print(\\\"Error setting metadata: \\\" + e[1], 0x1000)\\n }\\n quit()\\n break\\n case \\\"list\\\":\\n const base = itty.baseStyle.toString(16)\\n for (let m in meta) {\\n display.buffer(`\\\\x11[s${getStyle(typeof meta[m]).toString(16)}]${m}`)\\n display.buffer(\\\"\\\\x11[sE000] = \\\")\\n display.print(`\\\\x11[s${base}]${JSON.stringify(meta[m])}`)\\n }\\n display.render()\\n quit()\\n }\\n\\n async function parseValue() {\\n let v = await io.read([[\\\"Value: \\\", 0xe000]])\\n\\n try {\\n v = JSON.parse(v)\\n } catch (e) {\\n try {\\n v = JSON.parse('\\\"' + v + '\\\"')\\n } catch (e) {\\n throw [1, \\\"Cannot parse value\\\"]\\n }\\n }\\n value = v\\n }\\n \\n function getStyle(type) {\\n let style = 0xf000\\n switch(type) {\\n case \\\"object\\\":\\n style = 0x4000\\n break\\n case \\\"string\\\":\\n style = 0x6000\\n break\\n case \\\"number\\\":\\n style = 0x5000\\n break\\n case \\\"boolean\\\":\\n style = 0xa000\\n break\\n }\\n return style\\n }\\n\\n quit()\\n}\\n\\nfunction mkdir() {\\n if (!args[0]) {\\n display.print(\\\"No path provided\\\", 0xf000)\\n quit()\\n }\\n\\n try {\\n fs.makeDir(args[0])\\n } catch (e) {\\n display.print(\\\"Error creating directory\\\", 0x1000)\\n }\\n quit()\\n}\\n\\nfunction mv() {\\n if (!args[0] && !args[1]) {\\n display.print(\\\"Not enough arguments\\\", 0xf000)\\n quit()\\n }\\n\\n try {\\n fs.moveNode(args[0], args[1])\\n } catch (e) {\\n display.print(e[1] || \\\"Error moving node\\\", 0x9000)\\n }\\n\\n quit()\\n}\\n\\nfunction plist() {\\n const baseStyle = itty.baseStyle\\n // iterate over itty.program\\n\\n const programList = exec.listPrograms()\\n for (let p of programList) {\\n if (!p)\\n continue\\n display.buffer(\\\"\\\\x11[sF000][\\\")\\n display.buffer(\\\"\\\\x11[s6000]\\\" + p.id)\\n display.buffer(\\\"\\\\x11[sF000]] (\\\")\\n display.buffer(\\\"\\\\x11[s4000]\\\" + p.uid)\\n display.buffer(\\\"\\\\x11[sF000])\\\\t\\\")\\n display.buffer(\\\"\\\\x11[sE000]\\\" + p.name)\\n if (p.background) {\\n display.buffer(\\\"\\\\x11[sF000]\\\\t(\\\")\\n display.buffer(\\\"\\\\x11[s5000]background\\\")\\n display.buffer(\\\"\\\\x11[sF000])\\\")\\n }\\n display.write(\\\"\\\\n\\\")\\n }\\n\\n itty.baseStyle = baseStyle\\n quit()\\n}\\n\\nfunction programs() {\\n for (let path of itty.env.path) {\\n path = fs.resolve(path)\\n pathList = fs.listDir(path)\\n for (let p in pathList) {\\n p = pathList[p]\\n let name = p.shortname\\n\\n if (p.mode === \\\"l\\\")\\n p = fs.getNode(path + p.name)\\n\\n if (!p)\\n continue\\n\\n if (p.metadata.runner) {\\n if (itty.cursorX + name.length >= itty.width) {\\n itty.cursorX = 0\\n itty.cursorY++\\n }\\n while (itty.cursorY >= itty.height)\\n display.lift()\\n display.buffer(name + \\\" \\\")\\n }\\n }\\n }\\n \\n display.render()\\n quit()\\n}\\n\\nfunction rm() {\\n if (!args[0]) {\\n display.print(\\\"No path provided\\\", 0xf000)\\n quit()\\n }\\n\\n for (let node of args) {\\n try {\\n fs.removeNode(node)\\n } catch (e) {\\n display.print(e[1] || \\\"Error removing node\\\", 0x9000)\\n }\\n }\\n\\n quit()\\n}\\n\\nasync function scroll() {\\n if (!pipe)\\n quit()\\n\\n program.enterFullscreen()\\n const baseStyle = itty.baseStyle\\n\\n const lines = pipe.split(\\\"\\\\n\\\")\\n\\n function renderLines() {\\n display.clear()\\n for (let i = start; i < lines.length; i++) {\\n if ((lines[i].length + Math.floor(lines[i].length / itty.width) * 12) / itty.width > itty.height - itty.cursorY) {\\n break\\n }\\n display.print(lines[i] || \\\"\\\\x11 \\\")\\n }\\n }\\n\\n let start = 0\\n renderLines()\\n\\n program.onInput = (k) => {\\n switch (k) {\\n case \\\"%^\\\":\\n case \\\"k\\\":\\n start = Math.max(start - 1, 0)\\n renderLines()\\n break\\n case \\\"%v\\\":\\n case \\\"j\\\":\\n start = Math.min(start + 1, lines.length - 1)\\n renderLines()\\n break\\n case \\\"%e\\\":\\n case \\\"q\\\":\\n try {\\n quit()\\n } catch {}\\n }\\n }\\n\\n program.onResize = () => {\\n renderLines()\\n }\\n\\n program.onQuit = () => {\\n itty.baseStyle = baseStyle\\n display.clear()\\n }\\n}\\n\\nasync function shutdown() {\\n await exec.shutdown()\\n}\\n\\nfunction slog() {\\n // ensure log file exists\\n\\n let logfile\\n\\n try {\\n logfile = io.open(\\\"/itty/slog\\\", \\\"a\\\")\\n } catch {}\\n\\n if (!logfile) {\\n fs.getNode(\\\"/itty/\\\").content[\\\"slog\\\"] = {\\n protected: false,\\n mode: \\\"f\\\",\\n content: \\\"\\\",\\n parent: fs.getNode(\\\"/itty/\\\"),\\n name: \\\"slog\\\",\\n js: \\\"\\\",\\n metadata: {}\\n }\\n logfile = io.open(\\\"/itty/slog\\\", \\\"a\\\")\\n }\\n\\n const message = JSON.parse(pipe)\\n\\n if (message[1][0] !== 0)\\n console.log(\\\"ERROR\\\", message[1])\\n\\n logfile.print(`(${message[0].id}/${message[0].uid}) ${message[0].name}: [${message[1][0] || \\\"INFO\\\"}] ${message[1][1]}`)\\n logfile.writeOut()\\n\\n quit()\\n}\\n\\nfunction touch() {\\n if (!args[0]) {\\n display.print(\\\"No path provided\\\", 0xf000)\\n quit()\\n }\\n\\n try {\\n fs.makeNode(args[0], \\\"f\\\")\\n } catch (e) {\\n switch (e[0]) {\\n case 1:\\n display.print(\\\"File already exists\\\", 0xf000)\\n break\\n case 3:\\n display.print(\\\"Parent directory is protected\\\", 0x9000)\\n break\\n case 10:\\n display.print(\\\"Parent directory does not exist\\\", 0x9000)\\n break\\n default:\\n display.print(\\\"Error creating file\\\", 0x9000)\\n }\\n }\\n quit()\\n}\\n\\nasync function yarn() {\\n function spool(nodes) {\\n const yarnball = {}\\n\\n for (let node of nodes) {\\n let n\\n try {\\n n = fs.getNode(node)\\n } catch {\\n continue\\n }\\n\\n yarnball[n.name] = n\\n }\\n\\n return JSON.stringify(yarnball, (key, value) => {if (key === \\\"parent\\\" || key === \\\"name\\\" || key === \\\"ext\\\") {return undefined}; return value})\\n }\\n\\n function unspool(yarnball, path) {\\n fs.makeDir(path)\\n const node = fs.getNode(path)\\n if (node.protected)\\n io.error([3, \\\"Destination is protected\\\"])\\n\\n try {\\n yarnball = JSON.parse(yarnball)\\n } catch {\\n io.error([5, \\\"Cannot parse yarnball\\\"])\\n }\\n\\n for (let file of Object.keys(yarnball)) {\\n if (node.content[file])\\n continue\\n const obj = yarnball[file]\\n node.content[file] = obj\\n }\\n fs.refreshPathAttributes(node)\\n fs.save()\\n }\\n\\n let yarnfile\\n let meta\\n let jsonfile\\n\\n switch (args[0]) {\\n case \\\"spool\\\":\\n yarnfile = io.open(args[1], \\\"w\\\")\\n yarnfile.write(spool(args.slice(2)))\\n yarnfile.writeOut()\\n break\\n case \\\"unspool\\\":\\n yarnfile = io.open(args[1], \\\"r\\\")\\n unspool(yarnfile.read(), args[2])\\n break\\n case \\\"metadata-from-json\\\":\\n try {\\n yarnfile = fs.getNode(args[1])\\n } catch {\\n io.error([1, \\\"Cannot find yarnball\\\"])\\n }\\n if (yarnfile.protected)\\n io.error([2, \\\"Yarnball is protected\\\"])\\n\\n try {\\n jsonfile = fs.getNode(args[2])\\n } catch {\\n io.error([1, \\\"Cannot find JSON file\\\"])\\n }\\n\\n try {\\n meta = JSON.parse(jsonfile.content)\\n } catch {\\n io.error([6, \\\"Cannot parse JSON\\\"])\\n }\\n\\n yarnfile.metadata = meta\\n break\\n }\\n quit()\\n}\\n\\nfunction ln() {\\n if (!args[1]) {\\n display.print(\\\"Not enough arguments\\\", 0xf000)\\n quit()\\n }\\n\\n try {\\n fs.makeLink(args[1], args[0])\\n } catch (e) {\\n display.print(e[1] || \\\"Error creating link\\\", 0x9000)\\n }\\n\\n quit()\\n}\\n\\nasync function woman() {\\n if (!args[0]) {\\n display.print(\\\"No page specified\\\", 0xf000)\\n quit()\\n }\\n\\n // try to find the requested node\\n const dir = fs.resolve(itty.env.dir_docs || \\\"/itty/docs\\\")\\n const page = fs.getNode(dir + args[0])\\n\\n if (!page) {\\n display.print(\\\"Page does not exist\\\", 0xf000)\\n quit()\\n }\\n\\n program.autoOutput = false\\n io.output(io.fread(page))\\n\\n if (!program.visible)\\n quit()\\n\\n // get the expected max line count\\n let linecount = 0\\n\\n for (let line of io.flines(page)) {\\n l = (line.length + Math.ceil(line.length / itty.width) * 12) || 1\\n linecount += Math.ceil(l / itty.width)\\n }\\n\\n if (linecount < itty.height - 1) {\\n const baseStyle = itty.baseStyle\\n for (let line of io.flines(page))\\n display.print(line)\\n itty.baseStyle = baseStyle\\n quit()\\n } else {\\n pipe = \\\"\\\"\\n for (let line of io.flines(page))\\n pipe += \\\"\\\\n\\\" + line\\n pipe = pipe.replace(/^\\\\n/, \\\"\\\")\\n await scroll()\\n }\\n}\\n\\nasync function persist() {\\n // switch over arguments\\n\\n let disk\\n let diskmeta\\n let content\\n\\n switch (args[0]) {\\n case \\\"set\\\":\\n if (!args[2]) {\\n display.print(\\\"Not enough arguments\\\", 0xf000)\\n quit()\\n }\\n\\n try {\\n fs.setPersistence(args[1], Number(args[2]))\\n } catch {\\n display.print(\\\"Error setting persistence level\\\", 0x9000)\\n quit()\\n }\\n\\n display.print(`Set persistence level for ${args[1]} to ${args[2]}`, 0x3000)\\n break\\n case \\\"get\\\":\\n if (!args[1]) {\\n display.print(\\\"Not enough arguments\\\", 0xf000)\\n quit()\\n }\\n\\n args[1] = fs.resolve(args[1], false)\\n\\n diskmeta = localStorage.getItem(\\\"diskmeta\\\") || '{\\\"whitelist\\\": [], \\\"blacklist\\\": []}'\\n \\n try {\\n diskmeta = JSON.parse(diskmeta)\\n } catch {\\n display.print(\\\"Error parsing persistence metadata\\\", 0x9000)\\n quit()\\n }\\n\\n if (diskmeta.whitelist.includes(args[1]))\\n display.print(`${args[1]} is marked to persist`, 0x3000)\\n else if (diskmeta.blacklist.includes(args[1]))\\n display.print(`${args[1]} is marked to not persist`, 0x1000)\\n else\\n display.print(`${args[1]} is unmarked`, 0x7000)\\n\\n break\\n case \\\"list\\\":\\n diskmeta = localStorage.getItem(\\\"diskmeta\\\") || '{\\\"whitelist\\\": [], \\\"blacklist\\\": []}'\\n \\n try {\\n diskmeta = JSON.parse(diskmeta)\\n } catch {\\n display.print(\\\"Error parsing persistence metadata\\\", 0x9000)\\n quit()\\n }\\n\\n if (diskmeta.whitelist.length) {\\n display.print(\\\"Marked to persist:\\\", 0xB050)\\n for (let p of diskmeta.whitelist)\\n display.print(p, 0x3000)\\n }\\n\\n if (diskmeta.blacklist.length) {\\n display.print(\\\"Marked to not persist:\\\", 0x9050)\\n for (let p of diskmeta.blacklist)\\n display.print(p, 0x1000)\\n }\\n\\n break\\n case \\\"backup\\\":\\n diskmeta = localStorage.getItem(\\\"diskmeta\\\") || '{\\\"whitelist\\\": [], \\\"blacklist\\\": []}'\\n disk = localStorage.getItem(\\\"disksaved\\\") || '{}'\\n\\n try {\\n content = JSON.stringify({\\n disk: disk,\\n meta: diskmeta\\n })\\n } catch {\\n display.print(\\\"Error converting saved disk into a string\\\", 0x9000)\\n quit()\\n }\\n\\n const blob = new Blob([content], { type: \\\"text/plain\\\" })\\n const url = URL.createObjectURL(blob)\\n\\n const a = document.createElement(\\\"a\\\")\\n a.href = url\\n a.download = \\\"Itty Disk\\\"\\n a.style.display = \\\"none\\\"\\n\\n document.body.appendChild(a)\\n a.click()\\n document.body.removeChild(a)\\n\\n URL.revokeObjectURL(url)\\n break\\n case \\\"restore\\\":\\n content = await getFile()\\n content = content.text\\n\\n try {\\n content = JSON.parse(content)\\n } catch {\\n display.print(\\\"Error parsing disk content\\\", 0x9000)\\n quit()\\n }\\n\\n console.log(content)\\n\\n try {\\n localStorage.setItem(\\\"diskmeta\\\", content.meta)\\n localStorage.setItem(\\\"disksaved\\\", content.disk)\\n } catch {\\n display.print(\\\"Error storing disk data\\\", 0x9000)\\n quit()\\n }\\n\\n await exec.shutdown(true)\\n\\n break\\n case \\\"save\\\":\\n fs.save()\\n break\\n default:\\n display.print(\\\"No subcommand specified\\\", 0xf000)\\n }\\n\\n quit()\\n\\n function getFile() {\\n return new Promise(resolve => {\\n const input = document.createElement(\\\"input\\\")\\n input.type = \\\"file\\\"\\n input.accept = \\\"*/*\\\"\\n input.style.display = \\\"none\\\"\\n\\n document.body.appendChild(input)\\n\\n input.onchange = async () => {\\n const file = input.files[0]\\n const text = await file.text();\\n document.body.removeChild(input)\\n resolve({ file, text })\\n }\\n\\n input.click()\\n })\\n }\\n}\"}}"} |