Skip to content

Instantly share code, notes, and snippets.

@0xdevalias
Last active November 27, 2023 08:38
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
Custom themes/CSS styling hacks/overrides for Beeper (universal chat app aggregator, built on top of matrix)

Beeper Custom Theme Styles

Table of Contents

devalias' Beeper CSS Hacks

See devalias-beeper-css-hacks.css for the customisations I am using myself.

You'll also find a number of hacks/techniques on my theme issue:

See Also

My Other Related Deepdive Gist's and Projects

Bonus Section

Accessing the Matrix client from within Beeper / Electron's DevTools

mxMatrixClientPeg.matrixClient

eg. to send a message:

const roomId = "!KlacjKWnARbprTLuRM:nova.chat";

mxMatrixClientPeg.matrixClient.sendMessage(roomId, {
  msgtype: "m.text",
  body: "This is a test message sent using mxMatrixClientPeg.matrixClient.sendMessage"
})

Enabling 'internal' mode to see extra labs features/etc

(The following tip comes via @cameronaaron from the Beeper Community)

Open DevTools console, and enter the following:

bpWhoamiMonitor.whoami.userInfo.channel = "INTERNAL"

Then go to your Beeper Settings -> Labs -> and look at all the new features you can flip (note that you can also probably flip all of these directly via /devtools -> Settings Explorer as well)

Accessing the React internals for the Beeper 'Rooms' component using Beeper / Electron's DevTools

const el = $('#matrixchat > .mx_MatrixChat_wrapper > .mx_MatrixChat > .bp_LeftPanel > .bp_LeftPanel_contentWrapper > .bp_LeftPanel_content > .rooms')
 
const elProps = Object.getOwnPropertyNames(el);
 
const elReactFiberKey = elProps.filter(k => k.includes('__reactFiber'))
const elReactPropsKey = elProps.filter(k => k.includes('__reactProps'))

const elReactInternals = {
  reactFiber: el[elReactFiberKey],
  reactProps: el[elReactPropsKey],
}
 
//console.log(elReactInternals)

const UnreadList = elReactInternals.reactProps.children[3].props.children[0]
const ReadList = elReactInternals.reactProps.children[3].props.children[1]

console.log('Inbox Chats', UnreadList.props.unreads)
// (303) [Room, Room, ...]

console.log('Archived Chats', ReadList.props.rooms)
// (1633) [Room, Room, ...]

Some notes on how to achieve custom Beeper / Electron JS hacks/customisations (eg. more customizability than CSS hacks alone)

Warning: This section is way more advanced than CSS hacks, and comes with much higher risks if you run arbitrary untrusted code, as using malicious JS code might steal your beeper auth tokens, any of your private messages, etc, etc. You've been warned.

These notes originally come from my Debugging Electron Apps (and related memory issues) (0xdevalias gist) gist (Ref):

On macOS you can get to the Beeper *.asar files by:

cd /Applications/Beeper.app/Contents/Resources

⇒ ls
af.lproj/           cs.lproj/      fa.lproj/   icon.icns  ml.lproj/     ru.lproj/  todesktop-runtime-config.json
am.lproj/           da.lproj/      fi.lproj/   icons/     mr.lproj/     sk.lproj/  tr.lproj/
app-update.yml      de.lproj/      fil.lproj/  id.lproj/  ms.lproj/     sl.lproj/  uk.lproj/
app.asar            el.lproj/      fr.lproj/   it.lproj/  nb.lproj/     sr.lproj/  ur.lproj/
app.asar.unpacked/  en.lproj/      gu.lproj/   ja.lproj/  nl.lproj/     sv.lproj/  vi.lproj/
ar.lproj/           en_GB.lproj/   he.lproj/   kn.lproj/  pl.lproj/     sw.lproj/  webapp.asar
bg.lproj/           es.lproj/      hi.lproj/   ko.lproj/  pt_BR.lproj/  ta.lproj/  zh_CN.lproj/
bn.lproj/           es_419.lproj/  hr.lproj/   lt.lproj/  pt_PT.lproj/  te.lproj/  zh_TW.lproj/
ca.lproj/           et.lproj/      hu.lproj/   lv.lproj/  ro.lproj/     th.lproj/

Where the most relevant files/folders there are:

  • app.asar
  • app.asar.unpacked/
  • webapp.asar

From memory, I believe app.asar is more related to the core electron/element type features, and webapp.asar was more related to the more custom Beeper features; but I didn't look super deeply into that side of things.

We can then use the node asar package via npx to inspect the contents of the *.asar files:

⇒ npx asar list --is-pack app.asar | grep -v node_modules

⇒ npx asar list --is-pack webapp.asar | grep -v node_modules

We can then run Beeper passing the node remote debugging --inspect-brk command to set a breakpoint at the entrypoint of the code:

⇒ open /Applications/Beeper.app --args --inspect-brk=1337

Which we can then connect to by opening a Chrome browser, navigating to chrome://inspect/#devices, and under 'Remote Target' looking for something like the following:

image

electron/js2c/browser_init file:///

Then clicking on 'inspect', which will open the Chrome DevTools 'Sources' tab and show the entrypoint line where the debugger has stopped execution, in this case, in the file:///Applications/Beeper.app/Contents/Resources/app.asar/lib/electron-main.js file:

image

We can then skim around the code in this file to understand what it does, and what other options are available.

For example, here are some command line arguments documentation; of which --devtools sounds interesting:

if (argv["help"]) {
    console.log("Options:");
    console.log("  --profile-dir {path}: Path to where to store the profile.");
    console.log("  --profile {name}:     Name of alternate profile to use, allows for running multiple accounts.");
    console.log("  --devtools:           Install and use react-devtools and react-perf.");
    console.log("  --no-update:          Disable automatic updating.");
    console.log("  --default-frame:      Use OS-default window decorations.");
    console.log("  --hidden:             Start the application hidden in the system tray.");
    console.log("  --help:               Displays this help message.");
    console.log("And more such as --proxy, see:" +
        "https://electronjs.org/docs/api/command-line-switches");
    electron_1.app.exit();
}

We can also see some path loading aspects of where the app looks for webapp.asar:

// Find the webapp resources and set up things that require them
async function setupGlobals() {
    // find the webapp asar.
    asarPath = await tryPaths("webapp", __dirname, [
        // If run from the source checkout, this will be in the directory above
        '../webapp.asar',
        // but if run from a packaged application, electron-main.js will be in
        // a different asar file so it will be two levels above
        '../../webapp.asar',
        // also try without the 'asar' suffix to allow symlinking in a directory
        '../webapp',
        // from a packaged application
        '../../webapp',
        // Workaround for developing beeper on windows, where symlinks are poorly supported.
        "../../nova-web/webapp",
    ]);
    console.log("Web App Path is", asarPath);
    iconsPath = await tryPaths("icons", __dirname, [
        '../res/icons',
        '../../icons'
    ]);
    console.log("iconsPath path is", iconsPath);
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    vectorConfig = require(asarPath + 'config.json');
    console.log("Loading vector config for brand", vectorConfig.brand);
    try {
        // Load local config and use it to override values from the one baked with the build
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        const localConfig = require(path_1.default.join(electron_1.app.getPath('userData'), 'config.json'));

There are also some hidden/undocumented CLI arguments, localdev / localapi:

const localdev = Array.isArray(argv._) && argv._.includes("localdev");
const localapi = Array.isArray(argv._) && argv._.includes("localapi");

We find the code that processes the --devtools arg here:

if (argv['devtools']) {
        try {
            const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF, } = require("electron-devtools-installer");
            await installExt([REACT_DEVELOPER_TOOLS, REACT_PERF], { loadExtensionOptions: { allowFileAccess: true } });
        }
        catch (e) {
            console.log(e);
        }
    }

A little further down we see how localdev / localapi are handled:

if (localdev) {
        // Open dev tools at startup if in dev mode
        mainWindow.webContents.openDevTools();
        electron_1.app.on("certificate-error", (event, webContents, url, error, certificate, callback) => {
            // On certificate error we disable default behaviour (stop loading the page)
            // and we then say "it is all fine - true" to the callback
            event.preventDefault();
            callback(true);
        });
    }
    if (localapi) {
        vectorConfig.novaApiUrl = `https://localhost:4001`;
    }
    mainWindow.loadURL(localdev ? "http://localhost:8080" : 'nova://nova-web/webapp/');

Then beyond that, you're sort of getting deeper into the internals of Electron apps and how Beeper / Element is built on top of Electron; so really depends what you're wanting to achieve at that point.


While at the initial 'entrypoint' debugger breakpoint (from --inspect-brk), we could also choose to manually load/inject some custom code of our own. For example:

With a code file like:

// /Users/devalias/Desktop/beeperInjectionHax.js
console.log("Hello World, is this custom JS in Beeper?");

We could run the following in the Chrome Devtools console while at the initial app loading debug breakpoint:

require('/Users/devalias/Desktop/beeperInjectionHax.js')

Which would show an output message such as:

beeperInjectionHax.js:1 Hello World, is this custom JS in Beeper?

image

Various Beeper Inbox Selectors (Favourite, Pinned, Not Pinned, Unread, Etc)

// Inbox - Favourites List
$$('.rooms > .favourites [data-type="bp_RoomTile"]')

// Inbox - Favourites List - Unread
$$('.rooms > .favourites .isUnread[data-type="bp_RoomTile"]')

// Inbox - Favourites List - Muted
$$('.rooms > .favourites .isMuted[data-type="bp_RoomTile"]')

// Inbox Chats
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]')

// Inbox Chats - Favourite
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:has([data-src="img/beeper/heart-filled16.b7ad82d.svg"])')

// Inbox Chats - Pinned
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:has([data-src="img/beeper/pin-filled16.b7cb2af.svg"])')

// Inbox Chats - Not Pinned
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:not(:has([data-src="img/beeper/pin-filled16.b7cb2af.svg"]))')

// Inbox - Unread - Favourite-Avatar
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .favourite-avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon')

// Inbox - Unread - Avatar
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon')

// Inbox - Unread - Combined
$$('.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .favourite-avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon, .rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon')

Counting chats that match the above

This is a bit of a hacky WIP / PoC, but it seems to do the trick:

/*************************/
/* Counting Chat Types */
/************************/

/* Initialize counters */
.rooms {
  counter-reset: unread favourite pinned not-pinned;
}

/* Increment favorites */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] [data-src="img/beeper/heart-filled16.b7ad82d.svg"] {
  counter-increment: favourite;
}

/* Increment pinned */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] [data-src="img/beeper/pin-filled16.b7cb2af.svg"] {
  counter-increment: pinned;
}

/* Increment not pinned */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:not(:has([data-src="img/beeper/pin-filled16.b7cb2af.svg"])) {
  counter-increment: not-pinned;
}

/* Increment unread */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .favourite-avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon,
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon {
  counter-increment: unread;
}

/* Show Counters */
/*.rooms::after {
  content: "Counters: Unread (" counter(unread) "), Favourite (" counter(favourite) "), Pinned (" counter(pinned) "), Not-Pinned (" counter(not-pinned) ")"
}*/
.rooms::after {
  content: "Unread (" counter(unread) "), Not-Pinned (" counter(not-pinned) ")";

  font-size: 10px;

  position: absolute;
  top: calc(31vh - 2px);
  left: calc(8vw - 4px);

  background-color: dimgrey;
}

/* Set position to relative for the hovered element */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] {
  position: relative;
}

/* Show the not-pinned counter on hover */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:hover::before {
  content: "Not-Pinned above: " counter(not-pinned);

  font-size: 10px;
  white-space: nowrap;

  position: absolute;
  bottom: 5px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;

  border-radius: 3px;
  padding: 2px;

  background-color: dimgrey;
}

Beeper - Password Reset (JWT)

See the following repo for my notes (formatted as a ChatGPT prompt) on implementing a JWT-based password reset flow using Beeper's new 'email login' flow; as well as Proof of Concept (PoC) code implementing a CLI tool for using this flow.

/* Will this work with https://developer.mozilla.org/en-US/docs/Web/CSS/@import ? I don't think so.. but maybe I was using it wrong..? */
/***************************************************************/
/* De-emphasise distracting chats - *Beeper* Community/Hackers */
/***************************************************************/
.bp_LeftPanel:not(:has(svg[data-src="img/beeper/back16.024b7d1.svg"])) div[data-type="bp_RoomTile"]:has(.nv_BridgedIcon_beeper, svg[data-src="img/beeper/color-beeper16.1c8391b.svg"]):has(div[title*="Beeper"i]):has(div[title*="Community"i], div[title*="Hackers"i]) {
opacity: 0.5;
}
/*******************************************************/
/* De-emphasise distracting chats - UC Outdoors Club */
/*******************************************************/
.bp_LeftPanel:not(:has(svg[data-src="img/beeper/back16.024b7d1.svg"])) div[data-type="bp_RoomTile"]:has(.nv_BridgedIcon_facebook, svg[data-src="img/beeper/color-facebook16.a8cac84.svg"]):has(div[title*="UC Outdoors Club"]) {
opacity: 0.5;
}
/**********************************************/
/* Hide Left Panel/Sidebar While Chat Is Open */
/**********************************************/
/*.mx_MatrixChat:has(> .bp_MainPanel > .mx_RoomView) > .bp_LeftPanel {
display: none;
}
.mx_MatrixChat:has(> .bp_MainPanel > .mx_RoomView) > div:has(.spaceBar) {
display: none;
}*/
/*************************/
/* Counting Chat Types */
/************************/
/* Initialize counters */
.rooms {
counter-reset: unread favourite pinned not-pinned;
}
/* Increment favorites */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] [data-src="img/beeper/heart-filled16.b7ad82d.svg"] {
counter-increment: favourite;
}
/* Increment pinned */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] [data-src="img/beeper/pin-filled16.b7cb2af.svg"] {
counter-increment: pinned;
}
/* Increment not pinned */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:not(:has([data-src="img/beeper/pin-filled16.b7cb2af.svg"])) {
counter-increment: not-pinned;
}
/* Increment unread */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .favourite-avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon,
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] > div > .avatar+div > div > div > span:not(.bp_icon)+span > .bp_icon {
counter-increment: unread;
}
/* Show Counters */
/*.rooms::after {
content: "Counters: Unread (" counter(unread) "), Favourite (" counter(favourite) "), Pinned (" counter(pinned) "), Not-Pinned (" counter(not-pinned) ")"
}*/
.rooms::after {
content: "Unread (" counter(unread) "), Not-Pinned (" counter(not-pinned) ")";
font-size: 10px;
position: absolute;
top: calc(31vh - 2px);
left: calc(8vw - 4px);
background-color: dimgrey;
}
/* Set position to relative for the hovered element */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"] {
position: relative;
}
/* Show the not-pinned counter on hover */
.rooms > .rooms_scroll-container [data-type="bp_RoomTile"]:hover::before {
content: "Not-Pinned above: " counter(not-pinned);
font-size: 10px;
white-space: nowrap;
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
border-radius: 3px;
padding: 2px;
background-color: dimgrey;
}
/******************************************************************/
/* Ensure 'report bug' dialog doesn't take over the entire screen */
/******************************************************************/
#mx_Dialog_Container div[aria-describedby="report_bug"] {
/* Don't take up the entire screen */
width: fit-content;
height: fit-content;
padding: 20px;
border-radius: 20px;
/* Center the dialog */
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/**********************/
/* Main Settings Menu */
/**********************/
#mx_ContextualMenu_Container .mx_AccessibleButton[aria-label="Chat Networks"] {
display: none;
}
#mx_ContextualMenu_Container .mx_AccessibleButton[aria-label="Mark All As Read"] {
display: none;
}
#mx_ContextualMenu_Container .mx_AccessibleButton[aria-label="Check for Update"] {
display: none;
}
#mx_ContextualMenu_Container .mx_AccessibleButton[aria-label="Download Mobile App"] {
display: none;
}
/*******************/
/* Settings Dialog */
/*******************/
/* Settings Dialog - Tab - Chat Networks */
.mx_SettingsDialog_content > .mx_TabbedView > .mx_TabbedView_tabLabels > .mx_TabbedView_tabLabel:has(> span.mx_UserSettingsDialog_chatSettingsIcon) > .mx_TabbedView_tabLabel_text::after {
content: "🔗";
margin-left: 16px;
}
/* Settings Dialog - Tab - Manage Subscription */
.mx_SettingsDialog_content > .mx_TabbedView > .mx_TabbedView_tabLabels > .mx_TabbedView_tabLabel:has(> span.mx_UserSettingsDialog_manageSubscriptionIcon) > .mx_TabbedView_tabLabel_text::after {
content: "🔗";
margin-left: 16px;
}
/* Settings Dialog - Appearance - Custom CSS TextArea */
.mx_AppearanceUserSettingsTab .mx_Field.mx_Field_textarea {
width: 100% !important;
height: 500px !important;
}
.mx_AppearanceUserSettingsTab .mx_Field.mx_Field_textarea textarea {
font-family: Menlo, monospace;
font-size: 13px !important;
}
/**********************/
/* Inbox - Favourites */
/**********************/
/* Inbox - Favourites */
.bp_LeftPanel .rooms > .favourites {
/* Custom Variables */
--devalias-fav-section-max-height: 30vh;
--devalias-fav-grid-row-gap: 6px;
--devalias-fav-grid-col-gap: 6px;
--devalias-fav-width: 100%;
--devalias-fav-height: fit-content;
--devalias-fav-max-width: 64px;
--devalias-fav-avatar-width: 30px;
--devalias-fav-avatar-height: 30px;
--devalias-fav-avatar-font-size: 17px;
}
.bp_LeftPanel .rooms > .favourites > .favourites__icons > .favourites__tiles {
max-height: var(--devalias-fav-section-max-height, none) !important;
overflow: auto;
padding-left: unset;
padding: 0;
/* Favourites Grid Spacing */
grid-row-gap: unset;
grid-gap: unset;
gap: unset;
row-gap: var(--devalias-fav-grid-row-gap, 12px);
}
.bp_LeftPanel .rooms > .favourites > .favourites__icons > .favourites__tiles > .favourites__row {
height: fit-content;
/* Favourites Grid Spacing */
grid-gap: unset;
gap: unset;
column-gap: var(--devalias-fav-grid-col-gap, 12px);
}
.bp_LeftPanel .rooms > .favourites > .favourites__icons > .favourites__tiles > .favourites__row > div {
height: var(--devalias-fav-height, 60px) !important;
}
.bp_LeftPanel .rooms .favourites .bp_RoomTile.small {
width: var(--devalias-fav-width, 100%) !important;
height: var(--devalias-fav-height, 48px) !important;
max-width: var(--devalias-fav-max-width, 100%) !important;
}
.bp_LeftPanel .rooms .favourites .bp_RoomTile.small .mx_BaseAvatar_image {
width: var(--devalias-fav-avatar-width, 44px) !important;
height: var(--devalias-fav-avatar-height, 44px) !important;
}
.bp_LeftPanel .rooms .favourites .bp_RoomTile.small .mx_BaseAvatar_initial {
font-size: var(--devalias-fav-avatar-font-size, 19.8px) !important;
width: var(--devalias-fav-avatar-width, 44px) !important;
line-height: var(--devalias-fav-avatar-width, 44px) !important;
}
.bp_LeftPanel .rooms .favourites .bp_RoomTile.small .outline {
max-width: var(--devalias-fav-max-width, 110px) !important;
}
.bp_LeftPanel .rooms .favourites .bp_RoomTile.small .outline > span {
max-width: var(--devalias-fav-max-width, 110px) !important;
}
/************************/
/* Beeper Space Sidebar */
/************************/
/* Beeper Space Sidebar - Collapsed - Hide Floating 'Open Archive' Button */
.mx_MatrixChat > div > div > .mx_AccessibleButton:has(> .bp_icon > div > svg[data-src="img/beeper/archive16.2003809.svg"]) {
display: none;
}
/* Beeper Space Sidebar - Show Floating 'Archive All Read Messages' Button */
.mx_MatrixChat > div > div:has(.mx_AccessibleButton > .bp_icon > div > svg[data-src="img/beeper/new-sweep16.978771b.svg"]) {
width: max-content !important;
/*position: absolute;
top: calc(38vh);
left: 15vw;*/
}
.mx_MatrixChat > div > div > div:has(.mx_AccessibleButton > .bp_icon > div > svg[data-src="img/beeper/new-sweep16.978771b.svg"]) {
display: block !important;
}
.mx_MatrixChat > div > div > div > .mx_AccessibleButton:has(> .bp_icon > div > svg[data-src="img/beeper/new-sweep16.978771b.svg"]) {
opacity: unset;
}
/* Beeper Space Sidebar - Hide Original Icons */
#beeperSpaceBar svg:not(svg[data-src="img/beeper/square-inbox16.ea471fd.svg"]):not(svg[data-src="img/beeper/square-lowpriority16.6779879.svg"]):not(svg[data-src="img/beeper/square-archive16.b1ef8a0.svg"]):not(svg[data-src="img/beeper/square-bookmarks16.6853926.svg"]):not(svg[data-src="img/beeper/add-network16.1eb5cb1.svg"]) {
display: none
}
/* Beeper Space Sidebar - Use Lineart Icons */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg) > div > div > div {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
/* Beeper Space Sidebar - Use Lineart Icons - Facebook */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-facebook16.9d9e23d.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/messenger.1544eb2.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - Instagram */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-instagram16.1e8ed4e.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/instagram.e9184e9.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - iMessage */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-imessage16.11b6604.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/imessage.aedae37.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - Twitter */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-twitter16.ddd9bad.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/twitter.972096c.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - Telegram */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-telegram16.d011ded.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/telegram.77fa320.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - Signal */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-signal16.85ba0c4.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/signal.315d199.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - LinkedIn */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-linkedin16.f764edc.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/linkedin.2297fef.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - Discord */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-discord16.e91cca3.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/discord.6daf490.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - Beeper */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-beeper16.749ed9b.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/beeper.c685f80.svg");
}
/* Beeper Space Sidebar - Use Lineart Icons - WhatsApp */
#beeperSpaceBar > .mx_AccessibleButton:has(.bp_icon svg[data-src="img/beeper/square-whatsapp16.411f722.svg"]) > div > div > div {
background-image: url("nova://nova-web/webapp/img/social/lineart-color/dark/whatsapp.2210499.svg");
}
/*****************/
/* Uncategorized */
/*****************/
/* Ensure full image preview is visible, rather than a zoomed part of it */
.mx_EventTile_image .mx_MImageBody img.mx_MImageBody_thumbnail {
object-fit: contain !important;
}
@0xdevalias
Copy link
Author

I understood half and managed to implement another half of that, and attained that translucid (i.e. sort of transparent, but blurred) look — which I find still benefits the app a lot.

Thanks again @0xdevalias!

@jorgeparamos Awesome, glad you could get some of it figured :) If you're open to sharing the CSS hacks you ended up with here they might be useful for someone else in future too 🖤

@evkaw
Copy link

evkaw commented Oct 10, 2023

Someone please help me get rid of the "invite to beeper" button in chats, I don't know how to use CSS nor how to get into the Inspect tool.. Thanks

@0xdevalias
Copy link
Author

Someone please help me get rid of the "invite to beeper" button in chats, I don't know how to use CSS nor how to get into the Inspect tool.. Thanks

@evkaw On Beeper Desktop, go to the menu bar -> View -> Toggle Developer Tools; then from the devtools you can select the 'Elements' tab, and then in the top left you should find the 'inspect element' tool (Shortcut seems to be apple+shift+c, or whatever the windows equivalent of that is)

Something like this would probably do it:

.bp_MainPanel > .mx_RoomView > .mx_RoomHeader .mx_RoomHeader_rightActions .mx_RoomHeader_rightActions_static > :nth-child(1 of .mx_HeaderButtons) {
  display: none;
}

@FaviFake
Copy link

FaviFake commented Nov 6, 2023

Hey,

I don't know why, but Beeper seems to be the only messaging app that uses a much smaller font for the chat list compared to all other UI elements. Also, the message previews occupy two rows instead of just one. I'd love if you could make the text used in the chat list as big as the one used in the message bubbles, and, if possible, also remove the second row in the preview. I have no idea how CSS works, so idk if what I'm asking is very hard or incredibly simple. I've made a badly-photoshopped concept to show how it could look:

Before

image

After

1699185663659

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment