a1279a65ce
Builds the URL-clipping path of the "Send to Readest" feature: paste a
link, the renderer ingests the rendered page, and a self-contained EPUB
lands in the library. No server proxy, no external CDN refs left in the
EPUB once it's saved.
Architecture
- New Rust `clip_url` command spawns a hidden Tauri WebviewWindow at the
target URL with a real Chrome UA + WebKit fingerprint mask, so TLS-
fingerprint and JS-challenge walls (Cloudflare, Medium, X, WeChat MP)
resolve naturally instead of bouncing the server proxy.
- Capture transport is URL-payload navigation to a one-shot
127.0.0.1:RANDOM_PORT/clip/{token}?d={url-safe-base64} listener.
Top-level navigation isn't governed by CSP connect-src / form-action /
WebKit Private Network Access — the four earlier transports
(fetch, <form>, custom URI scheme, window.name) were each blocked by
one of those.
- Page-to-EPUB bundler (`assetBundler`) walks <img>/<picture> with
src → data-src → data-original → data-srcset → srcset fallback so lazy-
loading sites don't ship a 60px LQIP; fetches assets in parallel with a
per-asset timeout + per-asset/total caps; failed images degrade to alt-
text placeholders. A per-site rules table (seeded with WeChat MP) + a
selector fallback catches articles Readability misextracts. Builder
prepends the article <h1> + byline so the EPUB has a proper opening.
- Nested EPUB TOC built from h1–h6.
UI surfaces
- "From Web URL" entry in the library Import menu, gated to Tauri; web
build hides the URL field and points at the browser extension.
- `ImportFromUrlDialog` with auto-height (overrides Dialog's `sm:h-[65%]`
default) and a dim placeholder for the URL field.
- Clip webview window styled to match Readest's main window — macOS
decorations + overlay title bar; other desktops decorationless with a
drop shadow; native background + in-page loading overlay pick up the
caller's `themeCode.bg`/`fg` so light/dark/eink/custom themes all
render correctly. Title localised, all five overlay/title strings
translated across 33 locales.
Notes
- Gates the macOS traffic-light positioner to main/reader-* windows so
the decorationless clip window no longer null-derefs in
`position_traffic_lights`.
- Stricter validation across the path: schemes restricted to http/https,
hex-color parsing rejects malformed values, server endpoint returns
400 on missing/invalid base64.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
import clsx from 'clsx';
|
|
import { MdLink, MdRssFeed } from 'react-icons/md';
|
|
import { IoFileTray } from 'react-icons/io5';
|
|
import { useEnv } from '@/context/EnvContext';
|
|
import { useTranslation } from '@/hooks/useTranslation';
|
|
import MenuItem from '@/components/MenuItem';
|
|
import Menu from '@/components/Menu';
|
|
|
|
interface ImportMenuProps {
|
|
setIsDropdownOpen?: (open: boolean) => void;
|
|
onImportBooksFromFiles: () => void;
|
|
onImportBooksFromDirectory?: () => void;
|
|
onImportBookFromUrl?: () => void;
|
|
onOpenCatalogManager: () => void;
|
|
}
|
|
|
|
const ImportMenu: React.FC<ImportMenuProps> = ({
|
|
setIsDropdownOpen,
|
|
onImportBooksFromFiles,
|
|
onImportBooksFromDirectory,
|
|
onImportBookFromUrl,
|
|
onOpenCatalogManager,
|
|
}) => {
|
|
const _ = useTranslation();
|
|
const { appService } = useEnv();
|
|
|
|
const handleImportFromFiles = () => {
|
|
onImportBooksFromFiles();
|
|
setIsDropdownOpen?.(false);
|
|
};
|
|
|
|
const handleImportFromDirectory = () => {
|
|
onImportBooksFromDirectory?.();
|
|
setIsDropdownOpen?.(false);
|
|
};
|
|
|
|
const handleImportFromUrl = () => {
|
|
onImportBookFromUrl?.();
|
|
setIsDropdownOpen?.(false);
|
|
};
|
|
|
|
const handleOpenCatalogManager = () => {
|
|
onOpenCatalogManager();
|
|
setIsDropdownOpen?.(false);
|
|
};
|
|
|
|
return (
|
|
<Menu
|
|
className={clsx('dropdown-content bg-base-100 rounded-box !relative z-[1] mt-3 p-2 shadow')}
|
|
onCancel={() => setIsDropdownOpen?.(false)}
|
|
>
|
|
<MenuItem
|
|
label={_('From Local File')}
|
|
Icon={<IoFileTray className='h-5 w-5' />}
|
|
onClick={handleImportFromFiles}
|
|
/>
|
|
{onImportBooksFromDirectory && (
|
|
<MenuItem
|
|
label={_('From Directory')}
|
|
Icon={<IoFileTray className='h-5 w-5' />}
|
|
onClick={handleImportFromDirectory}
|
|
/>
|
|
)}
|
|
{onImportBookFromUrl && (
|
|
<MenuItem
|
|
label={_('From Web URL')}
|
|
Icon={<MdLink className='h-5 w-5' />}
|
|
onClick={handleImportFromUrl}
|
|
/>
|
|
)}
|
|
<MenuItem
|
|
label={appService?.isOnlineCatalogsAccessible ? _('Online Library') : _('OPDS Catalogs')}
|
|
Icon={<MdRssFeed className='h-5 w-5' />}
|
|
onClick={handleOpenCatalogManager}
|
|
/>
|
|
</Menu>
|
|
);
|
|
};
|
|
|
|
export default ImportMenu;
|