You like Marquee and want to help making it better? Awesome! We are working to make this process as easy and transparent as possible. We might be not quite there yet but this guide will help you to ramp you up as a contributor and give you everything you need to make your first contribution. If there is any information missing that prevents you from sending in a pull request, please let us know. We treat these kind of issues like actual bugs.

Code of Conduct

Everyone who participates in this project, either as a user or a contributor, is obliged to follow the projects Code of Conduct. Every violation against it will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Find A Way To Contribute

The project offers a variety of ways to contribute. If you struggle to find something suited for you, join the Marquee support channel on Gitter or Discord and reach out to the maintainer there. Don't be shy, they are there to help!
You can participate by:
  • contributing code
  • improving documentation
  • help out folks in our support channels
  • create educational content (blog posts, tutorials, videos, etc.)
  • spread the good word about the project (e.g. via Twitter)
  • create bugs if you discover them while using Marquee
  • make feature requests if you are missing something in the project
The maintainers of the project try to organize all issues in the way that should allow anyone to have enough context to start working on it. If this is not the case please mention it in the issue thread so that either the issue creator or a maintainer can provide more information.

Contributing Code

In order to propose changes to the code, please check out the repository:
1git clone
2cd vscode-marquee
and install neccessary dependencies:
1yarn install
Next, build the project:
1yarn build:dev
and open up VS Code:
1code .
Now, you can start to run the built version when pressing F5. Make sure to run yarn watch (or SHIFT + CMD + B) in one of your terminals so that the project is being rebuild everytime you change a file. To restart the extension you can press CMD + R or Control + R (like reloading a website in a browser).
In order to ensure that types are updated and bundle recompiled after making changes, ensure to run the watch task:
1yarn watch

Adding a new Core Widget

Marquee maintains its codebase as a monorepo containing various of modules within the packages directory:
  • /packages/dialog: utility package for creating dialogs within the webview
  • /packages/extension: code that runs on the extension host
  • /packages/gui: core webview application
  • /packages/utils: common utility modules
  • /packages/widget: common widget components
  • /packages/wdiget-xxx: core Marquee widget code
When creating a new widget, e.g. a foobar widget, add a new directory within that folder, e.g. /packages/widget-foobar, and follow the folder structure as other widget modules. The package.json should point to an entry file that exports basic widget information, e.g.:
widget-foobar sample folder structure
2...other packages
4    │   build
5    │   extension
6    │   │   package.json
7    │   src
8    │   │   constants.ts
9    │   │   extension.ts
10    │   │   index.tsx
11    │   │   types.ts
12    │   │   Widget.tsx
14    │   tsconfig.json
Create a tsconfig.json file in the widget-foobar root
2  "extends": "../../tsconfig",
3  "compilerOptions": {
4    "target": "ESNext",
5    "module": "ESNext",
6    "moduleResolution": "node",
7    "baseUrl": ".",
8    "outDir": "./build",
9    "rootDir": "./src",
10    "skipLibCheck": true
11  },
12  "include": [
13    "src/**/*",
14    "../../@types"
15  ]
Create a package.json in the widget-foobar root
2  "name": "@vscode-marquee/widget-foobar",
3  "description": "Marquee Foobar Widget",
4  "version": "0.1.0",
5  "private": true,
6  "main": "./build/index.js",
7  "types": "./build/index.d.ts",
8  "dependencies": {
9    "@vscode-marquee/utils": "^0.1.0",
10    "@vscode-marquee/widget": "^0.1.0"
11  }
Setting marquee up with our Widget.tsx ui
1import React from 'react'
2import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3import { faCloudSun } from '@fortawesome/free-solid-svg-icons/faCloudSun'
5import FoobarWidget from './Widget'
7export default {
8  name: 'foobar',
9  icon: <FontAwesomeIcon icon={faCloudSun} />,
10  label: 'Foo Bar',
11  tags: [],
12  description: '...',
13  component: FoobarWidget,
Add the new package to the workspace list in /package.json also adding a new watch:foobar script and import it in /packages/gui/src/constants.ts so it is loaded by the webview. A basic widget component looks as following:
1import React, { useContext } from 'react'
2import { Grid, Typography } from '@mui/material'
4import wrapper, { Dragger, HidePop, HeaderWrapper, NavIconDropdown } from '@vscode-marquee/widget'
5import type { MarqueeWidgetProps } from '@vscode-marquee/widget'
7const Foobar = ({ ToggleFullScreen, fullscreenMode, minimizeNavIcon }: MarqueeWidgetProps) => {
8  // ...
9  const NavButtons = () => (
10    <Grid item>
11      <Grid
12        container
13        justifyContent="right"
14        direction={minimizeNavIcon ? 'column-reverse' : 'row'}
15        spacing={1}
16        alignItems="center"
17        padding={minimizeNavIcon ? 0.5 : 0}
18      >
19        <CopyToClipboardButton />
20        <Grid item>
21          <HidePop name="markdown" />
22        </Grid>
23        <Grid item>
24          <ToggleFullScreen />
25        </Grid>
26        {!fullscreenMode &&
27          <Grid item>
28            <Dragger />
29          </Grid>
30        }
31      </Grid>
32    </Grid>
33  )
35  return (
36    <>
37      <HeaderWrapper>
38        <Grid item>
39          <Typography variant="subtitle1">Foobar</Typography>
40        </Grid>
41        {minimizeNavIcon ?
42          <PopupState variant='popper' popupId='widget-markdown'>
43            {(popupState) => {
44              return (
45                <NavIconDropdown popupState={popupState}>
46                  <NavButtons />
47                </NavIconDropdown>
48              )}}
49          </PopupState>
50          :
51          <Grid item xs={8}>
52            <NavButtons />
53          </Grid>
54        }
55      </HeaderWrapper>
56      <Grid item xs>
57        <Grid
58          container
59          wrap="nowrap"
60          direction="column"
61          style={{ height: '100%' }}
62        >
63          <Grid item xs style={{ overflow: 'hidden' }}>
64            Hello World
65          </Grid>
66        </Grid>
67      </Grid>
68    </>
69  )
72export default wrapper((props: any) => (
73  <Foobar {...props} />
74), 'foobar')

Run Code within Extension Host

If you need to run certain code within the extension host, e.g. if you like fetch data for the widget, create an extension folder within the widget directory and point to a file that exports an activate method, e.g.:
Create a package/widget-foobar/extension/package.json
2  "name": "@vscode-marquee/widget-foobar-extension",
3  "description": "extension logic for Foobar Widget",
4  "main": "../build/extension.js",
5  "module": "../build/extension.js",
6  "types": "../build/extension.d.ts"
The activate method is called when Marquee as extension is activated. You can use it to iniate an ExtensionManager instance that simplifies state and configuration management for the widget, e.g.:
1import vscode from 'vscode'
3import ExtensionManager from '@vscode-marquee/utils/extension'
5import { DEFAULT_CONFIGURATION, DEFAULT_STATE } from './constants'
6import type { Configuration, State } from './types'
8const STATE_KEY = 'widgets.foobar'
10export function activate (
11  context: vscode.ExtensionContext,
12  channel: vscode.OutputChannel
13) {
14  const stateManager = new ExtensionManager<State, Configuration>(
15    context,
16    channel,
17    STATE_KEY,
20  )
22  return {
23    marquee: {
24      disposable: stateManager,
25      defaultState: stateManager.state,
26      defaultConfiguration: stateManager.configuration,
27      setup: stateManager.setBroadcaster.bind(stateManager)
28    }
29  }
Lastly import the method within the packages/extension/src/stateManager.ts so that the activate method will be called accordingly and the tangle instance attached to the webview.

Reporting New Issues

When opening a new issue, always make sure to fill out the issue template. This step is very important! Not doing so may result in your issue not managed in a timely fashion. Don't take this personally if this happens, and feel free to open a new issue once you've gathered all the information required by the template.
  • One issue, one bug: Please report a single bug per issue.
  • Provide reproduction steps: List all the steps necessary to reproduce the issue. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort.

Security Bugs

Release New Version

Package releases are made using a GitHub workflow. All you need to do is go to the Manual NPM Publish workflow and trigger a new run. Choose the appropriate version upgrade based on the Semantic Versioning and the release channel. To help choose the right release type, here are some general guidelines:
  • Breaking Changes: never do these by yourself! A major release is always a collaborative effort between all TSC members. It requires consensus from all of them.
  • Minor Release: minor releases are always required if a new, user focused feature was added to one of the packages. For example, if a command was added to WebdriverIO or if a service provides a new form of integration, a minor version bump would be appropriate. However if an internal package like @wdio/local-runner exposes a new interface that is solely used internally, we can consider that as a patch release.
  • Patch Release: every time a bug is fixed, documentation (this includes TypeScript definitions) gets updated or existing functionality is improved, we should do a patch release.