mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-05 05:39:11 +00:00
Dynamically generate navigation menu based on user info from server
This commit is contained in:
@@ -71,14 +71,19 @@ div.khoj-header {
|
||||
gap: 20px;
|
||||
padding: 24px 16px 0px 0px;
|
||||
margin: 0 0 16px 0;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
/* Keeps the navigation menu clickable */
|
||||
a.khoj-nav {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
div.khoj-nav {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
nav.khoj-nav {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
@@ -112,12 +117,96 @@ a.khoj-logo {
|
||||
a.khoj-nav-selected {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
span.khoj-nav-item-text {
|
||||
padding-left: 8px;
|
||||
}
|
||||
img.khoj-logo {
|
||||
width: min(60vw, 90px);
|
||||
max-width: 100%;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
/* Dropdown in navigation menu*/
|
||||
#khoj-nav-menu-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.khoj-nav-dropdown-content {
|
||||
display: block;
|
||||
grid-auto-flow: row;
|
||||
position: absolute;
|
||||
background-color: var(--background-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
right: 5vw;
|
||||
top: 64px;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
pointer-events: none;
|
||||
text-align: left;
|
||||
}
|
||||
.khoj-nav-dropdown-content.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.khoj-nav-dropdown-content a {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
.khoj-nav-dropdown-content a:hover {
|
||||
background-color: var(--primary-hover);
|
||||
}
|
||||
.khoj-nav-username {
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
border: 3px dotted var(--main-text-color);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.circle:hover {
|
||||
background-color: var(--primary-hover);
|
||||
}
|
||||
.user-initial {
|
||||
background-color: var(--background-color);
|
||||
color: black;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 20px;
|
||||
box-sizing: unset;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.subscribed {
|
||||
border: 3px solid var(--primary-hover);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.khoj-nav-dropdown-content {
|
||||
display: block;
|
||||
grid-auto-flow: row;
|
||||
position: absolute;
|
||||
background-color: var(--background-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
div.khoj-header {
|
||||
display: grid;
|
||||
@@ -131,4 +220,10 @@ img.khoj-logo {
|
||||
grid-gap: 0px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
a.khoj-nav {
|
||||
padding: 0 16px;
|
||||
}
|
||||
span.khoj-nav-item-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,11 +629,21 @@
|
||||
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", async() => {
|
||||
// Setup the header pane
|
||||
document.getElementById("khoj-header").innerHTML = await populateHeaderPane();
|
||||
// Setup the nav menu
|
||||
document.getElementById("profile-picture").addEventListener("click", toggleNavMenu);
|
||||
// Set the active nav pane
|
||||
document.getElementById("chat-nav")?.classList.add("khoj-nav-selected");
|
||||
})
|
||||
|
||||
window.addEventListener('load', async() => {
|
||||
await loadChat();
|
||||
});
|
||||
|
||||
async function loadChat() {
|
||||
// Load chat history
|
||||
const hostURL = await window.hostURLAPI.getURL();
|
||||
const khojToken = await window.tokenAPI.getToken();
|
||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
||||
@@ -1065,20 +1075,10 @@
|
||||
}
|
||||
</script>
|
||||
<body>
|
||||
<div id="khoj-empty-container" class="khoj-empty-container">
|
||||
</div>
|
||||
<div id="khoj-empty-container" class="khoj-empty-container"></div>
|
||||
|
||||
<!--Add Header Logo and Nav Pane-->
|
||||
<div class="khoj-header">
|
||||
<a class="khoj-logo" href="/">
|
||||
<img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
|
||||
</a>
|
||||
<nav class="khoj-nav">
|
||||
<a class="khoj-nav khoj-nav-selected" href="./chat.html">💬 Chat</a>
|
||||
<a class="khoj-nav" href="./search.html">🔎 Search</a>
|
||||
<a class="khoj-nav" href="./config.html">⚙️ Settings</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="khoj-header" class="khoj-header"></div>
|
||||
|
||||
<div id="chat-section-wrapper">
|
||||
<div id="side-panel-wrapper">
|
||||
|
||||
@@ -9,19 +9,20 @@
|
||||
<link rel="stylesheet" href="./assets/khoj.css">
|
||||
</head>
|
||||
<script src="./utils.js"></script>
|
||||
<script>
|
||||
window.addEventListener("DOMContentLoaded", async() => {
|
||||
// Setup the header pane
|
||||
document.getElementById("khoj-header").innerHTML = await populateHeaderPane();
|
||||
// Setup the nav menu
|
||||
document.getElementById("profile-picture").addEventListener("click", toggleNavMenu);
|
||||
// Set the active nav pane
|
||||
document.getElementById("settings-nav")?.classList.add("khoj-nav-selected");
|
||||
})
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<!--Add Header Logo and Nav Pane-->
|
||||
<div class="khoj-header">
|
||||
<a class="khoj-logo" href="./chat.html">
|
||||
<img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
|
||||
</a>
|
||||
<nav class="khoj-nav">
|
||||
<a class="khoj-nav" href="./chat.html">💬 Chat</a>
|
||||
<a class="khoj-nav" href="./search.html">🔎 Search</a>
|
||||
<a class="khoj-nav khoj-nav-selected" href="./config.html">⚙️ Settings</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="khoj-header" class="khoj-header"></div>
|
||||
<div class="section-cards">
|
||||
<div class="card-description-row">
|
||||
<div class="card configuration">
|
||||
|
||||
@@ -351,6 +351,17 @@ async function deleteAllFiles () {
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch user info from Khoj server
|
||||
async function getUserInfo() {
|
||||
const getUserInfoURL = `${store.get('hostURL') || KHOJ_URL}/api/v1/user?client=desktop`;
|
||||
const headers = { 'Authorization': `Bearer ${store.get('khojToken')}` };
|
||||
try {
|
||||
let response = await axios.get(getUserInfoURL, { headers });
|
||||
return response.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
let firstRun = true;
|
||||
let win = null;
|
||||
@@ -466,6 +477,7 @@ app.whenReady().then(() => {
|
||||
|
||||
ipcMain.handle('setToken', setToken);
|
||||
ipcMain.handle('getToken', getToken);
|
||||
ipcMain.handle('getUserInfo', getUserInfo);
|
||||
|
||||
ipcMain.handle('syncData', (event, regenerate) => {
|
||||
syncData(regenerate);
|
||||
|
||||
@@ -53,6 +53,10 @@ contextBridge.exposeInMainWorld('syncDataAPI', {
|
||||
deleteAllFiles: () => ipcRenderer.invoke('deleteAllFiles')
|
||||
})
|
||||
|
||||
contextBridge.exposeInMainWorld('userInfoAPI', {
|
||||
getUserInfo: () => ipcRenderer.invoke('getUserInfo')
|
||||
})
|
||||
|
||||
contextBridge.exposeInMainWorld('tokenAPI', {
|
||||
setToken: (token) => ipcRenderer.invoke('setToken', token),
|
||||
getToken: () => ipcRenderer.invoke('getToken')
|
||||
|
||||
@@ -246,6 +246,16 @@
|
||||
window.history.pushState({}, "", url.href);
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", async() => {
|
||||
// Setup the header pane
|
||||
document.getElementById("khoj-header").innerHTML = await populateHeaderPane();
|
||||
// Setup the nav menu
|
||||
document.getElementById("profile-picture").addEventListener("click", toggleNavMenu);
|
||||
// Set the active nav pane
|
||||
document.getElementById("search-nav")?.classList.add("khoj-nav-selected");
|
||||
})
|
||||
|
||||
|
||||
window.addEventListener("load", async function() {
|
||||
// Dynamically populate type dropdown based on enabled content types and type passed as URL query parameter
|
||||
await populate_type_dropdown();
|
||||
@@ -259,16 +269,7 @@
|
||||
|
||||
<body>
|
||||
<!--Add Header Logo and Nav Pane-->
|
||||
<div class="khoj-header">
|
||||
<a class="khoj-logo" href="./index.html">
|
||||
<img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
|
||||
</a>
|
||||
<nav class="khoj-nav">
|
||||
<a class="khoj-nav" href="./chat.html">💬 Chat</a>
|
||||
<a class="khoj-nav khoj-nav-selected" href="./search.html">🔎 Search</a>
|
||||
<a class="khoj-nav" href="./config.html">⚙️ Settings</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="khoj-header" class="khoj-header"></div>
|
||||
|
||||
<!--Add Text Box To Enter Query, Trigger Incremental Search OnChange -->
|
||||
<input type="text" id="query" class="option" onkeyup=incrementalSearch(event) autofocus="autofocus" placeholder="Search your knowledge base using natural language">
|
||||
|
||||
@@ -24,3 +24,56 @@ window.appInfoAPI.getInfo((_, info) => {
|
||||
khojTitleElement.innerHTML = '<b>Khoj for ' + (info.platform === 'win32' ? 'Windows' : info.platform === 'darwin' ? 'macOS' : 'Linux') + '</b>';
|
||||
}
|
||||
});
|
||||
|
||||
function toggleNavMenu() {
|
||||
let menu = document.getElementById("khoj-nav-menu");
|
||||
menu.classList.toggle("show");
|
||||
}
|
||||
|
||||
// Close the dropdown menu if the user clicks outside of it
|
||||
document.addEventListener('click', function(event) {
|
||||
let menu = document.getElementById("khoj-nav-menu");
|
||||
let menuContainer = document.getElementById("khoj-nav-menu-container");
|
||||
let isClickOnMenu = menuContainer.contains(event.target) || menuContainer === event.target;
|
||||
if (isClickOnMenu === false && menu.classList.contains("show")) {
|
||||
menu.classList.remove("show");
|
||||
}
|
||||
});
|
||||
|
||||
async function populateHeaderPane() {
|
||||
let userInfo = null;
|
||||
try {
|
||||
userInfo = await window.userInfoAPI.getUserInfo();
|
||||
} catch (error) {
|
||||
console.log("User not logged in");
|
||||
}
|
||||
|
||||
let username = userInfo?.username ?? "?";
|
||||
let user_photo = userInfo?.photo;
|
||||
let is_active = userInfo?.is_active;
|
||||
let has_documents = userInfo?.has_documents;
|
||||
|
||||
// Populate the header element with the navigation pane
|
||||
return `
|
||||
<a class="khoj-logo" href="/">
|
||||
<img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
|
||||
</a>
|
||||
<nav class="khoj-nav">
|
||||
<a id="chat-nav" class="khoj-nav" href="./chat.html">💬 <span class="khoj-nav-item-text">Chat</span></a>
|
||||
${has_documents ? '<a id="search-nav" class="khoj-nav" href="./search.html">🔎 <span class="khoj-nav-item-text">Search</span></a>' : ''}
|
||||
${username ? `
|
||||
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
|
||||
${user_photo && user_photo != "None" ? `
|
||||
<img id="profile-picture" class="${is_active ? 'circle subscribed' : 'circle'}" src="${user_photo}" alt="${username[0].toUpperCase()}" referrerpolicy="no-referrer">
|
||||
` : `
|
||||
<div id="profile-picture" class="${is_active ? 'circle user-initial subscribed' : 'circle user-initial'}" alt="${username[0].toUpperCase()}">${username[0].toUpperCase()}</div>
|
||||
`}
|
||||
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
|
||||
<div class="khoj-nav-username"> ${username} </div>
|
||||
<a id="settings-nav" class="khoj-nav" href="./config.html">⚙️ Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user