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
|
@@ -83,6 +83,7 @@ function optionalToolDefinitions(gitCommand, gitTimeoutMs) {
|
|
|
83
83
|
{ id: "bibtex", name: "BibTeX", command: "bibtex", requirement: "optional", purpose: "BibTeX bibliography builds" },
|
|
84
84
|
{ id: "biber", name: "Biber", command: "biber", requirement: "optional", purpose: "Biber bibliography builds" },
|
|
85
85
|
{ id: "makeindex", name: "MakeIndex", command: "makeindex", requirement: "optional", purpose: "Index generation", versionArgs: [] },
|
|
86
|
+
{ id: "chktex", name: "ChkTeX", command: "chktex", requirement: "optional", purpose: "LaTeX writing checks", versionArgs: ["--version"] },
|
|
86
87
|
{ id: "harper-cli", name: "Harper CLI", command: "harper-cli", requirement: "optional", purpose: "TexLite spelling and grammar checks" },
|
|
87
88
|
{ id: "harper-ls", name: "Harper language server", command: "harper-ls", requirement: "optional", purpose: "External editor integration (not used by TexLite)" }
|
|
88
89
|
];
|
package/dist/server/history.js
CHANGED
|
@@ -272,8 +272,8 @@ export class ProjectHistoryService {
|
|
|
272
272
|
return { restoredPaths: [filePath], manifest };
|
|
273
273
|
}
|
|
274
274
|
this.restoreProjectTree(projectId, manifest);
|
|
275
|
-
this.db.prepare("UPDATE projects SET main_file = ?, engine = ?, latexmkrc = ? WHERE id = ?")
|
|
276
|
-
.run(manifest.settings.mainFile, manifest.settings.engine, manifest.settings.latexmkrc, projectId);
|
|
275
|
+
this.db.prepare("UPDATE projects SET main_file = ?, engine = ?, latexmkrc = ?, chktex_enabled = ? WHERE id = ?")
|
|
276
|
+
.run(manifest.settings.mainFile, manifest.settings.engine, manifest.settings.latexmkrc, manifest.settings.chktexEnabled ? 1 : 0, projectId);
|
|
277
277
|
return { restoredPaths: Object.keys(manifest.files).sort(), manifest };
|
|
278
278
|
}
|
|
279
279
|
assertRestoreTargetIsFile(projectId, filePath) {
|
|
@@ -570,7 +570,7 @@ export class ProjectHistoryService {
|
|
|
570
570
|
}
|
|
571
571
|
}
|
|
572
572
|
function settings(project) {
|
|
573
|
-
return { mainFile: project.main_file, engine: project.engine, latexmkrc: project.latexmkrc };
|
|
573
|
+
return { mainFile: project.main_file, engine: project.engine, latexmkrc: project.latexmkrc, chktexEnabled: Boolean(project.chktex_enabled) };
|
|
574
574
|
}
|
|
575
575
|
function cloneManifest(manifest) {
|
|
576
576
|
return { version: 1, files: { ...manifest.files }, settings: { ...manifest.settings } };
|
|
@@ -579,7 +579,7 @@ function parseManifest(value) {
|
|
|
579
579
|
const parsed = JSON.parse(value);
|
|
580
580
|
if (parsed.version !== 1 || !parsed.files || !parsed.settings)
|
|
581
581
|
throw new Error("History manifest is invalid");
|
|
582
|
-
return parsed;
|
|
582
|
+
return { ...parsed, settings: { ...parsed.settings, chktexEnabled: parsed.settings.chktexEnabled === true } };
|
|
583
583
|
}
|
|
584
584
|
function parseStringArray(value) {
|
|
585
585
|
try {
|
package/dist/server/i18n.js
CHANGED
|
@@ -11,6 +11,10 @@ const enMessages = {
|
|
|
11
11
|
CITATION_NOT_FOUND: "The citation does not exist.",
|
|
12
12
|
CITATION_TAG_NOT_FOUND: "The citation tag does not exist.",
|
|
13
13
|
CITATION_TOO_LARGE: "The BibTeX entry is too large.",
|
|
14
|
+
CHK_TEX_SOURCE_INVALID: "The source to check is invalid.",
|
|
15
|
+
CHK_TEX_SOURCE_TOO_LARGE: "The source to check is too large.",
|
|
16
|
+
CHK_TEX_SUPERSEDED: "A newer ChkTeX check replaced this request.",
|
|
17
|
+
CHK_TEX_UNAVAILABLE: "ChkTeX is not installed or cannot run on this server. Install the TeX Live chktex package and try again.",
|
|
14
18
|
COMMENT_DELETE_FORBIDDEN: "You can only delete your own comments.",
|
|
15
19
|
COMMENT_EDIT_FORBIDDEN: "You can only edit your own comments.",
|
|
16
20
|
COMMENT_FILE_NOT_FOUND: "The file for this comment does not exist.",
|
|
@@ -77,6 +81,7 @@ const enMessages = {
|
|
|
77
81
|
PROJECT_ICON_INVALID: "Enter a valid installed Lucide icon short name.",
|
|
78
82
|
PROJECT_NOT_FOUND: "The project does not exist or is no longer accessible.",
|
|
79
83
|
PROJECT_OWNER_ONLY: "Only the project owner can perform this action.",
|
|
84
|
+
PROJECT_SETTINGS_CONFLICT: "These project settings changed in another session. Refresh the project page before saving again.",
|
|
80
85
|
PROJECT_TRANSFER_FORBIDDEN: "Only the current project owner can transfer ownership.",
|
|
81
86
|
PROJECT_TRANSFER_SELF: "You already own this project.",
|
|
82
87
|
PROJECT_TRANSFER_TARGET_INVALID: "Choose a new project owner.",
|
|
@@ -106,7 +111,7 @@ const enMessages = {
|
|
|
106
111
|
WORD_COUNT_FAILED: "TeXcount could not count this source.",
|
|
107
112
|
WORD_COUNT_SOURCE_INVALID: "Select LaTeX source text to count.",
|
|
108
113
|
WORD_COUNT_SOURCE_TOO_LARGE: "The selected source is too large to count.",
|
|
109
|
-
WORD_COUNT_UNAVAILABLE: "
|
|
114
|
+
WORD_COUNT_UNAVAILABLE: "The TeX Live texcount package is not installed on this server. Install it and try again.",
|
|
110
115
|
ZIP_DUPLICATE_ENTRY: "The ZIP archive contains a duplicate filename: {{path}}.",
|
|
111
116
|
ZIP_ENTRY_READ_FAILED: "A ZIP archive entry could not be read.",
|
|
112
117
|
ZIP_ENTRY_TOO_LARGE: "The ZIP archive contains a file that exceeds the permitted size.",
|
|
@@ -143,6 +148,10 @@ const zhMessages = {
|
|
|
143
148
|
CITATION_NOT_FOUND: "引用条目不存在。",
|
|
144
149
|
CITATION_TAG_NOT_FOUND: "引用标签不存在。",
|
|
145
150
|
CITATION_TOO_LARGE: "BibTeX 条目过大。",
|
|
151
|
+
CHK_TEX_SOURCE_INVALID: "待检查源码格式不正确。",
|
|
152
|
+
CHK_TEX_SOURCE_TOO_LARGE: "待检查源码过大。",
|
|
153
|
+
CHK_TEX_SUPERSEDED: "更新的 ChkTeX 检查已替代此请求。",
|
|
154
|
+
CHK_TEX_UNAVAILABLE: "服务器未安装 ChkTeX,或 ChkTeX 当前无法运行。请在服务器安装 TeX Live 的 chktex 包后重试。",
|
|
146
155
|
COMMENT_DELETE_FORBIDDEN: "只能删除自己的批注。",
|
|
147
156
|
COMMENT_EDIT_FORBIDDEN: "只能编辑自己的批注。",
|
|
148
157
|
COMMENT_FILE_NOT_FOUND: "批注对应的文件不存在。",
|
|
@@ -209,6 +218,7 @@ const zhMessages = {
|
|
|
209
218
|
PROJECT_ICON_INVALID: "请输入已安装的有效 Lucide 图标短名称。",
|
|
210
219
|
PROJECT_NOT_FOUND: "项目不存在或你已无法访问。",
|
|
211
220
|
PROJECT_OWNER_ONLY: "只有项目所有者可以执行此操作。",
|
|
221
|
+
PROJECT_SETTINGS_CONFLICT: "项目设置已在其他会话中更改。请刷新项目页面后再保存。",
|
|
212
222
|
PROJECT_TRANSFER_FORBIDDEN: "只有当前项目所有者可以转让项目。",
|
|
213
223
|
PROJECT_TRANSFER_SELF: "你已经是该项目的所有者。",
|
|
214
224
|
PROJECT_TRANSFER_TARGET_INVALID: "请选择新的项目所有者。",
|
|
@@ -238,7 +248,7 @@ const zhMessages = {
|
|
|
238
248
|
WORD_COUNT_FAILED: "TeXcount 无法统计这段源码。",
|
|
239
249
|
WORD_COUNT_SOURCE_INVALID: "请选择 LaTeX 源码后再统计。",
|
|
240
250
|
WORD_COUNT_SOURCE_TOO_LARGE: "所选源码过大,无法统计。",
|
|
241
|
-
WORD_COUNT_UNAVAILABLE: "服务器未安装
|
|
251
|
+
WORD_COUNT_UNAVAILABLE: "服务器未安装 TeX Live 的 texcount 包,请安装后重试。",
|
|
242
252
|
ZIP_DUPLICATE_ENTRY: "ZIP 压缩包包含重复文件名:{{path}}。",
|
|
243
253
|
ZIP_ENTRY_READ_FAILED: "无法读取 ZIP 压缩包中的文件。",
|
|
244
254
|
ZIP_ENTRY_TOO_LARGE: "ZIP 压缩包中存在超过限制的文件。",
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
const CHUNK_SIZE = 4 * 1024 * 1024;
|
|
5
|
+
const SESSION_TTL_MS = 30 * 60 * 1000;
|
|
6
|
+
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
|
|
7
|
+
const MAX_ACTIVE_UPLOADS = 8;
|
|
8
|
+
const MAX_ACTIVE_UPLOADS_PER_USER = 2;
|
|
9
|
+
const MAX_IN_FLIGHT_CHUNKS_PER_UPLOAD = 4;
|
|
10
|
+
export class ProjectImportUploadError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code) {
|
|
13
|
+
super(code);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = "ProjectImportUploadError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Bounded, per-user temporary storage for parallel ZIP upload chunks. */
|
|
19
|
+
export class ProjectImportUploads {
|
|
20
|
+
rootDirectory;
|
|
21
|
+
sessions = new Map();
|
|
22
|
+
cleanupTimer;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.rootDirectory = path.join(config.dataDir, "tmp", "project-imports");
|
|
25
|
+
this.cleanupTimer = setInterval(() => { void this.cleanupExpired().catch(() => undefined); }, CLEANUP_INTERVAL_MS);
|
|
26
|
+
this.cleanupTimer.unref();
|
|
27
|
+
}
|
|
28
|
+
async initialize() {
|
|
29
|
+
await fs.mkdir(this.rootDirectory, { recursive: true, mode: 0o700 });
|
|
30
|
+
// Any remaining entries are abandoned uploads from a previous server run.
|
|
31
|
+
const entries = await fs.readdir(this.rootDirectory, { withFileTypes: true });
|
|
32
|
+
await Promise.all(entries.map((entry) => fs.rm(path.join(this.rootDirectory, entry.name), { recursive: true, force: true })));
|
|
33
|
+
}
|
|
34
|
+
async start(userId, filename, size, maxBytes) {
|
|
35
|
+
await this.cleanupExpired();
|
|
36
|
+
if (!Number.isSafeInteger(size) || size <= 0 || size > maxBytes)
|
|
37
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_SIZE_INVALID");
|
|
38
|
+
const active = [...this.sessions.values()];
|
|
39
|
+
const reservedBytes = active.reduce((sum, session) => sum + session.size, 0);
|
|
40
|
+
if (active.length >= MAX_ACTIVE_UPLOADS || active.filter((session) => session.userId === userId).length >= MAX_ACTIVE_UPLOADS_PER_USER || reservedBytes + size > maxBytes * 2) {
|
|
41
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_LIMIT");
|
|
42
|
+
}
|
|
43
|
+
const id = randomUUID();
|
|
44
|
+
const directory = path.join(this.rootDirectory, id);
|
|
45
|
+
await fs.mkdir(directory, { recursive: false, mode: 0o700 });
|
|
46
|
+
const chunkCount = Math.ceil(size / CHUNK_SIZE);
|
|
47
|
+
const timestamp = Date.now();
|
|
48
|
+
this.sessions.set(id, {
|
|
49
|
+
id, userId, filename, size, chunkCount, directory, received: new Set(), inFlight: new Set(),
|
|
50
|
+
updatedAt: timestamp, completing: false
|
|
51
|
+
});
|
|
52
|
+
return { uploadId: id, chunkSize: CHUNK_SIZE, chunkCount };
|
|
53
|
+
}
|
|
54
|
+
async writeChunk(userId, uploadId, index, bytes) {
|
|
55
|
+
const session = this.getSession(userId, uploadId);
|
|
56
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= session.chunkCount || session.completing) {
|
|
57
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_CHUNK_INVALID");
|
|
58
|
+
}
|
|
59
|
+
if (session.inFlight.size >= MAX_IN_FLIGHT_CHUNKS_PER_UPLOAD)
|
|
60
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_LIMIT");
|
|
61
|
+
if (session.received.has(index) || session.inFlight.has(index))
|
|
62
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_CHUNK_DUPLICATE");
|
|
63
|
+
const expectedSize = Math.min(CHUNK_SIZE, session.size - index * CHUNK_SIZE);
|
|
64
|
+
if (bytes.length !== expectedSize)
|
|
65
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_CHUNK_INVALID");
|
|
66
|
+
session.inFlight.add(index);
|
|
67
|
+
try {
|
|
68
|
+
await fs.writeFile(path.join(session.directory, `${index}.part`), bytes, { flag: "wx", mode: 0o600 });
|
|
69
|
+
session.received.add(index);
|
|
70
|
+
session.updatedAt = Date.now();
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
session.inFlight.delete(index);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async assemble(userId, uploadId) {
|
|
77
|
+
const session = this.getSession(userId, uploadId);
|
|
78
|
+
if (session.completing || session.inFlight.size > 0 || session.received.size !== session.chunkCount) {
|
|
79
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_INCOMPLETE");
|
|
80
|
+
}
|
|
81
|
+
session.completing = true;
|
|
82
|
+
try {
|
|
83
|
+
const buffer = Buffer.allocUnsafe(session.size);
|
|
84
|
+
let offset = 0;
|
|
85
|
+
for (let index = 0; index < session.chunkCount; index += 1) {
|
|
86
|
+
const chunk = await fs.readFile(path.join(session.directory, `${index}.part`));
|
|
87
|
+
if (chunk.length !== Math.min(CHUNK_SIZE, session.size - offset))
|
|
88
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_CHUNK_INVALID");
|
|
89
|
+
chunk.copy(buffer, offset);
|
|
90
|
+
offset += chunk.length;
|
|
91
|
+
}
|
|
92
|
+
if (offset !== session.size)
|
|
93
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_CHUNK_INVALID");
|
|
94
|
+
return { filename: session.filename, buffer };
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error instanceof ProjectImportUploadError)
|
|
98
|
+
throw error;
|
|
99
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_INCOMPLETE");
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await this.removeSession(session);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async cancel(userId, uploadId) {
|
|
106
|
+
const session = this.sessions.get(uploadId);
|
|
107
|
+
if (!session || session.userId !== userId || session.completing || session.inFlight.size > 0)
|
|
108
|
+
return;
|
|
109
|
+
await this.removeSession(session);
|
|
110
|
+
}
|
|
111
|
+
async close() {
|
|
112
|
+
clearInterval(this.cleanupTimer);
|
|
113
|
+
await Promise.all([...this.sessions.values()].map((session) => this.removeSession(session)));
|
|
114
|
+
}
|
|
115
|
+
getSession(userId, uploadId) {
|
|
116
|
+
const session = this.sessions.get(uploadId);
|
|
117
|
+
if (!session || session.userId !== userId || Date.now() - session.updatedAt > SESSION_TTL_MS) {
|
|
118
|
+
if (session && Date.now() - session.updatedAt > SESSION_TTL_MS)
|
|
119
|
+
void this.removeSession(session).catch(() => undefined);
|
|
120
|
+
throw new ProjectImportUploadError("ZIP_UPLOAD_NOT_FOUND");
|
|
121
|
+
}
|
|
122
|
+
return session;
|
|
123
|
+
}
|
|
124
|
+
async cleanupExpired() {
|
|
125
|
+
const expired = [...this.sessions.values()].filter((session) => !session.completing && session.inFlight.size === 0 && Date.now() - session.updatedAt > SESSION_TTL_MS);
|
|
126
|
+
await Promise.all(expired.map((session) => this.removeSession(session)));
|
|
127
|
+
}
|
|
128
|
+
async removeSession(session) {
|
|
129
|
+
if (this.sessions.get(session.id) === session)
|
|
130
|
+
this.sessions.delete(session.id);
|
|
131
|
+
await fs.rm(session.directory, { recursive: true, force: true });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -477,6 +477,9 @@ export function registerProjectFileRoutes(app, context) {
|
|
|
477
477
|
throw error;
|
|
478
478
|
}
|
|
479
479
|
fs.rmSync(absolute, { recursive: true, force: true });
|
|
480
|
+
if (currentProject.latexmkrc === relative || currentProject.latexmkrc?.startsWith(`${relative}/`)) {
|
|
481
|
+
db.prepare("UPDATE projects SET latexmkrc = NULL WHERE id = ?").run(id);
|
|
482
|
+
}
|
|
480
483
|
const deleteResult = db.prepare("DELETE FROM comments WHERE project_id = ? AND (file_path = ? OR file_path GLOB ?)").run(id, relative, `${escapeGlobPattern(relative)}/*`);
|
|
481
484
|
touchProject(db, id, user.id);
|
|
482
485
|
collaboration.removePath(id, relative);
|
|
@@ -97,6 +97,7 @@ export function projectJson(project, tags = [], commentsSummary, unreadMentionCo
|
|
|
97
97
|
latexmkrc: project.latexmkrc,
|
|
98
98
|
engine: project.engine,
|
|
99
99
|
icon: project.icon,
|
|
100
|
+
chktexEnabled: Boolean(project.chktex_enabled),
|
|
100
101
|
permission: project.permission,
|
|
101
102
|
tags,
|
|
102
103
|
unresolvedCommentCount: commentsSummary?.unresolvedCount ?? 0,
|
|
@@ -11,14 +11,15 @@ import { accessibleProject, canEdit } from "../projects.js";
|
|
|
11
11
|
import { writeProjectArchive } from "../archive.js";
|
|
12
12
|
import { extractProjectZip, ZipValidationError } from "../zip.js";
|
|
13
13
|
import { HarperLintSupersededError, HarperUnavailableError } from "../harper.js";
|
|
14
|
+
import { ChktexLintSupersededError, ChktexUnavailableError } from "../chktex.js";
|
|
14
15
|
import { digestToken } from "../security.js";
|
|
15
|
-
import { supportsWritingChecks } from "../../shared/writingChecks.js";
|
|
16
|
+
import { supportsChktexChecks, supportsWritingChecks } from "../../shared/writingChecks.js";
|
|
16
17
|
import { unreadMentionCountsForProjects } from "../commentMentions.js";
|
|
17
18
|
import { commentsSummaryForProject, commentsSummaryForProjects, dictionaryWord, escapeLikePattern, now, projectJson, requireProjectOwnerPermission, tagColors, tagsForProject, tagsForProjects, text } from "./projectShared.js";
|
|
18
19
|
const clientIdPattern = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
|
|
19
20
|
/** Register project catalog, metadata, archive, dictionary, tag, export, and deletion routes. */
|
|
20
21
|
export function registerProjectCatalogRoutes(app, context) {
|
|
21
|
-
const { config, db, collaboration, projectMutations, latexCompletions, projectOutlines, harper, recordHistory } = context;
|
|
22
|
+
const { config, db, collaboration, projectMutations, latexCompletions, projectOutlines, harper, chktex, recordHistory } = context;
|
|
22
23
|
// Advanced project icons are served as cacheable SVG masks. The browser
|
|
23
24
|
// therefore does not have to bundle Lucide's entire icon catalogue merely
|
|
24
25
|
// because an owner selected an icon outside the curated picker.
|
|
@@ -192,12 +193,12 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
192
193
|
const body = request.body;
|
|
193
194
|
const project = {
|
|
194
195
|
id: randomUUID(), owner_id: user.id, last_modified_by: user.id, name: text(body?.name, 120),
|
|
195
|
-
main_file: "main.tex", latexmkrc: null, engine: config.defaultEngine, icon: null, created_at: now(), updated_at: now()
|
|
196
|
+
main_file: "main.tex", latexmkrc: null, engine: config.defaultEngine, icon: null, chktex_enabled: 0, created_at: now(), updated_at: now()
|
|
196
197
|
};
|
|
197
198
|
createProjectFiles(config, project.id);
|
|
198
199
|
try {
|
|
199
|
-
db.prepare(`INSERT INTO projects (id, owner_id, last_modified_by, name, main_file, latexmkrc, engine, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
200
|
-
.run(project.id, project.owner_id, project.last_modified_by, project.name, project.main_file, project.latexmkrc, project.engine, project.created_at, project.updated_at);
|
|
200
|
+
db.prepare(`INSERT INTO projects (id, owner_id, last_modified_by, name, main_file, latexmkrc, engine, chktex_enabled, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
201
|
+
.run(project.id, project.owner_id, project.last_modified_by, project.name, project.main_file, project.latexmkrc, project.engine, project.chktex_enabled, project.created_at, project.updated_at);
|
|
201
202
|
}
|
|
202
203
|
catch (error) {
|
|
203
204
|
removeProjectDirectory(config, project.id);
|
|
@@ -224,16 +225,16 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
224
225
|
const fallbackName = path.basename(part.filename, path.extname(part.filename));
|
|
225
226
|
const project = {
|
|
226
227
|
id: randomUUID(), owner_id: user.id, last_modified_by: user.id, name: text(query.name || fallbackName, 120),
|
|
227
|
-
main_file: "", latexmkrc: null, engine: config.defaultEngine, icon: null, created_at: now(), updated_at: now()
|
|
228
|
+
main_file: "", latexmkrc: null, engine: config.defaultEngine, icon: null, chktex_enabled: 0, created_at: now(), updated_at: now()
|
|
228
229
|
};
|
|
229
230
|
fs.mkdirSync(sourceRoot(config, project.id), { recursive: true, mode: 0o700 });
|
|
230
231
|
fs.mkdirSync(outputRoot(config, project.id), { recursive: true, mode: 0o700 });
|
|
231
232
|
try {
|
|
232
233
|
const extracted = await extractProjectZip(await part.toBuffer(), sourceRoot(config, project.id), config.maxUploadBytes);
|
|
233
234
|
project.main_file = extracted.mainFile;
|
|
234
|
-
db.prepare(`INSERT INTO projects (id, owner_id, last_modified_by, name, main_file, latexmkrc, engine, created_at, updated_at)
|
|
235
|
-
VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?)`)
|
|
236
|
-
.run(project.id, project.owner_id, project.last_modified_by, project.name, project.main_file, project.engine, project.created_at, project.updated_at);
|
|
235
|
+
db.prepare(`INSERT INTO projects (id, owner_id, last_modified_by, name, main_file, latexmkrc, engine, chktex_enabled, created_at, updated_at)
|
|
236
|
+
VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?, ?)`)
|
|
237
|
+
.run(project.id, project.owner_id, project.last_modified_by, project.name, project.main_file, project.engine, project.chktex_enabled, project.created_at, project.updated_at);
|
|
237
238
|
}
|
|
238
239
|
catch (error) {
|
|
239
240
|
removeProjectDirectory(config, project.id);
|
|
@@ -262,7 +263,7 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
262
263
|
const requestedName = typeof body?.name === "string" && body.name.trim() ? body.name : `${source.name.slice(0, 115)} (1)`;
|
|
263
264
|
const project = {
|
|
264
265
|
id: randomUUID(), owner_id: user.id, last_modified_by: user.id, name: text(requestedName, 120),
|
|
265
|
-
main_file: source.main_file, latexmkrc: source.latexmkrc, engine: source.engine, icon: source.icon, created_at: now(), updated_at: now()
|
|
266
|
+
main_file: source.main_file, latexmkrc: source.latexmkrc, engine: source.engine, icon: source.icon, chktex_enabled: source.chktex_enabled, created_at: now(), updated_at: now()
|
|
266
267
|
};
|
|
267
268
|
try {
|
|
268
269
|
// Duplicate the source tree only after flushing the live Yjs room and
|
|
@@ -276,9 +277,9 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
276
277
|
}
|
|
277
278
|
}
|
|
278
279
|
});
|
|
279
|
-
db.prepare(`INSERT INTO projects (id, owner_id, last_modified_by, name, main_file, latexmkrc, engine, icon, created_at, updated_at)
|
|
280
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
281
|
-
.run(project.id, project.owner_id, project.last_modified_by, project.name, project.main_file, project.latexmkrc, project.engine, project.icon, project.created_at, project.updated_at);
|
|
280
|
+
db.prepare(`INSERT INTO projects (id, owner_id, last_modified_by, name, main_file, latexmkrc, engine, icon, chktex_enabled, created_at, updated_at)
|
|
281
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
282
|
+
.run(project.id, project.owner_id, project.last_modified_by, project.name, project.main_file, project.latexmkrc, project.engine, project.icon, project.chktex_enabled, project.created_at, project.updated_at);
|
|
282
283
|
}
|
|
283
284
|
catch (error) {
|
|
284
285
|
removeProjectDirectory(config, project.id);
|
|
@@ -417,6 +418,58 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
417
418
|
return apiError(reply, 503, "HARPER_UNAVAILABLE");
|
|
418
419
|
}
|
|
419
420
|
});
|
|
421
|
+
app.post("/api/projects/:id/chktex", async (request, reply) => {
|
|
422
|
+
const user = requireUser(request, reply, db);
|
|
423
|
+
if (!user)
|
|
424
|
+
return;
|
|
425
|
+
const { id } = request.params;
|
|
426
|
+
const project = accessibleProject(db, id, user);
|
|
427
|
+
if (!project)
|
|
428
|
+
return apiError(reply, 404, "PROJECT_NOT_FOUND");
|
|
429
|
+
if (!project.chktex_enabled)
|
|
430
|
+
return { issues: [] };
|
|
431
|
+
const body = request.body;
|
|
432
|
+
const source = body?.source;
|
|
433
|
+
const sourcePath = body?.path;
|
|
434
|
+
const clientId = body?.clientId;
|
|
435
|
+
const sequence = body?.sequence;
|
|
436
|
+
if (typeof source !== "string" || typeof sourcePath !== "string")
|
|
437
|
+
return apiError(reply, 400, "CHK_TEX_SOURCE_INVALID");
|
|
438
|
+
if (clientId !== undefined && (typeof clientId !== "string" || !clientIdPattern.test(clientId))) {
|
|
439
|
+
return apiError(reply, 400, "CHK_TEX_SOURCE_INVALID");
|
|
440
|
+
}
|
|
441
|
+
if (sequence !== undefined && (typeof sequence !== "number" || !Number.isSafeInteger(sequence) || sequence <= 0 || clientId === undefined)) {
|
|
442
|
+
return apiError(reply, 400, "CHK_TEX_SOURCE_INVALID");
|
|
443
|
+
}
|
|
444
|
+
if (Buffer.byteLength(source, "utf8") > maxCollaborativeFileBytes(config)) {
|
|
445
|
+
return apiError(reply, 413, "CHK_TEX_SOURCE_TOO_LARGE");
|
|
446
|
+
}
|
|
447
|
+
let filePath;
|
|
448
|
+
try {
|
|
449
|
+
filePath = safeRelativePath(sourcePath);
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
return apiError(reply, 400, "CHK_TEX_SOURCE_INVALID");
|
|
453
|
+
}
|
|
454
|
+
if (!supportsChktexChecks(filePath))
|
|
455
|
+
return { issues: [] };
|
|
456
|
+
const laneClientId = clientId ?? `legacy:${digestToken(request.cookies.texlite_session ?? "")}`;
|
|
457
|
+
const lane = `${id}\0${user.id}\0${laneClientId}\0${filePath}`;
|
|
458
|
+
try {
|
|
459
|
+
return { issues: await chktex.lint(source, filePath, lane, typeof sequence === "number" ? sequence : undefined) };
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
if (error instanceof ChktexLintSupersededError)
|
|
463
|
+
return apiError(reply, 409, "CHK_TEX_SUPERSEDED");
|
|
464
|
+
if (error instanceof ChktexUnavailableError) {
|
|
465
|
+
request.log.debug({ projectId: id }, "Optional ChkTeX command unavailable");
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
request.log.error({ err: error, projectId: id }, "ChkTeX writing check failed");
|
|
469
|
+
}
|
|
470
|
+
return apiError(reply, 503, "CHK_TEX_UNAVAILABLE");
|
|
471
|
+
}
|
|
472
|
+
});
|
|
420
473
|
app.post("/api/projects/:id/dictionary", async (request, reply) => {
|
|
421
474
|
const user = requireUser(request, reply, db);
|
|
422
475
|
if (!user)
|
|
@@ -469,6 +522,21 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
469
522
|
if (!currentProject || currentProject.permission !== "owner") {
|
|
470
523
|
return apiError(reply, 403, "PROJECT_OWNER_ONLY");
|
|
471
524
|
}
|
|
525
|
+
if (body.expectedSettings !== undefined) {
|
|
526
|
+
if (!body.expectedSettings || typeof body.expectedSettings !== "object" || Array.isArray(body.expectedSettings)) {
|
|
527
|
+
return apiError(reply, 400, "REQUEST_INVALID");
|
|
528
|
+
}
|
|
529
|
+
const expected = body.expectedSettings;
|
|
530
|
+
const currentSettings = {
|
|
531
|
+
name: currentProject.name, mainFile: currentProject.main_file, engine: currentProject.engine,
|
|
532
|
+
latexmkrc: currentProject.latexmkrc, chktexEnabled: Boolean(currentProject.chktex_enabled)
|
|
533
|
+
};
|
|
534
|
+
for (const field of Object.keys(currentSettings)) {
|
|
535
|
+
if (Object.hasOwn(body, field) && (!Object.hasOwn(expected, field) || expected[field] !== currentSettings[field])) {
|
|
536
|
+
return apiError(reply, 409, "PROJECT_SETTINGS_CONFLICT");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
472
540
|
const name = typeof body.name === "string" ? text(body.name, 120) : currentProject.name;
|
|
473
541
|
let mainFile = currentProject.main_file;
|
|
474
542
|
if (body.mainFile !== undefined) {
|
|
@@ -479,6 +547,7 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
479
547
|
}
|
|
480
548
|
const engine = typeof body.engine === "string" && config.allowedEngines.includes(body.engine)
|
|
481
549
|
? body.engine : currentProject.engine;
|
|
550
|
+
const chktexEnabled = typeof body.chktexEnabled === "boolean" ? body.chktexEnabled : Boolean(currentProject.chktex_enabled);
|
|
482
551
|
const latexmkrc = body.latexmkrc === null || body.latexmkrc === ""
|
|
483
552
|
? null
|
|
484
553
|
: typeof body.latexmkrc === "string" ? safeRelativePath(body.latexmkrc) : currentProject.latexmkrc;
|
|
@@ -520,8 +589,11 @@ export function registerProjectCatalogRoutes(app, context) {
|
|
|
520
589
|
return apiError(reply, 400, "LATEXMKRC_INVALID", { path: latexmkrc });
|
|
521
590
|
}
|
|
522
591
|
}
|
|
523
|
-
|
|
524
|
-
|
|
592
|
+
const chktexSettingChanged = Boolean(currentProject.chktex_enabled) !== chktexEnabled;
|
|
593
|
+
db.prepare("UPDATE projects SET name = ?, main_file = ?, latexmkrc = ?, engine = ?, chktex_enabled = ?, updated_at = ?, last_modified_by = ? WHERE id = ?")
|
|
594
|
+
.run(name, mainFile, latexmkrc, engine, chktexEnabled ? 1 : 0, now(), user.id, id);
|
|
595
|
+
if (chktexSettingChanged)
|
|
596
|
+
collaboration.signalChktexSetting(id);
|
|
525
597
|
recordHistory(id, user.id, "settings", []);
|
|
526
598
|
return {
|
|
527
599
|
project: projectJson(accessibleProject(db, id, user), tagsForProject(db, id, user.id), commentsSummaryForProject(db, id))
|
|
@@ -2,3 +2,7 @@
|
|
|
2
2
|
export function supportsWritingChecks(path) {
|
|
3
3
|
return !/\.(?:bib|bst)$/i.test(path.trim());
|
|
4
4
|
}
|
|
5
|
+
/** ChkTeX checks LaTeX source files, not arbitrary project assets. */
|
|
6
|
+
export function supportsChktexChecks(path) {
|
|
7
|
+
return /\.(?:tex|sty|cls)$/i.test(path.trim());
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./SystemMetricsDialog-hJQATmd4.js","./react-BVUjbTTn.js","./index-p1NHkGGG.js","./index-BiwJj0Np.css","./refresh-cw-LtwnqlWh.js","./hard-drive-BWZnOgnm.js","./x-eisRQJv_.js","./sigma-BgvHGPto.js","./tags-m9kppdi0.js","./CitationLibraryDialog-BTmnCP4V.js","./citationLibrary-ZeQPCv5O.js","./plus-BVXOlQXP.js","./check-C1db8iNn.js","./search-DXo_aAp0.js","./AdminUsers-B12FJUMy.js","./user-plus-MRmKeDtX.js","./key-round-CoDwyVJc.js","./TagManagementDialog-BHC8qULq.js","./ProjectIconPickerDialog-GzRuM0oQ.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{c as r,a as Je,s as Fe,S as ha,L as ma,b as Tt,M as se,d as _e,e as Ze,C as ua,f as ya,g as C,h as D,_ as me,i as Ke}from"./index-p1NHkGGG.js";import{j as e,r as i,u as ka}from"./react-BVUjbTTn.js";import{H as ga}from"./hard-drive-BWZnOgnm.js";import{G as ja,F as xa,L as fa,N as va,a as ba,b as Ma,c as Ca,S as wa,B as Et,d as Na,e as za,P as Sa,D as Ta,T as Pa,M as Ft,A as It,f as Pt,U as Aa,g as At,h as La,C as Lt,i as Da,j as Ea,X as Fa,k as Ia}from"./x-eisRQJv_.js";import{S as qa}from"./sigma-BgvHGPto.js";import{T as We}from"./tags-m9kppdi0.js";const $a=r("Activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);const Ba=r("ArchiveRestore",[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h2",key:"tvwodi"}],["path",{d:"M20 8v11a2 2 0 0 1-2 2h-2",key:"1gkqxj"}],["path",{d:"m9 15 3-3 3 3",key:"1pd0qc"}],["path",{d:"M12 12v9",key:"192myk"}]]);const Qe=r("Archive",[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",key:"1s80jp"}],["path",{d:"M10 12h4",key:"a56b0p"}]]);const Va=r("ArrowDownUp",[["path",{d:"m3 16 4 4 4-4",key:"1co6wj"}],["path",{d:"M7 20V4",key:"1yoxec"}],["path",{d:"m21 8-4-4-4 4",key:"1c9v7m"}],["path",{d:"M17 4v16",key:"7dpous"}]]);const qt=r("ArrowRightLeft",[["path",{d:"m16 3 4 4-4 4",key:"1x1c3m"}],["path",{d:"M20 7H4",key:"zbl0bi"}],["path",{d:"m8 21-4-4 4-4",key:"h9nckh"}],["path",{d:"M4 17h16",key:"g4d7ey"}]]);const Oa=r("Atom",[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["path",{d:"M20.2 20.2c2.04-2.03.02-7.36-4.5-11.9-4.54-4.52-9.87-6.54-11.9-4.5-2.04 2.03-.02 7.36 4.5 11.9 4.54 4.52 9.87 6.54 11.9 4.5Z",key:"1l2ple"}],["path",{d:"M15.7 15.7c4.52-4.54 6.54-9.87 4.5-11.9-2.03-2.04-7.36-.02-11.9 4.5-4.52 4.54-6.54 9.87-4.5 11.9 2.03 2.04 7.36.02 11.9-4.5Z",key:"1wam0m"}]]);const Ua=r("Beaker",[["path",{d:"M4.5 3h15",key:"c7n0jr"}],["path",{d:"M6 3v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V3",key:"m1uhx7"}],["path",{d:"M6 14h12",key:"4cwo0f"}]]);const Ha=r("Bookmark",[["path",{d:"m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z",key:"1fy3hk"}]]);const Ra=r("Bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);const _a=r("Braces",[["path",{d:"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1",key:"ezmyqa"}],["path",{d:"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1",key:"e1hn23"}]]);const Za=r("BrainCircuit",[["path",{d:"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z",key:"l5xja"}],["path",{d:"M9 13a4.5 4.5 0 0 0 3-4",key:"10igwf"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5",key:"105sqy"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396",key:"ql3yin"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516",key:"2e4loj"}],["path",{d:"M12 13h4",key:"1ku699"}],["path",{d:"M12 18h6a2 2 0 0 1 2 2v1",key:"105ag5"}],["path",{d:"M12 8h8",key:"1lhi5i"}],["path",{d:"M16 8V5a2 2 0 0 1 2-2",key:"u6izg6"}],["circle",{cx:"16",cy:"13",r:".5",key:"ry7gng"}],["circle",{cx:"18",cy:"3",r:".5",key:"1aiba7"}],["circle",{cx:"20",cy:"21",r:".5",key:"yhc1fs"}],["circle",{cx:"20",cy:"8",r:".5",key:"1e43v0"}]]);const Ka=r("Brain",[["path",{d:"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z",key:"l5xja"}],["path",{d:"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z",key:"ep3f8r"}],["path",{d:"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4",key:"1p4c4q"}],["path",{d:"M17.599 6.5a3 3 0 0 0 .399-1.375",key:"tmeiqw"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5",key:"105sqy"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396",key:"ql3yin"}],["path",{d:"M19.938 10.5a4 4 0 0 1 .585.396",key:"1qfode"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516",key:"2e4loj"}],["path",{d:"M19.967 17.484A4 4 0 0 1 18 18",key:"159ez6"}]]);const Wa=r("Calculator",[["rect",{width:"16",height:"20",x:"4",y:"2",rx:"2",key:"1nb95v"}],["line",{x1:"8",x2:"16",y1:"6",y2:"6",key:"x4nwl0"}],["line",{x1:"16",x2:"16",y1:"14",y2:"18",key:"wjye3r"}],["path",{d:"M16 10h.01",key:"1m94wz"}],["path",{d:"M12 10h.01",key:"1nrarc"}],["path",{d:"M8 10h.01",key:"19clt8"}],["path",{d:"M12 14h.01",key:"1etili"}],["path",{d:"M8 14h.01",key:"6423bh"}],["path",{d:"M12 18h.01",key:"mhygvu"}],["path",{d:"M8 18h.01",key:"lrp35t"}]]);const $t=r("CalendarDays",[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}],["path",{d:"M8 14h.01",key:"6423bh"}],["path",{d:"M12 14h.01",key:"1etili"}],["path",{d:"M16 14h.01",key:"1gbofw"}],["path",{d:"M8 18h.01",key:"lrp35t"}],["path",{d:"M12 18h.01",key:"mhygvu"}],["path",{d:"M16 18h.01",key:"kzsmim"}]]);const Ga=r("ChartColumn",[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}],["path",{d:"M18 17V9",key:"2bz60n"}],["path",{d:"M13 17V5",key:"1frdt8"}],["path",{d:"M8 17v-3",key:"17ska0"}]]);const Ja=r("ChartLine",[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}],["path",{d:"m19 9-5 5-4-4-3 3",key:"2osh9i"}]]);const Qa=r("ChartNoAxesCombined",[["path",{d:"M12 16v5",key:"zza2cw"}],["path",{d:"M16 14v7",key:"1g90b9"}],["path",{d:"M20 10v11",key:"1iqoj0"}],["path",{d:"m22 3-8.646 8.646a.5.5 0 0 1-.708 0L9.354 8.354a.5.5 0 0 0-.707 0L2 15",key:"1fw8x9"}],["path",{d:"M4 18v3",key:"1yp0dc"}],["path",{d:"M8 14v7",key:"n3cwzv"}]]);const Xa=r("ChartPie",[["path",{d:"M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951c-.55-.055-.998.398-.998.95v8a1 1 0 0 0 1 1z",key:"pzmjnu"}],["path",{d:"M21.21 15.89A10 10 0 1 1 8 2.83",key:"k2fpak"}]]);const Ya=r("ChartScatter",[["circle",{cx:"7.5",cy:"7.5",r:".5",fill:"currentColor",key:"kqv944"}],["circle",{cx:"18.5",cy:"5.5",r:".5",fill:"currentColor",key:"lysivs"}],["circle",{cx:"11.5",cy:"11.5",r:".5",fill:"currentColor",key:"byv1b8"}],["circle",{cx:"7.5",cy:"16.5",r:".5",fill:"currentColor",key:"nkw3mc"}],["circle",{cx:"17.5",cy:"14.5",r:".5",fill:"currentColor",key:"1gjh6j"}],["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}]]);const es=r("CircuitBoard",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M11 9h4a2 2 0 0 0 2-2V3",key:"1ve2rv"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}],["path",{d:"M7 21v-4a2 2 0 0 1 2-2h4",key:"1fwkro"}],["circle",{cx:"15",cy:"15",r:"2",key:"3i40o0"}]]);const ts=r("Cloud",[["path",{d:"M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z",key:"p7xjir"}]]);const as=r("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);const ss=r("Copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);const ns=r("Cpu",[["rect",{width:"16",height:"16",x:"4",y:"4",rx:"2",key:"14l7u7"}],["rect",{width:"6",height:"6",x:"9",y:"9",rx:"1",key:"5aljv4"}],["path",{d:"M15 2v2",key:"13l42r"}],["path",{d:"M15 20v2",key:"15mkzm"}],["path",{d:"M2 15h2",key:"1gxd5l"}],["path",{d:"M2 9h2",key:"1bbxkp"}],["path",{d:"M20 15h2",key:"19e6y8"}],["path",{d:"M20 9h2",key:"19tzq7"}],["path",{d:"M9 2v2",key:"165o2o"}],["path",{d:"M9 20v2",key:"i2bqo8"}]]);const rs=r("Database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]]);const os=r("Dna",[["path",{d:"m10 16 1.5 1.5",key:"11lckj"}],["path",{d:"m14 8-1.5-1.5",key:"1ohn8i"}],["path",{d:"M15 2c-1.798 1.998-2.518 3.995-2.807 5.993",key:"80uv8i"}],["path",{d:"m16.5 10.5 1 1",key:"696xn5"}],["path",{d:"m17 6-2.891-2.891",key:"xu6p2f"}],["path",{d:"M2 15c6.667-6 13.333 0 20-6",key:"1pyr53"}],["path",{d:"m20 9 .891.891",key:"3xwk7g"}],["path",{d:"M3.109 14.109 4 15",key:"q76aoh"}],["path",{d:"m6.5 12.5 1 1",key:"cs35ky"}],["path",{d:"m7 18 2.891 2.891",key:"1sisit"}],["path",{d:"M9 22c1.798-1.998 2.518-3.995 2.807-5.993",key:"q3hbxp"}]]);const is=r("Earth",[["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54",key:"1djwo0"}],["path",{d:"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",key:"1tzkfa"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05",key:"14pb5j"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);const cs=r("Ellipsis",[["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}],["circle",{cx:"19",cy:"12",r:"1",key:"1wjl8i"}],["circle",{cx:"5",cy:"12",r:"1",key:"1pcz8c"}]]);const ls=r("FileChartColumnIncreasing",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M8 18v-2",key:"qcmpov"}],["path",{d:"M12 18v-4",key:"q1q25u"}],["path",{d:"M16 18v-6",key:"15y0np"}]]);const ds=r("FilePenLine",[["path",{d:"m18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2",key:"142zxg"}],["path",{d:"M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z",key:"2t3380"}],["path",{d:"M8 18h1",key:"13wk12"}]]);const ps=r("FileStack",[["path",{d:"M21 7h-3a2 2 0 0 1-2-2V2",key:"9rb54x"}],["path",{d:"M21 6v6.5c0 .8-.7 1.5-1.5 1.5h-7c-.8 0-1.5-.7-1.5-1.5v-9c0-.8.7-1.5 1.5-1.5H17Z",key:"1059l0"}],["path",{d:"M7 8v8.8c0 .3.2.6.4.8.2.2.5.4.8.4H15",key:"16874u"}],["path",{d:"M3 12v8.8c0 .3.2.6.4.8.2.2.5.4.8.4H11",key:"k2ox98"}]]);const hs=r("FlaskConical",[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2",key:"18mbvz"}],["path",{d:"M6.453 15h11.094",key:"3shlmq"}],["path",{d:"M8.5 2h7",key:"csnxdl"}]]);const ms=r("FolderArchive",[["circle",{cx:"15",cy:"19",r:"2",key:"u2pros"}],["path",{d:"M20.9 19.8A2 2 0 0 0 22 18V8a2 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 2h5.1",key:"1jj40k"}],["path",{d:"M15 11v-1",key:"cntcp"}],["path",{d:"M15 17v-2",key:"1279jj"}]]);const us=r("FolderGit2",[["path",{d:"M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5",key:"1w6njk"}],["circle",{cx:"13",cy:"12",r:"2",key:"1j92g6"}],["path",{d:"M18 19c-2.8 0-5-2.2-5-5v8",key:"pkpw2h"}],["circle",{cx:"20",cy:"19",r:"2",key:"1obnsp"}]]);const ys=r("FolderKanban",[["path",{d:"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z",key:"1fr9dc"}],["path",{d:"M8 10v4",key:"tgpxqk"}],["path",{d:"M12 10v2",key:"hh53o1"}],["path",{d:"M16 10v6",key:"1d6xys"}]]);const Ge=r("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);const ks=r("GraduationCap",[["path",{d:"M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z",key:"j76jl0"}],["path",{d:"M22 10v6",key:"1lu8f3"}],["path",{d:"M6 12.5V16a6 3 0 0 0 12 0v-3.5",key:"1r8lef"}]]);const gs=r("History",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]]);const js=r("Image",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",key:"1xmnt7"}]]);const xs=r("Languages",[["path",{d:"m5 8 6 6",key:"1wu5hv"}],["path",{d:"m4 14 6-6 2-3",key:"1k1g8d"}],["path",{d:"M2 5h12",key:"or177f"}],["path",{d:"M7 2h1",key:"1t2jsx"}],["path",{d:"m22 22-5-10-5 10",key:"don7ne"}],["path",{d:"M14 18h6",key:"1m8k6r"}]]);const fs=r("Library",[["path",{d:"m16 6 4 14",key:"ji33uf"}],["path",{d:"M12 6v14",key:"1n7gus"}],["path",{d:"M8 8v12",key:"1gg7y9"}],["path",{d:"M4 4v16",key:"6qkkli"}]]);const vs=r("Lightbulb",[["path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5",key:"1gvzjb"}],["path",{d:"M9 18h6",key:"x1upvd"}],["path",{d:"M10 22h4",key:"ceow96"}]]);const bs=r("Map",[["path",{d:"M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z",key:"169xi5"}],["path",{d:"M15 5.764v15",key:"1pn4in"}],["path",{d:"M9 3.236v15",key:"1uimfh"}]]);const Ms=r("Microscope",[["path",{d:"M6 18h8",key:"1borvv"}],["path",{d:"M3 22h18",key:"8prr45"}],["path",{d:"M14 22a7 7 0 1 0 0-14h-1",key:"1jwaiy"}],["path",{d:"M9 14h2",key:"197e7h"}],["path",{d:"M9 12a2 2 0 0 1-2-2V6h6v4a2 2 0 0 1-2 2Z",key:"1bmzmy"}],["path",{d:"M12 6V3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3",key:"1drr47"}]]);const Cs=r("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);const ws=r("Newspaper",[["path",{d:"M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2",key:"7pis2x"}],["path",{d:"M18 14h-8",key:"sponae"}],["path",{d:"M15 18h-5",key:"95g1m2"}],["path",{d:"M10 6h8v4h-8V6Z",key:"smlsk5"}]]);const Ns=r("NotebookPen",[["path",{d:"M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4",key:"re6nr2"}],["path",{d:"M2 6h4",key:"aawbzj"}],["path",{d:"M2 10h4",key:"l0bgd4"}],["path",{d:"M2 14h4",key:"1gsvsf"}],["path",{d:"M2 18h4",key:"1bu2t1"}],["path",{d:"M21.378 5.626a1 1 0 1 0-3.004-3.004l-5.01 5.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z",key:"pqwjuv"}]]);const zs=r("PenTool",[["path",{d:"M15.707 21.293a1 1 0 0 1-1.414 0l-1.586-1.586a1 1 0 0 1 0-1.414l5.586-5.586a1 1 0 0 1 1.414 0l1.586 1.586a1 1 0 0 1 0 1.414z",key:"nt11vn"}],["path",{d:"m18 13-1.375-6.874a1 1 0 0 0-.746-.776L3.235 2.028a1 1 0 0 0-1.207 1.207L5.35 15.879a1 1 0 0 0 .776.746L13 18",key:"15qc1e"}],["path",{d:"m2.3 2.3 7.286 7.286",key:"1wuzzi"}],["circle",{cx:"11",cy:"11",r:"2",key:"xmgehs"}]]);const Ss=r("Presentation",[["path",{d:"M2 3h20",key:"91anmk"}],["path",{d:"M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3",key:"2k9sn8"}],["path",{d:"m7 21 5-5 5 5",key:"bip4we"}]]);const Ts=r("Quote",[["path",{d:"M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z",key:"rib7q0"}],["path",{d:"M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z",key:"1ymkrd"}]]);const Ps=r("Regex",[["path",{d:"M17 3v10",key:"15fgeh"}],["path",{d:"m12.67 5.5 8.66 5",key:"1gpheq"}],["path",{d:"m12.67 10.5 8.66-5",key:"1dkfa6"}],["path",{d:"M9 17a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2z",key:"swwfx4"}]]);const As=r("Rocket",[["path",{d:"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z",key:"m3kijz"}],["path",{d:"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z",key:"1fmvmk"}],["path",{d:"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0",key:"1f8sc4"}],["path",{d:"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5",key:"qeys4"}]]);const Ls=r("Server",[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]]);const Ds=r("ShieldCheck",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);const Bt=r("Sparkles",[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]]);const Es=r("StickyNote",[["path",{d:"M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z",key:"qazsjp"}],["path",{d:"M15 3v4a2 2 0 0 0 2 2h4",key:"40519r"}]]);const Fs=r("Target",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"6",key:"1vlfrh"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}]]);const Is=r("Telescope",[["path",{d:"m10.065 12.493-6.18 1.318a.934.934 0 0 1-1.108-.702l-.537-2.15a1.07 1.07 0 0 1 .691-1.265l13.504-4.44",key:"k4qptu"}],["path",{d:"m13.56 11.747 4.332-.924",key:"19l80z"}],["path",{d:"m16 21-3.105-6.21",key:"7oh9d"}],["path",{d:"M16.485 5.94a2 2 0 0 1 1.455-2.425l1.09-.272a1 1 0 0 1 1.212.727l1.515 6.06a1 1 0 0 1-.727 1.213l-1.09.272a2 2 0 0 1-2.425-1.455z",key:"m7xp4m"}],["path",{d:"m6.158 8.633 1.114 4.456",key:"74o979"}],["path",{d:"m8 21 3.105-6.21",key:"1fvxut"}],["circle",{cx:"12",cy:"13",r:"2",key:"1c1ljs"}]]);const qs=r("Terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]]);const $s=r("TestTubeDiagonal",[["path",{d:"M21 7 6.82 21.18a2.83 2.83 0 0 1-3.99-.01a2.83 2.83 0 0 1 0-4L17 3",key:"1ub6xw"}],["path",{d:"m16 2 6 6",key:"1gw87d"}],["path",{d:"M12 16H4",key:"1cjfip"}]]);const Bs=r("TestTubes",[["path",{d:"M9 2v17.5A2.5 2.5 0 0 1 6.5 22A2.5 2.5 0 0 1 4 19.5V2",key:"1hjrqt"}],["path",{d:"M20 2v17.5a2.5 2.5 0 0 1-2.5 2.5a2.5 2.5 0 0 1-2.5-2.5V2",key:"16lc8n"}],["path",{d:"M3 2h7",key:"7s29d5"}],["path",{d:"M14 2h7",key:"7sicin"}],["path",{d:"M9 16H4",key:"1bfye3"}],["path",{d:"M20 16h-5",key:"ddnjpe"}]]);const Vs=r("University",[["circle",{cx:"12",cy:"10",r:"1",key:"1gnqs8"}],["path",{d:"M22 20V8h-4l-6-4-6 4H2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2",key:"1qj5sn"}],["path",{d:"M6 17v.01",key:"roodi6"}],["path",{d:"M6 13v.01",key:"67c122"}],["path",{d:"M18 17v.01",key:"12ktxm"}],["path",{d:"M18 13v.01",key:"tn1rt1"}],["path",{d:"M14 22v-5a2 2 0 0 0-2-2a2 2 0 0 0-2 2v5",key:"11g7fi"}]]);const Os=r("UsersRound",[["path",{d:"M18 21a8 8 0 0 0-16 0",key:"3ypg7q"}],["circle",{cx:"10",cy:"8",r:"5",key:"o932ke"}],["path",{d:"M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3",key:"10s06x"}]]);const Us=r("Workflow",[["rect",{width:"8",height:"8",x:"3",y:"3",rx:"2",key:"by2w9f"}],["path",{d:"M7 11v4a2 2 0 0 0 2 2h4",key:"xkn7yn"}],["rect",{width:"8",height:"8",x:"13",y:"13",rx:"2",key:"1cgmvn"}]]),Vt=["file-text","book-open","notebook-pen","book-marked","scroll-text","library","graduation-cap","presentation","image","folder-kanban","microscope","flask-conical","test-tube-diagonal","atom","dna","telescope","earth","map","languages","university","code-2","file-code-2","database","server","network","circuit-board","brain","brain-circuit","bot","shield-check","workflow","sigma","calculator","chart-no-axes-combined","line-chart","bar-chart-3","pie-chart","chart-scatter","file-spreadsheet","file-chart-column-increasing","lightbulb","rocket","target","sparkles","file-pen-line","file-stack","folder","folder-open","folder-archive","archive","bookmark","newspaper","notepad-text","sticky-note","pen-tool","quote","list-tree","file-search","beaker","test-tubes","folder-git-2","git-branch","terminal","braces","regex","cpu","hard-drive","cloud","users-round","calendar-days"],Hs=new Set(Vt);function Rs(s){return typeof s=="string"&&Hs.has(s)}function _s(s){if(typeof s!="string")return null;const p=s.trim().replace(/^lucide:/i,"");return!p||p.length>96?null:/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(p)?p:null}const Zs={"file-text":za,"book-open":Na,"notebook-pen":Ns,"book-marked":Et,"scroll-text":wa,library:fs,"graduation-cap":ks,presentation:Ss,image:js,"folder-kanban":ys,microscope:Ms,"flask-conical":hs,"test-tube-diagonal":$s,atom:Oa,dna:os,telescope:Is,earth:is,map:bs,languages:xs,university:Vs,"code-2":as,"file-code-2":Ca,database:rs,server:Ls,network:Cs,"circuit-board":es,brain:Ka,"brain-circuit":Za,bot:Ra,"shield-check":Ds,workflow:Us,sigma:qa,calculator:Wa,"chart-no-axes-combined":Qa,"line-chart":Ja,"bar-chart-3":Ga,"pie-chart":Xa,"chart-scatter":Ya,"file-spreadsheet":Ma,"file-chart-column-increasing":ls,lightbulb:vs,rocket:As,target:Fs,sparkles:Bt,"file-pen-line":ds,"file-stack":ps,folder:ba,"folder-open":Ge,"folder-archive":ms,archive:Qe,bookmark:Ha,newspaper:ws,"notepad-text":va,"sticky-note":Es,"pen-tool":zs,quote:Ts,"list-tree":fa,"file-search":xa,beaker:Ua,"test-tubes":Bs,"folder-git-2":us,"git-branch":ja,terminal:qs,braces:_a,regex:Ps,cpu:ns,"hard-drive":ga,cloud:ts,"users-round":Os,"calendar-days":$t},hn=Vt.map(s=>({name:s}));function Ks(s){const p=Array.from(s.trim())[0];return p?p.toLocaleUpperCase():"?"}function Ws({icon:s,fallback:p,size:x=18}){const f=Rs(s)?Zs[s]:null;if(f)return e.jsx(f,{"aria-hidden":"true",size:x,strokeWidth:1.9});const v=_s(s);if(v){const a=`url(${Je(`/api/project-icons/${encodeURIComponent(v)}`)})`,k={maskImage:a,WebkitMaskImage:a};return e.jsx("span",{"aria-hidden":"true",className:"project-icon-external-glyph",style:k})}return e.jsx("span",{"aria-hidden":"true",children:p})}function Ot({icon:s,projectName:p,editable:x=!1,onEdit:f,editLabel:v,title:c,className:a=""}){const k=e.jsx(Ws,{icon:s,fallback:Ks(p)}),m=`project-icon-avatar${x?" editable":""}${a?` ${a}`:""}`;return x&&f?e.jsx("button",{type:"button",className:m,title:v,"aria-label":v,onClick:f,children:k}):e.jsx("span",{className:m,title:c,"aria-hidden":"true",children:k})}function Ut({project:s,currentUser:p,showArchived:x,archiveBusy:f,menuOpen:v,t:c,variant:a,onToggleMenu:k,onCloseMenu:m,onAssignTags:y,onRename:E,onDuplicate:d,onArchive:z,onTransfer:B,onDelete:S}){const b=i.useRef(null),T=s.permission==="owner",F=p.role==="admin"||p.canCreateProjects,R=s.ownerId===p.id,V=s.permission==="owner";i.useEffect(()=>{if(!v)return;const l=L=>{L.target instanceof Node&&!b.current?.contains(L.target)&&m()},A=L=>{L.key==="Escape"&&m()};return document.addEventListener("pointerdown",l),document.addEventListener("keydown",A),()=>{document.removeEventListener("pointerdown",l),document.removeEventListener("keydown",A)}},[v,m]);const P=[{id:"tags",label:c("tags.assign"),icon:e.jsx(We,{"aria-hidden":!0,size:15}),section:"main",onSelect:y},...T?[{id:"rename",label:c("projects.rename"),icon:e.jsx(Sa,{"aria-hidden":!0,size:15}),section:"main",onSelect:E}]:[],...F?[{id:"duplicate",label:c("projects.duplicate"),icon:e.jsx(ss,{"aria-hidden":!0,size:15}),section:"main",onSelect:d}]:[],{id:"download",label:c("projects.download"),icon:e.jsx(Ta,{"aria-hidden":!0,size:15}),section:"main",href:Je(`/api/projects/${s.id}/download`)},{id:"archive",label:c(x?"projects.unarchive":"projects.archive"),icon:x?e.jsx(Ba,{"aria-hidden":!0,size:15}):e.jsx(Qe,{"aria-hidden":!0,size:15}),section:"main",disabled:f,onSelect:z},...R?[{id:"transfer",label:c("projects.transfer"),icon:e.jsx(qt,{"aria-hidden":!0,size:15}),section:"main",onSelect:B}]:[],...V?[{id:"delete",label:c("common.delete"),icon:e.jsx(Pa,{"aria-hidden":!0,size:15}),section:"danger",onSelect:S}]:[]],_=P.filter(l=>l.section==="main"),M=P.filter(l=>l.section==="danger"),ne=l=>{l.disabled||(m(),l.onSelect?.())};return e.jsxs("div",{ref:b,className:`project-action-menu-root project-action-menu-${a}`,onClick:l=>l.stopPropagation(),children:[e.jsx("button",{type:"button",className:"project-action-menu-trigger","aria-label":c("projects.projectActions",{project:s.name}),"aria-expanded":v,"aria-controls":`project-actions-${s.id}`,onClick:l=>{l.stopPropagation(),k()},children:e.jsx(cs,{"aria-hidden":!0,size:19})}),v&&e.jsxs("div",{id:`project-actions-${s.id}`,className:"project-action-menu",role:"group","aria-label":c("projects.projectActions",{project:s.name}),children:[_.map(l=>l.href?e.jsxs("a",{href:l.href,download:!0,onClick:A=>{A.stopPropagation(),m()},children:[l.icon,l.label]},l.id):e.jsxs("button",{type:"button",disabled:l.disabled,onClick:A=>{A.stopPropagation(),ne(l)},children:[l.icon,l.label]},l.id)),M.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"project-action-menu-separator","aria-hidden":"true"}),M.map(l=>e.jsxs("button",{type:"button",className:"danger",onClick:A=>{A.stopPropagation(),ne(l)},children:[l.icon,l.label]},l.id))]})]})]})}function Gs({project:s,currentUser:p,showArchived:x,archiveBusy:f,menuOpen:v,t:c,formatCreatedDate:a,formatUpdatedTime:k,formatExactTime:m,onOpenProject:y,onOpenMention:E,onToggleMenu:d,onCloseMenu:z,onAssignTags:B,onRename:S,onDuplicate:b,onArchive:T,onTransfer:F,onDelete:R,onChooseIcon:V}){const P=s.ownerDisplayName??s.ownerUsername??c("projects.deletedUser"),_=s.lastModifiedDisplayName??s.lastModifiedUsername??c("projects.deletedUser");return e.jsxs("article",{className:`project-card project-list-row${s.ownerId===p.id?" owned-project":""}${v?" project-list-menu-open":""}`,onClick:M=>{M.target===M.currentTarget&&y()},children:[e.jsx(Ot,{icon:s.icon,projectName:s.name,title:s.name,editable:s.permission==="owner",editLabel:c("projectIcons.change"),onEdit:V,className:"project-list-avatar"}),e.jsx("span",{className:"project-list-summary",children:e.jsx("button",{type:"button",className:"project-list-cell-open project-list-summary-open",onClick:y,"aria-label":c("projects.openProject",{project:s.name}),children:e.jsxs("span",{className:"project-list-title-line",children:[e.jsx("strong",{className:"project-list-title",title:s.name,children:s.name}),!!(s.unresolvedCommentCount&&s.unresolvedCommentCount>0)&&e.jsxs("span",{className:"project-comments-badge unresolved",title:c("projects.unresolvedCommentsTooltip",{unresolved:s.unresolvedCommentCount,total:s.commentCount??s.unresolvedCommentCount}),children:[e.jsx(Ft,{"aria-hidden":!0,size:10}),e.jsx("span",{children:c("projects.unresolvedCount",{count:s.unresolvedCommentCount})})]}),s.tags?.length>0&&e.jsx("span",{className:"project-list-tags",children:s.tags.map(M=>e.jsx("span",{className:`tag tag-${M.color} project-list-tag`,title:M.name,children:M.name},M.id))})]})})}),e.jsxs("button",{type:"button",className:"project-list-cell-open project-list-owner",onClick:y,title:P,"aria-label":c("projects.openProject",{project:s.name}),children:[e.jsxs("span",{className:"sr-only",children:[c("projects.owner"),": "]}),P]}),e.jsxs("button",{type:"button",className:"project-list-cell-open project-list-created",onClick:y,title:m(s.createdAt),"aria-label":c("projects.openProject",{project:s.name}),children:[e.jsxs("span",{className:"sr-only",children:[c("projects.created"),": "]}),e.jsx("time",{dateTime:s.createdAt,children:a(s.createdAt)})]}),e.jsxs("button",{type:"button",className:"project-list-cell-open project-list-modified",onClick:y,title:c("projects.modifiedByUser",{time:m(s.updatedAt),user:_}),"aria-label":c("projects.openProject",{project:s.name}),children:[e.jsxs("span",{className:"sr-only",children:[c("projects.modified"),": "]}),e.jsx("time",{dateTime:s.updatedAt,children:k(s.updatedAt)}),e.jsx("small",{children:c("projects.byUser",{user:_})})]}),s.unreadMentionCount&&s.unreadMentionCount>0?e.jsxs("button",{className:"project-mentions-badge project-list-mentions",type:"button",title:c("projects.unreadMentionsTooltip",{count:s.unreadMentionCount}),onClick:E,children:[e.jsx(It,{"aria-hidden":!0,size:10}),e.jsx("span",{children:s.unreadMentionCount})]}):e.jsx("span",{className:"project-list-mentions"}),e.jsx(Ut,{variant:"list",project:s,currentUser:p,showArchived:x,archiveBusy:f,menuOpen:v,t:c,onToggleMenu:d,onCloseMenu:z,onAssignTags:B,onRename:S,onDuplicate:b,onArchive:T,onTransfer:F,onDelete:R})]})}const Js=i.lazy(()=>me(()=>import("./SystemMetricsDialog-hJQATmd4.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8]),import.meta.url).then(s=>({default:s.SystemMetricsDialog}))),Qs=i.lazy(()=>me(()=>import("./CitationLibraryDialog-BTmnCP4V.js"),__vite__mapDeps([9,1,2,3,10,6,11,12,13,8]),import.meta.url).then(s=>({default:s.CitationLibraryDialog}))),Xs=i.lazy(()=>me(()=>import("./AdminUsers-B12FJUMy.js"),__vite__mapDeps([14,1,2,3,6,15,16,5,7,8]),import.meta.url).then(s=>({default:s.AdminUsers}))),Ys=i.lazy(()=>me(()=>import("./TagManagementDialog-BHC8qULq.js"),__vite__mapDeps([17,1,2,3,11,6,12]),import.meta.url).then(s=>({default:s.TagManagementDialog}))),en=i.lazy(()=>me(()=>import("./ProjectIconPickerDialog-GzRuM0oQ.js"),__vite__mapDeps([18,1,2,3,13,5,6,7,8]),import.meta.url).then(s=>({default:s.ProjectIconPickerDialog})));function tn(s){return s instanceof Error&&s.name==="AbortError"}function an(s,p,x,f){const v=new Date(s).getTime();if(!Number.isFinite(v))return s;const c=v-x,a=Math.abs(c),k=60*1e3,m=60*k,y=24*m,E=3*y;if(a>E){const S=Math.floor(a/y),b=c<0?"Past":"Future";return f(`projects.relativeDays${b}`,{days:S,hours:0,minutes:0})}if(a>=y){const S=Math.floor(a/y),b=Math.floor(a%y/m),T=c<0?"Past":"Future",F=b>0?"DaysHours":"Days";return f(`projects.relative${F}${T}`,{days:S,hours:b,minutes:0})}if(a>=m){const S=Math.floor(a/m),b=Math.floor(a%m/k),T=c<0?"Past":"Future",F=b>0?"HoursMinutes":"Hours";return f(`projects.relative${F}${T}`,{days:0,hours:S,minutes:b})}const d="minute",z=k,B=Math.sign(c)*Math.max(1,Math.floor(a/z));return new Intl.RelativeTimeFormat(p,{numeric:"always"}).format(B,d)}function sn(s){return s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/1024/1024).toFixed(1)} MB`}function nn({site:s,user:p,initialData:x,onDataChange:f,onUser:v,onOpenProject:c}){const{t:a}=ka(),[k,m]=i.useState(()=>x?.projects??[]),[y,E]=i.useState(()=>x?.tags??[]),[d,z]=i.useState(()=>x?.pagination??{page:1,pageSize:20,total:x?.projects.length??0,totalPages:x?.projects.length?1:0}),[B,S]=i.useState(!!x),[b,T]=i.useState(!1),[F,R]=i.useState(!1),[V,P]=i.useState(!1),[_,M]=i.useState(""),[ne,l]=i.useState(""),[A,L]=i.useState(""),[Xe,Z]=i.useState(""),[Ye,K]=i.useState(""),[Ht,re]=i.useState(""),[Rt,ue]=i.useState(!1),[ye,et]=i.useState(""),[W,tt]=i.useState(!1),[_t,ke]=i.useState(!1),[Ie,qe]=i.useState(""),[I,at]=i.useState(null),[st,O]=i.useState(""),ge=i.useRef(null),nt=i.useRef(null),[rt,ot]=i.useState(!1),[U,Zt]=i.useState(""),[w,it]=i.useState(""),[je,$e]=i.useState(!1),[Kt,Be]=i.useState(!1),[G,J]=i.useState(null),[ct,xe]=i.useState(null),[Ve,oe]=i.useState(null),[fe,ve]=i.useState(""),[Q,lt]=i.useState(!1),[be,ie]=i.useState(null),[Me,Y]=i.useState(""),[Ce,dt]=i.useState(!1),[ce,we]=i.useState(null),[Ne,pt]=i.useState(!1),[ze,Se]=i.useState(null),[Oe,ht]=i.useState([]),[Te,le]=i.useState(""),[ee,Pe]=i.useState(!1),[mt,te]=i.useState(""),[de,Wt]=i.useState(()=>localStorage.getItem(Fe("texlite-project-view"))==="list"?"list":"grid"),[q,Gt]=i.useState(()=>localStorage.getItem(Fe("texlite-project-sort"))==="created"?"created":"updated"),[u,Jt]=i.useState(!1),[ut,yt]=i.useState(""),[Ae,H]=i.useState(null),[N,$]=i.useState(1),[Qt,Xt]=i.useState(()=>Date.now()),[kt,Yt]=i.useState(""),Ue=i.useRef(0),pe=i.useRef(null),gt=(t,n,o,h,g)=>`${t?"archived":"active"}|${n}|${o}|${h}|${g}`,jt=gt(u,N,U,w,q),he=(t,n,o,h,g)=>{const j=new URLSearchParams({page:String(n),pageSize:"20",sort:g});t&&j.set("archived","1"),o.trim()&&j.set("search",o.trim()),h&&j.set("tag",h);const X=gt(t,n,o,h,g),De=++Ue.current;pe.current?.abort();const Ee=new AbortController;return pe.current=Ee,Promise.all([C(`/api/projects?${j.toString()}`,{signal:Ee.signal}),C("/api/tags",{signal:Ee.signal})]).then(([ae,pa])=>{De===Ue.current&&(m(ae.projects),E(pa.tags),z(ae.pagination),$(ae.pagination.page),S(!0),Yt(X))}).catch(ae=>{!tn(ae)&&De===Ue.current&&M(D(ae))}).finally(()=>{pe.current===Ee&&(pe.current=null)})};i.useEffect(()=>(he(u,N,U,w,q),()=>pe.current?.abort()),[u,N,U,w,q]),i.useEffect(()=>{B&&!u&&kt===jt&&f(k,y,d)},[k,y,d,B,kt,jt,u]),i.useEffect(()=>{const t=window.setInterval(()=>Xt(Date.now()),6e4);return()=>window.clearInterval(t)},[]),i.useEffect(()=>{if(!je)return;const t=o=>{o.target instanceof Node&&!nt.current?.contains(o.target)&&$e(!1)},n=o=>{o.key==="Escape"&&$e(!1)};return document.addEventListener("pointerdown",t),document.addEventListener("keydown",n),()=>{document.removeEventListener("pointerdown",t),document.removeEventListener("keydown",n)}},[je]);const xt=t=>{H(null),Wt(t),localStorage.setItem(Fe("texlite-project-view"),t)},ea=t=>{Gt(t),$(1),localStorage.setItem(Fe("texlite-project-sort"),t)},ft=t=>{Jt(t),$(1)},vt=t=>{it(t),$(1)},bt=async()=>{if(!(!ye.trim()||W)){tt(!0),l("");try{const{project:t}=await C("/api/projects",{method:"POST",body:JSON.stringify({name:ye})});ue(!1),et("");const n=u||N!==1?k:[t,...k].slice(0,d.pageSize),o=u?d:{...d,total:d.total+1,totalPages:Math.ceil((d.total+1)/d.pageSize)};m(n),z(o),u||f(n,y,o),c(t.id)}catch(t){l(D(t))}finally{tt(!1)}}},ta=async()=>{if(!I)return;const t=s.maxUploadSizeMB;if(I.size>t*1024*1024)return O(a("errors.fileTooLarge",{size:t}));ot(!0),O("");const n=new FormData;n.append("file",I);try{const{project:o}=await C(`/api/projects/import?name=${encodeURIComponent(Ie.trim())}`,{method:"POST",body:n}),h=u||N!==1?k:[o,...k].slice(0,d.pageSize),g=u?d:{...d,total:d.total+1,totalPages:Math.ceil((d.total+1)/d.pageSize)};m(h),z(g),u||f(h,y,g),ke(!1),at(null),qe(""),c(o.id)}catch(o){O(D(o))}finally{ot(!1)}},He=t=>{if(t&&!t.name.toLocaleLowerCase().endsWith(".zip")){O(a("errors.zipOnly"));return}O(""),at(t),t&&!Ie&&qe(t.name.replace(/\.zip$/i,""))},aa=async()=>{await C("/api/auth/logout",{method:"POST"}),v(null)},sa=async t=>{if(!G)return;const n=G.tags.some(o=>o.id===t.id);L("");try{const h=(await C(n?`/api/projects/${G.id}/tags/${t.id}`:`/api/projects/${G.id}/tags`,n?{method:"DELETE"}:{method:"POST",body:JSON.stringify({tagId:t.id})})).project;J(h),m(g=>g.map(j=>j.id===h.id?h:j))}catch(o){L(D(o))}},na=t=>{E(n=>[...n,t].sort((o,h)=>o.name.localeCompare(h.name)))},ra=t=>{const n=o=>o.map(h=>h.id===t.id?t:h);E(o=>n(o).sort((h,g)=>h.name.localeCompare(g.name))),m(o=>o.map(h=>({...h,tags:n(h.tags)}))),J(o=>o?{...o,tags:n(o.tags)}:null)},oa=t=>{E(n=>n.filter(o=>o.id!==t)),m(n=>n.map(o=>({...o,tags:o.tags.filter(h=>h.id!==t)}))),J(n=>n?{...n,tags:n.tags.filter(o=>o.id!==t)}:null),it(n=>n===t?"":n)},ia=async(t,n)=>{const o=await C(`/api/projects/${t}/icon`,{method:"PATCH",body:JSON.stringify({icon:n})});m(h=>{const g=h.map(j=>j.id===o.project.id?o.project:j);return q==="updated"?g.sort((j,X)=>X.updatedAt.localeCompare(j.updatedAt)||j.name.localeCompare(X.name)):g})},Mt=async()=>{if(!(!Ve||!fe.trim()||Q)){lt(!0),Z("");try{const t=await C(`/api/projects/${Ve.id}`,{method:"PATCH",body:JSON.stringify({name:fe})});m(n=>n.map(o=>o.id===t.project.id?t.project:o)),oe(null),ve("")}catch(t){Z(D(t))}finally{lt(!1)}}},Ct=async()=>{if(!(!be||!Me.trim())){dt(!0),K("");try{await C(`/api/projects/${be.id}/duplicate`,{method:"POST",body:JSON.stringify({name:Me})}),ie(null),Y(""),he(u,N,U,w,q)}catch(t){K(D(t))}finally{dt(!1)}}},ca=async()=>{if(!(!ce||Ne)){pt(!0),re("");try{await C(`/api/projects/${ce.id}`,{method:"DELETE"}),m(g=>g.filter(j=>j.id!==ce.id));const t=Math.max(0,d.total-1),n=Math.ceil(t/d.pageSize),o=n===0?1:Math.min(N,n),h={...d,page:o,total:t,totalPages:n};z(h),we(null),o!==N?$(o):he(u,N,U,w,q)}catch(t){re(D(t))}finally{pt(!1)}}},wt=async t=>{Se(t),le(""),ht([]),te(""),Pe(!0);try{const n=await C("/api/users");ht(n.users.filter(o=>o.id!==t.ownerId))}catch(n){te(D(n))}finally{Pe(!1)}},la=async()=>{if(!(!ze||!Te)){Pe(!0),te("");try{await C(`/api/projects/${ze.id}/owner`,{method:"PUT",body:JSON.stringify({userId:Te})}),Se(null),le(""),he(u,N,U,w,q)}catch(t){te(D(t))}finally{Pe(!1)}}},Nt=async t=>{const n=!u;yt(t.id),M("");try{await C(`/api/projects/${t.id}/archive`,{method:n?"PUT":"DELETE"});const o=k.filter(De=>De.id!==t.id);m(o);const h=Math.max(0,d.total-1),g=Math.ceil(h/d.pageSize),j=g===0?1:Math.min(N,g),X={...d,page:j,total:h,totalPages:g};z(X),u||f(o,y,X),j!==N?$(j):he(u,N,U,w,q)}catch(o){M(D(o))}finally{yt("")}},zt=async t=>{try{const o=(await C(`/api/projects/${t.id}/mentions?unread=1&limit=1`)).mentions[0];c(t.id,o?.id)}catch(n){M(D(n))}},Le=k,Re=t=>new Date(t).toLocaleString(Ke.resolvedLanguage),da=t=>{const n=new Date(t);return Number.isFinite(n.getTime())?new Intl.DateTimeFormat(Ke.resolvedLanguage??"en",{year:"numeric",month:"2-digit",day:"2-digit"}).format(n):t},St=t=>an(t,Ke.resolvedLanguage??"en",Qt,(n,o)=>a(n,o));return e.jsxs("div",{className:"page",children:[e.jsxs("header",{className:"topbar",children:[e.jsxs("a",{className:"brand-link",href:Je("/"),"aria-label":s.siteName,children:[e.jsx("span",{className:"site-title",children:s.siteName}),e.jsx(ha,{siteName:s.siteName})]}),e.jsxs("div",{className:"top-actions",children:[p.role==="admin"&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"ghost",onClick:()=>R(!0),children:[e.jsx($a,{"aria-hidden":!0,size:14}),a("metrics.title")]}),e.jsxs("button",{className:`ghost${b?" top-return-action":""}`,onClick:()=>{T(t=>!t),P(!1)},children:[b?e.jsx(Pt,{"aria-hidden":!0,size:14}):e.jsx(Aa,{"aria-hidden":!0,size:14}),a(b?"users.back":"users.manage")]})]}),e.jsxs("button",{className:`ghost${V?" top-return-action":""}`,onClick:()=>{T(!1),P(t=>!t)},children:[V?e.jsx(Pt,{"aria-hidden":!0,size:14}):e.jsx(Et,{"aria-hidden":!0,size:14}),a(V?"users.back":"citationLibrary.title")]}),e.jsx(ma,{compact:!0}),e.jsxs("span",{className:"top-user-identity",children:[e.jsx("strong",{children:p.displayName}),e.jsxs("small",{children:["@",p.username]})]}),e.jsx("button",{className:"ghost",onClick:aa,children:a("auth.logout")})]})]}),b?e.jsx("main",{className:"dashboard",children:e.jsx(Tt,{onClose:()=>T(!1),children:e.jsx(Xs,{currentUser:p,minPasswordLength:s.minPasswordLength})})}):V?e.jsx("main",{className:"dashboard citation-library-page-shell",children:e.jsx(Tt,{onClose:()=>P(!1),children:e.jsx(Qs,{page:!0,open:!0,onOpenChange:P,onBack:()=>P(!1),currentUserId:p.id,maxBibtexBytes:s.maxCitationBibtexBytes})})}):e.jsxs("main",{className:"dashboard",children:[e.jsxs("div",{className:"section-title",children:[e.jsxs("div",{children:[e.jsxs("h1",{children:[e.jsx(Ge,{"aria-hidden":!0,size:25}),a("projects.title")]}),e.jsx("p",{className:"muted",children:p.canCreateProjects?a("projects.subtitle"):a("projects.restricted")})]}),e.jsxs("div",{className:"section-actions",children:[e.jsxs("button",{onClick:()=>Be(!0),children:[e.jsx(We,{"aria-hidden":!0,size:15}),a("tags.manage")]}),p.canCreateProjects&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>{O(""),ke(!0)},children:[e.jsx(At,{"aria-hidden":!0,size:15}),a("projects.upload")]}),e.jsxs("button",{className:"primary",onClick:()=>{l(""),ue(!0)},children:[e.jsx(La,{"aria-hidden":!0,size:15}),a("projects.new")]})]})]})]}),_&&e.jsx("p",{className:"error",children:_}),e.jsxs("div",{className:"project-catalog-layout",children:[e.jsxs("aside",{ref:nt,className:`project-tag-sidebar${w?" has-active-filter":""}`,children:[e.jsxs("button",{type:"button",className:"project-tag-toggle",title:a("projects.filterTags"),"aria-label":a("projects.filterTags"),"aria-expanded":je,"aria-controls":"dashboard-tag-filters",onClick:()=>$e(t=>!t),children:[e.jsxs("span",{className:"project-tag-toggle-copy",children:[e.jsx(We,{"aria-hidden":!0,size:15}),e.jsx("span",{children:a("projects.filterTags")})]}),e.jsx(Lt,{className:"project-tag-toggle-chevron","aria-hidden":!0,size:15})]}),je&&e.jsxs("div",{id:"dashboard-tag-filters",className:"project-tag-filters",role:"group","aria-label":a("projects.filterTags"),children:[e.jsx("button",{type:"button",className:w?"":"active","aria-pressed":!w,onClick:()=>vt(""),children:a("projects.allTags")}),y.length>0?y.map(t=>e.jsxs("button",{type:"button",className:w===t.id?"active":"",title:t.name,"aria-pressed":w===t.id,onClick:()=>vt(w===t.id?"":t.id),children:[e.jsx(Dt,{color:t.color}),e.jsx("span",{children:t.name})]},t.id)):e.jsx("p",{className:"project-tag-empty",children:a("tags.none")})]})]}),e.jsxs("div",{className:"project-catalog-content",children:[e.jsxs("div",{className:"project-toolbar",children:[e.jsx("input",{type:"search",placeholder:a("projects.search"),value:U,onChange:t=>{Zt(t.target.value),$(1)}}),e.jsxs("div",{className:"project-scope",role:"tablist","aria-label":a("projects.scope"),children:[e.jsxs("button",{className:u?"":"active",onClick:()=>ft(!1),role:"tab","aria-selected":!u,children:[e.jsx(Ge,{size:14}),a("projects.active")]}),e.jsxs("button",{className:u?"active":"",onClick:()=>ft(!0),role:"tab","aria-selected":u,children:[e.jsx(Qe,{size:14}),a("projects.archived")]})]}),e.jsxs("label",{className:"project-sort",children:[e.jsx(Va,{size:14}),e.jsx("span",{children:a("projects.sortBy")}),e.jsxs("select",{value:q,onChange:t=>ea(t.target.value),children:[e.jsx("option",{value:"updated",children:a("projects.sortModified")}),e.jsx("option",{value:"created",children:a("projects.sortCreated")})]})]}),e.jsxs("div",{className:"view-toggle",children:[e.jsx("button",{className:de==="grid"?"active":"",onClick:()=>xt("grid"),title:a("projects.grid"),children:"▦"}),e.jsx("button",{className:de==="list"?"active":"",onClick:()=>xt("list"),title:a("projects.list"),children:"☷"})]})]}),e.jsxs("div",{className:`project-grid ${de==="list"?"list-view":""}${Ae?" project-list-menu-active":""}`,children:[de==="list"&&Le.length>0&&e.jsxs("div",{className:"project-list-header","aria-hidden":"true",children:[e.jsx("span",{className:"project-list-header-avatar"}),e.jsx("span",{children:a("projects.title")}),e.jsx("span",{children:a("projects.owner")}),e.jsx("span",{className:"project-list-header-created",children:a("projects.created")}),e.jsx("span",{children:a("projects.modified")}),e.jsx("span",{className:"project-list-header-mentions",children:a("projects.mentionsColumn")}),e.jsx("span",{className:"project-list-header-actions",children:a("projects.actions")})]}),de==="list"?Le.map(t=>e.jsx(Gs,{project:t,currentUser:p,showArchived:u,archiveBusy:ut===t.id,menuOpen:Ae===t.id,t:a,formatCreatedDate:da,formatUpdatedTime:St,formatExactTime:Re,onOpenProject:()=>{H(null),c(t.id)},onOpenMention:()=>{zt(t)},onToggleMenu:()=>H(n=>n===t.id?null:t.id),onCloseMenu:()=>H(n=>n===t.id?null:n),onAssignTags:()=>{L(""),J(t)},onRename:()=>{Z(""),oe(t),ve(t.name)},onDuplicate:()=>{K(""),ie(t),Y(`${t.name} (1)`)},onArchive:()=>{Nt(t)},onTransfer:()=>{wt(t)},onDelete:()=>{we(t),re("")},onChooseIcon:()=>xe(t)},t.id)):Le.map(t=>e.jsxs("article",{className:`project-card project-grid-card${t.ownerId===p.id?" owned-project":""}${Ae===t.id?" project-card-menu-open":""}`,role:"button",tabIndex:0,"aria-label":a("projects.openProject",{project:t.name}),onClick:()=>{H(null),c(t.id)},onKeyDown:n=>{n.target!==n.currentTarget||n.key!=="Enter"&&n.key!==" "||(n.preventDefault(),H(null),c(t.id))},children:[e.jsxs("div",{className:"project-card-meta-row",children:[e.jsxs("div",{className:"project-card-badges",children:[e.jsx("span",{className:"owner-badge",title:t.ownerDisplayName??t.ownerUsername??a("projects.deletedUser"),children:t.ownerDisplayName??t.ownerUsername??a("projects.deletedUser")}),!!(t.unreadMentionCount&&t.unreadMentionCount>0)&&e.jsxs("button",{className:"project-mentions-badge",type:"button",title:a("projects.unreadMentionsTooltip",{count:t.unreadMentionCount}),"aria-label":a("projects.unreadMentionsTooltip",{count:t.unreadMentionCount}),onClick:n=>{n.stopPropagation(),zt(t)},children:[e.jsx(It,{"aria-hidden":!0,size:11}),e.jsx("span",{children:t.unreadMentionCount})]})]}),e.jsx(Ut,{variant:"grid",project:t,currentUser:p,showArchived:u,archiveBusy:ut===t.id,menuOpen:Ae===t.id,t:a,onToggleMenu:()=>H(n=>n===t.id?null:t.id),onCloseMenu:()=>H(n=>n===t.id?null:n),onAssignTags:()=>{L(""),J(t)},onRename:()=>{Z(""),oe(t),ve(t.name)},onDuplicate:()=>{K(""),ie(t),Y(`${t.name} (1)`)},onArchive:()=>{Nt(t)},onTransfer:()=>{wt(t)},onDelete:()=>{we(t),re("")}})]}),e.jsxs("div",{className:"project-card-title-row",children:[e.jsx("span",{className:"project-card-icon-control",onClick:t.permission==="owner"?n=>n.stopPropagation():void 0,children:e.jsx(Ot,{icon:t.icon,projectName:t.name,title:t.name,editable:t.permission==="owner",editLabel:a("projectIcons.change"),onEdit:()=>xe(t),className:"project-card-icon"})}),e.jsx("strong",{className:"project-card-title",title:t.name,children:t.name})]}),e.jsx("div",{className:"project-card-tags","aria-label":a("tags.assignTitle",{project:t.name}),title:t.tags?.map(n=>n.name).join(", ")||void 0,children:t.tags?.map(n=>e.jsx("span",{className:`tag tag-${n.color}`,title:n.name,children:n.name},n.id))}),e.jsxs("dl",{className:"project-meta",children:[e.jsxs("div",{children:[e.jsxs("dt",{className:"project-meta-created-label",children:[e.jsxs("span",{children:[e.jsx($t,{"aria-hidden":!0,size:13}),a("projects.created")]}),!!(t.unresolvedCommentCount&&t.unresolvedCommentCount>0)&&e.jsxs("span",{className:"project-comments-badge unresolved",title:a("projects.unresolvedCommentsTooltip",{unresolved:t.unresolvedCommentCount,total:t.commentCount??t.unresolvedCommentCount}),children:[e.jsx(Ft,{"aria-hidden":!0,size:11}),e.jsx("span",{children:a("projects.unresolvedCount",{count:t.unresolvedCommentCount})})]})]}),e.jsx("dd",{children:e.jsx("time",{dateTime:t.createdAt,children:Re(t.createdAt)})})]}),e.jsxs("div",{children:[e.jsxs("dt",{children:[e.jsx(gs,{"aria-hidden":!0,size:13}),a("projects.modified")]}),e.jsxs("dd",{title:a("projects.modifiedByUser",{time:Re(t.updatedAt),user:t.lastModifiedDisplayName??t.lastModifiedUsername??a("projects.deletedUser")}),children:[e.jsx("time",{dateTime:t.updatedAt,children:St(t.updatedAt)}),e.jsxs("span",{className:"project-modified-by",children:[" · ",a("projects.byUser",{user:t.lastModifiedDisplayName??t.lastModifiedUsername??a("projects.deletedUser")})]})]})]})]})]},t.id)),Le.length===0&&(k.length===0?u?e.jsx("div",{className:"empty",children:a("projects.noArchived")}):e.jsxs("div",{className:"project-empty",children:[e.jsx("span",{className:"project-empty-icon",children:e.jsx(Bt,{size:28})}),e.jsx("h2",{children:a("projects.emptyTitle")}),e.jsx("p",{children:p.canCreateProjects?a("projects.emptyDescription"):a("projects.emptyRestricted")})]}):e.jsx("div",{className:"empty",children:a("projects.noMatches")}))]}),d.totalPages>1&&e.jsxs("nav",{className:"project-pagination","aria-label":a("projects.pagination"),children:[e.jsxs("button",{disabled:d.page<=1,onClick:()=>$(t=>Math.max(1,t-1)),title:a("projects.previousPage"),children:[e.jsx(Da,{size:15}),a("projects.previousPage")]}),e.jsx("span",{children:a("projects.pageOf",{page:d.page,totalPages:d.totalPages,count:d.total})}),e.jsxs("button",{disabled:d.page>=d.totalPages,onClick:()=>$(t=>Math.min(d.totalPages,t+1)),title:a("projects.nextPage"),children:[e.jsx(Lt,{size:15}),a("projects.nextPage")]})]})]})]}),e.jsx(se,{open:Rt,title:a("projects.new"),description:a("projects.newDescription"),onOpenChange:t=>{!t&&W||(ue(t),t||l(""))},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{disabled:W,onClick:()=>ue(!1),children:a("common.cancel")}),e.jsxs("button",{className:"primary",disabled:W||!ye.trim(),"aria-busy":W,onClick:()=>{bt()},children:[W&&e.jsx(_e,{className:"spin",size:14}),a(W?"common.loading":"common.create")]})]}),children:e.jsxs(e.Fragment,{children:[ne&&e.jsx("p",{className:"error dialog-error",children:ne}),e.jsxs("label",{className:"form-field",children:[a("projects.name"),e.jsx("input",{autoFocus:!0,value:ye,onChange:t=>{et(t.target.value),l("")},onKeyDown:t=>{t.key==="Enter"&&bt()}})]})]})}),e.jsx(se,{open:_t,title:a("projects.upload"),description:a("projects.uploadDescription",{size:s.maxUploadSizeMB}),onOpenChange:t=>{ke(t),t||O("")},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>{ke(!1),O("")},children:a("common.cancel")}),e.jsx("button",{className:"primary",disabled:!I||rt,onClick:()=>{ta()},children:a(rt?"projects.importing":"projects.import")})]}),children:e.jsxs("div",{className:"form-stack",children:[st&&e.jsx("p",{className:"error import-error",children:st}),e.jsxs("div",{className:`upload-picker${I?" has-file":""}`,onDragOver:t=>{t.preventDefault(),t.dataTransfer.dropEffect="copy"},onDrop:t=>{t.preventDefault(),He(t.dataTransfer.files[0]??null)},children:[e.jsx("input",{ref:ge,className:"sr-only",type:"file",accept:".zip,application/zip",onChange:t=>He(t.target.files?.[0]??null)}),e.jsx(Ea,{size:34}),e.jsxs("div",{className:"upload-picker-copy",children:[e.jsx("strong",{children:I?.name??a("projects.chooseZip")}),e.jsx("span",{children:I?a("projects.selectedFileSize",{size:sn(I.size)}):a("projects.dropZip")})]}),e.jsxs("button",{type:"button",onClick:()=>ge.current?.click(),children:[e.jsx(At,{size:15}),a("projects.browse")]}),I&&e.jsx("button",{className:"upload-clear",type:"button",title:a("projects.clearFile"),"aria-label":a("projects.clearFile"),onClick:()=>{He(null),ge.current&&(ge.current.value="")},children:e.jsx(Fa,{size:14})})]}),e.jsxs("label",{className:"form-field",children:[a("projects.name"),e.jsx("input",{value:Ie,onChange:t=>qe(t.target.value)})]})]})}),Kt&&e.jsx(Ze,{title:a("tags.manage"),onClose:()=>Be(!1),children:e.jsx(Ys,{open:!0,onOpenChange:Be,onTagCreated:na,onTagUpdated:ra,onTagDeleted:oa})}),ct&&e.jsx(Ze,{title:a("projectIcons.title"),onClose:()=>xe(null),children:e.jsx(en,{project:ct,open:!0,onOpenChange:t=>{t||xe(null)},onSave:ia})}),e.jsx(se,{open:!!G,title:a("tags.assignTitle",{project:G?.name??""}),description:a("tags.assignDescription"),onOpenChange:t=>{t||(J(null),L(""))},footer:e.jsx("button",{onClick:()=>{J(null),L("")},children:a("common.close")}),children:e.jsxs("div",{className:"tag-assignment-list",children:[A&&e.jsx("p",{className:"error dialog-error",children:A}),y.map(t=>e.jsxs("label",{children:[e.jsx("input",{type:"checkbox",checked:!!G?.tags.some(n=>n.id===t.id),onChange:()=>{sa(t)}}),e.jsx(Dt,{color:t.color}),e.jsx("span",{children:t.name})]},t.id)),y.length===0&&e.jsx("p",{className:"muted",children:a("tags.empty")})]})}),e.jsx(se,{open:!!Ve,title:a("projects.renameTitle"),onOpenChange:t=>{!t&&Q||t||(oe(null),Z(""))},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{disabled:Q,onClick:()=>{oe(null),Z("")},children:a("common.cancel")}),e.jsxs("button",{className:"primary",disabled:Q||!fe.trim(),"aria-busy":Q,onClick:()=>{Mt()},children:[Q&&e.jsx(_e,{className:"spin",size:14}),a(Q?"common.loading":"projects.rename")]})]}),children:e.jsxs(e.Fragment,{children:[Xe&&e.jsx("p",{className:"error dialog-error",children:Xe}),e.jsxs("label",{className:"form-field",children:[a("projects.name"),e.jsx("input",{autoFocus:!0,value:fe,onChange:t=>{ve(t.target.value),Z("")},onKeyDown:t=>{t.key==="Enter"&&Mt()}})]})]})}),e.jsx(se,{open:!!be,title:a("projects.duplicateTitle"),description:a("projects.duplicateDescription",{project:be?.name??""}),onOpenChange:t=>{!t&&!Ce&&(ie(null),Y(""),K(""))},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{disabled:Ce,onClick:()=>{ie(null),Y(""),K("")},children:a("common.cancel")}),e.jsx("button",{className:"primary",disabled:Ce||!Me.trim(),onClick:()=>{Ct()},children:a(Ce?"projects.duplicating":"projects.duplicate")})]}),children:e.jsxs(e.Fragment,{children:[Ye&&e.jsx("p",{className:"error dialog-error",children:Ye}),e.jsxs("label",{className:"form-field",children:[a("projects.name"),e.jsx("input",{autoFocus:!0,value:Me,onChange:t=>{Y(t.target.value),K("")},onKeyDown:t=>{t.key==="Enter"&&Ct()}})]})]})}),e.jsx(se,{open:!!ze,title:a("projects.transferOwnership"),description:a("projects.transferDescription",{project:ze?.name??""}),onOpenChange:t=>{!t&&!ee&&(Se(null),le(""),te(""))},footer:e.jsxs(e.Fragment,{children:[e.jsx("button",{disabled:ee,onClick:()=>{Se(null),le(""),te("")},children:a("common.cancel")}),e.jsxs("button",{className:"primary",disabled:ee||!Te,onClick:()=>{la()},children:[ee?e.jsx(_e,{className:"spin",size:14}):e.jsx(qt,{size:14}),a("projects.transfer")]})]}),children:e.jsxs("div",{className:"form-stack",children:[mt&&e.jsx("p",{className:"error dialog-error",children:mt}),e.jsxs("label",{className:"form-field",children:[a("projects.newOwner"),e.jsxs("select",{disabled:ee||Oe.length===0,value:Te,onChange:t=>le(t.target.value),children:[e.jsx("option",{value:"",children:ee?a("common.loading"):Oe.length>0?a("projects.chooseNewOwner"):a("projects.noTransferUsers")}),Oe.map(t=>e.jsxs("option",{value:t.id,children:[t.displayName??t.username," (@",t.username,")"]},t.id))]})]}),e.jsxs("p",{className:"warning",children:[e.jsx(Ia,{size:15}),a("projects.transferWarning")]})]})}),e.jsx(ua,{open:!!ce,title:a("projects.deleteTitle"),description:a("projects.deleteDescription",{project:ce?.name??""}),confirmLabel:a(Ne?"common.loading":"common.delete"),danger:!0,busy:Ne,error:Ht,onCancel:()=>{Ne||(we(null),re(""))},onConfirm:()=>{ca()}})]}),F&&e.jsx(Ze,{title:a("metrics.title"),onClose:()=>R(!1),children:e.jsx(Js,{open:!0,onOpenChange:R})}),e.jsx(ya,{})]})}function Dt({color:s}){return e.jsx("span",{className:`tag-dot tag-${s}`})}const mn=Object.freeze(Object.defineProperty({__proto__:null,Dashboard:nn},Symbol.toStringTag,{value:"Module"}));export{$a as A,mn as D,Ws as P,Ds as S,Ks as a,Rs as i,_s as n,hn as p};
|