0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-06 14:50:20 -05:00

Point documentation to the help center.

This commit is contained in:
Andrey Antukh 2021-03-05 09:10:57 +01:00
parent 5ae823b25c
commit 868f18fd21
10 changed files with 3 additions and 1102 deletions

View file

@ -9,27 +9,11 @@
# PENPOT #
Were excited to share that Uxbox is now Penpot! Were changing the name, but keeping the same project essence. Stay in the loop for more news coming early 2021. Alpha release is close!
The open-source solution for design and prototyping.
![PENPOT](https://raw.githubusercontent.com/penpot/penpot/develop/docs/screenshot.png)
![PENPOT](https://penpot.app/images/workspace-ui.jpg)
## Introduction ##
The open-source solution for design and prototyping. PENPOT is
currently at an early development stage but we are working hard to
bring you the beta version as soon as possible. Follow the project
progress in Twitter or Github and stay tuned!
## SVG based ##
Penpot works with SVG, a standard format, for all your designs and
prototypes . This means that all your stuff in Penpot is portable and
editable in many other vector tools and easy to use on the web.
[See SVG specification](https://www.w3.org/Graphics/SVG/)
## Contributing ##
**Open to you!**
@ -43,7 +27,7 @@ Please refer to the [Contributing Guide](./CONTRIBUTING.md)
## Documentation ##
Please refer to [docs/ directory](./docs/).
Please refer to the [help center](https://help.penpot.app).
## License ##

View file

@ -1,62 +0,0 @@
# Getting Started ##
This documentation intends to explain how to get penpot application and run it locally.
The simplest approach is using docker and docker-compose.
## Install Docker ##
Skip this section if you already have docker installed, up and running.
You can install docker and its dependencies from your distribution
repository with:
```bash
sudo apt-get install docker docker-compose
```
Or follow installation instructions from docker.com; (for Debian
https://docs.docker.com/engine/install/debian/).
Ensure that the docker is started and optionally enable it to start
with the system:
```bash
sudo systemctl start docker
sudo systemctl enable docker
```
And finally, add your user to the docker group:
```basb
sudo usermod -aG docker $USER
```
This will make use of the docker without `sudo` command all the time.
NOTE: probably you will need to re-login again to make this change
take effect.
## Start penpot application ##
You can create it from scratch or take a base from the [penpot
repository][1]
[1]: https://raw.githubusercontent.com/penpot/penpot/develop/docker/images/docker-compose.yaml
```bash
wget https://raw.githubusercontent.com/penpot/penpot/develop/docker/images/docker-compose.yaml
```
And then:
```bash
docker-compose -p penpot -f docker-compose.yaml up
```
The docker compose file contains the essential configuration for
getting the application running, and many essential configurations
already explained in the comments. All other configuration options are
explained in [configuration guide](./05-Configuration-Guide.md).

View file

@ -1,146 +0,0 @@
# Developer Guide #
This is a generic "getting started" guide for the Penpot platform. It
intends to explain how to get the development environment up and
running with many additional tips.
The main development environment consists in a docker compose
configuration that starts the external services and the development
container (called **devenv**).
We use tmux script in order to multiplex the single terminal and run
both the backend and frontend in the same container.
## System requirements ##
You should have `docker` and `docker-compose` installed in your system
in order to set up properly the development enviroment.
In debian like linux distributions you can install it executing:
```bash
sudo apt-get install docker docker-compose
```
Start and enable docker environment:
```bash
sudo systemctl start docker
sudo systemctl enable docker
```
Add your user to the docker group:
```basb
sudo usermod -aG docker $USER
```
And finally, increment user watches:
```
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```
NOTE: you probably need to login again for group change take the effect.
## Start the devenv ##
**Requires a minimum knowledge of tmux usage in order to use that
development environment.**
For start it, staying in this repository, execute:
```bash
./manage.sh pull-devenv
./manage.sh run-devenv
```
This will do the following:
- Pulls the latest devenv image.
- Starts all the containers in the background.
- Attaches to the **devenv** container and executes the tmux session.
- The tmux session automatically starts all the necessary services.
You can execute the individual steps manully if you want:
```bash
./manage.sh build-devenv # builds the devenv docker image (not necessary in normal sircumstances)
./manage.sh start-devenv # starts background running containers
./manage.sh run-devenv # enters to new tmux session inside of one of the running containers
./manage.sh stop-devenv # stops background running containers
./manage.sh drop-devenv # removes all the volumes, containers and networks used by the devenv
```
Now having the the container running and tmux open inside the
container, you are free to execute any commands and open many shells
as you want.
You can create a new shell just pressing the **Ctr+b c** shortcut. And
**Ctrl+b w** for switch between windows, **Ctrl+b &** for kill the
current window.
For more info: https://tmuxcheatsheet.com/
### Inside the tmux session
#### gulp
The styles and many related tasks are executed thanks to gulp and they are
executed in the tmux **window 0**. This is a normal gulp watcher with some
additional tasks.
#### shadow-cljs
The frontend build process is located on the tmux **window 1**.
**Shadow-cljs** is used for build and serve the frontend code. For
more information, please refer to `02-Frontend-Developer-Guide.md`.
By default the **window 1** executes the shadow-cljs watch process,
that starts a new JVM/Clojure instance if there is no one running.
Finally, you can start a REPL linked to the instance and the current
connected browser, by opening a third window with `Ctrl+c` and running
`npx shadow-cljs cljs-repl main`.
#### exporter
The exporter app (clojurescript app running in nodejs) is located in
**window 2**, and you can go directly to it using `ctrl+b 2` shortcut.
There you will found the window split in two slices. On the top slice
you will have the build process (using shadow-cljs in the same way as
frontend application), and on the bot slice the script that launeches
the node process.
If some reason scripts does not stars correctly, you can manually
execute `node target/app.js ` to start the exporter app.
#### backend
The backend related environment is located in the tmux **window 3**,
and you can go directly to it using `ctrl+b 2` shortcut.
By default the backend will be started in non-interactive mode for
convenience but you can just press `Ctrl+c` and execute `./scripts/repl`
for start the repl.
On the REPL you have this helper functions:
- `(start)`: start all the environment
- `(stop)`: stops the environment
- `(restart)`: stops, reload and start again.
And many other that are defined in the `dev/user.clj` file.
If some exception is raised when code is reloaded, just use
`(repl/refresh-all)` in order to finish correctly the code swaping and
later use `(restart)` again.
For more information, please refer to: `03-Backend-Guide.md`.

View file

@ -1,283 +0,0 @@
# Frontend Guide #
This guide intends to explain the essential details of the frontend
application.
## Visual debug mode and utilities
Debugging a problem in the viewport algorithms for grouping and
rotating is difficult. We have set a visual debug mode that displays
some annotations on screen, to help understanding what's happening.
To activate it, open the javascript console and type
```javascript
app.util.debug.toggle_debug("option")
```
Current options are `bounding-boxes`, `group`, `events` and
`rotation-handler`.
You can also activate or deactivate all visual aids with
```javascript
app.util.debug.debug_all()
app.util.debug.debug_none()
```
## Logging, Tracing & Debugging
As a traditional way for debugging and tracing you have the followimg approach:
Print data to the devtool console using clojurescript helper:
**prn**. This helper automatically formats the clojure and js data
structures as plain EDN for easy visual inspection of the data and the
type of the data.
```clojure
(prn "message" expression)
```
An alternative is using the pprint function, usefull for pretty
printing a medium-big data sturcture for completly understand it.
```clojure
(:require [cljs.pprint :refer [pprint]])
(pprint expression)
; Outputs a clojure value as a string, nicely formatted and with data type information.
```
Use the js native functions for printing data. The clj->js converts
the clojure data sturcture to js data sturcture and it is
inspeccionable in the devtools console.
```clojure
(js/console.log "message" (clj->js expression))
```
Also we can insert breakpoints in the code with this function:
```clojure
(js-debugger)
```
You can also set a breakpoint from the sources tab in devtools. One
way of locating a source file is to output a trace with
(js/console.log) and then clicking in the source link that shows in
the console.
### Logging framework
Additionally to the traditional way of putting traces in the code, we
have a logging framework with steroids. It is usefull for casual
debugging (as replacement for a `prn` and `js/console.log`) and as a
permanent traces in the code.
You have the ability to specify the logging level per namespace and
all logging is ellided in production build.
Lets start with a simple example:
```clojure
(ns some.ns
(:require [app.util.logging :as log]))
;; This function sets the level to the current namespace; messages
;; with level behind this will not be printed.
(log/set-level! :info)
;; Log some data; The `app.util.logging` has the following
;; functions/macros:
(log/error :msg "error message")
(log/warn :msg "warn message")
(log/info :msg "info message")
(log/debug :msg "debug message")
(log/trace :msg "trace message")
```
Each macro accept arbitrary number of key values pairs:
```clojure
(log/info :foo "bar" :msg "test" :value 1 :items #{1 2 3})
```
Some keys ara treated as special cases for helping in debugging:
```clojure
;; The special case for :js/whatever; if you namespace the key
;; with `js/`, the variable will be printed as javascript
;; inspectionable object.
(let [foobar {:a 1 :b 2}]
(log/info :msg "Some data" :js/data foobar))
;; The special case for `:err`; If you attach this key, the
;; exception stack trace is printed as additional log entry.
```
## Access to clojure from javascript console
The penpot namespace of the main application is exported, so that is
accessible from javascript console in Chrome developer tools. Object
names and data types are converted to javascript style. For example
you can emit the event to reset zoom level by typing this at the
console (there is autocompletion for help):
```javascript
app.main.store.emit_BANG_(app.main.data.workspace.reset_zoom)
```
## Debug state and objects
There are also some useful functions to visualize the global state or
any complex object. To use them from clojure:
```clojure
(ns app.util.debug)
(logjs <msg> <var>) ; to print the value of a variable
(tap <fn>) ; to include a function with side effect (e.g. logjs) in a transducer.
(ns app.main.store)
(dump-state) ; to print in console all the global state
(dump-objects) ; to print in console all objects in workspace
```
But last ones are most commonly used from javscript console:
```javascript
app.main.store.dump_state()
app.main.store.dump_objects()
```
And we have also exported `pprint` and `clj->js` functions for the console:
```javascript
pp(js_expression) // equivalent to cljs.pprint.pprint(js_expression)
dbg(js_expression) // equivalent to cljs.core.clj__GT_js(js_expression)
```
## Icons & Assets
The icons used on the frontend application are loaded using svgsprite
(properly handled by the gulp watch task). All icons should be on SVG
format located in `resources/images/icons`. The gulp task will
generate the sprite and the embedd it into the `index.html`.
Then, you can reference the icon from the sprite using the
`app.builtins.icons/icon-xref` macro:
```clojure
(ns some.namespace
(:require-macros [app.main.ui.icons :refer [icon-xref]]))
(icon-xref :arrow)
```
For performance reasons, all used icons are statically defined in the
`src/app/main/ui/icons.cljs` file.
## Translations (I18N) ##
### How it Works ###
All the translation strings of this application are stored in
`resources/locales.json` file. It has a self explanatory format that
looks like this:
```json
{
"auth.email-or-username" : {
"used-in" : [ "src/app/main/ui/auth/login.cljs:61" ],
"translations" : {
"en" : "Email or Username",
"fr" : "adresse email ou nom d'utilisateur"
}
},
"ds.num-projects" : {
"translations": {
"en": ["1 project", "%s projects"]
}
},
}
```
For development convenience, you can forget about the specific format
of that file, and just add a simple key-value entry pairs like this:
```
{
[...],
"foo1": "bar1",
"foo2": "bar2"
}
```
The file is automatically bundled into the `index.html` file on
compile time (in development and production). The bundled content is a
simplified version of this data structure for avoid load unnecesary
data.
The development environment has a watch process that detect changes on
that file and recompiles the `index.html`. **There are no hot reload
for translations strings**, you just need to refresh the browser tab
for refresh the translations in the running the application.
If you have used the short (key-value) format, the watch process will
automatically convert it to the apropriate format before generate the
`index.html`.
Finally, when you have finished to adding texts, execute the following
command for reformat the file, and track the usage locations (the
"used-in" list) before commiting the file into the repository:
```bash
clojure -Adev locales.clj collect src/app/main/ resources/locales.json
```
NOTE: Later, we will need to think and implement the way to export and
import to other formats (mainly for transifex and similar services
compatibility).
### How to use it ###
You have two aproaches for translate strings: one for general purpose
and other specific for React components (that leverages reactivity for
language changes).
The `app.util.i18n/tr` is the general purpose function. This is a
simple use case example:
```clojure
(require '[app.util.i18n :refer [tr])
(tr "auth.email-or-username")
;; => "Email or Username"
```
If you have defined plurals for some translation resource, then you
need to pass an additional parameter marked as counter in order to
allow the system know when to show the plural:
```clojure
(require '[app.util.i18n :as i18n :refer [tr]])
(tr "ds.num-projects" (i18n/c 10))
;; => "10 projects"
(tr "ds.num-projects" (i18n/c 1))
;; => "1 project"
```

View file

@ -1,76 +0,0 @@
# Backend Developer Guide #
This guide intends to explain the essential details of the backend
application.
## Fixtures ##
This is a development feature that allows populate the database with a
good amount of content (usually used for just test the application or
perform performance tweaks on queries).
In order to load fixtures, enter to the REPL environment executing the
`bin/repl` script, and then execute `(app.cli.fixtures/run {:preset :small})`.
You also can execute this as a standalone script with:
```bash
clojure -Adev -X:fn-fixtures
```
NOTE: It is an optional step because the application can start with an
empty database.
This by default will create a bunch of users that can be used to login
in the aplication. All users uses the following pattern:
- Username: `profileN@example.com`
- Password: `123123`
Where `N` is a number from 0 to 5 on the default fixture parameters.
If you have a REPL access to the running process, you can execute it
from there:
```clojure
(require 'app.cli.fixtures)
(app.cli.fixtures/run :small)
```
To access to the running process repl you usually will execute this
command:
```bash
rlwrap netcat localhost 6062
```
## Migrations
The database migrations are located in two directories:
- `src/app/migrations` (contains migration scripts in clojure)
- `src/app/migrations/sql` (contains the pure SQL migrations)
The SQL migration naming consists in the following:
```
XXXX-<add|mod|del|drop|[...verb...]>-<table-name>-<any-additional-text>
```
Examples:
```
0025-del-generic-tokens-table
0026-mod-profile-table-add-is-active-field
```
**NOTE**: if table name has more than one words, we still use `-` as a separator.
If you need to have a global overview of the all schema of the database you can extract it
using postgresql:
```bash
# (in the devenv environment)
pg_dump -h postgres -s > schema.sql
```

View file

@ -1,61 +0,0 @@
# Common's guide #
This section intends to have articles that related to both frontend
and backend, such as: code style hints, architecture dicisions, etc...
## Assertions ##
Penpot source code has this types of assertions:
**assert**: just using the clojure builtin `assert` macro.
Example:
```clojure
(assert (number? 3) "optional message")
```
This asserts are only executed on development mode. On production
environment all assets like this will be ignored by runtime.
**spec/assert**: using the `app.common.spec/assert` macro.
Also, if you are using clojure.spec, you have the spec based
`clojure.spec.alpha/assert` macro. In the same way as the
`clojure.core/assert`, on production environment this asserts will be
removed by the compiler/runtime.
Example:
````clojure
(require '[clojure.spec.alpha :as s]
'[app.common.spec :as us])
(s/def ::number number?)
(us/assert ::number 3)
```
In the same way as the `assert` macro, this performs the spec
assertion only on development build. On production this code will
completely removed.
**spec/verify**: An assertion type that is executed always.
Example:
```clojure
(require '[app.common.spec :as us])
(us/verify ::number 3)
```
This macro enables you have assetions on production code.
**Why don't use the `clojure.spec.alpha/assert` instead of the `app.common.spec/assert`?**
The Penpot variant does not peforms additional runtime checks for know
if asserts are disabled in "runtime". As a result it generates much
simplier code at development and production builds.

View file

@ -1,240 +0,0 @@
# Configuration Guide #
This section intends to explain all available configuration options.
## Backend ##
The default approach for pass options to backend application is using
environment variables. Almost all environment variables starts with
the `PENPOT_` prefix.
NOTE: All the examples that comes with values, they represent the
**default** values.
### Configuration Options
#### Database Connection
```sh
PENPOT_DATABASE_USERNAME=penpot
PENPOT_DATABASE_PASSWORD=penpot
PENPOT_DATABASE_URI=postgresql://127.0.0.1/penpot
```
The username and password are optional.
#### Email (SMTP)
```sh
PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
# When not enabled, the emails are printed to the console.
PENPOT_SMTP_ENABLED=false
PENPOT_SMTP_HOST=<host>
PENPOT_SMTP_PORT=25
PENPOT_SMTP_USER=<username>
PENPOT_SMTP_PASSWORD=<password>
PENPOT_SMTP_SSL=false
PENPOT_SMTP_TLS=false
```
#### Storage (assets)
Assets storage is implemented using "plugable" backends. Currently
there are three backends available: `db`, `fs` and `s3` (for AWS S3).
##### fs backend
The default backend is: **fs**.
```sh
PENPOT_STORAGE_BACKEND=fs
PENPOT_STORAGE_FS_DIRECTORY=resources/public/assets`
```
The fs backend is hightly coupled with nginx way to serve files using
`x-accel-redirect` and for correctly configuring it you will need to
touch your nginx config for correctly expose the directory specified
in `PENPOT_STORAGE_FS_DIRECTORY` environment.
For more concrete example look at the devenv nginx configurtion
located in `<repo-root>/docker/devenv/files/nginx.conf`.
**NOTE**: The **fs** storage backend is used for store temporal files
when a user uploads an image and that image need to be processed for
creating thumbnails. So is **hightly recommeded** setting up a correct
directory for this backend independently if it is used as main backend
or not.
##### db backend
In some circumstances or just for convenience you can use the `db`
backend that stores all media uploaded by the user directly inside the
database. This backend, at expenses of some overhead, facilitates the
backups, because with this backend all that you need to backup is the
postgresql database. Convenient for small installations and personal
use.
```sh
PENPOT_STORAGE_BACKEND=db
```
##### s3 backend
And finally, you can use AWS S3 service as backend for assets
storage. For this you will need to have AWS credentials, an bucket and
the region of the bucket.
```sh
AWS_ACCESS_KEY_ID=<you-access-key-id-here>
AWS_SECRET_ACCESS_KEY=<your-secret-access-key-here>
PENPOT_STORAGE_BACKEND=s3
PENPOT_STORAGE_S3_REGION=<aws-region>
PENPOT_STORAGE_S3_BUCKET=<bucket-name>
```
Right now, only `eu-central-1` region is supported. If you need others, open an issue.
#### Redis
The redis configuration is very simple, just provide with a valid redis URI. Redis is used
mainly for websocket notifications coordination.
```sh
PENPOT_REDIS_URI=redis://localhost/0
```
#### HTTP Server
```sh
PENPOT_HTTP_SERVER_PORT=6060
PENPOT_PUBLIC_URI=http://localhost:3449
PENPOT_REGISTRATION_ENABLED=true
# comma-separated domains, defaults to `""` which means that all domains are allowed)
PENPOT_REGISTRATION_DOMAIN_WHITELIST=""
```
#### Server REPL
The production environment by default starts a server REPL where you
can connect and perform diagnosis operations. For this you will need
`netcat` or `telnet` installed in the server.
```bash
$ rlwrap netcat localhost 6062
user=>
```
The default configuration is:
```sh
PENPOT_SREPL_HOST=127.0.0.1
PENPOT_SREPL_PORT=6062
```
#### Auth with 3rd party
**NOTE**: a part of setting this configuration on backend, frontend
application will also require configuration tweaks for make it work.
##### Google
```sh
PENPOT_GOOGLE_CLIENT_ID=<client-id>
PENPOT_GOOGLE_CLIENT_SECRET=<client-secret>
```
##### Gitlab
```sh
PENPOT_GITLAB_BASE_URI=https://gitlab.com
PENPOT_GITLAB_CLIENT_ID=<client-id>
PENPOT_GITLAB_CLIENT_SECRET=<client-secret>
```
##### Github
```sh
PENPOT_GITHUB_CLIENT_ID=<client-id>
PENPOT_GITHUB_CLIENT_SECRET=<client-secret>
```
##### LDAP
```sh
PENPOT_LDAP_AUTH_HOST=
PENPOT_LDAP_AUTH_PORT=
PENPOT_LDAP_AUTH_VERSION=3
PENPOT_LDAP_BIND_DN=
PENPOT_LDAP_BIND_PASSWORD=
PENPOT_LDAP_AUTH_SSL=false
PENPOT_LDAP_AUTH_STARTTLS=false
PENPOT_LDAP_AUTH_BASE_DN=
PENPOT_LDAP_AUTH_USER_QUERY=(|(uid=:username)(mail=:username))
PENPOT_LDAP_AUTH_USERNAME_ATTRIBUTE=uid
PENPOT_LDAP_AUTH_EMAIL_ATTRIBUTE=mail
PENPOT_LDAP_AUTH_FULLNAME_ATTRIBUTE=displayName
PENPOT_LDAP_AUTH_AVATAR_ATTRIBUTE=jpegPhoto
```
## Frontend ##
In comparison with backend frontend only has a few number of runtime
configuration options and are located in the
`<dist-root>/js/config.js` file. This file is completly optional; if
it exists, it is loaded by the main index.html.
The `config.js` consists in a bunch of globar variables that are read
by the frontend application on the bootstrap.
### Auth with 3rd party
If any of the following variables are defined, they will enable the
corresponding auth button in the login page
```js
var penpotGoogleClientID = "<google-client-id-here>";
var penpotGitlabClientID = "<gitlab-client-id-here>";
var penpotGithubClientID = "<github-client-id-here>";
var penpotLoginWithLDAP = <true|false>;
```
**NOTE:** The configuration should match the backend configuration for
respective services.
### Demo warning and Demo users
It is possible to display a warning message on a demo environment and
disable/enable demo users:
```js
var penpotDemoWarning = <true|false>;
var penpotAllowDemoUsers = <true|false>;
```
**NOTE:** The configuration for demo users should match the backend
configuration.
## Exporter ##
The exporter application only have a single configuration option and
it can be provided using environment variables in the same way as
backend.
```sh
PENPOT_PUBLIC_URI=http://pubic-domain
```
This environment variable indicates where the exporter can access to
the public frontend application (because it uses special pages from it
to render the shapes in the underlying headless web browser).

View file

@ -1,45 +0,0 @@
# Testing guide #
## Backend / Common
You can run the tests directly with:
```bash
~/penpot/backend$ clojure -M:dev:tests
```
Alternatively, you can run them from a REPL. First starting a REPL.
```bash
~/penpot/backend$ scripts/repl
```
And then:
```bash
user=> (run-tests)
user=> (run-tests 'namespace)
user=> (run-tests 'namespace/test)
```
## Frontend
Frontend tests have to be compiled first, and then run with node.
```bash
npx shadow-cljs compile tests && node target/tests.js
```
Or run the watch (that automatically runs the test):
```bash
npx shadow-cljs watch tests
```
## Linter
We can execute the linter for the whole codebase with the following command:
```bash
clj-kondo --lint common:backend/src:frontend/src
```

View file

@ -1,170 +0,0 @@
# Collaborative Edition & Persistence protocol
This is a collection of design notes for collaborative edition feature
and persistence protocol.
## Persistence Operations
This is a page data structure:
```
{:version 2
:options {}
:rmap
{:id1 :default
:id2 :default
:id3 :id1}
:objects
{:root
{:type :root
:shapes [:id1 :id2]}
:id1
{:type :canvas
:shapes [:id3]}
:id2 {:type :rect}
:id3 {:type :circle}}}
```
This is a potential list of persistent ops:
```
{:type :mod-opts
:operations [<op>, ...]
{:type :add-obj
:id <uuid>
:parent <uuid>
:obj <shape-object>}
{:type :mod-obj
:id <uuid>
:operations [<op>, ...]}
{:type :mov-obj
:id <uuid>
:frame-id <uuid>}
{:type :del-obj
:id <uuid>}
```
This is a potential list of operations:
```
{:type :set
:attr <any>
:val <any>}
{:type :abs-order
:id <uuid>
:index <int>}
{:type :rel-order
:id <uuid>
:loc <one-of:up,down,top,bottom>}
```
## Ephemeral communication (Websocket protocol)
### `join` ###
Sent by clients for notify joining a concrete page-id inside a file.
```clojure
{:type :join
:page-id <id>
:version <number>
}
```
Will cause:
- A posible `:page-changes`.
- Broadcast `:joined` message to all users of the file.
The `joined` message has this aspect:
```clojure
{:type :joined
:page-id <id>
:user-id <id>
}
```
### `who` ###
Sent by clients for request the list of users in the channel.
```clojure
{:type :who}
```
Will cause:
- Reply to the client with the current users list:
```clojure
{:type :who
:users #{<id>,...}}
```
This will be sent all the time user joins or leaves the channel for
maintain the frontend updated with the lates participants. This
message is also sent at the beggining of connection from server to
client.
### `pointer-update` ###
This is sent by client to server and then, broadcasted to the rest of
channel participants.
```clojure
{:type :pointer-update
:page-id <id>
:x <number>
:y <number>
}
```
The server broadcast message will look like:
```clojure
{:type :pointer-update
:user-id <id>
:page-id <id>
:x <number>
:y <number>
}
```
### `:page-snapshot` ###
A message that server sends to client for notify page changes. It can be sent
on `join` and when a page change is commited to the database.
```clojure
{:type :page-snapshot
:user-id <id>
:page-id <id>
:version <number>
:operations [<op>, ...]
}
```
This message is only sent to users that does not perform this change.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB