@stigg/terminal 0.0.1-alpha
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/LICENSE +15 -0
- package/README.md +47 -0
- package/dist/api/client.d.ts +6 -0
- package/dist/api/client.js +48 -0
- package/dist/api/format-key.d.ts +7 -0
- package/dist/api/format-key.js +12 -0
- package/dist/api/graphql-client.d.ts +5 -0
- package/dist/api/graphql-client.js +44 -0
- package/dist/api/operations.d.ts +65 -0
- package/dist/api/operations.js +77 -0
- package/dist/api/types.d.ts +18 -0
- package/dist/api/types.js +1 -0
- package/dist/auth/callback-server.d.ts +14 -0
- package/dist/auth/callback-server.js +145 -0
- package/dist/auth/config.d.ts +2 -0
- package/dist/auth/config.js +24 -0
- package/dist/auth/oauth.d.ts +17 -0
- package/dist/auth/oauth.js +94 -0
- package/dist/auth/storage.d.ts +6 -0
- package/dist/auth/storage.js +34 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +3 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +79 -0
- package/dist/commands/dash.d.ts +1 -0
- package/dist/commands/dash.js +24 -0
- package/dist/commands/debug.d.ts +1 -0
- package/dist/commands/debug.js +53 -0
- package/dist/commands/env.d.ts +1 -0
- package/dist/commands/env.js +15 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +59 -0
- package/dist/commands/mcp.d.ts +7 -0
- package/dist/commands/mcp.js +37 -0
- package/dist/commands/skills.d.ts +6 -0
- package/dist/commands/skills.js +48 -0
- package/dist/headless/host-agent.d.ts +19 -0
- package/dist/headless/host-agent.js +64 -0
- package/dist/headless/init-phase1.d.ts +9 -0
- package/dist/headless/init-phase1.js +173 -0
- package/dist/headless/init-phase2.d.ts +36 -0
- package/dist/headless/init-phase2.js +150 -0
- package/dist/headless/next-step-prompt.d.ts +7 -0
- package/dist/headless/next-step-prompt.js +25 -0
- package/dist/headless/options.d.ts +30 -0
- package/dist/headless/options.js +77 -0
- package/dist/headless/reporter.d.ts +16 -0
- package/dist/headless/reporter.js +41 -0
- package/dist/headless/setup.d.ts +29 -0
- package/dist/headless/setup.js +80 -0
- package/dist/launch/agent.d.ts +55 -0
- package/dist/launch/agent.js +134 -0
- package/dist/mcp/clients/base.d.ts +49 -0
- package/dist/mcp/clients/base.js +66 -0
- package/dist/mcp/clients/claude-code.d.ts +22 -0
- package/dist/mcp/clients/claude-code.js +120 -0
- package/dist/mcp/clients/claude-desktop.d.ts +9 -0
- package/dist/mcp/clients/claude-desktop.js +35 -0
- package/dist/mcp/clients/codex.d.ts +13 -0
- package/dist/mcp/clients/codex.js +113 -0
- package/dist/mcp/clients/cursor.d.ts +9 -0
- package/dist/mcp/clients/cursor.js +26 -0
- package/dist/mcp/clients/index.d.ts +7 -0
- package/dist/mcp/clients/index.js +27 -0
- package/dist/mcp/clients/mcp-remote.d.ts +11 -0
- package/dist/mcp/clients/mcp-remote.js +13 -0
- package/dist/mcp/clients/vscode.d.ts +17 -0
- package/dist/mcp/clients/vscode.js +84 -0
- package/dist/mcp/clients.d.ts +4 -0
- package/dist/mcp/clients.js +50 -0
- package/dist/mcp/config-merge.d.ts +9 -0
- package/dist/mcp/config-merge.js +51 -0
- package/dist/mcp/writer.d.ts +6 -0
- package/dist/mcp/writer.js +65 -0
- package/dist/setup/storage.d.ts +21 -0
- package/dist/setup/storage.js +34 -0
- package/dist/skills/install.d.ts +19 -0
- package/dist/skills/install.js +64 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.js +1 -0
- package/dist/ui/components/Card.d.ts +11 -0
- package/dist/ui/components/Card.js +19 -0
- package/dist/ui/components/ContextRow.d.ts +11 -0
- package/dist/ui/components/ContextRow.js +8 -0
- package/dist/ui/components/Footer.d.ts +10 -0
- package/dist/ui/components/Footer.js +9 -0
- package/dist/ui/components/Header.d.ts +11 -0
- package/dist/ui/components/Header.js +6 -0
- package/dist/ui/components/JsonPreview.d.ts +13 -0
- package/dist/ui/components/JsonPreview.js +25 -0
- package/dist/ui/components/SectionTitle.d.ts +6 -0
- package/dist/ui/components/SectionTitle.js +5 -0
- package/dist/ui/hooks/useAsyncEffect.d.ts +3 -0
- package/dist/ui/hooks/useAsyncEffect.js +20 -0
- package/dist/ui/hooks/useResize.d.ts +4 -0
- package/dist/ui/hooks/useResize.js +20 -0
- package/dist/ui/hud.d.ts +15 -0
- package/dist/ui/hud.js +30 -0
- package/dist/ui/ink-theme.d.ts +2 -0
- package/dist/ui/ink-theme.js +34 -0
- package/dist/ui/intro/LogoView.d.ts +12 -0
- package/dist/ui/intro/LogoView.js +226 -0
- package/dist/ui/intro/MatrixIntro.d.ts +6 -0
- package/dist/ui/intro/MatrixIntro.js +80 -0
- package/dist/ui/intro/logo.d.ts +6 -0
- package/dist/ui/intro/logo.js +21 -0
- package/dist/ui/messages.d.ts +5 -0
- package/dist/ui/messages.js +5 -0
- package/dist/ui/screens/DashScreen.d.ts +6 -0
- package/dist/ui/screens/DashScreen.js +27 -0
- package/dist/ui/screens/DebugScreen.d.ts +6 -0
- package/dist/ui/screens/DebugScreen.js +39 -0
- package/dist/ui/screens/InitScreen.d.ts +6 -0
- package/dist/ui/screens/InitScreen.js +138 -0
- package/dist/ui/screens/MenuScreen.d.ts +7 -0
- package/dist/ui/screens/MenuScreen.js +38 -0
- package/dist/ui/state.d.ts +72 -0
- package/dist/ui/state.js +107 -0
- package/dist/ui/steps/AccountStep.d.ts +8 -0
- package/dist/ui/steps/AccountStep.js +42 -0
- package/dist/ui/steps/ApiKeyStep.d.ts +8 -0
- package/dist/ui/steps/ApiKeyStep.js +91 -0
- package/dist/ui/steps/ClientsStep.d.ts +10 -0
- package/dist/ui/steps/ClientsStep.js +69 -0
- package/dist/ui/steps/CredentialKindStep.d.ts +7 -0
- package/dist/ui/steps/CredentialKindStep.js +18 -0
- package/dist/ui/steps/EnvironmentStep.d.ts +8 -0
- package/dist/ui/steps/EnvironmentStep.js +37 -0
- package/dist/ui/steps/LoginStep.d.ts +7 -0
- package/dist/ui/steps/LoginStep.js +56 -0
- package/dist/ui/steps/SkillsStep.d.ts +7 -0
- package/dist/ui/steps/SkillsStep.js +7 -0
- package/dist/ui/steps/SummaryStep.d.ts +8 -0
- package/dist/ui/steps/SummaryStep.js +41 -0
- package/dist/ui/steps/WritingStep.d.ts +10 -0
- package/dist/ui/steps/WritingStep.js +96 -0
- package/dist/ui/theme.d.ts +53 -0
- package/dist/ui/theme.js +66 -0
- package/dist/ui/tui/App.d.ts +10 -0
- package/dist/ui/tui/App.js +51 -0
- package/dist/ui/tui/components/ContextStrip.d.ts +11 -0
- package/dist/ui/tui/components/ContextStrip.js +8 -0
- package/dist/ui/tui/components/TitleBar.d.ts +6 -0
- package/dist/ui/tui/components/TitleBar.js +18 -0
- package/dist/ui/tui/components/WizardChecklist.d.ts +7 -0
- package/dist/ui/tui/components/WizardChecklist.js +69 -0
- package/dist/ui/tui/hooks/keyboard-hints-utils.d.ts +26 -0
- package/dist/ui/tui/hooks/keyboard-hints-utils.js +69 -0
- package/dist/ui/tui/hooks/useKeyBindings.d.ts +14 -0
- package/dist/ui/tui/hooks/useKeyBindings.js +44 -0
- package/dist/ui/tui/hooks/useKeyboardHints.d.ts +13 -0
- package/dist/ui/tui/hooks/useKeyboardHints.js +38 -0
- package/dist/ui/tui/hooks/useStdoutDimensions.d.ts +8 -0
- package/dist/ui/tui/hooks/useStdoutDimensions.js +28 -0
- package/dist/ui/tui/primitives/BlinkingLabel.d.ts +19 -0
- package/dist/ui/tui/primitives/BlinkingLabel.js +25 -0
- package/dist/ui/tui/primitives/ConfirmPrompt.d.ts +16 -0
- package/dist/ui/tui/primitives/ConfirmPrompt.js +36 -0
- package/dist/ui/tui/primitives/KeyboardHintsBar.d.ts +2 -0
- package/dist/ui/tui/primitives/KeyboardHintsBar.js +8 -0
- package/dist/ui/tui/primitives/PickerMenu.d.ts +38 -0
- package/dist/ui/tui/primitives/PickerMenu.js +162 -0
- package/dist/ui/tui/primitives/PromptLabel.d.ts +6 -0
- package/dist/ui/tui/primitives/PromptLabel.js +6 -0
- package/dist/ui/tui/primitives/ScreenContainer.d.ts +39 -0
- package/dist/ui/tui/primitives/ScreenContainer.js +39 -0
- package/dist/ui/tui/primitives/Spinner.d.ts +7 -0
- package/dist/ui/tui/primitives/Spinner.js +18 -0
- package/dist/ui/tui/screens/DashScreen.d.ts +6 -0
- package/dist/ui/tui/screens/DashScreen.js +37 -0
- package/dist/ui/tui/screens/DebugScreen.d.ts +6 -0
- package/dist/ui/tui/screens/DebugScreen.js +48 -0
- package/dist/ui/tui/screens/EnvScreen.d.ts +6 -0
- package/dist/ui/tui/screens/EnvScreen.js +192 -0
- package/dist/ui/tui/screens/InitScreen.d.ts +9 -0
- package/dist/ui/tui/screens/InitScreen.js +102 -0
- package/dist/ui/tui/screens/MenuScreen.d.ts +7 -0
- package/dist/ui/tui/screens/MenuScreen.js +84 -0
- package/dist/ui/tui/start-tui.d.ts +11 -0
- package/dist/ui/tui/start-tui.js +72 -0
- package/dist/ui/tui/steps/AccountStep.d.ts +8 -0
- package/dist/ui/tui/steps/AccountStep.js +42 -0
- package/dist/ui/tui/steps/ApiKeyStep.d.ts +8 -0
- package/dist/ui/tui/steps/ApiKeyStep.js +53 -0
- package/dist/ui/tui/steps/ClientsStep.d.ts +10 -0
- package/dist/ui/tui/steps/ClientsStep.js +52 -0
- package/dist/ui/tui/steps/CredentialKindStep.d.ts +7 -0
- package/dist/ui/tui/steps/CredentialKindStep.js +18 -0
- package/dist/ui/tui/steps/EnvironmentStep.d.ts +8 -0
- package/dist/ui/tui/steps/EnvironmentStep.js +38 -0
- package/dist/ui/tui/steps/LoginStep.d.ts +8 -0
- package/dist/ui/tui/steps/LoginStep.js +79 -0
- package/dist/ui/tui/steps/SkillsStep.d.ts +7 -0
- package/dist/ui/tui/steps/SkillsStep.js +7 -0
- package/dist/ui/tui/steps/SummaryStep.d.ts +10 -0
- package/dist/ui/tui/steps/SummaryStep.js +133 -0
- package/dist/ui/tui/steps/WritingStep.d.ts +10 -0
- package/dist/ui/tui/steps/WritingStep.js +101 -0
- package/package.json +62 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
const COLOR = {
|
|
4
|
+
add: "green",
|
|
5
|
+
remove: "red",
|
|
6
|
+
context: undefined,
|
|
7
|
+
header: "cyan",
|
|
8
|
+
};
|
|
9
|
+
const PREFIX = {
|
|
10
|
+
add: "+ ",
|
|
11
|
+
remove: "- ",
|
|
12
|
+
context: " ",
|
|
13
|
+
header: "",
|
|
14
|
+
};
|
|
15
|
+
export function JsonPreview({ lines, title, }) {
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "column", children: [title && _jsx(Text, { dimColor: true, children: title }), lines.map((line, i) => {
|
|
17
|
+
const color = COLOR[line.kind];
|
|
18
|
+
return (_jsxs(Text, { color: color, dimColor: line.kind === "context", children: [PREFIX[line.kind], line.text] }, i));
|
|
19
|
+
})] }));
|
|
20
|
+
}
|
|
21
|
+
export function jsonToDiffLines(entry, serverName) {
|
|
22
|
+
const text = JSON.stringify({ [serverName]: entry }, null, 2);
|
|
23
|
+
const lines = text.split("\n").slice(1, -1);
|
|
24
|
+
return lines.map((line) => ({ kind: "add", text: line.replace(/^ /, "") }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
export function useAsyncEffect(fn, deps, onError) {
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
const signal = { cancelled: false };
|
|
5
|
+
fn(signal).catch((err) => {
|
|
6
|
+
if (signal.cancelled)
|
|
7
|
+
return;
|
|
8
|
+
if (onError) {
|
|
9
|
+
onError(err);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// Last resort. Step components should pass onError so the error lands
|
|
13
|
+
// in wizard state rather than scribbling above the Ink frame.
|
|
14
|
+
process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
|
15
|
+
});
|
|
16
|
+
return () => {
|
|
17
|
+
signal.cancelled = true;
|
|
18
|
+
};
|
|
19
|
+
}, deps);
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
export function useResize() {
|
|
3
|
+
const [size, setSize] = useState(() => ({
|
|
4
|
+
cols: process.stdout.columns ?? 80,
|
|
5
|
+
rows: process.stdout.rows ?? 24,
|
|
6
|
+
}));
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const handler = () => {
|
|
9
|
+
setSize({
|
|
10
|
+
cols: process.stdout.columns ?? 80,
|
|
11
|
+
rows: process.stdout.rows ?? 24,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
process.stdout.on("resize", handler);
|
|
15
|
+
return () => {
|
|
16
|
+
process.stdout.off("resize", handler);
|
|
17
|
+
};
|
|
18
|
+
}, []);
|
|
19
|
+
return size;
|
|
20
|
+
}
|
package/dist/ui/hud.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type StartTuiResult } from './tui/start-tui.js';
|
|
2
|
+
export type HudScreen = 'menu' | 'init' | 'dash' | 'env';
|
|
3
|
+
export interface HudOptions {
|
|
4
|
+
initialScreen?: HudScreen;
|
|
5
|
+
/** Fixed loopback port for the OAuth callback in the `init` flow. */
|
|
6
|
+
callbackPort?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function runHud(options?: HudOptions): Promise<StartTuiResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Run the HUD and, if the user chose to hand off into an agent from the summary
|
|
11
|
+
* screen, spawn it once the TUI has fully torn down. Exits the process with the
|
|
12
|
+
* agent's exit code. On launch failure, prints the seed prompt so the user can
|
|
13
|
+
* paste it manually, then exits non-zero.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runHudThenMaybeLaunch(options?: HudOptions): Promise<void>;
|
package/dist/ui/hud.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { launchAgent } from '../launch/agent.js';
|
|
3
|
+
import { startTUI } from './tui/start-tui.js';
|
|
4
|
+
export async function runHud(options = {}) {
|
|
5
|
+
return startTUI({
|
|
6
|
+
initialScreen: options.initialScreen,
|
|
7
|
+
callbackPort: options.callbackPort,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Run the HUD and, if the user chose to hand off into an agent from the summary
|
|
12
|
+
* screen, spawn it once the TUI has fully torn down. Exits the process with the
|
|
13
|
+
* agent's exit code. On launch failure, prints the seed prompt so the user can
|
|
14
|
+
* paste it manually, then exits non-zero.
|
|
15
|
+
*/
|
|
16
|
+
export async function runHudThenMaybeLaunch(options = {}) {
|
|
17
|
+
const { launch } = await runHud(options);
|
|
18
|
+
if (!launch)
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
const code = await launchAgent(launch);
|
|
22
|
+
process.exit(code);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
26
|
+
process.stderr.write(`\n${pc.red('✗')} Could not launch your agent: ${reason}\n\n` +
|
|
27
|
+
`Paste this into your agent to get started:\n${launch.prompt}\n`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defaultTheme, extendTheme } from "@inkjs/ui";
|
|
2
|
+
import { BRAND_LIME } from "./theme.js";
|
|
3
|
+
const FOCUS = BRAND_LIME.brightest;
|
|
4
|
+
const SELECTED = BRAND_LIME.primary;
|
|
5
|
+
function labelStyle(props) {
|
|
6
|
+
if (props.isFocused)
|
|
7
|
+
return { color: FOCUS };
|
|
8
|
+
if (props.isSelected)
|
|
9
|
+
return { color: SELECTED };
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
export const cyberdeckTheme = extendTheme(defaultTheme, {
|
|
13
|
+
components: {
|
|
14
|
+
Select: {
|
|
15
|
+
styles: {
|
|
16
|
+
focusIndicator: () => ({ color: FOCUS }),
|
|
17
|
+
selectedIndicator: () => ({ color: SELECTED }),
|
|
18
|
+
label: labelStyle,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
MultiSelect: {
|
|
22
|
+
styles: {
|
|
23
|
+
focusIndicator: () => ({ color: FOCUS }),
|
|
24
|
+
selectedIndicator: () => ({ color: SELECTED }),
|
|
25
|
+
label: labelStyle,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
Spinner: {
|
|
29
|
+
styles: {
|
|
30
|
+
frame: () => ({ color: FOCUS }),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface LogoProps {
|
|
3
|
+
/**
|
|
4
|
+
* Width in terminal cells available to the logo row. Use the shell's
|
|
5
|
+
* clamped content width — NOT raw terminal columns — so the tail extends
|
|
6
|
+
* to the shell's right edge instead of overflowing past it and pushing
|
|
7
|
+
* the STIGG glyphs off the left when the parent centers the row.
|
|
8
|
+
*/
|
|
9
|
+
containerWidth: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function Logo({ containerWidth }: LogoProps): React.ReactElement;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { BRAND_LIME, Colors, shouldRenderAnimation } from '../theme.js';
|
|
5
|
+
import { BlinkingLabel } from '../tui/primitives/BlinkingLabel.js';
|
|
6
|
+
import { isLogoCellFilled, LOGO_COLS, LOGO_LINES, LOGO_ROWS, logoCellAt } from './logo.js';
|
|
7
|
+
const TERMINAL_LABEL_TEXT = 'terminal';
|
|
8
|
+
const TERMINAL_LABEL_EMOJI = '⚡';
|
|
9
|
+
const TERMINAL_LABEL_WIDTH = 10; // 8 chars + 2-cell emoji
|
|
10
|
+
const LABEL_GAP = 2;
|
|
11
|
+
const TAIL_ORIGIN_COL = LOGO_COLS + LABEL_GAP + TERMINAL_LABEL_WIDTH;
|
|
12
|
+
const RAIN_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*<>?{}[]/=+-*';
|
|
13
|
+
const GRADIENT_PALETTE = [
|
|
14
|
+
BRAND_LIME.brightest,
|
|
15
|
+
BRAND_LIME.primary,
|
|
16
|
+
BRAND_LIME.mid,
|
|
17
|
+
BRAND_LIME.forest,
|
|
18
|
+
BRAND_LIME.deep,
|
|
19
|
+
];
|
|
20
|
+
const FRAME_MS = 80;
|
|
21
|
+
const GRADIENT_TICK_MS = 1800;
|
|
22
|
+
const LETTER_ANIMATION_MS = 1400; // duration of the slide-down animation itself
|
|
23
|
+
const LETTER_LOOP_MS = 5000; // total cycle: animate, then idle, then pick next letter
|
|
24
|
+
const RAIN_GREEN = '#39ff66';
|
|
25
|
+
const PARTICLE_CHARS = ['.', "'", '·', '*', '✦', '˖'];
|
|
26
|
+
const PARTICLE_BASE_COLORS = [
|
|
27
|
+
'#ffffff', // white
|
|
28
|
+
'#dcdcff', // pale lavender
|
|
29
|
+
'#cce6ff', // pale blue
|
|
30
|
+
'#fff5d6', // pale warm
|
|
31
|
+
'#aaaaaa', // distant gray
|
|
32
|
+
];
|
|
33
|
+
const PARTICLE_COUNT = 18;
|
|
34
|
+
const PARTICLE_MIN_SPEED = 0.3;
|
|
35
|
+
const PARTICLE_MAX_SPEED = 1.4;
|
|
36
|
+
const PARTICLE_MIN_BRIGHTNESS = 0.2;
|
|
37
|
+
const RIGHT_MARGIN = 2;
|
|
38
|
+
const GREEN_FLASH_CHANCE_PER_FRAME = 0.004; // ~1 flash/sec across all particles
|
|
39
|
+
const GREEN_FLASH_MIN_MS = 350;
|
|
40
|
+
const GREEN_FLASH_MAX_MS = 750;
|
|
41
|
+
function pick(arr) {
|
|
42
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
43
|
+
}
|
|
44
|
+
function spawnParticle(startCol) {
|
|
45
|
+
return {
|
|
46
|
+
row: Math.floor(Math.random() * LOGO_ROWS),
|
|
47
|
+
col: startCol,
|
|
48
|
+
char: pick(PARTICLE_CHARS),
|
|
49
|
+
baseColor: pick(PARTICLE_BASE_COLORS),
|
|
50
|
+
speed: PARTICLE_MIN_SPEED + Math.random() * (PARTICLE_MAX_SPEED - PARTICLE_MIN_SPEED),
|
|
51
|
+
twinklePhase: Math.random() * Math.PI * 2,
|
|
52
|
+
twinkleSpeed: 0.05 + Math.random() * 0.15,
|
|
53
|
+
greenUntil: 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function scaleColor(hex, factor) {
|
|
57
|
+
const r = Math.max(0, Math.min(255, Math.round(parseInt(hex.slice(1, 3), 16) * factor)));
|
|
58
|
+
const g = Math.max(0, Math.min(255, Math.round(parseInt(hex.slice(3, 5), 16) * factor)));
|
|
59
|
+
const b = Math.max(0, Math.min(255, Math.round(parseInt(hex.slice(5, 7), 16) * factor)));
|
|
60
|
+
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
|
|
61
|
+
}
|
|
62
|
+
function randChar() {
|
|
63
|
+
return RAIN_CHARS.charAt(Math.floor(Math.random() * RAIN_CHARS.length));
|
|
64
|
+
}
|
|
65
|
+
function ansi(hex, ch) {
|
|
66
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
67
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
68
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
69
|
+
return `\x1b[1;38;2;${r};${g};${b}m${ch}\x1b[0m`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Hard-coded column ranges for each letter of STIGG in the ANSI Shadow font.
|
|
73
|
+
*
|
|
74
|
+
* Layout (col 0 is a leading space):
|
|
75
|
+
* [space] S(1-8) T(9-17) I(18-20) G(21-29) G(30-38)
|
|
76
|
+
*/
|
|
77
|
+
const LETTER_RANGES = [
|
|
78
|
+
[1, 8], // S
|
|
79
|
+
[9, 17], // T
|
|
80
|
+
[18, 20], // I
|
|
81
|
+
[21, 29], // G
|
|
82
|
+
[30, 38], // G
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* For a cell inside an animating letter, work out what should be displayed
|
|
86
|
+
* given the animation progress p ∈ [0, 1].
|
|
87
|
+
*
|
|
88
|
+
* Tape layout (top → bottom, 3 × LOGO_ROWS tall):
|
|
89
|
+
* [ original (the "rebuilt" copy)
|
|
90
|
+
* rain (the matrix-style filler)
|
|
91
|
+
* original (the starting copy) ]
|
|
92
|
+
*
|
|
93
|
+
* Viewport (LOGO_ROWS tall) starts at the bottom of the tape and scrolls UP
|
|
94
|
+
* to the top as p goes 0 → 1. So:
|
|
95
|
+
* p = 0 → viewport shows the bottom ORIG (letter intact)
|
|
96
|
+
* p = 0.5 → viewport shows RAIN (chars decomposed and sliding)
|
|
97
|
+
* p = 1 → viewport shows the top ORIG (letter rebuilt, loop ready)
|
|
98
|
+
*/
|
|
99
|
+
function visibleChar(row, col, progress) {
|
|
100
|
+
const totalScroll = 2 * LOGO_ROWS;
|
|
101
|
+
const viewportTop = Math.floor((1 - progress) * totalScroll);
|
|
102
|
+
const tapeIndex = viewportTop + row;
|
|
103
|
+
if (tapeIndex < LOGO_ROWS) {
|
|
104
|
+
return { ch: logoCellAt(tapeIndex, col), rain: false };
|
|
105
|
+
}
|
|
106
|
+
if (tapeIndex < 2 * LOGO_ROWS) {
|
|
107
|
+
return { ch: randChar(), rain: true };
|
|
108
|
+
}
|
|
109
|
+
const adjusted = tapeIndex - 2 * LOGO_ROWS;
|
|
110
|
+
return { ch: logoCellAt(adjusted, col), rain: false };
|
|
111
|
+
}
|
|
112
|
+
export function Logo({ containerWidth }) {
|
|
113
|
+
const animate = shouldRenderAnimation();
|
|
114
|
+
const startedAtRef = useRef(Date.now());
|
|
115
|
+
const letterIndexRef = useRef(0);
|
|
116
|
+
const particlesRef = useRef(null);
|
|
117
|
+
const [, setTick] = useState(0);
|
|
118
|
+
if (animate && particlesRef.current === null) {
|
|
119
|
+
const span = Math.max(20, containerWidth - TAIL_ORIGIN_COL - RIGHT_MARGIN);
|
|
120
|
+
const initial = [];
|
|
121
|
+
for (let i = 0; i < PARTICLE_COUNT; i++) {
|
|
122
|
+
const p = spawnParticle(TAIL_ORIGIN_COL);
|
|
123
|
+
p.col = TAIL_ORIGIN_COL + Math.random() * span;
|
|
124
|
+
initial.push(p);
|
|
125
|
+
}
|
|
126
|
+
particlesRef.current = initial;
|
|
127
|
+
}
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!animate)
|
|
130
|
+
return;
|
|
131
|
+
const id = setInterval(() => {
|
|
132
|
+
const elapsed = Date.now() - startedAtRef.current;
|
|
133
|
+
if (elapsed >= LETTER_LOOP_MS && LETTER_RANGES.length > 0) {
|
|
134
|
+
startedAtRef.current = Date.now();
|
|
135
|
+
let next = Math.floor(Math.random() * LETTER_RANGES.length);
|
|
136
|
+
if (LETTER_RANGES.length > 1 && next === letterIndexRef.current) {
|
|
137
|
+
next = (next + 1) % LETTER_RANGES.length;
|
|
138
|
+
}
|
|
139
|
+
letterIndexRef.current = next;
|
|
140
|
+
}
|
|
141
|
+
if (particlesRef.current) {
|
|
142
|
+
const maxCol = Math.max(TAIL_ORIGIN_COL + 4, containerWidth - RIGHT_MARGIN);
|
|
143
|
+
const tickNow = Date.now();
|
|
144
|
+
for (const p of particlesRef.current) {
|
|
145
|
+
p.col += p.speed;
|
|
146
|
+
p.twinklePhase += p.twinkleSpeed;
|
|
147
|
+
if (p.greenUntil <= tickNow && Math.random() < GREEN_FLASH_CHANCE_PER_FRAME) {
|
|
148
|
+
p.greenUntil =
|
|
149
|
+
tickNow +
|
|
150
|
+
GREEN_FLASH_MIN_MS +
|
|
151
|
+
Math.random() * (GREEN_FLASH_MAX_MS - GREEN_FLASH_MIN_MS);
|
|
152
|
+
}
|
|
153
|
+
if (p.col >= maxCol) {
|
|
154
|
+
const next = spawnParticle(TAIL_ORIGIN_COL);
|
|
155
|
+
p.row = next.row;
|
|
156
|
+
p.col = TAIL_ORIGIN_COL;
|
|
157
|
+
p.char = next.char;
|
|
158
|
+
p.baseColor = next.baseColor;
|
|
159
|
+
p.speed = next.speed;
|
|
160
|
+
p.twinklePhase = next.twinklePhase;
|
|
161
|
+
p.twinkleSpeed = next.twinkleSpeed;
|
|
162
|
+
p.greenUntil = next.greenUntil;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
setTick((t) => t + 1);
|
|
167
|
+
}, FRAME_MS);
|
|
168
|
+
return () => clearInterval(id);
|
|
169
|
+
}, [animate, containerWidth]);
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
const gradientPhase = animate ? Math.floor(now / GRADIENT_TICK_MS) % GRADIENT_PALETTE.length : 0;
|
|
172
|
+
const activeRange = animate && LETTER_RANGES.length > 0
|
|
173
|
+
? LETTER_RANGES[letterIndexRef.current % LETTER_RANGES.length]
|
|
174
|
+
: null;
|
|
175
|
+
const animProgress = activeRange != null ? Math.min(1, (now - startedAtRef.current) / LETTER_ANIMATION_MS) : 0;
|
|
176
|
+
const renderedLines = [];
|
|
177
|
+
for (let row = 0; row < LOGO_ROWS; row++) {
|
|
178
|
+
const chars = [...(LOGO_LINES[row] ?? '')];
|
|
179
|
+
const lineColor = GRADIENT_PALETTE[(gradientPhase + row) % GRADIENT_PALETTE.length];
|
|
180
|
+
let line = '';
|
|
181
|
+
for (let col = 0; col < chars.length; col++) {
|
|
182
|
+
const isInActiveLetter = activeRange !== null && col >= activeRange[0] && col <= activeRange[1];
|
|
183
|
+
if (isInActiveLetter && isLogoCellFilled(row, col)) {
|
|
184
|
+
const { ch, rain } = visibleChar(row, col, animProgress);
|
|
185
|
+
if (ch === ' ') {
|
|
186
|
+
line += ' ';
|
|
187
|
+
}
|
|
188
|
+
else if (rain) {
|
|
189
|
+
line += ansi(RAIN_GREEN, ch);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
line += ansi(lineColor, ch);
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const ch = chars[col];
|
|
197
|
+
if (ch === ' ') {
|
|
198
|
+
line += ' ';
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
line += ansi(lineColor, ch);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
renderedLines.push(line);
|
|
205
|
+
}
|
|
206
|
+
const tailWidth = Math.max(0, containerWidth - TAIL_ORIGIN_COL - RIGHT_MARGIN);
|
|
207
|
+
const tailCells = Array.from({ length: LOGO_ROWS }, () => Array.from({ length: tailWidth }, () => ' '));
|
|
208
|
+
if (animate && particlesRef.current && tailWidth > 0) {
|
|
209
|
+
const renderNow = Date.now();
|
|
210
|
+
for (const p of particlesRef.current) {
|
|
211
|
+
const colInTail = Math.floor(p.col) - TAIL_ORIGIN_COL;
|
|
212
|
+
if (colInTail < 0 || colInTail >= tailWidth)
|
|
213
|
+
continue;
|
|
214
|
+
if (p.row < 0 || p.row >= LOGO_ROWS)
|
|
215
|
+
continue;
|
|
216
|
+
const pulse = (Math.sin(p.twinklePhase) + 1) / 2;
|
|
217
|
+
const brightness = PARTICLE_MIN_BRIGHTNESS + (1 - PARTICLE_MIN_BRIGHTNESS) * pulse;
|
|
218
|
+
const isGreenFlash = p.greenUntil > renderNow;
|
|
219
|
+
const baseColor = isGreenFlash ? BRAND_LIME.primary : p.baseColor;
|
|
220
|
+
tailCells[p.row][colInTail] = ansi(scaleColor(baseColor, brightness), p.char);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const tailRows = tailCells.map((cells) => cells.join(''));
|
|
224
|
+
const labelRow = LOGO_ROWS - 1; // align label baseline with bottom of STIGG ascii
|
|
225
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexDirection: "column", children: renderedLines.map((line, i) => (_jsx(Text, { children: line }, i))) }), _jsx(Box, { flexDirection: "column", marginLeft: LABEL_GAP, width: TERMINAL_LABEL_WIDTH, children: Array.from({ length: LOGO_ROWS }, (_, i) => i === labelRow ? (_jsxs(Text, { bold: true, color: Colors.accent, children: [TERMINAL_LABEL_TEXT, _jsx(BlinkingLabel, { text: TERMINAL_LABEL_EMOJI, padWidth: 2, bold: true })] }, i)) : (_jsx(Text, { children: ' '.repeat(TERMINAL_LABEL_WIDTH) }, i))) }), _jsx(Box, { flexDirection: "column", children: tailRows.map((line, i) => (_jsx(Text, { children: line }, i))) })] }));
|
|
226
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { BRAND_LIME, shouldRenderAnimation } from "../theme.js";
|
|
6
|
+
import { Logo, computeWaveTail, renderLeafLines } from "./LogoView.js";
|
|
7
|
+
import { isLogoCellFilled, LOGO_COLS, LOGO_ROWS, SUBTITLE, logoCellAt, } from "./logo.js";
|
|
8
|
+
const RAIN_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*<>?{}[]/=+-*";
|
|
9
|
+
const FRAME_MS = 60;
|
|
10
|
+
const TAIL_LEN = 4;
|
|
11
|
+
const MAX_DURATION_MS = 1500;
|
|
12
|
+
function randChar() {
|
|
13
|
+
return RAIN_CHARS.charAt(Math.floor(Math.random() * RAIN_CHARS.length));
|
|
14
|
+
}
|
|
15
|
+
function brand(hex, ch) {
|
|
16
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
17
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
18
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
19
|
+
return `\x1b[1;38;2;${r};${g};${b}m${ch}\x1b[0m`;
|
|
20
|
+
}
|
|
21
|
+
export function MatrixIntro({ onComplete }) {
|
|
22
|
+
const animate = shouldRenderAnimation();
|
|
23
|
+
const completedRef = useRef(false);
|
|
24
|
+
const [, setTick] = useState(0);
|
|
25
|
+
const headsRef = useRef(Array.from({ length: LOGO_COLS }, () => Math.floor(-Math.random() * LOGO_ROWS * 1.8)));
|
|
26
|
+
const speedRef = useRef(Array.from({ length: LOGO_COLS }, () => 0.6 + Math.random() * 0.6));
|
|
27
|
+
const complete = () => {
|
|
28
|
+
if (completedRef.current)
|
|
29
|
+
return;
|
|
30
|
+
completedRef.current = true;
|
|
31
|
+
onComplete();
|
|
32
|
+
};
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!animate) {
|
|
35
|
+
const id = setTimeout(complete, 250);
|
|
36
|
+
return () => clearTimeout(id);
|
|
37
|
+
}
|
|
38
|
+
const start = Date.now();
|
|
39
|
+
const id = setInterval(() => {
|
|
40
|
+
for (let i = 0; i < LOGO_COLS; i++) {
|
|
41
|
+
headsRef.current[i] += speedRef.current[i];
|
|
42
|
+
}
|
|
43
|
+
setTick((t) => t + 1);
|
|
44
|
+
if (Date.now() - start >= MAX_DURATION_MS) {
|
|
45
|
+
clearInterval(id);
|
|
46
|
+
complete();
|
|
47
|
+
}
|
|
48
|
+
}, FRAME_MS);
|
|
49
|
+
return () => clearInterval(id);
|
|
50
|
+
}, [animate]);
|
|
51
|
+
if (!animate) {
|
|
52
|
+
return _jsx(Logo, {});
|
|
53
|
+
}
|
|
54
|
+
const lines = [];
|
|
55
|
+
for (let row = 0; row < LOGO_ROWS; row++) {
|
|
56
|
+
let line = "";
|
|
57
|
+
for (let col = 0; col < LOGO_COLS; col++) {
|
|
58
|
+
const head = headsRef.current[col];
|
|
59
|
+
const headRow = Math.floor(head);
|
|
60
|
+
const logoCh = logoCellAt(row, col);
|
|
61
|
+
const isFilled = isLogoCellFilled(row, col);
|
|
62
|
+
if (row < headRow - TAIL_LEN) {
|
|
63
|
+
line += isFilled ? brand(BRAND_LIME.brightest, logoCh) : " ";
|
|
64
|
+
}
|
|
65
|
+
else if (row === headRow) {
|
|
66
|
+
line += brand(BRAND_LIME.primary, randChar());
|
|
67
|
+
}
|
|
68
|
+
else if (row < headRow && row >= headRow - TAIL_LEN) {
|
|
69
|
+
line += pc.green(randChar());
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
line += " ";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
lines.push(line);
|
|
76
|
+
}
|
|
77
|
+
const tailRows = computeWaveTail(Date.now());
|
|
78
|
+
const leafLines = renderLeafLines();
|
|
79
|
+
return (_jsxs(Box, { flexDirection: "column", children: [leafLines.map((line, i) => (_jsx(Text, { children: line }, `leaf-${i}`))), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsx(Text, { dimColor: true, children: SUBTITLE }) }), lines.map((line, i) => (_jsx(Text, { children: line + (tailRows[i] ?? "") }, `cd-${i}`)))] }));
|
|
80
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const LOGO_LINES: string[];
|
|
2
|
+
export declare const LOGO_ROWS: number;
|
|
3
|
+
export declare const LOGO_COLS: number;
|
|
4
|
+
export declare const SUBTITLE = "stigg \u00B7";
|
|
5
|
+
export declare function logoCellAt(row: number, col: number): string;
|
|
6
|
+
export declare function isLogoCellFilled(row: number, col: number): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const LOGO_LINES = [
|
|
2
|
+
' ███████╗████████╗██╗ ██████╗ ██████╗ ',
|
|
3
|
+
' ██╔════╝╚══██╔══╝██║██╔════╝ ██╔════╝ ',
|
|
4
|
+
' ███████╗ ██║ ██║██║ ███╗██║ ███╗',
|
|
5
|
+
' ╚════██║ ██║ ██║██║ ██║██║ ██║',
|
|
6
|
+
' ███████║ ██║ ██║╚██████╔╝╚██████╔╝',
|
|
7
|
+
' ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ',
|
|
8
|
+
];
|
|
9
|
+
export const LOGO_ROWS = LOGO_LINES.length;
|
|
10
|
+
export const LOGO_COLS = Math.max(...LOGO_LINES.map((l) => [...l].length));
|
|
11
|
+
export const SUBTITLE = 'stigg ·';
|
|
12
|
+
export function logoCellAt(row, col) {
|
|
13
|
+
const line = LOGO_LINES[row];
|
|
14
|
+
if (!line)
|
|
15
|
+
return ' ';
|
|
16
|
+
const chars = [...line];
|
|
17
|
+
return chars[col] ?? ' ';
|
|
18
|
+
}
|
|
19
|
+
export function isLogoCellFilled(row, col) {
|
|
20
|
+
return logoCellAt(row, col) !== ' ';
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import open from "open";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { Card } from "../components/Card.js";
|
|
6
|
+
import { Footer } from "../components/Footer.js";
|
|
7
|
+
const DASHBOARD_URL = "https://app.stigg.io";
|
|
8
|
+
export function DashScreen({ onDone }) {
|
|
9
|
+
const [phase, setPhase] = useState("opening");
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
let cancelled = false;
|
|
12
|
+
open(DASHBOARD_URL)
|
|
13
|
+
.then(() => {
|
|
14
|
+
if (!cancelled)
|
|
15
|
+
setPhase("launched");
|
|
16
|
+
})
|
|
17
|
+
.catch(() => {
|
|
18
|
+
if (!cancelled)
|
|
19
|
+
setPhase("failed");
|
|
20
|
+
});
|
|
21
|
+
return () => {
|
|
22
|
+
cancelled = true;
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
25
|
+
useInput(() => onDone());
|
|
26
|
+
return (_jsx(Card, { title: "stigg \u00B7 cyberdeck \u00B7 dash", footer: _jsx(Footer, { hints: [{ key: "any key", label: "back to menu" }] }), children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Opening ", _jsx(Text, { color: "cyan", children: DASHBOARD_URL }), " in your browser\u2026"] }), _jsxs(Box, { marginTop: 1, children: [phase === "opening" && _jsx(Text, { dimColor: true, children: "(launching)" }), phase === "launched" && (_jsx(Text, { color: "green", children: "\u2713 Browser launched." })), phase === "failed" && (_jsx(Text, { color: "yellow", children: "Couldn't open a browser automatically \u2014 open the URL above manually." }))] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "(placeholder \u2014 the real command will surface usage, recent entitlement events, and active subscriptions inline.)" }) })] }) }));
|
|
27
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Card } from "../components/Card.js";
|
|
4
|
+
import { Footer } from "../components/Footer.js";
|
|
5
|
+
const SAMPLE_LOGS = [
|
|
6
|
+
{
|
|
7
|
+
time: "16:42:13.221",
|
|
8
|
+
level: "INFO",
|
|
9
|
+
source: "feature.access",
|
|
10
|
+
message: "customer=acme_corp has_access=true plan=growth feature=ai_credits",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
time: "16:42:14.034",
|
|
14
|
+
level: "INFO",
|
|
15
|
+
source: "usage.report",
|
|
16
|
+
message: "customer=acme_corp feature=api_calls value=1 source=mcp",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
time: "16:42:18.512",
|
|
20
|
+
level: "WARN",
|
|
21
|
+
source: "entitlement.exceeded",
|
|
22
|
+
message: "customer=acme_corp feature=api_calls usage=1001 limit=1000 reset_at=2026-07-01",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
time: "16:42:21.118",
|
|
26
|
+
level: "INFO",
|
|
27
|
+
source: "subscription.updated",
|
|
28
|
+
message: "customer=acme_corp from=growth to=growth_v2 status=active",
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
const LEVEL_COLOR = {
|
|
32
|
+
INFO: "green",
|
|
33
|
+
WARN: "yellow",
|
|
34
|
+
ERROR: "red",
|
|
35
|
+
};
|
|
36
|
+
export function DebugScreen({ onDone }) {
|
|
37
|
+
useInput(() => onDone());
|
|
38
|
+
return (_jsx(Card, { title: "stigg \u00B7 cyberdeck \u00B7 debug", footer: _jsx(Footer, { hints: [{ key: "any key", label: "back to menu" }] }), children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Streaming audit + usage logs (placeholder, sample output)\u2026" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: SAMPLE_LOGS.map((log, i) => (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: log.time }), " ", _jsx(Text, { color: LEVEL_COLOR[log.level], children: log.level.padEnd(5) }), " ", _jsx(Text, { color: "cyan", children: log.source.padEnd(22) }), " ", log.message] }, i))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "(placeholder \u2014 the real command will live-tail audit + usage events with `--since`, `--follow`, `--filter` flags.)" }) })] }) }));
|
|
39
|
+
}
|