@nexus-ai-fs/tui 0.9.18
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 +30 -0
- package/package.json +48 -0
- package/src/app.tsx +349 -0
- package/src/index.tsx +137 -0
- package/src/opentui-env.d.ts +61 -0
- package/src/panels/access/access-panel.tsx +597 -0
- package/src/panels/access/alert-list.tsx +77 -0
- package/src/panels/access/constraint-creator.tsx +128 -0
- package/src/panels/access/constraint-list.tsx +72 -0
- package/src/panels/access/credential-list.tsx +68 -0
- package/src/panels/access/delegation-chain-view.tsx +110 -0
- package/src/panels/access/delegation-completer.tsx +120 -0
- package/src/panels/access/delegation-creator.tsx +237 -0
- package/src/panels/access/delegation-list.tsx +74 -0
- package/src/panels/access/fraud-score-view.tsx +94 -0
- package/src/panels/access/manifest-creator.tsx +167 -0
- package/src/panels/access/manifest-list.tsx +105 -0
- package/src/panels/access/namespace-config-view.tsx +525 -0
- package/src/panels/access/permission-checker.tsx +231 -0
- package/src/panels/agents/agent-status-view.tsx +196 -0
- package/src/panels/agents/agents-panel.tsx +493 -0
- package/src/panels/agents/delegation-list.tsx +154 -0
- package/src/panels/agents/inbox-view.tsx +96 -0
- package/src/panels/agents/trajectories-tab.tsx +40 -0
- package/src/panels/api-console/api-console-panel.tsx +189 -0
- package/src/panels/api-console/codegen-viewer.tsx +36 -0
- package/src/panels/api-console/codegen.ts +112 -0
- package/src/panels/api-console/endpoint-list.tsx +57 -0
- package/src/panels/api-console/request-builder.tsx +69 -0
- package/src/panels/api-console/response-viewer.tsx +54 -0
- package/src/panels/connectors/available-tab.tsx +357 -0
- package/src/panels/connectors/connector-row.tsx +121 -0
- package/src/panels/connectors/connectors-panel.tsx +88 -0
- package/src/panels/connectors/error-parser.ts +116 -0
- package/src/panels/connectors/mounted-tab.tsx +179 -0
- package/src/panels/connectors/skills-tab.tsx +235 -0
- package/src/panels/connectors/template-generator.ts +211 -0
- package/src/panels/connectors/write-tab.tsx +514 -0
- package/src/panels/events/audit-tab.tsx +69 -0
- package/src/panels/events/audit-trail.tsx +75 -0
- package/src/panels/events/connector-detail.tsx +49 -0
- package/src/panels/events/connector-list.tsx +73 -0
- package/src/panels/events/connectors-tab.tsx +92 -0
- package/src/panels/events/event-replay.tsx +80 -0
- package/src/panels/events/events-panel.tsx +414 -0
- package/src/panels/events/events-tab.tsx +212 -0
- package/src/panels/events/lock-list.tsx +54 -0
- package/src/panels/events/locks-tab.tsx +103 -0
- package/src/panels/events/mcl-replay.tsx +77 -0
- package/src/panels/events/mcl-tab.tsx +83 -0
- package/src/panels/events/operations-tab-wrapper.tsx +62 -0
- package/src/panels/events/operations-tab.tsx +41 -0
- package/src/panels/events/replay-tab.tsx +76 -0
- package/src/panels/events/secrets-audit.tsx +64 -0
- package/src/panels/events/secrets-tab.tsx +75 -0
- package/src/panels/events/subscription-list.tsx +54 -0
- package/src/panels/events/subscriptions-tab.tsx +82 -0
- package/src/panels/files/file-aspects.tsx +93 -0
- package/src/panels/files/file-editor.tsx +160 -0
- package/src/panels/files/file-explorer-keybindings.ts +468 -0
- package/src/panels/files/file-explorer-panel.tsx +545 -0
- package/src/panels/files/file-lineage.tsx +163 -0
- package/src/panels/files/file-list-item.tsx +28 -0
- package/src/panels/files/file-metadata.tsx +62 -0
- package/src/panels/files/file-preview.tsx +108 -0
- package/src/panels/files/file-schema.tsx +89 -0
- package/src/panels/files/file-tree-node.tsx +44 -0
- package/src/panels/files/file-tree.tsx +169 -0
- package/src/panels/files/share-links-tab.tsx +33 -0
- package/src/panels/files/uploads-tab.tsx +45 -0
- package/src/panels/payments/approval-list.tsx +83 -0
- package/src/panels/payments/balance-card.tsx +43 -0
- package/src/panels/payments/budget-card.tsx +70 -0
- package/src/panels/payments/payments-panel.tsx +451 -0
- package/src/panels/payments/policy-list.tsx +64 -0
- package/src/panels/payments/reservation-list.tsx +78 -0
- package/src/panels/payments/transaction-list.tsx +103 -0
- package/src/panels/payments/transfer-form.tsx +109 -0
- package/src/panels/search/column-search.tsx +79 -0
- package/src/panels/search/knowledge-view.tsx +100 -0
- package/src/panels/search/memory-list.tsx +197 -0
- package/src/panels/search/playbook-list.tsx +77 -0
- package/src/panels/search/rlm-answer-view.tsx +105 -0
- package/src/panels/search/search-panel.tsx +405 -0
- package/src/panels/search/search-results.tsx +116 -0
- package/src/panels/stack/stack-panel.tsx +474 -0
- package/src/panels/versions/conflicts-tab.tsx +59 -0
- package/src/panels/versions/entry-detail.tsx +89 -0
- package/src/panels/versions/transaction-actions.tsx +34 -0
- package/src/panels/versions/transaction-list.tsx +90 -0
- package/src/panels/versions/versions-panel.tsx +276 -0
- package/src/panels/workflows/execution-list.tsx +102 -0
- package/src/panels/workflows/scheduler-view.tsx +135 -0
- package/src/panels/workflows/workflow-list.tsx +88 -0
- package/src/panels/workflows/workflows-panel.tsx +295 -0
- package/src/panels/zones/brick-detail.tsx +136 -0
- package/src/panels/zones/brick-list.tsx +56 -0
- package/src/panels/zones/cache-tab.tsx +118 -0
- package/src/panels/zones/drift-view.tsx +97 -0
- package/src/panels/zones/mcp-mounts-tab.tsx +38 -0
- package/src/panels/zones/memories-tab.tsx +37 -0
- package/src/panels/zones/reindex-status.tsx +84 -0
- package/src/panels/zones/workspaces-tab.tsx +37 -0
- package/src/panels/zones/zone-list.tsx +73 -0
- package/src/panels/zones/zones-panel.tsx +559 -0
- package/src/services/command-runner.ts +303 -0
- package/src/shared/accessibility-announcements.ts +44 -0
- package/src/shared/action-registry.ts +466 -0
- package/src/shared/brick-states.ts +91 -0
- package/src/shared/command-palette.ts +35 -0
- package/src/shared/components/announcement-bar.tsx +30 -0
- package/src/shared/components/app-confirm-dialog.tsx +29 -0
- package/src/shared/components/breadcrumb.tsx +21 -0
- package/src/shared/components/brick-gate.tsx +60 -0
- package/src/shared/components/command-output.tsx +95 -0
- package/src/shared/components/command-palette.tsx +97 -0
- package/src/shared/components/confirm-dialog.tsx +61 -0
- package/src/shared/components/diff-viewer.tsx +219 -0
- package/src/shared/components/empty-state.tsx +36 -0
- package/src/shared/components/error-bar.tsx +60 -0
- package/src/shared/components/error-boundary.tsx +53 -0
- package/src/shared/components/help-overlay.tsx +99 -0
- package/src/shared/components/identity-switcher.tsx +168 -0
- package/src/shared/components/loading-indicator.tsx +40 -0
- package/src/shared/components/pagination-bar.tsx +68 -0
- package/src/shared/components/pre-connection-screen.tsx +398 -0
- package/src/shared/components/scroll-indicator.tsx +46 -0
- package/src/shared/components/side-nav-utils.ts +68 -0
- package/src/shared/components/side-nav.tsx +287 -0
- package/src/shared/components/spinner.tsx +26 -0
- package/src/shared/components/status-bar.tsx +117 -0
- package/src/shared/components/styled-text.tsx +72 -0
- package/src/shared/components/sub-tab-bar-utils.ts +100 -0
- package/src/shared/components/sub-tab-bar.tsx +40 -0
- package/src/shared/components/tab-bar-utils.ts +36 -0
- package/src/shared/components/tab-bar.tsx +50 -0
- package/src/shared/components/text-input.tsx +73 -0
- package/src/shared/components/tooltip.tsx +53 -0
- package/src/shared/components/virtual-list.tsx +93 -0
- package/src/shared/components/welcome-screen.tsx +111 -0
- package/src/shared/hooks/use-api.ts +10 -0
- package/src/shared/hooks/use-brick-available.ts +42 -0
- package/src/shared/hooks/use-confirm.ts +66 -0
- package/src/shared/hooks/use-connection-state.ts +67 -0
- package/src/shared/hooks/use-copy.ts +31 -0
- package/src/shared/hooks/use-fresh-server.ts +62 -0
- package/src/shared/hooks/use-keyboard.ts +58 -0
- package/src/shared/hooks/use-list-navigation.ts +106 -0
- package/src/shared/hooks/use-swr.ts +117 -0
- package/src/shared/hooks/use-tab-fallback.ts +32 -0
- package/src/shared/hooks/use-text-input.ts +113 -0
- package/src/shared/hooks/use-visible-tabs.ts +61 -0
- package/src/shared/lib/circular-buffer.ts +82 -0
- package/src/shared/lib/clipboard.ts +14 -0
- package/src/shared/nav-items.ts +73 -0
- package/src/shared/navigation.ts +110 -0
- package/src/shared/status-breadcrumb.ts +74 -0
- package/src/shared/syntax-style.ts +3 -0
- package/src/shared/tab-visibility.ts +15 -0
- package/src/shared/text-style.ts +23 -0
- package/src/shared/theme.ts +179 -0
- package/src/shared/utils/format-size.ts +20 -0
- package/src/shared/utils/format-text.ts +10 -0
- package/src/shared/utils/format-time.ts +72 -0
- package/src/shared/utils/lru-cache.ts +75 -0
- package/src/stores/access-store-types.ts +154 -0
- package/src/stores/access-store.ts +674 -0
- package/src/stores/agents-store.ts +404 -0
- package/src/stores/announcement-store.ts +46 -0
- package/src/stores/api-console-store.ts +476 -0
- package/src/stores/connectors-store.ts +434 -0
- package/src/stores/create-api-action.ts +140 -0
- package/src/stores/delegation-store.ts +300 -0
- package/src/stores/error-store.ts +102 -0
- package/src/stores/events-store.ts +163 -0
- package/src/stores/files-store.ts +630 -0
- package/src/stores/first-run-store.ts +34 -0
- package/src/stores/global-store.ts +255 -0
- package/src/stores/infra-store.ts +461 -0
- package/src/stores/knowledge-store.ts +358 -0
- package/src/stores/lineage-store.ts +126 -0
- package/src/stores/mcp-store.ts +147 -0
- package/src/stores/payments-store.ts +545 -0
- package/src/stores/search-store-types.ts +155 -0
- package/src/stores/search-store.ts +656 -0
- package/src/stores/share-link-store.ts +151 -0
- package/src/stores/stack-store.ts +352 -0
- package/src/stores/ui-store.ts +161 -0
- package/src/stores/upload-store.ts +131 -0
- package/src/stores/versions-store.ts +355 -0
- package/src/stores/workflows-store.ts +402 -0
- package/src/stores/workspace-store.ts +185 -0
- package/src/stores/zones-store.ts +378 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { TabDef } from "./hooks/use-visible-tabs.js";
|
|
2
|
+
import type { PanelId } from "../stores/global-store.js";
|
|
3
|
+
import type { AccessTab } from "../stores/access-store.js";
|
|
4
|
+
import type { AgentTab } from "../stores/agents-store.js";
|
|
5
|
+
import type { SearchTab } from "../stores/search-store.js";
|
|
6
|
+
import type { ZoneTab } from "../stores/zones-store.js";
|
|
7
|
+
import type { WorkflowTab } from "../stores/workflows-store.js";
|
|
8
|
+
import type { PaymentsTab } from "../stores/payments-store.js";
|
|
9
|
+
import type { EventsPanelTab } from "../stores/infra-store.js";
|
|
10
|
+
import { NAV_ITEMS, type NavItem } from "./nav-items.js";
|
|
11
|
+
|
|
12
|
+
export interface TopLevelTab {
|
|
13
|
+
readonly id: PanelId;
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly shortcut: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PanelDescriptor {
|
|
19
|
+
readonly id: PanelId;
|
|
20
|
+
readonly tabLabel: string;
|
|
21
|
+
readonly breadcrumbLabel: string;
|
|
22
|
+
readonly shortcut: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function navItemToPanelDescriptor(item: NavItem): PanelDescriptor {
|
|
26
|
+
return {
|
|
27
|
+
id: item.id,
|
|
28
|
+
tabLabel: item.label,
|
|
29
|
+
breadcrumbLabel: item.fullLabel,
|
|
30
|
+
shortcut: item.shortcut,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const PANEL_DESCRIPTORS: Readonly<Record<PanelId, PanelDescriptor>> = Object.fromEntries(
|
|
35
|
+
NAV_ITEMS.map((item) => [item.id, navItemToPanelDescriptor(item)]),
|
|
36
|
+
) as Readonly<Record<PanelId, PanelDescriptor>>;
|
|
37
|
+
|
|
38
|
+
export const PANEL_TABS: readonly TopLevelTab[] = NAV_ITEMS.map(({ id, label, shortcut }) => ({
|
|
39
|
+
id,
|
|
40
|
+
label,
|
|
41
|
+
shortcut,
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
export const ACCESS_TABS: readonly TabDef<AccessTab>[] = [
|
|
45
|
+
{ id: "manifests", label: "Manifests", brick: "access_manifest" },
|
|
46
|
+
{ id: "alerts", label: "Alerts", brick: "governance" },
|
|
47
|
+
{ id: "credentials", label: "Credentials", brick: "auth" },
|
|
48
|
+
{ id: "fraud", label: "Fraud", brick: "governance" },
|
|
49
|
+
{ id: "delegations", label: "Delegations", brick: "delegation" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export const AGENT_TABS: readonly TabDef<AgentTab>[] = [
|
|
53
|
+
{ id: "status", label: "Status", brick: "agent_runtime" },
|
|
54
|
+
{ id: "delegations", label: "Delegations", brick: "delegation" },
|
|
55
|
+
{ id: "inbox", label: "Inbox", brick: "ipc" },
|
|
56
|
+
{ id: "trajectories", label: "Trajectories", brick: "agent_runtime" },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export const SEARCH_TABS: readonly TabDef<SearchTab>[] = [
|
|
60
|
+
{ id: "search", label: "Search", brick: "search" },
|
|
61
|
+
{ id: "knowledge", label: "Knowledge", brick: "catalog" },
|
|
62
|
+
{ id: "memories", label: "Memories", brick: "memory" },
|
|
63
|
+
{ id: "playbooks", label: "Playbooks", brick: null },
|
|
64
|
+
{ id: "ask", label: "Ask", brick: "rlm" },
|
|
65
|
+
{ id: "columns", label: "Columns", brick: "catalog" },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
export const ZONE_TABS: readonly TabDef<ZoneTab>[] = [
|
|
69
|
+
{ id: "zones", label: "Zones", brick: null },
|
|
70
|
+
{ id: "bricks", label: "Bricks", brick: null },
|
|
71
|
+
{ id: "drift", label: "Drift", brick: null },
|
|
72
|
+
{ id: "reindex", label: "Reindex", brick: ["search", "versioning"] },
|
|
73
|
+
{ id: "workspaces", label: "Workspaces", brick: "workspace" },
|
|
74
|
+
{ id: "mcp", label: "MCP", brick: "mcp" },
|
|
75
|
+
{ id: "cache", label: "Cache", brick: "cache" },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
export const WORKFLOW_TABS: readonly TabDef<WorkflowTab>[] = [
|
|
79
|
+
{ id: "workflows", label: "Workflows", brick: null },
|
|
80
|
+
{ id: "executions", label: "Executions", brick: null },
|
|
81
|
+
{ id: "scheduler", label: "Scheduler", brick: null },
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
export const PAYMENTS_TABS: readonly TabDef<PaymentsTab>[] = [
|
|
85
|
+
{ id: "balance", label: "Balance", brick: null },
|
|
86
|
+
{ id: "reservations", label: "Reservations", brick: null },
|
|
87
|
+
{ id: "transactions", label: "Transactions", brick: null },
|
|
88
|
+
{ id: "policies", label: "Policies", brick: null },
|
|
89
|
+
{ id: "approvals", label: "Approvals", brick: null },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
export const EVENTS_TABS: readonly TabDef<EventsPanelTab>[] = [
|
|
93
|
+
{ id: "events", label: "Events", brick: "eventlog" },
|
|
94
|
+
{ id: "mcl", label: "MCL", brick: "catalog" },
|
|
95
|
+
{ id: "replay", label: "Replay", brick: "eventlog" },
|
|
96
|
+
{ id: "operations", label: "Operations", brick: "eventlog" },
|
|
97
|
+
{ id: "connectors", label: "Connectors", brick: null },
|
|
98
|
+
{ id: "subscriptions", label: "Subscriptions", brick: "eventlog" },
|
|
99
|
+
{ id: "locks", label: "Locks", brick: null },
|
|
100
|
+
{ id: "secrets", label: "Secrets", brick: "auth" },
|
|
101
|
+
{ id: "audit", label: "Audit", brick: "auth" },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
export function getTabLabel<T extends string>(
|
|
105
|
+
tabs: readonly { readonly id: T; readonly label: string }[],
|
|
106
|
+
activeTab: T | null | undefined,
|
|
107
|
+
): string | null {
|
|
108
|
+
if (!activeTab) return null;
|
|
109
|
+
return tabs.find((tab) => tab.id === activeTab)?.label ?? null;
|
|
110
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ConnectionStatus, PanelId } from "../stores/global-store.js";
|
|
2
|
+
import type { AccessTab } from "../stores/access-store.js";
|
|
3
|
+
import type { AgentTab } from "../stores/agents-store.js";
|
|
4
|
+
import type { SearchTab } from "../stores/search-store.js";
|
|
5
|
+
import type { ZoneTab } from "../stores/zones-store.js";
|
|
6
|
+
import type { WorkflowTab } from "../stores/workflows-store.js";
|
|
7
|
+
import type { PaymentsTab } from "../stores/payments-store.js";
|
|
8
|
+
import type { EventsPanelTab } from "../stores/infra-store.js";
|
|
9
|
+
import {
|
|
10
|
+
ACCESS_TABS,
|
|
11
|
+
AGENT_TABS,
|
|
12
|
+
EVENTS_TABS,
|
|
13
|
+
PANEL_DESCRIPTORS,
|
|
14
|
+
PAYMENTS_TABS,
|
|
15
|
+
SEARCH_TABS,
|
|
16
|
+
WORKFLOW_TABS,
|
|
17
|
+
ZONE_TABS,
|
|
18
|
+
getTabLabel,
|
|
19
|
+
} from "./navigation.js";
|
|
20
|
+
|
|
21
|
+
export interface StatusBreadcrumbState {
|
|
22
|
+
readonly connectionStatus: ConnectionStatus;
|
|
23
|
+
readonly activePanel: PanelId | null | undefined;
|
|
24
|
+
readonly accessTab?: AccessTab | null;
|
|
25
|
+
readonly agentTab?: AgentTab | null;
|
|
26
|
+
readonly paymentsTab?: PaymentsTab | null;
|
|
27
|
+
readonly searchTab?: SearchTab | null;
|
|
28
|
+
readonly workflowTab?: WorkflowTab | null;
|
|
29
|
+
readonly zoneTab?: ZoneTab | null;
|
|
30
|
+
readonly eventsTab?: EventsPanelTab | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function deriveStatusBreadcrumb(state: StatusBreadcrumbState): string | null {
|
|
34
|
+
const { activePanel, connectionStatus } = state;
|
|
35
|
+
if (connectionStatus !== "connected" || !activePanel) return null;
|
|
36
|
+
|
|
37
|
+
const panelLabel = PANEL_DESCRIPTORS[activePanel]?.breadcrumbLabel;
|
|
38
|
+
if (!panelLabel) return null;
|
|
39
|
+
|
|
40
|
+
let subTabLabel: string | null = null;
|
|
41
|
+
|
|
42
|
+
switch (activePanel) {
|
|
43
|
+
case "access":
|
|
44
|
+
subTabLabel = getTabLabel(ACCESS_TABS, state.accessTab);
|
|
45
|
+
break;
|
|
46
|
+
case "agents":
|
|
47
|
+
subTabLabel = getTabLabel(AGENT_TABS, state.agentTab);
|
|
48
|
+
break;
|
|
49
|
+
case "payments":
|
|
50
|
+
subTabLabel = getTabLabel(PAYMENTS_TABS, state.paymentsTab);
|
|
51
|
+
break;
|
|
52
|
+
case "infrastructure":
|
|
53
|
+
subTabLabel = getTabLabel(EVENTS_TABS, state.eventsTab);
|
|
54
|
+
break;
|
|
55
|
+
case "search":
|
|
56
|
+
subTabLabel = getTabLabel(SEARCH_TABS, state.searchTab);
|
|
57
|
+
break;
|
|
58
|
+
case "workflows":
|
|
59
|
+
subTabLabel = getTabLabel(WORKFLOW_TABS, state.workflowTab);
|
|
60
|
+
break;
|
|
61
|
+
case "zones":
|
|
62
|
+
subTabLabel = getTabLabel(ZONE_TABS, state.zoneTab);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
subTabLabel = null;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!subTabLabel || subTabLabel === panelLabel) {
|
|
70
|
+
return panelLabel;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `${panelLabel} > ${subTabLabel}`;
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TabDef } from "./hooks/use-visible-tabs.js";
|
|
2
|
+
|
|
3
|
+
export function filterVisibleTabs<T extends string>(
|
|
4
|
+
allTabs: readonly TabDef<T>[],
|
|
5
|
+
enabledBricks: readonly string[],
|
|
6
|
+
featuresLoaded: boolean,
|
|
7
|
+
): readonly TabDef<T>[] {
|
|
8
|
+
if (!featuresLoaded) return allTabs;
|
|
9
|
+
|
|
10
|
+
return allTabs.filter((tab) => {
|
|
11
|
+
if (tab.brick === null) return true;
|
|
12
|
+
if (typeof tab.brick === "string") return enabledBricks.includes(tab.brick);
|
|
13
|
+
return tab.brick.some((b) => enabledBricks.includes(b));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createTextAttributes } from "@opentui/core";
|
|
2
|
+
|
|
3
|
+
interface TextStyleOptions {
|
|
4
|
+
readonly fg?: string;
|
|
5
|
+
readonly bg?: string;
|
|
6
|
+
readonly bold?: boolean;
|
|
7
|
+
readonly dim?: boolean;
|
|
8
|
+
readonly underline?: boolean;
|
|
9
|
+
readonly inverse?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function textStyle(options: TextStyleOptions = {}): {
|
|
13
|
+
fg?: string;
|
|
14
|
+
bg?: string;
|
|
15
|
+
attributes: number;
|
|
16
|
+
} {
|
|
17
|
+
const { fg, bg, bold, dim, underline, inverse } = options;
|
|
18
|
+
return {
|
|
19
|
+
fg,
|
|
20
|
+
bg,
|
|
21
|
+
attributes: createTextAttributes({ bold, dim, underline, inverse }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic color system for the TUI.
|
|
3
|
+
*
|
|
4
|
+
* All visual styling should reference these tokens rather than raw color names.
|
|
5
|
+
* Colors use ANSI 16 names for universal terminal compatibility.
|
|
6
|
+
*
|
|
7
|
+
* @see Issue #3066, Phase A1
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Semantic color tokens
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Status-based colors for state indicators across all panels.
|
|
16
|
+
*/
|
|
17
|
+
export const statusColor = {
|
|
18
|
+
/** Healthy, active, committed, connected */
|
|
19
|
+
healthy: "green",
|
|
20
|
+
/** Completed successfully — same as healthy (terminal vs ongoing state) */
|
|
21
|
+
success: "green",
|
|
22
|
+
/** Warning, starting, stopping, stale */
|
|
23
|
+
warning: "yellow",
|
|
24
|
+
/** Error, failed, rolled_back, expired */
|
|
25
|
+
error: "red",
|
|
26
|
+
/** Info, informational highlights */
|
|
27
|
+
info: "cyan",
|
|
28
|
+
/** Focus, selected, active UI chrome */
|
|
29
|
+
focus: "yellow",
|
|
30
|
+
/** Secondary text, hints, timestamps */
|
|
31
|
+
dim: "gray",
|
|
32
|
+
/** Agent identity, delegation chains */
|
|
33
|
+
identity: "magenta",
|
|
34
|
+
/** File paths, URNs, zone IDs */
|
|
35
|
+
reference: "blue",
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
export type StatusColorKey = keyof typeof statusColor;
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Connection status → color mapping
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
export const connectionColor: Record<string, string> = {
|
|
45
|
+
connected: statusColor.healthy,
|
|
46
|
+
connecting: statusColor.warning,
|
|
47
|
+
disconnected: statusColor.dim,
|
|
48
|
+
error: statusColor.error,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Brick state → color mapping
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
export const brickStateColor: Record<string, string> = {
|
|
56
|
+
active: statusColor.healthy,
|
|
57
|
+
registered: statusColor.info,
|
|
58
|
+
starting: statusColor.warning,
|
|
59
|
+
stopping: statusColor.warning,
|
|
60
|
+
unmounted: statusColor.dim,
|
|
61
|
+
unregistered: statusColor.dim,
|
|
62
|
+
failed: statusColor.error,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// =============================================================================
|
|
66
|
+
// Transaction status → color mapping
|
|
67
|
+
// =============================================================================
|
|
68
|
+
|
|
69
|
+
export const transactionStatusColor: Record<string, string> = {
|
|
70
|
+
active: statusColor.warning,
|
|
71
|
+
committed: statusColor.healthy,
|
|
72
|
+
rolled_back: statusColor.error,
|
|
73
|
+
expired: statusColor.dim,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// HTTP status code → color mapping
|
|
78
|
+
// =============================================================================
|
|
79
|
+
|
|
80
|
+
export function httpStatusColor(status: number): string {
|
|
81
|
+
if (status >= 200 && status < 300) return statusColor.healthy;
|
|
82
|
+
if (status >= 400 && status < 500) return statusColor.warning;
|
|
83
|
+
if (status >= 500) return statusColor.error;
|
|
84
|
+
return statusColor.dim;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// Agent phase → color mapping
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
export const agentPhaseColor: Record<string, string> = {
|
|
92
|
+
ready: statusColor.healthy,
|
|
93
|
+
active: statusColor.healthy,
|
|
94
|
+
warming: statusColor.warning,
|
|
95
|
+
evicting: statusColor.warning,
|
|
96
|
+
evicted: statusColor.dim,
|
|
97
|
+
failed: statusColor.error,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// Agent state → color mapping (for agent list)
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
export const agentStateColor: Record<string, string> = {
|
|
105
|
+
registered: statusColor.info,
|
|
106
|
+
delegated: statusColor.identity,
|
|
107
|
+
running: statusColor.healthy,
|
|
108
|
+
connected: statusColor.healthy,
|
|
109
|
+
disconnected: statusColor.dim,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// =============================================================================
|
|
113
|
+
// Delegation mode → color mapping
|
|
114
|
+
// =============================================================================
|
|
115
|
+
|
|
116
|
+
export const delegationModeColor: Record<string, string> = {
|
|
117
|
+
shared: statusColor.info,
|
|
118
|
+
copy: statusColor.warning,
|
|
119
|
+
clean: statusColor.error,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// =============================================================================
|
|
123
|
+
// Delegation status → color mapping
|
|
124
|
+
// =============================================================================
|
|
125
|
+
|
|
126
|
+
export const delegationStatusColor: Record<string, string> = {
|
|
127
|
+
active: statusColor.healthy,
|
|
128
|
+
revoked: statusColor.error,
|
|
129
|
+
expired: statusColor.dim,
|
|
130
|
+
completed: statusColor.info,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// =============================================================================
|
|
134
|
+
// Focus / UI chrome colors
|
|
135
|
+
// =============================================================================
|
|
136
|
+
|
|
137
|
+
export const focusColor = {
|
|
138
|
+
/** Active pane border */
|
|
139
|
+
activeBorder: statusColor.focus,
|
|
140
|
+
/** Inactive pane border */
|
|
141
|
+
inactiveBorder: statusColor.dim,
|
|
142
|
+
/** Selected item highlight */
|
|
143
|
+
selected: statusColor.focus,
|
|
144
|
+
/** Help bar action keys */
|
|
145
|
+
actionKey: statusColor.focus,
|
|
146
|
+
/** Help bar navigation keys */
|
|
147
|
+
navKey: statusColor.dim,
|
|
148
|
+
} as const;
|
|
149
|
+
|
|
150
|
+
// =============================================================================
|
|
151
|
+
// Rich color palette — hex values for modern terminals
|
|
152
|
+
//
|
|
153
|
+
// Used by shared chrome components (tab bar, status bar, error bar).
|
|
154
|
+
// Components should prefer semantic tokens (statusColor, focusColor) for
|
|
155
|
+
// state-dependent colors and use palette for structural UI chrome.
|
|
156
|
+
// =============================================================================
|
|
157
|
+
|
|
158
|
+
export const palette = {
|
|
159
|
+
/** Primary accent — active tabs, selected items, key bindings */
|
|
160
|
+
accent: "#D4A017",
|
|
161
|
+
/** Secondary accent — hover, secondary actions */
|
|
162
|
+
accentDim: "#B8890F",
|
|
163
|
+
/** Success — green confirmation */
|
|
164
|
+
success: "#34D399",
|
|
165
|
+
/** Error — bright red */
|
|
166
|
+
error: "#F87171",
|
|
167
|
+
/** Error secondary — softer red for hints */
|
|
168
|
+
errorDim: "#FCA5A5",
|
|
169
|
+
/** Warning — amber */
|
|
170
|
+
warning: "#FBBF24",
|
|
171
|
+
/** Muted text — labels, separators */
|
|
172
|
+
muted: "#8B8B92",
|
|
173
|
+
/** Very muted — borders, inactive chrome */
|
|
174
|
+
faint: "#2A2A2E",
|
|
175
|
+
/** Bright text — active content */
|
|
176
|
+
bright: "#EDEDEF",
|
|
177
|
+
/** Header/title text */
|
|
178
|
+
title: "#EDEDEF",
|
|
179
|
+
} as const;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consistent byte-size formatting across all panels.
|
|
3
|
+
*
|
|
4
|
+
* Output: "0 B", "1.5 KB", "2.3 MB", "1.1 GB"
|
|
5
|
+
* - Space before unit, standard suffixes
|
|
6
|
+
* - Supports up to GB
|
|
7
|
+
*
|
|
8
|
+
* @see Issue #3102, Decision 5A
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const KB = 1024;
|
|
12
|
+
const MB = KB * 1024;
|
|
13
|
+
const GB = MB * 1024;
|
|
14
|
+
|
|
15
|
+
export function formatSize(bytes: number): string {
|
|
16
|
+
if (bytes < KB) return `${bytes} B`;
|
|
17
|
+
if (bytes < MB) return `${(bytes / KB).toFixed(1)} KB`;
|
|
18
|
+
if (bytes < GB) return `${(bytes / MB).toFixed(1)} MB`;
|
|
19
|
+
return `${(bytes / GB).toFixed(1)} GB`;
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text truncation utility for terminal column alignment.
|
|
3
|
+
*
|
|
4
|
+
* @see Issue #3102, Decision 8A
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export function truncateText(text: string, maxLen: number): string {
|
|
8
|
+
if (text.length <= maxLen) return text;
|
|
9
|
+
return `${text.slice(0, maxLen - 3)}...`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consistent date/time formatting across all panels.
|
|
3
|
+
*
|
|
4
|
+
* Strategy:
|
|
5
|
+
* - Relative time for < 24 hours ("2h ago", "5m ago")
|
|
6
|
+
* - Absolute for >= 24 hours ("Mar 15, 14:30")
|
|
7
|
+
* - Consistent 19-char max width for absolute timestamps
|
|
8
|
+
*
|
|
9
|
+
* @see Issue #3066, Phase E8
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const SECOND = 1_000;
|
|
13
|
+
const MINUTE = 60 * SECOND;
|
|
14
|
+
const HOUR = 60 * MINUTE;
|
|
15
|
+
const DAY = 24 * HOUR;
|
|
16
|
+
|
|
17
|
+
const MONTHS = [
|
|
18
|
+
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
19
|
+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Format a timestamp for display in the TUI.
|
|
24
|
+
*
|
|
25
|
+
* @param input - Unix epoch ms, ISO string, or Date object
|
|
26
|
+
* @param now - Current time in ms (injectable for testing). Defaults to Date.now()
|
|
27
|
+
* @param utc - If true, use UTC for absolute timestamps (recommended for distributed systems). Default: true.
|
|
28
|
+
* @returns Formatted time string, max 19 chars
|
|
29
|
+
*/
|
|
30
|
+
export function formatTimestamp(input: number | string | Date, now?: number, utc = true): string {
|
|
31
|
+
const date = input instanceof Date ? input : new Date(input);
|
|
32
|
+
const ts = date.getTime();
|
|
33
|
+
|
|
34
|
+
if (Number.isNaN(ts)) return "—";
|
|
35
|
+
|
|
36
|
+
const currentTime = now ?? Date.now();
|
|
37
|
+
const delta = currentTime - ts;
|
|
38
|
+
|
|
39
|
+
// Future timestamps: show absolute
|
|
40
|
+
if (delta < 0) {
|
|
41
|
+
return formatAbsolute(date, utc);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// < 1 minute
|
|
45
|
+
if (delta < MINUTE) {
|
|
46
|
+
const secs = Math.floor(delta / SECOND);
|
|
47
|
+
return secs <= 1 ? "just now" : `${secs}s ago`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// < 1 hour
|
|
51
|
+
if (delta < HOUR) {
|
|
52
|
+
const mins = Math.floor(delta / MINUTE);
|
|
53
|
+
return `${mins}m ago`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// < 24 hours
|
|
57
|
+
if (delta < DAY) {
|
|
58
|
+
const hours = Math.floor(delta / HOUR);
|
|
59
|
+
return `${hours}h ago`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return formatAbsolute(date, utc);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function formatAbsolute(date: Date, utc: boolean): string {
|
|
66
|
+
const month = MONTHS[utc ? date.getUTCMonth() : date.getMonth()]!;
|
|
67
|
+
const day = utc ? date.getUTCDate() : date.getDate();
|
|
68
|
+
const hours = String(utc ? date.getUTCHours() : date.getHours()).padStart(2, "0");
|
|
69
|
+
const minutes = String(utc ? date.getUTCMinutes() : date.getMinutes()).padStart(2, "0");
|
|
70
|
+
const suffix = utc ? "Z" : "";
|
|
71
|
+
return `${month} ${String(day).padStart(2, " ")}, ${hours}:${minutes}${suffix}`;
|
|
72
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic LRU cache with O(n) eviction.
|
|
3
|
+
*
|
|
4
|
+
* Uses a monotonic access counter for LRU ordering.
|
|
5
|
+
* Eviction finds the minimum-access-order entry in a single pass (O(n))
|
|
6
|
+
* instead of sorting (O(n log n)).
|
|
7
|
+
*
|
|
8
|
+
* @see Issue #3102, Decisions 7A + 16A
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface CacheEntry<T> {
|
|
12
|
+
data: T;
|
|
13
|
+
fetchedAt: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let accessCounter = 0;
|
|
17
|
+
|
|
18
|
+
export class LruCache<T = unknown> {
|
|
19
|
+
private readonly _map = new Map<string, { entry: CacheEntry<T>; accessOrder: number }>();
|
|
20
|
+
private readonly _maxSize: number;
|
|
21
|
+
|
|
22
|
+
constructor(maxSize: number = 200) {
|
|
23
|
+
this._maxSize = maxSize;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get(key: string): CacheEntry<T> | undefined {
|
|
27
|
+
const item = this._map.get(key);
|
|
28
|
+
if (!item) return undefined;
|
|
29
|
+
// Update access order for LRU tracking (monotonic counter avoids Date.now() ties)
|
|
30
|
+
item.accessOrder = ++accessCounter;
|
|
31
|
+
return item.entry;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set(key: string, entry: CacheEntry<T>): void {
|
|
35
|
+
this._map.set(key, { entry, accessOrder: ++accessCounter });
|
|
36
|
+
this._evictIfNeeded();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
has(key: string): boolean {
|
|
40
|
+
return this._map.has(key);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
delete(key: string): void {
|
|
44
|
+
this._map.delete(key);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
clear(): void {
|
|
48
|
+
this._map.clear();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get size(): number {
|
|
52
|
+
return this._map.size;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private _evictIfNeeded(): void {
|
|
56
|
+
while (this._map.size > this._maxSize) {
|
|
57
|
+
// O(n) min-scan: find the least-recently-accessed entry
|
|
58
|
+
let minKey: string | undefined;
|
|
59
|
+
let minOrder = Infinity;
|
|
60
|
+
|
|
61
|
+
for (const [key, item] of this._map) {
|
|
62
|
+
if (item.accessOrder < minOrder) {
|
|
63
|
+
minOrder = item.accessOrder;
|
|
64
|
+
minKey = key;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (minKey !== undefined) {
|
|
69
|
+
this._map.delete(minKey);
|
|
70
|
+
} else {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|