docs-i18n 0.10.0 → 0.11.1

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.
Files changed (67) hide show
  1. package/admin/app/lib/auth-client.ts +21 -0
  2. package/admin/app/lib/auth.server.ts +62 -0
  3. package/admin/app/routeTree.gen.ts +21 -3
  4. package/admin/app/routes/index.tsx +68 -8
  5. package/admin/app/routes/login.tsx +207 -0
  6. package/admin/app/server.ts +23 -0
  7. package/admin/app/styles.css +146 -0
  8. package/admin/app/worker.ts +61 -0
  9. package/admin/dist/client/assets/auth-client-DqJZaFZR.js +3 -0
  10. package/admin/dist/client/assets/login-CQjkiBAF.js +1 -0
  11. package/admin/dist/client/assets/main-bc5tcdGg.js +18 -0
  12. package/admin/dist/client/assets/routes-CTPOG_v1.js +3 -0
  13. package/admin/dist/client/assets/{styles-DJ6QEJmN.css → styles-zS0BbO7e.css} +1 -1
  14. package/admin/dist/client/assets/useNavigate-CEOVvMjz.js +1 -0
  15. package/admin/dist/server/assets/{react-dom-CpO9xk_L.js → __tanstack-start-server-fn-resolver-N1N5mZVv.js} +83 -507
  16. package/admin/dist/server/assets/_tanstack-start-manifest_v-BgMVPPt2.js +21 -0
  17. package/admin/dist/server/assets/adapter-DjXlUL1J.js +2253 -0
  18. package/admin/dist/server/assets/auth-client-CoUpyQIM.js +807 -0
  19. package/admin/dist/server/assets/auth-dSAIPjBw.js +72 -0
  20. package/admin/dist/server/assets/auth.server-BLVDnTCZ.js +31352 -0
  21. package/admin/dist/server/assets/bun-sqlite-dialect-C8OaCWSL-BNNY-FoT.js +154 -0
  22. package/admin/dist/server/assets/compiled-query-CnFG_BVV.js +6967 -0
  23. package/admin/dist/server/assets/d1-sqlite-dialect-sYHNqBte-Bjz-cybU.js +115 -0
  24. package/admin/dist/server/assets/dist-C3-e8E2B.js +215 -0
  25. package/admin/dist/server/assets/dist-CUzFWZag.js +6039 -0
  26. package/admin/dist/server/assets/{init-DJr2glb3.js → dist-DBv71kqn.js} +21 -381
  27. package/admin/dist/server/assets/error-CASJ5tIm.js +457 -0
  28. package/admin/dist/server/assets/error-codes-BhMTGvV6.js +1153 -0
  29. package/admin/dist/server/assets/init-CJJUsPDL.js +49 -0
  30. package/admin/dist/server/assets/job-manager-D9Ab9hgu.js +179 -0
  31. package/admin/dist/server/assets/{jobs-bQfYqSk7.js → jobs-DrEe9YOj.js} +18 -5
  32. package/admin/dist/server/assets/kysely-adapter-YC8RFPyc.js +2 -0
  33. package/admin/dist/server/assets/login-SqzTMYOZ.js +228 -0
  34. package/admin/dist/server/assets/migrator-ZpVZslbq.js +2926 -0
  35. package/admin/dist/server/assets/{misc-DOk3t9vs.js → misc-BSoYldBT.js} +22 -9
  36. package/admin/dist/server/assets/{models-CBb8Owe5.js → models-CoviNHUP.js} +1 -1
  37. package/admin/dist/server/assets/node-sqlite-dialect-BJIaP6lL.js +154 -0
  38. package/admin/dist/server/assets/{router-DlU_fGDK.js → router-Cxm33tn3.js} +23 -11
  39. package/admin/dist/server/assets/{routes-pIM0fgUO.js → routes-CxGhew8Z.js} +56 -22
  40. package/admin/dist/server/assets/{routes-BcfX6iub.js → routes-DtWFPDUc.js} +43 -29
  41. package/admin/dist/server/assets/sqlite-adapter-CL2EidjD.js +69 -0
  42. package/admin/dist/server/assets/{start-BS52hm79.js → start-BiybVoR2.js} +1 -1
  43. package/admin/dist/server/assets/status-B1AGLvHn.js +162 -0
  44. package/admin/dist/server/assets/status-D7PU72hm.js +262 -0
  45. package/admin/dist/server/assets/string-B4XlckmJ.js +6 -0
  46. package/admin/dist/server/assets/useNavigate-0H08s_Q2.js +29 -0
  47. package/admin/dist/server/assets/useRouter-BXJvr8to.js +508 -0
  48. package/admin/dist/server/server.js +70 -90
  49. package/admin/package.json +6 -1
  50. package/admin/server/functions/auth.ts +89 -0
  51. package/admin/server/functions/jobs.ts +30 -2
  52. package/admin/server/functions/misc.ts +25 -5
  53. package/admin/server/functions/status.ts +52 -3
  54. package/admin/server/index.ts +1 -1
  55. package/admin/server/init.ts +21 -9
  56. package/admin/server/services/d1-status.ts +282 -0
  57. package/admin/vite.config.ts +29 -15
  58. package/admin/wrangler.jsonc +16 -0
  59. package/package.json +1 -1
  60. package/template/content/docs-i18n/en/cli.md +9 -5
  61. package/template/content/docs-i18n/en/deployment.md +85 -68
  62. package/admin/dist/client/assets/main-CSFhgz4p.js +0 -17
  63. package/admin/dist/client/assets/routes-C6bCOSX-.js +0 -3
  64. package/admin/dist/server/assets/_tanstack-start-manifest_v-BE5XHVZ2.js +0 -17
  65. package/admin/dist/server/assets/redirect-DtfSYi2g.js +0 -51
  66. package/admin/dist/server/assets/status-CZz8Rs_7.js +0 -81
  67. /package/admin/dist/server/assets/{createServerRpc-DJq9yo-B.js → createServerRpc-Cyyxq9HQ.js} +0 -0
@@ -0,0 +1,49 @@
1
+ import { r as __require } from "./chunk-CNvmzFzq.js";
2
+ //#region server/init.ts
3
+ var _initialized = false;
4
+ var _config;
5
+ function isCfWorkers() {
6
+ return !!globalThis.__CF_WORKERS;
7
+ }
8
+ /**
9
+ * Initialize admin server services with the project config.
10
+ */
11
+ async function initAdmin(config) {
12
+ _config = config;
13
+ if (!isCfWorkers()) {
14
+ const { initStatus } = await import("./status-B1AGLvHn.js");
15
+ const { setConfig } = await import("./job-manager-D9Ab9hgu.js");
16
+ initStatus(config);
17
+ setConfig(config);
18
+ }
19
+ _initialized = true;
20
+ }
21
+ function getConfig() {
22
+ return _config;
23
+ }
24
+ function getProjectRoot() {
25
+ if (isCfWorkers()) return "(cf-workers)";
26
+ if (process.env.DOCS_I18N_PROJECT_ROOT) return process.env.DOCS_I18N_PROJECT_ROOT;
27
+ try {
28
+ const { readFileSync } = __require("node:fs");
29
+ const { resolve } = __require("node:path");
30
+ return readFileSync(resolve(process.env.TMPDIR || process.env.TEMP || "/tmp", "docs-i18n-project-root"), "utf-8").trim();
31
+ } catch {}
32
+ return process.cwd();
33
+ }
34
+ /**
35
+ * Ensure admin is initialized. Lazy-loads config on first call.
36
+ * On CF Workers, this is a no-op (D1 doesn't need config).
37
+ */
38
+ async function ensureInit() {
39
+ if (_initialized) return;
40
+ if (isCfWorkers()) {
41
+ _initialized = true;
42
+ return;
43
+ }
44
+ if (typeof window !== "undefined") return;
45
+ const mod = await import(`${getProjectRoot()}/docs-i18n.config.ts`);
46
+ await initAdmin(mod.default ?? mod);
47
+ }
48
+ //#endregion
49
+ export { ensureInit, getConfig, getProjectRoot };
@@ -0,0 +1,179 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ //#region server/services/job-manager.ts
5
+ function getProjectRoot() {
6
+ if (process.env.DOCS_I18N_PROJECT_ROOT) return process.env.DOCS_I18N_PROJECT_ROOT;
7
+ try {
8
+ return readFileSync(resolve(process.env.TMPDIR || process.env.TEMP || "/tmp", "docs-i18n-project-root"), "utf-8").trim();
9
+ } catch {}
10
+ return process.cwd();
11
+ }
12
+ var _config;
13
+ function setConfig(config) {
14
+ _config = config;
15
+ }
16
+ var JobManager = class {
17
+ jobs = /* @__PURE__ */ new Map();
18
+ processes = /* @__PURE__ */ new Map();
19
+ subscribers = /* @__PURE__ */ new Map();
20
+ nextId = 1;
21
+ start(opts) {
22
+ for (const [, job] of this.jobs) if (job.lang === opts.lang && job.version === opts.version && job.status === "running") throw new Error(`Job already running for ${opts.lang}/${opts.version}`);
23
+ const id = `job-${this.nextId++}`;
24
+ const job = {
25
+ id,
26
+ lang: opts.lang,
27
+ version: opts.version,
28
+ project: opts.project ?? "",
29
+ status: "running",
30
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
31
+ logLines: [],
32
+ translatedFiles: 0,
33
+ totalFiles: 0,
34
+ toTranslate: 0,
35
+ errorFiles: 0
36
+ };
37
+ this.jobs.set(id, job);
38
+ let projectArg = opts.project ?? "";
39
+ let versionArg = opts.version;
40
+ if (!projectArg && opts.version.includes("/")) {
41
+ const parts = opts.version.split("/");
42
+ projectArg = parts.slice(0, -1).join("/");
43
+ versionArg = parts[parts.length - 1];
44
+ }
45
+ const args = [
46
+ "translate",
47
+ "--lang",
48
+ opts.lang,
49
+ "--version",
50
+ versionArg,
51
+ "--max",
52
+ String(opts.max ?? 999),
53
+ "--concurrency",
54
+ String(opts.concurrency ?? 3)
55
+ ];
56
+ if (projectArg) args.push("--project", projectArg);
57
+ if (opts.model) args.push("--model", opts.model);
58
+ if (opts.files?.length) args.push("--files", opts.files.join(","));
59
+ const apiKey = _config?.llm?.apiKey || process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY || "";
60
+ if (apiKey) args.push("--api-key", apiKey);
61
+ const projectRoot = getProjectRoot();
62
+ const childEnv = {
63
+ ...process.env,
64
+ NO_TTY: "1",
65
+ FORCE_COLOR: "0"
66
+ };
67
+ try {
68
+ const envContent = readFileSync(resolve(projectRoot, ".env"), "utf-8");
69
+ for (const line of envContent.split("\n")) {
70
+ const match = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)\s*$/);
71
+ if (match && !childEnv[match[1]]) childEnv[match[1]] = match[2];
72
+ }
73
+ } catch {}
74
+ const proc = spawn("npx", ["docs-i18n", ...args], {
75
+ cwd: projectRoot,
76
+ stdio: [
77
+ "ignore",
78
+ "pipe",
79
+ "pipe"
80
+ ],
81
+ env: childEnv,
82
+ shell: true
83
+ });
84
+ this.processes.set(id, proc);
85
+ proc.stdout?.on("data", (d) => {
86
+ for (const line of d.toString().split("\n").filter(Boolean)) {
87
+ this.addLog(id, line);
88
+ this.parseProgress(job, line);
89
+ }
90
+ });
91
+ proc.stderr?.on("data", (d) => {
92
+ for (const line of d.toString().split("\n").filter(Boolean)) this.addLog(id, line);
93
+ });
94
+ proc.on("exit", (code) => {
95
+ job.status = code === 0 ? "completed" : "failed";
96
+ job.exitCode = code;
97
+ job.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
98
+ job.currentFile = void 0;
99
+ this.addLog(id, `Process exited with code ${code}`);
100
+ this.processes.delete(id);
101
+ this.emit(id, {
102
+ type: "exit",
103
+ code
104
+ });
105
+ });
106
+ proc.on("error", (err) => {
107
+ job.status = "failed";
108
+ job.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
109
+ this.addLog(id, `Process error: ${err.message}`);
110
+ this.processes.delete(id);
111
+ this.emit(id, {
112
+ type: "exit",
113
+ code: -1
114
+ });
115
+ });
116
+ return job;
117
+ }
118
+ cancel(id) {
119
+ const proc = this.processes.get(id);
120
+ const job = this.jobs.get(id);
121
+ if (!proc || !job) return false;
122
+ proc.kill("SIGTERM");
123
+ job.status = "cancelled";
124
+ job.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
125
+ this.processes.delete(id);
126
+ this.emit(id, {
127
+ type: "exit",
128
+ code: null
129
+ });
130
+ return true;
131
+ }
132
+ list() {
133
+ return [...this.jobs.values()].sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
134
+ }
135
+ get(id) {
136
+ return this.jobs.get(id);
137
+ }
138
+ remove(id) {
139
+ const job = this.jobs.get(id);
140
+ if (!job || job.status === "running") return false;
141
+ this.jobs.delete(id);
142
+ this.subscribers.delete(id);
143
+ return true;
144
+ }
145
+ subscribe(id, callback) {
146
+ if (!this.subscribers.has(id)) this.subscribers.set(id, /* @__PURE__ */ new Set());
147
+ this.subscribers.get(id)?.add(callback);
148
+ return () => {
149
+ this.subscribers.get(id)?.delete(callback);
150
+ };
151
+ }
152
+ addLog(id, line) {
153
+ const job = this.jobs.get(id);
154
+ if (!job) return;
155
+ const entry = `[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })}] ${line}`;
156
+ job.logLines = job.logLines ?? [];
157
+ job.logLines.push(entry);
158
+ if (job.logLines.length > 500) job.logLines.shift();
159
+ this.emit(id, {
160
+ type: "log",
161
+ data: entry
162
+ });
163
+ }
164
+ emit(id, event) {
165
+ this.subscribers.get(id)?.forEach((cb) => cb(event));
166
+ }
167
+ parseProgress(job, line) {
168
+ const cached = line.match(/\+(\d+) cached/);
169
+ if (cached) job.translatedFiles += Number.parseInt(cached[1], 10);
170
+ const untranslated = line.match(/(\d+) untranslated keys/);
171
+ if (untranslated) job.toTranslate = Number.parseInt(untranslated[1], 10);
172
+ const chunk = line.match(/chunk \d+\/(\d+)/);
173
+ if (chunk) job.totalFiles = Number.parseInt(chunk[1], 10);
174
+ if (line.includes("⏳")) job.currentFile = line.replace(/^⏳\s*/, "");
175
+ }
176
+ };
177
+ var jobManager = new JobManager();
178
+ //#endregion
179
+ export { jobManager, setConfig };
@@ -1,15 +1,23 @@
1
1
  import { t as createServerFn } from "../server.js";
2
- import { t as createServerRpc } from "./createServerRpc-DJq9yo-B.js";
3
- import { ensureInit, l as jobManager } from "./init-DJr2glb3.js";
2
+ import { t as createServerRpc } from "./createServerRpc-Cyyxq9HQ.js";
4
3
  //#region server/functions/jobs.ts?tss-serverfn-split
4
+ function isCfWorkers() {
5
+ return !!globalThis.__CF_WORKERS;
6
+ }
7
+ async function getJobManager() {
8
+ const { jobManager } = await import("./job-manager-D9Ab9hgu.js");
9
+ return jobManager;
10
+ }
5
11
  var fetchJobs_createServerFn_handler = createServerRpc({
6
12
  id: "421de02ce39dde6e27cf4689e837ec072cbd01e63f8cdd5c2a3f42f0bd5ca613",
7
13
  name: "fetchJobs",
8
14
  filename: "server/functions/jobs.ts"
9
15
  }, (opts) => fetchJobs.__executeServer(opts));
10
16
  var fetchJobs = createServerFn({ method: "GET" }).handler(fetchJobs_createServerFn_handler, async () => {
17
+ if (isCfWorkers()) return [];
18
+ const { ensureInit } = await import("./init-CJJUsPDL.js");
11
19
  await ensureInit();
12
- return jobManager.list().map((j) => ({
20
+ return (await getJobManager()).list().map((j) => ({
13
21
  ...j,
14
22
  logLines: (j.logLines ?? []).slice(-20)
15
23
  }));
@@ -20,9 +28,11 @@ var createJob_createServerFn_handler = createServerRpc({
20
28
  filename: "server/functions/jobs.ts"
21
29
  }, (opts) => createJob.__executeServer(opts));
22
30
  var createJob = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(createJob_createServerFn_handler, async ({ data }) => {
31
+ if (isCfWorkers()) throw new Error("Job creation is not available on CF Workers");
32
+ const { ensureInit } = await import("./init-CJJUsPDL.js");
23
33
  await ensureInit();
24
34
  if (!data.lang || !data.version) throw new Error("Missing lang or version");
25
- return jobManager.start(data);
35
+ return (await getJobManager()).start(data);
26
36
  });
27
37
  var fetchJob_createServerFn_handler = createServerRpc({
28
38
  id: "8a56694c9d7b29422a3e7d2f6b803be100d79d3853d92d465cb55ed572781e62",
@@ -30,7 +40,8 @@ var fetchJob_createServerFn_handler = createServerRpc({
30
40
  filename: "server/functions/jobs.ts"
31
41
  }, (opts) => fetchJob.__executeServer(opts));
32
42
  var fetchJob = createServerFn({ method: "GET" }).inputValidator((d) => d).handler(fetchJob_createServerFn_handler, async ({ data }) => {
33
- const job = jobManager.get(data.id);
43
+ if (isCfWorkers()) throw new Error("Jobs not available on CF Workers");
44
+ const job = (await getJobManager()).get(data.id);
34
45
  if (!job) throw new Error("Job not found");
35
46
  return job;
36
47
  });
@@ -40,6 +51,8 @@ var deleteJob_createServerFn_handler = createServerRpc({
40
51
  filename: "server/functions/jobs.ts"
41
52
  }, (opts) => deleteJob.__executeServer(opts));
42
53
  var deleteJob = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(deleteJob_createServerFn_handler, async ({ data }) => {
54
+ if (isCfWorkers()) throw new Error("Jobs not available on CF Workers");
55
+ const jobManager = await getJobManager();
43
56
  const job = jobManager.get(data.id);
44
57
  if (!job) throw new Error("Job not found");
45
58
  if (job.status === "running") jobManager.cancel(data.id);
@@ -0,0 +1,2 @@
1
+ import { r as kyselyAdapter, t as createKyselyAdapter } from "./dist-CUzFWZag.js";
2
+ export { createKyselyAdapter, kyselyAdapter };
@@ -0,0 +1,228 @@
1
+ import { i as __toESM } from "./chunk-CNvmzFzq.js";
2
+ import { l as require_react, r as require_jsx_runtime } from "./useRouter-BXJvr8to.js";
3
+ import { t as useNavigate } from "./useNavigate-0H08s_Q2.js";
4
+ import { t as authClient } from "./auth-client-CoUpyQIM.js";
5
+ //#region app/routes/login.tsx?tsr-split=component
6
+ var import_react = /* @__PURE__ */ __toESM(require_react());
7
+ var import_jsx_runtime = require_jsx_runtime();
8
+ function LoginPage() {
9
+ const navigate = useNavigate();
10
+ const [mode, setMode] = (0, import_react.useState)("login");
11
+ const [username, setUsername] = (0, import_react.useState)("");
12
+ const [password, setPassword] = (0, import_react.useState)("");
13
+ const [email, setEmail] = (0, import_react.useState)("");
14
+ const [name, setName] = (0, import_react.useState)("");
15
+ const [error, setError] = (0, import_react.useState)(null);
16
+ const [loading, setLoading] = (0, import_react.useState)(false);
17
+ const handleLogin = async (e) => {
18
+ e.preventDefault();
19
+ setError(null);
20
+ setLoading(true);
21
+ try {
22
+ const { error: authError } = await authClient.signIn.username({
23
+ username,
24
+ password
25
+ });
26
+ if (authError) {
27
+ setError(authError.message || "Login failed");
28
+ setLoading(false);
29
+ return;
30
+ }
31
+ navigate({ to: "/" });
32
+ } catch (err) {
33
+ setError(err instanceof Error ? err.message : "Login failed");
34
+ setLoading(false);
35
+ }
36
+ };
37
+ const handleRegister = async (e) => {
38
+ e.preventDefault();
39
+ setError(null);
40
+ setLoading(true);
41
+ try {
42
+ const { error: authError } = await authClient.signUp.email({
43
+ email,
44
+ password,
45
+ name: name || username,
46
+ username
47
+ });
48
+ if (authError) {
49
+ setError(authError.message || "Registration failed");
50
+ setLoading(false);
51
+ return;
52
+ }
53
+ navigate({ to: "/" });
54
+ } catch (err) {
55
+ setError(err instanceof Error ? err.message : "Registration failed");
56
+ setLoading(false);
57
+ }
58
+ };
59
+ const handleAnonymousLogin = async () => {
60
+ setError(null);
61
+ setLoading(true);
62
+ try {
63
+ const { error: authError } = await authClient.signIn.anonymous();
64
+ if (authError) {
65
+ setError(authError.message || "Anonymous login failed");
66
+ setLoading(false);
67
+ return;
68
+ }
69
+ navigate({ to: "/" });
70
+ } catch (err) {
71
+ setError(err instanceof Error ? err.message : "Anonymous login failed");
72
+ setLoading(false);
73
+ }
74
+ };
75
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
76
+ className: "login-page",
77
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
78
+ className: "login-card",
79
+ children: [
80
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Translation Admin" }),
81
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
82
+ className: "login-subtitle",
83
+ children: "Sign in to manage translations"
84
+ }),
85
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
86
+ className: "login-mode-toggle",
87
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
88
+ type: "button",
89
+ className: mode === "login" ? "active" : "",
90
+ onClick: () => {
91
+ setMode("login");
92
+ setError(null);
93
+ },
94
+ children: "Sign In"
95
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
96
+ type: "button",
97
+ className: mode === "register" ? "active" : "",
98
+ onClick: () => {
99
+ setMode("register");
100
+ setError(null);
101
+ },
102
+ children: "Register"
103
+ })]
104
+ }),
105
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
106
+ className: "login-error",
107
+ children: error
108
+ }),
109
+ mode === "login" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
110
+ onSubmit: handleLogin,
111
+ children: [
112
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
113
+ htmlFor: "login-username",
114
+ children: "Username"
115
+ }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
117
+ id: "login-username",
118
+ type: "text",
119
+ value: username,
120
+ onChange: (e) => setUsername(e.target.value),
121
+ placeholder: "admin",
122
+ required: true,
123
+ autoComplete: "username"
124
+ }),
125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
126
+ htmlFor: "login-password",
127
+ children: "Password"
128
+ }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
130
+ id: "login-password",
131
+ type: "password",
132
+ value: password,
133
+ onChange: (e) => setPassword(e.target.value),
134
+ placeholder: "password",
135
+ required: true,
136
+ autoComplete: "current-password"
137
+ }),
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
139
+ type: "submit",
140
+ className: "btn login-btn",
141
+ disabled: loading,
142
+ children: loading ? "Signing in..." : "Sign In"
143
+ })
144
+ ]
145
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
146
+ onSubmit: handleRegister,
147
+ children: [
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
149
+ htmlFor: "reg-username",
150
+ children: "Username"
151
+ }),
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
153
+ id: "reg-username",
154
+ type: "text",
155
+ value: username,
156
+ onChange: (e) => setUsername(e.target.value),
157
+ placeholder: "username",
158
+ required: true,
159
+ autoComplete: "username"
160
+ }),
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
162
+ htmlFor: "reg-name",
163
+ children: "Display Name"
164
+ }),
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
166
+ id: "reg-name",
167
+ type: "text",
168
+ value: name,
169
+ onChange: (e) => setName(e.target.value),
170
+ placeholder: "John Doe",
171
+ autoComplete: "name"
172
+ }),
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
174
+ htmlFor: "reg-email",
175
+ children: "Email"
176
+ }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
178
+ id: "reg-email",
179
+ type: "email",
180
+ value: email,
181
+ onChange: (e) => setEmail(e.target.value),
182
+ placeholder: "user@example.com",
183
+ required: true,
184
+ autoComplete: "email"
185
+ }),
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
187
+ htmlFor: "reg-password",
188
+ children: "Password"
189
+ }),
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
191
+ id: "reg-password",
192
+ type: "password",
193
+ value: password,
194
+ onChange: (e) => setPassword(e.target.value),
195
+ placeholder: "password",
196
+ required: true,
197
+ minLength: 4,
198
+ autoComplete: "new-password"
199
+ }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
201
+ type: "submit",
202
+ className: "btn login-btn",
203
+ disabled: loading,
204
+ children: loading ? "Creating account..." : "Create Account"
205
+ })
206
+ ]
207
+ }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
209
+ className: "login-divider",
210
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "or" })
211
+ }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
213
+ type: "button",
214
+ className: "btn btn-outline login-btn",
215
+ onClick: handleAnonymousLogin,
216
+ disabled: loading,
217
+ children: "Continue as Guest"
218
+ }),
219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
220
+ className: "login-hint",
221
+ children: "Guests can view translations but cannot make changes."
222
+ })
223
+ ]
224
+ })
225
+ });
226
+ }
227
+ //#endregion
228
+ export { LoginPage as component };