# How Flux's Plugin System Works With the latest release of ADAM, a new framework for building decentralized applications, we have also updated our main app, Flux, a decentralized alternative to Discord or Slack. In this release, we have worked hard on a feature we think our community will be very excited about: Plugins! Communities can now add new features to Flux, without asking us for permission. For example, if you want to play chess with your community, you can create a plugin, publish it, and anyone can now install it and try it out with their community. Think Wordpress plugins, but for social networks! This is part of Coasys's bigger vision of creating a new living web where users and communities are not just passive consumers of apps but can extend or invent their own communication tools. This way, the web becomes evolvable, extendable and unrestricted, giving communities control and the power to move their data across apps. ## Background Context ### 1. The ADAM Layer The first thing that makes Flux's new plugin system possible is The ADAM Layer, an app development framework where all data is stored on your device and shared with others in Neighbourhoods. Each community in Flux is really just an ADAM Neighbourhood, and thus compatible with any other ADAM app. You can think of each Neighbourhood as a shared database where users can collaborate on data. The data is organized in a graph, and is really just a collection of "links" created by users. These links can be added, updated or removed by individual users, and the history of all these changes are synced between the people in the Neighbourhood. Let's say we want to add a todo item to a channel called "communication" in a Flux community. A very simplified example of how we might create this data could look something like this: ```javascript const links = [ { source: "communication", predicate: "has todo", target: "write blog post"}, { source: "write blog post", predicate: "has status", target: "in progress"}, { source: "write blog post", predicate: "is done", target: "false"} ]; someNeighbourhood.addLinks(links); ``` This would result in a graph structure like this: ![Screenshot 2023-11-07 at 12.26.08.png](https://hackmd.io/_uploads/S1akaiDX6.png) Under the hood, the specific storage of these links, and technical details like how to merge and sync the data is handled by the underlying Link Language that is installed on the Neighbourhood. ADAM by default provides a couple of implementations you can use, but you are also free to create your own. Joshua Parkin, the co-founder of ADAM, wrote a great and detailed blog post about how to make a Link Language here, if you are interested in going deeper. With this architecture ADAM gives users the freedom to choose where and how the data is stored, while app developers have a unified API over any kind of backend technology. In addition to this they also get a datastructure that can represent *any* datastructure like SQL tables, NoSQL document stores, key-value pairs, and complex multi-relationship models like hypergraphs. ### 2. Web Components The second thing that makes our new plugin system possible is web components. Web components are a web standard that lets you extend HTML with custom elements encapsulating interactive functionality. ```htmlmixed <my-plugin></my-plugin> ``` When you're done building a plugin, you just publish it as a web component on NPM. Flux can then find the plugin, load the web component in the UI on the fly, and pass down the relevant props (current neighbourhood, and current channel) for the plugin to use. ![Screenshot 2023-11-15 at 15.43.13](https://hackmd.io/_uploads/HJ1XvwMNp.png) The great thing about web components is that we now have a unified way of loading small interactive components using just regular javascript. Most javascript frameworks can compile to web components, so you can write a Flux plugin in your favorite javascript framework. For instance, our main Flux app is written in Vue, but most of our plugins were written in Preact and then compiled to web components at build time! ## Building Your First Plugin ### 1. Setup First off, you'll need to download ADAM and get it running. Once that's done, run this command in your terminal: ```bash npx @fluxapp/create ``` The terminal will ask you for the name of your plugin, as well as what framework you want to use. Right now, we only support Preact, but we'll be adding support for Vue, Svelte, Lit, and more. Then install the dependencies and start your development server: ``` cd [app-name] npm install npm run dev ``` As a starting point the code includes a Todo example, the Flux UI component library, a testing enviroment that simulates the Flux app, and a build setup with Vite ready to convert your Preact app to a web component and publish it on NPM. In the package.json of your plugin, you'll need to update these fields create the correct metadata for your plugin: ```json "name": "my-plugin-name", "fluxapp": { "name": "Name of your plugin", "description": "Description", "icon": "bootstrap-icons name" }, "keywords": [ "flux-plugin", "ad4m-view" ] ``` The keywords help the Flux app search the NPM registry and find your plugin. #### Using your own tooling You can build your plugin with whatever framework you prefer, but there are a few things you need to comply with. The main export of your npm module needs to be a class that extends HTMLBaseElement: ``` export default class MyFluxPlugin extends HTMLBaseElement ``` For instance, a Vue app can be converted to a custom element like this: ``` import { defineCustomElement } from 'vue' import Plugin from './Plugin.vue' // defineCustomElement returns an instance of HTMLBaseElement const MyFluxPlugin = defineCustomElement(Plugin) export default MyFluxPlugin ``` For testing purposes we have a component called `@fluxapp/flux-container`. This is essentially a web component that simulates the most important part of the Flux app like authenticating with ADAM, selecting a community and a channel, and passing the relevant props to your plugin. Point your dev server to a index.html file, and you could should be able to test your plugin inside across communities and channels: ```htmlembedded <body> <flux-container> <flux-plugin></flux-plugin> </flux-container> <script type="module"> import Plugin from "./src/main.ts"; import "@fluxapp/flux-container"; customElements.define("flux-plugin", Plugin); </script> </body> ``` ### 2. Flux UI When bootstrapping a new plugin using `@fluxapp/create` you also get Flux UI included and ready to go. This is the component library that Flux is using for all it's UI, and we reccommend using these elements to develop your app, as it keeps theming working for the end user, so your plugin can follow the users selected theme. ### 3. Main component As shown in the introduction, Flux's main UI will pass three props down to your plugin/web component: `perspective`: PerspectiveProxy // TODO add link `source`: string `agent`: AgentClient // TODO add link **PerspectiveProxy** represents the ADAM Neighbourhood (or community in Flux) that the user is currently in. **Source** is the current position in the graph, and is essentially the "id" of the channel where the plugin is being used. **Agent** is an instance of AgentClient, which gives you access to the current user/agent. ```jsx import { PerspectiveProxy } from "@perspect3vism/ad4m"; import { AgentClient } from "@perspect3vism/ad4m/lib/src/agent/AgentClient"; type Props = { agent: AgentClient; perspective: PerspectiveProxy; source: string; }; export default function Plugin({ agent, perspective, source }: Props) { return <div>Currently selected community is {perspective.name}</div>; } ``` ### 4. Subjects With ADAM you don't have to worry about creating a database, or maintain server infrastructure. As we saw in the introduction, the data in a Neighbourhood is just a collection of links structured like a graph. Working with these links might be fine for very simple use cases, but it quickly becomes dificult to work with, especially if you are used to database schemas. In the introduction we demonstrated how to add a todo item on a channel in a Neighbourhood by adding links to the graph. Even if the data in a Neighbourhood is really just a collection of links, we do have a conceptual idea of what a Todo consist of: ![Screenshot 2023-11-16 at 09.28.20](https://hackmd.io/_uploads/HJ4agw7Np.png) The green cluster here represents the collection of links that our todo item consist of. So how do we make this collection of associations explicit? With ADAM we can create something called Subjects, that basically are "virtual objects" on top of our graph data. It enables us to define the relationships between data, and make it into a "thing". Normally you would maybe call these Objects, but because Neighbourhoods can decide and define their own concepts we call them "Subjects" as they are a subjective overlay to the specific Neighbourhood graph data. You are free to create any kind of subject, but let's start with the common types that are used by Flux. Because Flux Plugins share a lot of functionality, it comes bundled with several commonly used subjects that can be imported from @fluxapp/api: - Community - Channel - Plugin - Message - Post We also made hooks for React/Preact to make it easier for developers to get realtime updates, caching, pagination and more. Here is an example of how we could get some information about the current channel our plugin is loaded in: ```jsx import { PerspectiveProxy } from "@perspect3vism/ad4m"; import { AgentClient } from "@perspect3vism/ad4m/lib/src/agent/AgentClient"; import { useSubject} from "@fluxapp/react-web"; import { Channel } from "@fluxapp/api"; type Props = { agent: AgentClient; perspective: PerspectiveProxy; source: string; }; export default function Plugin({ agent, perspective, source }: Props) { const { entry } = useSubject({ perspective, id: source, subject: Channel, }); return <div>Currently selected channel: {entry.name}</div>; } ``` As mentioned before, the `source` prop that get's passed down from the main Flux app, is a reference to a location in our graph, and in the case of Flux, the channel "id". ### 5. Custom Subjects For our little plugin we are going to demonstrate how to make a very simple Todo app, so we need a new Subject called Todo: ```ts import { SDNAClass, subjectProperty, subjectFlag } from "@perspect3vism/ad4m"; @SDNAClass({ name: "Todo", }) export default class Todo { @subjectProperty({ through: "todo://title", writable: true, resolveLanguage: "literal", }) title: string; @subjectProperty({ through: "todo://has_status", writable: true, resolveLanguage: "literal", }) status: string; @subjectProperty({ through: "todo://is_done", writable: true, resolveLanguage: "literal", }) done: boolean; } ``` Now we can import this Subject and use it in our plugin to add and display some todos. ```jsx import styles from "./App.module.css"; import { PerspectiveProxy } from "@perspect3vism/ad4m"; import { AgentClient } from "@perspect3vism/ad4m/lib/src/agent/AgentClient"; import { useSubjects} from "@fluxapp/react-web"; import Todo from "./models/Todo"; type Props = { agent: AgentClient; perspective: PerspectiveProxy; source: string; }; export default function Plugin({ agent, perspective, source }: Props) { const [inputValue, setInputValue] = useState(""); const { entries: todos, repo } = useSubjects({ perspective, source, subject: Todo, }); function addTodo() { repo.create({ title: inputValue, done: false }); setInputValue(""); } return ( <div> <ul> {todos.map((todo) => ( <li> <label> <input type="checkbox" checked={todo.done} onChange={(e: any) => repo.update(todo.id, { done: e.target.checked }) } /> {todo.title} </label> </li> ))} </ul> <input value={inputValue} onChange={(e: any) => setInputValue(e.target.value)} type="input" /> <button>Add todo</button> </div> ); } ``` ### 6. Publishing your plugin Once you've finished writing your plugin, you'll want to publish it so that Flux can locate it. If you used @fluxapp/create, all you need to do is update the package.json with a unique NPM name, and complete the flux fields. Run npm run build, followed by npm publish. Voila, your plugin will now be available for installation in any community! What's pretty mind-blowing is that you haven't just created a new plugin for Flux, but also a web component that any ADAM app can use. Moreover, your plugin can work in a Neighbourhood running on various backends like Holochain, Hypercore, Gun.db, or even AWS. As a developer, you don't have to fret about the underlying technology (unless you aim to create a new ADAM Language for a specific backend or p2p tech). Plugins can also be used inside of plugins, which make them more powerful than first expected. For example we have a comment-section plugin, that we are using inside of the chat, post and kanban board. If we wanted to add comments to each todo item in our list, we could to it as easy as this: ```jsx import styles from "./App.module.css"; import { PerspectiveProxy } from "@perspect3vism/ad4m"; import { AgentClient } from "@perspect3vism/ad4m/lib/src/agent/AgentClient"; import CommentSection from "@fluxapp/comment-section"; import Todo from "./models/Todo"; import { useSubjects} from "@fluxapp/react-web"; customElements.define("comment-section", CommentSection); type Props = { agent: AgentClient; perspective: PerspectiveProxy; source: string; }; export default function Plugin({ agent, perspective, source }: Props) { const [inputValue, setInputValue] = useState(""); const { entries: todos, repo } = useSubjects({ perspective, source, subject: Todo, }); function addTodo() { repo.create({ title: inputValue, done: false }); setInputValue(""); } return ( <div> <ul> {todos.map((todo) => ( <li> <label> <input type="checkbox" checked={todo.done} onChange={(e: any) => repo.update(todo.id, { done: e.target.checked }) } /> {todo.title} </label> <comment-section perspective={perspective} source={todo.id} agent={agent}></comment-section> </li> ))} </ul> <input value={inputValue} onChange={(e: any) => setInputValue(e.target.value)} type="input" /> <button>Add todo</button> </div> ); } ``` For a more documentation on creating a plugin, you can read more here: https://docs.fluxsocial.io/ https://docs.ad4m.dev/ ## Future vision We invite anyone who is interested in building custom plugins to join our Discord (oh yes, the irony) and to reach out so we can help you get started. This is just the first step towards a bigger vision where digital communities can be in complete control over their communication tools, adapting, evolving their own collective sense making capabilities. Future plans for Flux and ADAM is to have deeper integrations with LLM's so not only developers can start creating on ADAM, but anyone could start speaking apps and interaction patterns into existence by using plain english. Not worrying about setting up any infrastructure, but just deploy it directly to their communities