texlite 0.7.7 → 0.8.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/DESIGN.md +460 -0
- package/NPM_TESTING.md +130 -0
- package/OPERATIONS.md +246 -0
- package/README.md +78 -306
- package/README.zh-CN.md +61 -330
- package/dist/client/assets/{GitDialog-Bm5yMpQK.js → GitDialog-DdLrbz_H.js} +1 -1
- package/dist/client/assets/{HistoryDialog-Po4KHT_2.js → HistoryDialog-AlF9iozX.js} +1 -1
- package/dist/client/assets/{LatexEditor-CnKCXaHr.js → LatexEditor-DH9v1wgS.js} +22 -22
- package/dist/client/assets/{PdfPreview-Cd20VIjT.js → PdfPreview-CanYnyfJ.js} +1 -1
- package/dist/client/assets/ProjectNavigationDialogs-DiXtkTj1.js +1 -0
- package/dist/client/assets/{SystemMetricsDialog-DdOlcqqK.js → SystemMetricsDialog-DimBI7er.js} +1 -1
- package/dist/client/assets/index-CdiutUgf.css +1 -0
- package/dist/client/assets/index-vpWlpUOr.js +64 -0
- package/dist/client/assets/{minus-O6pK-6mA.js → minus-DR-ttrx3.js} +1 -1
- package/dist/client/assets/spellCheck-Wn7_eF9j.js +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/app.js +3 -3
- package/dist/server/compiler.js +139 -10
- package/dist/server/harper.js +210 -119
- package/dist/server/latexSpellMask.js +257 -0
- package/dist/server/projects.js +0 -12
- package/dist/server/routes/compile.js +54 -41
- package/dist/server/routes/projectFiles.js +11 -0
- package/dist/server/routes/projectShared.js +1 -1
- package/dist/server/routes/projects.js +20 -19
- package/package.json +4 -2
- package/dist/client/assets/ProjectNavigationDialogs-BDOtIoTy.js +0 -1
- package/dist/client/assets/index-MDwr9W9R.css +0 -1
- package/dist/client/assets/index-zIjCeZFK.js +0 -64
- package/dist/client/assets/spellCheck-Ce3o_xO-.js +0 -4
package/README.zh-CN.md
CHANGED
|
@@ -1,46 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# TexLite
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
**文档:** [English](README.md) · [Design(英文)](DESIGN.md) · 简体中文(当前文件)
|
|
3
|
+
面向*小型、可信*研究团队的轻量自托管 Overleaf 替代方案。直接使用现有的 LaTeX
|
|
4
|
+
发行版,无需沉重的服务栈。
|
|
6
5
|
|
|
7
6
|
[](https://github.com/SWUFE-DB-Group/TexLite/actions/workflows/ci.yml)
|
|
8
7
|
[](https://www.npmjs.com/package/texlite)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
**文档:** [English](README.md) · [运维指南(英文)](OPERATIONS.md) · [设计文档(英文)](DESIGN.md) · 简体中文(当前文件)
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
请参阅英文文档 [DESIGN.md](DESIGN.md)。
|
|
11
|
+
**网站:** [TexLite GitHub Pages](https://swufe-db-group.github.io/TexLite/)
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+

|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
- npm
|
|
19
|
-
- git(可选;仅项目所有者使用 Git/GitHub 集成时需要)
|
|
20
|
-
- latexmk
|
|
21
|
-
- 至少一个配置好的引擎:pdflatex、xelatex 或 lualatex
|
|
15
|
+
## 为什么选择 TexLite
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
- **掌控写作环境。** 直接使用宿主机 TeX 环境,并将源码、历史版本和编译产物保存在
|
|
18
|
+
一个本地数据目录中。
|
|
19
|
+
- **无需庞大服务栈即可协作。** 默认部署只有一个 Node.js 进程、SQLite 和本地文件,
|
|
20
|
+
同时提供实时编辑和源码级批注,适合少量可信协作者。
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
node --version
|
|
27
|
-
npm --version
|
|
28
|
-
latexmk --version
|
|
29
|
-
xelatex --version
|
|
30
|
-
# 可选,仅在需要 Git/GitHub 集成时检查:
|
|
31
|
-
git --version
|
|
32
|
-
~~~
|
|
22
|
+
## 与 Overleaf 的实际区别
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
[Overleaf](https://www.overleaf.com/about/features-overview) 在团队需要其托管产品或更完整生态时
|
|
25
|
+
仍然是很好的选择。TexLite 面向的是更窄的自托管场景:
|
|
35
26
|
|
|
36
|
-
|
|
27
|
+
- 共享托管服务在使用高峰期可能排队、变慢或出现编译超时。
|
|
28
|
+
- Overleaf 开源 [Community Edition](https://github.com/overleaf/overleaf) 的
|
|
29
|
+
[Docker 部署路径](https://docs.overleaf.com/on-premises/getting-started/what-is-the-overleaf-toolkit) 更复杂,且
|
|
30
|
+
[源码批注仅限 Server Pro](https://docs.overleaf.com/on-premises/user-and-project-management/roles-and-permissions)。
|
|
31
|
+
- TexLite 直接使用服务器已有的 TeX 环境,以较小的单机栈运行,同时提供实时源码批注
|
|
32
|
+
与回复。
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
自托管并不保证每份文档都编译得更快,速度仍取决于宿主机和文档本身;它带来的是对容量、
|
|
35
|
+
TeX 更新、数据位置与协作工作流的掌控。
|
|
36
|
+
|
|
37
|
+
若是以个人、本地桌面工作流为主,建议优先考虑
|
|
38
|
+
[VS Code + LaTeX Workshop](https://github.com/James-Yu/LaTeX-Workshop) 或
|
|
39
|
+
[TeXstudio](https://texstudio.org/)。TexLite 的目标是浏览器中的共同写作,而不是取代个人 IDE。
|
|
40
|
+
|
|
41
|
+
## 核心写作工作流
|
|
42
|
+
|
|
43
|
+
- 项目支持文件夹、ZIP 导入/导出、标签、分享、所有权转让、归档和私有的个人引用库。
|
|
44
|
+
- 基于 CodeMirror 的编辑器提供 LaTeX/BibTeX 高亮、折叠、补全、可选 Vim 模式、格式化、
|
|
45
|
+
拼写/语法辅助、搜索替换及源码/PDF SyncTeX 跳转。
|
|
46
|
+
- 基于 Yjs 的协同源码编辑支持活动会话、源码锚定批注、回复、解决状态,以及让审阅者
|
|
47
|
+
可批注但不能修改源码的权限模型。
|
|
48
|
+
- 使用 `latexmk` 和可选择引擎进行编译,提供项目设置、结构化诊断、缓存的成功 PDF、
|
|
49
|
+
可下载产物以及可选的项目级 `latexmkrc`。
|
|
50
|
+
- 每个项目拥有历史版本和仅所有者可用的 Git/GitHub 备份;Git 是可选依赖,仅在使用
|
|
51
|
+
集成时才检查。
|
|
39
52
|
|
|
40
|
-
|
|
53
|
+
## 快速开始
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
安装 Node.js 24 或更高版本、`latexmk`,以及至少一个 TeX 引擎,例如 `pdflatex`、
|
|
56
|
+
`xelatex` 或 `lualatex`。只有使用可选 Git/GitHub 集成时才需要 Git。
|
|
44
57
|
|
|
45
58
|
~~~bash
|
|
46
59
|
npm install --global texlite
|
|
@@ -49,319 +62,37 @@ texlite start
|
|
|
49
62
|
texlite status
|
|
50
63
|
~~~
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
~~~bash
|
|
55
|
-
texlite stop
|
|
56
|
-
texlite restart
|
|
57
|
-
texlite logs
|
|
58
|
-
~~~
|
|
59
|
-
|
|
60
|
-
默认配置文件为
|
|
61
|
-
`$XDG_CONFIG_HOME/texlite/texlite.config.json`;如果没有设置
|
|
62
|
-
`XDG_CONFIG_HOME`,则为 `~/.config/texlite/texlite.config.json`。默认数据目录为
|
|
63
|
-
`$XDG_DATA_HOME/texlite`;如果没有设置 `XDG_DATA_HOME`,则为
|
|
64
|
-
`~/.local/share/texlite`。可以使用 `--config PATH` 或 `TEXLITE_CONFIG`
|
|
65
|
-
指定其他配置文件。
|
|
65
|
+
访问 <http://127.0.0.1:3000>。`texlite init` 会创建配置并建立第一个管理员;TexLite
|
|
66
|
+
不开放公众注册。
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
`start`、`status`、`stop`、`restart` 和 `logs` 使用 npm 包内置的 PM2。后台启动会
|
|
69
|
-
等待 HTTP 健康检查真正就绪;如果 PM2 进程存在但没有实际提供 TexLite 服务,
|
|
70
|
-
`status` 会显示 `unhealthy`。`restart` 会重新创建 PM2 条目,确保 npm 升级后使用
|
|
71
|
-
当前版本的包路径和环境变量。启动失败的重试次数是有限的,不会进入无限重启循环。
|
|
72
|
-
|
|
73
|
-
升级时可以执行:
|
|
68
|
+
升级与日常管理:
|
|
74
69
|
|
|
75
70
|
~~~bash
|
|
76
71
|
npm update --global texlite
|
|
77
72
|
texlite restart
|
|
78
|
-
~~~
|
|
79
|
-
|
|
80
|
-
卸载 npm 包不会删除配置和项目数据:
|
|
81
|
-
|
|
82
|
-
~~~bash
|
|
83
|
-
npm uninstall --global texlite
|
|
84
|
-
~~~
|
|
85
|
-
|
|
86
|
-
### 从仓库/源码运行
|
|
87
|
-
|
|
88
|
-
~~~bash
|
|
89
|
-
npm ci
|
|
90
|
-
cp texlite.config.example.json texlite.config.json
|
|
91
|
-
export TEXLITE_CONFIG="$PWD/texlite.config.json"
|
|
92
|
-
# 按需要编辑 texlite.config.json
|
|
93
|
-
npm run init
|
|
94
|
-
npm run build
|
|
95
|
-
npm start
|
|
96
|
-
~~~
|
|
97
|
-
|
|
98
|
-
默认只监听 localhost,也不开放公众注册。初始化命令会要求创建第一个
|
|
99
|
-
管理员;没有有效管理员时服务不会启动。
|
|
100
|
-
|
|
101
|
-
如果需要非交互初始化,可以只为该命令设置以下环境变量:
|
|
102
|
-
|
|
103
|
-
~~~bash
|
|
104
|
-
TEXLITE_INIT_USERNAME=admin \
|
|
105
|
-
TEXLITE_INIT_DISPLAY_NAME=Administrator \
|
|
106
|
-
TEXLITE_INIT_PASSWORD='至少 8 个字符的密码' \
|
|
107
|
-
npm run init
|
|
108
|
-
~~~
|
|
109
|
-
|
|
110
|
-
在共享机器上不要让密码进入 shell 历史记录。
|
|
111
|
-
|
|
112
|
-
## 开发和验证
|
|
113
|
-
|
|
114
|
-
在两个终端分别运行 API/server 和 Vite:
|
|
115
|
-
|
|
116
|
-
~~~bash
|
|
117
|
-
npm run dev # API/server:http://127.0.0.1:3000
|
|
118
|
-
npm run dev:web # Vite 界面:http://127.0.0.1:5173
|
|
119
|
-
~~~
|
|
120
|
-
|
|
121
|
-
Vite 会把 /api 请求代理到后端。生产式运行前可以执行:
|
|
122
|
-
|
|
123
|
-
~~~bash
|
|
124
|
-
npm run typecheck
|
|
125
|
-
npm test
|
|
126
|
-
npm run build
|
|
127
|
-
npm start
|
|
128
|
-
~~~
|
|
129
|
-
|
|
130
|
-
## 配置
|
|
131
|
-
|
|
132
|
-
配置文件路径优先级如下:
|
|
133
|
-
|
|
134
|
-
1. `texlite --config PATH`;
|
|
135
|
-
2. `TEXLITE_CONFIG`;
|
|
136
|
-
3. `$XDG_CONFIG_HOME/texlite/texlite.config.json`;
|
|
137
|
-
4. `~/.config/texlite/texlite.config.json`。
|
|
138
|
-
|
|
139
|
-
配置中的相对路径以配置文件所在目录为基准。`storage.dataDir` 默认使用
|
|
140
|
-
`$XDG_DATA_HOME/texlite`;未设置时使用 `~/.local/share/texlite`。可以在配置中
|
|
141
|
-
设置 `storage.dataDir`,或使用 `TEXLITE_DATA_DIR` 指定其他数据目录。生产环境
|
|
142
|
-
的前端资源默认定位到 npm 包内部的 `dist/client`,开发或自定义部署可以用
|
|
143
|
-
`TEXLITE_CLIENT_DIR` 覆盖。
|
|
144
|
-
|
|
145
|
-
示例配置:
|
|
146
|
-
|
|
147
|
-
~~~json
|
|
148
|
-
{
|
|
149
|
-
"siteName": "TexLite",
|
|
150
|
-
"adminEmail": "admin@example.com",
|
|
151
|
-
"sessionDays": 14,
|
|
152
|
-
"server": { "host": "127.0.0.1", "port": 3000 },
|
|
153
|
-
"storage": { "dataDir": ".texlite" },
|
|
154
|
-
"uploads": { "maxFileSizeMB": 50 },
|
|
155
|
-
"history": { "maxVersions": 200, "maxStorageMB": 128 },
|
|
156
|
-
"git": {
|
|
157
|
-
"binary": "git",
|
|
158
|
-
"operationTimeoutSeconds": 120,
|
|
159
|
-
"githubApiBaseUrl": "https://api.github.com"
|
|
160
|
-
},
|
|
161
|
-
"latex": {
|
|
162
|
-
"latexmk": "latexmk",
|
|
163
|
-
"defaultEngine": "xelatex",
|
|
164
|
-
"allowedEngines": ["pdflatex", "xelatex", "lualatex"],
|
|
165
|
-
"extraArgs": [],
|
|
166
|
-
"allowProjectLatexmkrc": true,
|
|
167
|
-
"compileTimeoutSeconds": 600,
|
|
168
|
-
"maxCompileJobs": 10
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
~~~
|
|
172
|
-
|
|
173
|
-
仓库中的示例为了方便源码开发,显式使用 `.texlite`。`texlite init` 自动生成
|
|
174
|
-
的 npm 配置则使用前面所述的 XDG 数据目录默认值。
|
|
175
|
-
|
|
176
|
-
重要配置:
|
|
177
|
-
|
|
178
|
-
- server.host、server.port:监听地址和端口。除非已经准备好安全部署,否则保持 127.0.0.1。
|
|
179
|
-
- storage.dataDir:SQLite 数据库、项目源码、编译输出和 Git token 加密密钥的位置。
|
|
180
|
-
- uploads.maxFileSizeMB:项目上传、ZIP 条目、项目文件和资源的最大大小,默认 50 MB。
|
|
181
|
-
- history.maxVersions:每个项目最多保留的普通、未标记版本数;初始版本和已标记版本受保护。
|
|
182
|
-
- history.maxStorageMB:每个项目去重历史对象的软上限。超过后优先删除最旧的普通版本;受保护版本和当前内部基线可使实际用量超过上限。
|
|
183
|
-
- latex.defaultEngine、latex.allowedEngines、latex.extraArgs:界面中可选择的编译方式。
|
|
184
|
-
- latex.compileTimeoutSeconds:单次编译超时时间。
|
|
185
|
-
- latex.maxCompileJobs:全局 LaTeX 进程并发上限。同一项目的同一主文档串行编译;多个源码版本排队时只保留最新请求。不同主文档使用独立工作区,可在该全局上限内并行编译。
|
|
186
|
-
- latex.allowProjectLatexmkrc:是否允许项目提供多行 latexmkrc。该文件是可执行的 Perl 配置,只建议对可信用户开启。
|
|
187
|
-
|
|
188
|
-
### 生效默认值和启动校验
|
|
189
|
-
|
|
190
|
-
如果没有设置某项,texLite 使用以下内置默认值(自动生成的示例配置
|
|
191
|
-
可以显式写出例如管理员邮箱这样的值):
|
|
192
|
-
|
|
193
|
-
| 配置项 | 生效默认值 |
|
|
194
|
-
| --- | --- |
|
|
195
|
-
| `siteName` | `TexLite` |
|
|
196
|
-
| `adminEmail` | 空 |
|
|
197
|
-
| `server.host` / `server.port` | `127.0.0.1` / `3000` |
|
|
198
|
-
| `storage.dataDir` | `$XDG_DATA_HOME/texlite` 或 `~/.local/share/texlite` |
|
|
199
|
-
| `clientDir` | npm 包内部的 `dist/client` |
|
|
200
|
-
| `sessionDays` | `14` 天 |
|
|
201
|
-
| `uploads.maxFileSizeMB` | `50` MB |
|
|
202
|
-
| `history.maxVersions` | 每项目 `200` 个普通版本 |
|
|
203
|
-
| `history.maxStorageMB` | 每项目 `128` MB(软上限) |
|
|
204
|
-
| `latex.latexmk` | `latexmk` |
|
|
205
|
-
| `latex.defaultEngine` | `xelatex` |
|
|
206
|
-
| `latex.allowedEngines` | `pdflatex`、`xelatex`、`lualatex` |
|
|
207
|
-
| `latex.extraArgs` | `[]` |
|
|
208
|
-
| `latex.allowProjectLatexmkrc` | `true` |
|
|
209
|
-
| `latex.compileTimeoutSeconds` | `600` 秒 |
|
|
210
|
-
| `latex.maxCompileJobs` | `10` |
|
|
211
|
-
| `git.binary` / `git.operationTimeoutSeconds` | `git` / `120` 秒 |
|
|
212
|
-
| `git.githubApiBaseUrl` | `https://api.github.com` |
|
|
213
|
-
|
|
214
|
-
配置会在环境检查、打开数据库和启动 HTTP 监听之前完成校验。显式设置的
|
|
215
|
-
非法值不会静默回退到默认值。允许范围为:端口 `1–65535`,会话有效期
|
|
216
|
-
`1–3650` 天,单文件大小 `1–2048` MB,历史版本数 `10–5000`,历史存储
|
|
217
|
-
`16–102400` MB,编译超时 `1–3600` 秒,编译并发数 `1–32`,Git 超时
|
|
218
|
-
`1–3600` 秒。引擎名称必须受支持且不能重复;允许引擎
|
|
219
|
-
列表必须包含选中的默认引擎。数据目录和项目目录不能指向文件,数据目录
|
|
220
|
-
不能是文件系统根目录;数据目录不存在时,其父目录必须已经存在且可写。
|
|
221
|
-
GitHub API 地址必须是 `http://` 或 `https://` URL。
|
|
222
|
-
|
|
223
|
-
JSON 类型错误、必填字符串为空、整数格式错误(包括带小数或非数字的环境
|
|
224
|
-
变量)、不支持的引擎、非法 URL 和不可用路径都会停止启动,并显示配置项、
|
|
225
|
-
期望格式及修复提示。`npm run init` 也执行同样的校验,因此可以在创建第一个
|
|
226
|
-
管理员前先发现配置问题。
|
|
227
|
-
|
|
228
|
-
环境变量会覆盖配置文件中的对应值:
|
|
229
|
-
|
|
230
|
-
~~~text
|
|
231
|
-
TEXLITE_CONFIG
|
|
232
|
-
XDG_CONFIG_HOME XDG_DATA_HOME
|
|
233
|
-
TEXLITE_SITE_NAME TEXLITE_ADMIN_EMAIL
|
|
234
|
-
TEXLITE_HOST TEXLITE_PORT
|
|
235
|
-
TEXLITE_DATA_DIR TEXLITE_CLIENT_DIR
|
|
236
|
-
TEXLITE_SESSION_DAYS TEXLITE_MAX_UPLOAD_SIZE_MB
|
|
237
|
-
TEXLITE_HISTORY_MAX_VERSIONS TEXLITE_HISTORY_MAX_STORAGE_MB
|
|
238
|
-
TEXLITE_LATEXMK TEXLITE_DEFAULT_ENGINE
|
|
239
|
-
TEXLITE_COMPILE_TIMEOUT TEXLITE_MAX_COMPILE_JOBS
|
|
240
|
-
TEXLITE_GIT TEXLITE_GIT_TIMEOUT
|
|
241
|
-
TEXLITE_GITHUB_API_URL
|
|
242
|
-
~~~
|
|
243
|
-
|
|
244
|
-
texLite 不执行 tlmgr,也不会自动安装缺失宏包。宿主机更新 TeX Live 后,下一次编译会直接使用新环境。
|
|
245
|
-
|
|
246
|
-
## 服务管理
|
|
247
|
-
|
|
248
|
-
通过全局 npm 安装时,生命周期命令使用包内置的 PM2,不需要额外全局安装:
|
|
249
|
-
|
|
250
|
-
~~~bash
|
|
251
|
-
texlite start
|
|
252
|
-
texlite status
|
|
253
73
|
texlite logs
|
|
254
|
-
texlite restart
|
|
255
|
-
texlite stop
|
|
256
74
|
~~~
|
|
257
75
|
|
|
258
|
-
`texlite
|
|
259
|
-
`
|
|
260
|
-
|
|
261
|
-
`texlite doctor` 会校验配置、路径、LaTeX 和管理员;使用 `--git` 可以额外
|
|
262
|
-
检查可选的 Git 集成。`texlite config` 显示生效的配置和路径。
|
|
263
|
-
`texlite serve` 以前台方式运行,不启动 PM2。
|
|
76
|
+
`texlite serve` 会以前台方式运行,适合调试、Docker 或 systemd。`start`、`stop`、
|
|
77
|
+
`restart`、`status`、`logs` 使用 npm 包内置的 PM2 运行时;完整命令请执行
|
|
78
|
+
`texlite help`。
|
|
264
79
|
|
|
265
|
-
|
|
266
|
-
<summary>从仓库部署并单独安装 PM2</summary>
|
|
80
|
+
## 文档导航
|
|
267
81
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
~~~bash
|
|
275
|
-
npm install --global pm2
|
|
276
|
-
npm run build
|
|
277
|
-
pm2 start ecosystem.config.cjs
|
|
278
|
-
pm2 status
|
|
279
|
-
pm2 logs texlite
|
|
280
|
-
~~~
|
|
281
|
-
|
|
282
|
-
对应的 npm 快捷命令是 npm run pm2:start、npm run pm2:restart、npm run pm2:stop、npm run pm2:delete、npm run pm2:logs 和 npm run pm2:save。
|
|
283
|
-
|
|
284
|
-
发布新代码后:
|
|
285
|
-
|
|
286
|
-
~~~bash
|
|
287
|
-
npm run build
|
|
288
|
-
pm2 restart texlite --update-env
|
|
289
|
-
~~~
|
|
290
|
-
|
|
291
|
-
如果需要开机自动恢复,执行 pm2 startup 输出的命令,然后保存进程列表:
|
|
292
|
-
|
|
293
|
-
~~~bash
|
|
294
|
-
pm2 save
|
|
295
|
-
~~~
|
|
296
|
-
|
|
297
|
-
常用命令还有 pm2 stop texlite、pm2 restart texlite、pm2 delete texlite 和 pm2 monit。
|
|
298
|
-
|
|
299
|
-
</details>
|
|
300
|
-
|
|
301
|
-
## 协作和编译模型
|
|
302
|
-
|
|
303
|
-
编辑器使用 Yjs CRDT 支持并发编辑。项目顶部显示活动浏览器会话,远程光标使用不同颜色;单个项目的目标规模约为 10 个活动会话。只有服务端确认源码已经持久化后,保存状态才会显示“已保存”。尚未同步的更新也会保留在浏览器 IndexedDB 中,并在临时断线后恢复;恢复历史版本或 Git checkout 会轮换协作 epoch,避免旧的本地草稿覆盖刚恢复的源码树。删除或移动文件后,其他会话仍绑定旧编辑器时产生的迟到修改会被拒绝。
|
|
304
|
-
|
|
305
|
-
项目设置中保存的主文档是默认主文档。用户打开另一个包含 `\documentclass` 的 `.tex` 文件后,该文件仅在当前浏览器会话中成为编译主文档;打开被引用的普通片段不会切换主文档。系统只编译当前主文档。编译状态、日志、保留的 PDF、编译产物、大纲和 SyncTeX 均按主文档隔离,因此查看不同主文档的协作者不会看到彼此的编译提示,也不会覆盖彼此的 PDF。
|
|
306
|
-
|
|
307
|
-
编译与编辑相互隔离:
|
|
308
|
-
|
|
309
|
-
1. 服务端捕获不可变源码快照。
|
|
310
|
-
2. 将有变化的文件增量同步到按项目、主文档和编译设置区分的持久编译工作区。
|
|
311
|
-
3. latexmk 复用依赖数据库和辅助文件;同一主文档的任务仍然串行执行。
|
|
312
|
-
4. 将 PDF、日志、SyncTeX 和其他产物复制到不可变的运行快照。
|
|
313
|
-
5. 编译成功后,以原子方式发布最新产物快照。
|
|
314
|
-
|
|
315
|
-
同一主文档的可变缓存不会被并发使用,不同主文档使用独立缓存,已经发布的产物也不会被原地修改。编译时仍可查看旧 PDF;BibTeX 文档所需的多轮编译由 latexmk 自动处理,参考文献输入未变化时不会重复运行 BibTeX。编译响应提供 Server-Timing 响应头,可以分别查看快照、缓存同步、latexmk、产物复制和请求总耗时。TexLite 不保留可浏览的编译历史:仅按主文档保存最近一次尝试的状态,以及仍用于 PDF 预览的最近成功结果(如果需要)。更早的数据库记录和不可变产物包会自动清理。
|
|
316
|
-
|
|
317
|
-
## 历史、导航和诊断
|
|
318
|
-
|
|
319
|
-
自动历史会记录源码/文件操作、编译器设置、Git 操作、恢复操作和服务端确认的协作保存。协作保存采用固定的两分钟版本窗口,因此连续编辑仍会形成有用的恢复点,同时不会按每次按键记录。TexLite 默认保留最近 200 个普通版本,以及全部已标记版本和初始版本。文件内容以完整的 SHA-256 对象保存:未变化内容会复用,变化文件则产生一个新的完整对象。默认每个项目设置 128 MB 软上限,超过后优先删除最旧的普通版本;受保护版本和当前内部基线可能使实际用量超过上限。项目所有者可以查看历史存储用量、删除单个版本或清空全部历史,而不会改变当前项目文件。不再被引用的对象会被清理。历史功能适合修正写作误操作,但不能替代对完整数据目录的备份。
|
|
320
|
-
|
|
321
|
-
Ctrl/Cmd+P 用于快速打开项目文件,Ctrl/Cmd+Shift+F 用于全项目纯文本搜索和替换。全项目替换按一次操作暂存,并自动创建历史版本。大纲从主文档开始跟随 `\\input`、`\\include` 和 `\\subfile`。结构化警告/错误会解析项目内文件名,点击后直接跳到源码行;完整 latexmk 原始日志仍然保留。
|
|
322
|
-
|
|
323
|
-
管理员可在项目列表打开“系统状态”。指标只包含数量和耗时,绝不包含源码、密码、Git token 或批注;也可通过需要管理员登录的 `GET /api/health/metrics` 获取。
|
|
324
|
-
|
|
325
|
-
## GitHub 备份
|
|
326
|
-
|
|
327
|
-
Git 面板仅对项目所有者显示。GitHub personal access token 按项目配置并加密保存到 SQLite。本地仓库位于项目源码目录;每次命令临时使用以下身份:
|
|
328
|
-
|
|
329
|
-
~~~text
|
|
330
|
-
user.name = 项目所有者 username
|
|
331
|
-
user.email = <username>@texlite.com
|
|
332
|
-
~~~
|
|
333
|
-
|
|
334
|
-
对于 fine-grained token,在可信用户部署中建议授予仓库 Administration 和 Contents 的读写权限,并选择 All repositories。配置 token 时远程仓库可能还不存在,之后也可能创建新仓库。token 不会出现在远程 URL 或命令行参数中。
|
|
335
|
-
|
|
336
|
-
普通 checkout 遵循 Git 的默认保留行为,冲突时拒绝操作。只有在确认框中明确选择强制选项,才会丢弃已跟踪、未跟踪和 ignored 的工作区文件。checkout 历史版本后,需要先返回默认分支才能提交或 push。
|
|
337
|
-
|
|
338
|
-
## 数据、备份和删除
|
|
339
|
-
|
|
340
|
-
默认数据目录:
|
|
341
|
-
|
|
342
|
-
~~~text
|
|
343
|
-
.texlite/
|
|
344
|
-
├── texlite.db
|
|
345
|
-
├── texlite.db-wal
|
|
346
|
-
├── texlite.db-shm
|
|
347
|
-
├── git-token.key
|
|
348
|
-
└── projects/
|
|
349
|
-
└── <project-id>/
|
|
350
|
-
├── source/
|
|
351
|
-
└── output/
|
|
352
|
-
└── .texlite/ # 编译缓存/运行和历史对象
|
|
353
|
-
~~~
|
|
354
|
-
|
|
355
|
-
请同时备份 texlite.db、git-token.key 和 projects/。恢复已保存的 GitHub token 必须保留加密密钥。在线备份 SQLite 文件时也要包含 WAL 文件,或者使用 SQLite 感知的备份方式。
|
|
356
|
-
|
|
357
|
-
删除用户会删除其成员关系,但既有批注仍会保留,并显示为“已删除用户”。根据管理员选择,该用户拥有的项目可以转移给当前管理员,也可以连同文件一起删除。最后一个有效管理员不能被删除、禁用或降级。
|
|
358
|
-
|
|
359
|
-
## 安全边界
|
|
82
|
+
| 需要了解 | 阅读 |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| 安装、配置路径、生效默认值、环境变量、服务管理、备份与安全边界 | [运维指南(英文)](OPERATIONS.md) |
|
|
85
|
+
| 协作、源码持久化、编译隔离、历史版本和设计权衡 | [设计文档(英文)](DESIGN.md) |
|
|
86
|
+
| 发布前测试 npm 包 | [NPM 测试指南(英文)](NPM_TESTING.md) |
|
|
87
|
+
| 完整配置起点 | [texlite.config.example.json](texlite.config.example.json) |
|
|
360
88
|
|
|
361
|
-
|
|
89
|
+
## 范围与安全
|
|
362
90
|
|
|
363
|
-
|
|
91
|
+
TexLite 是一个面向可信用户的单宿主机应用,并非 LaTeX 编译沙箱:LaTeX 本身以及启用的
|
|
92
|
+
项目 `latexmkrc` 都可能执行强大的本机行为。除非已配备适合不可信环境的认证、网络控制和
|
|
93
|
+
独立编译沙箱,否则请保持默认的 `127.0.0.1` 监听地址。
|
|
364
94
|
|
|
365
|
-
|
|
95
|
+
## 许可证
|
|
366
96
|
|
|
367
|
-
|
|
97
|
+
TexLite 使用 GNU Affero General Public License v3.0,详见 [LICENSE](LICENSE)。如需
|
|
98
|
+
闭源修改,或需要不同于 AGPL-3.0 的商业条款,请联系版权所有者。
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{u as ye,r as n,j as e}from"./react-BoEjKzFZ.js";import{j as u,M as H,L as p,G as q,K as ke,C as ie,k as ne,T as be,l as ve,P as Ce,a as l}from"./index-zIjCeZFK.js";import{M as Ne,a as we}from"./minus-O6pK-6mA.js";const ze=u("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=u("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=u("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=u("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=u("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 Se=u("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=u("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=u("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 $e({open:h,project:o,onOpenChange:y,onBeforeMutation:r}){const{t:s,i18n:E}=ye(),[i,m]=n.useState(null),[J,K]=n.useState([]),[T,k]=n.useState(""),[F,V]=n.useState(()=>re(o.name)),[_,ce]=n.useState(!0),[D,Z]=n.useState(""),[b,M]=n.useState(null),[f,j]=n.useState(null),[U,$]=n.useState(!1),[le,v]=n.useState(!1),[R,L]=n.useState(!0),[P,A]=n.useState(!0),[a,C]=n.useState(""),[Q,N]=n.useState(""),[X,B]=n.useState(""),[g,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`);K(S.commits)}else K([])};n.useEffect(()=>{if(!h){document.fullscreenElement&&document.exitFullscreen().catch(()=>{}),x(!1),k("");return}N(""),B(""),M(null),x(!1),V(re(o.name)),C("load"),r().then(()=>z(!0)).catch(t=>N(t instanceof Error?t.message:s("errors.generic"))).finally(()=>C(""))},[h,o.id]),n.useEffect(()=>{const t=()=>x(document.fullscreenElement===O.current);return document.addEventListener("fullscreenchange",t),()=>document.removeEventListener("fullscreenchange",t)},[]),n.useEffect(()=>{if(!g)return;const t=c=>{c.key==="Escape"&&!document.fullscreenElement&&x(!1)};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[g]);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(g){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:U})}),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:h,extraWide:!0,className:g?"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":R,onClick:()=>L(t=>!t),children:[e.jsxs("span",{className:"git-section-heading",children:[e.jsx(ke,{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")})]})]}),R?e.jsx(ie,{size:16}):e.jsx(ne,{size:16})]}),R&&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(be,{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=>V(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(ze,{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(ve,{size:14}),s("git.discardChanges")]}),!i.branch&&e.jsxs("button",{disabled:!!a,onClick:()=>{$(!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${g?" 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(Ne,{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(g?"git.exitFullscreenDiff":"git.fullscreenDiff"),"aria-label":s(g?"git.exitFullscreenDiff":"git.fullscreenDiff"),onClick:()=>{fe()},children:g?e.jsx(Se,{size:15}):e.jsx(we,{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:()=>{$(!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:U?"danger":"primary",onClick:xe,children:s("git.checkout")})]}),children:e.jsxs("label",{className:"git-force-checkout",children:[e.jsx("input",{type:"checkbox",checked:U,onChange:t=>$(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:h,empty:o,fontSize:y}){return h.trim()?e.jsx("pre",{className:"git-diff",style:{fontSize:`${y}px`},children:h.split(`
|
|
1
|
+
import{u as ye,r as n,j as e}from"./react-BoEjKzFZ.js";import{j as u,M as H,L as p,G as q,K as ke,C as ie,k as ne,T as be,l as ve,P as Ce,a as l}from"./index-vpWlpUOr.js";import{M as Ne,a as we}from"./minus-DR-ttrx3.js";const ze=u("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=u("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=u("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=u("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=u("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 Se=u("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=u("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=u("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 $e({open:h,project:o,onOpenChange:y,onBeforeMutation:r}){const{t:s,i18n:E}=ye(),[i,m]=n.useState(null),[J,K]=n.useState([]),[T,k]=n.useState(""),[F,V]=n.useState(()=>re(o.name)),[_,ce]=n.useState(!0),[D,Z]=n.useState(""),[b,M]=n.useState(null),[f,j]=n.useState(null),[U,$]=n.useState(!1),[le,v]=n.useState(!1),[R,L]=n.useState(!0),[P,A]=n.useState(!0),[a,C]=n.useState(""),[Q,N]=n.useState(""),[X,B]=n.useState(""),[g,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`);K(S.commits)}else K([])};n.useEffect(()=>{if(!h){document.fullscreenElement&&document.exitFullscreen().catch(()=>{}),x(!1),k("");return}N(""),B(""),M(null),x(!1),V(re(o.name)),C("load"),r().then(()=>z(!0)).catch(t=>N(t instanceof Error?t.message:s("errors.generic"))).finally(()=>C(""))},[h,o.id]),n.useEffect(()=>{const t=()=>x(document.fullscreenElement===O.current);return document.addEventListener("fullscreenchange",t),()=>document.removeEventListener("fullscreenchange",t)},[]),n.useEffect(()=>{if(!g)return;const t=c=>{c.key==="Escape"&&!document.fullscreenElement&&x(!1)};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[g]);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(g){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:U})}),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:h,extraWide:!0,className:g?"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":R,onClick:()=>L(t=>!t),children:[e.jsxs("span",{className:"git-section-heading",children:[e.jsx(ke,{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")})]})]}),R?e.jsx(ie,{size:16}):e.jsx(ne,{size:16})]}),R&&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(be,{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=>V(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(ze,{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(ve,{size:14}),s("git.discardChanges")]}),!i.branch&&e.jsxs("button",{disabled:!!a,onClick:()=>{$(!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${g?" 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(Ne,{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(g?"git.exitFullscreenDiff":"git.fullscreenDiff"),"aria-label":s(g?"git.exitFullscreenDiff":"git.fullscreenDiff"),onClick:()=>{fe()},children:g?e.jsx(Se,{size:15}):e.jsx(we,{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:()=>{$(!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:U?"danger":"primary",onClick:xe,children:s("git.checkout")})]}),children:e.jsxs("label",{className:"git-force-checkout",children:[e.jsx("input",{type:"checkbox",checked:U,onChange:t=>$(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:h,empty:o,fontSize:y}){return h.trim()?e.jsx("pre",{className:"git-diff",style:{fontSize:`${y}px`},children:h.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(h){return h.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"-").replace(/-+/g,"-").replace(/^[.-]+|[.-]+$/g,"").slice(0,100)||"texlite-project"}export{$e as GitDialog};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{g as re,u as ne,r as I,j as u}from"./react-BoEjKzFZ.js";import{j as z,a as R,M as ie,L as O,T as J,l as Q,S as se,F as ae,n as G}from"./index-
|
|
1
|
+
import{g as re,u as ne,r as I,j as u}from"./react-BoEjKzFZ.js";import{j as z,a as R,M as ie,L as O,T as J,l as Q,S as se,F as ae,n as G}from"./index-vpWlpUOr.js";const te=z("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);const V=z("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 le=z("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 he=z("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"}]]);var H={exports:{}},K;function oe(){return K||(K=1,(function(C){var o=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},k=-1,A=1,f=0;o.Diff=function(e,r){return[e,r]},o.prototype.diff_main=function(e,r,n,s){typeof s>"u"&&(this.Diff_Timeout<=0?s=Number.MAX_VALUE:s=new Date().getTime()+this.Diff_Timeout*1e3);var a=s;if(e==null||r==null)throw new Error("Null input. (diff_main)");if(e==r)return e?[new o.Diff(f,e)]:[];typeof n>"u"&&(n=!0);var i=n,t=this.diff_commonPrefix(e,r),l=e.substring(0,t);e=e.substring(t),r=r.substring(t),t=this.diff_commonSuffix(e,r);var h=e.substring(e.length-t);e=e.substring(0,e.length-t),r=r.substring(0,r.length-t);var g=this.diff_compute_(e,r,i,a);return l&&g.unshift(new o.Diff(f,l)),h&&g.push(new o.Diff(f,h)),this.diff_cleanupMerge(g),g},o.prototype.diff_compute_=function(e,r,n,s){var a;if(!e)return[new o.Diff(A,r)];if(!r)return[new o.Diff(k,e)];var i=e.length>r.length?e:r,t=e.length>r.length?r:e,l=i.indexOf(t);if(l!=-1)return a=[new o.Diff(A,i.substring(0,l)),new o.Diff(f,t),new o.Diff(A,i.substring(l+t.length))],e.length>r.length&&(a[0][0]=a[2][0]=k),a;if(t.length==1)return[new o.Diff(k,e),new o.Diff(A,r)];var h=this.diff_halfMatch_(e,r);if(h){var g=h[0],c=h[1],v=h[2],m=h[3],b=h[4],p=this.diff_main(g,v,n,s),M=this.diff_main(c,m,n,s);return p.concat([new o.Diff(f,b)],M)}return n&&e.length>100&&r.length>100?this.diff_lineMode_(e,r,s):this.diff_bisect_(e,r,s)},o.prototype.diff_lineMode_=function(e,r,n){var s=this.diff_linesToChars_(e,r);e=s.chars1,r=s.chars2;var a=s.lineArray,i=this.diff_main(e,r,!1,n);this.diff_charsToLines_(i,a),this.diff_cleanupSemantic(i),i.push(new o.Diff(f,""));for(var t=0,l=0,h=0,g="",c="";t<i.length;){switch(i[t][0]){case A:h++,c+=i[t][1];break;case k:l++,g+=i[t][1];break;case f:if(l>=1&&h>=1){i.splice(t-l-h,l+h),t=t-l-h;for(var v=this.diff_main(g,c,!1,n),m=v.length-1;m>=0;m--)i.splice(t,0,v[m]);t=t+v.length}h=0,l=0,g="",c="";break}t++}return i.pop(),i},o.prototype.diff_bisect_=function(e,r,n){for(var s=e.length,a=r.length,i=Math.ceil((s+a)/2),t=i,l=2*i,h=new Array(l),g=new Array(l),c=0;c<l;c++)h[c]=-1,g[c]=-1;h[t+1]=0,g[t+1]=0;for(var v=s-a,m=v%2!=0,b=0,p=0,M=0,E=0,_=0;_<i&&!(new Date().getTime()>n);_++){for(var S=-_+b;S<=_-p;S+=2){var D=t+S,w;S==-_||S!=_&&h[D-1]<h[D+1]?w=h[D+1]:w=h[D-1]+1;for(var N=w-S;w<s&&N<a&&e.charAt(w)==r.charAt(N);)w++,N++;if(h[D]=w,w>s)p+=2;else if(N>a)b+=2;else if(m){var j=t+v-S;if(j>=0&&j<l&&g[j]!=-1){var T=s-g[j];if(w>=T)return this.diff_bisectSplit_(e,r,w,N,n)}}}for(var P=-_+M;P<=_-E;P+=2){var j=t+P,T;P==-_||P!=_&&g[j-1]<g[j+1]?T=g[j+1]:T=g[j-1]+1;for(var B=T-P;T<s&&B<a&&e.charAt(s-T-1)==r.charAt(a-B-1);)T++,B++;if(g[j]=T,T>s)E+=2;else if(B>a)M+=2;else if(!m){var D=t+v-P;if(D>=0&&D<l&&h[D]!=-1){var w=h[D],N=t+w-D;if(T=s-T,w>=T)return this.diff_bisectSplit_(e,r,w,N,n)}}}}return[new o.Diff(k,e),new o.Diff(A,r)]},o.prototype.diff_bisectSplit_=function(e,r,n,s,a){var i=e.substring(0,n),t=r.substring(0,s),l=e.substring(n),h=r.substring(s),g=this.diff_main(i,t,!1,a),c=this.diff_main(l,h,!1,a);return g.concat(c)},o.prototype.diff_linesToChars_=function(e,r){var n=[],s={};n[0]="";function a(h){for(var g="",c=0,v=-1,m=n.length;v<h.length-1;){v=h.indexOf(`
|
|
2
2
|
`,c),v==-1&&(v=h.length-1);var b=h.substring(c,v+1);(s.hasOwnProperty?s.hasOwnProperty(b):s[b]!==void 0)?g+=String.fromCharCode(s[b]):(m==i&&(b=h.substring(c),v=h.length),g+=String.fromCharCode(m),s[b]=m,n[m++]=b),c=v+1}return g}var i=4e4,t=a(e);i=65535;var l=a(r);return{chars1:t,chars2:l,lineArray:n}},o.prototype.diff_charsToLines_=function(e,r){for(var n=0;n<e.length;n++){for(var s=e[n][1],a=[],i=0;i<s.length;i++)a[i]=r[s.charCodeAt(i)];e[n][1]=a.join("")}},o.prototype.diff_commonPrefix=function(e,r){if(!e||!r||e.charAt(0)!=r.charAt(0))return 0;for(var n=0,s=Math.min(e.length,r.length),a=s,i=0;n<a;)e.substring(i,a)==r.substring(i,a)?(n=a,i=n):s=a,a=Math.floor((s-n)/2+n);return a},o.prototype.diff_commonSuffix=function(e,r){if(!e||!r||e.charAt(e.length-1)!=r.charAt(r.length-1))return 0;for(var n=0,s=Math.min(e.length,r.length),a=s,i=0;n<a;)e.substring(e.length-a,e.length-i)==r.substring(r.length-a,r.length-i)?(n=a,i=n):s=a,a=Math.floor((s-n)/2+n);return a},o.prototype.diff_commonOverlap_=function(e,r){var n=e.length,s=r.length;if(n==0||s==0)return 0;n>s?e=e.substring(n-s):n<s&&(r=r.substring(0,n));var a=Math.min(n,s);if(e==r)return a;for(var i=0,t=1;;){var l=e.substring(a-t),h=r.indexOf(l);if(h==-1)return i;t+=h,(h==0||e.substring(a-t)==r.substring(0,t))&&(i=t,t++)}},o.prototype.diff_halfMatch_=function(e,r){if(this.Diff_Timeout<=0)return null;var n=e.length>r.length?e:r,s=e.length>r.length?r:e;if(n.length<4||s.length*2<n.length)return null;var a=this;function i(p,M,E){for(var _=p.substring(E,E+Math.floor(p.length/4)),S=-1,D="",w,N,j,T;(S=M.indexOf(_,S+1))!=-1;){var P=a.diff_commonPrefix(p.substring(E),M.substring(S)),B=a.diff_commonSuffix(p.substring(0,E),M.substring(0,S));D.length<B+P&&(D=M.substring(S-B,S)+M.substring(S,S+P),w=p.substring(0,E-B),N=p.substring(E+P),j=M.substring(0,S-B),T=M.substring(S+P))}return D.length*2>=p.length?[w,N,j,T,D]:null}var t=i(n,s,Math.ceil(n.length/4)),l=i(n,s,Math.ceil(n.length/2)),h;if(!t&&!l)return null;l?t?h=t[4].length>l[4].length?t:l:h=l:h=t;var g,c,v,m;e.length>r.length?(g=h[0],c=h[1],v=h[2],m=h[3]):(v=h[0],m=h[1],g=h[2],c=h[3]);var b=h[4];return[g,c,v,m,b]},o.prototype.diff_cleanupSemantic=function(e){for(var r=!1,n=[],s=0,a=null,i=0,t=0,l=0,h=0,g=0;i<e.length;)e[i][0]==f?(n[s++]=i,t=h,l=g,h=0,g=0,a=e[i][1]):(e[i][0]==A?h+=e[i][1].length:g+=e[i][1].length,a&&a.length<=Math.max(t,l)&&a.length<=Math.max(h,g)&&(e.splice(n[s-1],0,new o.Diff(k,a)),e[n[s-1]+1][0]=A,s--,s--,i=s>0?n[s-1]:-1,t=0,l=0,h=0,g=0,a=null,r=!0)),i++;for(r&&this.diff_cleanupMerge(e),this.diff_cleanupSemanticLossless(e),i=1;i<e.length;){if(e[i-1][0]==k&&e[i][0]==A){var c=e[i-1][1],v=e[i][1],m=this.diff_commonOverlap_(c,v),b=this.diff_commonOverlap_(v,c);m>=b?(m>=c.length/2||m>=v.length/2)&&(e.splice(i,0,new o.Diff(f,v.substring(0,m))),e[i-1][1]=c.substring(0,c.length-m),e[i+1][1]=v.substring(m),i++):(b>=c.length/2||b>=v.length/2)&&(e.splice(i,0,new o.Diff(f,c.substring(0,b))),e[i-1][0]=A,e[i-1][1]=v.substring(0,v.length-b),e[i+1][0]=k,e[i+1][1]=c.substring(b),i++),i++}i++}},o.prototype.diff_cleanupSemanticLossless=function(e){function r(b,p){if(!b||!p)return 6;var M=b.charAt(b.length-1),E=p.charAt(0),_=M.match(o.nonAlphaNumericRegex_),S=E.match(o.nonAlphaNumericRegex_),D=_&&M.match(o.whitespaceRegex_),w=S&&E.match(o.whitespaceRegex_),N=D&&M.match(o.linebreakRegex_),j=w&&E.match(o.linebreakRegex_),T=N&&b.match(o.blanklineEndRegex_),P=j&&p.match(o.blanklineStartRegex_);return T||P?5:N||j?4:_&&!D&&w?3:D||w?2:_||S?1:0}for(var n=1;n<e.length-1;){if(e[n-1][0]==f&&e[n+1][0]==f){var s=e[n-1][1],a=e[n][1],i=e[n+1][1],t=this.diff_commonSuffix(s,a);if(t){var l=a.substring(a.length-t);s=s.substring(0,s.length-t),a=l+a.substring(0,a.length-t),i=l+i}for(var h=s,g=a,c=i,v=r(s,a)+r(a,i);a.charAt(0)===i.charAt(0);){s+=a.charAt(0),a=a.substring(1)+i.charAt(0),i=i.substring(1);var m=r(s,a)+r(a,i);m>=v&&(v=m,h=s,g=a,c=i)}e[n-1][1]!=h&&(h?e[n-1][1]=h:(e.splice(n-1,1),n--),e[n][1]=g,c?e[n+1][1]=c:(e.splice(n+1,1),n--))}n++}},o.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/,o.whitespaceRegex_=/\s/,o.linebreakRegex_=/[\r\n]/,o.blanklineEndRegex_=/\n\r?\n$/,o.blanklineStartRegex_=/^\r?\n\r?\n/,o.prototype.diff_cleanupEfficiency=function(e){for(var r=!1,n=[],s=0,a=null,i=0,t=!1,l=!1,h=!1,g=!1;i<e.length;)e[i][0]==f?(e[i][1].length<this.Diff_EditCost&&(h||g)?(n[s++]=i,t=h,l=g,a=e[i][1]):(s=0,a=null),h=g=!1):(e[i][0]==k?g=!0:h=!0,a&&(t&&l&&h&&g||a.length<this.Diff_EditCost/2&&t+l+h+g==3)&&(e.splice(n[s-1],0,new o.Diff(k,a)),e[n[s-1]+1][0]=A,s--,a=null,t&&l?(h=g=!0,s=0):(s--,i=s>0?n[s-1]:-1,h=g=!1),r=!0)),i++;r&&this.diff_cleanupMerge(e)},o.prototype.diff_cleanupMerge=function(e){e.push(new o.Diff(f,""));for(var r=0,n=0,s=0,a="",i="",t;r<e.length;)switch(e[r][0]){case A:s++,i+=e[r][1],r++;break;case k:n++,a+=e[r][1],r++;break;case f:n+s>1?(n!==0&&s!==0&&(t=this.diff_commonPrefix(i,a),t!==0&&(r-n-s>0&&e[r-n-s-1][0]==f?e[r-n-s-1][1]+=i.substring(0,t):(e.splice(0,0,new o.Diff(f,i.substring(0,t))),r++),i=i.substring(t),a=a.substring(t)),t=this.diff_commonSuffix(i,a),t!==0&&(e[r][1]=i.substring(i.length-t)+e[r][1],i=i.substring(0,i.length-t),a=a.substring(0,a.length-t))),r-=n+s,e.splice(r,n+s),a.length&&(e.splice(r,0,new o.Diff(k,a)),r++),i.length&&(e.splice(r,0,new o.Diff(A,i)),r++),r++):r!==0&&e[r-1][0]==f?(e[r-1][1]+=e[r][1],e.splice(r,1)):r++,s=0,n=0,a="",i="";break}e[e.length-1][1]===""&&e.pop();var l=!1;for(r=1;r<e.length-1;)e[r-1][0]==f&&e[r+1][0]==f&&(e[r][1].substring(e[r][1].length-e[r-1][1].length)==e[r-1][1]?(e[r][1]=e[r-1][1]+e[r][1].substring(0,e[r][1].length-e[r-1][1].length),e[r+1][1]=e[r-1][1]+e[r+1][1],e.splice(r-1,1),l=!0):e[r][1].substring(0,e[r+1][1].length)==e[r+1][1]&&(e[r-1][1]+=e[r+1][1],e[r][1]=e[r][1].substring(e[r+1][1].length)+e[r+1][1],e.splice(r+1,1),l=!0)),r++;l&&this.diff_cleanupMerge(e)},o.prototype.diff_xIndex=function(e,r){var n=0,s=0,a=0,i=0,t;for(t=0;t<e.length&&(e[t][0]!==A&&(n+=e[t][1].length),e[t][0]!==k&&(s+=e[t][1].length),!(n>r));t++)a=n,i=s;return e.length!=t&&e[t][0]===k?i:i+(r-a)},o.prototype.diff_prettyHtml=function(e){for(var r=[],n=/&/g,s=/</g,a=/>/g,i=/\n/g,t=0;t<e.length;t++){var l=e[t][0],h=e[t][1],g=h.replace(n,"&").replace(s,"<").replace(a,">").replace(i,"¶<br>");switch(l){case A:r[t]='<ins style="background:#e6ffe6;">'+g+"</ins>";break;case k:r[t]='<del style="background:#ffe6e6;">'+g+"</del>";break;case f:r[t]="<span>"+g+"</span>";break}}return r.join("")},o.prototype.diff_text1=function(e){for(var r=[],n=0;n<e.length;n++)e[n][0]!==A&&(r[n]=e[n][1]);return r.join("")},o.prototype.diff_text2=function(e){for(var r=[],n=0;n<e.length;n++)e[n][0]!==k&&(r[n]=e[n][1]);return r.join("")},o.prototype.diff_levenshtein=function(e){for(var r=0,n=0,s=0,a=0;a<e.length;a++){var i=e[a][0],t=e[a][1];switch(i){case A:n+=t.length;break;case k:s+=t.length;break;case f:r+=Math.max(n,s),n=0,s=0;break}}return r+=Math.max(n,s),r},o.prototype.diff_toDelta=function(e){for(var r=[],n=0;n<e.length;n++)switch(e[n][0]){case A:r[n]="+"+encodeURI(e[n][1]);break;case k:r[n]="-"+e[n][1].length;break;case f:r[n]="="+e[n][1].length;break}return r.join(" ").replace(/%20/g," ")},o.prototype.diff_fromDelta=function(e,r){for(var n=[],s=0,a=0,i=r.split(/\t/g),t=0;t<i.length;t++){var l=i[t].substring(1);switch(i[t].charAt(0)){case"+":try{n[s++]=new o.Diff(A,decodeURI(l))}catch{throw new Error("Illegal escape in diff_fromDelta: "+l)}break;case"-":case"=":var h=parseInt(l,10);if(isNaN(h)||h<0)throw new Error("Invalid number in diff_fromDelta: "+l);var g=e.substring(a,a+=h);i[t].charAt(0)=="="?n[s++]=new o.Diff(f,g):n[s++]=new o.Diff(k,g);break;default:if(i[t])throw new Error("Invalid diff operation in diff_fromDelta: "+i[t])}}if(a!=e.length)throw new Error("Delta length ("+a+") does not equal source text length ("+e.length+").");return n},o.prototype.match_main=function(e,r,n){if(e==null||r==null||n==null)throw new Error("Null input. (match_main)");return n=Math.max(0,Math.min(n,e.length)),e==r?0:e.length?e.substring(n,n+r.length)==r?n:this.match_bitap_(e,r,n):-1},o.prototype.match_bitap_=function(e,r,n){if(r.length>this.Match_MaxBits)throw new Error("Pattern too long for this browser.");var s=this.match_alphabet_(r),a=this;function i(w,N){var j=w/r.length,T=Math.abs(n-N);return a.Match_Distance?j+T/a.Match_Distance:T?1:j}var t=this.Match_Threshold,l=e.indexOf(r,n);l!=-1&&(t=Math.min(i(0,l),t),l=e.lastIndexOf(r,n+r.length),l!=-1&&(t=Math.min(i(0,l),t)));var h=1<<r.length-1;l=-1;for(var g,c,v=r.length+e.length,m,b=0;b<r.length;b++){for(g=0,c=v;g<c;)i(b,n+c)<=t?g=c:v=c,c=Math.floor((v-g)/2+g);v=c;var p=Math.max(1,n-c+1),M=Math.min(n+c,e.length)+r.length,E=Array(M+2);E[M+1]=(1<<b)-1;for(var _=M;_>=p;_--){var S=s[e.charAt(_-1)];if(b===0?E[_]=(E[_+1]<<1|1)&S:E[_]=(E[_+1]<<1|1)&S|((m[_+1]|m[_])<<1|1)|m[_+1],E[_]&h){var D=i(b,_-1);if(D<=t)if(t=D,l=_-1,l>n)p=Math.max(1,2*n-l);else break}}if(i(b+1,n)>t)break;m=E}return l},o.prototype.match_alphabet_=function(e){for(var r={},n=0;n<e.length;n++)r[e.charAt(n)]=0;for(var n=0;n<e.length;n++)r[e.charAt(n)]|=1<<e.length-n-1;return r},o.prototype.patch_addContext_=function(e,r){if(r.length!=0){if(e.start2===null)throw Error("patch not initialized");for(var n=r.substring(e.start2,e.start2+e.length1),s=0;r.indexOf(n)!=r.lastIndexOf(n)&&n.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)s+=this.Patch_Margin,n=r.substring(e.start2-s,e.start2+e.length1+s);s+=this.Patch_Margin;var a=r.substring(e.start2-s,e.start2);a&&e.diffs.unshift(new o.Diff(f,a));var i=r.substring(e.start2+e.length1,e.start2+e.length1+s);i&&e.diffs.push(new o.Diff(f,i)),e.start1-=a.length,e.start2-=a.length,e.length1+=a.length+i.length,e.length2+=a.length+i.length}},o.prototype.patch_make=function(e,r,n){var s,a;if(typeof e=="string"&&typeof r=="string"&&typeof n>"u")s=e,a=this.diff_main(s,r,!0),a.length>2&&(this.diff_cleanupSemantic(a),this.diff_cleanupEfficiency(a));else if(e&&typeof e=="object"&&typeof r>"u"&&typeof n>"u")a=e,s=this.diff_text1(a);else if(typeof e=="string"&&r&&typeof r=="object"&&typeof n>"u")s=e,a=r;else if(typeof e=="string"&&typeof r=="string"&&n&&typeof n=="object")s=e,a=n;else throw new Error("Unknown call format to patch_make.");if(a.length===0)return[];for(var i=[],t=new o.patch_obj,l=0,h=0,g=0,c=s,v=s,m=0;m<a.length;m++){var b=a[m][0],p=a[m][1];switch(!l&&b!==f&&(t.start1=h,t.start2=g),b){case A:t.diffs[l++]=a[m],t.length2+=p.length,v=v.substring(0,g)+p+v.substring(g);break;case k:t.length1+=p.length,t.diffs[l++]=a[m],v=v.substring(0,g)+v.substring(g+p.length);break;case f:p.length<=2*this.Patch_Margin&&l&&a.length!=m+1?(t.diffs[l++]=a[m],t.length1+=p.length,t.length2+=p.length):p.length>=2*this.Patch_Margin&&l&&(this.patch_addContext_(t,c),i.push(t),t=new o.patch_obj,l=0,c=v,h=g);break}b!==A&&(h+=p.length),b!==k&&(g+=p.length)}return l&&(this.patch_addContext_(t,c),i.push(t)),i},o.prototype.patch_deepCopy=function(e){for(var r=[],n=0;n<e.length;n++){var s=e[n],a=new o.patch_obj;a.diffs=[];for(var i=0;i<s.diffs.length;i++)a.diffs[i]=new o.Diff(s.diffs[i][0],s.diffs[i][1]);a.start1=s.start1,a.start2=s.start2,a.length1=s.length1,a.length2=s.length2,r[n]=a}return r},o.prototype.patch_apply=function(e,r){if(e.length==0)return[r,[]];e=this.patch_deepCopy(e);var n=this.patch_addPadding(e);r=n+r+n,this.patch_splitMax(e);for(var s=0,a=[],i=0;i<e.length;i++){var t=e[i].start2+s,l=this.diff_text1(e[i].diffs),h,g=-1;if(l.length>this.Match_MaxBits?(h=this.match_main(r,l.substring(0,this.Match_MaxBits),t),h!=-1&&(g=this.match_main(r,l.substring(l.length-this.Match_MaxBits),t+l.length-this.Match_MaxBits),(g==-1||h>=g)&&(h=-1))):h=this.match_main(r,l,t),h==-1)a[i]=!1,s-=e[i].length2-e[i].length1;else{a[i]=!0,s=h-t;var c;if(g==-1?c=r.substring(h,h+l.length):c=r.substring(h,g+this.Match_MaxBits),l==c)r=r.substring(0,h)+this.diff_text2(e[i].diffs)+r.substring(h+l.length);else{var v=this.diff_main(l,c,!1);if(l.length>this.Match_MaxBits&&this.diff_levenshtein(v)/l.length>this.Patch_DeleteThreshold)a[i]=!1;else{this.diff_cleanupSemanticLossless(v);for(var m=0,b,p=0;p<e[i].diffs.length;p++){var M=e[i].diffs[p];M[0]!==f&&(b=this.diff_xIndex(v,m)),M[0]===A?r=r.substring(0,h+b)+M[1]+r.substring(h+b):M[0]===k&&(r=r.substring(0,h+b)+r.substring(h+this.diff_xIndex(v,m+M[1].length))),M[0]!==k&&(m+=M[1].length)}}}}}return r=r.substring(n.length,r.length-n.length),[r,a]},o.prototype.patch_addPadding=function(e){for(var r=this.Patch_Margin,n="",s=1;s<=r;s++)n+=String.fromCharCode(s);for(var s=0;s<e.length;s++)e[s].start1+=r,e[s].start2+=r;var a=e[0],i=a.diffs;if(i.length==0||i[0][0]!=f)i.unshift(new o.Diff(f,n)),a.start1-=r,a.start2-=r,a.length1+=r,a.length2+=r;else if(r>i[0][1].length){var t=r-i[0][1].length;i[0][1]=n.substring(i[0][1].length)+i[0][1],a.start1-=t,a.start2-=t,a.length1+=t,a.length2+=t}if(a=e[e.length-1],i=a.diffs,i.length==0||i[i.length-1][0]!=f)i.push(new o.Diff(f,n)),a.length1+=r,a.length2+=r;else if(r>i[i.length-1][1].length){var t=r-i[i.length-1][1].length;i[i.length-1][1]+=n.substring(0,t),a.length1+=t,a.length2+=t}return n},o.prototype.patch_splitMax=function(e){for(var r=this.Match_MaxBits,n=0;n<e.length;n++)if(!(e[n].length1<=r)){var s=e[n];e.splice(n--,1);for(var a=s.start1,i=s.start2,t="";s.diffs.length!==0;){var l=new o.patch_obj,h=!0;for(l.start1=a-t.length,l.start2=i-t.length,t!==""&&(l.length1=l.length2=t.length,l.diffs.push(new o.Diff(f,t)));s.diffs.length!==0&&l.length1<r-this.Patch_Margin;){var g=s.diffs[0][0],c=s.diffs[0][1];g===A?(l.length2+=c.length,i+=c.length,l.diffs.push(s.diffs.shift()),h=!1):g===k&&l.diffs.length==1&&l.diffs[0][0]==f&&c.length>2*r?(l.length1+=c.length,a+=c.length,h=!1,l.diffs.push(new o.Diff(g,c)),s.diffs.shift()):(c=c.substring(0,r-l.length1-this.Patch_Margin),l.length1+=c.length,a+=c.length,g===f?(l.length2+=c.length,i+=c.length):h=!1,l.diffs.push(new o.Diff(g,c)),c==s.diffs[0][1]?s.diffs.shift():s.diffs[0][1]=s.diffs[0][1].substring(c.length))}t=this.diff_text2(l.diffs),t=t.substring(t.length-this.Patch_Margin);var v=this.diff_text1(s.diffs).substring(0,this.Patch_Margin);v!==""&&(l.length1+=v.length,l.length2+=v.length,l.diffs.length!==0&&l.diffs[l.diffs.length-1][0]===f?l.diffs[l.diffs.length-1][1]+=v:l.diffs.push(new o.Diff(f,v))),h||e.splice(++n,0,l)}}},o.prototype.patch_toText=function(e){for(var r=[],n=0;n<e.length;n++)r[n]=e[n];return r.join("")},o.prototype.patch_fromText=function(e){var r=[];if(!e)return r;for(var n=e.split(`
|
|
3
3
|
`),s=0,a=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;s<n.length;){var i=n[s].match(a);if(!i)throw new Error("Invalid patch string: "+n[s]);var t=new o.patch_obj;for(r.push(t),t.start1=parseInt(i[1],10),i[2]===""?(t.start1--,t.length1=1):i[2]=="0"?t.length1=0:(t.start1--,t.length1=parseInt(i[2],10)),t.start2=parseInt(i[3],10),i[4]===""?(t.start2--,t.length2=1):i[4]=="0"?t.length2=0:(t.start2--,t.length2=parseInt(i[4],10)),s++;s<n.length;){var l=n[s].charAt(0);try{var h=decodeURI(n[s].substring(1))}catch{throw new Error("Illegal escape in patch_fromText: "+h)}if(l=="-")t.diffs.push(new o.Diff(k,h));else if(l=="+")t.diffs.push(new o.Diff(A,h));else if(l==" ")t.diffs.push(new o.Diff(f,h));else{if(l=="@")break;if(l!=="")throw new Error('Invalid patch mode "'+l+'" in: '+h)}s++}}return r},o.patch_obj=function(){this.diffs=[],this.start1=null,this.start2=null,this.length1=0,this.length2=0},o.patch_obj.prototype.toString=function(){var e,r;this.length1===0?e=this.start1+",0":this.length1==1?e=this.start1+1:e=this.start1+1+","+this.length1,this.length2===0?r=this.start2+",0":this.length2==1?r=this.start2+1:r=this.start2+1+","+this.length2;for(var n=["@@ -"+e+" +"+r+` @@
|
|
4
4
|
`],s,a=0;a<this.diffs.length;a++){switch(this.diffs[a][0]){case A:s="+";break;case k:s="-";break;case f:s=" ";break}n[a+1]=s+encodeURI(this.diffs[a][1])+`
|