bangonit 0.3.4 → 0.4.0
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 +24 -33
- package/app/desktopapp/dist/main/index.js +4 -2
- package/app/desktopapp/dist/main/ipc.js +96 -20
- package/app/desktopapp/dist/main/preload.js +4 -3
- package/app/desktopapp/dist/main/tabs.js +43 -3
- package/app/replay/dist/replay.css +1 -1
- package/app/replay/dist/replay.js +24 -24
- package/app/webapp/.next/standalone/app/webapp/.next/BUILD_ID +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/app-build-manifest.json +6 -6
- package/app/webapp/.next/standalone/app/webapp/.next/app-path-routes-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/build-manifest.json +2 -2
- package/app/webapp/.next/standalone/app/webapp/.next/prerender-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found.rsc +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app/page.js +3 -3
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app.rsc +2 -2
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/index.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/index.rsc +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app-paths-manifest.json +3 -3
- package/app/webapp/.next/standalone/app/webapp/.next/server/chunks/708.js +4 -3
- package/app/webapp/.next/standalone/app/webapp/.next/server/pages/404.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/pages/500.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/pages-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/server-reference-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/app/page-d6a322343ab207d9.js +1 -0
- package/app/webapp/.next/standalone/package.json +5 -2
- package/app/webapp/.next/static/chunks/app/app/page-d6a322343ab207d9.js +1 -0
- package/app/webapp/src/shared/api/chat.ts +6 -3
- package/app/webapp/src/shared/components/AppShell.tsx +6 -5
- package/app/webapp/src/shared/components/SessionView.tsx +4 -3
- package/app/webapp/src/shared/lib/browser/index.ts +17 -27
- package/app/webapp/src/shared/lib/browser/recorder.ts +13 -4
- package/app/webapp/src/shared/lib/browser/snapshot.ts +19 -5
- package/app/webapp/src/shared/lib/browser/types.ts +21 -6
- package/app/webapp/src/shared/types/global.d.ts +6 -4
- package/bin/src/cli/bangonit.js +19 -11
- package/package.json +5 -2
- package/scripts/regen-replays.sh +101 -0
- package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/app/page-df00a757f649931a.js +0 -1
- package/app/webapp/.next/static/chunks/app/app/page-df00a757f649931a.js +0 -1
- /package/app/webapp/.next/standalone/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_buildManifest.js +0 -0
- /package/app/webapp/.next/standalone/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_ssgManifest.js +0 -0
- /package/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_buildManifest.js +0 -0
- /package/app/webapp/.next/static/{bBZVP6sUilA3wyt0vYqDc → GYX27NS_2uYjr9ksgGxIo}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@ Bang On It! replaces annoying manual QA and flakey end-to-end tests with a CLI-f
|
|
|
9
9
|
```bash
|
|
10
10
|
|
|
11
11
|
# Run a single test
|
|
12
|
-
npx bangonit run \
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
npx bangonit run --plan \
|
|
13
|
+
"Go to localhost:3000, login as test@test.com (password: 12345), \
|
|
14
|
+
and click all the buttons in the dashboard and make sure they work"
|
|
15
15
|
|
|
16
16
|
# Run a suite of test plans
|
|
17
17
|
npx bangonit run testplans/*.md --concurrency 3
|
|
@@ -47,7 +47,7 @@ The problem is that traditional E2E testing is its own bottleneck. Writing Selen
|
|
|
47
47
|
|
|
48
48
|
Bang On It! removes that bottleneck. You describe what to test in plain English. An AI agent launches a real browser and executes the test — clicking buttons, filling forms, navigating pages, and verifying results. No selectors, no page objects, no flaky waits. Tests that take minutes to write instead of hours, and that don't break when you rename a CSS class because the agent interprets the website just like a real user would.
|
|
49
49
|
|
|
50
|
-
The loop is simple: agent writes code, agent tests code, human reviews the test plans. Bang On It! is the testing layer for that loop.
|
|
50
|
+
The loop is simple: agent writes code, agent tests code, human reviews the test plans. And because test plans are plain English, anyone on the team — PMs, designers, QA — can write and review them, not just engineers. Bang On It! is the testing layer for that loop.
|
|
51
51
|
|
|
52
52
|
## Bang On It vs...
|
|
53
53
|
|
|
@@ -55,53 +55,46 @@ The loop is simple: agent writes code, agent tests code, human reviews the test
|
|
|
55
55
|
|
|
56
56
|
Claude Code can drive a browser via Anthropic's computer use or third-party browser MCP servers. The approach is similar in spirit — an AI agent interacting with a real browser using natural language instructions. But Claude Code is a general-purpose coding agent, not a testing tool. It can _do_ browser testing, the same way Playwright can, but it wasn't built for it.
|
|
57
57
|
|
|
58
|
-
**Key technical differences:**
|
|
59
|
-
|
|
60
58
|
- **Purpose-built browser tooling.** Claude Code's computer use takes full-screen screenshots and uses pixel coordinates for every interaction — it's controlling a generic desktop, not a browser specifically. Bang On It! uses the browser's accessibility tree for element identification (fast, text-based, no image processing needed) and only falls back to screenshots when visual verification is required (charts, colors, layout). This hybrid approach is dramatically faster — a text snapshot is returned in milliseconds vs. capturing, transmitting, and processing a full screenshot on every single action.
|
|
61
59
|
- **Batched actions.** Claude Code executes one browser action per tool call — click, wait for response, screenshot, next action. Bang On It! batches multiple actions into a single tool call (navigate + click + type + observe) and only captures page state at the end. Fewer round-trips to the model means faster test execution.
|
|
62
|
-
- **Parallel test execution.** Claude Code runs one browser session at a time. Bang On It!
|
|
60
|
+
- **Parallel test execution.** Claude Code runs one browser session at a time. Bang On It! is a real test runner that can run N agents in parallel (`--concurrency N`), each with its own isolated browser partition — separate cookies, localStorage, and session state. A 10-test suite runs in the time of 2 tests, not 10.
|
|
63
61
|
- **Session recordings.** Bang On It! records every test run as a self-contained HTML replay with a multi-agent timeline view — video clips, console logs, and tool invocations all synced together. Share a link, not a screenshot. Claude Code has no recording capability.
|
|
64
62
|
- **Real-time UI.** Bang On It! includes a live observation UI where you can watch agents execute in real time — see the cursor move, watch pages load, monitor progress across parallel agents. Claude Code outputs text to a terminal.
|
|
65
63
|
- **CI-native.** Bang On It! generates GitHub Actions workflows out of the box, handles headless execution, uploads results as artifacts, and optionally pushes recordings to S3. Wiring Claude Code into CI for browser testing is a DIY project.
|
|
64
|
+
- **Realistic input simulation.** Claude Code's computer use moves the mouse in straight lines and types text as a single string. Bang On It! drives the browser through CDP `Input.dispatchMouseEvent` and Electron `sendInputEvent` — mouse movements follow eased Bézier-style curves, keystrokes fire individual `keyDown`/`char`/`keyUp` events with randomized 30–100ms delays. This catches hover states, drag interactions, debounced inputs, and event listeners that only trigger on real input events.
|
|
66
65
|
|
|
67
66
|
Claude Code is an excellent coding agent. But for the specific job of testing a web app in a browser — fast, in parallel, with recordings and a UI — Bang On It! is purpose-built for exactly that.
|
|
68
67
|
|
|
69
|
-
### vs
|
|
68
|
+
### vs OSS browser automation (Playwright, Selenium, Cypress)
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
These are powerful browser automation libraries — but they're _libraries_, not testing tools. You write code: selectors, page objects, explicit waits, retry logic, assertion helpers. When the UI changes, your selectors break and you're back in the maintenance treadmill.
|
|
72
71
|
|
|
73
|
-
**
|
|
72
|
+
**Development speed:**
|
|
74
73
|
|
|
75
|
-
- **No
|
|
74
|
+
- **No test code.** Test plans are plain English Markdown files. A checkout flow test is 5 lines of English, not 50–100 lines of code. PMs, designers, and QA testers can write and review test plans without learning a framework or writing a line of code.
|
|
75
|
+
- **No selectors.** Bang On It! uses the browser's accessibility tree to identify elements, the same way a screen reader does. No CSS selectors or XPath expressions to break when you rename a class or restructure your markup.
|
|
76
76
|
- **No page objects.** The AI agent interprets the page semantically on every interaction. There's no abstraction layer to keep in sync with your UI.
|
|
77
|
-
- **No explicit waits.** Bang On It! tracks real network activity via Chrome DevTools Protocol events. It knows when the page is idle — no `waitForSelector`, no `sleep(2000)`, no polling.
|
|
78
|
-
- **No test code at all.** Test plans are plain English Markdown files. A non-engineer can write and review them.
|
|
79
|
-
- **Self-correcting execution.** If a button moves, changes label, or is behind a modal, the agent adapts. A Playwright script just fails.
|
|
80
|
-
|
|
81
|
-
Playwright and Selenium dispatch actions programmatically — they call `element.click()` directly in the DOM, bypassing the browser's input pipeline entirely. Bang On It! drives the browser through real OS-level input events via Chrome DevTools Protocol. Mouse movements follow eased curves with realistic interpolation. Keystrokes fire individual `keyDown`, `char`, and `keyUp` events with randomized inter-key delays (30–100ms). This matters because many modern apps have hover states, drag interactions, debounced inputs, and event listeners that only trigger on real input events — not synthetic DOM calls.
|
|
82
77
|
|
|
83
|
-
|
|
78
|
+
**Debuggability:**
|
|
84
79
|
|
|
85
|
-
|
|
80
|
+
- **Session recordings.** Every test run can be recorded as a self-contained HTML replay with video clips, console logs, and tool invocations all synced on a timeline. Share a link, not a log file.
|
|
81
|
+
- **Real-time UI.** Watch agents execute live — see the cursor move, watch pages load, monitor progress across parallel agents. Playwright gives you a trace viewer after the fact; Bang On It! lets you watch in real time.
|
|
82
|
+
- **Real browser.** Bang On It! launches actual Chromium via Electron. Multi-tab, cross-origin, OAuth popups, file downloads — all work naturally. Cypress runs in an iframe and can't test these natively.
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
**De-flaking:**
|
|
88
85
|
|
|
89
|
-
**
|
|
90
|
-
|
|
91
|
-
- **
|
|
92
|
-
- **No selector coupling.** Cypress tests break when `data-cy` attributes are removed or DOM structure changes. Bang On It! reads the page like a user does — through its semantic structure, not its implementation details.
|
|
93
|
-
- **Parallel isolation.** Each Bang On It! agent runs in its own browser partition with fully isolated cookies, localStorage, and session state. Cypress parallelization requires Dashboard (paid) and splits at the file level.
|
|
94
|
-
- **Minutes to write vs hours.** A Cypress test for a checkout flow is 50-100 lines of code. The equivalent Bang On It! test plan is 5 lines of English.
|
|
95
|
-
- **Realistic input simulation.** Cypress uses `cy.click()` and `cy.type()` which dispatch synthetic jQuery-style events. Bang On It! sends real input events through Chrome DevTools Protocol — eased mouse curves, per-character keystroke events with natural timing — catching bugs that only surface with real user interaction patterns.
|
|
86
|
+
- **No explicit waits.** Bang On It! tracks real network activity via Chrome DevTools Protocol. It knows when the page is idle — no `waitForSelector`, no `sleep(2000)`, no polling.
|
|
87
|
+
- **Self-correcting execution.** If a button moves, changes label, or is behind a modal, the agent adapts. A Playwright script just fails.
|
|
88
|
+
- **Realistic input simulation.** These tools dispatch events synthetically — `element.click()` and `element.value = "text"` bypass the browser's input pipeline entirely. Bang On It! sends real input events through CDP — eased mouse curves, per-character keystroke events with natural timing, real `mouseWheel` events for scrolling. This catches hover menus that don't open, inputs that don't validate on blur, drag-and-drop that doesn't work, and custom components that listen for native events.
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
These tools are great for building custom browser automation. But if the goal is _testing_, you're writing and maintaining a lot of infrastructure that Bang On It! eliminates entirely.
|
|
98
91
|
|
|
99
|
-
|
|
92
|
+
### vs Commercial AI record-and-replay
|
|
100
93
|
|
|
101
|
-
|
|
94
|
+
These tools use AI to help you create and maintain traditional selector-based tests — typically through record-and-replay with smart locators that auto-heal when elements move. They reduce maintenance, but the underlying model is still the same: a recorded script of UI interactions replayed deterministically.
|
|
102
95
|
|
|
103
96
|
- **Generative, not recorded.** Bang On It! doesn't record and replay a fixed script. The AI agent reads your test plan and _decides_ how to execute it on each run. If the UI changes, the agent figures out the new path — it doesn't try to heal a stale recording.
|
|
104
|
-
- **No vendor lock-in.** Test plans are Markdown files in your repo.
|
|
97
|
+
- **No vendor lock-in.** Test plans are Markdown files in your repo. No proprietary test format, no cloud dashboard required, no per-seat pricing. You own your tests.
|
|
105
98
|
- **Runs locally and in CI.** These tools are typically cloud-hosted services. Bang On It! runs on your machine or in your own CI pipeline. Your app never leaves your network.
|
|
106
99
|
- **Understands intent, not just actions.** A recorded test replays "click the third button in the sidebar." Bang On It! executes "verify the user can navigate to settings" — if the settings link moves from the sidebar to a top nav, the test still passes.
|
|
107
100
|
|
|
@@ -109,7 +102,7 @@ These tools use AI to _help_ you create and maintain traditional selector-based
|
|
|
109
102
|
|
|
110
103
|
Manual QA catches things automated tests miss — but it doesn't scale, it's slow, and humans get tired. The same tester clicking through the same flow for the 50th time will miss things.
|
|
111
104
|
|
|
112
|
-
Bang On It! runs the same tests with the same thoroughness every time, in parallel, in seconds. Write the test plan once, run it on every PR. Keep manual QA for exploratory testing where human judgment matters
|
|
105
|
+
Bang On It! runs the same tests with the same thoroughness every time, in parallel, in seconds. Write the test plan once, run it on every PR. And because test plans are plain English, your QA team can write them directly — translating their domain knowledge into automated tests without waiting on engineering. Keep manual QA for exploratory testing where human judgment matters; let Bang On It! handle the repetitive verification.
|
|
113
106
|
|
|
114
107
|
### Performance and realistic simulation
|
|
115
108
|
|
|
@@ -202,8 +195,6 @@ You write test plans in plain English (or Markdown). Bang On It! spins up a real
|
|
|
202
195
|
4. Observes the results via DOM snapshots and screenshots
|
|
203
196
|
5. Reports pass/fail with a summary of what happened
|
|
204
197
|
|
|
205
|
-
No selectors. No page objects. No flaky waits. Just describe what to test and let it bang on your app.
|
|
206
|
-
|
|
207
198
|
## Test Plans
|
|
208
199
|
|
|
209
200
|
Test plans are Markdown files. `boi init` creates a recommended two-tier structure:
|
|
@@ -187,8 +187,6 @@ async function createWindow() {
|
|
|
187
187
|
const recordDir = path.join(baseDir, timestamp);
|
|
188
188
|
fs.mkdirSync(recordDir, { recursive: true });
|
|
189
189
|
(0, ipc_1.setRunDir)(recordDir);
|
|
190
|
-
if (!cliArgs.json)
|
|
191
|
-
console.log(`Recording to: ${recordDir}`);
|
|
192
190
|
}
|
|
193
191
|
mainWindow.loadURL((0, ipc_1.getWebappUrl)());
|
|
194
192
|
// Once webapp is loaded, send test plans
|
|
@@ -198,16 +196,19 @@ async function createWindow() {
|
|
|
198
196
|
if (cliArgs.testPlanFiles.length > 0 || cliArgs.plan) {
|
|
199
197
|
const plans = [];
|
|
200
198
|
const names = [];
|
|
199
|
+
const planDirs = [];
|
|
201
200
|
// Inline plan from --plan flag
|
|
202
201
|
if (cliArgs.plan) {
|
|
203
202
|
plans.push(cliArgs.plan);
|
|
204
203
|
names.push("inline");
|
|
204
|
+
planDirs.push(undefined);
|
|
205
205
|
}
|
|
206
206
|
// Plans from files
|
|
207
207
|
for (const file of cliArgs.testPlanFiles) {
|
|
208
208
|
const absPath = path.isAbsolute(file) ? file : path.join(cliArgs.cwd, file);
|
|
209
209
|
try {
|
|
210
210
|
plans.push(fs.readFileSync(absPath, "utf-8"));
|
|
211
|
+
planDirs.push(path.dirname(absPath));
|
|
211
212
|
}
|
|
212
213
|
catch (err) {
|
|
213
214
|
console.error(`Error reading test plan ${file}: ${err.message}`);
|
|
@@ -227,6 +228,7 @@ async function createWindow() {
|
|
|
227
228
|
retries: parsed[i].frontmatter.retries ?? cliArgs.retries,
|
|
228
229
|
extraPrompt: cliArgs.prompt || undefined,
|
|
229
230
|
record: cliArgs.record || undefined,
|
|
231
|
+
planDir: planDirs[i] || undefined,
|
|
230
232
|
});
|
|
231
233
|
}
|
|
232
234
|
}
|
|
@@ -50,11 +50,21 @@ const path = __importStar(require("path"));
|
|
|
50
50
|
const fs = __importStar(require("fs"));
|
|
51
51
|
const electron_2 = require("electron");
|
|
52
52
|
const electron_store_1 = __importDefault(require("electron-store"));
|
|
53
|
+
const child_process_1 = require("child_process");
|
|
53
54
|
const tabs_1 = require("./tabs");
|
|
54
55
|
const store = new electron_store_1.default();
|
|
55
56
|
let mainWindow = null;
|
|
56
57
|
let bangerCwd = process.cwd();
|
|
57
58
|
let runDir = null;
|
|
59
|
+
function findGitRoot(from) {
|
|
60
|
+
try {
|
|
61
|
+
return (0, child_process_1.execSync)("git rev-parse --show-toplevel", { cwd: from, stdio: ["ignore", "pipe", "ignore"] })
|
|
62
|
+
.toString().trim();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
58
68
|
function clearStore() {
|
|
59
69
|
store.clear();
|
|
60
70
|
}
|
|
@@ -78,9 +88,11 @@ function getWebappUrl() {
|
|
|
78
88
|
}
|
|
79
89
|
function registerIpcHandlers() {
|
|
80
90
|
// --- Tab management ---
|
|
81
|
-
electron_1.ipcMain.handle("register-tab", (_, { agentId, tabId, wcId, initialUrl }) => {
|
|
91
|
+
electron_1.ipcMain.handle("register-tab", (_, { agentId, tabId, wcId, initialUrl, planDir }) => {
|
|
82
92
|
const ctx = (0, tabs_1.getOrCreateContext)(agentId);
|
|
83
93
|
(0, tabs_1.setDataDir)(ctx, path.join(electron_2.app.getPath("userData"), "browser", "shared"));
|
|
94
|
+
if (planDir)
|
|
95
|
+
ctx.planDir = planDir;
|
|
84
96
|
(0, tabs_1.addTab)(ctx, tabId, wcId, sendToRenderer, agentId, initialUrl);
|
|
85
97
|
});
|
|
86
98
|
electron_1.ipcMain.handle("set-active-tab", (_, { agentId, tabId }) => {
|
|
@@ -196,6 +208,35 @@ function registerIpcHandlers() {
|
|
|
196
208
|
const ctx = (0, tabs_1.getContext)(agentId);
|
|
197
209
|
return ctx?.downloads || [];
|
|
198
210
|
});
|
|
211
|
+
// --- File chooser ---
|
|
212
|
+
electron_1.ipcMain.handle("handle-file-chooser", (_, { agentId, action, files }) => {
|
|
213
|
+
const { handleFileChooser } = require("./tabs");
|
|
214
|
+
if (action === "cancel") {
|
|
215
|
+
return handleFileChooser(agentId, "cancel");
|
|
216
|
+
}
|
|
217
|
+
const ctx = (0, tabs_1.getContext)(agentId);
|
|
218
|
+
const baseDir = ctx?.planDir || bangerCwd;
|
|
219
|
+
const sandboxRoot = findGitRoot(baseDir) || baseDir;
|
|
220
|
+
const resolved = files?.map((f) => {
|
|
221
|
+
const abs = path.isAbsolute(f) ? path.resolve(f) : path.resolve(baseDir, f);
|
|
222
|
+
let real;
|
|
223
|
+
try {
|
|
224
|
+
real = fs.realpathSync(abs);
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
throw new Error(`Upload file not found: ${abs}`);
|
|
228
|
+
}
|
|
229
|
+
if (!real.startsWith(sandboxRoot + path.sep) && real !== sandboxRoot) {
|
|
230
|
+
throw new Error(`Upload path "${f}" escapes sandbox root ${sandboxRoot}`);
|
|
231
|
+
}
|
|
232
|
+
return real;
|
|
233
|
+
});
|
|
234
|
+
return handleFileChooser(agentId, "accept", resolved);
|
|
235
|
+
});
|
|
236
|
+
electron_1.ipcMain.handle("get-file-chooser-state", (_, agentId) => {
|
|
237
|
+
const { getFileChooserState } = require("./tabs");
|
|
238
|
+
return getFileChooserState(agentId);
|
|
239
|
+
});
|
|
199
240
|
// --- Agent CRUD ---
|
|
200
241
|
electron_1.ipcMain.handle("get-agents", () => store.get("agents", []));
|
|
201
242
|
electron_1.ipcMain.handle("set-agents", (_, agents) => store.set("agents", agents));
|
|
@@ -236,22 +277,28 @@ function registerIpcHandlers() {
|
|
|
236
277
|
sendToRenderer(data.type, data);
|
|
237
278
|
});
|
|
238
279
|
// --- Recording ---
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
fs.mkdirSync(clipDir, { recursive: true });
|
|
242
|
-
const clipPath = path.join(clipDir, `clip-${opts.clipIndex}-tab-${opts.tabId}.webm`);
|
|
243
|
-
fs.writeFileSync(clipPath, Buffer.from(opts.data));
|
|
244
|
-
});
|
|
245
|
-
electron_1.ipcMain.handle("save-replay-data", async (_, opts) => {
|
|
246
|
-
fs.mkdirSync(opts.runDir, { recursive: true });
|
|
247
|
-
fs.writeFileSync(path.join(opts.runDir, "data.json"), opts.data);
|
|
248
|
-
});
|
|
280
|
+
// Generates one HTML file per agent + an index.html redirect.
|
|
281
|
+
// Each agent HTML is self-contained with base64-encoded video clips.
|
|
249
282
|
electron_1.ipcMain.handle("generate-replay-html", async (_, opts) => {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
283
|
+
fs.mkdirSync(opts.runDir, { recursive: true });
|
|
284
|
+
const parsed = JSON.parse(opts.data);
|
|
285
|
+
const agents = parsed.agents || [];
|
|
286
|
+
// Build slug map: agent id → filename
|
|
287
|
+
const slugify = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "agent";
|
|
288
|
+
const usedSlugs = new Set();
|
|
289
|
+
const agentFiles = {};
|
|
290
|
+
for (const agent of agents) {
|
|
291
|
+
let slug = slugify(agent.name);
|
|
292
|
+
if (usedSlugs.has(slug)) {
|
|
293
|
+
let i = 2;
|
|
294
|
+
while (usedSlugs.has(`${slug}-${i}`))
|
|
295
|
+
i++;
|
|
296
|
+
slug = `${slug}-${i}`;
|
|
297
|
+
}
|
|
298
|
+
usedSlugs.add(slug);
|
|
299
|
+
agentFiles[agent.id] = `${slug}.html`;
|
|
300
|
+
}
|
|
301
|
+
// Load pre-built replay viewer bundle
|
|
255
302
|
let replayJs = "";
|
|
256
303
|
let replayCss = "";
|
|
257
304
|
const candidates = [
|
|
@@ -268,21 +315,50 @@ function registerIpcHandlers() {
|
|
|
268
315
|
if (fs.existsSync(replayCssPath)) {
|
|
269
316
|
replayCss = fs.readFileSync(replayCssPath, "utf-8");
|
|
270
317
|
}
|
|
271
|
-
|
|
318
|
+
// Generate one HTML per agent
|
|
319
|
+
for (const agent of agents) {
|
|
320
|
+
const agentData = {
|
|
321
|
+
...parsed,
|
|
322
|
+
focusAgentId: agent.id,
|
|
323
|
+
agentFiles,
|
|
324
|
+
// Only include this agent's clips (with base64 data)
|
|
325
|
+
clips: (parsed.clips || []).filter((c) => c.agentId === agent.id),
|
|
326
|
+
// Only include this agent's messages and console logs
|
|
327
|
+
messages: { [agent.id]: parsed.messages?.[agent.id] || [] },
|
|
328
|
+
consoleLogs: { [agent.id]: parsed.consoleLogs?.[agent.id] || [] },
|
|
329
|
+
// Keep full timeline for time range, but filter to this agent
|
|
330
|
+
timeline: (parsed.timeline || []).filter((e) => e.agentId === agent.id),
|
|
331
|
+
};
|
|
332
|
+
const html = `<!DOCTYPE html>
|
|
272
333
|
<html lang="en">
|
|
273
334
|
<head>
|
|
274
335
|
<meta charset="UTF-8">
|
|
275
336
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
276
|
-
<title
|
|
337
|
+
<title>${agent.name} - Session Replay</title>
|
|
277
338
|
<style>${replayCss}</style>
|
|
278
339
|
</head>
|
|
279
340
|
<body>
|
|
280
341
|
<div id="root"></div>
|
|
281
|
-
<script>window.__REPLAY_DATA__ = ${
|
|
342
|
+
<script>window.__REPLAY_DATA__ = ${JSON.stringify(agentData)};</script>
|
|
282
343
|
<script>${replayJs}</script>
|
|
283
344
|
</body>
|
|
284
345
|
</html>`;
|
|
285
|
-
|
|
346
|
+
fs.writeFileSync(path.join(opts.runDir, agentFiles[agent.id]), html);
|
|
347
|
+
}
|
|
348
|
+
// index.html redirects to first agent
|
|
349
|
+
const firstFile = agents.length > 0 ? agentFiles[agents[0].id] : "";
|
|
350
|
+
const indexHtml = `<!DOCTYPE html>
|
|
351
|
+
<html lang="en">
|
|
352
|
+
<head>
|
|
353
|
+
<meta charset="UTF-8">
|
|
354
|
+
<meta http-equiv="refresh" content="0; url=${firstFile}">
|
|
355
|
+
<title>Session Replay</title>
|
|
356
|
+
</head>
|
|
357
|
+
<body>
|
|
358
|
+
<a href="${firstFile}">Open replay</a>
|
|
359
|
+
</body>
|
|
360
|
+
</html>`;
|
|
361
|
+
fs.writeFileSync(path.join(opts.runDir, "index.html"), indexHtml);
|
|
286
362
|
});
|
|
287
363
|
electron_1.ipcMain.handle("get-run-dir", () => runDir);
|
|
288
364
|
}
|
|
@@ -27,7 +27,7 @@ contextBridge.exposeInMainWorld("bangonit", {
|
|
|
27
27
|
goForward: (agentId) => ipcRenderer.invoke("go-forward", agentId),
|
|
28
28
|
getURL: (agentId) => ipcRenderer.invoke("get-url", agentId),
|
|
29
29
|
// Tab management
|
|
30
|
-
registerTab: (agentId, tabId, wcId, initialUrl) => ipcRenderer.invoke("register-tab", { agentId, tabId, wcId, initialUrl }),
|
|
30
|
+
registerTab: (agentId, tabId, wcId, initialUrl, planDir) => ipcRenderer.invoke("register-tab", { agentId, tabId, wcId, initialUrl, planDir }),
|
|
31
31
|
setActiveTab: (agentId, tabId) => ipcRenderer.invoke("set-active-tab", { agentId, tabId }),
|
|
32
32
|
getTabInfo: (agentId) => ipcRenderer.invoke("get-tab-info", agentId),
|
|
33
33
|
requestNewTab: (agentId, url) => ipcRenderer.invoke("request-new-tab", { agentId, url }),
|
|
@@ -35,6 +35,9 @@ contextBridge.exposeInMainWorld("bangonit", {
|
|
|
35
35
|
requestSelectTab: (agentId, tabId) => ipcRenderer.invoke("request-select-tab", { agentId, tabId }),
|
|
36
36
|
// Downloads
|
|
37
37
|
getDownloads: (agentId) => ipcRenderer.invoke("get-downloads", agentId),
|
|
38
|
+
// File chooser
|
|
39
|
+
handleFileChooser: (agentId, action, files) => ipcRenderer.invoke("handle-file-chooser", { agentId, action, files }),
|
|
40
|
+
getFileChooserState: (agentId) => ipcRenderer.invoke("get-file-chooser-state", agentId),
|
|
38
41
|
// Events from main process
|
|
39
42
|
onTabUpdated: (cb) => onEvent("tab-updated", cb),
|
|
40
43
|
onOpenNewTab: (cb) => onEvent("open-new-tab", cb),
|
|
@@ -70,8 +73,6 @@ contextBridge.exposeInMainWorld("bangonit", {
|
|
|
70
73
|
// Bang On It!: forward browser console messages to main
|
|
71
74
|
emitConsoleMessage: (data) => ipcRenderer.send("console-message", data),
|
|
72
75
|
// Recording
|
|
73
|
-
saveRecordingClip: (opts) => ipcRenderer.invoke("save-recording-clip", opts),
|
|
74
|
-
saveReplayData: (opts) => ipcRenderer.invoke("save-replay-data", opts),
|
|
75
76
|
generateReplayHtml: (opts) => ipcRenderer.invoke("generate-replay-html", opts),
|
|
76
77
|
getRunDir: () => ipcRenderer.invoke("get-run-dir"),
|
|
77
78
|
});
|
|
@@ -41,6 +41,8 @@ exports.destroyContext = destroyContext;
|
|
|
41
41
|
exports.allContexts = allContexts;
|
|
42
42
|
exports.setDataDir = setDataDir;
|
|
43
43
|
exports.addTab = addTab;
|
|
44
|
+
exports.handleFileChooser = handleFileChooser;
|
|
45
|
+
exports.getFileChooserState = getFileChooserState;
|
|
44
46
|
exports.getActiveWc = getActiveWc;
|
|
45
47
|
exports.getActiveTab = getActiveTab;
|
|
46
48
|
const electron_1 = require("electron");
|
|
@@ -150,10 +152,12 @@ function setupCdp(wc, sendToRenderer, agentId, tabId) {
|
|
|
150
152
|
method === "Page.screencastFrame") {
|
|
151
153
|
sendToRenderer("cdp-event", { agentId, tabId, method, params });
|
|
152
154
|
}
|
|
153
|
-
//
|
|
155
|
+
// Track file chooser dialogs — agent can accept via browser_upload tool
|
|
154
156
|
if (method === "Page.fileChooserOpened") {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
const ctx = agentContexts.get(agentId);
|
|
158
|
+
if (ctx) {
|
|
159
|
+
ctx.pendingFileChooser = { tabId, mode: params.mode, backendNodeId: params.backendNodeId };
|
|
160
|
+
}
|
|
157
161
|
}
|
|
158
162
|
});
|
|
159
163
|
}
|
|
@@ -305,6 +309,42 @@ function addTab(ctx, tabId, wcId, sendToRenderer, agentId, initialUrl) {
|
|
|
305
309
|
wc.loadURL(initialUrl);
|
|
306
310
|
}
|
|
307
311
|
}
|
|
312
|
+
async function handleFileChooser(agentId, action, files) {
|
|
313
|
+
const ctx = agentContexts.get(agentId);
|
|
314
|
+
if (!ctx)
|
|
315
|
+
return { ok: false, error: "No agent context" };
|
|
316
|
+
if (!ctx.pendingFileChooser)
|
|
317
|
+
return { ok: false, error: "No file picker is open" };
|
|
318
|
+
const tab = ctx.tabs.get(ctx.pendingFileChooser.tabId);
|
|
319
|
+
if (!tab)
|
|
320
|
+
return { ok: false, error: "Tab no longer exists" };
|
|
321
|
+
if (action === "cancel") {
|
|
322
|
+
ctx.pendingFileChooser = null;
|
|
323
|
+
return { ok: true };
|
|
324
|
+
}
|
|
325
|
+
if (!files?.length) {
|
|
326
|
+
return { ok: false, error: "No files provided" };
|
|
327
|
+
}
|
|
328
|
+
const { backendNodeId } = ctx.pendingFileChooser;
|
|
329
|
+
try {
|
|
330
|
+
await tab.wc.debugger.sendCommand("DOM.setFileInputFiles", {
|
|
331
|
+
files,
|
|
332
|
+
backendNodeId,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
catch (e) {
|
|
336
|
+
console.error("[tabs] DOM.setFileInputFiles failed:", e.message);
|
|
337
|
+
return { ok: false, error: e.message };
|
|
338
|
+
}
|
|
339
|
+
ctx.pendingFileChooser = null;
|
|
340
|
+
return { ok: true };
|
|
341
|
+
}
|
|
342
|
+
function getFileChooserState(agentId) {
|
|
343
|
+
const ctx = agentContexts.get(agentId);
|
|
344
|
+
if (!ctx?.pendingFileChooser)
|
|
345
|
+
return { open: false };
|
|
346
|
+
return { open: true, mode: ctx.pendingFileChooser.mode };
|
|
347
|
+
}
|
|
308
348
|
function getActiveWc(agentId) {
|
|
309
349
|
const ctx = agentContexts.get(agentId);
|
|
310
350
|
if (!ctx || ctx.activeTabId === null)
|
|
@@ -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:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,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}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: rgb(17 24 39 / 10%);--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: rgb(255 255 255 / 10%);--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-invert{--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.top-0{top:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-1\.5{margin-left:.375rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-2{height:.5rem}.h-6{height:1.5rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:8rem}.max-h-60{max-height:15rem}.max-h-full{max-height:100%}.min-h-0{min-height:0px}.w-0\.5{width:.125rem}.w-2{width:.5rem}.w-48{width:12rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-full{max-width:100%}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.rotate-90{--tw-rotate: 90deg;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-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-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-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-zinc-500{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.border-zinc-800{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-950\/50{background-color:#451a0380}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-600\/30{background-color:#2563eb4d}.bg-blue-900\/60{background-color:#1e3a8a99}.bg-blue-950\/30{background-color:#1725544d}.bg-green-950\/50{background-color:#052e1680}.bg-red-950\/50{background-color:#450a0a80}.bg-zinc-700{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / 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-900\/50{background-color:#18181b80}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-zinc-200{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.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-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-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}body{margin:0;background:#09090b;color:#d4d4d8;font-family:ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}.hover\:border-zinc-500:hover{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.hover\:border-zinc-700:hover{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.hover\:text-zinc-400:hover{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}@media (min-width: 768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-\[420px\]{width:420px}.md\:shrink-0{flex-shrink:0}.md\:gap-3{gap:.75rem}.md\:border-r{border-right-width:1px}.md\:px-2{padding-left:.5rem;padding-right:.5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:py-1{padding-top:.25rem;padding-bottom:.25rem}.md\:py-3{padding-top:.75rem;padding-bottom:.75rem}}
|
|
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:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,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}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows),0 3px 0 var(--tw-prose-kbd-shadows);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: rgb(17 24 39 / 10%);--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: rgb(255 255 255 / 10%);--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-invert{--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.top-0{top:0}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-1\.5{margin-left:.375rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-2{height:.5rem}.h-6{height:1.5rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:8rem}.max-h-60{max-height:15rem}.max-h-full{max-height:100%}.min-h-0{min-height:0px}.w-0\.5{width:.125rem}.w-2{width:.5rem}.w-48{width:12rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-full{max-width:100%}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.rotate-90{--tw-rotate: 90deg;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-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-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-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-zinc-500{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.border-zinc-800{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-950\/50{background-color:#451a0380}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-600\/30{background-color:#2563eb4d}.bg-blue-900\/60{background-color:#1e3a8a99}.bg-blue-950\/30{background-color:#1725544d}.bg-green-950\/50{background-color:#052e1680}.bg-red-950\/50{background-color:#450a0a80}.bg-zinc-700{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / 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-900\/50{background-color:#18181b80}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-zinc-200{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.no-underline{text-decoration-line:none}.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-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-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}body{margin:0;background:#09090b;color:#d4d4d8;font-family:ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}.hover\:border-zinc-500:hover{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.hover\:border-zinc-700:hover{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.hover\:text-zinc-400:hover{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}@media (min-width: 768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-\[420px\]{width:420px}.md\:shrink-0{flex-shrink:0}.md\:gap-3{gap:.75rem}.md\:border-r{border-right-width:1px}.md\:px-2{padding-left:.5rem;padding-right:.5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:py-1{padding-top:.25rem;padding-bottom:.25rem}.md\:py-3{padding-top:.75rem;padding-bottom:.75rem}}
|