saeeol 1.2.1 → 1.2.2
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 +11 -11
- package/src/cli/cmd/tui/component/dialog/dialog-agent.tsx +32 -0
- package/src/cli/cmd/tui/component/dialog/dialog-command.tsx +190 -0
- package/src/cli/cmd/tui/component/dialog/dialog-console-org.tsx +103 -0
- package/src/cli/cmd/tui/component/dialog/dialog-go-upsell.tsx +159 -0
- package/src/cli/cmd/tui/component/dialog/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog/dialog-model.tsx +238 -0
- package/src/cli/cmd/tui/component/dialog/dialog-provider.tsx +343 -0
- package/src/cli/cmd/tui/component/dialog/dialog-session-delete-failed.tsx +103 -0
- package/src/cli/cmd/tui/component/dialog/dialog-session-list.tsx +301 -0
- package/src/cli/cmd/tui/component/dialog/dialog-session-rename.tsx +35 -0
- package/src/cli/cmd/tui/component/dialog/dialog-skill.tsx +37 -0
- package/src/cli/cmd/tui/component/dialog/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog/dialog-status.tsx +190 -0
- package/src/cli/cmd/tui/component/dialog/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/dialog/dialog-variant.tsx +39 -0
- package/src/cli/cmd/tui/component/dialog/dialog-workspace-create.tsx +200 -0
- package/src/cli/cmd/tui/component/dialog/dialog-workspace-unavailable.tsx +81 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +1 -32
- package/src/cli/cmd/tui/component/dialog-command.tsx +1 -190
- package/src/cli/cmd/tui/component/dialog-console-org.tsx +1 -103
- package/src/cli/cmd/tui/component/dialog-go-upsell.tsx +1 -159
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +1 -86
- package/src/cli/cmd/tui/component/dialog-model.tsx +1 -238
- package/src/cli/cmd/tui/component/dialog-provider.tsx +1 -343
- package/src/cli/cmd/tui/component/dialog-session-delete-failed.tsx +1 -103
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +1 -301
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +1 -35
- package/src/cli/cmd/tui/component/dialog-skill.tsx +1 -37
- package/src/cli/cmd/tui/component/dialog-stash.tsx +1 -87
- package/src/cli/cmd/tui/component/dialog-status.tsx +1 -190
- package/src/cli/cmd/tui/component/dialog-tag.tsx +1 -44
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +1 -50
- package/src/cli/cmd/tui/component/dialog-variant.tsx +1 -39
- package/src/cli/cmd/tui/component/dialog-workspace-create.tsx +1 -200
- package/src/cli/cmd/tui/component/dialog-workspace-unavailable.tsx +1 -81
- package/src/tool/apply_patch.ts +1 -334
- package/src/tool/bash.ts +1 -656
- package/src/tool/core/external-directory.ts +55 -0
- package/src/tool/core/invalid.ts +21 -0
- package/src/tool/core/recall.ts +164 -0
- package/src/tool/core/recall.txt +12 -0
- package/src/tool/core/schema.ts +16 -0
- package/src/tool/core/tool.ts +162 -0
- package/src/tool/core/truncate.ts +160 -0
- package/src/tool/core/truncation-dir.ts +4 -0
- package/src/tool/diagnostics.ts +1 -20
- package/src/tool/edit-replacers.ts +1 -288
- package/src/tool/edit-utils.ts +1 -86
- package/src/tool/edit.ts +1 -262
- package/src/tool/external-directory.ts +1 -55
- package/src/tool/file/apply_patch.ts +334 -0
- package/src/tool/file/apply_patch.txt +33 -0
- package/src/tool/file/bash.ts +656 -0
- package/src/tool/file/bash.txt +119 -0
- package/src/tool/file/edit-replacers.ts +288 -0
- package/src/tool/file/edit-utils.ts +86 -0
- package/src/tool/file/edit.ts +262 -0
- package/src/tool/file/edit.txt +10 -0
- package/src/tool/file/read.ts +389 -0
- package/src/tool/file/read.txt +14 -0
- package/src/tool/file/write.ts +114 -0
- package/src/tool/file/write.txt +8 -0
- package/src/tool/glob.ts +1 -115
- package/src/tool/grep.ts +1 -151
- package/src/tool/integration/diagnostics.ts +20 -0
- package/src/tool/integration/lsp.ts +113 -0
- package/src/tool/integration/lsp.txt +24 -0
- package/src/tool/integration/mcp-exa.ts +73 -0
- package/src/tool/integration/package.ts +168 -0
- package/src/tool/integration/registry.ts +375 -0
- package/src/tool/invalid.ts +1 -21
- package/src/tool/lsp.ts +1 -113
- package/src/tool/mcp-exa.ts +1 -73
- package/src/tool/package.ts +1 -168
- package/src/tool/plan.ts +1 -30
- package/src/tool/question.ts +1 -52
- package/src/tool/read.ts +1 -389
- package/src/tool/recall.ts +1 -164
- package/src/tool/registry.ts +1 -375
- package/src/tool/schema.ts +1 -16
- package/src/tool/search/glob.ts +115 -0
- package/src/tool/search/glob.txt +6 -0
- package/src/tool/search/grep.ts +151 -0
- package/src/tool/search/grep.txt +8 -0
- package/src/tool/search/warpgrep.ts +107 -0
- package/src/tool/search/warpgrep.txt +10 -0
- package/src/tool/search/webfetch.ts +202 -0
- package/src/tool/search/webfetch.txt +13 -0
- package/src/tool/search/websearch.ts +71 -0
- package/src/tool/search/websearch.txt +14 -0
- package/src/tool/skill.ts +1 -91
- package/src/tool/task.ts +1 -197
- package/src/tool/todo.ts +1 -62
- package/src/tool/tool.ts +1 -162
- package/src/tool/truncate.ts +1 -160
- package/src/tool/truncation-dir.ts +1 -4
- package/src/tool/warpgrep.ts +1 -107
- package/src/tool/webfetch.ts +1 -202
- package/src/tool/websearch.ts +1 -71
- package/src/tool/workflow/plan-enter.txt +14 -0
- package/src/tool/workflow/plan-exit.txt +13 -0
- package/src/tool/workflow/plan.ts +30 -0
- package/src/tool/workflow/question.ts +52 -0
- package/src/tool/workflow/question.txt +11 -0
- package/src/tool/workflow/skill.ts +91 -0
- package/src/tool/workflow/skill.txt +5 -0
- package/src/tool/workflow/task.ts +197 -0
- package/src/tool/workflow/task.txt +57 -0
- package/src/tool/workflow/todo.ts +62 -0
- package/src/tool/workflow/todowrite.txt +167 -0
- package/src/tool/write.ts +1 -114
|
@@ -1,343 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useSync } from "@tui/context/sync"
|
|
3
|
-
import { map, pipe, sortBy } from "remeda"
|
|
4
|
-
import { DialogSelect } from "@tui/ui/dialog-select"
|
|
5
|
-
import { useDialog } from "@tui/ui/dialog"
|
|
6
|
-
import { useSDK } from "../context/sdk"
|
|
7
|
-
import { DialogPrompt } from "../ui/dialog-prompt"
|
|
8
|
-
import { Link } from "../ui/link"
|
|
9
|
-
import { useTheme } from "../context/theme"
|
|
10
|
-
import { TextAttributes } from "@opentui/core"
|
|
11
|
-
import type { ProviderAuthAuthorization, ProviderAuthMethod } from "@saeeol/sdk/v2"
|
|
12
|
-
import { DialogModel } from "./dialog-model"
|
|
13
|
-
import { useKeyboard } from "@opentui/solid"
|
|
14
|
-
import * as Clipboard from "@tui/util/clipboard"
|
|
15
|
-
import { useToast } from "../ui/toast"
|
|
16
|
-
import { isConsoleManagedProvider } from "@tui/util/provider-origin"
|
|
17
|
-
import * as Provider from "@/saeeol/cli/cmd/tui/component/dialog-provider"
|
|
18
|
-
import { useConnected } from "./use-connected"
|
|
19
|
-
|
|
20
|
-
const PROVIDER_PRIORITY: Record<string, number> = Provider.PROVIDER_PRIORITY
|
|
21
|
-
|
|
22
|
-
export function createDialogProviderOptions() {
|
|
23
|
-
const sync = useSync()
|
|
24
|
-
const dialog = useDialog()
|
|
25
|
-
const sdk = useSDK()
|
|
26
|
-
const toast = useToast()
|
|
27
|
-
const { theme } = useTheme()
|
|
28
|
-
const onboarded = useConnected()
|
|
29
|
-
const options = createMemo(() => {
|
|
30
|
-
return pipe(
|
|
31
|
-
sync.data.provider_next.all,
|
|
32
|
-
sortBy((x) => PROVIDER_PRIORITY[x.id] ?? 99),
|
|
33
|
-
map((provider) => {
|
|
34
|
-
const consoleManaged = isConsoleManagedProvider(sync.data.console_state.consoleManagedProviders, provider.id)
|
|
35
|
-
const connected = sync.data.provider_next.connected.includes(provider.id)
|
|
36
|
-
const failed = sync.data.provider_next.failed ?? []
|
|
37
|
-
const failedGutter = Provider.renderGutter(provider.id, failed, theme)
|
|
38
|
-
const failedDesc = Provider.failedDescription(provider.id, failed)
|
|
39
|
-
const baseDesc = Provider.PROVIDER_DESCRIPTIONS[provider.id]
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
title: Provider.PROVIDER_TITLES[provider.id] ?? provider.name,
|
|
43
|
-
value: provider.id,
|
|
44
|
-
description: failedDesc ?? baseDesc,
|
|
45
|
-
footer: consoleManaged ? sync.data.console_state.activeOrgName : undefined,
|
|
46
|
-
category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other",
|
|
47
|
-
gutter: failedGutter ?? (connected && onboarded() ? () => <text fg={theme.success}>✓</text> : undefined),
|
|
48
|
-
async onSelect() {
|
|
49
|
-
if (consoleManaged) return
|
|
50
|
-
|
|
51
|
-
const methods = sync.data.provider_auth[provider.id] ?? [
|
|
52
|
-
{
|
|
53
|
-
type: "api",
|
|
54
|
-
label: "API key",
|
|
55
|
-
},
|
|
56
|
-
]
|
|
57
|
-
let index: number | null = 0
|
|
58
|
-
if (methods.length > 1) {
|
|
59
|
-
index = await new Promise<number | null>((resolve) => {
|
|
60
|
-
dialog.replace(
|
|
61
|
-
() => (
|
|
62
|
-
<DialogSelect
|
|
63
|
-
title="Select auth method"
|
|
64
|
-
options={methods.map((x, index) => ({
|
|
65
|
-
title: x.label,
|
|
66
|
-
value: index,
|
|
67
|
-
}))}
|
|
68
|
-
onSelect={(option) => resolve(option.value)}
|
|
69
|
-
/>
|
|
70
|
-
),
|
|
71
|
-
() => resolve(null),
|
|
72
|
-
)
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
if (index == null) return
|
|
76
|
-
const method = methods[index]
|
|
77
|
-
if (method.type === "oauth") {
|
|
78
|
-
let inputs: Record<string, string> | undefined
|
|
79
|
-
if (method.prompts?.length) {
|
|
80
|
-
const value = await PromptsMethod({
|
|
81
|
-
dialog,
|
|
82
|
-
prompts: method.prompts,
|
|
83
|
-
})
|
|
84
|
-
if (!value) return
|
|
85
|
-
inputs = value
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const result = await sdk.client.provider.oauth.authorize({
|
|
89
|
-
providerID: provider.id,
|
|
90
|
-
method: index,
|
|
91
|
-
inputs,
|
|
92
|
-
})
|
|
93
|
-
if (result.error) {
|
|
94
|
-
toast.show({
|
|
95
|
-
variant: "error",
|
|
96
|
-
message: JSON.stringify(result.error),
|
|
97
|
-
})
|
|
98
|
-
dialog.clear()
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
if (result.data?.method === "code") {
|
|
102
|
-
dialog.replace(() => (
|
|
103
|
-
<CodeMethod
|
|
104
|
-
providerID={provider.id}
|
|
105
|
-
title={method.label}
|
|
106
|
-
index={index}
|
|
107
|
-
authorization={result.data!}
|
|
108
|
-
/>
|
|
109
|
-
))
|
|
110
|
-
}
|
|
111
|
-
if (result.data?.method === "auto") {
|
|
112
|
-
const saeeol = Provider.renderAutoMethod({
|
|
113
|
-
providerID: provider.id,
|
|
114
|
-
title: method.label,
|
|
115
|
-
index,
|
|
116
|
-
authorization: result.data!,
|
|
117
|
-
useSDK,
|
|
118
|
-
useTheme,
|
|
119
|
-
DialogModel,
|
|
120
|
-
})
|
|
121
|
-
if (saeeol) {
|
|
122
|
-
dialog.replace(saeeol)
|
|
123
|
-
} else {
|
|
124
|
-
dialog.replace(() => (
|
|
125
|
-
<AutoMethod
|
|
126
|
-
providerID={provider.id}
|
|
127
|
-
title={method.label}
|
|
128
|
-
index={index}
|
|
129
|
-
authorization={result.data!}
|
|
130
|
-
/>
|
|
131
|
-
))
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (method.type === "api") {
|
|
136
|
-
let metadata: Record<string, string> | undefined
|
|
137
|
-
if (method.prompts?.length) {
|
|
138
|
-
const value = await PromptsMethod({ dialog, prompts: method.prompts })
|
|
139
|
-
if (!value) return
|
|
140
|
-
metadata = value
|
|
141
|
-
}
|
|
142
|
-
return dialog.replace(() => (
|
|
143
|
-
<ApiMethod providerID={provider.id} title={method.label} metadata={metadata} />
|
|
144
|
-
))
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
}),
|
|
149
|
-
)
|
|
150
|
-
})
|
|
151
|
-
return options
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function DialogProvider() {
|
|
155
|
-
const options = createDialogProviderOptions()
|
|
156
|
-
return <DialogSelect title="Connect a provider" options={options()} />
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
interface AutoMethodProps {
|
|
160
|
-
index: number
|
|
161
|
-
providerID: string
|
|
162
|
-
title: string
|
|
163
|
-
authorization: ProviderAuthAuthorization
|
|
164
|
-
}
|
|
165
|
-
function AutoMethod(props: AutoMethodProps) {
|
|
166
|
-
const { theme } = useTheme()
|
|
167
|
-
const sdk = useSDK()
|
|
168
|
-
const dialog = useDialog()
|
|
169
|
-
const sync = useSync()
|
|
170
|
-
const toast = useToast()
|
|
171
|
-
|
|
172
|
-
useKeyboard((evt) => {
|
|
173
|
-
if (evt.name === "c" && !evt.ctrl && !evt.meta) {
|
|
174
|
-
const code = props.authorization.instructions.match(/[A-Z0-9]{4}-[A-Z0-9]{4,5}/)?.[0] ?? props.authorization.url
|
|
175
|
-
Clipboard.copy(code)
|
|
176
|
-
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
|
|
177
|
-
.catch(toast.error)
|
|
178
|
-
}
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
onMount(async () => {
|
|
182
|
-
const result = await sdk.client.provider.oauth.callback({
|
|
183
|
-
providerID: props.providerID,
|
|
184
|
-
method: props.index,
|
|
185
|
-
})
|
|
186
|
-
if (result.error) {
|
|
187
|
-
dialog.clear()
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
await sdk.client.instance.dispose()
|
|
191
|
-
await sync.bootstrap()
|
|
192
|
-
dialog.replace(() => <DialogModel providerID={props.providerID} />)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
|
|
197
|
-
<box flexDirection="row" justifyContent="space-between">
|
|
198
|
-
<text attributes={TextAttributes.BOLD} fg={theme.text}>
|
|
199
|
-
{props.title}
|
|
200
|
-
</text>
|
|
201
|
-
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
|
|
202
|
-
esc
|
|
203
|
-
</text>
|
|
204
|
-
</box>
|
|
205
|
-
<box gap={1}>
|
|
206
|
-
<Link href={props.authorization.url} fg={theme.primary} />
|
|
207
|
-
<text fg={theme.textMuted}>{props.authorization.instructions}</text>
|
|
208
|
-
</box>
|
|
209
|
-
<text fg={theme.textMuted}>Waiting for authorization...</text>
|
|
210
|
-
<text fg={theme.text}>
|
|
211
|
-
c <span style={{ fg: theme.textMuted }}>copy</span>
|
|
212
|
-
</text>
|
|
213
|
-
</box>
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
interface CodeMethodProps {
|
|
218
|
-
index: number
|
|
219
|
-
title: string
|
|
220
|
-
providerID: string
|
|
221
|
-
authorization: ProviderAuthAuthorization
|
|
222
|
-
}
|
|
223
|
-
function CodeMethod(props: CodeMethodProps) {
|
|
224
|
-
const { theme } = useTheme()
|
|
225
|
-
const sdk = useSDK()
|
|
226
|
-
const sync = useSync()
|
|
227
|
-
const dialog = useDialog()
|
|
228
|
-
const [error, setError] = createSignal(false)
|
|
229
|
-
|
|
230
|
-
return (
|
|
231
|
-
<DialogPrompt
|
|
232
|
-
title={props.title}
|
|
233
|
-
placeholder="Authorization code"
|
|
234
|
-
onConfirm={async (value) => {
|
|
235
|
-
const { error } = await sdk.client.provider.oauth.callback({
|
|
236
|
-
providerID: props.providerID,
|
|
237
|
-
method: props.index,
|
|
238
|
-
code: value,
|
|
239
|
-
})
|
|
240
|
-
if (!error) {
|
|
241
|
-
await sdk.client.instance.dispose()
|
|
242
|
-
await sync.bootstrap()
|
|
243
|
-
dialog.replace(() => <DialogModel providerID={props.providerID} />)
|
|
244
|
-
return
|
|
245
|
-
}
|
|
246
|
-
setError(true)
|
|
247
|
-
}}
|
|
248
|
-
description={() => (
|
|
249
|
-
<box gap={1}>
|
|
250
|
-
<text fg={theme.textMuted}>{props.authorization.instructions}</text>
|
|
251
|
-
<Link href={props.authorization.url} fg={theme.primary} />
|
|
252
|
-
<Show when={error()}>
|
|
253
|
-
<text fg={theme.error}>Invalid code</text>
|
|
254
|
-
</Show>
|
|
255
|
-
</box>
|
|
256
|
-
)}
|
|
257
|
-
/>
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
interface ApiMethodProps {
|
|
262
|
-
providerID: string
|
|
263
|
-
title: string
|
|
264
|
-
metadata?: Record<string, string>
|
|
265
|
-
}
|
|
266
|
-
function ApiMethod(props: ApiMethodProps) {
|
|
267
|
-
const dialog = useDialog()
|
|
268
|
-
const sdk = useSDK()
|
|
269
|
-
const sync = useSync()
|
|
270
|
-
const { theme } = useTheme()
|
|
271
|
-
|
|
272
|
-
return (
|
|
273
|
-
<DialogPrompt
|
|
274
|
-
title={props.title}
|
|
275
|
-
placeholder="API key"
|
|
276
|
-
description={Provider.renderApiDescription(props.providerID, theme)}
|
|
277
|
-
onConfirm={async (value) => {
|
|
278
|
-
if (!value) return
|
|
279
|
-
await sdk.client.auth.set({
|
|
280
|
-
providerID: props.providerID,
|
|
281
|
-
auth: {
|
|
282
|
-
type: "api",
|
|
283
|
-
key: value,
|
|
284
|
-
...(props.metadata ? { metadata: props.metadata } : {}),
|
|
285
|
-
},
|
|
286
|
-
})
|
|
287
|
-
await sdk.client.instance.dispose()
|
|
288
|
-
await sync.bootstrap()
|
|
289
|
-
dialog.replace(() => <DialogModel providerID={props.providerID} />)
|
|
290
|
-
}}
|
|
291
|
-
/>
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
interface PromptsMethodProps {
|
|
296
|
-
dialog: ReturnType<typeof useDialog>
|
|
297
|
-
prompts: NonNullable<ProviderAuthMethod["prompts"]>[number][]
|
|
298
|
-
}
|
|
299
|
-
async function PromptsMethod(props: PromptsMethodProps) {
|
|
300
|
-
const inputs: Record<string, string> = {}
|
|
301
|
-
for (const prompt of props.prompts) {
|
|
302
|
-
if (prompt.when) {
|
|
303
|
-
const value = inputs[prompt.when.key]
|
|
304
|
-
if (value === undefined) continue
|
|
305
|
-
const matches = prompt.when.op === "eq" ? value === prompt.when.value : value !== prompt.when.value
|
|
306
|
-
if (!matches) continue
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (prompt.type === "select") {
|
|
310
|
-
const value = await new Promise<string | null>((resolve) => {
|
|
311
|
-
props.dialog.replace(
|
|
312
|
-
() => (
|
|
313
|
-
<DialogSelect
|
|
314
|
-
title={prompt.message}
|
|
315
|
-
options={prompt.options.map((x) => ({
|
|
316
|
-
title: x.label,
|
|
317
|
-
value: x.value,
|
|
318
|
-
description: x.hint,
|
|
319
|
-
}))}
|
|
320
|
-
onSelect={(option) => resolve(option.value)}
|
|
321
|
-
/>
|
|
322
|
-
),
|
|
323
|
-
() => resolve(null),
|
|
324
|
-
)
|
|
325
|
-
})
|
|
326
|
-
if (value === null) return null
|
|
327
|
-
inputs[prompt.key] = value
|
|
328
|
-
continue
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const value = await new Promise<string | null>((resolve) => {
|
|
332
|
-
props.dialog.replace(
|
|
333
|
-
() => (
|
|
334
|
-
<DialogPrompt title={prompt.message} placeholder={prompt.placeholder} onConfirm={(value) => resolve(value)} />
|
|
335
|
-
),
|
|
336
|
-
() => resolve(null),
|
|
337
|
-
)
|
|
338
|
-
})
|
|
339
|
-
if (value === null) return null
|
|
340
|
-
inputs[prompt.key] = value
|
|
341
|
-
}
|
|
342
|
-
return inputs
|
|
343
|
-
}
|
|
1
|
+
export * from "./dialog/dialog-provider"
|
|
@@ -1,103 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useTheme } from "../context/theme"
|
|
3
|
-
import { useDialog } from "../ui/dialog"
|
|
4
|
-
import { createStore } from "solid-js/store"
|
|
5
|
-
import { For } from "solid-js"
|
|
6
|
-
import { useKeyboard } from "@opentui/solid"
|
|
7
|
-
|
|
8
|
-
export function DialogSessionDeleteFailed(props: {
|
|
9
|
-
session: string
|
|
10
|
-
workspace: string
|
|
11
|
-
onDelete?: () => boolean | void | Promise<boolean | void>
|
|
12
|
-
onRestore?: () => boolean | void | Promise<boolean | void>
|
|
13
|
-
onDone?: () => void
|
|
14
|
-
}) {
|
|
15
|
-
const dialog = useDialog()
|
|
16
|
-
const { theme } = useTheme()
|
|
17
|
-
const [store, setStore] = createStore({
|
|
18
|
-
active: "delete" as "delete" | "restore",
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
const options = [
|
|
22
|
-
{
|
|
23
|
-
id: "delete" as const,
|
|
24
|
-
title: "Delete workspace",
|
|
25
|
-
description: "Delete the workspace and all sessions attached to it.",
|
|
26
|
-
run: props.onDelete,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: "restore" as const,
|
|
30
|
-
title: "Restore to new workspace",
|
|
31
|
-
description: "Try to restore this session into a new workspace.",
|
|
32
|
-
run: props.onRestore,
|
|
33
|
-
},
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
async function confirm() {
|
|
37
|
-
const result = await options.find((item) => item.id === store.active)?.run?.()
|
|
38
|
-
if (result === false) return
|
|
39
|
-
props.onDone?.()
|
|
40
|
-
if (!props.onDone) dialog.clear()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
useKeyboard((evt) => {
|
|
44
|
-
if (evt.name === "return") {
|
|
45
|
-
evt.preventDefault()
|
|
46
|
-
evt.stopPropagation()
|
|
47
|
-
void confirm()
|
|
48
|
-
}
|
|
49
|
-
if (evt.name === "left" || evt.name === "up") {
|
|
50
|
-
setStore("active", "delete")
|
|
51
|
-
}
|
|
52
|
-
if (evt.name === "right" || evt.name === "down") {
|
|
53
|
-
setStore("active", "restore")
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<box paddingLeft={2} paddingRight={2} gap={1}>
|
|
59
|
-
<box flexDirection="row" justifyContent="space-between">
|
|
60
|
-
<text attributes={TextAttributes.BOLD} fg={theme.text}>
|
|
61
|
-
Failed to Delete Session
|
|
62
|
-
</text>
|
|
63
|
-
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
|
|
64
|
-
esc
|
|
65
|
-
</text>
|
|
66
|
-
</box>
|
|
67
|
-
<text fg={theme.textMuted} wrapMode="word">
|
|
68
|
-
{`The session "${props.session}" could not be deleted because the workspace "${props.workspace}" is not available.`}
|
|
69
|
-
</text>
|
|
70
|
-
<text fg={theme.textMuted} wrapMode="word">
|
|
71
|
-
Choose how you want to recover this broken workspace session.
|
|
72
|
-
</text>
|
|
73
|
-
<box flexDirection="column" paddingBottom={1} gap={1}>
|
|
74
|
-
<For each={options}>
|
|
75
|
-
{(item) => (
|
|
76
|
-
<box
|
|
77
|
-
flexDirection="column"
|
|
78
|
-
paddingLeft={1}
|
|
79
|
-
paddingRight={1}
|
|
80
|
-
paddingTop={1}
|
|
81
|
-
paddingBottom={1}
|
|
82
|
-
backgroundColor={item.id === store.active ? theme.primary : undefined}
|
|
83
|
-
onMouseUp={() => {
|
|
84
|
-
setStore("active", item.id)
|
|
85
|
-
void confirm()
|
|
86
|
-
}}
|
|
87
|
-
>
|
|
88
|
-
<text
|
|
89
|
-
attributes={TextAttributes.BOLD}
|
|
90
|
-
fg={item.id === store.active ? theme.selectedListItemText : theme.text}
|
|
91
|
-
>
|
|
92
|
-
{item.title}
|
|
93
|
-
</text>
|
|
94
|
-
<text fg={item.id === store.active ? theme.selectedListItemText : theme.textMuted} wrapMode="word">
|
|
95
|
-
{item.description}
|
|
96
|
-
</text>
|
|
97
|
-
</box>
|
|
98
|
-
)}
|
|
99
|
-
</For>
|
|
100
|
-
</box>
|
|
101
|
-
</box>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
1
|
+
export * from "./dialog/dialog-session-delete-failed"
|