Skip to content

Instantly share code, notes, and snippets.

@jerieljan
Created March 8, 2026 02:22
Show Gist options
  • Select an option

  • Save jerieljan/6058327a254ac538c21cd8de9c73e523 to your computer and use it in GitHub Desktop.

Select an option

Save jerieljan/6058327a254ac538c21cd8de9c73e523 to your computer and use it in GitHub Desktop.
Adds service icons for Assistant, News and Translate on Kagi.

README

This script adds quick navigation buttons for Kagi Assistant, News and Translate while on Kagi and Kagi Search pages and Kagi Assistant pages.

This script is not endorsed by Kagi in any way and is not official. This script contains mostly AI-generated output with minor human edits

Requirements

  • Greasemonkey / Tampermonkey extensions are required to use this.

Usage

Tampermonkey

  • Tampermonkey -> Dashboard -> New
  • Paste the UserScript and save.
// ==UserScript==
// @name Kagi Service Switcher
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Add quick navigation buttons for Kagi Assistant, Translate, and News
// @author jerieljan
// @match https://www.kagi.com/*
// @match https://kagi.com/*
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// Service definitions with URLs and icons
const services =[
{
name: 'Assistant',
url: 'https://assistant.kagi.com',
icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10.5"></circle><circle cx="12" cy="12" r="7.75"></circle><path d="M19.2 14.9c-1.1.5-2.1 1.1-4.2 1.1-4 0-5-3-8.5-3-.9 0-1.6.1-2.1.3m15-3.5c-1.1.5-2.1 1.2-4.4 1.2-4 0-5-3-8.5-3-.4 0-.8 0-1.2.1"></path></svg>',
title: 'Kagi Assistant'
},
{
name: 'Translate',
url: 'https://translate.kagi.com',
icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><path d="M12.75 9.5V6.25a3.25 3.25 0 00-3.25-3.25H5.5a3.25 3.25 0 00-3.25 3.25v5.75a3.25 3.25 0 003.25 3.25H9.5a3.25 3.25 0 001.5-.25"></path><rect x="11" y="9.25" width="10.5" height="12.25" rx="3.25"></rect><path d="M7.4 5.4 9.6 10.3 8.2 10.2M10 6 4.8 6.7M9.9 12.2H6.6A1.7 1.7 0 014.9 10.5M10.2 7.9l-5.2.7" stroke-width="1"></path><path d="M3.5 16.5a2.5 2.5 0 002.5 2.5h1.5m-.5-1.75 1.25 1.75-1.25 1.75M19.75 6.5a2.5 2.5 0 00-2.5-2.5h-1.5m.5 1.75-1.25-1.75 1.25-1.75" stroke-width="1"></path><path d="M14.25 18.25v-4.25a1 1 0 014.25 0v4.25m-4.25-2.5h4.25" stroke-width="1"></path></svg>',
title: 'Kagi Translate'
},
{
name: 'News',
url: 'https://news.kagi.com',
icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><path d="M4.25 7.5a1 1 0 010-4.5H9c1.5 0 2.25 0 2.75.5s.5 1.25.5 2.75V7.5ZM19 12.5C19 10.25 19 9 18.25 8.25 17.5 7.5 16.25 7.5 13.5 7.5H4.25a2.25 2.25 0 01-2.25-2.25V14.5C2 17.25 2 18.5 2.75 19.25S5 20 7.5 20H14.5"></path><path d="M4.5 11h7.5m-7.5 2.5h4.5m-4.5 2.5h4.5" stroke-width="1"></path><circle cx="17.5" cy="16.5" r="4.5"></circle><path d="M22 16.5a4.5 4.5 90 00-5.75 4.25M13.25 16.5a4.5 4.5 90 005.75-4.25" stroke-width=".75"></path></svg>',
title: 'Kagi News'
}
];
// Add styles
GM_addStyle(`
.kagi-switcher-container {
display: flex;
align-items: center;
gap: 8px;
}
/* Base button structural styles (Colors removed & separated) */
.kagi-switcher-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
text-decoration: none;
border-radius: 24px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 13px;
font-weight: 500;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
border: none;
cursor: pointer;
white-space: nowrap;
}
.kagi-switcher-btn:hover {
transform: translateY(-1px);
}
.kagi-switcher-btn svg {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.kagi-switcher-btn span {
display: inline;
}
/* =======================================
1. Assistant Theme (#c9c1ff)
======================================= */
.kagi-switcher-btn:nth-child(1) {
/* Starts slightly lighter, ends on your exact target hex */
background: linear-gradient(135deg, #e0daff 0%, #c9c1ff 100%);
box-shadow: 0 2px 8px rgba(201, 193, 255, 0.4);
/* Dark text for readability, as #c9c1ff is very bright */
color: #2b2a33;
}
.kagi-switcher-btn:nth-child(1):hover {
/* Shifts darker on hover */
background: linear-gradient(135deg, #c9c1ff 0%, #a295f5 100%);
box-shadow: 0 4px 12px rgba(162, 149, 245, 0.4);
}
/* =======================================
2. Translate Theme (#74bd44)
======================================= */
.kagi-switcher-btn:nth-child(2) {
background: linear-gradient(135deg, #8cd45c 0%, #74bd44 100%);
box-shadow: 0 2px 8px rgba(116, 189, 68, 0.3);
color: #2b2a33;
}
.kagi-switcher-btn:nth-child(2):hover {
background: linear-gradient(135deg, #74bd44 0%, #5b9e31 100%);
box-shadow: 0 4px 12px rgba(116, 189, 68, 0.4);
}
/* =======================================
3. News Theme (#ffb319)
======================================= */
.kagi-switcher-btn:nth-child(3) {
background: linear-gradient(135deg, #ffc94d 0%, #ffb319 100%);
box-shadow: 0 2px 8px rgba(255, 179, 25, 0.3);
color: #2b2a33;
}
.kagi-switcher-btn:nth-child(3):hover {
background: linear-gradient(135deg, #ffb319 0%, #e59b00 100%);
box-shadow: 0 4px 12px rgba(255, 179, 25, 0.4);
}
/* =======================================
Responsive & Modifiers
======================================= */
.kagi-switcher-compact .kagi-switcher-btn {
padding: 6px 10px;
font-size: 12px;
}
.kagi-switcher-compact .kagi-switcher-btn span {
display: none;
}
@media (max-width: 1200px) {
.kagi-switcher-btn span {
display: none;
}
.kagi-switcher-btn {
padding: 8px;
}
}
.kagi-switcher-search-position {
display: flex;
align-items: center;
margin-left: 16px;
}
`);
// Create button elements
function createSwitcherButtons(compact = false) {
const container = document.createElement('div');
container.className = `kagi-switcher-container${compact ? ' kagi-switcher-compact' : ''}`;
services.forEach(service => {
const btn = document.createElement('a');
btn.href = service.url;
btn.className = 'kagi-switcher-btn';
btn.title = service.title;
btn.innerHTML = `${service.icon}<span>${service.name}</span>`;
container.appendChild(btn);
});
return container;
}
// Insert on homepage (left of app_nav_dropdown)
function insertOnHomepage() {
// Try to find the app_nav_dropdown or account container
const navDropdown = document.querySelector('.app_nav_dropdown, [class*="app_nav_dropdown"]');
if (navDropdown && navDropdown.parentElement) {
const container = createSwitcherButtons();
navDropdown.parentElement.insertBefore(container, navDropdown);
console.log('[Kagi Switcher] Inserted buttons on homepage (left of nav dropdown)');
return true;
}
// Fallback: Try to find the account container
const accountContainer = document.querySelector('#accountContainer, .user-auth-bar');
if (accountContainer) {
const firstChild = accountContainer.querySelector('.flex, .header_links, button');
if (firstChild) {
const container = createSwitcherButtons();
accountContainer.insertBefore(container, firstChild);
console.log('[Kagi Switcher] Inserted buttons in account container');
return true;
}
}
// Fallback: Try to find the top-right navigation area
const headerNav = document.querySelector('header nav, nav[class*="header"], [class*="top-nav"]');
if (headerNav) {
const container = createSwitcherButtons();
headerNav.appendChild(container);
console.log('[Kagi Switcher] Inserted buttons in header navigation');
return true;
}
return false;
}
// Insert on search page (before app_nav_dropdown)
function insertOnSearchPage() {
// Try to find the app_nav_dropdown in the account container
const navDropdown = document.querySelector('.app_nav_dropdown, [class*="app_nav_dropdown"]');
if (navDropdown && navDropdown.parentElement) {
const container = createSwitcherButtons(true);
container.classList.add('kagi-switcher-search-position');
navDropdown.parentElement.insertBefore(container, navDropdown);
console.log('[Kagi Switcher] Inserted buttons on search page (left of app_nav_dropdown)');
return true;
}
// Fallback: Try the account container
const accountContainer = document.querySelector('#accountContainer, .user-auth-bar');
if (accountContainer) {
// Find the flex container within account area
const flexContainer = accountContainer.querySelector('.flex.align-center') || accountContainer;
const firstChild = flexContainer.firstElementChild;
if (firstChild) {
const container = createSwitcherButtons(true);
container.classList.add('kagi-switcher-search-position');
flexContainer.insertBefore(container, firstChild);
console.log('[Kagi Switcher] Inserted buttons in search header');
return true;
}
}
// Last resort: Insert after search form
const searchFormBox = document.querySelector('.search_form_box');
if (searchFormBox && searchFormBox.parentElement) {
const container = createSwitcherButtons(true);
container.classList.add('kagi-switcher-search-position');
// Insert after the search form box
const nextSibling = searchFormBox.nextElementSibling;
if (nextSibling) {
searchFormBox.parentElement.insertBefore(container, nextSibling);
} else {
searchFormBox.parentElement.appendChild(container);
}
console.log('[Kagi Switcher] Inserted buttons after search form');
return true;
}
return false;
}
// Main insertion logic
function insertButtons() {
// Check if already inserted
if (document.querySelector('.kagi-switcher-container')) {
return;
}
const isSearchPage = window.location.pathname.startsWith('/search') ||
window.location.pathname.startsWith('/html/search');
if (isSearchPage) {
if (!insertOnSearchPage()) {
// Retry after a short delay
setTimeout(insertOnSearchPage, 500);
}
} else {
if (!insertOnHomepage()) {
// Retry after a short delay in case of dynamic loading
setTimeout(insertOnHomepage, 1000);
}
}
}
// Initialize
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', insertButtons);
} else {
insertButtons();
}
// Handle dynamic page changes (SPA navigation)
const observer = new MutationObserver((mutations) => {
if (!document.querySelector('.kagi-switcher-container')) {
insertButtons();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Also try inserting after a delay for dynamically loaded content
setTimeout(insertButtons, 1500);
console.log('[Kagi Switcher] Userscript loaded and ready');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment