@interactive-inc/claude-funnel 0.10.0 → 0.10.1
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/dist/bin.js +448 -448
- package/dist/connectors/slack.d.ts +1 -29
- package/dist/gateway/daemon.js +166 -166
- package/dist/index.d.ts +4 -11
- package/dist/index.js +133 -120
- package/dist/slack-event-processor-CS-bAit9.d.ts +43 -0
- package/package.json +1 -6
- package/dist/slack-connector-schema-D7zAHN8k.d.ts +0 -15
- package/lib/bin.ts +0 -3
- package/lib/cli/factory.ts +0 -10
- package/lib/cli/index.ts +0 -85
- package/lib/cli/router/query-to-cli-args.ts +0 -20
- package/lib/cli/router/to-request.ts +0 -113
- package/lib/cli/router/validator.ts +0 -27
- package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +0 -27
- package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +0 -40
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +0 -41
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +0 -22
- package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +0 -23
- package/lib/cli/routes/channels.$channel.connectors.$connector.ts +0 -26
- package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +0 -92
- package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +0 -22
- package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +0 -63
- package/lib/cli/routes/channels.$channel.connectors.ts +0 -26
- package/lib/cli/routes/channels.$channel.publish.ts +0 -52
- package/lib/cli/routes/channels.$channel.rename.$newName.ts +0 -22
- package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +0 -34
- package/lib/cli/routes/channels.$channel.ts +0 -34
- package/lib/cli/routes/channels.add.$channel.ts +0 -33
- package/lib/cli/routes/channels.remove.$channel.ts +0 -20
- package/lib/cli/routes/channels.ts +0 -39
- package/lib/cli/routes/claude.ts +0 -70
- package/lib/cli/routes/gateway.listeners.ts +0 -41
- package/lib/cli/routes/gateway.logs.ts +0 -123
- package/lib/cli/routes/gateway.restart.ts +0 -50
- package/lib/cli/routes/gateway.run.ts +0 -41
- package/lib/cli/routes/gateway.start.ts +0 -50
- package/lib/cli/routes/gateway.status.ts +0 -19
- package/lib/cli/routes/gateway.stop.ts +0 -32
- package/lib/cli/routes/gateway.ts +0 -55
- package/lib/cli/routes/index.ts +0 -219
- package/lib/cli/routes/profiles.$profile.as-default.ts +0 -22
- package/lib/cli/routes/profiles.$profile.rename.$newName.ts +0 -22
- package/lib/cli/routes/profiles.$profile.run.ts +0 -36
- package/lib/cli/routes/profiles.add.$profile.ts +0 -49
- package/lib/cli/routes/profiles.remove.$profile.ts +0 -20
- package/lib/cli/routes/profiles.set.$profile.ts +0 -45
- package/lib/cli/routes/profiles.ts +0 -40
- package/lib/cli/routes/status.ts +0 -93
- package/lib/cli/routes/update.ts +0 -27
- package/lib/connectors/connector-adapter.ts +0 -9
- package/lib/connectors/connector-config-schema.ts +0 -16
- package/lib/connectors/connector-factory.ts +0 -94
- package/lib/connectors/connector-listener.ts +0 -20
- package/lib/connectors/discord-adapter.ts +0 -51
- package/lib/connectors/discord-connector-schema.ts +0 -12
- package/lib/connectors/discord-event-processor.ts +0 -48
- package/lib/connectors/discord-listener.ts +0 -111
- package/lib/connectors/discord.ts +0 -4
- package/lib/connectors/gh-adapter.ts +0 -48
- package/lib/connectors/gh-connector-schema.ts +0 -12
- package/lib/connectors/gh-listener.ts +0 -137
- package/lib/connectors/gh.ts +0 -3
- package/lib/connectors/match-cron.ts +0 -78
- package/lib/connectors/schedule-connector-schema.ts +0 -33
- package/lib/connectors/schedule-listener.ts +0 -207
- package/lib/connectors/schedule-state-store.ts +0 -54
- package/lib/connectors/schedule.ts +0 -4
- package/lib/connectors/slack-adapter.ts +0 -36
- package/lib/connectors/slack-connector-schema.ts +0 -13
- package/lib/connectors/slack-event-processor.ts +0 -97
- package/lib/connectors/slack-listener.ts +0 -97
- package/lib/connectors/slack.ts +0 -4
- package/lib/engine/channels/channels.ts +0 -520
- package/lib/engine/claude/claude.ts +0 -205
- package/lib/engine/claude/gateway-controller.ts +0 -4
- package/lib/engine/fs/file-system.ts +0 -23
- package/lib/engine/fs/memory-file-system.ts +0 -102
- package/lib/engine/fs/node-file-system.ts +0 -68
- package/lib/engine/http/http-client.ts +0 -17
- package/lib/engine/http/memory-http-client.ts +0 -36
- package/lib/engine/http/node-http-client.ts +0 -23
- package/lib/engine/id/id-generator.ts +0 -7
- package/lib/engine/id/memory-id-generator.ts +0 -20
- package/lib/engine/id/node-id-generator.ts +0 -7
- package/lib/engine/logger/logger.ts +0 -11
- package/lib/engine/logger/memory-logger.ts +0 -28
- package/lib/engine/logger/node-logger.ts +0 -49
- package/lib/engine/logger/noop-logger.ts +0 -9
- package/lib/engine/mcp/channel-server.ts +0 -123
- package/lib/engine/mcp/channel-subscriber.ts +0 -82
- package/lib/engine/mcp/mcp.ts +0 -126
- package/lib/engine/mcp/read-channel-connectors.ts +0 -34
- package/lib/engine/mcp/read-gateway-token.ts +0 -16
- package/lib/engine/mcp/usage-hint-for-type.ts +0 -15
- package/lib/engine/process/memory-process-runner.ts +0 -88
- package/lib/engine/process/node-process-runner.ts +0 -91
- package/lib/engine/process/process-runner.ts +0 -33
- package/lib/engine/profiles/profile-channel-checker.ts +0 -7
- package/lib/engine/profiles/profiles.ts +0 -126
- package/lib/engine/settings/mock-settings-reader.ts +0 -27
- package/lib/engine/settings/settings-reader.ts +0 -6
- package/lib/engine/settings/settings-schema.ts +0 -48
- package/lib/engine/settings/settings-store.ts +0 -110
- package/lib/engine/time/clock.ts +0 -15
- package/lib/engine/time/memory-clock.ts +0 -26
- package/lib/engine/time/node-clock.ts +0 -7
- package/lib/funnel.ts +0 -294
- package/lib/gateway/auth-middleware.ts +0 -44
- package/lib/gateway/broadcaster.ts +0 -319
- package/lib/gateway/channel-publisher.ts +0 -67
- package/lib/gateway/daemon.ts +0 -47
- package/lib/gateway/factory.ts +0 -10
- package/lib/gateway/funnel-event-store.ts +0 -155
- package/lib/gateway/gateway-server.ts +0 -426
- package/lib/gateway/gateway-token.ts +0 -79
- package/lib/gateway/gateway.ts +0 -209
- package/lib/gateway/kill-competing-slack-gateways.ts +0 -56
- package/lib/gateway/listener-supervisor.ts +0 -339
- package/lib/gateway/listeners-client.ts +0 -128
- package/lib/gateway/publish-schema.ts +0 -27
- package/lib/gateway/resolve-daemon-script.ts +0 -26
- package/lib/gateway/routes/channels.connectors.call.ts +0 -39
- package/lib/gateway/routes/channels.publish.ts +0 -44
- package/lib/gateway/routes/health.ts +0 -13
- package/lib/gateway/routes/index.ts +0 -26
- package/lib/gateway/routes/listeners.list.ts +0 -6
- package/lib/gateway/routes/listeners.restart.ts +0 -15
- package/lib/gateway/routes/listeners.start.ts +0 -15
- package/lib/gateway/routes/listeners.stop.ts +0 -15
- package/lib/gateway/routes/route-deps.ts +0 -19
- package/lib/gateway/routes/status.ts +0 -15
- package/lib/gateway/routes/validator.ts +0 -17
- package/lib/index.ts +0 -67
- package/lib/logger/leuco-human-file-writer.ts +0 -65
- package/lib/logger/leuco-human-logger.ts +0 -98
- package/lib/logger/leuco-human-record.ts +0 -16
- package/lib/logger/leuco-human-stdout-writer.ts +0 -26
- package/lib/logger/leuco-human-writer.ts +0 -14
- package/lib/logger/leuco-logger-memory-sink.ts +0 -67
- package/lib/logger/leuco-logger-record.ts +0 -13
- package/lib/logger/leuco-logger-sink.ts +0 -33
- package/lib/logger/leuco-logger-sqlite-sink.ts +0 -355
- package/lib/logger/leuco-logger.ts +0 -135
- package/lib/tui/app.tsx +0 -357
- package/lib/tui/components/add-row.tsx +0 -18
- package/lib/tui/components/brand.tsx +0 -27
- package/lib/tui/components/card.tsx +0 -44
- package/lib/tui/components/detail-bar.tsx +0 -46
- package/lib/tui/components/editable-field.tsx +0 -33
- package/lib/tui/components/empty-state.tsx +0 -11
- package/lib/tui/components/gateway-status.tsx +0 -66
- package/lib/tui/components/keymap.tsx +0 -29
- package/lib/tui/components/menu-item.tsx +0 -73
- package/lib/tui/components/menu.tsx +0 -26
- package/lib/tui/components/panel-header.tsx +0 -22
- package/lib/tui/components/readonly-field.tsx +0 -18
- package/lib/tui/components/section-header.tsx +0 -25
- package/lib/tui/components/selection-accent.tsx +0 -32
- package/lib/tui/components/session-item.tsx +0 -33
- package/lib/tui/components/session-list.tsx +0 -33
- package/lib/tui/components/ui/hascii/accordion-item.tsx +0 -88
- package/lib/tui/components/ui/hascii/accordion.tsx +0 -96
- package/lib/tui/components/ui/hascii/alert-dialog.tsx +0 -43
- package/lib/tui/components/ui/hascii/badge.tsx +0 -51
- package/lib/tui/components/ui/hascii/breadcrumb.tsx +0 -58
- package/lib/tui/components/ui/hascii/button.tsx +0 -194
- package/lib/tui/components/ui/hascii/card-content.tsx +0 -14
- package/lib/tui/components/ui/hascii/card-description.tsx +0 -13
- package/lib/tui/components/ui/hascii/card-footer.tsx +0 -14
- package/lib/tui/components/ui/hascii/card-header.tsx +0 -14
- package/lib/tui/components/ui/hascii/card-title.tsx +0 -13
- package/lib/tui/components/ui/hascii/card.tsx +0 -27
- package/lib/tui/components/ui/hascii/checkbox.tsx +0 -65
- package/lib/tui/components/ui/hascii/command.tsx +0 -159
- package/lib/tui/components/ui/hascii/dialog-content.tsx +0 -14
- package/lib/tui/components/ui/hascii/dialog-description.tsx +0 -13
- package/lib/tui/components/ui/hascii/dialog-footer.tsx +0 -14
- package/lib/tui/components/ui/hascii/dialog-header.tsx +0 -14
- package/lib/tui/components/ui/hascii/dialog-title.tsx +0 -13
- package/lib/tui/components/ui/hascii/dialog.tsx +0 -27
- package/lib/tui/components/ui/hascii/file-tree.tsx +0 -142
- package/lib/tui/components/ui/hascii/focus-group.tsx +0 -62
- package/lib/tui/components/ui/hascii/form-item.tsx +0 -43
- package/lib/tui/components/ui/hascii/input-otp.tsx +0 -86
- package/lib/tui/components/ui/hascii/input.tsx +0 -130
- package/lib/tui/components/ui/hascii/pagination.tsx +0 -105
- package/lib/tui/components/ui/hascii/progress.tsx +0 -28
- package/lib/tui/components/ui/hascii/select.tsx +0 -131
- package/lib/tui/components/ui/hascii/separator.tsx +0 -35
- package/lib/tui/components/ui/hascii/sidebar-content.tsx +0 -23
- package/lib/tui/components/ui/hascii/sidebar-header.tsx +0 -14
- package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +0 -67
- package/lib/tui/components/ui/hascii/sidebar.tsx +0 -24
- package/lib/tui/components/ui/hascii/skeleton.tsx +0 -60
- package/lib/tui/components/ui/hascii/slider.tsx +0 -91
- package/lib/tui/components/ui/hascii/snackbar.tsx +0 -75
- package/lib/tui/components/ui/hascii/sparkline.tsx +0 -53
- package/lib/tui/components/ui/hascii/spinner.tsx +0 -47
- package/lib/tui/components/ui/hascii/stepper.tsx +0 -54
- package/lib/tui/components/ui/hascii/switch.tsx +0 -66
- package/lib/tui/components/ui/hascii/table.tsx +0 -95
- package/lib/tui/components/ui/hascii/tabs.tsx +0 -59
- package/lib/tui/components/ui/hascii/toggle-group-item.tsx +0 -45
- package/lib/tui/components/ui/hascii/toggle-group.tsx +0 -99
- package/lib/tui/components/ui/hascii/tree.tsx +0 -104
- package/lib/tui/components/view-shell.tsx +0 -44
- package/lib/tui/filter-input.tsx +0 -33
- package/lib/tui/hooks/hascii/use-pressable.ts +0 -54
- package/lib/tui/parse-comma-list.ts +0 -14
- package/lib/tui/profile-launcher.tsx +0 -61
- package/lib/tui/scrollbar-options.ts +0 -19
- package/lib/tui/sidebar.tsx +0 -50
- package/lib/tui/theme.ts +0 -40
- package/lib/tui/tui.tsx +0 -20
- package/lib/tui/types.ts +0 -38
- package/lib/tui/unique-name.ts +0 -18
- package/lib/tui/use-event-stream.ts +0 -133
- package/lib/tui/use-snapshot.ts +0 -99
- package/lib/tui/utils/hascii/form-item-context.tsx +0 -23
- package/lib/tui/utils/hascii/input-focus-context.tsx +0 -31
- package/lib/tui/utils/hascii/theme-context.tsx +0 -26
- package/lib/tui/utils/hascii/theme.ts +0 -176
- package/lib/tui/views/channels-view.tsx +0 -108
- package/lib/tui/views/connectors-view.tsx +0 -164
- package/lib/tui/views/events-view.tsx +0 -160
- package/lib/tui/views/listeners-view.tsx +0 -80
- package/lib/tui/views/profiles-view.tsx +0 -152
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { funnel } from "@/tui/theme"
|
|
3
|
-
import { usePressable } from "@/tui/hooks/hascii/use-pressable"
|
|
4
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
label: string
|
|
8
|
-
active: boolean
|
|
9
|
-
count?: number
|
|
10
|
-
onSelect: () => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const ROW_HEIGHT = funnel.paddingY * 2 + 1
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* One row in the sidebar nav.
|
|
17
|
-
*
|
|
18
|
-
* Active state: a thin `▏` (U+258F LEFT ONE EIGHTH BLOCK) rule painted
|
|
19
|
-
* in the primary color, stacked to fill the row height. The rule is
|
|
20
|
-
* narrower than a full character cell so it reads as a delicate accent
|
|
21
|
-
* instead of a heavy block. The sidebar's own background is `muted`,
|
|
22
|
-
* so hover/active lift the row to `secondaryHover` / `secondaryActive`
|
|
23
|
-
* to read against it.
|
|
24
|
-
*/
|
|
25
|
-
export function MenuItem(props: Props) {
|
|
26
|
-
const theme = useHasciiTheme()
|
|
27
|
-
const press = usePressable({ onPress: props.onSelect })
|
|
28
|
-
|
|
29
|
-
const bg = press.isPressed
|
|
30
|
-
? theme.color.secondaryActive
|
|
31
|
-
: props.active || press.isHovered
|
|
32
|
-
? theme.color.secondaryHover
|
|
33
|
-
: undefined
|
|
34
|
-
|
|
35
|
-
const isLifted = props.active || press.isHovered || press.isPressed
|
|
36
|
-
const fg = isLifted ? theme.color.foreground : theme.color.foreground
|
|
37
|
-
const countFg = isLifted ? theme.color.foreground : funnel.faint
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<box
|
|
41
|
-
{...press.bind}
|
|
42
|
-
style={{
|
|
43
|
-
flexDirection: "row",
|
|
44
|
-
justifyContent: "space-between",
|
|
45
|
-
backgroundColor: bg,
|
|
46
|
-
paddingLeft: funnel.paddingX,
|
|
47
|
-
paddingRight: funnel.paddingX,
|
|
48
|
-
paddingTop: funnel.paddingY,
|
|
49
|
-
paddingBottom: funnel.paddingY,
|
|
50
|
-
}}
|
|
51
|
-
>
|
|
52
|
-
{props.active ? (
|
|
53
|
-
<box
|
|
54
|
-
style={{
|
|
55
|
-
position: "absolute",
|
|
56
|
-
left: 0,
|
|
57
|
-
top: 0,
|
|
58
|
-
bottom: 0,
|
|
59
|
-
flexDirection: "column",
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
{Array.from({ length: ROW_HEIGHT }, (_, index) => (
|
|
63
|
-
<text key={index} fg={funnel.primary}>
|
|
64
|
-
▏
|
|
65
|
-
</text>
|
|
66
|
-
))}
|
|
67
|
-
</box>
|
|
68
|
-
) : null}
|
|
69
|
-
<text fg={fg}>{props.label}</text>
|
|
70
|
-
{props.count !== undefined ? <text fg={countFg}>{String(props.count)}</text> : null}
|
|
71
|
-
</box>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { MenuItem } from "@/tui/components/menu-item"
|
|
3
|
-
import type { MenuItem as MenuItemType, View } from "@/tui/types"
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
items: MenuItemType[]
|
|
7
|
-
active: View
|
|
8
|
-
onSelect: (view: View) => void
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Vertical list of clickable nav rows. Each row is a `MenuItem`. */
|
|
12
|
-
export function Menu(props: Props) {
|
|
13
|
-
return (
|
|
14
|
-
<box style={{ flexDirection: "column" }}>
|
|
15
|
-
{props.items.map((item) => (
|
|
16
|
-
<MenuItem
|
|
17
|
-
key={item.view}
|
|
18
|
-
label={item.label}
|
|
19
|
-
active={item.view === props.active}
|
|
20
|
-
count={item.count}
|
|
21
|
-
onSelect={() => props.onSelect(item.view)}
|
|
22
|
-
/>
|
|
23
|
-
))}
|
|
24
|
-
</box>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { funnel } from "@/tui/theme"
|
|
3
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
label: string
|
|
7
|
-
count?: number
|
|
8
|
-
hint?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Dim section label rendered at the top of every Panel. */
|
|
12
|
-
export function PanelHeader(props: Props) {
|
|
13
|
-
const theme = useHasciiTheme()
|
|
14
|
-
const text = props.count !== undefined ? `${props.label} (${props.count})` : props.label
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<text fg={theme.color.mutedForeground}>
|
|
18
|
-
{text}
|
|
19
|
-
{props.hint ? <span fg={funnel.faint}>{` · ${props.hint}`}</span> : null}
|
|
20
|
-
</text>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { HasciiFormItem } from "@/tui/components/ui/hascii/form-item"
|
|
2
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
3
|
-
|
|
4
|
-
type Props = {
|
|
5
|
-
label: string
|
|
6
|
-
value: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Static label + value row that mirrors the EditableField layout. */
|
|
10
|
-
export function ReadonlyField(props: Props) {
|
|
11
|
-
const theme = useHasciiTheme()
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<HasciiFormItem label={props.label} labelWidth={12}>
|
|
15
|
-
<text fg={theme.color.foreground}>{props.value}</text>
|
|
16
|
-
</HasciiFormItem>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { funnel } from "@/tui/theme"
|
|
3
|
-
|
|
4
|
-
type Props = {
|
|
5
|
-
label: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Tiny faint label rendered above each sidebar section. Wrapped in a box
|
|
10
|
-
* because OpenTUI ignores padding on `<text>`; only boxes lay out with
|
|
11
|
-
* padding.
|
|
12
|
-
*/
|
|
13
|
-
export function SectionHeader(props: Props) {
|
|
14
|
-
return (
|
|
15
|
-
<box
|
|
16
|
-
style={{
|
|
17
|
-
flexDirection: "row",
|
|
18
|
-
paddingLeft: funnel.paddingX,
|
|
19
|
-
paddingRight: funnel.paddingX,
|
|
20
|
-
}}
|
|
21
|
-
>
|
|
22
|
-
<text fg={funnel.faint}>{props.label}</text>
|
|
23
|
-
</box>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { funnel } from "@/tui/theme"
|
|
3
|
-
|
|
4
|
-
const RULE_LENGTH = 20
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A thin `▏` (U+258F) primary-colour rule pinned to the left edge of
|
|
8
|
-
* the parent box. Stacked tall enough to span any reasonable card or
|
|
9
|
-
* field-group height; rows past the parent's bottom edge are clipped.
|
|
10
|
-
*
|
|
11
|
-
* Use inside a `position: relative` parent to mark it as the "current"
|
|
12
|
-
* selection — same look as the sidebar `MenuItem` active accent.
|
|
13
|
-
*/
|
|
14
|
-
export function SelectionAccent() {
|
|
15
|
-
return (
|
|
16
|
-
<box
|
|
17
|
-
style={{
|
|
18
|
-
position: "absolute",
|
|
19
|
-
left: 0,
|
|
20
|
-
top: 0,
|
|
21
|
-
bottom: 0,
|
|
22
|
-
flexDirection: "column",
|
|
23
|
-
}}
|
|
24
|
-
>
|
|
25
|
-
{Array.from({ length: RULE_LENGTH }, (_, index) => (
|
|
26
|
-
<text key={index} fg={funnel.primary}>
|
|
27
|
-
▏
|
|
28
|
-
</text>
|
|
29
|
-
))}
|
|
30
|
-
</box>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { funnel } from "@/tui/theme"
|
|
3
|
-
import type { Session } from "@/tui/types"
|
|
4
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
session: Session
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** One connected WebSocket session — channel name + connector summary. */
|
|
11
|
-
export function SessionItem(props: Props) {
|
|
12
|
-
const theme = useHasciiTheme()
|
|
13
|
-
const { session } = props
|
|
14
|
-
const summary =
|
|
15
|
-
session.connectors.length === 0
|
|
16
|
-
? "(no connectors)"
|
|
17
|
-
: session.connectors.length === 1
|
|
18
|
-
? session.connectors[0]
|
|
19
|
-
: `${session.connectors.length} connectors`
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<box
|
|
23
|
-
style={{
|
|
24
|
-
flexDirection: "column",
|
|
25
|
-
paddingLeft: funnel.paddingX,
|
|
26
|
-
paddingRight: funnel.paddingX,
|
|
27
|
-
}}
|
|
28
|
-
>
|
|
29
|
-
<text fg={theme.color.foreground}>{session.channel || "(unnamed)"}</text>
|
|
30
|
-
<text fg={funnel.faint}>{summary}</text>
|
|
31
|
-
</box>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/react */
|
|
2
|
-
import { SessionItem } from "@/tui/components/session-item"
|
|
3
|
-
import { funnel } from "@/tui/theme"
|
|
4
|
-
import type { Session } from "@/tui/types"
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
sessions: Session[]
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** Vertical list of connected sessions (Claude MCP clients) for the sidebar. */
|
|
11
|
-
export function SessionList(props: Props) {
|
|
12
|
-
if (props.sessions.length === 0) {
|
|
13
|
-
return (
|
|
14
|
-
<box
|
|
15
|
-
style={{
|
|
16
|
-
flexDirection: "row",
|
|
17
|
-
paddingLeft: funnel.paddingX,
|
|
18
|
-
paddingRight: funnel.paddingX,
|
|
19
|
-
}}
|
|
20
|
-
>
|
|
21
|
-
<text fg={funnel.faint}>(none)</text>
|
|
22
|
-
</box>
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<box style={{ flexDirection: "column" }}>
|
|
28
|
-
{props.sessions.map((session, index) => (
|
|
29
|
-
<SessionItem key={`${session.channel}-${index}`} session={session} />
|
|
30
|
-
))}
|
|
31
|
-
</box>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react"
|
|
2
|
-
import type { HasciiTheme } from "@/tui/utils/hascii/theme"
|
|
3
|
-
import { useHasciiAccordion } from "@/tui/components/ui/hascii/accordion"
|
|
4
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
5
|
-
import { usePressable } from "@/tui/hooks/hascii/use-pressable"
|
|
6
|
-
|
|
7
|
-
export type Props = {
|
|
8
|
-
value: string
|
|
9
|
-
title: string
|
|
10
|
-
children?: ReactNode
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const HEADER_HEIGHT = 3
|
|
14
|
-
|
|
15
|
-
const pickHeaderBg = (
|
|
16
|
-
isOpen: boolean,
|
|
17
|
-
isHovered: boolean,
|
|
18
|
-
isPressed: boolean,
|
|
19
|
-
theme: HasciiTheme,
|
|
20
|
-
): string | undefined => {
|
|
21
|
-
if (isPressed) return theme.color.secondaryActive
|
|
22
|
-
if (isHovered && isOpen) return theme.color.hoverActive
|
|
23
|
-
if (isHovered) return theme.color.secondaryHover
|
|
24
|
-
if (isOpen) return theme.color.secondaryActive
|
|
25
|
-
return undefined
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Single collapsible row inside HasciiAccordion. Header tracks the same hover/active palette as HasciiSidebarMenuItem; body uses a muted text color. */
|
|
29
|
-
export function HasciiAccordionItem(props: Props) {
|
|
30
|
-
const accordion = useHasciiAccordion()
|
|
31
|
-
const theme = useHasciiTheme()
|
|
32
|
-
|
|
33
|
-
const isOpen = accordion?.isOpen(props.value) ?? false
|
|
34
|
-
|
|
35
|
-
const press = usePressable({
|
|
36
|
-
onPress: () => accordion?.toggle(props.value),
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
const headerBg = pickHeaderBg(isOpen, press.isHovered, press.isPressed, theme)
|
|
40
|
-
|
|
41
|
-
const titleFg = isOpen || press.isHovered ? theme.color.foreground : theme.color.mutedForeground
|
|
42
|
-
|
|
43
|
-
const indicator = isOpen ? "▾" : "▸"
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<box flexDirection="column">
|
|
47
|
-
<box
|
|
48
|
-
flexDirection="row"
|
|
49
|
-
alignItems="center"
|
|
50
|
-
gap={1}
|
|
51
|
-
paddingLeft={2}
|
|
52
|
-
paddingRight={2}
|
|
53
|
-
paddingTop={1}
|
|
54
|
-
paddingBottom={1}
|
|
55
|
-
height={HEADER_HEIGHT}
|
|
56
|
-
backgroundColor={headerBg}
|
|
57
|
-
{...press.bind}
|
|
58
|
-
>
|
|
59
|
-
{isOpen ? (
|
|
60
|
-
<box position="absolute" left={0} top={0} bottom={0} flexDirection="column">
|
|
61
|
-
{Array.from({ length: HEADER_HEIGHT }, (_, index) => (
|
|
62
|
-
<text key={index} fg={theme.color.primary}>
|
|
63
|
-
▏
|
|
64
|
-
</text>
|
|
65
|
-
))}
|
|
66
|
-
</box>
|
|
67
|
-
) : null}
|
|
68
|
-
<text fg={titleFg}>{indicator}</text>
|
|
69
|
-
<text fg={titleFg}>{props.title}</text>
|
|
70
|
-
</box>
|
|
71
|
-
{isOpen ? (
|
|
72
|
-
<box
|
|
73
|
-
paddingLeft={4}
|
|
74
|
-
paddingRight={2}
|
|
75
|
-
paddingTop={1}
|
|
76
|
-
paddingBottom={1}
|
|
77
|
-
backgroundColor={theme.color.muted}
|
|
78
|
-
>
|
|
79
|
-
{typeof props.children === "string" ? (
|
|
80
|
-
<text fg={theme.color.mutedForeground}>{props.children}</text>
|
|
81
|
-
) : (
|
|
82
|
-
props.children
|
|
83
|
-
)}
|
|
84
|
-
</box>
|
|
85
|
-
) : null}
|
|
86
|
-
</box>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext, useState } from "react"
|
|
2
|
-
import type { ReactNode } from "react"
|
|
3
|
-
|
|
4
|
-
type Mode = "single" | "multiple"
|
|
5
|
-
|
|
6
|
-
type SingleProps = {
|
|
7
|
-
type?: "single"
|
|
8
|
-
value?: string
|
|
9
|
-
defaultValue?: string
|
|
10
|
-
onChange?: (value: string) => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type MultipleProps = {
|
|
14
|
-
type: "multiple"
|
|
15
|
-
value?: string[]
|
|
16
|
-
defaultValue?: string[]
|
|
17
|
-
onChange?: (value: string[]) => void
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type Props = (SingleProps | MultipleProps) & {
|
|
21
|
-
children?: ReactNode
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
type ContextValue = {
|
|
25
|
-
mode: Mode
|
|
26
|
-
isOpen: (value: string) => boolean
|
|
27
|
-
toggle: (value: string) => void
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const AccordionContext = createContext<ContextValue | null>(null)
|
|
31
|
-
|
|
32
|
-
/** Read the current Accordion context. Returns null when called outside HasciiAccordion. */
|
|
33
|
-
export function useHasciiAccordion(): ContextValue | null {
|
|
34
|
-
return useContext(AccordionContext)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const isSingle = (props: Props): props is SingleProps & { children?: ReactNode } =>
|
|
38
|
-
props.type !== "multiple"
|
|
39
|
-
|
|
40
|
-
/** Vertical stack of collapsible HasciiAccordionItem children. type="single" only opens one section at a time. */
|
|
41
|
-
export function HasciiAccordion(props: Props) {
|
|
42
|
-
const internalSingleState = useState<string>(isSingle(props) ? (props.defaultValue ?? "") : "")
|
|
43
|
-
const internalMultipleState = useState<string[]>(
|
|
44
|
-
!isSingle(props) ? (props.defaultValue ?? []) : [],
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
if (isSingle(props)) {
|
|
48
|
-
const internal = internalSingleState[0]
|
|
49
|
-
const setInternal = internalSingleState[1]
|
|
50
|
-
const current = props.value ?? internal
|
|
51
|
-
|
|
52
|
-
const toggle = (value: string) => {
|
|
53
|
-
const next = current === value ? "" : value
|
|
54
|
-
|
|
55
|
-
if (props.value === undefined) setInternal(next)
|
|
56
|
-
props.onChange?.(next)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const ctx: ContextValue = {
|
|
60
|
-
mode: "single",
|
|
61
|
-
isOpen: (value) => value === current,
|
|
62
|
-
toggle,
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<AccordionContext.Provider value={ctx}>
|
|
67
|
-
<box flexDirection="column">{props.children}</box>
|
|
68
|
-
</AccordionContext.Provider>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const internal = internalMultipleState[0]
|
|
73
|
-
const setInternal = internalMultipleState[1]
|
|
74
|
-
const current = props.value ?? internal
|
|
75
|
-
|
|
76
|
-
const toggle = (value: string) => {
|
|
77
|
-
const next = current.includes(value)
|
|
78
|
-
? current.filter((entry) => entry !== value)
|
|
79
|
-
: [...current, value]
|
|
80
|
-
|
|
81
|
-
if (props.value === undefined) setInternal(next)
|
|
82
|
-
props.onChange?.(next)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const ctx: ContextValue = {
|
|
86
|
-
mode: "multiple",
|
|
87
|
-
isOpen: (value) => current.includes(value),
|
|
88
|
-
toggle,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<AccordionContext.Provider value={ctx}>
|
|
93
|
-
<box flexDirection="column">{props.children}</box>
|
|
94
|
-
</AccordionContext.Provider>
|
|
95
|
-
)
|
|
96
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { HasciiButton } from "@/tui/components/ui/hascii/button"
|
|
2
|
-
import { HasciiDialog } from "@/tui/components/ui/hascii/dialog"
|
|
3
|
-
import { HasciiDialogDescription } from "@/tui/components/ui/hascii/dialog-description"
|
|
4
|
-
import { HasciiDialogFooter } from "@/tui/components/ui/hascii/dialog-footer"
|
|
5
|
-
import { HasciiDialogHeader } from "@/tui/components/ui/hascii/dialog-header"
|
|
6
|
-
import { HasciiDialogTitle } from "@/tui/components/ui/hascii/dialog-title"
|
|
7
|
-
|
|
8
|
-
export type Props = {
|
|
9
|
-
title: string
|
|
10
|
-
description?: string
|
|
11
|
-
okText?: string
|
|
12
|
-
cancelText?: string
|
|
13
|
-
width?: number
|
|
14
|
-
onOk?: () => void
|
|
15
|
-
onCancel?: () => void
|
|
16
|
-
onClose?: () => void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Convenience wrapper around HasciiDialog. Renders a title, optional description, and one or two footer buttons (OK / optional Cancel). */
|
|
20
|
-
export function HasciiAlertDialog(props: Props) {
|
|
21
|
-
const okText = props.okText ?? "OK"
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<HasciiDialog width={props.width} onClose={props.onClose}>
|
|
25
|
-
<HasciiDialogHeader>
|
|
26
|
-
<HasciiDialogTitle>{props.title}</HasciiDialogTitle>
|
|
27
|
-
{props.description !== undefined ? (
|
|
28
|
-
<HasciiDialogDescription>{props.description}</HasciiDialogDescription>
|
|
29
|
-
) : null}
|
|
30
|
-
</HasciiDialogHeader>
|
|
31
|
-
<HasciiDialogFooter>
|
|
32
|
-
{props.cancelText !== undefined ? (
|
|
33
|
-
<HasciiButton variant="secondary" size="default" onPress={props.onCancel}>
|
|
34
|
-
{props.cancelText}
|
|
35
|
-
</HasciiButton>
|
|
36
|
-
) : null}
|
|
37
|
-
<HasciiButton variant="default" size="default" onPress={props.onOk}>
|
|
38
|
-
{okText}
|
|
39
|
-
</HasciiButton>
|
|
40
|
-
</HasciiDialogFooter>
|
|
41
|
-
</HasciiDialog>
|
|
42
|
-
)
|
|
43
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react"
|
|
2
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
3
|
-
|
|
4
|
-
type Variant = "default" | "secondary" | "outline" | "destructive"
|
|
5
|
-
|
|
6
|
-
export type Props = {
|
|
7
|
-
variant?: Variant
|
|
8
|
-
children?: ReactNode
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Compact status indicator with default, secondary, outline, and destructive variants. */
|
|
12
|
-
export function HasciiBadge(props: Props) {
|
|
13
|
-
const variant = props.variant ?? "default"
|
|
14
|
-
const theme = useHasciiTheme()
|
|
15
|
-
|
|
16
|
-
if (variant === "secondary") {
|
|
17
|
-
return (
|
|
18
|
-
<box paddingLeft={1} paddingRight={1} backgroundColor={theme.color.secondary}>
|
|
19
|
-
<text fg={theme.color.secondaryForeground}>{props.children}</text>
|
|
20
|
-
</box>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (variant === "outline") {
|
|
25
|
-
return (
|
|
26
|
-
<box
|
|
27
|
-
paddingLeft={1}
|
|
28
|
-
paddingRight={1}
|
|
29
|
-
border
|
|
30
|
-
borderStyle="rounded"
|
|
31
|
-
borderColor={theme.color.border}
|
|
32
|
-
>
|
|
33
|
-
<text fg={theme.color.foreground}>{props.children}</text>
|
|
34
|
-
</box>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (variant === "destructive") {
|
|
39
|
-
return (
|
|
40
|
-
<box paddingLeft={1} paddingRight={1} backgroundColor={theme.color.destructive}>
|
|
41
|
-
<text fg={theme.color.destructiveForeground}>{props.children}</text>
|
|
42
|
-
</box>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<box paddingLeft={1} paddingRight={1} backgroundColor={theme.color.primary}>
|
|
48
|
-
<text fg={theme.color.primaryForeground}>{props.children}</text>
|
|
49
|
-
</box>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
|
|
2
|
-
|
|
3
|
-
export type BreadcrumbItem = {
|
|
4
|
-
label: string
|
|
5
|
-
value?: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type Props = {
|
|
9
|
-
items: BreadcrumbItem[]
|
|
10
|
-
separator?: string
|
|
11
|
-
onSelect?: (value: string) => void
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Horizontal trail of crumbs joined by a separator. The last item is rendered as the current location. */
|
|
15
|
-
export function HasciiBreadcrumb(props: Props) {
|
|
16
|
-
const separator = props.separator ?? "›"
|
|
17
|
-
const theme = useHasciiTheme()
|
|
18
|
-
|
|
19
|
-
const cells: { id: string; node: import("react").ReactNode }[] = []
|
|
20
|
-
|
|
21
|
-
for (let index = 0; index < props.items.length; index++) {
|
|
22
|
-
const item = props.items[index]
|
|
23
|
-
if (item === undefined) continue
|
|
24
|
-
|
|
25
|
-
const isLast = index === props.items.length - 1
|
|
26
|
-
const fg = isLast ? theme.color.foreground : theme.color.mutedForeground
|
|
27
|
-
const onPress =
|
|
28
|
-
isLast || item.value === undefined ? undefined : () => props.onSelect?.(item.value as string)
|
|
29
|
-
|
|
30
|
-
cells.push({
|
|
31
|
-
id: `crumb-${index}`,
|
|
32
|
-
node: (
|
|
33
|
-
<box paddingLeft={0} paddingRight={0} onMouseUp={onPress}>
|
|
34
|
-
<text fg={fg}>{item.label}</text>
|
|
35
|
-
</box>
|
|
36
|
-
),
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
if (!isLast) {
|
|
40
|
-
cells.push({
|
|
41
|
-
id: `sep-${index}`,
|
|
42
|
-
node: (
|
|
43
|
-
<box paddingLeft={1} paddingRight={1}>
|
|
44
|
-
<text fg={theme.color.mutedForeground}>{separator}</text>
|
|
45
|
-
</box>
|
|
46
|
-
),
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<box flexDirection="row" alignItems="center">
|
|
53
|
-
{cells.map((cell) => (
|
|
54
|
-
<box key={cell.id}>{cell.node}</box>
|
|
55
|
-
))}
|
|
56
|
-
</box>
|
|
57
|
-
)
|
|
58
|
-
}
|