diff --git a/src/components/common/UserHeader.tsx b/src/components/common/UserHeader.tsx
index eae71db9a3ae59d0190261af7c3b539a00b57f8f..6a8265e34524a6b9bd35d143caabe87b9d7202a6 100644
--- a/src/components/common/UserHeader.tsx
+++ b/src/components/common/UserHeader.tsx
@@ -1,11 +1,17 @@
+import Tooltip from "./Tooltip";
 import { User } from "revolt.js";
 import Header from "../ui/Header";
 import UserIcon from "./UserIcon";
+import { Text } from "preact-i18n";
 import UserStatus from './UserStatus';
 import styled from "styled-components";
 import { Localizer } from 'preact-i18n';
+import { Link } from "react-router-dom";
+import IconButton from "../ui/IconButton";
 import { Settings } from "@styled-icons/feather";
+import { openContextMenu } from "preact-context-menu";
 import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
+import { useIntermediate } from "../../context/intermediate/Intermediate";
 
 const HeaderBase = styled.div`
     gap: 0;
@@ -39,12 +45,10 @@ interface Props {
 }
 
 export default function UserHeader({ user }: Props) {
-    function openPresenceSelector() {
-        // openContextMenu("Status");
-    }
+    const { writeClipboard } = useIntermediate();
 
-    function writeClipboard(a: string) {
-        alert('unimplemented');
+    function openPresenceSelector() {
+        openContextMenu("Status");
     }
 
     return (
@@ -57,12 +61,12 @@ export default function UserHeader({ user }: Props) {
             />
             <HeaderBase>
                 <Localizer>
-                    {/*<Tooltip content={<Text id="app.special.copy_username" />}>*/}
+                    <Tooltip content={<Text id="app.special.copy_username" />}>
                         <span className="username"
                             onClick={() => writeClipboard(user.username)}>
                             @{user.username}
                         </span>
-                    {/*</Tooltip>*/}
+                    </Tooltip>
                 </Localizer>
                 <span className="status"
                     onClick={openPresenceSelector}>
@@ -70,9 +74,11 @@ export default function UserHeader({ user }: Props) {
                 </span>
             </HeaderBase>
             { !isTouchscreenDevice && <div className="actions">
-                {/*<IconButton to="/settings">*/}
-                    <Settings size={24} />
-                {/*</IconButton>*/}
+                <Link to="/settings">
+                    <IconButton>
+                        <Settings size={24} />
+                    </IconButton>
+                </Link>
             </div> }
         </Header>
     )
diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx
index cc4d6dcb9aaef39d9cfcd7189259c3c4d69ef300..bf1b99ce9390a40441f5f6f1a8dfaf14a4fd9602 100644
--- a/src/components/navigation/left/HomeSidebar.tsx
+++ b/src/components/navigation/left/HomeSidebar.tsx
@@ -22,6 +22,7 @@ import Header from '../../ui/Header';
 import UserHeader from "../../common/UserHeader";
 import Category from '../../ui/Category';
 import PaintCounter from "../../../lib/PaintCounter";
+import { useIntermediate } from "../../../context/intermediate/Intermediate";
 
 type Props = WithDispatcher & {
     unreads: Unreads;
@@ -50,7 +51,7 @@ function HomeSidebar(props: Props) {
     const { pathname } = useLocation();
     const client = useContext(AppContext);
     const { channel } = useParams<{ channel: string }>();
-    // const { openScreen, writeClipboard } = useContext(IntermediateContext);
+    const { openScreen, writeClipboard } = useIntermediate();
 
     const ctx = useForceUpdate();
     const users = useUsers(undefined, ctx);
@@ -119,7 +120,7 @@ function HomeSidebar(props: Props) {
                                 <Text id="app.main.categories.conversations" />
                             ) as any
                         }
-                        action={() => /*openScreen({ id: "special_input", type: "create_group" })*/{}}
+                        action={() => openScreen({ id: "special_input", type: "create_group" })}
                     />
                 </Localizer>
                 {channelsArr.length === 0 && <img src="/assets/images/placeholder.svg" />}
diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c1d35ba08186eb539e432f797c42732a6bcef32e
--- /dev/null
+++ b/src/lib/ContextMenus.tsx
@@ -0,0 +1,656 @@
+import { Text } from "preact-i18n";
+import { useContext } from "preact/hooks";
+import { useHistory } from "react-router-dom";
+import { Attachment, Channels, Message, Servers, Users } from "revolt.js/dist/api/objects";
+import {
+    ContextMenu,
+    ContextMenuWithData,
+    MenuItem
+} from "preact-context-menu";
+import { ChannelPermission, ServerPermission, UserPermission } from "revolt.js/dist/api/permissions";
+import { QueuedMessage } from "../redux/reducers/queue";
+import { WithDispatcher } from "../redux/reducers";
+import { useIntermediate } from "../context/intermediate/Intermediate";
+import { AppContext, ClientStatus, StatusContext } from "../context/revoltjs/RevoltClient";
+import { takeError } from "../context/revoltjs/util";
+import { useChannel, useChannelPermission, useForceUpdate, useServer, useServerPermission, useUser, useUserPermission } from "../context/revoltjs/hooks";
+import { Children } from "../types/Preact";
+import LineDivider from "../components/ui/LineDivider";
+import { connectState } from "../redux/connector";
+
+interface ContextMenuData {
+    user?: string;
+    server?: string;
+    server_list?: string;
+    channel?: string;
+    message?: Message;
+
+    unread?: boolean;
+    queued?: QueuedMessage;
+    contextualChannel?: string;
+}
+
+type Action =
+    | { action: "copy_id"; id: string }
+    | { action: "copy_selection" }
+    | { action: "copy_text"; content: string }
+    | { action: "mark_as_read"; channel: Channels.Channel }
+    | { action: "retry_message"; message: QueuedMessage }
+    | { action: "cancel_message"; message: QueuedMessage }
+    | { action: "mention"; user: string }
+    | { action: "quote_message"; content: string }
+    | { action: "edit_message"; id: string }
+    | { action: "delete_message"; target: Channels.Message }
+    | { action: "open_file"; attachment: Attachment }
+    | { action: "save_file"; attachment: Attachment }
+    | { action: "copy_file_link"; attachment: Attachment }
+    | { action: "open_link"; link: string }
+    | { action: "copy_link"; link: string }
+    | { action: "remove_member"; channel: string; user: string }
+    | { action: "kick_member"; target: Servers.Server; user: string }
+    | { action: "ban_member"; target: Servers.Server; user: string }
+    | { action: "view_profile"; user: string }
+    | { action: "message_user"; user: string }
+    | { action: "block_user"; user: string }
+    | { action: "unblock_user"; user: string }
+    | { action: "add_friend"; user: string }
+    | { action: "remove_friend"; user: string }
+    | { action: "cancel_friend"; user: string }
+    | { action: "set_presence"; presence: Users.Presence }
+    | { action: "set_status" }
+    | { action: "clear_status" }
+    | { action: "create_channel"; server: string }
+    | { action: "create_invite"; target: Channels.GroupChannel | Channels.TextChannel }
+    | { action: "leave_group"; target: Channels.GroupChannel }
+    | { action: "delete_channel"; target: Channels.TextChannel }
+    | { action: "close_dm"; target: Channels.DirectMessageChannel }
+    | { action: "leave_server"; target: Servers.Server }
+    | { action: "delete_server"; target: Servers.Server }
+    | { action: "open_channel_settings", id: string }
+    | { action: "open_server_settings", id: string }
+    | { action: "open_server_channel_settings", server: string, id: string };
+
+function ContextMenus(props: WithDispatcher) {
+    const { openScreen, writeClipboard } = useIntermediate();
+    const client = useContext(AppContext);
+    const userId = client.user!._id;
+    const status = useContext(StatusContext);
+    const isOnline = status === ClientStatus.ONLINE;
+    const history = useHistory();
+
+    function contextClick(data?: Action) {
+        if (typeof data === "undefined") return;
+
+        (async () => {
+            switch (data.action) {
+                case "copy_id":
+                    writeClipboard(data.id);
+                    break;
+                case "copy_selection":
+                    writeClipboard(document.getSelection()?.toString() ?? '');
+                    break;
+                case "mark_as_read":
+                    if (data.channel.channel_type === 'SavedMessages') return;
+                    props.dispatcher({
+                        type: "UNREADS_MARK_READ",
+                        channel: data.channel._id,
+                        message: data.channel.channel_type === 'TextChannel' ? data.channel.last_message : data.channel.last_message._id,
+                        request: true
+                    });
+                    break;
+
+                case "retry_message":
+                    {
+                        const nonce = data.message.id;
+                        const fail = (error: any) =>
+                            props.dispatcher({
+                                type: "QUEUE_FAIL",
+                                nonce,
+                                error
+                            });
+
+                        client.channels
+                            .sendMessage(
+                                data.message.channel,
+                                {
+                                    content: data.message.data.content as string,
+                                    nonce
+                                }
+                            )
+                            .catch(fail);
+
+                        props.dispatcher({
+                            type: "QUEUE_START",
+                            nonce
+                        });
+                    }
+                    break;
+
+                case "cancel_message":
+                    {
+                        props.dispatcher({
+                            type: "QUEUE_REMOVE",
+                            nonce: data.message.id
+                        });
+                    }
+                    break;
+
+                case "mention":
+                    {
+                        // edit draft
+                        /*InternalEventEmitter.emit(
+                            "append_messagebox",
+                            `<@${data.user}>`,
+                            "mention"
+                        );*/
+                    }
+                    break;
+
+                case "copy_text":
+                    writeClipboard(data.content);
+                    break;
+                case "quote_message":
+                    {
+                        // edit draft
+                        /*InternalEventEmitter.emit(
+                            "append_messagebox",
+                            data.content,
+                            "quote"
+                        );*/
+                    }
+                    break;
+
+                case "edit_message":
+                    {
+                        // InternalEventEmitter.emit("edit_message", data.id);
+                    }
+                    break;
+
+                case "open_file":
+                    {
+                        window
+                            .open(
+                                client.generateFileURL(data.attachment),
+                                "_blank"
+                            )
+                            ?.focus();
+                    }
+                    break;
+
+                case "save_file":
+                    {
+                        window.open(
+                            // ! FIXME: do this from revolt.js
+                            client.generateFileURL(data.attachment)?.replace('attachments', 'attachments/download'),
+                            "_blank"
+                        );
+                    }
+                    break;
+
+                case "copy_file_link":
+                    {
+                        const { _id, filename } = data.attachment;
+                        writeClipboard(
+                            // ! FIXME: do from r.js
+                            client.generateFileURL(data.attachment) + '/${encodeURI(filename)}',
+                        );
+                    }
+                    break;
+
+                case "open_link":
+                    {
+                        window.open(data.link, "_blank")?.focus();
+                    }
+                    break;
+
+                case "copy_link":
+                    {
+                        writeClipboard(data.link);
+                    }
+                    break;
+
+                case "remove_member":
+                    {
+                        client.channels.removeMember(data.channel, data.user);
+                    }
+                    break;
+
+                case "view_profile":
+                    openScreen({ id: 'profile', user_id: data.user });
+                    break;
+
+                case "message_user":
+                    {
+                        const channel = await client.users.openDM(data.user);
+                        if (channel) {
+                            history.push(`/channel/${channel._id}`);
+                        }
+                    }
+                    break;
+
+                case "add_friend":
+                    {
+                        let user = client.users.get(data.user);
+                        if (user) {
+                            await client.users.addFriend(user.username);
+                        }
+                    }
+                    break;
+
+                case "block_user":
+                    await client.users.blockUser(data.user);
+                    break;
+                case "unblock_user":
+                    await client.users.unblockUser(data.user);
+                    break;
+                case "remove_friend":
+                case "cancel_friend":
+                    await client.users.removeFriend(data.user);
+                    break;
+
+                case "set_presence":
+                    {
+                        await client.users.editUser({
+                            status: {
+                                ...client.user?.status,
+                                presence: data.presence
+                            }
+                        });
+                    }
+                    break;
+
+                case "set_status": openScreen({ id: "special_input", type: "set_custom_status" }); break;
+
+                case "clear_status":
+                    {
+                        let { text, ...status } = client.user?.status ?? {};
+                        await client.users.editUser({ status });
+                    }
+                    break;
+                
+                case "leave_group":
+                case "close_dm":
+                case "leave_server":
+                case "delete_channel":
+                case "delete_server":
+                case "delete_message":
+                // @ts-expect-error
+                case "create_invite": openScreen({ id: "special_prompt", type: data.action, target: data.target }); break;
+
+                case "ban_member":
+                case "kick_member": openScreen({ id: "special_prompt", type: data.action, target: data.target, user: data.user }); break;
+
+                case "create_channel": openScreen({ id: "special_input", type: "create_channel", server: data.server }); break;
+
+                case "open_channel_settings": history.push(`/channel/${data.id}/settings`); break;
+                case "open_server_channel_settings": history.push(`/server/${data.server}/channel/${data.id}/settings`); break;
+                case "open_server_settings": history.push(`/server/${data.id}/settings`); break;
+            }
+        })().catch(err => {
+            openScreen({ id: "error", error: takeError(err) });
+        });
+    }
+
+    return (
+        <>
+            <ContextMenuWithData id="Menu" onClose={contextClick}>
+                {({
+                    user: uid,
+                    channel: cid,
+                    server: sid,
+                    message,
+                    server_list,
+                    queued,
+                    unread,
+                    contextualChannel: cxid
+                }: ContextMenuData) => {
+                    const forceUpdate = useForceUpdate();
+                    const elements: Children[] = [];
+                    var lastDivider = false;
+
+                    function generateAction(
+                        action: Action,
+                        locale?: string,
+                        disabled?: boolean,
+                        tip?: Children
+                    ) {
+                        lastDivider = false;
+                        elements.push(
+                            <MenuItem data={action} disabled={disabled}>
+                                <Text
+                                    id={`app.context_menu.${locale ??
+                                        action.action}`}
+                                />
+                                { tip && <div className="tip">
+                                    { tip }
+                                </div> }
+                            </MenuItem>
+                        );
+                    }
+
+                    function pushDivider() {
+                        if (lastDivider || elements.length === 0) return;
+                        lastDivider = true;
+                        elements.push(<LineDivider />);
+                    }
+
+                    if (server_list) {
+                        let permissions = useServerPermission(server_list, forceUpdate);
+                        if (permissions & ServerPermission.ManageChannels) generateAction({ action: 'create_channel', server: server_list });
+                        if (permissions & ServerPermission.ManageServer) generateAction({ action: 'open_server_settings', id: server_list });
+
+                        return elements;
+                    }
+
+                    if (document.getSelection()?.toString().length ?? 0 > 0) {
+                        generateAction({ action: "copy_selection" }, undefined, undefined, <Text id="shortcuts.ctrlc" />);
+                        pushDivider();
+                    }
+
+                    const channel = useChannel(cid, forceUpdate);
+                    const contextualChannel = useChannel(cxid, forceUpdate);
+                    const targetChannel = channel ?? contextualChannel;
+
+                    const user = useUser(uid, forceUpdate);
+                    const server = useServer(targetChannel?.channel_type === 'TextChannel' ? targetChannel.server : sid, forceUpdate);
+
+                    const channelPermissions = targetChannel ? useChannelPermission(targetChannel._id, forceUpdate) : 0;
+                    const serverPermissions = server ? useServerPermission(server._id, forceUpdate) : (
+                        targetChannel?.channel_type === 'TextChannel' ? useServerPermission(targetChannel.server, forceUpdate) : 0
+                    );
+                    const userPermissions = user ? useUserPermission(user._id, forceUpdate) : 0;
+
+                    if (channel && unread) {
+                        generateAction(
+                            { action: "mark_as_read", channel },
+                            undefined,
+                            true
+                        );
+                    }
+
+                    if (contextualChannel) {
+                        if (user && user._id !== userId) {
+                            generateAction({
+                                action: "mention",
+                                user: user._id
+                            });
+
+                            pushDivider();
+                        }
+                    }
+
+                    if (user) {
+                        let actions: string[];
+                        switch (user.relationship) {
+                            case Users.Relationship.User: actions = []; break;
+                            case Users.Relationship.Friend:
+                                actions = [
+                                    "remove_friend",
+                                    "block_user"
+                                ];
+                                break;
+                            case Users.Relationship.Incoming:
+                                actions = ["add_friend", "block_user"];
+                                break;
+                            case Users.Relationship.Outgoing:
+                                actions = ["cancel_friend", "block_user"];
+                                break;
+                            case Users.Relationship.Blocked:
+                                actions = ["unblock_user"];
+                                break;
+                            case Users.Relationship.BlockedOther:
+                                actions = ["block_user"];
+                                break;
+                            case Users.Relationship.None:
+                            default:
+                                actions = ["add_friend", "block_user"];
+                        }
+
+                        if (userPermissions & UserPermission.ViewProfile) {
+                            generateAction({ action: 'view_profile', user: user._id });
+                        }
+
+                        if (user._id !== userId && userPermissions & UserPermission.SendMessage) {
+                            generateAction({ action: 'message_user', user: user._id });
+                        }
+
+                        for (const action of actions) {
+                            generateAction({
+                                action: action as any,
+                                user: user._id
+                            });
+                        }
+                    }
+
+                    if (contextualChannel) {
+                        if (contextualChannel.channel_type === "Group" && uid) {
+                            if (
+                                contextualChannel.owner === userId &&
+                                userId !== uid
+                            ) {
+                                generateAction({
+                                    action: "remove_member",
+                                    channel: contextualChannel._id,
+                                    user: uid
+                                });
+                            }
+                        }
+
+                        if (server && uid && userId !== uid && uid !== server.owner) {
+                            if (serverPermissions & ServerPermission.KickMembers)
+                                generateAction({ action: "kick_member", target: server, user: uid });
+                            
+                            if (serverPermissions & ServerPermission.BanMembers)
+                                generateAction({ action: "ban_member", target: server, user: uid });
+                        }
+                    }
+
+                    if (queued) {
+                        generateAction({
+                            action: "retry_message",
+                            message: queued
+                        });
+
+                        generateAction({
+                            action: "cancel_message",
+                            message: queued
+                        });
+                    }
+
+                    if (message && !queued) {
+                        if (
+                            typeof message.content === "string" &&
+                            message.content.length > 0
+                        ) {
+                            generateAction({
+                                action: "quote_message",
+                                content: message.content
+                            });
+                            generateAction({
+                                action: "copy_text",
+                                content: message.content
+                            });
+                        }
+
+                        if (message.author === userId) {
+                            generateAction({
+                                action: "edit_message",
+                                id: message._id
+                            });
+                        }
+
+                        if (message.author === userId ||
+                            channelPermissions & ChannelPermission.ManageMessages) {
+                            generateAction({
+                                action: "delete_message",
+                                target: message
+                            });
+                        }
+
+                        if (message.attachments) {
+                            pushDivider();
+                            const { metadata } = message.attachments[0];
+                            const { type } = metadata;
+
+                            generateAction(
+                                {
+                                    action: "open_file",
+                                    attachment: message.attachments[0]
+                                },
+                                type === "Image"
+                                    ? "open_image"
+                                    : type === "Video"
+                                    ? "open_video"
+                                    : "open_file"
+                            );
+
+                            generateAction(
+                                {
+                                    action: "save_file",
+                                    attachment: message.attachments[0]
+                                },
+                                type === "Image"
+                                    ? "save_image"
+                                    : type === "Video"
+                                    ? "save_video"
+                                    : "save_file"
+                            );
+
+                            generateAction(
+                                {
+                                    action: "copy_file_link",
+                                    attachment: message.attachments[0]
+                                },
+                                "copy_link"
+                            );
+                        }
+
+                        if (document.activeElement?.tagName === "A") {
+                            let link = document.activeElement.getAttribute(
+                                "href"
+                            );
+                            if (link) {
+                                pushDivider();
+                                generateAction({ action: "open_link", link });
+                                generateAction({ action: "copy_link", link });
+                            }
+                        }
+                    }
+
+                    let id = server?._id ?? channel?._id ?? user?._id ?? message?._id;
+                    if (id) {
+                        pushDivider();
+
+                        if (channel) {
+                            switch (channel.channel_type) {
+                                case 'Group':
+                                    // ! generateAction({ action: "create_invite", target: channel }); FIXME: add support for group invites
+                                    generateAction({ action: "open_channel_settings", id: channel._id }, "open_group_settings");
+                                    generateAction({ action: "leave_group", target: channel }, "leave_group");
+                                    break;
+                                case 'DirectMessage':
+                                    generateAction({ action: "close_dm", target: channel });
+                                    break;
+                                case 'TextChannel':
+                                    // ! FIXME: add permission for invites
+                                    generateAction({ action: "create_invite", target: channel });
+
+                                    if (serverPermissions & ServerPermission.ManageServer)
+                                        generateAction({ action: "open_server_channel_settings", server: channel.server, id: channel._id }, "open_channel_settings");
+                                    
+                                    if (serverPermissions & ServerPermission.ManageChannels)
+                                        generateAction({ action: "delete_channel", target: channel });
+
+                                    break;
+                            }
+                        }
+
+                        if (sid && server) {
+                            if (serverPermissions & ServerPermission.ManageServer)
+                                generateAction({ action: "open_server_settings", id: server._id }, "open_server_settings");
+
+                            if (userId === server.owner) {
+                                generateAction({ action: "delete_server", target: server }, "delete_server");
+                            } else {
+                                generateAction({ action: "leave_server", target: server }, "leave_server");
+                            }
+                        }
+
+                        generateAction(
+                            { action: "copy_id", id },
+                            sid ? "copy_sid" :
+                            cid ? "copy_cid" :
+                            message ? "copy_mid" : "copy_uid"
+                        );
+                    }
+
+                    return elements;
+                }}
+            </ContextMenuWithData>
+            <ContextMenu id="Status" onClose={contextClick}>
+                <span data-disabled={true}>@{client.user?.username}</span>
+                <LineDivider />
+                <MenuItem
+                    data={{
+                        action: "set_presence",
+                        presence: Users.Presence.Online
+                    }}
+                    disabled={!isOnline}
+                >
+                    <div className="indicator online" />
+                    <Text id={`app.status.online`} />
+                </MenuItem>
+                <MenuItem
+                    data={{
+                        action: "set_presence",
+                        presence: Users.Presence.Idle
+                    }}
+                    disabled={!isOnline}
+                >
+                    <div className="indicator idle" />
+                    <Text id={`app.status.idle`} />
+                </MenuItem>
+                <MenuItem
+                    data={{
+                        action: "set_presence",
+                        presence: Users.Presence.Busy
+                    }}
+                    disabled={!isOnline}
+                >
+                    <div className="indicator busy" />
+                    <Text id={`app.status.busy`} />
+                </MenuItem>
+                <MenuItem
+                    data={{
+                        action: "set_presence",
+                        presence: Users.Presence.Invisible
+                    }}
+                    disabled={!isOnline}
+                >
+                    <div className="indicator invisible" />
+                    <Text id={`app.status.invisible`} />
+                </MenuItem>
+                <LineDivider />
+                <MenuItem data={{ action: "set_status" }} disabled={!isOnline}>
+                    <Text id={`app.context_menu.custom_status`} />
+                </MenuItem>
+                {client.user?.status?.text && (
+                    <MenuItem
+                        data={{ action: "clear_status" }}
+                        disabled={!isOnline}
+                    >
+                        <Text id={`app.context_menu.clear_status`} />
+                    </MenuItem>
+                )}
+            </ContextMenu>
+        </>
+    );
+}
+
+export default connectState(
+    ContextMenus,
+    () => {
+        return {};
+    },
+    true
+);
diff --git a/src/lib/PaintCounter.tsx b/src/lib/PaintCounter.tsx
index 668ce68227b44fd95320aaa643ccf3501fb3d243..487a015c08de2964d20e405c5cc014d75ea27180 100644
--- a/src/lib/PaintCounter.tsx
+++ b/src/lib/PaintCounter.tsx
@@ -2,8 +2,8 @@ import { useState } from "preact/hooks";
 
 const counts: { [key: string]: number } = {};
 
-export default function PaintCounter({ small }: { small?: boolean }) {
-    if (import.meta.env.PROD) return null;
+export default function PaintCounter({ small, always }: { small?: boolean, always?: boolean }) {
+    if (import.meta.env.PROD && !always) return null;
 
     const [uniqueId] = useState('' + Math.random());
     const count = counts[uniqueId] ?? 0;
diff --git a/src/pages/App.tsx b/src/pages/App.tsx
index 4648649f8738c73e52010ac8101c1258964b7eed..f0ff58d337376054e7bc8ba56ce14102dc4b54ce 100644
--- a/src/pages/App.tsx
+++ b/src/pages/App.tsx
@@ -1,14 +1,17 @@
 import { Docked, OverlappingPanels } from "react-overlapping-panels";
 import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
-import Popovers from "../context/intermediate/Popovers";
 import { Switch, Route } from "react-router-dom";
 import styled from "styled-components";
 
+import Popovers from "../context/intermediate/Popovers";
+import ContextMenus from "../lib/ContextMenus";
+
 import LeftSidebar from "../components/navigation/LeftSidebar";
 import RightSidebar from "../components/navigation/RightSidebar";
 
 import Home from './home/Home';
 import Friends from "./friends/Friends";
+import Developer from "./developer/Developer";
 
 const Routes = styled.div`
     min-width: 0;
@@ -28,6 +31,10 @@ export default function App() {
             docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
             <Routes>
                 <Switch>
+                    <Route path="/dev">
+                        <Developer />
+                    </Route>
+
                     <Route path="/friends">
                         <Friends />
                     </Route>
@@ -37,6 +44,7 @@ export default function App() {
                     </Route>
                 </Switch>
             </Routes>
+            <ContextMenus />
             <Popovers />
         </OverlappingPanels>
     );
diff --git a/src/pages/developer/Developer.tsx b/src/pages/developer/Developer.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e7a5cbc013aeaf395fd7e8872b5639c37f4c3bcd
--- /dev/null
+++ b/src/pages/developer/Developer.tsx
@@ -0,0 +1,39 @@
+import { useContext } from "preact/hooks";
+import Header from "../../components/ui/Header";
+import PaintCounter from "../../lib/PaintCounter";
+import { AppContext } from "../../context/revoltjs/RevoltClient";
+import { useUserPermission } from "../../context/revoltjs/hooks";
+
+export default function Developer() {
+    // const voice = useContext(VoiceContext);
+    const client = useContext(AppContext);
+    const userPermission = useUserPermission(client.user!._id);
+
+    return (
+        <div>
+            <Header placement="primary">Developer Tab</Header>
+            <div style={{ padding: "16px" }}>
+                <PaintCounter always />
+            </div>
+            <div style={{ padding: "16px" }}>
+                <b>User ID:</b> {client.user!._id} <br/>
+                <b>Permission against self:</b> {userPermission} <br/>
+            </div>
+            <div style={{ padding: "16px" }}>
+                {/*<span>
+                    <b>Voice Status:</b> {VoiceStatus[voice.status]}
+                </span>
+                <br />
+                <span>
+                    <b>Voice Room ID:</b> {voice.roomId || "undefined"}
+                </span>
+                <br />
+                <span>
+                    <b>Voice Participants:</b> [
+                    {Array.from(voice.participants.keys()).join(", ")}]
+                </span>
+                <br />*/}
+            </div>
+        </div>
+    );
+}
diff --git a/src/pages/home/Home.module.scss b/src/pages/home/Home.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1210ee80facf10af1e1f0ee8dfd2551d316fd5d0
--- /dev/null
+++ b/src/pages/home/Home.module.scss
@@ -0,0 +1,28 @@
+.home {
+    user-select: none;
+
+    h3 {
+        margin: 1em 0;
+        font-size: 48px;
+        text-align: center;
+
+        img {
+            height: 36px;
+        }
+    }
+
+    ul {
+        margin: auto;
+        display: block;
+        font-size: 18px;
+        text-align: center;
+
+        li {
+            list-style: lower-greek;
+        }
+    }
+}
+
+[data-light="true"] .home svg {
+    filter: invert(100%);
+}
diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx
index 9ccb04f0ab6af80ea530cb1854e324b40407648f..78e0348c306ee485c7241ac16870b81c8568e322 100644
--- a/src/pages/home/Home.tsx
+++ b/src/pages/home/Home.tsx
@@ -1,9 +1,35 @@
-import PaintCounter from "../../lib/PaintCounter";
+import styles from "./Home.module.scss";
+import { Link } from "react-router-dom";
+
+import { Text } from "preact-i18n";
+import Header from "../../components/ui/Header";
+// import WideLogo from "../../../../../assets/wide.svg";
 
 export default function Home() {
     return (
-        <div>
-            <PaintCounter />
+        <div className={styles.home}>
+            <Header placement="primary"><Text id="app.navigation.tabs.home" /></Header>
+            <h3>
+                <Text id="app.special.modals.onboarding.welcome" /> {/*<WideLogo />*/}
+            </h3>
+            <ul>
+                <li>
+                    Go to your <Link to="/friends">friends list</Link>.
+                </li>
+                <li>
+                    Give <Link to="/settings/feedback">feedback</Link>.
+                </li>
+                <li>
+                    Join <Link to="/invite/Testers">testers server</Link>.
+                </li>
+                <li>
+                    View{" "}
+                    <a href="https://gitlab.insrt.uk/revolt" target="_blank">
+                        source code
+                    </a>
+                    .
+                </li>
+            </ul>
         </div>
     );
 }
diff --git a/src/styles/_context-menu.scss b/src/styles/_context-menu.scss
new file mode 100644
index 0000000000000000000000000000000000000000..159efac95cd6cdd7ae3bf4da89b390a46395cf07
--- /dev/null
+++ b/src/styles/_context-menu.scss
@@ -0,0 +1,58 @@
+.preact-context-menu .context-menu {
+    z-index: 100;
+    min-width: 180px;
+    padding: 6px 8px;
+    user-select: none;
+    border-radius: 4px;
+    color: var(--secondary-foreground);
+    background: var(--primary-background) !important;
+    box-shadow: 0px 0px 8px 8px rgba(0, 0, 0, 0.05);
+
+    > span {
+        gap: 6px;
+        margin: 2px 0;
+        display: flex;
+        padding: 6px 8px;
+        border-radius: 3px;
+        font-size: .875rem;
+        align-items: center;
+        white-space: nowrap;
+        
+        &:not([data-disabled="true"]) {
+            cursor: pointer;
+            
+            &:hover {
+                background: var(--secondary-background);
+            }
+        }
+
+        .tip {
+            flex-grow: 1;
+            font-size: .650rem;
+            text-align: right;
+            color: var(--tertiary-foreground);
+        }
+    }
+
+    .indicator {
+        width: 8px;
+        height: 8px;
+        border-radius: 50%;
+
+        &.online {
+            background: var(--status-online);
+        }
+
+        &.idle {
+            background: var(--status-away);
+        }
+
+        &.busy {
+            background: var(--status-busy);
+        }
+
+        &.invisible {
+            background: var(--status-invisible);
+        }
+    }
+}
diff --git a/src/styles/index.scss b/src/styles/index.scss
index 7869c3d3aeb57b63e648a860c975148a722a80b7..4508966e94a32400c0636fc00bd2020d1f67dc6a 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -1,3 +1,4 @@
+@import "context-menu";
 @import "elements";
 @import "fonts";
 @import "page";