{"id":28859704,"url":"https://github.com/knilink/reed.el","last_synced_at":"2026-05-05T13:39:40.298Z","repository":{"id":296708495,"uuid":"993771125","full_name":"knilink/reed.el","owner":"knilink","description":"write react text ui app with elisp","archived":false,"fork":false,"pushed_at":"2025-06-17T12:56:08.000Z","size":136,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-20T04:02:23.357Z","etag":null,"topics":["elisp","emacs","emacs-lisp","emacs-modules","react","rust","ui"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/knilink.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-31T13:42:43.000Z","updated_at":"2025-06-17T12:56:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"d3399e4f-527b-4134-b64a-2913accc221c","html_url":"https://github.com/knilink/reed.el","commit_stats":null,"previous_names":["knilink/reed.el"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/knilink/reed.el","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knilink%2Freed.el","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knilink%2Freed.el/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knilink%2Freed.el/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knilink%2Freed.el/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knilink","download_url":"https://codeload.github.com/knilink/reed.el/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knilink%2Freed.el/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32652168,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["elisp","emacs","emacs-lisp","emacs-modules","react","rust","ui"],"created_at":"2025-06-20T04:01:49.308Z","updated_at":"2026-05-05T13:39:40.293Z","avatar_url":"https://github.com/knilink.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React-like Emacs Elisp Design\n\nAim to streamline the process of building model interactive flex layout app that resemble the react development experience.\n\n## Get Started\n\n### Building Dynamic Module\nAssume you already have `rust` installed.\n```sh\ngit clone https://github.com/knilink/reed.el.git\ncd reed.el\ncargo build --release\n```\nIf success, `libreed.so` should be generated at `target/release/libreed.so`\n\n### First App\nCreate a file `my-first-app.el` with the following content\n```elisp\n;; -*- lexical-binding: t -*-\n(require 'reed-elx)\n(require 'reed-hooks)\n(require 'reed-style)\n(require 'reed-render-helper)\n\n(fc! Container (children)\n     (elx!\n      (div\n       :style (style!*\n               (size (width . 100%) (height . AUTO))\n               (flex_direction . 'Column)\n               (align_items . '(Center)))\n       ({} children))))\n\n(fc! Row (children)\n     (elx!\n      (div\n       :style (style!*\n               (justify_content . '(Center))\n               (size (width . 100%) (height . AUTO)))\n       ({} children))))\n\n(fc! App ()\n     (let ((current-time-sig (use-signal (lambda () (current-time-string))))\n           (buffer-name-ref (use-ref (lambda () (buffer-name)))))\n       (use-hook-with-cleanup\n        (lambda ()\n          (run-with-timer\n           0 1\n           (lambda ()\n             (funcall current-time-sig (current-time-string))\n             (reed-handle-render (funcall buffer-name-ref)))))\n        (lambda (timer-handle) (cancel-timer timer-handle)))\n       (elx!\n        (Container\n         (Row (p (span\n                  :face '(:foreground \"red\" :background \"yellow\")\n                  \"Hello, \")\n                 (span\n                  :face '(:foreground \"green\" :background \"blue\")\n                  \"World!\")))\n         (Row (p \"Time is now: \" ({} (funcall current-time-sig))))))))\n\n(reed-render-buffer \"my-first-app\" #'App)\n```\nThen run the app with\n```sh\nemacs -l reed.el/target/release/libreed.so -L reed.el/lisp -l ./my-first-app.el -Q\n```\nIf everything goes well, result bellow can be seen.\n![image](https://github.com/user-attachments/assets/b1e96488-8c7b-4631-9826-a0ca4367d73d)\n\n\n\n## Example App\n- [oh-puhn-text-ui.el](https://github.com/knilink/oh-puhn-text-ui.el): A more interactive text ui llm frontend.\n\n## Elements\n### Tags\n- **`div`**\n   The fundamental layout container. Used for structuring UI components with flexible styling options.\n\n- **`p`** (Paragraph)\n   Text container that serves as a leaf node in the layout hierarchy.\n   - Children can only be `span` elements\n   - Primarily used for text content with styling\n\n- **`span`**\n   Inline text styling element.\n   - Only valid as a child of `p` elements\n   - Supports only the `face` property for text styling\n   - Used for applying different styles to portions of text\n\n### Attributes\n- **`style`**\n  S-expressions serialized [taffy](https://github.com/DioxusLabs/taffy) layout Style.\n\n- **`face`**\n  Face text property for styling text.\n\n- **`ref`**\n  Provides a way to obtain element id, subsequently can be use to retrieve element position etc. A ref is created with `use-ref` hook.\n\n### Event Listeners\nEvent listeners will be triggered accordingly depends on event type and position attribute, event payload will be forwarded directly to listeners.\n- **`onclick`**: Triggered when cursor event `click` is emitted and the position is inside the target element.\n- **`onfocus`**: Triggered when click inside an element while the target element isn't focused.\n- **`onblur`**: Triggered when click outside an element while the target element is focused.\n- **`onhover`**: Triggered when cursor `move` is emitted and the position is inside the target element.\n- **`onleave`**: Triggered when cursor `move` is emitted and the position is outside the target element.\n\nTo emit a cursor event:\n\n```el\n(reed-handle-cursor-event my-registered-app-name 'click my-event-position my-event-payload)\n(reed-handle-cursor-event \"my-first-app\" 'move 123 '(:position 123))\n```\n\n## Elx Macros\n\n### **elx!**\nProvides a convenient way to create react element.\nTag starts with lower case will be treated as a native element tag otherwise a component.\nSyntax: `(elx! (tag ,@attr-plist ,@children) (Component ,@props-plist ,@children) \u0026rest)`\n\n### **{}** Curly Brace Syntax\nThe body of `{}` will be treated as dynamic content and evaluated during runtime.\nFor example,\n```elisp\n(elx! (div ({} (if flag (elx! (p \"true\")) \"false\"))))\n```\nis equivalent to\n```jsx\n\u003cdiv\u003e{flag ? \u003cp\u003etrue\u003c/p\u003e : \"false\"}\u003c/div\u003e\n```\n\n### **fc!** Functional Component\nUsed to define reusable React-like components that returns a element, use keyword symbol to define alias\nExample:\n```\n(fc! MyComponent (foo :bar bar-alias)\n  (message \"[bar-alias] %s\" bar-alias)\n  (elx! ...))\n\n(elx! (MyComponent :foo \"foo-value\" :bar \"bar-value\"))\n```\n\n## Style\nS-expressions serialized [taffy](https://github.com/DioxusLabs/taffy) layout Style.\n\n### Length\nTo create the 4 different type of serialized length attribute, use `reed-taffy-length`.\n- percent: `(reed-taffy-length 'percent 0.5)`\n- length: `(reed-taffy-length 'length 80.0)`\n- auto: `(reed-taffy-length 'auto 0.0)`\n- zero: `(reed-taffy-length 'zero 0.0)`\n\n### Optional Attribute\nWhen specifying attribute declared as `Optional` in rust, e.g. [`justify_content: Option\u003cJustifyContent\u003e`](https://github.com/DioxusLabs/taffy/blob/33adacc8083672045b89fc3b7e796b8ec95e258d/src/compute/flexbox.rs#L150). The value need to be wrapped with brackets `()` to represent `Some(attr)`, e.g. `(justify_content . '(Center))`.\n\n### **style!** macro\nHelper to compose style property, main to help expanding short handed length. Length value needs to be ended either `pt` or `%`, e.g.\n```elisp\n(style!\n  (margin\n    (left . 100pt)\n    (right . 20%)\n    (top . ZERO)\n    (bottom . AUTO)))\n```\n\n### **style!\\*** macro\nSame as `style!` but style will be expanded into static string during macro expansion. Recommended if style is static throughout the entire component lifetime.\n\n## Hooks\n\n### **use-signal**\n```\n(fc! App ()\n     (let ((count-sig (use-signal (lambda() 0))))\n       (elx!\n        (p\n         \"Count: \"\n         ({} (funcall count-sig)))\n        (p \" \")\n        (p\n         :face '(:background \"gray80\" :box (:line-width 1 :style released-button))\n         :onclick (lambda (e)\n                    (funcall count-sig (1+ (funcall count-sig))))\n           \"click me!\"))))\n```\n\n### **use-effect**\n```elisp\n(fc! App ()\n     (let ((element-ref (use-ref (lambda())))\n           (location-sig (use-signal (lambda())))\n           (buffer-name-ref (use-ref (lambda () (buffer-name)))))\n       (use-effect\n        (lambda ()\n          (run-with-timer\n           0 nil\n           (lambda ()\n             (unless (funcall location-sig)\n               (funcall\n                location-sig\n                (reed-get-absolut-location\n                 (funcall buffer-name-ref)\n                 (funcall element-ref)))\n               (reed-handle-render (funcall buffer-name-ref)))))))\n       (elx!\n        (div\n         :style (style!*\n                 (padding (left . ZERO) (right . ZERO) (top . 10pt) (bottom . ZERO))\n                 (justify_content . '(Center))\n                 (size (width . 100%) (height . AUTO)))\n         (p\n          :ref element-ref\n          ({} (format \"this element located at %s\" (funcall location-sig))))))))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknilink%2Freed.el","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknilink%2Freed.el","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknilink%2Freed.el/lists"}