default: package
start:
- docker-compose up
+ podman-compose up
-docker-build:
- docker-compose build
+install-extension-dependencies:
+ cd extension && pnpm install
-build: create-build-folder
+build: install-extansion-dependencies create-build-folder
cd extension && pnpm build
package: package-for-firefox package-for-chrome package-for-safari
cd extension && zip -r ../build/junction.zip manifest.json junction.js build/content_script.js icons/ sounds/
package-for-safari: build
- xcodebuild build -project safari/Junction.xcodeproj -scheme Junction -configuration Release -derivedDataPath ./build/_xcode
+ xcodebuild build -project safari/Junction.xcodeproj -scheme Junction -configuration Release -derivedDataPath ./build/_xcode | xcbeautify
cp -r ./build/_xcode/Build/Products/Release/Junction.app ./build/
create-build-folder:
mkdir -p build
+
+format:
+ prettier . --write
+ xcodebuild -project safari/Junction.xcodeproj | xcbeautify
# Junction
-Connect people through any URL
+Connect people through any URL.
-## Extension
+Junction is a web extension for Firefox, Chrome and Safari that lets you start
+an audio call at any URL.
-First, make sure to build the plugin. From the extension's root you can call:
+## Building
+
+Running `make` or `make package` will regenerate the content script, and
+package the extension for all browsers in the `./build/` directory with
+the following outputs:
+
+- `junction.xpi`: The firefox extension
+- `junction.zip`: The chrome extension
+- `Junction.app`: The safari extension app
+
+## Developing
+
+The project is divided into three main parts:
+
+- `server`: A server that handles message passing to establish the WebRTC
+ connections.
+- `extension`: The main code of the extension itself.
+- `safari`: The safari app, it wraps the extension using symlinks.
+
+### Dependencies
+
+- `make` to run the build commands.
+- `xcode` to build the safari extension.
+- `node` and `pnpm` to build the extension, and if you want to run the server
+ directly.
+- `podman` and `podman-compose` if you want to run the server as a container.
+- `prettier` to format Javascript, JSON, Yaml, and Markdown.
+- `xcbeautify` to format swift
+
+### Server
+
+The server is a socket.io server that relays messages between peers to help
+establish a WebRTC connection.
+
+#### Configuration
+
+You can use environment variables to control the server.
+
+- `JUNCTION_SERVER_PORT` which port to listen on. Defaults to `8000`.
+
+### Extension
+
+The extension uses WebExtension manifest v2 and is compatible with Firefox,
+Chrome and Safari.
+
+You can build all extensions using `make` in the root of the directory.
+
+If you want to run manually then you can run `pnpm install` from the
+`extension` folder to install dependencies, and `pnpm build` to generate the
+content script.
```
$ pnpm install && pnpm build
```
-### Testing on Firefox
+#### Testing on Firefox
In order to test on firefox, first go to `about:debugging`, then click
on the `This Firefox` option. Then click on `Load Temporary Add-On` and
-point the browser to the `extension/manifest.json` file.
+point the browser to the `build/junction.xpi` file if you're using `make`, or
+`extension/manifest.json` file if you're testing without packaging.
-This will enable the extension and will allow you to use the inspector to debug.
+This will enable the extension and will allow you to use the inspector to
+debug.
-### Testing on Chrome
+#### Testing on Chrome
In order to test on chrome, first go to `chrome://extensions/`. Make sure
-`Developer mode` is enabled. Then click `Load Unpacked` and point the browser
-to the `extension` directory.
+`Developer mode` is enabled. You can drag the `build/junction.zip` file or
+click `Load Unpacked` and point the browser to the `extension` directory.
+
+#### Testing on Safari
+
+Open `Junction.app` and then enable the extension in Safari.
-### Packaging
+### Formatting
-The makefile includes scripts to create a distributable package. `make package`
-should generate all supported files in the `build/` directory.
+Running `make format` will format the code with prettify and xcbeautify
-version: "3.8"
+version: "3.9"
services:
server:
shouldCreateOffer,
mediaStream,
onOffer: (data) => socket.emit("relayOffer", data),
- socket
+ socket,
});
playTada();
});
internals.injectedScript = true;
},
);
- } else {
- internals.getRoot().tabs.connect(activeTabs[0].id)
- }
- if (execution && !execution[0]) {
- internals.onDisconnect();
+ if (execution && !execution[0]) {
+ internals.onDisconnect();
+ }
+ } else {
+ internals.getRoot().tabs.connect(activeTabs[0].id);
}
},
+
hangUp() {
internals.getRoot().browserAction.disable();
internals.port.postMessage({
},
};
-export function addPeer({ peerId, shouldCreateOffer, mediaStream, onOffer, socket }) {
+export function addPeer({
+ peerId,
+ shouldCreateOffer,
+ mediaStream,
+ onOffer,
+ socket,
+}) {
const peerConnection = new RTCPeerConnection(
{ iceServers: internals.kIceServers },
{ optional: [{ DtlsSrtpKeyAgreement: true }] },
{
- "colors" : [
+ "colors": [
{
- "idiom" : "universal"
+ "idiom": "universal"
}
],
- "info" : {
- "author" : "xcode",
- "version" : 1
+ "info": {
+ "author": "xcode",
+ "version": 1
}
}
{
- "images" : [
+ "images": [
{
- "filename" : "icon_16x16.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "16x16"
+ "filename": "icon_16x16.png",
+ "idiom": "mac",
+ "scale": "1x",
+ "size": "16x16"
},
{
- "filename" : "icon_16x16@2x.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "16x16"
+ "filename": "icon_16x16@2x.png",
+ "idiom": "mac",
+ "scale": "2x",
+ "size": "16x16"
},
{
- "filename" : "icon_32x32.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "32x32"
+ "filename": "icon_32x32.png",
+ "idiom": "mac",
+ "scale": "1x",
+ "size": "32x32"
},
{
- "filename" : "icon_32x32@2x.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "32x32"
+ "filename": "icon_32x32@2x.png",
+ "idiom": "mac",
+ "scale": "2x",
+ "size": "32x32"
},
{
- "filename" : "icon_128x128.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "128x128"
+ "filename": "icon_128x128.png",
+ "idiom": "mac",
+ "scale": "1x",
+ "size": "128x128"
},
{
- "filename" : "icon_128x128@2x.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "128x128"
+ "filename": "icon_128x128@2x.png",
+ "idiom": "mac",
+ "scale": "2x",
+ "size": "128x128"
},
{
- "filename" : "icon_256x256.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "256x256"
+ "filename": "icon_256x256.png",
+ "idiom": "mac",
+ "scale": "1x",
+ "size": "256x256"
},
{
- "filename" : "icon_256x256@2x.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "256x256"
+ "filename": "icon_256x256@2x.png",
+ "idiom": "mac",
+ "scale": "2x",
+ "size": "256x256"
},
{
- "filename" : "icon_512x512.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "512x512"
+ "filename": "icon_512x512.png",
+ "idiom": "mac",
+ "scale": "1x",
+ "size": "512x512"
},
{
- "filename" : "icon_512x512@2x.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "512x512"
+ "filename": "icon_512x512@2x.png",
+ "idiom": "mac",
+ "scale": "2x",
+ "size": "512x512"
}
],
- "info" : {
- "author" : "xcode",
- "version" : 1
+ "info": {
+ "author": "xcode",
+ "version": 1
}
}
{
- "info" : {
- "author" : "xcode",
- "version" : 1
+ "info": {
+ "author": "xcode",
+ "version": 1
}
}
{
- "images" : [
+ "images": [
{
- "idiom" : "universal",
- "scale" : "1x"
+ "idiom": "universal",
+ "scale": "1x"
},
{
- "idiom" : "universal",
- "scale" : "2x"
+ "idiom": "universal",
+ "scale": "2x"
},
{
- "idiom" : "universal",
- "scale" : "3x"
+ "idiom": "universal",
+ "scale": "3x"
}
],
- "info" : {
- "author" : "xcode",
- "version" : 1
+ "info": {
+ "author": "xcode",
+ "version": 1
}
}
-<!DOCTYPE html>
+<!doctype html>
<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'" />
- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1, user-scalable=no"
+ />
- <link rel="stylesheet" href="../Style.css">
+ <link rel="stylesheet" href="../Style.css" />
<script src="../Script.js" defer></script>
-</head>
-<body>
- <img src="../Icon.png" width="128" height="128" alt="Junction Icon">
- <p class="state-unknown">You can turn on Junction’s extension in Safari Extensions preferences.</p>
- <p class="state-on">Junction’s extension is currently on. You can turn it off in Safari Extensions preferences.</p>
- <p class="state-off">Junction’s extension is currently off. You can turn it on in Safari Extensions preferences.</p>
- <button class="open-preferences">Quit and Open Safari Extensions Preferences…</button>
-</body>
+ </head>
+ <body>
+ <img src="../Icon.png" width="128" height="128" alt="Junction Icon" />
+ <p class="state-unknown">
+ You can turn on Junction’s extension in Safari Extensions preferences.
+ </p>
+ <p class="state-on">
+ Junction’s extension is currently on. You can turn it off in Safari
+ Extensions preferences.
+ </p>
+ <p class="state-off">
+ Junction’s extension is currently off. You can turn it on in Safari
+ Extensions preferences.
+ </p>
+ <button class="open-preferences">
+ Quit and Open Safari Extensions Preferences…
+ </button>
+ </body>
</html>
function show(enabled, useSettingsInsteadOfPreferences) {
- if (useSettingsInsteadOfPreferences) {
- document.getElementsByClassName('state-on')[0].innerText = "Junction’s extension is currently on. You can turn it off in the Extensions section of Safari Settings.";
- document.getElementsByClassName('state-off')[0].innerText = "Junction’s extension is currently off. You can turn it on in the Extensions section of Safari Settings.";
- document.getElementsByClassName('state-unknown')[0].innerText = "You can turn on Junction’s extension in the Extensions section of Safari Settings.";
- document.getElementsByClassName('open-preferences')[0].innerText = "Quit and Open Safari Settings…";
- }
+ if (useSettingsInsteadOfPreferences) {
+ document.getElementsByClassName("state-on")[0].innerText =
+ "Junction’s extension is currently on. You can turn it off in the Extensions section of Safari Settings.";
+ document.getElementsByClassName("state-off")[0].innerText =
+ "Junction’s extension is currently off. You can turn it on in the Extensions section of Safari Settings.";
+ document.getElementsByClassName("state-unknown")[0].innerText =
+ "You can turn on Junction’s extension in the Extensions section of Safari Settings.";
+ document.getElementsByClassName("open-preferences")[0].innerText =
+ "Quit and Open Safari Settings…";
+ }
- if (typeof enabled === "boolean") {
- document.body.classList.toggle(`state-on`, enabled);
- document.body.classList.toggle(`state-off`, !enabled);
- } else {
- document.body.classList.remove(`state-on`);
- document.body.classList.remove(`state-off`);
- }
+ if (typeof enabled === "boolean") {
+ document.body.classList.toggle(`state-on`, enabled);
+ document.body.classList.toggle(`state-off`, !enabled);
+ } else {
+ document.body.classList.remove(`state-on`);
+ document.body.classList.remove(`state-off`);
+ }
}
function openPreferences() {
- webkit.messageHandlers.controller.postMessage("open-preferences");
+ webkit.messageHandlers.controller.postMessage("open-preferences");
}
-document.querySelector("button.open-preferences").addEventListener("click", openPreferences);
+document
+ .querySelector("button.open-preferences")
+ .addEventListener("click", openPreferences);
* {
- -webkit-user-select: none;
- -webkit-user-drag: none;
- cursor: default;
+ -webkit-user-select: none;
+ -webkit-user-drag: none;
+ cursor: default;
}
:root {
- color-scheme: light dark;
+ color-scheme: light dark;
- --spacing: 20px;
+ --spacing: 20px;
}
html {
- height: 100%;
+ height: 100%;
}
body {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-direction: column;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
- gap: var(--spacing);
- margin: 0 calc(var(--spacing) * 2);
- height: 100%;
+ gap: var(--spacing);
+ margin: 0 calc(var(--spacing) * 2);
+ height: 100%;
- font: -apple-system-short-body;
- text-align: center;
+ font: -apple-system-short-body;
+ text-align: center;
}
body:not(.state-on, .state-off) :is(.state-on, .state-off) {
- display: none;
+ display: none;
}
body.state-on :is(.state-off, .state-unknown) {
- display: none;
+ display: none;
}
body.state-off :is(.state-on, .state-unknown) {
- display: none;
+ display: none;
}
button {
- font-size: 1em;
+ font-size: 1em;
}
export function addPeer(peerId, shouldCreateOffer) {
return internals.emitter.bind(null, types.ADD_PEER, {
peerId,
- shouldCreateOffer
+ shouldCreateOffer,
});
}