Add CONFIG.md and PCN.md documentation
This commit is contained in:
		
							
								
								
									
										111
									
								
								docs/CONFIG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								docs/CONFIG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| # Configuration | ||||
|  | ||||
| [Folders](#folders) ⬥  | ||||
| [settings.py](#settingspy) ⬥  | ||||
| [accounts.json](#accountsjson) | ||||
|  | ||||
|  | ||||
| ## Folders | ||||
|  | ||||
| On Linux, the folders are: | ||||
|  | ||||
| - `$XDG_CONFIG_HOME/mirage` or `~/.config/mirage` for config files | ||||
| - `$XDG_DATA_HOME/mirage` or `~/.local/share/mirage` for user data | ||||
| - `$XDG_CACHE_HOME/mirage` or `~/.cache/mirage` for cache data | ||||
|  | ||||
| For Flatpak installations, the folders are: | ||||
|  | ||||
| - `~/.var/app/io.github.mirukana.mirage/config/mirage` for config files | ||||
| - `~/.var/app/io.github.mirukana.mirage/data/mirage` for user data | ||||
| - `~/.var/app/io.github.mirukana.mirage/cache/mirage` for cache data | ||||
|  | ||||
| The folder locations can also be overriden by these environment variables: | ||||
|  | ||||
| - `$MIRAGE_CONFIG_DIR` for config files | ||||
| - `$MIRAGE_DATA_DIR` for user data | ||||
| - `$MIRAGE_CACHE_DIR` for cache data | ||||
|  | ||||
| The user data folder contains saved encryption data, interface states and | ||||
| [themes](THEMING.md).   | ||||
| The cache data folder contains downloaded files and thumbnails. | ||||
|  | ||||
|  | ||||
| ## settings.py | ||||
|  | ||||
| A file written in the [PCN format](PCN.md), located in the  | ||||
| [config folder](#folders), which is manually created by the user to configure  | ||||
| the application's behavior. | ||||
|  | ||||
| The default `settings.py`, used when no user-written file exists, documents all  | ||||
| the possible options and can be found at: | ||||
|  | ||||
| - [`src/config/settings.py`][1] in the repository | ||||
| - `/usr/share/examples/mirage/settings.py` on Linux installations | ||||
| - `~/.local/share/flatpak/app/io.github.mirukana.mirage/current/active/files/share/examples/mirage/settings.py` for per-user Flatpak installations | ||||
| - `/var/lib/flatpak/app/io.github.mirukana.mirage/current/active/files/share/examples/mirage/settings.py` for system-wide Flatpak installations | ||||
|  | ||||
| Rather than copying the entire default file, it is recommended to  | ||||
| [`include`](PCN.md#including-built-in-files) it and only add the settings  | ||||
| you want to override. | ||||
| For example, a user settings file that only changes the theme and some keybinds | ||||
| could look like this: | ||||
|  | ||||
| ```python3 | ||||
| self.include_builtin("config/settings.py") | ||||
|  | ||||
| class General: | ||||
|     theme: str = "Glass.qpl" | ||||
|  | ||||
| class Keys: | ||||
|     reset_zoom = ["Ctrl+Backspace"] | ||||
|  | ||||
|     class Messages: | ||||
|         open_links_files            = ["Ctrl+Shift+O"] | ||||
|         open_links_files_externally = ["Ctrl+O"] | ||||
| ``` | ||||
|  | ||||
| When this file is saved while the application is running, the settings will | ||||
| automatically be reloaded, except for some options which require a restart. | ||||
| The default `settings.py` indicates which options require a restart. | ||||
|  | ||||
| [1]: https://github.com/mirukana/mirage/tree/master/src/config/settings.py | ||||
|  | ||||
|  | ||||
| ## accounts.json | ||||
|  | ||||
| This JSON file, located in the [config folder](#folders), is managed by the  | ||||
| interface and doesn't need to be manually edited, except for changing account  | ||||
| positions via their `order` key.  | ||||
| The `order` key can be any number. If multiple accounts have the same `order`, | ||||
| they are sorted lexically by user ID. | ||||
|  | ||||
| This file should never be shared, as anyone obtaining your access tokens will | ||||
| be able to use your accounts. | ||||
| Within the application, from the Sessions tab of your account's settings, | ||||
| access tokens can be revoked by signing out sessions,  | ||||
| provided you have the account's password. | ||||
|  | ||||
| Example file: | ||||
|  | ||||
| ```json | ||||
| { | ||||
|     "@user_id:example.org": { | ||||
|         "device_id": "ABCDEFGHIJ", | ||||
|         "enabled": true, | ||||
|         "homeserver": "https://example.org", | ||||
|         "order": 0, | ||||
|         "presence": "online", | ||||
|         "status_msg": "", | ||||
|         "token": "<a long access token>" | ||||
|     }, | ||||
|     "@account_2:example.org": { | ||||
|         "device_id": "KLMNOPQRST", | ||||
|         "enabled": true, | ||||
|         "homeserver": "https://example.org", | ||||
|         "order": 1, | ||||
|         "presence": "invisible", | ||||
|         "status_msg": "", | ||||
|         "token": "<a long access token>" | ||||
|     } | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										406
									
								
								docs/PCN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										406
									
								
								docs/PCN.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,406 @@ | ||||
| # PCN File Format | ||||
|  | ||||
| Config and theme files are written in the PCN (Python Config Notation) format, | ||||
| which are organized in a hierarchy of sections and properties. | ||||
| PCN files can also contain usual Python code, such as imports and | ||||
| custom functions. | ||||
|  | ||||
| - [Overview](#overview) | ||||
| - [Sections](#sections) | ||||
|   - [Including Built-in Files](#including-built-in-files) | ||||
|   - [Including User Files](#including-user-files) | ||||
|   - [Inheritance](#inheritance) | ||||
| - [Properties](#properties) | ||||
|   - [Common Types](#common-types) | ||||
|   - [Expressions](#expressions) | ||||
|   - [Section Access](#section-access) | ||||
|   - [Bracket Access](#bracket-access) | ||||
| - [GUI files](#gui-files) | ||||
|  | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| ```python3 | ||||
| # Lines starting with a "#" are considered comments.  | ||||
| # Comments can also be added to the end of normal lines. | ||||
|  | ||||
| # Sections can contain indented properties, other sections or functions. | ||||
| class Example: | ||||
|     # Properties are written as "name: type = value", examples: | ||||
|     integer_number:   int       = 5 | ||||
|     decimal_number:   float     = 2.5 | ||||
|     character_string: str       = "Sample text" | ||||
|     boolean:          bool      = True  # or False | ||||
|     string_list:      List[str] = ["foo", "bar", "baz"] | ||||
|  | ||||
|     # Property values can be any Python expression, e.g. math operations: | ||||
|     other_number: int  = (5 * 4) / 2 | ||||
|  | ||||
|     # "self" points to the current section, Example, containing other_number. | ||||
|     above_10: bool = self.other_number > 10  # result: False | ||||
|  | ||||
|     class Names: | ||||
|         # Property names with characters outside of a-z A-Z 0-9 _ need quoting: | ||||
|         "@alice:example.org": str = "Alice" | ||||
|         "@bob:example.org":   str = "Bob" | ||||
|  | ||||
|         # Section content can also be accessed with the "self[name]" syntax, | ||||
|         # which works with quoted properties like the ones above: | ||||
|         alice_name: str = self["@alice:example.org"]  # result: Alice | ||||
|  | ||||
|         # Child sections are also accessible from "self": | ||||
|         child_integer: int = self.Test.integer  # result: 5 | ||||
|  | ||||
|         class Test: | ||||
|             # "parent" refers to the section parent of this one, here "Names". | ||||
|             alice_name: str = parent["@alice:example.org"]  # result: "Alice" | ||||
|             integer:    int = parent.parent.integer_number  # Example.integer_number, which is 5 | ||||
|  | ||||
|             # Top-level sections can also be accessed directly by names: | ||||
|             alice_name_2: str = Example.Names["@alice:example.org"] | ||||
|             integer_2:    int = Example.integer_number | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ## Sections | ||||
|  | ||||
| Sections are defined like Python classes, and can contain properties,  | ||||
| other sections, or Python functions.   | ||||
| A section's name should be written as `CamelCase`, and can only contain | ||||
| letters, digits and underscores.   | ||||
| The content of a section must be indented by that section's indentation plus  | ||||
| four spaces: | ||||
|  | ||||
| ```python3 | ||||
| class FirstSection: | ||||
|     content_spaces: int = 0 + 4 | ||||
|  | ||||
|     class SectionInsideFirst: | ||||
|         content_spaces: int = 4 + 4 | ||||
|  | ||||
| class SecondSection: | ||||
|     content_spaces: int = 0 + 4 | ||||
| ``` | ||||
|  | ||||
| Empty sections can be created using the `pass` keyword: | ||||
|  | ||||
| ```python3 | ||||
| class Empty: | ||||
|     pass | ||||
| ``` | ||||
|  | ||||
| ### Including Built-in Files | ||||
|  | ||||
| A section, including the file's root (which is treated as a section) | ||||
| can include files that are supplied by the application using the  | ||||
| `self.include_builtin(path)` function.  | ||||
|  | ||||
| `path` is the relative path to a file in the application's source folder,  | ||||
| for example `self.include_builtin("config/settings.py")` refers to  | ||||
| [`src/config/settings.py`][1]. | ||||
|  | ||||
| The sections and properties from the included file will be recursively merged, | ||||
| see [Including User Files](#including-user-files) for an example. | ||||
|  | ||||
| [1]: https://github.com/mirukana/mirage/tree/master/src/config/settings.py | ||||
|  | ||||
| ### Including User Files | ||||
|  | ||||
| Similar to [including built-in files](#including-built-in-files), user-written  | ||||
| local files can be included with `self.include_file(path)`, where `path` | ||||
| is an absolute or relative (from the current file's directory) file path. | ||||
|  | ||||
| Example with two files, `a.py`: | ||||
|  | ||||
| ```python3 | ||||
| self.include_file("b.py") | ||||
|  | ||||
| class Shared: | ||||
|     text:           str = "Sample" | ||||
|     gets_overriden: str = "A" | ||||
|  | ||||
|     class FromA:  | ||||
|         number: int = 1 | ||||
| ``` | ||||
|  | ||||
| and `b.py`: | ||||
|  | ||||
| ```python3 | ||||
| class Shared: | ||||
|     gets_overriden: str = "B" | ||||
|  | ||||
|     class FromB:  | ||||
|         number: int = 2 | ||||
| ``` | ||||
|  | ||||
| This results in a merged PCN looking like so: | ||||
|  | ||||
| ```python3 | ||||
| class Shared: | ||||
|     text:           str = "Sample" | ||||
|     gets_overriden: str = "B" | ||||
|  | ||||
|     class FromA:  | ||||
|         number: int = 1 | ||||
|  | ||||
|     class FromB:  | ||||
|         number: int = 2 | ||||
| ``` | ||||
|  | ||||
| Include functions can also be used inside a section other than the root. | ||||
| If `a.py` had the include line inside `Shared`, the result would be: | ||||
|  | ||||
| ```python3 | ||||
| class Shared: | ||||
|     text:           str = "Sample" | ||||
|     gets_overriden: str = "A" | ||||
|  | ||||
|     class FromA:  | ||||
|         number: int = 1 | ||||
|  | ||||
|     class Shared: | ||||
|         gets_overriden: str = "B" | ||||
|  | ||||
|         class FromB:  | ||||
|             number: int = 2 | ||||
| ``` | ||||
|  | ||||
| ### Inheritance | ||||
|  | ||||
| Like other Python classes, sections can inherit from other sections.  | ||||
| Unlike including files, sections are not merged recursively. | ||||
|  | ||||
| This file: | ||||
|  | ||||
| ```python3 | ||||
| class Mixin: | ||||
|     first:  bool = True | ||||
|     second: bool = False | ||||
|  | ||||
| class First(Mixin): | ||||
|     pass | ||||
|  | ||||
| class Second(Mixin): | ||||
|     third: int = 100 | ||||
| ``` | ||||
|  | ||||
| Would be equivalent to: | ||||
|  | ||||
| ```python3 | ||||
| class First(Mixin): | ||||
|     first:  bool = True | ||||
|     second: bool = False | ||||
|  | ||||
| class Second(Mixin): | ||||
|     first:  bool = True | ||||
|     second: bool = False | ||||
|     third:  int  = 100 | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ## Properties | ||||
|  | ||||
| Properties have a name, optional type annotation and value.  | ||||
| Standard property names should be written in `snake_case`. | ||||
| In most cases, it is recommended to include type annotations, to make clear  | ||||
| what a property's value should be: | ||||
|  | ||||
| ```python3 | ||||
|     with_type:    int            = 3 | ||||
|     complex_type: Dict[str, int] = {"abc": 1, "def": 2, "ghi": 3} | ||||
|  | ||||
|     any_type: Any = None | ||||
|     same_as_above = None | ||||
| ``` | ||||
|  | ||||
| If the property's name starts with a digit or contains characters other than | ||||
| letters, digits or underscores, that name must be quoted: | ||||
|  | ||||
| ```python3 | ||||
| "!alice:example.org" = "Alice" | ||||
| ``` | ||||
|  | ||||
| Properties with these names can only be accessed by the | ||||
| [brackets syntax](#bracket-access). | ||||
|  | ||||
| ### Common types | ||||
|  | ||||
| - `int`: An integer number, e.g. `4`. | ||||
|  | ||||
| - `float`: Floating point number, e.g. `4.5`. Can also be an integer. | ||||
|  | ||||
| - `str`: String, a piece of text. | ||||
|   If the text contains quotes or backslashes, escape them with a backslash. | ||||
|   Other properties can be included by combining strings or using an f-string: | ||||
|  | ||||
|   ```python3 | ||||
|   escaped:  str = "C:\\Users\\Alice \"Foo\" Bar" | ||||
|   number:   int = 1 | ||||
|   combined: str = "foo " + self.number | ||||
|   fstring:  str = f"foo {number}" | ||||
|   ``` | ||||
|  | ||||
| - `bool`: Boolean, a value that can be either `True` or `False`. | ||||
|  | ||||
| - `None`: A `None` value, represents an absence of choice. | ||||
|  | ||||
| - `Any`: A value that can be of any type. | ||||
|  | ||||
| - `list`: List of values, e.g. `[1, 2, 3]`.   | ||||
|   The type can be written as `list` or `List[type]` to specify what type the  | ||||
|   list's item should be. | ||||
|  | ||||
| - `tuple`: Similar to lists, but the length cannot be changed once created.   | ||||
|   Can be written as `tuple`, `Tuple[type, type]` to specify for example that  | ||||
|   the tuple must have two items of certain types, or `Tuple[type, ...]` | ||||
|   for a tuple with any number of items of a certain same type: | ||||
|  | ||||
|   ```python3 | ||||
|   anything:     tuple                 = (1, 2, 3, "foo") | ||||
|   many_ints:    Tuple[int, ...]       = (1, 2, 3, 4, 5) | ||||
|   three_values: Tuple[int, str, bool] = (1, "example", False) | ||||
|   ``` | ||||
|  | ||||
| - `dict`: Mapping of keys to values. Can be written as `dict` or  | ||||
|   `Dict[key_type, value_type]`: | ||||
|  | ||||
|   ```python3 | ||||
|   anything:      dict           = {1: 2, "foo": "bar", True: 1.5} | ||||
|   account_order: Dict[str, int] = {"@a:example.com": 1, "@b:example.com": 2} | ||||
|   ``` | ||||
|  | ||||
| - `Optional[type]`: A value that can be either that type or `None` | ||||
|  | ||||
| - `Union[type1, type2]`: A value that can be one of the type in the `Union`.  | ||||
|   The number of types can be more than two. | ||||
|  | ||||
| ### Expressions | ||||
|  | ||||
| A property's value can be any Python expression. Properties can also  | ||||
| refer to other properties, no matter what section they belong to or what | ||||
| order they are defined in.  | ||||
|  | ||||
| This PCN code: | ||||
|  | ||||
| ```python3 | ||||
| class Section1: | ||||
|     other: int = self.text.lower() * 2  # "exampleexample" | ||||
|     text:  str = "Example" | ||||
| ``` | ||||
|  | ||||
| Is roughly equivalent to this in standard Python: | ||||
|  | ||||
| ```python3 | ||||
| class Section1: | ||||
|     @property | ||||
|     def other(self) -> str: | ||||
|         return self.text.lower() * 2 | ||||
|  | ||||
|     @property | ||||
|     def text(self) -> str: | ||||
|         return "Example" | ||||
| ``` | ||||
|  | ||||
| ### Section Access | ||||
|  | ||||
| The current section and its properties are accessed via `self`: | ||||
|  | ||||
| ```python3 | ||||
| class Base: | ||||
|     number: int = 10 | ||||
|     other:  int = self.number * 2  # 20 | ||||
| ``` | ||||
|  | ||||
| The parent section is accessed via `parent`: | ||||
|  | ||||
| ```python3 | ||||
| class Base: | ||||
|     number: int = 10 | ||||
|  | ||||
|     class Inner: | ||||
|         number: int = parent.number * 2 | ||||
| ``` | ||||
|  | ||||
| Child sections can be accessed by `self.SectionName`: | ||||
|  | ||||
| ```python3 | ||||
| class Base: | ||||
|     number: int = self.Inner.number | ||||
|  | ||||
|     class Inner: | ||||
|         number: int = 10 | ||||
| ``` | ||||
|  | ||||
| Any section (or property, or function) defined at the root/top-level of the  | ||||
| file can be accessed by name: | ||||
|  | ||||
| ```python3 | ||||
| class First: | ||||
|     class InsideFirst: | ||||
|         number: int = Second.number * 2  # 20 | ||||
|         other:  int = Second.InsideSecond.number  # 50 | ||||
|  | ||||
| class Second: | ||||
|     number: int = 10 | ||||
|  | ||||
|     class InsideSecond: | ||||
|         number: int = 50 | ||||
| ``` | ||||
|  | ||||
| The root (which behaves like a section) can also be explicitely accessed  | ||||
| with `self.root`: | ||||
|  | ||||
| ```python3 | ||||
| number: int = 10 | ||||
|  | ||||
| class First: | ||||
|     root_num: int = self.root.number  # Same as just saying "number" | ||||
|  | ||||
| class Second:  | ||||
|     first_num: int = self.root.First.root_num  # Same as "First.root_num" | ||||
| ``` | ||||
|  | ||||
| ### Bracket Access | ||||
|  | ||||
| Inner sections and properties can also be accessed by the  | ||||
| `section[name]` syntax. This is the only way to access properties with  | ||||
| non-standard names (as described in [Properties](#properties)): | ||||
|  | ||||
| ```python3 | ||||
| class Names: | ||||
|     "!alice:example.org": str = "alice" | ||||
|  | ||||
|     class Capitalized: | ||||
|         alice: str = parent["!alice:example.org"].capitalize()  # "Alice" | ||||
| ``` | ||||
|  | ||||
| The syntax can also be used to access properties dynamically: | ||||
|  | ||||
| ```python3 | ||||
| class Names: | ||||
|     alice:         str = "Alice" | ||||
|     property_name: str = "alice" | ||||
|     first_person:  str = self[property_name] | ||||
| ``` | ||||
|  | ||||
| Top-level properties can only be accessed this way using `self.root`: | ||||
|  | ||||
| ```python3 | ||||
| "!alice:example.org": str = "Alice" | ||||
|  | ||||
| class Names: | ||||
|     alice: str = self.root["!alice:example.org"] = "Alice" | ||||
| ``` | ||||
|  | ||||
|  | ||||
| # GUI Files | ||||
|  | ||||
| When properties for PCN files are edited from the user interface  | ||||
| (programmatically or due to user actions), a separate file with a `.gui.json` | ||||
| extension is created in the same folder. | ||||
|  | ||||
| These files take priority and override properties from the equivalent user  | ||||
| files. They should not be edited by hand. | ||||
| When a property in the user config file is edited, any equivalent property  | ||||
| in the GUI file is automatically dropped, | ||||
| to let the user's setting apply again. | ||||
		Reference in New Issue
	
	Block a user
	