zdashboard 2.8.4 → 2.8.6

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 (38) hide show
  1. package/dist/cli.js +520 -292
  2. package/dist/cli.js.map +1 -1
  3. package/dist/web/assets/CodeViewer-_jAxbWVL.js +10 -0
  4. package/dist/web/assets/{FileIcon-1KBdrfw3.js → FileIcon-68etP2t6.js} +1 -1
  5. package/dist/web/assets/{FilterPills-CCubROoX.js → FilterPills-DGfzMpn_.js} +1 -1
  6. package/dist/web/assets/ImageViewer-ChZdzYPf.js +298 -0
  7. package/dist/web/assets/{Sidebar-DE0UBp9b.js → Sidebar-D83uLTNy.js} +1 -1
  8. package/dist/web/assets/{Sidebar-D4sJtPI_.js → Sidebar-N0HiMJRK.js} +1 -1
  9. package/dist/web/assets/Workspace-BE7p2hNz.js +1 -0
  10. package/dist/web/assets/Workspace-BpSq60xh.js +1 -0
  11. package/dist/web/assets/{Workspace-CvG5lIPT.js → Workspace-DW8xX3Tg.js} +1 -1
  12. package/dist/web/assets/Workspace-DjL1fdji.js +2 -0
  13. package/dist/web/assets/Workspace-DrN9--Je.js +1 -0
  14. package/dist/web/assets/index-DH1Mqw-g.css +1 -0
  15. package/dist/web/assets/{index-Co7MWSFf.js → index-D_uh-zkI.js} +3 -3
  16. package/dist/web/assets/{usePluginData-IoRqReIJ.js → usePluginData-Bk2M7LNj.js} +1 -1
  17. package/dist/web/assets/{web-BZSTgldm.js → web-BZukfmmT.js} +2 -2
  18. package/dist/web/assets/{web-DOEmlI8d.js → web-CEB9kTEr.js} +2 -2
  19. package/dist/web/assets/web-CGvrDSNo.js +2 -0
  20. package/dist/web/assets/{web-DxbVzGf3.js → web-CLKkH6eO.js} +2 -2
  21. package/dist/web/assets/web-CbW9uGDV.js +2 -0
  22. package/dist/web/index.html +2 -2
  23. package/package.json +1 -1
  24. package/dist/web/assets/BatchView-pClKtVa8.js +0 -2
  25. package/dist/web/assets/CheckpointViewer-CAbXbMti.js +0 -1
  26. package/dist/web/assets/CodeViewer-Cs0mG1h6.js +0 -279
  27. package/dist/web/assets/DependencyGraph-Czdg00AM.js +0 -1
  28. package/dist/web/assets/ProgressBar-DVfldtzq.js +0 -1
  29. package/dist/web/assets/SingleChangeView-BYcaGOyR.js +0 -1
  30. package/dist/web/assets/ViewHeader-DPfbaJmK.js +0 -1
  31. package/dist/web/assets/Workspace-B7MhdnwY.js +0 -2
  32. package/dist/web/assets/Workspace-CAcrVL-m.js +0 -1
  33. package/dist/web/assets/Workspace-DWHHR42N.js +0 -1
  34. package/dist/web/assets/Workspace-cXLWoMNL.js +0 -1
  35. package/dist/web/assets/index-CwS-KyOV.css +0 -1
  36. package/dist/web/assets/index-D6EJRHBV.js +0 -29
  37. package/dist/web/assets/web-CX1FEFxC.js +0 -2
  38. package/dist/web/assets/web-DLwxO4fO.js +0 -2
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import path11 from "path";
5
- import fs11 from "fs";
4
+ import path9 from "path";
5
+ import fs9 from "fs";
6
6
  import { fileURLToPath as fileURLToPath2 } from "url";
7
7
  import { access, readdir } from "fs/promises";
8
8
  import { parseArgs as utilParseArgs } from "util";
@@ -43,7 +43,7 @@ import { fileURLToPath } from "url";
43
43
  // package.json
44
44
  var package_default = {
45
45
  name: "zdashboard",
46
- version: "2.8.4",
46
+ version: "2.8.6",
47
47
  description: "ZCode skill dashboard platform \u2014 pluggable viewers for zdesign/zview/zreview/zgoal",
48
48
  type: "module",
49
49
  bin: {
@@ -452,13 +452,13 @@ var ServerService = class extends Service {
452
452
  });
453
453
  this.start(config.port);
454
454
  }
455
- route(path12, handler) {
456
- this.routes.set(path12, handler);
457
- this.ctx.effect(() => () => this.routes.delete(path12));
455
+ route(path10, handler) {
456
+ this.routes.set(path10, handler);
457
+ this.ctx.effect(() => () => this.routes.delete(path10));
458
458
  }
459
- sse(path12, onConnect) {
460
- this.sses.set(path12, onConnect);
461
- this.ctx.effect(() => () => this.sses.delete(path12));
459
+ sse(path10, onConnect) {
460
+ this.sses.set(path10, onConnect);
461
+ this.ctx.effect(() => () => this.sses.delete(path10));
462
462
  }
463
463
  static(prefix, dir) {
464
464
  this.prefixStatic.set(prefix, dir);
@@ -757,7 +757,7 @@ import path5 from "path";
757
757
  import fs5 from "fs";
758
758
  import path4 from "path";
759
759
  function walkDir(root, opts = {}) {
760
- const { skip = /* @__PURE__ */ new Set(), maxDepth, onFile, onDir } = opts;
760
+ const { skip = /* @__PURE__ */ new Set(), maxDepth, allowDotDirs = false, onFile, onDir } = opts;
761
761
  function walk(dir, rel, depth) {
762
762
  if (typeof maxDepth === "number" && depth > maxDepth) return;
763
763
  let ents;
@@ -767,7 +767,7 @@ function walkDir(root, opts = {}) {
767
767
  return;
768
768
  }
769
769
  for (const ent of ents) {
770
- if (ent.name.startsWith(".")) continue;
770
+ if (ent.name.startsWith(".") && !(allowDotDirs && ent.isDirectory())) continue;
771
771
  if (skip.has(ent.name)) continue;
772
772
  const abs = path4.join(dir, ent.name);
773
773
  const r = rel ? `${rel}/${ent.name}` : ent.name;
@@ -835,12 +835,14 @@ function prefixPaths(nodes, prefix) {
835
835
  function scanTree(root, scanDirs, opts) {
836
836
  const tree = [];
837
837
  const defaultExpandDepth = typeof opts?.defaultExpandDepth === "number" ? opts.defaultExpandDepth : 2;
838
+ const dotDeclared = new Set(Array.isArray(opts?.dotDirs) ? opts.dotDirs : []);
838
839
  const dirs = Array.isArray(scanDirs) ? scanDirs.filter((d) => typeof d === "string" && d) : [];
839
840
  for (const dir of dirs) {
841
+ if (dir.startsWith(".") && !dotDeclared.has(dir)) continue;
840
842
  const dirPath = path5.join(root, dir);
841
843
  if (!fs6.existsSync(dirPath)) continue;
842
844
  const paths = [];
843
- walkDir(dirPath, { maxDepth: 4, onFile: (_, rel) => paths.push(rel) });
845
+ walkDir(dirPath, { maxDepth: 4, allowDotDirs: dotDeclared.has(dir), onFile: (_, rel) => paths.push(rel) });
844
846
  const prefix = dir;
845
847
  const children = prefixPaths(buildTree(paths, defaultExpandDepth), prefix);
846
848
  if (children.length) {
@@ -851,7 +853,8 @@ function scanTree(root, scanDirs, opts) {
851
853
  }
852
854
 
853
855
  // src/core/tree.ts
854
- var CONVENTION_SCAN_DIRS = ["openspec", "docs"];
856
+ var CONVENTION_SCAN_DIRS = ["openspec", "docs", ".zdev/apply"];
857
+ var CONVENTION_DOT_DIRS = CONVENTION_SCAN_DIRS.filter((d) => d.startsWith("."));
855
858
  var apply = {
856
859
  inject: ["server"],
857
860
  apply(ctx, config) {
@@ -862,7 +865,7 @@ var apply = {
862
865
  const url = new URL(req.url || "/", "http://localhost");
863
866
  const wt = url.searchParams.get("wt");
864
867
  const scanRoot = wt ? decodeURIComponent(wt) : config.root;
865
- const tree = scanTree(scanRoot, [...CONVENTION_SCAN_DIRS]);
868
+ const tree = scanTree(scanRoot, [...CONVENTION_SCAN_DIRS], { dotDirs: [...CONVENTION_DOT_DIRS] });
866
869
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
867
870
  res.end(JSON.stringify({ tree }));
868
871
  } catch {
@@ -1228,18 +1231,18 @@ function definePlugin(def) {
1228
1231
  );
1229
1232
  return { ...defaults, ...stored };
1230
1233
  },
1231
- route(path12, handler) {
1232
- ctx.server.route(path12, respondWith(handler));
1234
+ route(path10, handler) {
1235
+ ctx.server.route(path10, respondWith(handler));
1233
1236
  },
1234
- guardedRoute(path12, handler) {
1237
+ guardedRoute(path10, handler) {
1235
1238
  const token = () => ctx.server.stopToken;
1236
- ctx.server.route(path12, (req, res) => {
1239
+ ctx.server.route(path10, (req, res) => {
1237
1240
  if (!guarded(req, res, token())) return;
1238
1241
  void respondWith(handler)(req, res);
1239
1242
  });
1240
1243
  },
1241
- sse(path12, handler) {
1242
- ctx.server.sse(path12, handler);
1244
+ sse(path10, handler) {
1245
+ ctx.server.sse(path10, handler);
1243
1246
  },
1244
1247
  static(prefix, dir) {
1245
1248
  ctx.server.static(prefix, dir);
@@ -1350,240 +1353,12 @@ var apply3 = defineBuiltin({
1350
1353
  }
1351
1354
  });
1352
1355
 
1353
- // src/plugins/apply/scan.ts
1354
- import fs7 from "fs";
1355
- import path6 from "path";
1356
-
1357
- // src/plugins/apply/parse-tasks.ts
1358
- var TASK_RE = /^\s*-\s*\[([ xX])\]\s*(.*)$/;
1359
- var MANUAL_PREFIX = "\u{1F527}[\u4EBA\u5DE5]";
1360
- function parseTasks(md) {
1361
- return md.split(/\r?\n/).filter((l) => TASK_RE.test(l)).map((l) => {
1362
- const m = l.match(TASK_RE);
1363
- const text = (m?.[2] ?? l).trim();
1364
- return { text, checked: m?.[1] !== " ", manual: text.startsWith(MANUAL_PREFIX) };
1365
- });
1366
- }
1367
- function countTasks(md) {
1368
- const tasks = parseTasks(md);
1369
- const normal = tasks.filter((t) => !t.manual);
1370
- return {
1371
- total: normal.length,
1372
- done: normal.filter((t) => t.checked).length,
1373
- manual: tasks.length - normal.length
1374
- };
1375
- }
1376
-
1377
- // src/plugins/apply/scan.ts
1378
- function readText(p) {
1379
- try {
1380
- return fs7.readFileSync(p, "utf8");
1381
- } catch {
1382
- return "";
1383
- }
1384
- }
1385
- function worktreeDir(root, name) {
1386
- return path6.join(root, ".zworktree", name);
1387
- }
1388
- function changeDir(root, name) {
1389
- return path6.join(root, "openspec", "changes", name);
1390
- }
1391
- function parseDependsOn(proposal) {
1392
- const deps = [];
1393
- const m = proposal.match(/^##\s+依赖\s*\n([\s\S]*?)(?:\n#{1,6}\s|\n---\s*\n|$)/i);
1394
- if (!m) return deps;
1395
- for (const line of m[1].split("\n")) {
1396
- if (!/^-\s+/.test(line)) continue;
1397
- const name = line.replace(/^-\s+/, "").trim();
1398
- if (name) deps.push(name);
1399
- }
1400
- return deps;
1401
- }
1402
- function scanApplyChanges(root) {
1403
- const changesDir = path6.join(root, "openspec", "changes");
1404
- if (!fs7.existsSync(changesDir)) return [];
1405
- const out = [];
1406
- for (const ent of fs7.readdirSync(changesDir, { withFileTypes: true })) {
1407
- if (!ent.isDirectory() || ent.name.startsWith(".") || ent.name === "archive") continue;
1408
- const wt = worktreeDir(root, ent.name);
1409
- const tasks = readText(path6.join(wt, "openspec", "changes", ent.name, "tasks.md"));
1410
- if (!tasks) {
1411
- const fallback = readText(path6.join(changesDir, ent.name, "tasks.md"));
1412
- if (fallback) {
1413
- const { total, done } = countTasks(fallback);
1414
- const wtChange = path6.join(wt, "openspec", "changes", ent.name);
1415
- const mainChange = path6.join(changesDir, ent.name);
1416
- out.push({
1417
- name: ent.name,
1418
- path: `openspec/changes/${ent.name}`,
1419
- total,
1420
- done,
1421
- hasProposal: fs7.existsSync(path6.join(wtChange, "proposal.md")) || fs7.existsSync(path6.join(mainChange, "proposal.md")),
1422
- hasDesign: fs7.existsSync(path6.join(wtChange, "design.md")) || fs7.existsSync(path6.join(mainChange, "design.md")),
1423
- // worktree 目录存在即算执行中(tasks.md 可能尚未落盘)
1424
- inWorktree: fs7.existsSync(wtChange)
1425
- });
1426
- }
1427
- } else {
1428
- const { total, done } = countTasks(tasks);
1429
- const wtChange = path6.join(wt, "openspec", "changes", ent.name);
1430
- const mainChange = path6.join(changesDir, ent.name);
1431
- out.push({
1432
- name: ent.name,
1433
- path: `openspec/changes/${ent.name}`,
1434
- total,
1435
- done,
1436
- // 任一侧存在即算(详情读取是 worktree 优先、主目录兜底,列表标记保持一致)
1437
- hasProposal: fs7.existsSync(path6.join(wtChange, "proposal.md")) || fs7.existsSync(path6.join(mainChange, "proposal.md")),
1438
- hasDesign: fs7.existsSync(path6.join(wtChange, "design.md")) || fs7.existsSync(path6.join(mainChange, "design.md")),
1439
- inWorktree: true
1440
- });
1441
- }
1442
- }
1443
- out.sort((a, b) => a.name.localeCompare(b.name));
1444
- return out;
1445
- }
1446
- function readApplyChange(root, name) {
1447
- const wt = worktreeDir(root, name);
1448
- const mainChangeDir = changeDir(root, name);
1449
- const proposalFrom = fs7.existsSync(path6.join(wt, "openspec", "changes", name, "proposal.md")) ? path6.join(wt, "openspec", "changes", name, "proposal.md") : path6.join(mainChangeDir, "proposal.md");
1450
- const designFrom = fs7.existsSync(path6.join(wt, "openspec", "changes", name, "design.md")) ? path6.join(wt, "openspec", "changes", name, "design.md") : path6.join(mainChangeDir, "design.md");
1451
- const tasksFrom = fs7.existsSync(path6.join(wt, "openspec", "changes", name, "tasks.md")) ? path6.join(wt, "openspec", "changes", name, "tasks.md") : path6.join(mainChangeDir, "tasks.md");
1452
- const proposal = readText(proposalFrom);
1453
- const design = readText(designFrom);
1454
- const tasks = readText(tasksFrom);
1455
- const { total, done } = countTasks(tasks);
1456
- const inWorktree = fs7.existsSync(path6.join(wt, "openspec", "changes", name));
1457
- const dependsOn = parseDependsOn(proposal);
1458
- const hasTestStrategy = /^##\s+测试策略\s*$/m.test(design);
1459
- return {
1460
- name,
1461
- path: `openspec/changes/${name}`,
1462
- total,
1463
- done,
1464
- hasProposal: !!proposal,
1465
- hasDesign: !!design,
1466
- inWorktree,
1467
- proposal,
1468
- design,
1469
- tasks,
1470
- dependsOn,
1471
- hasTestStrategy
1472
- };
1473
- }
1474
-
1475
- // src/plugins/apply/batch.ts
1476
- import fs8 from "fs";
1477
- import path7 from "path";
1478
- var RUN_ID_PATTERN = /^[A-Za-z0-9-]+$/;
1479
- function isPlainObject(v) {
1480
- return typeof v === "object" && v !== null && !Array.isArray(v);
1481
- }
1482
- function readText2(p) {
1483
- try {
1484
- return fs8.readFileSync(p, "utf8");
1485
- } catch {
1486
- return null;
1487
- }
1488
- }
1489
- function readBatchState(root, explicitRun) {
1490
- const applyDir = path7.join(root, ".zdev", "apply");
1491
- const requested = explicitRun?.trim() ?? "";
1492
- const runId = requested && RUN_ID_PATTERN.test(requested) ? requested : readText2(path7.join(applyDir, "CURRENT"))?.trim() ?? "";
1493
- if (!runId || !RUN_ID_PATTERN.test(runId)) {
1494
- return { run: null, state: null };
1495
- }
1496
- const parsed = (() => {
1497
- const text = readText2(path7.join(applyDir, "runs", runId, "state.json"));
1498
- if (text == null) return null;
1499
- try {
1500
- return JSON.parse(text);
1501
- } catch {
1502
- return null;
1503
- }
1504
- })();
1505
- if (!isPlainObject(parsed)) {
1506
- return { run: { id: runId }, state: null };
1507
- }
1508
- return { run: { id: runId }, state: parsed };
1509
- }
1510
- function projectGraph(state) {
1511
- if (!state) {
1512
- return { changes: [], batches: [], conflicts: [] };
1513
- }
1514
- const rawChanges = state.changes;
1515
- const rawBatches = state.batches;
1516
- const rawConflicts = state.conflicts;
1517
- return {
1518
- changes: (Array.isArray(rawChanges) ? rawChanges : []).map((c) => ({
1519
- name: c.name,
1520
- status: c.status,
1521
- dependencies: Array.isArray(c?.dependencies) ? c.dependencies : [],
1522
- batchIndex: c.batchIndex
1523
- })),
1524
- batches: Array.isArray(rawBatches) ? rawBatches : [],
1525
- conflicts: Array.isArray(rawConflicts) ? rawConflicts : []
1526
- };
1527
- }
1528
- var BATCH_LOGS_TAIL = 100;
1529
- function tailLogs(state) {
1530
- const logs = state?.logs;
1531
- return (Array.isArray(logs) ? logs : []).slice(-BATCH_LOGS_TAIL);
1532
- }
1533
- function readBatchPlan(root, runId) {
1534
- return readText2(path7.join(root, ".zdev", "apply", "runs", runId, "plan.md"));
1535
- }
1536
-
1537
- // src/plugins/apply/manifest.ts
1538
- var manifest2 = {
1539
- mode: "apply",
1540
- label: "\u6267\u884C\u8FDB\u5EA6",
1541
- icon: "\u2699\uFE0F",
1542
- description: "OpenSpec \u6267\u884C\u8FDB\u5EA6 \xB7 zapply \u5355 change \u4E0E\u6279\u91CF",
1543
- order: 40
1544
- };
1545
-
1546
- // src/plugins/apply/index.ts
1547
- function errorResponse(res, status, msg) {
1548
- res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
1549
- res.end(JSON.stringify({ error: msg }));
1550
- return void 0;
1551
- }
1552
- var apply4 = defineBuiltin({
1553
- manifest: manifest2,
1554
- setup(ctx, root) {
1555
- ctx.route("/__apply", async () => scanApplyChanges(root));
1556
- ctx.route("/__apply/change", async (req, res) => {
1557
- const url = new URL(req.url || "", "http://x");
1558
- const name = url.searchParams.get("name");
1559
- const bad = (msg) => errorResponse(res, 400, msg);
1560
- if (!name) return bad("missing name");
1561
- if (name.includes("..") || name.includes("/") || name.includes("\\")) return bad("invalid name");
1562
- try {
1563
- return readApplyChange(root, name);
1564
- } catch (e) {
1565
- return bad(e instanceof Error ? e.message : "unknown error");
1566
- }
1567
- });
1568
- const queryRun = (req) => new URL(req.url || "", "http://x").searchParams.get("run") ?? void 0;
1569
- ctx.route("/__apply/batch", async (req) => readBatchState(root, queryRun(req)));
1570
- ctx.route("/__apply/batch/graph", async (req) => projectGraph(readBatchState(root, queryRun(req)).state));
1571
- ctx.route("/__apply/batch/logs", async (req) => tailLogs(readBatchState(root, queryRun(req)).state));
1572
- ctx.route("/__apply/batch/plan", async (req, res) => {
1573
- const snap = readBatchState(root, queryRun(req));
1574
- const plan = snap.run ? readBatchPlan(root, snap.run.id) : null;
1575
- if (plan == null) return errorResponse(res, 404, "plan not found");
1576
- return { plan };
1577
- });
1578
- }
1579
- });
1580
-
1581
1356
  // src/plugins/design/index.ts
1582
- import path9 from "path";
1583
- import fs9 from "fs";
1357
+ import path7 from "path";
1358
+ import fs7 from "fs";
1584
1359
 
1585
1360
  // src/server/design-assets.ts
1586
- import path8 from "path";
1361
+ import path6 from "path";
1587
1362
  var PAGE_EXTS = [".html", ".htm"];
1588
1363
  var ICON_EXTS = [".svg", ".png", ".ico", ".jpg", ".jpeg", ".gif", ".webp"];
1589
1364
  var VIDEO_EXTS = [".mp4", ".webm", ".mov", ".ogg", ".ogv"];
@@ -1611,17 +1386,17 @@ function scanAssets(root, subDir = "") {
1611
1386
  const keys = ["page", "component", "icon", "token", "md", "video", "audio", "pdf", "font"];
1612
1387
  for (const k of keys) out[k] = [];
1613
1388
  const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".cache"]);
1614
- const scanRoot = subDir ? path8.join(root, subDir) : root;
1389
+ const scanRoot = subDir ? path6.join(root, subDir) : root;
1615
1390
  walkDir(scanRoot, { skip: SKIP, onFile: (abs, rel) => {
1616
- const ext = path8.extname(abs).toLowerCase();
1391
+ const ext = path6.extname(abs).toLowerCase();
1617
1392
  const t = categorize(rel, ext);
1618
- if (t) out[t].push({ path: rel, name: path8.basename(abs), ext, type: t });
1393
+ if (t) out[t].push({ path: rel, name: path6.basename(abs), ext, type: t });
1619
1394
  } });
1620
1395
  return out;
1621
1396
  }
1622
1397
 
1623
1398
  // src/plugins/design/manifest.ts
1624
- var manifest3 = {
1399
+ var manifest2 = {
1625
1400
  mode: "design",
1626
1401
  label: "\u8BBE\u8BA1\u8D44\u4EA7",
1627
1402
  icon: "\u{1F3A8}",
@@ -1636,35 +1411,35 @@ function emptyScan() {
1636
1411
  for (const k of ASSET_KEYS) out[k] = [];
1637
1412
  return out;
1638
1413
  }
1639
- var apply5 = defineBuiltin({
1640
- manifest: manifest3,
1414
+ var apply4 = defineBuiltin({
1415
+ manifest: manifest2,
1641
1416
  setup(ctx, root) {
1642
1417
  ctx.route("/__design/assets", async () => {
1643
- const designRoot = path9.join(root, ".zdev", "design");
1644
- return fs9.existsSync(designRoot) ? scanAssets(designRoot) : emptyScan();
1418
+ const designRoot = path7.join(root, ".zdev", "design");
1419
+ return fs7.existsSync(designRoot) ? scanAssets(designRoot) : emptyScan();
1645
1420
  });
1646
1421
  ctx.route("/__design/asset", async (req, res) => {
1647
1422
  const rel = new URL(req.url || "/", "http://localhost").searchParams.get("path") ?? "";
1648
- if (!rel || rel.includes("..") || rel.includes("\\") || path9.isAbsolute(rel)) {
1423
+ if (!rel || rel.includes("..") || rel.includes("\\") || path7.isAbsolute(rel)) {
1649
1424
  res.writeHead(400);
1650
1425
  res.end("bad path");
1651
1426
  return;
1652
1427
  }
1653
- const designRoot = path9.join(root, ".zdev", "design");
1654
- const filePath = path9.join(designRoot, rel);
1655
- if (!filePath.startsWith(designRoot + path9.sep)) {
1428
+ const designRoot = path7.join(root, ".zdev", "design");
1429
+ const filePath = path7.join(designRoot, rel);
1430
+ if (!filePath.startsWith(designRoot + path7.sep)) {
1656
1431
  res.writeHead(400);
1657
1432
  res.end("bad path");
1658
1433
  return;
1659
1434
  }
1660
- fs9.stat(filePath, (err) => {
1435
+ fs7.stat(filePath, (err) => {
1661
1436
  if (err) {
1662
1437
  res.writeHead(404);
1663
1438
  return res.end("Not found");
1664
1439
  }
1665
- const ct = MIME[path9.extname(filePath).toLowerCase()] ?? "application/octet-stream";
1440
+ const ct = MIME[path7.extname(filePath).toLowerCase()] ?? "application/octet-stream";
1666
1441
  res.writeHead(200, { "Content-Type": ct, "Cache-Control": "no-cache" });
1667
- const stream = fs9.createReadStream(filePath);
1442
+ const stream = fs7.createReadStream(filePath);
1668
1443
  stream.on("error", () => {
1669
1444
  try {
1670
1445
  res.destroy();
@@ -1678,7 +1453,7 @@ var apply5 = defineBuiltin({
1678
1453
  });
1679
1454
 
1680
1455
  // src/plugins/view/manifest.ts
1681
- var manifest4 = {
1456
+ var manifest3 = {
1682
1457
  mode: "view",
1683
1458
  label: "\u9879\u76EE\u6D4F\u89C8",
1684
1459
  icon: "\u{1F441}\uFE0F",
@@ -1687,19 +1462,19 @@ var manifest4 = {
1687
1462
  };
1688
1463
 
1689
1464
  // src/plugins/view/index.ts
1690
- var apply6 = defineBuiltin({
1691
- manifest: manifest4,
1465
+ var apply5 = defineBuiltin({
1466
+ manifest: manifest3,
1692
1467
  setup() {
1693
1468
  }
1694
1469
  });
1695
1470
 
1696
1471
  // src/plugins/stats/index.ts
1697
- import fs10 from "fs";
1698
- import path10 from "path";
1472
+ import fs8 from "fs";
1473
+ import path8 from "path";
1699
1474
  import { execFile as execFile6 } from "child_process";
1700
1475
 
1701
1476
  // src/plugins/stats/manifest.ts
1702
- var manifest5 = {
1477
+ var manifest4 = {
1703
1478
  mode: "stats",
1704
1479
  label: "\u9879\u76EE\u7EDF\u8BA1",
1705
1480
  icon: "\u{1F4CA}",
@@ -1716,14 +1491,14 @@ function execFilePromise(file, args, opts) {
1716
1491
  }
1717
1492
  function scan(root) {
1718
1493
  const stats = {
1719
- root: path10.basename(root),
1494
+ root: path8.basename(root),
1720
1495
  files: 0,
1721
1496
  dirs: 0,
1722
1497
  totalSize: 0,
1723
1498
  byExt: [],
1724
1499
  markdown: 0,
1725
1500
  openspec: { active: 0, archived: 0 },
1726
- hasJust: fs10.existsSync(path10.join(root, "justfile")),
1501
+ hasJust: fs8.existsSync(path8.join(root, "justfile")),
1727
1502
  worktrees: 0
1728
1503
  };
1729
1504
  const extMap = /* @__PURE__ */ new Map();
@@ -1733,22 +1508,22 @@ function scan(root) {
1733
1508
  }, onFile: (abs) => {
1734
1509
  stats.files++;
1735
1510
  try {
1736
- stats.totalSize += fs10.statSync(abs).size;
1511
+ stats.totalSize += fs8.statSync(abs).size;
1737
1512
  } catch {
1738
1513
  }
1739
- const ext = path10.extname(abs).toLowerCase() || "(\u65E0\u6269\u5C55\u540D)";
1514
+ const ext = path8.extname(abs).toLowerCase() || "(\u65E0\u6269\u5C55\u540D)";
1740
1515
  extMap.set(ext, (extMap.get(ext) ?? 0) + 1);
1741
1516
  if (ext === ".md" || ext === ".markdown") stats.markdown++;
1742
1517
  } });
1743
1518
  };
1744
1519
  walk(root);
1745
- const changesDir = path10.join(root, "openspec", "changes");
1746
- if (fs10.existsSync(changesDir)) {
1747
- for (const ent of fs10.readdirSync(changesDir, { withFileTypes: true })) {
1520
+ const changesDir = path8.join(root, "openspec", "changes");
1521
+ if (fs8.existsSync(changesDir)) {
1522
+ for (const ent of fs8.readdirSync(changesDir, { withFileTypes: true })) {
1748
1523
  if (!ent.isDirectory() || ent.name.startsWith(".")) continue;
1749
1524
  if (ent.name === "archive") {
1750
1525
  try {
1751
- stats.openspec.archived = fs10.readdirSync(path10.join(changesDir, "archive"), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).length;
1526
+ stats.openspec.archived = fs8.readdirSync(path8.join(changesDir, "archive"), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).length;
1752
1527
  } catch {
1753
1528
  }
1754
1529
  } else stats.openspec.active++;
@@ -1757,8 +1532,8 @@ function scan(root) {
1757
1532
  stats.byExt = Array.from(extMap.entries()).map(([ext, count]) => ({ ext, count })).sort((a, b) => b.count - a.count).slice(0, 10);
1758
1533
  return stats;
1759
1534
  }
1760
- var apply7 = defineBuiltin({
1761
- manifest: manifest5,
1535
+ var apply6 = defineBuiltin({
1536
+ manifest: manifest4,
1762
1537
  setup(ctx, root) {
1763
1538
  ctx.route("/__stats/data", async () => {
1764
1539
  const stats = scan(root);
@@ -1783,8 +1558,461 @@ var apply7 = defineBuiltin({
1783
1558
  }
1784
1559
  });
1785
1560
 
1561
+ // src/plugins/market/manifest.ts
1562
+ var manifest5 = {
1563
+ mode: "market",
1564
+ label: "\u7075\u611F\u5E02\u573A",
1565
+ icon: "\u2728",
1566
+ description: "Logo / \u52A8\u6548 / \u8BBE\u8BA1\u7075\u611F\u6D4F\u89C8 \xB7 \u8F6C\u63D0\u793A\u8BCD\u95ED\u73AF",
1567
+ order: 40
1568
+ };
1569
+
1570
+ // src/plugins/market/sources/logotypes.ts
1571
+ var LOGOTYPES = [
1572
+ // ---- tech 消费与硬件 ----
1573
+ { id: "apple", name: "Apple", category: "tech" },
1574
+ { id: "google", name: "Google", category: "tech" },
1575
+ { id: "samsung", name: "Samsung", category: "tech" },
1576
+ { id: "huawei", name: "Huawei", category: "tech" },
1577
+ { id: "xiaomi", name: "Xiaomi", category: "tech" },
1578
+ { id: "sony", name: "Sony", category: "tech" },
1579
+ { id: "panasonic", name: "Panasonic", category: "tech" },
1580
+ { id: "lg", name: "LG", category: "tech" },
1581
+ { id: "nvidia", name: "NVIDIA", category: "tech" },
1582
+ { id: "amd", name: "AMD", category: "tech" },
1583
+ { id: "intel", name: "Intel", category: "tech" },
1584
+ { id: "oracle", name: "Oracle", category: "tech" },
1585
+ { id: "dell", name: "Dell", category: "tech" },
1586
+ { id: "asus", name: "ASUS", category: "tech" },
1587
+ { id: "lenovo", name: "Lenovo", category: "tech" },
1588
+ { id: "razer", name: "Razer", category: "tech" },
1589
+ { id: "logitech", name: "Logitech", category: "tech" },
1590
+ { id: "nikon", name: "Nikon", category: "tech" },
1591
+ { id: "tesla", name: "Tesla", category: "tech" },
1592
+ { id: "nokia", name: "Nokia", category: "tech" },
1593
+ { id: "oneplus", name: "OnePlus", category: "tech" },
1594
+ { id: "oppo", name: "OPPO", category: "tech" },
1595
+ // ---- social 社交内容 ----
1596
+ { id: "facebook", name: "Facebook", category: "social" },
1597
+ { id: "instagram", name: "Instagram", category: "social" },
1598
+ { id: "whatsapp", name: "WhatsApp", category: "social" },
1599
+ { id: "signal", name: "Signal", category: "social" },
1600
+ { id: "telegram", name: "Telegram", category: "social" },
1601
+ { id: "discord", name: "Discord", category: "social" },
1602
+ { id: "slack", name: "Slack", category: "social" },
1603
+ { id: "x", name: "X", category: "social" },
1604
+ { id: "threads", name: "Threads", category: "social" },
1605
+ { id: "bluesky", name: "Bluesky", category: "social" },
1606
+ { id: "mastodon", name: "Mastodon", category: "social" },
1607
+ { id: "reddit", name: "Reddit", category: "social" },
1608
+ { id: "pinterest", name: "Pinterest", category: "social" },
1609
+ { id: "tiktok", name: "TikTok", category: "social" },
1610
+ { id: "youtube", name: "YouTube", category: "social" },
1611
+ { id: "twitch", name: "Twitch", category: "social" },
1612
+ { id: "wechat", name: "WeChat", category: "social" },
1613
+ { id: "sinaweibo", name: "Sina Weibo", category: "social" },
1614
+ { id: "bilibili", name: "bilibili", category: "social" },
1615
+ { id: "tencentqq", name: "Tencent QQ", category: "social" },
1616
+ // ---- dev 开发 ----
1617
+ { id: "github", name: "GitHub", category: "dev" },
1618
+ { id: "gitlab", name: "GitLab", category: "dev" },
1619
+ { id: "bitbucket", name: "Bitbucket", category: "dev" },
1620
+ { id: "git", name: "Git", category: "dev" },
1621
+ { id: "githubactions", name: "GitHub Actions", category: "dev" },
1622
+ { id: "react", name: "React", category: "dev" },
1623
+ { id: "vuedotjs", name: "Vue.js", category: "dev" },
1624
+ { id: "angular", name: "Angular", category: "dev" },
1625
+ { id: "svelte", name: "Svelte", category: "dev" },
1626
+ { id: "nextdotjs", name: "Next.js", category: "dev" },
1627
+ { id: "nuxtdotjs", name: "Nuxt", category: "dev" },
1628
+ { id: "astro", name: "Astro", category: "dev" },
1629
+ { id: "typescript", name: "TypeScript", category: "dev" },
1630
+ { id: "javascript", name: "JavaScript", category: "dev" },
1631
+ { id: "nodedotjs", name: "Node.js", category: "dev" },
1632
+ { id: "python", name: "Python", category: "dev" },
1633
+ { id: "rust", name: "Rust", category: "dev" },
1634
+ { id: "go", name: "Go", category: "dev" },
1635
+ { id: "swift", name: "Swift", category: "dev" },
1636
+ { id: "kotlin", name: "Kotlin", category: "dev" },
1637
+ { id: "php", name: "PHP", category: "dev" },
1638
+ { id: "laravel", name: "Laravel", category: "dev" },
1639
+ { id: "ruby", name: "Ruby", category: "dev" },
1640
+ { id: "rubyonrails", name: "Ruby on Rails", category: "dev" },
1641
+ { id: "cplusplus", name: "C++", category: "dev" },
1642
+ { id: "sass", name: "Sass", category: "dev" },
1643
+ { id: "tailwindcss", name: "Tailwind CSS", category: "dev" },
1644
+ { id: "pnpm", name: "pnpm", category: "dev" },
1645
+ { id: "npm", name: "npm", category: "dev" },
1646
+ { id: "yarn", name: "Yarn", category: "dev" },
1647
+ { id: "vite", name: "Vite", category: "dev" },
1648
+ { id: "vitest", name: "Vitest", category: "dev" },
1649
+ { id: "storybook", name: "Storybook", category: "dev" },
1650
+ { id: "docker", name: "Docker", category: "dev" },
1651
+ { id: "kubernetes", name: "Kubernetes", category: "dev" },
1652
+ { id: "terraform", name: "Terraform", category: "dev" },
1653
+ { id: "ansible", name: "Ansible", category: "dev" },
1654
+ { id: "jenkins", name: "Jenkins", category: "dev" },
1655
+ { id: "nginx", name: "NGINX", category: "dev" },
1656
+ { id: "postgresql", name: "PostgreSQL", category: "dev" },
1657
+ { id: "mysql", name: "MySQL", category: "dev" },
1658
+ { id: "mongodb", name: "MongoDB", category: "dev" },
1659
+ { id: "redis", name: "Redis", category: "dev" },
1660
+ { id: "sqlite", name: "SQLite", category: "dev" },
1661
+ { id: "elasticsearch", name: "Elasticsearch", category: "dev" },
1662
+ { id: "graphql", name: "GraphQL", category: "dev" },
1663
+ { id: "firebase", name: "Firebase", category: "dev" },
1664
+ { id: "linux", name: "Linux", category: "dev" },
1665
+ { id: "ubuntu", name: "Ubuntu", category: "dev" },
1666
+ { id: "archlinux", name: "Arch Linux", category: "dev" },
1667
+ { id: "vim", name: "Vim", category: "dev" },
1668
+ { id: "xcode", name: "Xcode", category: "dev" },
1669
+ { id: "dotnet", name: ".NET", category: "dev" },
1670
+ { id: "sublimetext", name: "Sublime Text", category: "dev" },
1671
+ // ---- cloud 云服务 ----
1672
+ { id: "googlecloud", name: "Google Cloud", category: "cloud" },
1673
+ { id: "cloudflare", name: "Cloudflare", category: "cloud" },
1674
+ { id: "vercel", name: "Vercel", category: "cloud" },
1675
+ { id: "netlify", name: "Netlify", category: "cloud" },
1676
+ { id: "heroku", name: "Heroku", category: "cloud" },
1677
+ { id: "digitalocean", name: "DigitalOcean", category: "cloud" },
1678
+ { id: "hetzner", name: "Hetzner", category: "cloud" },
1679
+ { id: "fastly", name: "Fastly", category: "cloud" },
1680
+ { id: "flydotio", name: "Fly.io", category: "cloud" },
1681
+ { id: "render", name: "Render", category: "cloud" },
1682
+ { id: "supabase", name: "Supabase", category: "cloud" },
1683
+ { id: "alibabacloud", name: "Alibaba Cloud", category: "cloud" },
1684
+ { id: "scaleway", name: "Scaleway", category: "cloud" },
1685
+ // ---- finance 金融支付 ----
1686
+ { id: "visa", name: "Visa", category: "finance" },
1687
+ { id: "mastercard", name: "Mastercard", category: "finance" },
1688
+ { id: "paypal", name: "PayPal", category: "finance" },
1689
+ { id: "stripe", name: "Stripe", category: "finance" },
1690
+ { id: "wise", name: "Wise", category: "finance" },
1691
+ { id: "square", name: "Square", category: "finance" },
1692
+ { id: "applepay", name: "Apple Pay", category: "finance" },
1693
+ { id: "googlepay", name: "Google Pay", category: "finance" },
1694
+ { id: "alipay", name: "Alipay", category: "finance" },
1695
+ { id: "bitcoin", name: "Bitcoin", category: "finance" },
1696
+ { id: "ethereum", name: "Ethereum", category: "finance" },
1697
+ { id: "binance", name: "Binance", category: "finance" },
1698
+ { id: "coinbase", name: "Coinbase", category: "finance" },
1699
+ { id: "americanexpress", name: "American Express", category: "finance" },
1700
+ // ---- media 媒体娱乐 ----
1701
+ { id: "netflix", name: "Netflix", category: "media" },
1702
+ { id: "spotify", name: "Spotify", category: "media" },
1703
+ { id: "applemusic", name: "Apple Music", category: "media" },
1704
+ { id: "soundcloud", name: "SoundCloud", category: "media" },
1705
+ { id: "youtubemusic", name: "YouTube Music", category: "media" },
1706
+ { id: "lastdotfm", name: "Last.fm", category: "media" },
1707
+ { id: "primevideo", name: "Prime Video", category: "media" },
1708
+ { id: "crunchyroll", name: "Crunchyroll", category: "media" },
1709
+ { id: "vlcmediaplayer", name: "VLC Media Player", category: "media" },
1710
+ { id: "unsplash", name: "Unsplash", category: "media" },
1711
+ { id: "behance", name: "Behance", category: "media" },
1712
+ { id: "dribbble", name: "Dribbble", category: "media" },
1713
+ { id: "substack", name: "Substack", category: "media" },
1714
+ { id: "pixiv", name: "pixiv", category: "media" },
1715
+ // ---- gaming 游戏 ----
1716
+ { id: "steam", name: "Steam", category: "gaming" },
1717
+ { id: "epicgames", name: "Epic Games", category: "gaming" },
1718
+ { id: "playstation", name: "PlayStation", category: "gaming" },
1719
+ { id: "nintendo", name: "Nintendo", category: "gaming" },
1720
+ { id: "nintendoswitch", name: "Nintendo Switch", category: "gaming" },
1721
+ { id: "roblox", name: "Roblox", category: "gaming" },
1722
+ { id: "unity", name: "Unity", category: "gaming" },
1723
+ { id: "unrealengine", name: "Unreal Engine", category: "gaming" },
1724
+ { id: "godotengine", name: "Godot Engine", category: "gaming" },
1725
+ { id: "itchdotio", name: "itch.io", category: "gaming" },
1726
+ { id: "riotgames", name: "Riot Games", category: "gaming" },
1727
+ { id: "dota2", name: "Dota 2", category: "gaming" },
1728
+ { id: "ea", name: "EA", category: "gaming" },
1729
+ // ---- design 设计创作 ----
1730
+ { id: "figma", name: "Figma", category: "design" },
1731
+ { id: "sketch", name: "Sketch", category: "design" },
1732
+ { id: "canva", name: "Canva", category: "design" },
1733
+ { id: "adobe", name: "Adobe", category: "design" },
1734
+ { id: "adobephotoshop", name: "Adobe Photoshop", category: "design" },
1735
+ { id: "adobeillustrator", name: "Adobe Illustrator", category: "design" },
1736
+ { id: "adobeaftereffects", name: "Adobe After Effects", category: "design" },
1737
+ { id: "adobepremierepro", name: "Adobe Premiere Pro", category: "design" },
1738
+ { id: "blender", name: "Blender", category: "design" },
1739
+ { id: "affinitydesigner", name: "Affinity Designer", category: "design" },
1740
+ { id: "framer", name: "Framer", category: "design" },
1741
+ { id: "webflow", name: "Webflow", category: "design" },
1742
+ { id: "wordpress", name: "WordPress", category: "design" },
1743
+ { id: "penpot", name: "Penpot", category: "design" },
1744
+ { id: "excalidraw", name: "Excalidraw", category: "design" },
1745
+ { id: "miro", name: "Miro", category: "design" },
1746
+ // ---- shopping 电商零售 ----
1747
+ { id: "shopify", name: "Shopify", category: "shopping" },
1748
+ { id: "woocommerce", name: "WooCommerce", category: "shopping" },
1749
+ { id: "magento", name: "Magento", category: "shopping" },
1750
+ { id: "amazon", name: "Amazon", category: "shopping" },
1751
+ { id: "ebay", name: "eBay", category: "shopping" },
1752
+ { id: "etsy", name: "Etsy", category: "shopping" },
1753
+ { id: "ikea", name: "IKEA", category: "shopping" },
1754
+ { id: "target", name: "Target", category: "shopping" },
1755
+ { id: "walmart", name: "Walmart", category: "shopping" },
1756
+ { id: "zalando", name: "Zalando", category: "shopping" },
1757
+ // ---- productivity 效率办公 ----
1758
+ { id: "notion", name: "Notion", category: "productivity" },
1759
+ { id: "todoist", name: "Todoist", category: "productivity" },
1760
+ { id: "asana", name: "Asana", category: "productivity" },
1761
+ { id: "clickup", name: "ClickUp", category: "productivity" },
1762
+ { id: "jira", name: "Jira", category: "productivity" },
1763
+ { id: "confluence", name: "Confluence", category: "productivity" },
1764
+ { id: "linear", name: "Linear", category: "productivity" },
1765
+ { id: "zapier", name: "Zapier", category: "productivity" },
1766
+ { id: "n8n", name: "n8n", category: "productivity" },
1767
+ { id: "googledrive", name: "Google Drive", category: "productivity" },
1768
+ { id: "googledocs", name: "Google Docs", category: "productivity" },
1769
+ { id: "gmail", name: "Gmail", category: "productivity" },
1770
+ { id: "dropbox", name: "Dropbox", category: "productivity" },
1771
+ { id: "zoom", name: "Zoom", category: "productivity" },
1772
+ { id: "googlemeet", name: "Google Meet", category: "productivity" },
1773
+ // ---- ai 人工智能 ----
1774
+ { id: "openai", name: "OpenAI", category: "ai" },
1775
+ { id: "anthropic", name: "Anthropic", category: "ai" },
1776
+ { id: "huggingface", name: "Hugging Face", category: "ai" },
1777
+ { id: "tensorflow", name: "TensorFlow", category: "ai" },
1778
+ { id: "pytorch", name: "PyTorch", category: "ai" },
1779
+ { id: "opencv", name: "OpenCV", category: "ai" },
1780
+ { id: "scikitlearn", name: "scikit-learn", category: "ai" },
1781
+ { id: "jupyter", name: "Jupyter", category: "ai" },
1782
+ { id: "ollama", name: "Ollama", category: "ai" },
1783
+ { id: "langchain", name: "LangChain", category: "ai" },
1784
+ { id: "googlegemini", name: "Google Gemini", category: "ai" }
1785
+ ];
1786
+
1787
+ // src/plugins/market/sources/motions.ts
1788
+ var MOTIONS = [
1789
+ // ---- animate.css:attention seekers 吸睛 ----
1790
+ { id: "animate-bounce", name: "\u5F39\u8DF3", desc: "\u4E0A\u4E0B\u5F39\u8DF3,\u5E38\u7528\u4E8E\u5F15\u5BFC\u6CE8\u610F", cls: "animate__bounce", lib: "animate.css" },
1791
+ { id: "animate-flash", name: "\u95EA\u70C1", desc: "\u900F\u660E\u5EA6\u5FEB\u901F\u95EA\u70C1", cls: "animate__flash", lib: "animate.css" },
1792
+ { id: "animate-pulse", name: "\u8109\u51B2", desc: "\u7F29\u653E\u547C\u5438,\u5E38\u7528\u4E8E\u52A0\u8F7D\u4E0E\u76F4\u64AD\u72B6\u6001", cls: "animate__pulse", lib: "animate.css" },
1793
+ { id: "animate-rubberband", name: "\u6A61\u76AE\u7B4B", desc: "\u5F39\u6027\u62C9\u4F38,\u5F3A\u8C03\u8DA3\u5473\u53CD\u9988", cls: "animate__rubberBand", lib: "animate.css" },
1794
+ { id: "animate-shakex", name: "\u6A2A\u5411\u6296\u52A8", desc: "\u5DE6\u53F3\u6296\u52A8,\u5E38\u7528\u4E8E\u9519\u8BEF\u63D0\u793A", cls: "animate__shakeX", lib: "animate.css" },
1795
+ { id: "animate-shakey", name: "\u7EB5\u5411\u6296\u52A8", desc: "\u4E0A\u4E0B\u6296\u52A8,\u8868\u5355\u6821\u9A8C\u53CD\u9988", cls: "animate__shakeY", lib: "animate.css" },
1796
+ { id: "animate-headshake", name: "\u6447\u5934", desc: "\u7ED5\u7EB5\u8F74\u5C0F\u5E45\u6446\u5934", cls: "animate__headShake", lib: "animate.css" },
1797
+ { id: "animate-swing", name: "\u6446\u52A8", desc: "\u9876\u90E8\u60AC\u6302\u5F0F\u5DE6\u53F3\u6446\u52A8", cls: "animate__swing", lib: "animate.css" },
1798
+ { id: "animate-tada", name: "\u6B22\u547C", desc: "\u7F29\u653E\u52A0\u65CB\u8F6C\u7684\u7EC4\u5408\u5F3A\u8C03", cls: "animate__tada", lib: "animate.css" },
1799
+ { id: "animate-wobble", name: "\u6447\u6643", desc: "\u5927\u5E45\u5DE6\u53F3\u6447\u6643", cls: "animate__wobble", lib: "animate.css" },
1800
+ { id: "animate-jello", name: "\u679C\u51BB", desc: "\u503E\u659C\u52A0\u7F29\u653E\u7684\u679C\u51BB\u611F", cls: "animate__jello", lib: "animate.css" },
1801
+ { id: "animate-heartbeat", name: "\u5FC3\u8DF3", desc: "\u53CC\u51FB\u5F0F\u5FC3\u8DF3\u7F29\u653E", cls: "animate__heartBeat", lib: "animate.css" },
1802
+ // ---- animate.css:入场 ----
1803
+ { id: "animate-flipinx", name: "\u7FFB\u8F6C\u5165\u573A X", desc: "\u7ED5 X \u8F74\u7FFB\u8F6C\u5165\u573A", cls: "animate__flipInX", lib: "animate.css" },
1804
+ { id: "animate-flipiny", name: "\u7FFB\u8F6C\u5165\u573A Y", desc: "\u7ED5 Y \u8F74\u7FFB\u8F6C\u5165\u573A", cls: "animate__flipInY", lib: "animate.css" },
1805
+ { id: "animate-fadein", name: "\u6DE1\u5165", desc: "\u900F\u660E\u5EA6\u6E10\u5165", cls: "animate__fadeIn", lib: "animate.css" },
1806
+ { id: "animate-fadeindown", name: "\u6DE1\u5165\u4E0B\u79FB", desc: "\u81EA\u4E0A\u800C\u4E0B\u6DE1\u5165", cls: "animate__fadeInDown", lib: "animate.css" },
1807
+ { id: "animate-fadeinleft", name: "\u6DE1\u5165\u53F3\u79FB", desc: "\u81EA\u5DE6\u5411\u53F3\u6DE1\u5165", cls: "animate__fadeInLeft", lib: "animate.css" },
1808
+ { id: "animate-fadeinright", name: "\u6DE1\u5165\u5DE6\u79FB", desc: "\u81EA\u53F3\u5411\u5DE6\u6DE1\u5165", cls: "animate__fadeInRight", lib: "animate.css" },
1809
+ { id: "animate-fadeinup", name: "\u6DE1\u5165\u4E0A\u79FB", desc: "\u81EA\u4E0B\u800C\u4E0A\u6DE1\u5165,\u5361\u7247\u5217\u8868\u5E38\u7528", cls: "animate__fadeInUp", lib: "animate.css" },
1810
+ { id: "animate-fadeinupbig", name: "\u5927\u5E45\u6DE1\u5165\u4E0A\u79FB", desc: "\u5927\u8DDD\u79BB\u81EA\u4E0B\u800C\u4E0A\u6DE1\u5165", cls: "animate__fadeInUpBig", lib: "animate.css" },
1811
+ { id: "animate-zoomin", name: "\u653E\u5927\u5165\u573A", desc: "\u7531\u5C0F\u653E\u5927\u6DE1\u5165", cls: "animate__zoomIn", lib: "animate.css" },
1812
+ { id: "animate-zoomindown", name: "\u653E\u5927\u5165\u573A(\u4E0B)", desc: "\u81EA\u4E0A\u800C\u4E0B\u653E\u5927\u5165\u573A", cls: "animate__zoomInDown", lib: "animate.css" },
1813
+ { id: "animate-slideindown", name: "\u6ED1\u5165(\u4E0B)", desc: "\u81EA\u4E0A\u6ED1\u5165,\u901A\u77E5\u6761\u5E38\u7528", cls: "animate__slideInDown", lib: "animate.css" },
1814
+ { id: "animate-slideinleft", name: "\u6ED1\u5165(\u5DE6)", desc: "\u81EA\u5DE6\u6ED1\u5165,\u4FA7\u680F\u62BD\u5C49\u5E38\u7528", cls: "animate__slideInLeft", lib: "animate.css" },
1815
+ { id: "animate-slideinright", name: "\u6ED1\u5165(\u53F3)", desc: "\u81EA\u53F3\u6ED1\u5165,\u4FA7\u680F\u62BD\u5C49\u5E38\u7528", cls: "animate__slideInRight", lib: "animate.css" },
1816
+ { id: "animate-slideinup", name: "\u6ED1\u5165(\u4E0A)", desc: "\u81EA\u4E0B\u6ED1\u5165,\u5E95\u90E8\u5F39\u5C42\u5E38\u7528", cls: "animate__slideInUp", lib: "animate.css" },
1817
+ { id: "animate-backindown", name: "\u56DE\u5F39\u5165\u573A(\u4E0B)", desc: "\u5E26\u56DE\u5F39\u66F2\u7EBF\u81EA\u4E0A\u800C\u4E0B\u5165\u573A", cls: "animate__backInDown", lib: "animate.css" },
1818
+ { id: "animate-backinleft", name: "\u56DE\u5F39\u5165\u573A(\u5DE6)", desc: "\u5E26\u56DE\u5F39\u66F2\u7EBF\u81EA\u5DE6\u5165\u573A", cls: "animate__backInLeft", lib: "animate.css" },
1819
+ { id: "animate-backinup", name: "\u56DE\u5F39\u5165\u573A(\u4E0A)", desc: "\u5E26\u56DE\u5F39\u66F2\u7EBF\u81EA\u4E0B\u800C\u4E0A\u5165\u573A", cls: "animate__backInUp", lib: "animate.css" },
1820
+ { id: "animate-bouncein", name: "\u5F39\u8DF3\u5165\u573A", desc: "\u5F39\u6027\u5F39\u8DF3\u5165\u573A", cls: "animate__bounceIn", lib: "animate.css" },
1821
+ { id: "animate-bounceindown", name: "\u5F39\u8DF3\u5165\u573A(\u4E0B)", desc: "\u81EA\u4E0A\u5F39\u8DF3\u5165\u573A", cls: "animate__bounceInDown", lib: "animate.css" },
1822
+ { id: "animate-bounceinup", name: "\u5F39\u8DF3\u5165\u573A(\u4E0A)", desc: "\u81EA\u4E0B\u5F39\u8DF3\u5165\u573A", cls: "animate__bounceInUp", lib: "animate.css" },
1823
+ { id: "animate-rotatein", name: "\u65CB\u8F6C\u5165\u573A", desc: "\u65CB\u8F6C\u6DE1\u5165", cls: "animate__rotateIn", lib: "animate.css" },
1824
+ { id: "animate-rotateindownleft", name: "\u65CB\u8F6C\u5165\u573A(\u5DE6\u4E0B)", desc: "\u81EA\u5DE6\u4E0B\u89D2\u65CB\u8F6C\u6DE1\u5165", cls: "animate__rotateInDownLeft", lib: "animate.css" },
1825
+ { id: "animate-lightspeedinright", name: "\u5149\u901F\u5165\u573A", desc: "\u659C\u5207\u52A0\u9AD8\u901F\u98DE\u5165", cls: "animate__lightSpeedInRight", lib: "animate.css" },
1826
+ { id: "animate-rollin", name: "\u6EDA\u5165", desc: "\u6C34\u5E73\u6EDA\u8F6C\u5165\u573A", cls: "animate__rollIn", lib: "animate.css" },
1827
+ { id: "animate-jackinthebox", name: "\u8DF3\u8DF3\u76D2", desc: "\u5F39\u51FA\u76D2\u5F0F\u8DA3\u5473\u5165\u573A", cls: "animate__jackInTheBox", lib: "animate.css" },
1828
+ // ---- animate.css:退场 ----
1829
+ { id: "animate-flipoutx", name: "\u7FFB\u8F6C\u9000\u573A X", desc: "\u7ED5 X \u8F74\u7FFB\u8F6C\u9000\u573A", cls: "animate__flipOutX", lib: "animate.css" },
1830
+ { id: "animate-fadeout", name: "\u6DE1\u51FA", desc: "\u900F\u660E\u5EA6\u6E10\u51FA", cls: "animate__fadeOut", lib: "animate.css" },
1831
+ { id: "animate-fadeoutdown", name: "\u6DE1\u51FA\u4E0B\u79FB", desc: "\u5411\u4E0B\u6DE1\u51FA,\u5F39\u5C42\u5173\u95ED\u5E38\u7528", cls: "animate__fadeOutDown", lib: "animate.css" },
1832
+ { id: "animate-zoomout", name: "\u7F29\u5C0F\u9000\u573A", desc: "\u7F29\u5C0F\u6DE1\u51FA", cls: "animate__zoomOut", lib: "animate.css" },
1833
+ { id: "animate-slideoutup", name: "\u6ED1\u51FA(\u4E0A)", desc: "\u5411\u4E0A\u6ED1\u51FA,\u901A\u77E5\u5173\u95ED\u5E38\u7528", cls: "animate__slideOutUp", lib: "animate.css" },
1834
+ { id: "animate-bounceout", name: "\u5F39\u8DF3\u9000\u573A", desc: "\u5F39\u6027\u5F39\u8DF3\u9000\u573A", cls: "animate__bounceOut", lib: "animate.css" },
1835
+ { id: "animate-hinge", name: "\u94F0\u94FE", desc: "\u60AC\u6302\u6446\u52A8\u5760\u843D,\u559C\u5267\u5F0F\u9000\u573A", cls: "animate__hinge", lib: "animate.css" },
1836
+ // ---- hover.css:hover 反馈 ----
1837
+ { id: "hover-grow", name: "\u653E\u5927", desc: "hover \u65F6\u8F7B\u5FAE\u653E\u5927", cls: "hvr-grow", lib: "hover.css" },
1838
+ { id: "hover-shrink", name: "\u7F29\u5C0F", desc: "hover \u65F6\u8F7B\u5FAE\u7F29\u5C0F", cls: "hvr-shrink", lib: "hover.css" },
1839
+ { id: "hover-pulse", name: "\u8109\u51B2", desc: "hover \u671F\u95F4\u7F29\u653E\u8109\u51B2", cls: "hvr-pulse", lib: "hover.css" },
1840
+ { id: "hover-push", name: "\u63A8\u6324", desc: "hover \u65F6\u538B\u6241\u518D\u56DE\u5F39", cls: "hvr-push", lib: "hover.css" },
1841
+ { id: "hover-pop", name: "\u5F39\u51FA", desc: "hover \u65F6\u5F39\u51FA\u653E\u5927", cls: "hvr-pop", lib: "hover.css" },
1842
+ { id: "hover-bounce-in", name: "\u5F39\u5165", desc: "hover \u65F6\u5F39\u6027\u653E\u5927", cls: "hvr-bounce-in", lib: "hover.css" },
1843
+ { id: "hover-bounce-out", name: "\u5F39\u51FA", desc: "hover \u65F6\u5F39\u6027\u7F29\u5C0F", cls: "hvr-bounce-out", lib: "hover.css" },
1844
+ { id: "hover-rotate", name: "\u65CB\u8F6C", desc: "hover \u65F6\u65CB\u8F6C 4 \u5EA6", cls: "hvr-rotate", lib: "hover.css" },
1845
+ { id: "hover-grow-rotate", name: "\u653E\u5927\u65CB\u8F6C", desc: "hover \u65F6\u653E\u5927\u5E76\u65CB\u8F6C", cls: "hvr-grow-rotate", lib: "hover.css" },
1846
+ { id: "hover-float", name: "\u4E0A\u6D6E", desc: "hover \u65F6\u4E0A\u6D6E,\u6309\u94AE\u5E38\u7528", cls: "hvr-float", lib: "hover.css" },
1847
+ { id: "hover-sink", name: "\u4E0B\u6C89", desc: "hover \u65F6\u4E0B\u6C89\u6309\u538B\u611F", cls: "hvr-sink", lib: "hover.css" },
1848
+ { id: "hover-bob", name: "\u6D6E\u52A8", desc: "hover \u671F\u95F4\u6301\u7EED\u4E0A\u4E0B\u6D6E\u52A8", cls: "hvr-bob", lib: "hover.css" },
1849
+ { id: "hover-hang", name: "\u60AC\u6302", desc: "hover \u671F\u95F4\u60AC\u6302\u6446\u52A8", cls: "hvr-hang", lib: "hover.css" },
1850
+ { id: "hover-skew", name: "\u659C\u5207", desc: "hover \u65F6\u659C\u5207\u53D8\u5F62", cls: "hvr-skew", lib: "hover.css" },
1851
+ { id: "hover-wobble-horizontal", name: "\u6A2A\u5411\u6447\u6446", desc: "hover \u65F6\u6C34\u5E73\u6447\u6446", cls: "hvr-wobble-horizontal", lib: "hover.css" },
1852
+ { id: "hover-wobble-vertical", name: "\u7EB5\u5411\u6447\u6446", desc: "hover \u65F6\u5782\u76F4\u6447\u6446", cls: "hvr-wobble-vertical", lib: "hover.css" },
1853
+ { id: "hover-buzz", name: "\u9707\u52A8", desc: "hover \u65F6\u5C0F\u5E45\u9AD8\u9891\u9707\u52A8", cls: "hvr-buzz", lib: "hover.css" },
1854
+ { id: "hover-sweep-to-right", name: "\u80CC\u666F\u53F3\u626B", desc: "\u80CC\u666F\u8272\u81EA\u5DE6\u5411\u53F3\u626B\u8FC7", cls: "hvr-sweep-to-right", lib: "hover.css" },
1855
+ { id: "hover-bounce-to-right", name: "\u5F39\u6027\u53F3\u586B", desc: "\u80CC\u666F\u5F39\u6027\u586B\u6EE1(\u5411\u53F3)", cls: "hvr-bounce-to-right", lib: "hover.css" },
1856
+ { id: "hover-radial-out", name: "\u5F84\u5411\u6269\u6563", desc: "\u80CC\u666F\u81EA\u4E2D\u5FC3\u5706\u5F62\u6269\u6563", cls: "hvr-radial-out", lib: "hover.css" },
1857
+ { id: "hover-underline-from-left", name: "\u4E0B\u5212\u7EBF\u6ED1\u5165", desc: "\u4E0B\u5212\u7EBF\u81EA\u5DE6\u6ED1\u5165", cls: "hvr-underline-from-left", lib: "hover.css" },
1858
+ { id: "hover-glow", name: "\u53D1\u5149", desc: "hover \u65F6\u5916\u53D1\u5149", cls: "hvr-glow", lib: "hover.css" },
1859
+ { id: "hover-shadow", name: "\u9634\u5F71", desc: "hover \u65F6\u51FA\u73B0\u6295\u5F71", cls: "hvr-shadow", lib: "hover.css" },
1860
+ { id: "hover-grow-shadow", name: "\u653E\u5927\u6295\u5F71", desc: "hover \u65F6\u653E\u5927\u5E76\u6295\u5F71", cls: "hvr-grow-shadow", lib: "hover.css" },
1861
+ { id: "hover-float-shadow", name: "\u4E0A\u6D6E\u6295\u5F71", desc: "hover \u65F6\u4E0A\u6D6E\u5E76\u6295\u5F71", cls: "hvr-float-shadow", lib: "hover.css" }
1862
+ ];
1863
+
1864
+ // src/plugins/market/sources/inspirations.ts
1865
+ var INSPIRATIONS = [
1866
+ { id: "shadcn", name: "shadcn/ui", desc: "\u53EF\u590D\u5236\u7C98\u8D34\u7684 React \u7EC4\u4EF6\u96C6,\u4EE3\u7801\u5F52\u4F60\u6240\u6709", url: "https://ui.shadcn.com", tags: ["react", "tailwind", "\u8BBE\u8BA1\u7CFB\u7EDF"] },
1867
+ { id: "radix", name: "Radix UI", desc: "\u65E0\u6837\u5F0F\u53EF\u8BBF\u95EE\u6027 React \u57FA\u7840\u7EC4\u4EF6", url: "https://www.radix-ui.com", tags: ["react", "\u65E0\u969C\u788D", "\u57FA\u7840\u7EC4\u4EF6"] },
1868
+ { id: "tailwindcss", name: "Tailwind CSS", desc: "\u539F\u5B50\u5316 CSS \u6846\u67B6\u7684\u5B98\u65B9\u7AD9\u70B9", url: "https://tailwindcss.com", tags: ["css", "\u539F\u5B50\u5316", "\u5B98\u7F51"] },
1869
+ { id: "antd", name: "Ant Design", desc: "\u4F01\u4E1A\u7EA7 React \u8BBE\u8BA1\u7CFB\u7EDF\u4E0E\u7EC4\u4EF6\u5E93", url: "https://ant.design", tags: ["react", "\u8BBE\u8BA1\u7CFB\u7EDF", "\u4F01\u4E1A\u7EA7"] },
1870
+ { id: "arco", name: "Arco Design", desc: "\u5B57\u8282\u8DF3\u52A8\u4F01\u4E1A\u7EA7\u8BBE\u8BA1\u7CFB\u7EDF", url: "https://arco.design", tags: ["react", "\u8BBE\u8BA1\u7CFB\u7EDF", "\u4F01\u4E1A\u7EA7"] },
1871
+ { id: "semi", name: "Semi Design", desc: "\u6296\u97F3\u524D\u7AEF\u56E2\u961F\u8BBE\u8BA1\u7CFB\u7EDF", url: "https://semi.design", tags: ["react", "\u8BBE\u8BA1\u7CFB\u7EDF", "\u4E3B\u9898\u5316"] },
1872
+ { id: "chakra", name: "Chakra UI", desc: "\u4EE5\u65E0\u969C\u788D\u548C\u4E3B\u9898\u8457\u79F0\u7684 React \u7EC4\u4EF6\u5E93", url: "https://chakra-ui.com", tags: ["react", "\u65E0\u969C\u788D", "\u4E3B\u9898"] },
1873
+ { id: "mui", name: "MUI", desc: "Material Design \u98CE\u683C React \u7EC4\u4EF6\u5E93", url: "https://mui.com", tags: ["react", "material", "\u7EC4\u4EF6\u5E93"] },
1874
+ { id: "mantine", name: "Mantine", desc: "\u7EC4\u4EF6+hooks \u9F50\u5168\u7684 React \u5168\u5BB6\u6876", url: "https://mantine.dev", tags: ["react", "\u7EC4\u4EF6\u5E93", "hooks"] },
1875
+ { id: "naiveui", name: "Naive UI", desc: "Vue 3 \u5168\u7C7B\u578B\u7EC4\u4EF6\u5E93,\u4E3B\u9898\u5B9A\u5236\u7EC6", url: "https://www.naiveui.com", tags: ["vue", "\u7EC4\u4EF6\u5E93", "\u4E3B\u9898"] },
1876
+ { id: "element-plus", name: "Element Plus", desc: "Vue 3 \u8001\u724C\u4F01\u4E1A\u7EA7\u7EC4\u4EF6\u5E93", url: "https://element-plus.org", tags: ["vue", "\u7EC4\u4EF6\u5E93", "\u4F01\u4E1A\u7EA7"] },
1877
+ { id: "daisyui", name: "daisyUI", desc: "Tailwind \u8BED\u4E49\u5316\u7EC4\u4EF6\u7C7B\u4E0E\u591A\u4E3B\u9898", url: "https://daisyui.com", tags: ["tailwind", "css", "\u4E3B\u9898"] },
1878
+ { id: "nextui", name: "NextUI", desc: "\u7F8E\u89C2\u4F18\u5148\u7684 React + Tailwind \u7EC4\u4EF6\u5E93", url: "https://nextui.org", tags: ["react", "tailwind", "\u7EC4\u4EF6\u5E93"] },
1879
+ { id: "headlessui", name: "Headless UI", desc: "\u5B8C\u5168\u65E0\u6837\u5F0F\u3001\u5E26\u884C\u4E3A\u7684 React/Vue \u7EC4\u4EF6", url: "https://headlessui.com", tags: ["react", "vue", "\u65E0\u6837\u5F0F"] },
1880
+ { id: "carbon", name: "Carbon Design System", desc: "IBM \u5F00\u6E90\u8BBE\u8BA1\u7CFB\u7EDF,token \u4F53\u7CFB\u6559\u79D1\u4E66", url: "https://carbondesignsystem.com", tags: ["\u8BBE\u8BA1\u7CFB\u7EDF", "token", "\u6587\u6863"] },
1881
+ { id: "polaris", name: "Polaris", desc: "Shopify \u8BBE\u8BA1\u7CFB\u7EDF,\u7535\u5546\u540E\u53F0\u8303\u5F0F", url: "https://polaris.shopify.com", tags: ["\u8BBE\u8BA1\u7CFB\u7EDF", "react", "\u7535\u5546"] },
1882
+ { id: "material3", name: "Material Design 3", desc: "Google \u52A8\u6001\u53D6\u8272\u4E0E\u5F62\u53D8\u8BBE\u8BA1\u89C4\u8303", url: "https://m3.material.io", tags: ["google", "\u8BBE\u8BA1\u7CFB\u7EDF", "\u52A8\u6548"] },
1883
+ { id: "storybook", name: "Storybook", desc: "\u7EC4\u4EF6\u5DE5\u4F5C\u53F0\u4E0E\u6587\u6863\u7684\u4E8B\u5B9E\u6807\u51C6", url: "https://storybook.js.org", tags: ["\u7EC4\u4EF6\u6587\u6863", "\u5DE5\u4F5C\u53F0", "\u534F\u4F5C"] },
1884
+ { id: "geist", name: "Geist", desc: "Vercel \u6781\u7B80\u8BBE\u8BA1\u7CFB\u7EDF", url: "https://vercel.com/geist", tags: ["\u8BBE\u8BA1\u7CFB\u7EDF", "\u6781\u7B80", "vercel"] },
1885
+ { id: "openprops", name: "Open Props", desc: "\u8D85\u5B9E\u7528 CSS \u53D8\u91CF\u96C6\u5408(\u52A8\u6548/\u989C\u8272/\u5E03\u5C40)", url: "https://open-props.style", tags: ["css", "\u53D8\u91CF", "\u52A8\u6548"] },
1886
+ { id: "everylayout", name: "Every Layout", desc: "\u7528 CSS \u539F\u751F\u80FD\u529B\u89E3\u51B3\u5E03\u5C40\u95EE\u9898", url: "https://every-layout.dev", tags: ["css", "\u5E03\u5C40", "\u54CD\u5E94\u5F0F"] },
1887
+ { id: "animatecss", name: "Animate.css", desc: "\u6700\u6D41\u884C\u7684\u5373\u7528\u578B CSS \u52A8\u753B\u5E93\u5B98\u7F51", url: "https://animate.style", tags: ["css", "\u52A8\u6548", "\u5E93"] },
1888
+ { id: "csstricks", name: "CSS-Tricks", desc: "CSS \u6280\u5DE7\u4E0E\u6A21\u5F0F\u7684\u957F\u9752\u793E\u533A", url: "https://css-tricks.com", tags: ["css", "\u6559\u7A0B", "\u6A21\u5F0F"] },
1889
+ { id: "webdev", name: "web.dev", desc: "Google Web \u6700\u4F73\u5B9E\u8DF5\u4E0E\u6027\u80FD\u6307\u5357", url: "https://web.dev", tags: ["\u6027\u80FD", "\u6700\u4F73\u5B9E\u8DF5", "google"] },
1890
+ { id: "excalidraw", name: "Excalidraw", desc: "\u624B\u7ED8\u98CE\u534F\u4F5C\u767D\u677F,\u5F00\u6E90\u865A\u62DF\u767D\u677F\u6807\u6746", url: "https://excalidraw.com", tags: ["\u767D\u677F", "\u624B\u7ED8\u98CE", "\u534F\u4F5C"] },
1891
+ { id: "tldraw", name: "tldraw", desc: "\u65E0\u9650\u753B\u5E03 SDK \u4E0E\u7CBE\u7F8E\u767D\u677F", url: "https://www.tldraw.com", tags: ["\u767D\u677F", "\u753B\u5E03", "sdk"] },
1892
+ { id: "penpot", name: "Penpot", desc: "\u5F00\u6E90 Figma \u66FF\u4EE3,\u8BBE\u8BA1\u5F00\u53D1\u540C\u6E90", url: "https://penpot.app", tags: ["\u8BBE\u8BA1\u5DE5\u5177", "\u5F00\u6E90", "\u534F\u4F5C"] },
1893
+ { id: "cal", name: "Cal.com", desc: "\u5F00\u6E90\u65E5\u7A0B\u9884\u7EA6,\u4EA7\u54C1\u7AD9\u4E0E\u4EA4\u4E92\u8303\u5F0F", url: "https://cal.com", tags: ["saas", "\u65E5\u7A0B", "\u8868\u5355"] },
1894
+ { id: "dub", name: "Dub", desc: "\u5F00\u6E90\u77ED\u94FE\u5E73\u53F0,\u73B0\u4EE3 SaaS \u754C\u9762\u8303\u672C", url: "https://dub.co", tags: ["saas", "\u77ED\u94FE", "\u5206\u6790"] },
1895
+ { id: "twenty", name: "Twenty", desc: "\u5F00\u6E90 Notion \u98CE CRM,\u8868\u683C\u4EA4\u4E92\u6559\u79D1\u4E66", url: "https://twenty.com", tags: ["crm", "\u8868\u683C", "saas"] },
1896
+ { id: "umami", name: "Umami", desc: "\u9690\u79C1\u53CB\u597D\u7684\u5F00\u6E90\u7AD9\u70B9\u7EDF\u8BA1,\u4EEA\u8868\u76D8\u7B80\u6D01", url: "https://umami.is", tags: ["\u7EDF\u8BA1", "\u9690\u79C1", "\u4EEA\u8868\u76D8"] },
1897
+ { id: "plausible", name: "Plausible", desc: "\u6781\u7B80\u5F00\u6E90\u5206\u6790,\u4E00\u5C4F\u4EEA\u8868\u76D8\u8303\u5F0F", url: "https://plausible.io", tags: ["\u7EDF\u8BA1", "\u9690\u79C1", "\u6781\u7B80"] },
1898
+ { id: "outline", name: "Outline", desc: "\u5F00\u6E90\u56E2\u961F wiki,\u6392\u7248\u4E0E\u5FEB\u6377\u952E\u4F53\u9A8C\u4F73", url: "https://www.getoutline.com", tags: ["\u6587\u6863", "wiki", "\u56E2\u961F"] },
1899
+ { id: "plane", name: "Plane", desc: "\u5F00\u6E90\u9879\u76EE\u7BA1\u7406,issue/\u770B\u677F/\u5468\u671F", url: "https://plane.so", tags: ["\u9879\u76EE\u7BA1\u7406", "\u770B\u677F", "issue"] },
1900
+ { id: "huly", name: "Huly", desc: "\u5F00\u6E90\u5168\u6808\u9879\u76EE\u7BA1\u7406,\u89C6\u89C9\u534E\u4E3D", url: "https://huly.io", tags: ["\u9879\u76EE\u7BA1\u7406", "\u89C6\u89C9", "\u5168\u6808"] },
1901
+ { id: "uptime-kuma", name: "Uptime Kuma", desc: "\u81EA\u6258\u7BA1\u76D1\u63A7\u4E0E\u72B6\u6001\u9875", url: "https://uptime.kuma.pet", tags: ["\u76D1\u63A7", "\u72B6\u6001\u9875", "\u81EA\u6258\u7BA1"] },
1902
+ { id: "homepage", name: "Homepage", desc: "\u73B0\u4EE3\u5316\u81EA\u6258\u7BA1\u5BFC\u822A\u4EEA\u8868\u76D8", url: "https://gethomepage.dev", tags: ["\u4EEA\u8868\u76D8", "\u5BFC\u822A", "\u81EA\u6258\u7BA1"] },
1903
+ { id: "nocodb", name: "NocoDB", desc: "\u5F00\u6E90 Airtable \u66FF\u4EE3,\u6570\u636E\u5E93\u8868\u683C\u5316", url: "https://nocodb.com", tags: ["\u6570\u636E\u5E93", "\u8868\u683C", "\u4F4E\u4EE3\u7801"] },
1904
+ { id: "bruno", name: "Bruno", desc: "\u79BB\u7EBF\u4F18\u5148\u5F00\u6E90 API \u5BA2\u6237\u7AEF", url: "https://www.usebruno.com", tags: ["api", "\u79BB\u7EBF\u4F18\u5148", "\u5F00\u53D1\u5DE5\u5177"] },
1905
+ { id: "grafana", name: "Grafana", desc: "\u5F00\u6E90\u53EF\u89C6\u5316\u76D1\u63A7\u9762\u677F\u6807\u6746", url: "https://grafana.com/oss/grafana/", tags: ["\u76D1\u63A7", "\u53EF\u89C6\u5316", "\u4EEA\u8868\u76D8"] }
1906
+ ];
1907
+
1908
+ // src/plugins/market/sources/index.ts
1909
+ var MARKETS = ["logos", "motions", "inspirations"];
1910
+ function isMarketName(v) {
1911
+ return MARKETS.includes(v);
1912
+ }
1913
+ function catalogFor(market) {
1914
+ switch (market) {
1915
+ case "logos":
1916
+ return { entries: LOGOTYPES.map((e) => ({ ...e, slug: e.id })) };
1917
+ case "motions":
1918
+ return { entries: MOTIONS.map((e) => ({ ...e })) };
1919
+ case "inspirations":
1920
+ return { entries: INSPIRATIONS.map((e) => ({ ...e })) };
1921
+ default:
1922
+ return null;
1923
+ }
1924
+ }
1925
+
1926
+ // src/plugins/market/index.ts
1927
+ var ALLOWED_PROXY_HOSTS = ["cdn.jsdelivr.net", "data.jsdelivr.com"];
1928
+ var ALLOWED_PROXY_PORTS = /* @__PURE__ */ new Set(["", "80", "443"]);
1929
+ var PROXY_TIMEOUT_MS = 8e3;
1930
+ var PROXY_CACHE_MAX_ENTRIES = 200;
1931
+ var PROXY_CACHE_TTL_MS = 10 * 60 * 1e3;
1932
+ var CATALOG_PREFIX = "/__market/catalog/";
1933
+ var apply7 = defineBuiltin({
1934
+ manifest: manifest5,
1935
+ setup(ctx) {
1936
+ const cache = /* @__PURE__ */ new Map();
1937
+ ctx.route("/__market/proxy", async (req, res) => {
1938
+ const raw = new URL(req.url || "/", "http://localhost").searchParams.get("url");
1939
+ if (!raw) {
1940
+ res.writeHead(400);
1941
+ res.end("missing url");
1942
+ return;
1943
+ }
1944
+ let target;
1945
+ try {
1946
+ target = new URL(raw);
1947
+ } catch {
1948
+ res.writeHead(400);
1949
+ res.end("bad url");
1950
+ return;
1951
+ }
1952
+ if (target.protocol !== "https:" && target.protocol !== "http:") {
1953
+ res.writeHead(403);
1954
+ res.end("scheme not allowed");
1955
+ return;
1956
+ }
1957
+ if (!ALLOWED_PROXY_HOSTS.includes(target.hostname)) {
1958
+ res.writeHead(403);
1959
+ res.end("host not allowed");
1960
+ return;
1961
+ }
1962
+ if (!ALLOWED_PROXY_PORTS.has(target.port)) {
1963
+ res.writeHead(403);
1964
+ res.end("port not allowed");
1965
+ return;
1966
+ }
1967
+ const hit = cache.get(raw);
1968
+ if (hit && Date.now() - hit.cachedAt < PROXY_CACHE_TTL_MS) {
1969
+ res.writeHead(200, { "Content-Type": hit.contentType, "X-Market-Cache": "hit", "Cache-Control": "no-cache" });
1970
+ res.end(hit.body);
1971
+ return;
1972
+ }
1973
+ if (hit) cache.delete(raw);
1974
+ const controller = new AbortController();
1975
+ const timer = setTimeout(() => controller.abort(), PROXY_TIMEOUT_MS);
1976
+ try {
1977
+ const upstream = await fetch(raw, { signal: controller.signal, redirect: "error" });
1978
+ if (!upstream.ok) {
1979
+ res.writeHead(502);
1980
+ res.end("upstream error");
1981
+ return;
1982
+ }
1983
+ const body = await upstream.text();
1984
+ const contentType = upstream.headers.get("content-type") ?? "application/octet-stream";
1985
+ cache.set(raw, { body, contentType, cachedAt: Date.now() });
1986
+ if (cache.size > PROXY_CACHE_MAX_ENTRIES) {
1987
+ const oldest = cache.keys().next().value;
1988
+ if (oldest !== void 0) cache.delete(oldest);
1989
+ }
1990
+ res.writeHead(200, { "Content-Type": contentType, "X-Market-Cache": "miss", "Cache-Control": "no-cache" });
1991
+ res.end(body);
1992
+ } catch {
1993
+ if (!res.headersSent) {
1994
+ res.writeHead(502);
1995
+ res.end("upstream failed");
1996
+ }
1997
+ } finally {
1998
+ clearTimeout(timer);
1999
+ }
2000
+ });
2001
+ ctx.route("/__market/catalog", async (req, res) => {
2002
+ const pathname = new URL(req.url || "/", "http://localhost").pathname;
2003
+ const market = pathname.startsWith(CATALOG_PREFIX) ? pathname.slice(CATALOG_PREFIX.length).split("/")[0] : "";
2004
+ if (!market || !isMarketName(market)) {
2005
+ res.writeHead(404);
2006
+ res.end("unknown market");
2007
+ return;
2008
+ }
2009
+ return catalogFor(market);
2010
+ });
2011
+ }
2012
+ });
2013
+
1786
2014
  // src/cli.ts
1787
- var __dirname2 = path11.dirname(fileURLToPath2(import.meta.url));
2015
+ var __dirname2 = path9.dirname(fileURLToPath2(import.meta.url));
1788
2016
  var DEFAULT_PORT = 4190;
1789
2017
  function parseArgs() {
1790
2018
  const args = process.argv.slice(2);
@@ -1834,7 +2062,7 @@ async function loadExternal(ctx, dir, root) {
1834
2062
  if (!ent.isDirectory()) continue;
1835
2063
  const candidates = tsxLoaded ? ["index.ts", "index.js", "index.mjs"] : ["index.js", "index.mjs"];
1836
2064
  for (const name of candidates) {
1837
- const p = path11.join(dir, ent.name, name);
2065
+ const p = path9.join(dir, ent.name, name);
1838
2066
  if (await pathExists(p)) {
1839
2067
  try {
1840
2068
  const mod = await import(p);
@@ -1849,8 +2077,8 @@ async function loadExternal(ctx, dir, root) {
1849
2077
  const m = ctx.dashboard.get(ent.name);
1850
2078
  if (m && m.mode === ent.name) {
1851
2079
  const patch = { external: true };
1852
- const webDir = path11.join(dir, ent.name, "web");
1853
- if (await pathExists(path11.join(webDir, "index.html"))) {
2080
+ const webDir = path9.join(dir, ent.name, "web");
2081
+ if (await pathExists(path9.join(webDir, "index.html"))) {
1854
2082
  ctx.server.static(`${PLUGIN_STATIC_PREFIX}${ent.name}/`, webDir);
1855
2083
  if (!m.viewerUrl) patch.viewerUrl = `${PLUGIN_STATIC_PREFIX}${ent.name}/`;
1856
2084
  }
@@ -1869,7 +2097,7 @@ async function loadExternal(ctx, dir, root) {
1869
2097
  }
1870
2098
  async function main() {
1871
2099
  const args = parseArgs();
1872
- const root = path11.resolve(args.dir);
2100
+ const root = path9.resolve(args.dir);
1873
2101
  const existing = await findReusable(root);
1874
2102
  if (existing && !args.restart) {
1875
2103
  const u = `http://localhost:${existing.port}` + (args.page ? `#${args.page}` : "");
@@ -1883,14 +2111,14 @@ async function main() {
1883
2111
  console.log(`[zdashboard] --restart\uFF1A\u505C\u6B62\u65E7\u5B9E\u4F8B pid=${oldRecord.pid}`);
1884
2112
  await stopInstance(oldRecord);
1885
2113
  }
1886
- const appDir = path11.resolve(__dirname2, "web");
1887
- const BUILTIN_PLUGINS = [apply7, apply3, apply4, apply5, apply6];
2114
+ const appDir = path9.resolve(__dirname2, "web");
2115
+ const BUILTIN_PLUGINS = [apply6, apply3, apply4, apply5, apply7];
1888
2116
  if (stripDeadPluginConfig(root, BUILTIN_PLUGINS.map((p) => p.manifest))) {
1889
2117
  console.log("[zdashboard] \u5DF2\u6E05\u7406 dashboard.json \u4E2D\u5185\u7F6E\u63D2\u4EF6\u7684\u8FC7\u65F6\u914D\u7F6E\u952E");
1890
2118
  }
1891
2119
  const startPort = oldRecord && !args.portExplicit ? oldRecord.port : args.port;
1892
- const zdevDir = path11.join(root, ".zdev");
1893
- const dataDir = fs11.existsSync(zdevDir) ? ".zdev/" : "";
2120
+ const zdevDir = path9.join(root, ".zdev");
2121
+ const dataDir = fs9.existsSync(zdevDir) ? ".zdev/" : "";
1894
2122
  const det = await detect(root);
1895
2123
  const ctx = new Context();
1896
2124
  ctx.plugin(ServerService, {
@@ -1915,7 +2143,7 @@ async function main() {
1915
2143
  }
1916
2144
  }
1917
2145
  if (args.plugins) {
1918
- await loadExternal(ctx, path11.resolve(args.plugins), root);
2146
+ await loadExternal(ctx, path9.resolve(args.plugins), root);
1919
2147
  }
1920
2148
  const shutdown = () => {
1921
2149
  try {