Git Product home page Git Product logo

blast's Introduction

Blast Launcher (aka C8763)

Update: I wrote an introduction post for Blast Launcher.

Blast is an open-source educational and experimental project that aims to utilize the extension ecosystem of Raycast Launcher

Raycast is a SaaS software, while providing paid subscription services for teams, for personal is copmletely free. You can also extend your workflow by building extensions on your own. The extensions are implemented using custom React components(@raycast/api), and each command within an extension can be thought of as a separate React application. I think that's the magic of Raycast, where everyone can easily build and integrate their own tools without much effors on top of the huge Node.js and React.js ecosystem.

While Raycast is great, it is a closed-source software and the extension ecosystem can only be used in Raycast. So here's where this project comes in.

Blast provides an open-source React renderer to render these Raycast extensions.

Demo

demo_todo

Architecture

Blast uses the following components:

  • Node.js to write the backend and implement a custom React renderer with react-reconciler
  • Electron & React.js to write the front end and render the React element tree as HTML
  • Use rpc-websockets to communicate between the backend and front end

In the architecture of Blast, the backend uses Node.js and the react-reconciler package to implement a custom React renderer. The element tree created during this process is then emitted as a JSON object tree to the front end, which is an Electron app built with React.js and rendered as HTML. While the front end is built with React, it is also framework agnostic as it can accept the plain JSON element tree from the backend.

For higher performance, a custom renderer such as React-native may send operations to the host app to build a shadow element tree alongside the renderer. However, Blast was designed for educational and experimental purposes and therefore emits the entire element tree as JSON during the resetAfterCommit phase, which is called every time the component is updated. This is less performant but sufficient for the needs of this project as the component tree is not complex and high performance is not required.

You can learn more about the blast architecture in the following documents:

Basic usage

Install

  • Download the latest version of Blast from the release page
  • Unzip the downloaded file and move the Blast app to the Applications folder
  • Note: On macOS, you may need to allow the app to be opened in "System Preferences"
    • or right click on the Blast app and select "Open" to open the app
    • If it doesn't show up in the context menu, do it again.

After opening the Blast app, it will ask to install Node.js runtime. Just click "Install" and wait for the installation to complete. It downloads node.js and extract it to ~/.blast/node.

Usage

The shortcut key to open the Blast window is ⌘;, Command + ;. It will be configurable in the future.

Development

# Make sure you have pnpm v7 installed, then install dependencies with pnpm
pnpm install

# Start build
pnpm run watch

# Start front end in dev mode
pnpm run start:client

Debugging

See runtime logs

tail -f ~/.blast/logs/runtime.log
tail -f ~/.blast/logs/runtime.err.log

Debugging runtime with React DevTools

Just run:

pnpm react-devtools

And start the application. It should automatically connect to the React DevTools.

react-devtools

Inspiration and related projects

  • SunbeamLauncher: A frontend agnostic Launcher app, written in Go, and you can write extensions in any language.
  • ⌘K, cmdk: A React.js Component to build Launcher app UI. The front end of Blast is based on this project. The author also provided several launcher themes, such as Linear, Raycast and Vercel,but they are only for demo purposes so you need to modify them to use.
  • Sittly Launcher: Another project inspired by raycast! I discovered it while regularly searching for projects with the "raycast" keyword on GitHub. 😂 Oops, sorry for the early spoiler.

FAQs

Why naming blast?

Our Chief Marketing Officer(nickname ChatGPT) gave me this idea.

I was asking him/her/it to come up with words similar to "raycast".

Why C8763?

星爆臉素材

Blast(爆破)應該滿星爆的吧!但星爆氣流斬原文是 starburst stream。算了。

Links

License

MIT

blast's People

Contributors

yukaii 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

Watchers

 avatar  avatar  avatar

Forkers

steward379

blast's Issues

Mirror the raycast/extensions repostiory and publish them automatically to the package registry

Steps

  • TBD Use a workflow to sync repository to the forked repository blast/extension

Possible solutions...

The Raycast team runs the "publish" action with every new commit from branch main. Since it is a built-in action event on GitHub and run by the workflow inside the repository, they can ensure that the action runs with every commit. 1

If I manually or automatically mirror the raycast/extensions repository, the synchronization status (i.e., the last commit that was synced) must be stored somewhere, and generating the differences (i.e., determining which extensions need to be updated) will be more complex.

Alternatively, if I fork the raycast/extensions repository and replace the actions it runs, and handle git merging within the GitHub action context, I would still need to track the last synced commit 2, then push each commit in between one by one to the forked repository, ensuring that every "publish" action is enqueued. Additionally, if the synchronization process is run periodically as a workflow, I would need to ensure that only one synchronization workflow action is running at a time. 3

Footnotes

  1. However, there may be a race condition where, for example, commit 1 changes extension A and commit 2 also changes extension A. In this case, the publish action triggered by commit 1 might run after the action triggered by commit 2, causing the latest published extension to be reverted back to commit 1. But since pull requests are manually merged, this scenario is unlikely to occur. (maybe?)

  2. Can it be stored as JSON artifacts?

  3. GitHub Actions supports only run one job at a time, so it will be fine

Windows Build

  • dependency @raycast/cli will run post-install script, bin/ray only compatible on linux/macOS, so pnpm install failed

Towards 1.0

Brief

Now, Blast is packaged and distributed as an Electron app.

Once downloaded and launched, it will download and extract the node.js runtime to $HOME/.blast/node. This will be used to initiate the blast-runtime script.

The primary objective for version 1.0 is to create an interface that can run as many Raycast extensions as possible.

Exploration

  • How to track api implementation progress in public?
  • Explore node worker thread model for runtime to starting extensions (separated threads for each extension command)

Runtime & API

  • Passing WebSocket server running port through command line argument
  • Make party parrot extension works
  • Make LocalStorage extension-wise
  • Cache API
  • Check all icons
  • Toast API

Frontend

  • #21
  • Polish Form Component
  • Refactor List & Command component
  • Form navigation
  • Handle default action
  • Register Keybindings
  • #35

Design

  • #31
  • Replacing Design system, maybe fluent ui

Archived

Extension App Architecture

  • #5
  • #7
  • #9
  • #16
    • Generate command lists from extension directory
    • Load command dynamically
    • Store: list packages from npm org @blast-extensions, and fetch contributed commands

Enginerring

  • 🔽 extract renderer core, using yarn workspaces or something
    • Eventually I use pnpm workspace

Marketplace

  • #10
  • Implement ray package to package extension like this Use ray build directly
  • #11

Rethink about the runtime process model and electron

I have some difficulties starting the application shipping process around commit fb29964.

Initially, I started the runtime in electron's utility process and struggled with packager bundling 2fcb790. Eventually, I found it too troublesome to eval/load the extension module and its dependencies inside the electron main process, so I decided to manage the Node.js binary on the user's computer and run the runtime with that nodejs runtime.

Regarding the shipping of the runtime, currently I use esbuild to build another single file cjs module of blast-runtime. I have two options:

  • Use that downloaded node binary's npm to install @blastlauncher/runtime with ~/.blast/extensions prefix, just like other blast-extensions. The version can be explicitly found in ~/.blast/extensions/package.json, and version pinning and upgrading can also be easily done.
  • Ship the runtime module as electron assets. We can bundle it with electron-forge's webpack, and there's no need to handle some spawn process magic. But it needs to be upgraded with the electron app itself.

About the compatibility between the runtime and frontend, it's another story.

Tasks

  • Update the @blastlauncher/runtime package to include minimum runtime dependencies. All deps should be bundled with esbuild.
  • Publish a new version of runtime. Use it along and pack with electron-client module webpack
  • Implement a node binary manager module in @blastlauncher/utils (We can search for an existing solution first).
  • Integrate the node binary manager in the electron-client.
  • Implement a runtime process manager in the electron-client.

Ship early, ship often

  • #24
  • Shortcut to toggle window(Just copy and paste scriptkit impl or other electron based launcher apps) (Set to CMD + ; for now)
  • #30
  • Build electron app in CI
  • Enable auto update channel for electron application
  • #31

Milestone MVP

Goal: Working Example for todo-list Extension

Tasks

API that Needs to be Supported

  • List/ListItem, ...
  • ActionPanel
  • Action.Push (with basic push/pop stack routing)
  • Action shortcut serialization
  • Empty view
  • Form and text field
  • Icon

Reconciler-Related

  • Explore basic element type
  • Explore render protocol
    • Is it a good idea to send the whole component tree as JSON through WebSocket? (Or is it quicker to implement?)

High-Level Topics

  • How to reuse existing type declarations?
  • Background job
  • Bundling
  • Make the whole app in React architecture?

Internal Application Backlog

Store

  • Not refreshing command when popping to root
  • installed/uninstalled label

General Navigation

  • No way to pop the root(Add esc as keybinding)

macOS App signing

  • Apple developer program
  • Does self-issued certificate signing work today?

So let's check the answer(against Raycast Architecture)

Read Raycast's awesome writeup first.

What I was right about Raycast

  • The observation around Raycast's open source strategy is that they manually review and prevent some security issues, while still sharing solutions. (I have a blog post that discusses Raycast in a "developer" way. The post is in Traditional Chinese)
  • Raycast uses React Reconciler and JSON representation for components, which is similar to the approach taken in blast.
  • Raycast uses a prebuilt Go binary with esbuild included to build the Raycast CLI, a similar approach is taken in blast-cli.

Differences (where blast could improve)

  • Blast could improve by adding worker thread isolation for running extensions.
  • Raycast currently uses file descriptors and JSON-RPC for IPC, which seems to not work on Windows.
  • Raycast uses serial queues and buffered streams to ensure that messages arrive and leave in order.
  • Raycast uses JSON patch to incrementally update the component tree.

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.