feat(plugin): ctx.dom.clipboard api

This commit is contained in:
5rahim
2026-04-07 10:56:55 +02:00
parent 224f5560f7
commit 9f8ac13789
8 changed files with 2226 additions and 1044 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1508,6 +1508,10 @@ declare namespace $ui {
onResize(cb: (size: { width: number, height: number }) => void): () => void
}
clipboard: {
write(text: string): void
}
}
interface Notification {

View File

@@ -76,6 +76,10 @@ func (d *DOMManager) BindToObj(vm *goja.Runtime, obj *goja.Object) {
_ = viewportObj.Set("getSize", d.jsViewportGetSize)
_ = domObj.Set("viewport", viewportObj)
clipboardObj := vm.NewObject()
_ = clipboardObj.Set("write", d.jsClipboardWrite)
_ = domObj.Set("clipboard", clipboardObj)
_ = obj.Set("dom", domObj)
}
@@ -1808,3 +1812,20 @@ func (d *DOMManager) jsViewportGetSize(_ goja.FunctionCall) goja.Value {
<-done
return d.ctx.vm.ToValue(payload)
}
/////////////////////////////////////////////
func (d *DOMManager) jsClipboardWrite(call goja.FunctionCall) goja.Value {
content, ok := call.Argument(0).Export().(string)
if !ok {
d.ctx.handleTypeError("clipboard: write requires a string")
return goja.Undefined()
}
d.ctx.SendEventToClient(ServerDOMClipboardWriteEvent, &ServerDOMClipboardWriteEventPayload{
Text: content,
})
return goja.Undefined()
}

View File

@@ -231,6 +231,8 @@ const (
ServerDOMManipulateEvent ServerEventType = "dom:manipulate" // When the server manipulates a DOM element
ServerDOMObserveInViewEvent ServerEventType = "dom:observe-in-view"
ServerDOMGetViewportSizeEvent ServerEventType = "dom:get-viewport-size"
ServerDOMClipboardWriteEvent ServerEventType = "dom:clipboard:write"
)
type ServerTrayUpdatedEventPayload struct {
@@ -370,6 +372,10 @@ type ServerCommandPaletteSetInputEventPayload struct {
type ServerScreenGetCurrentEventPayload struct{}
type ServerDOMClipboardWriteEventPayload struct {
Text string `json:"text"`
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func NewClientPluginEvent(data map[string]interface{}) *ClientPluginEvent {

View File

@@ -5414,7 +5414,6 @@ export type Chapter = {
* - Package: videofile
*/
export type MediaInfo = {
ready: any
sha: string
path: string
extension: string
@@ -5475,5 +5474,9 @@ export type Video = {
width: number
height: number
bitrate: number
pixFmt: string
colorSpace: string
colorTransfer: string
colorPrimaries: string
}

View File

@@ -82,6 +82,7 @@ export enum PluginServerEvents {
DOMManipulate = "dom:manipulate",
DOMObserveInView = "dom:observe-in-view",
DOMGetViewportSize = "dom:get-viewport-size",
DOMClipboardWrite = "dom:clipboard:write",
}
/////////////////////////////////////////////////////////////////////////////////////
@@ -1245,3 +1246,17 @@ export function usePluginListenDOMGetViewportSizeEvent(cb: (payload: Plugin_Serv
})
}
export type Plugin_Server_DOMClipboardWriteEventPayload = {
text: string
}
export function usePluginListenDOMClipboardWriteEvent(cb: (payload: Plugin_Server_DOMClipboardWriteEventPayload, extensionId: string) => void,
extensionID: string,
) {
return useWebsocketPluginMessageListener<Plugin_Server_DOMClipboardWriteEventPayload>({
extensionId: extensionID,
type: PluginServerEvents.DOMClipboardWrite,
onMessage: cb,
})
}

View File

@@ -1,12 +1,14 @@
import { useListExtensionData } from "@/api/hooks/extensions.hooks"
import { useIsMainTabRef } from "@/app/websocket-provider"
import { useDebounce } from "@/hooks/use-debounce"
import { copyToClipboard } from "@/lib/helpers/browser.ts"
import { usePathname, useRouter, useSearchParams } from "@/lib/navigation"
import { WSEvents } from "@/lib/server/ws-events"
import { startTransition, useEffect, useState } from "react"
import { useWindowSize } from "react-use"
import { useWebsocketMessageListener } from "../../_hooks/handle-websockets"
import { PluginCommandPalettes } from "./command/plugin-command-palettes"
import { usePluginListenDOMClipboardWriteEvent } from "./generated/plugin-events"
import {
usePluginListenDOMGetViewportSizeEvent,
usePluginListenScreenGetCurrentEvent,
@@ -69,6 +71,11 @@ export function PluginManager() {
usePluginListenScreenReloadEvent((event) => {
if (!isMainTabRef.current) return
router.refresh()
}, "") // Li
usePluginListenDOMClipboardWriteEvent((event) => {
if (!isMainTabRef.current) return
copyToClipboard(event.text)
}, "") // Listen to all plugins
return <>

View File

@@ -70,7 +70,6 @@ export function AdvancedSearchOptions() {
draft.sorting = [v] as any
return
})}
disabled={!!params.title && params.title.length > 0}
/>
</div>
<div