{"id":20805742,"url":"https://github.com/danbugs/dancing_web","last_synced_at":"2025-05-07T04:23:25.694Z","repository":{"id":47176626,"uuid":"398122149","full_name":"danbugs/dancing_web","owner":"danbugs","description":"This is a proof-of-concept of a modern C web-framework that compiles to Wasm and is used for building user interfaces.","archived":false,"fork":false,"pushed_at":"2021-09-11T19:04:08.000Z","size":463,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-31T06:21:47.198Z","etag":null,"topics":["c","web-framework","webassembly"],"latest_commit_sha":null,"homepage":"https://danbugs.github.io/dancing_web","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danbugs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-20T01:37:09.000Z","updated_at":"2024-02-12T19:23:09.000Z","dependencies_parsed_at":"2022-09-12T18:40:40.759Z","dependency_job_id":null,"html_url":"https://github.com/danbugs/dancing_web","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbugs%2Fdancing_web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbugs%2Fdancing_web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbugs%2Fdancing_web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danbugs%2Fdancing_web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danbugs","download_url":"https://codeload.github.com/danbugs/dancing_web/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252811228,"owners_count":21807910,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","web-framework","webassembly"],"created_at":"2024-11-17T19:16:08.702Z","updated_at":"2025-05-07T04:23:25.672Z","avatar_url":"https://github.com/danbugs.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DanCing Web 💃🕸 (DCW)\r\n\r\n## Getting Started\r\n\r\nDancing Web is now distributed with the [Tarantella Package Manager](https://github.com/danbugs/tarantella) — a tool I've made to simplify setup of projects like these! \r\n\r\nTo install the Tarantella Package Manager, you need the Rust toolchain — for instructions on how to install it, see [this](https://www.rust-lang.org/tools/install). Once you're done with that, you can install Tarantella by running: `cargo install tarantella`.\r\n\r\nTo start-off, setup a new main module WASM project with: `tapm new my-first-project`. This will start a new C WASM project with:\r\n- a build folder, \r\n- a dependencies folder,\r\n- an index.html,\r\n- a `Makefile`,\r\n- a releases folder, \r\n- a src folder with some starting code in `main.c`, and a\r\n- `Tarantella.toml` file.\r\n\r\nNext, add the latest version of Dancing Web as a dependency with: `tapm add \"danbugs/dancing_web\"`. This will automatically download DCW to your dependencies folder, add it to your `Tarantella.toml` list of dependencies, and append `dependencies/dcw/dcw.o` to the `DEPENDENCIES` variable of your `Makefile` to facilitate compilation.\r\n\r\nNow, let's create our first HTML-equivalent \"C Markdown Language\" file (`.cml`) to be rendered from C! First, create a folder called `frontend/` and, inside it, create a `hello_world.cml` file with the following code:\r\n\r\n```HTML\r\nHTML(\r\n\u003cdiv\u003e\r\n  \u003ch1 class=\"red\"\u003e${hello_world()}$\u003c/h1\u003e\r\n\u003c/div\u003e\r\n\u003cstyle\u003e\r\n  .red {\r\n    color: red;\r\n  }\r\n\u003c/style\u003e\r\n);\r\n```\r\n\r\nYou can call C functions within the `${` and `}$` markers (called renderable markers) or within the `$E` and `}$` markers (called executable markers).\r\n  - Renderable markers are for C functions that return content that is meant to be rendered, and\r\n  - Executable markers are for C functions that are meant to be called but don't necessarily render HTML (e.g., functions within a button's `onclick`).\r\n\r\nNext, in our `main.c`, replace its' contents with:\r\n\r\n```C\r\n#include \u003cstdio.h\u003e\r\n#include \u003cstdlib.h\u003e\r\n#include \u003cemscripten.h\u003e\r\n#include \"../dependencies/dcw/dcw.h\"\r\n\r\nextern void display_html(html_t raw_html);\r\n// ^^^ rendering function from DCW that is \r\n// external to our main module\r\n\r\n                     //    this is compulsory for functions\r\nEMSCRIPTEN_KEEPALIVE // \u003c- you intend to call from HTML\r\nhtml_t hello_world()\r\n{\r\n    // ^^^ functions that return content to be rendered \r\n    // must return the html_t or char* types\r\n\r\n    html_t tmp;\r\n    // ^^^ you don't need to free this malloc,\r\n    // the framework will do it for you.\r\n\r\n    asprintf(\u0026tmp, \"%s\", \"Hello, World!\");\r\n    // ^^^ this works because html_t expands \r\n    // to char*\r\n\r\n    return tmp;\r\n}\r\n// ^^^ this is the hello_world function \r\n// we are calling from our .cml file\r\n\r\n\r\nint main()\r\n{\r\n    html_t html =\r\n#include \"../frontend/hello_world.cml\"\r\n    ;\r\n    // ^^^ this brings the .cml component into scope!\r\n    // things to note:\r\n    // - the #include statement must be in a line of its' \r\n    // own, and\r\n    // - the ';' is optional but, if present, must also be \r\n    // on a line of its' own \r\n    // (usually, I like to include the ';' because it \r\n    // helps with formatting)\r\n\r\n    display_html(html);\r\n    // ^^^ this will render our html.\r\n}\r\n```\r\n\r\nTo finish off, in your `Makefile`, append `--js-library dependencies/dcw/dcw.js` to your `EMCC_FLAGS` variable. It should look like this: `EMCC_CFLAGS=-s MAIN_MODULE=1 --js-library dependencies/dcw/dcw.js`.\r\n\r\nNow, let's build the project with: `tapm build`. This will use the `Makefile` to compile the project to the `build/` folder.\r\n\r\nTo test the project, run: `tapm run`. Navigating to `http://127.0.0.1:4000` in your browser, you should see:\r\n\r\n![getting-started-1](https://i.imgur.com/zGWqSow.png)\r\n\r\nFor more complex examples, view the project in the `examples/` folder of the repo!\r\n\r\n## Appendix\r\n\r\n### Why use `.cml` files?\r\n\r\nIf you are using an IDE, to avoid C auto-formatting the HTML to something that doesn't work, you can create separate files for it. These files can have any extension but I've been using `.cml` (i.e., C Markup Language).\r\n\r\nIn addition, to get code auto-formatting working for `.cml` files on VSCode, open `settings.json` and add the following at the end of it:\r\n\r\n```\r\n\"files.associations\": {\r\n    \"*.cml\": \"html\"\r\n},\r\n```\r\n\r\n### An alternative to `.cml` files\r\n\r\nYou can write HTML directly within C, like this:\r\n\r\n```\r\nhtml_t html = HTML(\r\n\u003cdiv\u003e\r\n  \u003ch1 class=\"red\"\u003e${hello_world()}$\u003c/h1\u003e\r\n\u003c/div\u003e\r\n\u003cstyle\u003e\r\n  .red {\r\n    color: red;\r\n  }\r\n\u003c/style\u003e\r\n);\r\n```\r\n\r\n...or:\r\n\r\n```\r\nhtml_t html = HTMLIFY(\" \\\r\n\u003cdiv\u003e \\\r\n  \u003ch1 class='red'\u003e${hello_world()}$\u003c/h1\u003e \\\r\n\u003c/div\u003e \\\r\n\u003cstyle\u003e \\\r\n  .red { \\\r\n    color: red; \\\r\n  } \\\r\n\u003c/style\u003e \\\r\n\");\r\n```\r\n\r\n### Returning other types as `html_t`\r\n\r\n```\r\nEMSCRIPTEN_KEEPALIVE\r\nhtml_t add_two_numbers(int a, int b)\r\n{\r\n    char *tmp;\r\n    int result = a + b;\r\n    asprintf(\u0026tmp, \"%d\", result);\r\n    return tmp;\r\n}\r\n```\r\n\r\n### Returning unmalloc-ed items\r\n\r\nFor the record, a function like:\r\n\r\n```\r\nEMSCRIPTEN_KEEPALIVE\r\nhtml_t hello_world()\r\n{\r\n    return \"Hello, World!\";\r\n}\r\n```\r\n... would also work. Thing is, it is not the best because the framework will try to free a pointer that wasn't malloc-ed. While this doesn't cause an error, I wouldn't call it a best practice.\r\n\r\n### Notes to Self\r\n\r\nTo check for any problems (i.e., mem leaks and whatnot) in the code at runtime, compile like so: `emcc hello_world.c ../../src/dcw.c --js-library ../../src/dcw.js -fsanitize=address -s ALLOW_MEMORY_GROWTH -s INITIAL_MEMORY=285212672 -gsource-map --source-map-base http://127.0.0.1:4000`. After that, run with: `tapm run`.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanbugs%2Fdancing_web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanbugs%2Fdancing_web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanbugs%2Fdancing_web/lists"}