@xp266/dshtui 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/bin/dshtui.js +65 -0
- package/cordis.patch.yml +81 -0
- package/lib/contract/index.d.mts +2 -0
- package/lib/contract/index.mjs +5 -0
- package/lib/dialog.d.mts +156 -0
- package/lib/dialog.mjs +3 -0
- package/lib/index-DqnaSXqP.d.mts +823 -0
- package/lib/index.d.mts +17 -0
- package/lib/index.mjs +11404 -0
- package/lib/prod-react-Dzof4qJx.mjs +4 -0
- package/lib/surface-DdxzIgIY.mjs +3126 -0
- package/lib/vendor.d.mts +3 -0
- package/lib/vendor.mjs +4 -0
- package/package.json +128 -0
- package/src/apply-theme.ts +8 -0
- package/src/boot-log.ts +35 -0
- package/src/chat/blocks.ts +41 -0
- package/src/chat/bridge.ts +1076 -0
- package/src/chat/builtin-tool-views.ts +53 -0
- package/src/chat/chat-face.ts +16 -0
- package/src/chat/chat-nodes.ts +53 -0
- package/src/chat/efforts.ts +4 -0
- package/src/chat/interactions.ts +329 -0
- package/src/chat/models.ts +263 -0
- package/src/chat/partial-json.ts +125 -0
- package/src/chat/presets.ts +26 -0
- package/src/chat/question-view.ts +106 -0
- package/src/chat/retry-status.ts +33 -0
- package/src/chat/session-groups.ts +34 -0
- package/src/chat/session-list.ts +311 -0
- package/src/chat/store.ts +636 -0
- package/src/chat/todo-view.ts +66 -0
- package/src/chat/tool-view.ts +311 -0
- package/src/chat/tool-views.ts +114 -0
- package/src/contract/index.ts +704 -0
- package/src/contract/upstream.ts +165 -0
- package/src/core/caret-nonce.ts +19 -0
- package/src/core/composer-layout.ts +65 -0
- package/src/core/edit.ts +152 -0
- package/src/core/field-view.ts +52 -0
- package/src/core/fields.ts +185 -0
- package/src/core/metrics.ts +26 -0
- package/src/core/paste.ts +149 -0
- package/src/core/segments.ts +112 -0
- package/src/core/text.ts +290 -0
- package/src/dialog.ts +11 -0
- package/src/env.ts +38 -0
- package/src/harness-home.ts +22 -0
- package/src/hot-theme.ts +56 -0
- package/src/index.tsx +185 -0
- package/src/kernel/registry.ts +102 -0
- package/src/kernel/surface.ts +33 -0
- package/src/log.ts +249 -0
- package/src/model/message.ts +89 -0
- package/src/model/selection.ts +63 -0
- package/src/performance-guard.ts +28 -0
- package/src/runtime/prod-react.ts +10 -0
- package/src/terminal/background.ts +66 -0
- package/src/terminal/capabilities.ts +54 -0
- package/src/terminal/clipboard-backends.ts +69 -0
- package/src/terminal/clipboard.ts +161 -0
- package/src/terminal/cursor-shape.ts +13 -0
- package/src/terminal/glyphs.ts +44 -0
- package/src/terminal/mouse.ts +154 -0
- package/src/terminal/probe.ts +61 -0
- package/src/terminal/screen.ts +349 -0
- package/src/theme-settings.ts +31 -0
- package/src/theme.ts +319 -0
- package/src/ui/app.tsx +454 -0
- package/src/ui/chrome/caps.tsx +9 -0
- package/src/ui/chrome/hint-service.ts +87 -0
- package/src/ui/chrome/input-status.ts +27 -0
- package/src/ui/chrome/key-gate.tsx +29 -0
- package/src/ui/chrome/status-bar.tsx +147 -0
- package/src/ui/contributions.ts +47 -0
- package/src/ui/dialog/defaults-dialog.tsx +152 -0
- package/src/ui/dialog/dialog-item.tsx +38 -0
- package/src/ui/dialog/dialog.tsx +386 -0
- package/src/ui/dialog/effort-dialog.tsx +45 -0
- package/src/ui/dialog/geometry.ts +131 -0
- package/src/ui/dialog/items.ts +124 -0
- package/src/ui/dialog/list-dialog.tsx +63 -0
- package/src/ui/dialog/models-dialog.tsx +203 -0
- package/src/ui/dialog/presets-dialog.tsx +46 -0
- package/src/ui/dialog/providers-dialog.tsx +313 -0
- package/src/ui/dialog/sessions-dialog.tsx +163 -0
- package/src/ui/dialog/sizes.ts +7 -0
- package/src/ui/dialog/status-lines.ts +11 -0
- package/src/ui/dialog/todo-dialog.tsx +29 -0
- package/src/ui/dialog/use-dialog-input.ts +186 -0
- package/src/ui/extension-point.ts +188 -0
- package/src/ui/home-logo.ts +132 -0
- package/src/ui/hooks/use-async-action.ts +25 -0
- package/src/ui/hooks/use-async-list.ts +71 -0
- package/src/ui/hooks/use-caret.ts +24 -0
- package/src/ui/hooks/use-chat-events.ts +153 -0
- package/src/ui/hooks/use-mouse-selection.ts +352 -0
- package/src/ui/hooks/use-scroll.ts +49 -0
- package/src/ui/hooks/use-terminal-size.ts +20 -0
- package/src/ui/input/commands.ts +118 -0
- package/src/ui/input/composer-bus.ts +14 -0
- package/src/ui/input/composer-fields.ts +111 -0
- package/src/ui/input/composer-keys.ts +21 -0
- package/src/ui/input/composer-paste.ts +20 -0
- package/src/ui/input/input-bar.tsx +279 -0
- package/src/ui/input/use-composer.ts +495 -0
- package/src/ui/key-arbiter.ts +37 -0
- package/src/ui/keymap.ts +15 -0
- package/src/ui/layout-service.ts +77 -0
- package/src/ui/message/layout.ts +763 -0
- package/src/ui/message/md/block.ts +247 -0
- package/src/ui/message/md/engine.ts +430 -0
- package/src/ui/message/md/extensions.ts +58 -0
- package/src/ui/message/md/highlight.ts +626 -0
- package/src/ui/message/md/index.ts +14 -0
- package/src/ui/message/md/inline.ts +97 -0
- package/src/ui/message/md/palette.ts +87 -0
- package/src/ui/message/message-list.tsx +69 -0
- package/src/ui/message/message-row.tsx +137 -0
- package/src/ui/message/message-views.ts +16 -0
- package/src/ui/message/renderers.ts +39 -0
- package/src/ui/message/tool-diff.ts +188 -0
- package/src/ui/message/warmup.ts +41 -0
- package/src/ui/message/wave.ts +98 -0
- package/src/ui/overlay.ts +22 -0
- package/src/ui/panels/approval-panel.tsx +193 -0
- package/src/ui/panels/exports.ts +3 -0
- package/src/ui/panels/question-model.ts +424 -0
- package/src/ui/panels/question-panel.tsx +154 -0
- package/src/ui/panels/surface.tsx +106 -0
- package/src/ui/panels-builtin.tsx +86 -0
- package/src/ui/pointer/builtins.ts +281 -0
- package/src/ui/pointer/registry.ts +25 -0
- package/src/ui/region.tsx +31 -0
- package/src/ui/selection/service.ts +92 -0
- package/src/ui/selection-registry.ts +88 -0
- package/src/ui/selection.tsx +148 -0
- package/src/ui/spinner-tick.tsx +24 -0
- package/src/ui/use-available-windows.ts +31 -0
- package/src/ui/widgets/actions.tsx +84 -0
- package/src/ui/widgets/button.tsx +57 -0
- package/src/ui/widgets/checkbox.tsx +58 -0
- package/src/ui/widgets/header.tsx +36 -0
- package/src/ui/widgets/index.ts +8 -0
- package/src/ui/widgets/input.tsx +76 -0
- package/src/ui/widgets/registry.ts +32 -0
- package/src/ui/widgets/search.tsx +59 -0
- package/src/ui/widgets/select.tsx +97 -0
- package/src/ui/widgets/static.tsx +37 -0
- package/src/ui/widgets/types.ts +1 -0
- package/src/ui/window-services-bridge.ts +71 -0
- package/src/ui/window-services.ts +20 -0
- package/src/ui/windows/models-window.tsx +15 -0
- package/src/ui/windows/providers-window.tsx +14 -0
- package/src/ui/windows/service-window.tsx +18 -0
- package/src/ui/windows/sessions-window.tsx +25 -0
- package/src/ui/windows-builtin.tsx +94 -0
- package/src/ui/windows.ts +18 -0
- package/src/vendor.ts +12 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { MarkStyle, Segment } from '../../core/segments.ts'
|
|
2
|
+
|
|
3
|
+
const WAVE_STEPS = 6
|
|
4
|
+
const LIGHTEN_SPAN = 0.6
|
|
5
|
+
|
|
6
|
+
interface Hsl {
|
|
7
|
+
h: number
|
|
8
|
+
s: number
|
|
9
|
+
l: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const palettes = new Map<string, string[]>()
|
|
13
|
+
|
|
14
|
+
export function wavePalette(base: string): string[] {
|
|
15
|
+
const cached = palettes.get(base)
|
|
16
|
+
if (cached !== undefined) return cached
|
|
17
|
+
const parsed = hexToHsl(base)
|
|
18
|
+
if (parsed === undefined) return [base]
|
|
19
|
+
const palette: string[] = []
|
|
20
|
+
for (let step = 0; step < WAVE_STEPS; step++) {
|
|
21
|
+
const lightness = parsed.l + (1 - parsed.l) * ((step + 1) / WAVE_STEPS) * LIGHTEN_SPAN
|
|
22
|
+
palette.push(hslToHex({ h: parsed.h, s: parsed.s, l: lightness }))
|
|
23
|
+
}
|
|
24
|
+
palettes.set(base, palette)
|
|
25
|
+
return palette
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Comet wave: the lightest of the six palette colors leads at `head`, trailing
|
|
30
|
+
* progressively darker colors behind it until they blend back into `rest`. The
|
|
31
|
+
* band travels right one step per phase tick, fully exits past the last
|
|
32
|
+
* character, shows one all-rest frame, then re-enters from the left.
|
|
33
|
+
*/
|
|
34
|
+
export function waveSegments(text: string, phase: number, palette: string[], rest: string, base: MarkStyle = {}): Segment[] {
|
|
35
|
+
if (text === '' || palette.length === 0) return []
|
|
36
|
+
const clusters = [...text]
|
|
37
|
+
const cycle = clusters.length + WAVE_STEPS
|
|
38
|
+
const head = ((phase % cycle) + cycle) % cycle
|
|
39
|
+
const segments: Segment[] = []
|
|
40
|
+
let runText = ''
|
|
41
|
+
let runColor: string | undefined
|
|
42
|
+
const flush = (): void => {
|
|
43
|
+
if (runText === '' || runColor === undefined) return
|
|
44
|
+
segments.push({ text: runText, style: { ...base, color: runColor } })
|
|
45
|
+
runText = ''
|
|
46
|
+
}
|
|
47
|
+
for (let index = 0; index < clusters.length; index++) {
|
|
48
|
+
const offset = head - index
|
|
49
|
+
const color = offset >= 0 && offset < WAVE_STEPS ? palette[WAVE_STEPS - 1 - offset]! : rest
|
|
50
|
+
if (runColor !== undefined && color !== runColor) {
|
|
51
|
+
flush()
|
|
52
|
+
}
|
|
53
|
+
runColor = color
|
|
54
|
+
runText += clusters[index]
|
|
55
|
+
}
|
|
56
|
+
flush()
|
|
57
|
+
return segments
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hexToHsl(hex: string): Hsl | undefined {
|
|
61
|
+
const match = /^#([\da-f]{6})$/i.exec(hex)
|
|
62
|
+
if (match === null) return undefined
|
|
63
|
+
const value = Number.parseInt(match[1]!, 16)
|
|
64
|
+
const r = ((value >> 16) & 0xff) / 255
|
|
65
|
+
const g = ((value >> 8) & 0xff) / 255
|
|
66
|
+
const b = (value & 0xff) / 255
|
|
67
|
+
const max = Math.max(r, g, b)
|
|
68
|
+
const min = Math.min(r, g, b)
|
|
69
|
+
const l = (max + min) / 2
|
|
70
|
+
if (max === min) return { h: 0, s: 0, l }
|
|
71
|
+
const d = max - min
|
|
72
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
|
|
73
|
+
let h: number
|
|
74
|
+
if (max === r) h = (g - b) / d + (g < b ? 6 : 0)
|
|
75
|
+
else if (max === g) h = (b - r) / d + 2
|
|
76
|
+
else h = (r - g) / d + 4
|
|
77
|
+
return { h: h * 60, s, l }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function hslToHex({ h, s, l }: Hsl): string {
|
|
81
|
+
const hue = ((h % 360) + 360) % 360
|
|
82
|
+
const c = (1 - Math.abs(2 * l - 1)) * s
|
|
83
|
+
const x = c * (1 - Math.abs(((hue / 60) % 2) - 1))
|
|
84
|
+
const m = l - c / 2
|
|
85
|
+
let r = 0
|
|
86
|
+
let g = 0
|
|
87
|
+
let b = 0
|
|
88
|
+
if (hue < 60) [r, g, b] = [c, x, 0]
|
|
89
|
+
else if (hue < 120) [r, g, b] = [x, c, 0]
|
|
90
|
+
else if (hue < 180) [r, g, b] = [0, c, x]
|
|
91
|
+
else if (hue < 240) [r, g, b] = [0, x, c]
|
|
92
|
+
else if (hue < 300) [r, g, b] = [x, 0, c]
|
|
93
|
+
else [r, g, b] = [c, 0, x]
|
|
94
|
+
const toHex = (channel: number): string => Math.round((channel + m) * 255)
|
|
95
|
+
.toString(16)
|
|
96
|
+
.padStart(2, '0')
|
|
97
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
|
98
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface OverlayStack<T> {
|
|
4
|
+
top: T | undefined
|
|
5
|
+
push(entry: T): void
|
|
6
|
+
pop(): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useOverlayStack<T>(initial: T[] = []): OverlayStack<T> {
|
|
10
|
+
const [entries, setEntries] = useState<T[]>(initial)
|
|
11
|
+
const push = useCallback((entry: T) => {
|
|
12
|
+
setEntries(current => [...current, entry])
|
|
13
|
+
}, [])
|
|
14
|
+
const pop = useCallback(() => {
|
|
15
|
+
setEntries(current => current.slice(0, -1))
|
|
16
|
+
}, [])
|
|
17
|
+
return {
|
|
18
|
+
top: entries[entries.length - 1],
|
|
19
|
+
push,
|
|
20
|
+
pop,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Box, Text, useInput } from 'ink'
|
|
2
|
+
import { panelAnchorRow } from '../layout-service.ts'
|
|
3
|
+
import { isKeyConsumed } from '../key-arbiter.ts'
|
|
4
|
+
import { useEffect, useState } from 'react'
|
|
5
|
+
import type { Ref } from 'react'
|
|
6
|
+
import { useImperativeHandle } from 'react'
|
|
7
|
+
import { wrapLines } from '../../core/text.ts'
|
|
8
|
+
import { mergeRuns } from '../../core/segments.ts'
|
|
9
|
+
import type { Segment } from '../../core/segments.ts'
|
|
10
|
+
import { COLORS } from '../../theme.ts'
|
|
11
|
+
import { glyphs } from '../../terminal/glyphs.ts'
|
|
12
|
+
import { CHROME_TEXT_X } from '../../core/metrics.ts'
|
|
13
|
+
import { writeCursorShape } from '../../terminal/cursor-shape.ts'
|
|
14
|
+
import { SelectableText } from '../selection.tsx'
|
|
15
|
+
import { PanelSurface, panelLegend } from './surface.tsx'
|
|
16
|
+
import type { PanelPointerHandle, PanelRow } from './surface.tsx'
|
|
17
|
+
|
|
18
|
+
export type { PanelPointerHandle } from './surface.tsx'
|
|
19
|
+
|
|
20
|
+
export const APPROVAL_SECTION_ROWS = 3
|
|
21
|
+
const BUTTON_GAP_TEXT = ' '.repeat(8)
|
|
22
|
+
const BUTTON_ALLOW_TEXT = 'Allow once'
|
|
23
|
+
const ALLOW_BLOCK = ` ${BUTTON_ALLOW_TEXT} `
|
|
24
|
+
const REJECT_BLOCK = ' Reject '
|
|
25
|
+
export const APPROVAL_BUTTON_COLS = {
|
|
26
|
+
allow: CHROME_TEXT_X,
|
|
27
|
+
reject: CHROME_TEXT_X + ALLOW_BLOCK.length + BUTTON_GAP_TEXT.length,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ApprovalPanelProps {
|
|
31
|
+
handleRef?: Ref<PanelPointerHandle>
|
|
32
|
+
reason?: string
|
|
33
|
+
command?: string
|
|
34
|
+
background: string
|
|
35
|
+
active: boolean
|
|
36
|
+
columns: number
|
|
37
|
+
rows: number
|
|
38
|
+
innerWidth: number
|
|
39
|
+
blockWidth: number
|
|
40
|
+
onDecide(outcome: 'allowed-once' | 'rejected'): void
|
|
41
|
+
onResize(height: number): void
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function section(text: string | undefined, innerWidth: number): string[] {
|
|
45
|
+
if (text === undefined || text.trim() === '') return []
|
|
46
|
+
return wrapLines(text, innerWidth)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function reasonSection(raw: string | undefined, innerWidth: number): Segment[][] {
|
|
50
|
+
if (raw === undefined || raw.trim() === '') return []
|
|
51
|
+
const colon = raw.indexOf(':')
|
|
52
|
+
const rows: Segment[][] = []
|
|
53
|
+
if (colon === -1) {
|
|
54
|
+
for (const line of wrapLines(raw, Math.max(4, innerWidth))) {
|
|
55
|
+
rows.push([{ text: line, style: { color: COLORS.errorText } }])
|
|
56
|
+
}
|
|
57
|
+
return rows
|
|
58
|
+
}
|
|
59
|
+
rows.push([{ text: raw.slice(0, colon + 1), style: { color: COLORS.errorText } }])
|
|
60
|
+
const description = raw.slice(colon + 1).trim()
|
|
61
|
+
if (description !== '') {
|
|
62
|
+
rows.push([])
|
|
63
|
+
for (const line of wrapLines(description, Math.max(4, innerWidth))) {
|
|
64
|
+
rows.push([{ text: line, style: {} }])
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return rows
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function commandSection(lines: string[]): Segment[][] {
|
|
71
|
+
return lines.map(line => [{ text: line, style: { color: COLORS.warning } }])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function clamp(value: number, max: number): number {
|
|
75
|
+
return Math.max(0, Math.min(max, value))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function ApprovalPanel({ handleRef, reason, command, background, active, columns, rows, innerWidth, blockWidth, onDecide, onResize }: ApprovalPanelProps) {
|
|
79
|
+
const [focusAllow, setFocusAllow] = useState(true)
|
|
80
|
+
const [scroll, setScroll] = useState({ reason: 0, command: 0 })
|
|
81
|
+
const allReason = reasonSection(reason, innerWidth)
|
|
82
|
+
const allCommand = section(command, innerWidth)
|
|
83
|
+
const reasonOverflow = Math.max(0, allReason.length - APPROVAL_SECTION_ROWS)
|
|
84
|
+
const commandOverflow = Math.max(0, allCommand.length - APPROVAL_SECTION_ROWS)
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setScroll(current => ({
|
|
87
|
+
reason: Math.min(current.reason, reasonOverflow),
|
|
88
|
+
command: Math.min(current.command, commandOverflow),
|
|
89
|
+
}))
|
|
90
|
+
}, [reasonOverflow, commandOverflow])
|
|
91
|
+
const visibleReason = allReason.slice(scroll.reason, scroll.reason + APPROVAL_SECTION_ROWS)
|
|
92
|
+
const visibleCommand = allCommand.slice(scroll.command, scroll.command + APPROVAL_SECTION_ROWS)
|
|
93
|
+
const showCommand = visibleCommand.some(line => line.trim() !== '')
|
|
94
|
+
const body: PanelRow[] = []
|
|
95
|
+
for (const row of visibleReason) body.push({ segments: row })
|
|
96
|
+
if (visibleReason.length > 0) body.push({ segments: [] })
|
|
97
|
+
if (showCommand) {
|
|
98
|
+
for (const row of commandSection(visibleCommand)) body.push({ segments: row })
|
|
99
|
+
body.push({ segments: [] })
|
|
100
|
+
}
|
|
101
|
+
const buttonRowIndex = body.length
|
|
102
|
+
const legend = panelLegend([
|
|
103
|
+
{ key: glyphs.pageFlip, description: 'select' },
|
|
104
|
+
{ key: 'enter', description: 'submit' },
|
|
105
|
+
])
|
|
106
|
+
body.push({ segments: mergeRuns([{ text: BUTTON_ALLOW_TEXT + BUTTON_GAP_TEXT + 'Reject', style: {} }]) })
|
|
107
|
+
body.push({ segments: [] })
|
|
108
|
+
body.push({ segments: legend })
|
|
109
|
+
const bodyCount = body.length
|
|
110
|
+
const totalHeight = bodyCount + 2
|
|
111
|
+
const bodyStart = panelAnchorRow(rows, bodyCount)
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
writeCursorShape('hide')
|
|
114
|
+
return () => {
|
|
115
|
+
writeCursorShape('show')
|
|
116
|
+
writeCursorShape('reset')
|
|
117
|
+
}
|
|
118
|
+
}, [])
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
onResize(totalHeight)
|
|
121
|
+
}, [totalHeight])
|
|
122
|
+
useImperativeHandle(handleRef, () => ({
|
|
123
|
+
clickAt(y, x) {
|
|
124
|
+
if (!active) return
|
|
125
|
+
if (y !== bodyStart + buttonRowIndex) return
|
|
126
|
+
if (x >= APPROVAL_BUTTON_COLS.allow && x < APPROVAL_BUTTON_COLS.allow + ALLOW_BLOCK.length) {
|
|
127
|
+
onDecide('allowed-once')
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
if (x >= APPROVAL_BUTTON_COLS.reject && x < APPROVAL_BUTTON_COLS.reject + REJECT_BLOCK.length) {
|
|
131
|
+
onDecide('rejected')
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
wheel(dir) {
|
|
135
|
+
if (!active) return false
|
|
136
|
+
const delta = dir === -1 ? -1 : 1
|
|
137
|
+
if (reasonOverflow > 0) {
|
|
138
|
+
setScroll(current => ({ ...current, reason: clamp(current.reason + delta, reasonOverflow) }))
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
if (commandOverflow > 0) {
|
|
142
|
+
setScroll(current => ({ ...current, command: clamp(current.command + delta, commandOverflow) }))
|
|
143
|
+
return true
|
|
144
|
+
}
|
|
145
|
+
return false
|
|
146
|
+
},
|
|
147
|
+
}))
|
|
148
|
+
useInput((input, key) => {
|
|
149
|
+
if (!active || isKeyConsumed()) return
|
|
150
|
+
if (key.leftArrow || key.rightArrow || key.tab) {
|
|
151
|
+
setFocusAllow(current => !current)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
if (key.upArrow || key.downArrow) {
|
|
155
|
+
const delta = key.upArrow ? -1 : 1
|
|
156
|
+
if (reasonOverflow > 0) {
|
|
157
|
+
setScroll(current => ({ ...current, reason: clamp(current.reason + delta, reasonOverflow) }))
|
|
158
|
+
} else if (commandOverflow > 0) {
|
|
159
|
+
setScroll(current => ({ ...current, command: clamp(current.command + delta, commandOverflow) }))
|
|
160
|
+
}
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
if (key.return) {
|
|
164
|
+
onDecide(focusAllow ? 'allowed-once' : 'rejected')
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
if (key.escape) {
|
|
168
|
+
onDecide('rejected')
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
const buttonY = bodyStart + buttonRowIndex
|
|
172
|
+
return (
|
|
173
|
+
<PanelSurface
|
|
174
|
+
columns={columns}
|
|
175
|
+
rows={rows}
|
|
176
|
+
body={body.map((row, index) => index === buttonRowIndex
|
|
177
|
+
? {
|
|
178
|
+
segments: row.segments,
|
|
179
|
+
content: (
|
|
180
|
+
<Box flexDirection="row">
|
|
181
|
+
<SelectableText y={buttonY} col={APPROVAL_BUTTON_COLS.allow} text={ALLOW_BLOCK} inverse={focusAllow} />
|
|
182
|
+
<Text>{BUTTON_GAP_TEXT}</Text>
|
|
183
|
+
<SelectableText y={buttonY} col={APPROVAL_BUTTON_COLS.reject} text={REJECT_BLOCK} inverse={!focusAllow} />
|
|
184
|
+
</Box>
|
|
185
|
+
),
|
|
186
|
+
}
|
|
187
|
+
: row)}
|
|
188
|
+
bodyStart={bodyStart}
|
|
189
|
+
background={background}
|
|
190
|
+
blockWidth={blockWidth}
|
|
191
|
+
/>
|
|
192
|
+
)
|
|
193
|
+
}
|