@@ -5,7 +5,7 @@ import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda"
|
|||||||
import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
|
import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
|
||||||
import { useDialog } from "@tui/ui/dialog"
|
import { useDialog } from "@tui/ui/dialog"
|
||||||
import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
|
import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
|
||||||
import { Keybind } from "@/util/keybind"
|
import { useKeybind } from "../context/keybind"
|
||||||
import * as fuzzysort from "fuzzysort"
|
import * as fuzzysort from "fuzzysort"
|
||||||
|
|
||||||
export function useConnected() {
|
export function useConnected() {
|
||||||
@@ -19,6 +19,7 @@ export function DialogModel(props: { providerID?: string }) {
|
|||||||
const local = useLocal()
|
const local = useLocal()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
|
const keybind = useKeybind()
|
||||||
const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
|
const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
|
||||||
const [query, setQuery] = createSignal("")
|
const [query, setQuery] = createSignal("")
|
||||||
|
|
||||||
@@ -207,14 +208,14 @@ export function DialogModel(props: { providerID?: string }) {
|
|||||||
<DialogSelect
|
<DialogSelect
|
||||||
keybind={[
|
keybind={[
|
||||||
{
|
{
|
||||||
keybind: Keybind.parse("ctrl+a")[0],
|
keybind: keybind.all.model_provider_list?.[0],
|
||||||
title: connected() ? "Connect provider" : "View all providers",
|
title: connected() ? "Connect provider" : "View all providers",
|
||||||
onTrigger() {
|
onTrigger() {
|
||||||
dialog.replace(() => <DialogProvider />)
|
dialog.replace(() => <DialogProvider />)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keybind: Keybind.parse("ctrl+f")[0],
|
keybind: keybind.all.model_favorite_toggle?.[0],
|
||||||
title: "Favorite",
|
title: "Favorite",
|
||||||
disabled: !connected(),
|
disabled: !connected(),
|
||||||
onTrigger: (option) => {
|
onTrigger: (option) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useRoute } from "@tui/context/route"
|
|||||||
import { useSync } from "@tui/context/sync"
|
import { useSync } from "@tui/context/sync"
|
||||||
import { createMemo, createSignal, createResource, onMount, Show } from "solid-js"
|
import { createMemo, createSignal, createResource, onMount, Show } from "solid-js"
|
||||||
import { Locale } from "@/util/locale"
|
import { Locale } from "@/util/locale"
|
||||||
import { Keybind } from "@/util/keybind"
|
import { useKeybind } from "../context/keybind"
|
||||||
import { useTheme } from "../context/theme"
|
import { useTheme } from "../context/theme"
|
||||||
import { useSDK } from "../context/sdk"
|
import { useSDK } from "../context/sdk"
|
||||||
import { DialogSessionRename } from "./dialog-session-rename"
|
import { DialogSessionRename } from "./dialog-session-rename"
|
||||||
@@ -14,9 +14,10 @@ import "opentui-spinner/solid"
|
|||||||
|
|
||||||
export function DialogSessionList() {
|
export function DialogSessionList() {
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const sync = useSync()
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const sync = useSync()
|
||||||
|
const keybind = useKeybind()
|
||||||
|
const { theme } = useTheme()
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const kv = useKV()
|
const kv = useKV()
|
||||||
|
|
||||||
@@ -29,8 +30,6 @@ export function DialogSessionList() {
|
|||||||
return result.data ?? []
|
return result.data ?? []
|
||||||
})
|
})
|
||||||
|
|
||||||
const deleteKeybind = "ctrl+d"
|
|
||||||
|
|
||||||
const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
|
const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
|
||||||
|
|
||||||
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||||
@@ -52,7 +51,7 @@ export function DialogSessionList() {
|
|||||||
const status = sync.data.session_status?.[x.id]
|
const status = sync.data.session_status?.[x.id]
|
||||||
const isWorking = status?.type === "busy"
|
const isWorking = status?.type === "busy"
|
||||||
return {
|
return {
|
||||||
title: isDeleting ? `Press ${deleteKeybind} again to confirm` : x.title,
|
title: isDeleting ? `Press ${keybind.print("session_delete")} again to confirm` : x.title,
|
||||||
bg: isDeleting ? theme.error : undefined,
|
bg: isDeleting ? theme.error : undefined,
|
||||||
value: x.id,
|
value: x.id,
|
||||||
category,
|
category,
|
||||||
@@ -89,7 +88,7 @@ export function DialogSessionList() {
|
|||||||
}}
|
}}
|
||||||
keybind={[
|
keybind={[
|
||||||
{
|
{
|
||||||
keybind: Keybind.parse(deleteKeybind)[0],
|
keybind: keybind.all.session_delete?.[0],
|
||||||
title: "delete",
|
title: "delete",
|
||||||
onTrigger: async (option) => {
|
onTrigger: async (option) => {
|
||||||
if (toDelete() === option.value) {
|
if (toDelete() === option.value) {
|
||||||
@@ -103,7 +102,7 @@ export function DialogSessionList() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keybind: Keybind.parse("ctrl+r")[0],
|
keybind: keybind.all.session_rename?.[0],
|
||||||
title: "rename",
|
title: "rename",
|
||||||
onTrigger: async (option) => {
|
onTrigger: async (option) => {
|
||||||
dialog.replace(() => <DialogSessionRename session={option.value} />)
|
dialog.replace(() => <DialogSessionRename session={option.value} />)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { useDialog } from "@tui/ui/dialog"
|
|||||||
import { DialogSelect } from "@tui/ui/dialog-select"
|
import { DialogSelect } from "@tui/ui/dialog-select"
|
||||||
import { createMemo, createSignal } from "solid-js"
|
import { createMemo, createSignal } from "solid-js"
|
||||||
import { Locale } from "@/util/locale"
|
import { Locale } from "@/util/locale"
|
||||||
import { Keybind } from "@/util/keybind"
|
|
||||||
import { useTheme } from "../context/theme"
|
import { useTheme } from "../context/theme"
|
||||||
|
import { useKeybind } from "../context/keybind"
|
||||||
import { usePromptStash, type StashEntry } from "./prompt/stash"
|
import { usePromptStash, type StashEntry } from "./prompt/stash"
|
||||||
|
|
||||||
function getRelativeTime(timestamp: number): string {
|
function getRelativeTime(timestamp: number): string {
|
||||||
@@ -30,6 +30,7 @@ export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) {
|
|||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const stash = usePromptStash()
|
const stash = usePromptStash()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
const keybind = useKeybind()
|
||||||
|
|
||||||
const [toDelete, setToDelete] = createSignal<number>()
|
const [toDelete, setToDelete] = createSignal<number>()
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) {
|
|||||||
const isDeleting = toDelete() === index
|
const isDeleting = toDelete() === index
|
||||||
const lineCount = (entry.input.match(/\n/g)?.length ?? 0) + 1
|
const lineCount = (entry.input.match(/\n/g)?.length ?? 0) + 1
|
||||||
return {
|
return {
|
||||||
title: isDeleting ? "Press ctrl+d again to confirm" : getStashPreview(entry.input),
|
title: isDeleting ? `Press ${keybind.print("stash_delete")} again to confirm` : getStashPreview(entry.input),
|
||||||
bg: isDeleting ? theme.error : undefined,
|
bg: isDeleting ? theme.error : undefined,
|
||||||
value: index,
|
value: index,
|
||||||
description: getRelativeTime(entry.timestamp),
|
description: getRelativeTime(entry.timestamp),
|
||||||
@@ -69,7 +70,7 @@ export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) {
|
|||||||
}}
|
}}
|
||||||
keybind={[
|
keybind={[
|
||||||
{
|
{
|
||||||
keybind: Keybind.parse("ctrl+d")[0],
|
keybind: keybind.all.stash_delete?.[0],
|
||||||
title: "delete",
|
title: "delete",
|
||||||
onTrigger: (option) => {
|
onTrigger: (option) => {
|
||||||
if (toDelete() === option.value) {
|
if (toDelete() === option.value) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export interface DialogSelectProps<T> {
|
|||||||
onSelect?: (option: DialogSelectOption<T>) => void
|
onSelect?: (option: DialogSelectOption<T>) => void
|
||||||
skipFilter?: boolean
|
skipFilter?: boolean
|
||||||
keybind?: {
|
keybind?: {
|
||||||
keybind: Keybind.Info
|
keybind?: Keybind.Info
|
||||||
title: string
|
title: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
onTrigger: (option: DialogSelectOption<T>) => void
|
onTrigger: (option: DialogSelectOption<T>) => void
|
||||||
@@ -166,7 +166,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const item of props.keybind ?? []) {
|
for (const item of props.keybind ?? []) {
|
||||||
if (item.disabled) continue
|
if (item.disabled || !item.keybind) continue
|
||||||
if (Keybind.match(item.keybind, keybind.parse(evt))) {
|
if (Keybind.match(item.keybind, keybind.parse(evt))) {
|
||||||
const s = selected()
|
const s = selected()
|
||||||
if (s) {
|
if (s) {
|
||||||
@@ -188,7 +188,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
|||||||
}
|
}
|
||||||
props.ref?.(ref)
|
props.ref?.(ref)
|
||||||
|
|
||||||
const keybinds = createMemo(() => props.keybind?.filter((x) => !x.disabled) ?? [])
|
const keybinds = createMemo(() => props.keybind?.filter((x) => !x.disabled && x.keybind) ?? [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box gap={1} paddingBottom={1}>
|
<box gap={1} paddingBottom={1}>
|
||||||
|
|||||||
@@ -621,7 +621,11 @@ export namespace Config {
|
|||||||
session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
|
session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
|
||||||
session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
|
session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
|
||||||
session_fork: z.string().optional().default("none").describe("Fork session from message"),
|
session_fork: z.string().optional().default("none").describe("Fork session from message"),
|
||||||
session_rename: z.string().optional().default("none").describe("Rename session"),
|
session_rename: z.string().optional().default("ctrl+r").describe("Rename session"),
|
||||||
|
session_delete: z.string().optional().default("ctrl+d").describe("Delete session"),
|
||||||
|
stash_delete: z.string().optional().default("ctrl+d").describe("Delete stash entry"),
|
||||||
|
model_provider_list: z.string().optional().default("ctrl+a").describe("Open provider list from model dialog"),
|
||||||
|
model_favorite_toggle: z.string().optional().default("ctrl+f").describe("Toggle model favorite status"),
|
||||||
session_share: z.string().optional().default("none").describe("Share current session"),
|
session_share: z.string().optional().default("none").describe("Share current session"),
|
||||||
session_unshare: z.string().optional().default("none").describe("Unshare current session"),
|
session_unshare: z.string().optional().default("none").describe("Unshare current session"),
|
||||||
session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
|
session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export namespace Keybind {
|
|||||||
leader: boolean // our custom field
|
leader: boolean // our custom field
|
||||||
}
|
}
|
||||||
|
|
||||||
export function match(a: Info, b: Info): boolean {
|
export function match(a: Info | undefined, b: Info): boolean {
|
||||||
// Normalize super field (undefined and false are equivalent)
|
if (!a) return false
|
||||||
const normalizedA = { ...a, super: a.super ?? false }
|
const normalizedA = { ...a, super: a.super ?? false }
|
||||||
const normalizedB = { ...b, super: b.super ?? false }
|
const normalizedB = { ...b, super: b.super ?? false }
|
||||||
return isDeepEqual(normalizedA, normalizedB)
|
return isDeepEqual(normalizedA, normalizedB)
|
||||||
@@ -32,7 +32,8 @@ export namespace Keybind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toString(info: Info): string {
|
export function toString(info: Info | undefined): string {
|
||||||
|
if (!info) return ""
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
|
|
||||||
if (info.ctrl) parts.push("ctrl")
|
if (info.ctrl) parts.push("ctrl")
|
||||||
|
|||||||
@@ -966,6 +966,22 @@ export type KeybindsConfig = {
|
|||||||
* Rename session
|
* Rename session
|
||||||
*/
|
*/
|
||||||
session_rename?: string
|
session_rename?: string
|
||||||
|
/**
|
||||||
|
* Delete session
|
||||||
|
*/
|
||||||
|
session_delete?: string
|
||||||
|
/**
|
||||||
|
* Delete stash entry
|
||||||
|
*/
|
||||||
|
stash_delete?: string
|
||||||
|
/**
|
||||||
|
* Open provider list from model dialog
|
||||||
|
*/
|
||||||
|
model_provider_list?: string
|
||||||
|
/**
|
||||||
|
* Toggle model favorite status
|
||||||
|
*/
|
||||||
|
model_favorite_toggle?: string
|
||||||
/**
|
/**
|
||||||
* Share current session
|
* Share current session
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8168,7 +8168,27 @@
|
|||||||
},
|
},
|
||||||
"session_rename": {
|
"session_rename": {
|
||||||
"description": "Rename session",
|
"description": "Rename session",
|
||||||
"default": "none",
|
"default": "ctrl+r",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"session_delete": {
|
||||||
|
"description": "Delete session",
|
||||||
|
"default": "ctrl+d",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"stash_delete": {
|
||||||
|
"description": "Delete stash entry",
|
||||||
|
"default": "ctrl+d",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"model_provider_list": {
|
||||||
|
"description": "Open provider list from model dialog",
|
||||||
|
"default": "ctrl+a",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"model_favorite_toggle": {
|
||||||
|
"description": "Toggle model favorite status",
|
||||||
|
"default": "ctrl+f",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"session_share": {
|
"session_share": {
|
||||||
|
|||||||
Reference in New Issue
Block a user