diff --git a/src/App.tsx b/src/App.tsx
index a5a24d9..cff5e81 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -28,7 +28,15 @@ export function App() {
const audio = useAudio();
const toast = useToast();
const renderer = useRenderer();
- const { theme } = useTheme();
+ const themeContext = useTheme();
+ const theme = themeContext.theme;
+
+ // Create a reactive expression for background color
+ const backgroundColor = () => {
+ return themeContext.selected === "system"
+ ? "transparent"
+ : themeContext.theme.surface;
+ };
const keybind = useKeybinds();
const audioNav = useAudioNavStore();
@@ -62,11 +70,11 @@ export function App() {
useKeyboard(
(keyEvent) => {
+ const isCycle = keybind.match("cycle", keyEvent);
const isUp = keybind.match("up", keyEvent);
const isDown = keybind.match("down", keyEvent);
const isLeft = keybind.match("left", keyEvent);
const isRight = keybind.match("right", keyEvent);
- const isCycle = keybind.match("cycle", keyEvent);
const isDive = keybind.match("dive", keyEvent);
const isOut = keybind.match("out", keyEvent);
const isToggle = keybind.match("audio-toggle", keyEvent);
@@ -75,27 +83,31 @@ export function App() {
const isSeekForward = keybind.match("audio-seek-forward", keyEvent);
const isSeekBackward = keybind.match("audio-seek-backward", keyEvent);
const isQuit = keybind.match("quit", keyEvent);
- console.log({
- up: isUp,
- down: isDown,
- left: isLeft,
- right: isRight,
- cycle: isCycle,
- dive: isDive,
- out: isOut,
- audioToggle: isToggle,
- audioNext: isNext,
- audioPrev: isPrev,
- audioSeekForward: isSeekForward,
- audioSeekBackward: isSeekBackward,
- quit: isQuit,
- });
+ const isInverting = keybind.isInverting(keyEvent);
// only handling top navigation here, cycle through tabs, just to high priority(player) all else to be handled in each tab
if (nav.activeDepth == 0) {
- if (isCycle) {
+ if (
+ (isCycle && !isInverting) ||
+ (isDown && !isInverting) ||
+ (isUp && isInverting)
+ ) {
nav.nextTab();
+ return;
}
+ if (
+ (isCycle && isInverting) ||
+ (isDown && isInverting) ||
+ (isUp && !isInverting)
+ ) {
+ nav.prevTab();
+ return;
+ }
+ if ((isRight && !isInverting) || (isLeft && isInverting)) {
+ nav.setActiveDepth(1);
+ }
+ }
+ if (nav.activeDepth == 1) {
}
},
{ release: false },
@@ -117,7 +129,11 @@ export function App() {
flexDirection="column"
width="100%"
height="100%"
- backgroundColor={theme.surface}
+ backgroundColor={
+ themeContext.selected === "system"
+ ? "transparent"
+ : themeContext.theme.surface
+ }
>
{DEBUG && (
@@ -149,10 +165,7 @@ export function App() {
)}
-
+
{LayerGraph[nav.activeTab]()}
diff --git a/src/components/TabNavigation.tsx b/src/components/TabNavigation.tsx
index f3266a2..7244b3b 100644
--- a/src/components/TabNavigation.tsx
+++ b/src/components/TabNavigation.tsx
@@ -1,12 +1,8 @@
import { useTheme } from "@/context/ThemeContext";
-import { TABS } from "@/utils/navigation";
+import { TABS, TabsCount } from "@/utils/navigation";
import { For } from "solid-js";
import { SelectableBox, SelectableText } from "@/components/Selectable";
-
-interface TabNavigationProps {
- activeTab: TABS;
- onTabSelect: (tab: TABS) => void;
-}
+import { useNavigation } from "@/context/NavigationContext";
export const tabs: TabDefinition[] = [
{ id: TABS.FEED, label: "Feed" },
@@ -17,32 +13,36 @@ export const tabs: TabDefinition[] = [
{ id: TABS.SETTINGS, label: "Settings" },
];
-export function TabNavigation(props: TabNavigationProps) {
+export function TabNavigation() {
const { theme } = useTheme();
+ const { activeTab, setActiveTab, activeDepth } = useNavigation();
return (
{(tab) => (
- tab.id == props.activeTab}
- onMouseDown={() => props.onTabSelect(tab.id)}
- >
- tab.id == props.activeTab}
- primary
- alignSelf="center"
- >
- {tab.label}
-
-
+ tab.id == activeTab}
+ onMouseDown={() => setActiveTab(tab.id)}
+ >
+ tab.id == activeTab}
+ primary
+ alignSelf="center"
+ >
+ {tab.label}
+
+
)}
diff --git a/src/config/keybind.jsonc b/src/config/keybind.jsonc
index 6e85465..f607a97 100644
--- a/src/config/keybind.jsonc
+++ b/src/config/keybind.jsonc
@@ -6,10 +6,10 @@
"cycle": ["tab"], // this will cycle no matter the depth/orientation
"dive": ["return"],
"out": ["esc"],
- "inverse": ["shift"],
+ "inverseModifier": ["shift"],
"leader": ":", // will not trigger while focused on input
"quit": ["q"],
- "refresh": ["r"],
+ "refresh": ["r"],
"audio-toggle": ["p"],
"audio-pause": [],
"audio-play": [],
diff --git a/src/context/KeybindContext.tsx b/src/context/KeybindContext.tsx
index 248645f..5f2c781 100644
--- a/src/context/KeybindContext.tsx
+++ b/src/context/KeybindContext.tsx
@@ -15,7 +15,7 @@ export type KeybindsResolved = {
cycle: string[]; // this will cycle no matter the depth/orientation
dive: string[];
out: string[];
- inverse: string[];
+ inverseModifier: string;
leader: string; // will not trigger while focused on input
quit: string[];
"audio-toggle": string[];
@@ -57,7 +57,7 @@ export const { use: useKeybinds, provider: KeybindProvider } =
cycle: [],
dive: [],
out: [],
- inverse: [],
+ inverseModifier: "",
leader: "",
quit: [],
refresh: [],
@@ -100,6 +100,18 @@ export const { use: useKeybinds, provider: KeybindProvider } =
return false;
}
+ function isInverting(evt: {
+ name: string;
+ ctrl?: boolean;
+ meta?: boolean;
+ shift?: boolean;
+ }) {
+ if (store.inverseModifier === "ctrl" && evt.ctrl) return true;
+ if (store.inverseModifier === "meta" && evt.meta) return true;
+ if (store.inverseModifier === "shift" && evt.shift) return true;
+ return false;
+ }
+
// Load on mount
onMount(() => {
load().catch(() => {});
@@ -115,6 +127,7 @@ export const { use: useKeybinds, provider: KeybindProvider } =
save,
print,
match,
+ isInverting,
};
},
});
diff --git a/src/pages/Feed/FeedPage.tsx b/src/pages/Feed/FeedPage.tsx
index a0182ec..98c2b76 100644
--- a/src/pages/Feed/FeedPage.tsx
+++ b/src/pages/Feed/FeedPage.tsx
@@ -12,6 +12,7 @@ import { useTheme } from "@/context/ThemeContext";
import { SelectableBox, SelectableText } from "@/components/Selectable";
import { useNavigation } from "@/context/NavigationContext";
import { LoadingIndicator } from "@/components/LoadingIndicator";
+import { TABS } from "@/utils/navigation";
enum FeedPaneType {
FEED = 1,
@@ -23,27 +24,21 @@ const ITEMS_PER_BATCH = 50;
export function FeedPage() {
const feedStore = useFeedStore();
- const [isRefreshing, setIsRefreshing] = createSignal(false);
- const [loadedEpisodesCount, setLoadedEpisodesCount] =
- createSignal(ITEMS_PER_BATCH);
const nav = useNavigation();
-
+ const { theme } = useTheme();
+ const [selectedEpisodeID, setSelectedEpisodeID] = createSignal<
+ string | undefined
+ >();
const allEpisodes = () => feedStore.getAllEpisodesChronological();
- const paginatedEpisodes = () => {
- const episodes = allEpisodes();
- return episodes.slice(0, loadedEpisodesCount());
- };
-
const formatDate = (date: Date): string => {
return format(date, "MMM d, yyyy");
};
const groupEpisodesByDate = () => {
const groups: Record> = {};
- const episodes = paginatedEpisodes();
- for (const item of episodes) {
+ for (const item of allEpisodes()) {
const dateKey = formatDate(new Date(item.episode.pubDate));
if (!groups[dateKey]) {
groups[dateKey] = [];
@@ -51,14 +46,13 @@ export function FeedPage() {
groups[dateKey].push(item);
}
- return Object.entries(groups)
- .sort(([a, _aItems], [b, _bItems]) => {
- // Convert date strings back to Date objects for proper chronological sorting
- const dateA = new Date(a);
- const dateB = new Date(b);
- // Sort in descending order (newest first)
- return dateB.getTime() - dateA.getTime();
- });
+ return Object.entries(groups).sort(([a, _aItems], [b, _bItems]) => {
+ // Convert date strings back to Date objects for proper chronological sorting
+ const dateA = new Date(a);
+ const dateB = new Date(b);
+ // Sort in descending order (newest first)
+ return dateB.getTime() - dateA.getTime();
+ });
};
const formatDuration = (seconds: number): string => {
@@ -68,7 +62,6 @@ export function FeedPage() {
return `${mins}m`;
};
- const { theme } = useTheme();
return (
- {/* Status line */}
-
- Refreshing feeds...
-
-
0}
fallback={
@@ -99,42 +87,49 @@ export function FeedPage() {
{date}
- {(item) => (
- false}
- flexDirection="column"
- gap={0}
- paddingLeft={1}
- paddingRight={1}
- paddingTop={0}
- paddingBottom={0}
- onMouseDown={() => {
- // Selection is handled by App's keyboard navigation
- }}
- >
- false} primary>
- {item.episode.title}
-
-
- false} primary>
- {item.feed.podcast.title}
+ {(item) => {
+ const isSelected = () => {
+ if (
+ nav.activeTab == TABS.FEED &&
+ nav.activeDepth == FeedPaneType.FEED &&
+ selectedEpisodeID() &&
+ selectedEpisodeID() === item.episode.id
+ ) {
+ return true;
+ }
+ return false;
+ };
+ return (
+ {
+ // Selection is handled by App's keyboard navigation
+ }}
+ >
+
+ {item.episode.title}
- false} tertiary>
- {formatDuration(item.episode.duration)}
-
-
-
- )}
+
+
+ {item.feed.podcast.title}
+
+
+ {formatDuration(item.episode.duration)}
+
+
+
+ );
+ }}
)}
- {/* Loading indicator */}
-
-
-
-
-
diff --git a/src/utils/keybinds-persistence.ts b/src/utils/keybinds-persistence.ts
index 378597b..f30f2f2 100644
--- a/src/utils/keybinds-persistence.ts
+++ b/src/utils/keybinds-persistence.ts
@@ -28,7 +28,7 @@ const DEFAULT_KEYBINDS: KeybindsResolved = {
cycle: ["tab"],
dive: ["return"],
out: ["esc"],
- inverse: ["shift"],
+ inverseModifier: "shift",
leader: ":",
quit: ["q"],
"audio-toggle": ["p"],