ankui 0.1.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/LICENSE +21 -0
- package/README.md +369 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +192 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/access.d.ts +6 -0
- package/dist/commands/access.js +12 -0
- package/dist/commands/access.js.map +1 -0
- package/dist/commands/caps.d.ts +6 -0
- package/dist/commands/caps.js +12 -0
- package/dist/commands/caps.js.map +1 -0
- package/dist/commands/discover.d.ts +10 -0
- package/dist/commands/discover.js +120 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +12 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/launch-tui.d.ts +15 -0
- package/dist/commands/launch-tui.js +53 -0
- package/dist/commands/launch-tui.js.map +1 -0
- package/dist/commands/list.d.ts +11 -0
- package/dist/commands/list.js +51 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.js +12 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/scan-all.d.ts +15 -0
- package/dist/commands/scan-all.js +32 -0
- package/dist/commands/scan-all.js.map +1 -0
- package/dist/commands/show.d.ts +7 -0
- package/dist/commands/show.js +12 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/watch.d.ts +32 -0
- package/dist/commands/watch.js +205 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config/ankui-config.d.ts +13 -0
- package/dist/config/ankui-config.js +123 -0
- package/dist/config/ankui-config.js.map +1 -0
- package/dist/scanner/access-review.d.ts +2 -0
- package/dist/scanner/access-review.js +183 -0
- package/dist/scanner/access-review.js.map +1 -0
- package/dist/scanner/adapters/claude.d.ts +2 -0
- package/dist/scanner/adapters/claude.js +298 -0
- package/dist/scanner/adapters/claude.js.map +1 -0
- package/dist/scanner/adapters/codex.d.ts +2 -0
- package/dist/scanner/adapters/codex.js +152 -0
- package/dist/scanner/adapters/codex.js.map +1 -0
- package/dist/scanner/adapters/cursor.d.ts +2 -0
- package/dist/scanner/adapters/cursor.js +114 -0
- package/dist/scanner/adapters/cursor.js.map +1 -0
- package/dist/scanner/adapters/gemini.d.ts +2 -0
- package/dist/scanner/adapters/gemini.js +191 -0
- package/dist/scanner/adapters/gemini.js.map +1 -0
- package/dist/scanner/adapters/index.d.ts +35 -0
- package/dist/scanner/adapters/index.js +85 -0
- package/dist/scanner/adapters/index.js.map +1 -0
- package/dist/scanner/adapters/opencode.d.ts +2 -0
- package/dist/scanner/adapters/opencode.js +231 -0
- package/dist/scanner/adapters/opencode.js.map +1 -0
- package/dist/scanner/adapters/shared.d.ts +57 -0
- package/dist/scanner/adapters/shared.js +239 -0
- package/dist/scanner/adapters/shared.js.map +1 -0
- package/dist/scanner/adapters/skills-sh.d.ts +2 -0
- package/dist/scanner/adapters/skills-sh.js +71 -0
- package/dist/scanner/adapters/skills-sh.js.map +1 -0
- package/dist/scanner/capability-map.d.ts +8 -0
- package/dist/scanner/capability-map.js +57 -0
- package/dist/scanner/capability-map.js.map +1 -0
- package/dist/scanner/discovery.d.ts +31 -0
- package/dist/scanner/discovery.js +320 -0
- package/dist/scanner/discovery.js.map +1 -0
- package/dist/scanner/filesystem-crawler.d.ts +32 -0
- package/dist/scanner/filesystem-crawler.js +210 -0
- package/dist/scanner/filesystem-crawler.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.js +120 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/multi-project.d.ts +31 -0
- package/dist/scanner/multi-project.js +227 -0
- package/dist/scanner/multi-project.js.map +1 -0
- package/dist/scanner/parallel.d.ts +4 -0
- package/dist/scanner/parallel.js +41 -0
- package/dist/scanner/parallel.js.map +1 -0
- package/dist/scanner/parsing.d.ts +9 -0
- package/dist/scanner/parsing.js +135 -0
- package/dist/scanner/parsing.js.map +1 -0
- package/dist/scanner/paths.d.ts +8 -0
- package/dist/scanner/paths.js +39 -0
- package/dist/scanner/paths.js.map +1 -0
- package/dist/scanner/preview.d.ts +12 -0
- package/dist/scanner/preview.js +29 -0
- package/dist/scanner/preview.js.map +1 -0
- package/dist/scanner/project-discovery.d.ts +11 -0
- package/dist/scanner/project-discovery.js +35 -0
- package/dist/scanner/project-discovery.js.map +1 -0
- package/dist/scanner/ripgrep.d.ts +11 -0
- package/dist/scanner/ripgrep.js +61 -0
- package/dist/scanner/ripgrep.js.map +1 -0
- package/dist/scanner/safety.d.ts +37 -0
- package/dist/scanner/safety.js +330 -0
- package/dist/scanner/safety.js.map +1 -0
- package/dist/scanner/skill-naming.d.ts +5 -0
- package/dist/scanner/skill-naming.js +53 -0
- package/dist/scanner/skill-naming.js.map +1 -0
- package/dist/scanner/watcher.d.ts +15 -0
- package/dist/scanner/watcher.js +71 -0
- package/dist/scanner/watcher.js.map +1 -0
- package/dist/tui/App.d.ts +33 -0
- package/dist/tui/App.js +201 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/components/Breadcrumb.d.ts +10 -0
- package/dist/tui/components/Breadcrumb.js +11 -0
- package/dist/tui/components/Breadcrumb.js.map +1 -0
- package/dist/tui/components/DisclosureRow.d.ts +17 -0
- package/dist/tui/components/DisclosureRow.js +13 -0
- package/dist/tui/components/DisclosureRow.js.map +1 -0
- package/dist/tui/components/DotLeaderRow.d.ts +17 -0
- package/dist/tui/components/DotLeaderRow.js +34 -0
- package/dist/tui/components/DotLeaderRow.js.map +1 -0
- package/dist/tui/components/EmptyStateWhisper.d.ts +5 -0
- package/dist/tui/components/EmptyStateWhisper.js +6 -0
- package/dist/tui/components/EmptyStateWhisper.js.map +1 -0
- package/dist/tui/components/Frame.d.ts +15 -0
- package/dist/tui/components/Frame.js +21 -0
- package/dist/tui/components/Frame.js.map +1 -0
- package/dist/tui/components/IdleWhisper.d.ts +11 -0
- package/dist/tui/components/IdleWhisper.js +12 -0
- package/dist/tui/components/IdleWhisper.js.map +1 -0
- package/dist/tui/components/KeyHint.d.ts +13 -0
- package/dist/tui/components/KeyHint.js +12 -0
- package/dist/tui/components/KeyHint.js.map +1 -0
- package/dist/tui/components/LoadingSplash.d.ts +12 -0
- package/dist/tui/components/LoadingSplash.js +55 -0
- package/dist/tui/components/LoadingSplash.js.map +1 -0
- package/dist/tui/components/ProgressBar.d.ts +16 -0
- package/dist/tui/components/ProgressBar.js +24 -0
- package/dist/tui/components/ProgressBar.js.map +1 -0
- package/dist/tui/components/SearchBox.d.ts +5 -0
- package/dist/tui/components/SearchBox.js +6 -0
- package/dist/tui/components/SearchBox.js.map +1 -0
- package/dist/tui/components/SectionHeader.d.ts +12 -0
- package/dist/tui/components/SectionHeader.js +14 -0
- package/dist/tui/components/SectionHeader.js.map +1 -0
- package/dist/tui/components/SkillViewport.d.ts +8 -0
- package/dist/tui/components/SkillViewport.js +42 -0
- package/dist/tui/components/SkillViewport.js.map +1 -0
- package/dist/tui/components/Spinner.d.ts +11 -0
- package/dist/tui/components/Spinner.js +13 -0
- package/dist/tui/components/Spinner.js.map +1 -0
- package/dist/tui/components/StatusPill.d.ts +13 -0
- package/dist/tui/components/StatusPill.js +15 -0
- package/dist/tui/components/StatusPill.js.map +1 -0
- package/dist/tui/components/TabBar.d.ts +20 -0
- package/dist/tui/components/TabBar.js +26 -0
- package/dist/tui/components/TabBar.js.map +1 -0
- package/dist/tui/components/TextInput.d.ts +13 -0
- package/dist/tui/components/TextInput.js +34 -0
- package/dist/tui/components/TextInput.js.map +1 -0
- package/dist/tui/hooks/use-idle-whisper.d.ts +17 -0
- package/dist/tui/hooks/use-idle-whisper.js +66 -0
- package/dist/tui/hooks/use-idle-whisper.js.map +1 -0
- package/dist/tui/hooks/use-rotating-message.d.ts +20 -0
- package/dist/tui/hooks/use-rotating-message.js +34 -0
- package/dist/tui/hooks/use-rotating-message.js.map +1 -0
- package/dist/tui/input/use-keys.d.ts +16 -0
- package/dist/tui/input/use-keys.js +32 -0
- package/dist/tui/input/use-keys.js.map +1 -0
- package/dist/tui/messages.d.ts +18 -0
- package/dist/tui/messages.js +59 -0
- package/dist/tui/messages.js.map +1 -0
- package/dist/tui/render.d.ts +12 -0
- package/dist/tui/render.js +112 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/screens/AccessTab.d.ts +6 -0
- package/dist/tui/screens/AccessTab.js +26 -0
- package/dist/tui/screens/AccessTab.js.map +1 -0
- package/dist/tui/screens/DoctorTab.d.ts +6 -0
- package/dist/tui/screens/DoctorTab.js +24 -0
- package/dist/tui/screens/DoctorTab.js.map +1 -0
- package/dist/tui/screens/FirstRunScan.d.ts +11 -0
- package/dist/tui/screens/FirstRunScan.js +128 -0
- package/dist/tui/screens/FirstRunScan.js.map +1 -0
- package/dist/tui/screens/McpsTab.d.ts +6 -0
- package/dist/tui/screens/McpsTab.js +30 -0
- package/dist/tui/screens/McpsTab.js.map +1 -0
- package/dist/tui/screens/Overview.d.ts +6 -0
- package/dist/tui/screens/Overview.js +75 -0
- package/dist/tui/screens/Overview.js.map +1 -0
- package/dist/tui/screens/ProjectDrillIn.d.ts +10 -0
- package/dist/tui/screens/ProjectDrillIn.js +16 -0
- package/dist/tui/screens/ProjectDrillIn.js.map +1 -0
- package/dist/tui/screens/Settings.d.ts +8 -0
- package/dist/tui/screens/Settings.js +71 -0
- package/dist/tui/screens/Settings.js.map +1 -0
- package/dist/tui/screens/ToolTab.d.ts +9 -0
- package/dist/tui/screens/ToolTab.js +38 -0
- package/dist/tui/screens/ToolTab.js.map +1 -0
- package/dist/tui/screens/UserScopeDrillIn.d.ts +13 -0
- package/dist/tui/screens/UserScopeDrillIn.js +15 -0
- package/dist/tui/screens/UserScopeDrillIn.js.map +1 -0
- package/dist/tui/state/navigation.d.ts +1 -0
- package/dist/tui/state/navigation.js +11 -0
- package/dist/tui/state/navigation.js.map +1 -0
- package/dist/tui/state/settings-state.d.ts +22 -0
- package/dist/tui/state/settings-state.js +30 -0
- package/dist/tui/state/settings-state.js.map +1 -0
- package/dist/tui/state/tui-state.d.ts +57 -0
- package/dist/tui/state/tui-state.js +86 -0
- package/dist/tui/state/tui-state.js.map +1 -0
- package/dist/tui/theme/borders.d.ts +20 -0
- package/dist/tui/theme/borders.js +25 -0
- package/dist/tui/theme/borders.js.map +1 -0
- package/dist/tui/theme/colors.d.ts +14 -0
- package/dist/tui/theme/colors.js +18 -0
- package/dist/tui/theme/colors.js.map +1 -0
- package/dist/tui/theme/icons.d.ts +30 -0
- package/dist/tui/theme/icons.js +51 -0
- package/dist/tui/theme/icons.js.map +1 -0
- package/dist/tui/util/doctor-grouping.d.ts +27 -0
- package/dist/tui/util/doctor-grouping.js +67 -0
- package/dist/tui/util/doctor-grouping.js.map +1 -0
- package/dist/tui/util/finding-grouping.d.ts +24 -0
- package/dist/tui/util/finding-grouping.js +43 -0
- package/dist/tui/util/finding-grouping.js.map +1 -0
- package/dist/tui/util/mcp-grouping.d.ts +27 -0
- package/dist/tui/util/mcp-grouping.js +96 -0
- package/dist/tui/util/mcp-grouping.js.map +1 -0
- package/dist/tui/util/scan-history.d.ts +15 -0
- package/dist/tui/util/scan-history.js +43 -0
- package/dist/tui/util/scan-history.js.map +1 -0
- package/dist/tui/util/skill-filter.d.ts +2 -0
- package/dist/tui/util/skill-filter.js +7 -0
- package/dist/tui/util/skill-filter.js.map +1 -0
- package/dist/tui/util/skill-grouping.d.ts +3 -0
- package/dist/tui/util/skill-grouping.js +31 -0
- package/dist/tui/util/skill-grouping.js.map +1 -0
- package/dist/types.d.ts +151 -0
- package/dist/types.js +143 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errors.d.ts +1 -0
- package/dist/utils/errors.js +7 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/format-access.d.ts +3 -0
- package/dist/utils/format-access.js +94 -0
- package/dist/utils/format-access.js.map +1 -0
- package/dist/utils/format-caps.d.ts +3 -0
- package/dist/utils/format-caps.js +139 -0
- package/dist/utils/format-caps.js.map +1 -0
- package/dist/utils/format-doctor.d.ts +3 -0
- package/dist/utils/format-doctor.js +105 -0
- package/dist/utils/format-doctor.js.map +1 -0
- package/dist/utils/format-list.d.ts +7 -0
- package/dist/utils/format-list.js +112 -0
- package/dist/utils/format-list.js.map +1 -0
- package/dist/utils/format-mcp.d.ts +3 -0
- package/dist/utils/format-mcp.js +130 -0
- package/dist/utils/format-mcp.js.map +1 -0
- package/dist/utils/format-multi-project.d.ts +3 -0
- package/dist/utils/format-multi-project.js +100 -0
- package/dist/utils/format-multi-project.js.map +1 -0
- package/dist/utils/format-show.d.ts +6 -0
- package/dist/utils/format-show.js +143 -0
- package/dist/utils/format-show.js.map +1 -0
- package/dist/utils/format.d.ts +3 -0
- package/dist/utils/format.js +53 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/paths.d.ts +1 -0
- package/dist/utils/paths.js +10 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { SECTION_UNDERLINE } from "../theme/icons.js";
|
|
4
|
+
import { repeat } from "../theme/borders.js";
|
|
5
|
+
/**
|
|
6
|
+
* Spaces every glyph in the uppercased label so `overview` → `O V E R V I E W`.
|
|
7
|
+
* Used as the section divider in every screen.
|
|
8
|
+
*/
|
|
9
|
+
export function SectionHeader({ label, underlineWidth = 60 }) {
|
|
10
|
+
const spaced = Array.from(label.toUpperCase()).join(" ");
|
|
11
|
+
const rule = repeat(SECTION_UNDERLINE, underlineWidth);
|
|
12
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: spaced }), _jsx(Text, { dimColor: true, children: rule })] }));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=SectionHeader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SectionHeader.js","sourceRoot":"","sources":["../../../src/tui/components/SectionHeader.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAS7C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,KAAK,EACL,cAAc,GAAG,EAAE,EACA;IACnB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;IACvD,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,kBAAE,MAAM,GAAQ,EAC1B,KAAC,IAAI,IAAC,QAAQ,kBAAE,IAAI,GAAQ,IACxB,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Skill } from "../../types.js";
|
|
3
|
+
export interface SkillViewportProps {
|
|
4
|
+
skills: ReadonlyArray<Skill>;
|
|
5
|
+
cursor: number;
|
|
6
|
+
visibleCount?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function SkillViewport({ skills, cursor, visibleCount }: SkillViewportProps): React.ReactElement;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { ACCENT } from "../theme/colors.js";
|
|
4
|
+
import { ACTIVE_PREFIX } from "../theme/icons.js";
|
|
5
|
+
import { groupSkillsByKind } from "../util/skill-grouping.js";
|
|
6
|
+
const DEFAULT_VISIBLE_COUNT = 12;
|
|
7
|
+
const NAME_WIDTH = 44;
|
|
8
|
+
export function SkillViewport({ skills, cursor, visibleCount = DEFAULT_VISIBLE_COUNT }) {
|
|
9
|
+
const rows = flattenSkills(skills);
|
|
10
|
+
const safeCursor = clamp(cursor, rows.length);
|
|
11
|
+
const count = Math.max(1, visibleCount);
|
|
12
|
+
const start = windowStart(safeCursor, rows.length, count);
|
|
13
|
+
const visible = rows.slice(start, start + count);
|
|
14
|
+
const end = start + visible.length;
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", children: [visible.map((row, offset) => {
|
|
16
|
+
const index = start + offset;
|
|
17
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: index === safeCursor ? ACCENT : undefined, children: index === safeCursor ? ACTIVE_PREFIX : " " }), _jsx(Text, { children: ` ${fit(row.skill.name, NAME_WIDTH)} ` }), _jsx(Text, { dimColor: true, children: formatKind(row.kind) })] }, row.skill.id));
|
|
18
|
+
}), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `${safeCursor + 1}/${rows.length} · showing ${start + 1}-${end} · ↑/↓ scroll` }) })] }));
|
|
19
|
+
}
|
|
20
|
+
function flattenSkills(skills) {
|
|
21
|
+
return [...groupSkillsByKind(skills).entries()].flatMap(([kind, list]) => list.map((skill) => ({ skill, kind })));
|
|
22
|
+
}
|
|
23
|
+
function windowStart(cursor, total, visibleCount) {
|
|
24
|
+
if (total <= visibleCount)
|
|
25
|
+
return 0;
|
|
26
|
+
const preferred = cursor - Math.floor(visibleCount / 2);
|
|
27
|
+
return Math.max(0, Math.min(total - visibleCount, preferred));
|
|
28
|
+
}
|
|
29
|
+
function clamp(cursor, total) {
|
|
30
|
+
if (total <= 0)
|
|
31
|
+
return 0;
|
|
32
|
+
return Math.max(0, Math.min(total - 1, cursor));
|
|
33
|
+
}
|
|
34
|
+
function fit(value, width) {
|
|
35
|
+
if (value.length > width)
|
|
36
|
+
return `${value.slice(0, width - 3)}...`;
|
|
37
|
+
return value.padEnd(width, " ");
|
|
38
|
+
}
|
|
39
|
+
function formatKind(kind) {
|
|
40
|
+
return kind.replaceAll("_", " ");
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=SkillViewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SkillViewport.js","sourceRoot":"","sources":["../../../src/tui/components/SkillViewport.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface SpinnerProps {
|
|
3
|
+
/**
|
|
4
|
+
* Current frame index (0..9). Out-of-range values are wrapped via modulo.
|
|
5
|
+
* Parent owns the interval timer (e.g., setInterval(100ms) → setFrame(f+1)).
|
|
6
|
+
*/
|
|
7
|
+
frame: number;
|
|
8
|
+
/** Optional label rendered after the braille glyph, e.g., "Remembering...". */
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function Spinner({ frame, label }: SpinnerProps): React.ReactElement;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { SPINNER_FRAMES } from "../theme/icons.js";
|
|
4
|
+
export function Spinner({ frame, label }) {
|
|
5
|
+
const idx = mod(frame, SPINNER_FRAMES.length);
|
|
6
|
+
const glyph = SPINNER_FRAMES[idx];
|
|
7
|
+
return (_jsxs(Text, { children: [glyph, label !== undefined ? ` ${label}` : ""] }));
|
|
8
|
+
}
|
|
9
|
+
/** JS `%` is sign-preserving; we want a true modulo for negative frames. */
|
|
10
|
+
function mod(n, m) {
|
|
11
|
+
return ((n % m) + m) % m;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../../src/tui/components/Spinner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAYnD,MAAM,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAgB;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,CACL,MAAC,IAAI,eACF,KAAK,EACL,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IACpC,CACR,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,GAAG,CAAC,CAAS,EAAE,CAAS;IAC/B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AccessLevel } from "../../types.js";
|
|
3
|
+
export interface StatusPillProps {
|
|
4
|
+
/** Capability label, e.g., "database". */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Access level — drives the color of the pill. */
|
|
7
|
+
accessLevel: AccessLevel;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Inline `● label · level` pill. Color comes from `colorForAccessLevel`.
|
|
11
|
+
* `unknown` renders dim italic per the spec's color palette.
|
|
12
|
+
*/
|
|
13
|
+
export declare function StatusPill({ label, accessLevel }: StatusPillProps): React.ReactElement;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { colorForAccessLevel } from "../theme/colors.js";
|
|
4
|
+
import { DOT_LEADER, STATUS_DOT } from "../theme/icons.js";
|
|
5
|
+
/**
|
|
6
|
+
* Inline `● label · level` pill. Color comes from `colorForAccessLevel`.
|
|
7
|
+
* `unknown` renders dim italic per the spec's color palette.
|
|
8
|
+
*/
|
|
9
|
+
export function StatusPill({ label, accessLevel }) {
|
|
10
|
+
const color = colorForAccessLevel(accessLevel);
|
|
11
|
+
const dim = accessLevel === "unknown";
|
|
12
|
+
const italic = accessLevel === "unknown";
|
|
13
|
+
return (_jsxs(Text, { color: color, dimColor: dim, italic: italic, children: [STATUS_DOT, " ", label, " ", DOT_LEADER, " ", accessLevel] }));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=StatusPill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusPill.js","sourceRoot":"","sources":["../../../src/tui/components/StatusPill.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAG3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAS3D;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACK;IAChB,MAAM,KAAK,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,WAAW,KAAK,SAAS,CAAC;IACtC,MAAM,MAAM,GAAG,WAAW,KAAK,SAAS,CAAC;IAEzC,OAAO,CACL,MAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,aAC9C,UAAU,OAAG,KAAK,OAAG,UAAU,OAAG,WAAW,IACzC,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface TabItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TabBarProps {
|
|
7
|
+
/**
|
|
8
|
+
* Up to two rows of tabs. Row 0 = tools row (Overview + each detected
|
|
9
|
+
* tool). Row 1 = cross-tool views (MCPs, Access, Doctor, Settings).
|
|
10
|
+
*/
|
|
11
|
+
rows: ReadonlyArray<ReadonlyArray<TabItem>>;
|
|
12
|
+
/** Which tab id is currently active. */
|
|
13
|
+
activeId: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Two-row tab bar. Active tab is uppercased and cyan; inactive tabs are
|
|
17
|
+
* dim + regular case. A second visual line per row renders `━` underneath
|
|
18
|
+
* the active label so the cursor of focus is unambiguous.
|
|
19
|
+
*/
|
|
20
|
+
export declare function TabBar({ rows, activeId }: TabBarProps): React.ReactElement;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { HEAVY } from "../theme/borders.js";
|
|
5
|
+
import { ACCENT } from "../theme/colors.js";
|
|
6
|
+
const TAB_GAP = " "; // 3 spaces between tabs
|
|
7
|
+
/**
|
|
8
|
+
* Two-row tab bar. Active tab is uppercased and cyan; inactive tabs are
|
|
9
|
+
* dim + regular case. A second visual line per row renders `━` underneath
|
|
10
|
+
* the active label so the cursor of focus is unambiguous.
|
|
11
|
+
*/
|
|
12
|
+
export function TabBar({ rows, activeId }) {
|
|
13
|
+
return (_jsx(Box, { flexDirection: "column", children: rows.map((row, rowIndex) => (_jsxs(Fragment, { children: [_jsx(Box, { children: row.map((tab, tabIndex) => {
|
|
14
|
+
const isActive = tab.id === activeId;
|
|
15
|
+
const displayLabel = isActive ? tab.label.toUpperCase() : tab.label;
|
|
16
|
+
return (_jsxs(Fragment, { children: [tabIndex > 0 && _jsx(Text, { children: TAB_GAP }), _jsx(Text, { color: isActive ? ACCENT : undefined, dimColor: !isActive, children: displayLabel })] }, tab.id));
|
|
17
|
+
}) }), _jsx(Box, { children: row.map((tab, tabIndex) => {
|
|
18
|
+
const isActive = tab.id === activeId;
|
|
19
|
+
const displayLabel = isActive ? tab.label.toUpperCase() : tab.label;
|
|
20
|
+
const underline = isActive
|
|
21
|
+
? HEAVY.horizontal.repeat(displayLabel.length)
|
|
22
|
+
: " ".repeat(displayLabel.length);
|
|
23
|
+
return (_jsxs(Fragment, { children: [tabIndex > 0 && _jsx(Text, { children: TAB_GAP }), _jsx(Text, { color: ACCENT, children: underline })] }, `u-${tab.id}`));
|
|
24
|
+
}) })] }, `row-${rowIndex}`))) }));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=TabBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabBar.js","sourceRoot":"","sources":["../../../src/tui/components/TabBar.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAiB5C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,wBAAwB;AAE/C;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAe;IACpD,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,CAC3B,MAAC,QAAQ,eACP,KAAC,GAAG,cACD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;wBACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC;wBACrC,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;wBACpE,OAAO,CACL,MAAC,QAAQ,eACN,QAAQ,GAAG,CAAC,IAAI,KAAC,IAAI,cAAE,OAAO,GAAQ,EACvC,KAAC,IAAI,IAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,QAAQ,YAC5D,YAAY,GACR,KAJM,GAAG,CAAC,EAAE,CAKV,CACZ,CAAC;oBACJ,CAAC,CAAC,GACE,EACN,KAAC,GAAG,cACD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;wBACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC;wBACrC,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;wBACpE,MAAM,SAAS,GAAG,QAAQ;4BACxB,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;4BAC9C,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;wBACpC,OAAO,CACL,MAAC,QAAQ,eACN,QAAQ,GAAG,CAAC,IAAI,KAAC,IAAI,cAAE,OAAO,GAAQ,EACvC,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,YAAG,SAAS,GAAQ,KAF1B,KAAK,GAAG,CAAC,EAAE,EAAE,CAGjB,CACZ,CAAC;oBACJ,CAAC,CAAC,GACE,KA7BO,OAAO,QAAQ,EAAE,CA8BrB,CACZ,CAAC,GACE,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface TextInputProps {
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (next: string) => void;
|
|
5
|
+
onSubmit: (value: string) => void;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Minimal one-line text input. Captures printable characters, backspace,
|
|
10
|
+
* and return. Arrow keys are ignored so the parent can keep them for
|
|
11
|
+
* selection. The cursor is a trailing `▌` when the buffer has focus.
|
|
12
|
+
*/
|
|
13
|
+
export declare function TextInput({ value, onChange, onSubmit, placeholder }: TextInputProps): React.ReactElement;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text, useInput } from "ink";
|
|
3
|
+
/**
|
|
4
|
+
* Minimal one-line text input. Captures printable characters, backspace,
|
|
5
|
+
* and return. Arrow keys are ignored so the parent can keep them for
|
|
6
|
+
* selection. The cursor is a trailing `▌` when the buffer has focus.
|
|
7
|
+
*/
|
|
8
|
+
export function TextInput({ value, onChange, onSubmit, placeholder }) {
|
|
9
|
+
useInput((input, key) => {
|
|
10
|
+
if (key.return) {
|
|
11
|
+
onSubmit(value);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (key.backspace || key.delete) {
|
|
15
|
+
if (value.length > 0)
|
|
16
|
+
onChange(value.slice(0, -1));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
20
|
+
return; // reserved for parent
|
|
21
|
+
}
|
|
22
|
+
if (key.tab || key.escape || key.ctrl || key.meta) {
|
|
23
|
+
return; // reserved for parent
|
|
24
|
+
}
|
|
25
|
+
// Treat any other input as printable. Ink delivers paste/multichar in one
|
|
26
|
+
// call, so concatenate the whole `input` string.
|
|
27
|
+
if (input && input.length > 0) {
|
|
28
|
+
onChange(value + input);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const displayValue = value.length === 0 ? placeholder ?? "" : value;
|
|
32
|
+
return (_jsxs(Text, { children: [value.length === 0 ? _jsx(Text, { dimColor: true, children: displayValue }) : _jsx(Text, { children: displayValue }), _jsx(Text, { color: "cyan", children: "\u258C" })] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=TextInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextInput.js","sourceRoot":"","sources":["../../../src/tui/components/TextInput.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AASrC;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACI;IACf,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACpE,OAAO,CAAC,sBAAsB;QAChC,CAAC;QACD,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YAClD,OAAO,CAAC,sBAAsB;QAChC,CAAC;QACD,0EAA0E;QAC1E,iDAAiD;QACjD,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACpE,OAAO,CACL,MAAC,IAAI,eACF,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,QAAQ,kBAAE,YAAY,GAAQ,CAAC,CAAC,CAAC,KAAC,IAAI,cAAE,YAAY,GAAQ,EACxF,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,uBAAS,IACtB,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface UseIdleWhisperOptions {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
/** ms of inactivity before a roll is attempted. Default 30_000. */
|
|
4
|
+
idleMs?: number;
|
|
5
|
+
/** ms the whisper stays on screen once shown. Default 5_000. */
|
|
6
|
+
lingerMs?: number;
|
|
7
|
+
/** probability [0..1] that an idle tick produces a whisper. Default 0.05. */
|
|
8
|
+
chance?: number;
|
|
9
|
+
/** injectable for tests. Default Math.random. */
|
|
10
|
+
random?: () => number;
|
|
11
|
+
}
|
|
12
|
+
export interface UseIdleWhisperResult {
|
|
13
|
+
whisper: string | null;
|
|
14
|
+
/** Call from a parent key handler to reset idle timer. */
|
|
15
|
+
bump: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function useIdleWhisper(opts: UseIdleWhisperOptions): UseIdleWhisperResult;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { pickRandomWhisper } from "../messages.js";
|
|
3
|
+
export function useIdleWhisper(opts) {
|
|
4
|
+
const idleMs = opts.idleMs ?? 30_000;
|
|
5
|
+
const lingerMs = opts.lingerMs ?? 5_000;
|
|
6
|
+
const chance = opts.chance ?? 0.05;
|
|
7
|
+
const random = opts.random ?? Math.random;
|
|
8
|
+
const [whisper, setWhisper] = useState(null);
|
|
9
|
+
const whisperRef = useRef(null);
|
|
10
|
+
const lastBumpRef = useRef(Date.now());
|
|
11
|
+
const clearTimeoutIdRef = useRef(null);
|
|
12
|
+
const bump = useCallback(() => {
|
|
13
|
+
lastBumpRef.current = Date.now();
|
|
14
|
+
if (clearTimeoutIdRef.current !== null) {
|
|
15
|
+
clearTimeout(clearTimeoutIdRef.current);
|
|
16
|
+
clearTimeoutIdRef.current = null;
|
|
17
|
+
}
|
|
18
|
+
whisperRef.current = null;
|
|
19
|
+
setWhisper(null);
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!opts.enabled) {
|
|
23
|
+
whisperRef.current = null;
|
|
24
|
+
setWhisper(null);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Use a short tick that respects idleMs; in tests idleMs is small so this
|
|
28
|
+
// converges quickly. In production idleMs=30s; we tick every idleMs.
|
|
29
|
+
const tickMs = Math.max(5, Math.min(idleMs, 1_000));
|
|
30
|
+
const id = setInterval(() => {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
const elapsed = now - lastBumpRef.current;
|
|
33
|
+
if (elapsed < idleMs)
|
|
34
|
+
return;
|
|
35
|
+
// Idle threshold passed. Roll the dice — but only if no whisper is
|
|
36
|
+
// currently displayed (avoid stacking).
|
|
37
|
+
if (whisperRef.current !== null)
|
|
38
|
+
return;
|
|
39
|
+
if (random() >= chance) {
|
|
40
|
+
// Missed roll — push the next attempt one full idle window out so we
|
|
41
|
+
// don't spam every tick.
|
|
42
|
+
lastBumpRef.current = now;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const pick = pickRandomWhisper(random);
|
|
46
|
+
whisperRef.current = pick;
|
|
47
|
+
setWhisper(pick);
|
|
48
|
+
clearTimeoutIdRef.current = setTimeout(() => {
|
|
49
|
+
whisperRef.current = null;
|
|
50
|
+
setWhisper(null);
|
|
51
|
+
// Reset idle clock so the next whisper waits a full idle window.
|
|
52
|
+
lastBumpRef.current = Date.now();
|
|
53
|
+
clearTimeoutIdRef.current = null;
|
|
54
|
+
}, lingerMs);
|
|
55
|
+
}, tickMs);
|
|
56
|
+
return () => {
|
|
57
|
+
clearInterval(id);
|
|
58
|
+
if (clearTimeoutIdRef.current !== null) {
|
|
59
|
+
clearTimeout(clearTimeoutIdRef.current);
|
|
60
|
+
clearTimeoutIdRef.current = null;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}, [opts.enabled, idleMs, lingerMs, chance, random]);
|
|
64
|
+
return { whisper, bump };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=use-idle-whisper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-idle-whisper.js","sourceRoot":"","sources":["../../../src/tui/hooks/use-idle-whisper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface UseRotatingMessageOptions {
|
|
2
|
+
/** When false, hook holds at index 0 ("Remembering..."). */
|
|
3
|
+
active: boolean;
|
|
4
|
+
/** Rotation interval in ms. Default 2500. */
|
|
5
|
+
intervalMs?: number;
|
|
6
|
+
/** Injectable randomness for tests. Default Math.random. */
|
|
7
|
+
random?: () => number;
|
|
8
|
+
}
|
|
9
|
+
export interface UseRotatingMessageResult {
|
|
10
|
+
message: string;
|
|
11
|
+
index: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Rotates through REMEMBER_MESSAGES:
|
|
15
|
+
* - index 0 ("Remembering...") always shown first
|
|
16
|
+
* - index 1 ("Anghkooey.") always shown second
|
|
17
|
+
* - indices 2..N shuffled deterministically once per active-cycle
|
|
18
|
+
* Caller toggles `active` to start/stop rotation.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useRotatingMessage(opts: UseRotatingMessageOptions): UseRotatingMessageResult;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { REMEMBER_MESSAGES, shuffleRemainder } from "../messages.js";
|
|
3
|
+
/**
|
|
4
|
+
* Rotates through REMEMBER_MESSAGES:
|
|
5
|
+
* - index 0 ("Remembering...") always shown first
|
|
6
|
+
* - index 1 ("Anghkooey.") always shown second
|
|
7
|
+
* - indices 2..N shuffled deterministically once per active-cycle
|
|
8
|
+
* Caller toggles `active` to start/stop rotation.
|
|
9
|
+
*/
|
|
10
|
+
export function useRotatingMessage(opts) {
|
|
11
|
+
const intervalMs = opts.intervalMs ?? 2500;
|
|
12
|
+
const random = opts.random ?? Math.random;
|
|
13
|
+
// Build the rotation order once per `active` flip, so a single scan
|
|
14
|
+
// session sees a stable sequence (no re-shuffles per render).
|
|
15
|
+
const sequence = useMemo(() => shuffleRemainder(REMEMBER_MESSAGES, 2, random),
|
|
16
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
17
|
+
[opts.active]);
|
|
18
|
+
const [index, setIndex] = useState(0);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!opts.active) {
|
|
21
|
+
setIndex(0);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const id = setInterval(() => {
|
|
25
|
+
setIndex((prev) => (prev + 1) % sequence.length);
|
|
26
|
+
}, intervalMs);
|
|
27
|
+
return () => {
|
|
28
|
+
clearInterval(id);
|
|
29
|
+
};
|
|
30
|
+
}, [opts.active, intervalMs, sequence.length]);
|
|
31
|
+
const message = sequence[index] ?? REMEMBER_MESSAGES[0];
|
|
32
|
+
return { message, index };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=use-rotating-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-rotating-message.js","sourceRoot":"","sources":["../../../src/tui/hooks/use-rotating-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAgBrE;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAA+B;IAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IAE1C,oEAAoE;IACpE,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,CAAC,EAAE,MAAM,CAAC;IACpD,uDAAuD;IACvD,CAAC,IAAI,CAAC,MAAM,CAAC,CACd,CAAC;IAEF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,QAAQ,CAAC,CAAC,CAAC,CAAC;YACZ,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE;YAC1B,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnD,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,OAAO,GAAG,EAAE;YACV,aAAa,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAE/C,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface KeyHandlers {
|
|
2
|
+
onArrowUp?: () => void;
|
|
3
|
+
onArrowDown?: () => void;
|
|
4
|
+
onArrowLeft?: () => void;
|
|
5
|
+
onArrowRight?: () => void;
|
|
6
|
+
onEnter?: () => void;
|
|
7
|
+
onEscape?: () => void;
|
|
8
|
+
onQuit?: () => void;
|
|
9
|
+
onRefresh?: () => void;
|
|
10
|
+
onSlash?: () => void;
|
|
11
|
+
/** Receives any printable character not otherwise handled. */
|
|
12
|
+
onTextInput?: (ch: string) => void;
|
|
13
|
+
/** Receives a backspace event. */
|
|
14
|
+
onBackspace?: () => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function useKeys(handlers: KeyHandlers): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useInput } from "ink";
|
|
2
|
+
export function useKeys(handlers) {
|
|
3
|
+
useInput((input, key) => {
|
|
4
|
+
// Reserved for future focus navigation; tab does not cycle app tabs.
|
|
5
|
+
if (key.tab)
|
|
6
|
+
return;
|
|
7
|
+
if (key.upArrow)
|
|
8
|
+
return handlers.onArrowUp?.();
|
|
9
|
+
if (key.downArrow)
|
|
10
|
+
return handlers.onArrowDown?.();
|
|
11
|
+
if (key.leftArrow)
|
|
12
|
+
return handlers.onArrowLeft?.();
|
|
13
|
+
if (key.rightArrow)
|
|
14
|
+
return handlers.onArrowRight?.();
|
|
15
|
+
if (key.return)
|
|
16
|
+
return handlers.onEnter?.();
|
|
17
|
+
if (key.escape)
|
|
18
|
+
return handlers.onEscape?.();
|
|
19
|
+
if (key.backspace || key.delete)
|
|
20
|
+
return handlers.onBackspace?.();
|
|
21
|
+
if (input === "/")
|
|
22
|
+
return handlers.onSlash?.();
|
|
23
|
+
if (input === "q")
|
|
24
|
+
return handlers.onQuit?.();
|
|
25
|
+
if (input === "r")
|
|
26
|
+
return handlers.onRefresh?.();
|
|
27
|
+
if (input && input.length === 1 && input >= " " && input !== "/") {
|
|
28
|
+
handlers.onTextInput?.(input);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=use-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-keys.js","sourceRoot":"","sources":["../../../src/tui/input/use-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAkB/B,MAAM,UAAU,OAAO,CAAC,QAAqB;IAC3C,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,qEAAqE;QACrE,IAAI,GAAG,CAAC,GAAG;YAAE,OAAO;QACpB,IAAI,GAAG,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC;QAC/C,IAAI,GAAG,CAAC,SAAS;YAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QACnD,IAAI,GAAG,CAAC,SAAS;YAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QACnD,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC;QACrD,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC7C,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QACjE,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9C,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC;QACjD,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACjE,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const REMEMBER_MESSAGES: ReadonlyArray<string>;
|
|
2
|
+
export declare const SCAN_COMPLETE = "Remembered.";
|
|
3
|
+
export declare const EMPTY_STATE_WHISPERS: {
|
|
4
|
+
readonly noFindings: "the talismans are holding.";
|
|
5
|
+
readonly noMcps: "no servers configured. you haven't asked for help yet.";
|
|
6
|
+
readonly noWarnings: "quiet tonight.";
|
|
7
|
+
readonly noProjectSkills: "nothing left here to remember.";
|
|
8
|
+
};
|
|
9
|
+
export type EmptyStateWhisperKey = keyof typeof EMPTY_STATE_WHISPERS;
|
|
10
|
+
export declare const IDLE_WHISPERS: ReadonlyArray<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Returns a new array: first `keepLeading` items in their original positions,
|
|
13
|
+
* remaining items shuffled (Fisher-Yates). `random` defaults to Math.random and
|
|
14
|
+
* can be injected for deterministic tests.
|
|
15
|
+
*/
|
|
16
|
+
export declare function shuffleRemainder<T>(items: ReadonlyArray<T>, keepLeading: number, random?: () => number): T[];
|
|
17
|
+
/** Picks a uniformly random idle whisper. `random` injectable for tests. */
|
|
18
|
+
export declare function pickRandomWhisper(random?: () => number): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const REMEMBER_MESSAGES = [
|
|
2
|
+
"Remembering...",
|
|
3
|
+
"Anghkooey.",
|
|
4
|
+
"The village remembers every agent you've configured.",
|
|
5
|
+
"Searching for what you left behind...",
|
|
6
|
+
"Some skills haven't slept in a long time.",
|
|
7
|
+
"Counting the things in your filesystem that don't want to be forgotten.",
|
|
8
|
+
"Whispering 'anghkooey' to your home directory...",
|
|
9
|
+
"Reading the symbols carved into .claude/...",
|
|
10
|
+
"We never really leave a project. The configs stay.",
|
|
11
|
+
"Marking every door you've opened to an AI.",
|
|
12
|
+
"Listening for what your filesystem won't say out loud.",
|
|
13
|
+
"Some MCPs come out only at night. We're awake.",
|
|
14
|
+
"What you saved, what you forgot, what's still watching.",
|
|
15
|
+
"Following the children's chant.",
|
|
16
|
+
"Your past sessions are humming a tune somewhere.",
|
|
17
|
+
"The lighthouse is on. We're still here.",
|
|
18
|
+
"Some doors should stay closed. We're just counting them.",
|
|
19
|
+
"Boyd would want a perimeter check.",
|
|
20
|
+
"Tabitha drew this once. We're tracing it again.",
|
|
21
|
+
"anghkooey · anghkooey · anghkooey"
|
|
22
|
+
];
|
|
23
|
+
export const SCAN_COMPLETE = "Remembered.";
|
|
24
|
+
export const EMPTY_STATE_WHISPERS = {
|
|
25
|
+
noFindings: "the talismans are holding.",
|
|
26
|
+
noMcps: "no servers configured. you haven't asked for help yet.",
|
|
27
|
+
noWarnings: "quiet tonight.",
|
|
28
|
+
noProjectSkills: "nothing left here to remember."
|
|
29
|
+
};
|
|
30
|
+
export const IDLE_WHISPERS = [
|
|
31
|
+
"every scan is another lap.",
|
|
32
|
+
"anghkooey.",
|
|
33
|
+
"the village remembers.",
|
|
34
|
+
"remember, remember.",
|
|
35
|
+
"what you saved is still watching.",
|
|
36
|
+
"the lighthouse is on.",
|
|
37
|
+
"we never really leave a project."
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Returns a new array: first `keepLeading` items in their original positions,
|
|
41
|
+
* remaining items shuffled (Fisher-Yates). `random` defaults to Math.random and
|
|
42
|
+
* can be injected for deterministic tests.
|
|
43
|
+
*/
|
|
44
|
+
export function shuffleRemainder(items, keepLeading, random = Math.random) {
|
|
45
|
+
const out = [...items];
|
|
46
|
+
for (let i = out.length - 1; i > keepLeading; i--) {
|
|
47
|
+
const j = keepLeading + Math.floor(random() * (i - keepLeading + 1));
|
|
48
|
+
const tmp = out[i];
|
|
49
|
+
out[i] = out[j];
|
|
50
|
+
out[j] = tmp;
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
/** Picks a uniformly random idle whisper. `random` injectable for tests. */
|
|
55
|
+
export function pickRandomWhisper(random = Math.random) {
|
|
56
|
+
const idx = Math.floor(random() * IDLE_WHISPERS.length);
|
|
57
|
+
return IDLE_WHISPERS[Math.min(idx, IDLE_WHISPERS.length - 1)];
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/tui/messages.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAA0B;IACtD,gBAAgB;IAChB,YAAY;IACZ,sDAAsD;IACtD,uCAAuC;IACvC,2CAA2C;IAC3C,yEAAyE;IACzE,kDAAkD;IAClD,6CAA6C;IAC7C,oDAAoD;IACpD,4CAA4C;IAC5C,wDAAwD;IACxD,gDAAgD;IAChD,yDAAyD;IACzD,iCAAiC;IACjC,kDAAkD;IAClD,yCAAyC;IACzC,0DAA0D;IAC1D,oCAAoC;IACpC,iDAAiD;IACjD,mCAAmC;CACpC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU,EAAE,4BAA4B;IACxC,MAAM,EAAE,wDAAwD;IAChE,UAAU,EAAE,gBAAgB;IAC5B,eAAe,EAAE,gCAAgC;CACzC,CAAC;AAIX,MAAM,CAAC,MAAM,aAAa,GAA0B;IAClD,4BAA4B;IAC5B,YAAY;IACZ,wBAAwB;IACxB,qBAAqB;IACrB,mCAAmC;IACnC,uBAAuB;IACvB,kCAAkC;CACnC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAuB,EACvB,WAAmB,EACnB,SAAuB,IAAI,CAAC,MAAM;IAElC,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACvB,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QAClD,MAAM,CAAC,GAAG,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACnB,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAChB,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,SAAuB,IAAI,CAAC,MAAM;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MultiProjectScanResult } from "../types.js";
|
|
2
|
+
import { type DataSource } from "./App.js";
|
|
3
|
+
export interface RenderTuiLoadScanOptions {
|
|
4
|
+
/** Function that resolves with the scan result. Called once on mount. */
|
|
5
|
+
loadScan: () => Promise<MultiProjectScanResult>;
|
|
6
|
+
}
|
|
7
|
+
export interface RenderTuiFirstRunOptions {
|
|
8
|
+
mode: "firstRun";
|
|
9
|
+
homeDir: string;
|
|
10
|
+
onConfigChange: (devRoots: string[]) => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare function renderTui(input: RenderTuiLoadScanOptions | RenderTuiFirstRunOptions | MultiProjectScanResult | DataSource): Promise<void>;
|