npm package: @replit/workspace
Description: [![Run on Repl.it](https://repl.it/badge/github/replit/workspace)](https://repl.it/github/replit/workspace)
Author: Replit, Inc.
Latest version: 2.5.2

# workspace

[![Run on Repl.it](https://repl.it/badge/github/replit/workspace)](https://repl.it/github/replit/workspace)

to run the dev server locally:
- run `yarn` in the repos root
- then `yarn start:dev`
- should be up and running with a dev server at `localhost:1234`

# Technology and architecture
At its core the workspace consists of a window manager and an event bus/state manager. In reality the architecutre can be used for building any application, we only use it to build the IDE at repl.it.

You should read [Mason's blog post](https://repl.it/site/blog/ide) to understand why we ended up building our IDE using this technology and to get a high level overview of what the main constituents are. We'll divelge into details below, focusing on how to build a plugin.

_**disclaimer** this is work in progress and the API will likely change, there are some nuances that are under heavy consideration. The core architecture will remain the same and we'll probably provide a backwards compatibility layer for old plugins_

### Prequisits
Although the core of the architecture does not rely on it but we decided to go with __React__ for rendering and window management and __Redux__ for message passing and state management.

### Plugins

A plugin can consist of a __reducer__ (internal state builder), __receiver__ (a side-effect manager), and/__or__ a __component__ (react component). A plugin can have one or more of these parts.

#### Reducer

Each plugin builds its state from actions that are passed to it. They are no different from [Redux Reducers]( https://redux.js.org/basics/reducers).

Plugins can only reach into their own state, since actions are global any state a plugin needs can be built in the reducers.


#### Receiver 

Since reducers should be pure functions but real applications generally need some way to do side-effects we introduced the concept of receivers. Receivers are implemented using [Redux Middlewares](https://redux.js.org/advanced/middleware).  Receivers get an action and return a function that gets `dispatch` and `getState` passed, the returned function is basically a redux thunk.

Receivers' signature looks like this. 
```typescript
function receiver(
  workspaceId: Opaque<string>,
  pluginId: Opaque<string>,
  action: string,
): null | Thunk;

type Thunk = (dispatch: ReduxDispatch, getState: ReduxGetState) => any;
```

We hope to get rid of pluginId and workspaceId as we clearup some things, see __the state fiasco__ below.

#### The state fiasco

- `wid` = workspace id
- `pud` = plugin id

For now to access a plugin's state, you would `getState().workspace[wid].plugins[pud].state`. Reaching into another plugin's state will get you fired.

Also when you dispatch an action that needs to be consumed by the workspace you should include the workspace id (wid). So your action would like this, `dispatch({ wid, type: 'EVAL' })`. If your notice you're actions aren't being passed to the reducers, this is probably why, we're sorry, we'll fix this soon. Something that you might find useful is that if you also include `pud` the action will only be consumed by your plugin (reducer/receiver)

For historical reasons, we eneded up having some global state that's shared between plugins, [see here](https://github.com/replit/repl-it-web/blob/76a556432d64d63bc08d007c74eda2e0075f6913/app/workspace/types.ts#L99-L111), we can easily move these to be part of each plugin's internal state for the plugins that need them, right now they're part of the [core's reducer](https://github.com/replit/workspace/blob/d3d5f5ef0e54cc9bff99f1bcd56776d0ac1a70c8/core/reducer.js). if you wanna access this state: `getState().workspace[wid].activeFile`.

Also there is some state that's outside the workspace's state this is because we had a gradual move to the workspace from our redux app, [see here](https://github.com/replit/workspace/blob/f564cb29bc08a02ca0fe86837ce067aeca34f4b2/core/index.js#L58-L101). To access this state: `getState().user`
 

#### Components

Components are just good old React components. They recieve the following props from the window manager:
```
  wid: string;
  pud: string;
  style: { [prop: string]: string | number };
  dimensions: { w: { px: number; pc: number }; h: { px: number; pc: number } };
  theme: ReplitThemes;
```
- wid is for you dispatch actions and access state
- pud is for accessing state
- style should be passed to your top level div ([it's literally just `width: 100%, height: 100%`](https://github.com/replit/workspace/blob/74959da3603c7c133e1b83152fbf52834e1718cf/core/WindowManager.react.js#L17))
- dimensions are calculated by the window manager, you can use it to figure out your plugin's dimensions, you generally don't need this
- Themes are a string oneof 'replitlight' | 'replitdark', these should be explained elsewhere (ping Tim for now) but it's what we use for theming and we'll be looking into a more robust thing that probably passes the full theme object.

You can wrap your component in [react-redux's connect](https://react-redux.js.org/api/connect) or use the [react-redux hooks](https://react-redux.js.org/api/hooks) to access state, you can use wid and pud props if you need as explained in the state fiasco.