@sonicjs-cms/core 2.3.13 → 2.3.14

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 (49) hide show
  1. package/dist/{chunk-F56JKQTA.js → chunk-AVPUX57O.js} +3 -3
  2. package/dist/{chunk-F56JKQTA.js.map → chunk-AVPUX57O.js.map} +1 -1
  3. package/dist/{chunk-MF7DWI5P.cjs → chunk-AZLU3ROK.cjs} +4 -2
  4. package/dist/chunk-AZLU3ROK.cjs.map +1 -0
  5. package/dist/{chunk-VMEBHBYY.js → chunk-CAJOP354.js} +2 -2
  6. package/dist/{chunk-VMEBHBYY.js.map → chunk-CAJOP354.js.map} +1 -1
  7. package/dist/{chunk-ARLXQU2S.cjs → chunk-D4PJFFOV.cjs} +560 -465
  8. package/dist/chunk-D4PJFFOV.cjs.map +1 -0
  9. package/dist/{chunk-W4CE7XME.cjs → chunk-ETS5XSAG.cjs} +2 -2
  10. package/dist/{chunk-W4CE7XME.cjs.map → chunk-ETS5XSAG.cjs.map} +1 -1
  11. package/dist/{chunk-2NTBZ2Y7.js → chunk-H34L445M.js} +3 -3
  12. package/dist/{chunk-2NTBZ2Y7.js.map → chunk-H34L445M.js.map} +1 -1
  13. package/dist/{chunk-FHCN7KR2.js → chunk-SKPETEM5.js} +3 -3
  14. package/dist/{chunk-FHCN7KR2.js.map → chunk-SKPETEM5.js.map} +1 -1
  15. package/dist/{chunk-W2IAEG4W.cjs → chunk-SZE3XVET.cjs} +3 -3
  16. package/dist/{chunk-W2IAEG4W.cjs.map → chunk-SZE3XVET.cjs.map} +1 -1
  17. package/dist/{chunk-RP66TPEJ.js → chunk-T4XRPNX2.js} +415 -320
  18. package/dist/chunk-T4XRPNX2.js.map +1 -0
  19. package/dist/{chunk-DN45O5XV.js → chunk-V5LBQN3I.js} +4 -2
  20. package/dist/chunk-V5LBQN3I.js.map +1 -0
  21. package/dist/{chunk-5NCBFP37.cjs → chunk-XWPGIFS7.cjs} +4 -4
  22. package/dist/{chunk-5NCBFP37.cjs.map → chunk-XWPGIFS7.cjs.map} +1 -1
  23. package/dist/{chunk-XR6XACXJ.cjs → chunk-YIXSSJWD.cjs} +5 -5
  24. package/dist/{chunk-XR6XACXJ.cjs.map → chunk-YIXSSJWD.cjs.map} +1 -1
  25. package/dist/index.cjs +1080 -87
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +1003 -10
  28. package/dist/index.js.map +1 -1
  29. package/dist/middleware.cjs +23 -23
  30. package/dist/middleware.js +2 -2
  31. package/dist/migrations-3A53GREK.cjs +13 -0
  32. package/dist/{migrations-ZAYXZXON.cjs.map → migrations-3A53GREK.cjs.map} +1 -1
  33. package/dist/migrations-WF6VIVU2.js +4 -0
  34. package/dist/{migrations-43GTELB5.js.map → migrations-WF6VIVU2.js.map} +1 -1
  35. package/dist/routes.cjs +25 -25
  36. package/dist/routes.js +5 -5
  37. package/dist/services.cjs +2 -2
  38. package/dist/services.js +1 -1
  39. package/dist/templates.cjs +17 -17
  40. package/dist/templates.js +2 -2
  41. package/dist/utils.cjs +11 -11
  42. package/dist/utils.js +1 -1
  43. package/package.json +1 -1
  44. package/dist/chunk-ARLXQU2S.cjs.map +0 -1
  45. package/dist/chunk-DN45O5XV.js.map +0 -1
  46. package/dist/chunk-MF7DWI5P.cjs.map +0 -1
  47. package/dist/chunk-RP66TPEJ.js.map +0 -1
  48. package/dist/migrations-43GTELB5.js +0 -4
  49. package/dist/migrations-ZAYXZXON.cjs +0 -13
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var chunk7FOAMNTI_cjs = require('./chunk-7FOAMNTI.cjs');
4
- var chunk5NCBFP37_cjs = require('./chunk-5NCBFP37.cjs');
4
+ var chunkXWPGIFS7_cjs = require('./chunk-XWPGIFS7.cjs');
5
5
  var chunkILZ3DP4I_cjs = require('./chunk-ILZ3DP4I.cjs');
6
- var chunkW4CE7XME_cjs = require('./chunk-W4CE7XME.cjs');
7
- var chunkMF7DWI5P_cjs = require('./chunk-MF7DWI5P.cjs');
8
- var chunkW2IAEG4W_cjs = require('./chunk-W2IAEG4W.cjs');
6
+ var chunkETS5XSAG_cjs = require('./chunk-ETS5XSAG.cjs');
7
+ var chunkAZLU3ROK_cjs = require('./chunk-AZLU3ROK.cjs');
8
+ var chunkSZE3XVET_cjs = require('./chunk-SZE3XVET.cjs');
9
9
  var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
10
10
  var hono = require('hono');
11
11
  var cors = require('hono/cors');
@@ -44,7 +44,7 @@ apiContentCrudRoutes.get("/:id", async (c) => {
44
44
  }, 500);
45
45
  }
46
46
  });
47
- apiContentCrudRoutes.post("/", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
47
+ apiContentCrudRoutes.post("/", chunkXWPGIFS7_cjs.requireAuth(), async (c) => {
48
48
  try {
49
49
  const db = c.env.DB;
50
50
  const user = c.get("user");
@@ -110,7 +110,7 @@ apiContentCrudRoutes.post("/", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
110
110
  }, 500);
111
111
  }
112
112
  });
113
- apiContentCrudRoutes.put("/:id", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
113
+ apiContentCrudRoutes.put("/:id", chunkXWPGIFS7_cjs.requireAuth(), async (c) => {
114
114
  try {
115
115
  const id = c.req.param("id");
116
116
  const db = c.env.DB;
@@ -174,7 +174,7 @@ apiContentCrudRoutes.put("/:id", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
174
174
  }, 500);
175
175
  }
176
176
  });
177
- apiContentCrudRoutes.delete("/:id", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
177
+ apiContentCrudRoutes.delete("/:id", chunkXWPGIFS7_cjs.requireAuth(), async (c) => {
178
178
  try {
179
179
  const id = c.req.param("id");
180
180
  const db = c.env.DB;
@@ -210,7 +210,7 @@ apiRoutes.use("*", async (c, next) => {
210
210
  c.header("X-Response-Time", `${totalTime}ms`);
211
211
  });
212
212
  apiRoutes.use("*", async (c, next) => {
213
- const cacheEnabled = await chunk5NCBFP37_cjs.isPluginActive(c.env.DB, "core-cache");
213
+ const cacheEnabled = await chunkXWPGIFS7_cjs.isPluginActive(c.env.DB, "core-cache");
214
214
  c.set("cacheEnabled", cacheEnabled);
215
215
  await next();
216
216
  });
@@ -335,12 +335,12 @@ apiRoutes.get("/content", async (c) => {
335
335
  });
336
336
  }
337
337
  }
338
- const filter = chunkW2IAEG4W_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
338
+ const filter = chunkSZE3XVET_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
339
339
  if (!filter.limit) {
340
340
  filter.limit = 50;
341
341
  }
342
342
  filter.limit = Math.min(filter.limit, 1e3);
343
- const builder3 = new chunkW2IAEG4W_cjs.QueryFilterBuilder();
343
+ const builder3 = new chunkSZE3XVET_cjs.QueryFilterBuilder();
344
344
  const queryResult = builder3.build("content", filter);
345
345
  if (queryResult.errors.length > 0) {
346
346
  return c.json({
@@ -427,7 +427,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
427
427
  if (!collectionResult) {
428
428
  return c.json({ error: "Collection not found" }, 404);
429
429
  }
430
- const filter = chunkW2IAEG4W_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
430
+ const filter = chunkSZE3XVET_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
431
431
  if (!filter.where) {
432
432
  filter.where = { and: [] };
433
433
  }
@@ -443,7 +443,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
443
443
  filter.limit = 50;
444
444
  }
445
445
  filter.limit = Math.min(filter.limit, 1e3);
446
- const builder3 = new chunkW2IAEG4W_cjs.QueryFilterBuilder();
446
+ const builder3 = new chunkSZE3XVET_cjs.QueryFilterBuilder();
447
447
  const queryResult = builder3.build("content", filter);
448
448
  if (queryResult.errors.length > 0) {
449
449
  return c.json({
@@ -568,7 +568,7 @@ var fileValidationSchema = zod.z.object({
568
568
  // 50MB max
569
569
  });
570
570
  var apiMediaRoutes = new hono.Hono();
571
- apiMediaRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
571
+ apiMediaRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
572
572
  apiMediaRoutes.post("/upload", async (c) => {
573
573
  try {
574
574
  const user = c.get("user");
@@ -1312,8 +1312,8 @@ apiSystemRoutes.get("/env", (c) => {
1312
1312
  });
1313
1313
  var api_system_default = apiSystemRoutes;
1314
1314
  var adminApiRoutes = new hono.Hono();
1315
- adminApiRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
1316
- adminApiRoutes.use("*", chunk5NCBFP37_cjs.requireRole(["admin", "editor"]));
1315
+ adminApiRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
1316
+ adminApiRoutes.use("*", chunkXWPGIFS7_cjs.requireRole(["admin", "editor"]));
1317
1317
  adminApiRoutes.get("/stats", async (c) => {
1318
1318
  try {
1319
1319
  const db = c.env.DB;
@@ -1722,7 +1722,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
1722
1722
  });
1723
1723
  adminApiRoutes.get("/migrations/status", async (c) => {
1724
1724
  try {
1725
- const { MigrationService: MigrationService2 } = await import('./migrations-ZAYXZXON.cjs');
1725
+ const { MigrationService: MigrationService2 } = await import('./migrations-3A53GREK.cjs');
1726
1726
  const db = c.env.DB;
1727
1727
  const migrationService = new MigrationService2(db);
1728
1728
  const status = await migrationService.getMigrationStatus();
@@ -1747,7 +1747,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
1747
1747
  error: "Unauthorized. Admin access required."
1748
1748
  }, 403);
1749
1749
  }
1750
- const { MigrationService: MigrationService2 } = await import('./migrations-ZAYXZXON.cjs');
1750
+ const { MigrationService: MigrationService2 } = await import('./migrations-3A53GREK.cjs');
1751
1751
  const db = c.env.DB;
1752
1752
  const migrationService = new MigrationService2(db);
1753
1753
  const result = await migrationService.runPendingMigrations();
@@ -1766,7 +1766,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
1766
1766
  });
1767
1767
  adminApiRoutes.get("/migrations/validate", async (c) => {
1768
1768
  try {
1769
- const { MigrationService: MigrationService2 } = await import('./migrations-ZAYXZXON.cjs');
1769
+ const { MigrationService: MigrationService2 } = await import('./migrations-3A53GREK.cjs');
1770
1770
  const db = c.env.DB;
1771
1771
  const migrationService = new MigrationService2(db);
1772
1772
  const validation = await migrationService.validateSchema();
@@ -1841,8 +1841,8 @@ function renderLoginPage(data, demoLoginActive = false) {
1841
1841
  <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
1842
1842
  <div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
1843
1843
  <!-- Alerts -->
1844
- ${data.error ? `<div class="mb-6">${chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
1845
- ${data.message ? `<div class="mb-6">${chunkMF7DWI5P_cjs.renderAlert({ type: "success", message: data.message })}</div>` : ""}
1844
+ ${data.error ? `<div class="mb-6">${chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
1845
+ ${data.message ? `<div class="mb-6">${chunkAZLU3ROK_cjs.renderAlert({ type: "success", message: data.message })}</div>` : ""}
1846
1846
 
1847
1847
  <!-- Form Response (HTMX target) -->
1848
1848
  <div id="form-response" class="mb-6"></div>
@@ -2006,7 +2006,7 @@ function renderRegisterPage(data) {
2006
2006
  <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
2007
2007
  <div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
2008
2008
  <!-- Alerts -->
2009
- ${data.error ? `<div class="mb-6">${chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
2009
+ ${data.error ? `<div class="mb-6">${chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
2010
2010
 
2011
2011
  <!-- Form -->
2012
2012
  <form
@@ -2119,6 +2119,27 @@ function renderRegisterPage(data) {
2119
2119
  </html>
2120
2120
  `;
2121
2121
  }
2122
+ async function isRegistrationEnabled(db) {
2123
+ try {
2124
+ const plugin = await db.prepare("SELECT settings FROM plugins WHERE id = ?").bind("core-auth").first();
2125
+ if (plugin?.settings) {
2126
+ const settings = JSON.parse(plugin.settings);
2127
+ const enabled = settings?.registration?.enabled;
2128
+ return enabled !== false && enabled !== 0;
2129
+ }
2130
+ return true;
2131
+ } catch {
2132
+ return true;
2133
+ }
2134
+ }
2135
+ async function isFirstUserRegistration(db) {
2136
+ try {
2137
+ const result = await db.prepare("SELECT COUNT(*) as count FROM users").first();
2138
+ return result?.count === 0;
2139
+ } catch {
2140
+ return false;
2141
+ }
2142
+ }
2122
2143
  var baseRegistrationSchema = zod.z.object({
2123
2144
  email: zod.z.string().email("Valid email is required"),
2124
2145
  password: zod.z.string().min(8, "Password must be at least 8 characters"),
@@ -2170,7 +2191,15 @@ authRoutes.get("/login", async (c) => {
2170
2191
  }
2171
2192
  return c.html(renderLoginPage(pageData, demoLoginActive));
2172
2193
  });
2173
- authRoutes.get("/register", (c) => {
2194
+ authRoutes.get("/register", async (c) => {
2195
+ const db = c.env.DB;
2196
+ const isFirstUser = await isFirstUserRegistration(db);
2197
+ if (!isFirstUser) {
2198
+ const registrationEnabled = await isRegistrationEnabled(db);
2199
+ if (!registrationEnabled) {
2200
+ return c.redirect("/auth/login?error=Registration is currently disabled");
2201
+ }
2202
+ }
2174
2203
  const error = c.req.query("error");
2175
2204
  const pageData = {
2176
2205
  error: error || void 0
@@ -2186,6 +2215,13 @@ authRoutes.post(
2186
2215
  async (c) => {
2187
2216
  try {
2188
2217
  const db = c.env.DB;
2218
+ const isFirstUser = await isFirstUserRegistration(db);
2219
+ if (!isFirstUser) {
2220
+ const registrationEnabled = await isRegistrationEnabled(db);
2221
+ if (!registrationEnabled) {
2222
+ return c.json({ error: "Registration is currently disabled" }, 403);
2223
+ }
2224
+ }
2189
2225
  let requestData;
2190
2226
  try {
2191
2227
  requestData = await c.req.json();
@@ -2212,7 +2248,7 @@ authRoutes.post(
2212
2248
  if (existingUser) {
2213
2249
  return c.json({ error: "User with this email or username already exists" }, 400);
2214
2250
  }
2215
- const passwordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword(password);
2251
+ const passwordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword(password);
2216
2252
  const userId = crypto.randomUUID();
2217
2253
  const now = /* @__PURE__ */ new Date();
2218
2254
  await db.prepare(`
@@ -2232,7 +2268,7 @@ authRoutes.post(
2232
2268
  now.getTime(),
2233
2269
  now.getTime()
2234
2270
  ).run();
2235
- const token = await chunk5NCBFP37_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer");
2271
+ const token = await chunkXWPGIFS7_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer");
2236
2272
  cookie.setCookie(c, "auth_token", token, {
2237
2273
  httpOnly: true,
2238
2274
  secure: true,
@@ -2285,11 +2321,11 @@ authRoutes.post("/login", async (c) => {
2285
2321
  if (!user) {
2286
2322
  return c.json({ error: "Invalid email or password" }, 401);
2287
2323
  }
2288
- const isValidPassword = await chunk5NCBFP37_cjs.AuthManager.verifyPassword(password, user.password_hash);
2324
+ const isValidPassword = await chunkXWPGIFS7_cjs.AuthManager.verifyPassword(password, user.password_hash);
2289
2325
  if (!isValidPassword) {
2290
2326
  return c.json({ error: "Invalid email or password" }, 401);
2291
2327
  }
2292
- const token = await chunk5NCBFP37_cjs.AuthManager.generateToken(user.id, user.email, user.role);
2328
+ const token = await chunkXWPGIFS7_cjs.AuthManager.generateToken(user.id, user.email, user.role);
2293
2329
  cookie.setCookie(c, "auth_token", token, {
2294
2330
  httpOnly: true,
2295
2331
  secure: true,
@@ -2338,7 +2374,7 @@ authRoutes.get("/logout", (c) => {
2338
2374
  });
2339
2375
  return c.redirect("/auth/login?message=You have been logged out successfully");
2340
2376
  });
2341
- authRoutes.get("/me", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
2377
+ authRoutes.get("/me", chunkXWPGIFS7_cjs.requireAuth(), async (c) => {
2342
2378
  try {
2343
2379
  const user = c.get("user");
2344
2380
  if (!user) {
@@ -2355,13 +2391,13 @@ authRoutes.get("/me", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
2355
2391
  return c.json({ error: "Failed to get user" }, 500);
2356
2392
  }
2357
2393
  });
2358
- authRoutes.post("/refresh", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
2394
+ authRoutes.post("/refresh", chunkXWPGIFS7_cjs.requireAuth(), async (c) => {
2359
2395
  try {
2360
2396
  const user = c.get("user");
2361
2397
  if (!user) {
2362
2398
  return c.json({ error: "Not authenticated" }, 401);
2363
2399
  }
2364
- const token = await chunk5NCBFP37_cjs.AuthManager.generateToken(user.userId, user.email, user.role);
2400
+ const token = await chunkXWPGIFS7_cjs.AuthManager.generateToken(user.userId, user.email, user.role);
2365
2401
  cookie.setCookie(c, "auth_token", token, {
2366
2402
  httpOnly: true,
2367
2403
  secure: true,
@@ -2378,6 +2414,17 @@ authRoutes.post("/refresh", chunk5NCBFP37_cjs.requireAuth(), async (c) => {
2378
2414
  authRoutes.post("/register/form", async (c) => {
2379
2415
  try {
2380
2416
  const db = c.env.DB;
2417
+ const isFirstUser = await isFirstUserRegistration(db);
2418
+ if (!isFirstUser) {
2419
+ const registrationEnabled = await isRegistrationEnabled(db);
2420
+ if (!registrationEnabled) {
2421
+ return c.html(html.html`
2422
+ <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
2423
+ Registration is currently disabled. Please contact an administrator.
2424
+ </div>
2425
+ `);
2426
+ }
2427
+ }
2381
2428
  const formData = await c.req.formData();
2382
2429
  const requestData = {
2383
2430
  email: formData.get("email"),
@@ -2410,7 +2457,8 @@ authRoutes.post("/register/form", async (c) => {
2410
2457
  </div>
2411
2458
  `);
2412
2459
  }
2413
- const passwordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword(password);
2460
+ const passwordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword(password);
2461
+ const role = isFirstUser ? "admin" : "viewer";
2414
2462
  const userId = crypto.randomUUID();
2415
2463
  const now = /* @__PURE__ */ new Date();
2416
2464
  await db.prepare(`
@@ -2423,14 +2471,13 @@ authRoutes.post("/register/form", async (c) => {
2423
2471
  firstName,
2424
2472
  lastName,
2425
2473
  passwordHash,
2426
- "admin",
2427
- // First user gets admin role
2474
+ role,
2428
2475
  1,
2429
2476
  // is_active
2430
2477
  now.getTime(),
2431
2478
  now.getTime()
2432
2479
  ).run();
2433
- const token = await chunk5NCBFP37_cjs.AuthManager.generateToken(userId, normalizedEmail, "admin");
2480
+ const token = await chunkXWPGIFS7_cjs.AuthManager.generateToken(userId, normalizedEmail, role);
2434
2481
  cookie.setCookie(c, "auth_token", token, {
2435
2482
  httpOnly: true,
2436
2483
  secure: false,
@@ -2439,12 +2486,13 @@ authRoutes.post("/register/form", async (c) => {
2439
2486
  maxAge: 60 * 60 * 24
2440
2487
  // 24 hours
2441
2488
  });
2489
+ const redirectUrl = role === "admin" ? "/admin/dashboard" : "/admin/dashboard";
2442
2490
  return c.html(html.html`
2443
2491
  <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
2444
- Account created successfully! Redirecting to admin dashboard...
2492
+ Account created successfully! Redirecting...
2445
2493
  <script>
2446
2494
  setTimeout(() => {
2447
- window.location.href = '/admin/dashboard';
2495
+ window.location.href = '${redirectUrl}';
2448
2496
  }, 2000);
2449
2497
  </script>
2450
2498
  </div>
@@ -2481,7 +2529,7 @@ authRoutes.post("/login/form", async (c) => {
2481
2529
  </div>
2482
2530
  `);
2483
2531
  }
2484
- const isValidPassword = await chunk5NCBFP37_cjs.AuthManager.verifyPassword(password, user.password_hash);
2532
+ const isValidPassword = await chunkXWPGIFS7_cjs.AuthManager.verifyPassword(password, user.password_hash);
2485
2533
  if (!isValidPassword) {
2486
2534
  return c.html(html.html`
2487
2535
  <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
@@ -2489,7 +2537,7 @@ authRoutes.post("/login/form", async (c) => {
2489
2537
  </div>
2490
2538
  `);
2491
2539
  }
2492
- const token = await chunk5NCBFP37_cjs.AuthManager.generateToken(user.id, user.email, user.role);
2540
+ const token = await chunkXWPGIFS7_cjs.AuthManager.generateToken(user.id, user.email, user.role);
2493
2541
  cookie.setCookie(c, "auth_token", token, {
2494
2542
  httpOnly: true,
2495
2543
  secure: false,
@@ -2548,7 +2596,7 @@ authRoutes.post("/seed-admin", async (c) => {
2548
2596
  `).run();
2549
2597
  const existingAdmin = await db.prepare("SELECT id FROM users WHERE email = ? OR username = ?").bind("admin@sonicjs.com", "admin").first();
2550
2598
  if (existingAdmin) {
2551
- const passwordHash2 = await chunk5NCBFP37_cjs.AuthManager.hashPassword("sonicjs!");
2599
+ const passwordHash2 = await chunkXWPGIFS7_cjs.AuthManager.hashPassword("sonicjs!");
2552
2600
  await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(passwordHash2, Date.now(), existingAdmin.id).run();
2553
2601
  return c.json({
2554
2602
  message: "Admin user already exists (password updated)",
@@ -2560,7 +2608,7 @@ authRoutes.post("/seed-admin", async (c) => {
2560
2608
  }
2561
2609
  });
2562
2610
  }
2563
- const passwordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword("sonicjs!");
2611
+ const passwordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword("sonicjs!");
2564
2612
  const userId = "admin-user-id";
2565
2613
  const now = Date.now();
2566
2614
  const adminEmail = "admin@sonicjs.com".toLowerCase();
@@ -2780,7 +2828,7 @@ authRoutes.post("/accept-invitation", async (c) => {
2780
2828
  if (existingUsername) {
2781
2829
  return c.json({ error: "Username is already taken" }, 400);
2782
2830
  }
2783
- const passwordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword(password);
2831
+ const passwordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword(password);
2784
2832
  const updateStmt = db.prepare(`
2785
2833
  UPDATE users SET
2786
2834
  username = ?,
@@ -2799,7 +2847,7 @@ authRoutes.post("/accept-invitation", async (c) => {
2799
2847
  Date.now(),
2800
2848
  invitedUser.id
2801
2849
  ).run();
2802
- const authToken = await chunk5NCBFP37_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role);
2850
+ const authToken = await chunkXWPGIFS7_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role);
2803
2851
  cookie.setCookie(c, "auth_token", authToken, {
2804
2852
  httpOnly: true,
2805
2853
  secure: true,
@@ -3029,7 +3077,7 @@ authRoutes.post("/reset-password", async (c) => {
3029
3077
  if (Date.now() > user.password_reset_expires) {
3030
3078
  return c.json({ error: "Reset token has expired" }, 400);
3031
3079
  }
3032
- const newPasswordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword(password);
3080
+ const newPasswordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword(password);
3033
3081
  try {
3034
3082
  const historyStmt = db.prepare(`
3035
3083
  INSERT INTO password_history (id, user_id, password_hash, created_at)
@@ -3287,7 +3335,7 @@ app.post("/test-cleanup/content", async (c) => {
3287
3335
  var test_cleanup_default = app;
3288
3336
 
3289
3337
  // src/templates/pages/admin-content-form.template.ts
3290
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
3338
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
3291
3339
 
3292
3340
  // src/templates/components/dynamic-field.template.ts
3293
3341
  function renderDynamicField(field, options = {}) {
@@ -4428,6 +4476,13 @@ function getMDXEditorInitScript(config) {
4428
4476
  // Store reference to editor instance
4429
4477
  textarea.easyMDEInstance = easyMDE;
4430
4478
 
4479
+ // Sync changes back to textarea
4480
+ easyMDE.codemirror.on("change", () => {
4481
+ textarea.value = easyMDE.value();
4482
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
4483
+ textarea.dispatchEvent(new Event("change", { bubbles: true }));
4484
+ });
4485
+
4431
4486
  console.log('EasyMDE initialized for field:', textarea.id || textarea.name);
4432
4487
  } catch (error) {
4433
4488
  console.error('Error initializing EasyMDE:', error);
@@ -4526,8 +4581,8 @@ function renderContentFormPage(data) {
4526
4581
  <!-- Form Content -->
4527
4582
  <div class="px-6 py-6">
4528
4583
  <div id="form-messages">
4529
- ${data.error ? chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
4530
- ${data.success ? chunkMF7DWI5P_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
4584
+ ${data.error ? chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
4585
+ ${data.success ? chunkAZLU3ROK_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
4531
4586
  </div>
4532
4587
 
4533
4588
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
@@ -4762,7 +4817,7 @@ function renderContentFormPage(data) {
4762
4817
  </div>
4763
4818
 
4764
4819
  <!-- Confirmation Dialogs -->
4765
- ${chunkMF7DWI5P_cjs.renderConfirmationDialog({
4820
+ ${chunkAZLU3ROK_cjs.renderConfirmationDialog({
4766
4821
  id: "duplicate-content-confirm",
4767
4822
  title: "Duplicate Content",
4768
4823
  message: "Create a copy of this content?",
@@ -4773,7 +4828,7 @@ function renderContentFormPage(data) {
4773
4828
  onConfirm: "performDuplicateContent()"
4774
4829
  })}
4775
4830
 
4776
- ${chunkMF7DWI5P_cjs.renderConfirmationDialog({
4831
+ ${chunkAZLU3ROK_cjs.renderConfirmationDialog({
4777
4832
  id: "delete-content-confirm",
4778
4833
  title: "Delete Content",
4779
4834
  message: "Are you sure you want to delete this content? This action cannot be undone.",
@@ -4784,7 +4839,7 @@ function renderContentFormPage(data) {
4784
4839
  onConfirm: `performDeleteContent('${data.id}')`
4785
4840
  })}
4786
4841
 
4787
- ${chunkMF7DWI5P_cjs.getConfirmationDialogScript()}
4842
+ ${chunkAZLU3ROK_cjs.getConfirmationDialogScript()}
4788
4843
 
4789
4844
  ${data.tinymceEnabled ? getTinyMCEScript(data.tinymceSettings?.apiKey) : "<!-- TinyMCE plugin not active -->"}
4790
4845
 
@@ -5089,11 +5144,11 @@ function renderContentFormPage(data) {
5089
5144
  content: pageContent,
5090
5145
  version: data.version
5091
5146
  };
5092
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
5147
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
5093
5148
  }
5094
5149
 
5095
5150
  // src/templates/pages/admin-content-list.template.ts
5096
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
5151
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
5097
5152
  function renderContentListPage(data) {
5098
5153
  const urlParams = new URLSearchParams();
5099
5154
  if (data.modelName && data.modelName !== "all") urlParams.set("model", data.modelName);
@@ -5498,8 +5553,8 @@ function renderContentListPage(data) {
5498
5553
 
5499
5554
  <!-- Content List -->
5500
5555
  <div id="content-list">
5501
- ${chunkMF7DWI5P_cjs.renderTable(tableData)}
5502
- ${chunkMF7DWI5P_cjs.renderPagination(paginationData)}
5556
+ ${chunkAZLU3ROK_cjs.renderTable(tableData)}
5557
+ ${chunkAZLU3ROK_cjs.renderPagination(paginationData)}
5503
5558
  </div>
5504
5559
 
5505
5560
  </div>
@@ -5708,7 +5763,7 @@ function renderContentListPage(data) {
5708
5763
  </script>
5709
5764
 
5710
5765
  <!-- Confirmation Dialog for Bulk Actions -->
5711
- ${chunkMF7DWI5P_cjs.renderConfirmationDialog({
5766
+ ${chunkAZLU3ROK_cjs.renderConfirmationDialog({
5712
5767
  id: "bulk-action-confirm",
5713
5768
  title: "Confirm Bulk Action",
5714
5769
  message: "Are you sure you want to perform this action? This operation will affect multiple items.",
@@ -5720,7 +5775,7 @@ function renderContentListPage(data) {
5720
5775
  })}
5721
5776
 
5722
5777
  <!-- Confirmation Dialog Script -->
5723
- ${chunkMF7DWI5P_cjs.getConfirmationDialogScript()}
5778
+ ${chunkAZLU3ROK_cjs.getConfirmationDialogScript()}
5724
5779
  `;
5725
5780
  const layoutData = {
5726
5781
  title: "Content Management",
@@ -5730,7 +5785,7 @@ function renderContentListPage(data) {
5730
5785
  version: data.version,
5731
5786
  content: pageContent
5732
5787
  };
5733
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
5788
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
5734
5789
  }
5735
5790
 
5736
5791
  // src/templates/components/version-history.template.ts
@@ -5924,7 +5979,7 @@ async function isPluginActive2(db, pluginId) {
5924
5979
 
5925
5980
  // src/routes/admin-content.ts
5926
5981
  var adminContentRoutes = new hono.Hono();
5927
- adminContentRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
5982
+ adminContentRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
5928
5983
  async function getCollectionFields(db, collectionId) {
5929
5984
  const cache = chunk7FOAMNTI_cjs.getCacheService(chunk7FOAMNTI_cjs.CACHE_CONFIGS.collection);
5930
5985
  return cache.getOrSet(
@@ -7133,7 +7188,7 @@ ${JSON.stringify(data, null, 2)}
7133
7188
  var admin_content_default = adminContentRoutes;
7134
7189
 
7135
7190
  // src/templates/pages/admin-profile.template.ts
7136
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
7191
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
7137
7192
  function renderAvatarImage(avatarUrl, firstName, lastName) {
7138
7193
  return `<div id="avatar-image-container" class="w-24 h-24 rounded-full mx-auto mb-4 overflow-hidden bg-gradient-to-br from-cyan-400 to-purple-400 flex items-center justify-center ring-4 ring-zinc-950/5 dark:ring-white/10">
7139
7194
  ${avatarUrl ? `<img src="${avatarUrl}" alt="Profile picture" class="w-full h-full object-cover">` : `<span class="text-2xl font-bold text-white">${firstName.charAt(0)}${lastName.charAt(0)}</span>`}
@@ -7153,8 +7208,8 @@ function renderProfilePage(data) {
7153
7208
  </div>
7154
7209
 
7155
7210
  <!-- Alert Messages -->
7156
- ${data.error ? chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
7157
- ${data.success ? chunkMF7DWI5P_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
7211
+ ${data.error ? chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
7212
+ ${data.success ? chunkAZLU3ROK_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
7158
7213
 
7159
7214
  <!-- Profile Form -->
7160
7215
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
@@ -7541,7 +7596,7 @@ function renderProfilePage(data) {
7541
7596
  version: data.version,
7542
7597
  content: pageContent
7543
7598
  };
7544
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
7599
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
7545
7600
  }
7546
7601
 
7547
7602
  // src/templates/components/alert.template.ts
@@ -7824,7 +7879,7 @@ function renderActivityLogsPage(data) {
7824
7879
  user: data.user,
7825
7880
  content: pageContent
7826
7881
  };
7827
- return chunkMF7DWI5P_cjs.renderAdminLayout(layoutData);
7882
+ return chunkAZLU3ROK_cjs.renderAdminLayout(layoutData);
7828
7883
  }
7829
7884
  function getActionBadgeClass(action) {
7830
7885
  if (action.includes("login") || action.includes("logout")) {
@@ -7844,7 +7899,7 @@ function formatAction(action) {
7844
7899
  }
7845
7900
 
7846
7901
  // src/templates/pages/admin-user-edit.template.ts
7847
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
7902
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
7848
7903
 
7849
7904
  // src/templates/components/confirmation-dialog.template.ts
7850
7905
  function renderConfirmationDialog2(options) {
@@ -7965,8 +8020,8 @@ function renderUserEditPage(data) {
7965
8020
 
7966
8021
  <!-- Alert Messages -->
7967
8022
  <div id="form-messages">
7968
- ${data.error ? chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
7969
- ${data.success ? chunkMF7DWI5P_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
8023
+ ${data.error ? chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
8024
+ ${data.success ? chunkAZLU3ROK_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
7970
8025
  </div>
7971
8026
 
7972
8027
  <!-- User Edit Form -->
@@ -7985,7 +8040,7 @@ function renderUserEditPage(data) {
7985
8040
  <input
7986
8041
  type="text"
7987
8042
  name="first_name"
7988
- value="${chunkW2IAEG4W_cjs.escapeHtml(data.userToEdit.firstName || "")}"
8043
+ value="${chunkSZE3XVET_cjs.escapeHtml(data.userToEdit.firstName || "")}"
7989
8044
  required
7990
8045
  class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
7991
8046
  />
@@ -7996,7 +8051,7 @@ function renderUserEditPage(data) {
7996
8051
  <input
7997
8052
  type="text"
7998
8053
  name="last_name"
7999
- value="${chunkW2IAEG4W_cjs.escapeHtml(data.userToEdit.lastName || "")}"
8054
+ value="${chunkSZE3XVET_cjs.escapeHtml(data.userToEdit.lastName || "")}"
8000
8055
  required
8001
8056
  class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
8002
8057
  />
@@ -8007,7 +8062,7 @@ function renderUserEditPage(data) {
8007
8062
  <input
8008
8063
  type="text"
8009
8064
  name="username"
8010
- value="${chunkW2IAEG4W_cjs.escapeHtml(data.userToEdit.username || "")}"
8065
+ value="${chunkSZE3XVET_cjs.escapeHtml(data.userToEdit.username || "")}"
8011
8066
  required
8012
8067
  class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
8013
8068
  />
@@ -8018,7 +8073,7 @@ function renderUserEditPage(data) {
8018
8073
  <input
8019
8074
  type="email"
8020
8075
  name="email"
8021
- value="${chunkW2IAEG4W_cjs.escapeHtml(data.userToEdit.email || "")}"
8076
+ value="${chunkSZE3XVET_cjs.escapeHtml(data.userToEdit.email || "")}"
8022
8077
  required
8023
8078
  class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
8024
8079
  />
@@ -8029,7 +8084,7 @@ function renderUserEditPage(data) {
8029
8084
  <input
8030
8085
  type="tel"
8031
8086
  name="phone"
8032
- value="${chunkW2IAEG4W_cjs.escapeHtml(data.userToEdit.phone || "")}"
8087
+ value="${chunkSZE3XVET_cjs.escapeHtml(data.userToEdit.phone || "")}"
8033
8088
  class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
8034
8089
  />
8035
8090
  </div>
@@ -8043,7 +8098,7 @@ function renderUserEditPage(data) {
8043
8098
  class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-500/30 dark:outline-zinc-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400 sm:text-sm/6"
8044
8099
  >
8045
8100
  ${data.roles.map((role) => `
8046
- <option value="${chunkW2IAEG4W_cjs.escapeHtml(role.value)}" ${data.userToEdit.role === role.value ? "selected" : ""}>${chunkW2IAEG4W_cjs.escapeHtml(role.label)}</option>
8101
+ <option value="${chunkSZE3XVET_cjs.escapeHtml(role.value)}" ${data.userToEdit.role === role.value ? "selected" : ""}>${chunkSZE3XVET_cjs.escapeHtml(role.label)}</option>
8047
8102
  `).join("")}
8048
8103
  </select>
8049
8104
  <svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-600 dark:text-zinc-400 sm:size-4">
@@ -8059,7 +8114,7 @@ function renderUserEditPage(data) {
8059
8114
  name="bio"
8060
8115
  rows="3"
8061
8116
  class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
8062
- >${chunkW2IAEG4W_cjs.escapeHtml(data.userToEdit.bio || "")}</textarea>
8117
+ >${chunkSZE3XVET_cjs.escapeHtml(data.userToEdit.bio || "")}</textarea>
8063
8118
  </div>
8064
8119
  </div>
8065
8120
 
@@ -8259,11 +8314,11 @@ function renderUserEditPage(data) {
8259
8314
  user: data.user,
8260
8315
  content: pageContent
8261
8316
  };
8262
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
8317
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
8263
8318
  }
8264
8319
 
8265
8320
  // src/templates/pages/admin-user-new.template.ts
8266
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
8321
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
8267
8322
  function renderUserNewPage(data) {
8268
8323
  const pageContent = `
8269
8324
  <div>
@@ -8302,8 +8357,8 @@ function renderUserNewPage(data) {
8302
8357
 
8303
8358
  <!-- Alert Messages -->
8304
8359
  <div id="form-messages">
8305
- ${data.error ? chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
8306
- ${data.success ? chunkMF7DWI5P_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
8360
+ ${data.error ? chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
8361
+ ${data.success ? chunkAZLU3ROK_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
8307
8362
  </div>
8308
8363
 
8309
8364
  <!-- User New Form -->
@@ -8547,11 +8602,11 @@ function renderUserNewPage(data) {
8547
8602
  user: data.user,
8548
8603
  content: pageContent
8549
8604
  };
8550
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
8605
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
8551
8606
  }
8552
8607
 
8553
8608
  // src/templates/pages/admin-users-list.template.ts
8554
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
8609
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
8555
8610
  function renderUsersListPage(data) {
8556
8611
  const columns = [
8557
8612
  {
@@ -8702,8 +8757,8 @@ function renderUsersListPage(data) {
8702
8757
  </div>
8703
8758
 
8704
8759
  <!-- Alert Messages -->
8705
- ${data.error ? chunkMF7DWI5P_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
8706
- ${data.success ? chunkMF7DWI5P_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
8760
+ ${data.error ? chunkAZLU3ROK_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
8761
+ ${data.success ? chunkAZLU3ROK_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
8707
8762
 
8708
8763
  <!-- Stats -->
8709
8764
  <div class="mb-6">
@@ -8880,10 +8935,10 @@ function renderUsersListPage(data) {
8880
8935
  </div>
8881
8936
 
8882
8937
  <!-- Users Table -->
8883
- ${chunkMF7DWI5P_cjs.renderTable(tableData)}
8938
+ ${chunkAZLU3ROK_cjs.renderTable(tableData)}
8884
8939
 
8885
8940
  <!-- Pagination -->
8886
- ${data.pagination ? chunkMF7DWI5P_cjs.renderPagination(data.pagination) : ""}
8941
+ ${data.pagination ? chunkAZLU3ROK_cjs.renderPagination(data.pagination) : ""}
8887
8942
  </div>
8888
8943
 
8889
8944
  <script>
@@ -8954,12 +9009,12 @@ function renderUsersListPage(data) {
8954
9009
  version: data.version,
8955
9010
  content: pageContent
8956
9011
  };
8957
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
9012
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
8958
9013
  }
8959
9014
 
8960
9015
  // src/routes/admin-users.ts
8961
9016
  var userRoutes = new hono.Hono();
8962
- userRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
9017
+ userRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
8963
9018
  userRoutes.get("/", (c) => {
8964
9019
  return c.redirect("/admin/dashboard");
8965
9020
  });
@@ -9058,12 +9113,12 @@ userRoutes.put("/profile", async (c) => {
9058
9113
  const db = c.env.DB;
9059
9114
  try {
9060
9115
  const formData = await c.req.formData();
9061
- const firstName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("first_name")?.toString());
9062
- const lastName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("last_name")?.toString());
9063
- const username = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("username")?.toString());
9116
+ const firstName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("first_name")?.toString());
9117
+ const lastName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("last_name")?.toString());
9118
+ const username = chunkSZE3XVET_cjs.sanitizeInput(formData.get("username")?.toString());
9064
9119
  const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
9065
- const phone = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
9066
- const bio = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
9120
+ const phone = chunkSZE3XVET_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
9121
+ const bio = chunkSZE3XVET_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
9067
9122
  const timezone = formData.get("timezone")?.toString() || "UTC";
9068
9123
  const language = formData.get("language")?.toString() || "en";
9069
9124
  const emailNotifications = formData.get("email_notifications") === "1";
@@ -9114,7 +9169,7 @@ userRoutes.put("/profile", async (c) => {
9114
9169
  Date.now(),
9115
9170
  user.userId
9116
9171
  ).run();
9117
- await chunk5NCBFP37_cjs.logActivity(
9172
+ await chunkXWPGIFS7_cjs.logActivity(
9118
9173
  db,
9119
9174
  user.userId,
9120
9175
  "profile.update",
@@ -9177,7 +9232,7 @@ userRoutes.post("/profile/avatar", async (c) => {
9177
9232
  SELECT first_name, last_name FROM users WHERE id = ?
9178
9233
  `);
9179
9234
  const userData = await userStmt.bind(user.userId).first();
9180
- await chunk5NCBFP37_cjs.logActivity(
9235
+ await chunkXWPGIFS7_cjs.logActivity(
9181
9236
  db,
9182
9237
  user.userId,
9183
9238
  "profile.avatar_update",
@@ -9248,7 +9303,7 @@ userRoutes.post("/profile/password", async (c) => {
9248
9303
  dismissible: true
9249
9304
  }));
9250
9305
  }
9251
- const validPassword = await chunk5NCBFP37_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
9306
+ const validPassword = await chunkXWPGIFS7_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
9252
9307
  if (!validPassword) {
9253
9308
  return c.html(renderAlert2({
9254
9309
  type: "error",
@@ -9256,7 +9311,7 @@ userRoutes.post("/profile/password", async (c) => {
9256
9311
  dismissible: true
9257
9312
  }));
9258
9313
  }
9259
- const newPasswordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword(newPassword);
9314
+ const newPasswordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword(newPassword);
9260
9315
  const historyStmt = db.prepare(`
9261
9316
  INSERT INTO password_history (id, user_id, password_hash, created_at)
9262
9317
  VALUES (?, ?, ?, ?)
@@ -9272,7 +9327,7 @@ userRoutes.post("/profile/password", async (c) => {
9272
9327
  WHERE id = ?
9273
9328
  `);
9274
9329
  await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
9275
- await chunk5NCBFP37_cjs.logActivity(
9330
+ await chunkXWPGIFS7_cjs.logActivity(
9276
9331
  db,
9277
9332
  user.userId,
9278
9333
  "profile.password_change",
@@ -9339,7 +9394,7 @@ userRoutes.get("/users", async (c) => {
9339
9394
  `);
9340
9395
  const countResult = await countStmt.bind(...params).first();
9341
9396
  const totalUsers = countResult?.total || 0;
9342
- await chunk5NCBFP37_cjs.logActivity(
9397
+ await chunkXWPGIFS7_cjs.logActivity(
9343
9398
  db,
9344
9399
  user.userId,
9345
9400
  "users.list_view",
@@ -9441,12 +9496,12 @@ userRoutes.post("/users/new", async (c) => {
9441
9496
  const user = c.get("user");
9442
9497
  try {
9443
9498
  const formData = await c.req.formData();
9444
- const firstName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("first_name")?.toString());
9445
- const lastName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("last_name")?.toString());
9446
- const username = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("username")?.toString());
9499
+ const firstName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("first_name")?.toString());
9500
+ const lastName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("last_name")?.toString());
9501
+ const username = chunkSZE3XVET_cjs.sanitizeInput(formData.get("username")?.toString());
9447
9502
  const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
9448
- const phone = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
9449
- const bio = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
9503
+ const phone = chunkSZE3XVET_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
9504
+ const bio = chunkSZE3XVET_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
9450
9505
  const role = formData.get("role")?.toString() || "viewer";
9451
9506
  const password = formData.get("password")?.toString() || "";
9452
9507
  const confirmPassword = formData.get("confirm_password")?.toString() || "";
@@ -9493,7 +9548,7 @@ userRoutes.post("/users/new", async (c) => {
9493
9548
  dismissible: true
9494
9549
  }));
9495
9550
  }
9496
- const passwordHash = await chunk5NCBFP37_cjs.AuthManager.hashPassword(password);
9551
+ const passwordHash = await chunkXWPGIFS7_cjs.AuthManager.hashPassword(password);
9497
9552
  const userId = crypto.randomUUID();
9498
9553
  const createStmt = db.prepare(`
9499
9554
  INSERT INTO users (
@@ -9516,7 +9571,7 @@ userRoutes.post("/users/new", async (c) => {
9516
9571
  Date.now(),
9517
9572
  Date.now()
9518
9573
  ).run();
9519
- await chunk5NCBFP37_cjs.logActivity(
9574
+ await chunkXWPGIFS7_cjs.logActivity(
9520
9575
  db,
9521
9576
  user.userId,
9522
9577
  "user!.create",
@@ -9554,7 +9609,7 @@ userRoutes.get("/users/:id", async (c) => {
9554
9609
  if (!userRecord) {
9555
9610
  return c.json({ error: "User not found" }, 404);
9556
9611
  }
9557
- await chunk5NCBFP37_cjs.logActivity(
9612
+ await chunkXWPGIFS7_cjs.logActivity(
9558
9613
  db,
9559
9614
  user.userId,
9560
9615
  "user!.view",
@@ -9647,12 +9702,12 @@ userRoutes.put("/users/:id", async (c) => {
9647
9702
  const userId = c.req.param("id");
9648
9703
  try {
9649
9704
  const formData = await c.req.formData();
9650
- const firstName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("first_name")?.toString());
9651
- const lastName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("last_name")?.toString());
9652
- const username = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("username")?.toString());
9705
+ const firstName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("first_name")?.toString());
9706
+ const lastName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("last_name")?.toString());
9707
+ const username = chunkSZE3XVET_cjs.sanitizeInput(formData.get("username")?.toString());
9653
9708
  const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
9654
- const phone = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
9655
- const bio = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
9709
+ const phone = chunkSZE3XVET_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
9710
+ const bio = chunkSZE3XVET_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
9656
9711
  const role = formData.get("role")?.toString() || "viewer";
9657
9712
  const isActive = formData.get("is_active") === "1";
9658
9713
  const emailVerified = formData.get("email_verified") === "1";
@@ -9703,7 +9758,7 @@ userRoutes.put("/users/:id", async (c) => {
9703
9758
  Date.now(),
9704
9759
  userId
9705
9760
  ).run();
9706
- await chunk5NCBFP37_cjs.logActivity(
9761
+ await chunkXWPGIFS7_cjs.logActivity(
9707
9762
  db,
9708
9763
  user.userId,
9709
9764
  "user!.update",
@@ -9748,7 +9803,7 @@ userRoutes.post("/users/:id/toggle", async (c) => {
9748
9803
  UPDATE users SET is_active = ?, updated_at = ? WHERE id = ?
9749
9804
  `);
9750
9805
  await toggleStmt.bind(active ? 1 : 0, Date.now(), userId).run();
9751
- await chunk5NCBFP37_cjs.logActivity(
9806
+ await chunkXWPGIFS7_cjs.logActivity(
9752
9807
  db,
9753
9808
  user.userId,
9754
9809
  active ? "user.activate" : "user.deactivate",
@@ -9789,7 +9844,7 @@ userRoutes.delete("/users/:id", async (c) => {
9789
9844
  DELETE FROM users WHERE id = ?
9790
9845
  `);
9791
9846
  await deleteStmt.bind(userId).run();
9792
- await chunk5NCBFP37_cjs.logActivity(
9847
+ await chunkXWPGIFS7_cjs.logActivity(
9793
9848
  db,
9794
9849
  user.userId,
9795
9850
  "user!.hard_delete",
@@ -9808,7 +9863,7 @@ userRoutes.delete("/users/:id", async (c) => {
9808
9863
  UPDATE users SET is_active = 0, updated_at = ? WHERE id = ?
9809
9864
  `);
9810
9865
  await deleteStmt.bind(Date.now(), userId).run();
9811
- await chunk5NCBFP37_cjs.logActivity(
9866
+ await chunkXWPGIFS7_cjs.logActivity(
9812
9867
  db,
9813
9868
  user.userId,
9814
9869
  "user!.soft_delete",
@@ -9835,8 +9890,8 @@ userRoutes.post("/invite-user", async (c) => {
9835
9890
  const formData = await c.req.formData();
9836
9891
  const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
9837
9892
  const role = formData.get("role")?.toString()?.trim() || "viewer";
9838
- const firstName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("first_name")?.toString());
9839
- const lastName = chunkW2IAEG4W_cjs.sanitizeInput(formData.get("last_name")?.toString());
9893
+ const firstName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("first_name")?.toString());
9894
+ const lastName = chunkSZE3XVET_cjs.sanitizeInput(formData.get("last_name")?.toString());
9840
9895
  if (!email || !firstName || !lastName) {
9841
9896
  return c.json({ error: "Email, first name, and last name are required" }, 400);
9842
9897
  }
@@ -9874,7 +9929,7 @@ userRoutes.post("/invite-user", async (c) => {
9874
9929
  Date.now(),
9875
9930
  Date.now()
9876
9931
  ).run();
9877
- await chunk5NCBFP37_cjs.logActivity(
9932
+ await chunkXWPGIFS7_cjs.logActivity(
9878
9933
  db,
9879
9934
  user.userId,
9880
9935
  "user!.invite_sent",
@@ -9931,7 +9986,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
9931
9986
  Date.now(),
9932
9987
  userId
9933
9988
  ).run();
9934
- await chunk5NCBFP37_cjs.logActivity(
9989
+ await chunkXWPGIFS7_cjs.logActivity(
9935
9990
  db,
9936
9991
  user.userId,
9937
9992
  "user!.invitation_resent",
@@ -9967,7 +10022,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
9967
10022
  }
9968
10023
  const deleteStmt = db.prepare(`DELETE FROM users WHERE id = ?`);
9969
10024
  await deleteStmt.bind(userId).run();
9970
- await chunk5NCBFP37_cjs.logActivity(
10025
+ await chunkXWPGIFS7_cjs.logActivity(
9971
10026
  db,
9972
10027
  user.userId,
9973
10028
  "user!.invitation_cancelled",
@@ -10050,7 +10105,7 @@ userRoutes.get("/activity-logs", async (c) => {
10050
10105
  ...log,
10051
10106
  details: log.details ? JSON.parse(log.details) : null
10052
10107
  }));
10053
- await chunk5NCBFP37_cjs.logActivity(
10108
+ await chunkXWPGIFS7_cjs.logActivity(
10054
10109
  db,
10055
10110
  user.userId,
10056
10111
  "activity.logs_viewed",
@@ -10157,7 +10212,7 @@ userRoutes.get("/activity-logs/export", async (c) => {
10157
10212
  csvRows.push(row.join(","));
10158
10213
  }
10159
10214
  const csvContent = csvRows.join("\n");
10160
- await chunk5NCBFP37_cjs.logActivity(
10215
+ await chunkXWPGIFS7_cjs.logActivity(
10161
10216
  db,
10162
10217
  user.userId,
10163
10218
  "activity.logs_exported",
@@ -10375,7 +10430,7 @@ function getFileIcon(mimeType) {
10375
10430
  }
10376
10431
 
10377
10432
  // src/templates/pages/admin-media-library.template.ts
10378
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
10433
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
10379
10434
  function renderMediaLibraryPage(data) {
10380
10435
  const pageContent = `
10381
10436
  <div>
@@ -11310,7 +11365,7 @@ function renderMediaLibraryPage(data) {
11310
11365
  version: data.version,
11311
11366
  content: pageContent
11312
11367
  };
11313
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
11368
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
11314
11369
  }
11315
11370
 
11316
11371
  // src/templates/components/media-file-details.template.ts
@@ -11496,7 +11551,7 @@ var fileValidationSchema2 = zod.z.object({
11496
11551
  // 50MB max
11497
11552
  });
11498
11553
  var adminMediaRoutes = new hono.Hono();
11499
- adminMediaRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
11554
+ adminMediaRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
11500
11555
  adminMediaRoutes.get("/", async (c) => {
11501
11556
  try {
11502
11557
  const user = c.get("user");
@@ -12082,7 +12137,7 @@ adminMediaRoutes.put("/:id", async (c) => {
12082
12137
  `);
12083
12138
  }
12084
12139
  });
12085
- adminMediaRoutes.delete("/cleanup", chunk5NCBFP37_cjs.requireRole("admin"), async (c) => {
12140
+ adminMediaRoutes.delete("/cleanup", chunkXWPGIFS7_cjs.requireRole("admin"), async (c) => {
12086
12141
  try {
12087
12142
  const db = c.env.DB;
12088
12143
  const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
@@ -12332,12 +12387,39 @@ function formatFileSize(bytes) {
12332
12387
  }
12333
12388
 
12334
12389
  // src/templates/pages/admin-plugins-list.template.ts
12335
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
12390
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
12336
12391
  function renderPluginsListPage(data) {
12392
+ const categories = [
12393
+ { value: "content", label: "Content Management" },
12394
+ { value: "media", label: "Media" },
12395
+ { value: "editor", label: "Editors" },
12396
+ { value: "seo", label: "SEO & Analytics" },
12397
+ { value: "security", label: "Security" },
12398
+ { value: "utilities", label: "Utilities" },
12399
+ { value: "system", label: "System" },
12400
+ { value: "development", label: "Development" },
12401
+ { value: "demo", label: "Demo" }
12402
+ ];
12403
+ const statuses = [
12404
+ { value: "active", label: "Active" },
12405
+ { value: "inactive", label: "Inactive" },
12406
+ { value: "uninstalled", label: "Available to Install" },
12407
+ { value: "error", label: "Error" }
12408
+ ];
12409
+ const categoryCounts = {};
12410
+ categories.forEach((cat) => {
12411
+ categoryCounts[cat.value] = data.plugins.filter((p) => p.category === cat.value).length;
12412
+ });
12413
+ categories.sort((a, b) => (categoryCounts[b.value] || 0) - (categoryCounts[a.value] || 0));
12414
+ const statusCounts = {};
12415
+ statuses.forEach((status) => {
12416
+ statusCounts[status.value] = data.plugins.filter((p) => p.status === status.value).length;
12417
+ });
12418
+ statuses.sort((a, b) => (statusCounts[b.value] || 0) - (statusCounts[a.value] || 0));
12337
12419
  const pageContent = `
12338
12420
  <div>
12339
12421
  <!-- Header -->
12340
- <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-6">
12422
+ <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-8">
12341
12423
  <div>
12342
12424
  <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">Plugins</h1>
12343
12425
  <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">Manage and extend functionality with plugins</p>
@@ -12345,7 +12427,7 @@ function renderPluginsListPage(data) {
12345
12427
  </div>
12346
12428
 
12347
12429
  <!-- Experimental Notice -->
12348
- <div class="mb-6 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 p-4">
12430
+ <div class="mb-8 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 p-4">
12349
12431
  <div class="flex items-start">
12350
12432
  <div class="flex-shrink-0">
12351
12433
  <svg class="h-5 w-5 text-amber-600 dark:text-amber-400" viewBox="0 0 20 20" fill="currentColor">
@@ -12366,176 +12448,174 @@ function renderPluginsListPage(data) {
12366
12448
  </div>
12367
12449
  </div>
12368
12450
 
12369
- <!-- Stats -->
12370
- <div class="mb-6">
12371
- <h3 class="text-base font-semibold text-zinc-950 dark:text-white">Plugin Statistics</h3>
12372
- <dl class="mt-5 grid grid-cols-1 divide-zinc-950/5 dark:divide-white/10 overflow-hidden rounded-lg bg-zinc-800/75 dark:bg-zinc-800/75 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 md:grid-cols-5 md:divide-x md:divide-y-0">
12373
- <div class="px-4 py-5 sm:p-6">
12374
- <dt class="text-base font-normal text-zinc-700 dark:text-zinc-100">Total Plugins</dt>
12375
- <dd class="mt-1 flex items-baseline justify-between md:block lg:flex">
12376
- <div class="flex items-baseline text-2xl font-semibold text-cyan-400">
12377
- ${data.stats?.total || 0}
12378
- </div>
12379
- <div class="inline-flex items-baseline rounded-full bg-lime-400/10 text-lime-600 dark:text-lime-400 px-2.5 py-0.5 text-sm font-medium md:mt-2 lg:mt-0">
12380
- <svg viewBox="0 0 20 20" fill="currentColor" class="-ml-1 mr-0.5 size-5 shrink-0 self-center">
12381
- <path d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z" clip-rule="evenodd" fill-rule="evenodd" />
12382
- </svg>
12383
- <span class="sr-only">Increased by</span>
12384
- 8.5%
12385
- </div>
12386
- </dd>
12387
- </div>
12388
- <div class="px-4 py-5 sm:p-6">
12389
- <dt class="text-base font-normal text-zinc-700 dark:text-zinc-100">Active Plugins</dt>
12390
- <dd class="mt-1 flex items-baseline justify-between md:block lg:flex">
12391
- <div class="flex items-baseline text-2xl font-semibold text-lime-400">
12392
- ${data.stats?.active || 0}
12393
- </div>
12394
- <div class="inline-flex items-baseline rounded-full bg-lime-400/10 text-lime-600 dark:text-lime-400 px-2.5 py-0.5 text-sm font-medium md:mt-2 lg:mt-0">
12395
- <svg viewBox="0 0 20 20" fill="currentColor" class="-ml-1 mr-0.5 size-5 shrink-0 self-center">
12396
- <path d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z" clip-rule="evenodd" fill-rule="evenodd" />
12397
- </svg>
12398
- <span class="sr-only">Increased by</span>
12399
- 12.3%
12400
- </div>
12401
- </dd>
12451
+ <div class="flex flex-col lg:flex-row gap-8">
12452
+ <!-- Sidebar Filters -->
12453
+ <aside class="w-full lg:w-48 flex-shrink-0 space-y-8 lg:sticky lg:top-6 lg:self-start">
12454
+ <!-- Categories Filter -->
12455
+ <div>
12456
+ <h3 class="text-sm font-semibold text-zinc-950 dark:text-white mb-4">Categories</h3>
12457
+ <div class="space-y-3">
12458
+ ${categories.map((cat) => {
12459
+ const count = categoryCounts[cat.value] || 0;
12460
+ const isDisabled = count === 0;
12461
+ return `
12462
+ <div class="flex items-center ${isDisabled ? "opacity-50" : ""}">
12463
+ <input
12464
+ id="category-${cat.value}"
12465
+ name="category"
12466
+ value="${cat.value}"
12467
+ type="checkbox"
12468
+ onchange="filterAndSortPlugins()"
12469
+ class="h-4 w-4 rounded border-zinc-300 dark:border-zinc-700 text-zinc-900 focus:ring-zinc-600 dark:bg-zinc-900 disabled:cursor-not-allowed"
12470
+ ${isDisabled ? "disabled" : ""}
12471
+ >
12472
+ <label for="category-${cat.value}" class="ml-3 text-sm text-zinc-600 dark:text-zinc-400 select-none ${isDisabled ? "cursor-not-allowed" : ""}">
12473
+ ${cat.label} <span class="text-zinc-400 dark:text-zinc-500">(${count})</span>
12474
+ </label>
12475
+ </div>
12476
+ `;
12477
+ }).join("")}
12478
+ </div>
12402
12479
  </div>
12403
- <div class="px-4 py-5 sm:p-6">
12404
- <dt class="text-base font-normal text-zinc-700 dark:text-zinc-100">Inactive Plugins</dt>
12405
- <dd class="mt-1 flex items-baseline justify-between md:block lg:flex">
12406
- <div class="flex items-baseline text-2xl font-semibold text-purple-400">
12407
- ${data.stats?.inactive || 0}
12408
- </div>
12409
- <div class="inline-flex items-baseline rounded-full bg-pink-400/10 text-pink-600 dark:text-pink-400 px-2.5 py-0.5 text-sm font-medium md:mt-2 lg:mt-0">
12410
- <svg viewBox="0 0 20 20" fill="currentColor" class="-ml-1 mr-0.5 size-5 shrink-0 self-center">
12411
- <path d="M10 3a.75.75 0 0 1 .75.75v10.638l3.96-4.158a.75.75 0 1 1 1.08 1.04l-5.25 5.5a.75.75 0 0 1-1.08 0l-5.25-5.5a.75.75 0 1 1 1.08-1.04l3.96 4.158V3.75A.75.75 0 0 1 10 3Z" clip-rule="evenodd" fill-rule="evenodd" />
12412
- </svg>
12413
- <span class="sr-only">Decreased by</span>
12414
- 3.2%
12415
- </div>
12416
- </dd>
12480
+
12481
+ <div class="h-px bg-zinc-200 dark:bg-zinc-800 lg:hidden"></div>
12482
+
12483
+ <!-- Status Filter -->
12484
+ <div>
12485
+ <h3 class="text-sm font-semibold text-zinc-950 dark:text-white mb-4">Status</h3>
12486
+ <div class="space-y-3">
12487
+ ${statuses.map((status) => {
12488
+ const count = statusCounts[status.value] || 0;
12489
+ const isDisabled = count === 0;
12490
+ let colorClass = "";
12491
+ let ringClass = "";
12492
+ let dotClass = "";
12493
+ switch (status.value) {
12494
+ case "active":
12495
+ colorClass = "text-emerald-700 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10";
12496
+ ringClass = "ring-emerald-600/20";
12497
+ dotClass = "bg-emerald-500 dark:bg-emerald-400";
12498
+ break;
12499
+ case "inactive":
12500
+ colorClass = "text-zinc-700 dark:text-zinc-400 bg-zinc-50 dark:bg-zinc-500/10";
12501
+ ringClass = "ring-zinc-600/20";
12502
+ dotClass = "bg-zinc-500 dark:bg-zinc-400";
12503
+ break;
12504
+ case "error":
12505
+ colorClass = "text-red-700 dark:text-red-400 bg-red-50 dark:bg-red-500/10";
12506
+ ringClass = "ring-red-600/20";
12507
+ dotClass = "bg-red-500 dark:bg-red-400";
12508
+ break;
12509
+ case "uninstalled":
12510
+ colorClass = "text-yellow-700 dark:text-yellow-400 bg-yellow-50 dark:bg-yellow-500/10";
12511
+ ringClass = "ring-yellow-600/20";
12512
+ dotClass = "bg-yellow-500 dark:bg-yellow-400";
12513
+ break;
12514
+ default:
12515
+ colorClass = "text-zinc-700 dark:text-zinc-400 bg-zinc-50 dark:bg-zinc-500/10";
12516
+ ringClass = "ring-zinc-600/20";
12517
+ dotClass = "bg-zinc-500 dark:bg-zinc-400";
12518
+ }
12519
+ return `
12520
+ <div class="flex items-center ${isDisabled ? "opacity-50" : ""}">
12521
+ <input
12522
+ id="status-${status.value}"
12523
+ name="status"
12524
+ value="${status.value}"
12525
+ type="checkbox"
12526
+ onchange="filterAndSortPlugins()"
12527
+ class="h-4 w-4 rounded border-zinc-300 dark:border-zinc-700 text-zinc-900 focus:ring-zinc-600 dark:bg-zinc-900 disabled:cursor-not-allowed"
12528
+ ${isDisabled ? "disabled" : ""}
12529
+ >
12530
+ <label for="status-${status.value}" class="ml-3 cursor-pointer select-none flex items-center ${isDisabled ? "cursor-not-allowed" : ""}">
12531
+ <span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset ${colorClass} ${ringClass}">
12532
+ <span class="mr-1.5 h-1.5 w-1.5 rounded-full ${dotClass}"></span>
12533
+ ${status.label}
12534
+ </span>
12535
+ <span class="ml-2 text-xs text-zinc-500 dark:text-zinc-400">(${count})</span>
12536
+ </label>
12537
+ </div>
12538
+ `;
12539
+ }).join("")}
12540
+ </div>
12417
12541
  </div>
12418
- <div class="px-4 py-5 sm:p-6">
12419
- <dt class="text-base font-normal text-zinc-700 dark:text-zinc-100">Plugin Errors</dt>
12420
- <dd class="mt-1 flex items-baseline justify-between md:block lg:flex">
12421
- <div class="flex items-baseline text-2xl font-semibold text-pink-400">
12422
- ${data.stats?.errors || 0}
12423
- </div>
12424
- <div class="inline-flex items-baseline rounded-full bg-pink-400/10 text-pink-600 dark:text-pink-400 px-2.5 py-0.5 text-sm font-medium md:mt-2 lg:mt-0">
12425
- <svg viewBox="0 0 20 20" fill="currentColor" class="-ml-1 mr-0.5 size-5 shrink-0 self-center">
12426
- <path d="M10 3a.75.75 0 0 1 .75.75v10.638l3.96-4.158a.75.75 0 1 1 1.08 1.04l-5.25 5.5a.75.75 0 0 1-1.08 0l-5.25-5.5a.75.75 0 1 1 1.08-1.04l3.96 4.158V3.75A.75.75 0 0 1 10 3Z" clip-rule="evenodd" fill-rule="evenodd" />
12427
- </svg>
12428
- <span class="sr-only">Decreased by</span>
12429
- 1.5%
12430
- </div>
12431
- </dd>
12542
+ </aside>
12543
+
12544
+ <!-- Main Content -->
12545
+ <div class="flex-1 min-w-0">
12546
+ <!-- Stats Row (Compact) -->
12547
+ <div class="flex flex-wrap gap-4 mb-6">
12548
+ <div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
12549
+ <div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Total</div>
12550
+ <div class="mt-1 text-lg font-semibold text-zinc-900 dark:text-white">${data.stats?.total || 0}</div>
12551
+ </div>
12552
+ <div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
12553
+ <div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Active</div>
12554
+ <div class="mt-1 text-lg font-semibold text-emerald-600 dark:text-emerald-400">${data.stats?.active || 0}</div>
12555
+ </div>
12556
+ <div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
12557
+ <div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Available</div>
12558
+ <div class="mt-1 text-lg font-semibold text-zinc-600 dark:text-zinc-400">${data.stats?.uninstalled || 0}</div>
12559
+ </div>
12560
+ <div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
12561
+ <div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Errors</div>
12562
+ <div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">${data.stats?.errors || 0}</div>
12563
+ </div>
12432
12564
  </div>
12433
- <div class="px-4 py-5 sm:p-6">
12434
- <dt class="text-base font-normal text-zinc-700 dark:text-zinc-100">Available to Install</dt>
12435
- <dd class="mt-1 flex items-baseline justify-between md:block lg:flex">
12436
- <div class="flex items-baseline text-2xl font-semibold text-zinc-400">
12437
- ${data.stats?.uninstalled || 0}
12438
- </div>
12439
- <div class="inline-flex items-baseline rounded-full bg-zinc-400/10 text-zinc-600 dark:text-zinc-400 px-2.5 py-0.5 text-sm font-medium md:mt-2 lg:mt-0">
12440
- <svg viewBox="0 0 20 20" fill="currentColor" class="-ml-1 mr-0.5 size-5 shrink-0 self-center">
12441
- <path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
12565
+
12566
+ <!-- Toolbar -->
12567
+ <div class="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between mb-6">
12568
+ <div class="relative flex-1 w-full">
12569
+ <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
12570
+ <svg class="h-4 w-4 text-zinc-400" viewBox="0 0 20 20" fill="currentColor">
12571
+ <path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
12442
12572
  </svg>
12443
- <span class="sr-only">Available</span>
12444
- Ready
12445
12573
  </div>
12446
- </dd>
12447
- </div>
12448
- </dl>
12449
- </div>
12574
+ <input
12575
+ id="search-input"
12576
+ type="text"
12577
+ placeholder="Search plugins..."
12578
+ oninput="filterAndSortPlugins()"
12579
+ class="block w-full h-9 rounded-md border-0 py-1.5 pl-10 text-zinc-900 ring-1 ring-inset ring-zinc-300 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-900 dark:text-white dark:ring-zinc-700 dark:focus:ring-zinc-500 sm:text-sm sm:leading-6"
12580
+ >
12581
+ </div>
12450
12582
 
12451
- <!-- Filters -->
12452
- <div class="relative rounded-xl overflow-hidden mb-6">
12453
- <!-- Gradient Background -->
12454
- <div class="absolute inset-0 bg-gradient-to-r from-cyan-500/10 via-blue-500/10 to-purple-500/10 dark:from-cyan-400/20 dark:via-blue-400/20 dark:to-purple-400/20"></div>
12583
+ <div class="flex items-center gap-3 w-full sm:w-auto">
12584
+ <select id="sort-filter" onchange="filterAndSortPlugins()" class="block w-full sm:w-auto h-9 rounded-md border-0 py-1.5 pl-3 pr-8 text-zinc-900 ring-1 ring-inset ring-zinc-300 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-900 dark:text-white dark:ring-zinc-700 dark:focus:ring-zinc-500 sm:text-sm sm:leading-6">
12585
+ <option value="name-asc">Name (A-Z)</option>
12586
+ <option value="name-desc">Name (Z-A)</option>
12587
+ <option value="newest">Newest Installed</option>
12588
+ <option value="updated">Recently Updated</option>
12589
+ <option value="popular">Popularity</option>
12590
+ <option value="rating">Highest Rated</option>
12591
+ </select>
12455
12592
 
12456
- <div class="relative bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10">
12457
- <div class="px-6 py-5">
12458
- <div class="flex items-center justify-between">
12459
- <div class="flex items-center space-x-4 flex-1">
12460
- <div>
12461
- <label class="block text-sm/6 font-medium text-zinc-950 dark:text-white">Category</label>
12462
- <div class="mt-2 grid grid-cols-1">
12463
- <select id="category-filter" onchange="filterPlugins()" class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-cyan-500/30 dark:outline-cyan-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-cyan-500 dark:focus-visible:outline-cyan-400 sm:text-sm/6 min-w-48">
12464
- <option value="">All Categories</option>
12465
- <option value="content">Content Management</option>
12466
- <option value="media">Media</option>
12467
- <option value="seo">SEO & Analytics</option>
12468
- <option value="security">Security</option>
12469
- <option value="utilities">Utilities</option>
12470
- <option value="system">System</option>
12471
- <option value="development">Development</option>
12472
- <option value="demo">Demo</option>
12473
- </select>
12474
- <svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-cyan-600 dark:text-cyan-400 sm:size-4">
12475
- <path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
12476
- </svg>
12477
- </div>
12478
- </div>
12479
- <div>
12480
- <label class="block text-sm/6 font-medium text-zinc-950 dark:text-white">Status</label>
12481
- <div class="mt-2 grid grid-cols-1">
12482
- <select id="status-filter" onchange="filterPlugins()" class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-cyan-500/30 dark:outline-cyan-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-cyan-500 dark:focus-visible:outline-cyan-400 sm:text-sm/6 min-w-48">
12483
- <option value="">All Status</option>
12484
- <option value="active">Active</option>
12485
- <option value="inactive">Inactive</option>
12486
- <option value="uninstalled">Available to Install</option>
12487
- <option value="error">Error</option>
12488
- </select>
12489
- <svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-cyan-600 dark:text-cyan-400 sm:size-4">
12490
- <path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
12491
- </svg>
12492
- </div>
12493
- </div>
12494
- <div class="flex-1 max-w-md">
12495
- <label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Search</label>
12496
- <div class="relative group">
12497
- <div class="absolute left-3.5 top-2.5 flex items-center justify-center w-5 h-5 rounded-full bg-gradient-to-br from-cyan-400 to-blue-500 dark:from-cyan-300 dark:to-blue-400 opacity-90 group-focus-within:opacity-100 transition-opacity">
12498
- <svg class="h-3 w-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
12499
- <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
12500
- </svg>
12501
- </div>
12502
- <input
12503
- id="search-input"
12504
- type="text"
12505
- placeholder="Search plugins..."
12506
- oninput="filterPlugins()"
12507
- class="w-full rounded-full bg-transparent px-11 py-2 text-sm text-zinc-950 dark:text-white placeholder-zinc-500 dark:placeholder-zinc-400 border-2 border-cyan-200/50 dark:border-cyan-700/50 focus:outline-none focus:border-cyan-500 dark:focus:border-cyan-400 focus:shadow-lg focus:shadow-cyan-500/20 dark:focus:shadow-cyan-400/20 transition-all duration-300"
12508
- />
12509
- </div>
12510
- </div>
12511
- </div>
12512
- <div class="flex items-center gap-x-3 ml-4">
12513
- <button
12514
- onclick="location.reload()"
12515
- class="inline-flex items-center gap-x-1.5 px-3 py-1.5 bg-white/90 dark:bg-zinc-800/90 backdrop-blur-sm text-zinc-950 dark:text-white text-sm font-medium rounded-full ring-1 ring-inset ring-cyan-200/50 dark:ring-cyan-700/50 hover:bg-gradient-to-r hover:from-cyan-50 hover:to-blue-50 dark:hover:from-cyan-900/30 dark:hover:to-blue-900/30 hover:ring-cyan-300 dark:hover:ring-cyan-600 transition-all duration-200"
12516
- >
12517
- <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12518
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
12519
- </svg>
12520
- Refresh
12521
- </button>
12522
- </div>
12593
+ <button
12594
+ onclick="location.reload()"
12595
+ class="inline-flex items-center gap-x-1.5 rounded-md bg-white dark:bg-zinc-900 px-3 py-1.5 h-9 text-sm font-semibold text-zinc-900 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-800"
12596
+ >
12597
+ <svg class="h-4 w-4 text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12598
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
12599
+ </svg>
12600
+ </button>
12523
12601
  </div>
12524
12602
  </div>
12603
+
12604
+ <!-- Plugins Grid -->
12605
+ <div id="plugins-grid" class="grid gap-6" style="grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));">
12606
+ ${data.plugins.map((plugin) => renderPluginCard(plugin)).join("")}
12607
+ </div>
12525
12608
  </div>
12526
12609
  </div>
12527
-
12528
- <!-- Plugins Grid -->
12529
- <div id="plugins-grid" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
12530
- ${data.plugins.map((plugin) => renderPluginCard(plugin)).join("")}
12531
12610
  </div>
12532
12611
 
12533
12612
  <script>
12534
- async function togglePlugin(pluginId, action) {
12535
- const button = event.target;
12536
- const originalText = button.textContent;
12613
+ async function togglePlugin(pluginId, action, event) {
12614
+ const button = event.target.closest('button');
12615
+ if (!button) return;
12616
+
12537
12617
  button.disabled = true;
12538
- button.textContent = action === 'activate' ? 'Activating...' : 'Deactivating...';
12618
+ button.classList.add('opacity-50', 'cursor-wait');
12539
12619
 
12540
12620
  try {
12541
12621
  const response = await fetch(\`/admin/plugins/\${pluginId}/\${action}\`, {
@@ -12551,27 +12631,36 @@ function renderPluginsListPage(data) {
12551
12631
  // Update UI
12552
12632
  const card = button.closest('.plugin-card');
12553
12633
  const statusBadge = card.querySelector('.status-badge');
12634
+ const knob = button.querySelector('.toggle-knob');
12554
12635
 
12555
12636
  if (action === 'activate') {
12556
12637
  // Update status badge
12557
- statusBadge.className = 'status-badge inline-flex items-center rounded-md px-2.5 py-1 text-sm font-medium ring-1 ring-inset bg-lime-50 dark:bg-lime-500/10 text-lime-700 dark:text-lime-300 ring-lime-700/10 dark:ring-lime-400/20';
12558
- statusBadge.innerHTML = '<div class="w-2 h-2 bg-lime-500 dark:bg-lime-400 rounded-full mr-2"></div>Active';
12559
- // Update card border to green
12560
- card.className = 'plugin-card rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-[3px] ring-lime-500 dark:ring-lime-400 p-6 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-all';
12561
- // Update button
12562
- button.textContent = 'Deactivate';
12563
- button.onclick = () => togglePlugin(pluginId, 'deactivate');
12564
- button.className = 'bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium transition-colors';
12638
+ statusBadge.className = 'status-badge inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ring-1 ring-inset bg-emerald-50 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 ring-emerald-600/20';
12639
+ statusBadge.innerHTML = '<div class="w-1.5 h-1.5 bg-emerald-500 dark:bg-emerald-400 rounded-full mr-1.5"></div>Active';
12640
+
12641
+ // Update button state to Active
12642
+ button.className = 'bg-emerald-600 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-emerald-600 focus:ring-offset-2 toggle-button';
12643
+ button.setAttribute('aria-checked', 'true');
12644
+ button.onclick = (event) => togglePlugin(pluginId, 'deactivate', event);
12645
+
12646
+ // Update knob position
12647
+ if (knob) {
12648
+ knob.className = 'translate-x-5 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out toggle-knob';
12649
+ }
12565
12650
  } else {
12566
12651
  // Update status badge
12567
- statusBadge.className = 'status-badge inline-flex items-center rounded-md px-2.5 py-1 text-sm font-medium ring-1 ring-inset bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-700/10 dark:ring-zinc-400/20';
12568
- statusBadge.innerHTML = '<div class="w-2 h-2 bg-zinc-500 dark:bg-zinc-400 rounded-full mr-2"></div>Inactive';
12569
- // Update card border to pink
12570
- card.className = 'plugin-card rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-[3px] ring-pink-500 dark:ring-pink-400 p-6 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-all';
12571
- // Update button
12572
- button.textContent = 'Activate';
12573
- button.onclick = () => togglePlugin(pluginId, 'activate');
12574
- button.className = 'bg-lime-600 dark:bg-lime-700 hover:bg-lime-700 dark:hover:bg-lime-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium transition-colors';
12652
+ statusBadge.className = 'status-badge inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ring-1 ring-inset bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-600/20';
12653
+ statusBadge.innerHTML = '<div class="w-1.5 h-1.5 bg-zinc-500 dark:bg-zinc-400 rounded-full mr-1.5"></div>Inactive';
12654
+
12655
+ // Update button state to Inactive
12656
+ button.className = 'bg-zinc-200 dark:bg-zinc-700 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-emerald-600 focus:ring-offset-2 toggle-button';
12657
+ button.setAttribute('aria-checked', 'false');
12658
+ button.onclick = (event) => togglePlugin(pluginId, 'activate', event);
12659
+
12660
+ // Update knob position
12661
+ if (knob) {
12662
+ knob.className = 'translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out toggle-knob';
12663
+ }
12575
12664
  }
12576
12665
 
12577
12666
  showNotification(\`Plugin \${action}d successfully\`, 'success');
@@ -12580,9 +12669,9 @@ function renderPluginsListPage(data) {
12580
12669
  }
12581
12670
  } catch (error) {
12582
12671
  showNotification(error.message, 'error');
12583
- button.textContent = originalText;
12584
12672
  } finally {
12585
12673
  button.disabled = false;
12674
+ button.classList.remove('opacity-50', 'cursor-wait');
12586
12675
  }
12587
12676
  }
12588
12677
 
@@ -12666,81 +12755,92 @@ function renderPluginsListPage(data) {
12666
12755
  showNotification('Plugin details coming soon!', 'info');
12667
12756
  }
12668
12757
 
12669
- function showNotification(message, type) {
12670
- const notification = document.createElement('div');
12671
- const bgColor = type === 'success' ? 'bg-green-600' : type === 'error' ? 'bg-red-600' : 'bg-blue-600';
12672
- notification.className = \`fixed top-4 right-4 px-4 py-2 rounded-lg text-white z-50 \${bgColor}\`;
12673
- notification.textContent = message;
12674
- document.body.appendChild(notification);
12675
-
12676
- setTimeout(() => {
12677
- notification.remove();
12678
- }, 3000);
12679
- }
12680
-
12681
- function filterPlugins() {
12682
- const categoryFilter = document.getElementById('category-filter').value.toLowerCase();
12683
- const statusFilter = document.getElementById('status-filter').value.toLowerCase();
12758
+ function filterAndSortPlugins() {
12759
+ // Get checked categories
12760
+ const checkedCategories = Array.from(document.querySelectorAll('input[name="category"]:checked'))
12761
+ .map(cb => cb.value.toLowerCase());
12762
+
12763
+ // Get checked statuses
12764
+ const checkedStatuses = Array.from(document.querySelectorAll('input[name="status"]:checked'))
12765
+ .map(cb => cb.value.toLowerCase());
12766
+
12684
12767
  const searchInput = document.getElementById('search-input').value.toLowerCase();
12768
+ const sortValue = document.getElementById('sort-filter').value;
12685
12769
 
12686
- const pluginCards = document.querySelectorAll('.plugin-card');
12687
- let visibleCount = 0;
12688
-
12689
- pluginCards.forEach(card => {
12690
- // Get plugin data from card attributes
12770
+ const pluginsGrid = document.getElementById('plugins-grid');
12771
+ const pluginCards = Array.from(pluginsGrid.querySelectorAll('.plugin-card'));
12772
+
12773
+ // Filter
12774
+ const visibleCards = pluginCards.filter(card => {
12691
12775
  const category = card.getAttribute('data-category')?.toLowerCase() || '';
12692
12776
  const status = card.getAttribute('data-status')?.toLowerCase() || '';
12693
12777
  const name = card.getAttribute('data-name')?.toLowerCase() || '';
12694
12778
  const description = card.getAttribute('data-description')?.toLowerCase() || '';
12695
12779
 
12696
- // Check if plugin matches all filters
12697
- let matches = true;
12698
-
12699
- // Category filter
12700
- if (categoryFilter && category !== categoryFilter) {
12701
- matches = false;
12702
- }
12703
-
12704
- // Status filter
12705
- if (statusFilter && status !== statusFilter) {
12706
- matches = false;
12707
- }
12708
-
12709
- // Search filter - check if search term is in name or description
12710
- if (searchInput && !name.includes(searchInput) && !description.includes(searchInput)) {
12711
- matches = false;
12712
- }
12780
+ // Category filter: if any selected, must match one of them
12781
+ if (checkedCategories.length > 0 && !checkedCategories.includes(category)) return false;
12782
+
12783
+ // Status filter: if any selected, must match one of them
12784
+ if (checkedStatuses.length > 0 && !checkedStatuses.includes(status)) return false;
12785
+
12786
+ // Search filter
12787
+ if (searchInput && !name.includes(searchInput) && !description.includes(searchInput)) return false;
12788
+
12789
+ return true;
12790
+ });
12713
12791
 
12714
- // Show/hide card
12715
- if (matches) {
12716
- card.style.display = '';
12717
- visibleCount++;
12718
- } else {
12719
- card.style.display = 'none';
12792
+ // Sort
12793
+ visibleCards.sort((a, b) => {
12794
+ const aName = a.getAttribute('data-name') || '';
12795
+ const bName = b.getAttribute('data-name') || '';
12796
+ const aInstalled = parseInt(a.getAttribute('data-installed') || '0');
12797
+ const bInstalled = parseInt(b.getAttribute('data-installed') || '0');
12798
+ const aUpdated = parseInt(a.getAttribute('data-updated') || '0');
12799
+ const bUpdated = parseInt(b.getAttribute('data-updated') || '0');
12800
+ const aDownloads = parseInt(a.getAttribute('data-downloads') || '0');
12801
+ const bDownloads = parseInt(b.getAttribute('data-downloads') || '0');
12802
+ const aRating = parseFloat(a.getAttribute('data-rating') || '0');
12803
+ const bRating = parseFloat(b.getAttribute('data-rating') || '0');
12804
+
12805
+ switch (sortValue) {
12806
+ case 'name-desc': return bName.localeCompare(aName);
12807
+ case 'newest': return bInstalled - aInstalled;
12808
+ case 'updated': return bUpdated - aUpdated;
12809
+ case 'popular': return bDownloads - aDownloads;
12810
+ case 'rating': return bRating - aRating;
12811
+ case 'name-asc':
12812
+ default: return aName.localeCompare(bName);
12720
12813
  }
12721
12814
  });
12722
12815
 
12723
- // Show/hide "no results" message
12816
+ // Re-append
12817
+ pluginCards.forEach(card => card.style.display = 'none'); // Hide all first
12818
+
12819
+ // If no results
12724
12820
  let noResultsMsg = document.getElementById('no-results-message');
12725
- if (visibleCount === 0) {
12821
+ if (visibleCards.length === 0) {
12726
12822
  if (!noResultsMsg) {
12727
12823
  noResultsMsg = document.createElement('div');
12728
12824
  noResultsMsg.id = 'no-results-message';
12729
- noResultsMsg.className = 'col-span-full text-center py-12';
12825
+ noResultsMsg.className = 'col-span-full text-center py-12 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-dashed border-zinc-300 dark:border-zinc-700';
12730
12826
  noResultsMsg.innerHTML = \`
12731
12827
  <div class="flex flex-col items-center">
12732
- <svg class="w-16 h-16 text-zinc-400 dark:text-zinc-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12828
+ <svg class="w-12 h-12 text-zinc-400 dark:text-zinc-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12733
12829
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
12734
12830
  </svg>
12735
- <h3 class="text-lg font-semibold text-zinc-950 dark:text-white mb-2">No plugins found</h3>
12831
+ <h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-1">No plugins found</h3>
12736
12832
  <p class="text-sm text-zinc-500 dark:text-zinc-400">Try adjusting your filters or search terms</p>
12737
12833
  </div>
12738
12834
  \`;
12739
- document.getElementById('plugins-grid').appendChild(noResultsMsg);
12835
+ pluginsGrid.appendChild(noResultsMsg);
12740
12836
  }
12741
12837
  noResultsMsg.style.display = '';
12742
- } else if (noResultsMsg) {
12743
- noResultsMsg.style.display = 'none';
12838
+ } else {
12839
+ if (noResultsMsg) noResultsMsg.style.display = 'none';
12840
+ visibleCards.forEach(card => {
12841
+ card.style.display = '';
12842
+ pluginsGrid.appendChild(card); // Re-appending moves it to the end, effectively sorting
12843
+ });
12744
12844
  }
12745
12845
  }
12746
12846
  </script>
@@ -12767,120 +12867,115 @@ function renderPluginsListPage(data) {
12767
12867
  version: data.version,
12768
12868
  content: pageContent
12769
12869
  };
12770
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
12870
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
12771
12871
  }
12772
12872
  function renderPluginCard(plugin) {
12773
12873
  const statusColors = {
12774
- active: "bg-lime-50 dark:bg-lime-500/10 text-lime-700 dark:text-lime-300 ring-lime-700/10 dark:ring-lime-400/20",
12775
- inactive: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-700/10 dark:ring-zinc-400/20",
12776
- error: "bg-red-50 dark:bg-red-500/10 text-red-700 dark:text-red-400 ring-red-700/10 dark:ring-red-400/20",
12777
- uninstalled: "bg-zinc-100 dark:bg-zinc-600/10 text-zinc-600 dark:text-zinc-500 ring-zinc-600/10 dark:ring-zinc-500/20"
12874
+ active: "bg-emerald-50 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 ring-emerald-600/20",
12875
+ inactive: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-600/20",
12876
+ error: "bg-red-50 dark:bg-red-500/10 text-red-700 dark:text-red-400 ring-red-600/20",
12877
+ uninstalled: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-600 dark:text-zinc-500 ring-zinc-600/20"
12778
12878
  };
12779
12879
  const statusIcons = {
12780
- active: '<div class="w-2 h-2 bg-lime-500 dark:bg-lime-400 rounded-full mr-2"></div>',
12781
- inactive: '<div class="w-2 h-2 bg-zinc-500 dark:bg-zinc-400 rounded-full mr-2"></div>',
12782
- error: '<div class="w-2 h-2 bg-red-500 dark:bg-red-400 rounded-full mr-2"></div>',
12783
- uninstalled: '<div class="w-2 h-2 bg-zinc-400 dark:bg-zinc-600 rounded-full mr-2"></div>'
12784
- };
12785
- const borderColors = {
12786
- active: "ring-[3px] ring-lime-500 dark:ring-lime-400",
12787
- inactive: "ring-[3px] ring-pink-500 dark:ring-pink-400",
12788
- error: "ring-[3px] ring-red-500 dark:ring-red-400",
12789
- uninstalled: "ring-[3px] ring-zinc-400 dark:ring-zinc-600"
12880
+ active: '<div class="w-1.5 h-1.5 bg-emerald-500 dark:bg-emerald-400 rounded-full mr-1.5"></div>',
12881
+ inactive: '<div class="w-1.5 h-1.5 bg-zinc-500 dark:bg-zinc-400 rounded-full mr-1.5"></div>',
12882
+ error: '<div class="w-1.5 h-1.5 bg-red-500 dark:bg-red-400 rounded-full mr-1.5"></div>',
12883
+ uninstalled: '<div class="w-1.5 h-1.5 bg-zinc-400 dark:bg-zinc-600 rounded-full mr-1.5"></div>'
12790
12884
  };
12791
12885
  const criticalCorePlugins = ["core-auth", "core-media"];
12792
12886
  const canToggle = !criticalCorePlugins.includes(plugin.id);
12793
12887
  let actionButton = "";
12794
12888
  if (plugin.status === "uninstalled") {
12795
- actionButton = `<button onclick="installPlugin('${plugin.name}')" class="bg-cyan-600 dark:bg-cyan-700 hover:bg-cyan-700 dark:hover:bg-cyan-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium transition-colors">Install</button>`;
12796
- } else if (plugin.status === "active") {
12797
- actionButton = `<button onclick="togglePlugin('${plugin.id}', 'deactivate')" class="bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium transition-colors">Deactivate</button>`;
12889
+ actionButton = `<button onclick="installPlugin('${plugin.name}')" class="w-full sm:w-auto bg-zinc-900 dark:bg-white hover:bg-zinc-800 dark:hover:bg-zinc-100 text-white dark:text-zinc-900 px-3 py-1.5 rounded-md text-xs font-medium transition-colors shadow-sm">Install</button>`;
12798
12890
  } else {
12799
- actionButton = `<button onclick="togglePlugin('${plugin.id}', 'activate')" class="bg-lime-600 dark:bg-lime-700 hover:bg-lime-700 dark:hover:bg-lime-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium transition-colors">Activate</button>`;
12891
+ const isActive = plugin.status === "active";
12892
+ const action = isActive ? "deactivate" : "activate";
12893
+ const bgClass = isActive ? "bg-emerald-600" : "bg-zinc-200 dark:bg-zinc-700";
12894
+ const translateClass = isActive ? "translate-x-5" : "translate-x-0";
12895
+ if (canToggle) {
12896
+ actionButton = `
12897
+ <button onclick="togglePlugin('${plugin.id}', '${action}', event)" type="button" class="${bgClass} relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-emerald-600 focus:ring-offset-2 toggle-button" role="switch" aria-checked="${isActive}">
12898
+ <span class="sr-only">Toggle plugin</span>
12899
+ <span aria-hidden="true" class="${translateClass} pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out toggle-knob"></span>
12900
+ </button>
12901
+ `;
12902
+ } else {
12903
+ actionButton = `
12904
+ <div class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-not-allowed rounded-full border-2 border-transparent bg-emerald-600/50 opacity-50" title="Core plugin cannot be disabled">
12905
+ <span class="translate-x-5 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0"></span>
12906
+ </div>
12907
+ `;
12908
+ }
12800
12909
  }
12801
12910
  return `
12802
- <div class="plugin-card rounded-xl bg-white dark:bg-zinc-900 shadow-sm ${borderColors[plugin.status]} p-6 hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-all" data-category="${plugin.category}" data-status="${plugin.status}" data-name="${plugin.displayName}" data-description="${plugin.description}">
12911
+ <div class="plugin-card flex flex-col h-full rounded-md bg-white dark:bg-zinc-900 ring-1 ring-zinc-950/10 dark:ring-white/10 p-5 transition-all hover:shadow-md"
12912
+ data-category="${plugin.category}"
12913
+ data-status="${plugin.status}"
12914
+ data-name="${plugin.displayName}"
12915
+ data-description="${plugin.description}"
12916
+ data-downloads="${plugin.downloadCount || 0}"
12917
+ data-rating="${plugin.rating || 0}">
12803
12918
  <div class="flex items-start justify-between mb-4">
12804
12919
  <div class="flex items-center gap-3">
12805
- <div class="w-12 h-12 rounded-lg flex items-center justify-center ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 bg-zinc-50 dark:bg-zinc-800">
12920
+ <div class="w-10 h-10 rounded-md flex items-center justify-center bg-zinc-50 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 ring-1 ring-inset ring-zinc-200 dark:ring-zinc-700/50">
12806
12921
  ${plugin.icon || getDefaultPluginIcon(plugin.category)}
12807
12922
  </div>
12808
12923
  <div>
12809
- <h3 class="text-lg font-semibold text-zinc-950 dark:text-white">${plugin.displayName}</h3>
12810
- <p class="text-sm text-zinc-500 dark:text-zinc-400">v${plugin.version} by ${plugin.author}</p>
12924
+ <div class="flex items-center gap-2">
12925
+ <h3 class="text-sm font-semibold text-zinc-900 dark:text-white">${plugin.displayName}</h3>
12926
+ <span class="status-badge inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ring-1 ring-inset ${statusColors[plugin.status]}">
12927
+ ${statusIcons[plugin.status]}${plugin.status.charAt(0).toUpperCase() + plugin.status.slice(1)}
12928
+ </span>
12929
+ </div>
12930
+ <p class="text-xs text-zinc-500 dark:text-zinc-400">v${plugin.version} \u2022 ${plugin.author}</p>
12811
12931
  </div>
12812
12932
  </div>
12813
- <div class="flex flex-col items-end gap-2">
12814
- <span class="status-badge inline-flex items-center rounded-md px-2.5 py-1 text-sm font-medium ring-1 ring-inset ${statusColors[plugin.status]}">
12815
- ${statusIcons[plugin.status]}${plugin.status.charAt(0).toUpperCase() + plugin.status.slice(1)}
12816
- </span>
12817
- ${plugin.isCore ? '<span class="inline-flex items-center rounded-md px-2.5 py-1 text-sm font-medium bg-cyan-50 dark:bg-cyan-500/10 text-cyan-700 dark:text-cyan-300 ring-1 ring-inset ring-cyan-700/10 dark:ring-cyan-400/20">Core</span>' : ""}
12933
+
12934
+ <div class="flex items-center gap-1">
12935
+ ${plugin.status !== "uninstalled" ? `
12936
+ <button onclick="showPluginDetails('${plugin.id}')" class="text-zinc-400 hover:text-zinc-600 dark:text-zinc-500 dark:hover:text-zinc-300 p-1.5 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Plugin Details">
12937
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12938
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
12939
+ </svg>
12940
+ </button>
12941
+ ` : ""}
12942
+
12943
+ ${!plugin.isCore && plugin.status !== "uninstalled" ? `
12944
+ <button onclick="uninstallPlugin('${plugin.id}')" class="text-zinc-400 hover:text-red-600 dark:text-zinc-500 dark:hover:text-red-400 p-1.5 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Uninstall Plugin">
12945
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12946
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
12947
+ </svg>
12948
+ </button>
12949
+ ` : ""}
12818
12950
  </div>
12819
12951
  </div>
12820
12952
 
12821
- <p class="text-zinc-600 dark:text-zinc-300 text-sm mb-4 line-clamp-3">${plugin.description}</p>
12953
+ <p class="text-zinc-600 dark:text-zinc-400 text-sm mb-4 line-clamp-2 flex-grow">${plugin.description}</p>
12822
12954
 
12823
- <div class="flex items-center gap-4 mb-4 text-xs text-zinc-500 dark:text-zinc-400">
12824
- <span class="flex items-center gap-1">
12825
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12826
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
12827
- </svg>
12955
+ <div class="flex flex-wrap items-center gap-2 mb-5">
12956
+ <span class="inline-flex items-center rounded-md bg-zinc-100 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400">
12828
12957
  ${plugin.category}
12829
12958
  </span>
12830
-
12831
- ${plugin.downloadCount ? `
12832
- <span class="flex items-center gap-1">
12833
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12834
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
12835
- </svg>
12836
- ${plugin.downloadCount.toLocaleString()}
12837
- </span>
12838
- ` : ""}
12839
-
12840
- ${plugin.rating ? `
12841
- <span class="flex items-center gap-1">
12842
- <svg class="w-4 h-4 text-yellow-500 dark:text-yellow-400 fill-current" viewBox="0 0 24 24">
12843
- <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
12844
- </svg>
12845
- ${plugin.rating}
12846
- </span>
12847
- ` : ""}
12848
-
12849
- <span>${plugin.lastUpdated}</span>
12850
- </div>
12851
-
12852
- ${plugin.dependencies && plugin.dependencies.length > 0 ? `
12853
- <div class="mb-4">
12854
- <p class="text-xs text-zinc-500 dark:text-zinc-400 mb-2">Dependencies:</p>
12855
- <div class="flex flex-wrap gap-1">
12856
- ${plugin.dependencies.map((dep) => `<span class="inline-block bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 text-xs px-2 py-1 rounded">${dep}</span>`).join("")}
12857
- </div>
12959
+ ${plugin.isCore ? '<span class="inline-flex items-center rounded-md bg-zinc-100 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400">Core</span>' : ""}
12960
+
12961
+ ${plugin.dependencies && plugin.dependencies.map((dep) => `
12962
+ <span class="inline-flex items-center rounded-md bg-zinc-100 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400">
12963
+ ${dep}
12964
+ </span>
12965
+ `).join("") || ""}
12858
12966
  </div>
12859
- ` : ""}
12860
12967
 
12861
- <div class="flex items-center justify-between">
12968
+ <div class="flex items-center justify-between pt-4 border-t border-zinc-100 dark:border-zinc-800 mt-auto">
12862
12969
  <div class="flex gap-2">
12863
- ${plugin.status === "uninstalled" ? actionButton : canToggle ? actionButton : ""}
12864
- ${plugin.status !== "uninstalled" ? `
12865
- <button onclick="openPluginSettings('${plugin.id}')" class="bg-white dark:bg-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-700 text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors">
12866
- Settings
12867
- </button>
12868
- ` : ""}
12970
+ ${actionButton}
12869
12971
  </div>
12870
12972
 
12871
12973
  <div class="flex items-center gap-2">
12872
12974
  ${plugin.status !== "uninstalled" ? `
12873
- <button onclick="showPluginDetails('${plugin.id}')" class="text-zinc-500 dark:text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-300 p-1.5 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Plugin Details">
12874
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12875
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
12876
- </svg>
12877
- </button>
12878
- ` : ""}
12879
-
12880
- ${!plugin.isCore && plugin.status !== "uninstalled" ? `
12881
- <button onclick="uninstallPlugin('${plugin.id}')" class="text-zinc-500 dark:text-zinc-400 hover:text-red-600 dark:hover:text-red-400 p-1.5 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Uninstall Plugin">
12882
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12883
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
12975
+ <button onclick="openPluginSettings('${plugin.id}')" class="text-zinc-400 hover:text-zinc-600 dark:text-zinc-500 dark:hover:text-zinc-300 p-1.5 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Settings">
12976
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
12977
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
12978
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
12884
12979
  </svg>
12885
12980
  </button>
12886
12981
  ` : ""}
@@ -13424,7 +13519,7 @@ function renderPluginSettingsPage(data) {
13424
13519
  user,
13425
13520
  content: pageContent
13426
13521
  };
13427
- return chunkMF7DWI5P_cjs.renderAdminLayout(layoutData);
13522
+ return chunkAZLU3ROK_cjs.renderAdminLayout(layoutData);
13428
13523
  }
13429
13524
  function renderStatusBadge(status) {
13430
13525
  const statusColors = {
@@ -13687,7 +13782,7 @@ function formatTimestamp(timestamp) {
13687
13782
 
13688
13783
  // src/routes/admin-plugins.ts
13689
13784
  var adminPluginRoutes = new hono.Hono();
13690
- adminPluginRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
13785
+ adminPluginRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
13691
13786
  var AVAILABLE_PLUGINS = [
13692
13787
  {
13693
13788
  id: "third-party-faq",
@@ -14203,7 +14298,7 @@ function formatLastUpdated(timestamp) {
14203
14298
  }
14204
14299
 
14205
14300
  // src/templates/pages/admin-logs-list.template.ts
14206
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
14301
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
14207
14302
  function renderLogsListPage(data) {
14208
14303
  const { logs, pagination, filters, user } = data;
14209
14304
  const content = `
@@ -14514,7 +14609,7 @@ function renderLogsListPage(data) {
14514
14609
  user,
14515
14610
  content
14516
14611
  };
14517
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
14612
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
14518
14613
  }
14519
14614
  function renderLogDetailsPage(data) {
14520
14615
  const { log, user } = data;
@@ -14726,7 +14821,7 @@ function renderLogDetailsPage(data) {
14726
14821
  </div>
14727
14822
  </div>
14728
14823
  `;
14729
- return chunkMF7DWI5P_cjs.adminLayoutV2({
14824
+ return chunkAZLU3ROK_cjs.adminLayoutV2({
14730
14825
  title: `Log Details - ${log.id}`,
14731
14826
  user,
14732
14827
  content
@@ -14969,7 +15064,7 @@ function renderLogConfigPage(data) {
14969
15064
 
14970
15065
  <script src="https://unpkg.com/htmx.org@1.9.6"></script>
14971
15066
  `;
14972
- return chunkMF7DWI5P_cjs.adminLayoutV2({
15067
+ return chunkAZLU3ROK_cjs.adminLayoutV2({
14973
15068
  title: "Log Configuration",
14974
15069
  user,
14975
15070
  content
@@ -14978,7 +15073,7 @@ function renderLogConfigPage(data) {
14978
15073
 
14979
15074
  // src/routes/admin-logs.ts
14980
15075
  var adminLogsRoutes = new hono.Hono();
14981
- adminLogsRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
15076
+ adminLogsRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
14982
15077
  adminLogsRoutes.get("/", async (c) => {
14983
15078
  try {
14984
15079
  const user = c.get("user");
@@ -15350,7 +15445,7 @@ adminDesignRoutes.get("/", (c) => {
15350
15445
  role: user.role
15351
15446
  } : void 0
15352
15447
  };
15353
- return c.html(chunkMF7DWI5P_cjs.renderDesignPage(pageData));
15448
+ return c.html(chunkAZLU3ROK_cjs.renderDesignPage(pageData));
15354
15449
  });
15355
15450
  var adminCheckboxRoutes = new hono.Hono();
15356
15451
  adminCheckboxRoutes.get("/", (c) => {
@@ -15362,7 +15457,7 @@ adminCheckboxRoutes.get("/", (c) => {
15362
15457
  role: user.role
15363
15458
  } : void 0
15364
15459
  };
15365
- return c.html(chunkMF7DWI5P_cjs.renderCheckboxPage(pageData));
15460
+ return c.html(chunkAZLU3ROK_cjs.renderCheckboxPage(pageData));
15366
15461
  });
15367
15462
 
15368
15463
  // src/templates/pages/admin-testimonials-form.template.ts
@@ -15390,7 +15485,7 @@ function renderTestimonialsForm(data) {
15390
15485
  </div>
15391
15486
  </div>
15392
15487
 
15393
- ${message ? chunkMF7DWI5P_cjs.renderAlert({ type: messageType || "info", message, dismissible: true }) : ""}
15488
+ ${message ? chunkAZLU3ROK_cjs.renderAlert({ type: messageType || "info", message, dismissible: true }) : ""}
15394
15489
 
15395
15490
  <!-- Form -->
15396
15491
  <div class="backdrop-blur-xl bg-white/10 rounded-xl border border-white/20 shadow-2xl">
@@ -15619,7 +15714,7 @@ function renderTestimonialsForm(data) {
15619
15714
  user: data.user,
15620
15715
  content: pageContent
15621
15716
  };
15622
- return chunkMF7DWI5P_cjs.renderAdminLayout(layoutData);
15717
+ return chunkAZLU3ROK_cjs.renderAdminLayout(layoutData);
15623
15718
  }
15624
15719
  function escapeHtml4(unsafe) {
15625
15720
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
@@ -15645,7 +15740,7 @@ adminTestimonialsRoutes.get("/", async (c) => {
15645
15740
  const offset = (currentPage - 1) * limit;
15646
15741
  const db = c.env?.DB;
15647
15742
  if (!db) {
15648
- return c.html(chunkMF7DWI5P_cjs.renderTestimonialsList({
15743
+ return c.html(chunkAZLU3ROK_cjs.renderTestimonialsList({
15649
15744
  testimonials: [],
15650
15745
  totalCount: 0,
15651
15746
  currentPage: 1,
@@ -15685,7 +15780,7 @@ adminTestimonialsRoutes.get("/", async (c) => {
15685
15780
  `;
15686
15781
  const { results: testimonials } = await db.prepare(dataQuery).bind(...params, limit, offset).all();
15687
15782
  const totalPages = Math.ceil(totalCount / limit);
15688
- return c.html(chunkMF7DWI5P_cjs.renderTestimonialsList({
15783
+ return c.html(chunkAZLU3ROK_cjs.renderTestimonialsList({
15689
15784
  testimonials: testimonials || [],
15690
15785
  totalCount,
15691
15786
  currentPage,
@@ -15699,7 +15794,7 @@ adminTestimonialsRoutes.get("/", async (c) => {
15699
15794
  } catch (error) {
15700
15795
  console.error("Error fetching testimonials:", error);
15701
15796
  const user = c.get("user");
15702
- return c.html(chunkMF7DWI5P_cjs.renderTestimonialsList({
15797
+ return c.html(chunkAZLU3ROK_cjs.renderTestimonialsList({
15703
15798
  testimonials: [],
15704
15799
  totalCount: 0,
15705
15800
  currentPage: 1,
@@ -16018,7 +16113,7 @@ function renderCodeExamplesForm(data) {
16018
16113
  </div>
16019
16114
  </div>
16020
16115
 
16021
- ${message ? chunkMF7DWI5P_cjs.renderAlert({ type: messageType || "info", message, dismissible: true }) : ""}
16116
+ ${message ? chunkAZLU3ROK_cjs.renderAlert({ type: messageType || "info", message, dismissible: true }) : ""}
16022
16117
 
16023
16118
  <!-- Form -->
16024
16119
  <div class="backdrop-blur-xl bg-white/10 rounded-xl border border-white/20 shadow-2xl">
@@ -16288,7 +16383,7 @@ function renderCodeExamplesForm(data) {
16288
16383
  user: data.user,
16289
16384
  content: pageContent
16290
16385
  };
16291
- return chunkMF7DWI5P_cjs.renderAdminLayout(layoutData);
16386
+ return chunkAZLU3ROK_cjs.renderAdminLayout(layoutData);
16292
16387
  }
16293
16388
  function escapeHtml5(unsafe) {
16294
16389
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
@@ -16315,7 +16410,7 @@ adminCodeExamplesRoutes.get("/", async (c) => {
16315
16410
  const offset = (currentPage - 1) * limit;
16316
16411
  const db = c.env?.DB;
16317
16412
  if (!db) {
16318
- return c.html(chunkMF7DWI5P_cjs.renderCodeExamplesList({
16413
+ return c.html(chunkAZLU3ROK_cjs.renderCodeExamplesList({
16319
16414
  codeExamples: [],
16320
16415
  totalCount: 0,
16321
16416
  currentPage: 1,
@@ -16355,7 +16450,7 @@ adminCodeExamplesRoutes.get("/", async (c) => {
16355
16450
  `;
16356
16451
  const { results: codeExamples } = await db.prepare(dataQuery).bind(...params, limit, offset).all();
16357
16452
  const totalPages = Math.ceil(totalCount / limit);
16358
- return c.html(chunkMF7DWI5P_cjs.renderCodeExamplesList({
16453
+ return c.html(chunkAZLU3ROK_cjs.renderCodeExamplesList({
16359
16454
  codeExamples: codeExamples || [],
16360
16455
  totalCount,
16361
16456
  currentPage,
@@ -16369,7 +16464,7 @@ adminCodeExamplesRoutes.get("/", async (c) => {
16369
16464
  } catch (error) {
16370
16465
  console.error("Error fetching code examples:", error);
16371
16466
  const user = c.get("user");
16372
- return c.html(chunkMF7DWI5P_cjs.renderCodeExamplesList({
16467
+ return c.html(chunkAZLU3ROK_cjs.renderCodeExamplesList({
16373
16468
  codeExamples: [],
16374
16469
  totalCount: 0,
16375
16470
  currentPage: 1,
@@ -16758,7 +16853,7 @@ function renderDashboardPage(data) {
16758
16853
  version: data.version,
16759
16854
  content: pageContent
16760
16855
  };
16761
- return chunkMF7DWI5P_cjs.renderAdminLayout(layoutData);
16856
+ return chunkAZLU3ROK_cjs.renderAdminLayout(layoutData);
16762
16857
  }
16763
16858
  function renderStatsCards(stats) {
16764
16859
  const cards = [
@@ -17306,9 +17401,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
17306
17401
  }
17307
17402
 
17308
17403
  // src/routes/admin-dashboard.ts
17309
- var VERSION = chunkW2IAEG4W_cjs.getCoreVersion();
17404
+ var VERSION = chunkSZE3XVET_cjs.getCoreVersion();
17310
17405
  var router = new hono.Hono();
17311
- router.use("*", chunk5NCBFP37_cjs.requireAuth());
17406
+ router.use("*", chunkXWPGIFS7_cjs.requireAuth());
17312
17407
  router.get("/", async (c) => {
17313
17408
  const user = c.get("user");
17314
17409
  try {
@@ -17533,7 +17628,7 @@ router.get("/system-status", async (c) => {
17533
17628
  });
17534
17629
 
17535
17630
  // src/templates/pages/admin-collections-list.template.ts
17536
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
17631
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
17537
17632
 
17538
17633
  // src/templates/components/table.template.ts
17539
17634
  function renderTable2(data) {
@@ -18007,11 +18102,11 @@ function renderCollectionsListPage(data) {
18007
18102
  version: data.version,
18008
18103
  content: pageContent
18009
18104
  };
18010
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
18105
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
18011
18106
  }
18012
18107
 
18013
18108
  // src/templates/pages/admin-collections-form.template.ts
18014
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
18109
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
18015
18110
  function getFieldTypeBadge(fieldType) {
18016
18111
  const typeLabels = {
18017
18112
  "text": "Text",
@@ -18272,7 +18367,7 @@ function renderCollectionFormPage(data) {
18272
18367
  }
18273
18368
  </style>
18274
18369
 
18275
- ${chunkMF7DWI5P_cjs.renderForm(formData)}
18370
+ ${chunkAZLU3ROK_cjs.renderForm(formData)}
18276
18371
 
18277
18372
  ${isEdit && data.managed ? `
18278
18373
  <!-- Read-Only Fields Display for Managed Collections -->
@@ -19061,12 +19156,12 @@ function renderCollectionFormPage(data) {
19061
19156
  version: data.version,
19062
19157
  content: pageContent
19063
19158
  };
19064
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
19159
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
19065
19160
  }
19066
19161
 
19067
19162
  // src/routes/admin-collections.ts
19068
19163
  var adminCollectionsRoutes = new hono.Hono();
19069
- adminCollectionsRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
19164
+ adminCollectionsRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
19070
19165
  adminCollectionsRoutes.get("/", async (c) => {
19071
19166
  try {
19072
19167
  const user = c.get("user");
@@ -19765,7 +19860,7 @@ adminCollectionsRoutes.post("/:collectionId/fields/reorder", async (c) => {
19765
19860
  });
19766
19861
 
19767
19862
  // src/templates/pages/admin-settings.template.ts
19768
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
19863
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
19769
19864
  function renderSettingsPage(data) {
19770
19865
  const activeTab = data.activeTab || "general";
19771
19866
  const pageContent = `
@@ -20147,7 +20242,7 @@ function renderSettingsPage(data) {
20147
20242
  version: data.version,
20148
20243
  content: pageContent
20149
20244
  };
20150
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
20245
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
20151
20246
  }
20152
20247
  function renderTabButton(tabId, label, iconPath, activeTab) {
20153
20248
  const isActive = activeTab === tabId;
@@ -21229,7 +21324,7 @@ function renderDatabaseToolsSettings(settings) {
21229
21324
 
21230
21325
  // src/routes/admin-settings.ts
21231
21326
  var adminSettingsRoutes = new hono.Hono();
21232
- adminSettingsRoutes.use("*", chunk5NCBFP37_cjs.requireAuth());
21327
+ adminSettingsRoutes.use("*", chunkXWPGIFS7_cjs.requireAuth());
21233
21328
  function getMockSettings(user) {
21234
21329
  return {
21235
21330
  general: {
@@ -21397,7 +21492,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
21397
21492
  adminSettingsRoutes.get("/api/migrations/status", async (c) => {
21398
21493
  try {
21399
21494
  const db = c.env.DB;
21400
- const migrationService = new chunkW4CE7XME_cjs.MigrationService(db);
21495
+ const migrationService = new chunkETS5XSAG_cjs.MigrationService(db);
21401
21496
  const status = await migrationService.getMigrationStatus();
21402
21497
  return c.json({
21403
21498
  success: true,
@@ -21421,7 +21516,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
21421
21516
  }, 403);
21422
21517
  }
21423
21518
  const db = c.env.DB;
21424
- const migrationService = new chunkW4CE7XME_cjs.MigrationService(db);
21519
+ const migrationService = new chunkETS5XSAG_cjs.MigrationService(db);
21425
21520
  const result = await migrationService.runPendingMigrations();
21426
21521
  return c.json({
21427
21522
  success: result.success,
@@ -21439,7 +21534,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
21439
21534
  adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
21440
21535
  try {
21441
21536
  const db = c.env.DB;
21442
- const migrationService = new chunkW4CE7XME_cjs.MigrationService(db);
21537
+ const migrationService = new chunkETS5XSAG_cjs.MigrationService(db);
21443
21538
  const validation = await migrationService.validateSchema();
21444
21539
  return c.json({
21445
21540
  success: true,
@@ -21684,5 +21779,5 @@ exports.auth_default = auth_default;
21684
21779
  exports.router = router;
21685
21780
  exports.test_cleanup_default = test_cleanup_default;
21686
21781
  exports.userRoutes = userRoutes;
21687
- //# sourceMappingURL=chunk-ARLXQU2S.cjs.map
21688
- //# sourceMappingURL=chunk-ARLXQU2S.cjs.map
21782
+ //# sourceMappingURL=chunk-D4PJFFOV.cjs.map
21783
+ //# sourceMappingURL=chunk-D4PJFFOV.cjs.map