<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy"
content="default-src 'none';
script-src 'unsafe-inline';
style-src 'unsafe-inline';
img-src data: blob:;">
<title>{{TITLE}}</title>
<style>
{{CSS}}
/* Search-specific styles */
.search-header {
margin-bottom: var(--daemon-space-lg);
}
.search-header__title {
display: flex;
align-items: center;
gap: var(--daemon-space-md);
margin-bottom: var(--daemon-space-sm);
}
.search-header__count {
color: var(--daemon-text-muted);
font-size: 0.875rem;
}
.search-filter {
display: flex;
align-items: center;
gap: var(--daemon-space-sm);
margin-top: var(--daemon-space-md);
}
.search-filter__label {
color: var(--daemon-text-muted);
font-size: 0.875rem;
}
.search-filter__select {
width: auto;
min-width: 150px;
}
.result-card {
display: flex;
flex-direction: column;
gap: var(--daemon-space-sm);
}
.result-card__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--daemon-space-sm);
}
.result-card__content {
color: var(--daemon-text);
line-height: 1.6;
margin: var(--daemon-space-sm) 0;
}
.result-card__content mark {
background-color: rgba(139, 92, 246, 0.3);
color: var(--daemon-text);
padding: 0 2px;
border-radius: 2px;
}
.result-card__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--daemon-space-sm);
font-size: 0.75rem;
color: var(--daemon-text-muted);
}
.result-card__date {
opacity: 0.8;
}
.result-card__tags {
display: flex;
flex-wrap: wrap;
gap: var(--daemon-space-xs);
}
.result-card__tag {
font-size: 0.7rem;
padding: 1px 6px;
background-color: var(--daemon-bg-alt);
border-radius: var(--daemon-radius);
}
.hidden {
display: none !important;
}
.daemon-card__actions {
margin-top: var(--daemon-space-md);
padding-top: var(--daemon-space-sm);
border-top: var(--daemon-border);
}
/* Score Breakdown */
.daemon-score-breakdown {
margin-top: var(--daemon-space-sm);
}
.daemon-score-breakdown summary {
cursor: pointer;
color: var(--daemon-text-muted);
font-size: 0.875rem;
}
.daemon-score-breakdown summary:hover {
color: var(--daemon-accent);
}
.daemon-score-components {
padding: var(--daemon-space-sm) 0;
display: grid;
gap: var(--daemon-space-xs);
}
.daemon-score-component {
display: flex;
justify-content: space-between;
font-size: 0.875rem;
}
.daemon-score-component__label {
color: var(--daemon-text-muted);
}
.daemon-score-component__value {
font-family: var(--daemon-font-mono);
color: var(--daemon-accent);
}
</style>
</head>
<body>
<!-- Real-time update notification badge -->
<div id="update-indicator" class="daemon-update-badge" role="status" aria-live="polite">
<span class="daemon-update-badge__dot"></span>
<span>New data available</span>
<button id="refresh-btn" class="daemon-btn daemon-btn--small">Refresh</button>
</div>
<div id="app">
<div class="daemon-container" style="padding: var(--daemon-space-lg);">
<!-- Header Section -->
<header class="search-header">
<div class="search-header__title">
<h1>Search Results</h1>
<span class="daemon-badge">{{TOPIC}}</span>
</div>
<p class="search-header__count">{{RESULT_COUNT}}</p>
<!-- Category Filter -->
<div class="search-filter">
<label class="search-filter__label" for="category-filter">Filter by:</label>
<select id="category-filter" class="daemon-input search-filter__select">
<option value="all">All Categories</option>
<option value="decision">Decisions</option>
<option value="warning">Warnings</option>
<option value="pattern">Patterns</option>
<option value="learning">Learnings</option>
</select>
</div>
</header>
<!-- Results Grid -->
<div class="daemon-grid daemon-grid--auto" id="results-grid">
{{CONTENT}}
</div>
<!-- Pagination Controls -->
{{PAGINATION}}
</div>
</div>
<script>
{{SCRIPT}}
// Category filter functionality
(function() {
const filterSelect = document.getElementById('category-filter');
const resultsGrid = document.getElementById('results-grid');
if (!filterSelect || !resultsGrid) return;
filterSelect.addEventListener('change', function() {
const selectedCategory = this.value;
const cards = resultsGrid.querySelectorAll('.daemon-card');
cards.forEach(function(card) {
const cardCategory = card.getAttribute('data-category');
if (selectedCategory === 'all' || cardCategory === selectedCategory) {
card.classList.remove('hidden');
} else {
card.classList.add('hidden');
}
});
// Update visible count
const visibleCards = resultsGrid.querySelectorAll('.daemon-card:not(.hidden)');
const totalCards = resultsGrid.querySelectorAll('.daemon-card');
const countEl = document.querySelector('.search-header__count');
if (countEl && selectedCategory !== 'all') {
countEl.textContent = 'Showing ' + visibleCards.length + ' of ' + totalCards.length + ' results';
}
});
})();
// Record Outcome button handler
(function() {
document.querySelectorAll('[data-action="record-outcome"]').forEach(function(btn) {
btn.addEventListener('click', function(e) {
var memoryId = e.target.dataset.memoryId;
// Send message to host to initiate record_outcome flow
if (window.SecureMessenger) {
SecureMessenger.send('tool_request', {
tool: 'record_outcome',
args: { memory_id: parseInt(memoryId, 10) }
});
}
});
});
})();
// Keyword highlighting function (available for dynamic use)
function highlightKeywords(content, query) {
if (!query || !content) return content;
var words = query.split(/\s+/).filter(function(word) {
return word.length > 2;
});
var result = content;
words.forEach(function(word) {
var escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
var pattern = new RegExp('(' + escaped + ')', 'gi');
result = result.replace(pattern, '<mark>$1</mark>');
});
return result;
}
// Pagination button handler
(function() {
var paginationContainer = document.querySelector('.daemon-pagination');
if (!paginationContainer) return;
var offset = parseInt(paginationContainer.dataset.offset || '0', 10);
var limit = parseInt(paginationContainer.dataset.limit || '10', 10);
document.querySelectorAll('.daemon-pagination__btn').forEach(function(btn) {
btn.addEventListener('click', function(e) {
if (btn.disabled) return;
var action = btn.dataset.action;
var newOffset = action === 'next'
? offset + limit
: Math.max(0, offset - limit);
// Send message to host to request new page
if (window.SecureMessenger) {
SecureMessenger.send('pagination', {
offset: newOffset,
limit: limit
});
}
});
});
})();
// Real-time update notification receiver
(function() {
var updateTimeout = null;
var DEBOUNCE_MS = 300;
var indicator = document.getElementById('update-indicator');
var refreshBtn = document.getElementById('refresh-btn');
if (window.SecureMessenger) {
SecureMessenger.on('data_updated', function(data) {
clearTimeout(updateTimeout);
updateTimeout = setTimeout(function() {
if (indicator) {
indicator.classList.add('daemon-update-badge--visible');
indicator.setAttribute('data-last-update', data.last_update || '');
}
}, DEBOUNCE_MS);
});
}
if (refreshBtn) {
refreshBtn.addEventListener('click', function() {
if (indicator) {
indicator.classList.remove('daemon-update-badge--visible');
}
if (window.SecureMessenger) {
SecureMessenger.send('tool_request', { tool: 'refresh_ui' });
}
});
}
})();
</script>
</body>
</html>