Git Product home page Git Product logo

multi's Introduction

Multi logo

Multi

Create a custom, lightweight macOS app from a group of websites, complete with:

  • Native notifications, file uploads, and dialogs
  • Customization options with JSON, CSS, and JavaScript
  • CLI for creating and updating apps
  • Options for tabbed or floating windows

Watch me create a Slack clone from scratch in 30 seconds (high res video):

Demo GIF

Table of contents

I've also written a few blog posts that discuss some of the decisions behind Multi:

If you enjoy using Multi, please consider showing your appreciation with a donation!

Buy Me a Coffee at ko-fi.com

Installation

The easiest method is to use Homebrew:

brew install --cask multi

Alternatively, you can manually download and run the latest .dmg from Releases.

JSON configuration

Multi apps store their configuration in a single JSON file. If your app is named Test, then you'll find that file at /Applications/Multi/Test.app/Contents/Resources/config.json. The JSON configuration uses the following top-level fields:

Field Name Type Description
tabs Array Titles and URLs of tabs for this app (Required)
windowed Boolean Start this app with each tab in its own window
alwaysNotify Boolean Show macOS notifications even if this app is currently focused
alwaysOnTop Boolean Position this app's window on top of all others
terminateWithLastWindow Boolean Determine if this app closes once all tabs/windows are closed
openNewWindowsInBackground Boolean Determines if browser app becomes active when opening external links
openNewWindowsWith String Override system default browser for external links—value is a bundle identifier like com.apple.Safari, com.google.Chrome, or com.mozilla.firefox

The tabs field is an array of objects with the following fields:

Field Name Type Description
url String Starting page for this tab (Required)
title String Name for this tab
customJs Array of Strings Custom JS URLs (see Custom JS/CSS)
customCss Array of Strings Custom CSS URLs (see Custom JS/CSS)
customCookies Array of Objects Custom cookies using HTTPCookiePropertyKey
basicAuthUser String User name credential for requests that use basic access authentication
basicAuthPassword String Password credential for requests that use basic access authentication
userAgent String Override the default WebKit user agent header

Here's a bare minimum example to recreate the Slack demo video above:

{ "tabs": [{ "url": "https://app.slack.com/client" }] }

Here's a fancier example that uses the optional fields referenced above:

{
  "tabs": [
    {
      "title": "Dancing",
      "url": "https://rc.kofi.sexy/bathroom-floss",
      "basicAuthUser": "user",
      "basicAuthPassword": "password",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"
    },
    {
      "title": "Walking",
      "url": "https://kofi.sexy/cel-shading",
      "customJs": [ "file:///Users/kofi/Documents/dotfiles/main/example.js" ],
      "customCss": [ "https://example.com/custom.css" ],
      "customCookies": [
        {
          "name": "login_token_tab",
          "value": "eyJoZWxsbyI6ICJ3b3JsZCJ9",
          "domain": ".example.com",
          "path": "/"
        }
      ]
    }
  ],
  "windowed": true,
  "alwaysNotify": true,
  "alwaysOnTop": true,
  "terminateWithLastWindow": true,
  "openNewWindowsInBackground": true,
  "openNewWindowsWith": "com.apple.Safari"
}

If your configuration file fails to decode, you can use the settings window to fix the issues. Optional fields will always default to "empty" values (i.e. false, "", []).

Using the CLI: create-mac-app

You can create and update Multi apps entirely from the command-line with the included script. In fact, the Multi configuration UI just runs this script under-the-hood! The create-mac-app script takes its options as environment variables. For instance, here's how you'd create a bare-minimum app named Test:

MULTI_APP_NAME='Test' /Applications/Multi.app/Contents/Resources/create-mac-app

When you open Test, you'll be greeted with the preferences window, where you can finish configuring your app. If you'd like to configure your app entirely from the command-line, you can set any of the following variables:

MULTI_ICON_PATH PNG or ICNS path to icon image
MULTI_JSON_CONFIG See JSON configuration
MULTI_OVERWRITE Set to 1 to replace an existing Multi app with the same name

Custom JS/CSS

Multi lets you customize any site by injecting JavaScript and CSS on every page in your app. Each custom JS/CSS file is specified with a URL, which gives you a few options for how you want to manage your customizations:

  1. Host your file online, and use its URL: ex. https://raw.githubusercontent.com/kofigumbs/dotfiles/master/example.js
  2. Reference a local file on your computer: ex. file:///Users/kofi/workspace/dotfiles/example.js
  3. Encode your script directly in the JSON using Data URIs: ex. data:,console.log%28%27Hello%2C%20from%20Multi%21%27%29%3B%0A

Custom JS/CSS is one of the most important parts of Multi. It lets the main project stay small and focused, while letting you extend it with new features that fit your use case. If you have a neat JS/CSS snippet, you'd like to share, please open an Issue or Pull Request! Here are a few that have come up before:

Fix links in GMail and Google Calendar

Google seems to be doing some trickery here. Instead of allowing the browser to handle the links, they use JS to open a blank popup window, then dynamically set the URL to google.com/url?q=REAL_URL_HERE. Presumably all of this is so that they can track you for a few moments on your way out of their app. Custom JS solution:

window.addEventListener("DOMContentLoaded", () => {
  const listener = e => e.stopPropagation();
  const query = () => document.querySelectorAll("a[target=_blank]").forEach(a => {
    a.removeEventListener("click", listener);
    a.addEventListener("click", listener, true);
  });
  setInterval(query, 400); // wait time between DOM queries, in milliseconds
});

Find in page

Multi doesn't include any search functionality (Cmd-F). Custom JS solution:

window.addEventListener("DOMContentLoaded", () => {
  const highlightResults = (text, color) => {
    document.designMode = "on"; // https://stackoverflow.com/a/5887719
    var selection = window.getSelection();
    selection.collapse(document.body, 0);
    while (window.find(text)) {
      document.execCommand("HiliteColor", false, color);
      selection.collapseToEnd();
    }
    document.designMode = "off";
  };

  let mostRecentSearchText = "";
  const search = text => {
    highlightResults(mostRecentSearchText, "transparent");
    highlightResults(text, "rgb(255 255 1 / 50%)");
    mostRecentSearchText = text;
  };

  const input = document.createElement("input");
  input.placeholder = "Search...";
  input.style.padding = "10px 15px";
  input.style.fontSize = "15px";
  input.style.borderRadius = "3px";
  input.style.border = "solid 1px lightgray";

  const form = document.createElement("form");
  form.style.display = "none";
  form.style.position = "fixed";
  form.style.top = "15px";
  form.style.right = "15px";
  form.style.zIndex = "2147483647"; // https://stackoverflow.com/a/856569
  form.addEventListener("submit", e => {
    e.preventDefault();
    search(input.value);
  });

  const close = document.createElement("a");
  close.innerText = "⨯";
  close.href = "javascript:void(0)";
  close.style.fontSize = "30px";
  close.style.padding = "15px";
  close.style.textDecoration = "none";
  close.addEventListener("click", e => {
    e.preventDefault();
    search("");
    form.style.display = "none";
  });

  form.appendChild(input);
  form.appendChild(close);
  document.body.appendChild(form);

  document.addEventListener("keydown", event => {
    if (event.metaKey && event.key === "f") {
      event.preventDefault();
      form.style.display = "block";
      input.focus();
    }
  });
});

Drag-and-drop to open URLs

Say you have a URL outside of Multi (maybe in an email), and you want to open it in Multi. Custom JS solution:

document.addEventListener("dragover", e => e.preventDefault());

Preview link targets

Multi doesn't include any hover-to-preview-link-target functionality. Custom CSS solution:

a:hover::after {
  content: attr(href);
  position: fixed;
  left: 4px;
  bottom: 4px;
  padding: 4px;
  font-size: 12px;
  font-family: -apple-system, BlinkMacSystemFont;
  font-weight: normal;
  color: black;
  background: ghostwhite;
  border: solid 1px black;
  border-radius: 1px;
}

multi's People

Contributors

alechemy avatar chrismessina avatar clinejj avatar dotsam avatar hyperknot avatar kofigumbs avatar shaharhd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

multi's Issues

software license is ambiguous

The README says that multi is GPLv3, but it also says you should "purchase a license". This is ambiguous, as the GPL is a license. It also says on your website:

Multi is licensed, not sold. You can only use Multi if you agree with these usage terms. A Multi license can be used in one of two ways:

This suggests that the software is not GPLv3.

Could you please put a LICENSE file in the repository that clarifies that the software is GPL licensed, if indeed it is?

Unable to download a file

I've created an multi application for basecamp, but I'm unable to download files within the the application. Is that something that can be supported?

Check for extra config fields

Currently, there's no feedback for misspelled configuration fields. In #46, it took a couple of messages back-and-forth to discover that customJS should have been customJs. The configuration app should be smarter.

An issue when building & how to solve it

If you get this error:

$ ./create-mac-app someapp 1.png
# error: terminated(72): xcrun --sdk macosx --find xctest output:
#     xcrun: error: unable to find utility "xctest", not a developer tool or in PATH

Try:

$ sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
$ ./create-mac-app someapp 1.png
# [7/7] Linking Multi

😆

Add ability to select .icns files as the icon

Not a big issue as I can manually set an .icns file on a created app using the Finder. But it seems a bit weird that you can't select one at creation, especially as Multi is generating one, but from a single image, so it will lack any small size optimisation/hinting.

Shortcut to copy current URL to clipboard

Is it possible to create a keyboard shortcut that will grab the current URL? I sometimes need to share a link with colleagues in Basecamp or other Multi apps, but I can’t always get the current link from the apps unless it’s an item I can right-click on.

View layout bugged on 10.13.6

When run on High Sierra (10.13.6), the view layout has a fixed width & height that doesn't adapt to the window's dimensions.

Sample image

I'm really loving the simplicity of this whole project, sadly I'm not familiar with Swift so I don't know how to figure out dynamic window sizing.

Remember window size/position

Whenever I open my Multi app, the window opens to the full screen. It should open at the size and position it was at last time.

Tab Bar

I would like a tab bar at the top. I actually like showing the tabs and clicking on them. Is this possible?

Image not found Mac OS

Getting this error on trying to load a newly downloaded icon

➜  multi git:(master) ./create-mac-app hotlist /Users/umanjain/Downloads/icons8-internet-folder-100.png
dyld: Library not loaded: @rpath/llbuild.framework/Versions/A/llbuild
  Referenced from: /Library/Developer/CommandLineTools/usr/bin/swift-build
  Reason: image not found
./create-mac-app: line 26: 74897 Abort trap: 6           swift build

Improve notifications

I noticed that Slack notifications work okay, but if I get a message in a certain channel and click on it, it doesn’t open the channel, just the Slack window.

Another thing I can’t get to work are notifications in the app icons. I never know how many unread messages there are in Slack, Google Mail, etc.

Is this something that could be added to the app icons?

Allow downloads/pop-ups in certain apps

Multi apps don’t seem to allow downloads, which might be due to the Better blocker as pop-ups are also blocked. Is it possible to allow the download of files/images somehow? I can’t download files from Slack and other apps. There’s an option to download them, but nothing happens.

Custom JS support

Hello! Thanks for awesome project!
How about adding custom js support?

Error on creating new Multi app

Here are the settings I used to generate a new app:

{
  "tabs": [
    {
      "title": "Substack Reader",
      "url": "https://reader.substack.com"
    }
  ]
}

When Terminal opened up, there was an error on creation, although the app was created successfully:

CleanShot 2020-12-16 at 11 19 36@2x

NSInvalidArgumentException on created app launch

Big "[App] quit unexpectedly" when launching any app created with Multi 2.0.4 on Mac OS 10.13.6. This comes directly after resolving my swift runtime issue (#28), though not sure if they're related.

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSMenu setItemArray:]: unrecognized selector sent to instance 0x604000269b00'
terminating with uncaught exception of type NSException
abort() called

Clicked links in Gmail, other apps don't work

I've got an issue where clicking on a link in Gmail causes an error message to appear about popups being blocked. Is there a workaround or setting to whitelist or enable links like this to work?

Love the app, and love the fact that I managed to compile it for Apple Silicon with zero issues. It's crazy fast!

CleanShot 2020-12-03 at 07 52 01

Copy with Command-C not working

Title says it all. I created an app with 4 websites. I go to the fisrt one with Command-1. I select some text and hit Command-C. Then Command-2, and put my cursor in a box and hit Command-V and it pastes in the wrong thing. It pastes in whatever was on my clipboard before I tried all of this. So the Command-C didn't work.

Find in page feature

I was hoping to use Multi to wrap our backend software, and it's almost perfect for us. But the inability to use Command + F to find text within the page is necessary for us to do our work quickly. It would be great if this was added at some point if you have time.

Custom JS

@kofigumbs Could you share a full example of an app with the customJs feature?

I can't get any code to run.

Also, if your example you have console.log. How do I launch the console/dev tools in the app?

Feature request: closing the window without quitting the application

Most Mac applications allow you to close the application window, but keep the application running. By default, this can be done either by cmdW or the red cross in the top-left corner. Clicking on the application icon on the dock would open a new window after the fact.

With the recent handy addition of the minimize command, this would complete the set of basic OS-level shortcuts you would usually find in a native application.

Currently, the feature set is partially covered:

  • The window should close from the red cross button
  • The window should close on cmdW
  • A new window should be opened when there are no windows and the application icon is clicked

It might be worth considering hiding the window instead of closing it, allowing the application to continue receiving notifications without having a user-facing window open.

Open links in background

A nice additional configuration setting would be to open links in the background, so they open in a new browser tab, but the browser window isn't brought to the foreground.

Issues on OSX 10.11

Hello! This project is exciting. I'm trying to run it on OS 10.11 (perhaps the best version of OSX?), which has Swift 3.0.2. I'm running into these problems:

$ ./create-mac-app name icon.png
Package.swift:12:5: error: argument 'targets' must precede argument 'dependencies'
    targets: [
    ^
Can't parse Package.swift manifest file because it contains invalid format. Fix Package.swift file format and try again.

This was easy to fix by re-ordering targets and dependencies in Package.swift like this:

    targets: [
        .target(name: "Multi"),
    ],
    dependencies: [
    ]

But now I'm getting this error:

Package.swift:5:22: error: incorrect argument label in call (have 'name:products:targets:dependencies:', expected 'name:pkgConfig:targets:dependencies:')
let package = Package(
                     ^
Can't parse Package.swift manifest file because it contains invalid format. Fix Package.swift file format and try again.

I'm not sure what to do about this one. I'm not a Swift export, unfortunately.

Zoom feature

Some web pages use small fonts and in some other cases I would like to present something on a beamer, where it would be handy to be able to zoom in (cmd++), out (cmd+-) and back to default (cmd+0). Currently, it is not possible.

Ability to add custom CSS (and maybe JS?)

Hi, first off I wanted to say I LOVE this app, I think I know have a problem as every website I use regularly has been turned into a multi-app. The problem with this, however, is any extensions I had no longer work inside multi. My example use case would be I am using a website I don't want to sign in on and I can use this website without signing in just fine, there's just always a part of the webpage taken up telling me to sign-in. In Chrome I made a simple extension to just run some CSS on that specific domain to hide that banner, but in Multi I am not seeing a way this is possible. I think that would be an amazing feature too and would be something you could edit in the JSON configuration.

On a similar note, I think the ability to add your own javascript into the Multi app would be fantastic (another feature an extension could accomplish). This way I could have JS implemented to do things like translate text and such. Maybe I am overly optimistic about this project, but I am envisioning a sort of plugin store for multi where you could package and install extensions to run.

Lastly, I didn't see anywhere to donate to this (I would try to contribute to this project with code but I know nothing about Swift apps and such, so a monetary donation is the next best thing). Do you have a Paypal link or OpenCollective fund you would consider adding to the readme?

Thanks!

Basic Authentication

Hi there!
I am using multi for some web-apps I have written myself. My very basic web app is protected by basic authentication on Nginx. In Chrome or Safari when opening my webpage for the first time, I get a popup asking me for a username and password to access this site. If the login credentials are incorrect it returns an Unauthorized error via Nginx, but if correct I can access my content.

When launching a multi app protected by basic authentication, there is no prompt for log-in details and it instead immediately shows an unauthorized error. My current solution to the problem was to open the web app in Safari and login and save the login to my keychain which then allowed me to access the site in multi.

External links broken in Gmail and Google Calendar

I seem to be getting a related issue with Gmail and Google Calendar, where clicking any link to another site in Gmail causes it to open an alert with:

"Grrr! A popup blocker may be preventing the application from opening the page. If you have a popup blocker, try disabling it to open the window."

and the browser opens but the link does not get passed into it. If I use the openNewWindowsWith set to Safari.

With Chrome as my default browser and no openNewWindowsWith config, I also get a MacOS popup that says "The application can’t be opened. -50". This looks to be something with how Gmail/Calendar handle links?

Originally posted by @clinejj in #25 (comment)

Unable to create Multi app: "Cannot allocate configuration script."

App version: 2.0.1
macOS version: 10.15.6

I gave Multi Full Disk Access in Security & Privacy settings.

Expected behavior:

I can create a Multi app with a custom icon via the GUI interface.

Current behavior:

I get an error when creating a Multi app right after pressing "Save & Launch":

Screen Shot 2020-08-12 at 12 58 46

Screen Shot 2020-08-12 at 12 58 41

Screen Shot 2020-08-12 at 13 06 10

However, an app is created, but it doesn't work. The following window opens:

Screen Shot 2020-08-12 at 13 06 31

Enable microphone and screen sharing

Some of the web apps like Slack, Discord, Meet don't have access to mic and screen sharing permissions. Would be awesome to also have this feature enabled.

Thanks

Side-by-side view

Suggestion from Twitter.

Would be really useful if you have a particular workflow where you regularly work with two or three specific websites at once. Make sure to consider #7 when designing.

Swift "image not found" error

I'm running into a similar issue to #2 when trying to initially run Multi.app (2.0.3). I'm on OS 10.13.6 and currently do not have the full Xcode installed. Installing it now and trying out that fix to see if it resolves things - it seems desirable to have this great tool work out of the box though, if possible!

Termination Reason:    DYLD, [0x1] Library missing

Application Specific Information:
dyld: launch, loading dependent libraries

Dyld Error Message:
  Library not loaded: @rpath/libswiftCore.dylib
  Referenced from: /Applications/Multi.app/Preferences
  Reason: image not found

Can't sign in to a second team on slack

My slack multi has this config, dead standard:

$ cat /Applications/Multi/Slack\ Safari.app/Contents/Resources/config.json
{
  "tabs": [
    {
      "title": "Slack Safari",
      "url": "https://app.slack.com/client"
    }
  ]
}

It works fine for the one team that I am signed in with, and I like it so I want to sign in to another team.

Unfortunately, it pops open system Safari when I click Add workspaces -> sign in to another team, and when I add a team, it doesn't redirect back to the Multi app. Demo: https://www.youtube.com/watch?v=9jFgToEW4Lc&feature=youtu.be

(nothing happens after I click "allow")

Any ideas for how to log into a second team?

Refresh (command + R)

Another small thing missing from the browser: being able to refresh. Lower priority for us, but just wanted to record it in case you continue working on this for a while.

Thanks again for making it!

multi opens up in inconvenient locations

I have an external monitor as my primary monitor. When I open up multi apps, they often up almost completely off the desktop, with just a tiny bit visible.

Attached is an example of where a multi app started itself up:

multibug

multi apps should ideally start themselves where they were most recently located

Slack doesn't auto-update in Multi

Hi-ho -

I have an instance of Slak running in Multi and realized today that it's not auto-updating when new messages come; I need to explcitly reload the screen to see new messages.

Not sure what kind of troubleshooting or logs would help you. Let me know if I can help narrow this down!

Attachments in Asana for Google Drive don't work

Trying to add an attachment to an Asana ticket from Google Drive doesn't work. On the normal site, when attaching something from Google Drive, it will display a popup window for you to select the appropriate Google account. When using Asana in multi, the pop-up never shows. The other external services appear to work (Box, OneDrive, and Dropbox), so this might be how Google's OAuth flow works.

Support custom deep links

It'd be really cool to be able to register URLs that the app is capable of handling, that way things like "Open in Slack App" work properly.

Display link targets

It would be nice to have a way of seeing where a link is pointing when hovering over it.

The way Safari does it when you use View > Show Status Bar is nice. The URL appears at the bottom of the window while hovering over the link.

Issue with slack notifications

Tried out Slack today, but my notifications don't seem to be working with a minimal config. Might be cool to document the successful configs somewhere.

Reconsider the licensing strategy

I realize this is going to a be a touchy subject. I'm sorry for that.

I really think you should reconsider the trial period. I can't tell you how much mac software I've uninstalled from engineers' laptops and replaced with other alternatives because this unsustainable pattern. You've done great work here, and you shouldn't detract from it by forcing people to pay. Sure, you'll make money, but you'll also join the stack of niche macos software that never gets anywhere beyond it's loyal following and is eventually unmaintained.

I think you'd have much much much more success keeping this FOSS, waiting for interest to build, and then license support to companies and/or have paid bounties for development. I can only say that at my company (fortune 100) we started chatting about using Multi, and that discussion was completely thrown out the window today.

It's a bummer that I'm uninstalling Multi today.

Enable Spelling and Grammar feature

Most applications allow you to enable a spelling and grammar feature, which can highlight errors in the textarea inputs for a given web application.

Allow drag and drop URLs

I would like to use Multi for Substack Reader, but to authenticate, Substack sends me an email with a link that use to login. I can't seem to open this URL within the Multi app that I created.

If I could drag and drop the URL that I received in my email, I could authenticate successfully.

Javascript Injection

Support javascript injection onload for additional customization.

The use case I have is to add content security policy to gmail in order to prevent it from loading new emails throughout the day, and only retrieve them at certain hours (to prevent gmail addiction).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.