texlite 0.9.4 → 0.11.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 +12 -14
- package/OPERATIONS.md +12 -6
- package/README.md +70 -41
- package/README.zh-CN.md +59 -34
- package/dist/client/assets/{AdminUsers-B12FJUMy.js → AdminUsers-BExOMZsW.js} +1 -1
- package/dist/client/assets/{CitationLibraryDialog-BTmnCP4V.js → CitationLibraryDialog-tVD3i2gg.js} +1 -1
- package/dist/client/assets/Dashboard-DRt4rjKP.js +2 -0
- package/dist/client/assets/{GitDialog--MoUIW0n.js → GitDialog-CjDCi1uh.js} +1 -1
- package/dist/client/assets/{HistoryDialog-LgKbjsTZ.js → HistoryDialog-Pa9mK3Ky.js} +1 -1
- package/dist/client/assets/{LatexEditor-Dj2zIaQm.js → LatexEditor-8r486VeG.js} +2 -2
- package/dist/client/assets/PdfPreview-CMkO72I5.js +1 -0
- package/dist/client/assets/{ProjectIconPickerDialog-GzRuM0oQ.js → ProjectIconPickerDialog-B52xbSyr.js} +1 -1
- package/dist/client/assets/{ProjectNavigationDialogs-BiArHmQ9.js → ProjectNavigationDialogs-isGMXCfG.js} +1 -1
- package/dist/client/assets/ProjectSettings-Br_kJ113.js +1 -0
- package/dist/client/assets/ProjectWorkspace-CcbrdCy8.js +32 -0
- package/dist/client/assets/{SelectionHistoryDialog-Bpv6nvrC.js → SelectionHistoryDialog-ZvAhIJMy.js} +2 -2
- package/dist/client/assets/{SystemMetricsDialog-hJQATmd4.js → SystemMetricsDialog-D4ouU4HI.js} +1 -1
- package/dist/client/assets/{TagManagementDialog-BHC8qULq.js → TagManagementDialog-qhZfHgTr.js} +1 -1
- package/dist/client/assets/{WordCountDialog-DZhhO94k.js → WordCountDialog-tFhMCGyn.js} +1 -1
- package/dist/client/assets/check-DKIW1zpR.js +1 -0
- package/dist/client/assets/chktex-tM1XXPk2.js +3 -0
- package/dist/client/assets/{hard-drive-BWZnOgnm.js → hard-drive-CkIPwUSV.js} +1 -1
- package/dist/client/assets/index-90X2GUd_.css +1 -0
- package/dist/client/assets/index-CqBmQAzW.js +10 -0
- package/dist/client/assets/{index-kTamGfTl.js → index-DTYEGzB7.js} +1 -1
- package/dist/client/assets/{key-round-CoDwyVJc.js → key-round-BEnQo7IM.js} +1 -1
- package/dist/client/assets/{minimize-2-D100opW2.js → minimize-2-CLuDohEf.js} +1 -1
- package/dist/client/assets/{minus-DDTYB-Oz.js → minus-BpuvzV2m.js} +1 -1
- package/dist/client/assets/{plus-BVXOlQXP.js → plus-BFr75l91.js} +1 -1
- package/dist/client/assets/{refresh-cw-LtwnqlWh.js → refresh-cw-DyA4NKh7.js} +1 -1
- package/dist/client/assets/{search-DXo_aAp0.js → search-Nu4OcY_n.js} +1 -1
- package/dist/client/assets/{sigma-BgvHGPto.js → sigma-B-wjepsY.js} +1 -1
- package/dist/client/assets/{spellCheck-BtRppKcC.js → spellCheck-C-bNJsIU.js} +1 -1
- package/dist/client/assets/{tags-m9kppdi0.js → tags-BSA08jK3.js} +1 -1
- package/dist/client/assets/{type-YO4aprqE.js → type-BCdIbDmF.js} +1 -1
- package/dist/client/assets/{user-plus-MRmKeDtX.js → user-plus-gFwY-NA3.js} +1 -1
- package/dist/client/assets/{x-eisRQJv_.js → x-Cspw_pa4.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/app.js +8 -0
- package/dist/server/chktex.js +377 -0
- package/dist/server/collaboration.js +6 -0
- package/dist/server/db.js +6 -1
- package/dist/server/environment.js +1 -0
- package/dist/server/history.js +4 -4
- package/dist/server/i18n.js +12 -2
- package/dist/server/projectImportUploads.js +133 -0
- package/dist/server/routes/projectFiles.js +3 -0
- package/dist/server/routes/projectShared.js +1 -0
- package/dist/server/routes/projects.js +87 -15
- package/dist/shared/writingChecks.js +4 -0
- package/package.json +1 -1
- package/dist/client/assets/Dashboard-JA5_M3TK.js +0 -2
- package/dist/client/assets/PdfPreview-C_W7w360.js +0 -1
- package/dist/client/assets/ProjectSettings-DgJohWUv.js +0 -1
- package/dist/client/assets/ProjectWorkspace-C6sYiBF2.js +0 -32
- package/dist/client/assets/check-C1db8iNn.js +0 -1
- package/dist/client/assets/index-BiwJj0Np.css +0 -1
- package/dist/client/assets/index-p1NHkGGG.js +0 -12
package/DESIGN.md
CHANGED
|
@@ -25,7 +25,7 @@ and day-to-day operation.
|
|
|
25
25
|
| Browser UI | React, Vite, CodeMirror, PDF.js |
|
|
26
26
|
| Localization | Frontend JSON resources plus a server-side error-code catalog, selected from `Accept-Language` |
|
|
27
27
|
| Editor language features | CodeMirror LaTeX syntax/folding, auto-pairs, project completion index, optional Vim mode |
|
|
28
|
-
| Writing assistance | Optional host `harper-cli
|
|
28
|
+
| Writing assistance | Optional host `harper-cli` plus opt-in host `chktex`; Harper provides spelling/grammar suggestions, ChkTeX provides LaTeX writing diagnostics, and the project dictionary is stored by the server |
|
|
29
29
|
| API and static server | Fastify, WebSocket |
|
|
30
30
|
| Collaboration | Yjs, y-websocket, awareness messages, y-codemirror.next |
|
|
31
31
|
| Database | SQLite through better-sqlite3, foreign keys, WAL mode, and `synchronous=NORMAL` |
|
|
@@ -119,7 +119,7 @@ Startup checks Node.js, `latexmk`, and every configured LaTeX engine.
|
|
|
119
119
|
default host commands, requiring Node.js, `latexmk`, and at least one supported
|
|
120
120
|
LaTeX engine. `texlite doctor` also reports optional host tools in a
|
|
121
121
|
requirement/status/version table: Git, TeXcount, bibliography/index tools,
|
|
122
|
-
Harper CLI, and `harper-ls`. A host without Git can run the editor and compiler;
|
|
122
|
+
ChkTeX, Harper CLI, and `harper-ls`. A host without Git can run the editor and compiler;
|
|
123
123
|
Git is checked on demand when an owner opens or uses Git integration. Formatting is also optional: the browser loads
|
|
124
124
|
the bundled npm `tex-fmt` WASM module and `bibtex-tidy` only when formatting is
|
|
125
125
|
requested. TexLite never installs or updates TeX packages.
|
|
@@ -273,18 +273,16 @@ Editor tab remounts therefore preserve Ctrl/Cmd+Z and Vim undo history without
|
|
|
273
273
|
leaving obsolete CodeMirror/Yjs observers behind. Managers are released when
|
|
274
274
|
the collaboration object is destroyed or loses edit permission.
|
|
275
275
|
|
|
276
|
-
TexLite probes the optional host `harper-cli` command at startup.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
fails, CodeMirror enables the browser's built-in English spellchecker until it
|
|
287
|
-
retries.
|
|
276
|
+
TexLite probes the optional host `harper-cli` command at startup. Harper uses
|
|
277
|
+
its native LaTeX-aware parser and returns safe replacement suggestions. The
|
|
278
|
+
project dictionary is kept in SQLite and filters project-specific spelling
|
|
279
|
+
results in the browser. If Harper is absent or fails, CodeMirror enables the
|
|
280
|
+
browser's built-in English spellchecker until it retries. A project owner may
|
|
281
|
+
also enable the optional host `chktex` compiler setting. ChkTeX starts only
|
|
282
|
+
after two seconds of editor idle time, coalesces identical source inputs, and
|
|
283
|
+
uses a process-wide queue capped at eight checks across projects and users. Its
|
|
284
|
+
LaTeX writing diagnostics use a blue-gray wavy underline and never replace
|
|
285
|
+
the compile diagnostics; the setting is off by default.
|
|
288
286
|
|
|
289
287
|
Formatting is independent from the editor's local appearance. A user can
|
|
290
288
|
manually format a selection or enable the per-user/per-project “format before
|
package/OPERATIONS.md
CHANGED
|
@@ -206,7 +206,7 @@ preferred.
|
|
|
206
206
|
| `latex.extraArgs` | `[]` | Additional configured compiler arguments. |
|
|
207
207
|
| `latex.compileTimeoutSeconds` | `600` | Per-job time limit. |
|
|
208
208
|
| `latex.maxCompileJobs` | `10` | Global concurrent LaTeX-process limit. |
|
|
209
|
-
| `latex.allowProjectLatexmkrc` | `true` |
|
|
209
|
+
| `latex.allowProjectLatexmkrc` | `true` | Allows project owners to opt into an existing project `latexmkrc`; the per-project selection starts off. Edit the file in the project editor. |
|
|
210
210
|
| `git.binary` / `git.operationTimeoutSeconds` | `git` / `120` seconds | Used only by optional Git integration. |
|
|
211
211
|
| `git.githubApiBaseUrl` | `https://api.github.com` | GitHub REST API endpoint. |
|
|
212
212
|
|
|
@@ -294,11 +294,17 @@ Valid base paths start with `/`, contain URL-safe path segments, and have no
|
|
|
294
294
|
query, fragment, `.` or `..` segment. A trailing slash in configuration is
|
|
295
295
|
accepted and normalized. Restart TexLite after changing this setting.
|
|
296
296
|
|
|
297
|
-
Before every compile TexLite passes `-norc` to `latexmk`. A `.latexmkrc`
|
|
298
|
-
in a ZIP upload, Git checkout, or project file tree is ignored
|
|
299
|
-
|
|
300
|
-
with `-r`.
|
|
301
|
-
|
|
297
|
+
Before every compile TexLite passes `-norc` to `latexmk`. A `.latexmkrc` or
|
|
298
|
+
`latexmkrc` found in a ZIP upload, Git checkout, or project file tree is ignored
|
|
299
|
+
until the owner enables it in Project Settings. TexLite then passes that file
|
|
300
|
+
with `-r`. The setting appears only when an rc file exists; the file is edited
|
|
301
|
+
in the project editor, not in settings. For an unconfigured project, a root
|
|
302
|
+
`latexmkrc` is preferred over a root `.latexmkrc` when both exist; an existing
|
|
303
|
+
selection is preserved. Removing an active rc file disables it. A `latexmkrc`
|
|
304
|
+
is executable Perl configuration; it may override the selected engine and
|
|
305
|
+
other compiler options, and collaborators who can edit the file can change
|
|
306
|
+
subsequent builds. Keep the server-level option off when project editors are
|
|
307
|
+
not trusted.
|
|
302
308
|
|
|
303
309
|
TexLite never runs `tlmgr` or installs TeX packages. Updating the host TeX
|
|
304
310
|
distribution changes the environment used by subsequent compiles.
|
package/README.md
CHANGED
|
@@ -25,6 +25,65 @@ teams. Use your existing LaTeX distribution, with no heavyweight service stack.
|
|
|
25
25
|
process, SQLite, and local files—plus real-time editing and source-level
|
|
26
26
|
comments for a small trusted team.
|
|
27
27
|
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
Install Node.js 24 or newer, `latexmk`, and at least one TeX engine such as
|
|
31
|
+
`pdflatex`, `xelatex`, or `lualatex`. Git is needed only for the optional
|
|
32
|
+
Git/GitHub integration.
|
|
33
|
+
|
|
34
|
+
After installation, `texlite requirements` checks the relevant host software
|
|
35
|
+
and versions before initialization.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install --global texlite
|
|
39
|
+
texlite requirements
|
|
40
|
+
texlite init
|
|
41
|
+
texlite start
|
|
42
|
+
texlite status
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Open <http://127.0.0.1:3000>. `texlite init` creates the configuration and the
|
|
46
|
+
first administrator; public registration is deliberately unavailable.
|
|
47
|
+
To mount TexLite below an existing domain path such as `/texlite`, configure
|
|
48
|
+
`server.basePath` and follow the [reverse-proxy guide](OPERATIONS.md#reverse-proxy-subpath).
|
|
49
|
+
|
|
50
|
+
For upgrades and routine management:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm update --global texlite
|
|
54
|
+
texlite restart
|
|
55
|
+
texlite logs
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`texlite serve` runs in the foreground for debugging, Docker, or systemd.
|
|
59
|
+
`start`, `stop`, `restart`, `status`, and `logs` use the PM2 runtime bundled
|
|
60
|
+
with the npm package. Run `texlite help` for the complete command list.
|
|
61
|
+
|
|
62
|
+
<details>
|
|
63
|
+
<summary>How to access TexLite publicly</summary>
|
|
64
|
+
|
|
65
|
+
Keep TexLite bound to `127.0.0.1` and use
|
|
66
|
+
[TexLite Share](https://share.zhongpu.info/) when you need convenient,
|
|
67
|
+
HTTPS-protected public access. TexLite Share is currently hosted in Hong Kong,
|
|
68
|
+
so access from mainland China or regions outside Southeast Asia may be slower.
|
|
69
|
+
With TexLite running locally on its default port, install and launch the tunnel
|
|
70
|
+
client with:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
curl -fsSL https://raw.githubusercontent.com/ChenZhongPu/TexLite-Share/main/install-client.sh | bash
|
|
74
|
+
~/.local/bin/texlite-tunnel-client --server-url https://share.zhongpu.info
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For a non-default local port, specify the address explicitly:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
~/.local/bin/texlite-tunnel-client \
|
|
81
|
+
--server-url https://share.zhongpu.info \
|
|
82
|
+
--local-addr 127.0.0.1:8080
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
</details>
|
|
86
|
+
|
|
28
87
|
## A practical distinction from Overleaf
|
|
29
88
|
|
|
30
89
|
[Overleaf](https://www.overleaf.com/about/features-overview) is a strong choice
|
|
@@ -51,7 +110,7 @@ shared browser writing, not a replacement for a personal IDE.
|
|
|
51
110
|
- Projects with folders, ZIP import/export, tags, sharing, ownership transfer,
|
|
52
111
|
archiving, and a private per-user citation library.
|
|
53
112
|
- CodeMirror editing with LaTeX/BibTeX highlighting, folding, completion,
|
|
54
|
-
optional Vim mode, formatting, spelling/grammar assistance, search/replace,
|
|
113
|
+
optional Vim mode, formatting, spelling/grammar/ChkTeX assistance, search/replace,
|
|
55
114
|
and source/PDF SyncTeX navigation.
|
|
56
115
|
- Yjs-based collaborative source editing, active-session presence, comments
|
|
57
116
|
anchored to source text, replies, resolution, and permissions that let
|
|
@@ -62,40 +121,6 @@ shared browser writing, not a replacement for a personal IDE.
|
|
|
62
121
|
- Per-project history and owner-only Git/GitHub backup. Git is optional and is
|
|
63
122
|
checked only when its integration is used.
|
|
64
123
|
|
|
65
|
-
## Quick start
|
|
66
|
-
|
|
67
|
-
Install Node.js 24 or newer, `latexmk`, and at least one TeX engine such as
|
|
68
|
-
`pdflatex`, `xelatex`, or `lualatex`. Git is needed only for the optional
|
|
69
|
-
Git/GitHub integration.
|
|
70
|
-
|
|
71
|
-
After installation, `texlite requirements` checks the relevant host software
|
|
72
|
-
and versions before initialization.
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
npm install --global texlite
|
|
76
|
-
texlite requirements
|
|
77
|
-
texlite init
|
|
78
|
-
texlite start
|
|
79
|
-
texlite status
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Open <http://127.0.0.1:3000>. `texlite init` creates the configuration and the
|
|
83
|
-
first administrator; public registration is deliberately unavailable.
|
|
84
|
-
To mount TexLite below an existing domain path such as `/texlite`, configure
|
|
85
|
-
`server.basePath` and follow the [reverse-proxy guide](OPERATIONS.md#reverse-proxy-subpath).
|
|
86
|
-
|
|
87
|
-
For upgrades and routine management:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
npm update --global texlite
|
|
91
|
-
texlite restart
|
|
92
|
-
texlite logs
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
`texlite serve` runs in the foreground for debugging, Docker, or systemd.
|
|
96
|
-
`start`, `stop`, `restart`, `status`, and `logs` use the PM2 runtime bundled
|
|
97
|
-
with the npm package. Run `texlite help` for the complete command list.
|
|
98
|
-
|
|
99
124
|
<a id="docker-deployment"></a>
|
|
100
125
|
|
|
101
126
|
### Docker deployment
|
|
@@ -131,12 +156,16 @@ for its user-facing configuration and operations guide.
|
|
|
131
156
|
|
|
132
157
|
TexLite is a single-host application for trusted users. It is not a compiler
|
|
133
158
|
sandbox: LaTeX and an enabled project `latexmkrc` can execute powerful local
|
|
134
|
-
behaviour.
|
|
135
|
-
|
|
136
|
-
[
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
behaviour. Do not expose its service port directly.
|
|
160
|
+
|
|
161
|
+
[TexLite Share](https://share.zhongpu.info/) provides the simplest
|
|
162
|
+
TexLite-specific sharing path. Depending on the deployment environment, common
|
|
163
|
+
alternatives include [Tailscale Serve](https://tailscale.com/docs/reference/tailscale-cli/serve)
|
|
164
|
+
for a private tailnet, [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/)
|
|
165
|
+
for a managed public endpoint, and [frp](https://github.com/fatedier/frp) for a
|
|
166
|
+
self-managed reverse tunnel. Whichever access layer is used, retain TexLite's
|
|
167
|
+
authentication, enable HTTPS, restrict the audience, and remember that LaTeX
|
|
168
|
+
compilation is not an untrusted-code sandbox.
|
|
140
169
|
|
|
141
170
|
## License
|
|
142
171
|
|
package/README.zh-CN.md
CHANGED
|
@@ -24,35 +24,6 @@
|
|
|
24
24
|
- **无需庞大服务栈即可协作。** 默认部署只有一个 Node.js 进程、SQLite 和本地文件,
|
|
25
25
|
同时提供实时编辑和源码级批注,适合少量可信协作者。
|
|
26
26
|
|
|
27
|
-
## 与 Overleaf 的实际区别
|
|
28
|
-
|
|
29
|
-
[Overleaf](https://www.overleaf.com/about/features-overview) 在团队需要其托管产品或更完整生态时
|
|
30
|
-
仍然是很好的选择。TexLite 面向的是更窄的自托管场景:
|
|
31
|
-
|
|
32
|
-
- 共享托管服务在使用高峰期可能排队、变慢或出现编译超时。
|
|
33
|
-
- Overleaf 开源 [Community Edition](https://github.com/overleaf/overleaf) 的
|
|
34
|
-
[Docker 部署路径](https://docs.overleaf.com/on-premises/getting-started/what-is-the-overleaf-toolkit) 更复杂,且源码批注等高级功能 [仅限 Server Pro](https://docs.overleaf.com/on-premises/user-and-project-management/roles-and-permissions)。
|
|
35
|
-
*如果不介意一个更庞大的 Docker 镜像,[我们也准备好了](#docker-deployment) :)*
|
|
36
|
-
|
|
37
|
-
自托管并不保证每份文档都编译得更快,速度仍取决于宿主机和文档本身;它带来的是对容量、
|
|
38
|
-
TeX 更新、数据位置与协作工作流的掌控。
|
|
39
|
-
|
|
40
|
-
若是以个人、本地桌面工作流为主,建议优先考虑
|
|
41
|
-
[VS Code + LaTeX Workshop](https://github.com/James-Yu/LaTeX-Workshop) 或
|
|
42
|
-
[TeXstudio](https://texstudio.org/)。TexLite 的目标是浏览器中的共同写作,而不是取代个人 IDE。
|
|
43
|
-
|
|
44
|
-
## 核心写作工作流
|
|
45
|
-
|
|
46
|
-
- 项目支持文件夹、ZIP 导入/导出、标签、分享、所有权转让、归档和私有的个人引用库。
|
|
47
|
-
- 基于 CodeMirror 的编辑器提供 LaTeX/BibTeX 高亮、折叠、补全、可选 Vim 模式、格式化、
|
|
48
|
-
拼写/语法辅助、搜索替换及源码/PDF SyncTeX 跳转。
|
|
49
|
-
- 基于 Yjs 的协同源码编辑支持活动会话、源码锚定批注、回复、解决状态,以及让审阅者
|
|
50
|
-
可批注但不能修改源码的权限模型。
|
|
51
|
-
- 使用 `latexmk` 和可选择引擎进行编译,提供项目设置、结构化诊断、缓存的成功 PDF、
|
|
52
|
-
可下载产物以及可选的项目级 `latexmkrc`。
|
|
53
|
-
- 每个项目拥有历史版本和仅所有者可用的 Git/GitHub 备份;Git 是可选依赖,仅在使用
|
|
54
|
-
集成时才检查。
|
|
55
|
-
|
|
56
27
|
## 快速开始
|
|
57
28
|
|
|
58
29
|
安装 Node.js 24 或更高版本、`latexmk`,以及至少一个 TeX 引擎,例如 `pdflatex`、
|
|
@@ -85,6 +56,57 @@ texlite logs
|
|
|
85
56
|
`restart`、`status`、`logs` 使用 npm 包内置的 PM2 运行时;完整命令请执行
|
|
86
57
|
`texlite help`。
|
|
87
58
|
|
|
59
|
+
<details>
|
|
60
|
+
<summary>如何公开访问 TexLite</summary>
|
|
61
|
+
|
|
62
|
+
建议让 TexLite 继续监听 `127.0.0.1`。如果需要方便、安全地公开访问,请使用
|
|
63
|
+
[TexLite Share](https://share.zhongpu.info/)。TexLite Share 目前部署在香港,来自中国大陆或
|
|
64
|
+
东南亚以外地区的访问可能较慢。确保 TexLite 已在默认端口启动,然后安装并运行隧道客户端:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
curl -fsSL https://raw.githubusercontent.com/ChenZhongPu/TexLite-Share/main/install-client.sh | bash
|
|
68
|
+
~/.local/bin/texlite-tunnel-client --server-url https://share.zhongpu.info
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
如果 TexLite 使用非默认端口,请明确指定本地地址:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
~/.local/bin/texlite-tunnel-client \
|
|
75
|
+
--server-url https://share.zhongpu.info \
|
|
76
|
+
--local-addr 127.0.0.1:8080
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
</details>
|
|
80
|
+
|
|
81
|
+
## 与 Overleaf 的实际区别
|
|
82
|
+
|
|
83
|
+
[Overleaf](https://www.overleaf.com/about/features-overview) 在团队需要其托管产品或更完整生态时
|
|
84
|
+
仍然是很好的选择。TexLite 面向的是更窄的自托管场景:
|
|
85
|
+
|
|
86
|
+
- 共享托管服务在使用高峰期可能排队、变慢或出现编译超时。
|
|
87
|
+
- Overleaf 开源 [Community Edition](https://github.com/overleaf/overleaf) 的
|
|
88
|
+
[Docker 部署路径](https://docs.overleaf.com/on-premises/getting-started/what-is-the-overleaf-toolkit) 更复杂,且源码批注等高级功能 [仅限 Server Pro](https://docs.overleaf.com/on-premises/user-and-project-management/roles-and-permissions)。
|
|
89
|
+
*如果不介意一个更庞大的 Docker 镜像,[我们也准备好了](#docker-deployment) :)*
|
|
90
|
+
|
|
91
|
+
自托管并不保证每份文档都编译得更快,速度仍取决于宿主机和文档本身;它带来的是对容量、
|
|
92
|
+
TeX 更新、数据位置与协作工作流的掌控。
|
|
93
|
+
|
|
94
|
+
若是以个人、本地桌面工作流为主,建议优先考虑
|
|
95
|
+
[VS Code + LaTeX Workshop](https://github.com/James-Yu/LaTeX-Workshop) 或
|
|
96
|
+
[TeXstudio](https://texstudio.org/)。TexLite 的目标是浏览器中的共同写作,而不是取代个人 IDE。
|
|
97
|
+
|
|
98
|
+
## 核心写作工作流
|
|
99
|
+
|
|
100
|
+
- 项目支持文件夹、ZIP 导入/导出、标签、分享、所有权转让、归档和私有的个人引用库。
|
|
101
|
+
- 基于 CodeMirror 的编辑器提供 LaTeX/BibTeX 高亮、折叠、补全、可选 Vim 模式、格式化、
|
|
102
|
+
拼写/语法/ChkTeX 辅助、搜索替换及源码/PDF SyncTeX 跳转。
|
|
103
|
+
- 基于 Yjs 的协同源码编辑支持活动会话、源码锚定批注、回复、解决状态,以及让审阅者
|
|
104
|
+
可批注但不能修改源码的权限模型。
|
|
105
|
+
- 使用 `latexmk` 和可选择引擎进行编译,提供项目设置、结构化诊断、缓存的成功 PDF、
|
|
106
|
+
可下载产物以及可选的项目级 `latexmkrc`。
|
|
107
|
+
- 每个项目拥有历史版本和仅所有者可用的 Git/GitHub 备份;Git 是可选依赖,仅在使用
|
|
108
|
+
集成时才检查。
|
|
109
|
+
|
|
88
110
|
<a id="docker-deployment"></a>
|
|
89
111
|
|
|
90
112
|
### Docker 部署
|
|
@@ -119,11 +141,14 @@ Docker 版的用户配置和日常运维请参见
|
|
|
119
141
|
|
|
120
142
|
TexLite 是一个面向可信用户的单宿主机应用,并非 LaTeX 编译沙箱:LaTeX 本身以及启用的
|
|
121
143
|
项目 `latexmkrc` 都可能执行强大的本机行为。
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
[
|
|
126
|
-
|
|
144
|
+
不要直接暴露其服务端口。
|
|
145
|
+
|
|
146
|
+
[TexLite Share](https://share.zhongpu.info/) 是最简单、专门面向 TexLite 的分享方式。
|
|
147
|
+
根据部署环境,也可以使用 [Tailscale Serve](https://tailscale.com/docs/reference/tailscale-cli/serve)
|
|
148
|
+
构建私有 Tailnet,通过 [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/)
|
|
149
|
+
提供托管的公网入口,或使用 [frp](https://github.com/fatedier/frp) 自行维护反向隧道。
|
|
150
|
+
无论采用哪一种访问层,都应保留 TexLite 登录认证、启用 HTTPS、限制访问范围,并注意
|
|
151
|
+
LaTeX 编译环境并不是面向不可信代码的沙箱。
|
|
127
152
|
|
|
128
153
|
## 许可证
|
|
129
154
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as Y,r as n,j as e}from"./react-BVUjbTTn.js";import{c as m,M as D,d as _,C as ee,g as u,h}from"./index-p1NHkGGG.js";import{U as se,T as R,X as O}from"./x-eisRQJv_.js";import{U as $}from"./user-plus-MRmKeDtX.js";import{S as ae}from"./Dashboard-JA5_M3TK.js";import{K as B}from"./key-round-CoDwyVJc.js";import"./hard-drive-BWZnOgnm.js";import"./sigma-BgvHGPto.js";import"./tags-m9kppdi0.js";const re=m("Dices",[["rect",{width:"12",height:"12",x:"2",y:"10",rx:"2",ry:"2",key:"6agr2n"}],["path",{d:"m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6",key:"1o487t"}],["path",{d:"M6 18h.01",key:"uhywen"}],["path",{d:"M10 14h.01",key:"ssrbsk"}],["path",{d:"M15 6h.01",key:"cblpky"}],["path",{d:"M18 9h.01",key:"2061c0"}]]);const ne=m("FolderCheck",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}],["path",{d:"m9 13 2 2 4-4",key:"6343dt"}]]);const te=m("FolderX",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}],["path",{d:"m9.5 10.5 5 5",key:"ra9qjz"}],["path",{d:"m14.5 10.5-5 5",key:"l2rkpq"}]]);const ie=m("ShieldOff",[["path",{d:"m2 2 20 20",key:"1ooewy"}],["path",{d:"M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71",key:"1jlk70"}],["path",{d:"M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264",key:"18rp1v"}]]);const oe=m("UserCheck",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["polyline",{points:"16 11 18 13 22 9",key:"1pwet4"}]]);const le=m("UserX",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"17",x2:"22",y1:"8",y2:"13",key:"3nzzx3"}],["line",{x1:"22",x2:"17",y1:"8",y2:"13",key:"1swrse"}]]);function de(p=10){const t="ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%",a=crypto.getRandomValues(new Uint8Array(p));return Array.from(a,S=>t[S%t.length]).join("")}function be({currentUser:p,minPasswordLength:t}){const{t:a}=Y(),[S,J]=n.useState([]),[E,d]=n.useState(""),[V,C]=n.useState(!1),[r,o]=n.useState({username:"",displayName:"",password:"",role:"user",canCreateProjects:!1}),[k,g]=n.useState(null),[j,P]=n.useState(""),[x,w]=n.useState(null),[z,N]=n.useState(!1),[i,F]=n.useState(!1),[U,y]=n.useState(""),[l,f]=n.useState(null),[A,H]=n.useState(!1),[X,c]=n.useState(""),b=()=>u("/api/admin/users").then(({users:s})=>J(s)).catch(s=>d(h(s)));n.useEffect(()=>{b()},[]);const q=async()=>{if(r.password.length<t)return d(a("auth.passwordMinimum",{count:t}));try{await u("/api/admin/users",{method:"POST",body:JSON.stringify({username:r.username,displayName:r.displayName||r.username,password:r.password,role:r.role,canCreateProjects:r.canCreateProjects})}),C(!1),o({username:"",displayName:"",password:"",role:"user",canCreateProjects:!1}),await b()}catch(s){d(h(s))}},M=async(s,v)=>{try{await u(`/api/admin/users/${s.id}`,{method:"PATCH",body:JSON.stringify(v)}),await b()}catch(T){d(h(T))}},K=async s=>{if(!s.disabled){f({target:s,setting:"disable"}),c("");return}await M(s,{disabled:!1})},Z=async s=>{if(s.role==="admin"){f({target:s,setting:"demote"}),c("");return}await M(s,{role:"admin"})},G=async s=>{if(s.canCreateProjects){f({target:s,setting:"denyProjectCreation"}),c("");return}await M(s,{canCreateProjects:!0})},I=async()=>{if(!l)return;const{target:s,setting:v}=l,T=v==="disable"?{disabled:!0}:v==="demote"?{role:"user"}:{canCreateProjects:!1};H(!0),c("");try{await u(`/api/admin/users/${s.id}`,{method:"PATCH",body:JSON.stringify(T)}),await b(),f(null)}catch(W){c(h(W))}finally{H(!1)}},L=async()=>{if(!(!k||!j)){if(j.length<t)return d(a("auth.passwordMinimum",{count:t}));try{await u(`/api/admin/users/${k.id}`,{method:"PATCH",body:JSON.stringify({password:j})}),g(null),P("")}catch(s){d(h(s))}}},Q=async()=>{if(!(!x||i)){F(!0),y("");try{await u(`/api/admin/users/${x.id}`,{method:"DELETE",body:JSON.stringify({deleteProjects:z})}),w(null),N(!1),await b()}catch(s){y(h(s))}finally{F(!1)}}};return e.jsxs("main",{className:"dashboard",children:[e.jsxs("div",{className:"section-title",children:[e.jsxs("div",{children:[e.jsxs("h1",{children:[e.jsx(se,{"aria-hidden":!0,size:25}),a("users.manage")]}),e.jsx("p",{className:"muted",children:a("users.onlyAdmin")})]}),e.jsxs("button",{className:"primary icon-button",onClick:()=>C(!0),children:[e.jsx($,{"aria-hidden":!0,size:15}),a("users.add")]})]}),E&&e.jsx("p",{className:"error",children:E}),e.jsx("div",{className:"table-card",children:e.jsxs("table",{children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:a("common.user")}),e.jsx("th",{children:a("users.role")}),e.jsx("th",{children:a("users.createProjects")}),e.jsx("th",{children:a("users.ownedProjects")}),e.jsx("th",{children:a("users.status")}),e.jsx("th",{children:a("users.actions")})]})}),e.jsx("tbody",{children:S.map(s=>e.jsxs("tr",{children:[e.jsxs("td",{children:[e.jsx("strong",{children:s.displayName}),e.jsxs("small",{children:["@",s.username]})]}),e.jsx("td",{children:s.role==="admin"?a("common.admin"):a("common.user")}),e.jsx("td",{children:s.canCreateProjects?a("users.allow"):a("users.deny")}),e.jsx("td",{children:s.ownedProjects}),e.jsx("td",{children:s.disabled?a("common.disabled"):a("common.normal")}),e.jsxs("td",{children:[e.jsxs("button",{className:"icon-button",disabled:s.id===p.id,onClick:()=>K(s),children:[s.disabled?e.jsx(oe,{"aria-hidden":!0,size:13}):e.jsx(le,{"aria-hidden":!0,size:13}),s.disabled?a("users.enable"):a("users.disable")]}),e.jsxs("button",{className:"icon-button",disabled:s.id===p.id,onClick:()=>Z(s),children:[s.role==="admin"?e.jsx(ie,{"aria-hidden":!0,size:13}):e.jsx(ae,{"aria-hidden":!0,size:13}),s.role==="admin"?a("users.demote"):a("users.promote")]}),e.jsxs("button",{className:"icon-button",disabled:s.role==="admin",onClick:()=>G(s),children:[s.canCreateProjects?e.jsx(te,{"aria-hidden":!0,size:13}):e.jsx(ne,{"aria-hidden":!0,size:13}),s.canCreateProjects?a("users.denyCreate"):a("users.allowCreate")]}),e.jsxs("button",{className:"icon-button",onClick:()=>{g(s),P("")},children:[e.jsx(B,{"aria-hidden":!0,size:13}),a("users.resetPassword")]}),e.jsxs("button",{className:"icon-button danger-text",disabled:s.id===p.id,onClick:()=>{w(s),N(!1),y("")},children:[e.jsx(R,{"aria-hidden":!0,size:13}),a("common.delete")]})]})]},s.id))})]})}),e.jsx(D,{open:V,title:a("users.add"),description:a("users.addDescription"),onOpenChange:C,footer:e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"icon-button",onClick:()=>C(!1),children:[e.jsx(O,{"aria-hidden":!0,size:14}),a("common.cancel")]}),e.jsxs("button",{className:"primary icon-button",disabled:r.password.length<t,onClick:()=>{q()},children:[e.jsx($,{"aria-hidden":!0,size:14}),a("users.createUser")]})]}),children:e.jsxs("div",{className:"form-stack",children:[e.jsxs("label",{className:"form-field",children:[a("auth.username"),e.jsx("input",{value:r.username,onChange:s=>o({...r,username:s.target.value})})]}),e.jsxs("label",{className:"form-field",children:[a("users.displayName"),e.jsx("input",{value:r.displayName,onChange:s=>o({...r,displayName:s.target.value})})]}),e.jsxs("label",{className:"form-field",children:[a("users.initialPassword"),e.jsxs("span",{className:"password-generator",children:[e.jsx("input",{minLength:t,autoComplete:"new-password",value:r.password,onChange:s=>o({...r,password:s.target.value})}),e.jsxs("button",{type:"button",title:a("users.generatePassword"),onClick:()=>o({...r,password:de()}),children:[e.jsx(re,{size:15}),a("users.randomPassword")]})]}),e.jsx("small",{className:"field-hint",children:a("auth.passwordMinimum",{count:t})})]}),e.jsxs("label",{className:"form-field",children:[a("users.role"),e.jsxs("select",{value:r.role,onChange:s=>o({...r,role:s.target.value}),children:[e.jsx("option",{value:"user",children:a("common.user")}),e.jsx("option",{value:"admin",children:a("common.admin")})]})]}),e.jsxs("label",{className:"checkbox-field",children:[e.jsx("input",{type:"checkbox",checked:r.canCreateProjects||r.role==="admin",disabled:r.role==="admin",onChange:s=>o({...r,canCreateProjects:s.target.checked})})," ",a("users.allowCreate")]})]})}),e.jsx(D,{open:!!k,title:a("users.resetPassword"),description:a("users.resetDescription",{username:k?.username??""}),onOpenChange:s=>{s||g(null)},footer:e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"icon-button",onClick:()=>g(null),children:[e.jsx(O,{"aria-hidden":!0,size:14}),a("common.cancel")]}),e.jsxs("button",{className:"primary icon-button",disabled:j.length<t,onClick:()=>{L()},children:[e.jsx(B,{"aria-hidden":!0,size:14}),a("users.reset")]})]}),children:e.jsxs("label",{className:"form-field",children:[a("auth.newPassword"),e.jsx("input",{autoFocus:!0,type:"password",minLength:t,autoComplete:"new-password",value:j,onChange:s=>P(s.target.value)}),e.jsx("small",{className:"field-hint",children:a("auth.passwordMinimum",{count:t})})]})}),e.jsx(D,{open:!!x,title:a("users.deleteTitle"),description:a("users.deleteDescription",{username:x?.username??""}),onOpenChange:s=>{!s&&!i&&(w(null),y(""))},footer:e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"icon-button",disabled:i,onClick:()=>{w(null),y("")},children:[e.jsx(O,{"aria-hidden":!0,size:14}),a("common.cancel")]}),e.jsxs("button",{className:"danger icon-button",disabled:i,"aria-busy":i,onClick:()=>{Q()},children:[i?e.jsx(_,{className:"spin","aria-hidden":!0,size:14}):e.jsx(R,{"aria-hidden":!0,size:14}),a(i?"common.loading":"users.deleteTitle")]})]}),children:e.jsxs(e.Fragment,{children:[U&&e.jsx("p",{className:"error dialog-error",children:U}),e.jsxs("fieldset",{className:"choice-group",disabled:i,children:[e.jsx("legend",{children:a("users.ownedChoice",{count:x?.ownedProjects??0})}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",checked:!z,onChange:()=>N(!1)})," ",a("users.transferProjects")]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",checked:z,onChange:()=>N(!0)})," ",a("users.deleteProjects")]})]})]})}),e.jsx(ee,{open:!!l,title:a(`userConfirmations.${l?.setting??"disable"}Title`),description:a(`userConfirmations.${l?.setting??"disable"}Description`,{username:l?.target.username??""}),confirmLabel:a(`userConfirmations.${l?.setting??"disable"}Confirm`),danger:!0,busy:A,error:X,onCancel:()=>{A||(f(null),c(""))},onConfirm:()=>{I()}})]})}export{be as AdminUsers};
|
|
1
|
+
import{u as Y,r as n,j as e}from"./react-BVUjbTTn.js";import{c as m,M as D,d as _,C as ee,g as u,h}from"./index-CqBmQAzW.js";import{U as se,T as R,X as O}from"./x-Cspw_pa4.js";import{U as $}from"./user-plus-gFwY-NA3.js";import{S as ae}from"./Dashboard-DRt4rjKP.js";import{K as B}from"./key-round-BEnQo7IM.js";import"./hard-drive-CkIPwUSV.js";import"./sigma-B-wjepsY.js";import"./tags-BSA08jK3.js";const re=m("Dices",[["rect",{width:"12",height:"12",x:"2",y:"10",rx:"2",ry:"2",key:"6agr2n"}],["path",{d:"m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6",key:"1o487t"}],["path",{d:"M6 18h.01",key:"uhywen"}],["path",{d:"M10 14h.01",key:"ssrbsk"}],["path",{d:"M15 6h.01",key:"cblpky"}],["path",{d:"M18 9h.01",key:"2061c0"}]]);const ne=m("FolderCheck",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}],["path",{d:"m9 13 2 2 4-4",key:"6343dt"}]]);const te=m("FolderX",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}],["path",{d:"m9.5 10.5 5 5",key:"ra9qjz"}],["path",{d:"m14.5 10.5-5 5",key:"l2rkpq"}]]);const ie=m("ShieldOff",[["path",{d:"m2 2 20 20",key:"1ooewy"}],["path",{d:"M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71",key:"1jlk70"}],["path",{d:"M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264",key:"18rp1v"}]]);const oe=m("UserCheck",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["polyline",{points:"16 11 18 13 22 9",key:"1pwet4"}]]);const le=m("UserX",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"17",x2:"22",y1:"8",y2:"13",key:"3nzzx3"}],["line",{x1:"22",x2:"17",y1:"8",y2:"13",key:"1swrse"}]]);function de(p=10){const t="ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%",a=crypto.getRandomValues(new Uint8Array(p));return Array.from(a,S=>t[S%t.length]).join("")}function be({currentUser:p,minPasswordLength:t}){const{t:a}=Y(),[S,J]=n.useState([]),[E,d]=n.useState(""),[V,C]=n.useState(!1),[r,o]=n.useState({username:"",displayName:"",password:"",role:"user",canCreateProjects:!1}),[k,g]=n.useState(null),[j,P]=n.useState(""),[x,w]=n.useState(null),[z,N]=n.useState(!1),[i,F]=n.useState(!1),[U,y]=n.useState(""),[l,f]=n.useState(null),[A,H]=n.useState(!1),[X,c]=n.useState(""),b=()=>u("/api/admin/users").then(({users:s})=>J(s)).catch(s=>d(h(s)));n.useEffect(()=>{b()},[]);const q=async()=>{if(r.password.length<t)return d(a("auth.passwordMinimum",{count:t}));try{await u("/api/admin/users",{method:"POST",body:JSON.stringify({username:r.username,displayName:r.displayName||r.username,password:r.password,role:r.role,canCreateProjects:r.canCreateProjects})}),C(!1),o({username:"",displayName:"",password:"",role:"user",canCreateProjects:!1}),await b()}catch(s){d(h(s))}},M=async(s,v)=>{try{await u(`/api/admin/users/${s.id}`,{method:"PATCH",body:JSON.stringify(v)}),await b()}catch(T){d(h(T))}},K=async s=>{if(!s.disabled){f({target:s,setting:"disable"}),c("");return}await M(s,{disabled:!1})},Z=async s=>{if(s.role==="admin"){f({target:s,setting:"demote"}),c("");return}await M(s,{role:"admin"})},G=async s=>{if(s.canCreateProjects){f({target:s,setting:"denyProjectCreation"}),c("");return}await M(s,{canCreateProjects:!0})},I=async()=>{if(!l)return;const{target:s,setting:v}=l,T=v==="disable"?{disabled:!0}:v==="demote"?{role:"user"}:{canCreateProjects:!1};H(!0),c("");try{await u(`/api/admin/users/${s.id}`,{method:"PATCH",body:JSON.stringify(T)}),await b(),f(null)}catch(W){c(h(W))}finally{H(!1)}},L=async()=>{if(!(!k||!j)){if(j.length<t)return d(a("auth.passwordMinimum",{count:t}));try{await u(`/api/admin/users/${k.id}`,{method:"PATCH",body:JSON.stringify({password:j})}),g(null),P("")}catch(s){d(h(s))}}},Q=async()=>{if(!(!x||i)){F(!0),y("");try{await u(`/api/admin/users/${x.id}`,{method:"DELETE",body:JSON.stringify({deleteProjects:z})}),w(null),N(!1),await b()}catch(s){y(h(s))}finally{F(!1)}}};return e.jsxs("main",{className:"dashboard",children:[e.jsxs("div",{className:"section-title",children:[e.jsxs("div",{children:[e.jsxs("h1",{children:[e.jsx(se,{"aria-hidden":!0,size:25}),a("users.manage")]}),e.jsx("p",{className:"muted",children:a("users.onlyAdmin")})]}),e.jsxs("button",{className:"primary icon-button",onClick:()=>C(!0),children:[e.jsx($,{"aria-hidden":!0,size:15}),a("users.add")]})]}),E&&e.jsx("p",{className:"error",children:E}),e.jsx("div",{className:"table-card",children:e.jsxs("table",{children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:a("common.user")}),e.jsx("th",{children:a("users.role")}),e.jsx("th",{children:a("users.createProjects")}),e.jsx("th",{children:a("users.ownedProjects")}),e.jsx("th",{children:a("users.status")}),e.jsx("th",{children:a("users.actions")})]})}),e.jsx("tbody",{children:S.map(s=>e.jsxs("tr",{children:[e.jsxs("td",{children:[e.jsx("strong",{children:s.displayName}),e.jsxs("small",{children:["@",s.username]})]}),e.jsx("td",{children:s.role==="admin"?a("common.admin"):a("common.user")}),e.jsx("td",{children:s.canCreateProjects?a("users.allow"):a("users.deny")}),e.jsx("td",{children:s.ownedProjects}),e.jsx("td",{children:s.disabled?a("common.disabled"):a("common.normal")}),e.jsxs("td",{children:[e.jsxs("button",{className:"icon-button",disabled:s.id===p.id,onClick:()=>K(s),children:[s.disabled?e.jsx(oe,{"aria-hidden":!0,size:13}):e.jsx(le,{"aria-hidden":!0,size:13}),s.disabled?a("users.enable"):a("users.disable")]}),e.jsxs("button",{className:"icon-button",disabled:s.id===p.id,onClick:()=>Z(s),children:[s.role==="admin"?e.jsx(ie,{"aria-hidden":!0,size:13}):e.jsx(ae,{"aria-hidden":!0,size:13}),s.role==="admin"?a("users.demote"):a("users.promote")]}),e.jsxs("button",{className:"icon-button",disabled:s.role==="admin",onClick:()=>G(s),children:[s.canCreateProjects?e.jsx(te,{"aria-hidden":!0,size:13}):e.jsx(ne,{"aria-hidden":!0,size:13}),s.canCreateProjects?a("users.denyCreate"):a("users.allowCreate")]}),e.jsxs("button",{className:"icon-button",onClick:()=>{g(s),P("")},children:[e.jsx(B,{"aria-hidden":!0,size:13}),a("users.resetPassword")]}),e.jsxs("button",{className:"icon-button danger-text",disabled:s.id===p.id,onClick:()=>{w(s),N(!1),y("")},children:[e.jsx(R,{"aria-hidden":!0,size:13}),a("common.delete")]})]})]},s.id))})]})}),e.jsx(D,{open:V,title:a("users.add"),description:a("users.addDescription"),onOpenChange:C,footer:e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"icon-button",onClick:()=>C(!1),children:[e.jsx(O,{"aria-hidden":!0,size:14}),a("common.cancel")]}),e.jsxs("button",{className:"primary icon-button",disabled:r.password.length<t,onClick:()=>{q()},children:[e.jsx($,{"aria-hidden":!0,size:14}),a("users.createUser")]})]}),children:e.jsxs("div",{className:"form-stack",children:[e.jsxs("label",{className:"form-field",children:[a("auth.username"),e.jsx("input",{value:r.username,onChange:s=>o({...r,username:s.target.value})})]}),e.jsxs("label",{className:"form-field",children:[a("users.displayName"),e.jsx("input",{value:r.displayName,onChange:s=>o({...r,displayName:s.target.value})})]}),e.jsxs("label",{className:"form-field",children:[a("users.initialPassword"),e.jsxs("span",{className:"password-generator",children:[e.jsx("input",{minLength:t,autoComplete:"new-password",value:r.password,onChange:s=>o({...r,password:s.target.value})}),e.jsxs("button",{type:"button",title:a("users.generatePassword"),onClick:()=>o({...r,password:de()}),children:[e.jsx(re,{size:15}),a("users.randomPassword")]})]}),e.jsx("small",{className:"field-hint",children:a("auth.passwordMinimum",{count:t})})]}),e.jsxs("label",{className:"form-field",children:[a("users.role"),e.jsxs("select",{value:r.role,onChange:s=>o({...r,role:s.target.value}),children:[e.jsx("option",{value:"user",children:a("common.user")}),e.jsx("option",{value:"admin",children:a("common.admin")})]})]}),e.jsxs("label",{className:"checkbox-field",children:[e.jsx("input",{type:"checkbox",checked:r.canCreateProjects||r.role==="admin",disabled:r.role==="admin",onChange:s=>o({...r,canCreateProjects:s.target.checked})})," ",a("users.allowCreate")]})]})}),e.jsx(D,{open:!!k,title:a("users.resetPassword"),description:a("users.resetDescription",{username:k?.username??""}),onOpenChange:s=>{s||g(null)},footer:e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"icon-button",onClick:()=>g(null),children:[e.jsx(O,{"aria-hidden":!0,size:14}),a("common.cancel")]}),e.jsxs("button",{className:"primary icon-button",disabled:j.length<t,onClick:()=>{L()},children:[e.jsx(B,{"aria-hidden":!0,size:14}),a("users.reset")]})]}),children:e.jsxs("label",{className:"form-field",children:[a("auth.newPassword"),e.jsx("input",{autoFocus:!0,type:"password",minLength:t,autoComplete:"new-password",value:j,onChange:s=>P(s.target.value)}),e.jsx("small",{className:"field-hint",children:a("auth.passwordMinimum",{count:t})})]})}),e.jsx(D,{open:!!x,title:a("users.deleteTitle"),description:a("users.deleteDescription",{username:x?.username??""}),onOpenChange:s=>{!s&&!i&&(w(null),y(""))},footer:e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"icon-button",disabled:i,onClick:()=>{w(null),y("")},children:[e.jsx(O,{"aria-hidden":!0,size:14}),a("common.cancel")]}),e.jsxs("button",{className:"danger icon-button",disabled:i,"aria-busy":i,onClick:()=>{Q()},children:[i?e.jsx(_,{className:"spin","aria-hidden":!0,size:14}):e.jsx(R,{"aria-hidden":!0,size:14}),a(i?"common.loading":"users.deleteTitle")]})]}),children:e.jsxs(e.Fragment,{children:[U&&e.jsx("p",{className:"error dialog-error",children:U}),e.jsxs("fieldset",{className:"choice-group",disabled:i,children:[e.jsx("legend",{children:a("users.ownedChoice",{count:x?.ownedProjects??0})}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",checked:!z,onChange:()=>N(!1)})," ",a("users.transferProjects")]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",checked:z,onChange:()=>N(!0)})," ",a("users.deleteProjects")]})]})]})}),e.jsx(ee,{open:!!l,title:a(`userConfirmations.${l?.setting??"disable"}Title`),description:a(`userConfirmations.${l?.setting??"disable"}Description`,{username:l?.target.username??""}),confirmLabel:a(`userConfirmations.${l?.setting??"disable"}Confirm`),danger:!0,busy:A,error:X,onCancel:()=>{A||(f(null),c(""))},onConfirm:()=>{I()}})]})}export{be as AdminUsers};
|
package/dist/client/assets/{CitationLibraryDialog-BTmnCP4V.js → CitationLibraryDialog-tVD3i2gg.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as Yt,r,j as t}from"./react-BVUjbTTn.js";import{c as At,g as S,h as k,d as H,C as Bt,M as ut}from"./index-p1NHkGGG.js";import{parseBibEntriesResult as Qt,citationBibtexLimitLabel as Xt,parseSingleBibEntry as Kt,citationVenue as Gt}from"./citationLibrary-ZeQPCv5O.js";import{B as tt,X as et,i as bt,C as Zt,k as zt,T as ti}from"./x-eisRQJv_.js";import{P as it}from"./plus-BVXOlQXP.js";import{C as J}from"./check-C1db8iNn.js";import{S as ii}from"./search-DXo_aAp0.js";import{T as ei}from"./tags-m9kppdi0.js";const ai=At("Import",[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m8 11 4 4 4-4",key:"1dohi6"}],["path",{d:"M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4",key:"1ywtjm"}]]);const ni=At("PenLine",[["path",{d:"M12 20h9",key:"t2du7b"}],["path",{d:"M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z",key:"1ykcvy"}]]),si=[],ri={status:"empty",entries:si};function hi({open:o,onOpenChange:j,page:u=!1,onBack:b,currentFile:v="",currentSource:p="",readOnly:E=!1,currentUserId:m,maxBibtexBytes:l,onInsert:f}){const{t:e}=Yt(),[K,A]=r.useState("library"),[z,d]=r.useState(""),[L,P]=r.useState(""),[I,N]=r.useState([]),[at,C]=r.useState(1),[g,ht]=r.useState({page:1,pageSize:60,total:0,totalPages:0}),[Ft,U]=r.useState(0),[q,pt]=r.useState([]),[Y,gt]=r.useState(""),[xt,_t]=r.useState("green"),[h,x]=r.useState(null),[B,F]=r.useState([]),[nt,jt]=r.useState(!1),[Rt,vt]=r.useState(!1),[V,O]=r.useState(null),[y,T]=r.useState(null),[st,rt]=r.useState(""),[Dt,_]=r.useState(!1),[Q,ot]=r.useState(""),[ft,ct]=r.useState(null),[lt,X]=r.useState(null),[It,W]=r.useState(""),[$,c]=r.useState(""),[Lt,M]=r.useState(""),[Ct,w]=r.useState(new Map),[Ot,G]=r.useState(!1),[$t,Ht]=r.useState(0),R=!!(/\.bib$/i.test(v)&&f),dt=r.useMemo(()=>o&&R?Qt(p,l):ri,[o,p,R,l]),Nt=dt.status==="too-large",Tt=dt.status==="invalid",D=dt.entries,Jt=r.useMemo(()=>new Set(D.map(i=>i.citationKey.toLowerCase())),[D]);r.useEffect(()=>{if(!o)return;const i=new AbortController,a=window.setTimeout(()=>{vt(!0),c("");const n=new URLSearchParams;z.trim()&&n.set("q",z.trim()),L&&n.set("tag",L),n.set("page",String(at)),n.set("pageSize","60"),S(`/api/citations?${n.toString()}`,{signal:i.signal}).then(s=>{N(s.entries),ht(s.pagination),s.pagination.page!==at&&C(s.pagination.page)}).catch(s=>{s instanceof Error&&s.name==="AbortError"||c(k(s))}).finally(()=>{i.signal.aborted||vt(!1)})},z.trim()?180:0);return()=>{window.clearTimeout(a),i.abort()}},[o,z,L,at,Ft]),r.useEffect(()=>{if(!o)return;const i=new AbortController;return S("/api/citations/tags",{signal:i.signal}).then(a=>pt(a.tags)).catch(a=>{a instanceof Error&&a.name==="AbortError"||c(k(a))}),()=>i.abort()},[o]),r.useEffect(()=>{if(!o||!R){w(new Map),G(!1);return}const i=new AbortController,a=[...new Set(D.map(n=>n.citationKey))];return a.length?(G(!0),S("/api/citations/lookup",{method:"POST",body:JSON.stringify({keys:a}),signal:i.signal}).then(n=>{w(new Map(n.matches.map(s=>[s.citationKey.toLowerCase(),s])))}).catch(n=>{n instanceof Error&&n.name==="AbortError"||c(k(n))}).finally(()=>{i.signal.aborted||G(!1)}),()=>i.abort()):(w(new Map),G(!1),()=>i.abort())},[o,R,D,$t]),r.useEffect(()=>{o&&(A("library"),d(""),P(""),C(1),ht({page:1,pageSize:60,total:0,totalPages:0}),T(null),_(!1),ot(""),x(null),ct(null),X(null),W(""),c(""),M(""),w(new Map))},[o,R]);const Ut=async i=>{O(i.citationKey),c("");try{const a=Ct.get(i.citationKey.toLowerCase()),n=await S("/api/citations",{method:"POST",body:JSON.stringify({bibtex:i.bibtex,citationKey:i.citationKey,entryType:i.entryType,title:i.title,authors:i.authors,year:i.year,overwrite:!!a,expectedRevision:a?.revision})});N(s=>[n.entry,...s.filter(Z=>Z.id!==n.entry.id&&Z.citationKey.toLowerCase()!==n.entry.citationKey.toLowerCase())]),w(s=>new Map(s).set(n.entry.citationKey.toLowerCase(),{id:n.entry.id,citationKey:n.entry.citationKey,revision:n.entry.revision})),x(n.entry),F(n.entry.tags.map(s=>s.id)),M(n.updated?e("citationLibrary.updated"):e("citationLibrary.saved")),C(1),U(s=>s+1)}catch(a){c(k(a)),Ht(n=>n+1)}finally{O(null)}},wt=async()=>{if(y){O(y.citationKey),c("");try{const i=Kt(st,l);if(!i){c(e("citationErrors.invalid"));return}const a=await S(`/api/citations/${encodeURIComponent(y.id)}`,{method:"PATCH",body:JSON.stringify({bibtex:i.bibtex,citationKey:i.citationKey,entryType:i.entryType,title:i.title,authors:i.authors,year:i.year,expectedRevision:y.revision})});N(n=>[a.entry,...n.filter(s=>s.id!==a.entry.id)]),w(n=>{const s=new Map(n);return s.delete(y.citationKey.toLowerCase()),s.set(a.entry.citationKey.toLowerCase(),{id:a.entry.id,citationKey:a.entry.citationKey,revision:a.entry.revision}),s}),T(null),M(e("citationLibrary.updated")),C(1),U(n=>n+1)}catch(i){c(k(i))}finally{O(null)}}},qt=async()=>{if(Q.trim()){O("__new__"),c("");try{const i=Kt(Q,l);if(!i){c(e("citationErrors.invalid"));return}const a=await S("/api/citations",{method:"POST",body:JSON.stringify({bibtex:i.bibtex,citationKey:i.citationKey,entryType:i.entryType,title:i.title,authors:i.authors,year:i.year})});N(n=>[a.entry,...n.filter(s=>s.id!==a.entry.id&&s.citationKey.toLowerCase()!==a.entry.citationKey.toLowerCase())]),w(n=>new Map(n).set(a.entry.citationKey.toLowerCase(),{id:a.entry.id,citationKey:a.entry.citationKey,revision:a.entry.revision})),_(!1),ot(""),M(a.updated?e("citationLibrary.updated"):e("citationLibraryActions.added")),x(a.entry),F(a.entry.tags.map(n=>n.id)),C(1),U(n=>n+1)}catch(i){c(k(i))}finally{O(null)}}},St=async()=>{if(Y.trim()){c("");try{const i=await S("/api/citations/tags",{method:"POST",body:JSON.stringify({name:Y.trim(),color:xt})});pt(a=>[...a.filter(n=>n.id!==i.tag.id),i.tag].sort((n,s)=>n.name.localeCompare(s.name))),gt(""),M(e("citationLibrary.tagCreated"))}catch(i){c(k(i))}}},Vt=i=>{T(null),_(!1),x(i),F(i.tags.map(a=>a.id))},yt=async()=>{if(h){jt(!0),c("");try{const i=await S(`/api/citations/${encodeURIComponent(h.id)}/tags`,{method:"PATCH",body:JSON.stringify({tagIds:B,expectedRevision:h.revision})});N(a=>a.map(n=>n.id===i.entry.id?i.entry:n)),w(a=>new Map(a).set(i.entry.citationKey.toLowerCase(),{id:i.entry.id,citationKey:i.entry.citationKey,revision:i.entry.revision})),x(i.entry),F(i.entry.tags.map(a=>a.id)),M(e("citationLibrary.tagsUpdated")),U(a=>a+1)}catch(i){c(k(i))}finally{jt(!1)}}},Wt=async i=>{ct(i.id),W("");try{await S(`/api/citations/${encodeURIComponent(i.id)}`,{method:"DELETE"}),N(a=>a.filter(n=>n.id!==i.id)),w(a=>{const n=new Map(a);return n.delete(i.citationKey.toLowerCase()),n}),y?.id===i.id&&T(null),h?.id===i.id&&x(null),M(e("citationLibrary.deleted")),C(1),U(a=>a+1),X(null)}catch(a){W(k(a))}finally{ct(null)}},kt=t.jsxs("div",{className:"citation-library-dialog",children:[t.jsxs("div",{className:"citation-library-tabs",role:"tablist","aria-label":e("citationLibrary.title"),children:[t.jsxs("button",{type:"button",role:"tab","aria-selected":K==="library",className:K==="library"?"active":"",onClick:()=>A("library"),children:[t.jsx(tt,{size:15}),e("citationLibrary.myLibrary"),t.jsx("span",{children:g.total})]}),R&&t.jsxs("button",{type:"button",role:"tab","aria-selected":K==="current",className:K==="current"?"active":"",onClick:()=>A("current"),children:[t.jsx(it,{size:15}),e("citationLibrary.currentFile"),t.jsx("span",{children:D.length})]})]}),$&&!(u&&(h||y))&&t.jsx("p",{className:"error dialog-error",children:$}),Lt&&t.jsxs("p",{className:"citation-library-notice",role:"status",children:[t.jsx(J,{size:14}),Lt,t.jsx("button",{type:"button","aria-label":e("common.close"),onClick:()=>M(""),children:t.jsx(et,{size:13})})]}),K==="library"?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"citation-library-search-row",children:[t.jsxs("label",{className:"citation-library-search",children:[t.jsx(ii,{size:15}),t.jsx("input",{autoFocus:!0,placeholder:e("citationLibrary.searchPlaceholder"),value:z,onChange:i=>{d(i.target.value),C(1)}}),t.jsx("span",{children:e("citationLibrary.searchHint")})]}),t.jsxs("button",{type:"button",className:"citation-add-entry-button",onClick:()=>{_(!0),T(null),x(null)},children:[t.jsx(it,{size:14}),e("citationLibraryActions.addEntry")]})]}),t.jsxs("div",{className:"citation-tag-bar",children:[t.jsxs("div",{className:"citation-tag-filters",children:[t.jsx("button",{type:"button",className:L?"":"active",onClick:()=>{P(""),C(1)},children:e("citationLibrary.allTags")}),q.map(i=>t.jsxs("button",{type:"button",className:L===i.id?"active":"",onClick:()=>{P(L===i.id?"":i.id),C(1)},children:[t.jsx("span",{className:`citation-tag-dot tag-${i.color}`}),i.name]},i.id)),q.length===0&&t.jsx("span",{className:"muted",children:e("citationLibrary.noTags")})]}),t.jsxs("div",{className:"citation-tag-create",children:[t.jsx("input",{value:Y,placeholder:e("citationLibrary.newTagPlaceholder"),onChange:i=>gt(i.target.value),onKeyDown:i=>{i.key==="Enter"&&(i.preventDefault(),St())}}),t.jsx("select",{"aria-label":e("citationLibrary.tagColor"),value:xt,onChange:i=>_t(i.target.value),children:["red","orange","yellow","green","blue","purple","gray"].map(i=>t.jsx("option",{value:i,children:e(`tags.${i}`)},i))}),t.jsxs("button",{type:"button",disabled:!Y.trim(),title:e("citationLibrary.createTag"),onClick:()=>{St()},children:[t.jsx(it,{size:13}),e("citationLibrary.createTag")]})]})]}),Dt&&t.jsxs("div",{className:"citation-edit-panel",children:[t.jsxs("div",{className:"citation-edit-header",children:[t.jsxs("div",{children:[t.jsx("strong",{children:e("citationLibraryActions.addEntryTitle")}),t.jsx("small",{children:e("citationLibraryActions.addEntryDescription")})]}),t.jsx("button",{type:"button","aria-label":e("common.close"),onClick:()=>_(!1),children:t.jsx(et,{size:15})})]}),t.jsx("textarea",{autoFocus:!0,value:Q,onChange:i=>ot(i.target.value),placeholder:e("citationLibraryActions.addEntryPlaceholder"),spellCheck:!1,rows:9}),t.jsxs("div",{className:"citation-edit-actions",children:[t.jsx("button",{type:"button",onClick:()=>_(!1),children:e("common.cancel")}),t.jsxs("button",{type:"button",className:"primary",disabled:!Q.trim()||V==="__new__",onClick:()=>{qt()},children:[V==="__new__"?t.jsx(H,{className:"spin",size:14}):t.jsx(J,{size:14}),e("citationLibraryActions.addEntry")]})]})]}),Rt?t.jsxs("div",{className:"citation-library-empty",children:[t.jsx(H,{className:"spin",size:22}),t.jsx("span",{children:e("common.loading")})]}):I.length===0?t.jsxs("div",{className:"citation-library-empty",children:[t.jsx(tt,{size:28}),t.jsx("strong",{children:e("citationLibrary.emptyTitle")}),t.jsx("span",{children:e(L?"citationLibrary.noTagMatches":"citationLibrary.emptyDescription")})]}):t.jsx("div",{className:"citation-entry-list",children:I.map(i=>t.jsxs("div",{className:"citation-entry-item",children:[t.jsx(Pt,{entry:i,currentUserId:m,selected:h?.id===i.id||y?.id===i.id,alreadyInFile:!u&&Jt.has(i.citationKey.toLowerCase()),importDisabled:E,deleting:ft===i.id,onImport:f?async()=>{await f(i)&&j(!1)}:void 0,onEdit:i.ownerId===m?()=>{x(null),_(!1),T(i),rt(i.bibtex)}:void 0,onTags:i.ownerId===m?()=>Vt(i):void 0,onDelete:i.ownerId===m?()=>{X(i),W("")}:void 0,t:e}),!u&&h?.id===i.id&&t.jsx(mt,{entry:h,tags:q,selectedIds:B,saving:nt,onClose:()=>x(null),onToggle:a=>F(n=>n.includes(a)?n.filter(s=>s!==a):[...n,a]),onSave:()=>{yt()},t:e}),!u&&y?.id===i.id&&t.jsx(Mt,{title:e("citationLibrary.editTitle",{key:y.citationKey}),value:st,saving:V===y.citationKey,onChange:rt,onClose:()=>T(null),onSave:()=>{wt()},t:e})]},i.id))}),g.totalPages>1&&t.jsxs("nav",{className:"citation-library-pagination","aria-label":e("citationLibraryPagination.pageOf",{page:g.page,totalPages:g.totalPages,count:g.total}),children:[t.jsxs("button",{type:"button",disabled:g.page<=1,onClick:()=>C(i=>Math.max(1,i-1)),children:[t.jsx(bt,{size:14}),e("citationLibraryPagination.previous")]}),t.jsx("span",{children:e("citationLibraryPagination.pageOf",{page:g.page,totalPages:g.totalPages,count:g.total})}),t.jsxs("button",{type:"button",disabled:g.page>=g.totalPages,onClick:()=>C(i=>Math.min(g.totalPages,i+1)),children:[e("citationLibraryPagination.next"),t.jsx(Zt,{size:14})]})]})]}):t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"citation-current-heading",children:[t.jsxs("div",{children:[t.jsx("strong",{children:e("citationLibrary.currentFileTitle")}),t.jsx("span",{children:e("citationLibrary.currentFileDescription")})]}),t.jsxs("button",{type:"button",onClick:()=>A("library"),children:[t.jsx(bt,{size:14}),e("citationLibrary.backToLibrary")]})]}),Nt&&t.jsxs("p",{className:"warning citation-library-warning",children:[t.jsx(zt,{size:15}),e("citationLibrary.fileTooLarge",{size:Xt(l)})]}),Tt&&t.jsxs("p",{className:"warning citation-library-warning",children:[t.jsx(zt,{size:15}),e("citationLibrary.fileInvalid")]}),D.length===0?t.jsxs("div",{className:"citation-library-empty",children:[t.jsx(tt,{size:28}),t.jsx("strong",{children:e("citationLibrary.noEntries")}),t.jsx("span",{children:e(Nt?"citationLibrary.fileTooLargeHint":Tt?"citationLibrary.fileInvalidHint":"citationLibrary.noEntriesDescription")})]}):t.jsx("div",{className:"citation-entry-list",children:D.map((i,a)=>t.jsxs("div",{className:"citation-entry-item",children:[t.jsx(Pt,{entry:i,saved:Ct.has(i.citationKey.toLowerCase()),saving:V===i.citationKey,saveDisabled:Ot,onSave:()=>{Ut(i)},t:e}),!u&&h?.citationKey.toLowerCase()===i.citationKey.toLowerCase()&&t.jsx(mt,{entry:h,tags:q,selectedIds:B,saving:nt,onClose:()=>x(null),onToggle:n=>F(s=>s.includes(n)?s.filter(Z=>Z!==n):[...s,n]),onSave:()=>{yt()},t:e})]},`${i.citationKey}-${a}`))})]})]}),Et=t.jsx(Bt,{open:!!lt,title:e("citationLibrary.deleteTitle"),description:e("citationLibrary.deleteDescription",{key:lt?.citationKey??""}),confirmLabel:e("common.delete"),danger:!0,error:It,busy:!!ft,onCancel:()=>{X(null),W("")},onConfirm:()=>{const i=lt;i&&Wt(i)}});return u?t.jsxs(t.Fragment,{children:[t.jsxs("section",{className:"citation-library-page",children:[t.jsxs("header",{className:"citation-library-page-header",children:[t.jsxs("div",{children:[t.jsxs("h1",{children:[t.jsx(tt,{"aria-hidden":!0,size:26}),e("citationLibrary.title")]}),t.jsx("p",{children:e("citationLibraryHomeDescription")})]}),b&&t.jsxs("button",{type:"button",onClick:b,children:[t.jsx(bt,{size:15}),e("projects.title")]})]}),kt]}),h&&t.jsx(ut,{open:!0,title:e("citationLibrary.tagsTitle",{key:h.citationKey}),description:e("citationLibrary.tags"),onOpenChange:i=>{i||(x(null),c(""))},children:t.jsxs(t.Fragment,{children:[$&&t.jsx("p",{className:"error dialog-error",children:$}),t.jsx(mt,{entry:h,tags:q,selectedIds:B,saving:nt,modal:!0,onClose:()=>{x(null),c("")},onToggle:i=>F(a=>a.includes(i)?a.filter(n=>n!==i):[...a,i]),onSave:()=>{yt()},t:e})]})}),y&&t.jsx(ut,{open:!0,extraWide:!0,title:e("citationLibrary.editTitle",{key:y.citationKey}),description:e("citationLibrary.title"),onOpenChange:i=>{i||(T(null),c(""))},children:t.jsxs(t.Fragment,{children:[$&&t.jsx("p",{className:"error dialog-error",children:$}),t.jsx(Mt,{title:e("citationLibrary.editTitle",{key:y.citationKey}),value:st,saving:V===y.citationKey,modal:!0,onChange:rt,onClose:()=>{T(null),c("")},onSave:()=>{wt()},t:e})]})}),Et]}):t.jsxs(t.Fragment,{children:[t.jsx(ut,{open:o,extraWide:!0,className:"citation-library-modal",title:e("citationLibrary.title"),description:e(R?"citationLibrary.description":"citationLibraryHomeDescription",{file:v}),onOpenChange:j,footer:t.jsx("button",{onClick:()=>j(!1),children:e("common.close")}),children:kt}),Et]})}function Pt({entry:o,currentUserId:j,selected:u,alreadyInFile:b,saved:v,saving:p,saveDisabled:E,importDisabled:m,deleting:l,onSave:f,onImport:e,onEdit:K,onTags:A,onDelete:z,t:d}){const L="id"in o,P=L?o:null,I=Gt(o);return t.jsxs("article",{className:`citation-entry-card${u?" selected":""}${b?" already-in-file":""}`,title:b?d("citationErrors.alreadyInFile",{key:o.citationKey}):void 0,children:[t.jsxs("div",{className:"citation-entry-main",children:[t.jsxs("div",{className:"citation-entry-heading",children:[t.jsx("code",{children:o.citationKey}),t.jsxs("span",{children:["@",o.entryType]}),I&&t.jsx("span",{className:"citation-entry-venue",title:I,children:I}),v&&t.jsxs("em",{children:[t.jsx(J,{size:11}),d("citationLibrary.savedLabel")]})]}),t.jsx("strong",{children:o.title||d("citationLibrary.untitled")}),t.jsxs("p",{children:[o.authors||d("citationLibrary.authorUnknown"),o.year&&t.jsxs("span",{children:[" · ",o.year]})]}),P&&P.tags.length>0&&t.jsx("div",{className:"citation-entry-tags",children:P.tags.map(N=>t.jsxs("span",{className:`citation-tag-pill tag-${N.color}`,children:[t.jsx("span",{className:"citation-tag-dot"}),N.name]},N.id))})]}),t.jsxs("div",{className:"citation-entry-actions",children:[f&&t.jsxs("button",{type:"button",className:v?"saved":"primary",disabled:p||E,onClick:f,children:[p||E?t.jsx(H,{className:"spin",size:13}):v?t.jsx(J,{size:13}):t.jsx(it,{size:13}),d(p?"citationLibrary.saving":v?"citationLibrary.update":"citationLibrary.save")]}),e&&t.jsxs("button",{type:"button",disabled:m||b,title:b?d("citationErrors.alreadyInFile",{key:o.citationKey}):d(m?"citationLibrary.importRequiresWrite":"citationLibrary.importToFile"),onClick:()=>{e()},children:[t.jsx(ai,{size:13}),d("citationLibrary.import")]}),P&&A&&t.jsxs("button",{type:"button",onClick:A,children:[t.jsx(ei,{size:13}),d("citationLibrary.tags")]}),L&&K&&t.jsxs("button",{type:"button",onClick:K,children:[t.jsx(ni,{size:13}),d("common.edit")]}),L&&z&&t.jsxs("button",{type:"button",className:"danger-text",disabled:l,onClick:z,children:[l?t.jsx(H,{className:"spin",size:13}):t.jsx(ti,{size:13}),d("common.delete")]})]})]})}function mt({entry:o,tags:j,selectedIds:u,saving:b,modal:v=!1,onClose:p,onToggle:E,onSave:m,t:l}){const f=t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"citation-tag-choice-list",children:j.length===0?t.jsx("span",{className:"muted",children:l("citationLibrary.noTagsToAssign")}):j.map(e=>t.jsxs("label",{children:[t.jsx("input",{type:"checkbox",checked:u.includes(e.id),onChange:()=>E(e.id)}),t.jsx("span",{className:`citation-tag-dot tag-${e.color}`}),e.name]},e.id))}),t.jsxs("div",{className:"citation-edit-actions",children:[t.jsx("button",{type:"button",onClick:p,children:l("common.cancel")}),t.jsxs("button",{type:"button",className:"primary",disabled:b||j.length===0,onClick:m,children:[b?t.jsx(H,{className:"spin",size:14}):t.jsx(J,{size:14}),l("citationLibrary.saveTags")]})]})]});return v?t.jsx("div",{className:"citation-modal-form",children:f}):t.jsxs("div",{className:"citation-tag-editor",children:[t.jsxs("div",{className:"citation-edit-header",children:[t.jsx("strong",{children:l("citationLibrary.tagsTitle",{key:o.citationKey})}),t.jsx("button",{type:"button","aria-label":l("common.close"),onClick:p,children:t.jsx(et,{size:15})})]}),f]})}function Mt({title:o,value:j,saving:u,modal:b=!1,onChange:v,onClose:p,onSave:E,t:m}){const l=t.jsxs(t.Fragment,{children:[t.jsx("textarea",{autoFocus:!b,value:j,onChange:f=>v(f.target.value),spellCheck:!1,rows:12}),t.jsxs("div",{className:"citation-edit-actions",children:[t.jsx("button",{type:"button",onClick:p,children:m("common.cancel")}),t.jsxs("button",{type:"button",className:"primary",disabled:!j.trim()||u,onClick:E,children:[u?t.jsx(H,{className:"spin",size:14}):t.jsx(J,{size:14}),m("common.save")]})]})]});return b?t.jsx("div",{className:"citation-modal-form citation-edit-panel",children:l}):t.jsxs("div",{className:"citation-edit-panel",children:[t.jsxs("div",{className:"citation-edit-header",children:[t.jsx("strong",{children:o}),t.jsx("button",{type:"button","aria-label":m("common.close"),onClick:p,children:t.jsx(et,{size:15})})]}),l]})}export{hi as CitationLibraryDialog};
|
|
1
|
+
import{u as Yt,r,j as t}from"./react-BVUjbTTn.js";import{c as At,g as S,h as k,d as H,C as Bt,M as ut}from"./index-CqBmQAzW.js";import{parseBibEntriesResult as Qt,citationBibtexLimitLabel as Xt,parseSingleBibEntry as Kt,citationVenue as Gt}from"./citationLibrary-ZeQPCv5O.js";import{B as tt,X as et,i as bt,C as Zt,k as zt,T as ti}from"./x-Cspw_pa4.js";import{P as it}from"./plus-BFr75l91.js";import{C as J}from"./check-DKIW1zpR.js";import{S as ii}from"./search-Nu4OcY_n.js";import{T as ei}from"./tags-BSA08jK3.js";const ai=At("Import",[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m8 11 4 4 4-4",key:"1dohi6"}],["path",{d:"M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4",key:"1ywtjm"}]]);const ni=At("PenLine",[["path",{d:"M12 20h9",key:"t2du7b"}],["path",{d:"M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z",key:"1ykcvy"}]]),si=[],ri={status:"empty",entries:si};function hi({open:o,onOpenChange:j,page:u=!1,onBack:b,currentFile:v="",currentSource:p="",readOnly:E=!1,currentUserId:m,maxBibtexBytes:l,onInsert:f}){const{t:e}=Yt(),[K,A]=r.useState("library"),[z,d]=r.useState(""),[L,P]=r.useState(""),[I,N]=r.useState([]),[at,C]=r.useState(1),[g,ht]=r.useState({page:1,pageSize:60,total:0,totalPages:0}),[Ft,U]=r.useState(0),[q,pt]=r.useState([]),[Y,gt]=r.useState(""),[xt,_t]=r.useState("green"),[h,x]=r.useState(null),[B,F]=r.useState([]),[nt,jt]=r.useState(!1),[Rt,vt]=r.useState(!1),[V,O]=r.useState(null),[y,T]=r.useState(null),[st,rt]=r.useState(""),[Dt,_]=r.useState(!1),[Q,ot]=r.useState(""),[ft,ct]=r.useState(null),[lt,X]=r.useState(null),[It,W]=r.useState(""),[$,c]=r.useState(""),[Lt,M]=r.useState(""),[Ct,w]=r.useState(new Map),[Ot,G]=r.useState(!1),[$t,Ht]=r.useState(0),R=!!(/\.bib$/i.test(v)&&f),dt=r.useMemo(()=>o&&R?Qt(p,l):ri,[o,p,R,l]),Nt=dt.status==="too-large",Tt=dt.status==="invalid",D=dt.entries,Jt=r.useMemo(()=>new Set(D.map(i=>i.citationKey.toLowerCase())),[D]);r.useEffect(()=>{if(!o)return;const i=new AbortController,a=window.setTimeout(()=>{vt(!0),c("");const n=new URLSearchParams;z.trim()&&n.set("q",z.trim()),L&&n.set("tag",L),n.set("page",String(at)),n.set("pageSize","60"),S(`/api/citations?${n.toString()}`,{signal:i.signal}).then(s=>{N(s.entries),ht(s.pagination),s.pagination.page!==at&&C(s.pagination.page)}).catch(s=>{s instanceof Error&&s.name==="AbortError"||c(k(s))}).finally(()=>{i.signal.aborted||vt(!1)})},z.trim()?180:0);return()=>{window.clearTimeout(a),i.abort()}},[o,z,L,at,Ft]),r.useEffect(()=>{if(!o)return;const i=new AbortController;return S("/api/citations/tags",{signal:i.signal}).then(a=>pt(a.tags)).catch(a=>{a instanceof Error&&a.name==="AbortError"||c(k(a))}),()=>i.abort()},[o]),r.useEffect(()=>{if(!o||!R){w(new Map),G(!1);return}const i=new AbortController,a=[...new Set(D.map(n=>n.citationKey))];return a.length?(G(!0),S("/api/citations/lookup",{method:"POST",body:JSON.stringify({keys:a}),signal:i.signal}).then(n=>{w(new Map(n.matches.map(s=>[s.citationKey.toLowerCase(),s])))}).catch(n=>{n instanceof Error&&n.name==="AbortError"||c(k(n))}).finally(()=>{i.signal.aborted||G(!1)}),()=>i.abort()):(w(new Map),G(!1),()=>i.abort())},[o,R,D,$t]),r.useEffect(()=>{o&&(A("library"),d(""),P(""),C(1),ht({page:1,pageSize:60,total:0,totalPages:0}),T(null),_(!1),ot(""),x(null),ct(null),X(null),W(""),c(""),M(""),w(new Map))},[o,R]);const Ut=async i=>{O(i.citationKey),c("");try{const a=Ct.get(i.citationKey.toLowerCase()),n=await S("/api/citations",{method:"POST",body:JSON.stringify({bibtex:i.bibtex,citationKey:i.citationKey,entryType:i.entryType,title:i.title,authors:i.authors,year:i.year,overwrite:!!a,expectedRevision:a?.revision})});N(s=>[n.entry,...s.filter(Z=>Z.id!==n.entry.id&&Z.citationKey.toLowerCase()!==n.entry.citationKey.toLowerCase())]),w(s=>new Map(s).set(n.entry.citationKey.toLowerCase(),{id:n.entry.id,citationKey:n.entry.citationKey,revision:n.entry.revision})),x(n.entry),F(n.entry.tags.map(s=>s.id)),M(n.updated?e("citationLibrary.updated"):e("citationLibrary.saved")),C(1),U(s=>s+1)}catch(a){c(k(a)),Ht(n=>n+1)}finally{O(null)}},wt=async()=>{if(y){O(y.citationKey),c("");try{const i=Kt(st,l);if(!i){c(e("citationErrors.invalid"));return}const a=await S(`/api/citations/${encodeURIComponent(y.id)}`,{method:"PATCH",body:JSON.stringify({bibtex:i.bibtex,citationKey:i.citationKey,entryType:i.entryType,title:i.title,authors:i.authors,year:i.year,expectedRevision:y.revision})});N(n=>[a.entry,...n.filter(s=>s.id!==a.entry.id)]),w(n=>{const s=new Map(n);return s.delete(y.citationKey.toLowerCase()),s.set(a.entry.citationKey.toLowerCase(),{id:a.entry.id,citationKey:a.entry.citationKey,revision:a.entry.revision}),s}),T(null),M(e("citationLibrary.updated")),C(1),U(n=>n+1)}catch(i){c(k(i))}finally{O(null)}}},qt=async()=>{if(Q.trim()){O("__new__"),c("");try{const i=Kt(Q,l);if(!i){c(e("citationErrors.invalid"));return}const a=await S("/api/citations",{method:"POST",body:JSON.stringify({bibtex:i.bibtex,citationKey:i.citationKey,entryType:i.entryType,title:i.title,authors:i.authors,year:i.year})});N(n=>[a.entry,...n.filter(s=>s.id!==a.entry.id&&s.citationKey.toLowerCase()!==a.entry.citationKey.toLowerCase())]),w(n=>new Map(n).set(a.entry.citationKey.toLowerCase(),{id:a.entry.id,citationKey:a.entry.citationKey,revision:a.entry.revision})),_(!1),ot(""),M(a.updated?e("citationLibrary.updated"):e("citationLibraryActions.added")),x(a.entry),F(a.entry.tags.map(n=>n.id)),C(1),U(n=>n+1)}catch(i){c(k(i))}finally{O(null)}}},St=async()=>{if(Y.trim()){c("");try{const i=await S("/api/citations/tags",{method:"POST",body:JSON.stringify({name:Y.trim(),color:xt})});pt(a=>[...a.filter(n=>n.id!==i.tag.id),i.tag].sort((n,s)=>n.name.localeCompare(s.name))),gt(""),M(e("citationLibrary.tagCreated"))}catch(i){c(k(i))}}},Vt=i=>{T(null),_(!1),x(i),F(i.tags.map(a=>a.id))},yt=async()=>{if(h){jt(!0),c("");try{const i=await S(`/api/citations/${encodeURIComponent(h.id)}/tags`,{method:"PATCH",body:JSON.stringify({tagIds:B,expectedRevision:h.revision})});N(a=>a.map(n=>n.id===i.entry.id?i.entry:n)),w(a=>new Map(a).set(i.entry.citationKey.toLowerCase(),{id:i.entry.id,citationKey:i.entry.citationKey,revision:i.entry.revision})),x(i.entry),F(i.entry.tags.map(a=>a.id)),M(e("citationLibrary.tagsUpdated")),U(a=>a+1)}catch(i){c(k(i))}finally{jt(!1)}}},Wt=async i=>{ct(i.id),W("");try{await S(`/api/citations/${encodeURIComponent(i.id)}`,{method:"DELETE"}),N(a=>a.filter(n=>n.id!==i.id)),w(a=>{const n=new Map(a);return n.delete(i.citationKey.toLowerCase()),n}),y?.id===i.id&&T(null),h?.id===i.id&&x(null),M(e("citationLibrary.deleted")),C(1),U(a=>a+1),X(null)}catch(a){W(k(a))}finally{ct(null)}},kt=t.jsxs("div",{className:"citation-library-dialog",children:[t.jsxs("div",{className:"citation-library-tabs",role:"tablist","aria-label":e("citationLibrary.title"),children:[t.jsxs("button",{type:"button",role:"tab","aria-selected":K==="library",className:K==="library"?"active":"",onClick:()=>A("library"),children:[t.jsx(tt,{size:15}),e("citationLibrary.myLibrary"),t.jsx("span",{children:g.total})]}),R&&t.jsxs("button",{type:"button",role:"tab","aria-selected":K==="current",className:K==="current"?"active":"",onClick:()=>A("current"),children:[t.jsx(it,{size:15}),e("citationLibrary.currentFile"),t.jsx("span",{children:D.length})]})]}),$&&!(u&&(h||y))&&t.jsx("p",{className:"error dialog-error",children:$}),Lt&&t.jsxs("p",{className:"citation-library-notice",role:"status",children:[t.jsx(J,{size:14}),Lt,t.jsx("button",{type:"button","aria-label":e("common.close"),onClick:()=>M(""),children:t.jsx(et,{size:13})})]}),K==="library"?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"citation-library-search-row",children:[t.jsxs("label",{className:"citation-library-search",children:[t.jsx(ii,{size:15}),t.jsx("input",{autoFocus:!0,placeholder:e("citationLibrary.searchPlaceholder"),value:z,onChange:i=>{d(i.target.value),C(1)}}),t.jsx("span",{children:e("citationLibrary.searchHint")})]}),t.jsxs("button",{type:"button",className:"citation-add-entry-button",onClick:()=>{_(!0),T(null),x(null)},children:[t.jsx(it,{size:14}),e("citationLibraryActions.addEntry")]})]}),t.jsxs("div",{className:"citation-tag-bar",children:[t.jsxs("div",{className:"citation-tag-filters",children:[t.jsx("button",{type:"button",className:L?"":"active",onClick:()=>{P(""),C(1)},children:e("citationLibrary.allTags")}),q.map(i=>t.jsxs("button",{type:"button",className:L===i.id?"active":"",onClick:()=>{P(L===i.id?"":i.id),C(1)},children:[t.jsx("span",{className:`citation-tag-dot tag-${i.color}`}),i.name]},i.id)),q.length===0&&t.jsx("span",{className:"muted",children:e("citationLibrary.noTags")})]}),t.jsxs("div",{className:"citation-tag-create",children:[t.jsx("input",{value:Y,placeholder:e("citationLibrary.newTagPlaceholder"),onChange:i=>gt(i.target.value),onKeyDown:i=>{i.key==="Enter"&&(i.preventDefault(),St())}}),t.jsx("select",{"aria-label":e("citationLibrary.tagColor"),value:xt,onChange:i=>_t(i.target.value),children:["red","orange","yellow","green","blue","purple","gray"].map(i=>t.jsx("option",{value:i,children:e(`tags.${i}`)},i))}),t.jsxs("button",{type:"button",disabled:!Y.trim(),title:e("citationLibrary.createTag"),onClick:()=>{St()},children:[t.jsx(it,{size:13}),e("citationLibrary.createTag")]})]})]}),Dt&&t.jsxs("div",{className:"citation-edit-panel",children:[t.jsxs("div",{className:"citation-edit-header",children:[t.jsxs("div",{children:[t.jsx("strong",{children:e("citationLibraryActions.addEntryTitle")}),t.jsx("small",{children:e("citationLibraryActions.addEntryDescription")})]}),t.jsx("button",{type:"button","aria-label":e("common.close"),onClick:()=>_(!1),children:t.jsx(et,{size:15})})]}),t.jsx("textarea",{autoFocus:!0,value:Q,onChange:i=>ot(i.target.value),placeholder:e("citationLibraryActions.addEntryPlaceholder"),spellCheck:!1,rows:9}),t.jsxs("div",{className:"citation-edit-actions",children:[t.jsx("button",{type:"button",onClick:()=>_(!1),children:e("common.cancel")}),t.jsxs("button",{type:"button",className:"primary",disabled:!Q.trim()||V==="__new__",onClick:()=>{qt()},children:[V==="__new__"?t.jsx(H,{className:"spin",size:14}):t.jsx(J,{size:14}),e("citationLibraryActions.addEntry")]})]})]}),Rt?t.jsxs("div",{className:"citation-library-empty",children:[t.jsx(H,{className:"spin",size:22}),t.jsx("span",{children:e("common.loading")})]}):I.length===0?t.jsxs("div",{className:"citation-library-empty",children:[t.jsx(tt,{size:28}),t.jsx("strong",{children:e("citationLibrary.emptyTitle")}),t.jsx("span",{children:e(L?"citationLibrary.noTagMatches":"citationLibrary.emptyDescription")})]}):t.jsx("div",{className:"citation-entry-list",children:I.map(i=>t.jsxs("div",{className:"citation-entry-item",children:[t.jsx(Pt,{entry:i,currentUserId:m,selected:h?.id===i.id||y?.id===i.id,alreadyInFile:!u&&Jt.has(i.citationKey.toLowerCase()),importDisabled:E,deleting:ft===i.id,onImport:f?async()=>{await f(i)&&j(!1)}:void 0,onEdit:i.ownerId===m?()=>{x(null),_(!1),T(i),rt(i.bibtex)}:void 0,onTags:i.ownerId===m?()=>Vt(i):void 0,onDelete:i.ownerId===m?()=>{X(i),W("")}:void 0,t:e}),!u&&h?.id===i.id&&t.jsx(mt,{entry:h,tags:q,selectedIds:B,saving:nt,onClose:()=>x(null),onToggle:a=>F(n=>n.includes(a)?n.filter(s=>s!==a):[...n,a]),onSave:()=>{yt()},t:e}),!u&&y?.id===i.id&&t.jsx(Mt,{title:e("citationLibrary.editTitle",{key:y.citationKey}),value:st,saving:V===y.citationKey,onChange:rt,onClose:()=>T(null),onSave:()=>{wt()},t:e})]},i.id))}),g.totalPages>1&&t.jsxs("nav",{className:"citation-library-pagination","aria-label":e("citationLibraryPagination.pageOf",{page:g.page,totalPages:g.totalPages,count:g.total}),children:[t.jsxs("button",{type:"button",disabled:g.page<=1,onClick:()=>C(i=>Math.max(1,i-1)),children:[t.jsx(bt,{size:14}),e("citationLibraryPagination.previous")]}),t.jsx("span",{children:e("citationLibraryPagination.pageOf",{page:g.page,totalPages:g.totalPages,count:g.total})}),t.jsxs("button",{type:"button",disabled:g.page>=g.totalPages,onClick:()=>C(i=>Math.min(g.totalPages,i+1)),children:[e("citationLibraryPagination.next"),t.jsx(Zt,{size:14})]})]})]}):t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"citation-current-heading",children:[t.jsxs("div",{children:[t.jsx("strong",{children:e("citationLibrary.currentFileTitle")}),t.jsx("span",{children:e("citationLibrary.currentFileDescription")})]}),t.jsxs("button",{type:"button",onClick:()=>A("library"),children:[t.jsx(bt,{size:14}),e("citationLibrary.backToLibrary")]})]}),Nt&&t.jsxs("p",{className:"warning citation-library-warning",children:[t.jsx(zt,{size:15}),e("citationLibrary.fileTooLarge",{size:Xt(l)})]}),Tt&&t.jsxs("p",{className:"warning citation-library-warning",children:[t.jsx(zt,{size:15}),e("citationLibrary.fileInvalid")]}),D.length===0?t.jsxs("div",{className:"citation-library-empty",children:[t.jsx(tt,{size:28}),t.jsx("strong",{children:e("citationLibrary.noEntries")}),t.jsx("span",{children:e(Nt?"citationLibrary.fileTooLargeHint":Tt?"citationLibrary.fileInvalidHint":"citationLibrary.noEntriesDescription")})]}):t.jsx("div",{className:"citation-entry-list",children:D.map((i,a)=>t.jsxs("div",{className:"citation-entry-item",children:[t.jsx(Pt,{entry:i,saved:Ct.has(i.citationKey.toLowerCase()),saving:V===i.citationKey,saveDisabled:Ot,onSave:()=>{Ut(i)},t:e}),!u&&h?.citationKey.toLowerCase()===i.citationKey.toLowerCase()&&t.jsx(mt,{entry:h,tags:q,selectedIds:B,saving:nt,onClose:()=>x(null),onToggle:n=>F(s=>s.includes(n)?s.filter(Z=>Z!==n):[...s,n]),onSave:()=>{yt()},t:e})]},`${i.citationKey}-${a}`))})]})]}),Et=t.jsx(Bt,{open:!!lt,title:e("citationLibrary.deleteTitle"),description:e("citationLibrary.deleteDescription",{key:lt?.citationKey??""}),confirmLabel:e("common.delete"),danger:!0,error:It,busy:!!ft,onCancel:()=>{X(null),W("")},onConfirm:()=>{const i=lt;i&&Wt(i)}});return u?t.jsxs(t.Fragment,{children:[t.jsxs("section",{className:"citation-library-page",children:[t.jsxs("header",{className:"citation-library-page-header",children:[t.jsxs("div",{children:[t.jsxs("h1",{children:[t.jsx(tt,{"aria-hidden":!0,size:26}),e("citationLibrary.title")]}),t.jsx("p",{children:e("citationLibraryHomeDescription")})]}),b&&t.jsxs("button",{type:"button",onClick:b,children:[t.jsx(bt,{size:15}),e("projects.title")]})]}),kt]}),h&&t.jsx(ut,{open:!0,title:e("citationLibrary.tagsTitle",{key:h.citationKey}),description:e("citationLibrary.tags"),onOpenChange:i=>{i||(x(null),c(""))},children:t.jsxs(t.Fragment,{children:[$&&t.jsx("p",{className:"error dialog-error",children:$}),t.jsx(mt,{entry:h,tags:q,selectedIds:B,saving:nt,modal:!0,onClose:()=>{x(null),c("")},onToggle:i=>F(a=>a.includes(i)?a.filter(n=>n!==i):[...a,i]),onSave:()=>{yt()},t:e})]})}),y&&t.jsx(ut,{open:!0,extraWide:!0,title:e("citationLibrary.editTitle",{key:y.citationKey}),description:e("citationLibrary.title"),onOpenChange:i=>{i||(T(null),c(""))},children:t.jsxs(t.Fragment,{children:[$&&t.jsx("p",{className:"error dialog-error",children:$}),t.jsx(Mt,{title:e("citationLibrary.editTitle",{key:y.citationKey}),value:st,saving:V===y.citationKey,modal:!0,onChange:rt,onClose:()=>{T(null),c("")},onSave:()=>{wt()},t:e})]})}),Et]}):t.jsxs(t.Fragment,{children:[t.jsx(ut,{open:o,extraWide:!0,className:"citation-library-modal",title:e("citationLibrary.title"),description:e(R?"citationLibrary.description":"citationLibraryHomeDescription",{file:v}),onOpenChange:j,footer:t.jsx("button",{onClick:()=>j(!1),children:e("common.close")}),children:kt}),Et]})}function Pt({entry:o,currentUserId:j,selected:u,alreadyInFile:b,saved:v,saving:p,saveDisabled:E,importDisabled:m,deleting:l,onSave:f,onImport:e,onEdit:K,onTags:A,onDelete:z,t:d}){const L="id"in o,P=L?o:null,I=Gt(o);return t.jsxs("article",{className:`citation-entry-card${u?" selected":""}${b?" already-in-file":""}`,title:b?d("citationErrors.alreadyInFile",{key:o.citationKey}):void 0,children:[t.jsxs("div",{className:"citation-entry-main",children:[t.jsxs("div",{className:"citation-entry-heading",children:[t.jsx("code",{children:o.citationKey}),t.jsxs("span",{children:["@",o.entryType]}),I&&t.jsx("span",{className:"citation-entry-venue",title:I,children:I}),v&&t.jsxs("em",{children:[t.jsx(J,{size:11}),d("citationLibrary.savedLabel")]})]}),t.jsx("strong",{children:o.title||d("citationLibrary.untitled")}),t.jsxs("p",{children:[o.authors||d("citationLibrary.authorUnknown"),o.year&&t.jsxs("span",{children:[" · ",o.year]})]}),P&&P.tags.length>0&&t.jsx("div",{className:"citation-entry-tags",children:P.tags.map(N=>t.jsxs("span",{className:`citation-tag-pill tag-${N.color}`,children:[t.jsx("span",{className:"citation-tag-dot"}),N.name]},N.id))})]}),t.jsxs("div",{className:"citation-entry-actions",children:[f&&t.jsxs("button",{type:"button",className:v?"saved":"primary",disabled:p||E,onClick:f,children:[p||E?t.jsx(H,{className:"spin",size:13}):v?t.jsx(J,{size:13}):t.jsx(it,{size:13}),d(p?"citationLibrary.saving":v?"citationLibrary.update":"citationLibrary.save")]}),e&&t.jsxs("button",{type:"button",disabled:m||b,title:b?d("citationErrors.alreadyInFile",{key:o.citationKey}):d(m?"citationLibrary.importRequiresWrite":"citationLibrary.importToFile"),onClick:()=>{e()},children:[t.jsx(ai,{size:13}),d("citationLibrary.import")]}),P&&A&&t.jsxs("button",{type:"button",onClick:A,children:[t.jsx(ei,{size:13}),d("citationLibrary.tags")]}),L&&K&&t.jsxs("button",{type:"button",onClick:K,children:[t.jsx(ni,{size:13}),d("common.edit")]}),L&&z&&t.jsxs("button",{type:"button",className:"danger-text",disabled:l,onClick:z,children:[l?t.jsx(H,{className:"spin",size:13}):t.jsx(ti,{size:13}),d("common.delete")]})]})]})}function mt({entry:o,tags:j,selectedIds:u,saving:b,modal:v=!1,onClose:p,onToggle:E,onSave:m,t:l}){const f=t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"citation-tag-choice-list",children:j.length===0?t.jsx("span",{className:"muted",children:l("citationLibrary.noTagsToAssign")}):j.map(e=>t.jsxs("label",{children:[t.jsx("input",{type:"checkbox",checked:u.includes(e.id),onChange:()=>E(e.id)}),t.jsx("span",{className:`citation-tag-dot tag-${e.color}`}),e.name]},e.id))}),t.jsxs("div",{className:"citation-edit-actions",children:[t.jsx("button",{type:"button",onClick:p,children:l("common.cancel")}),t.jsxs("button",{type:"button",className:"primary",disabled:b||j.length===0,onClick:m,children:[b?t.jsx(H,{className:"spin",size:14}):t.jsx(J,{size:14}),l("citationLibrary.saveTags")]})]})]});return v?t.jsx("div",{className:"citation-modal-form",children:f}):t.jsxs("div",{className:"citation-tag-editor",children:[t.jsxs("div",{className:"citation-edit-header",children:[t.jsx("strong",{children:l("citationLibrary.tagsTitle",{key:o.citationKey})}),t.jsx("button",{type:"button","aria-label":l("common.close"),onClick:p,children:t.jsx(et,{size:15})})]}),f]})}function Mt({title:o,value:j,saving:u,modal:b=!1,onChange:v,onClose:p,onSave:E,t:m}){const l=t.jsxs(t.Fragment,{children:[t.jsx("textarea",{autoFocus:!b,value:j,onChange:f=>v(f.target.value),spellCheck:!1,rows:12}),t.jsxs("div",{className:"citation-edit-actions",children:[t.jsx("button",{type:"button",onClick:p,children:m("common.cancel")}),t.jsxs("button",{type:"button",className:"primary",disabled:!j.trim()||u,onClick:E,children:[u?t.jsx(H,{className:"spin",size:14}):t.jsx(J,{size:14}),m("common.save")]})]})]});return b?t.jsx("div",{className:"citation-modal-form citation-edit-panel",children:l}):t.jsxs("div",{className:"citation-edit-panel",children:[t.jsxs("div",{className:"citation-edit-header",children:[t.jsx("strong",{children:o}),t.jsx("button",{type:"button","aria-label":m("common.close"),onClick:p,children:t.jsx(et,{size:15})})]}),l]})}export{hi as CitationLibraryDialog};
|