compressed-context 0.1.0 → 0.1.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/package.json +64 -64
- package/src/compression/active-summary-cache.ts +50 -50
- package/src/compression/adopt.ts +57 -57
- package/src/compression/auto.ts +372 -372
- package/src/compression/babel.ts +85 -85
- package/src/compression/block.ts +25 -25
- package/src/compression/cache-history.ts +75 -75
- package/src/compression/compaction.ts +189 -189
- package/src/compression/decompress-tool.ts +74 -74
- package/src/compression/decompress.ts +292 -292
- package/src/compression/deleted-sessions.ts +24 -24
- package/src/compression/distill.ts +220 -220
- package/src/compression/events.ts +275 -275
- package/src/compression/markdown.ts +53 -53
- package/src/compression/normalize.ts +114 -114
- package/src/compression/render.ts +70 -70
- package/src/compression/schema.ts +84 -84
- package/src/compression/select.ts +123 -123
- package/src/compression/session-message.ts +112 -112
- package/src/compression/settings-rpc.ts +111 -111
- package/src/compression/settings.ts +175 -175
- package/src/compression/state.ts +248 -248
- package/src/compression/summarize.ts +595 -595
- package/src/compression/summary.ts +73 -73
- package/src/compression/tool.ts +142 -142
- package/src/compression/transcript-store.ts +258 -258
- package/src/compression/transcript.ts +63 -63
- package/src/compression/validate.ts +81 -81
- package/src/i18n/en.ts +93 -93
- package/src/i18n/index.ts +28 -28
- package/src/i18n/types.ts +89 -89
- package/src/i18n/zh.ts +93 -93
- package/src/index.ts +108 -108
- package/src/shared/blocks.ts +17 -17
- package/src/shared/cache.ts +199 -199
- package/src/shared/format.ts +23 -23
- package/src/shared/media.ts +184 -184
- package/src/shared/parse.ts +25 -25
- package/src/shared/parts.ts +72 -72
- package/src/shared/rpc.ts +145 -145
- package/src/shared/scan.ts +32 -32
- package/src/shared/settings.ts +471 -471
- package/src/shared/stats.ts +142 -142
- package/src/shared/tokens.ts +259 -259
- package/src/tui/chart/cache-chart.ts +237 -237
- package/src/tui/chart/color.ts +58 -58
- package/src/tui/index.tsx +111 -111
- package/src/tui/panel/cache.tsx +203 -203
- package/src/tui/panel/data.ts +105 -105
- package/src/tui/panel/index.tsx +176 -176
- package/src/tui/panel/summaries.tsx +192 -192
- package/src/tui/settings/dialogs.tsx +215 -215
- package/src/tui/settings/options.ts +238 -238
- package/src/tui/settings.tsx +418 -418
- package/src/tui/types.ts +4 -4
|
@@ -1,215 +1,215 @@
|
|
|
1
|
-
import type { Context } from "@opencode/plugin/tui/context"
|
|
2
|
-
import { createMemo, createSignal, Show, type ParentProps } from "solid-js"
|
|
3
|
-
|
|
4
|
-
import { formatTokens } from "../../shared/format.ts"
|
|
5
|
-
import type { Messages } from "../../i18n/index.ts"
|
|
6
|
-
import {
|
|
7
|
-
MAX_TRIGGER_PERCENT,
|
|
8
|
-
MIN_TRIGGER_PERCENT,
|
|
9
|
-
parseSummaryPercent,
|
|
10
|
-
parseTokenCount,
|
|
11
|
-
} from "../../shared/settings.ts"
|
|
12
|
-
import { percentLabel } from "./options.ts"
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 输入:TUI 上下文 + 标题 + 内容。
|
|
16
|
-
* 输出:与原生对话框一致的外框(左右 3 格内边距、标题栏、底部操作行)。
|
|
17
|
-
* 预期:标题在左、esc 在右;内容区与底部操作行各留一空行。
|
|
18
|
-
*/
|
|
19
|
-
function DialogFrame(
|
|
20
|
-
props: ParentProps<{ readonly context: Context; readonly title: string; readonly submit: string }>,
|
|
21
|
-
) {
|
|
22
|
-
const theme = props.context.theme
|
|
23
|
-
return (
|
|
24
|
-
<box paddingLeft={3} paddingRight={3} paddingBottom={1} gap={1}>
|
|
25
|
-
<box flexDirection="row" justifyContent="space-between" gap={1}>
|
|
26
|
-
<text fg={theme.text.base}>
|
|
27
|
-
<b>{props.title}</b>
|
|
28
|
-
</text>
|
|
29
|
-
<text
|
|
30
|
-
fg={theme.text.muted}
|
|
31
|
-
onMouseUp={() => {
|
|
32
|
-
props.context.ui.dialog.clear()
|
|
33
|
-
}}
|
|
34
|
-
>
|
|
35
|
-
esc
|
|
36
|
-
</text>
|
|
37
|
-
</box>
|
|
38
|
-
<box gap={1}>{props.children}</box>
|
|
39
|
-
<text fg={theme.text.muted}>{props.submit} enter</text>
|
|
40
|
-
</box>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 输入:TUI 上下文 + 文案 + 解析/预览/提示 + 确认回调。
|
|
46
|
-
* 输出:单行输入对话框;Token/Percent/Threshold 共用。
|
|
47
|
-
* 预期:只接受 parse 返回数字的输入;非法时显示 invalid,不写入。
|
|
48
|
-
*/
|
|
49
|
-
function DialogInput(props: {
|
|
50
|
-
readonly context: Context
|
|
51
|
-
readonly copy: Messages
|
|
52
|
-
readonly title: string
|
|
53
|
-
readonly hint: string
|
|
54
|
-
readonly invalid: string
|
|
55
|
-
readonly placeholder: string
|
|
56
|
-
readonly initial: string
|
|
57
|
-
readonly parse: (text: string) => number | undefined
|
|
58
|
-
readonly preview: (value: number) => string
|
|
59
|
-
readonly extra?: string
|
|
60
|
-
readonly onConfirm: (value: number) => void
|
|
61
|
-
}) {
|
|
62
|
-
const theme = props.context.theme
|
|
63
|
-
const [text, setText] = createSignal(props.initial)
|
|
64
|
-
const parsed = createMemo(() => props.parse(text()))
|
|
65
|
-
let area: { plainText: string } | undefined
|
|
66
|
-
|
|
67
|
-
const confirm = (): void => {
|
|
68
|
-
const value = parsed()
|
|
69
|
-
if (value === undefined) return
|
|
70
|
-
props.onConfirm(value)
|
|
71
|
-
props.context.ui.dialog.clear()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 原生 DialogPrompt 用模态层接管回车;这里同样注册,避免 textarea 插入换行。
|
|
75
|
-
props.context.keymap.layer(() => ({
|
|
76
|
-
mode: "modal",
|
|
77
|
-
priority: 1,
|
|
78
|
-
commands: [{ bind: "enter", title: "Submit", group: "Dialog", run: confirm }],
|
|
79
|
-
}))
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<DialogFrame context={props.context} title={props.title} submit={props.copy.settings.submit}>
|
|
83
|
-
<textarea
|
|
84
|
-
height={1}
|
|
85
|
-
wrapMode="none"
|
|
86
|
-
initialValue={props.initial}
|
|
87
|
-
placeholder={props.placeholder}
|
|
88
|
-
placeholderColor={theme.text.muted}
|
|
89
|
-
textColor={theme.text.formfield.base}
|
|
90
|
-
focusedTextColor={theme.text.formfield.base}
|
|
91
|
-
cursorColor={theme.text.base}
|
|
92
|
-
onContentChange={() => setText(area?.plainText ?? "")}
|
|
93
|
-
ref={(input) => {
|
|
94
|
-
area = input
|
|
95
|
-
setTimeout(() => {
|
|
96
|
-
if (!input.isDestroyed) input.focus()
|
|
97
|
-
}, 1)
|
|
98
|
-
}}
|
|
99
|
-
/>
|
|
100
|
-
<Show
|
|
101
|
-
when={parsed() !== undefined}
|
|
102
|
-
fallback={<text fg={theme.text.feedback.error.base}>{props.invalid}</text>}
|
|
103
|
-
>
|
|
104
|
-
<text fg={theme.text.muted}>{props.preview(parsed() ?? 0)}</text>
|
|
105
|
-
</Show>
|
|
106
|
-
<Show when={props.extra !== undefined}>
|
|
107
|
-
<text fg={theme.text.muted}>{props.extra}</text>
|
|
108
|
-
</Show>
|
|
109
|
-
<text fg={theme.text.muted}>{props.hint}</text>
|
|
110
|
-
</DialogFrame>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** 输入:TUI 上下文 + 文案 + 标题/提示 + 初始值 + 合法范围 + 确认回调。 */
|
|
115
|
-
export function TokenDialog(props: {
|
|
116
|
-
readonly context: Context
|
|
117
|
-
readonly copy: Messages
|
|
118
|
-
readonly title: string
|
|
119
|
-
readonly hint: string
|
|
120
|
-
readonly invalid: string
|
|
121
|
-
readonly placeholder: string
|
|
122
|
-
readonly initial: string
|
|
123
|
-
readonly min: number
|
|
124
|
-
readonly max: number
|
|
125
|
-
readonly allowZero?: boolean
|
|
126
|
-
readonly onConfirm: (value: number) => void
|
|
127
|
-
}) {
|
|
128
|
-
return (
|
|
129
|
-
<DialogInput
|
|
130
|
-
context={props.context}
|
|
131
|
-
copy={props.copy}
|
|
132
|
-
title={props.title}
|
|
133
|
-
hint={props.hint}
|
|
134
|
-
invalid={props.invalid}
|
|
135
|
-
placeholder={props.placeholder}
|
|
136
|
-
initial={props.initial}
|
|
137
|
-
parse={(text) => {
|
|
138
|
-
if (props.allowZero === true && text.trim() === "0") return 0
|
|
139
|
-
return parseTokenCount(text, props.min, props.max)
|
|
140
|
-
}}
|
|
141
|
-
preview={(value) =>
|
|
142
|
-
value === 0 ? props.copy.settings.off : props.copy.settings.limitPreview(formatTokens(value))
|
|
143
|
-
}
|
|
144
|
-
onConfirm={props.onConfirm}
|
|
145
|
-
/>
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** 输入:TUI 上下文 + 文案 + 初始百分比 + 窗口 + 允许范围 + 确认回调。 */
|
|
150
|
-
export function PercentDialog(props: {
|
|
151
|
-
readonly context: Context
|
|
152
|
-
readonly copy: Messages
|
|
153
|
-
readonly title: string
|
|
154
|
-
readonly hint: string
|
|
155
|
-
readonly invalid: string
|
|
156
|
-
readonly initial: number
|
|
157
|
-
readonly windowTokens: number | undefined
|
|
158
|
-
readonly minPercent: number
|
|
159
|
-
readonly maxPercent: number
|
|
160
|
-
readonly onConfirm: (value: number) => void
|
|
161
|
-
}) {
|
|
162
|
-
return (
|
|
163
|
-
<DialogInput
|
|
164
|
-
context={props.context}
|
|
165
|
-
copy={props.copy}
|
|
166
|
-
title={props.title}
|
|
167
|
-
hint={props.hint}
|
|
168
|
-
invalid={props.invalid}
|
|
169
|
-
placeholder="10%"
|
|
170
|
-
initial={`${props.initial.toString()}%`}
|
|
171
|
-
parse={(text) => {
|
|
172
|
-
const value = parseSummaryPercent(text, props.windowTokens)
|
|
173
|
-
if (value === undefined || value < props.minPercent || value > props.maxPercent) {
|
|
174
|
-
return undefined
|
|
175
|
-
}
|
|
176
|
-
return value
|
|
177
|
-
}}
|
|
178
|
-
preview={(value) => percentLabel(value, props.windowTokens)}
|
|
179
|
-
extra={
|
|
180
|
-
props.windowTokens === undefined
|
|
181
|
-
? props.copy.settings.windowUnknown
|
|
182
|
-
: props.copy.settings.windowKnown(formatTokens(props.windowTokens))
|
|
183
|
-
}
|
|
184
|
-
onConfirm={props.onConfirm}
|
|
185
|
-
/>
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** 输入:TUI 上下文 + 文案 + 初始值 + 确认回调。 */
|
|
190
|
-
export function ThresholdDialog(props: {
|
|
191
|
-
readonly context: Context
|
|
192
|
-
readonly copy: Messages
|
|
193
|
-
readonly initial: string
|
|
194
|
-
readonly onConfirm: (value: number) => void
|
|
195
|
-
}) {
|
|
196
|
-
return (
|
|
197
|
-
<DialogInput
|
|
198
|
-
context={props.context}
|
|
199
|
-
copy={props.copy}
|
|
200
|
-
title={props.copy.settings.threshold}
|
|
201
|
-
hint={props.copy.settings.thresholdHint}
|
|
202
|
-
invalid={props.copy.settings.thresholdInvalid}
|
|
203
|
-
placeholder="80"
|
|
204
|
-
initial={props.initial}
|
|
205
|
-
parse={(text) => {
|
|
206
|
-
if (!/^\d{1,2}$/.test(text)) return undefined
|
|
207
|
-
const value = Number(text)
|
|
208
|
-
if (value < MIN_TRIGGER_PERCENT || value > MAX_TRIGGER_PERCENT) return undefined
|
|
209
|
-
return value
|
|
210
|
-
}}
|
|
211
|
-
preview={(value) => props.copy.settings.thresholdPreview(value.toString())}
|
|
212
|
-
onConfirm={props.onConfirm}
|
|
213
|
-
/>
|
|
214
|
-
)
|
|
215
|
-
}
|
|
1
|
+
import type { Context } from "@opencode/plugin/tui/context"
|
|
2
|
+
import { createMemo, createSignal, Show, type ParentProps } from "solid-js"
|
|
3
|
+
|
|
4
|
+
import { formatTokens } from "../../shared/format.ts"
|
|
5
|
+
import type { Messages } from "../../i18n/index.ts"
|
|
6
|
+
import {
|
|
7
|
+
MAX_TRIGGER_PERCENT,
|
|
8
|
+
MIN_TRIGGER_PERCENT,
|
|
9
|
+
parseSummaryPercent,
|
|
10
|
+
parseTokenCount,
|
|
11
|
+
} from "../../shared/settings.ts"
|
|
12
|
+
import { percentLabel } from "./options.ts"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 输入:TUI 上下文 + 标题 + 内容。
|
|
16
|
+
* 输出:与原生对话框一致的外框(左右 3 格内边距、标题栏、底部操作行)。
|
|
17
|
+
* 预期:标题在左、esc 在右;内容区与底部操作行各留一空行。
|
|
18
|
+
*/
|
|
19
|
+
function DialogFrame(
|
|
20
|
+
props: ParentProps<{ readonly context: Context; readonly title: string; readonly submit: string }>,
|
|
21
|
+
) {
|
|
22
|
+
const theme = props.context.theme
|
|
23
|
+
return (
|
|
24
|
+
<box paddingLeft={3} paddingRight={3} paddingBottom={1} gap={1}>
|
|
25
|
+
<box flexDirection="row" justifyContent="space-between" gap={1}>
|
|
26
|
+
<text fg={theme.text.base}>
|
|
27
|
+
<b>{props.title}</b>
|
|
28
|
+
</text>
|
|
29
|
+
<text
|
|
30
|
+
fg={theme.text.muted}
|
|
31
|
+
onMouseUp={() => {
|
|
32
|
+
props.context.ui.dialog.clear()
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
esc
|
|
36
|
+
</text>
|
|
37
|
+
</box>
|
|
38
|
+
<box gap={1}>{props.children}</box>
|
|
39
|
+
<text fg={theme.text.muted}>{props.submit} enter</text>
|
|
40
|
+
</box>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 输入:TUI 上下文 + 文案 + 解析/预览/提示 + 确认回调。
|
|
46
|
+
* 输出:单行输入对话框;Token/Percent/Threshold 共用。
|
|
47
|
+
* 预期:只接受 parse 返回数字的输入;非法时显示 invalid,不写入。
|
|
48
|
+
*/
|
|
49
|
+
function DialogInput(props: {
|
|
50
|
+
readonly context: Context
|
|
51
|
+
readonly copy: Messages
|
|
52
|
+
readonly title: string
|
|
53
|
+
readonly hint: string
|
|
54
|
+
readonly invalid: string
|
|
55
|
+
readonly placeholder: string
|
|
56
|
+
readonly initial: string
|
|
57
|
+
readonly parse: (text: string) => number | undefined
|
|
58
|
+
readonly preview: (value: number) => string
|
|
59
|
+
readonly extra?: string
|
|
60
|
+
readonly onConfirm: (value: number) => void
|
|
61
|
+
}) {
|
|
62
|
+
const theme = props.context.theme
|
|
63
|
+
const [text, setText] = createSignal(props.initial)
|
|
64
|
+
const parsed = createMemo(() => props.parse(text()))
|
|
65
|
+
let area: { plainText: string } | undefined
|
|
66
|
+
|
|
67
|
+
const confirm = (): void => {
|
|
68
|
+
const value = parsed()
|
|
69
|
+
if (value === undefined) return
|
|
70
|
+
props.onConfirm(value)
|
|
71
|
+
props.context.ui.dialog.clear()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 原生 DialogPrompt 用模态层接管回车;这里同样注册,避免 textarea 插入换行。
|
|
75
|
+
props.context.keymap.layer(() => ({
|
|
76
|
+
mode: "modal",
|
|
77
|
+
priority: 1,
|
|
78
|
+
commands: [{ bind: "enter", title: "Submit", group: "Dialog", run: confirm }],
|
|
79
|
+
}))
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<DialogFrame context={props.context} title={props.title} submit={props.copy.settings.submit}>
|
|
83
|
+
<textarea
|
|
84
|
+
height={1}
|
|
85
|
+
wrapMode="none"
|
|
86
|
+
initialValue={props.initial}
|
|
87
|
+
placeholder={props.placeholder}
|
|
88
|
+
placeholderColor={theme.text.muted}
|
|
89
|
+
textColor={theme.text.formfield.base}
|
|
90
|
+
focusedTextColor={theme.text.formfield.base}
|
|
91
|
+
cursorColor={theme.text.base}
|
|
92
|
+
onContentChange={() => setText(area?.plainText ?? "")}
|
|
93
|
+
ref={(input) => {
|
|
94
|
+
area = input
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
if (!input.isDestroyed) input.focus()
|
|
97
|
+
}, 1)
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
<Show
|
|
101
|
+
when={parsed() !== undefined}
|
|
102
|
+
fallback={<text fg={theme.text.feedback.error.base}>{props.invalid}</text>}
|
|
103
|
+
>
|
|
104
|
+
<text fg={theme.text.muted}>{props.preview(parsed() ?? 0)}</text>
|
|
105
|
+
</Show>
|
|
106
|
+
<Show when={props.extra !== undefined}>
|
|
107
|
+
<text fg={theme.text.muted}>{props.extra}</text>
|
|
108
|
+
</Show>
|
|
109
|
+
<text fg={theme.text.muted}>{props.hint}</text>
|
|
110
|
+
</DialogFrame>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** 输入:TUI 上下文 + 文案 + 标题/提示 + 初始值 + 合法范围 + 确认回调。 */
|
|
115
|
+
export function TokenDialog(props: {
|
|
116
|
+
readonly context: Context
|
|
117
|
+
readonly copy: Messages
|
|
118
|
+
readonly title: string
|
|
119
|
+
readonly hint: string
|
|
120
|
+
readonly invalid: string
|
|
121
|
+
readonly placeholder: string
|
|
122
|
+
readonly initial: string
|
|
123
|
+
readonly min: number
|
|
124
|
+
readonly max: number
|
|
125
|
+
readonly allowZero?: boolean
|
|
126
|
+
readonly onConfirm: (value: number) => void
|
|
127
|
+
}) {
|
|
128
|
+
return (
|
|
129
|
+
<DialogInput
|
|
130
|
+
context={props.context}
|
|
131
|
+
copy={props.copy}
|
|
132
|
+
title={props.title}
|
|
133
|
+
hint={props.hint}
|
|
134
|
+
invalid={props.invalid}
|
|
135
|
+
placeholder={props.placeholder}
|
|
136
|
+
initial={props.initial}
|
|
137
|
+
parse={(text) => {
|
|
138
|
+
if (props.allowZero === true && text.trim() === "0") return 0
|
|
139
|
+
return parseTokenCount(text, props.min, props.max)
|
|
140
|
+
}}
|
|
141
|
+
preview={(value) =>
|
|
142
|
+
value === 0 ? props.copy.settings.off : props.copy.settings.limitPreview(formatTokens(value))
|
|
143
|
+
}
|
|
144
|
+
onConfirm={props.onConfirm}
|
|
145
|
+
/>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** 输入:TUI 上下文 + 文案 + 初始百分比 + 窗口 + 允许范围 + 确认回调。 */
|
|
150
|
+
export function PercentDialog(props: {
|
|
151
|
+
readonly context: Context
|
|
152
|
+
readonly copy: Messages
|
|
153
|
+
readonly title: string
|
|
154
|
+
readonly hint: string
|
|
155
|
+
readonly invalid: string
|
|
156
|
+
readonly initial: number
|
|
157
|
+
readonly windowTokens: number | undefined
|
|
158
|
+
readonly minPercent: number
|
|
159
|
+
readonly maxPercent: number
|
|
160
|
+
readonly onConfirm: (value: number) => void
|
|
161
|
+
}) {
|
|
162
|
+
return (
|
|
163
|
+
<DialogInput
|
|
164
|
+
context={props.context}
|
|
165
|
+
copy={props.copy}
|
|
166
|
+
title={props.title}
|
|
167
|
+
hint={props.hint}
|
|
168
|
+
invalid={props.invalid}
|
|
169
|
+
placeholder="10%"
|
|
170
|
+
initial={`${props.initial.toString()}%`}
|
|
171
|
+
parse={(text) => {
|
|
172
|
+
const value = parseSummaryPercent(text, props.windowTokens)
|
|
173
|
+
if (value === undefined || value < props.minPercent || value > props.maxPercent) {
|
|
174
|
+
return undefined
|
|
175
|
+
}
|
|
176
|
+
return value
|
|
177
|
+
}}
|
|
178
|
+
preview={(value) => percentLabel(value, props.windowTokens)}
|
|
179
|
+
extra={
|
|
180
|
+
props.windowTokens === undefined
|
|
181
|
+
? props.copy.settings.windowUnknown
|
|
182
|
+
: props.copy.settings.windowKnown(formatTokens(props.windowTokens))
|
|
183
|
+
}
|
|
184
|
+
onConfirm={props.onConfirm}
|
|
185
|
+
/>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** 输入:TUI 上下文 + 文案 + 初始值 + 确认回调。 */
|
|
190
|
+
export function ThresholdDialog(props: {
|
|
191
|
+
readonly context: Context
|
|
192
|
+
readonly copy: Messages
|
|
193
|
+
readonly initial: string
|
|
194
|
+
readonly onConfirm: (value: number) => void
|
|
195
|
+
}) {
|
|
196
|
+
return (
|
|
197
|
+
<DialogInput
|
|
198
|
+
context={props.context}
|
|
199
|
+
copy={props.copy}
|
|
200
|
+
title={props.copy.settings.threshold}
|
|
201
|
+
hint={props.copy.settings.thresholdHint}
|
|
202
|
+
invalid={props.copy.settings.thresholdInvalid}
|
|
203
|
+
placeholder="80"
|
|
204
|
+
initial={props.initial}
|
|
205
|
+
parse={(text) => {
|
|
206
|
+
if (!/^\d{1,2}$/.test(text)) return undefined
|
|
207
|
+
const value = Number(text)
|
|
208
|
+
if (value < MIN_TRIGGER_PERCENT || value > MAX_TRIGGER_PERCENT) return undefined
|
|
209
|
+
return value
|
|
210
|
+
}}
|
|
211
|
+
preview={(value) => props.copy.settings.thresholdPreview(value.toString())}
|
|
212
|
+
onConfirm={props.onConfirm}
|
|
213
|
+
/>
|
|
214
|
+
)
|
|
215
|
+
}
|