mirror of
https://github.com/perstarkse/minne.git
synced 2026-01-15 14:43:27 +01:00
Compare commits
1 Commits
main
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fe4ac9fec |
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**A graph-powered personal knowledge base that makes storing easy.**
|
**A graph-powered personal knowledge base that makes storing easy.**
|
||||||
|
|
||||||
Capture content effortlessly, let AI discover connections, and explore your knowledge through search, chat and visually. Self-hosted and privacy-focused.
|
Capture content effortlessly, let AI discover connections, and explore your knowledge visually. Self-hosted and privacy-focused.
|
||||||
|
|
||||||
[](https://github.com/perstarkse/minne/actions/workflows/release.yml)
|
[](https://github.com/perstarkse/minne/actions/workflows/release.yml)
|
||||||
[](https://www.gnu.org/licenses/agpl-3.0)
|
[](https://www.gnu.org/licenses/agpl-3.0)
|
||||||
|
|||||||
@@ -21,49 +21,19 @@ pub enum TemplateEngine {
|
|||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! create_template_engine {
|
macro_rules! create_template_engine {
|
||||||
// Single path argument
|
// Macro takes the relative path to the templates dir as input
|
||||||
($relative_path:expr) => {
|
($relative_path:expr) => {{
|
||||||
$crate::create_template_engine!($relative_path, Option::<&str>::None)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Path + Fallback argument
|
|
||||||
($relative_path:expr, $fallback_path:expr) => {{
|
|
||||||
// Code for debug builds (AutoReload)
|
// Code for debug builds (AutoReload)
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
// These lines execute in the CALLING crate's context
|
// These lines execute in the CALLING crate's context
|
||||||
let crate_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
let crate_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
let template_path = crate_dir.join($relative_path);
|
let template_path = crate_dir.join($relative_path);
|
||||||
let fallback_path = $fallback_path.map(|p| crate_dir.join(p));
|
|
||||||
|
|
||||||
let reloader = $crate::utils::template_engine::AutoReloader::new(move |notifier| {
|
let reloader = $crate::utils::template_engine::AutoReloader::new(move |notifier| {
|
||||||
let mut env = $crate::utils::template_engine::Environment::new();
|
let mut env = $crate::utils::template_engine::Environment::new();
|
||||||
|
env.set_loader($crate::utils::template_engine::path_loader(&template_path));
|
||||||
let loader_primary = $crate::utils::template_engine::path_loader(&template_path);
|
|
||||||
|
|
||||||
// Clone fallback_path for the closure
|
|
||||||
let fallback = fallback_path.clone();
|
|
||||||
|
|
||||||
env.set_loader(move |name| match loader_primary(name) {
|
|
||||||
Ok(Some(tmpl)) => Ok(Some(tmpl)),
|
|
||||||
Ok(None) => {
|
|
||||||
if let Some(ref fb_path) = fallback {
|
|
||||||
let loader_fallback =
|
|
||||||
$crate::utils::template_engine::path_loader(fb_path);
|
|
||||||
loader_fallback(name)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
});
|
|
||||||
|
|
||||||
notifier.set_fast_reload(true);
|
notifier.set_fast_reload(true);
|
||||||
notifier.watch_path(&template_path, true);
|
notifier.watch_path(&template_path, true);
|
||||||
if let Some(ref fb) = fallback_path {
|
|
||||||
notifier.watch_path(fb, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add contrib filters/functions
|
// Add contrib filters/functions
|
||||||
$crate::utils::template_engine::minijinja_contrib::add_to_environment(&mut env);
|
$crate::utils::template_engine::minijinja_contrib::add_to_environment(&mut env);
|
||||||
Ok(env)
|
Ok(env)
|
||||||
|
|||||||
@@ -12,13 +12,11 @@ include = ["lib"]
|
|||||||
# The installers to generate for each app
|
# The installers to generate for each app
|
||||||
installers = []
|
installers = []
|
||||||
# Target platforms to build apps for (Rust target-triple syntax)
|
# Target platforms to build apps for (Rust target-triple syntax)
|
||||||
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
targets = ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||||
# Skip checking whether the specified configuration files are up to date
|
# Skip checking whether the specified configuration files are up to date
|
||||||
allow-dirty = ["ci"]
|
allow-dirty = ["ci"]
|
||||||
|
|
||||||
[dist.github-custom-runners]
|
[dist.github-custom-runners]
|
||||||
aarch64-apple-darwin = "macos-latest"
|
|
||||||
x86_64-apple-darwin = "macos-15-intel"
|
|
||||||
x86_64-unknown-linux-gnu = "ubuntu-22.04"
|
x86_64-unknown-linux-gnu = "ubuntu-22.04"
|
||||||
x86_64-unknown-linux-musl = "ubuntu-22.04"
|
x86_64-unknown-linux-musl = "ubuntu-22.04"
|
||||||
x86_64-pc-windows-msvc = "windows-latest"
|
x86_64-pc-windows-msvc = "windows-latest"
|
||||||
|
|||||||
@@ -29,17 +29,15 @@ impl HtmlState {
|
|||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
reranker_pool: Option<Arc<RerankerPool>>,
|
reranker_pool: Option<Arc<RerankerPool>>,
|
||||||
embedding_provider: Arc<EmbeddingProvider>,
|
embedding_provider: Arc<EmbeddingProvider>,
|
||||||
template_engine: Option<Arc<TemplateEngine>>,
|
|
||||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let templates =
|
let template_engine = create_template_engine!("templates");
|
||||||
template_engine.unwrap_or_else(|| Arc::new(create_template_engine!("templates")));
|
debug!("Template engine created for html_router.");
|
||||||
debug!("Template engine configured for html_router.");
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
db,
|
db,
|
||||||
openai_client,
|
openai_client,
|
||||||
session_store,
|
session_store,
|
||||||
templates,
|
templates: Arc::new(template_engine),
|
||||||
config,
|
config,
|
||||||
storage,
|
storage,
|
||||||
reranker_pool,
|
reranker_pool,
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ pub struct ModelSettingsInput {
|
|||||||
processing_model: String,
|
processing_model: String,
|
||||||
image_processing_model: String,
|
image_processing_model: String,
|
||||||
voice_processing_model: String,
|
voice_processing_model: String,
|
||||||
embedding_model: Option<String>,
|
embedding_model: String,
|
||||||
embedding_dimensions: Option<u32>,
|
embedding_dimensions: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,9 +219,7 @@ pub async fn update_model_settings(
|
|||||||
.embedding_dimensions
|
.embedding_dimensions
|
||||||
.is_some_and(|new_dims| new_dims != current_settings.embedding_dimensions);
|
.is_some_and(|new_dims| new_dims != current_settings.embedding_dimensions);
|
||||||
(
|
(
|
||||||
input
|
input.embedding_model,
|
||||||
.embedding_model
|
|
||||||
.unwrap_or_else(|| current_settings.embedding_model.clone()),
|
|
||||||
input
|
input
|
||||||
.embedding_dimensions
|
.embedding_dimensions
|
||||||
.unwrap_or(current_settings.embedding_dimensions),
|
.unwrap_or(current_settings.embedding_dimensions),
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
{% extends 'admin/_layout.html' %}
|
|
||||||
|
|
||||||
{% block admin_navigation %}
|
|
||||||
<a href="/admin?section=overview"
|
|
||||||
class="nb-btn btn-sm px-4 {% if current_section == 'overview' %}nb-cta{% else %}btn-ghost{% endif %}">
|
|
||||||
Overview
|
|
||||||
</a>
|
|
||||||
<a href="/admin?section=models"
|
|
||||||
class="nb-btn btn-sm px-4 {% if current_section == 'models' %}nb-cta{% else %}btn-ghost{% endif %}">
|
|
||||||
Models
|
|
||||||
</a>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block admin_content %}
|
|
||||||
{% if current_section == 'models' %}
|
|
||||||
{% include 'admin/sections/models.html' %}
|
|
||||||
{% else %}
|
|
||||||
{% include 'admin/sections/overview.html' %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{% extends 'body_base.html' %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Admin{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<div id="admin-shell" class="flex justify-center grow mt-2 sm:mt-4 pb-4">
|
|
||||||
<div class="container flex flex-col gap-4">
|
|
||||||
<section class="nb-panel p-4 sm:p-5 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 class="text-xl font-extrabold tracking-tight">Admin Controls</h1>
|
|
||||||
</div>
|
|
||||||
<div class="text-xs opacity-60 sm:text-right">
|
|
||||||
Signed in as <span class="font-medium">{{ user.email }}</span>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<nav class="nb-panel p-2 flex flex-wrap gap-2 text-sm" hx-boost="true" hx-target="#admin-shell"
|
|
||||||
hx-select="#admin-shell" hx-swap="outerHTML" hx-push-url="true">
|
|
||||||
{% block admin_navigation %}
|
|
||||||
{% endblock %}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="admin-content" class="flex flex-col gap-4">
|
|
||||||
{% block admin_content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1 +1,38 @@
|
|||||||
{% extends "admin/_base.html" %}
|
{% extends 'body_base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Minne - Admin{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<div id="admin-shell" class="flex justify-center grow mt-2 sm:mt-4 pb-4">
|
||||||
|
<div class="container flex flex-col gap-4">
|
||||||
|
<section class="nb-panel p-4 sm:p-5 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-xl font-extrabold tracking-tight">Admin Controls</h1>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs opacity-60 sm:text-right">
|
||||||
|
Signed in as <span class="font-medium">{{ user.email }}</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<nav class="nb-panel p-2 flex flex-wrap gap-2 text-sm" hx-boost="true" hx-target="#admin-shell"
|
||||||
|
hx-select="#admin-shell" hx-swap="outerHTML" hx-push-url="true">
|
||||||
|
<a href="/admin?section=overview"
|
||||||
|
class="nb-btn btn-sm px-4 {% if current_section == 'overview' %}nb-cta{% else %}btn-ghost{% endif %}">
|
||||||
|
Overview
|
||||||
|
</a>
|
||||||
|
<a href="/admin?section=models"
|
||||||
|
class="nb-btn btn-sm px-4 {% if current_section == 'models' %}nb-cta{% else %}btn-ghost{% endif %}">
|
||||||
|
Models
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="admin-content" class="flex flex-col gap-4">
|
||||||
|
{% if current_section == 'models' %}
|
||||||
|
{% include 'admin/sections/models.html' %}
|
||||||
|
{% else %}
|
||||||
|
{% include 'admin/sections/overview.html' %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
{% extends "auth/_settings_layout.html" %}
|
|
||||||
|
|
||||||
{% block settings_header %}
|
|
||||||
<h1 class="text-xl font-extrabold tracking-tight">Account Settings</h1>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block settings_left_column %}
|
|
||||||
<label class="w-full">
|
|
||||||
<div class="text-xs uppercase tracking-wide opacity-70 mb-1">Email</div>
|
|
||||||
<input type="email" name="email" value="{{ user.email }}" class="nb-input w-full" disabled />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="w-full">
|
|
||||||
<div class="text-xs uppercase tracking-wide opacity-70 mb-1">API Key</div>
|
|
||||||
{% block api_key_section %}
|
|
||||||
{% if user.api_key %}
|
|
||||||
<div class="relative">
|
|
||||||
<input id="api_key_input" type="text" name="api_key" value="{{ user.api_key }}"
|
|
||||||
class="nb-input w-full pr-14" disabled />
|
|
||||||
<button type="button" id="copy_api_key_btn" onclick="copy_api_key()"
|
|
||||||
class="absolute inset-y-0 right-0 flex items-center px-2 nb-btn btn-sm" aria-label="Copy API key"
|
|
||||||
title="Copy API key">
|
|
||||||
{% include "icons/clipboard_icon.html" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<a href="https://www.icloud.com/shortcuts/66985f7b98a74aaeac6ba29c3f1f0960"
|
|
||||||
class="nb-btn nb-cta mt-2 w-full">Download iOS shortcut</a>
|
|
||||||
{% else %}
|
|
||||||
<button hx-post="/set-api-key" class="nb-btn nb-cta w-full" hx-swap="outerHTML">Create API-Key</button>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function copy_api_key() {
|
|
||||||
const input = document.getElementById('api_key_input');
|
|
||||||
if (!input) return;
|
|
||||||
if (navigator.clipboard && window.isSecureContext) {
|
|
||||||
navigator.clipboard.writeText(input.value)
|
|
||||||
.then(() => show_toast('API key copied!', 'success'))
|
|
||||||
.catch(() => show_toast('Copy failed', 'error'));
|
|
||||||
} else {
|
|
||||||
show_toast('Copy not supported', 'info');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label class="w-full">
|
|
||||||
<div class="text-xs uppercase tracking-wide opacity-70 mb-1">Timezone</div>
|
|
||||||
{% block timezone_section %}
|
|
||||||
<select name="timezone" class="nb-select w-full" hx-patch="/update-timezone" hx-swap="outerHTML">
|
|
||||||
{% for tz in timezones %}
|
|
||||||
<option value="{{ tz }}" {% if tz==user.timezone %}selected{% endif %}>{{ tz }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% endblock %}
|
|
||||||
</label>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block settings_right_column %}
|
|
||||||
<div>
|
|
||||||
{% block change_password_section %}
|
|
||||||
<button hx-get="/change-password" hx-swap="outerHTML" class="nb-btn w-full">Change Password</button>
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button hx-delete="/delete-account"
|
|
||||||
hx-confirm="This action will permanently delete your account and all data associated. Are you sure you want to continue?"
|
|
||||||
class="nb-btn btn-error w-full">Delete Account</button>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{% extends "head_base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Auth{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="min-h-[100dvh] flex flex-col items-center justify-center">
|
|
||||||
{% block auth_content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{% extends "body_base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Account{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<div class="flex justify-center grow mt-2 sm:mt-4 pb-4">
|
|
||||||
<div class="container">
|
|
||||||
<section class="mb-4">
|
|
||||||
<div class="nb-panel p-3 flex items-center justify-between">
|
|
||||||
{% block settings_header %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="grid grid-cols-1 lg:grid-cols-2 gap-4 space-y-2">
|
|
||||||
<!-- Left column -->
|
|
||||||
<div class="nb-panel p-4 space-y-2 flex flex-col">
|
|
||||||
{% block settings_left_column %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right column -->
|
|
||||||
<div class="nb-panel p-4 space-y-2">
|
|
||||||
{% block settings_right_column %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div id="account-result" class="mt-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1 +1,88 @@
|
|||||||
{% extends "auth/_account_settings_core.html" %}
|
{% extends "body_base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Minne - Account{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<div class="flex justify-center grow mt-2 sm:mt-4 pb-4">
|
||||||
|
<div class="container">
|
||||||
|
<section class="mb-4">
|
||||||
|
<div class="nb-panel p-3 flex items-center justify-between">
|
||||||
|
<h1 class="text-xl font-extrabold tracking-tight">Account Settings</h1>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="grid grid-cols-1 lg:grid-cols-2 gap-4 space-y-2">
|
||||||
|
<!-- Left column -->
|
||||||
|
<div class="nb-panel p-4 space-y-2 flex flex-col">
|
||||||
|
<label class="w-full">
|
||||||
|
<div class="text-xs uppercase tracking-wide opacity-70 mb-1">Email</div>
|
||||||
|
<input type="email" name="email" value="{{ user.email }}" class="nb-input w-full" disabled />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="w-full">
|
||||||
|
<div class="text-xs uppercase tracking-wide opacity-70 mb-1">API Key</div>
|
||||||
|
{% block api_key_section %}
|
||||||
|
{% if user.api_key %}
|
||||||
|
<div class="relative">
|
||||||
|
<input id="api_key_input" type="text" name="api_key" value="{{ user.api_key }}"
|
||||||
|
class="nb-input w-full pr-14" disabled />
|
||||||
|
<button type="button" id="copy_api_key_btn" onclick="copy_api_key()"
|
||||||
|
class="absolute inset-y-0 right-0 flex items-center px-2 nb-btn btn-sm" aria-label="Copy API key"
|
||||||
|
title="Copy API key">
|
||||||
|
{% include "icons/clipboard_icon.html" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<a href="https://www.icloud.com/shortcuts/66985f7b98a74aaeac6ba29c3f1f0960"
|
||||||
|
class="nb-btn nb-cta mt-2 w-full">Download iOS shortcut</a>
|
||||||
|
{% else %}
|
||||||
|
<button hx-post="/set-api-key" class="nb-btn nb-cta w-full" hx-swap="outerHTML">Create API-Key</button>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copy_api_key() {
|
||||||
|
const input = document.getElementById('api_key_input');
|
||||||
|
if (!input) return;
|
||||||
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
|
navigator.clipboard.writeText(input.value)
|
||||||
|
.then(() => show_toast('API key copied!', 'success'))
|
||||||
|
.catch(() => show_toast('Copy failed', 'error'));
|
||||||
|
} else {
|
||||||
|
show_toast('Copy not supported', 'info');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<label class="w-full">
|
||||||
|
<div class="text-xs uppercase tracking-wide opacity-70 mb-1">Timezone</div>
|
||||||
|
{% block timezone_section %}
|
||||||
|
<select name="timezone" class="nb-select w-full" hx-patch="/update-timezone" hx-swap="outerHTML">
|
||||||
|
{% for tz in timezones %}
|
||||||
|
<option value="{{ tz }}" {% if tz==user.timezone %}selected{% endif %}>{{ tz }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% endblock %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right column -->
|
||||||
|
<div class="nb-panel p-4 space-y-2">
|
||||||
|
<div>
|
||||||
|
{% block change_password_section %}
|
||||||
|
<button hx-get="/change-password" hx-swap="outerHTML" class="nb-btn w-full">Change Password</button>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button hx-delete="/delete-account"
|
||||||
|
hx-confirm="This action will permanently delete your account and all data associated. Are you sure you want to continue?"
|
||||||
|
class="nb-btn btn-error w-full">Delete Account</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="account-result" class="mt-4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
{% extends "auth/_layout.html" %}
|
{% extends "head_base.html" %}
|
||||||
|
|
||||||
{% block title %}Minne - Sign in{% endblock %}
|
{% block title %}Minne - Sign in{% endblock %}
|
||||||
|
|
||||||
{% block auth_content %}
|
{% block body %}
|
||||||
|
<div class="min-h-[100dvh] flex">
|
||||||
{% include "auth/signin_form.html" %}
|
{% include "auth/signin_form.html" %}
|
||||||
{% endblock %}
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
{% extends "auth/_layout.html" %}
|
{% extends "head_base.html" %}
|
||||||
|
|
||||||
{% block title %}Minne - Sign up{% endblock %}
|
{% block title %}Minne - Sign up{% endblock %}
|
||||||
|
|
||||||
{% block auth_content %}
|
{% block body %}
|
||||||
|
<div class="min-h-[100dvh] flex items-center">
|
||||||
<div class="container mx-auto px-4 sm:max-w-md">
|
<div class="container mx-auto px-4 sm:max-w-md">
|
||||||
<div class="nb-card p-5">
|
<div class="nb-card p-5">
|
||||||
<div class="flex items-center justify-between mb-3">
|
<div class="flex items-center justify-between mb-3">
|
||||||
@@ -45,9 +46,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
</div>
|
||||||
// Detect timezone and set hidden input
|
<script>
|
||||||
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
// Detect timezone and set hidden input
|
||||||
document.getElementById("timezone").value = timezone;
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
</script>
|
document.getElementById("timezone").value = timezone;
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
{% extends 'body_base.html' %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Chat{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<div class="flex grow relative justify-center mt-2 sm:mt-4">
|
|
||||||
<div class="container">
|
|
||||||
<section class="mb-3">
|
|
||||||
<div class="nb-panel p-3 flex items-center justify-between">
|
|
||||||
{% block chat_header_actions %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<div id="chat-scroll-container" class="overflow-auto hide-scrollbar">
|
|
||||||
{% block chat_content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function doScrollChatToBottom() {
|
|
||||||
const mainScroll = document.querySelector('main');
|
|
||||||
if (mainScroll) mainScroll.scrollTop = mainScroll.scrollHeight;
|
|
||||||
|
|
||||||
const chatScroll = document.getElementById('chat-scroll-container');
|
|
||||||
if (chatScroll) chatScroll.scrollTop = chatScroll.scrollHeight;
|
|
||||||
|
|
||||||
const chatContainer = document.getElementById('chat_container');
|
|
||||||
if (chatContainer) chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
||||||
|
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollChatToBottom() {
|
|
||||||
if (!window.location.pathname.startsWith('/chat')) return;
|
|
||||||
requestAnimationFrame(doScrollChatToBottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.scrollChatToBottom = scrollChatToBottom;
|
|
||||||
|
|
||||||
// Delay initial scroll to avoid interfering with view transition
|
|
||||||
document.addEventListener('DOMContentLoaded', () => setTimeout(scrollChatToBottom, 350));
|
|
||||||
|
|
||||||
function handleChatSwap(e) {
|
|
||||||
if (!window.location.pathname.startsWith('/chat')) return;
|
|
||||||
// Full page swap: delay for view transition; partial swap: immediate
|
|
||||||
if (e.detail && e.detail.target && e.detail.target.tagName === 'BODY') {
|
|
||||||
setTimeout(scrollChatToBottom, 350);
|
|
||||||
} else {
|
|
||||||
scrollChatToBottom();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanupChatListeners(e) {
|
|
||||||
if (e.detail && e.detail.target && e.detail.target.tagName === 'BODY') {
|
|
||||||
document.body.removeEventListener('htmx:afterSwap', window._chatEventHandlers.afterSwap);
|
|
||||||
document.body.removeEventListener('htmx:afterSettle', window._chatEventHandlers.afterSettle);
|
|
||||||
document.body.removeEventListener('htmx:beforeSwap', window._chatEventHandlers.beforeSwap);
|
|
||||||
delete window._chatEventHandlers;
|
|
||||||
window._chatListenersAttached = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window._chatEventHandlers = {
|
|
||||||
afterSwap: handleChatSwap,
|
|
||||||
afterSettle: handleChatSwap,
|
|
||||||
beforeSwap: cleanupChatListeners
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!window._chatListenersAttached) {
|
|
||||||
document.body.addEventListener('htmx:afterSwap', window._chatEventHandlers.afterSwap);
|
|
||||||
document.body.addEventListener('htmx:afterSettle', window._chatEventHandlers.afterSettle);
|
|
||||||
document.body.addEventListener('htmx:beforeSwap', window._chatEventHandlers.beforeSwap);
|
|
||||||
window._chatListenersAttached = true;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,14 +1,81 @@
|
|||||||
{% extends "chat/_layout.html" %}
|
{% extends 'body_base.html' %}
|
||||||
|
|
||||||
{% block chat_header_actions %}
|
{% block title %}Minne - Chat{% endblock %}
|
||||||
<h1 class="text-xl font-extrabold tracking-tight">Chat</h1>
|
|
||||||
<div class="text-xs opacity-70">Converse with your knowledge</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block chat_content %}
|
{% block main %}
|
||||||
{% include "chat/history.html" %}
|
<div class="flex grow relative justify-center mt-2 sm:mt-4">
|
||||||
|
<div class="container">
|
||||||
|
<section class="mb-3">
|
||||||
|
<div class="nb-panel p-3 flex items-center justify-between">
|
||||||
|
<h1 class="text-xl font-extrabold tracking-tight">Chat</h1>
|
||||||
|
<div class="text-xs opacity-70">Converse with your knowledge</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div id="chat-scroll-container" class="overflow-auto hide-scrollbar">
|
||||||
|
{% include "chat/history.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function doScrollChatToBottom() {
|
||||||
|
const mainScroll = document.querySelector('main');
|
||||||
|
if (mainScroll) mainScroll.scrollTop = mainScroll.scrollHeight;
|
||||||
|
|
||||||
|
const chatScroll = document.getElementById('chat-scroll-container');
|
||||||
|
if (chatScroll) chatScroll.scrollTop = chatScroll.scrollHeight;
|
||||||
|
|
||||||
|
const chatContainer = document.getElementById('chat_container');
|
||||||
|
if (chatContainer) chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||||
|
|
||||||
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollChatToBottom() {
|
||||||
|
if (!window.location.pathname.startsWith('/chat')) return;
|
||||||
|
requestAnimationFrame(doScrollChatToBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.scrollChatToBottom = scrollChatToBottom;
|
||||||
|
|
||||||
|
// Delay initial scroll to avoid interfering with view transition
|
||||||
|
document.addEventListener('DOMContentLoaded', () => setTimeout(scrollChatToBottom, 350));
|
||||||
|
|
||||||
|
function handleChatSwap(e) {
|
||||||
|
if (!window.location.pathname.startsWith('/chat')) return;
|
||||||
|
// Full page swap: delay for view transition; partial swap: immediate
|
||||||
|
if (e.detail && e.detail.target && e.detail.target.tagName === 'BODY') {
|
||||||
|
setTimeout(scrollChatToBottom, 350);
|
||||||
|
} else {
|
||||||
|
scrollChatToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupChatListeners(e) {
|
||||||
|
if (e.detail && e.detail.target && e.detail.target.tagName === 'BODY') {
|
||||||
|
document.body.removeEventListener('htmx:afterSwap', window._chatEventHandlers.afterSwap);
|
||||||
|
document.body.removeEventListener('htmx:afterSettle', window._chatEventHandlers.afterSettle);
|
||||||
|
document.body.removeEventListener('htmx:beforeSwap', window._chatEventHandlers.beforeSwap);
|
||||||
|
delete window._chatEventHandlers;
|
||||||
|
window._chatListenersAttached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window._chatEventHandlers = {
|
||||||
|
afterSwap: handleChatSwap,
|
||||||
|
afterSettle: handleChatSwap,
|
||||||
|
beforeSwap: cleanupChatListeners
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window._chatListenersAttached) {
|
||||||
|
document.body.addEventListener('htmx:afterSwap', window._chatEventHandlers.afterSwap);
|
||||||
|
document.body.addEventListener('htmx:afterSettle', window._chatEventHandlers.afterSettle);
|
||||||
|
document.body.addEventListener('htmx:beforeSwap', window._chatEventHandlers.beforeSwap);
|
||||||
|
window._chatListenersAttached = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block overlay %}
|
{% block overlay %}
|
||||||
{% include "chat/new_message_form.html" %}
|
{% include "chat/new_message_form.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
{% macro icon(name) %}
|
|
||||||
{% if name == "home" %}
|
|
||||||
{% include "icons/home_icon.html" %}
|
|
||||||
{% elif name == "book" %}
|
|
||||||
{% include "icons/book_icon.html" %}
|
|
||||||
{% elif name == "document" %}
|
|
||||||
{% include "icons/document_icon.html" %}
|
|
||||||
{% elif name == "chat" %}
|
|
||||||
{% include "icons/chat_icon.html" %}
|
|
||||||
{% elif name == "search" %}
|
|
||||||
{% include "icons/search_icon.html" %}
|
|
||||||
{% elif name == "scratchpad" %}
|
|
||||||
{% include "icons/scratchpad_icon.html" %}
|
|
||||||
{% endif %}
|
|
||||||
{% endmacro %}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<nav class="sticky top-0 z-10 nb-panel nb-panel-canvas border-t-0">
|
|
||||||
<div class="container mx-auto navbar">
|
|
||||||
<div class="mr-2 flex-1">
|
|
||||||
{% block navbar_search %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
<div class="flex-none">
|
|
||||||
<ul class="menu menu-horizontal px-2 gap-2 items-center">
|
|
||||||
{% block navbar_actions %}
|
|
||||||
{% endblock %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<div class="drawer-side z-20">
|
|
||||||
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
|
||||||
|
|
||||||
<ul class="menu p-0 w-72 h-full nb-canvas text-base-content flex flex-col border-r-2 border-neutral">
|
|
||||||
<!-- === TOP FIXED SECTION === -->
|
|
||||||
<div class="px-2 mt-4 space-y-3">
|
|
||||||
{% block sidebar_nav_items %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- === MIDDLE SCROLLABLE SECTION === -->
|
|
||||||
<span class="px-4 py-2 nb-label">Recent Chats</span>
|
|
||||||
<div class="flex-1 overflow-y-auto space-y-1 custom-scrollbar">
|
|
||||||
{% if conversation_archive is defined and conversation_archive %}
|
|
||||||
{% for conversation in conversation_archive %}
|
|
||||||
<li id="conversation-{{ conversation.id }}">
|
|
||||||
{% if edit_conversation_id == conversation.id %}
|
|
||||||
<!-- Edit mode -->
|
|
||||||
<form hx-patch="/chat/{{ conversation.id }}/title" hx-target=".drawer-side" hx-swap="outerHTML"
|
|
||||||
class="flex items-center gap-1 px-2 py-2 max-w-72 relative">
|
|
||||||
<input type="text" name="title" value="{{ conversation.title }}" class="nb-input nb-input-sm max-w-52" />
|
|
||||||
<div class="flex gap-0.5 absolute right-2">
|
|
||||||
<button type="submit" class="btn btn-ghost btn-xs !p-0">{% include "icons/check_icon.html" %}</button>
|
|
||||||
<button type="button" hx-get="/chat/sidebar" hx-target=".drawer-side" hx-swap="outerHTML"
|
|
||||||
class="btn btn-ghost btn-xs !p-0">
|
|
||||||
{% include "icons/x_icon.html" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<!-- View mode -->
|
|
||||||
<div class="flex w-full pl-4 pr-2 py-2">
|
|
||||||
<a hx-boost="true" href="/chat/{{ conversation.id }}" class="flex-grow text-sm truncate">
|
|
||||||
<span>{{ conversation.title }}</span>
|
|
||||||
</a>
|
|
||||||
<div class="flex items-center gap-0.5 ml-2">
|
|
||||||
<button hx-get="/chat/{{ conversation.id }}/title" hx-target=".drawer-side" hx-swap="outerHTML"
|
|
||||||
class="btn btn-ghost btn-xs">
|
|
||||||
{% include "icons/edit_icon.html" %}
|
|
||||||
</button>
|
|
||||||
<button hx-delete="/chat/{{ conversation.id }}" hx-target=".drawer-side" hx-swap="outerHTML"
|
|
||||||
hx-confirm="Are you sure you want to delete this chat?" class="btn btn-ghost btn-xs">
|
|
||||||
{% include "icons/delete_icon.html" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- === BOTTOM FIXED SECTION === -->
|
|
||||||
<div class="px-2 pb-4 space-y-3">
|
|
||||||
{% block sidebar_bottom_actions %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{% extends 'body_base.html' %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Content{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<main id="main_section" class="flex justify-center grow mt-2 sm:mt-4 gap-6 mb-10 w-full">
|
|
||||||
<div class="container">
|
|
||||||
<div class="nb-panel p-3 mb-4 flex items-center justify-between">
|
|
||||||
{% block content_header %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% block content_list %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,23 +1,29 @@
|
|||||||
{% extends 'content/_layout.html' %}
|
{% extends 'body_base.html' %}
|
||||||
|
|
||||||
{% block content_header %}
|
{% block title %}Minne - Content{% endblock %}
|
||||||
<h2 class="text-xl font-extrabold tracking-tight">Content</h2>
|
|
||||||
<form hx-get="/content" hx-target="#main_section" hx-swap="outerHTML" hx-push-url="true"
|
{% block main %}
|
||||||
class="flex items-center gap-2 mt-2 sm:mt-0">
|
<main id="main_section" class="flex justify-center grow mt-2 sm:mt-4 gap-6 mb-10 w-full">
|
||||||
<input type="hidden" name="page" value="1" />
|
<div class="container">
|
||||||
<div>
|
<div class="nb-panel p-3 mb-4 flex items-center justify-between">
|
||||||
<select name="category" class="nb-select">
|
<h2 class="text-xl font-extrabold tracking-tight">Content</h2>
|
||||||
<option value="">All Categories</option>
|
<form hx-get="/content" hx-target="#main_section" hx-swap="outerHTML" hx-push-url="true"
|
||||||
{% for category in categories %}
|
class="flex items-center gap-2 mt-2 sm:mt-0">
|
||||||
<option value="{{ category }}" {% if selected_category==category %}selected{% endif %}>{{ category }}
|
<input type="hidden" name="page" value="1" />
|
||||||
</option>
|
<div>
|
||||||
{% endfor %}
|
<select name="category" class="nb-select">
|
||||||
</select>
|
<option value="">All Categories</option>
|
||||||
|
{% for category in categories %}
|
||||||
|
<option value="{{ category }}" {% if selected_category==category %}selected{% endif %}>{{ category }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="nb-btn btn-sm">Filter</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="nb-btn btn-sm">Filter</button>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content_list %}
|
{% include "content/content_list.html" %}
|
||||||
{% include "content/content_list.html" %}
|
</div>
|
||||||
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
{% extends "dashboard/_layout.html" %}
|
|
||||||
|
|
||||||
{% block dashboard_header %}
|
|
||||||
<h1 class="text-xl font-extrabold tracking-tight">Dashboard</h1>
|
|
||||||
<button class="nb-btn nb-cta" hx-get="/ingress-form" hx-target="#modal" hx-swap="innerHTML">
|
|
||||||
{% include "icons/send_icon.html" %}
|
|
||||||
<span class="ml-2">Add Content</span>
|
|
||||||
</button>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block dashboard_widgets %}
|
|
||||||
{% include "dashboard/statistics.html" %}
|
|
||||||
{% include "dashboard/recent_content.html" %}
|
|
||||||
{% include "dashboard/active_jobs.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{% extends "body_base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Dashboard{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<div class="flex justify-center grow mt-2 sm:mt-4 pb-4 w-full">
|
|
||||||
<div class="container">
|
|
||||||
<section class="mb-4">
|
|
||||||
{% block dashboard_alerts %}
|
|
||||||
{% endblock %}
|
|
||||||
<div class="nb-panel p-3 flex items-center justify-between">
|
|
||||||
{% block dashboard_header %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{% block dashboard_widgets %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1 +1,26 @@
|
|||||||
{% extends "dashboard/_base.html" %}
|
{% extends "body_base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Minne - Dashboard{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<div class="flex justify-center grow mt-2 sm:mt-4 pb-4 w-full">
|
||||||
|
<div class="container">
|
||||||
|
<section class="mb-4">
|
||||||
|
<div class="nb-panel p-3 flex items-center justify-between">
|
||||||
|
<h1 class="text-xl font-extrabold tracking-tight">Dashboard</h1>
|
||||||
|
<button class="nb-btn nb-cta" hx-get="/ingress-form" hx-target="#modal" hx-swap="innerHTML">
|
||||||
|
{% include "icons/send_icon.html" %}
|
||||||
|
<span class="ml-2">Add Content</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% include "dashboard/statistics.html" %}
|
||||||
|
|
||||||
|
{% include "dashboard/recent_content.html" %}
|
||||||
|
|
||||||
|
{% include "dashboard/active_jobs.html" %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{% extends 'body_base.html' %}
|
|
||||||
{% block main %}
|
|
||||||
<main class="container justify-center flex-grow flex mx-auto mt-4">
|
|
||||||
<div class="flex flex-col space-y-4 text-center justify-center">
|
|
||||||
{% block error_content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
{% extends "errors/_layout.html" %}
|
{% extends 'body_base.html' %}
|
||||||
|
{% block main %}
|
||||||
{% block error_content %}
|
<main class="container justify-center flex-grow flex mx-auto mt-4">
|
||||||
<h1 class="text-2xl font-bold text-error">
|
<div class="flex flex-col space-y-4 text-center justify-center">
|
||||||
{{ status_code }}
|
<h1 class="text-2xl font-bold text-error">
|
||||||
</h1>
|
{{ status_code }}
|
||||||
<p class="text-2xl my-4">{{ title }}</p>
|
</h1>
|
||||||
<p class="text-base-content/60">{{ description }}</p>
|
<p class="text-2xl my-4">{{ title }}</p>
|
||||||
<a href="/" class="btn btn-primary mt-8">Go Home</a>
|
<p class="text-base-content/60">{{ description }}</p>
|
||||||
{% endblock %}
|
<a href="/" class="btn btn-primary mt-8">Go Home</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{% extends 'body_base.html' %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Knowledge{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<div id="knowledge_pane" class="flex justify-center grow mt-2 sm:mt-4 gap-6">
|
|
||||||
<div class="container">
|
|
||||||
<div class="nb-panel p-3 mb-4 space-y-3 sm:space-y-0 sm:flex sm:flex-row sm:justify-between sm:items-center">
|
|
||||||
{% block knowledge_header %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% block knowledge_content %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,46 +1,52 @@
|
|||||||
{% extends 'knowledge/_layout.html' %}
|
{% extends 'body_base.html' %}
|
||||||
|
|
||||||
{% block knowledge_header %}
|
{% block title %}Minne - Knowledge{% endblock %}
|
||||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
|
||||||
<h2 class="text-xl font-extrabold tracking-tight">Knowledge Entities</h2>
|
|
||||||
<button type="button" class="nb-btn nb-cta btn-sm mr-2" hx-get="/knowledge-entity/new" hx-target="#modal"
|
|
||||||
hx-swap="innerHTML">
|
|
||||||
New Entity
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<form hx-get="/knowledge" hx-target="#knowledge_pane" hx-push-url="true" hx-swap="outerHTML"
|
|
||||||
class="flex items-center gap-2 mt-2 sm:mt-0">
|
|
||||||
<input type="hidden" name="page" value="1" />
|
|
||||||
<div>
|
|
||||||
<select name="entity_type" class="nb-select">
|
|
||||||
<option value="">All Types</option>
|
|
||||||
{% for type in entity_types %}
|
|
||||||
<option value="{{ type }}" {% if selected_entity_type==type %}selected{% endif %}>{{ type }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<select name="content_category" class="nb-select">
|
|
||||||
<option value="">All Categories</option>
|
|
||||||
{% for category in content_categories %}
|
|
||||||
<option value="{{ category }}" {% if selected_content_category==category %}selected{% endif %}>{{ category
|
|
||||||
}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="nb-btn btn-sm">Filter</button>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block knowledge_content %}
|
{% block main %}
|
||||||
<h2 class="text-2xl font-bold mb-2 mt-10 ">Graph</h2>
|
<div id="knowledge_pane" class="flex justify-center grow mt-2 sm:mt-4 gap-6">
|
||||||
<div class="nb-card mt-4 p-2">
|
<div class="container">
|
||||||
<div id="knowledge-graph" class="w-full" style="height: 640px;"
|
<div class="nb-panel p-3 mb-4 space-y-3 sm:space-y-0 sm:flex sm:flex-row sm:justify-between sm:items-center">
|
||||||
data-entity-type="{{ selected_entity_type | default(value='') }}"
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
||||||
data-content-category="{{ selected_content_category | default(value='') }}">
|
<h2 class="text-xl font-extrabold tracking-tight">Knowledge Entities</h2>
|
||||||
|
<button type="button" class="nb-btn nb-cta btn-sm mr-2" hx-get="/knowledge-entity/new" hx-target="#modal"
|
||||||
|
hx-swap="innerHTML">
|
||||||
|
New Entity
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form hx-get="/knowledge" hx-target="#knowledge_pane" hx-push-url="true" hx-swap="outerHTML"
|
||||||
|
class="flex items-center gap-2 mt-2 sm:mt-0">
|
||||||
|
<input type="hidden" name="page" value="1" />
|
||||||
|
<div>
|
||||||
|
<select name="entity_type" class="nb-select">
|
||||||
|
<option value="">All Types</option>
|
||||||
|
{% for type in entity_types %}
|
||||||
|
<option value="{{ type }}" {% if selected_entity_type==type %}selected{% endif %}>{{ type }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select name="content_category" class="nb-select">
|
||||||
|
<option value="">All Categories</option>
|
||||||
|
{% for category in content_categories %}
|
||||||
|
<option value="{{ category }}" {% if selected_content_category==category %}selected{% endif %}>{{ category
|
||||||
|
}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="nb-btn btn-sm">Filter</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2 class="text-2xl font-bold mb-2 mt-10 ">Graph</h2>
|
||||||
|
<div class="nb-card mt-4 p-2">
|
||||||
|
<div id="knowledge-graph" class="w-full" style="height: 640px;"
|
||||||
|
data-entity-type="{{ selected_entity_type | default(value='') }}"
|
||||||
|
data-content-category="{{ selected_content_category | default(value='') }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include "knowledge/entity_list.html" %}
|
||||||
|
<h2 class="text-2xl font-bold mb-2 mt-2">Relationships</h2>
|
||||||
|
{% include "knowledge/relationship_table.html" %}
|
||||||
</div>
|
</div>
|
||||||
{% include "knowledge/entity_list.html" %}
|
</div>
|
||||||
<h2 class="text-2xl font-bold mb-2 mt-2">Relationships</h2>
|
{% endblock %}
|
||||||
{% include "knowledge/relationship_table.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
{% extends "components/_navbar_layout.html" %}
|
<nav class="sticky top-0 z-10 nb-panel nb-panel-canvas border-t-0" style="view-transition-name: navbar; contain: layout;">
|
||||||
|
<div class="container mx-auto navbar">
|
||||||
{% block navbar_search %}
|
<div class="mr-2 flex-1">
|
||||||
{% include "searchbar.html" %}
|
{% include "searchbar.html" %}
|
||||||
{% endblock %}
|
</div>
|
||||||
|
<div class="flex-none">
|
||||||
{% block navbar_actions %}
|
<ul class="menu menu-horizontal px-2 gap-2 items-center">
|
||||||
<label for="my-drawer" aria-label="open sidebar" class="hover:cursor-pointer lg:hidden">
|
<label for="my-drawer" aria-label="open sidebar" class="hover:cursor-pointer lg:hidden">
|
||||||
{% include "icons/hamburger_icon.html" %}
|
{% include "icons/hamburger_icon.html" %}
|
||||||
</label>
|
</label>
|
||||||
{% include "theme_toggle.html" %}
|
{% include "theme_toggle.html" %}
|
||||||
{% endblock %}
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
{% extends 'body_base.html' %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Scratchpad{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<main id="main_section" class="flex justify-center grow mt-2 sm:mt-4 gap-6 mb-10 w-full">
|
|
||||||
<div class="container">
|
|
||||||
{% block scratchpad_header %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scratchpad_content %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scratchpad_archived %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{% if new_scratchpad %}
|
|
||||||
<div hx-swap-oob="innerHTML:#modal">
|
|
||||||
<div hx-get="/scratchpad/{{ new_scratchpad.id }}/modal" hx-trigger="load" hx-target="#modal" hx-swap="innerHTML"></div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,101 +1,113 @@
|
|||||||
{% extends 'scratchpad/_layout.html' %}
|
{% extends 'body_base.html' %}
|
||||||
|
|
||||||
{% block scratchpad_header %}
|
{% block title %}Minne - Scratchpad{% endblock %}
|
||||||
<div class="nb-panel p-3 mb-4 flex items-center justify-between">
|
|
||||||
<h2 class="text-xl font-extrabold tracking-tight">Scratchpads</h2>
|
|
||||||
<form hx-post="/scratchpad" hx-target="#main_section" hx-swap="outerHTML" class="flex gap-2">
|
|
||||||
<input type="text" name="title" placeholder="Enter scratchpad title..." class="nb-input nb-input-sm" required>
|
|
||||||
<button type="submit" class="nb-btn nb-cta">
|
|
||||||
{% include "icons/scratchpad_icon.html" %} Create
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scratchpad_content %}
|
{% block main %}
|
||||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<main id="main_section" class="flex justify-center grow mt-2 sm:mt-4 gap-6 mb-10 w-full">
|
||||||
{% for scratchpad in scratchpads %}
|
<div class="container">
|
||||||
<div class="nb-card p-4 hover:nb-shadow-hover transition-all">
|
{% block header %}
|
||||||
<div class="flex justify-between items-start mb-2">
|
<div class="nb-panel p-3 mb-4 flex items-center justify-between">
|
||||||
<h3 class="font-semibold text-lg truncate flex-1">{{ scratchpad.title }}</h3>
|
<h2 class="text-xl font-extrabold tracking-tight">Scratchpads</h2>
|
||||||
<div class="flex gap-1 ml-2">
|
<form hx-post="/scratchpad" hx-target="#main_section" hx-swap="outerHTML" class="flex gap-2">
|
||||||
<button hx-get="/scratchpad/{{ scratchpad.id }}/modal" hx-target="#modal" hx-swap="innerHTML"
|
<input type="text" name="title" placeholder="Enter scratchpad title..." class="nb-input nb-input-sm" required>
|
||||||
class="nb-btn nb-btn-sm btn-ghost" title="Edit scratchpad">
|
|
||||||
{% include "icons/pencil_icon.html" %}
|
|
||||||
</button>
|
|
||||||
<form hx-post="/scratchpad/{{ scratchpad.id }}/archive" hx-target="#main_section" hx-swap="outerHTML"
|
|
||||||
class="inline-flex">
|
|
||||||
<button type="submit" class="nb-btn nb-btn-sm btn-ghost text-warning" title="Archive scratchpad">
|
|
||||||
{% include "icons/delete_icon.html" %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-sm text-base-content/70 mb-2">
|
|
||||||
{{ scratchpad.content[:100] }}{% if scratchpad.content|length > 100 %}...{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-base-content/50">
|
|
||||||
Last saved: {{ scratchpad.last_saved_at | datetimeformat(format="short", tz=user.timezone) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="col-span-full nb-panel p-8 text-center">
|
|
||||||
<h3 class="text-lg font-semibold mt-2 mb-2">No scratchpads yet</h3>
|
|
||||||
<p class="text-base-content/70 mb-4">Create your first scratchpad to start jotting down ideas</p>
|
|
||||||
<form hx-post="/scratchpad" hx-target="#main_section" hx-swap="outerHTML"
|
|
||||||
class="inline-flex gap-2">
|
|
||||||
<input type="text" name="title" placeholder="My first scratchpad..." class="nb-input" required>
|
|
||||||
<button type="submit" class="nb-btn nb-cta">
|
<button type="submit" class="nb-btn nb-cta">
|
||||||
{% include "icons/scratchpad_icon.html" %} Create Scratchpad
|
{% include "icons/scratchpad_icon.html" %} Create
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endblock %}
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scratchpad_archived %}
|
{% block content %}
|
||||||
{% if archived_scratchpads %}
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
<div class="mt-6">
|
{% for scratchpad in scratchpads %}
|
||||||
<details class="nb-panel p-3 space-y-4">
|
<div class="nb-card p-4 hover:nb-shadow-hover transition-all">
|
||||||
<summary class="flex items-center justify-between gap-2 text-sm font-semibold cursor-pointer">
|
<div class="flex justify-between items-start mb-2">
|
||||||
<span>Archived Scratchpads</span>
|
<h3 class="font-semibold text-lg truncate flex-1">{{ scratchpad.title }}</h3>
|
||||||
<span class="nb-badge">{{ archived_scratchpads|length }}</span>
|
<div class="flex gap-1 ml-2">
|
||||||
</summary>
|
<button hx-get="/scratchpad/{{ scratchpad.id }}/modal" hx-target="#modal" hx-swap="innerHTML"
|
||||||
|
class="nb-btn nb-btn-sm btn-ghost" title="Edit scratchpad">
|
||||||
<div class="text-sm text-base-content/60">Archived scratchpads were ingested into your knowledge base. You can
|
{% include "icons/pencil_icon.html" %}
|
||||||
restore them if you want to keep editing.</div>
|
</button>
|
||||||
|
<form hx-post="/scratchpad/{{ scratchpad.id }}/archive" hx-target="#main_section" hx-swap="outerHTML"
|
||||||
<div class="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
class="inline-flex">
|
||||||
{% for scratchpad in archived_scratchpads %}
|
<button type="submit" class="nb-btn nb-btn-sm btn-ghost text-warning" title="Archive scratchpad">
|
||||||
<div class="nb-card p-3 space-y-3">
|
{% include "icons/delete_icon.html" %}
|
||||||
<div class="flex items-start justify-between gap-3">
|
</button>
|
||||||
<div class="flex-1 min-w-0">
|
</form>
|
||||||
<h4 class="font-semibold text-base truncate" title="{{ scratchpad.title }}">{{ scratchpad.title }}</h4>
|
|
||||||
<div class="text-xs text-base-content/50">Archived {{ scratchpad.archived_at | datetimeformat(format="short", tz=user.timezone) }}</div>
|
|
||||||
{% if scratchpad.ingested_at %}
|
|
||||||
<div class="text-xs text-base-content/40">Ingestion started {{ scratchpad.ingested_at | datetimeformat(format="short", tz=user.timezone) }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 flex-shrink-0 flex-wrap justify-end">
|
|
||||||
<form hx-post="/scratchpad/{{ scratchpad.id }}/restore" hx-target="#main_section" hx-swap="outerHTML"
|
|
||||||
class="inline-flex">
|
|
||||||
<button type="submit" class="nb-btn nb-btn-sm">
|
|
||||||
Restore
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form hx-delete="/scratchpad/{{ scratchpad.id }}" hx-target="#main_section" hx-swap="outerHTML"
|
|
||||||
hx-confirm="Permanently delete this scratchpad?" class="inline-flex">
|
|
||||||
<button type="submit" class="nb-btn nb-btn-sm btn-ghost text-error" title="Delete permanently">
|
|
||||||
{% include "icons/delete_icon.html" %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
<div class="text-sm text-base-content/70 mb-2">
|
||||||
|
{{ scratchpad.content[:100] }}{% if scratchpad.content|length > 100 %}...{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-base-content/50">
|
||||||
|
Last saved: {{ scratchpad.last_saved_at | datetimeformat(format="short", tz=user.timezone) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
{% else %}
|
||||||
|
<div class="col-span-full nb-panel p-8 text-center">
|
||||||
|
<h3 class="text-lg font-semibold mt-2 mb-2">No scratchpads yet</h3>
|
||||||
|
<p class="text-base-content/70 mb-4">Create your first scratchpad to start jotting down ideas</p>
|
||||||
|
<form hx-post="/scratchpad" hx-target="#main_section" hx-swap="outerHTML"
|
||||||
|
class="inline-flex gap-2">
|
||||||
|
<input type="text" name="title" placeholder="My first scratchpad..." class="nb-input" required>
|
||||||
|
<button type="submit" class="nb-btn nb-cta">
|
||||||
|
{% include "icons/scratchpad_icon.html" %} Create Scratchpad
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% if archived_scratchpads %}
|
||||||
|
<div class="mt-6">
|
||||||
|
<details class="nb-panel p-3 space-y-4">
|
||||||
|
<summary class="flex items-center justify-between gap-2 text-sm font-semibold cursor-pointer">
|
||||||
|
<span>Archived Scratchpads</span>
|
||||||
|
<span class="nb-badge">{{ archived_scratchpads|length }}</span>
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
<div class="text-sm text-base-content/60">Archived scratchpads were ingested into your knowledge base. You can
|
||||||
|
restore them if you want to keep editing.</div>
|
||||||
|
|
||||||
|
<div class="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{% for scratchpad in archived_scratchpads %}
|
||||||
|
<div class="nb-card p-3 space-y-3">
|
||||||
|
<div class="flex items-start justify-between gap-3">
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<h4 class="font-semibold text-base truncate" title="{{ scratchpad.title }}">{{ scratchpad.title }}</h4>
|
||||||
|
<div class="text-xs text-base-content/50">Archived {{ scratchpad.archived_at | datetimeformat(format="short", tz=user.timezone) }}</div>
|
||||||
|
{% if scratchpad.ingested_at %}
|
||||||
|
<div class="text-xs text-base-content/40">Ingestion started {{ scratchpad.ingested_at | datetimeformat(format="short", tz=user.timezone) }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 flex-shrink-0 flex-wrap justify-end">
|
||||||
|
<form hx-post="/scratchpad/{{ scratchpad.id }}/restore" hx-target="#main_section" hx-swap="outerHTML"
|
||||||
|
class="inline-flex">
|
||||||
|
<button type="submit" class="nb-btn nb-btn-sm">
|
||||||
|
Restore
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<form hx-delete="/scratchpad/{{ scratchpad.id }}" hx-target="#main_section" hx-swap="outerHTML"
|
||||||
|
hx-confirm="Permanently delete this scratchpad?" class="inline-flex">
|
||||||
|
<button type="submit" class="nb-btn nb-btn-sm btn-ghost text-error" title="Delete permanently">
|
||||||
|
{% include "icons/delete_icon.html" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
</main>
|
||||||
|
|
||||||
|
{% if new_scratchpad %}
|
||||||
|
<div hx-swap-oob="innerHTML:#modal">
|
||||||
|
<div hx-get="/scratchpad/{{ new_scratchpad.id }}/modal" hx-trigger="load" hx-target="#modal" hx-swap="innerHTML"></div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
{% extends "body_base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Minne - Search{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
<div class="flex justify-center grow mt-2 sm:mt-4">
|
|
||||||
<div class="container">
|
|
||||||
<section class="mb-4">
|
|
||||||
<div class="nb-panel p-3 flex items-center justify-between">
|
|
||||||
{% block search_header %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% block search_results %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
{% extends "search/_layout.html" %}
|
{% extends "body_base.html" %}
|
||||||
|
|
||||||
{% block search_header %}
|
{% block title %}Minne - Search{% endblock %}
|
||||||
<h1 class="text-xl font-extrabold tracking-tight">Search</h1>
|
|
||||||
<div class="text-xs opacity-70">Find documents, entities, and snippets</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block search_results %}
|
{% block main %}
|
||||||
{% include "search/response.html" %}
|
<div class="flex justify-center grow mt-2 sm:mt-4">
|
||||||
|
<div class="container">
|
||||||
|
<section class="mb-4">
|
||||||
|
<div class="nb-panel p-3 flex items-center justify-between">
|
||||||
|
<h1 class="text-xl font-extrabold tracking-tight">Search</h1>
|
||||||
|
<div class="text-xs opacity-70">Find documents, entities, and snippets</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% include "search/response.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,52 +1,119 @@
|
|||||||
{% extends "components/_sidebar_layout.html" %}
|
{% macro icon(name) %}
|
||||||
{% from "components/_icon_macro.html" import icon %}
|
{% if name == "home" %}
|
||||||
|
{% include "icons/home_icon.html" %}
|
||||||
|
{% elif name == "book" %}
|
||||||
|
{% include "icons/book_icon.html" %}
|
||||||
|
{% elif name == "document" %}
|
||||||
|
{% include "icons/document_icon.html" %}
|
||||||
|
{% elif name == "chat" %}
|
||||||
|
{% include "icons/chat_icon.html" %}
|
||||||
|
{% elif name == "search" %}
|
||||||
|
{% include "icons/search_icon.html" %}
|
||||||
|
{% elif name == "scratchpad" %}
|
||||||
|
{% include "icons/scratchpad_icon.html" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block sidebar_nav_items %}
|
<div class="drawer-side z-20" style="view-transition-name: sidebar; contain: layout;">
|
||||||
{% for url, name, label in [
|
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
("/", "home", "Dashboard"),
|
|
||||||
("/knowledge", "book", "Knowledge"),
|
|
||||||
("/content", "document", "Content"),
|
|
||||||
("/chat", "chat", "Chat"),
|
|
||||||
("/search", "search", "Search"),
|
|
||||||
("/scratchpad", "scratchpad", "Scratchpad")
|
|
||||||
] %}
|
|
||||||
<li>
|
|
||||||
<a hx-boost="true" href="{{ url }}" class="nb-btn w-full justify-start gap-3 bg-base-100 hover:bg-base-200">
|
|
||||||
{{ icon(name) }}
|
|
||||||
<span class="uppercase tracking-wide">{{ label }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
<li>
|
|
||||||
<button class="nb-btn nb-cta w-full flex items-center gap-3 justify-start mt-2" hx-get="/ingress-form"
|
|
||||||
hx-target="#modal" hx-swap="innerHTML">{% include "icons/send_icon.html" %} Add
|
|
||||||
Content</button>
|
|
||||||
</li>
|
|
||||||
<div class="u-hairline mt-4"></div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block sidebar_bottom_actions %}
|
<ul class="menu p-0 w-72 h-full nb-canvas text-base-content flex flex-col border-r-2 border-neutral">
|
||||||
<li>
|
<!-- <a class="px-4 py-4 text-2xl font-extrabold tracking-tight text-primary border-b-2 border-neutral bg-base-100 nb-shadow" -->
|
||||||
<a hx-boost="true" href="/account"
|
<!-- href="/" hx-boost="true">Minne</a> -->
|
||||||
class="nb-btn w-full justify-start items-center gap-3 bg-base-100 hover:bg-base-200">
|
|
||||||
{% include "icons/user_icon.html" %}
|
<!-- === TOP FIXED SECTION === -->
|
||||||
<span class="uppercase tracking-wide">Account</span>
|
<div class="px-2 mt-4 space-y-3">
|
||||||
</a>
|
{% for url, name, label in [
|
||||||
</li>
|
("/", "home", "Dashboard"),
|
||||||
{% if user.admin %}
|
("/knowledge", "book", "Knowledge"),
|
||||||
<li>
|
("/content", "document", "Content"),
|
||||||
<a hx-boost="true" href="/admin"
|
("/chat", "chat", "Chat"),
|
||||||
class="nb-btn w-full justify-start items-center gap-3 bg-base-100 hover:bg-base-200">
|
("/search", "search", "Search"),
|
||||||
{% include "icons/wrench_screwdriver_icon.html" %}
|
("/scratchpad", "scratchpad", "Scratchpad")
|
||||||
<span class="uppercase tracking-wide">Admin</span>
|
] %}
|
||||||
</a>
|
<li>
|
||||||
</li>
|
<a hx-boost="true" href="{{ url }}" class="nb-btn w-full justify-start gap-3 bg-base-100 hover:bg-base-200">
|
||||||
{% endif %}
|
{{ icon(name) }}
|
||||||
<li>
|
<span class="uppercase tracking-wide">{{ label }}</span>
|
||||||
<a hx-boost="true" href="/signout"
|
</a>
|
||||||
class="nb-btn w-full justify-start items-center gap-3 bg-base-100 hover:bg-base-200 border-error text-error">
|
</li>
|
||||||
{% include "icons/logout_icon.html" %}
|
{% endfor %}
|
||||||
<span class="uppercase tracking-wide">Logout</span>
|
<li>
|
||||||
</a>
|
<button class="nb-btn nb-cta w-full flex items-center gap-3 justify-start mt-2" hx-get="/ingress-form"
|
||||||
</li>
|
hx-target="#modal" hx-swap="innerHTML">{% include "icons/send_icon.html" %} Add
|
||||||
{% endblock %}
|
Content</button>
|
||||||
|
</li>
|
||||||
|
<div class="u-hairline mt-4"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- === MIDDLE SCROLLABLE SECTION === -->
|
||||||
|
<span class="px-4 py-2 nb-label">Recent Chats</span>
|
||||||
|
<div class="flex-1 overflow-y-auto space-y-1 custom-scrollbar">
|
||||||
|
{% if conversation_archive is defined and conversation_archive %}
|
||||||
|
{% for conversation in conversation_archive %}
|
||||||
|
<li id="conversation-{{ conversation.id }}">
|
||||||
|
{% if edit_conversation_id == conversation.id %}
|
||||||
|
<!-- Edit mode -->
|
||||||
|
<form hx-patch="/chat/{{ conversation.id }}/title" hx-target=".drawer-side" hx-swap="outerHTML"
|
||||||
|
class="flex items-center gap-1 px-2 py-2 max-w-72 relative">
|
||||||
|
<input type="text" name="title" value="{{ conversation.title }}" class="nb-input nb-input-sm max-w-52" />
|
||||||
|
<div class="flex gap-0.5 absolute right-2">
|
||||||
|
<button type="submit" class="btn btn-ghost btn-xs !p-0">{% include "icons/check_icon.html" %}</button>
|
||||||
|
<button type="button" hx-get="/chat/sidebar" hx-target=".drawer-side" hx-swap="outerHTML"
|
||||||
|
class="btn btn-ghost btn-xs !p-0">
|
||||||
|
{% include "icons/x_icon.html" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<!-- View mode -->
|
||||||
|
<div class="flex w-full pl-4 pr-2 py-2">
|
||||||
|
<a hx-boost="true" href="/chat/{{ conversation.id }}" class="flex-grow text-sm truncate">
|
||||||
|
<span>{{ conversation.title }}</span>
|
||||||
|
</a>
|
||||||
|
<div class="flex items-center gap-0.5 ml-2">
|
||||||
|
<button hx-get="/chat/{{ conversation.id }}/title" hx-target=".drawer-side" hx-swap="outerHTML"
|
||||||
|
class="btn btn-ghost btn-xs">
|
||||||
|
{% include "icons/edit_icon.html" %}
|
||||||
|
</button>
|
||||||
|
<button hx-delete="/chat/{{ conversation.id }}" hx-target=".drawer-side" hx-swap="outerHTML"
|
||||||
|
hx-confirm="Are you sure you want to delete this chat?" class="btn btn-ghost btn-xs">
|
||||||
|
{% include "icons/delete_icon.html" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- === BOTTOM FIXED SECTION === -->
|
||||||
|
<div class="px-2 pb-4 space-y-3">
|
||||||
|
<li>
|
||||||
|
<a hx-boost="true" href="/account"
|
||||||
|
class="nb-btn w-full justify-start items-center gap-3 bg-base-100 hover:bg-base-200">
|
||||||
|
{% include "icons/user_icon.html" %}
|
||||||
|
<span class="uppercase tracking-wide">Account</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if user.admin %}
|
||||||
|
<li>
|
||||||
|
<a hx-boost="true" href="/admin"
|
||||||
|
class="nb-btn w-full justify-start items-center gap-3 bg-base-100 hover:bg-base-200">
|
||||||
|
{% include "icons/wrench_screwdriver_icon.html" %}
|
||||||
|
<span class="uppercase tracking-wide">Admin</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a hx-boost="true" href="/signout"
|
||||||
|
class="nb-btn w-full justify-start items-center gap-3 bg-base-100 hover:bg-base-200 border-error text-error">
|
||||||
|
{% include "icons/logout_icon.html" %}
|
||||||
|
<span class="uppercase tracking-wide">Logout</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
config.clone(),
|
config.clone(),
|
||||||
reranker_pool.clone(),
|
reranker_pool.clone(),
|
||||||
embedding_provider.clone(),
|
embedding_provider.clone(),
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -285,7 +284,6 @@ mod tests {
|
|||||||
config.clone(),
|
config.clone(),
|
||||||
None,
|
None,
|
||||||
embedding_provider,
|
embedding_provider,
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("failed to build html state");
|
.expect("failed to build html state");
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
config.clone(),
|
config.clone(),
|
||||||
reranker_pool,
|
reranker_pool,
|
||||||
embedding_provider,
|
embedding_provider,
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user