-
+
+
+
+
@@ -203,9 +213,9 @@ function parseUrlParams() {
const searchQuery = params.get('q');
if (searchQuery) {
document.getElementById('vectorSearchQuery').value = searchQuery;
- // Auto-trigger vector search
+ // Auto-trigger search
setTimeout(() => {
- runVectorSearch(new Event('submit'));
+ runSearch(new Event('submit'));
}, 100);
}
}
@@ -671,6 +681,8 @@ function updateFilterUrl() {
// Load memories on page load
document.addEventListener('DOMContentLoaded', function() {
+ onSearchModeChange();
+ onMinSimChange();
parseUrlParams();
updateFilterContext();
loadLocationDropdown();
@@ -905,10 +917,33 @@ async function deleteMemory(id) {
}
}
-async function runVectorSearch(event) {
+function onMinSimChange() {
+ const value = document.getElementById('vectorSearchMinSim').value;
+ document.getElementById('minSimValue').textContent = value + '%';
+}
+
+function onSearchModeChange() {
+ const mode = document.getElementById('searchMode').value;
+ const buttonLabel = document.getElementById('searchButtonLabel');
+ const input = document.getElementById('vectorSearchQuery');
+ const minSimInput = document.getElementById('vectorSearchMinSim');
+
+ if (mode === 'vector') {
+ buttonLabel.textContent = 'Vector Search';
+ input.placeholder = 'Vector search memories...';
+ minSimInput.value = '70';
+ } else {
+ buttonLabel.textContent = 'Search';
+ input.placeholder = 'Search memories...';
+ minSimInput.value = '25';
+ }
+ onMinSimChange();
+}
+
+async function runSearch(event) {
event.preventDefault();
const query = document.getElementById('vectorSearchQuery').value.trim();
- const minSim = parseFloat(document.getElementById('vectorSearchMinSim').value) || 0.7;
+ const method = document.getElementById('searchMode').value;
if (!query) {
// If search bar is cleared, revert to default list
isVectorSearch = false;
@@ -919,21 +954,24 @@ async function runVectorSearch(event) {
isVectorSearch = true;
document.getElementById('vectorSearchResults').innerHTML = `
`;
try {
- const response = await fetch(`/api/memory/search?query=${encodeURIComponent(query)}&minSimilarity=${minSim}&limit=20`);
+ const minSim = (parseFloat(document.getElementById('vectorSearchMinSim').value) || 0) / 100;
+ const url = `/api/memory/search?query=${encodeURIComponent(query)}&method=${method}&limit=20&minSimilarity=${minSim}`;
+ const response = await fetch(url);
const results = await response.json();
- displayVectorSearchResults(results);
+ displayVectorSearchResults(results, method);
} catch (error) {
- document.getElementById('vectorSearchResults').innerHTML = `
Error running vector search. Please try again.
`;
+ document.getElementById('vectorSearchResults').innerHTML = `
Error running search. Please try again.
`;
}
return false;
}
-function displayVectorSearchResults(results) {
+function displayVectorSearchResults(results, method) {
+ const resultsTitle = method === 'vector' ? 'Vector Search Results' : 'Search Results';
if (!results || results.length === 0) {
- document.getElementById('vectorSearchResults').innerHTML = `
No vector search results found.
`;
+ document.getElementById('vectorSearchResults').innerHTML = `
No search results found.
`;
return;
}
- let html = `
Vector Search Results
`;
+ let html = `
${resultsTitle}
`;
results.forEach(memory => {
const tags = createClickableTagBadges(memory.tags);
const title = memory.title || 'Untitled Memory';
diff --git a/src/Memorizer/Views/Home/ProjectDetail.cshtml b/src/Memorizer/Views/Home/ProjectDetail.cshtml
index b799e1c..c33c3b6 100644
--- a/src/Memorizer/Views/Home/ProjectDetail.cshtml
+++ b/src/Memorizer/Views/Home/ProjectDetail.cshtml
@@ -46,6 +46,23 @@
+
+
+
Status:
@@ -411,6 +428,59 @@ function renderProject() {
archiveBtn.innerHTML = '
Archive Project';
archiveBtn.onclick = archiveProject;
}
+
+ // Tag cloud
+ loadTagCloud();
+}
+
+const tagCloudMaxTags = 30;
+let allTagCounts = [];
+let tagCloudShowAll = false;
+
+async function loadTagCloud() {
+ const section = document.getElementById('tagCloudSection');
+ const empty = document.getElementById('noTagCloud');
+
+ try {
+ const response = await fetch(`/api/memory/tags/cloud?projectId=${projectId}`);
+ if (!response.ok) throw new Error('Failed to load tag cloud');
+
+ const tags = await response.json();
+ if (!tags || tags.length === 0) {
+ section.style.display = 'none';
+ return;
+ }
+
+ allTagCounts = tags;
+ tagCloudShowAll = false;
+ renderTagCloud();
+ empty.style.display = 'none';
+ section.style.display = 'block';
+ } catch (error) {
+ console.warn('Could not load tag cloud:', error);
+ section.style.display = 'none';
+ }
+}
+
+function renderTagCloud() {
+ const container = document.getElementById('tagCloud');
+ const visible = tagCloudShowAll ? allTagCounts : allTagCounts.slice(0, tagCloudMaxTags);
+ let html = visible.map(renderTagCloudItem).join('');
+ if (allTagCounts.length > tagCloudMaxTags) {
+ html += `
${tagCloudShowAll ? 'Show fewer' : `Show all ${allTagCounts.length} tags`}`;
+ }
+ container.innerHTML = html;
+}
+
+function toggleTagCloud() {
+ tagCloudShowAll = !tagCloudShowAll;
+ renderTagCloud();
+}
+
+function renderTagCloudItem(tagCount) {
+ const fontSize = 0.8 + Math.min(0.8, (tagCount.count - 1) * 0.15);
+ const href = `/memories?tag=${encodeURIComponent(tagCount.tag)}`;
+ return `
${escapeHtml(tagCount.tag)}`;
}
function renderStatusDropdown() {
diff --git a/src/Memorizer/Views/Home/WorkspaceDetail.cshtml b/src/Memorizer/Views/Home/WorkspaceDetail.cshtml
index ae3a4fd..7aa9d82 100644
--- a/src/Memorizer/Views/Home/WorkspaceDetail.cshtml
+++ b/src/Memorizer/Views/Home/WorkspaceDetail.cshtml
@@ -28,57 +28,70 @@
-
+
+
+
-
+
Nested Workspaces 0
-
-
-
-
No nested workspaces. Create one to organize related areas of work.
-
-
+
-
+
Projects 0
-
-
-
-
No projects yet. Create one to start organizing memories.
-
-
+
@@ -318,24 +331,85 @@ function renderWorkspace() {
// Child Workspaces
const childWorkspaces = workspace.childWorkspaces || [];
- document.getElementById('childWorkspaceCount').textContent = childWorkspaces.length;
+ const childWorkspacesSection = document.getElementById('childWorkspacesSection');
if (childWorkspaces.length > 0) {
+ document.getElementById('childWorkspaceCount').textContent = childWorkspaces.length;
document.getElementById('childWorkspacesList').innerHTML = childWorkspaces.map(renderChildWorkspaceCard).join('');
+ childWorkspacesSection.style.display = 'block';
+ document.getElementById('childWorkspacesDivider').style.display = 'block';
} else {
- document.getElementById('noChildWorkspaces').style.display = 'block';
+ childWorkspacesSection.style.display = 'none';
+ document.getElementById('childWorkspacesDivider').style.display = 'none';
}
// Projects
- document.getElementById('projectCount').textContent = workspace.projects.length;
+ const projectsSection = document.getElementById('projectsSection');
if (workspace.projects.length > 0) {
+ document.getElementById('projectCount').textContent = workspace.projects.length;
document.getElementById('projectsList').innerHTML = workspace.projects.map(renderProjectCard).join('');
+ projectsSection.style.display = 'block';
+ document.getElementById('projectsDivider').style.display = 'block';
} else {
- document.getElementById('noProjects').style.display = 'block';
+ projectsSection.style.display = 'none';
+ document.getElementById('projectsDivider').style.display = 'none';
}
// Memories
document.getElementById('viewAllMemoriesLink').href = `/memories?workspaceId=${workspaceId}`;
renderMemoriesList();
+
+ // Tag cloud
+ loadTagCloud();
+}
+
+const tagCloudMaxTags = 30;
+let allTagCounts = [];
+let tagCloudShowAll = false;
+
+async function loadTagCloud() {
+ const section = document.getElementById('tagCloudSection');
+ const empty = document.getElementById('noTagCloud');
+
+ try {
+ const response = await fetch(`/api/memory/tags/cloud?workspaceId=${workspaceId}`);
+ if (!response.ok) throw new Error('Failed to load tag cloud');
+
+ const tags = await response.json();
+ if (!tags || tags.length === 0) {
+ section.style.display = 'none';
+ return;
+ }
+
+ allTagCounts = tags;
+ tagCloudShowAll = false;
+ renderTagCloud();
+ empty.style.display = 'none';
+ section.style.display = 'block';
+ } catch (error) {
+ console.warn('Could not load tag cloud:', error);
+ section.style.display = 'none';
+ }
+}
+
+function renderTagCloud() {
+ const container = document.getElementById('tagCloud');
+ const visible = tagCloudShowAll ? allTagCounts : allTagCounts.slice(0, tagCloudMaxTags);
+ let html = visible.map(renderTagCloudItem).join('');
+ if (allTagCounts.length > tagCloudMaxTags) {
+ html += `
${tagCloudShowAll ? 'Show fewer' : `Show all ${allTagCounts.length} tags`}`;
+ }
+ container.innerHTML = html;
+}
+
+function toggleTagCloud() {
+ tagCloudShowAll = !tagCloudShowAll;
+ renderTagCloud();
+}
+
+function renderTagCloudItem(tagCount) {
+ const fontSize = 0.8 + Math.min(0.8, (tagCount.count - 1) * 0.15);
+ const href = `/memories?tag=${encodeURIComponent(tagCount.tag)}`;
+ return `
${escapeHtml(tagCount.tag)}`;
}
function renderProjectCard(project) {
diff --git a/src/Memorizer/Views/Shared/_Layout.cshtml b/src/Memorizer/Views/Shared/_Layout.cshtml
index 9c5f85c..1f1c219 100644
--- a/src/Memorizer/Views/Shared/_Layout.cshtml
+++ b/src/Memorizer/Views/Shared/_Layout.cshtml
@@ -103,6 +103,24 @@
+
+
+
+
+
@@ -194,6 +212,7 @@
+
@await RenderSectionAsync("Scripts", required: false)