texlite 0.2.0 → 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 CHANGED
@@ -5,6 +5,7 @@ texLite is a lightweight, local-first web workspace for writing, compiling, prev
5
5
  **Documentation:** English (this file) · [简体中文](README.zh-CN.md)
6
6
 
7
7
  [![CI](https://github.com/SWUFE-DB-Group/TexLite/actions/workflows/ci.yml/badge.svg)](https://github.com/SWUFE-DB-Group/TexLite/actions/workflows/ci.yml)
8
+ [![npm version](https://img.shields.io/npm/v/texlite?logo=npm&label=npm)](https://www.npmjs.com/package/texlite)
8
9
 
9
10
  ![texLite preview](preview.png)
10
11
 
@@ -25,7 +26,7 @@ texLite is a lightweight, local-first web workspace for writing, compiling, prev
25
26
  - List/grid project views, search, sorting by last modification or creation time, and private Finder-style color tags.
26
27
  - Project rename, source ZIP download, deletion, owner and modification metadata.
27
28
  - English and Chinese UI with browser-language detection and a language switcher.
28
- - CodeMirror LaTeX editing with syntax highlighting, folding, completion, quick file opening, current-file and project-wide search/replace, a cross-file outline, and configurable appearance.
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.
29
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.
30
31
  - Automatic content-addressed project history with labels, file comparison, single-file restore, and full-project restore checkpoints.
31
32
  - Source-range comments with replies, resolved state, user/time metadata, and anchor remapping after edits.
@@ -45,6 +46,7 @@ texLite is a lightweight, local-first web workspace for writing, compiling, prev
45
46
  | Database | SQLite via better-sqlite3 |
46
47
  | Files | Local project directories under the configured data directory |
47
48
  | Compilation | Host latexmk and a configured LaTeX engine |
49
+ | Formatting | Optional host `tex-fmt` executable |
48
50
  | Git backup | Host git and the GitHub REST API |
49
51
 
50
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.
@@ -56,6 +58,7 @@ texLite is designed as a single Node.js process. Do not run multiple application
56
58
  - git (optional; required only for the project-owner Git/GitHub integration)
57
59
  - latexmk
58
60
  - At least one configured engine: pdflatex, xelatex, or lualatex
61
+ - `tex-fmt` (optional; required only when using LaTeX formatting)
59
62
 
60
63
  Check the host before initialization:
61
64
 
@@ -64,12 +67,17 @@ node --version
64
67
  npm --version
65
68
  latexmk --version
66
69
  xelatex --version
70
+ # Optional, only when using LaTeX formatting:
71
+ tex-fmt --version
67
72
  # Optional, when Git/GitHub integration is needed:
68
73
  git --version
69
74
  ~~~
70
75
 
71
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.
72
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
+
73
81
  ## Quick start
74
82
 
75
83
  ### Global npm installation
@@ -102,7 +110,12 @@ different configuration file.
102
110
 
103
111
  `texlite serve` runs the server in the foreground and is suitable for Docker,
104
112
  systemd, and debugging. `start`, `status`, `stop`, `restart`, and `logs` use the
105
- 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.
106
119
 
107
120
  For a clean upgrade:
108
121
 
@@ -279,6 +292,7 @@ TEXLITE_HISTORY_MAX_VERSIONS TEXLITE_HISTORY_MAX_STORAGE_MB
279
292
  TEXLITE_LATEXMK TEXLITE_DEFAULT_ENGINE
280
293
  TEXLITE_COMPILE_TIMEOUT TEXLITE_MAX_COMPILE_JOBS
281
294
  TEXLITE_GIT TEXLITE_GIT_TIMEOUT
295
+ TEXLITE_TEX_FMT
282
296
  TEXLITE_GITHUB_API_URL
283
297
  ~~~
284
298
 
@@ -297,6 +311,9 @@ texlite restart
297
311
  texlite stop
298
312
  ~~~
299
313
 
314
+ `texlite status` uses a colored, systemctl-style terminal view. Use
315
+ `texlite status --json` for scripts and monitoring integrations.
316
+
300
317
  `texlite doctor` validates configuration, paths, LaTeX, and the administrator;
301
318
  add `--git` to check the optional Git integration. `texlite config` prints the
302
319
  effective paths and values. `texlite serve` keeps the process in the foreground
package/README.zh-CN.md CHANGED
@@ -5,6 +5,7 @@ texLite 是一个轻量、以本机为中心的 LaTeX 网页工作区,用于
5
5
  **文档:** [English](README.md) · 简体中文(当前文件)
6
6
 
7
7
  [![CI](https://github.com/SWUFE-DB-Group/TexLite/actions/workflows/ci.yml/badge.svg)](https://github.com/SWUFE-DB-Group/TexLite/actions/workflows/ci.yml)
8
+ [![npm version](https://img.shields.io/npm/v/texlite?logo=npm&label=npm)](https://www.npmjs.com/package/texlite)
8
9
 
9
10
  ![texLite 预览](preview.png)
10
11
 
@@ -25,7 +26,7 @@ texLite 是一个轻量、以本机为中心的 LaTeX 网页工作区,用于
25
26
  - 项目列表/网格视图、搜索、按最后修改时间或创建时间排序,以及每个用户私有的 Finder 风格彩色标签。
26
27
  - 项目重命名、源码 ZIP 下载、删除,以及所有者和修改信息展示。
27
28
  - 中文和英文界面,自动识别浏览器语言,并可手动切换。
28
- - 基于 CodeMirror 的 LaTeX 编辑器:语法高亮、折叠、补全、快速打开文件、当前文件及全项目搜索/替换、跨文件大纲和外观设置。
29
+ - 基于 CodeMirror 的 LaTeX 编辑器:语法高亮、折叠、补全、快速打开文件、当前文件及全项目搜索/替换、跨文件大纲、外观设置,以及可选的宿主机 `tex-fmt` 选区格式化和编译前格式化。
29
30
  - 基于 Yjs CRDT 的协作编辑,显示活动会话头像、远程光标,保存状态以服务端持久化确认为准,并用浏览器 IndexedDB 保留断线草稿;单个项目建议不超过约 10 个并发会话。
30
31
  - 内容寻址的自动项目历史:可添加版本标签、比较文件、恢复单个文件或带安全检查点地恢复整个项目。
31
32
  - 源码范围批注、回复、resolved 状态、用户/时间信息,以及源码变化后的批注锚点重映射。
@@ -45,6 +46,7 @@ texLite 是一个轻量、以本机为中心的 LaTeX 网页工作区,用于
45
46
  | 数据库 | better-sqlite3 访问 SQLite |
46
47
  | 文件 | 配置数据目录下的本地项目目录 |
47
48
  | 编译 | 宿主机 latexmk 和配置的 LaTeX 引擎 |
49
+ | 格式化 | 可选的宿主机 `tex-fmt` 可执行文件 |
48
50
  | Git 备份 | 宿主机 git 和 GitHub REST API |
49
51
 
50
52
  texLite 按单个 Node.js 进程设计。不要让多个应用实例同时使用同一个 SQLite 数据库或项目目录;协作状态、编译队列和文件系统都是单进程资源。
@@ -56,6 +58,7 @@ texLite 按单个 Node.js 进程设计。不要让多个应用实例同时使用
56
58
  - git(可选;仅项目所有者使用 Git/GitHub 集成时需要)
57
59
  - latexmk
58
60
  - 至少一个配置好的引擎:pdflatex、xelatex 或 lualatex
61
+ - `tex-fmt`(可选;仅使用 LaTeX 格式化功能时需要)
59
62
 
60
63
  初始化前可以检查:
61
64
 
@@ -64,12 +67,17 @@ node --version
64
67
  npm --version
65
68
  latexmk --version
66
69
  xelatex --version
70
+ # 可选,仅在使用 LaTeX 格式化时检查:
71
+ tex-fmt --version
67
72
  # 可选,仅在需要 Git/GitHub 集成时检查:
68
73
  git --version
69
74
  ~~~
70
75
 
71
76
  npm run init 和应用启动会检查 latexmk 以及 latex.allowedEngines 中列出的每个引擎。Git 不参与这项核心检查,因此未安装 Git 的宿主机仍可正常初始化并运行 TexLite。项目所有者打开 Git 面板或执行 Git/GitHub 操作时,TexLite 才会按需检查 git.binary;如果 Git 不可用,界面会给出可操作的错误提示。
72
77
 
78
+ 格式化功能同样是可选的。如果服务器的 `PATH` 中没有 `tex-fmt`,格式化控件会提示安装方法;TexLite 不会静默替换成其他格式化器。支持 `.tex`、`.bib`、`.cls` 和 `.sty`,项目中的 `tex-fmt.toml` 配置会自动生效。
79
+ 如果 PM2 使用了受限的 `PATH`,可以将 `TEXLITE_TEX_FMT` 设置为宿主机可执行文件的绝对路径。
80
+
73
81
  ## 快速开始
74
82
 
75
83
  ### 全局 npm 安装
@@ -100,7 +108,10 @@ texlite logs
100
108
  指定其他配置文件。
101
109
 
102
110
  `texlite serve` 以前台方式运行,适合 Docker、systemd 和调试。
103
- `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
+ 当前版本的包路径和环境变量。启动失败的重试次数是有限的,不会进入无限重启循环。
104
115
 
105
116
  升级时可以执行:
106
117
 
@@ -270,6 +281,7 @@ TEXLITE_HISTORY_MAX_VERSIONS TEXLITE_HISTORY_MAX_STORAGE_MB
270
281
  TEXLITE_LATEXMK TEXLITE_DEFAULT_ENGINE
271
282
  TEXLITE_COMPILE_TIMEOUT TEXLITE_MAX_COMPILE_JOBS
272
283
  TEXLITE_GIT TEXLITE_GIT_TIMEOUT
284
+ TEXLITE_TEX_FMT
273
285
  TEXLITE_GITHUB_API_URL
274
286
  ~~~
275
287
 
@@ -287,6 +299,9 @@ texlite restart
287
299
  texlite stop
288
300
  ~~~
289
301
 
302
+ `texlite status` 默认使用带颜色的 systemctl 风格终端视图;脚本或监控集成可使用
303
+ `texlite status --json` 获取结构化输出。
304
+
290
305
  `texlite doctor` 会校验配置、路径、LaTeX 和管理员;使用 `--git` 可以额外
291
306
  检查可选的 Git 集成。`texlite config` 显示生效的配置和路径。
292
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};