A character sheet creator for TTRPGs sheetr.app/
gleam dnd dnd5e atproto

Initial Commit

Signed-off-by: Naomi Roberts <mia@naomieow.xyz>

+1
.envrc
··· 1 + use flake
+24
README.md
··· 1 + # sheetr 2 + 3 + Monorepo for Sheetr and it's associated libraries. 4 + 5 + > NOTE: Libraries under the [`/alicia/`](/alicia/) directory will eventually be 6 + pulled out into their own repo when ready. As of current they are unstable and 7 + rely/will rely on path dependencies within this monorepo. 8 + 9 + ## Development 10 + Sheetr provides a `flake.nix` for use with the Nix package manager. This can be 11 + installed independently from your existing package manager as shown 12 + [here](https://nixos.org/download/). It is very possible (and fairly easy) to 13 + develop this project without Nix, however Nix helps ensure you are using the 14 + exact same devenv as everyone else. 15 + 16 + ### Generating code from Lexicons 17 + A useful helper is provided via the Nix flake. 18 + ```bash 19 + nix run .#lexgen 20 + 21 + # without Nix 22 + cd shared 23 + gleam run -m alicia/lexgen -- --dir=../lexicons 24 + ```
+32
TODO.md
··· 1 + # alicia 2 + 3 + ## alicia_lexgen 4 + 5 + - [ ] Codegen 6 + - [ ] 100% spec coverage 7 + - [x] Coverage for Sheetr lexicons 8 + - [ ] Ability to fetch lexicon schemas from internet/git 9 + - [ ] Possibly move parsing/gen into an `alicia_lex` or `alicia_lexicon` package 10 + and have `alicia_lexgen` as a pure CLI package. 11 + - [ ] Extract codegen library or use an external one. 12 + 13 + ## alicia_api 14 + 15 + > Waiting on `lexgen` 16 + 17 + - [ ] Generated code 18 + - [ ] `com.atproto.*` 19 + - [ ] `app.bsky.*` 20 + 21 + ## alicia_identity 22 + 23 + ## alicia_oauth 24 + 25 + ## xrpc 26 + Possibly have XRPC client libraries which are thin wrappers around existing HTTP 27 + clients like `httpc` or JavaScript's `fetch`. Could also provide helper package 28 + for `lustre` users that provides functions that return `effect`s instead/as well. 29 + 30 + ## crypto/common/syntax/repo 31 + All might be needed, unsure as to the exact architecture that will be used as of 32 + current so can't say for certain.
+3
alicia/README.md
··· 1 + # alicia 2 + 3 + An ATProtocol SDK in Gleam.
+4
alicia/identity/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
alicia/identity/README.md
··· 1 + # alicia_identity 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/alicia_identity)](https://hex.pm/packages/alicia_identity) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/alicia_identity/) 5 + 6 + ```sh 7 + gleam add alicia_identity@1 8 + ``` 9 + ```gleam 10 + import alicia_identity 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/alicia_identity>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+19
alicia/identity/gleam.toml
··· 1 + name = "alicia_identity" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + 18 + [dev-dependencies] 19 + gleeunit = ">= 1.0.0 and < 2.0.0"
+11
alicia/identity/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "gleam_stdlib", version = "0.67.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6CE3E4189A8B8EC2F73AB61A2FBDE49F159D6C9C61C49E3B3082E439F260D3D0" }, 6 + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 7 + ] 8 + 9 + [requirements] 10 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 11 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
alicia/identity/src/alicia/identity.gleam

This is a binary file and will not be displayed.

+13
alicia/identity/test/alicia_identity_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }
+4
alicia/lexgen/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
alicia/lexgen/README.md
··· 1 + # alicia_lexgen 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/alicia_lexgen)](https://hex.pm/packages/alicia_lexgen) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/alicia_lexgen/) 5 + 6 + ```sh 7 + gleam add alicia_lexgen@1 8 + ``` 9 + ```gleam 10 + import alicia_lexgen 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/alicia_lexgen>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+25
alicia/lexgen/gleam.toml
··· 1 + name = "alicia_lexgen" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + simplifile = ">= 2.3.2 and < 3.0.0" 18 + gleam_json = ">= 3.1.0 and < 4.0.0" 19 + glint = ">= 1.2.1 and < 2.0.0" 20 + argv = ">= 1.0.2 and < 2.0.0" 21 + snag = ">= 1.2.0 and < 2.0.0" 22 + pprint = ">= 1.0.6 and < 2.0.0" 23 + 24 + [dev-dependencies] 25 + gleeunit = ">= 1.0.0 and < 2.0.0"
+28
alicia/lexgen/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 6 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 7 + { name = "glam", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "237C2CE218A2A0A5D46D625F8EF5B78F964BC91018B78D692B17E1AB84295229" }, 8 + { name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" }, 9 + { name = "gleam_community_colour", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "E34DD2C896AC3792151EDA939DA435FF3B69922F33415ED3C4406C932FBE9634" }, 10 + { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, 11 + { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, 12 + { name = "gleam_stdlib", version = "0.67.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6CE3E4189A8B8EC2F73AB61A2FBDE49F159D6C9C61C49E3B3082E439F260D3D0" }, 13 + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 14 + { name = "glint", version = "1.2.1", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "2214C7CEFDE457CEE62140C3D4899B964E05236DA74E4243DFADF4AF29C382BB" }, 15 + { name = "pprint", version = "1.0.6", build_tools = ["gleam"], requirements = ["glam", "gleam_stdlib"], otp_app = "pprint", source = "hex", outer_checksum = "4E9B34AE03B2E81D60F230B9BAF1792BE1AC37AFB5564B8DEBEE56BAEC866B7D" }, 16 + { name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" }, 17 + { name = "snag", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "274F41D6C3ECF99F7686FDCE54183333E41D2C1CA5A3A673F9A8B2C7A4401077" }, 18 + ] 19 + 20 + [requirements] 21 + argv = { version = ">= 1.0.2 and < 2.0.0" } 22 + gleam_json = { version = ">= 3.1.0 and < 4.0.0" } 23 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 24 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 25 + glint = { version = ">= 1.2.1 and < 2.0.0" } 26 + pprint = { version = ">= 1.0.6 and < 2.0.0" } 27 + simplifile = { version = ">= 2.3.2 and < 3.0.0" } 28 + snag = { version = ">= 1.2.0 and < 2.0.0" }
+12
alicia/lexgen/src/alicia/lexgen.gleam
··· 1 + import alicia/lexgen/cli 2 + import argv 3 + import glint 4 + 5 + pub fn main() { 6 + glint.new() 7 + |> glint.with_name("alicia/lexgen") 8 + |> glint.pretty_help(glint.default_pretty_help()) 9 + |> glint.add(at: [], do: cli.run()) 10 + |> glint.add(at: ["check"], do: cli.check()) 11 + |> glint.run(argv.load().arguments) 12 + }
+212
alicia/lexgen/src/alicia/lexgen/cli.gleam
··· 1 + import alicia/lexgen/codegen 2 + import alicia/lexgen/lexicon 3 + import gleam/dict 4 + import gleam/int 5 + import gleam/io 6 + import gleam/list 7 + import gleam/result 8 + import gleam/string 9 + import glint 10 + import pprint 11 + import simplifile 12 + import snag 13 + 14 + fn dir_flag() -> glint.Flag(String) { 15 + glint.string_flag("dir") 16 + |> glint.flag_default("./lexicons") 17 + |> glint.flag_help( 18 + "Folder that alicia/lexgen will search for lexicon definitions.", 19 + ) 20 + } 21 + 22 + fn verbose_flag() -> glint.Flag(Bool) { 23 + glint.bool_flag("verbose") 24 + |> glint.flag_default(False) 25 + |> glint.flag_help("Whether verbose command output should be enabled.") 26 + } 27 + 28 + fn parse_lexicon( 29 + contents: String, 30 + verbose: Bool, 31 + ) -> Result(lexicon.Lexicon, snag.Snag) { 32 + let res = 33 + lexicon.parse(contents) 34 + |> snag.replace_error("Unable to parse lexicon") 35 + 36 + case verbose { 37 + True -> 38 + result.map(res, fn(lexicon) { 39 + io.println("Parsed lexicon with NSID " <> lexicon.id) 40 + lexicon 41 + }) 42 + False -> res 43 + } 44 + } 45 + 46 + fn read_lexicon(path: String, verbose: Bool) -> Result(String, snag.Snag) { 47 + case verbose { 48 + True -> io.println("Found " <> path) 49 + False -> Nil 50 + } 51 + simplifile.read(path) 52 + |> snag.replace_error("Unable to read lexicon " <> path) 53 + } 54 + 55 + fn read_lexicons(dir: String, verbose: Bool) -> Result(List(String), snag.Snag) { 56 + use files_and_folders <- result.try( 57 + simplifile.read_directory(dir) 58 + |> snag.replace_error("Unable to read directory " <> dir), 59 + ) 60 + Ok( 61 + list.map(files_and_folders, fn(file_or_folder) { 62 + let path = dir <> "/" <> file_or_folder 63 + case simplifile.is_file(path) { 64 + Ok(False) -> read_lexicons(path, verbose) 65 + Ok(True) -> [read_lexicon(path, verbose)] |> result.all 66 + Error(_) -> 67 + [ 68 + Error(snag.new( 69 + "Failed to determine whether " <> path <> " is a file or folder.", 70 + )), 71 + ] 72 + |> result.all 73 + } 74 + }) 75 + |> result.all 76 + |> result.map(list.flatten), 77 + ) 78 + |> result.flatten 79 + } 80 + 81 + fn is_upper(string: String) -> Bool { 82 + case string { 83 + "A" 84 + | "B" 85 + | "C" 86 + | "D" 87 + | "E" 88 + | "F" 89 + | "G" 90 + | "H" 91 + | "I" 92 + | "J" 93 + | "K" 94 + | "L" 95 + | "M" 96 + | "N" 97 + | "O" 98 + | "P" 99 + | "Q" 100 + | "R" 101 + | "S" 102 + | "T" 103 + | "U" 104 + | "V" 105 + | "W" 106 + | "X" 107 + | "Y" 108 + | "Z" -> True 109 + _ -> False 110 + } 111 + } 112 + 113 + fn to_snake_case(string: String) -> String { 114 + string 115 + |> string.to_graphemes 116 + |> list.map(fn(letter) { 117 + case is_upper(letter) { 118 + True -> "_" <> string.lowercase(letter) 119 + False -> letter 120 + } 121 + }) 122 + |> string.concat 123 + } 124 + 125 + fn generate_module( 126 + lexicon: lexicon.Lexicon, 127 + ) -> Result(#(String, codegen.Codegen), snag.Snag) { 128 + let split_nsid = 129 + lexicon.id 130 + |> string.split(".") 131 + use last <- result.try( 132 + list.last(split_nsid) 133 + |> snag.replace_error("Provided NSID was empty"), 134 + ) 135 + let module_path = 136 + string.join( 137 + split_nsid 138 + |> list.reverse 139 + |> list.drop(1) 140 + |> list.reverse 141 + |> list.map(to_snake_case), 142 + "/", 143 + ) 144 + // TODO: When we have a token, create a type with the name of the preceeding 145 + // part of the NSID with the token as a variant. For example: 146 + // `com.example.colour.red` would still be placed at 147 + // `com/example/colour/colour.gleam`, but would generate the code: 148 + // ```gleam 149 + // pub type Colour { 150 + // Red 151 + //} 152 + // ``` 153 + // instead of: 154 + // ```gleam 155 + // pub type Red { 156 + // Red 157 + //} 158 + // ``` 159 + let type_name = case last |> string.to_graphemes { 160 + [first, ..rest] -> 161 + case is_upper(first) { 162 + True -> last 163 + False -> string.uppercase(first) <> string.concat(rest) 164 + } 165 + [] -> "" 166 + } 167 + let module = 168 + codegen.new() 169 + |> codegen.add_type(type_name, variants: [codegen.variant(type_name, [])]) 170 + Ok(#(module_path, module)) 171 + } 172 + 173 + fn unique_or_merge(list: List(#(a, b)), merge: fn(b, b) -> b) -> List(#(a, b)) { 174 + list.fold(list, dict.new(), fn(acc, item) { 175 + let #(k, v) = item 176 + case dict.get(acc, k) { 177 + Ok(existing) -> dict.insert(acc, k, merge(v, existing)) 178 + Error(_) -> dict.insert(acc, k, v) 179 + } 180 + }) 181 + |> dict.to_list 182 + } 183 + 184 + pub fn run() -> glint.Command(Result(Nil, snag.Snag)) { 185 + use <- glint.command_help("Generates Gleam code from lexicon definitions") 186 + use dir <- glint.flag(dir_flag()) 187 + use verbose <- glint.flag(verbose_flag()) 188 + use _, _, flags <- glint.command() 189 + // dir_flag and verbose_flag supply defaults so asserting here is safe 190 + let assert Ok(dir) = dir(flags) 191 + let assert Ok(verbose) = verbose(flags) 192 + io.println("Reading lexicons from " <> dir) 193 + use lexicons <- result.try(read_lexicons(dir, verbose)) 194 + io.println("Parsing " <> int.to_string(list.length(lexicons)) <> " lexicons") 195 + use lexicons <- result.try( 196 + list.map(lexicons, parse_lexicon(_, verbose)) 197 + |> result.all, 198 + ) 199 + list.map(lexicons, generate_module) 200 + |> result.all 201 + |> result.map(unique_or_merge(_, codegen.merge)) 202 + |> pprint.debug 203 + Ok(Nil) 204 + } 205 + 206 + pub fn check() -> glint.Command(Result(Nil, snag.Snag)) { 207 + use <- glint.command_help( 208 + "Checks if existing Gleam code matches lexicon definitions", 209 + ) 210 + use _, _, _ <- glint.command() 211 + Ok(Nil) 212 + }
+85
alicia/lexgen/src/alicia/lexgen/codegen.gleam
··· 1 + import gleam/list 2 + import gleam/string 3 + 4 + pub opaque type Codegen { 5 + Codegen( 6 + module_comment: String, 7 + imports: List(String), 8 + types: List(String), 9 + functions: List(String), 10 + ) 11 + } 12 + 13 + pub fn new() -> Codegen { 14 + Codegen( 15 + module_comment: "//// Module generated by alicia/lexgen. DO NOT EDIT.", 16 + imports: [], 17 + types: [], 18 + functions: [], 19 + ) 20 + } 21 + 22 + pub fn generate(codegen c: Codegen) -> String { 23 + c.module_comment 24 + <> "\n\n" 25 + <> list.fold(c.imports, "", fn(acc, i) { acc <> i <> "\n" }) 26 + <> "\n" 27 + <> list.fold(c.types, "", fn(acc, t) { acc <> t <> "\n" }) 28 + <> "\n" 29 + <> list.fold(c.functions, "", fn(acc, f) { acc <> f <> "\n" }) 30 + } 31 + 32 + pub fn variant( 33 + name name: String, 34 + fields fields: List(#(String, String)), 35 + ) -> String { 36 + name 37 + <> case fields { 38 + [] -> "" 39 + _ -> 40 + "(" 41 + <> list.fold(fields, "", fn(acc, field) { 42 + acc 43 + <> case field.0 { 44 + "" -> field.1 45 + label -> label <> ": " <> field.1 46 + } 47 + <> ", " 48 + }) 49 + // remove last `, ` 50 + |> string.drop_end(2) 51 + <> ")" 52 + } 53 + <> "\n" 54 + } 55 + 56 + /// types added in reverse order 57 + pub fn add_type( 58 + codegen c: Codegen, 59 + name name: String, 60 + variants variants: List(String), 61 + ) -> Codegen { 62 + let code = 63 + "pub type " 64 + <> name 65 + <> case variants { 66 + [] -> "" 67 + _ -> 68 + " {\n" 69 + <> list.fold(variants, "", fn(acc, variant) { acc <> " " <> variant }) 70 + <> "}\n" 71 + } 72 + Codegen(..c, types: [code, ..c.types]) 73 + } 74 + 75 + pub fn merge(left: Codegen, right: Codegen) -> Codegen { 76 + Codegen( 77 + module_comment: case left.module_comment == right.module_comment { 78 + True -> left.module_comment 79 + False -> left.module_comment <> "\n" <> right.module_comment 80 + }, 81 + imports: list.append(left.imports, right.imports) |> list.unique, 82 + types: list.append(left.types, right.types) |> list.unique, 83 + functions: list.append(left.functions, right.functions) |> list.unique, 84 + ) 85 + }
+47
alicia/lexgen/src/alicia/lexgen/lexicon.gleam
··· 1 + import gleam/dict 2 + import gleam/dynamic/decode 3 + import gleam/json 4 + import gleam/option 5 + 6 + pub fn parse(lexicon: String) -> Result(Lexicon, json.DecodeError) { 7 + json.parse(lexicon, lexicon_decoder()) 8 + } 9 + 10 + pub type Lexicon { 11 + Lexicon(version: Int, id: String, defs: dict.Dict(String, LexiconDef)) 12 + } 13 + 14 + fn lexicon_decoder() -> decode.Decoder(Lexicon) { 15 + use version <- decode.field("lexicon", decode.int) 16 + use id <- decode.field("id", decode.string) 17 + use defs <- decode.field( 18 + "defs", 19 + decode.dict(decode.string, lexicon_def_decoder()), 20 + ) 21 + decode.success(Lexicon(version:, id:, defs:)) 22 + } 23 + 24 + pub type LexiconDef { 25 + LexiconDef( 26 + // TODO: maybe make type(s) here rather 27 + // than a bunch of optional fields? 28 + type_: String, 29 + key: option.Option(String), 30 + description: option.Option(String), 31 + ) 32 + } 33 + 34 + pub fn lexicon_def_decoder() -> decode.Decoder(LexiconDef) { 35 + use type_ <- decode.field("type", decode.string) 36 + use key <- decode.optional_field( 37 + "key", 38 + option.None, 39 + decode.optional(decode.string), 40 + ) 41 + use description <- decode.optional_field( 42 + "description", 43 + option.None, 44 + decode.optional(decode.string), 45 + ) 46 + decode.success(LexiconDef(type_:, key:, description:)) 47 + }
+13
alicia/lexgen/test/alicia_lexgen_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }
+4
backend/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
backend/README.md
··· 1 + # sheetr_server 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/sheetr_server)](https://hex.pm/packages/sheetr_server) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/sheetr_server/) 5 + 6 + ```sh 7 + gleam add sheetr_server@1 8 + ``` 9 + ```gleam 10 + import sheetr_server 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/sheetr_server>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+19
backend/gleam.toml
··· 1 + name = "sheetr_server" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + 18 + [dev-dependencies] 19 + gleeunit = ">= 1.0.0 and < 2.0.0"
+5
backend/src/sheetr_server.gleam
··· 1 + import gleam/io 2 + 3 + pub fn main() -> Nil { 4 + io.println("Hello from sheetr_server!") 5 + }
+13
backend/test/sheetr_server_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }
+27
flake.lock
··· 1 + { 2 + "nodes": { 3 + "nixpkgs": { 4 + "locked": { 5 + "lastModified": 1767273430, 6 + "narHash": "sha256-kDpoFwQ8GLrPiS3KL+sAwreXrph2KhdXuJzo5+vSLoo=", 7 + "owner": "NixOS", 8 + "repo": "nixpkgs", 9 + "rev": "76eec3925eb9bbe193934987d3285473dbcfad50", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "NixOS", 14 + "ref": "nixpkgs-unstable", 15 + "repo": "nixpkgs", 16 + "type": "github" 17 + } 18 + }, 19 + "root": { 20 + "inputs": { 21 + "nixpkgs": "nixpkgs" 22 + } 23 + } 24 + }, 25 + "root": "root", 26 + "version": 7 27 + }
+73
flake.nix
··· 1 + { 2 + inputs = { 3 + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 4 + }; 5 + 6 + outputs = {nixpkgs, ...} @ inputs: let 7 + lib = nixpkgs.lib; 8 + supportedSystems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; 9 + forEachSupportedSystem = f: 10 + lib.genAttrs supportedSystems (system: 11 + f { 12 + pkgs = import nixpkgs {inherit system;}; 13 + }); 14 + in { 15 + devShells = forEachSupportedSystem ({pkgs}: { 16 + default = pkgs.mkShell { 17 + packages = with pkgs; [ 18 + gleam 19 + erlang_28 20 + beam28Packages.rebar3 21 + jq 22 + ]; 23 + }; 24 + }); 25 + apps = forEachSupportedSystem ({pkgs}: let 26 + cdInto = service: '' 27 + rootDir=$(jj --ignore-working-copy root || git rev-parse --show-toplevel) || (echo "error: can't find repo root?"; exit 1) 28 + cd "$rootDir/${service}" 29 + ''; 30 + runtimeInputs = with pkgs; [ 31 + gleam 32 + erlang_28 33 + beam28Packages.rebar3 34 + ]; 35 + in { 36 + # TODO: make default app run both frontend and backend 37 + frontend = { 38 + type = "app"; 39 + program = "${(pkgs.writeShellApplication { 40 + inherit runtimeInputs; 41 + name = "run-frontend"; 42 + text = '' 43 + ${cdInto "frontend"} 44 + # TODO: dev server proxy thing 45 + ${pkgs.gleam}/bin/gleam run -m lustre/dev start 46 + ''; 47 + })}/bin/run-frontend"; 48 + }; 49 + backend = { 50 + type = "app"; 51 + program = "${(pkgs.writeShellApplication { 52 + inherit runtimeInputs; 53 + name = "run-backend"; 54 + text = '' 55 + ${cdInto "backend"} 56 + ${pkgs.gleam}/bin/gleam run 57 + ''; 58 + })}/bin/run-backend"; 59 + }; 60 + lexgen = { 61 + type = "app"; 62 + program = "${(pkgs.writeShellApplication { 63 + inherit runtimeInputs; 64 + name = "run-lexgen"; 65 + text = '' 66 + ${cdInto "shared"} 67 + ${pkgs.gleam}/bin/gleam run -m alicia/lexgen -- --dir=${./lexicons} 68 + ''; 69 + })}/bin/run-lexgen"; 70 + }; 71 + }); 72 + }; 73 + }
+4
frontend/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
frontend/README.md
··· 1 + # sheetr_client 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/sheetr_client)](https://hex.pm/packages/sheetr_client) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/sheetr_client/) 5 + 6 + ```sh 7 + gleam add sheetr_client@1 8 + ``` 9 + ```gleam 10 + import sheetr_client 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/sheetr_client>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+19
frontend/gleam.toml
··· 1 + name = "sheetr_client" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + 18 + [dev-dependencies] 19 + gleeunit = ">= 1.0.0 and < 2.0.0"
+5
frontend/src/sheetr_client.gleam
··· 1 + import gleam/io 2 + 3 + pub fn main() -> Nil { 4 + io.println("Hello from sheetr_client!") 5 + }
+13
frontend/test/sheetr_client_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }
+10
lexicons/system/dnd5e/alignment/chaoticEvil.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.chaoticEvil", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Chaotic Evil'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/chaoticGood.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.chaoticGood", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Chaotic Good'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/chaoticNeutral.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.chaoticNeutral", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Chaotic Neutral'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/lawfulEvil.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.lawfulEvil", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Lawful Evil'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/lawfulGood.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.lawfulGood", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Lawful Good'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/lawfulNeutral.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.lawfulNeutral", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Lawful Neutral'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/neutralEvil.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.neutralEvil", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Neutral Evil'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/neutralGood.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.neutralGood", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Neutral Good'" 8 + } 9 + } 10 + }
+10
lexicons/system/dnd5e/alignment/neutralNeutral.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.alignment.neutralNeutral", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "Alignment token representing 'Neutral Neutral', also known as 'True Neutral'" 8 + } 9 + } 10 + }
+34
lexicons/system/dnd5e/description.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e.description", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "D&D 5e Character description.", 9 + "record": { 10 + "type": "object", 11 + "required": [], 12 + "properties": { 13 + "name": { 14 + "type": "string" 15 + }, 16 + "alignment": { 17 + "type": "string", 18 + "knownValues": [ 19 + "app.sheetr.system.dnd5e.alignment.lawfulGood", 20 + "app.sheetr.system.dnd5e.alignment.lawfulNeutral", 21 + "app.sheetr.system.dnd5e.alignment.lawfulEvil", 22 + "app.sheetr.system.dnd5e.alignment.neutralGood", 23 + "app.sheetr.system.dnd5e.alignment.neutralNeutral", 24 + "app.sheetr.system.dnd5e.alignment.neutralEvil", 25 + "app.sheetr.system.dnd5e.alignment.chaoticGood", 26 + "app.sheetr.system.dnd5e.alignment.chaoticNeutral", 27 + "app.sheetr.system.dnd5e.alignment.chaoticEvil" 28 + ] 29 + } 30 + } 31 + } 32 + } 33 + } 34 + }
+21
lexicons/system/dnd5e/system.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.sheetr.system.dnd5e", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "A system for Sheetr to support D&D 5th edition.", 9 + "record": { 10 + "type": "object", 11 + "required": [], 12 + "properties": { 13 + "description": { 14 + "type": "string", 15 + "format": "at-uri" 16 + } 17 + } 18 + } 19 + } 20 + } 21 + }
+4
shared/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
shared/README.md
··· 1 + # sheetr_shared 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/sheetr_shared)](https://hex.pm/packages/sheetr_shared) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/sheetr_shared/) 5 + 6 + ```sh 7 + gleam add sheetr_shared@1 8 + ``` 9 + ```gleam 10 + import sheetr_shared 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/sheetr_shared>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+20
shared/gleam.toml
··· 1 + name = "sheetr_shared" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + 18 + [dev-dependencies] 19 + gleeunit = ">= 1.0.0 and < 2.0.0" 20 + alicia_lexgen = { path = "../alicia/lexgen" }
+24
shared/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "alicia_lexgen", version = "1.0.0", build_tools = ["gleam"], requirements = ["argv", "gleam_json", "gleam_stdlib", "glint", "pprint", "simplifile", "snag"], source = "local", path = "../alicia/lexgen" }, 6 + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 7 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 8 + { name = "glam", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "237C2CE218A2A0A5D46D625F8EF5B78F964BC91018B78D692B17E1AB84295229" }, 9 + { name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" }, 10 + { name = "gleam_community_colour", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "E34DD2C896AC3792151EDA939DA435FF3B69922F33415ED3C4406C932FBE9634" }, 11 + { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, 12 + { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, 13 + { name = "gleam_stdlib", version = "0.67.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6CE3E4189A8B8EC2F73AB61A2FBDE49F159D6C9C61C49E3B3082E439F260D3D0" }, 14 + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 15 + { name = "glint", version = "1.2.1", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "2214C7CEFDE457CEE62140C3D4899B964E05236DA74E4243DFADF4AF29C382BB" }, 16 + { name = "pprint", version = "1.0.6", build_tools = ["gleam"], requirements = ["glam", "gleam_stdlib"], otp_app = "pprint", source = "hex", outer_checksum = "4E9B34AE03B2E81D60F230B9BAF1792BE1AC37AFB5564B8DEBEE56BAEC866B7D" }, 17 + { name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" }, 18 + { name = "snag", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "274F41D6C3ECF99F7686FDCE54183333E41D2C1CA5A3A673F9A8B2C7A4401077" }, 19 + ] 20 + 21 + [requirements] 22 + alicia_lexgen = { path = "../alicia/lexgen" } 23 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 24 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
+5
shared/src/sheetr_shared.gleam
··· 1 + import gleam/io 2 + 3 + pub fn main() -> Nil { 4 + io.println("Hello from sheetr_shared!") 5 + }
+13
shared/test/sheetr_shared_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }