Compare commits

...

2 Commits

Author SHA1 Message Date
db74e20571 better copying 2026-02-06 16:51:00 -05:00
70f50eec2a covert html on fetch instead 2026-02-06 16:45:34 -05:00
4 changed files with 17 additions and 17 deletions

View File

@@ -20,7 +20,8 @@ import { useMultimediaKeys } from "@/hooks/useMultimediaKeys";
import { FeedVisibility } from "@/types/feed"; import { FeedVisibility } from "@/types/feed";
import { useAppKeyboard } from "@/hooks/useAppKeyboard"; import { useAppKeyboard } from "@/hooks/useAppKeyboard";
import { Clipboard } from "@/utils/clipboard"; import { Clipboard } from "@/utils/clipboard";
import { emit } from "@/utils/event-bus"; import { useToast } from "@/ui/toast";
import { useRenderer } from "@opentui/solid";
import type { TabId } from "@/components/Tab"; import type { TabId } from "@/components/Tab";
import type { AuthScreen } from "@/types/auth"; import type { AuthScreen } from "@/types/auth";
import type { Episode } from "@/types/episode"; import type { Episode } from "@/types/episode";
@@ -35,6 +36,8 @@ export function App() {
const feedStore = useFeedStore(); const feedStore = useFeedStore();
const appStore = useAppStore(); const appStore = useAppStore();
const audio = useAudio(); const audio = useAudio();
const toast = useToast();
const renderer = useRenderer();
// Global multimedia key handling — active when Player tab is NOT // Global multimedia key handling — active when Player tab is NOT
// focused (Player.tsx handles its own keys when focused). // focused (Player.tsx handles its own keys when focused).
@@ -105,13 +108,12 @@ export function App() {
Clipboard.copy(text) Clipboard.copy(text)
.then(() => { .then(() => {
emit("toast.show", { toast.show({ message: "Copied to Clipboard!", variant: "info" });
message: "Copied to clipboard",
variant: "info",
duration: 1500,
});
}) })
.catch(() => {}); .catch(toast.error)
.finally(() => {
renderer.clearSelection();
});
}); });
const getPanels = createMemo(() => { const getPanels = createMemo(() => {

View File

@@ -25,10 +25,10 @@ const decodeEntities = (value: string) =>
.replace(/'/g, "'") .replace(/'/g, "'")
/** /**
* Clean a description field: detect HTML vs plain text, and convert * Clean a field (description or title): detect HTML vs plain text, and convert
* HTML to readable plain text. Plain text just gets entity decoding. * HTML to readable plain text. Plain text just gets entity decoding.
*/ */
const cleanDescription = (raw: string): string => { const cleanField = (raw: string): string => {
if (!raw) return "" if (!raw) return ""
const decoded = decodeEntities(raw) const decoded = decodeEntities(raw)
const type = detectContentType(decoded) const type = detectContentType(decoded)
@@ -76,15 +76,15 @@ const parseEpisodeType = (raw: string): EpisodeType | undefined => {
export const parseRSSFeed = (xml: string, feedUrl: string): Podcast & { episodes: Episode[] } => { export const parseRSSFeed = (xml: string, feedUrl: string): Podcast & { episodes: Episode[] } => {
const channel = xml.match(/<channel[\s\S]*?<\/channel>/i)?.[0] ?? xml const channel = xml.match(/<channel[\s\S]*?<\/channel>/i)?.[0] ?? xml
const title = decodeEntities(getTagValue(channel, "title")) || "Untitled Podcast" const title = cleanField(getTagValue(channel, "title")) || "Untitled Podcast"
const description = cleanDescription(getTagValue(channel, "description")) const description = cleanField(getTagValue(channel, "description"))
const author = decodeEntities(getTagValue(channel, "itunes:author")) const author = decodeEntities(getTagValue(channel, "itunes:author"))
const lastUpdated = new Date() const lastUpdated = new Date()
const items = channel.match(/<item[\s\S]*?<\/item>/gi) ?? [] const items = channel.match(/<item[\s\S]*?<\/item>/gi) ?? []
const episodes = items.map((item, index) => { const episodes = items.map((item, index) => {
const epTitle = decodeEntities(getTagValue(item, "title")) || `Episode ${index + 1}` const epTitle = cleanField(getTagValue(item, "title")) || `Episode ${index + 1}`
const epDescription = cleanDescription(getTagValue(item, "description")) const epDescription = cleanField(getTagValue(item, "description"))
const pubDate = new Date(getTagValue(item, "pubDate") || Date.now()) const pubDate = new Date(getTagValue(item, "pubDate") || Date.now())
// Audio URL + file size + MIME type from <enclosure> // Audio URL + file size + MIME type from <enclosure>

View File

@@ -5,7 +5,6 @@
import type { Feed, FeedVisibility } from "@/types/feed"; import type { Feed, FeedVisibility } from "@/types/feed";
import { format } from "date-fns"; import { format } from "date-fns";
import { htmlToText } from "@/utils/html-to-text";
import { useTheme } from "@/context/ThemeContext"; import { useTheme } from "@/context/ThemeContext";
interface FeedItemProps { interface FeedItemProps {
@@ -55,7 +54,7 @@ export function FeedItem(props: FeedItemProps) {
</text> </text>
<text fg={visibilityColor()}>{visibilityIcon()}</text> <text fg={visibilityColor()}>{visibilityIcon()}</text>
<text fg={props.isSelected ? "white" : theme.accent}> <text fg={props.isSelected ? "white" : theme.accent}>
{htmlToText(props.feed.customName || props.feed.podcast.title)} {props.feed.customName || props.feed.podcast.title}
</text> </text>
{props.showEpisodeCount && <text fg="gray">({episodeCount()})</text>} {props.showEpisodeCount && <text fg="gray">({episodeCount()})</text>}
</box> </box>
@@ -81,7 +80,7 @@ export function FeedItem(props: FeedItemProps) {
<text fg="yellow">{pinnedIndicator()}</text> <text fg="yellow">{pinnedIndicator()}</text>
<text fg={props.isSelected ? "white" : theme.text}> <text fg={props.isSelected ? "white" : theme.text}>
<strong> <strong>
{htmlToText(props.feed.customName || props.feed.podcast.title)} {props.feed.customName || props.feed.podcast.title}
</strong> </strong>
</text> </text>
</box> </box>

View File

@@ -10,7 +10,6 @@ import { format } from "date-fns";
import type { Episode } from "@/types/episode"; import type { Episode } from "@/types/episode";
import type { Feed } from "@/types/feed"; import type { Feed } from "@/types/feed";
import { useTheme } from "@/context/ThemeContext"; import { useTheme } from "@/context/ThemeContext";
import { htmlToText } from "@/utils/html-to-text";
type FeedPageProps = { type FeedPageProps = {
focused: boolean; focused: boolean;