{"id":16369684,"url":"https://github.com/dcsunset/modaled","last_synced_at":"2026-03-03T17:30:16.431Z","repository":{"id":166477547,"uuid":"637897742","full_name":"DCsunset/modaled","owner":"DCsunset","description":"Build your own minor modes for modal editing in Emacs","archived":false,"fork":false,"pushed_at":"2025-01-24T20:04:39.000Z","size":137,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-24T20:28:23.322Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Emacs Lisp","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/DCsunset.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2023-05-08T16:37:10.000Z","updated_at":"2025-01-24T20:04:43.000Z","dependencies_parsed_at":"2023-10-15T04:21:03.598Z","dependency_job_id":"7ccccc71-c9e4-4c45-86c6-374481d35c66","html_url":"https://github.com/DCsunset/modaled","commit_stats":null,"previous_names":["DCsunset/modaled"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCsunset%2Fmodaled","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCsunset%2Fmodaled/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCsunset%2Fmodaled/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCsunset%2Fmodaled/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DCsunset","download_url":"https://codeload.github.com/DCsunset/modaled/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239885403,"owners_count":19713294,"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":[],"created_at":"2024-10-11T02:56:02.044Z","updated_at":"2025-02-20T17:40:56.862Z","avatar_url":"https://github.com/DCsunset.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Modaled\n\n[![MELPA](https://melpa.org/packages/modaled-badge.svg)](https://melpa.org/#/modaled)\n[![License](https://badgen.net/github/license/dcsunset/modaled)](https://github.com/DCsunset/modaled)\n\nModaled is a fully customizable modal editing package for Emacs.\n\nUnlike many popular modal editing modes,\nModaled doesn't provide any keybindings\nand nor does it come with any minor modes by default.\n\nThis package provides util functions to help you build your own keybindings and minor modes.\nIt is inspired by [evil](https://github.com/emacs-evil/evil), [modalka](https://github.com/mrkkrp/modalka), and [meow](https://github.com/meow-edit/meow),\nbut it supports building your own minor modes without any predefined states.\nCompared to modalka, Modaled supports creating multiple minor modes and comes with no default mode,\nwhich makes it possible to implement different states in modal editing and different keybindings for different situations.\nCompared to meow, Modaled has no default config and greatly simplifies the design for customization from scratch.\nYou can freely define many different states and their corresponding minor modes in Modaled.\n\n## Installation\n\nThis package is available on MELPA.\nYou can install it by `M-x package-install RET modaled RET` or use [use-package](https://github.com/jwiegley/use-package) to install it.\n\nTo install the package manually, download this repo to Emacs' `load-path`.\nThen add the following to the config file:\n\n```emacs-lisp\n(require 'modaled)\n```\n\nIf you are using Nix, this package is also available in [NUR](https://nur.nix-community.org/repos/dcsunset/).\n\nIf you are using Nix flake,\nyou can directly install the latest version (main branch) to your inputs and use it in emacsPackages:\n```nix\n{\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-unstable\";\n    modaled = {\n      url = \"github:DCsunset/modaled\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n\n  outputs = inputs@{ self, nixpkgs, ... }:\n    let\n      system = \"x86_64-linux\";\n    in {\n      nixosConfigurations.\"\u003chostname\u003e\" = nixpkgs.lib.nixosSystem {\n        inherit system;\n        modules = [\n          ({ config, pkgs, ... }: {\n            home-manager.users.\"\u003cuser\u003e\" = {\n              programs.emacs = {\n                enable = true;\n                extraPackages = epkgs: [\n                  inputs.modaled.packages.${system}.default\n                ];\n              };\n            };\n          })\n          ./configuration.nix\n        ];\n      };\n    };\n}\n```\n\n\n## Usage\n\n### Basics\n\nYou can define your own state and keybindings by `modaled-define-state STATE \u0026rest BODY`.\nYou can set the lighter or cursor type in body.\nIt will create a minor mode `modaled-STATE-state-mode` and keymap `modaled-STATE-state-keymap` automatically.\n\nThe mode is added to `emulation-mode-map-alists` by default to increase its priority unless `:no-emulation` is specified.\n\nBesides, the keymap is suppressed by default, which means using undefined keys will result in an error instead of inserting the raw character\nunless `:no-suppress` is set to true.\n\nYou can set up the keymap by `modaled-define-keys` (or directly using `define-key`) and enable the minor mode whenever you want.\n(Note: `modaled-define-state-keys`, `modaled-define-substate-keys` and `modaled-define-global-keys` are now deprecated)\n\n```emacs-lisp\n(modaled-define-state \"normal\"\n  ; use no-suppress to allow inserting the char if keybinding is not defined\n  ; :no-suppress t\n  ;; to prevent it from being added to emulation-mode-map-alist\n  ; :no-emulation t\n  :lighter \"[NOR]\"\n  :cursor-type 'box)\n; modaled-define-keys supports binding keys for multiple states or substates or globally\n(modaled-define-keys\n  :states '(\"normal\")\n  :bind\n  `((\"h\" . backward-char)\n    (\"l\" . forward-char)\n    (\"k\" . previous-line)\n    (\"j\" . next-line)\n    ;; you can also bind multiple keys to a command\n    ((\"a\" \"b\") . ,(lambda () (interactive) (message \"hello\")))))\n\n(modaled-define-state \"insert\"\n  :sparse t\n  ;; insert state must be no-suppress to support inserting char\n  :no-suppress t\n  :cursor-type 'bar\n  :lighter \"[INS]\")\n(modaled-define-keys\n  :states '(\"insert\" \"normal\")\n  ; bind a key to change back to default state from other states\n  :bind\n  '((\"\u003cescape\u003e\" . modaled-set-default-state)))\n```\n\nTo enable Modaled globally, you will need set init state and call `modaled-setup` function.\nThis will create hooks to enable the init state on buffer creation and major mode change.\nYou can set one global init using a custom function:\n```emacs-lisp\n;; set init state using a function\n(setq modaled-init-state-fn\n      (lambda ()\n        (if (eq major-mode 'vterm-mode)\n            \"insert\")\n          \"normal\"))\n;; enable globally based on init state\n(modaled-setup)\n\n;; manually switch to it\n(modaled-set-init-state)\n```\n\nYou can optionally set a function for main state.\nIt is different from the init state as it's not auto enabled on major mode change.\nInstead, it is just a helper to easily switch to it:\n```emacs-lisp\n(setq modaled-main-state-fn (lambda () \"normal\"))\n;; manually switch to it\n(modaled-set-main-state)\n```\n\nTo change the current state, you can use `modaled-set-state`, `modaled-set-default-state` or `modaled-set-main-state`:\n```emacs-lisp\n;; Disable all states\n(modaled-set-state nil)\n;; Enable/change to a defined state\n(modaled-set-state \"normal\")\n;; Reset to init state\n(modaled-set-init-state)\n;; Set to main state\n(modaled-set-main-state)\n```\n\n\nTo see supported arguments for each function, use `describe-function` (usually bound to `C-h f`) to see the docs.\n\n\n### Substates\n\nModaled also supports defining substates.\n\nIn Modaled, states are managed like a major mode (which means only one state should be enabled),\nwhile substates are unmanaged like a minor mode (multiple substates can be active at the same time).\nThe current state is stored in variable `modaled-state`.\nYou should only use `modaled-set-state` or `modaled-set-default-state` to change a state,\nbut you can enable substates by calling the minor mode function directly.\n\nSimilarly, substates are defined by `modaled-define-substate` and keybindings are defined by `modaled-define-keys`.\nThe function parameters are the same as those for states.\nThe corresponding minor mode and keymap are `modaled-SUBSTATE-substate-mode` and `modaled-SUBSTATE-substate-keymap`.\n\nSubstates are more flexible than states as you manage them directly.\nThere's also a helper function `modaled-enable-substate-on-state-change` to enable a substate when the state changes.\nThe example below shows how to define a substate only for org-mode and normal state:\n\n```emacs-lisp\n(modaled-define-substate \"org\")\n(modaled-define-keys\n  :substates '(\"org\")\n  :bind\n  '((\"SPC o\" . org-open-at-point)))\n; enable org substate when in normal state\n(modaled-enable-substate-on-state-change\n  \"org\"\n  :states '(\"normal\")\n  :major '(org-mode))\n```\n\n\n## Migration\n\n### Migration from v0.8 to v0.9\n\nStarting from v0.9, modaled uses `keymap-set` instead of `define-key` when binding keys.\nIt means that the keys used in `:bind` must satisfy `key-valid-p` and `kbd` should no longer be used.\n\nFor example, the following keys should be changed:\n- `[escape]` -\u003e `\"\u003cescape\u003e\"`\n- `(kbd \"M-\u003creturn\u003e\")` -\u003e `\"M-\u003creturn\u003e\"`\n- `\"a b\"` -\u003e `\"a SPC b\"`\n\n\n## License\n\nAGPL-3.0.\n\n    Copyright (C) 2023-2025  DCsunset\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcsunset%2Fmodaled","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcsunset%2Fmodaled","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcsunset%2Fmodaled/lists"}