diff --git a/agrifine-extension/dist/background.js b/agrifine-extension/dist/background.js
index e85c658..a5de481 100644
--- a/agrifine-extension/dist/background.js
+++ b/agrifine-extension/dist/background.js
@@ -632,7 +632,9 @@ var KEYS = {
FIELD_PROFILES: 'agrifine_field_profiles',
SETTINGS: 'agrifine_settings',
FARM_MEMORY: 'agrifine_farm_memory',
- API_KEY: 'agrifine_api_key' // session only
+ API_KEY: 'agrifine_api_key',
+ // session storage (always)
+ API_KEY_SAVED: 'agrifine_api_key_saved' // local storage (when user opts to remember)
};
// ── Generic helpers ──────────────────────────────────────────────────────────
@@ -1308,9 +1310,14 @@ chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: true
})["catch"](console.error);
+// Restore saved API key into session on service worker startup
+(0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.localGet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY_SAVED).then(function (saved) {
+ if (saved) (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.sessionSet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY, saved)["catch"](function () {});
+})["catch"](function () {});
+
// ── Message router ────────────────────────────────────────────────────────────
chrome.runtime.onMessage.addListener(function (message, _sender, sendResponse) {
- var _message$payload;
+ var _message$payload2;
switch (message.type) {
case 'ANTHROPIC_REQUEST':
handleAnthropicRequest(message.payload).then(sendResponse)["catch"](function (err) {
@@ -1322,16 +1329,27 @@ chrome.runtime.onMessage.addListener(function (message, _sender, sendResponse) {
// keep channel open for async response
case 'SET_API_KEY':
- (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.sessionSet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY, message.payload.key).then(function () {
- return sendResponse({
- ok: true
+ {
+ var _message$payload = message.payload,
+ key = _message$payload.key,
+ remember = _message$payload.remember;
+ var ops = [(0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.sessionSet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY, key)];
+ if (remember) {
+ ops.push((0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.localSet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY_SAVED, key));
+ } else {
+ ops.push((0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.localSet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY_SAVED, null));
+ }
+ Promise.all(ops).then(function () {
+ return sendResponse({
+ ok: true
+ });
+ })["catch"](function (err) {
+ return sendResponse({
+ error: err.message
+ });
});
- })["catch"](function (err) {
- return sendResponse({
- error: err.message
- });
- });
- return true;
+ return true;
+ }
case 'GET_PAGE_CONTENT':
sendResponse({
ok: true
@@ -1359,7 +1377,7 @@ chrome.runtime.onMessage.addListener(function (message, _sender, sendResponse) {
});
return true;
case 'READ_TAB_CONTENT':
- readTabContent((_message$payload = message.payload) === null || _message$payload === void 0 ? void 0 : _message$payload.tab_id).then(sendResponse)["catch"](function (err) {
+ readTabContent((_message$payload2 = message.payload) === null || _message$payload2 === void 0 ? void 0 : _message$payload2.tab_id).then(sendResponse)["catch"](function (err) {
return sendResponse({
error: err.message
});
diff --git a/agrifine-extension/dist/sidebar.css b/agrifine-extension/dist/sidebar.css
index c005e84..8197fcb 100644
--- a/agrifine-extension/dist/sidebar.css
+++ b/agrifine-extension/dist/sidebar.css
@@ -606,6 +606,9 @@ video {
.h-10 {
height: 2.5rem;
}
+.h-3 {
+ height: 0.75rem;
+}
.h-4 {
height: 1rem;
}
@@ -639,6 +642,9 @@ video {
.w-10 {
width: 2.5rem;
}
+.w-3 {
+ width: 0.75rem;
+}
.w-4 {
width: 1rem;
}
@@ -673,6 +679,11 @@ video {
.cursor-pointer {
cursor: pointer;
}
+.select-none {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
.resize-none {
resize: none;
}
@@ -1015,6 +1026,9 @@ video {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
}
+.accent-agri-500 {
+ accent-color: #22c55e;
+}
.opacity-0 {
opacity: 0;
}
diff --git a/agrifine-extension/dist/sidebar.html b/agrifine-extension/dist/sidebar.html
index f8ca317..5fd43ec 100644
--- a/agrifine-extension/dist/sidebar.html
+++ b/agrifine-extension/dist/sidebar.html
@@ -35,7 +35,14 @@
-
+
+
+
+
+
diff --git a/agrifine-extension/dist/sidebar.js b/agrifine-extension/dist/sidebar.js
index 9232e5d..62f4644 100644
--- a/agrifine-extension/dist/sidebar.js
+++ b/agrifine-extension/dist/sidebar.js
@@ -3290,7 +3290,9 @@ var KEYS = {
FIELD_PROFILES: 'agrifine_field_profiles',
SETTINGS: 'agrifine_settings',
FARM_MEMORY: 'agrifine_farm_memory',
- API_KEY: 'agrifine_api_key' // session only
+ API_KEY: 'agrifine_api_key',
+ // session storage (always)
+ API_KEY_SAVED: 'agrifine_api_key_saved' // local storage (when user opts to remember)
};
// ── Generic helpers ──────────────────────────────────────────────────────────
@@ -4202,16 +4204,16 @@ function activateTab(_x) {
return _activateTab.apply(this, arguments);
} // ── Settings panel ────────────────────────────────────────────────────────────
function _activateTab() {
- _activateTab = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(id) {
+ _activateTab = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(id) {
var main;
- return _regenerator().w(function (_context5) {
- while (1) switch (_context5.n) {
+ return _regenerator().w(function (_context6) {
+ while (1) switch (_context6.n) {
case 0:
if (moduleMap[id]) {
- _context5.n = 1;
+ _context6.n = 1;
break;
}
- return _context5.a(2);
+ return _context6.a(2);
case 1:
activeModuleId = id;
document.querySelectorAll('.tab-btn').forEach(function (btn) {
@@ -4221,12 +4223,12 @@ function _activateTab() {
});
main = document.getElementById('main-content');
main.innerHTML = '';
- _context5.n = 2;
+ _context6.n = 2;
return moduleMap[id].render(main);
case 2:
- return _context5.a(2);
+ return _context6.a(2);
}
- }, _callee5);
+ }, _callee6);
}));
return _activateTab.apply(this, arguments);
}
@@ -4236,86 +4238,118 @@ function setupSettings() {
var saveBtn = document.getElementById('btn-save-key');
var input = document.getElementById('api-key-input');
var status = document.getElementById('api-key-status');
+ var rememberChk = document.getElementById('api-key-remember');
+ var forgetBtn = document.getElementById('btn-forget-key');
var agRefineInput = document.getElementById('agrefine-url-input');
var agRefineStatus = document.getElementById('agrefine-url-status');
var agRefineSaveBtn = document.getElementById('btn-save-agrefine-url');
btn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
- var existing, agUrl;
+ var sessionKey, savedKey, agUrl;
return _regenerator().w(function (_context) {
while (1) switch (_context.n) {
case 0:
panel.classList.toggle('hidden');
if (panel.classList.contains('hidden')) {
- _context.n = 3;
+ _context.n = 4;
break;
}
_context.n = 1;
return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_7__.sessionGet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_7__.KEYS.API_KEY);
case 1:
- existing = _context.v;
- if (existing) {
+ sessionKey = _context.v;
+ _context.n = 2;
+ return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_7__.localGet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_7__.KEYS.API_KEY_SAVED);
+ case 2:
+ savedKey = _context.v;
+ if (sessionKey || savedKey) {
input.value = '';
input.placeholder = 'Key set — enter new key to replace';
- status.textContent = '✓ API key is active this session';
+ status.textContent = savedKey ? '✓ Key saved across sessions' : '✓ Key active this session only';
+ status.style.color = '#4ade80';
}
- _context.n = 2;
+ if (savedKey) {
+ rememberChk.checked = true;
+ forgetBtn.classList.remove('hidden');
+ }
+ _context.n = 3;
return (0,_utils_agrefine_bridge_js__WEBPACK_IMPORTED_MODULE_8__.getAgRefineUrl)();
- case 2:
+ case 3:
agUrl = _context.v;
if (agUrl) agRefineInput.value = agUrl;
- case 3:
+ case 4:
return _context.a(2);
}
}, _callee);
})));
- agRefineSaveBtn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
- var url;
+ saveBtn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
+ var key, remember;
return _regenerator().w(function (_context2) {
while (1) switch (_context2.n) {
+ case 0:
+ key = input.value.trim();
+ if (key.startsWith('sk-ant-')) {
+ _context2.n = 1;
+ break;
+ }
+ status.textContent = 'Key must start with sk-ant-';
+ status.style.color = '#f87171';
+ return _context2.a(2);
+ case 1:
+ remember = rememberChk.checked;
+ _context2.n = 2;
+ return chrome.runtime.sendMessage({
+ type: 'SET_API_KEY',
+ payload: {
+ key: key,
+ remember: remember
+ }
+ });
+ case 2:
+ input.value = '';
+ input.placeholder = 'Key set — enter new key to replace';
+ status.textContent = remember ? '✓ Key saved across sessions' : '✓ Saved for this session';
+ status.style.color = '#4ade80';
+ forgetBtn.classList.toggle('hidden', !remember);
+ case 3:
+ return _context2.a(2);
+ }
+ }, _callee2);
+ })));
+ forgetBtn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
+ return _regenerator().w(function (_context3) {
+ while (1) switch (_context3.n) {
+ case 0:
+ _context3.n = 1;
+ return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_7__.localSet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_7__.KEYS.API_KEY_SAVED, null);
+ case 1:
+ rememberChk.checked = false;
+ forgetBtn.classList.add('hidden');
+ status.textContent = 'Saved key removed';
+ status.style.color = '#3d4f66';
+ case 2:
+ return _context3.a(2);
+ }
+ }, _callee3);
+ })));
+ agRefineSaveBtn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
+ var url;
+ return _regenerator().w(function (_context4) {
+ while (1) switch (_context4.n) {
case 0:
url = agRefineInput.value.trim();
- _context2.n = 1;
+ _context4.n = 1;
return (0,_utils_agrefine_bridge_js__WEBPACK_IMPORTED_MODULE_8__.setAgRefineUrl)(url);
case 1:
- agRefineStatus.textContent = url ? "\u2713 AG-Refine URL saved" : '✓ Cleared';
+ agRefineStatus.textContent = url ? '✓ AG-Refine URL saved' : '✓ Cleared';
agRefineStatus.style.color = '#4ade80';
setTimeout(function () {
agRefineStatus.style.color = '#3d4f66';
agRefineStatus.textContent = 'Used to sync fields and outputs from your AG-Refine app.';
}, 2500);
case 2:
- return _context2.a(2);
+ return _context4.a(2);
}
- }, _callee2);
- })));
- saveBtn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
- var key;
- return _regenerator().w(function (_context3) {
- while (1) switch (_context3.n) {
- case 0:
- key = input.value.trim();
- if (key.startsWith('sk-ant-')) {
- _context3.n = 1;
- break;
- }
- status.textContent = 'Key must start with sk-ant-';
- return _context3.a(2);
- case 1:
- _context3.n = 2;
- return chrome.runtime.sendMessage({
- type: 'SET_API_KEY',
- payload: {
- key: key
- }
- });
- case 2:
- input.value = '';
- input.placeholder = 'Key set — enter new key to replace';
- status.textContent = '✓ Saved for this session';
- case 3:
- return _context3.a(2);
- }
- }, _callee3);
+ }, _callee4);
})));
}
@@ -4332,19 +4366,19 @@ function keepAlive() {
}
// ── Init ──────────────────────────────────────────────────────────────────────
-document.addEventListener('DOMContentLoaded', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
- return _regenerator().w(function (_context4) {
- while (1) switch (_context4.n) {
+document.addEventListener('DOMContentLoaded', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
+ return _regenerator().w(function (_context5) {
+ while (1) switch (_context5.n) {
case 0:
setupTabs();
setupSettings();
keepAlive();
- _context4.n = 1;
+ _context5.n = 1;
return activateTab(activeModuleId);
case 1:
- return _context4.a(2);
+ return _context5.a(2);
}
- }, _callee4);
+ }, _callee5);
})));
})();
diff --git a/agrifine-extension/src/background/index.js b/agrifine-extension/src/background/index.js
index 523a159..f88f8d8 100644
--- a/agrifine-extension/src/background/index.js
+++ b/agrifine-extension/src/background/index.js
@@ -1,10 +1,15 @@
-import { sessionGet, sessionSet, KEYS } from '../utils/storage.js';
+import { sessionGet, sessionSet, localGet, localSet, KEYS } from '../utils/storage.js';
import { fetchAnthropic } from '../utils/api.js';
import { syncFromAgRefine } from '../utils/agrefine-bridge.js';
// Open the side panel when the action icon is clicked
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(console.error);
+// Restore saved API key into session on service worker startup
+localGet(KEYS.API_KEY_SAVED).then((saved) => {
+ if (saved) sessionSet(KEYS.API_KEY, saved).catch(() => {});
+}).catch(() => {});
+
// ── Message router ────────────────────────────────────────────────────────────
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
switch (message.type) {
@@ -14,11 +19,19 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
);
return true; // keep channel open for async response
- case 'SET_API_KEY':
- sessionSet(KEYS.API_KEY, message.payload.key)
+ case 'SET_API_KEY': {
+ const { key, remember } = message.payload;
+ const ops = [sessionSet(KEYS.API_KEY, key)];
+ if (remember) {
+ ops.push(localSet(KEYS.API_KEY_SAVED, key));
+ } else {
+ ops.push(localSet(KEYS.API_KEY_SAVED, null));
+ }
+ Promise.all(ops)
.then(() => sendResponse({ ok: true }))
.catch((err) => sendResponse({ error: err.message }));
return true;
+ }
case 'GET_PAGE_CONTENT':
sendResponse({ ok: true });
diff --git a/agrifine-extension/src/sidebar/index.js b/agrifine-extension/src/sidebar/index.js
index 8483516..6bde897 100644
--- a/agrifine-extension/src/sidebar/index.js
+++ b/agrifine-extension/src/sidebar/index.js
@@ -5,7 +5,7 @@ import { FieldProfileModule } from '../modules/field-profile/index.js';
import { DashboardModule } from '../modules/dashboard/index.js';
import { CarbonEstimatorModule } from '../modules/carbon-estimator/index.js';
import { AgRefineModule } from '../ag-refine/index.js';
-import { sessionSet, sessionGet, KEYS } from '../utils/storage.js';
+import { sessionSet, sessionGet, localGet, localSet, KEYS } from '../utils/storage.js';
import { getAgRefineUrl, setAgRefineUrl } from '../utils/agrefine-bridge.js';
// ── Module registry ───────────────────────────────────────────────────────────
@@ -50,6 +50,8 @@ function setupSettings() {
const saveBtn = document.getElementById('btn-save-key');
const input = document.getElementById('api-key-input');
const status = document.getElementById('api-key-status');
+ const rememberChk = document.getElementById('api-key-remember');
+ const forgetBtn = document.getElementById('btn-forget-key');
const agRefineInput = document.getElementById('agrefine-url-input');
const agRefineStatus = document.getElementById('agrefine-url-status');
@@ -58,35 +60,58 @@ function setupSettings() {
btn.addEventListener('click', async () => {
panel.classList.toggle('hidden');
if (!panel.classList.contains('hidden')) {
- const existing = await sessionGet(KEYS.API_KEY);
- if (existing) {
+ const sessionKey = await sessionGet(KEYS.API_KEY);
+ const savedKey = await localGet(KEYS.API_KEY_SAVED);
+ if (sessionKey || savedKey) {
input.value = '';
input.placeholder = 'Key set — enter new key to replace';
- status.textContent = '✓ API key is active this session';
+ status.textContent = savedKey
+ ? '✓ Key saved across sessions'
+ : '✓ Key active this session only';
+ status.style.color = '#4ade80';
+ }
+ if (savedKey) {
+ rememberChk.checked = true;
+ forgetBtn.classList.remove('hidden');
}
const agUrl = await getAgRefineUrl();
if (agUrl) agRefineInput.value = agUrl;
}
});
- agRefineSaveBtn.addEventListener('click', async () => {
- const url = agRefineInput.value.trim();
- await setAgRefineUrl(url);
- agRefineStatus.textContent = url ? `✓ AG-Refine URL saved` : '✓ Cleared';
- agRefineStatus.style.color = '#4ade80';
- setTimeout(() => { agRefineStatus.style.color = '#3d4f66'; agRefineStatus.textContent = 'Used to sync fields and outputs from your AG-Refine app.'; }, 2500);
- });
-
saveBtn.addEventListener('click', async () => {
const key = input.value.trim();
if (!key.startsWith('sk-ant-')) {
status.textContent = 'Key must start with sk-ant-';
+ status.style.color = '#f87171';
return;
}
- await chrome.runtime.sendMessage({ type: 'SET_API_KEY', payload: { key } });
+ const remember = rememberChk.checked;
+ await chrome.runtime.sendMessage({ type: 'SET_API_KEY', payload: { key, remember } });
input.value = '';
input.placeholder = 'Key set — enter new key to replace';
- status.textContent = '✓ Saved for this session';
+ status.textContent = remember ? '✓ Key saved across sessions' : '✓ Saved for this session';
+ status.style.color = '#4ade80';
+ forgetBtn.classList.toggle('hidden', !remember);
+ });
+
+ forgetBtn.addEventListener('click', async () => {
+ await localSet(KEYS.API_KEY_SAVED, null);
+ rememberChk.checked = false;
+ forgetBtn.classList.add('hidden');
+ status.textContent = 'Saved key removed';
+ status.style.color = '#3d4f66';
+ });
+
+ agRefineSaveBtn.addEventListener('click', async () => {
+ const url = agRefineInput.value.trim();
+ await setAgRefineUrl(url);
+ agRefineStatus.textContent = url ? '✓ AG-Refine URL saved' : '✓ Cleared';
+ agRefineStatus.style.color = '#4ade80';
+ setTimeout(() => {
+ agRefineStatus.style.color = '#3d4f66';
+ agRefineStatus.textContent = 'Used to sync fields and outputs from your AG-Refine app.';
+ }, 2500);
});
}
diff --git a/agrifine-extension/src/sidebar/sidebar.html b/agrifine-extension/src/sidebar/sidebar.html
index f8ca317..5fd43ec 100644
--- a/agrifine-extension/src/sidebar/sidebar.html
+++ b/agrifine-extension/src/sidebar/sidebar.html
@@ -35,7 +35,14 @@
-
+
+
+
+
+
diff --git a/agrifine-extension/src/utils/storage.js b/agrifine-extension/src/utils/storage.js
index abfe4b1..11d545b 100644
--- a/agrifine-extension/src/utils/storage.js
+++ b/agrifine-extension/src/utils/storage.js
@@ -32,7 +32,8 @@ export const KEYS = {
FIELD_PROFILES: 'agrifine_field_profiles',
SETTINGS: 'agrifine_settings',
FARM_MEMORY: 'agrifine_farm_memory',
- API_KEY: 'agrifine_api_key', // session only
+ API_KEY: 'agrifine_api_key', // session storage (always)
+ API_KEY_SAVED: 'agrifine_api_key_saved', // local storage (when user opts to remember)
};
// ── Generic helpers ──────────────────────────────────────────────────────────