fix remote server keyboard and reconnect flows
This commit is contained in:
@@ -174,6 +174,15 @@ fn open_remote_window(app: AppHandle, payload: RemoteWindowPayload) -> Result<()
|
|||||||
let label = format!("remote-{}", payload.id);
|
let label = format!("remote-{}", payload.id);
|
||||||
let title = format!("{} - {}", payload.name, parsed.host_str().unwrap_or(payload.base_url.as_str()));
|
let title = format!("{} - {}", payload.name, parsed.host_str().unwrap_or(payload.base_url.as_str()));
|
||||||
|
|
||||||
|
if let Some(existing) = app.get_webview_window(&label) {
|
||||||
|
let _ = existing.navigate(parsed.clone());
|
||||||
|
let _ = existing.set_title(&title);
|
||||||
|
let _ = existing.show();
|
||||||
|
let _ = existing.unminimize();
|
||||||
|
let _ = existing.set_focus();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
app.state::<AppState>()
|
app.state::<AppState>()
|
||||||
.remote_origins
|
.remote_origins
|
||||||
.lock()
|
.lock()
|
||||||
|
|||||||
@@ -72,10 +72,15 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const selectedLanguageOption = () => languageOptions.find((opt) => opt.value === locale()) ?? languageOptions[0]
|
const selectedLanguageOption = () => languageOptions.find((opt) => opt.value === locale()) ?? languageOptions[0]
|
||||||
|
|
||||||
const folders = () => recentFolders()
|
const folders = () => recentFolders()
|
||||||
|
const serverList = () => remoteServers()
|
||||||
const isLoading = () => Boolean(props.isLoading)
|
const isLoading = () => Boolean(props.isLoading)
|
||||||
|
|
||||||
|
function getActiveListLength() {
|
||||||
|
return activeTab() === "local" ? folders().length : serverList().length
|
||||||
|
}
|
||||||
|
|
||||||
// Update selected binary when preferences change
|
// Update selected binary when preferences change
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const lastUsed = serverSettings().opencodeBinary
|
const lastUsed = serverSettings().opencodeBinary
|
||||||
@@ -87,7 +92,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
function scrollToIndex(index: number) {
|
function scrollToIndex(index: number) {
|
||||||
const container = recentListRef
|
const container = recentListRef
|
||||||
if (!container) return
|
if (!container) return
|
||||||
const element = container.querySelector(`[data-folder-index="${index}"]`) as HTMLElement | null
|
const element = container.querySelector(`[data-list-index="${index}"]`) as HTMLElement | null
|
||||||
if (!element) return
|
if (!element) return
|
||||||
|
|
||||||
const containerRect = container.getBoundingClientRect()
|
const containerRect = container.getBoundingClientRect()
|
||||||
@@ -136,19 +141,18 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const folderList = folders()
|
|
||||||
|
|
||||||
if (isBrowseShortcut) {
|
if (isBrowseShortcut) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
void handleBrowse()
|
void handleBrowse()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folderList.length === 0) return
|
const listLength = getActiveListLength()
|
||||||
|
if (listLength === 0) return
|
||||||
|
|
||||||
if (e.key === "ArrowDown") {
|
if (e.key === "ArrowDown") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const newIndex = Math.min(selectedIndex() + 1, folderList.length - 1)
|
const newIndex = Math.min(selectedIndex() + 1, listLength - 1)
|
||||||
setSelectedIndex(newIndex)
|
setSelectedIndex(newIndex)
|
||||||
setFocusMode("recent")
|
setFocusMode("recent")
|
||||||
scrollToIndex(newIndex)
|
scrollToIndex(newIndex)
|
||||||
@@ -161,7 +165,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
} else if (e.key === "PageDown") {
|
} else if (e.key === "PageDown") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const pageSize = 5
|
const pageSize = 5
|
||||||
const newIndex = Math.min(selectedIndex() + pageSize, folderList.length - 1)
|
const newIndex = Math.min(selectedIndex() + pageSize, listLength - 1)
|
||||||
setSelectedIndex(newIndex)
|
setSelectedIndex(newIndex)
|
||||||
setFocusMode("recent")
|
setFocusMode("recent")
|
||||||
scrollToIndex(newIndex)
|
scrollToIndex(newIndex)
|
||||||
@@ -179,7 +183,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
scrollToIndex(0)
|
scrollToIndex(0)
|
||||||
} else if (e.key === "End") {
|
} else if (e.key === "End") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const newIndex = folderList.length - 1
|
const newIndex = listLength - 1
|
||||||
setSelectedIndex(newIndex)
|
setSelectedIndex(newIndex)
|
||||||
setFocusMode("recent")
|
setFocusMode("recent")
|
||||||
scrollToIndex(newIndex)
|
scrollToIndex(newIndex)
|
||||||
@@ -188,10 +192,17 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
handleEnterKey()
|
handleEnterKey()
|
||||||
} else if (e.key === "Backspace" || e.key === "Delete") {
|
} else if (e.key === "Backspace" || e.key === "Delete") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (folderList.length > 0 && focusMode() === "recent") {
|
if (listLength > 0 && focusMode() === "recent") {
|
||||||
const folder = folderList[selectedIndex()]
|
if (activeTab() === "local") {
|
||||||
if (folder) {
|
const folder = folders()[selectedIndex()]
|
||||||
handleRemove(folder.path)
|
if (folder) {
|
||||||
|
handleRemove(folder.path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const server = serverList()[selectedIndex()]
|
||||||
|
if (server) {
|
||||||
|
removeRemoteServerProfile(server.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,15 +211,40 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
|
|
||||||
function handleEnterKey() {
|
function handleEnterKey() {
|
||||||
if (isLoading()) return
|
if (isLoading()) return
|
||||||
const folderList = folders()
|
|
||||||
const index = selectedIndex()
|
const index = selectedIndex()
|
||||||
|
|
||||||
const folder = folderList[index]
|
if (activeTab() === "local") {
|
||||||
if (folder) {
|
const folder = folders()[index]
|
||||||
handleFolderSelect(folder.path)
|
if (folder) {
|
||||||
|
handleFolderSelect(folder.path)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = serverList()[index]
|
||||||
|
if (server) {
|
||||||
|
void handleConnectSavedServer(server.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
activeTab()
|
||||||
|
setSelectedIndex(0)
|
||||||
|
setFocusMode("recent")
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const length = getActiveListLength()
|
||||||
|
if (length === 0) {
|
||||||
|
setSelectedIndex(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedIndex() >= length) {
|
||||||
|
setSelectedIndex(length - 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
window.addEventListener("keydown", handleKeyDown)
|
window.addEventListener("keydown", handleKeyDown)
|
||||||
@@ -672,12 +708,28 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="panel-list panel-list--fill flex-1 min-h-0 overflow-auto">
|
<div
|
||||||
|
class="panel-list panel-list--fill flex-1 min-h-0 overflow-auto"
|
||||||
|
ref={(el) => (recentListRef = el)}
|
||||||
|
>
|
||||||
<For each={remoteServers()}>
|
<For each={remoteServers()}>
|
||||||
{(server) => (
|
{(server, index) => (
|
||||||
<div class="panel-list-item">
|
<div
|
||||||
|
class="panel-list-item"
|
||||||
|
classList={{
|
||||||
|
"panel-list-item-highlight": focusMode() === "recent" && selectedIndex() === index(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class="flex items-center gap-2 w-full px-1">
|
<div class="flex items-center gap-2 w-full px-1">
|
||||||
<button class="panel-list-item-content flex-1" onClick={() => void handleConnectSavedServer(server.id)}>
|
<button
|
||||||
|
data-list-index={index()}
|
||||||
|
class="panel-list-item-content flex-1"
|
||||||
|
onClick={() => void handleConnectSavedServer(server.id)}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
setFocusMode("recent")
|
||||||
|
setSelectedIndex(index())
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class="flex items-center justify-between gap-3 w-full">
|
<div class="flex items-center justify-between gap-3 w-full">
|
||||||
<div class="flex-1 min-w-0 text-left">
|
<div class="flex-1 min-w-0 text-left">
|
||||||
<div class="flex items-center gap-2 mb-1">
|
<div class="flex items-center gap-2 mb-1">
|
||||||
@@ -688,7 +740,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
<span class="font-mono truncate-start flex-1 min-w-0">{server.baseUrl}</span>
|
<span class="font-mono truncate-start flex-1 min-w-0">{server.baseUrl}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Show when={connectingServerId() === server.id} fallback={<kbd class="kbd">↵</kbd>}>
|
<Show when={connectingServerId() === server.id} fallback={<Show when={focusMode() === "recent" && selectedIndex() === index()}><kbd class="kbd">↵</kbd></Show>}>
|
||||||
<Loader2 class="w-4 h-4 animate-spin icon-muted" />
|
<Loader2 class="w-4 h-4 animate-spin icon-muted" />
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
@@ -735,7 +787,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 w-full px-1">
|
<div class="flex items-center gap-2 w-full px-1">
|
||||||
<button
|
<button
|
||||||
data-folder-index={index()}
|
data-list-index={index()}
|
||||||
class="panel-list-item-content flex-1"
|
class="panel-list-item-content flex-1"
|
||||||
disabled={isLoading()}
|
disabled={isLoading()}
|
||||||
onClick={() => handleFolderSelect(folder.path)}
|
onClick={() => handleFolderSelect(folder.path)}
|
||||||
|
|||||||
Reference in New Issue
Block a user