feat: paste clipboard link from new button

This commit is contained in:
wolfmanster 2026-08-20 12:43:13 +08:00
parent 4428393b13
commit c0ce31e42a
4 changed files with 34 additions and 3 deletions

View file

@ -1,5 +1,7 @@
package com.xmedia.archive.plugin
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.result.ActivityResult
@ -116,6 +118,18 @@ class LocalArchivePlugin : Plugin() {
}
}
@PluginMethod
fun readClipboard(call: PluginCall) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
val text = clipboard?.primaryClip
?.takeIf { it.itemCount > 0 }
?.getItemAt(0)
?.coerceToText(context)
?.toString()
.orEmpty()
call.resolve(JSObject().put("text", text))
}
@PluginMethod
fun openMedia(call: PluginCall) = launchMediaIntent(call, Intent.ACTION_VIEW)

View file

@ -27,7 +27,7 @@ import {
X,
Youtube,
} from 'lucide-react';
import { cancelJob, clearHistory, clearXSession, createJobs, getDownloadFolder, getXSessionStatus, listJobs, openMedia, retryJob, selectDownloadFolder, startXLogin, subscribeJobs, xLoginSupported } from './api';
import { cancelJob, clearHistory, clearXSession, createJobs, getDownloadFolder, getXSessionStatus, listJobs, openMedia, readClipboardText, retryJob, selectDownloadFolder, startXLogin, subscribeJobs, xLoginSupported } from './api';
import type { DownloadJob, JobStatus, MediaItem } from './types';
const STATUS: Record<JobStatus, { label: string; className: string }> = {
@ -284,8 +284,14 @@ export default function App() {
const completedTotal = jobs.filter((job) => job.status === 'completed').length;
function focusComposer() {
document.querySelector('.ingest-panel')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
window.setTimeout(() => document.querySelector<HTMLTextAreaElement>('.ingest-panel textarea')?.focus(), 350);
const panel = document.querySelector<HTMLElement>('.ingest-panel');
const textarea = panel?.querySelector<HTMLTextAreaElement>('textarea');
panel?.scrollIntoView({ behavior: 'smooth', block: 'center' });
textarea?.focus();
void readClipboardText().then((clipboardText) => {
const value = clipboardText.trim();
if (value) setInput((current) => current.trim() ? `${current.trim()}\n${value}` : value);
}).catch(() => undefined);
}
async function connectX() {

View file

@ -69,6 +69,16 @@ export function clearXSession() {
return Promise.resolve({ configured: false });
}
export async function readClipboardText() {
try {
if (native) return (await LocalArchive.readClipboard()).text;
if (!navigator.clipboard?.readText) return '';
return await navigator.clipboard.readText();
} catch {
return '';
}
}
export function openMedia(id: string) {
if (native) return LocalArchive.openMedia({ id });
window.open(`/api/jobs/media/${id}`, '_blank', 'noopener');

View file

@ -15,6 +15,7 @@ export interface LocalArchivePlugin {
getXSessionStatus(): Promise<{ configured: boolean }>;
startXLogin(): Promise<{ configured: boolean; canceled: boolean }>;
clearXSession(): Promise<{ configured: boolean }>;
readClipboard(): Promise<{ text: string }>;
openMedia(input: { id: string }): Promise<void>;
selectDownloadFolder(): Promise<{ selected: boolean; uri?: string }>;
getDownloadFolder(): Promise<{ selected: boolean }>;