morpheus-cli 0.1.4 → 0.1.5
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/README.md +2 -0
- package/dist/channels/telegram.js +9 -1
- package/dist/config/schemas.js +3 -0
- package/dist/runtime/__tests__/agent.test.js +8 -4
- package/dist/runtime/__tests__/agent_memory_limit.test.js +61 -0
- package/dist/runtime/__tests__/agent_persistence.test.js +1 -1
- package/dist/runtime/__tests__/manual_start_verify.js +3 -0
- package/dist/runtime/agent.js +8 -5
- package/dist/runtime/memory/sqlite.js +5 -2
- package/dist/runtime/providers/factory.js +25 -6
- package/dist/runtime/tools/__tests__/factory.test.js +42 -0
- package/dist/runtime/tools/factory.js +34 -0
- package/dist/types/config.js +3 -0
- package/dist/ui/assets/{index-nNle8n-Z.css → index-D1kvj0eG.css} +1 -1
- package/dist/ui/assets/{index-Az60Fu0M.js → index-DTh8waF7.js} +7 -7
- package/dist/ui/index.html +2 -2
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -87,8 +87,9 @@ export class TelegramAdapter {
|
|
|
87
87
|
}
|
|
88
88
|
this.display.log(`Receiving voice message from @${user} (${duration}s)...`, { source: 'AgentAudio' });
|
|
89
89
|
let filePath = null;
|
|
90
|
+
let listeningMsg = null;
|
|
90
91
|
try {
|
|
91
|
-
await ctx.
|
|
92
|
+
listeningMsg = await ctx.reply("🎧Escutando...");
|
|
92
93
|
// Download
|
|
93
94
|
this.display.log(`Downloading audio for @${user}...`, { source: 'AgentAudio' });
|
|
94
95
|
const fileLink = await ctx.telegram.getFileLink(ctx.message.voice.file_id);
|
|
@@ -105,6 +106,13 @@ export class TelegramAdapter {
|
|
|
105
106
|
await ctx.sendChatAction('typing');
|
|
106
107
|
// Process with Agent
|
|
107
108
|
const response = await this.agent.chat(text);
|
|
109
|
+
// if (listeningMsg) {
|
|
110
|
+
// try {
|
|
111
|
+
// await ctx.telegram.deleteMessage(ctx.chat.id, listeningMsg.message_id);
|
|
112
|
+
// } catch (e) {
|
|
113
|
+
// // Ignore delete error
|
|
114
|
+
// }
|
|
115
|
+
// }
|
|
108
116
|
if (response) {
|
|
109
117
|
await ctx.reply(response);
|
|
110
118
|
this.display.log(`Responded to @${user} (via audio)`, { source: 'Telegram' });
|
package/dist/config/schemas.js
CHANGED
|
@@ -19,6 +19,9 @@ export const ConfigSchema = z.object({
|
|
|
19
19
|
api_key: z.string().optional(),
|
|
20
20
|
}).default(DEFAULT_CONFIG.llm),
|
|
21
21
|
audio: AudioConfigSchema.default(DEFAULT_CONFIG.audio),
|
|
22
|
+
memory: z.object({
|
|
23
|
+
limit: z.number().int().positive().default(DEFAULT_CONFIG.memory.limit),
|
|
24
|
+
}).default(DEFAULT_CONFIG.memory),
|
|
22
25
|
channels: z.object({
|
|
23
26
|
telegram: z.object({
|
|
24
27
|
enabled: z.boolean().default(false),
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { Agent } from '../agent.js';
|
|
3
3
|
import { ProviderFactory } from '../providers/factory.js';
|
|
4
|
+
import { ToolsFactory } from '../tools/factory.js';
|
|
4
5
|
import { DEFAULT_CONFIG } from '../../types/config.js';
|
|
5
6
|
import { AIMessage } from '@langchain/core/messages';
|
|
6
7
|
import * as fs from 'fs-extra';
|
|
7
8
|
import * as path from 'path';
|
|
8
9
|
import { homedir } from 'os';
|
|
9
10
|
vi.mock('../providers/factory.js');
|
|
11
|
+
vi.mock('../tools/factory.js');
|
|
10
12
|
describe('Agent', () => {
|
|
11
13
|
let agent;
|
|
12
14
|
const mockProvider = {
|
|
@@ -27,8 +29,9 @@ describe('Agent', () => {
|
|
|
27
29
|
// Ignore errors if database doesn't exist or is corrupted
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
|
-
mockProvider.invoke.mockResolvedValue(new AIMessage('Hello world'));
|
|
31
|
-
vi.mocked(ProviderFactory.create).
|
|
32
|
+
mockProvider.invoke.mockResolvedValue({ messages: [new AIMessage('Hello world')] });
|
|
33
|
+
vi.mocked(ProviderFactory.create).mockResolvedValue(mockProvider);
|
|
34
|
+
vi.mocked(ToolsFactory.create).mockResolvedValue([]);
|
|
32
35
|
agent = new Agent(DEFAULT_CONFIG);
|
|
33
36
|
});
|
|
34
37
|
afterEach(async () => {
|
|
@@ -44,7 +47,8 @@ describe('Agent', () => {
|
|
|
44
47
|
});
|
|
45
48
|
it('should initialize successfully', async () => {
|
|
46
49
|
await agent.initialize();
|
|
47
|
-
expect(
|
|
50
|
+
expect(ToolsFactory.create).toHaveBeenCalled();
|
|
51
|
+
expect(ProviderFactory.create).toHaveBeenCalledWith(DEFAULT_CONFIG.llm, []);
|
|
48
52
|
});
|
|
49
53
|
it('should chat successfully', async () => {
|
|
50
54
|
await agent.initialize();
|
|
@@ -67,7 +71,7 @@ describe('Agent', () => {
|
|
|
67
71
|
expect(history1[1].content).toBe('Hello world'); // AI
|
|
68
72
|
// Second turn
|
|
69
73
|
// Update mock return value for next call
|
|
70
|
-
mockProvider.invoke.mockResolvedValue(new AIMessage('I am fine'));
|
|
74
|
+
mockProvider.invoke.mockResolvedValue({ messages: [new AIMessage('I am fine')] });
|
|
71
75
|
await agent.chat('How are you?');
|
|
72
76
|
const history2 = await agent.getHistory();
|
|
73
77
|
expect(history2).toHaveLength(4);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { Agent } from '../agent.js';
|
|
3
|
+
import { ProviderFactory } from '../providers/factory.js';
|
|
4
|
+
import { DEFAULT_CONFIG } from '../../types/config.js';
|
|
5
|
+
import { AIMessage } from '@langchain/core/messages';
|
|
6
|
+
import * as fs from 'fs-extra';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
vi.mock('../providers/factory.js');
|
|
10
|
+
describe('Agent Memory Limit', () => {
|
|
11
|
+
let agent;
|
|
12
|
+
const mockProvider = {
|
|
13
|
+
invoke: vi.fn(),
|
|
14
|
+
};
|
|
15
|
+
const dbPath = path.join(homedir(), ".morpheus", "memory", "short-memory.db");
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
vi.resetAllMocks();
|
|
18
|
+
// Clean up DB
|
|
19
|
+
if (fs.existsSync(dbPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const Database = (await import("better-sqlite3")).default;
|
|
22
|
+
const db = new Database(dbPath);
|
|
23
|
+
db.exec("DELETE FROM messages");
|
|
24
|
+
db.close();
|
|
25
|
+
}
|
|
26
|
+
catch (err) { }
|
|
27
|
+
}
|
|
28
|
+
mockProvider.invoke.mockResolvedValue({
|
|
29
|
+
messages: [new AIMessage('Response')]
|
|
30
|
+
});
|
|
31
|
+
vi.mocked(ProviderFactory.create).mockResolvedValue(mockProvider);
|
|
32
|
+
});
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
if (agent) {
|
|
35
|
+
try {
|
|
36
|
+
await agent.clearMemory();
|
|
37
|
+
}
|
|
38
|
+
catch (err) { }
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
it('should respect configured memory limit', async () => {
|
|
42
|
+
const limitedConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
|
|
43
|
+
limitedConfig.memory.limit = 2; // Only last 2 messages (1 exchange)
|
|
44
|
+
agent = new Agent(limitedConfig);
|
|
45
|
+
await agent.initialize();
|
|
46
|
+
// Turn 1
|
|
47
|
+
await agent.chat('Msg 1');
|
|
48
|
+
// Turn 2
|
|
49
|
+
await agent.chat('Msg 2');
|
|
50
|
+
// Turn 3
|
|
51
|
+
await agent.chat('Msg 3');
|
|
52
|
+
// DB should have 6 messages (3 User + 3 AI)
|
|
53
|
+
// getHistory() should return only 2 (User Msg 3 + AI Response)
|
|
54
|
+
// Wait, SQLiteChatMessageHistory limit might be total messages? Or pairs?
|
|
55
|
+
// LangChain's limit usually means "last N messages".
|
|
56
|
+
const history = await agent.getHistory();
|
|
57
|
+
// Assuming limit=2 means 2 messages.
|
|
58
|
+
expect(history.length).toBeLessThanOrEqual(2);
|
|
59
|
+
expect(history[history.length - 1].content).toBe('Response');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -35,7 +35,7 @@ describe("Agent Persistence Integration", () => {
|
|
|
35
35
|
// Mock the SQLiteChatMessageHistory to use test path
|
|
36
36
|
// We'll use the default ~/.morpheus path for this test
|
|
37
37
|
mockProvider.invoke.mockResolvedValue(new AIMessage("Test response"));
|
|
38
|
-
vi.mocked(ProviderFactory.create).
|
|
38
|
+
vi.mocked(ProviderFactory.create).mockResolvedValue(mockProvider);
|
|
39
39
|
agent = new Agent(DEFAULT_CONFIG);
|
|
40
40
|
});
|
|
41
41
|
afterEach(async () => {
|
package/dist/runtime/agent.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HumanMessage, SystemMessage, AIMessage } from "@langchain/core/messages";
|
|
2
2
|
import { ProviderFactory } from "./providers/factory.js";
|
|
3
|
+
import { ToolsFactory } from "./tools/factory.js";
|
|
3
4
|
import { ConfigManager } from "../config/manager.js";
|
|
4
5
|
import { ProviderError } from "./errors.js";
|
|
5
6
|
import { DisplayManager } from "./display.js";
|
|
@@ -23,13 +24,15 @@ export class Agent {
|
|
|
23
24
|
// Note: API Key validation is delegated to ProviderFactory or the Provider itself
|
|
24
25
|
// to allow for Environment Variable fallback supported by LangChain.
|
|
25
26
|
try {
|
|
26
|
-
|
|
27
|
+
const tools = await ToolsFactory.create();
|
|
28
|
+
this.provider = await ProviderFactory.create(this.config.llm, tools);
|
|
27
29
|
if (!this.provider) {
|
|
28
30
|
throw new Error("Provider factory returned undefined");
|
|
29
31
|
}
|
|
30
32
|
// Initialize persistent memory with SQLite
|
|
31
33
|
this.history = new SQLiteChatMessageHistory({
|
|
32
34
|
sessionId: "default",
|
|
35
|
+
limit: this.config.memory?.limit || 100, // Fallback purely defensive if config type allows optional
|
|
33
36
|
});
|
|
34
37
|
}
|
|
35
38
|
catch (err) {
|
|
@@ -57,13 +60,13 @@ export class Agent {
|
|
|
57
60
|
...previousMessages,
|
|
58
61
|
userMessage
|
|
59
62
|
];
|
|
60
|
-
const response = await this.provider.invoke(messages);
|
|
61
|
-
|
|
63
|
+
const response = await this.provider.invoke({ messages });
|
|
64
|
+
// console.log('Agent response:', response);
|
|
62
65
|
// Persist messages to database
|
|
63
66
|
await this.history.addMessage(userMessage);
|
|
64
|
-
await this.history.addMessage(new AIMessage(
|
|
67
|
+
await this.history.addMessage(new AIMessage(response.messages[response.messages.length - 1].text));
|
|
65
68
|
this.display.log('Response generated.', { source: 'Agent' });
|
|
66
|
-
return
|
|
69
|
+
return response.messages[response.messages.length - 1].text;
|
|
67
70
|
}
|
|
68
71
|
catch (err) {
|
|
69
72
|
throw new ProviderError(this.config.llm.provider, err, "Chat request failed");
|
|
@@ -8,9 +8,11 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
8
8
|
lc_namespace = ["langchain", "stores", "message", "sqlite"];
|
|
9
9
|
db;
|
|
10
10
|
sessionId;
|
|
11
|
+
limit;
|
|
11
12
|
constructor(fields) {
|
|
12
13
|
super();
|
|
13
14
|
this.sessionId = fields.sessionId;
|
|
15
|
+
this.limit = fields.limit ? fields.limit : 20;
|
|
14
16
|
// Default path: ~/.morpheus/memory/short-memory.db
|
|
15
17
|
const dbPath = fields.databasePath || path.join(homedir(), ".morpheus", "memory", "short-memory.db");
|
|
16
18
|
// Ensure the directory exists
|
|
@@ -97,8 +99,9 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
97
99
|
*/
|
|
98
100
|
async getMessages() {
|
|
99
101
|
try {
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
+
// Esta query é válida para SQLite: seleciona os campos type e content da tabela messages filtrando por session_id, ordenando por id e limitando o número de resultados.
|
|
103
|
+
const stmt = this.db.prepare("SELECT type, content FROM messages WHERE session_id = ? ORDER BY id ASC LIMIT ?");
|
|
104
|
+
const rows = stmt.all(this.sessionId, this.limit);
|
|
102
105
|
return rows.map((row) => {
|
|
103
106
|
switch (row.type) {
|
|
104
107
|
case "human":
|
|
@@ -3,38 +3,57 @@ import { ChatAnthropic } from "@langchain/anthropic";
|
|
|
3
3
|
import { ChatOllama } from "@langchain/ollama";
|
|
4
4
|
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
5
5
|
import { ProviderError } from "../errors.js";
|
|
6
|
+
import { createAgent } from "langchain";
|
|
7
|
+
// import { MultiServerMCPClient, } from "@langchain/mcp-adapters"; // REMOVED
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { DisplayManager } from "../display.js";
|
|
6
10
|
export class ProviderFactory {
|
|
7
|
-
static create(config) {
|
|
11
|
+
static async create(config, tools = []) {
|
|
12
|
+
let display = DisplayManager.getInstance();
|
|
13
|
+
let model;
|
|
14
|
+
const responseSchema = z.object({
|
|
15
|
+
content: z.string().describe("The main response content from the agent"),
|
|
16
|
+
});
|
|
17
|
+
// Removed direct MCP client instantiation
|
|
8
18
|
try {
|
|
9
19
|
switch (config.provider) {
|
|
10
20
|
case 'openai':
|
|
11
|
-
|
|
21
|
+
model = new ChatOpenAI({
|
|
12
22
|
modelName: config.model,
|
|
13
23
|
temperature: config.temperature,
|
|
14
24
|
apiKey: config.api_key, // LangChain will also check process.env.OPENAI_API_KEY
|
|
15
25
|
});
|
|
26
|
+
break;
|
|
16
27
|
case 'anthropic':
|
|
17
|
-
|
|
28
|
+
model = new ChatAnthropic({
|
|
18
29
|
modelName: config.model,
|
|
19
30
|
temperature: config.temperature,
|
|
20
31
|
apiKey: config.api_key,
|
|
21
32
|
});
|
|
33
|
+
break;
|
|
22
34
|
case 'ollama':
|
|
23
35
|
// Ollama usually runs locally, api_key optional
|
|
24
|
-
|
|
36
|
+
model = new ChatOllama({
|
|
25
37
|
model: config.model,
|
|
26
38
|
temperature: config.temperature,
|
|
27
39
|
baseUrl: config.api_key, // Sometimes users might overload api_key for base URL or similar, but simplified here
|
|
28
40
|
});
|
|
41
|
+
break;
|
|
29
42
|
case 'gemini':
|
|
30
|
-
|
|
43
|
+
model = new ChatGoogleGenerativeAI({
|
|
31
44
|
model: config.model,
|
|
32
45
|
temperature: config.temperature,
|
|
33
|
-
apiKey: config.api_key
|
|
46
|
+
apiKey: config.api_key
|
|
34
47
|
});
|
|
48
|
+
break;
|
|
35
49
|
default:
|
|
36
50
|
throw new Error(`Unsupported provider: ${config.provider}`);
|
|
37
51
|
}
|
|
52
|
+
const toolsForAgent = tools;
|
|
53
|
+
return createAgent({
|
|
54
|
+
model: model,
|
|
55
|
+
tools: toolsForAgent,
|
|
56
|
+
});
|
|
38
57
|
}
|
|
39
58
|
catch (error) {
|
|
40
59
|
let suggestion = "Check your configuration and API keys.";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { ToolsFactory } from '../factory.js';
|
|
3
|
+
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
4
|
+
vi.mock("@langchain/mcp-adapters", () => {
|
|
5
|
+
return {
|
|
6
|
+
MultiServerMCPClient: vi.fn(),
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
vi.mock("../../display.js", () => ({
|
|
10
|
+
DisplayManager: {
|
|
11
|
+
getInstance: () => ({
|
|
12
|
+
log: vi.fn(),
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
}));
|
|
16
|
+
describe('ToolsFactory', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.resetAllMocks();
|
|
19
|
+
});
|
|
20
|
+
it('should create tools successfully', async () => {
|
|
21
|
+
const mockGetTools = vi.fn().mockResolvedValue(['tool1', 'tool2']);
|
|
22
|
+
// Mock the constructor and getTools method
|
|
23
|
+
MultiServerMCPClient.mockImplementation(function () {
|
|
24
|
+
return {
|
|
25
|
+
getTools: mockGetTools
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const tools = await ToolsFactory.create();
|
|
29
|
+
expect(MultiServerMCPClient).toHaveBeenCalled();
|
|
30
|
+
expect(mockGetTools).toHaveBeenCalled();
|
|
31
|
+
expect(tools).toEqual(['tool1', 'tool2']);
|
|
32
|
+
});
|
|
33
|
+
it('should return empty array on failure', async () => {
|
|
34
|
+
MultiServerMCPClient.mockImplementation(function () {
|
|
35
|
+
return {
|
|
36
|
+
getTools: vi.fn().mockRejectedValue(new Error('MCP Failed'))
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const tools = await ToolsFactory.create();
|
|
40
|
+
expect(tools).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
2
|
+
import { DisplayManager } from "../display.js";
|
|
3
|
+
export class ToolsFactory {
|
|
4
|
+
static async create() {
|
|
5
|
+
const display = DisplayManager.getInstance();
|
|
6
|
+
const client = new MultiServerMCPClient({
|
|
7
|
+
mcpServers: {
|
|
8
|
+
coingecko: {
|
|
9
|
+
transport: "stdio",
|
|
10
|
+
command: "npx",
|
|
11
|
+
args: ["-y", "mcp_coingecko_price_ts"],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
// log the MCP client's internal events
|
|
15
|
+
beforeToolCall: ({ serverName, name, args }) => {
|
|
16
|
+
display.log(`MCP Tool Call - Server: ${serverName}, Tool: ${name}, Args: ${JSON.stringify(args)}`);
|
|
17
|
+
return;
|
|
18
|
+
},
|
|
19
|
+
// log the results of tool calls
|
|
20
|
+
afterToolCall: (res) => {
|
|
21
|
+
display.log(`MCP Tool Result - ${JSON.stringify(res)}`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
try {
|
|
26
|
+
const tools = await client.getTools();
|
|
27
|
+
return tools;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
display.log(`Failed to initialize MCP tools: ${error}`, { level: 'warning' });
|
|
31
|
+
return []; // Return empty tools on failure to allow agent to start
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/types/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Courier New,Courier,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));font-family:Courier New,Courier,monospace;--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}body:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(0 143 17 / var(--tw-bg-opacity, 1))}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.z-10{z-index:10}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-full{width:100%}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-6{--tw-translate-x: 1.5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.border{border-width:1px}.border-x{border-left-width:1px;border-right-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-matrix-highlight{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.border-matrix-primary{--tw-border-opacity: 1;border-color:rgb(0 59 0 / var(--tw-border-opacity, 1))}.border-matrix-primary\/50{border-color:#003b0080}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-900\/20{background-color:#14532d33}.bg-matrix-base{--tw-bg-opacity: 1;background-color:rgb(13 2 8 / var(--tw-bg-opacity, 1))}.bg-matrix-base\/50{background-color:#0d020880}.bg-matrix-highlight{--tw-bg-opacity: 1;background-color:rgb(0 255 65 / var(--tw-bg-opacity, 1))}.bg-matrix-highlight\/10{background-color:#00ff411a}.bg-matrix-primary{--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}.bg-matrix-primary\/20{background-color:#003b0033}.bg-matrix-primary\/50{background-color:#003b0080}.bg-red-900\/10{background-color:#7f1d1d1a}.bg-red-900\/20{background-color:#7f1d1d33}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(12 12 12 / var(--tw-bg-opacity, 1))}.bg-zinc-950\/50{background-color:#0c0c0c80}.bg-\[linear-gradient\(rgba\(18\,16\,16\,0\)_50\%\,rgba\(0\,0\,0\,0\.1\)_50\%\)\,linear-gradient\(90deg\,rgba\(0\,255\,0\,0\.03\)\,rgba\(0\,255\,0\,0\.01\)\)\]{background-image:linear-gradient(#12101000 50%,#0000001a 50%),linear-gradient(90deg,#00ff0008,#00ff0003)}.bg-\[length\:100\%_2px\,3px_100\%\]{background-size:100% 2px,3px 100%}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-px{padding-bottom:1px}.pl-4{padding-left:1rem}.pr-4{padding-right:1rem}.text-left{text-align:left}.font-mono{font-family:Courier New,Courier,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.tracking-tighter{letter-spacing:-.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-500\/80{color:#22c55ecc}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-matrix-highlight{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.text-matrix-highlight\/50{color:#00ff4180}.text-matrix-highlight\/80{color:#00ff41cc}.text-matrix-primary{--tw-text-opacity: 1;color:rgb(0 59 0 / var(--tw-text-opacity, 1))}.text-matrix-secondary{--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.placeholder-matrix-secondary\/50::-moz-placeholder{color:#008f1180}.placeholder-matrix-secondary\/50::placeholder{color:#008f1180}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.hover\:border-matrix-highlight:hover{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-matrix-highlight\/90:hover{background-color:#00ff41e6}.hover\:bg-matrix-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(0 143 17 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-900:hover{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.hover\:text-matrix-highlight:hover{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.focus\:border-matrix-highlight:focus{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.focus\:border-red-500:focus{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-matrix-highlight:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 255 65 / var(--tw-ring-opacity, 1))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity, 1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus\:ring-offset-black:focus{--tw-ring-offset-color: #000}.group:hover .group-hover\:text-matrix-highlight{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.dark\:border-matrix-primary:is(.dark *){--tw-border-opacity: 1;border-color:rgb(0 59 0 / var(--tw-border-opacity, 1))}.dark\:bg-black:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:bg-matrix-primary:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}.dark\:bg-zinc-950:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(12 12 12 / var(--tw-bg-opacity, 1))}.dark\:text-matrix-highlight:is(.dark *){--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.dark\:text-matrix-secondary:is(.dark *){--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}.dark\:opacity-20:is(.dark *){opacity:.2}.dark\:hover\:bg-matrix-primary\/50:hover:is(.dark *){background-color:#003b0080}@media(min-width:768px){.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Courier New,Courier,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));font-family:Courier New,Courier,monospace;--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}body:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(0 143 17 / var(--tw-bg-opacity, 1))}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.z-10{z-index:10}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-full{width:100%}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-6{--tw-translate-x: 1.5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.border{border-width:1px}.border-x{border-left-width:1px;border-right-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-matrix-highlight{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.border-matrix-primary{--tw-border-opacity: 1;border-color:rgb(0 59 0 / var(--tw-border-opacity, 1))}.border-matrix-primary\/50{border-color:#003b0080}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-900\/20{background-color:#14532d33}.bg-matrix-base{--tw-bg-opacity: 1;background-color:rgb(13 2 8 / var(--tw-bg-opacity, 1))}.bg-matrix-base\/50{background-color:#0d020880}.bg-matrix-highlight{--tw-bg-opacity: 1;background-color:rgb(0 255 65 / var(--tw-bg-opacity, 1))}.bg-matrix-highlight\/10{background-color:#00ff411a}.bg-matrix-primary{--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}.bg-matrix-primary\/20{background-color:#003b0033}.bg-matrix-primary\/50{background-color:#003b0080}.bg-red-900\/10{background-color:#7f1d1d1a}.bg-red-900\/20{background-color:#7f1d1d33}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(12 12 12 / var(--tw-bg-opacity, 1))}.bg-zinc-950\/50{background-color:#0c0c0c80}.bg-\[linear-gradient\(rgba\(18\,16\,16\,0\)_50\%\,rgba\(0\,0\,0\,0\.1\)_50\%\)\,linear-gradient\(90deg\,rgba\(0\,255\,0\,0\.03\)\,rgba\(0\,255\,0\,0\.01\)\)\]{background-image:linear-gradient(#12101000 50%,#0000001a 50%),linear-gradient(90deg,#00ff0008,#00ff0003)}.bg-\[length\:100\%_2px\,3px_100\%\]{background-size:100% 2px,3px 100%}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-px{padding-bottom:1px}.pl-4{padding-left:1rem}.pr-4{padding-right:1rem}.text-left{text-align:left}.font-mono{font-family:Courier New,Courier,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.tracking-tighter{letter-spacing:-.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-matrix-highlight{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.text-matrix-highlight\/50{color:#00ff4180}.text-matrix-highlight\/80{color:#00ff41cc}.text-matrix-primary{--tw-text-opacity: 1;color:rgb(0 59 0 / var(--tw-text-opacity, 1))}.text-matrix-secondary{--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.placeholder-matrix-secondary\/50::-moz-placeholder{color:#008f1180}.placeholder-matrix-secondary\/50::placeholder{color:#008f1180}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.hover\:border-matrix-highlight:hover{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-matrix-highlight\/90:hover{background-color:#00ff41e6}.hover\:bg-matrix-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(0 143 17 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-900:hover{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.hover\:text-matrix-highlight:hover{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.focus\:border-matrix-highlight:focus{--tw-border-opacity: 1;border-color:rgb(0 255 65 / var(--tw-border-opacity, 1))}.focus\:border-red-500:focus{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-matrix-highlight:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 255 65 / var(--tw-ring-opacity, 1))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity, 1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus\:ring-offset-black:focus{--tw-ring-offset-color: #000}.group:hover .group-hover\:text-matrix-highlight{--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.dark\:border-matrix-primary:is(.dark *){--tw-border-opacity: 1;border-color:rgb(0 59 0 / var(--tw-border-opacity, 1))}.dark\:bg-black:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:bg-matrix-primary:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 59 0 / var(--tw-bg-opacity, 1))}.dark\:bg-zinc-950:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(12 12 12 / var(--tw-bg-opacity, 1))}.dark\:text-matrix-highlight:is(.dark *){--tw-text-opacity: 1;color:rgb(0 255 65 / var(--tw-text-opacity, 1))}.dark\:text-matrix-secondary:is(.dark *){--tw-text-opacity: 1;color:rgb(0 143 17 / var(--tw-text-opacity, 1))}.dark\:opacity-20:is(.dark *){opacity:.2}.dark\:hover\:bg-matrix-primary\/50:hover:is(.dark *){background-color:#003b0080}@media(min-width:768px){.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|