@@ -10,8 +10,10 @@ import {
1010 Tag ,
1111 Tooltip ,
1212 Typography ,
13+ Popover ,
1314} from "@arco-design/web-react" ;
1415import { IconDown } from "@arco-design/web-react/icon" ;
16+ import { v4 as uuidv4 } from "uuid" ;
1517import CodeEditor from "../components/CodeEditor" ;
1618import { useCallback , useEffect , useMemo , useState } from "react" ;
1719import type { Metadata , Script } from "@App/app/repo/scripts" ;
@@ -20,7 +22,7 @@ import type { Subscribe } from "@App/app/repo/subscribe";
2022import { i18nDescription , i18nName } from "@App/locales/locales" ;
2123import { useTranslation } from "react-i18next" ;
2224import type { ScriptInfo } from "@App/pkg/utils/script" ;
23- import { prepareScriptByCode , prepareSubscribeByCode } from "@App/pkg/utils/script" ;
25+ import { prepareScriptByCode , prepareSubscribeByCode , scriptInfoByCode } from "@App/pkg/utils/script" ;
2426import { nextTime } from "@App/pkg/utils/cron" ;
2527import { scriptClient , subscribeClient } from "../store/features/script" ;
2628
@@ -45,20 +47,34 @@ const useScriptInstall = () => {
4547 const [ diffCode , setDiffCode ] = useState < string > ( ) ;
4648 const [ oldScript , setOldScript ] = useState < Script | Subscribe > ( ) ;
4749 const [ isUpdate , setIsUpdate ] = useState < boolean > ( false ) ;
50+ const [ localFile , setLocalFile ] = useState < File | null > ( null ) ;
51+ const [ localFileHandle , setLocalFileHandle ] = useState < FileSystemFileHandle | null > ( null ) ;
4852 const { t } = useTranslation ( ) ;
4953
5054 useEffect ( ( ) => {
51- const url = new URL ( window . location . href ) ;
52- const uuid = url . searchParams . get ( "uuid" ) ;
53- if ( ! uuid ) {
54- return ;
55- }
56-
57- const loadScriptInfo = async ( ) => {
55+ const handle = async ( ) => {
5856 try {
59- const info : ScriptInfo = await scriptClient . getInstallInfo ( uuid ) ;
60- if ( ! info ) {
61- throw new Error ( "fetch script info failed" ) ;
57+ const url = new URL ( window . location . href ) ;
58+ const uuid = url . searchParams . get ( "uuid" ) ;
59+ let info : ScriptInfo | undefined ;
60+ if ( ! uuid ) {
61+ // 检查是不是本地文件安装
62+ const local = url . searchParams . get ( "local" ) === "true" ;
63+ if ( ! local || ! window . localFile ) {
64+ return ;
65+ }
66+ // 处理本地文件的安装流程
67+ // 处理成info对象
68+ const file = window . localFile ;
69+ setLocalFile ( file ) ;
70+ setLocalFileHandle ( window . localFileHandle ! ) ;
71+ const code = await file . text ( ) ;
72+ info = scriptInfoByCode ( code , "file:///*from-local*/" + file . name , "user" , false , uuidv4 ( ) ) ;
73+ } else {
74+ info = await scriptClient . getInstallInfo ( uuid ) ;
75+ if ( ! info ) {
76+ throw new Error ( "fetch script info failed" ) ;
77+ }
6278 }
6379
6480 let prepare :
@@ -96,8 +112,7 @@ const useScriptInstall = () => {
96112 Message . error ( t ( "script_info_load_failed" ) + " " + e . message ) ;
97113 }
98114 } ;
99-
100- loadScriptInfo ( ) ;
115+ handle ( ) ;
101116 } , [ t ] ) ;
102117
103118 return {
@@ -108,6 +123,8 @@ const useScriptInstall = () => {
108123 diffCode,
109124 oldScript,
110125 isUpdate,
126+ localFile,
127+ localFileHandle,
111128 } ;
112129} ;
113130
@@ -226,9 +243,13 @@ const useAntiFeatures = () => {
226243function App ( ) {
227244 const [ enable , setEnable ] = useState < boolean > ( false ) ;
228245 const [ btnText , setBtnText ] = useState < string > ( "" ) ;
246+ const [ scriptCode , setScriptCode ] = useState < string > ( "" ) ;
229247 const { t } = useTranslation ( ) ;
230248
231- const { scriptInfo, upsertScript, setUpsertScript, code, diffCode, oldScript, isUpdate } = useScriptInstall ( ) ;
249+ const { scriptInfo, upsertScript, setUpsertScript, code, diffCode, oldScript, isUpdate, localFile, localFileHandle } =
250+ useScriptInstall ( ) ;
251+
252+ const [ watchFile , setWatchFile ] = useState ( false ) ;
232253
233254 const metadata : Metadata = scriptInfo ?. metadata || { } ;
234255 const permissions = usePermissions ( scriptInfo , metadata ) ;
@@ -242,7 +263,7 @@ function App() {
242263 } else {
243264 setBtnText ( isUpdate ? t ( "update_script" ) ! : t ( "install_script" ) ) ;
244265 }
245-
266+ setScriptCode ( code || "" ) ;
246267 if ( upsertScript ) {
247268 document . title = `${ ! isUpdate ? t ( "install_script" ) : t ( "update_script" ) } - ${ i18nName ( upsertScript ) } - ScriptCat` ;
248269 }
@@ -324,12 +345,34 @@ function App() {
324345 [ scriptInfo ]
325346 ) ;
326347
348+ useEffect ( ( ) => {
349+ if ( ! watchFile ) {
350+ return ;
351+ }
352+ if ( ! upsertScript ) {
353+ return ;
354+ }
355+ // @ts -ignore
356+ const observer = new FileSystemObserver ( async ( records ) => {
357+ // 调用安装
358+ const code = await ( await records [ 0 ] . root . getFile ( ) ) . text ( ) ;
359+ setScriptCode ( code ) ;
360+ scriptClient . install ( upsertScript as Script , code ) . catch ( ( e ) => {
361+ Message . error ( t ( "install_failed" ) + ": " + e ) ;
362+ } ) ;
363+ } ) ;
364+ observer . observe ( localFileHandle ) ;
365+ return ( ) => {
366+ observer . disconnect ( ) ;
367+ } ;
368+ } , [ watchFile ] ) ;
369+
327370 return (
328371 < div className = "h-full" >
329372 < div className = "h-full" >
330373 < Grid . Row className = "mb-2" gutter = { 8 } >
331374 < Grid . Col flex = { 1 } className = "flex-col p-8px" >
332- < Space direction = "vertical" >
375+ < Space direction = "vertical" className = "w-full" >
333376 < div >
334377 { upsertScript ?. metadata . icon && (
335378 < Avatar size = { 32 } shape = "square" style = { { marginRight : "8px" } } >
@@ -391,6 +434,19 @@ function App() {
391434 < Button type = "primary" size = "small" icon = { < IconDown /> } />
392435 </ Dropdown >
393436 </ Button . Group >
437+ { localFile && (
438+ < Popover content = { t ( "watch_file_description" ) } >
439+ < Button
440+ type = "secondary"
441+ size = "small"
442+ onClick = { ( ) => {
443+ setWatchFile ( ! watchFile ) ;
444+ } }
445+ >
446+ { watchFile ? t ( "stop_watch_file" ) : t ( "watch_file" ) }
447+ </ Button >
448+ </ Popover >
449+ ) }
394450 { isUpdate ? (
395451 < Button . Group >
396452 < Button type = "primary" status = "danger" size = "small" onClick = { ( ) => handleClose ( ) } >
@@ -498,7 +554,7 @@ function App() {
498554 </ Grid . Row >
499555 </ Grid . Col >
500556 </ Grid . Row >
501- < CodeEditor id = "show-code" code = { code || undefined } diffCode = { diffCode || "" } />
557+ < CodeEditor id = "show-code" code = { scriptCode || undefined } diffCode = { diffCode || "" } />
502558 </ div >
503559 </ div >
504560 ) ;
0 commit comments