mirror of
https://github.com/vinta/awesome-python.git
synced 2026-06-27 19:32:12 +00:00
Add persistent API key storage with remember option
Previously the Anthropic API key was session-only and lost on Chrome restart. Now: - KEYS.API_KEY_SAVED added to storage schema (chrome.storage.local) - Background service worker restores saved key into session on startup, so the key is available immediately without re-entering it - SET_API_KEY handler accepts a `remember` flag: if true, writes to both session and local; if false, clears the local copy - Settings panel: "Remember across sessions" checkbox next to Save, "Forget saved key" button appears when a key is persisted - Status text distinguishes "Key saved across sessions" vs "Key active this session only" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu
This commit is contained in:
parent
cac81d3a86
commit
9b17585756
8 changed files with 209 additions and 90 deletions
42
agrifine-extension/dist/background.js
vendored
42
agrifine-extension/dist/background.js
vendored
|
|
@ -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
|
||||
});
|
||||
|
|
|
|||
14
agrifine-extension/dist/sidebar.css
vendored
14
agrifine-extension/dist/sidebar.css
vendored
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
9
agrifine-extension/dist/sidebar.html
vendored
9
agrifine-extension/dist/sidebar.html
vendored
|
|
@ -35,7 +35,14 @@
|
|||
<button id="btn-save-key"
|
||||
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 mt-1.5" style="color:#3d4f66;"></p>
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
<label class="flex items-center gap-1.5 cursor-pointer select-none">
|
||||
<input id="api-key-remember" type="checkbox" class="accent-agri-500 w-3 h-3" />
|
||||
<span class="text-xs" style="color:#3d4f66;">Remember across sessions</span>
|
||||
</label>
|
||||
<button id="btn-forget-key" class="text-xs hover:text-red-400 transition hidden" style="color:#3d4f66;">Forget saved key</button>
|
||||
</div>
|
||||
<p id="api-key-status" class="text-xs mt-1" style="color:#3d4f66;"></p>
|
||||
|
||||
<label class="block text-[10px] uppercase tracking-widest font-semibold mb-2 mt-3" style="color:#3d4f66;">AG-Refine App URL</label>
|
||||
<div class="flex gap-2">
|
||||
|
|
|
|||
150
agrifine-extension/dist/sidebar.js
vendored
150
agrifine-extension/dist/sidebar.js
vendored
|
|
@ -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);
|
||||
})));
|
||||
})();
|
||||
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,14 @@
|
|||
<button id="btn-save-key"
|
||||
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 mt-1.5" style="color:#3d4f66;"></p>
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
<label class="flex items-center gap-1.5 cursor-pointer select-none">
|
||||
<input id="api-key-remember" type="checkbox" class="accent-agri-500 w-3 h-3" />
|
||||
<span class="text-xs" style="color:#3d4f66;">Remember across sessions</span>
|
||||
</label>
|
||||
<button id="btn-forget-key" class="text-xs hover:text-red-400 transition hidden" style="color:#3d4f66;">Forget saved key</button>
|
||||
</div>
|
||||
<p id="api-key-status" class="text-xs mt-1" style="color:#3d4f66;"></p>
|
||||
|
||||
<label class="block text-[10px] uppercase tracking-widest font-semibold mb-2 mt-3" style="color:#3d4f66;">AG-Refine App URL</label>
|
||||
<div class="flex gap-2">
|
||||
|
|
|
|||
|
|
@ -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 ──────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue