mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 21:29:11 +00:00
Put logout, settings under dropdown menu with logged in user's profile picture
- Create dropdown menu. Put settings page, logout action under it - Make user's profile picture the dropdown menu heading - Create khoj.js to store shared js across web client It currently stores the dropdown menu open, close functionality - Put shared styling for khoj dropdown menu under khoj.css
This commit is contained in:
@@ -100,6 +100,65 @@ p#khoj-banner {
|
|||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dropdown in navigation menu*/
|
||||||
|
.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: 15vw;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
img.circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid var(--primary-hover);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 700px) {
|
||||||
|
.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) {
|
@media only screen and (max-width: 600px) {
|
||||||
div.khoj-header {
|
div.khoj-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
15
src/khoj/interface/web/assets/khoj.js
Normal file
15
src/khoj/interface/web/assets/khoj.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Toggle the navigation menu
|
||||||
|
function toggleMenu() {
|
||||||
|
var 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");
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="stylesheet" href="/static/assets/pico.min.css">
|
<link rel="stylesheet" href="/static/assets/pico.min.css">
|
||||||
<link rel="stylesheet" href="/static/assets/khoj.css">
|
<link rel="stylesheet" href="/static/assets/khoj.css">
|
||||||
</head>
|
</head>
|
||||||
|
<script type="text/javascript" src="/static/assets/khoj.js"></script>
|
||||||
<body class="khoj-configure">
|
<body class="khoj-configure">
|
||||||
<div class="khoj-header-wrapper">
|
<div class="khoj-header-wrapper">
|
||||||
<div class="filler"></div>
|
<div class="filler"></div>
|
||||||
@@ -18,7 +19,15 @@
|
|||||||
<nav class="khoj-nav">
|
<nav class="khoj-nav">
|
||||||
<a class="khoj-nav" href="/chat">💬 Chat</a>
|
<a class="khoj-nav" href="/chat">💬 Chat</a>
|
||||||
<a class="khoj-nav" href="/">🔎 Search</a>
|
<a class="khoj-nav" href="/">🔎 Search</a>
|
||||||
<a class="khoj-nav khoj-nav-selected" href="/config">⚙️ Settings</a>
|
<!-- Dropdown Menu -->
|
||||||
|
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
|
||||||
|
<img class="circle" src="{{ user_photo }}" alt="{{ username }}" onclick="toggleMenu()">
|
||||||
|
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
|
||||||
|
<div class="khoj-nav-username"> {{ username }} </div>
|
||||||
|
<a class="khoj-nav khoj-nav-selected" href="/config">⚙️ Settings</a>
|
||||||
|
<a class="khoj-nav" href="/auth/logout">🔑 Logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="filler"></div>
|
<div class="filler"></div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="manifest" href="/static/khoj_chat.webmanifest">
|
<link rel="manifest" href="/static/khoj_chat.webmanifest">
|
||||||
<link rel="stylesheet" href="/static/assets/khoj.css">
|
<link rel="stylesheet" href="/static/assets/khoj.css">
|
||||||
</head>
|
</head>
|
||||||
|
<script type="text/javascript" src="/static/assets/khoj.js"></script>
|
||||||
<script>
|
<script>
|
||||||
let chatOptions = [];
|
let chatOptions = [];
|
||||||
function copyProgrammaticOutput(event) {
|
function copyProgrammaticOutput(event) {
|
||||||
@@ -284,7 +285,14 @@
|
|||||||
<a class="khoj-nav khoj-nav-selected" href="/chat">💬 Chat</a>
|
<a class="khoj-nav khoj-nav-selected" href="/chat">💬 Chat</a>
|
||||||
<a class="khoj-nav" href="/">🔎 Search</a>
|
<a class="khoj-nav" href="/">🔎 Search</a>
|
||||||
{% if not demo %}
|
{% if not demo %}
|
||||||
<a class="khoj-nav" href="/config">⚙️ Settings</a>
|
<!-- Dropdown Menu -->
|
||||||
|
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
|
||||||
|
<img class="circle" src="{{ user_photo }}" alt="{{ username }}" onclick="toggleMenu()">
|
||||||
|
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
|
||||||
|
<div class="khoj-nav-username"> {{ username }} </div>
|
||||||
|
<a class="khoj-nav khoj-nav-selected" href="/config">⚙️ Settings</a>
|
||||||
|
<a class="khoj-nav" href="/auth/logout">🔑 Logout</a>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,11 +3,6 @@
|
|||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
{% if anonymous_mode == False %}
|
|
||||||
<div>
|
|
||||||
Logged in as {{ username }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<h2 class="section-title">Plugins</h2>
|
<h2 class="section-title">Plugins</h2>
|
||||||
<div class="section-cards">
|
<div class="section-cards">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -328,11 +323,6 @@
|
|||||||
<div class="finalize-buttons">
|
<div class="finalize-buttons">
|
||||||
<button id="reinitialize" type="submit" title="Regenerate index from scratch">🔄 Reinitialize</button>
|
<button id="reinitialize" type="submit" title="Regenerate index from scratch">🔄 Reinitialize</button>
|
||||||
</div>
|
</div>
|
||||||
{% if anonymous_mode == False %}
|
|
||||||
<div class="finalize-buttons">
|
|
||||||
<button id="logout" class="logout" onclick="window.location.href='/auth/logout'">Logout</button>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<script type="text/javascript" src="/static/assets/org.min.js"></script>
|
<script type="text/javascript" src="/static/assets/org.min.js"></script>
|
||||||
<script type="text/javascript" src="/static/assets/markdown-it.min.js"></script>
|
<script type="text/javascript" src="/static/assets/markdown-it.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/assets/khoj.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function render_image(item) {
|
function render_image(item) {
|
||||||
@@ -296,7 +297,15 @@
|
|||||||
<a class="khoj-nav" href="/chat">💬 Chat</a>
|
<a class="khoj-nav" href="/chat">💬 Chat</a>
|
||||||
<a class="khoj-nav khoj-nav-selected" href="/">🔎 Search</a>
|
<a class="khoj-nav khoj-nav-selected" href="/">🔎 Search</a>
|
||||||
{% if not demo %}
|
{% if not demo %}
|
||||||
<a class="khoj-nav" href="/config">⚙️ Settings</a>
|
<!-- Dropdown Menu -->
|
||||||
|
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
|
||||||
|
<img class="circle" src="{{ user_photo }}" alt="{{ username }}" onclick="toggleMenu()">
|
||||||
|
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
|
||||||
|
<div class="khoj-nav-username"> {{ username }} </div>
|
||||||
|
<a class="khoj-nav" href="/config">⚙️ Settings</a>
|
||||||
|
<a class="khoj-nav" href="/auth/logout">🔑 Logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,19 +34,52 @@ VALID_TEXT_CONTENT_TYPES = ["org", "markdown", "pdf", "plaintext"]
|
|||||||
@web_client.get("/", response_class=FileResponse)
|
@web_client.get("/", response_class=FileResponse)
|
||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def index(request: Request):
|
def index(request: Request):
|
||||||
return templates.TemplateResponse("index.html", context={"request": request, "demo": state.demo})
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
|
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"index.html",
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"demo": state.demo,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@web_client.post("/", response_class=FileResponse)
|
@web_client.post("/", response_class=FileResponse)
|
||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def index_post(request: Request):
|
def index_post(request: Request):
|
||||||
return templates.TemplateResponse("index.html", context={"request": request, "demo": state.demo})
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
|
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"index.html",
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"demo": state.demo,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@web_client.get("/chat", response_class=FileResponse)
|
@web_client.get("/chat", response_class=FileResponse)
|
||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def chat_page(request: Request):
|
def chat_page(request: Request):
|
||||||
return templates.TemplateResponse("chat.html", context={"request": request, "demo": state.demo})
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
|
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"chat.html",
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"demo": state.demo,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@web_client.get("/login", response_class=FileResponse)
|
@web_client.get("/login", response_class=FileResponse)
|
||||||
@@ -84,6 +117,7 @@ if not state.demo:
|
|||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def config_page(request: Request):
|
def config_page(request: Request):
|
||||||
user = request.user.object
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
enabled_content = set(EntryAdapters.get_unique_file_types(user).all())
|
enabled_content = set(EntryAdapters.get_unique_file_types(user).all())
|
||||||
default_full_config = FullConfig(
|
default_full_config = FullConfig(
|
||||||
content_type=None,
|
content_type=None,
|
||||||
@@ -129,6 +163,7 @@ if not state.demo:
|
|||||||
"current_model_state": successfully_configured,
|
"current_model_state": successfully_configured,
|
||||||
"anonymous_mode": state.anonymous_mode,
|
"anonymous_mode": state.anonymous_mode,
|
||||||
"username": user.username if user else None,
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,6 +171,7 @@ if not state.demo:
|
|||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def github_config_page(request: Request):
|
def github_config_page(request: Request):
|
||||||
user = request.user.object
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
current_github_config = get_user_github_config(user)
|
current_github_config = get_user_github_config(user)
|
||||||
|
|
||||||
if current_github_config:
|
if current_github_config:
|
||||||
@@ -158,13 +194,20 @@ if not state.demo:
|
|||||||
current_config = {} # type: ignore
|
current_config = {} # type: ignore
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"content_type_github_input.html", context={"request": request, "current_config": current_config}
|
"content_type_github_input.html",
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"current_config": current_config,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@web_client.get("/config/content_type/notion", response_class=HTMLResponse)
|
@web_client.get("/config/content_type/notion", response_class=HTMLResponse)
|
||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def notion_config_page(request: Request):
|
def notion_config_page(request: Request):
|
||||||
user = request.user.object
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
current_notion_config = get_user_notion_config(user)
|
current_notion_config = get_user_notion_config(user)
|
||||||
|
|
||||||
current_config = NotionContentConfig(
|
current_config = NotionContentConfig(
|
||||||
@@ -174,7 +217,13 @@ if not state.demo:
|
|||||||
current_config = json.loads(current_config.json())
|
current_config = json.loads(current_config.json())
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"content_type_notion_input.html", context={"request": request, "current_config": current_config}
|
"content_type_notion_input.html",
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"current_config": current_config,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@web_client.get("/config/content_type/{content_type}", response_class=HTMLResponse)
|
@web_client.get("/config/content_type/{content_type}", response_class=HTMLResponse)
|
||||||
@@ -185,6 +234,7 @@ if not state.demo:
|
|||||||
|
|
||||||
object = map_config_to_object(content_type)
|
object = map_config_to_object(content_type)
|
||||||
user = request.user.object
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
config = object.objects.filter(user=user).first()
|
config = object.objects.filter(user=user).first()
|
||||||
if config == None:
|
if config == None:
|
||||||
config = object.objects.create(user=user)
|
config = object.objects.create(user=user)
|
||||||
@@ -202,6 +252,8 @@ if not state.demo:
|
|||||||
"request": request,
|
"request": request,
|
||||||
"current_config": current_config,
|
"current_config": current_config,
|
||||||
"content_type": content_type,
|
"content_type": content_type,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -209,6 +261,7 @@ if not state.demo:
|
|||||||
@requires(["authenticated"], redirect="login_page")
|
@requires(["authenticated"], redirect="login_page")
|
||||||
def conversation_processor_config_page(request: Request):
|
def conversation_processor_config_page(request: Request):
|
||||||
user = request.user.object
|
user = request.user.object
|
||||||
|
user_picture = request.session.get("user", {}).get("picture")
|
||||||
openai_config = ConversationAdapters.get_openai_conversation_config(user)
|
openai_config = ConversationAdapters.get_openai_conversation_config(user)
|
||||||
|
|
||||||
if openai_config:
|
if openai_config:
|
||||||
@@ -229,5 +282,7 @@ if not state.demo:
|
|||||||
context={
|
context={
|
||||||
"request": request,
|
"request": request,
|
||||||
"current_config": current_processor_openai_config,
|
"current_config": current_processor_openai_config,
|
||||||
|
"username": user.username if user else None,
|
||||||
|
"user_photo": user_picture,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user