mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-30 19:03:01 +01:00
Create config page on web app to manage computer files indexed by Khoj
Remove the table of all files indexed by Khoj. This seems overkill and doesn't match the UI semantics of the other data sources like Github, Notion. Create instead a data source card for computer files with the same update, disable semantics of the Github and Notion data source cards Users can disable each data source from its card on the main config page. They can see/delete individual files indexed from the computer data source once they click into the computer files data source card on the config page
This commit is contained in:
parent
d527b644f4
commit
6e957584ac
5 changed files with 165 additions and 116 deletions
BIN
src/khoj/interface/web/assets/icons/computer.png
Normal file
BIN
src/khoj/interface/web/assets/icons/computer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -3,9 +3,40 @@
|
|||
|
||||
<div class="page">
|
||||
<div class="section">
|
||||
<h2 class="section-title">Plugins</h2>
|
||||
<h2 class="section-title">Content</h2>
|
||||
<div class="section-cards">
|
||||
<div class="card">
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="/static/assets/icons/computer.png" alt="Computer">
|
||||
<h3 class="card-title">
|
||||
Files
|
||||
{% if current_model_state.computer == True %}
|
||||
<img id="configured-icon-github" class="configured-icon" src="/static/assets/icons/confirm-icon.svg" alt="Configured">
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<p class="card-description">Manage files from your computer</p>
|
||||
</div>
|
||||
<div class="card-action-row">
|
||||
<a class="card-button" href="/config/content-source/computer">
|
||||
{% if current_model_state.computer %}
|
||||
Update
|
||||
{% else %}
|
||||
Setup
|
||||
{% endif %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"></path></svg>
|
||||
</a>
|
||||
</div>
|
||||
{% if current_model_state.computer %}
|
||||
<div id="clear-desktop" class="card-action-row">
|
||||
<button class="card-button" onclick="clearContentType('computer')">
|
||||
Disable
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="/static/assets/icons/github.svg" alt="Github">
|
||||
<h3 class="card-title">
|
||||
|
@ -47,11 +78,11 @@
|
|||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<p class="card-description">Configure your settings from Notion</p>
|
||||
<p class="card-description">Sync your Notion pages</p>
|
||||
</div>
|
||||
<div class="card-action-row">
|
||||
<a class="card-button" href="/config/content-source/notion">
|
||||
{% if current_model_state.content %}
|
||||
{% if current_model_state.notion %}
|
||||
Update
|
||||
{% else %}
|
||||
Setup
|
||||
|
@ -77,7 +108,7 @@
|
|||
<div class="card-title-row">
|
||||
<img class="card-icon" src="/static/assets/icons/chat.svg" alt="Chat">
|
||||
<h3 class="card-title">
|
||||
Chat Model
|
||||
Chat
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
|
@ -122,16 +153,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2 class="section-title">Manage Data</h2>
|
||||
<div class="section-manage-files">
|
||||
<div id="delete-all-files" class="delete-all=files">
|
||||
<button id="delete-all-files" type="submit" title="Delete all indexed files">🗑️ Remove All</button>
|
||||
</div>
|
||||
<div class="indexed-files">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section general-settings">
|
||||
<div id="results-count" title="Number of items to show in search and use for chat response">
|
||||
<label for="results-count-slider">Results Count: <span id="results-count-value">5</span></label>
|
||||
|
@ -363,70 +384,5 @@
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Get all currently indexed files
|
||||
function getAllFilenames() {
|
||||
fetch('/api/config/data/all')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var indexedFiles = document.getElementsByClassName("indexed-files")[0];
|
||||
indexedFiles.innerHTML = "";
|
||||
|
||||
if (data.length == 0) {
|
||||
document.getElementById("delete-all-files").style.display = "none";
|
||||
indexedFiles.innerHTML = "<div>Use the <a href='https://download.khoj.dev'>Khoj Desktop client</a> to index files.</div>";
|
||||
} else {
|
||||
document.getElementById("delete-all-files").style.display = "block";
|
||||
}
|
||||
|
||||
for (var filename of data) {
|
||||
let fileElement = document.createElement("div");
|
||||
fileElement.classList.add("file-element");
|
||||
|
||||
let fileNameElement = document.createElement("div");
|
||||
fileNameElement.classList.add("content-name");
|
||||
fileNameElement.innerHTML = filename;
|
||||
fileElement.appendChild(fileNameElement);
|
||||
|
||||
let buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("remove-button-container");
|
||||
let removeFileButton = document.createElement("button");
|
||||
removeFileButton.classList.add("remove-file-button");
|
||||
removeFileButton.innerHTML = "🗑️";
|
||||
removeFileButton.addEventListener("click", ((filename) => {
|
||||
return () => {
|
||||
removeFile(filename);
|
||||
};
|
||||
})(filename));
|
||||
buttonContainer.appendChild(removeFileButton);
|
||||
fileElement.appendChild(buttonContainer);
|
||||
indexedFiles.appendChild(fileElement);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Get all currently indexed files on page load
|
||||
getAllFilenames();
|
||||
|
||||
let deleteAllFilesButton = document.getElementById("delete-all-files");
|
||||
deleteAllFilesButton.addEventListener("click", function(event) {
|
||||
event.preventDefault();
|
||||
fetch('/api/config/data/all', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status == "ok") {
|
||||
getAllFilenames();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
107
src/khoj/interface/web/content_source_computer_input.html
Normal file
107
src/khoj/interface/web/content_source_computer_input.html
Normal file
|
@ -0,0 +1,107 @@
|
|||
{% extends "base_config.html" %}
|
||||
{% block content %}
|
||||
<div class="page">
|
||||
<div class="section">
|
||||
<h2 class="section-title">
|
||||
<img class="card-icon" src="/static/assets/icons/computer.png" alt="files">
|
||||
<span class="card-title-text">Files</span>
|
||||
<div class="instructions">
|
||||
<p class="card-description">Manage files from your computer</p>
|
||||
<p id="desktop-client" class="card-description">Download the <a href="https://download.khoj.dev">Khoj Desktop app</a> to sync files from your computer</p>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="section-manage-files">
|
||||
<div id="delete-all-files" class="delete-all-files">
|
||||
<button id="delete-all-files" type="submit" title="Remove all computer files from Khoj">🗑️ Delete all</button>
|
||||
</div>
|
||||
<div class="indexed-files">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
#desktop-client {
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function removeFile(path) {
|
||||
fetch('/api/config/data/file?filename=' + path, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.ok ? response.json() : Promise.reject(response))
|
||||
.then(data => {
|
||||
if (data.status == "ok") {
|
||||
getAllComputerFilenames();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Get all currently indexed files
|
||||
function getAllComputerFilenames() {
|
||||
fetch('/api/config/data/computer')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var indexedFiles = document.getElementsByClassName("indexed-files")[0];
|
||||
indexedFiles.innerHTML = "";
|
||||
|
||||
if (data.length == 0) {
|
||||
document.getElementById("delete-all-files").style.display = "none";
|
||||
indexedFiles.innerHTML = "<div class='card-description'>Use the <a href='https://download.khoj.dev'>Khoj Desktop client</a> to index files.</div>";
|
||||
} else {
|
||||
document.getElementById("delete-all-files").style.display = "block";
|
||||
}
|
||||
|
||||
for (var filename of data) {
|
||||
let fileElement = document.createElement("div");
|
||||
fileElement.classList.add("file-element");
|
||||
|
||||
let fileNameElement = document.createElement("div");
|
||||
fileNameElement.classList.add("content-name");
|
||||
fileNameElement.innerHTML = filename;
|
||||
fileElement.appendChild(fileNameElement);
|
||||
|
||||
let buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("remove-button-container");
|
||||
let removeFileButton = document.createElement("button");
|
||||
removeFileButton.classList.add("remove-file-button");
|
||||
removeFileButton.innerHTML = "🗑️";
|
||||
removeFileButton.addEventListener("click", ((filename) => {
|
||||
return () => {
|
||||
removeFile(filename);
|
||||
};
|
||||
})(filename));
|
||||
buttonContainer.appendChild(removeFileButton);
|
||||
fileElement.appendChild(buttonContainer);
|
||||
indexedFiles.appendChild(fileElement);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Get all currently indexed files on page load
|
||||
getAllComputerFilenames();
|
||||
|
||||
let deleteAllComputerFilesButton = document.getElementById("delete-all-files");
|
||||
deleteAllComputerFilesButton.addEventListener("click", function(event) {
|
||||
event.preventDefault();
|
||||
fetch('/api/config/data/content-source/computer', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status == "ok") {
|
||||
getAllComputerFilenames();
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -270,10 +270,11 @@ async def remove_file_data(
|
|||
return {"status": "ok"}
|
||||
|
||||
|
||||
@api.get("/config/data/all", response_model=List[str])
|
||||
@api.get("/config/data/{content_source}", response_model=List[str])
|
||||
@requires(["authenticated"])
|
||||
async def get_all_filenames(
|
||||
request: Request,
|
||||
content_source: str,
|
||||
client: Optional[str] = None,
|
||||
):
|
||||
user = request.user.object
|
||||
|
@ -285,27 +286,7 @@ async def get_all_filenames(
|
|||
client=client,
|
||||
)
|
||||
|
||||
return await sync_to_async(list)(EntryAdapters.aget_all_filenames(user))
|
||||
|
||||
|
||||
@api.delete("/config/data/all", status_code=200)
|
||||
@requires(["authenticated"])
|
||||
async def remove_all_config_data(
|
||||
request: Request,
|
||||
client: Optional[str] = None,
|
||||
):
|
||||
user = request.user.object
|
||||
|
||||
update_telemetry_state(
|
||||
request=request,
|
||||
telemetry_type="api",
|
||||
api="delete_all_config",
|
||||
client=client,
|
||||
)
|
||||
|
||||
await EntryAdapters.adelete_all_entries(user)
|
||||
|
||||
return {"status": "ok"}
|
||||
return await sync_to_async(list)(EntryAdapters.aget_all_filenames_by_source(user, content_source))
|
||||
|
||||
|
||||
@api.post("/config/data/conversation/model", status_code=200)
|
||||
|
|
|
@ -110,25 +110,14 @@ def login_page(request: Request):
|
|||
def config_page(request: Request):
|
||||
user = request.user.object
|
||||
user_picture = request.session.get("user", {}).get("picture")
|
||||
enabled_content = set(EntryAdapters.get_unique_file_types(user).all())
|
||||
enabled_content_source = set(EntryAdapters.get_unique_file_source(user).all())
|
||||
|
||||
successfully_configured = {
|
||||
"pdf": ("pdf" in enabled_content),
|
||||
"markdown": ("markdown" in enabled_content),
|
||||
"org": ("org" in enabled_content),
|
||||
"image": False,
|
||||
"github": ("github" in enabled_content),
|
||||
"notion": ("notion" in enabled_content),
|
||||
"plaintext": ("plaintext" in enabled_content),
|
||||
"computer": ("computer" in enabled_content_source),
|
||||
"github": ("github" in enabled_content_source),
|
||||
"notion": ("notion" in enabled_content_source),
|
||||
}
|
||||
|
||||
if state.content_index:
|
||||
successfully_configured.update(
|
||||
{
|
||||
"image": state.content_index.image is not None,
|
||||
}
|
||||
)
|
||||
|
||||
conversation_options = ConversationAdapters.get_conversation_processor_options().all()
|
||||
all_conversation_options = list()
|
||||
for conversation_option in conversation_options:
|
||||
|
@ -209,3 +198,19 @@ def notion_config_page(request: Request):
|
|||
"user_photo": user_picture,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@web_client.get("/config/content-source/computer", response_class=HTMLResponse)
|
||||
@requires(["authenticated"], redirect="login_page")
|
||||
def computer_config_page(request: Request):
|
||||
user = request.user.object
|
||||
user_picture = request.session.get("user", {}).get("picture")
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"content_source_computer_input.html",
|
||||
context={
|
||||
"request": request,
|
||||
"username": user.username,
|
||||
"user_photo": user_picture,
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue