Redesign Agrifine extension UI to match AG-Refine dark theme

Replace all light-mode Tailwind classes across every module with the
AG-Refine dark design system: #0f1621 body, #131c2b header/nav,
#1a2438 cards, #1e2d40 borders, agri-green (22c55e/16a34a) accents.

- tailwind.config.js: add `night` dark palette (950–300)
- sidebar.css: full dark rewrite (cards, tags, inputs, scrollbar)
- sidebar.html: AG/REFINE/FIELD INTELLIGENCE logo, dark tab bar with
  INTEL/INGEST/FIELDS/DATA/CARBON/AGENT labels and dot indicators
- sidebar/index.js: manage inactive tab color via inline style
- reading-list: card links → agri-400, summary/date → gray-400/500
- data-ingest: drop zone → night borders, file cards → dark theme
- field-profile: form → night-700 bg, inputs use .ag-input, cards dark
- dashboard: AI query bar and category pills → dark, answer box dark
- carbon-estimator: feature list and notify banner → dark palette
- ag-refine: suggestions → night-700, chat bubbles dark, tool call
  cards use night-700/border-night-600, errors use red-900/20 tint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu
This commit is contained in:
Claude 2026-06-27 06:28:50 +00:00
parent 05a3a3bbd0
commit 564ec1145e
No known key found for this signature in database
10 changed files with 169 additions and 142 deletions

View file

@ -40,10 +40,10 @@ export function AgRefineModule() {
<div class="px-4 pt-4 pb-2 flex-shrink-0">
<div class="flex items-center gap-2 mb-1">
<span class="text-base">🤖</span>
<h2 class="text-sm font-bold text-gray-800">AgriAgent</h2>
<span class="text-xs px-2 py-0.5 rounded-full bg-agri-100 text-agri-700 font-medium">AI Agent</span>
<h2 class="text-sm font-bold text-white">AgriAgent</h2>
<span class="text-xs px-2 py-0.5 rounded-full bg-night-600 text-agri-400 font-medium">AI Agent</span>
</div>
<p class="text-xs text-gray-400">Multi-step reasoning over all your farm data</p>
<p class="text-xs text-gray-500">Multi-step reasoning over all your farm data</p>
</div>
<!-- Chat history -->
@ -51,29 +51,29 @@ export function AgRefineModule() {
<!-- Suggested prompts (shown when empty) -->
<div id="agent-suggestions" class="px-4 pb-2 flex-shrink-0">
<p class="text-xs text-gray-400 mb-2">Try asking</p>
<p class="text-xs text-gray-500 mb-2">Try asking</p>
<div class="flex flex-col gap-1.5">
${SUGGESTED_PROMPTS.map((p) => `
<button class="suggest-btn text-left text-xs bg-agri-50 hover:bg-agri-100 text-agri-700 px-3 py-2 rounded-lg border border-agri-200 transition">
<button class="suggest-btn text-left text-xs bg-night-700 hover:bg-night-600 text-agri-400 px-3 py-2 rounded-lg border border-night-500 transition">
${p}
</button>`).join('')}
</div>
</div>
<!-- Input bar -->
<div class="flex-shrink-0 border-t border-gray-100 px-3 py-3 bg-white">
<div class="flex-shrink-0 border-t border-night-600 px-3 py-3">
<div class="flex gap-2 items-end">
<textarea id="agent-input" rows="2"
placeholder="Ask the agent anything about your farm…"
class="flex-1 text-sm border border-gray-300 rounded-xl px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400 resize-none"></textarea>
class="ag-input flex-1 rounded-xl resize-none"></textarea>
<button id="agent-send"
class="flex-shrink-0 bg-agri-600 hover:bg-agri-700 disabled:bg-gray-300 text-white rounded-xl px-3 py-2 transition">
class="flex-shrink-0 bg-agri-600 hover:bg-agri-700 disabled:bg-night-500 text-white rounded-xl px-3 py-2 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
</button>
</div>
<button id="agent-clear" class="text-xs text-gray-300 hover:text-gray-500 mt-1 transition">Clear conversation</button>
<button id="agent-clear" class="text-xs text-gray-500 hover:text-gray-300 mt-1 transition">Clear conversation</button>
</div>
</div>
`;
@ -197,28 +197,28 @@ export function AgRefineModule() {
<div class="flex flex-col gap-1.5">
${steps.map((step) => {
if (step.type === 'status') {
return `<div class="flex items-center gap-1.5 text-xs text-gray-400">
return `<div class="flex items-center gap-1.5 text-xs text-gray-500">
<span class="spinner flex-shrink-0"></span> ${escapeHtml(step.text)}
</div>`;
}
if (step.type === 'tool') {
return `<div class="flex items-center gap-2 text-xs bg-earth-50 border border-earth-200 rounded-lg px-3 py-1.5">
return `<div class="flex items-center gap-2 text-xs bg-night-700 border border-night-600 rounded-lg px-3 py-1.5">
<span>${step.icon}</span>
<span class="font-medium text-earth-700">${step.name}</span>
<span class="font-medium text-agri-400">${step.name}</span>
${step.done ? '<span class="ml-auto text-agri-500">✓</span>' : '<span class="spinner ml-auto flex-shrink-0"></span>'}
</div>`;
}
return '';
}).join('')}
${steps.length === 0 ? '<div class="flex items-center gap-1.5 text-xs text-gray-400"><span class="spinner"></span> Starting…</div>' : ''}
${steps.length === 0 ? '<div class="flex items-center gap-1.5 text-xs text-gray-500"><span class="spinner"></span> Starting…</div>' : ''}
</div>`;
}
if (msg.role === 'assistant') {
return `
<div class="flex gap-2 items-start">
<div class="flex-shrink-0 w-6 h-6 rounded-full bg-agri-100 flex items-center justify-center text-xs">🤖</div>
<div class="flex-1 bg-white border border-gray-200 rounded-2xl rounded-tl-sm px-3 py-2.5 text-sm text-gray-800 leading-relaxed shadow-sm whitespace-pre-wrap">
<div class="flex-shrink-0 w-6 h-6 rounded-full bg-night-600 flex items-center justify-center text-xs">🤖</div>
<div class="flex-1 bg-night-700 border border-night-600 rounded-2xl rounded-tl-sm px-3 py-2.5 text-sm text-gray-200 leading-relaxed whitespace-pre-wrap">
${escapeHtml(msg.text)}
</div>
</div>`;
@ -226,7 +226,7 @@ export function AgRefineModule() {
if (msg.role === 'error') {
return `
<div class="text-xs bg-red-50 border border-red-200 text-red-700 rounded-xl px-3 py-2">
<div class="text-xs bg-red-900/20 border border-red-900/40 text-red-400 rounded-xl px-3 py-2">
${escapeHtml(msg.text)}
</div>`;
}

View file

@ -12,15 +12,15 @@ export function CarbonEstimatorModule() {
<div class="px-4">
<!-- Phase 7 preview card -->
<div class="agri-card border-earth-200">
<div class="agri-card border-night-600">
<div class="flex items-center gap-3 mb-3">
<div class="w-10 h-10 rounded-full bg-earth-100 flex items-center justify-center text-xl">🌿</div>
<div class="w-10 h-10 rounded-full bg-night-600 flex items-center justify-center text-xl">🌿</div>
<div>
<h3 class="text-sm font-bold text-gray-800">Carbon Estimator</h3>
<h3 class="text-sm font-bold text-white">Carbon Estimator</h3>
<span class="coming-soon">Coming in Phase 7</span>
</div>
</div>
<p class="text-xs text-gray-500 leading-relaxed">
<p class="text-xs text-gray-400 leading-relaxed">
The Carbon Estimator will calculate your operation's Scope 3 emissions profile
and estimate carbon sequestration potential per field using USDA emission factors.
</p>
@ -35,19 +35,19 @@ export function CarbonEstimatorModule() {
['📄', 'Carbon Credit PDF', 'Downloadable eligibility summary for carbon marketplaces'],
['📡', 'Marketplace Handoff', 'Send your credit profile to Nori, Pachama, or others (Phase 8)'],
].map(([icon, title, desc]) => `
<div class="flex items-start gap-3 py-2.5 border-b border-gray-100 last:border-0">
<div class="flex items-start gap-3 py-2.5 border-b border-night-600 last:border-0">
<span class="text-base">${icon}</span>
<div>
<p class="text-xs font-semibold text-gray-700">${title}</p>
<p class="text-xs text-gray-400">${desc}</p>
<p class="text-xs font-semibold text-gray-200">${title}</p>
<p class="text-xs text-gray-500">${desc}</p>
</div>
</div>`).join('')}
</div>
<!-- Notify placeholder -->
<div class="mt-4 bg-agri-50 rounded-xl p-3 text-center">
<p class="text-xs text-agri-700 font-medium">Your field profile data is already being collected.</p>
<p class="text-xs text-agri-600 mt-0.5">Carbon estimates will populate automatically when Phase 7 lands.</p>
<div class="mt-4 bg-night-800 rounded-xl p-3 text-center">
<p class="text-xs text-agri-400 font-medium">Your field profile data is already being collected.</p>
<p class="text-xs text-agri-500 mt-0.5">Carbon estimates will populate automatically when Phase 7 lands.</p>
</div>
</div>
`;

View file

@ -41,13 +41,13 @@ export function DashboardModule() {
<div class="px-4 mb-3">
<div class="flex gap-2">
<input id="dash-ai-input" type="text" placeholder="Ask anything… e.g. highest yield field?"
class="flex-1 text-sm border border-gray-300 rounded-xl px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
class="ag-input flex-1 rounded-xl" />
<button id="dash-ai-btn"
class="bg-agri-600 hover:bg-agri-700 text-white text-sm px-4 py-2 rounded-xl transition flex-shrink-0">
Ask
</button>
</div>
<div id="dash-ai-answer" class="hidden mt-2 bg-white border border-agri-200 rounded-xl p-3 text-sm text-gray-700 leading-relaxed shadow-sm"></div>
<div id="dash-ai-answer" class="hidden mt-2 bg-night-700 border border-night-600 rounded-xl p-3 text-sm text-gray-200 leading-relaxed"></div>
</div>
<!-- Filters -->
@ -55,12 +55,12 @@ export function DashboardModule() {
<div class="flex gap-1.5 flex-wrap mb-2">
${CATEGORIES.map((c) => `
<button data-cat="${c}" class="cat-btn text-xs px-2.5 py-1 rounded-full border transition
${c === activeCategory ? 'bg-agri-600 text-white border-agri-600' : 'border-gray-300 text-gray-600 hover:border-agri-400'}">
${c === activeCategory ? 'bg-agri-600 text-white border-agri-600' : 'border-night-500 text-gray-400 hover:border-agri-500'}">
${c.charAt(0).toUpperCase() + c.slice(1)}
</button>`).join('')}
</div>
<input id="dash-search" type="text" placeholder="Search by keyword or field name…"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-1.5 focus:outline-none focus:ring-2 focus:ring-agri-400" />
class="ag-input" />
</div>
<!-- Unified list -->
@ -76,7 +76,7 @@ export function DashboardModule() {
btn.addEventListener('click', async () => {
activeCategory = btn.dataset.cat;
container.querySelectorAll('.cat-btn').forEach((b) => {
b.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition border-gray-300 text-gray-600 hover:border-agri-400`;
b.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition border-night-500 text-gray-400 hover:border-agri-500`;
});
btn.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition bg-agri-600 text-white border-agri-600`;
await this._renderDashboard(container);
@ -124,7 +124,7 @@ export function DashboardModule() {
maxTokens: 512,
});
answerEl.innerHTML = `<p class="font-medium text-agri-700 mb-1">Answer</p><span class="whitespace-pre-wrap">${escapeHtml(answer)}</span>`;
answerEl.innerHTML = `<p class="font-medium text-agri-400 mb-1">Answer</p><span class="whitespace-pre-wrap">${escapeHtml(answer)}</span>`;
} catch (err) {
answerEl.textContent = `Error: ${err.message}`;
} finally {
@ -173,12 +173,12 @@ export function DashboardModule() {
<div class="flex items-start gap-2">
<span class="text-lg flex-shrink-0">${sourceIcon}</span>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold text-gray-800 truncate">${escapeHtml(title)}</p>
${sub ? `<p class="text-xs text-gray-500 mt-0.5 leading-relaxed line-clamp-2">${escapeHtml(sub)}</p>` : ''}
<p class="text-sm font-semibold text-white truncate">${escapeHtml(title)}</p>
${sub ? `<p class="text-xs text-gray-400 mt-0.5 leading-relaxed line-clamp-2">${escapeHtml(sub)}</p>` : ''}
<div class="flex items-center gap-2 mt-1.5">
<span class="tag-pill bg-earth-100 text-earth-700">${escapeHtml(item._category)}</span>
<span class="tag-pill">${escapeHtml(item._category)}</span>
${(item.tags ?? []).filter((t) => t !== item._category).slice(0, 2).map((t) => `<span class="tag-pill">${escapeHtml(t)}</span>`).join('')}
${date ? `<span class="text-xs text-gray-300">${new Date(date).toLocaleDateString()}</span>` : ''}
${date ? `<span class="text-xs text-gray-500">${new Date(date).toLocaleDateString()}</span>` : ''}
</div>
</div>
</div>

View file

@ -20,16 +20,16 @@ export function DataIngestModule() {
<!-- Drop zone -->
<div class="px-4 mb-4">
<div id="drop-zone"
class="border-2 border-dashed border-agri-300 rounded-xl p-6 text-center cursor-pointer hover:border-agri-500 hover:bg-agri-50 transition">
class="border-2 border-dashed border-night-500 rounded-xl p-6 text-center cursor-pointer hover:border-agri-500 hover:bg-night-800 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 mx-auto text-agri-400 mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="text-sm font-medium text-gray-600">Drop CSV, Excel, or PDF here</p>
<p class="text-xs text-gray-400 mt-1">or click to select a file</p>
<p class="text-sm font-medium text-gray-300">Drop CSV, Excel, or PDF here</p>
<p class="text-xs text-gray-500 mt-1">or click to select a file</p>
<input id="file-input" type="file" accept=".csv,.xlsx,.xls,.pdf" class="hidden" />
</div>
<div id="ingest-status" class="text-xs text-center text-gray-400 mt-2 min-h-[1rem]"></div>
<div id="ingest-status" class="text-xs text-center text-gray-500 mt-2 min-h-[1rem]"></div>
</div>
<!-- File list -->
@ -48,16 +48,16 @@ export function DataIngestModule() {
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-agri-600', 'bg-agri-50');
dropZone.classList.add('border-agri-500', 'bg-night-800');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('border-agri-600', 'bg-agri-50');
dropZone.classList.remove('border-agri-500', 'bg-night-800');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-agri-600', 'bg-agri-50');
dropZone.classList.remove('border-agri-500', 'bg-night-800');
const file = e.dataTransfer.files[0];
if (file) this._processFile(file, container);
});
@ -212,17 +212,17 @@ export function DataIngestModule() {
<div class="agri-card" data-id="${f.id}">
<div class="flex items-start justify-between gap-2">
<div class="flex-1">
<span class="text-xs font-bold uppercase tracking-wide text-earth-600">${f.type}</span>
<p class="text-sm font-semibold text-gray-800 leading-snug mt-0.5">${f.filename}</p>
<p class="text-xs text-gray-400 mt-0.5">${new Date(f.uploadedAt).toLocaleDateString()}</p>
<span class="text-xs font-bold uppercase tracking-wide text-agri-400">${f.type}</span>
<p class="text-sm font-semibold text-white leading-snug mt-0.5">${f.filename}</p>
<p class="text-xs text-gray-500 mt-0.5">${new Date(f.uploadedAt).toLocaleDateString()}</p>
</div>
<button class="file-delete-btn text-gray-300 hover:text-red-400 transition flex-shrink-0" data-id="${f.id}" title="Remove">
<button class="file-delete-btn text-night-300 hover:text-red-400 transition flex-shrink-0" data-id="${f.id}" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 pointer-events-none" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
${f.preview ? `<pre class="text-xs text-gray-500 mt-2 whitespace-pre-wrap bg-gray-50 rounded p-2 overflow-hidden max-h-20">${f.preview}</pre>` : ''}
${f.preview ? `<pre class="text-xs text-gray-400 mt-2 whitespace-pre-wrap bg-night-800 rounded p-2 overflow-hidden max-h-20">${f.preview}</pre>` : ''}
</div>
`).join('');

View file

@ -23,27 +23,21 @@ export function FieldProfileModule() {
</div>
<!-- Create form -->
<div id="fp-form" class="hidden px-4 mb-4 bg-white border border-gray-200 rounded-xl mx-4 p-4 shadow-sm">
<h3 class="text-sm font-semibold text-gray-700 mb-3">New Field</h3>
<div id="fp-form" class="hidden px-4 mb-4 bg-night-700 border border-night-600 rounded-xl mx-4 p-4">
<h3 class="text-sm font-semibold text-white mb-3">New Field</h3>
<div class="space-y-2">
<input id="fp-name" type="text" placeholder="Field name *"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
<input id="fp-clu" type="text" placeholder="CLU ID (optional)"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
<input id="fp-name" type="text" placeholder="Field name *" class="ag-input" />
<input id="fp-clu" type="text" placeholder="CLU ID (optional)" class="ag-input" />
<div class="flex gap-2">
<input id="fp-acres" type="number" placeholder="Acres"
class="w-1/2 text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
<input id="fp-soil" type="text" placeholder="Soil type"
class="w-1/2 text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
<input id="fp-acres" type="number" placeholder="Acres" class="ag-input w-1/2" />
<input id="fp-soil" type="text" placeholder="Soil type" class="ag-input w-1/2" />
</div>
<div class="flex gap-2">
<input id="fp-lat" type="number" step="any" placeholder="Latitude"
class="w-1/2 text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
<input id="fp-lon" type="number" step="any" placeholder="Longitude"
class="w-1/2 text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400" />
<input id="fp-lat" type="number" step="any" placeholder="Latitude" class="ag-input w-1/2" />
<input id="fp-lon" type="number" step="any" placeholder="Longitude" class="ag-input w-1/2" />
</div>
<textarea id="fp-notes" rows="2" placeholder="Notes (AI-queryable)"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-agri-400 resize-none"></textarea>
class="ag-input resize-none"></textarea>
</div>
<div class="flex gap-2 mt-3">
<button id="fp-save-btn"
@ -51,7 +45,7 @@ export function FieldProfileModule() {
Save
</button>
<button id="fp-cancel-btn"
class="flex-1 border border-gray-300 text-gray-600 text-sm font-medium py-2 rounded-lg hover:bg-gray-50 transition">
class="flex-1 border border-night-500 text-gray-300 text-sm font-medium py-2 rounded-lg hover:bg-night-700 transition">
Cancel
</button>
</div>
@ -126,20 +120,20 @@ export function FieldProfileModule() {
<div class="agri-card cursor-pointer" data-id="${p.id}">
<div class="flex items-center justify-between">
<div class="flex-1">
<h3 class="text-sm font-bold text-gray-800">${p.name}</h3>
<h3 class="text-sm font-bold text-white">${p.name}</h3>
<div class="flex flex-wrap gap-x-3 gap-y-0.5 mt-1">
${p.acres ? `<span class="text-xs text-gray-500">${p.acres} ac</span>` : ''}
${p.soilType ? `<span class="text-xs text-gray-500">${p.soilType}</span>` : ''}
${p.cluId ? `<span class="text-xs text-agri-600">CLU ${p.cluId}</span>` : ''}
${p.acres ? `<span class="text-xs text-gray-400">${p.acres} ac</span>` : ''}
${p.soilType ? `<span class="text-xs text-gray-400">${p.soilType}</span>` : ''}
${p.cluId ? `<span class="text-xs text-agri-400">CLU ${p.cluId}</span>` : ''}
</div>
</div>
<div class="flex items-center gap-2">
<button class="fp-delete-btn text-gray-300 hover:text-red-400 transition" data-id="${p.id}" title="Delete">
<button class="fp-delete-btn text-night-300 hover:text-red-400 transition" data-id="${p.id}" title="Delete">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 pointer-events-none" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<svg class="fp-chevron h-4 w-4 text-gray-400 transition-transform ${expandedId === p.id ? 'rotate-90' : ''}"
<svg class="fp-chevron h-4 w-4 text-gray-500 transition-transform ${expandedId === p.id ? 'rotate-90' : ''}"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
@ -147,12 +141,12 @@ export function FieldProfileModule() {
</div>
<!-- Expanded detail -->
<div class="fp-detail ${expandedId === p.id ? '' : 'hidden'} mt-3 pt-3 border-t border-gray-100 text-xs text-gray-600 space-y-1">
<div class="fp-detail ${expandedId === p.id ? '' : 'hidden'} mt-3 pt-3 border-t border-night-600 text-xs text-gray-400 space-y-1">
${p.coordinates?.lat != null && p.coordinates?.lon != null ? `<p>📍 ${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}</p>` : ''}
${p.notes ? `<p>📝 ${p.notes}</p>` : ''}
<p class="text-gray-300">Weather data: <span class="coming-soon">Phase 6</span></p>
<p class="text-gray-300">Carbon potential: <span class="coming-soon">Phase 7</span></p>
<p class="text-gray-300">Added ${new Date(p.createdAt).toLocaleDateString()}</p>
<p class="text-gray-500">Weather data: <span class="coming-soon">Phase 6</span></p>
<p class="text-gray-500">Carbon potential: <span class="coming-soon">Phase 7</span></p>
<p class="text-gray-500">Added ${new Date(p.createdAt).toLocaleDateString()}</p>
</div>
</div>
`).join('');

View file

@ -23,18 +23,18 @@ export function ReadingListModule() {
async render(container) {
container.innerHTML = `
<div class="section-heading">Reading List</div>
<div class="section-heading">Intelligence Sources</div>
<!-- Save current page -->
<div class="px-4 mb-3">
<button id="rl-save-btn"
class="w-full flex items-center justify-center gap-2 bg-agri-600 hover:bg-agri-700 text-white text-sm font-medium py-2.5 rounded-xl transition">
class="w-full flex items-center justify-center gap-2 bg-agri-600 hover:bg-agri-700 text-white text-sm font-medium py-2.5 rounded-lg transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Save current page
</button>
<div id="rl-save-status" class="text-xs text-center text-gray-400 mt-1 min-h-[1rem]"></div>
<div id="rl-save-status" class="text-xs text-center mt-1 min-h-[1rem]" style="color:#3d4f66;"></div>
</div>
<!-- Tag filter -->
@ -141,18 +141,18 @@ export function ReadingListModule() {
listEl.innerHTML = filtered.map((item) => `
<div class="agri-card" data-id="${escapeHtml(item.id)}">
<div class="flex items-start justify-between gap-2">
<a href="${safeHref(item.url)}" target="_blank" rel="noopener noreferrer" class="text-sm font-semibold text-agri-700 hover:underline leading-snug flex-1">${escapeHtml(item.title)}</a>
<button class="rl-delete-btn text-gray-300 hover:text-red-400 transition flex-shrink-0" data-id="${escapeHtml(item.id)}" title="Remove">
<a href="${safeHref(item.url)}" target="_blank" rel="noopener noreferrer" class="text-sm font-semibold text-agri-400 hover:underline leading-snug flex-1">${escapeHtml(item.title)}</a>
<button class="rl-delete-btn text-night-300 hover:text-red-400 transition flex-shrink-0" data-id="${escapeHtml(item.id)}" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 pointer-events-none" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
${item.summary ? `<p class="text-xs text-gray-500 mt-1.5 leading-relaxed">${escapeHtml(item.summary)}</p>` : ''}
${item.summary ? `<p class="text-xs text-gray-400 mt-1.5 leading-relaxed">${escapeHtml(item.summary)}</p>` : ''}
<div class="mt-2">
${(item.tags ?? []).map((t) => `<span class="tag-pill">${escapeHtml(t)}</span>`).join('')}
</div>
<p class="text-xs text-gray-300 mt-2">${new Date(item.savedAt).toLocaleDateString()}</p>
<p class="text-xs text-gray-500 mt-2">${new Date(item.savedAt).toLocaleDateString()}</p>
</div>
`).join('');

View file

@ -32,7 +32,9 @@ async function activateTab(id) {
activeModuleId = id;
document.querySelectorAll('.tab-btn').forEach((btn) => {
btn.classList.toggle('active-tab', btn.dataset.tab === id);
const active = btn.dataset.tab === id;
btn.classList.toggle('active-tab', active);
btn.style.color = active ? '' : '#3d4f66';
});
const main = document.getElementById('main-content');

View file

@ -2,59 +2,70 @@
@tailwind components;
@tailwind utilities;
/* Active tab highlight */
/* ── Base ── */
body {
background-color: #0f1621;
color: #e2e8f0;
}
/* ── Active tab ── */
.tab-btn.active-tab {
@apply text-agri-700;
@apply text-agri-400;
}
.tab-btn.active-tab svg {
@apply stroke-agri-700;
@apply stroke-agri-400;
}
.tab-btn.active-tab .tab-dot {
@apply opacity-100;
}
/* Card styles shared across modules */
/* ── Shared card ── */
.agri-card {
@apply bg-white rounded-xl shadow-sm border border-gray-100 p-4 mb-3;
@apply bg-night-700 rounded-xl border border-night-600 p-4 mb-3 transition-colors;
}
.agri-card:hover {
@apply shadow-md;
@apply border-night-500;
}
/* Tag pill */
/* ── Tag pill ── */
.tag-pill {
@apply inline-block text-xs px-2 py-0.5 rounded-full bg-agri-100 text-agri-800 font-medium mr-1 mb-1;
@apply inline-block text-xs px-2 py-0.5 rounded-full bg-night-600 text-agri-400 font-medium mr-1 mb-1;
}
/* Section heading */
/* ── Section heading (OVERVIEW / HARVEST / SETUP style) ── */
.section-heading {
@apply text-xs uppercase tracking-widest font-semibold text-gray-400 mb-2 px-4 pt-4;
@apply text-[10px] uppercase tracking-widest font-semibold text-night-300 mb-2 px-4 pt-4;
}
/* Empty state */
/* ── Empty state ── */
.empty-state {
@apply flex flex-col items-center justify-center py-16 text-gray-400 text-sm text-center px-6;
@apply flex flex-col items-center justify-center py-16 text-gray-500 text-sm text-center px-6;
}
/* Spinner */
/* ── Spinner ── */
.spinner {
@apply inline-block w-5 h-5 border-2 border-agri-300 border-t-agri-700 rounded-full animate-spin;
@apply inline-block w-4 h-4 border-2 border-night-500 border-t-agri-400 rounded-full animate-spin;
}
/* Coming soon badge */
/* ── Coming soon badge ── */
.coming-soon {
@apply inline-block text-xs px-2 py-0.5 rounded-full bg-earth-100 text-earth-700 font-semibold;
@apply inline-block text-xs px-2 py-0.5 rounded-full bg-night-600 text-agri-400 font-semibold;
}
/* Scrollbar styling */
/* ── Input baseline ── */
.ag-input {
@apply w-full text-sm bg-night-800 border border-night-500 text-white placeholder-gray-500
rounded-lg px-3 py-2 focus:outline-none focus:ring-1 focus:ring-agri-500 focus:border-agri-500;
}
/* ── Scrollbar ── */
::-webkit-scrollbar {
width: 4px;
width: 3px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #bbf7d0;
background: #1e2d40;
border-radius: 2px;
}

View file

@ -3,18 +3,22 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Agrifine</title>
<title>AG Refine</title>
<link rel="stylesheet" href="sidebar.css" />
</head>
<body class="bg-agri-50 text-gray-900 h-screen flex flex-col overflow-hidden font-sans">
<body class="h-screen flex flex-col overflow-hidden font-sans" style="background:#0f1621;color:#e2e8f0;">
<!-- Header -->
<header class="flex items-center justify-between px-4 py-3 bg-agri-700 text-white shadow-md flex-shrink-0">
<div class="flex items-center gap-2">
<span class="text-xl font-bold tracking-tight">🌾 Agrifine</span>
<!-- Header — matches AG Refine branding -->
<header class="flex items-center justify-between px-4 py-3 flex-shrink-0" style="background:#131c2b;border-bottom:1px solid #1e2d40;">
<div class="flex items-start gap-2.5">
<div class="leading-none">
<div class="text-lg font-black text-white tracking-tight leading-none">AG</div>
<div class="text-[10px] font-bold text-white tracking-widest leading-none mt-0.5">REFINE</div>
<div class="text-[7px] font-semibold tracking-widest leading-none mt-1" style="color:#3d4f66;">FIELD INTELLIGENCE</div>
</div>
</div>
<button id="btn-settings" class="text-agri-200 hover:text-white transition" title="Settings">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<button id="btn-settings" class="transition" style="color:#3d4f66;" onmouseenter="this.style.color='#e2e8f0'" onmouseleave="this.style.color='#3d4f66'" title="Settings">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
@ -22,79 +26,85 @@
</button>
</header>
<!-- Settings panel (hidden by default) -->
<div id="settings-panel" class="hidden flex-shrink-0 bg-white border-b border-gray-200 px-4 py-3 shadow-sm">
<label class="block text-xs font-semibold text-gray-600 mb-1">Anthropic API Key</label>
<!-- Settings panel -->
<div id="settings-panel" class="hidden flex-shrink-0 px-4 py-3" style="background:#131c2b;border-bottom:1px solid #1e2d40;">
<label class="block text-[10px] uppercase tracking-widest font-semibold mb-2" style="color:#3d4f66;">Anthropic API Key</label>
<div class="flex gap-2">
<input id="api-key-input" type="password" placeholder="sk-ant-..."
class="flex-1 text-sm border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-agri-400" />
class="ag-input flex-1" />
<button id="btn-save-key"
class="text-xs bg-agri-600 text-white px-3 py-1 rounded hover:bg-agri-700 transition">Save</button>
class="text-xs bg-agri-600 hover:bg-agri-700 text-white px-3 py-1.5 rounded-lg transition font-medium">Save</button>
</div>
<p id="api-key-status" class="text-xs text-gray-400 mt-1"></p>
<p id="api-key-status" class="text-xs mt-1.5" style="color:#3d4f66;"></p>
</div>
<!-- Main content area -->
<main id="main-content" class="flex-1 overflow-y-auto">
<!-- Modules render here -->
</main>
<!-- Main content -->
<main id="main-content" class="flex-1 overflow-y-auto" style="background:#0f1621;"></main>
<!-- Bottom tab bar -->
<nav class="flex-shrink-0 bg-white border-t border-gray-200 shadow-lg">
<nav class="flex-shrink-0" style="background:#131c2b;border-top:1px solid #1e2d40;">
<div class="flex">
<button data-tab="reading-list"
class="tab-btn flex-1 flex flex-col items-center py-2 px-1 text-xs font-medium text-gray-500 hover:text-agri-600 transition active-tab">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
class="tab-btn flex-1 flex flex-col items-center pt-2 pb-1.5 px-1 text-[9px] font-semibold tracking-wide transition active-tab" style="color:#3d4f66;">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
<span>Reading</span>
<span>INTEL</span>
<div class="tab-dot w-1 h-1 rounded-full bg-agri-400 mt-0.5 opacity-0"></div>
</button>
<button data-tab="data-ingest"
class="tab-btn flex-1 flex flex-col items-center py-2 px-1 text-xs font-medium text-gray-500 hover:text-agri-600 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
class="tab-btn flex-1 flex flex-col items-center pt-2 pb-1.5 px-1 text-[9px] font-semibold tracking-wide transition" style="color:#3d4f66;">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<span>Ingest</span>
<span>INGEST</span>
<div class="tab-dot w-1 h-1 rounded-full bg-agri-400 mt-0.5 opacity-0"></div>
</button>
<button data-tab="field-profile"
class="tab-btn flex-1 flex flex-col items-center py-2 px-1 text-xs font-medium text-gray-500 hover:text-agri-600 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
class="tab-btn flex-1 flex flex-col items-center pt-2 pb-1.5 px-1 text-[9px] font-semibold tracking-wide transition" style="color:#3d4f66;">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Fields</span>
<span>FIELDS</span>
<div class="tab-dot w-1 h-1 rounded-full bg-agri-400 mt-0.5 opacity-0"></div>
</button>
<button data-tab="dashboard"
class="tab-btn flex-1 flex flex-col items-center py-2 px-1 text-xs font-medium text-gray-500 hover:text-agri-600 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
class="tab-btn flex-1 flex flex-col items-center pt-2 pb-1.5 px-1 text-[9px] font-semibold tracking-wide transition" style="color:#3d4f66;">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
<span>Dashboard</span>
<span>DATA</span>
<div class="tab-dot w-1 h-1 rounded-full bg-agri-400 mt-0.5 opacity-0"></div>
</button>
<button data-tab="carbon-estimator"
class="tab-btn flex-1 flex flex-col items-center py-2 px-1 text-xs font-medium text-gray-500 hover:text-agri-600 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
class="tab-btn flex-1 flex flex-col items-center pt-2 pb-1.5 px-1 text-[9px] font-semibold tracking-wide transition" style="color:#3d4f66;">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
<span>Carbon</span>
<span>CARBON</span>
<div class="tab-dot w-1 h-1 rounded-full bg-agri-400 mt-0.5 opacity-0"></div>
</button>
<button data-tab="ag-refine"
class="tab-btn flex-1 flex flex-col items-center py-2 px-1 text-xs font-medium text-gray-500 hover:text-agri-600 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
class="tab-btn flex-1 flex flex-col items-center pt-2 pb-1.5 px-1 text-[9px] font-semibold tracking-wide transition" style="color:#3d4f66;">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mb-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
<span>Agent</span>
<span>AGENT</span>
<div class="tab-dot w-1 h-1 rounded-full bg-agri-400 mt-0.5 opacity-0"></div>
</button>
</div>
</nav>

View file

@ -28,6 +28,16 @@ module.exports = {
800: '#813c15',
900: '#6a3214',
},
night: {
950: '#080c14',
900: '#0f1621',
800: '#131c2b',
700: '#1a2438',
600: '#1e2d40',
500: '#253047',
400: '#2d3d54',
300: '#3d4f66',
},
},
},
},