Skip to content

Instantly share code, notes, and snippets.

@silverqx
Last active March 25, 2025 09:55
Show Gist options
  • Save silverqx/99a8fe0b474d048e7fc8277a648e8ee1 to your computer and use it in GitHub Desktop.
Save silverqx/99a8fe0b474d048e7fc8277a648e8ee1 to your computer and use it in GitHub Desktop.
Nomi.ai keyboard shortcuts
// ==UserScript==
// @name Nomi.ai
// @namespace https://beta.nomi.ai/
// @version 0.2.0
// @description Nomi.ai keyboard shortcuts
// @author Silver Zachara <[email protected]>
// @match https://beta.nomi.ai/nomis/*
// @icon https://www.google.com/s2/favicons?domain=nomi.ai&sz=64
// @grant none
// ==/UserScript==
(function() {
'use strict';
let memoryBulbBtn = null
let textarea = null
ready(() => {
// Initialize moveing the memory bulb into the main textarea
initializeMoveMemoryBulb()
})
document.addEventListener('keydown', ev => {
// Add padding below the main input box
if (ev.code === 'F9')
modifyTextareaStyles()
})
/* Add padding below the main input box. */
function modifyTextareaStyles() {
const chatColumn = document.querySelector('.ChatSidebarLayout_root__jmxzE > :last-child')
if (chatColumn === null)
return
if (chatColumn.style.paddingBottom.length === 0) {
chatColumn.style.paddingBottom = '9rem'
moveMemoryBulb(true)
return
}
chatColumn.style.paddingBottom = ''
moveMemoryBulb()
}
/**
* Move the memory bulb into the main textarea.
*
* @param {boolean=} alternativePosition Alternative bottom position.
*/
function moveMemoryBulb(alternativePosition = false) {
if (memoryBulbBtn === null || textarea === null)
return
const textareaLeft = elementPosition(textarea).left
const bulbStyles = {
'position': 'absolute',
'left' : textareaLeft + 8 + 'px',
'bottom' : alternativePosition ? '172px' : '28px',
'z-index' : '1000',
}
const textareaStyles = {
'padding-left': '2rem',
}
memoryBulbBtn.style.cssText += cssObjectToString(bulbStyles)
textarea.style.cssText += cssObjectToString(textareaStyles)
}
/* Update the bulb left position based on the window width. */
function updateBulbPosition() {
// Nothing to do, media query kicks in and layout changes
if (window.innerWidth < 768)
return
if (memoryBulbBtn === null || textarea === null)
return
memoryBulbBtn.style.left = elementPosition(textarea).left + 8 + 'px'
}
/* Initialize moveing the memory bulb into the main textarea. */
async function initializeMoveMemoryBulb() {
waitForElement('textarea[aria-label="Chat Input"], nav[role="navigation"] button[title="Memory Indicator"]', 2)
.then((elements) => {
// Initialize the global element variables
initializeElements(elements)
moveMemoryBulb()
// Update the bulb left position based on the window width
// window.addEventListener('resize', () => updateBulbPosition())
window.onresize = updateBulbPosition
})
}
/**
* Initialize the global element variables.
*
* @param {Nodelist} elements
*/
function initializeElements(elements) {
// Nothing to do
if (elements.length !== 2)
return
memoryBulbBtn = elements[0]
textarea = elements[1]
}
/* Wait until the given selector is available in the DOM. */
function waitForElement(selector, length = 1) {
return new Promise(resolve => {
const elements = document.querySelectorAll(selector)
if (elements.length === length)
return resolve(elements)
const observer = new MutationObserver((/*mutations*/) => {
const elements = document.querySelectorAll(selector)
// Nothing to do
if (elements.length !== length)
return
observer.disconnect()
resolve(elements)
})
observer.observe(document.body, {
childList: true,
subtree: true
})
})
}
/** Get an element position relative to the window.
*
* @param {HTMLElement} elem
*/
function elementPosition(elem) {
const rect = elem.getBoundingClientRect()
const win = elem.ownerDocument.defaultView
return {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset,
}
}
function cssObjectToString(styles) {
return Object.entries(styles).map(([k, v]) => `${k}:${v}`).join(';')
}
function ready(fn) {
if (document.readyState !== 'loading')
fn()
else
document.addEventListener('DOMContentLoaded', fn)
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment