diff --git a/src/components/main-ui.tsx b/src/components/main-ui.tsx
index 004011d..ddd8e8b 100644
--- a/src/components/main-ui.tsx
+++ b/src/components/main-ui.tsx
@@ -13,6 +13,7 @@ import { OrganiseUI } from "./organise";
import { HeaderLayout } from "./header-layout";
import { PDFPreviewPane } from "./pdf-preview";
import { useFileListContext } from "../provider/fileListProvider";
+import { KeyboardNavProvider } from "../provider/keyboardNavProvider";
import { Button } from "./ui/button";
interface MainUIProps {
@@ -50,33 +51,35 @@ export function MainUI(props: MainUIProps) {
return (
-
-
-
-
-
- 0 ? "30%" : 6}>
- 0}
- fallback={
-
-
- }
- >
- setIsPreviewOpen(true)}
- onClose={() => setIsPreviewOpen(false)}
- />
-
+
+
+
+
-
-
+
+ 0 ? "30%" : 6}>
+ 0}
+ fallback={
+
+
+ }
+ >
+ setIsPreviewOpen(true)}
+ onClose={() => setIsPreviewOpen(false)}
+ />
+
+
+
+
+
);
}
diff --git a/src/hooks/useKeyboardNav.ts b/src/hooks/useKeyboardNav.ts
index 9555354..0103ba5 100644
--- a/src/hooks/useKeyboardNav.ts
+++ b/src/hooks/useKeyboardNav.ts
@@ -1,10 +1,10 @@
-import { createSignal, createMemo } from "solid-js";
+import { createSignal, createMemo, createContext, useContext } from "solid-js";
import { useBindings } from "@opentui/keymap/solid";
import type { FocusableElement } from "../model/models";
export type { FocusableElement };
-export function useKeyboardNav() {
+export function createKeyboardNav() {
const [focusIndex, setFocusIndex] = createSignal(0);
const [elements, setElements] = createSignal([]);
const [isInputMode, setIsInputMode] = createSignal(false);
@@ -62,7 +62,7 @@ export function useKeyboardNav() {
const getFocusedId = () => focusedElement()?.id ?? null;
const clearElements = () => {
- setElements([]);
+ setElements((prev) => prev.filter((e) => e.persistent));
setFocusIndex(0);
};
@@ -70,11 +70,10 @@ export function useKeyboardNav() {
priority: 100,
bindings: isInputMode()
? [
- // Let escape bubble to the app-level double-esc handler in src/index.tsx
{
key: "escape",
cmd: () => setIsInputMode(false),
- preventDefault: false,
+ preventDefault: false, // Let escape bubble to the app-level double-esc handler in src/index.tsx
},
{
key: "tab",
@@ -114,3 +113,9 @@ export function useKeyboardNav() {
setIsInputMode,
};
}
+
+export const KeyboardNavContext = createContext>();
+
+export function useKeyboardNav() {
+ return useContext(KeyboardNavContext) ?? createKeyboardNav();
+}
diff --git a/src/model/models.ts b/src/model/models.ts
index 97174d9..4918dd3 100644
--- a/src/model/models.ts
+++ b/src/model/models.ts
@@ -9,6 +9,7 @@ export interface FocusableElement {
onEnter?: () => void;
canFocus?: () => boolean;
onMouseDown?: () => void;
+ persistent?: boolean; // keeps element registered across clearElements calls
}
export interface Status {
diff --git a/src/provider/keyboardNavProvider.tsx b/src/provider/keyboardNavProvider.tsx
new file mode 100644
index 0000000..24f2453
--- /dev/null
+++ b/src/provider/keyboardNavProvider.tsx
@@ -0,0 +1,9 @@
+import { type ParentComponent } from "solid-js";
+import { KeyboardNavContext, createKeyboardNav } from "../hooks/useKeyboardNav";
+
+// Provides a single keyboard-nav instance so descendant panels (the active tool
+// and the PDF preview) share one focus ring instead of competing rings.
+export const KeyboardNavProvider: ParentComponent = (props) => {
+ const nav = createKeyboardNav();
+ return {props.children};
+};