@199-bio/engram 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/consolidation/consolidator.d.ts.map +1 -1
- package/dist/settings.d.ts.map +1 -0
- package/dist/web/chat-handler.d.ts.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/consolidation/consolidator.ts +2 -1
- package/src/settings.ts +73 -0
- package/src/web/chat-handler.ts +12 -1
- package/src/web/server.ts +47 -1
- package/src/web/static/app.js +100 -0
- package/src/web/static/index.html +35 -0
- package/src/web/static/style.css +243 -43
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/consolidation/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAU,MAAM,EAAW,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/consolidation/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAU,MAAM,EAAW,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0FtD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAA6B;gBAGzC,EAAE,EAAE,cAAc,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,YAAY;IAYvB,YAAY,IAAI,OAAO;IAIvB;;;OAGG;IACG,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC;QAC3D,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IAwEF;;OAEG;YACW,gBAAgB;IAoE9B;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuHjE;;OAEG;IACH,SAAS,IAAI;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,CAAC;KAClC;IAeD;;;OAGG;IACG,mBAAmB,CAAC,OAAO,GAAE;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IAwFF;;OAEG;YACW,2BAA2B;IAqDzC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;CAkBH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,cAAc;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAE5B;AAMD;;GAEG;AACH,wBAAgB,YAAY,IAAI,cAAc,CAU7C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAW3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAGvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAIvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../src/web/chat-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../src/web/chat-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAoQtD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,YAAY,CAAoG;gBAE5G,OAAO,EAAE;QACnB,EAAE,EAAE,cAAc,CAAC;QACnB,KAAK,EAAE,cAAc,CAAC;QACtB,MAAM,EAAE,YAAY,CAAC;KACtB;IASD;;OAEG;IACH,aAAa,IAAI,IAAI;IASrB,YAAY,IAAI,OAAO;IAIvB,MAAM,IAAI,OAAO;IAIjB,cAAc,IAAI,MAAM;IAIxB,YAAY,IAAI,IAAI;YAKN,YAAY;IAgBpB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzC,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC;YAsHrD,cAAc;YAgFd,WAAW;CAoQ1B"}
|
package/dist/web/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAyBtD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAmBnD;AAED,UAAU,gBAAgB;IACxB,EAAE,EAAE,cAAc,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,IAAI,CAAS;gBAET,OAAO,EAAE,gBAAgB;IAa/B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwD9B,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;IActB,IAAI,IAAI,IAAI;YAQE,aAAa;YA+Bb,SAAS;YAiZT,WAAW;IAgCzB,OAAO,CAAC,SAAS;CAclB"}
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import Anthropic from "@anthropic-ai/sdk";
|
|
15
15
|
import { EngramDatabase, Memory, Digest, Episode } from "../storage/database.js";
|
|
16
|
+
import { getAnthropicApiKey } from "../settings.js";
|
|
16
17
|
import { KnowledgeGraph } from "../graph/knowledge-graph.js";
|
|
17
18
|
import { HybridSearch } from "../retrieval/hybrid.js";
|
|
18
19
|
|
|
@@ -124,7 +125,7 @@ export class Consolidator {
|
|
|
124
125
|
this.graph = graph || null;
|
|
125
126
|
this.search = search || null;
|
|
126
127
|
|
|
127
|
-
const apiKey =
|
|
128
|
+
const apiKey = getAnthropicApiKey();
|
|
128
129
|
if (apiKey) {
|
|
129
130
|
this.client = new Anthropic({ apiKey });
|
|
130
131
|
}
|
package/src/settings.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings management for Engram
|
|
3
|
+
* Stores configuration in ~/.engram/settings.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import os from "os";
|
|
9
|
+
|
|
10
|
+
export interface EngramSettings {
|
|
11
|
+
anthropic_api_key?: string;
|
|
12
|
+
// Future settings can be added here
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const SETTINGS_DIR = process.env.ENGRAM_DB_PATH?.replace("~", os.homedir())
|
|
16
|
+
|| path.join(os.homedir(), ".engram");
|
|
17
|
+
const SETTINGS_FILE = path.join(SETTINGS_DIR, "settings.json");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load settings from file
|
|
21
|
+
*/
|
|
22
|
+
export function loadSettings(): EngramSettings {
|
|
23
|
+
try {
|
|
24
|
+
if (fs.existsSync(SETTINGS_FILE)) {
|
|
25
|
+
const data = fs.readFileSync(SETTINGS_FILE, "utf-8");
|
|
26
|
+
return JSON.parse(data);
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error("[Engram] Failed to load settings:", error);
|
|
30
|
+
}
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Save settings to file
|
|
36
|
+
*/
|
|
37
|
+
export function saveSettings(settings: EngramSettings): void {
|
|
38
|
+
try {
|
|
39
|
+
// Ensure directory exists
|
|
40
|
+
if (!fs.existsSync(SETTINGS_DIR)) {
|
|
41
|
+
fs.mkdirSync(SETTINGS_DIR, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("[Engram] Failed to save settings:", error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the Anthropic API key from settings or environment
|
|
52
|
+
* Priority: settings file > environment variable
|
|
53
|
+
*/
|
|
54
|
+
export function getAnthropicApiKey(): string | undefined {
|
|
55
|
+
const settings = loadSettings();
|
|
56
|
+
return settings.anthropic_api_key || process.env.ANTHROPIC_API_KEY;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set the Anthropic API key in settings
|
|
61
|
+
*/
|
|
62
|
+
export function setAnthropicApiKey(apiKey: string): void {
|
|
63
|
+
const settings = loadSettings();
|
|
64
|
+
settings.anthropic_api_key = apiKey;
|
|
65
|
+
saveSettings(settings);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if API key is configured (either in settings or env)
|
|
70
|
+
*/
|
|
71
|
+
export function hasAnthropicApiKey(): boolean {
|
|
72
|
+
return !!getAnthropicApiKey();
|
|
73
|
+
}
|
package/src/web/chat-handler.ts
CHANGED
|
@@ -7,6 +7,7 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
7
7
|
import { EngramDatabase, Entity, Memory } from "../storage/database.js";
|
|
8
8
|
import { KnowledgeGraph } from "../graph/knowledge-graph.js";
|
|
9
9
|
import { HybridSearch } from "../retrieval/hybrid.js";
|
|
10
|
+
import { getAnthropicApiKey } from "../settings.js";
|
|
10
11
|
|
|
11
12
|
// Tool definitions for Claude
|
|
12
13
|
const TOOLS: Anthropic.Tool[] = [
|
|
@@ -290,9 +291,19 @@ export class ChatHandler {
|
|
|
290
291
|
this.graph = options.graph;
|
|
291
292
|
this.search = options.search;
|
|
292
293
|
|
|
293
|
-
|
|
294
|
+
// Initialize client from settings or env var
|
|
295
|
+
this.refreshClient();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Refresh the Anthropic client (call after settings change)
|
|
300
|
+
*/
|
|
301
|
+
refreshClient(): void {
|
|
302
|
+
const apiKey = getAnthropicApiKey();
|
|
294
303
|
if (apiKey) {
|
|
295
304
|
this.client = new Anthropic({ apiKey });
|
|
305
|
+
} else {
|
|
306
|
+
this.client = null;
|
|
296
307
|
}
|
|
297
308
|
}
|
|
298
309
|
|
package/src/web/server.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { KnowledgeGraph } from "../graph/knowledge-graph.js";
|
|
|
13
13
|
import { HybridSearch } from "../retrieval/hybrid.js";
|
|
14
14
|
import { ChatHandler } from "./chat-handler.js";
|
|
15
15
|
import { Consolidator } from "../consolidation/consolidator.js";
|
|
16
|
+
import { loadSettings, saveSettings, hasAnthropicApiKey } from "../settings.js";
|
|
16
17
|
|
|
17
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
19
|
const __dirname = path.dirname(__filename);
|
|
@@ -379,13 +380,58 @@ export class EngramWebServer {
|
|
|
379
380
|
return;
|
|
380
381
|
}
|
|
381
382
|
|
|
383
|
+
// ============ Settings Endpoints ============
|
|
384
|
+
|
|
385
|
+
// GET /api/settings - get current settings (without exposing full API key)
|
|
386
|
+
if (pathname === "/api/settings" && method === "GET") {
|
|
387
|
+
const settings = loadSettings();
|
|
388
|
+
res.end(JSON.stringify({
|
|
389
|
+
has_api_key: hasAnthropicApiKey(),
|
|
390
|
+
api_key_preview: settings.anthropic_api_key
|
|
391
|
+
? `${settings.anthropic_api_key.slice(0, 12)}...${settings.anthropic_api_key.slice(-4)}`
|
|
392
|
+
: null,
|
|
393
|
+
api_key_source: settings.anthropic_api_key
|
|
394
|
+
? "settings"
|
|
395
|
+
: process.env.ANTHROPIC_API_KEY
|
|
396
|
+
? "environment"
|
|
397
|
+
: null,
|
|
398
|
+
}));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// POST /api/settings - update settings
|
|
403
|
+
if (pathname === "/api/settings" && method === "POST") {
|
|
404
|
+
const { anthropic_api_key } = body as { anthropic_api_key?: string };
|
|
405
|
+
|
|
406
|
+
if (anthropic_api_key !== undefined) {
|
|
407
|
+
const settings = loadSettings();
|
|
408
|
+
if (anthropic_api_key === "") {
|
|
409
|
+
// Clear the API key
|
|
410
|
+
delete settings.anthropic_api_key;
|
|
411
|
+
} else {
|
|
412
|
+
settings.anthropic_api_key = anthropic_api_key;
|
|
413
|
+
}
|
|
414
|
+
saveSettings(settings);
|
|
415
|
+
|
|
416
|
+
// Refresh the chat client
|
|
417
|
+
this.chat.refreshClient();
|
|
418
|
+
this.consolidator = new Consolidator(this.db); // Reinit consolidator
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
res.end(JSON.stringify({
|
|
422
|
+
success: true,
|
|
423
|
+
configured: this.chat.isConfigured(),
|
|
424
|
+
}));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
382
428
|
// GET /api/chat/status - check if chat is configured
|
|
383
429
|
if (pathname === "/api/chat/status" && method === "GET") {
|
|
384
430
|
res.end(JSON.stringify({
|
|
385
431
|
configured: this.chat.isConfigured(),
|
|
386
432
|
message: this.chat.isConfigured()
|
|
387
433
|
? "Chat is ready"
|
|
388
|
-
: "
|
|
434
|
+
: "Configure API key in Settings",
|
|
389
435
|
}));
|
|
390
436
|
return;
|
|
391
437
|
}
|
package/src/web/static/app.js
CHANGED
|
@@ -17,6 +17,7 @@ const views = {
|
|
|
17
17
|
entities: document.getElementById('entities-view'),
|
|
18
18
|
graph: document.getElementById('graph-view'),
|
|
19
19
|
consolidation: document.getElementById('consolidation-view'),
|
|
20
|
+
settings: document.getElementById('settings-view'),
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
const statsEl = document.getElementById('stats');
|
|
@@ -352,6 +353,7 @@ function switchView(view) {
|
|
|
352
353
|
if (view === 'entities') loadEntities(entityTypeFilter.value);
|
|
353
354
|
if (view === 'graph') loadGraph();
|
|
354
355
|
if (view === 'consolidation') loadConsolidation();
|
|
356
|
+
if (view === 'settings') loadSettings();
|
|
355
357
|
}
|
|
356
358
|
|
|
357
359
|
// ============ Consolidation ============
|
|
@@ -881,6 +883,104 @@ async function checkApiStatus() {
|
|
|
881
883
|
}
|
|
882
884
|
}
|
|
883
885
|
|
|
886
|
+
// ============ Settings ============
|
|
887
|
+
|
|
888
|
+
const apiStatusBadge = document.getElementById('api-status-badge');
|
|
889
|
+
const apiKeyInput = document.getElementById('api-key-input');
|
|
890
|
+
const toggleKeyVisibility = document.getElementById('toggle-key-visibility');
|
|
891
|
+
const saveApiKeyBtn = document.getElementById('save-api-key');
|
|
892
|
+
const clearApiKeyBtn = document.getElementById('clear-api-key');
|
|
893
|
+
|
|
894
|
+
async function loadSettings() {
|
|
895
|
+
try {
|
|
896
|
+
const settings = await api('/api/settings');
|
|
897
|
+
updateSettingsUI(settings);
|
|
898
|
+
} catch (e) {
|
|
899
|
+
console.error('Failed to load settings', e);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function updateSettingsUI(settings) {
|
|
904
|
+
if (settings.has_api_key) {
|
|
905
|
+
apiStatusBadge.textContent = `Configured (${settings.api_key_source})`;
|
|
906
|
+
apiStatusBadge.className = 'status-badge configured';
|
|
907
|
+
apiKeyInput.placeholder = settings.api_key_preview || 'sk-ant-api03-...';
|
|
908
|
+
apiKeyInput.value = '';
|
|
909
|
+
} else {
|
|
910
|
+
apiStatusBadge.textContent = 'Not configured';
|
|
911
|
+
apiStatusBadge.className = 'status-badge not-configured';
|
|
912
|
+
apiKeyInput.placeholder = 'sk-ant-api03-...';
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (toggleKeyVisibility) {
|
|
917
|
+
toggleKeyVisibility.addEventListener('click', () => {
|
|
918
|
+
const type = apiKeyInput.type === 'password' ? 'text' : 'password';
|
|
919
|
+
apiKeyInput.type = type;
|
|
920
|
+
toggleKeyVisibility.textContent = type === 'password' ? '👁' : '🙈';
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (saveApiKeyBtn) {
|
|
925
|
+
saveApiKeyBtn.addEventListener('click', async () => {
|
|
926
|
+
const apiKey = apiKeyInput.value.trim();
|
|
927
|
+
if (!apiKey) {
|
|
928
|
+
alert('Please enter an API key');
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (!apiKey.startsWith('sk-ant-')) {
|
|
933
|
+
alert('Invalid API key format. Should start with sk-ant-');
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
try {
|
|
938
|
+
saveApiKeyBtn.disabled = true;
|
|
939
|
+
saveApiKeyBtn.textContent = 'Saving...';
|
|
940
|
+
|
|
941
|
+
const result = await api('/api/settings', {
|
|
942
|
+
method: 'POST',
|
|
943
|
+
body: { anthropic_api_key: apiKey },
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
if (result.success) {
|
|
947
|
+
apiKeyInput.value = '';
|
|
948
|
+
await loadSettings();
|
|
949
|
+
await checkApiStatus();
|
|
950
|
+
alert('API key saved successfully!');
|
|
951
|
+
} else {
|
|
952
|
+
alert('Failed to save API key');
|
|
953
|
+
}
|
|
954
|
+
} catch (e) {
|
|
955
|
+
console.error('Failed to save API key', e);
|
|
956
|
+
alert('Error saving API key');
|
|
957
|
+
} finally {
|
|
958
|
+
saveApiKeyBtn.disabled = false;
|
|
959
|
+
saveApiKeyBtn.textContent = 'Save API Key';
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (clearApiKeyBtn) {
|
|
965
|
+
clearApiKeyBtn.addEventListener('click', async () => {
|
|
966
|
+
if (!confirm('Are you sure you want to clear the API key?')) return;
|
|
967
|
+
|
|
968
|
+
try {
|
|
969
|
+
clearApiKeyBtn.disabled = true;
|
|
970
|
+
await api('/api/settings', {
|
|
971
|
+
method: 'POST',
|
|
972
|
+
body: { anthropic_api_key: '' },
|
|
973
|
+
});
|
|
974
|
+
await loadSettings();
|
|
975
|
+
await checkApiStatus();
|
|
976
|
+
} catch (e) {
|
|
977
|
+
console.error('Failed to clear API key', e);
|
|
978
|
+
} finally {
|
|
979
|
+
clearApiKeyBtn.disabled = false;
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
884
984
|
// Initialize
|
|
885
985
|
checkApiStatus();
|
|
886
986
|
loadStats();
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<button class="nav-btn" data-view="entities">Entities</button>
|
|
15
15
|
<button class="nav-btn" data-view="graph">Graph</button>
|
|
16
16
|
<button class="nav-btn" data-view="consolidation">Consolidation</button>
|
|
17
|
+
<button class="nav-btn" data-view="settings">Settings</button>
|
|
17
18
|
</nav>
|
|
18
19
|
<div class="stats" id="stats"></div>
|
|
19
20
|
<div class="api-status" id="api-status" title="Anthropic API Status">
|
|
@@ -59,6 +60,40 @@
|
|
|
59
60
|
<div id="graph-container"></div>
|
|
60
61
|
</section>
|
|
61
62
|
|
|
63
|
+
<!-- Settings View -->
|
|
64
|
+
<section id="settings-view" class="view">
|
|
65
|
+
<div class="settings-container">
|
|
66
|
+
<h2>Settings</h2>
|
|
67
|
+
|
|
68
|
+
<div class="settings-section">
|
|
69
|
+
<h3>API Configuration</h3>
|
|
70
|
+
<p class="section-desc">Configure your Anthropic API key to enable Chat and Consolidation features.</p>
|
|
71
|
+
|
|
72
|
+
<div class="settings-row" id="api-key-status">
|
|
73
|
+
<label>Status:</label>
|
|
74
|
+
<span class="status-badge" id="api-status-badge">Checking...</span>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="settings-row">
|
|
78
|
+
<label for="api-key-input">API Key:</label>
|
|
79
|
+
<div class="input-group">
|
|
80
|
+
<input type="password" id="api-key-input" placeholder="sk-ant-api03-..." autocomplete="off">
|
|
81
|
+
<button type="button" id="toggle-key-visibility" title="Show/hide">👁</button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div class="settings-row">
|
|
86
|
+
<button id="save-api-key" class="primary-btn">Save API Key</button>
|
|
87
|
+
<button id="clear-api-key" class="danger-btn">Clear</button>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<p class="help-text">
|
|
91
|
+
Get your API key from <a href="https://console.anthropic.com/settings/keys" target="_blank">console.anthropic.com</a>
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</section>
|
|
96
|
+
|
|
62
97
|
<!-- Consolidation View -->
|
|
63
98
|
<section id="consolidation-view" class="view">
|
|
64
99
|
<div class="consolidation-header">
|
package/src/web/static/style.css
CHANGED
|
@@ -11,11 +11,18 @@
|
|
|
11
11
|
--text-secondary: #5c5c5c;
|
|
12
12
|
--text-muted: #8c8c8c;
|
|
13
13
|
--border: #d4d0c8;
|
|
14
|
+
--border-hover: #b8b4ac;
|
|
14
15
|
--accent: #d97706;
|
|
15
16
|
--accent-hover: #b45309;
|
|
17
|
+
--accent-subtle: rgba(217, 119, 6, 0.1);
|
|
16
18
|
--success: #059669;
|
|
19
|
+
--success-subtle: rgba(5, 150, 105, 0.1);
|
|
17
20
|
--danger: #dc2626;
|
|
21
|
+
--danger-subtle: rgba(220, 38, 38, 0.1);
|
|
18
22
|
--shadow: rgba(0, 0, 0, 0.06);
|
|
23
|
+
--shadow-lg: rgba(0, 0, 0, 0.1);
|
|
24
|
+
--radius: 3px;
|
|
25
|
+
--transition: 0.15s ease;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
* {
|
|
@@ -54,30 +61,34 @@ h2 {
|
|
|
54
61
|
header {
|
|
55
62
|
display: flex;
|
|
56
63
|
align-items: center;
|
|
57
|
-
gap:
|
|
58
|
-
padding: 1.5rem
|
|
64
|
+
gap: 1.5rem;
|
|
65
|
+
padding: 1rem 1.5rem;
|
|
59
66
|
background: var(--bg-secondary);
|
|
60
67
|
border-bottom: 1px solid var(--border);
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
header h1 {
|
|
64
71
|
color: var(--text-primary);
|
|
72
|
+
font-size: 1.25rem;
|
|
73
|
+
font-weight: 600;
|
|
74
|
+
letter-spacing: -0.01em;
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
nav {
|
|
68
78
|
display: flex;
|
|
69
|
-
gap: 0.
|
|
79
|
+
gap: 0.25rem;
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
.nav-btn {
|
|
73
83
|
font-family: inherit;
|
|
74
|
-
font-size: 0.
|
|
75
|
-
padding: 0.5rem
|
|
84
|
+
font-size: 0.8125rem;
|
|
85
|
+
padding: 0.5rem 0.875rem;
|
|
76
86
|
background: transparent;
|
|
77
87
|
border: 1px solid transparent;
|
|
88
|
+
border-radius: var(--radius);
|
|
78
89
|
color: var(--text-secondary);
|
|
79
90
|
cursor: pointer;
|
|
80
|
-
transition: all
|
|
91
|
+
transition: all var(--transition);
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
.nav-btn:hover {
|
|
@@ -87,8 +98,9 @@ nav {
|
|
|
87
98
|
|
|
88
99
|
.nav-btn.active {
|
|
89
100
|
color: var(--text-primary);
|
|
90
|
-
border-color: var(--border);
|
|
91
101
|
background: var(--bg-primary);
|
|
102
|
+
border-color: var(--border);
|
|
103
|
+
box-shadow: 0 1px 2px var(--shadow);
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
.stats {
|
|
@@ -102,12 +114,14 @@ nav {
|
|
|
102
114
|
.api-status {
|
|
103
115
|
display: flex;
|
|
104
116
|
align-items: center;
|
|
105
|
-
gap: 0.
|
|
117
|
+
gap: 0.5rem;
|
|
106
118
|
font-size: 0.75rem;
|
|
107
119
|
color: var(--text-muted);
|
|
108
|
-
padding: 0.
|
|
120
|
+
padding: 0.375rem 0.75rem;
|
|
109
121
|
background: var(--bg-tertiary);
|
|
110
|
-
border-radius:
|
|
122
|
+
border-radius: var(--radius);
|
|
123
|
+
border: 1px solid transparent;
|
|
124
|
+
transition: all var(--transition);
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
.api-dot {
|
|
@@ -115,16 +129,36 @@ nav {
|
|
|
115
129
|
height: 8px;
|
|
116
130
|
border-radius: 50%;
|
|
117
131
|
background: var(--text-muted);
|
|
118
|
-
transition:
|
|
132
|
+
transition: all var(--transition);
|
|
133
|
+
flex-shrink: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.api-status.connected {
|
|
137
|
+
background: var(--success-subtle);
|
|
138
|
+
border-color: var(--success);
|
|
119
139
|
}
|
|
120
140
|
|
|
121
141
|
.api-status.connected .api-dot {
|
|
122
142
|
background: var(--success);
|
|
123
|
-
box-shadow: 0 0
|
|
143
|
+
box-shadow: 0 0 6px var(--success);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.api-status.connected .api-label {
|
|
147
|
+
color: var(--success);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.api-status.disconnected {
|
|
151
|
+
background: var(--danger-subtle);
|
|
152
|
+
border-color: var(--danger);
|
|
124
153
|
}
|
|
125
154
|
|
|
126
155
|
.api-status.disconnected .api-dot {
|
|
127
156
|
background: var(--danger);
|
|
157
|
+
box-shadow: 0 0 6px var(--danger);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.api-status.disconnected .api-label {
|
|
161
|
+
color: var(--danger);
|
|
128
162
|
}
|
|
129
163
|
|
|
130
164
|
.api-status.checking .api-dot {
|
|
@@ -155,23 +189,26 @@ main {
|
|
|
155
189
|
/* Search bar */
|
|
156
190
|
.search-bar {
|
|
157
191
|
display: flex;
|
|
158
|
-
gap: 0.
|
|
192
|
+
gap: 0.5rem;
|
|
159
193
|
margin-bottom: 1.5rem;
|
|
160
194
|
}
|
|
161
195
|
|
|
162
196
|
.search-bar input {
|
|
163
197
|
flex: 1;
|
|
164
198
|
font-family: inherit;
|
|
165
|
-
font-size:
|
|
166
|
-
padding: 0.
|
|
199
|
+
font-size: 0.9375rem;
|
|
200
|
+
padding: 0.625rem 1rem;
|
|
167
201
|
border: 1px solid var(--border);
|
|
202
|
+
border-radius: var(--radius);
|
|
168
203
|
background: var(--bg-secondary);
|
|
169
204
|
color: var(--text-primary);
|
|
205
|
+
transition: all var(--transition);
|
|
170
206
|
}
|
|
171
207
|
|
|
172
208
|
.search-bar input:focus {
|
|
173
209
|
outline: none;
|
|
174
210
|
border-color: var(--accent);
|
|
211
|
+
box-shadow: 0 0 0 2px var(--accent-subtle);
|
|
175
212
|
}
|
|
176
213
|
|
|
177
214
|
.search-bar input::placeholder {
|
|
@@ -181,22 +218,33 @@ main {
|
|
|
181
218
|
/* Buttons */
|
|
182
219
|
button {
|
|
183
220
|
font-family: inherit;
|
|
184
|
-
font-size: 0.
|
|
185
|
-
|
|
221
|
+
font-size: 0.8125rem;
|
|
222
|
+
font-weight: 500;
|
|
223
|
+
padding: 0.5rem 1rem;
|
|
186
224
|
background: var(--text-primary);
|
|
187
225
|
color: var(--bg-primary);
|
|
188
|
-
border:
|
|
226
|
+
border: 1px solid transparent;
|
|
227
|
+
border-radius: var(--radius);
|
|
189
228
|
cursor: pointer;
|
|
190
|
-
transition:
|
|
229
|
+
transition: all var(--transition);
|
|
191
230
|
}
|
|
192
231
|
|
|
193
232
|
button:hover {
|
|
194
233
|
background: var(--text-secondary);
|
|
234
|
+
transform: translateY(-1px);
|
|
235
|
+
box-shadow: 0 2px 4px var(--shadow);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
button:active {
|
|
239
|
+
transform: translateY(0);
|
|
240
|
+
box-shadow: none;
|
|
195
241
|
}
|
|
196
242
|
|
|
197
243
|
button:disabled {
|
|
198
244
|
opacity: 0.5;
|
|
199
245
|
cursor: not-allowed;
|
|
246
|
+
transform: none;
|
|
247
|
+
box-shadow: none;
|
|
200
248
|
}
|
|
201
249
|
|
|
202
250
|
.toolbar {
|
|
@@ -237,18 +285,20 @@ button:disabled {
|
|
|
237
285
|
.list {
|
|
238
286
|
display: flex;
|
|
239
287
|
flex-direction: column;
|
|
240
|
-
gap:
|
|
288
|
+
gap: 0.75rem;
|
|
241
289
|
}
|
|
242
290
|
|
|
243
291
|
.list-item {
|
|
244
292
|
background: var(--bg-secondary);
|
|
245
293
|
border: 1px solid var(--border);
|
|
246
|
-
|
|
247
|
-
|
|
294
|
+
border-radius: var(--radius);
|
|
295
|
+
padding: 1rem 1.25rem;
|
|
296
|
+
transition: all var(--transition);
|
|
248
297
|
}
|
|
249
298
|
|
|
250
299
|
.list-item:hover {
|
|
251
|
-
border-color: var(--
|
|
300
|
+
border-color: var(--border-hover);
|
|
301
|
+
box-shadow: 0 2px 8px var(--shadow);
|
|
252
302
|
}
|
|
253
303
|
|
|
254
304
|
.memory-item .content {
|
|
@@ -488,10 +538,12 @@ button:disabled {
|
|
|
488
538
|
|
|
489
539
|
/* Chat toggle button */
|
|
490
540
|
.chat-toggle {
|
|
491
|
-
margin-left:
|
|
541
|
+
margin-left: auto;
|
|
492
542
|
background: var(--accent);
|
|
493
|
-
font-size: 0.
|
|
543
|
+
font-size: 0.75rem;
|
|
544
|
+
font-weight: 600;
|
|
494
545
|
padding: 0.5rem 1rem;
|
|
546
|
+
letter-spacing: 0.02em;
|
|
495
547
|
}
|
|
496
548
|
|
|
497
549
|
.chat-toggle:hover {
|
|
@@ -502,21 +554,27 @@ button:disabled {
|
|
|
502
554
|
background: var(--text-primary);
|
|
503
555
|
}
|
|
504
556
|
|
|
557
|
+
.chat-toggle.disabled {
|
|
558
|
+
background: var(--text-muted);
|
|
559
|
+
cursor: not-allowed;
|
|
560
|
+
opacity: 0.6;
|
|
561
|
+
}
|
|
562
|
+
|
|
505
563
|
/* Chat panel */
|
|
506
564
|
.chat-panel {
|
|
507
565
|
position: fixed;
|
|
508
566
|
top: 0;
|
|
509
567
|
right: 0;
|
|
510
|
-
width:
|
|
568
|
+
width: 380px;
|
|
511
569
|
height: 100vh;
|
|
512
570
|
background: var(--bg-primary);
|
|
513
571
|
border-left: 1px solid var(--border);
|
|
514
572
|
display: flex;
|
|
515
573
|
flex-direction: column;
|
|
516
|
-
box-shadow: -
|
|
574
|
+
box-shadow: -8px 0 32px var(--shadow-lg);
|
|
517
575
|
z-index: 100;
|
|
518
576
|
transform: translateX(0);
|
|
519
|
-
transition: transform 0.
|
|
577
|
+
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
520
578
|
}
|
|
521
579
|
|
|
522
580
|
.chat-panel.hidden {
|
|
@@ -529,37 +587,50 @@ button:disabled {
|
|
|
529
587
|
display: flex;
|
|
530
588
|
align-items: center;
|
|
531
589
|
justify-content: space-between;
|
|
532
|
-
padding: 1rem
|
|
590
|
+
padding: 0.875rem 1rem;
|
|
533
591
|
background: var(--bg-secondary);
|
|
534
592
|
border-bottom: 1px solid var(--border);
|
|
535
593
|
}
|
|
536
594
|
|
|
537
595
|
.chat-header h3 {
|
|
538
|
-
font-size:
|
|
539
|
-
font-weight:
|
|
596
|
+
font-size: 0.9375rem;
|
|
597
|
+
font-weight: 600;
|
|
598
|
+
letter-spacing: -0.01em;
|
|
540
599
|
}
|
|
541
600
|
|
|
542
601
|
.chat-actions {
|
|
543
602
|
display: flex;
|
|
544
|
-
|
|
603
|
+
align-items: center;
|
|
604
|
+
gap: 0.375rem;
|
|
545
605
|
}
|
|
546
606
|
|
|
547
607
|
.chat-actions button {
|
|
548
|
-
font-size: 0.
|
|
608
|
+
font-size: 0.6875rem;
|
|
609
|
+
font-weight: 500;
|
|
549
610
|
padding: 0.375rem 0.625rem;
|
|
550
611
|
background: var(--bg-tertiary);
|
|
551
612
|
color: var(--text-secondary);
|
|
613
|
+
border-radius: var(--radius);
|
|
552
614
|
}
|
|
553
615
|
|
|
554
616
|
.chat-actions button:hover {
|
|
555
617
|
background: var(--border);
|
|
556
618
|
color: var(--text-primary);
|
|
619
|
+
transform: none;
|
|
620
|
+
box-shadow: none;
|
|
557
621
|
}
|
|
558
622
|
|
|
559
623
|
#chat-close {
|
|
560
|
-
font-size: 1.
|
|
624
|
+
font-size: 1.125rem;
|
|
561
625
|
padding: 0.25rem 0.5rem;
|
|
562
626
|
line-height: 1;
|
|
627
|
+
background: transparent;
|
|
628
|
+
color: var(--text-muted);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
#chat-close:hover {
|
|
632
|
+
background: var(--danger-subtle);
|
|
633
|
+
color: var(--danger);
|
|
563
634
|
}
|
|
564
635
|
|
|
565
636
|
.chat-messages {
|
|
@@ -568,21 +639,22 @@ button:disabled {
|
|
|
568
639
|
padding: 1rem;
|
|
569
640
|
display: flex;
|
|
570
641
|
flex-direction: column;
|
|
571
|
-
gap:
|
|
642
|
+
gap: 0.75rem;
|
|
572
643
|
}
|
|
573
644
|
|
|
574
645
|
.chat-message {
|
|
575
|
-
padding: 0.
|
|
576
|
-
font-size: 0.
|
|
646
|
+
padding: 0.75rem 1rem;
|
|
647
|
+
font-size: 0.875rem;
|
|
577
648
|
line-height: 1.6;
|
|
578
|
-
max-width:
|
|
649
|
+
max-width: 88%;
|
|
650
|
+
border-radius: var(--radius);
|
|
579
651
|
}
|
|
580
652
|
|
|
581
653
|
.chat-message.user {
|
|
582
654
|
background: var(--accent);
|
|
583
655
|
color: white;
|
|
584
656
|
align-self: flex-end;
|
|
585
|
-
|
|
657
|
+
box-shadow: 0 2px 4px rgba(217, 119, 6, 0.2);
|
|
586
658
|
}
|
|
587
659
|
|
|
588
660
|
.chat-message.assistant {
|
|
@@ -685,7 +757,7 @@ button:disabled {
|
|
|
685
757
|
.chat-input-form {
|
|
686
758
|
display: flex;
|
|
687
759
|
gap: 0.5rem;
|
|
688
|
-
padding: 1rem;
|
|
760
|
+
padding: 0.875rem 1rem;
|
|
689
761
|
background: var(--bg-secondary);
|
|
690
762
|
border-top: 1px solid var(--border);
|
|
691
763
|
}
|
|
@@ -693,16 +765,19 @@ button:disabled {
|
|
|
693
765
|
.chat-input-form input {
|
|
694
766
|
flex: 1;
|
|
695
767
|
font-family: inherit;
|
|
696
|
-
font-size: 0.
|
|
697
|
-
padding: 0.
|
|
768
|
+
font-size: 0.875rem;
|
|
769
|
+
padding: 0.5rem 0.75rem;
|
|
698
770
|
border: 1px solid var(--border);
|
|
771
|
+
border-radius: var(--radius);
|
|
699
772
|
background: var(--bg-primary);
|
|
700
773
|
color: var(--text-primary);
|
|
774
|
+
transition: border-color var(--transition);
|
|
701
775
|
}
|
|
702
776
|
|
|
703
777
|
.chat-input-form input:focus {
|
|
704
778
|
outline: none;
|
|
705
779
|
border-color: var(--accent);
|
|
780
|
+
box-shadow: 0 0 0 2px var(--accent-subtle);
|
|
706
781
|
}
|
|
707
782
|
|
|
708
783
|
.chat-input-form input:disabled {
|
|
@@ -717,6 +792,7 @@ button:disabled {
|
|
|
717
792
|
|
|
718
793
|
.chat-input-form button {
|
|
719
794
|
background: var(--accent);
|
|
795
|
+
padding: 0.5rem 0.875rem;
|
|
720
796
|
}
|
|
721
797
|
|
|
722
798
|
.chat-input-form button:hover {
|
|
@@ -725,11 +801,13 @@ button:disabled {
|
|
|
725
801
|
|
|
726
802
|
.chat-input-form button:disabled {
|
|
727
803
|
background: var(--text-muted);
|
|
804
|
+
transform: none;
|
|
805
|
+
box-shadow: none;
|
|
728
806
|
}
|
|
729
807
|
|
|
730
808
|
/* Adjust main content when chat is open */
|
|
731
809
|
body.chat-open main {
|
|
732
|
-
margin-right:
|
|
810
|
+
margin-right: 380px;
|
|
733
811
|
}
|
|
734
812
|
|
|
735
813
|
/* Consolidation View */
|
|
@@ -978,6 +1056,128 @@ body.chat-open main {
|
|
|
978
1056
|
color: var(--text-primary);
|
|
979
1057
|
}
|
|
980
1058
|
|
|
1059
|
+
/* Settings View */
|
|
1060
|
+
.settings-container {
|
|
1061
|
+
max-width: 600px;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
.settings-section {
|
|
1065
|
+
background: var(--bg-secondary);
|
|
1066
|
+
border: 1px solid var(--border);
|
|
1067
|
+
border-radius: var(--radius);
|
|
1068
|
+
padding: 1.5rem;
|
|
1069
|
+
margin-bottom: 1.5rem;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.settings-section h3 {
|
|
1073
|
+
font-size: 1rem;
|
|
1074
|
+
font-weight: 600;
|
|
1075
|
+
margin-bottom: 0.5rem;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
.settings-row {
|
|
1079
|
+
display: flex;
|
|
1080
|
+
align-items: center;
|
|
1081
|
+
gap: 1rem;
|
|
1082
|
+
margin: 1rem 0;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
.settings-row label {
|
|
1086
|
+
font-size: 0.875rem;
|
|
1087
|
+
color: var(--text-secondary);
|
|
1088
|
+
min-width: 80px;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
.input-group {
|
|
1092
|
+
display: flex;
|
|
1093
|
+
flex: 1;
|
|
1094
|
+
gap: 0.5rem;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.input-group input {
|
|
1098
|
+
flex: 1;
|
|
1099
|
+
font-family: "SF Mono", Monaco, monospace;
|
|
1100
|
+
font-size: 0.875rem;
|
|
1101
|
+
padding: 0.5rem 0.75rem;
|
|
1102
|
+
border: 1px solid var(--border);
|
|
1103
|
+
border-radius: var(--radius);
|
|
1104
|
+
background: var(--bg-primary);
|
|
1105
|
+
color: var(--text-primary);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
.input-group input:focus {
|
|
1109
|
+
outline: none;
|
|
1110
|
+
border-color: var(--accent);
|
|
1111
|
+
box-shadow: 0 0 0 2px var(--accent-subtle);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.input-group button {
|
|
1115
|
+
padding: 0.5rem 0.75rem;
|
|
1116
|
+
background: var(--bg-tertiary);
|
|
1117
|
+
color: var(--text-secondary);
|
|
1118
|
+
font-size: 0.875rem;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.input-group button:hover {
|
|
1122
|
+
background: var(--border);
|
|
1123
|
+
transform: none;
|
|
1124
|
+
box-shadow: none;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
.primary-btn {
|
|
1128
|
+
background: var(--accent) !important;
|
|
1129
|
+
color: white !important;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
.primary-btn:hover {
|
|
1133
|
+
background: var(--accent-hover) !important;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.danger-btn {
|
|
1137
|
+
background: var(--bg-tertiary) !important;
|
|
1138
|
+
color: var(--text-secondary) !important;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
.danger-btn:hover {
|
|
1142
|
+
background: var(--danger) !important;
|
|
1143
|
+
color: white !important;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.status-badge {
|
|
1147
|
+
display: inline-block;
|
|
1148
|
+
font-size: 0.75rem;
|
|
1149
|
+
font-weight: 500;
|
|
1150
|
+
padding: 0.25rem 0.625rem;
|
|
1151
|
+
border-radius: var(--radius);
|
|
1152
|
+
background: var(--bg-tertiary);
|
|
1153
|
+
color: var(--text-muted);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
.status-badge.configured {
|
|
1157
|
+
background: var(--success-subtle);
|
|
1158
|
+
color: var(--success);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.status-badge.not-configured {
|
|
1162
|
+
background: var(--danger-subtle);
|
|
1163
|
+
color: var(--danger);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
.help-text {
|
|
1167
|
+
font-size: 0.8125rem;
|
|
1168
|
+
color: var(--text-muted);
|
|
1169
|
+
margin-top: 1rem;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
.help-text a {
|
|
1173
|
+
color: var(--accent);
|
|
1174
|
+
text-decoration: none;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
.help-text a:hover {
|
|
1178
|
+
text-decoration: underline;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
981
1181
|
/* Responsive */
|
|
982
1182
|
@media (max-width: 640px) {
|
|
983
1183
|
header {
|