From 322807c68d6fd169696094f71ef714f06a7c79a2 Mon Sep 17 00:00:00 2001 From: mac Date: Wed, 15 Jul 2026 18:01:56 +0800 Subject: [PATCH] feat: add desktop app hot update channel --- .gitignore | 15 + AGENTS.md | 36 + CANVAS_SPEC.md | 154 + CONTRACT.md | 103 + HANDOFF.md | 67 + README.md | 82 +- app.js | 1438 + build/_oldicons/icon.icns | Bin 0 -> 129390 bytes build/_oldicons/icon.ico | Bin 0 -> 19719 bytes build/_oldicons/icon.png | Bin 0 -> 39211 bytes build/fix-quarantine.command | 49 + build/icon.icns | Bin 0 -> 2235016 bytes build/icon.ico | Bin 0 -> 125656 bytes build/icon.png | Bin 0 -> 329385 bytes electron-builder.yml | 104 + electron/bootstrap.js | 54 + electron/lib/canvas.js | 569 + electron/lib/codex.js | 368 + electron/lib/hotstate.js | 113 + electron/lib/hotupdate.js | 137 + electron/lib/memory.js | 873 + electron/lib/provider_sync.js | 194 + electron/lib/proxy.js | 673 + electron/lib/purge.js | 176 + electron/lib/router.js | 884 + electron/lib/secrets.js | 66 + electron/lib/sessions.js | 234 + electron/lib/translate.js | 354 + electron/lib/zip.js | 50 + electron/main.js | 1446 + electron/preload.js | 122 + package-lock.json | 4579 + package.json | 25 + renderer/app.js | 2798 + renderer/catalog.json | 275655 ++++++++++++++++++++++++++++ renderer/index.html | 486 + renderer/logo.png | Bin 0 -> 14923 bytes renderer/plugins.json | 1 + renderer/styles.css | 653 + renderer/vendor/drawflow.min.css | 1 + renderer/vendor/drawflow.min.js | 1 + resources/singbox/README.md | 17 + scripts/afterPack.js | 26 + scripts/fetch-singbox.sh | 29 + scripts/publish-hot.js | 124 + 45 files changed, 292746 insertions(+), 10 deletions(-) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CANVAS_SPEC.md create mode 100644 CONTRACT.md create mode 100644 HANDOFF.md create mode 100644 app.js create mode 100644 build/_oldicons/icon.icns create mode 100644 build/_oldicons/icon.ico create mode 100644 build/_oldicons/icon.png create mode 100755 build/fix-quarantine.command create mode 100644 build/icon.icns create mode 100644 build/icon.ico create mode 100644 build/icon.png create mode 100644 electron-builder.yml create mode 100644 electron/bootstrap.js create mode 100644 electron/lib/canvas.js create mode 100644 electron/lib/codex.js create mode 100644 electron/lib/hotstate.js create mode 100644 electron/lib/hotupdate.js create mode 100644 electron/lib/memory.js create mode 100644 electron/lib/provider_sync.js create mode 100644 electron/lib/proxy.js create mode 100644 electron/lib/purge.js create mode 100644 electron/lib/router.js create mode 100644 electron/lib/secrets.js create mode 100644 electron/lib/sessions.js create mode 100644 electron/lib/translate.js create mode 100644 electron/lib/zip.js create mode 100644 electron/main.js create mode 100644 electron/preload.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 renderer/app.js create mode 100644 renderer/catalog.json create mode 100644 renderer/index.html create mode 100644 renderer/logo.png create mode 100644 renderer/plugins.json create mode 100644 renderer/styles.css create mode 100644 renderer/vendor/drawflow.min.css create mode 100644 renderer/vendor/drawflow.min.js create mode 100644 resources/singbox/README.md create mode 100644 scripts/afterPack.js create mode 100644 scripts/fetch-singbox.sh create mode 100644 scripts/publish-hot.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c171442 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# 依赖 / 构建产物 +node_modules/ +dist/ +dist-hot/ +output/ +.claude/ + +# sing-box 引擎二进制(太大,用 scripts/fetch-singbox.sh 拉取) +resources/singbox/*/sing-box +resources/singbox/*/sing-box.exe + +# 杂项 +.DS_Store +*.log +*.map diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b2b9a50 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,36 @@ + +## 项目记忆(Codex 插件市场自动维护 · 回答前据此 · 勿手动编辑本段) +**当前状态**:用户在询问 Windows 下 Codex 内容存储路径后,提供启动报错截图并问原因;当前需要检查截图所指的 .codex 数据库及备份文件状态,判断是自动修复后的提示还是会反复损坏。 +**已定决策**: +- 全量会话读取包含归档和 subagent +- 媒体预览保留dataUrl和原始url +- Grok多图参考不支持 +- Grok作为新增节点,不改Seedance +- 导入后保持当前全量/默认模式 +- 同步目标为当前账号 +- 默认刷新仍保持轻量列表 +- 密钥不写入项目源码 +- 使用 /v1/videos/{id} 查询 +- 使用 /v1/videos/generations 创建 +- 默认模型为 grok-imagine-video +- 需按截图路径检查数据库状态 +**下一步**: +- 查看截图中的具体报错路径 +- 检查 .codex 数据库和备份文件 +- 判断是否需恢复或清理损坏文件 +**关键文件**:/var/folders/xk/qrv10_rx12jg91fn322xlwr00000gn/T/codex-clipboard-41e58e05-0211-4045-8cc5-1084d4f7f471.png、%USERPROFILE%\.codex、%USERPROFILE%\.codex\sessions、%USERPROFILE%\.codex\archived_sessions、%USERPROFILE%\.codex\history.jsonl、%USERPROFILE%\.codex\config.toml、%USERPROFILE%\.codex\auth.json +**历史话题(需其中细节时用下面命令检索)**: +- 2026-07-15 工具箱新增Grok视频与全量会话同步 +- 2026-07-15 生成魔兽世界宣传海报 +- 2026-07-15 Codex skill软链改造 +- 2026-07-14 列出本地 skills +- 2026-07-14 Claude CLI 中转配置排查 +- 2026-07-14 3x-ui订阅分流修复 +- 2026-07-14 图片生成与规格调整 +- 2026-07-14 生成蝴蝶图片并保存 +- 2026-07-13 项目与画布功能分析 +- 2026-07-13 网站安装数据库连接失败 +- 2026-07-12 服务器BBR已开启 +- 2026-07-12 服务器服务与域名配置交接 +> 回答涉及本段未列出的历史细节前,先运行 `rg -n "关键词" /Users/mac/.codex/memory/31c67c70b625.md` 查证再答。 + diff --git a/CANVAS_SPEC.md b/CANVAS_SPEC.md new file mode 100644 index 0000000..516e15d --- /dev/null +++ b/CANVAS_SPEC.md @@ -0,0 +1,154 @@ +# 创意画布(Canvas Studio)集成契约 —— 冻结版 + +主控已勘定的集成事实,技术员必须严格遵守。目标:在插件市场旁新增「创意画布」, +Drawflow 卡片式节点画布,第一版跑通 **文生图 → 图生视频 → TTS配音** 链路, +全部走 tokendun 中转,**key 只在主进程**。 + +## 已知事实(不要重复调研) +- Electron 应用,**纯原生 JS,无 React/Vue,无构建步骤**:`renderer/index.html` 直接 ` + + + diff --git a/renderer/logo.png b/renderer/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fc00940079603d39123df33f10bbcc51126d0bda GIT binary patch literal 14923 zcmV-RI<&=!P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91V4wp41ONa40RR91U;qFB0I4%yP5=NowMj%lRCoc6od?)tMVas4)BWWz z5oU&*B*`FIC73|Wg1QD=amB=f_c0;ry87JpUIi1Ym=W_{TwO%)fki<@kQG)zK#+j} zhb&BD;+I2r-~aEedQYEjX5Fp%&Z(+5*IV(_snb)~|1h*RJhZ}!=25m}w*wv;>wDV+ z+im4NTg|Rp-?SUom~ERVtSDMmNM5VpP9km&RYoze5+2Efcdks@avFy?gQSbNrN1C> zX-QP5sm7!Uq0i+hA0h)s9Kr1tVN(Duc&hZ!35IP87PRaI2bw*9d0|iAsjvf=*xh@L z&V1J|dq*6qczebzz#--T}RPoZzkO~23@vuCfHA35%-2a6R~KWg^-znHBXmxbBL zqAoi-Bvmq4>)8w=uRnrEegl_)o+6pDkR|?ZSgw!`I%qRrsTLR^^E+BM>?NJ8D*1vI zeh8vY$a=_dXKV=45J2T$zMyW;-plM2`|CQewQfcK#7$P~y{PCN_*LfIVN~kx#@YekN>8jeV{cdyL=-Jn_?2l`O>XFl3Io4D{SGFv4H{1~dW_amr zi3%f17yv4Y;5rpozGq<N0S~feTZt||8r{@x3Iszg(LaWhuuhs0ri|?x0`>$)--0Co^caGA(N-Fd(PBbMA! zH2J_m7oiFde90uDfK}25r@ER)5Ti&8$h07Md=?jYJKyjP15D!(-gyI8}~JCB&2 zoNd&O?(grv7nHmB;~#I;`dV{eymjlq(0euP6dLT)cn^t!OG(L&J63^Hl#J-Wc4jaR z+8te;lx-T|2+qRcFJ)ADLS}`wLLPZ+u3O-`;~?vXXYnpOIF}&QmrV)cjLXS(Np}cHo!!7&9A2jkbrr!g;)*4&4Y(sP5v7f&s5hQOZUA3f=Mve=s zzy!*n3kY#{aG2;IEy>G72X*T9!sSABAlu9WR~11I2$?wzV~|7)EJ76qTLhPrGa)u` zWt>+&%DZ2>WR+Otg41&TpRaG(#<_*Zw#L>htJ(C#F;_oaEPHrU*U(cHR4U5}j0xiu zC;u=8W~RUeIm)Y72OS7Ph1avWH31Kj3Ry`HjF8#e2?3H}ZBU}z@jduM7x_qM@sSO& z2@ljaO4{Q;8bm|`N;5+e=0jMmWe=~Lx2x~7Wj0qFQ?o_89CYQQ$Pf{9TplF2Obd{; zqpCC{>CV&;R{+XkXs1jk&2%Zv{*e=fu2Ogcci0d#Rk@`%GClK1;FT^MiBe$+ohD0R7U<%b=_iRv4Wmd8}|GawI_m|5f)Z9$kKL&m-dlXWaaVDAR0^ zxDgcAc{+gu86d9;3C5_v5u+pPbBoJ#FlN~$AMg^FBt0b$xSK}K?vNdL2@Z$30WhY& zwyn@|VT9sji|)X3K(@;vpW&e|(PuEYynygIK+S|upr~YK(bFEhmKIOp-mIWfG7?R zfvOs=d`n9>n{8RW-n3^dH+#BYfJQ-Mx@Tj< zdVaB9;v5cHsVWhNcU@zq6PZ(TBP6zz&emO*@S8i_FuO5&bdUy}rB@Qsi zc6Qyo&ay1f(Wpriy6ZGSVN5BHA(G=;?6u4JCu@)WQzzH#sNE1O7-(H7q zT_1U_*~d?;S)W=kJB+ZWfbp`YRFP9g?e^={Gxpf#hTd7wH(NHU$@YNdJ@(SWhU`tp zFR=Yq^xJ%+p~`F7j3)8Hp2EI!T92(CpS7DGae$1*2wiR`cI0H(FS#*7By2)($FZLC zOyaA`ae8P{0bG@nR+w@RfV64^T+W_u7?(qq)qyY3xe%@-=sgx9loFQ&6$W5(eoL+S zMNezlzyDp`dex9q%g6aOTdUUok3ZUEKlsl{n`@{^5A>y`jS~??<;|*@&&=9RBP~1k zWee^7FI%LB-m+O%Q40ONX7_D0d+t|e?a3J_plgVq9bgY`XG)en$S7|K!sCMgWy*Gu z06^e>x<$x5K)lX9p>#O}Iia8#LAF$cwu}n0GR~HGgk&)xg7B1i3Pd0iMFIfJL(ho3 zc+**Rd&UYaE%Q*okaO3gvv%57*4w>nrDJ5k3Ore_Yndr0;=rU@avE05H>@>1Yc@7% zuQ__azJ2D-Hr%hbgbYd2H9SzW3w}LsAJq1+4KWm{5~Rb+;C6+P2nCSyis1JUabzMd zmUg*e$n-f%I@&&x^%bFD6Gb8qeqt9SWJe4$q-pT+u0}pz9#!%k#sH^z)9Te|TUz7W zMAP2*49m`kAkCEzte>-2eR-qZyGbp6$%qvrgKGT)YR>X%Z=V%?s*FCxs+3|tut8PO zf?>0r7TV=^%-h?)y~P?@y+qNbW}5caXVx`bNrC1Ow`<)=Kl1dAasrA6McgL^*qqXDs z8C_E?8>}^LuhFI*x@*JEdX`!vWY8%vbe#45E%wlpf-f9Oj+e8oS1q?kFj}vli&0x5 z&DEPLL=|C6N9^+3XYC8ujM;#mj;Zr`>0c~SZ$3&qb5h{OG+k%t^(WzELfehmkKrG( zIME4c%AQGCXDywf9ObFiW5`?cA?q=a;l53rO|=&G|%0~ z?DWI*RJ&)*b{Um#<K@hBBDH87+tcI2j*r>dCd!%1-cB$y ziPlMUi0X5{oU~VKINEb*j}Jvt>TxeUxNhhDV%DY^>!7EAYJ{}WBrn+!^2^F`$~2ro z#|34_&?C*R+eIWfP8SOh(Zi&57@S<(h$~l40xm-)bGmL|(&M6ntdu95H#9w=?z^aI zm%hR5*Jl*=&Z7!DXlFI59y#~%1me&n$^UuC2MK{wC=J5okfiDh^@QmiKPN9MFcDZDviCTpz=dl&?!$K zTiW573!p1o(&E`MX)mS_&8rBledoQ{(k4Lww8O}}}(VRJn0 z3-*=kC&ZeZPu|6Y+L7+69T^|3(x1`>dbRsFZ;x+j*ltT|US?KBuRF3|jdjlM(Wc~T zo&0MsL9qVCE+3X$YtkK#s@f2?xI|?{;~a@CX96BTRWM0iX$}Y>u0S{9N^rI=80Zq@ z38)Aqw9K-~*?FjG-}{^Fe8zY=lqc2=+ve^2H*UAfZ=1CHH#BT)PD6wy)mFVn&X*(l zRa;Eh$fdQ&>UISu6<%VJp*jep0};N|kTb`oYxcyphV8MG3Mgz^cAa=&kDYvQkLlUW zR;_EyX+VUU( zJ5rl{&)l=e-gHFWK6dq--TZ*;l35&3qaxg-#8x^TUL;^kHMrE$55iC!CkU&_wA2Ht z+!>dcvrt-4C6w$*T1fTS_fTlv;J9e8nn-tFq>ZqnlOTFF#{A?DlXlwIH*3F*wLR;3 zbw>7Z^8O6_|3~Tw$4~yQ*Y;iv(~d$mB1fr|-d9TEZJPAYysTkAzC%Q+fNNLH+e;4V zw^1D#WVD&nHU>ix?P8kB&~>OD0)P0HKKq9&=j?~KsGIo1S~eWX=*uQ~q?`L1xCfd; zwj(M*1&7v;EGiDZGz7W8kb(p!C6PnUi>}lGCNjX~u)P<{cqTRFeKjNH^IBmXEk|xv zmtH@CQFMKE^*_$oF^4YFLsZ>|92`HPVPN%!nma~=v3=mVb$9+eOZBXb^0->RrL@Bw zo`BFsxkoh7e)#z{yHXo)GkrDtmR3TKY?!ixm$hv7g*AJ|UITW>UW3}zC~Qje6ns;% zuSYAg3r_9zmDuHf)C8^0S@ukoAtq^GJcuW`Jo`lBPv{rI0~L~s;CU=%m?|p(qv>Mp zpiFnZlmHG93Tj0-e>d&DeCZ{HEz=(NTtkzpn$)}|(?+ZAW9?0kl4toZyyBCOTI8*c z)(4!6R(QjMdQovNvRj%rT!Y_Sn{JLFc0lB@-dPkUqXVzSjV)k_TQqD&!$?z4&)2S= zvTOgSt}#1f^`533wP(M*>&1)hWk-!_F~q7&wkT!~>AY9=*}dzg?cNP4B-X_GO3MK-0Gumzl@8~7X++>PgsRL9A3+B1qUWStMi^6h zde!EaV63%?RTk@0RjaZ4-j{E(;ds$eSZ&?-g_;$5AoB+X)zam6Xuy^stRnmLHmgrh zvi-Vv8%81gUwB%)tf;_4+ppHnHKZ3X*RGnju`wCOFu=nZ+Z0RmE_0`)))-!Bw>)0h z8DHOEANc-ye=ueWqA^g81)2js@}fSgGrp#!o9q)Z0!dvS1(-Sl51rT?r`YqroG!(w z2SBWlx>ebkvK|+eB__H|oCPT-gIE2fX;<8-qj$Te);zBgFbSrfph}Pd(b8%4^a8yK z@-nHMj+>N^esdod%LX;JZ~b=0is@-P^@x704CQVakNvwY?y>!L8BnEB1q!+`sXwuK z*48{erJ-K@G!|XIdtlZ+f1Sp*y2cRYTRLlBa8aum#XgdHMN2!V|L;G>ZE|jdU3kWF zZ)?~-sVC;=?q9cK_bu$VOv;1EP0z2c+npg$WQen!rE!iEU(h&D@@r9ihW42-2RH0d-6BsmCkfr-Bq;WDL94&$kHPBq!6_xrp+ z6KqYZ2W*{CSDKPMCIac0nz2)l9kt6pw6C0_u{nz#9;n+t{oqkM?~--CTk7L=ogqUL z{HVsVAyoi-NbDO?L2kd~p@(kBp3sbjrETN(={GL6w>*D=maW-$aYav?lXEH{ zV-gjh!g=sWw}>4mK3EA<1Q)BISPdyz45(0xgr8HPeXLUD_6%6FPc7E%Ns}RBX{~dG z=oT#2ezRhnPb+6Tf>z$6tqisNmRc{m7;U4m(V|^H2I_g`>(`Il%a0oI*A1whQ5QXW zuO1s6oUt(gj8XbSFEZ5El~Mq}6(P#kk6Q_LYVE^n?WA!&&SGiHXFigJN>yueeyW9}R}Ts;RT35O$d-8xUDI~*%0YFErY|a% zj&KB~WvkY!0@MRqT3m#zvP^D-ZeHYA4&*F21&Zxx63X@97q(vgsm-y;VUABU$%hG? zxJk>7llA86*xk);e7LaRJS>>X%7G;|x%OXDcb_rdv!%?0%p5@M7iq1MfH4;^Crq+> z!XA`!!}$zP&0L{ATT6#$lk=lqsfZTCORt6;h8kMGkN3#Qm&B%^JY|4cK{@fNa%enM zoaEg3=$ySs6`-#reB@&Rzye{_6EYNLU;V`3lR&!XN=bR7yJ{+0Jn5fc3GGOK5KCqN zRVrsl!d&8njtsk+`|9E9gU4z9Qkjp~#q9o#Eqh|T@bfz-?N{jis~&9R_-Sn|@IID{ zyvG_zJS{o1ayl!eUdknXELJ?Y0i*=Kjnrdz=ZOO>#3Oe-|uykZlH4eM##M;mzDUJ@Z+NwQo7cb;tXeDsE-Fu@>- zCt=dgnJvmf+Jr)fv7tmxqqSos`1Y^4Sh2 zqd?vx;Ah;0Jcb~}PNW-rIFjz@JfatIRb1i@h7>zZsYBda%DOoeN6eISo?*}x^A(YL zi^@qhOOU#XZ&E$PCt^P*sdTp; z*hG-c-ZHT%+vPnW48+pBlA|iZE$$$v0goJ)mRvwumxEN%yDaHVI+8jcPPginsW*Z! zti!2Uiwp2u67II3EGeXpskqc)sQ^b*t;u|Cp0;5b<&nDTb@#XII_<6aSgXAiZ+Uv* z@#vP8J-SXyHoYKEld%k{sBr$=njPU2CF8dql9)KV1Ldg{jASgb9Hkriwho{;o)lXq+~#CFx(ZmF;DftdN@ICQYd(+p3W1%0iKo=|+ql zg6DU3PMK~_RLOTFVU}ttP-CpWDCt)nB8+hwIF1FzFhZB51emLA8Qgm4bx}G3G0u;C zf;wx-n@M3uEIxO~D+bmsCxn7T3so)=BqFEFB%n$pLexDE-leYh(>?wq42bse$dmIr z7NZKFxUdjNaJe4Rfl0kC<=RFkl|dtV6)aT%=_&a+Q(DxdHUL@jyz_(sZ$p#n#2C}7^4z>yXOVcDwjdWCUhLu`WLYj>naHMm zCUN^J3`(Z$Xs1p@E-?rx1@wU@jx=y=@w_td$RiZ&IL!%k{guH;9^@{^~N!>ItknBZxQbpVhEI0Iu9g!2woqG1*u9v-k{ z!0VscV<#R^*UoHmqGmRKBL3MQWf;#R^!)B#nxyADy0QYIoOC4VY)Zh}(|9*=Pz1+Q z@F5A&Nekr|(5Z&~^e{@@#O2+u>9G&Ku;1SiB)Qt$(?j3SZyU2UPl%4E zbcWb&pGb@ck0X;|9g#}iMI(t3gsghS)8M)Umf13qNR-97)7leyNFPb;qX#jZ;pLPR zwxJ1Wsg_zL5Z(>&YLL|sry#a$WAYX&EzO5G5xF;VWk^2$LY&K1j2t%p{4JN#4hbhy zC?8&62%KOpKu(pK{q&~pjA!@RDTnr`?$e45O8ouUle6}XU+RaEwEf{v*X|^olO2Te z9R>^&O(yGhJo@RGUIBE$pzDxT6<$pTk&pisgoGgJxA)K5-91yP5L%lQsr5EB#_T*= z*jL}Y#1?2rk^L2313s{^X}`ID#x{;?FGsNELJ8qfUp*-e7ZgMa(Ir_;z1Vr~{#v2P z`#eDlprq5C25}f6GW`MYd)yoyG=Y|Fn5Q@2XjF=x6#>i+ z7+f&2(c(HPihv=52bgBBX=gYMC8`8(c`DhqC+6(i*KJcJEYiw?wSPm1Lla2IIQk!~W#<3A^|44ffGf7TAeTAJjyb zdIPPI(Cs92c@YpJe3-%(2zp2K#mjYnny}yAr`Fm(l1vxw&Cl%98PGznDN<{P7xUn9 z=L>+D9FHN-5t%E0|D^rPZ?^kMIvdH(kJj&wlK^Cd5h&KDQ#F+xL(WabTP&IO)DhSCxQ@%5WS2bjcWv^Y?6;vNv74*^buu`=Ubz?8v?Q zZJEYojbL5@s0o$TvI6trMh;#&nzjoryLp@a^VM5zs;RAL1bB(`MY_I1T%#W!VGk!{ zq&N*XjQ3-cS~RSmvD?;6+RgV(*+zZJ5V0IW=pac=N7$p;6fTzoEBayyNxZLbEFlgKlQV23phlUO@8wAq)OI z97wv8u%goCJpFQbetE+SoO0M<>8bLRMZ!dEib`l(nuxpz6j&igDMKDU+2Fh#yP-kc z$&vJ+8pccmNu-smY6(=SIK)MaVvVP~XlZHdq0rvFzY^4Xe1}&P5|T3qx|%|;59iDI{3pd(rlk-_2X-Mf52ky(nSR_-gCL5 zMB|+3SHiOi<_N!K1W~D@jZG;bgDsQGVuhRuzC;{%ak$}`4lqJo5g7&2Iqx;wO`q{Z zKHzyJC`GKo7Cz`F2c?5cqI4c^gq(LeIbTOFmX7w>(^mB9(ecLNjNiJgIQSP6Hk10+bXD-ms`NwM-#N!r^uWJS~d z?QJ7AsJA|T`a&zI(=HyfJD*Sm@P;TH7}jL*9rE4KY#|Yq;1!@u1&q~UQfU*wknL&R z!iVHb2GR~Fsa>>gkoeM~_=MJHRgEw>6O1`zgMj;QBrt(M1Dx35DGm@guW67N=QNp+ z%^wXlJ$l1L3l2F{2u78GWC#?Ft%%y(oL%ha`(`aMpS7#m6>q8g(Q`Hcp?AK_8)=V)Yj0U-2Uhu2 zYn%3s-_F@xk14}5jQ56Yd&tOTa%dP7im+6FvCWbUuQ;JfmpmfkQ~_xYs9G3`B(Mym zJ&%@1=eR;wCUe3?g&j<;dbWT45jFeTYkKXMcTd~-SIyd1z33=ax}lXmP#O+QLY1%S z?9TbGUuq}p-fvglrsazsV^27?Pv<>n?W31&($N$ZiAmHA7GIYw} zp$a1%`G}n;6z5Dt&}8QyxV$Cj_t{4-+iu^yUWcuG#S!ioU6(u;QF>5F-mvNTTyO6- z`^Gz#>BBzl3C-8+gnji>!Qb0x*W4tH zr!KU+AD^&?^q~lgEFXGsei-J%3`VwvQIX`id!QoPIe~<6WJON!3tkle_kS=1W!Y7^ z*zabz@E}NWi9RdeB6(^2dXe0ROw!tgPo!>PW0XBd%nuqp0#iCY&PcGu%g`^>-V zgdFzy5tX*HW1M$>ZjzDIBpU9BO^VwQ5Dy8NZHpnuxpV`Wm$i~=`7?T064{;-KZ$?=_4-W3oAhU~US#`Mwl ztWG_*?3M>rDI?l2^RFmz979l&;q(%csv+gPT->lPdP{BI?s}g3q!YL|Rv%gWPFo zU0SoBqyt>vd_%vVT;|teuF$su@BYcWO{)?LHr-h^MnHaKNh6#QMGs(U5}&u_12gvR zHxJvPI#!N~)sIcv+rKw%YiHzqe`}R1AY|?+MlP2=KIqpsRG&Px&t7`ypnotqqjwJH zUOH(vJs{yc9n(l;6lS@bv8Tfc2?MlDx$Qs*=u+er=m3Y2^*3VP;xZgMC(b+$Chhw< z8CRikjcDx-6i(h*IMA@Ky|&-<%Z&ECOIr5Y%QWddW5C{|w@-Y!ls2i7GWUt(gl{57 z2W{Aco&!F9%7~3D9I#U^ZQ0BIRW2DAw10f1h9BN>y77s0$F}8yQF;{UJ;iH}?z58* z9kO>_Yj(;H^?kwvHT%S=gErDHd49439(rcxMTh=Uz^-=Q!GQyn&34)-5OEIqm1G-G z(hKCv{fzSAAh;BJWs10z6^zjPrJJ0!&!WN>=>^Q!|5Im&^cCU_tD1I|KEODBFEOwz zrGQxoB3w*WMx?!v`e=d=Dt52g_ik(2z4~dg)sN2WtCx8@TrXDYb>xX5cob8H&X5gt zIw93aj*-0U)!J{mziAsbHS9Y#&ufQOKYcSQJeBH>t4fiKXw4-~v}Ibfp6-HGMF@qR zymQV0p-PtdMBs%9k_@UAFi@e-^XScpjoatt=a#M1$Jvvc=55$!ZKXaBx_`YUW>!aG zbTEjMNwc!95J@2Q(5@FJYqvJ-2+hBetMr>@%BD@R44upQ;+!4)thb8HsObZ7~X8BgAk-ptsvw9hAu)R z9Uj0O3zsKhrMz~!2*_qVfeC%&U3M29xYOuXs71B(g^P_(+KcqN3@17N4kBwHF%R|pjdblZ_t zk=Idyv>dhWmX>hCdVqPszS^bKdfM0Ng2zE91o%;M3Ph`-h4Qe}sM(pv_SoGY8niQy zksi*2ATL%XymVpqazk>gbZHkYvXn=wm&|ZJM1JKGc^NXyt5JV`!y3v1K&^g}wn2B*3;Kn< zbbnoXG4Beb!*$6Qe!3y#WH=K1O_u!Q4{*TQ-bPR8;=xX*w7n&}Fd;%tI8R$6gEDB> zMKv4k)oO}Yq%|rNxfEM{5GP<8yuc}gZ>=cr4p$yn*g~30QzVMyKgG!|0aF4<3L+}M zkOA!h8E{9arUjFGWkl0<8*SLj_t$^bv}@h&czDj9^E9>m{)S$PH1(mO4nt`^e7d%s z`OcOwuHkjH{@1;&+r_WKHs`!H)NABCK84p1JUXI-QoGSfYIeImd)=Z{$f!R2K3Mxc zY|Y@1=~?~u!-l49)_)Ae3Z`ENxDMZ&Ax(LlqQA{rVBM=9-k3)R6`{2}bMwL4wqBz5 z_LQ}uv;KQ8Q9@x}6Z>f2oE>>UuWg)Y*gg7M-#^(>PdR8s%a-Z;uzMb#w8vA(8`T59#Iqx~BF{CN4%@yK`lw&cj>rYbrVAl%|;xo zL^Sy=)4{1r&KS}!m-Oj}mB#G%cTL(04jQztpEhC#s?gQIy8Wz6#_ZZVr|he5AGNoi zpw)^SpQxy)8+AJGjPH)y!}qrN$GYHf(R$r+zkR7yeSMjI9tpVsmE& zg&arYs0OkEcX}vjA`XVg=N87#zg#N^?OdO}==1YRGQ9dz&4%k4^#${^g%PsRLDW z-V;uL?qYkMHq}0K@~r(YJs*7W4LiH@e|O)wUH<#+n)KyB^}fvdm#*686WHMg z^x4YYdTrHXQ}#C(uC+OTSzg$euGwb0FRt712aVXuT{JxSzKz*|E9&;s4=lIi_U*M# zU%AOX`s1yt$UgmYVvjzQ=(C5`P1z+kJgGB3oTAik?r)s2x1G4e_E=oA#TvW$RCQXf z!Op+pNn6KnLu;wX*YUskr@hn&^r_L}Svx^3f8$oI^FRBDeiTZrlL=fOmG7d5!jleH zxtJI73e773r_lR{v!6m_*hK1)*I=f!xaVl}dm z!CG1#+%Ts`rS7I*08 z(;&6@w}16fo$sOW0;sZa?rOEPX{|`oe}N(tOGqB<6l&VA^H{erefh`H#9bEil-pw) zn7ly?#!++vtF1W}0}4LAvnZI9z8UQaC2;bMQ{Wesmv;{S@+arP(PG1S5`U(N z@$SE9!%gpOfBdec`oD%7zH&Qm-w`|hfFXD2D*X!Xi7T}nQJB|Y`^kUZq#{8Ig1w3arkE_8XM;pdh$Gr4@g<*@w38e2Ai*6HQj zjrUC0D~?`ZKRtIByXua~JO}h@3p({q&W1jq&A~0>^Y*-_57{@)Tw?dF(|@iYMbFZ_ zL|}ePEm9dAK?C$9r_$N!{#Ad+5)w|~aUo>Ypo%$hAH5SkK*z(artX!zn{>dColgZi zZ(EN5>ML3T@;@(q;TIG3o|A^`!>?dX*Rmr&_7^+z;*EM|yOX_2zrXXUquN^U{mZny z=lf4;7eT*p&clvEo%Nx91^2_3ZuO70KlYj>cIEq*+G{V|XlH+ClYQ-srFPbfm*`sB z7NpcAH*VF$yiulL7)C0Nv>?e`eJZPA@qL$UvO&EGdcK@{?EW~N!a>1*B{FBXKdK)K z)bRAa?{Bp8Put1P)TG)DE&KBm({|S*YBY!Ht($`DEGtC5aJ`c1K8wDcB_uT|lku)! zZa-+5wqYLG-2Brq7um`Ung_Dg$uy)ngLsKM1PQ=K8)rHvCT%}$j4juA#$0fhKIPfI zZB9Sepr3?XUeikr46bQNY1nGDs&QWRY3$uYzXi9amY$EO*Zf8Ex6d=``bX|j*L(H4 zZJyHF`@w0YoBE02x_|W71>S;nzoM`B};k&FQWdepHAMm zU1yIp@gJa{$yuxClLt4;2!>Y8W&7(r_p;%Z-Mg`7n`iP+SbZ|j734MK-BYLe7WUe$ zAMU@qXw6N2;M5-vUU20dGBgdLWkd4va*Gg^;FY5>nz53NHkNTQSjNK$V=7Bc_*2tJ zg`~wJ#~;9iWIR!C0^}~Qkk6Eeb+ z$l;S0pJg)-j7-R^-KkZ^aCo_V`6+&cpodFn|BRD4j|m*dVi%i?4C7RymrpT0B!W<- zQ-R6mS3PTiUGcW5b9-!T?dnta-&bdBlCY>O=mbdtB|>-vL|LY6pNRMYZT5I5Sf+C( zrTyn-P|oGTbdwx9X^Y=oAb1qrR+)f168cOd=eP*6B8#%8xeH>+pfVxCS$vg&a0nfS zY}u(O-$*1p1ke%mWNm#^I2pesep)WQf<_v&&Ls_C2?2}Wf81L3O&rrj1&nGPLvTdDy-9!RPK7>gId9GaAj5QD4Lun1^sj@^IODgGc{){4B3iL@O0K{P&Nn|9P^JH|UXbS~i z4J9$`{L2A2`n1JT=(ra_V~*x{g9^%f}h|b~gx)X?&T5*B3{Ju_LJ`gZmiVePSwJTBTuwWEMwd!T zAi_W@c&uCCJmy9Sy_uMZtBpg|*FU=uM|HpannP)w-Y3ILEBNNLi)q^j+vQF;_UWm|;5eSg7)EWizf+%j6Px#0L zuQR!gr4&y@PH`m(wW}tP>ByI(q+qGlC%%daaI^_i4nUHFELezl5OPN-b+$<|BY;Sj zahb?|)DOVEcEJArcr5NcGyXq$Sj#ezA?UT@do?g!c+u}`cFyH<{XPR zK~R{oUI{tlXrU5}oGy9H;j%gkO+M1FmYF0r2~MmD1w3>LUeTU14hX39JhhmvCNpN9 zMBh#7wz*B^E%8>futG=(-Nb73gIcA3{jV)r8}BRXeP49Cd<(WJj0ddKRdrwc z{kp!(S0XXY9`7=ExQ`d5|WDv1zN0d*!@bJH)>nZL8w z&kP@T^}WT4tM1ePWcP5>*6YZj&k-zSJ05zJuh@$;lsKq;s{ok1qm>3BT$NCDRDhAi zRV{(TTuD&<^|+)jEqsdY?Xv7ZF71(3pdRp+>%UMucE168#lcN``JuISD+VTR)=U1c z7rg_&`fEm1&Hrn3)*4^G+!|9y+0e3s9@^gj)(1DYR<72|@t?2J8zNm(dI{1YtWqXN z&_W0S$ElRbnaYHdlsCW-d8{NeF6rnx$b{czW+EIyfabpokX9?VJMVHULNYBS9oBC8 ziw~*WiTi5PaYc summary { cursor: pointer; color: var(--muted); font-size: 11px; + font-weight: 600; list-style: none; user-select: none; } +.cv-adv > summary::-webkit-details-marker { display: none; } +.cv-adv > summary::before { content: "▸ "; } +.cv-adv[open] > summary::before { content: "▾ "; } +.cv-adv[open] > summary { margin-bottom: 6px; } + +/* 运行按钮(沿用主色渐变观感) */ +.cv-run { align-self: stretch; border: 0; background: var(--grad); color: #fff; + padding: 8px 14px; border-radius: 9px; cursor: pointer; font-size: 12px; + font-weight: 700; transition: .15s; } +.cv-run:hover { filter: brightness(1.08); } +.cv-run:disabled { opacity: .6; cursor: not-allowed; } + +/* 错误红字 */ +.cv-node-err { color: var(--danger); font-size: 11px; line-height: 1.5; + background: rgba(255,107,107,.1); border-radius: 8px; padding: 6px 9px; } + +/* 预览区(img/video/audio)圆角、限高、加载骨架 */ +.cv-node-preview { border-radius: 10px; overflow: hidden; background: var(--bg-2); + border: 1px solid var(--border); } +.cv-preview-shell { position: relative; background: #000; } +.cv-preview-actions { position: absolute; top: 6px; right: 6px; display: flex; gap: 5px; + z-index: 2; opacity: .92; } +.cv-preview-actions button { width: 26px; height: 26px; display: grid; place-items: center; + border: 1px solid rgba(255,255,255,.22); border-radius: 7px; background: rgba(15,17,23,.78); + color: #fff; cursor: pointer; font-size: 13px; line-height: 1; padding: 0; } +.cv-preview-actions button:hover { background: rgba(90,103,216,.92); border-color: rgba(255,255,255,.4); } +.cv-media { display: block; width: 100%; max-height: 220px; object-fit: contain; + background: #000; } +.cv-media-audio { display: block; width: 100%; } +.cv-meta { font-size: 10px; color: var(--muted); padding: 5px 8px; + border-top: 1px solid var(--border); } +.cv-skeleton { display: flex; align-items: center; justify-content: center; gap: 8px; + height: 120px; color: var(--muted); font-size: 12px; + background: linear-gradient(100deg, var(--bg-2) 30%, var(--card-2) 50%, var(--bg-2) 70%); + background-size: 200% 100%; animation: cv-shimmer 1.2s linear infinite; } +@keyframes cv-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } } +.cv-spin { width: 13px; height: 13px; border-radius: 50%; + border: 2px solid var(--border); border-top-color: var(--accent); + animation: cv-rot .7s linear infinite; } +@keyframes cv-rot { to { transform: rotate(360deg); } } + +/* —— 端口与连线用主题色 —— */ +.drawflow .drawflow-node .input, +.drawflow .drawflow-node .output { background: var(--card); + border: 2px solid var(--accent); width: 13px; height: 13px; } +.drawflow .drawflow-node .output:hover, +.drawflow .drawflow-node .input:hover { background: var(--accent); } +.drawflow .connection .main-path { stroke: var(--accent); stroke-width: 2.4px; } +.drawflow .connection .main-path:hover { stroke: var(--text); } +.drawflow .connection .point { stroke: var(--accent); fill: var(--card); } + +/* —— 沉浸式全屏:fixed 覆盖整个应用窗口(含顶部导航) —— */ +body.cv-noscroll { overflow: hidden; } +#tab-canvas.cv-fullscreen { position: fixed; inset: 0; z-index: 9999; margin: 0; + height: auto; padding: 14px 16px; background: var(--bg); } +#tab-canvas.cv-fullscreen .cv-stage { min-height: 0; border-radius: 12px; } diff --git a/renderer/vendor/drawflow.min.css b/renderer/vendor/drawflow.min.css new file mode 100644 index 0000000..c9c9442 --- /dev/null +++ b/renderer/vendor/drawflow.min.css @@ -0,0 +1 @@ +.drawflow,.drawflow .parent-node{position:relative}.parent-drawflow{display:flex;overflow:hidden;touch-action:none;outline:0}.drawflow{width:100%;height:100%;user-select:none;perspective:0}.drawflow .drawflow-node{display:flex;align-items:center;position:absolute;background:#0ff;width:160px;min-height:40px;border-radius:4px;border:2px solid #000;color:#000;z-index:2;padding:15px}.drawflow .drawflow-node.selected{background:red}.drawflow .drawflow-node:hover{cursor:move}.drawflow .drawflow-node .inputs,.drawflow .drawflow-node .outputs{width:0}.drawflow .drawflow-node .drawflow_content_node{width:100%;display:block}.drawflow .drawflow-node .input,.drawflow .drawflow-node .output{position:relative;width:20px;height:20px;background:#fff;border-radius:50%;border:2px solid #000;cursor:crosshair;z-index:1;margin-bottom:5px}.drawflow .drawflow-node .input{left:-27px;top:2px;background:#ff0}.drawflow .drawflow-node .output{right:-3px;top:2px}.drawflow svg{z-index:0;position:absolute;overflow:visible!important}.drawflow .connection{position:absolute;pointer-events:none;aspect-ratio:1/1}.drawflow .connection .main-path{fill:none;stroke-width:5px;stroke:#4682b4;pointer-events:all}.drawflow .connection .main-path:hover{stroke:#1266ab;cursor:pointer}.drawflow .connection .main-path.selected{stroke:#43b993}.drawflow .connection .point{cursor:move;stroke:#000;stroke-width:2;fill:#fff;pointer-events:all}.drawflow .connection .point.selected,.drawflow .connection .point:hover{fill:#1266ab}.drawflow .main-path{fill:none;stroke-width:5px;stroke:#4682b4}.drawflow-delete{position:absolute;display:block;width:30px;height:30px;background:#000;color:#fff;z-index:4;border:2px solid #fff;line-height:30px;font-weight:700;text-align:center;border-radius:50%;font-family:monospace;cursor:pointer}.drawflow>.drawflow-delete{margin-left:-15px;margin-top:15px}.parent-node .drawflow-delete{right:-15px;top:-15px} \ No newline at end of file diff --git a/renderer/vendor/drawflow.min.js b/renderer/vendor/drawflow.min.js new file mode 100644 index 0000000..ebdcbdd --- /dev/null +++ b/renderer/vendor/drawflow.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Drawflow=t():e.Drawflow=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var s=t[i]={i:i,l:!1,exports:{}};return e[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(i,s,function(t){return e[t]}.bind(null,s));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t),n.d(t,"default",(function(){return i}));class i{constructor(e,t=null,n=null){this.events={},this.container=e,this.precanvas=null,this.nodeId=1,this.ele_selected=null,this.node_selected=null,this.drag=!1,this.reroute=!1,this.reroute_fix_curvature=!1,this.curvature=.5,this.reroute_curvature_start_end=.5,this.reroute_curvature=.5,this.reroute_width=6,this.drag_point=!1,this.editor_selected=!1,this.connection=!1,this.connection_ele=null,this.connection_selected=null,this.canvas_x=0,this.canvas_y=0,this.pos_x=0,this.pos_x_start=0,this.pos_y=0,this.pos_y_start=0,this.mouse_x=0,this.mouse_y=0,this.line_path=5,this.first_click=null,this.force_first_input=!1,this.draggable_inputs=!0,this.useuuid=!1,this.parent=n,this.noderegister={},this.render=t,this.drawflow={drawflow:{Home:{data:{}}}},this.module="Home",this.editor_mode="edit",this.zoom=1,this.zoom_max=1.6,this.zoom_min=.5,this.zoom_value=.1,this.zoom_last_value=1,this.evCache=new Array,this.prevDiff=-1}start(){this.container.classList.add("parent-drawflow"),this.container.tabIndex=0,this.precanvas=document.createElement("div"),this.precanvas.classList.add("drawflow"),this.container.appendChild(this.precanvas),this.container.addEventListener("mouseup",this.dragEnd.bind(this)),this.container.addEventListener("mousemove",this.position.bind(this)),this.container.addEventListener("mousedown",this.click.bind(this)),this.container.addEventListener("touchend",this.dragEnd.bind(this)),this.container.addEventListener("touchmove",this.position.bind(this)),this.container.addEventListener("touchstart",this.click.bind(this)),this.container.addEventListener("contextmenu",this.contextmenu.bind(this)),this.container.addEventListener("keydown",this.key.bind(this)),this.container.addEventListener("wheel",this.zoom_enter.bind(this)),this.container.addEventListener("input",this.updateNodeValue.bind(this)),this.container.addEventListener("dblclick",this.dblclick.bind(this)),this.container.onpointerdown=this.pointerdown_handler.bind(this),this.container.onpointermove=this.pointermove_handler.bind(this),this.container.onpointerup=this.pointerup_handler.bind(this),this.container.onpointercancel=this.pointerup_handler.bind(this),this.container.onpointerout=this.pointerup_handler.bind(this),this.container.onpointerleave=this.pointerup_handler.bind(this),this.load()}pointerdown_handler(e){this.evCache.push(e)}pointermove_handler(e){for(var t=0;t100&&(n>this.prevDiff&&this.zoom_in(),n=n&&(n=parseInt(e)+1)}))})),this.nodeId=n}removeReouteConnectionSelected(){this.dispatch("connectionUnselected",!0),this.reroute_fix_curvature&&this.connection_selected.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.remove("selected")})}click(e){if(this.dispatch("click",e),"fixed"===this.editor_mode){if("parent-drawflow"!==e.target.classList[0]&&"drawflow"!==e.target.classList[0])return!1;this.ele_selected=e.target.closest(".parent-drawflow"),e.preventDefault()}else"view"===this.editor_mode?(null!=e.target.closest(".drawflow")||e.target.matches(".parent-drawflow"))&&(this.ele_selected=e.target.closest(".parent-drawflow"),e.preventDefault()):(this.first_click=e.target,this.ele_selected=e.target,0===e.button&&this.contextmenuDel(),null!=e.target.closest(".drawflow_content_node")&&(this.ele_selected=e.target.closest(".drawflow_content_node").parentElement));switch(this.ele_selected.classList[0]){case"drawflow-node":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected!=this.ele_selected&&this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.node_selected!=this.ele_selected&&this.dispatch("nodeSelected",this.ele_selected.id.slice(5)),this.node_selected=this.ele_selected,this.node_selected.classList.add("selected"),this.draggable_inputs?"SELECT"!==e.target.tagName&&(this.drag=!0):"INPUT"!==e.target.tagName&&"TEXTAREA"!==e.target.tagName&&"SELECT"!==e.target.tagName&&!0!==e.target.hasAttribute("contenteditable")&&(this.drag=!0);break;case"output":this.connection=!0,null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.drawConnection(e.target);break;case"parent-drawflow":case"drawflow":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.editor_selected=!0;break;case"main-path":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.connection_selected=this.ele_selected,this.connection_selected.classList.add("selected");const t=this.connection_selected.parentElement.classList;t.length>1&&(this.dispatch("connectionSelected",{output_id:t[2].slice(14),input_id:t[1].slice(13),output_class:t[3],input_class:t[4]}),this.reroute_fix_curvature&&this.connection_selected.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.add("selected")}));break;case"point":this.drag_point=!0,this.ele_selected.classList.add("selected");break;case"drawflow-delete":this.node_selected&&this.removeNodeId(this.node_selected.id),this.connection_selected&&this.removeConnection(),null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null)}"touchstart"===e.type?(this.pos_x=e.touches[0].clientX,this.pos_x_start=e.touches[0].clientX,this.pos_y=e.touches[0].clientY,this.pos_y_start=e.touches[0].clientY,this.mouse_x=e.touches[0].clientX,this.mouse_y=e.touches[0].clientY):(this.pos_x=e.clientX,this.pos_x_start=e.clientX,this.pos_y=e.clientY,this.pos_y_start=e.clientY),["input","output","main-path"].includes(this.ele_selected.classList[0])&&e.preventDefault(),this.dispatch("clickEnd",e)}position(e){if("touchmove"===e.type)var t=e.touches[0].clientX,n=e.touches[0].clientY;else t=e.clientX,n=e.clientY;if(this.connection&&this.updateConnection(t,n),this.editor_selected&&(i=this.canvas_x+-(this.pos_x-t),s=this.canvas_y+-(this.pos_y-n),this.dispatch("translate",{x:i,y:s}),this.precanvas.style.transform="translate("+i+"px, "+s+"px) scale("+this.zoom+")"),this.drag){e.preventDefault();var i=(this.pos_x-t)*this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom),s=(this.pos_y-n)*this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom);this.pos_x=t,this.pos_y=n,this.ele_selected.style.top=this.ele_selected.offsetTop-s+"px",this.ele_selected.style.left=this.ele_selected.offsetLeft-i+"px",this.drawflow.drawflow[this.module].data[this.ele_selected.id.slice(5)].pos_x=this.ele_selected.offsetLeft-i,this.drawflow.drawflow[this.module].data[this.ele_selected.id.slice(5)].pos_y=this.ele_selected.offsetTop-s,this.updateConnectionNodes(this.ele_selected.id)}if(this.drag_point){i=(this.pos_x-t)*this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom),s=(this.pos_y-n)*this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom);this.pos_x=t,this.pos_y=n;var o=this.pos_x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),l=this.pos_y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom));this.ele_selected.setAttributeNS(null,"cx",o),this.ele_selected.setAttributeNS(null,"cy",l);const e=this.ele_selected.parentElement.classList[2].slice(9),c=this.ele_selected.parentElement.classList[1].slice(13),d=this.ele_selected.parentElement.classList[3],a=this.ele_selected.parentElement.classList[4];let r=Array.from(this.ele_selected.parentElement.children).indexOf(this.ele_selected)-1;if(this.reroute_fix_curvature){r-=this.ele_selected.parentElement.querySelectorAll(".main-path").length-1,r<0&&(r=0)}const h=e.slice(5),u=this.drawflow.drawflow[this.module].data[h].outputs[d].connections.findIndex((function(e,t){return e.node===c&&e.output===a}));this.drawflow.drawflow[this.module].data[h].outputs[d].connections[u].points[r]={pos_x:o,pos_y:l};const p=this.ele_selected.parentElement.classList[2].slice(9);this.updateConnectionNodes(p)}"touchmove"===e.type&&(this.mouse_x=t,this.mouse_y=n),this.dispatch("mouseMove",{x:t,y:n})}dragEnd(e){if("touchend"===e.type)var t=this.mouse_x,n=this.mouse_y,i=document.elementFromPoint(t,n);else t=e.clientX,n=e.clientY,i=e.target;if(this.drag&&(this.pos_x_start==t&&this.pos_y_start==n||this.dispatch("nodeMoved",this.ele_selected.id.slice(5))),this.drag_point&&(this.ele_selected.classList.remove("selected"),this.pos_x_start==t&&this.pos_y_start==n||this.dispatch("rerouteMoved",this.ele_selected.parentElement.classList[2].slice(14))),this.editor_selected&&(this.canvas_x=this.canvas_x+-(this.pos_x-t),this.canvas_y=this.canvas_y+-(this.pos_y-n),this.editor_selected=!1),!0===this.connection)if("input"===i.classList[0]||this.force_first_input&&(null!=i.closest(".drawflow_content_node")||"drawflow-node"===i.classList[0])){if(!this.force_first_input||null==i.closest(".drawflow_content_node")&&"drawflow-node"!==i.classList[0])s=i.parentElement.parentElement.id,o=i.classList[1];else{if(null!=i.closest(".drawflow_content_node"))var s=i.closest(".drawflow_content_node").parentElement.id;else var s=i.id;if(0===Object.keys(this.getNodeFromId(s.slice(5)).inputs).length)var o=!1;else var o="input_1"}var l=this.ele_selected.parentElement.parentElement.id,c=this.ele_selected.classList[1];if(l!==s&&!1!==o){if(0===this.container.querySelectorAll(".connection.node_in_"+s+".node_out_"+l+"."+c+"."+o).length){this.connection_ele.classList.add("node_in_"+s),this.connection_ele.classList.add("node_out_"+l),this.connection_ele.classList.add(c),this.connection_ele.classList.add(o);var d=s.slice(5),a=l.slice(5);this.drawflow.drawflow[this.module].data[a].outputs[c].connections.push({node:d,output:o}),this.drawflow.drawflow[this.module].data[d].inputs[o].connections.push({node:a,input:c}),this.updateConnectionNodes("node-"+a),this.updateConnectionNodes("node-"+d),this.dispatch("connectionCreated",{output_id:a,input_id:d,output_class:c,input_class:o})}else this.dispatch("connectionCancel",!0),this.connection_ele.remove();this.connection_ele=null}else this.dispatch("connectionCancel",!0),this.connection_ele.remove(),this.connection_ele=null}else this.dispatch("connectionCancel",!0),this.connection_ele.remove(),this.connection_ele=null;this.drag=!1,this.drag_point=!1,this.connection=!1,this.ele_selected=null,this.editor_selected=!1,this.dispatch("mouseUp",e)}contextmenu(e){if(this.dispatch("contextmenu",e),e.preventDefault(),"fixed"===this.editor_mode||"view"===this.editor_mode)return!1;if(this.precanvas.getElementsByClassName("drawflow-delete").length&&this.precanvas.getElementsByClassName("drawflow-delete")[0].remove(),this.node_selected||this.connection_selected){var t=document.createElement("div");t.classList.add("drawflow-delete"),t.innerHTML="x",this.node_selected&&this.node_selected.appendChild(t),this.connection_selected&&this.connection_selected.parentElement.classList.length>1&&(t.style.top=e.clientY*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))+"px",t.style.left=e.clientX*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))+"px",this.precanvas.appendChild(t))}}contextmenuDel(){this.precanvas.getElementsByClassName("drawflow-delete").length&&this.precanvas.getElementsByClassName("drawflow-delete")[0].remove()}key(e){if(this.dispatch("keydown",e),"fixed"===this.editor_mode||"view"===this.editor_mode)return!1;("Delete"===e.key||"Backspace"===e.key&&e.metaKey)&&(null!=this.node_selected&&"INPUT"!==this.first_click.tagName&&"TEXTAREA"!==this.first_click.tagName&&!0!==this.first_click.hasAttribute("contenteditable")&&this.removeNodeId(this.node_selected.id),null!=this.connection_selected&&this.removeConnection())}zoom_enter(e,t){e.ctrlKey&&(e.preventDefault(),e.deltaY>0?this.zoom_out():this.zoom_in())}zoom_refresh(){this.dispatch("zoom",this.zoom),this.canvas_x=this.canvas_x/this.zoom_last_value*this.zoom,this.canvas_y=this.canvas_y/this.zoom_last_value*this.zoom,this.zoom_last_value=this.zoom,this.precanvas.style.transform="translate("+this.canvas_x+"px, "+this.canvas_y+"px) scale("+this.zoom+")"}zoom_in(){this.zoomthis.zoom_min&&(this.zoom-=this.zoom_value,this.zoom_refresh())}zoom_reset(){1!=this.zoom&&(this.zoom=1,this.zoom_refresh())}createCurvature(e,t,n,i,s,o){var l=e,c=t,d=n,a=i,r=s;switch(o){case"open":if(e>=n)var h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*(-1*r);else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;case"close":if(e>=n)h=l+Math.abs(d-l)*(-1*r),u=d-Math.abs(d-l)*r;else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;case"other":if(e>=n)h=l+Math.abs(d-l)*(-1*r),u=d-Math.abs(d-l)*(-1*r);else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;default:return" M "+l+" "+c+" C "+(h=l+Math.abs(d-l)*r)+" "+c+" "+(u=d-Math.abs(d-l)*r)+" "+a+" "+d+" "+a}}drawConnection(e){var t=document.createElementNS("http://www.w3.org/2000/svg","svg");this.connection_ele=t;var n=document.createElementNS("http://www.w3.org/2000/svg","path");n.classList.add("main-path"),n.setAttributeNS(null,"d",""),t.classList.add("connection"),t.appendChild(n),this.precanvas.appendChild(t);var i=e.parentElement.parentElement.id.slice(5),s=e.classList[1];this.dispatch("connectionStart",{output_id:i,output_class:s})}updateConnection(e,t){const n=this.precanvas,i=this.zoom;let s=n.clientWidth/(n.clientWidth*i);s=s||0;let o=n.clientHeight/(n.clientHeight*i);o=o||0;var l=this.connection_ele.children[0],c=this.ele_selected.offsetWidth/2+(this.ele_selected.getBoundingClientRect().x-n.getBoundingClientRect().x)*s,d=this.ele_selected.offsetHeight/2+(this.ele_selected.getBoundingClientRect().y-n.getBoundingClientRect().y)*o,a=e*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),r=t*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom)),h=this.curvature,u=this.createCurvature(c,d,a,r,h,"openclose");l.setAttributeNS(null,"d",u)}addConnection(e,t,n,i){var s=this.getModuleFromNodeId(e);if(s===this.getModuleFromNodeId(t)){var o=this.getNodeFromId(e),l=!1;for(var c in o.outputs[n].connections){var d=o.outputs[n].connections[c];d.node==t&&d.output==i&&(l=!0)}if(!1===l){if(this.drawflow.drawflow[s].data[e].outputs[n].connections.push({node:t.toString(),output:i}),this.drawflow.drawflow[s].data[t].inputs[i].connections.push({node:e.toString(),input:n}),this.module===s){var a=document.createElementNS("http://www.w3.org/2000/svg","svg"),r=document.createElementNS("http://www.w3.org/2000/svg","path");r.classList.add("main-path"),r.setAttributeNS(null,"d",""),a.classList.add("connection"),a.classList.add("node_in_node-"+t),a.classList.add("node_out_node-"+e),a.classList.add(n),a.classList.add(i),a.appendChild(r),this.precanvas.appendChild(a),this.updateConnectionNodes("node-"+e),this.updateConnectionNodes("node-"+t)}this.dispatch("connectionCreated",{output_id:e,input_id:t,output_class:n,input_class:i})}}}updateConnectionNodes(e){const t="node_in_"+e,n="node_out_"+e;this.line_path;const i=this.container,s=this.precanvas,o=this.curvature,l=this.createCurvature,c=this.reroute_curvature,d=this.reroute_curvature_start_end,a=this.reroute_fix_curvature,r=this.reroute_width,h=this.zoom;let u=s.clientWidth/(s.clientWidth*h);u=u||0;let p=s.clientHeight/(s.clientHeight*h);p=p||0;const f=i.querySelectorAll("."+n);Object.keys(f).map((function(t,n){if(null===f[t].querySelector(".point")){var m=i.querySelector("#"+e),g=f[t].classList[1].replace("node_in_",""),_=i.querySelector("#"+g).querySelectorAll("."+f[t].classList[4])[0],w=_.offsetWidth/2+(_.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=_.offsetHeight/2+(_.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=m.querySelectorAll("."+f[t].classList[3])[0],C=y.offsetWidth/2+(y.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,x=y.offsetHeight/2+(y.getBoundingClientRect().y-s.getBoundingClientRect().y)*p;const n=l(C,x,w,v,o,"openclose");f[t].children[0].setAttributeNS(null,"d",n)}else{const n=f[t].querySelectorAll(".point");let o="";const m=[];n.forEach((t,a)=>{if(0===a&&n.length-1==0){var f=i.querySelector("#"+e),g=((x=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(L=f.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(L.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=L.offsetHeight/2+(L.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=l(w,v,g,_,d,"open");o+=y,m.push(y);f=t;var C=t.parentElement.classList[1].replace("node_in_",""),x=(E=i.querySelector("#"+C)).querySelectorAll("."+t.parentElement.classList[4])[0];g=(R=E.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(R.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,_=R.offsetHeight/2+(R.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,y=l(w,v,g,_,d,"close");o+=y,m.push(y)}else if(0===a){var L;f=i.querySelector("#"+e),g=((x=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(L=f.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(L.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=L.offsetHeight/2+(L.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=l(w,v,g,_,d,"open");o+=y,m.push(y);f=t,g=((x=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,y=l(w,v,g,_,c,"other");o+=y,m.push(y)}else if(a===n.length-1){var E,R;f=t,C=t.parentElement.classList[1].replace("node_in_",""),x=(E=i.querySelector("#"+C)).querySelectorAll("."+t.parentElement.classList[4])[0],g=(R=E.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(R.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,_=R.offsetHeight/2+(R.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,y=l(w,v,g,_,d,"close");o+=y,m.push(y)}else{f=t,g=((x=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,y=l(w,v,g,_,c,"other");o+=y,m.push(y)}}),a?m.forEach((e,n)=>{f[t].children[n].setAttributeNS(null,"d",e)}):f[t].children[0].setAttributeNS(null,"d",o)}}));const m=i.querySelectorAll("."+t);Object.keys(m).map((function(t,n){if(null===m[t].querySelector(".point")){var h=i.querySelector("#"+e),f=m[t].classList[2].replace("node_out_",""),g=i.querySelector("#"+f).querySelectorAll("."+m[t].classList[3])[0],_=g.offsetWidth/2+(g.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=g.offsetHeight/2+(g.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,v=(h=h.querySelectorAll("."+m[t].classList[4])[0]).offsetWidth/2+(h.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,y=h.offsetHeight/2+(h.getBoundingClientRect().y-s.getBoundingClientRect().y)*p;const n=l(_,w,v,y,o,"openclose");m[t].children[0].setAttributeNS(null,"d",n)}else{const n=m[t].querySelectorAll(".point");let o="";const h=[];n.forEach((t,a)=>{if(0===a&&n.length-1==0){var f=i.querySelector("#"+e),m=((C=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,_=(E=f.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(E.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=E.offsetHeight/2+(E.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,v=l(m,g,_,w,d,"close");o+=v,h.push(v);f=t;var y=t.parentElement.classList[2].replace("node_out_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[3])[0];m=(x=L.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(x.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,g=x.offsetHeight/2+(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,_=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,d,"open");o+=v,h.push(v)}else if(0===a){var x;f=t,y=t.parentElement.classList[2].replace("node_out_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[3])[0],m=(x=L.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(x.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,g=x.offsetHeight/2+(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,_=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,d,"open");o+=v,h.push(v);f=t,_=((C=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,m=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,c,"other");o+=v,h.push(v)}else if(a===n.length-1){var L,E;f=t,y=t.parentElement.classList[1].replace("node_in_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[4])[0],_=(E=L.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(E.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=E.offsetHeight/2+(E.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,m=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,d,"close");o+=v,h.push(v)}else{f=t,_=((C=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,m=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,c,"other");o+=v,h.push(v)}}),a?h.forEach((e,n)=>{m[t].children[n].setAttributeNS(null,"d",e)}):m[t].children[0].setAttributeNS(null,"d",o)}}))}dblclick(e){null!=this.connection_selected&&this.reroute&&this.createReroutePoint(this.connection_selected),"point"===e.target.classList[0]&&this.removeReroutePoint(e.target)}createReroutePoint(e){this.connection_selected.classList.remove("selected");const t=this.connection_selected.parentElement.classList[2].slice(9),n=this.connection_selected.parentElement.classList[1].slice(13),i=this.connection_selected.parentElement.classList[3],s=this.connection_selected.parentElement.classList[4];this.connection_selected=null;const o=document.createElementNS("http://www.w3.org/2000/svg","circle");o.classList.add("point");var l=this.pos_x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),c=this.pos_y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom));o.setAttributeNS(null,"cx",l),o.setAttributeNS(null,"cy",c),o.setAttributeNS(null,"r",this.reroute_width);let d=0;if(this.reroute_fix_curvature){const t=e.parentElement.querySelectorAll(".main-path").length;var a=document.createElementNS("http://www.w3.org/2000/svg","path");if(a.classList.add("main-path"),a.setAttributeNS(null,"d",""),e.parentElement.insertBefore(a,e.parentElement.children[t]),1===t)e.parentElement.appendChild(o);else{const n=Array.from(e.parentElement.children).indexOf(e);d=n,e.parentElement.insertBefore(o,e.parentElement.children[n+t+1])}}else e.parentElement.appendChild(o);const r=t.slice(5),h=this.drawflow.drawflow[this.module].data[r].outputs[i].connections.findIndex((function(e,t){return e.node===n&&e.output===s}));void 0===this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points&&(this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points=[]),this.reroute_fix_curvature?(d>0||this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points!==[]?this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.splice(d,0,{pos_x:l,pos_y:c}):this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.push({pos_x:l,pos_y:c}),e.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.remove("selected")})):this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.push({pos_x:l,pos_y:c}),this.dispatch("addReroute",r),this.updateConnectionNodes(t)}removeReroutePoint(e){const t=e.parentElement.classList[2].slice(9),n=e.parentElement.classList[1].slice(13),i=e.parentElement.classList[3],s=e.parentElement.classList[4];let o=Array.from(e.parentElement.children).indexOf(e);const l=t.slice(5),c=this.drawflow.drawflow[this.module].data[l].outputs[i].connections.findIndex((function(e,t){return e.node===n&&e.output===s}));if(this.reroute_fix_curvature){const t=e.parentElement.querySelectorAll(".main-path").length;e.parentElement.children[t-1].remove(),o-=t,o<0&&(o=0)}else o--;this.drawflow.drawflow[this.module].data[l].outputs[i].connections[c].points.splice(o,1),e.remove(),this.dispatch("removeReroute",l),this.updateConnectionNodes(t)}registerNode(e,t,n=null,i=null){this.noderegister[e]={html:t,props:n,options:i}}getNodeFromId(e){var t=this.getModuleFromNodeId(e);return JSON.parse(JSON.stringify(this.drawflow.drawflow[t].data[e]))}getNodesFromName(e){var t=[];const n=this.drawflow.drawflow;return Object.keys(n).map((function(i,s){for(var o in n[i].data)n[i].data[o].name==e&&t.push(n[i].data[o].id)})),t}addNode(e,t,n,i,s,o,l,c,d=!1){if(this.useuuid)var a=this.getUuid();else a=this.nodeId;const r=document.createElement("div");r.classList.add("parent-node");const h=document.createElement("div");h.innerHTML="",h.setAttribute("id","node-"+a),h.classList.add("drawflow-node"),""!=o&&h.classList.add(...o.split(" "));const u=document.createElement("div");u.classList.add("inputs");const p=document.createElement("div");p.classList.add("outputs");const f={};for(var m=0;me(this.noderegister[c].html,{props:this.noderegister[c].props}),...this.noderegister[c].options}).$mount();_.appendChild(e.$el)}Object.entries(l).forEach((function(e,t){if("object"==typeof e[1])!function e(t,n,i){if(null===t)t=l[n];else t=t[n];null!==t&&Object.entries(t).forEach((function(n,s){if("object"==typeof n[1])e(t,n[0],i+"-"+n[0]);else for(var o=_.querySelectorAll("[df-"+i+"-"+n[0]+"]"),l=0;lt(this.noderegister[e.html].html,{props:this.noderegister[e.html].props}),...this.noderegister[e.html].options}).$mount();c.appendChild(t.$el)}Object.entries(e.data).forEach((function(t,n){if("object"==typeof t[1])!function t(n,i,s){if(null===n)n=e.data[i];else n=n[i];null!==n&&Object.entries(n).forEach((function(e,i){if("object"==typeof e[1])t(n,e[0],s+"-"+e[0]);else for(var o=c.querySelectorAll("[df-"+s+"-"+e[0]+"]"),l=0;l{const a=e.outputs[s].connections[o].node,r=e.outputs[s].connections[o].output,h=i.querySelector(".connection.node_in_node-"+a+".node_out_node-"+e.id+"."+s+"."+r);if(n&&0===d)for(var u=0;u{this.removeSingleConnection(e.id_output,e.id,e.output_class,e.input_class)}),delete this.drawflow.drawflow[n].data[e].inputs[t];const o=[],l=this.drawflow.drawflow[n].data[e].inputs;Object.keys(l).map((function(e,t){o.push(l[e])})),this.drawflow.drawflow[n].data[e].inputs={};const c=t.slice(6);let d=[];if(o.forEach((t,i)=>{t.connections.forEach((e,t)=>{d.push(e)}),this.drawflow.drawflow[n].data[e].inputs["input_"+(i+1)]=t}),d=new Set(d.map(e=>JSON.stringify(e))),d=Array.from(d).map(e=>JSON.parse(e)),this.module===n){this.container.querySelectorAll("#node-"+e+" .inputs .input").forEach((e,t)=>{const n=e.classList[1].slice(6);parseInt(c){this.drawflow.drawflow[n].data[t.node].outputs[t.input].connections.forEach((i,s)=>{if(i.node==e){const o=i.output.slice(6);if(parseInt(c){this.removeSingleConnection(e.id,e.id_input,e.output_class,e.input_class)}),delete this.drawflow.drawflow[n].data[e].outputs[t];const o=[],l=this.drawflow.drawflow[n].data[e].outputs;Object.keys(l).map((function(e,t){o.push(l[e])})),this.drawflow.drawflow[n].data[e].outputs={};const c=t.slice(7);let d=[];if(o.forEach((t,i)=>{t.connections.forEach((e,t)=>{d.push({node:e.node,output:e.output})}),this.drawflow.drawflow[n].data[e].outputs["output_"+(i+1)]=t}),d=new Set(d.map(e=>JSON.stringify(e))),d=Array.from(d).map(e=>JSON.parse(e)),this.module===n){this.container.querySelectorAll("#node-"+e+" .outputs .output").forEach((e,t)=>{const n=e.classList[1].slice(7);parseInt(c){this.drawflow.drawflow[n].data[t.node].inputs[t.output].connections.forEach((i,s)=>{if(i.node==e){const o=i.input.slice(7);if(parseInt(c)-1){this.module===s&&this.container.querySelector(".connection.node_in_node-"+t+".node_out_node-"+e+"."+n+"."+i).remove();var o=this.drawflow.drawflow[s].data[e].outputs[n].connections.findIndex((function(e,n){return e.node==t&&e.output===i}));this.drawflow.drawflow[s].data[e].outputs[n].connections.splice(o,1);var l=this.drawflow.drawflow[s].data[t].inputs[i].connections.findIndex((function(t,i){return t.node==e&&t.input===n}));return this.drawflow.drawflow[s].data[t].inputs[i].connections.splice(l,1),this.dispatch("connectionRemoved",{output_id:e,input_id:t,output_class:n,input_class:i}),!0}return!1}return!1}removeConnectionNodeId(e){const t="node_in_"+e,n="node_out_"+e,i=this.container.querySelectorAll("."+n);for(var s=i.length-1;s>=0;s--){var o=i[s].classList,l=this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.findIndex((function(e,t){return e.node===o[2].slice(14)&&e.input===o[3]}));this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.splice(l,1);var c=this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.findIndex((function(e,t){return e.node===o[1].slice(13)&&e.output===o[4]}));this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.splice(c,1),i[s].remove(),this.dispatch("connectionRemoved",{output_id:o[2].slice(14),input_id:o[1].slice(13),output_class:o[3],input_class:o[4]})}const d=this.container.querySelectorAll("."+t);for(s=d.length-1;s>=0;s--){o=d[s].classList,c=this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.findIndex((function(e,t){return e.node===o[1].slice(13)&&e.output===o[4]}));this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.splice(c,1);l=this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.findIndex((function(e,t){return e.node===o[2].slice(14)&&e.input===o[3]}));this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.splice(l,1),d[s].remove(),this.dispatch("connectionRemoved",{output_id:o[2].slice(14),input_id:o[1].slice(13),output_class:o[3],input_class:o[4]})}}getModuleFromNodeId(e){var t;const n=this.drawflow.drawflow;return Object.keys(n).map((function(i,s){Object.keys(n[i].data).map((function(n,s){n==e&&(t=i)}))})),t}addModule(e){this.drawflow.drawflow[e]={data:{}},this.dispatch("moduleCreated",e)}changeModule(e){this.dispatch("moduleChanged",e),this.module=e,this.precanvas.innerHTML="",this.canvas_x=0,this.canvas_y=0,this.pos_x=0,this.pos_y=0,this.mouse_x=0,this.mouse_y=0,this.zoom=1,this.zoom_last_value=1,this.precanvas.style.transform="",this.import(this.drawflow,!1)}removeModule(e){this.module===e&&this.changeModule("Home"),delete this.drawflow.drawflow[e],this.dispatch("moduleRemoved",e)}clearModuleSelected(){this.precanvas.innerHTML="",this.drawflow.drawflow[this.module]={data:{}}}clear(){this.precanvas.innerHTML="",this.drawflow={drawflow:{Home:{data:{}}}}}export(){const e=JSON.parse(JSON.stringify(this.drawflow));return this.dispatch("export",e),e}import(e,t=!0){this.clear(),this.drawflow=JSON.parse(JSON.stringify(e)),this.load(),t&&this.dispatch("import","import")}on(e,t){return"function"!=typeof t?(console.error("The listener callback must be a function, the given type is "+typeof t),!1):"string"!=typeof e?(console.error("The event name must be a string, the given type is "+typeof e),!1):(void 0===this.events[e]&&(this.events[e]={listeners:[]}),void this.events[e].listeners.push(t))}removeListener(e,t){if(!this.events[e])return!1;const n=this.events[e].listeners,i=n.indexOf(t);i>-1&&n.splice(i,1)}dispatch(e,t){if(void 0===this.events[e])return!1;this.events[e].listeners.forEach(e=>{e(t)})}getUuid(){for(var e=[],t=0;t<36;t++)e[t]="0123456789abcdef".substr(Math.floor(16*Math.random()),1);return e[14]="4",e[19]="0123456789abcdef".substr(3&e[19]|8,1),e[8]=e[13]=e[18]=e[23]="-",e.join("")}}}]).default})); \ No newline at end of file diff --git a/resources/singbox/README.md b/resources/singbox/README.md new file mode 100644 index 0000000..f752d35 --- /dev/null +++ b/resources/singbox/README.md @@ -0,0 +1,17 @@ +# sing-box 引擎(全局代理功能) + +「全局代理」功能内置 [sing-box](https://github.com/SagerNet/sing-box) 做本地中转 / TUN。 +这些二进制体积较大(每个约 45–50MB),**不纳入 git**。 + +打包 / 运行前先拉取一次: + +```bash +bash scripts/fetch-singbox.sh +``` + +会下载到: +- `darwin-arm64/sing-box` +- `darwin-x64/sing-box` +- `windows-x64/sing-box.exe` + +electron-builder 会按目标架构把对应二进制打进安装包(见 `electron-builder.yml` 的 `extraResources`)。 diff --git a/scripts/afterPack.js b/scripts/afterPack.js new file mode 100644 index 0000000..e9d8a50 --- /dev/null +++ b/scripts/afterPack.js @@ -0,0 +1,26 @@ +// afterPack 钩子:未购买 Apple 证书时,electron-builder(24) 会「跳过签名」, +// 导致 .app 的 bundle 封印(seal)残缺(codesign --verify 报 "code has no resources +// but signature indicates they must be present")。这里在打 DMG 前,用 ad-hoc 身份 +// 对整个 app(含内置 sing-box)做一次完整深度重新签名,得到有效封印—— +// 用户去掉隔离标记后即可稳定运行。 +const { execFileSync } = require('child_process'); +const path = require('path'); + +exports.default = async function afterPack(context) { + if (context.electronPlatformName !== 'darwin') return; + + const appName = `${context.packager.appInfo.productFilename}.app`; + const appPath = path.join(context.appOutDir, appName); + + console.log(` • ad-hoc 深度重签名 app=${appPath}`); + // --force 覆盖已有签名,--deep 递归签所有嵌套二进制(sing-box 等),-s - 表示 ad-hoc + execFileSync('codesign', ['--force', '--deep', '--sign', '-', appPath], { + stdio: 'inherit', + }); + + // 校验封印是否完整(失败则中断构建,避免发出坏包) + execFileSync('codesign', ['--verify', '--deep', '--strict', '--verbose=2', appPath], { + stdio: 'inherit', + }); + console.log(' • ad-hoc 签名校验通过 ✔'); +}; diff --git a/scripts/fetch-singbox.sh b/scripts/fetch-singbox.sh new file mode 100644 index 0000000..cf618ff --- /dev/null +++ b/scripts/fetch-singbox.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# 下载全局代理功能所需的 sing-box 引擎二进制(macOS arm64/x64 + Windows x64)。 +# 这些二进制不进 git(太大),打包前跑一次即可。用法: bash scripts/fetch-singbox.sh +set -euo pipefail +VER="${SINGBOX_VERSION:-1.13.14}" +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +DEST="$ROOT/resources/singbox" +mkdir -p "$DEST" +cd "$DEST" + +fetch() { # $1=平台目录 $2=资源名(不含扩展) $3=压缩类型 $4=包内二进制名 + local dir="$1" asset="$2" kind="$3" bin="$4" + echo "▶ $dir …" + if [ -f "$dir/$bin" ]; then echo " 已存在,跳过"; return; fi + mkdir -p "$dir" + local url="https://github.com/SagerNet/sing-box/releases/download/v${VER}/${asset}.${kind}" + if [ "$kind" = "tar.gz" ]; then + curl -fsSL "$url" -o t.tgz && tar xzf t.tgz && mv "${asset}/${bin}" "$dir/" && rm -rf t.tgz "$asset" + else + curl -fsSL "$url" -o t.zip && unzip -qo t.zip && mv "${asset}/${bin}" "$dir/" && rm -rf t.zip "$asset" + fi + chmod +x "$dir/$bin" 2>/dev/null || true + echo " ✓ $dir/$bin" +} + +fetch "darwin-arm64" "sing-box-${VER}-darwin-arm64" "tar.gz" "sing-box" +fetch "darwin-x64" "sing-box-${VER}-darwin-amd64" "tar.gz" "sing-box" +fetch "windows-x64" "sing-box-${VER}-windows-amd64" "zip" "sing-box.exe" +echo "全部就位 ✅ (sing-box v${VER})" diff --git a/scripts/publish-hot.js b/scripts/publish-hot.js new file mode 100644 index 0000000..4897723 --- /dev/null +++ b/scripts/publish-hot.js @@ -0,0 +1,124 @@ +#!/usr/bin/env node +// publish-hot.js —— 发布热更新包到自建 gitea。 +// +// 用法: +// GIT_TOKEN=xxx node scripts/publish-hot.js --version 1.5.1 [--min 1.5.0] \ +// [--notes "更新说明"] [--mandatory] [--requires-full] +// token 也可放在 ~/.tokendun-git-token 文件里(优先用环境变量 GIT_TOKEN)。 +// +// 流程:打包 electron/+renderer/ 成 zip → sha256(+可选签名) → 写 hotupdate.json +// → gitea 的 tag=hot release:删旧 bundle/manifest、传新的。 +"use strict"; +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const crypto = require("crypto"); +const { execFileSync } = require("child_process"); + +const ROOT = path.join(__dirname, ".."); +const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8")); + +// —— 参数 —— +const argv = process.argv.slice(2); +function arg(name, def) { + const i = argv.indexOf(name); + return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[i + 1] : def; +} +const has = (name) => argv.includes(name); +const version = arg("--version", pkg.hotContentVersion || pkg.version); +const minAppVersion = arg("--min", pkg.version); +const notes = arg("--notes", ""); +const mandatory = has("--mandatory"); +const requiresFull = has("--requires-full"); + +// —— token —— +let token = process.env.GIT_TOKEN || ""; +if (!token) { + try { token = fs.readFileSync(path.join(os.homedir(), ".tokendun-git-token"), "utf8").trim(); } catch {} +} +if (!token) { console.error("缺少 gitea token:设置 GIT_TOKEN 或写入 ~/.tokendun-git-token"); process.exit(1); } + +const OWNER = "backup-bot", REPO = "backup", TAG = "hot"; +const API = `https://up.tokendun.cc/api/v1/repos/${OWNER}/${REPO}`; +const DL = `https://up.tokendun.cc/${OWNER}/${REPO}/releases/download/${TAG}`; + +const OUT = path.join(ROOT, "dist-hot"); +fs.mkdirSync(OUT, { recursive: true }); +const bundleName = `bundle-${version}.zip`; +const bundlePath = path.join(OUT, bundleName); + +// —— 1) 打包(zip,deflate,可被 lib/zip.js 解压)—— +fs.rmSync(bundlePath, { force: true }); +console.log(`打包 electron/ + renderer/ → ${bundleName} …`); +execFileSync("zip", ["-r", "-X", "-q", bundlePath, "electron", "renderer", + "-x", "*.map", "-x", "*/node_modules/*"], { cwd: ROOT, stdio: "inherit" }); + +// —— 2) sha256(+ 可选 ed25519 签名)—— +const buf = fs.readFileSync(bundlePath); +const sha256 = crypto.createHash("sha256").update(buf).digest("hex"); +let signature = ""; +try { + const keyPath = path.join(os.homedir(), ".codex-hot-key.pem"); // 私钥(不入库) + if (fs.existsSync(keyPath)) { + const key = fs.readFileSync(keyPath); + signature = crypto.sign(null, Buffer.from(`${sha256}:${version}`), key).toString("base64"); + console.log("已用本地私钥签名"); + } +} catch (e) { console.warn("签名跳过:" + e.message); } + +// —— 3) manifest —— +const manifest = { + contentVersion: version, + minAppVersion, + requiresFull, + bundleUrl: `${DL}/${bundleName}`, + size: buf.length, + sha256, + signature, + notes, + mandatory, +}; +const manifestPath = path.join(OUT, "hotupdate.json"); +fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); +console.log(`sha256=${sha256} size=${(buf.length / 1048576).toFixed(2)}MB`); + +// —— 4) gitea API —— +function api(method, urlPath, body) { + const args = ["-s", "-X", method, "-H", `Authorization: token ${token}`, `${API}${urlPath}`]; + if (body !== undefined) { args.push("-H", "Content-Type: application/json", "-d", JSON.stringify(body)); } + return execFileSync("curl", args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }); +} +function apiJson(method, urlPath, body) { + const out = api(method, urlPath, body); + try { return JSON.parse(out); } catch { return null; } +} + +// 确保 hot release 存在 +let rel = apiJson("GET", `/releases/tags/${TAG}`); +if (!rel || !rel.id) { + console.log("创建 hot release …"); + rel = apiJson("POST", `/releases`, { tag_name: TAG, target_commitish: "main", name: "热更新通道", body: "asar 热更新包(自动发布)", draft: false, prerelease: false }); +} +if (!rel || !rel.id) { console.error("无法创建/获取 hot release"); process.exit(1); } + +// 删除同名旧 asset +for (const a of rel.assets || []) { + if (a.name === bundleName || a.name === "hotupdate.json" || /^bundle-.*\.zip$/.test(a.name)) { + api("DELETE", `/releases/${rel.id}/assets/${a.id}`); + console.log("删除旧 asset:", a.name); + } +} +// 上传 bundle + manifest +function upload(file, name) { + const code = execFileSync("curl", ["-s", "-o", "/dev/null", "-w", "%{http_code}", + "-X", "POST", "-H", `Authorization: token ${token}`, + "-F", `attachment=@${file};type=application/octet-stream`, + `${API}/releases/${rel.id}/assets?name=${encodeURIComponent(name)}`], { encoding: "utf8" }); + console.log(`上传 ${name} → HTTP ${code}`); +} +upload(bundlePath, bundleName); +upload(manifestPath, "hotupdate.json"); + +console.log(`\n✅ 热更 v${version} 已发布。`); +console.log(` minAppVersion=${minAppVersion} mandatory=${mandatory} requiresFull=${requiresFull}`); +console.log(` 客户端将从 ${API}/releases/tags/${TAG} 检测到。`);