texlite 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/README.zh-CN.md +16 -2
- package/dist/client/assets/{GitDialog-BHHxSBn3.js → GitDialog-BRIGO4No.js} +1 -1
- package/dist/client/assets/HistoryDialog-ClN5gb3K.js +1 -0
- package/dist/client/assets/{LatexEditor-B-0VRUPv.js → LatexEditor-CbYL1NGq.js} +22 -22
- package/dist/client/assets/{PdfPreview-617TWFJN.js → PdfPreview-BXV75mRa.js} +1 -1
- package/dist/client/assets/{ProjectNavigationDialogs-Cpt2LVEt.js → ProjectNavigationDialogs-BK3E3V9t.js} +1 -1
- package/dist/client/assets/{SystemMetricsDialog-fkFHVVzJ.js → SystemMetricsDialog-vrmlRA2s.js} +1 -1
- package/dist/client/assets/index-CtVAEMSo.css +1 -0
- package/dist/client/assets/index-DvWbA6GV.js +17 -0
- package/dist/client/assets/index-rBWMBTI-.js +5 -0
- package/dist/client/assets/{plus-DzbLkp6l.js → plus-CBJCSqRG.js} +1 -1
- package/dist/client/index.html +3 -3
- package/dist/server/app.js +35 -1
- package/dist/server/cli.js +95 -8
- package/dist/server/index.js +3 -1
- package/dist/server/latexFormatter.js +116 -0
- package/dist/server/pm2.js +120 -17
- package/dist/server/runtimePaths.js +1 -1
- package/dist/server/service.js +8 -0
- package/package.json +1 -1
- package/dist/client/assets/HistoryDialog-Dlv57tYf.js +0 -5
- package/dist/client/assets/index-Bc5CmFMl.js +0 -15
- package/dist/client/assets/index-CMqYAbDx.css +0 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ texLite is a lightweight, local-first web workspace for writing, compiling, prev
|
|
|
26
26
|
- List/grid project views, search, sorting by last modification or creation time, and private Finder-style color tags.
|
|
27
27
|
- Project rename, source ZIP download, deletion, owner and modification metadata.
|
|
28
28
|
- English and Chinese UI with browser-language detection and a language switcher.
|
|
29
|
-
- CodeMirror LaTeX editing with syntax highlighting, folding, completion, quick file opening, current-file and project-wide search/replace, a cross-file outline, and
|
|
29
|
+
- CodeMirror LaTeX editing with syntax highlighting, folding, completion, quick file opening, current-file and project-wide search/replace, a cross-file outline, configurable appearance, and optional host-side `tex-fmt` selection or pre-compile formatting.
|
|
30
30
|
- Yjs CRDT collaboration with active-session avatars, remote cursors, acknowledged server saves, and a browser-local IndexedDB draft for reconnect recovery; the practical limit is about ten concurrent sessions per project.
|
|
31
31
|
- Automatic content-addressed project history with labels, file comparison, single-file restore, and full-project restore checkpoints.
|
|
32
32
|
- Source-range comments with replies, resolved state, user/time metadata, and anchor remapping after edits.
|
|
@@ -46,6 +46,7 @@ texLite is a lightweight, local-first web workspace for writing, compiling, prev
|
|
|
46
46
|
| Database | SQLite via better-sqlite3 |
|
|
47
47
|
| Files | Local project directories under the configured data directory |
|
|
48
48
|
| Compilation | Host latexmk and a configured LaTeX engine |
|
|
49
|
+
| Formatting | Optional host `tex-fmt` executable |
|
|
49
50
|
| Git backup | Host git and the GitHub REST API |
|
|
50
51
|
|
|
51
52
|
texLite is designed as a single Node.js process. Do not run multiple application instances against the same SQLite database or project directory; the in-memory collaboration service and compile queue are intentionally single-instance.
|
|
@@ -57,6 +58,7 @@ texLite is designed as a single Node.js process. Do not run multiple application
|
|
|
57
58
|
- git (optional; required only for the project-owner Git/GitHub integration)
|
|
58
59
|
- latexmk
|
|
59
60
|
- At least one configured engine: pdflatex, xelatex, or lualatex
|
|
61
|
+
- `tex-fmt` (optional; required only when using LaTeX formatting)
|
|
60
62
|
|
|
61
63
|
Check the host before initialization:
|
|
62
64
|
|
|
@@ -65,12 +67,17 @@ node --version
|
|
|
65
67
|
npm --version
|
|
66
68
|
latexmk --version
|
|
67
69
|
xelatex --version
|
|
70
|
+
# Optional, only when using LaTeX formatting:
|
|
71
|
+
tex-fmt --version
|
|
68
72
|
# Optional, when Git/GitHub integration is needed:
|
|
69
73
|
git --version
|
|
70
74
|
~~~
|
|
71
75
|
|
|
72
76
|
npm run init and application startup check latexmk and every engine in latex.allowedEngines. Git is deliberately excluded from this core check, so a host without Git can initialize and run TexLite normally. When a project owner opens the Git panel or invokes a Git/GitHub operation, TexLite checks git.binary on demand and shows an actionable error if Git is unavailable.
|
|
73
77
|
|
|
78
|
+
Formatting is also optional. If `tex-fmt` is not on the server's `PATH`, the formatting control explains how to install it; TexLite does not silently substitute another formatter. The supported source types are `.tex`, `.bib`, `.cls`, and `.sty`; project-level `tex-fmt.toml` settings are honored.
|
|
79
|
+
If PM2 uses a restricted `PATH`, set `TEXLITE_TEX_FMT` to the absolute path of the host executable.
|
|
80
|
+
|
|
74
81
|
## Quick start
|
|
75
82
|
|
|
76
83
|
### Global npm installation
|
|
@@ -103,7 +110,12 @@ different configuration file.
|
|
|
103
110
|
|
|
104
111
|
`texlite serve` runs the server in the foreground and is suitable for Docker,
|
|
105
112
|
systemd, and debugging. `start`, `status`, `stop`, `restart`, and `logs` use the
|
|
106
|
-
PM2 dependency bundled with the package.
|
|
113
|
+
PM2 dependency bundled with the package. Managed startup waits until the HTTP
|
|
114
|
+
health endpoint is ready; `status` reports `unhealthy` if PM2 is running but the
|
|
115
|
+
managed process is not actually serving TexLite. `restart` recreates the PM2
|
|
116
|
+
entry so npm upgrades always use the current package paths and environment.
|
|
117
|
+
Failed startup attempts are bounded instead of entering an unlimited restart
|
|
118
|
+
loop.
|
|
107
119
|
|
|
108
120
|
For a clean upgrade:
|
|
109
121
|
|
|
@@ -280,6 +292,7 @@ TEXLITE_HISTORY_MAX_VERSIONS TEXLITE_HISTORY_MAX_STORAGE_MB
|
|
|
280
292
|
TEXLITE_LATEXMK TEXLITE_DEFAULT_ENGINE
|
|
281
293
|
TEXLITE_COMPILE_TIMEOUT TEXLITE_MAX_COMPILE_JOBS
|
|
282
294
|
TEXLITE_GIT TEXLITE_GIT_TIMEOUT
|
|
295
|
+
TEXLITE_TEX_FMT
|
|
283
296
|
TEXLITE_GITHUB_API_URL
|
|
284
297
|
~~~
|
|
285
298
|
|
|
@@ -298,6 +311,9 @@ texlite restart
|
|
|
298
311
|
texlite stop
|
|
299
312
|
~~~
|
|
300
313
|
|
|
314
|
+
`texlite status` uses a colored, systemctl-style terminal view. Use
|
|
315
|
+
`texlite status --json` for scripts and monitoring integrations.
|
|
316
|
+
|
|
301
317
|
`texlite doctor` validates configuration, paths, LaTeX, and the administrator;
|
|
302
318
|
add `--git` to check the optional Git integration. `texlite config` prints the
|
|
303
319
|
effective paths and values. `texlite serve` keeps the process in the foreground
|
package/README.zh-CN.md
CHANGED
|
@@ -26,7 +26,7 @@ texLite 是一个轻量、以本机为中心的 LaTeX 网页工作区,用于
|
|
|
26
26
|
- 项目列表/网格视图、搜索、按最后修改时间或创建时间排序,以及每个用户私有的 Finder 风格彩色标签。
|
|
27
27
|
- 项目重命名、源码 ZIP 下载、删除,以及所有者和修改信息展示。
|
|
28
28
|
- 中文和英文界面,自动识别浏览器语言,并可手动切换。
|
|
29
|
-
- 基于 CodeMirror 的 LaTeX
|
|
29
|
+
- 基于 CodeMirror 的 LaTeX 编辑器:语法高亮、折叠、补全、快速打开文件、当前文件及全项目搜索/替换、跨文件大纲、外观设置,以及可选的宿主机 `tex-fmt` 选区格式化和编译前格式化。
|
|
30
30
|
- 基于 Yjs CRDT 的协作编辑,显示活动会话头像、远程光标,保存状态以服务端持久化确认为准,并用浏览器 IndexedDB 保留断线草稿;单个项目建议不超过约 10 个并发会话。
|
|
31
31
|
- 内容寻址的自动项目历史:可添加版本标签、比较文件、恢复单个文件或带安全检查点地恢复整个项目。
|
|
32
32
|
- 源码范围批注、回复、resolved 状态、用户/时间信息,以及源码变化后的批注锚点重映射。
|
|
@@ -46,6 +46,7 @@ texLite 是一个轻量、以本机为中心的 LaTeX 网页工作区,用于
|
|
|
46
46
|
| 数据库 | better-sqlite3 访问 SQLite |
|
|
47
47
|
| 文件 | 配置数据目录下的本地项目目录 |
|
|
48
48
|
| 编译 | 宿主机 latexmk 和配置的 LaTeX 引擎 |
|
|
49
|
+
| 格式化 | 可选的宿主机 `tex-fmt` 可执行文件 |
|
|
49
50
|
| Git 备份 | 宿主机 git 和 GitHub REST API |
|
|
50
51
|
|
|
51
52
|
texLite 按单个 Node.js 进程设计。不要让多个应用实例同时使用同一个 SQLite 数据库或项目目录;协作状态、编译队列和文件系统都是单进程资源。
|
|
@@ -57,6 +58,7 @@ texLite 按单个 Node.js 进程设计。不要让多个应用实例同时使用
|
|
|
57
58
|
- git(可选;仅项目所有者使用 Git/GitHub 集成时需要)
|
|
58
59
|
- latexmk
|
|
59
60
|
- 至少一个配置好的引擎:pdflatex、xelatex 或 lualatex
|
|
61
|
+
- `tex-fmt`(可选;仅使用 LaTeX 格式化功能时需要)
|
|
60
62
|
|
|
61
63
|
初始化前可以检查:
|
|
62
64
|
|
|
@@ -65,12 +67,17 @@ node --version
|
|
|
65
67
|
npm --version
|
|
66
68
|
latexmk --version
|
|
67
69
|
xelatex --version
|
|
70
|
+
# 可选,仅在使用 LaTeX 格式化时检查:
|
|
71
|
+
tex-fmt --version
|
|
68
72
|
# 可选,仅在需要 Git/GitHub 集成时检查:
|
|
69
73
|
git --version
|
|
70
74
|
~~~
|
|
71
75
|
|
|
72
76
|
npm run init 和应用启动会检查 latexmk 以及 latex.allowedEngines 中列出的每个引擎。Git 不参与这项核心检查,因此未安装 Git 的宿主机仍可正常初始化并运行 TexLite。项目所有者打开 Git 面板或执行 Git/GitHub 操作时,TexLite 才会按需检查 git.binary;如果 Git 不可用,界面会给出可操作的错误提示。
|
|
73
77
|
|
|
78
|
+
格式化功能同样是可选的。如果服务器的 `PATH` 中没有 `tex-fmt`,格式化控件会提示安装方法;TexLite 不会静默替换成其他格式化器。支持 `.tex`、`.bib`、`.cls` 和 `.sty`,项目中的 `tex-fmt.toml` 配置会自动生效。
|
|
79
|
+
如果 PM2 使用了受限的 `PATH`,可以将 `TEXLITE_TEX_FMT` 设置为宿主机可执行文件的绝对路径。
|
|
80
|
+
|
|
74
81
|
## 快速开始
|
|
75
82
|
|
|
76
83
|
### 全局 npm 安装
|
|
@@ -101,7 +108,10 @@ texlite logs
|
|
|
101
108
|
指定其他配置文件。
|
|
102
109
|
|
|
103
110
|
`texlite serve` 以前台方式运行,适合 Docker、systemd 和调试。
|
|
104
|
-
`start`、`status`、`stop`、`restart` 和 `logs` 使用 npm 包内置的 PM2
|
|
111
|
+
`start`、`status`、`stop`、`restart` 和 `logs` 使用 npm 包内置的 PM2。后台启动会
|
|
112
|
+
等待 HTTP 健康检查真正就绪;如果 PM2 进程存在但没有实际提供 TexLite 服务,
|
|
113
|
+
`status` 会显示 `unhealthy`。`restart` 会重新创建 PM2 条目,确保 npm 升级后使用
|
|
114
|
+
当前版本的包路径和环境变量。启动失败的重试次数是有限的,不会进入无限重启循环。
|
|
105
115
|
|
|
106
116
|
升级时可以执行:
|
|
107
117
|
|
|
@@ -271,6 +281,7 @@ TEXLITE_HISTORY_MAX_VERSIONS TEXLITE_HISTORY_MAX_STORAGE_MB
|
|
|
271
281
|
TEXLITE_LATEXMK TEXLITE_DEFAULT_ENGINE
|
|
272
282
|
TEXLITE_COMPILE_TIMEOUT TEXLITE_MAX_COMPILE_JOBS
|
|
273
283
|
TEXLITE_GIT TEXLITE_GIT_TIMEOUT
|
|
284
|
+
TEXLITE_TEX_FMT
|
|
274
285
|
TEXLITE_GITHUB_API_URL
|
|
275
286
|
~~~
|
|
276
287
|
|
|
@@ -288,6 +299,9 @@ texlite restart
|
|
|
288
299
|
texlite stop
|
|
289
300
|
~~~
|
|
290
301
|
|
|
302
|
+
`texlite status` 默认使用带颜色的 systemctl 风格终端视图;脚本或监控集成可使用
|
|
303
|
+
`texlite status --json` 获取结构化输出。
|
|
304
|
+
|
|
291
305
|
`texlite doctor` 会校验配置、路径、LaTeX 和管理员;使用 `--git` 可以额外
|
|
292
306
|
检查可选的 Git 集成。`texlite config` 显示生效的配置和路径。
|
|
293
307
|
`texlite serve` 以前台方式运行,不启动 PM2。
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{u as ye,r as n,j as e}from"./react-CsO_xvbl.js";import{i as h,M as H,L as p,G as q,C as ie,j as ne,T as ke,R as be,k as l}from"./index-Bc5CmFMl.js";import{M as ve,P as Ce,a as Ne}from"./plus-DzbLkp6l.js";const we=h("CloudUpload",[["path",{d:"M12 13v8",key:"1l5pq0"}],["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242",key:"1pljnt"}],["path",{d:"m8 17 4-4 4 4",key:"1quai1"}]]);const ae=h("ExternalLink",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);const G=h("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);const oe=h("GitCommitHorizontal",[["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}],["line",{x1:"3",x2:"9",y1:"12",y2:"12",key:"1dyftd"}],["line",{x1:"15",x2:"21",y1:"12",y2:"12",key:"oup4p8"}]]);const W=h("Github",[["path",{d:"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4",key:"tonef"}],["path",{d:"M9 18c-4.51 2-5-2-7-2",key:"9comsn"}]]);const ze=h("KeyRound",[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]]);const Se=h("Minimize2",[["polyline",{points:"4 14 10 14 10 20",key:"11kfnr"}],["polyline",{points:"20 10 14 10 14 4",key:"rlmsce"}],["line",{x1:"14",x2:"21",y1:"10",y2:"3",key:"o5lafz"}],["line",{x1:"3",x2:"10",y1:"21",y2:"14",key:"1atl0r"}]]);const Ee=h("RefreshCcw",[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]]);const Te=h("Undo2",[["path",{d:"M9 14 4 9l5-5",key:"102s5s"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",key:"f3b9sd"}]]);function Ue({open:g,project:o,onOpenChange:y,onBeforeMutation:r}){const{t:s,i18n:E}=ye(),[i,m]=n.useState(null),[J,V]=n.useState([]),[T,k]=n.useState(""),[F,K]=n.useState(()=>re(o.name)),[_,ce]=n.useState(!0),[D,Z]=n.useState(""),[b,M]=n.useState(null),[f,j]=n.useState(null),[R,U]=n.useState(!1),[le,v]=n.useState(!1),[$,L]=n.useState(!0),[P,A]=n.useState(!0),[a,C]=n.useState(""),[Q,N]=n.useState(""),[X,B]=n.useState(""),[u,x]=n.useState(!1),[w,Y]=n.useState(10),O=n.useRef(null),z=async(t=!1)=>{const c=await l(`/api/projects/${o.id}/git`);if(m(c.status),t&&(L(!c.status.tokenConfigured),A(!c.status.remoteUrl)),c.status.initialized&&c.status.latestCommit){const S=await l(`/api/projects/${o.id}/git/history`);V(S.commits)}else V([])};n.useEffect(()=>{if(!g){document.fullscreenElement&&document.exitFullscreen().catch(()=>{}),x(!1),k("");return}N(""),B(""),M(null),x(!1),K(re(o.name)),C("load"),r().then(()=>z(!0)).catch(t=>N(t instanceof Error?t.message:s("errors.generic"))).finally(()=>C(""))},[g,o.id]),n.useEffect(()=>{const t=()=>x(document.fullscreenElement===O.current);return document.addEventListener("fullscreenchange",t),()=>document.removeEventListener("fullscreenchange",t)},[]),n.useEffect(()=>{if(!u)return;const t=c=>{c.key==="Escape"&&!document.fullscreenElement&&x(!1)};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[u]);const d=async(t,c,S,ee)=>{C(t),N(""),B("");try{await c(),S&&B(S)}catch(se){const te=se instanceof Error?se.message:s("errors.generic");N(ee?`${ee}: ${te}`:te)}finally{C("")}},de=()=>d("token",async()=>{const t=await l(`/api/projects/${o.id}/git/token`,{method:"PUT",body:JSON.stringify({token:T})});m(t.status),k(""),L(!1)},s("git.tokenSaved")),he=()=>d("remove-token",async()=>{const t=await l(`/api/projects/${o.id}/git/token`,{method:"DELETE"});m(t.status),k("")},s("git.tokenRemoved")),ge=()=>d("repository",async()=>{const t=await l(`/api/projects/${o.id}/git/repository`,{method:"POST",body:JSON.stringify({name:F,private:_})});m(t.status),A(!1)},s("git.repositoryCreated")),ue=()=>d("commit",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));const t=await l(`/api/projects/${o.id}/git/commit`,{method:"POST",body:JSON.stringify({message:D})});m(t.status),Z(""),M(null),await z()},s("git.committed")),me=()=>d("push",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));const t=await l(`/api/projects/${o.id}/git/push`,{method:"POST"});m(t.status),await z()},s("git.pushed"),s("git.pushFailed")),I=t=>d("diff",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));const c=t?`?revision=${encodeURIComponent(t)}`:"";M(await l(`/api/projects/${o.id}/git/diff${c}`))}),fe=async()=>{if(u){document.fullscreenElement&&await document.exitFullscreen().catch(()=>{}),x(!1);return}const t=O.current;if(t?.requestFullscreen)try{await t.requestFullscreen()}catch{}x(!0)},xe=()=>{const t=f;t&&(j(null),d("checkout",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));await l(`/api/projects/${o.id}/git/checkout`,{method:"POST",body:JSON.stringify({revision:t==="branch"?null:t.sha,force:R})}),window.location.reload()}))},pe=()=>{v(!1),d("discard",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));await l(`/api/projects/${o.id}/git/discard`,{method:"POST"}),window.location.reload()})},je=a==="load"&&!i;return e.jsxs(e.Fragment,{children:[e.jsx(H,{open:g,extraWide:!0,className:u?"git-dialog-modal-fullscreen":void 0,title:s("git.title"),description:s("git.description"),onOpenChange:y,footer:e.jsx("button",{onClick:()=>y(!1),children:s("common.close")}),children:e.jsxs("div",{className:"git-dialog",children:[je&&e.jsxs("div",{className:"git-loading",children:[e.jsx(p,{className:"spin",size:22}),s("common.loading")]}),Q&&e.jsx("p",{className:"git-message error",children:Q}),X&&e.jsxs("p",{className:"git-message success",children:[e.jsx(q,{size:14}),X]}),i&&e.jsxs(e.Fragment,{children:[e.jsxs("section",{className:"git-section",children:[e.jsxs("button",{className:"git-section-toggle",type:"button","aria-expanded":$,onClick:()=>L(t=>!t),children:[e.jsxs("span",{className:"git-section-heading",children:[e.jsx(ze,{size:16}),e.jsxs("span",{children:[e.jsx("strong",{children:s("git.authentication")}),e.jsx("small",{children:i.tokenConfigured?s("git.connectedAs",{login:i.githubLogin}):s("git.authenticationHint")})]})]}),$?e.jsx(ie,{size:16}):e.jsx(ne,{size:16})]}),$&&e.jsxs("div",{className:"git-section-content",children:[e.jsxs("div",{className:"git-token-guidance",children:[e.jsx("strong",{children:s("git.recommendedAccessTitle")}),e.jsx("span",{children:s("git.recommendedAccess")})]}),i.tokenConfigured&&e.jsxs("div",{className:"git-account",children:[e.jsx(W,{size:17}),e.jsx("span",{children:s("git.connectedAs",{login:i.githubLogin})}),e.jsxs("button",{className:"danger-text",disabled:!!a,onClick:he,children:[e.jsx(ke,{size:14}),s("git.removeToken")]})]}),e.jsxs("label",{className:"form-field",children:[i.tokenConfigured?s("git.replaceToken"):s("git.token"),e.jsx("input",{type:"password",autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",value:T,placeholder:s("git.tokenPlaceholder"),onChange:t=>k(t.target.value)})]}),e.jsxs("div",{className:"git-inline-actions",children:[e.jsxs("a",{href:"https://github.com/settings/personal-access-tokens/new",target:"_blank",rel:"noreferrer",children:[s("git.createToken"),e.jsx(ae,{size:12})]}),e.jsxs("button",{disabled:!!a||T.trim().length<20,onClick:de,children:[a==="token"&&e.jsx(p,{className:"spin",size:13}),s("git.saveToken")]})]})]})]}),e.jsxs("section",{className:"git-section",children:[e.jsxs("button",{className:"git-section-toggle",type:"button","aria-expanded":P,onClick:()=>A(t=>!t),children:[e.jsxs("span",{className:"git-section-heading",children:[e.jsx(W,{size:16}),e.jsxs("span",{children:[e.jsx("strong",{children:s("git.repository")}),e.jsx("small",{children:i.remoteUrl?i.repositoryName??i.remoteUrl:i.initialized?s("git.localReady"):s("git.localPending")})]})]}),P?e.jsx(ie,{size:16}):e.jsx(ne,{size:16})]}),P&&e.jsx("div",{className:"git-section-content",children:i.remoteUrl?e.jsxs("div",{className:"git-repository-card",children:[e.jsxs("div",{children:[e.jsx("strong",{children:i.repositoryName??s("git.origin")}),e.jsxs("small",{children:[i.branch??s("git.detachedAt",{revision:i.latestCommit?.shortSha??"HEAD"})," · ",i.dirty?s("git.changedFiles",{count:i.changedFiles}):s("git.clean")]})]}),i.repositoryHtmlUrl&&e.jsxs("a",{href:i.repositoryHtmlUrl,target:"_blank",rel:"noreferrer",children:[e.jsx(ae,{size:14}),s("git.openGitHub")]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"form-field",children:[s("git.repositoryName"),e.jsx("input",{value:F,onChange:t=>K(t.target.value)})]}),e.jsxs("label",{className:"checkbox-field",children:[e.jsx("input",{type:"checkbox",checked:_,onChange:t=>ce(t.target.checked)}),s("git.privateRepository")]}),e.jsxs("button",{className:"git-primary-action",disabled:!!a||!i.tokenConfigured||!F.trim(),onClick:ge,children:[a==="repository"?e.jsx(p,{className:"spin",size:14}):e.jsx(W,{size:14}),s("git.createRepository")]})]})})]}),i.initialized&&e.jsxs("section",{className:"git-section",children:[e.jsxs("div",{className:"git-section-heading",children:[e.jsx(oe,{size:16}),e.jsxs("div",{children:[e.jsx("strong",{children:s("git.commitAndPush")}),e.jsx("small",{children:i.branch?s("git.identity",{username:o.ownerUsername}):s("git.detachedHead")})]})]}),e.jsxs("label",{className:"form-field",children:[s("git.commitMessage"),e.jsx("textarea",{rows:3,value:D,placeholder:s("git.commitPlaceholder"),onChange:t=>Z(t.target.value)})]}),e.jsxs("div",{className:"git-action-row",children:[e.jsxs("button",{title:i.dirty?void 0:s("git.noChangesToCommit"),disabled:!!a||!i.branch||!i.dirty||!D.trim(),onClick:ue,children:[a==="commit"?e.jsx(p,{className:"spin",size:14}):e.jsx(oe,{size:14}),s("git.commit")]}),e.jsxs("button",{disabled:!!a||!i.branch||!i.tokenConfigured||!i.remoteUrl||!i.latestCommit,onClick:me,children:[a==="push"?e.jsx(p,{className:"spin",size:14}):e.jsx(we,{size:14}),s("git.push"),i.ahead>0&&e.jsx("span",{className:"git-count",children:i.ahead})]}),e.jsxs("button",{disabled:!!a||!i.latestCommit,onClick:()=>{I()},children:[a==="diff"?e.jsx(p,{className:"spin",size:14}):e.jsx(G,{size:14}),s("git.workingDiff")]}),e.jsxs("button",{className:"git-discard-button",title:i.restorable?s("git.discardChanges"):s("git.noChangesToDiscard"),disabled:!!a||!i.latestCommit||!i.restorable,onClick:()=>v(!0),children:[e.jsx(be,{size:14}),s("git.discardChanges")]}),!i.branch&&e.jsxs("button",{disabled:!!a,onClick:()=>{U(!1),j("branch")},children:[e.jsx(q,{size:14}),s("git.returnToBranch",{branch:i.defaultBranch})]}),e.jsxs("button",{disabled:!!a,onClick:()=>{d("refresh",z)},children:[e.jsx(Ee,{className:a==="refresh"?"spin":"",size:14}),s("git.refresh")]})]})]}),b&&e.jsxs("section",{ref:O,className:`git-section git-diff-section${u?" is-fullscreen":""}`,children:[e.jsxs("div",{className:"git-diff-heading",children:[e.jsxs("div",{className:"git-section-heading",children:[e.jsx(G,{size:16}),e.jsxs("div",{children:[e.jsx("strong",{children:s("git.diffTitle")}),e.jsxs("small",{children:[b.title,b.truncated?` · ${s("git.diffTruncated")}`:""]})]})]}),e.jsxs("div",{className:"git-diff-controls",children:[e.jsxs("div",{className:"git-diff-font-controls",role:"group","aria-label":s("git.diffFontSize"),children:[e.jsx("button",{type:"button",className:"git-diff-font-button",disabled:w<=8,title:s("git.diffFontDecrease"),"aria-label":s("git.diffFontDecrease"),onClick:()=>Y(t=>Math.max(8,t-1)),children:e.jsx(ve,{size:14})}),e.jsxs("span",{className:"git-diff-font-value","aria-live":"polite",children:[w,"px"]}),e.jsx("button",{type:"button",className:"git-diff-font-button",disabled:w>=24,title:s("git.diffFontIncrease"),"aria-label":s("git.diffFontIncrease"),onClick:()=>Y(t=>Math.min(24,t+1)),children:e.jsx(Ce,{size:14})})]}),e.jsx("button",{type:"button",className:"git-diff-fullscreen",title:s(u?"git.exitFullscreenDiff":"git.fullscreenDiff"),"aria-label":s(u?"git.exitFullscreenDiff":"git.fullscreenDiff"),onClick:()=>{fe()},children:u?e.jsx(Se,{size:15}):e.jsx(Ne,{size:15})})]})]}),e.jsx(Fe,{content:b.diff,empty:s("git.noChanges"),fontSize:w})]}),i.initialized&&e.jsxs("section",{className:"git-section",children:[e.jsxs("div",{className:"git-section-heading",children:[e.jsx(q,{size:16}),e.jsxs("div",{children:[e.jsx("strong",{children:s("git.history")}),e.jsx("small",{children:s("git.historyHint")})]})]}),e.jsxs("div",{className:"git-history",children:[J.map(t=>e.jsxs("article",{children:[e.jsx("code",{children:t.shortSha}),e.jsxs("div",{children:[e.jsx("strong",{children:t.message}),e.jsxs("small",{children:[t.authorName," · ",new Date(t.authoredAt).toLocaleString(E.resolvedLanguage)]})]}),e.jsxs("span",{children:[e.jsx("button",{title:s("git.viewCommitDiff"),disabled:!!a,onClick:()=>{I(t.sha)},children:e.jsx(G,{size:14})}),e.jsx("button",{title:s("git.checkoutVersion"),disabled:!!a,onClick:()=>{U(!1),j(t)},children:e.jsx(Te,{size:14})})]})]},t.sha)),J.length===0&&e.jsx("p",{className:"muted",children:s("git.noCommits")})]})]})]})]})}),e.jsx(H,{open:!!f,title:s(f==="branch"?"git.returnTitle":"git.checkoutTitle"),description:f==="branch"?s("git.returnDescription",{branch:i?.defaultBranch??"main"}):s("git.checkoutDescription",{revision:f?.shortSha??"",message:f?.message??""}),onOpenChange:t=>{t||j(null)},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>j(null),children:s("common.cancel")}),e.jsx("button",{className:R?"danger":"primary",onClick:xe,children:s("git.checkout")})]}),children:e.jsxs("label",{className:"git-force-checkout",children:[e.jsx("input",{type:"checkbox",checked:R,onChange:t=>U(t.target.checked)}),e.jsxs("span",{children:[e.jsx("strong",{children:s("git.forceCheckout")}),e.jsx("small",{children:s("git.forceCheckoutDescription")})]})]})}),e.jsx(H,{open:le,title:s("git.discardTitle"),description:s("git.discardDescription"),onOpenChange:v,footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>v(!1),children:s("common.cancel")}),e.jsx("button",{className:"danger",onClick:pe,children:s("git.discardConfirm")})]}),children:e.jsx(e.Fragment,{})})]})}function Fe({content:g,empty:o,fontSize:y}){return g.trim()?e.jsx("pre",{className:"git-diff",style:{fontSize:`${y}px`},children:g.split(`
|
|
1
|
+
import{u as ye,r as n,j as e}from"./react-CsO_xvbl.js";import{i as h,M as H,L as p,G as q,C as ie,j as ne,T as ke,R as be,k as l}from"./index-DvWbA6GV.js";import{M as ve,P as Ce,a as Ne}from"./plus-CBJCSqRG.js";const we=h("CloudUpload",[["path",{d:"M12 13v8",key:"1l5pq0"}],["path",{d:"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242",key:"1pljnt"}],["path",{d:"m8 17 4-4 4 4",key:"1quai1"}]]);const ae=h("ExternalLink",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);const G=h("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);const oe=h("GitCommitHorizontal",[["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}],["line",{x1:"3",x2:"9",y1:"12",y2:"12",key:"1dyftd"}],["line",{x1:"15",x2:"21",y1:"12",y2:"12",key:"oup4p8"}]]);const W=h("Github",[["path",{d:"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4",key:"tonef"}],["path",{d:"M9 18c-4.51 2-5-2-7-2",key:"9comsn"}]]);const ze=h("KeyRound",[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]]);const Se=h("Minimize2",[["polyline",{points:"4 14 10 14 10 20",key:"11kfnr"}],["polyline",{points:"20 10 14 10 14 4",key:"rlmsce"}],["line",{x1:"14",x2:"21",y1:"10",y2:"3",key:"o5lafz"}],["line",{x1:"3",x2:"10",y1:"21",y2:"14",key:"1atl0r"}]]);const Ee=h("RefreshCcw",[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]]);const Te=h("Undo2",[["path",{d:"M9 14 4 9l5-5",key:"102s5s"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",key:"f3b9sd"}]]);function Ue({open:g,project:o,onOpenChange:y,onBeforeMutation:r}){const{t:s,i18n:E}=ye(),[i,m]=n.useState(null),[J,V]=n.useState([]),[T,k]=n.useState(""),[F,K]=n.useState(()=>re(o.name)),[_,ce]=n.useState(!0),[D,Z]=n.useState(""),[b,M]=n.useState(null),[f,j]=n.useState(null),[R,U]=n.useState(!1),[le,v]=n.useState(!1),[$,L]=n.useState(!0),[P,A]=n.useState(!0),[a,C]=n.useState(""),[Q,N]=n.useState(""),[X,B]=n.useState(""),[u,x]=n.useState(!1),[w,Y]=n.useState(10),O=n.useRef(null),z=async(t=!1)=>{const c=await l(`/api/projects/${o.id}/git`);if(m(c.status),t&&(L(!c.status.tokenConfigured),A(!c.status.remoteUrl)),c.status.initialized&&c.status.latestCommit){const S=await l(`/api/projects/${o.id}/git/history`);V(S.commits)}else V([])};n.useEffect(()=>{if(!g){document.fullscreenElement&&document.exitFullscreen().catch(()=>{}),x(!1),k("");return}N(""),B(""),M(null),x(!1),K(re(o.name)),C("load"),r().then(()=>z(!0)).catch(t=>N(t instanceof Error?t.message:s("errors.generic"))).finally(()=>C(""))},[g,o.id]),n.useEffect(()=>{const t=()=>x(document.fullscreenElement===O.current);return document.addEventListener("fullscreenchange",t),()=>document.removeEventListener("fullscreenchange",t)},[]),n.useEffect(()=>{if(!u)return;const t=c=>{c.key==="Escape"&&!document.fullscreenElement&&x(!1)};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[u]);const d=async(t,c,S,ee)=>{C(t),N(""),B("");try{await c(),S&&B(S)}catch(se){const te=se instanceof Error?se.message:s("errors.generic");N(ee?`${ee}: ${te}`:te)}finally{C("")}},de=()=>d("token",async()=>{const t=await l(`/api/projects/${o.id}/git/token`,{method:"PUT",body:JSON.stringify({token:T})});m(t.status),k(""),L(!1)},s("git.tokenSaved")),he=()=>d("remove-token",async()=>{const t=await l(`/api/projects/${o.id}/git/token`,{method:"DELETE"});m(t.status),k("")},s("git.tokenRemoved")),ge=()=>d("repository",async()=>{const t=await l(`/api/projects/${o.id}/git/repository`,{method:"POST",body:JSON.stringify({name:F,private:_})});m(t.status),A(!1)},s("git.repositoryCreated")),ue=()=>d("commit",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));const t=await l(`/api/projects/${o.id}/git/commit`,{method:"POST",body:JSON.stringify({message:D})});m(t.status),Z(""),M(null),await z()},s("git.committed")),me=()=>d("push",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));const t=await l(`/api/projects/${o.id}/git/push`,{method:"POST"});m(t.status),await z()},s("git.pushed"),s("git.pushFailed")),I=t=>d("diff",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));const c=t?`?revision=${encodeURIComponent(t)}`:"";M(await l(`/api/projects/${o.id}/git/diff${c}`))}),fe=async()=>{if(u){document.fullscreenElement&&await document.exitFullscreen().catch(()=>{}),x(!1);return}const t=O.current;if(t?.requestFullscreen)try{await t.requestFullscreen()}catch{}x(!0)},xe=()=>{const t=f;t&&(j(null),d("checkout",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));await l(`/api/projects/${o.id}/git/checkout`,{method:"POST",body:JSON.stringify({revision:t==="branch"?null:t.sha,force:R})}),window.location.reload()}))},pe=()=>{v(!1),d("discard",async()=>{if(!await r())throw new Error(s("errors.collaborationUnavailable"));await l(`/api/projects/${o.id}/git/discard`,{method:"POST"}),window.location.reload()})},je=a==="load"&&!i;return e.jsxs(e.Fragment,{children:[e.jsx(H,{open:g,extraWide:!0,className:u?"git-dialog-modal-fullscreen":void 0,title:s("git.title"),description:s("git.description"),onOpenChange:y,footer:e.jsx("button",{onClick:()=>y(!1),children:s("common.close")}),children:e.jsxs("div",{className:"git-dialog",children:[je&&e.jsxs("div",{className:"git-loading",children:[e.jsx(p,{className:"spin",size:22}),s("common.loading")]}),Q&&e.jsx("p",{className:"git-message error",children:Q}),X&&e.jsxs("p",{className:"git-message success",children:[e.jsx(q,{size:14}),X]}),i&&e.jsxs(e.Fragment,{children:[e.jsxs("section",{className:"git-section",children:[e.jsxs("button",{className:"git-section-toggle",type:"button","aria-expanded":$,onClick:()=>L(t=>!t),children:[e.jsxs("span",{className:"git-section-heading",children:[e.jsx(ze,{size:16}),e.jsxs("span",{children:[e.jsx("strong",{children:s("git.authentication")}),e.jsx("small",{children:i.tokenConfigured?s("git.connectedAs",{login:i.githubLogin}):s("git.authenticationHint")})]})]}),$?e.jsx(ie,{size:16}):e.jsx(ne,{size:16})]}),$&&e.jsxs("div",{className:"git-section-content",children:[e.jsxs("div",{className:"git-token-guidance",children:[e.jsx("strong",{children:s("git.recommendedAccessTitle")}),e.jsx("span",{children:s("git.recommendedAccess")})]}),i.tokenConfigured&&e.jsxs("div",{className:"git-account",children:[e.jsx(W,{size:17}),e.jsx("span",{children:s("git.connectedAs",{login:i.githubLogin})}),e.jsxs("button",{className:"danger-text",disabled:!!a,onClick:he,children:[e.jsx(ke,{size:14}),s("git.removeToken")]})]}),e.jsxs("label",{className:"form-field",children:[i.tokenConfigured?s("git.replaceToken"):s("git.token"),e.jsx("input",{type:"password",autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",value:T,placeholder:s("git.tokenPlaceholder"),onChange:t=>k(t.target.value)})]}),e.jsxs("div",{className:"git-inline-actions",children:[e.jsxs("a",{href:"https://github.com/settings/personal-access-tokens/new",target:"_blank",rel:"noreferrer",children:[s("git.createToken"),e.jsx(ae,{size:12})]}),e.jsxs("button",{disabled:!!a||T.trim().length<20,onClick:de,children:[a==="token"&&e.jsx(p,{className:"spin",size:13}),s("git.saveToken")]})]})]})]}),e.jsxs("section",{className:"git-section",children:[e.jsxs("button",{className:"git-section-toggle",type:"button","aria-expanded":P,onClick:()=>A(t=>!t),children:[e.jsxs("span",{className:"git-section-heading",children:[e.jsx(W,{size:16}),e.jsxs("span",{children:[e.jsx("strong",{children:s("git.repository")}),e.jsx("small",{children:i.remoteUrl?i.repositoryName??i.remoteUrl:i.initialized?s("git.localReady"):s("git.localPending")})]})]}),P?e.jsx(ie,{size:16}):e.jsx(ne,{size:16})]}),P&&e.jsx("div",{className:"git-section-content",children:i.remoteUrl?e.jsxs("div",{className:"git-repository-card",children:[e.jsxs("div",{children:[e.jsx("strong",{children:i.repositoryName??s("git.origin")}),e.jsxs("small",{children:[i.branch??s("git.detachedAt",{revision:i.latestCommit?.shortSha??"HEAD"})," · ",i.dirty?s("git.changedFiles",{count:i.changedFiles}):s("git.clean")]})]}),i.repositoryHtmlUrl&&e.jsxs("a",{href:i.repositoryHtmlUrl,target:"_blank",rel:"noreferrer",children:[e.jsx(ae,{size:14}),s("git.openGitHub")]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"form-field",children:[s("git.repositoryName"),e.jsx("input",{value:F,onChange:t=>K(t.target.value)})]}),e.jsxs("label",{className:"checkbox-field",children:[e.jsx("input",{type:"checkbox",checked:_,onChange:t=>ce(t.target.checked)}),s("git.privateRepository")]}),e.jsxs("button",{className:"git-primary-action",disabled:!!a||!i.tokenConfigured||!F.trim(),onClick:ge,children:[a==="repository"?e.jsx(p,{className:"spin",size:14}):e.jsx(W,{size:14}),s("git.createRepository")]})]})})]}),i.initialized&&e.jsxs("section",{className:"git-section",children:[e.jsxs("div",{className:"git-section-heading",children:[e.jsx(oe,{size:16}),e.jsxs("div",{children:[e.jsx("strong",{children:s("git.commitAndPush")}),e.jsx("small",{children:i.branch?s("git.identity",{username:o.ownerUsername}):s("git.detachedHead")})]})]}),e.jsxs("label",{className:"form-field",children:[s("git.commitMessage"),e.jsx("textarea",{rows:3,value:D,placeholder:s("git.commitPlaceholder"),onChange:t=>Z(t.target.value)})]}),e.jsxs("div",{className:"git-action-row",children:[e.jsxs("button",{title:i.dirty?void 0:s("git.noChangesToCommit"),disabled:!!a||!i.branch||!i.dirty||!D.trim(),onClick:ue,children:[a==="commit"?e.jsx(p,{className:"spin",size:14}):e.jsx(oe,{size:14}),s("git.commit")]}),e.jsxs("button",{disabled:!!a||!i.branch||!i.tokenConfigured||!i.remoteUrl||!i.latestCommit,onClick:me,children:[a==="push"?e.jsx(p,{className:"spin",size:14}):e.jsx(we,{size:14}),s("git.push"),i.ahead>0&&e.jsx("span",{className:"git-count",children:i.ahead})]}),e.jsxs("button",{disabled:!!a||!i.latestCommit,onClick:()=>{I()},children:[a==="diff"?e.jsx(p,{className:"spin",size:14}):e.jsx(G,{size:14}),s("git.workingDiff")]}),e.jsxs("button",{className:"git-discard-button",title:i.restorable?s("git.discardChanges"):s("git.noChangesToDiscard"),disabled:!!a||!i.latestCommit||!i.restorable,onClick:()=>v(!0),children:[e.jsx(be,{size:14}),s("git.discardChanges")]}),!i.branch&&e.jsxs("button",{disabled:!!a,onClick:()=>{U(!1),j("branch")},children:[e.jsx(q,{size:14}),s("git.returnToBranch",{branch:i.defaultBranch})]}),e.jsxs("button",{disabled:!!a,onClick:()=>{d("refresh",z)},children:[e.jsx(Ee,{className:a==="refresh"?"spin":"",size:14}),s("git.refresh")]})]})]}),b&&e.jsxs("section",{ref:O,className:`git-section git-diff-section${u?" is-fullscreen":""}`,children:[e.jsxs("div",{className:"git-diff-heading",children:[e.jsxs("div",{className:"git-section-heading",children:[e.jsx(G,{size:16}),e.jsxs("div",{children:[e.jsx("strong",{children:s("git.diffTitle")}),e.jsxs("small",{children:[b.title,b.truncated?` · ${s("git.diffTruncated")}`:""]})]})]}),e.jsxs("div",{className:"git-diff-controls",children:[e.jsxs("div",{className:"git-diff-font-controls",role:"group","aria-label":s("git.diffFontSize"),children:[e.jsx("button",{type:"button",className:"git-diff-font-button",disabled:w<=8,title:s("git.diffFontDecrease"),"aria-label":s("git.diffFontDecrease"),onClick:()=>Y(t=>Math.max(8,t-1)),children:e.jsx(ve,{size:14})}),e.jsxs("span",{className:"git-diff-font-value","aria-live":"polite",children:[w,"px"]}),e.jsx("button",{type:"button",className:"git-diff-font-button",disabled:w>=24,title:s("git.diffFontIncrease"),"aria-label":s("git.diffFontIncrease"),onClick:()=>Y(t=>Math.min(24,t+1)),children:e.jsx(Ce,{size:14})})]}),e.jsx("button",{type:"button",className:"git-diff-fullscreen",title:s(u?"git.exitFullscreenDiff":"git.fullscreenDiff"),"aria-label":s(u?"git.exitFullscreenDiff":"git.fullscreenDiff"),onClick:()=>{fe()},children:u?e.jsx(Se,{size:15}):e.jsx(Ne,{size:15})})]})]}),e.jsx(Fe,{content:b.diff,empty:s("git.noChanges"),fontSize:w})]}),i.initialized&&e.jsxs("section",{className:"git-section",children:[e.jsxs("div",{className:"git-section-heading",children:[e.jsx(q,{size:16}),e.jsxs("div",{children:[e.jsx("strong",{children:s("git.history")}),e.jsx("small",{children:s("git.historyHint")})]})]}),e.jsxs("div",{className:"git-history",children:[J.map(t=>e.jsxs("article",{children:[e.jsx("code",{children:t.shortSha}),e.jsxs("div",{children:[e.jsx("strong",{children:t.message}),e.jsxs("small",{children:[t.authorName," · ",new Date(t.authoredAt).toLocaleString(E.resolvedLanguage)]})]}),e.jsxs("span",{children:[e.jsx("button",{title:s("git.viewCommitDiff"),disabled:!!a,onClick:()=>{I(t.sha)},children:e.jsx(G,{size:14})}),e.jsx("button",{title:s("git.checkoutVersion"),disabled:!!a,onClick:()=>{U(!1),j(t)},children:e.jsx(Te,{size:14})})]})]},t.sha)),J.length===0&&e.jsx("p",{className:"muted",children:s("git.noCommits")})]})]})]})]})}),e.jsx(H,{open:!!f,title:s(f==="branch"?"git.returnTitle":"git.checkoutTitle"),description:f==="branch"?s("git.returnDescription",{branch:i?.defaultBranch??"main"}):s("git.checkoutDescription",{revision:f?.shortSha??"",message:f?.message??""}),onOpenChange:t=>{t||j(null)},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>j(null),children:s("common.cancel")}),e.jsx("button",{className:R?"danger":"primary",onClick:xe,children:s("git.checkout")})]}),children:e.jsxs("label",{className:"git-force-checkout",children:[e.jsx("input",{type:"checkbox",checked:R,onChange:t=>U(t.target.checked)}),e.jsxs("span",{children:[e.jsx("strong",{children:s("git.forceCheckout")}),e.jsx("small",{children:s("git.forceCheckoutDescription")})]})]})}),e.jsx(H,{open:le,title:s("git.discardTitle"),description:s("git.discardDescription"),onOpenChange:v,footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>v(!1),children:s("common.cancel")}),e.jsx("button",{className:"danger",onClick:pe,children:s("git.discardConfirm")})]}),children:e.jsx(e.Fragment,{})})]})}function Fe({content:g,empty:o,fontSize:y}){return g.trim()?e.jsx("pre",{className:"git-diff",style:{fontSize:`${y}px`},children:g.split(`
|
|
2
2
|
`).map((r,s)=>{const E=r.startsWith("+")&&!r.startsWith("+++")?"addition":r.startsWith("-")&&!r.startsWith("---")?"deletion":r.startsWith("@@")?"hunk":r.startsWith("diff ")?"header":"";return e.jsxs("span",{className:E,children:[r,`
|
|
3
3
|
`]},s)})}):e.jsx("div",{className:"git-diff-empty",children:o})}function re(g){return g.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"-").replace(/-+/g,"-").replace(/^[.-]+|[.-]+$/g,"").slice(0,100)||"texlite-project"}export{Ue as GitDialog};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{u as Z,r as a,j as e}from"./react-CsO_xvbl.js";import{d as ee}from"./index-rBWMBTI-.js";import{i as T,k as j,M as se,L as D,T as _,R as I,S as te,F as ie,l as q}from"./index-DvWbA6GV.js";const re=T("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);const F=T("FileClock",[["path",{d:"M16 22h2a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3",key:"37hlfg"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["circle",{cx:"8",cy:"16",r:"6",key:"10v15b"}],["path",{d:"M9.5 17.5 8 16.25V14",key:"1o80t2"}]]);const ae=T("HardDrive",[["line",{x1:"22",x2:"2",y1:"12",y2:"12",key:"1y58io"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"oot6mr"}],["line",{x1:"6",x2:"6.01",y1:"16",y2:"16",key:"sgf278"}],["line",{x1:"10",x2:"10.01",y1:"16",y2:"16",key:"1l4acy"}]]);const oe=T("Tag",[["path",{d:"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z",key:"vktsd0"}],["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor",key:"kqv944"}]]);function de({open:r,project:c,onOpenChange:M,onBeforeMutation:V}){const{t,i18n:L}=Z(),[v,N]=a.useState([]),[l,z]=a.useState(null),[x,S]=a.useState(""),[o,b]=a.useState(null),[C,P]=a.useState(""),[u,p]=a.useState(null),[B,H]=a.useState(""),[k,w]=a.useState(null),[m,$]=a.useState(null),[y,n]=a.useState(""),[R,d]=a.useState(""),E=c.permission!=="read",U=c.permission==="owner";a.useEffect(()=>{if(!r)return;const s=new AbortController;return n("load"),d(""),p(null),j(`/api/projects/${c.id}/history`,{signal:s.signal}).then(i=>{N(i.versions),z(i.stats),S(h=>i.versions.some(g=>g.id===h)?h:i.versions[0]?.id??"")}).catch(i=>{J(i)||d(f(i))}).finally(()=>{s.signal.aborted||n("")}),()=>s.abort()},[r,c.id]),a.useEffect(()=>{if(!r||!x){b(null);return}const s=new AbortController;return n("detail"),p(null),j(`/api/projects/${c.id}/history/${x}`,{signal:s.signal}).then(i=>{b(i),H(i.version.label??"");const h=i.version.changedPaths.find(g=>i.files.some(A=>A.path===g))??i.files[0]?.path??"";P(h)}).catch(i=>{J(i)||d(f(i))}).finally(()=>{s.signal.aborted||n("")}),()=>s.abort()},[r,c.id,x]);const G=async(s=C)=>{if(s){n("compare"),d("");try{p(await j(`/api/projects/${c.id}/history/${x}/file?path=${encodeURIComponent(s)}`))}catch(i){d(f(i))}finally{n("")}}},K=async()=>{if(o){n("label"),d("");try{const s=await j(`/api/projects/${c.id}/history/${o.version.id}`,{method:"PATCH",body:JSON.stringify({label:B.trim()||null})});b(i=>i&&{...i,version:s.version}),N(i=>i.map(h=>h.id===s.version.id?s.version:h)),z(s.stats)}catch(s){d(f(s))}finally{n("")}}},W=async()=>{const s=m;if(s){$(null),n("delete"),d("");try{if(!await V())throw new Error(t("errors.collaborationUnavailable"));const i=s==="all"?`/api/projects/${c.id}/history`:`/api/projects/${c.id}/history/${s.id}`,h=await j(i,{method:"DELETE"});if(z(h.stats),s==="all")N([]),S(""),b(null),p(null);else{const g=v.filter(A=>A.id!==s.id);N(g),x===s.id&&(S(g[0]?.id??""),b(null),p(null))}}catch(i){d(f(i))}finally{n("")}}},Q=async()=>{const s=k;if(!(!s||!o)){w(null),n("restore"),d("");try{if(!await V())throw new Error(t("errors.collaborationUnavailable"));await j(`/api/projects/${c.id}/history/${o.version.id}/restore`,{method:"POST",body:JSON.stringify(s==="project"?{}:{path:s})}),window.location.reload()}catch(i){d(f(i)),n("")}}},X=a.useMemo(()=>{if(!u)return[];const s=new ee;s.Diff_Timeout=1;const i=s.diff_main(u.historical,u.comparison,!0);return s.diff_cleanupSemantic(i),i},[u]),Y=m&&m!=="all"?m.label||new Date(m.createdAt).toLocaleString(L.resolvedLanguage):"";return e.jsxs(e.Fragment,{children:[e.jsx(se,{open:r,extraWide:!0,className:"history-dialog-modal",title:t("history.title"),description:t("history.description"),onOpenChange:M,footer:e.jsx("button",{onClick:()=>M(!1),children:t("common.close")}),children:e.jsxs("div",{className:"history-dialog",children:[R&&e.jsx("p",{className:"error history-message",children:R}),y==="load"&&v.length===0?e.jsxs("div",{className:"history-loading",children:[e.jsx(D,{className:"spin",size:22}),t("common.loading")]}):e.jsxs(e.Fragment,{children:[e.jsxs("aside",{className:"history-timeline",children:[U&&l&&e.jsxs("div",{className:`history-stats${l.storageLimitExceeded?" exceeded":""}`,children:[e.jsxs("header",{children:[e.jsxs("span",{children:[e.jsx(ae,{size:14}),t("history.storage")]}),e.jsx("button",{type:"button",disabled:!!y||v.length===0,title:t("history.clearAll"),"aria-label":t("history.clearAll"),onClick:()=>$("all"),children:e.jsx(_,{size:13})})]}),e.jsx("strong",{children:t("history.storageUsage",{used:O(l.objectBytes),limit:O(l.maxStorageBytes)})}),e.jsx("progress",{max:l.maxStorageBytes,value:Math.min(l.objectBytes,l.maxStorageBytes)}),e.jsxs("small",{children:[t("history.versionUsage",{count:l.ordinaryVersionCount,limit:l.maxVersions}),l.labeledVersionCount>0?` · ${t("history.protectedVersions",{count:l.labeledVersionCount})}`:""]}),l.storageLimitExceeded&&e.jsx("small",{className:"history-storage-warning",children:t("history.storageExceeded")})]}),v.map(s=>e.jsxs("button",{className:s.id===x?"active":"",onClick:()=>S(s.id),children:[e.jsx(F,{size:15}),e.jsxs("span",{children:[e.jsx("strong",{children:s.label||t(`history.reasons.${s.reason}`)}),e.jsx("small",{children:new Date(s.createdAt).toLocaleString(L.resolvedLanguage)}),e.jsxs("small",{children:[s.author?.name??t("editor.deletedUser")," · ",t("history.changedCount",{count:s.changedPaths.length})]})]})]},s.id)),v.length===0&&e.jsx("p",{className:"muted padded",children:t("history.empty")})]}),e.jsx("section",{className:"history-detail",children:o&&e.jsxs(e.Fragment,{children:[e.jsxs("header",{className:"history-detail-header",children:[e.jsxs("div",{children:[e.jsx("strong",{children:o.version.label||t(`history.reasons.${o.version.reason}`)}),e.jsxs("small",{children:[e.jsx(re,{size:12}),new Date(o.version.createdAt).toLocaleString(L.resolvedLanguage)," · ",t("history.fileCount",{count:o.version.fileCount})]})]}),e.jsxs("span",{className:"history-detail-actions",children:[U&&e.jsxs("button",{className:"danger-text",disabled:!!y,onClick:()=>$(o.version),children:[e.jsx(_,{size:14}),t("history.deleteVersion")]}),E&&e.jsxs("button",{className:"danger-text",disabled:!!y,onClick:()=>w("project"),children:[e.jsx(I,{size:14}),t("history.restoreProject")]})]})]}),E&&e.jsxs("div",{className:"history-label",children:[e.jsxs("label",{children:[e.jsx(oe,{size:14}),e.jsx("input",{value:B,maxLength:80,placeholder:t("history.labelPlaceholder"),onChange:s=>H(s.target.value)})]}),e.jsxs("button",{disabled:y==="label"||B.trim()===(o.version.label??""),onClick:()=>{K()},children:[y==="label"?e.jsx(D,{className:"spin",size:13}):e.jsx(te,{size:13}),t("history.saveLabel")]})]}),e.jsxs("div",{className:"history-files",children:[e.jsx("div",{className:"history-file-list",children:o.files.map(s=>{const i=o.version.changedPaths.includes(s.path);return e.jsxs("button",{className:s.path===C?"active":"",title:s.path,onClick:()=>{P(s.path),p(null)},children:[e.jsx(ie,{size:13}),e.jsx("span",{children:s.path}),i&&e.jsx("small",{children:t("history.changed")})]},s.path)})}),e.jsxs("div",{className:"history-file-actions",children:[e.jsxs("button",{disabled:!C||!!y,onClick:()=>{G()},children:[y==="compare"?e.jsx(D,{className:"spin",size:13}):e.jsx(F,{size:13}),t("history.compareCurrent")]}),E&&e.jsxs("button",{disabled:!C||!!y,onClick:()=>w(C),children:[e.jsx(I,{size:13}),t("history.restoreFile")]})]})]}),e.jsx("div",{className:"history-comparison",children:u?e.jsxs(e.Fragment,{children:[e.jsxs("header",{children:[e.jsx("strong",{children:u.path}),e.jsx("small",{children:t("history.comparingCurrent")})]}),e.jsx("pre",{children:X.map(([s,i],h)=>e.jsx("span",{className:s<0?"history-removed":s>0?"history-added":"",children:i},h))})]}):e.jsxs("div",{className:"history-comparison-empty",children:[e.jsx(F,{size:25}),e.jsx("span",{children:t("history.chooseFile")})]})})]})})]})]})}),e.jsx(q,{open:!!k,title:t(k==="project"?"history.restoreProjectTitle":"history.restoreFileTitle"),description:k==="project"?t("history.restoreProjectDescription"):t("history.restoreFileDescription",{path:k??""}),confirmLabel:t("history.restore"),danger:!0,onCancel:()=>w(null),onConfirm:()=>{Q()}}),e.jsx(q,{open:!!m,title:t(m==="all"?"history.clearAllTitle":"history.deleteVersionTitle"),description:m==="all"?t("history.clearAllDescription"):t("history.deleteVersionDescription",{name:Y}),confirmLabel:t(m==="all"?"history.clearAll":"history.deleteVersion"),danger:!0,onCancel:()=>$(null),onConfirm:()=>{W()}})]})}function O(r){return r<1024?`${r} B`:r<1024*1024?`${Math.round(r/102.4)/10} KB`:r<1024*1024*1024?`${Math.round(r/(1024*1024)*10)/10} MB`:`${Math.round(r/(1024*1024*1024)*10)/10} GB`}function f(r){return r instanceof Error?r.message:String(r)}function J(r){return r instanceof Error&&r.name==="AbortError"}export{de as HistoryDialog};
|