create-next-imagicma 0.0.4 → 0.0.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 (106) hide show
  1. package/README.md +30 -13
  2. package/bin/create-next-imagicma.mjs +138 -22
  3. package/package.json +2 -1
  4. package/template/app/globals.css +331 -0
  5. package/template/app/layout.tsx +4 -6
  6. package/template/app/page.tsx +18 -40
  7. package/template/package.json +1 -1
  8. package/template/public/imagicma-picker-bridge.js +374 -0
  9. package/template-hono/.env.example +8 -0
  10. package/template-hono/.imagicma/AGENTS.md +7 -0
  11. package/template-hono/.imagicma/port.json +5 -0
  12. package/template-hono/AGENTS.md +39 -0
  13. package/template-hono/README.md +48 -0
  14. package/template-hono/client/src/App.tsx +13 -0
  15. package/template-hono/client/src/components/ErrorBoundary.tsx +74 -0
  16. package/template-hono/client/src/components/HelloClient.tsx +69 -0
  17. package/template-hono/client/src/globals.css +767 -0
  18. package/template-hono/client/src/main.tsx +24 -0
  19. package/template-hono/client/src/pages/HelloPage.tsx +22 -0
  20. package/template-hono/client/src/pages/HomePage.tsx +30 -0
  21. package/template-hono/client/src/providers.tsx +21 -0
  22. package/template-hono/components/ui/accordion.tsx +58 -0
  23. package/template-hono/components/ui/alert-dialog.tsx +141 -0
  24. package/template-hono/components/ui/alert.tsx +61 -0
  25. package/template-hono/components/ui/aspect-ratio.tsx +7 -0
  26. package/template-hono/components/ui/avatar.tsx +51 -0
  27. package/template-hono/components/ui/badge.tsx +40 -0
  28. package/template-hono/components/ui/breadcrumb.tsx +117 -0
  29. package/template-hono/components/ui/button.tsx +64 -0
  30. package/template-hono/components/ui/calendar.tsx +72 -0
  31. package/template-hono/components/ui/card.tsx +87 -0
  32. package/template-hono/components/ui/carousel.tsx +262 -0
  33. package/template-hono/components/ui/chart.tsx +365 -0
  34. package/template-hono/components/ui/checkbox.tsx +30 -0
  35. package/template-hono/components/ui/collapsible.tsx +11 -0
  36. package/template-hono/components/ui/command.tsx +153 -0
  37. package/template-hono/components/ui/context-menu.tsx +200 -0
  38. package/template-hono/components/ui/dialog.tsx +122 -0
  39. package/template-hono/components/ui/drawer.tsx +118 -0
  40. package/template-hono/components/ui/dropdown-menu.tsx +200 -0
  41. package/template-hono/components/ui/form.tsx +178 -0
  42. package/template-hono/components/ui/hover-card.tsx +29 -0
  43. package/template-hono/components/ui/input-otp.tsx +71 -0
  44. package/template-hono/components/ui/input.tsx +25 -0
  45. package/template-hono/components/ui/label.tsx +26 -0
  46. package/template-hono/components/ui/menubar.tsx +256 -0
  47. package/template-hono/components/ui/navigation-menu.tsx +130 -0
  48. package/template-hono/components/ui/pagination.tsx +119 -0
  49. package/template-hono/components/ui/popover.tsx +31 -0
  50. package/template-hono/components/ui/progress.tsx +28 -0
  51. package/template-hono/components/ui/radio-group.tsx +44 -0
  52. package/template-hono/components/ui/resizable.tsx +45 -0
  53. package/template-hono/components/ui/scroll-area.tsx +48 -0
  54. package/template-hono/components/ui/select.tsx +160 -0
  55. package/template-hono/components/ui/separator.tsx +31 -0
  56. package/template-hono/components/ui/sheet.tsx +140 -0
  57. package/template-hono/components/ui/sidebar.tsx +732 -0
  58. package/template-hono/components/ui/skeleton.tsx +17 -0
  59. package/template-hono/components/ui/slider.tsx +28 -0
  60. package/template-hono/components/ui/switch.tsx +29 -0
  61. package/template-hono/components/ui/table.tsx +119 -0
  62. package/template-hono/components/ui/tabs.tsx +55 -0
  63. package/template-hono/components/ui/textarea.tsx +24 -0
  64. package/template-hono/components/ui/toast.tsx +129 -0
  65. package/template-hono/components/ui/toaster.tsx +35 -0
  66. package/template-hono/components/ui/toggle-group.tsx +61 -0
  67. package/template-hono/components/ui/toggle.tsx +45 -0
  68. package/template-hono/components/ui/tooltip.tsx +30 -0
  69. package/template-hono/drizzle.config.ts +50 -0
  70. package/template-hono/eslint.config.mjs +13 -0
  71. package/template-hono/gitignore +40 -0
  72. package/template-hono/hooks/use-greeting.ts +15 -0
  73. package/template-hono/hooks/use-mobile.ts +21 -0
  74. package/template-hono/hooks/use-toast.ts +194 -0
  75. package/template-hono/index.html +13 -0
  76. package/template-hono/lib/queryClient.ts +59 -0
  77. package/template-hono/lib/theme/default-theme.ts +11 -0
  78. package/template-hono/lib/utils.ts +6 -0
  79. package/template-hono/package.json +82 -0
  80. package/template-hono/pnpm-lock.yaml +5162 -0
  81. package/template-hono/postcss.config.mjs +7 -0
  82. package/template-hono/process-compose.yaml +13 -0
  83. package/template-hono/public/favicon.ico +0 -0
  84. package/template-hono/public/file.svg +1 -0
  85. package/template-hono/public/globe.svg +1 -0
  86. package/template-hono/public/imagicma-picker-bridge.js +374 -0
  87. package/template-hono/public/next.svg +1 -0
  88. package/template-hono/public/vercel.svg +1 -0
  89. package/template-hono/public/window.svg +1 -0
  90. package/template-hono/scripts/imagicma-common.mjs +118 -0
  91. package/template-hono/scripts/imagicma-dev.mjs +29 -0
  92. package/template-hono/scripts/imagicma-guard.mjs +17 -0
  93. package/template-hono/scripts/imagicma-start.mjs +24 -0
  94. package/template-hono/server/app.ts +40 -0
  95. package/template-hono/server/db.ts +22 -0
  96. package/template-hono/server/dev-app.ts +5 -0
  97. package/template-hono/server/index.ts +94 -0
  98. package/template-hono/server/routes/greeting.ts +25 -0
  99. package/template-hono/server/storage.ts +39 -0
  100. package/template-hono/shared/routes.ts +13 -0
  101. package/template-hono/shared/schema.ts +17 -0
  102. package/template-hono/tailwind.config.mjs +97 -0
  103. package/template-hono/tsconfig.json +39 -0
  104. package/template-hono/tsconfig.server.json +15 -0
  105. package/template-hono/types/pg.d.ts +19 -0
  106. package/template-hono/vite.config.ts +125 -0
package/README.md CHANGED
@@ -1,43 +1,60 @@
1
1
  # create-next-imagicma
2
2
 
3
- 一个极简的项目脚手架(类似 `create-next-app`),用于从本仓库模板快速生成新项目。
3
+ 一个极简项目脚手架,用于从本仓库模板快速生成新项目。支持双模板:`hono` `next`。
4
4
 
5
5
  ## 使用
6
6
 
7
7
  ```bash
8
8
  npm install -g create-next-imagicma
9
- create-next-imagicma <project-dir> [--port <1-65535>] [--theme <name>]
9
+ create-next-imagicma <project-dir> [--template <hono|next>] [--port <1-65535>] [--theme <name>]
10
10
  ```
11
11
 
12
12
  本地(未发布)使用示例:
13
13
 
14
14
  ```bash
15
- cd /Users/alexliu/Project/nextjs-app
16
- node ./create-next-imagicma/bin/create-next-imagicma.mjs demo-5001 --port 5001
15
+ cd /Users/alexliu/Project/imagicma-template
16
+ node ./create-next-imagicma/bin/create-next-imagicma.mjs demo-hono --template hono --port 5001
17
+ node ./create-next-imagicma/bin/create-next-imagicma.mjs demo-next --template next --port 5001
17
18
  ```
18
19
 
19
20
  ## 参数
20
21
 
21
- - `--port <number>`:设置新项目 `dev/start` 的默认端口(写入 `next ... -p <port>`)。
22
- - `--theme <name>`:设置新项目默认主题(可选:`quadratic`、`nomad`、`honey`、`zen-garden`、`highlighter`)。
23
- - 运行时仍可覆盖,例如:`pnpm dev -- -p 6001` 或 `PORT=6001 pnpm start`
22
+ - `--template <hono|next>`:选择模板。默认 `hono`。
23
+ - `--port <number>`:设置新项目默认端口。
24
+ - `next` 模板:写入 `next dev/start -p <port>`
25
+ - `hono` 模板:写入 `/.imagicma/port.json`,并同步 `process-compose.yaml` 的 `PORT`
26
+ - `--theme <name>`:设置默认主题(`quadratic`、`nomad`、`honey`、`zen-garden`、`highlighter`)。
27
+ - `-v, --version`:显示版本号。
24
28
 
25
29
  ## 依赖安装策略
26
30
 
27
- - 优先使用 `pnpm install`
28
- - 如果没有 `pnpm`,会尝试 `corepack pnpm install`
29
- - 如果依然不可用,会自动回退到 `npm install`
31
+ - 优先 `pnpm install`
32
+ - `pnpm` 时尝试 `corepack pnpm install`
33
+ - 仍不可用则回退 `npm install`
30
34
 
31
35
  ## 维护模板
32
36
 
33
- 在脚手架目录执行(默认从同级的 `../nextjs-app` 同步):
37
+ 在脚手架目录执行:
34
38
 
35
39
  ```bash
36
40
  pnpm run sync-template
37
41
  ```
38
42
 
39
- 可通过环境变量自定义源仓库路径:
43
+ 默认同步:
44
+
45
+ - `../nextjs-app` -> `template/`
46
+ - `../hono-app` -> `template-hono/`
47
+
48
+ 可通过环境变量覆盖:
49
+
50
+ ```bash
51
+ SOURCE_REPO_NEXT=/abs/path/to/nextjs-app \
52
+ SOURCE_REPO_HONO=/abs/path/to/hono-app \
53
+ pnpm run sync-template
54
+ ```
55
+
56
+ 兼容旧变量(仅 Next):
40
57
 
41
58
  ```bash
42
- SOURCE_REPO=/abs/path/to/source pnpm run sync-template
59
+ SOURCE_REPO=/abs/path/to/nextjs-app pnpm run sync-template
43
60
  ```
@@ -7,7 +7,12 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
- const TEMPLATE_DIR = path.resolve(__dirname, "..", "template");
10
+
11
+ const TEMPLATE_DIR_NEXT = path.resolve(__dirname, "..", "template");
12
+ const TEMPLATE_DIR_HONO = path.resolve(__dirname, "..", "template-hono");
13
+ const TEMPLATE_CHOICES = ["hono", "next"];
14
+ const DEFAULT_TEMPLATE = "hono";
15
+
11
16
  const THEME_STYLES = [
12
17
  "quadratic",
13
18
  "nomad",
@@ -16,13 +21,29 @@ const THEME_STYLES = [
16
21
  "highlighter",
17
22
  ];
18
23
 
24
+ const PACKAGE_JSON_PATH = path.resolve(__dirname, "..", "package.json");
25
+ const HONO_LOCKED_SCRIPTS = {
26
+ predev: "node ./scripts/imagicma-guard.mjs dev",
27
+ dev: "node ./scripts/imagicma-dev.mjs",
28
+ prestart: "node ./scripts/imagicma-guard.mjs start",
29
+ start: "node ./scripts/imagicma-start.mjs",
30
+ };
31
+
32
+ async function getVersion() {
33
+ const raw = await fs.readFile(PACKAGE_JSON_PATH, "utf8");
34
+ const pkg = JSON.parse(raw);
35
+ return pkg.version ?? "0.0.0";
36
+ }
37
+
19
38
  function printHelp() {
20
- console.log(`create-next-imagicma <project-dir> [--port <1-65535>] [--theme <name>]
39
+ console.log(`create-next-imagicma <project-dir> [--template <hono|next>] [--port <1-65535>] [--theme <name>]
21
40
 
22
41
  参数:
23
- --port <number> 设置新项目 dev/start 的默认端口(next ... -p
24
- --theme <name> 设置默认主题(可选:${THEME_STYLES.join(", ")})
25
- -h, --help 显示帮助
42
+ --template <name> 选择模板(可选:${TEMPLATE_CHOICES.join("、")},默认:${DEFAULT_TEMPLATE}
43
+ --port <number> 设置新项目默认端口
44
+ --theme <name> 设置默认主题(可选:${THEME_STYLES.join(", ")})
45
+ -h, --help 显示帮助
46
+ -v, --version 显示版本号
26
47
  `);
27
48
  }
28
49
 
@@ -55,6 +76,21 @@ function parseTheme(raw) {
55
76
  return normalized;
56
77
  }
57
78
 
79
+ function parseTemplate(raw) {
80
+ const normalized = String(raw ?? "").trim().toLowerCase();
81
+ if (!normalized) {
82
+ throw new Error("--template 缺少值:请使用 --template <hono|next>");
83
+ }
84
+
85
+ if (!TEMPLATE_CHOICES.includes(normalized)) {
86
+ throw new Error(
87
+ `--template 参数不合法:${JSON.stringify(raw)}(可选:${TEMPLATE_CHOICES.join("、")})`,
88
+ );
89
+ }
90
+
91
+ return normalized;
92
+ }
93
+
58
94
  function sanitizePackageName(raw) {
59
95
  const normalized = raw
60
96
  .toLowerCase()
@@ -67,6 +103,7 @@ function parseArgs(argv) {
67
103
  let projectDir;
68
104
  let port;
69
105
  let theme;
106
+ let template = DEFAULT_TEMPLATE;
70
107
 
71
108
  for (let i = 0; i < argv.length; i += 1) {
72
109
  const arg = argv[i];
@@ -75,6 +112,10 @@ function parseArgs(argv) {
75
112
  return { help: true };
76
113
  }
77
114
 
115
+ if (arg === "-v" || arg === "--version") {
116
+ return { version: true };
117
+ }
118
+
78
119
  if (arg === "--port") {
79
120
  const value = argv[i + 1];
80
121
  if (!value) {
@@ -105,6 +146,21 @@ function parseArgs(argv) {
105
146
  continue;
106
147
  }
107
148
 
149
+ if (arg === "--template") {
150
+ const value = argv[i + 1];
151
+ if (!value) {
152
+ throw new Error("--template 缺少值:请使用 --template <hono|next>");
153
+ }
154
+ template = parseTemplate(value);
155
+ i += 1;
156
+ continue;
157
+ }
158
+
159
+ if (arg.startsWith("--template=")) {
160
+ template = parseTemplate(arg.slice("--template=".length));
161
+ continue;
162
+ }
163
+
108
164
  if (arg.startsWith("-")) {
109
165
  throw new Error(`未知参数:${arg}`);
110
166
  }
@@ -121,7 +177,14 @@ function parseArgs(argv) {
121
177
  throw new Error("缺少 <project-dir>:请指定要创建的项目目录名/路径");
122
178
  }
123
179
 
124
- return { projectDir, port, theme, help: false };
180
+ return {
181
+ projectDir,
182
+ port,
183
+ theme,
184
+ template,
185
+ help: false,
186
+ version: false,
187
+ };
125
188
  }
126
189
 
127
190
  async function ensureTargetDirReady(targetDir) {
@@ -139,20 +202,30 @@ async function ensureTargetDirReady(targetDir) {
139
202
  }
140
203
  }
141
204
 
142
- async function updatePackageName(targetDir, port) {
205
+ async function updatePackageName(targetDir, port, template) {
143
206
  const pkgPath = path.join(targetDir, "package.json");
144
207
  const raw = await fs.readFile(pkgPath, "utf8");
145
208
  const pkg = JSON.parse(raw);
146
- const devScript = port === undefined ? "next dev" : `next dev -p ${port}`;
147
- const startScript =
148
- port === undefined ? "next start" : `next start -p ${port}`;
149
209
 
150
210
  pkg.name = sanitizePackageName(path.basename(targetDir));
151
- pkg.scripts = {
152
- ...(pkg.scripts ?? {}),
153
- dev: devScript,
154
- start: startScript,
155
- };
211
+
212
+ if (template === "next") {
213
+ const devScript = port === undefined ? "next dev" : `next dev -p ${port}`;
214
+ const startScript =
215
+ port === undefined ? "next start" : `next start -p ${port}`;
216
+
217
+ pkg.scripts = {
218
+ ...(pkg.scripts ?? {}),
219
+ dev: devScript,
220
+ start: startScript,
221
+ };
222
+ } else {
223
+ pkg.scripts = {
224
+ ...(pkg.scripts ?? {}),
225
+ ...HONO_LOCKED_SCRIPTS,
226
+ };
227
+ }
228
+
156
229
  await fs.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
157
230
  }
158
231
 
@@ -178,6 +251,34 @@ async function updateProcessComposePort(targetDir, port) {
178
251
  await fs.writeFile(filePath, next);
179
252
  }
180
253
 
254
+ async function updateLockedPortFile(targetDir, port) {
255
+ if (port === undefined) return;
256
+
257
+ const filePath = path.join(targetDir, ".imagicma", "port.json");
258
+
259
+ let current = {};
260
+ try {
261
+ const raw = await fs.readFile(filePath, "utf8");
262
+ const parsed = JSON.parse(raw);
263
+ if (parsed && typeof parsed === "object") {
264
+ current = parsed;
265
+ }
266
+ } catch (error) {
267
+ if (!(error && typeof error === "object" && error.code === "ENOENT")) {
268
+ throw error;
269
+ }
270
+ }
271
+
272
+ const next = {
273
+ ...current,
274
+ port,
275
+ locked: true,
276
+ version: 1,
277
+ };
278
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
279
+ await fs.writeFile(filePath, `${JSON.stringify(next, null, 2)}\n`);
280
+ }
281
+
181
282
  async function updateDefaultTheme(targetDir, theme) {
182
283
  if (theme === undefined) return;
183
284
 
@@ -237,7 +338,6 @@ async function initGit(targetDir) {
237
338
  }
238
339
  return true;
239
340
  } catch (_error) {
240
- // 不存在 git 或其它异常时静默跳过,不报错
241
341
  return false;
242
342
  }
243
343
  }
@@ -280,33 +380,48 @@ async function installDependencies(targetDir) {
280
380
  }
281
381
  }
282
382
 
383
+ function getTemplateDir(template) {
384
+ if (template === "next") return TEMPLATE_DIR_NEXT;
385
+ return TEMPLATE_DIR_HONO;
386
+ }
387
+
283
388
  async function main() {
284
389
  const parsed = parseArgs(process.argv.slice(2));
285
390
  if (parsed.help) {
286
391
  printHelp();
287
392
  return;
288
393
  }
394
+ if (parsed.version) {
395
+ const version = await getVersion();
396
+ console.log(version);
397
+ return;
398
+ }
289
399
 
290
- const { projectDir, port, theme } = parsed;
400
+ const { projectDir, port, theme, template } = parsed;
291
401
  const targetDir = path.resolve(process.cwd(), projectDir);
402
+ const templateDir = getTemplateDir(template);
292
403
 
293
404
  await ensureTargetDirReady(targetDir);
294
405
 
295
- await fs.cp(TEMPLATE_DIR, targetDir, {
406
+ await fs.cp(templateDir, targetDir, {
296
407
  recursive: true,
297
408
  force: true,
298
409
  errorOnExist: false,
299
410
  });
300
- // npm publish 会默认排除 .gitignore,模板中以 gitignore 发布,此处还原为 .gitignore
411
+
301
412
  const gitignoreSrc = path.join(targetDir, "gitignore");
302
413
  try {
303
414
  await fs.access(gitignoreSrc);
304
415
  await fs.rename(gitignoreSrc, path.join(targetDir, ".gitignore"));
305
416
  } catch {
306
- // 无 gitignore 则跳过(如本地开发时仍保留 .gitignore 的情况)
417
+ // ignore
307
418
  }
308
- await updatePackageName(targetDir, port);
419
+
420
+ await updatePackageName(targetDir, port, template);
309
421
  await updateProcessComposePort(targetDir, port);
422
+ if (template === "hono") {
423
+ await updateLockedPortFile(targetDir, port);
424
+ }
310
425
  await updateDefaultTheme(targetDir, theme);
311
426
 
312
427
  await initGit(targetDir);
@@ -325,8 +440,9 @@ async function main() {
325
440
  }
326
441
 
327
442
  console.log(`\n✅ 项目已创建:${targetDir}`);
443
+ console.log(`模板类型:${template}`);
328
444
  if (port !== undefined) {
329
- console.log(`已设置默认端口:${port}(写入 dev/start 启动命令)`);
445
+ console.log(`已设置默认端口:${port}`);
330
446
  }
331
447
  if (theme !== undefined) {
332
448
  console.log(`已设置默认主题:${theme}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-next-imagicma",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-next-imagicma": "./bin/create-next-imagicma.mjs"
@@ -8,6 +8,7 @@
8
8
  "files": [
9
9
  "bin/**",
10
10
  "template/**",
11
+ "template-hono/**",
11
12
  "README.md"
12
13
  ],
13
14
  "scripts": {
@@ -428,6 +428,337 @@
428
428
  transform: translateY(0px);
429
429
  box-shadow: 0 6px 18px rgb(0 0 0 / 0.08);
430
430
  }
431
+
432
+ @keyframes aurora-shift-x {
433
+ from {
434
+ background-position: 0% 50%;
435
+ }
436
+ to {
437
+ background-position: 200% 50%;
438
+ }
439
+ }
440
+
441
+ @keyframes aurora-shift-y {
442
+ from {
443
+ background-position: 50% 0%;
444
+ }
445
+ to {
446
+ background-position: 50% 200%;
447
+ }
448
+ }
449
+
450
+ @keyframes aurora-breathe {
451
+ 0%,
452
+ 100% {
453
+ opacity: 0.55;
454
+ }
455
+ 50% {
456
+ opacity: 0.95;
457
+ }
458
+ }
459
+
460
+ @keyframes nebula-float-a {
461
+ 0%,
462
+ 100% {
463
+ transform: translate3d(0, 0, 0) scale(1);
464
+ }
465
+ 50% {
466
+ transform: translate3d(8%, 5%, 0) scale(1.08);
467
+ }
468
+ }
469
+
470
+ @keyframes nebula-float-b {
471
+ 0%,
472
+ 100% {
473
+ transform: translate3d(0, 0, 0) scale(1);
474
+ }
475
+ 50% {
476
+ transform: translate3d(-7%, 6%, 0) scale(1.12);
477
+ }
478
+ }
479
+
480
+ @keyframes nebula-float-c {
481
+ 0%,
482
+ 100% {
483
+ transform: translate3d(0, 0, 0) scale(1);
484
+ }
485
+ 50% {
486
+ transform: translate3d(5%, -6%, 0) scale(1.06);
487
+ }
488
+ }
489
+
490
+ @keyframes grain-shift {
491
+ 0% {
492
+ transform: translate3d(0, 0, 0);
493
+ }
494
+ 25% {
495
+ transform: translate3d(1%, -1%, 0);
496
+ }
497
+ 50% {
498
+ transform: translate3d(-1%, 1%, 0);
499
+ }
500
+ 75% {
501
+ transform: translate3d(0.5%, 1%, 0);
502
+ }
503
+ 100% {
504
+ transform: translate3d(0, 0, 0);
505
+ }
506
+ }
507
+
508
+ @keyframes loader-spin {
509
+ from {
510
+ transform: rotate(0deg);
511
+ }
512
+ to {
513
+ transform: rotate(360deg);
514
+ }
515
+ }
516
+
517
+ @keyframes loader-spin-reverse {
518
+ from {
519
+ transform: rotate(360deg);
520
+ }
521
+ to {
522
+ transform: rotate(0deg);
523
+ }
524
+ }
525
+
526
+ @keyframes loader-core-breathe {
527
+ 0%,
528
+ 100% {
529
+ transform: scale(0.92);
530
+ opacity: 0.85;
531
+ }
532
+ 50% {
533
+ transform: scale(1.04);
534
+ opacity: 1;
535
+ }
536
+ }
537
+
538
+ .nebula-orb {
539
+ position: absolute;
540
+ border-radius: 9999px;
541
+ mix-blend-mode: screen;
542
+ opacity: 0.72;
543
+ filter: blur(72px);
544
+ will-change: transform;
545
+ }
546
+
547
+ .nebula-orb-a {
548
+ left: -11rem;
549
+ top: -8rem;
550
+ height: 34rem;
551
+ width: 34rem;
552
+ background: radial-gradient(
553
+ circle,
554
+ rgba(56, 189, 248, 0.36) 0%,
555
+ rgba(99, 102, 241, 0.2) 45%,
556
+ rgba(255, 255, 255, 0) 72%
557
+ );
558
+ animation: nebula-float-a 16s ease-in-out infinite;
559
+ }
560
+
561
+ .nebula-orb-b {
562
+ right: -13rem;
563
+ top: 14%;
564
+ height: 38rem;
565
+ width: 38rem;
566
+ background: radial-gradient(
567
+ circle,
568
+ rgba(20, 184, 166, 0.33) 0%,
569
+ rgba(59, 130, 246, 0.16) 46%,
570
+ rgba(255, 255, 255, 0) 75%
571
+ );
572
+ animation: nebula-float-b 18s ease-in-out infinite;
573
+ }
574
+
575
+ .nebula-orb-c {
576
+ bottom: -16rem;
577
+ left: 26%;
578
+ height: 36rem;
579
+ width: 36rem;
580
+ background: radial-gradient(
581
+ circle,
582
+ rgba(167, 139, 250, 0.24) 0%,
583
+ rgba(56, 189, 248, 0.12) 45%,
584
+ rgba(255, 255, 255, 0) 74%
585
+ );
586
+ animation: nebula-float-c 15s ease-in-out infinite;
587
+ }
588
+
589
+ .nebula-grain {
590
+ position: absolute;
591
+ inset: -20%;
592
+ background-image: radial-gradient(rgba(15, 23, 42, 0.08) 0.7px, transparent 0.7px);
593
+ background-size: 3px 3px;
594
+ opacity: 0.08;
595
+ animation: grain-shift 8s steps(10) infinite;
596
+ }
597
+
598
+ .luxe-loader {
599
+ position: relative;
600
+ height: 96px;
601
+ width: 96px;
602
+ }
603
+
604
+ .luxe-loader__ring {
605
+ position: absolute;
606
+ border-radius: 9999px;
607
+ border: 2px solid transparent;
608
+ }
609
+
610
+ .luxe-loader__ring--outer {
611
+ inset: 0;
612
+ border-top-color: rgba(56, 189, 248, 0.98);
613
+ border-right-color: rgba(99, 102, 241, 0.94);
614
+ box-shadow:
615
+ 0 0 18px rgba(56, 189, 248, 0.6),
616
+ inset 0 0 12px rgba(59, 130, 246, 0.15);
617
+ animation: loader-spin 1.35s linear infinite;
618
+ }
619
+
620
+ .luxe-loader__ring--inner {
621
+ inset: 13px;
622
+ border-left-color: rgba(20, 184, 166, 0.96);
623
+ border-bottom-color: rgba(56, 189, 248, 0.88);
624
+ box-shadow: 0 0 14px rgba(20, 184, 166, 0.45);
625
+ animation: loader-spin-reverse 1.05s linear infinite;
626
+ }
627
+
628
+ .luxe-loader__core {
629
+ position: absolute;
630
+ inset: 34px;
631
+ border-radius: 9999px;
632
+ background: radial-gradient(
633
+ circle at 35% 30%,
634
+ rgba(255, 255, 255, 1) 0%,
635
+ rgba(210, 237, 255, 0.95) 42%,
636
+ rgba(107, 172, 255, 0.78) 100%
637
+ );
638
+ box-shadow:
639
+ 0 0 24px rgba(56, 189, 248, 0.48),
640
+ 0 0 34px rgba(99, 102, 241, 0.24);
641
+ animation: loader-core-breathe 2.1s ease-in-out infinite;
642
+ }
643
+
644
+ .luxe-loader__orbit {
645
+ position: absolute;
646
+ inset: 0;
647
+ animation: loader-spin 2.5s linear infinite;
648
+ }
649
+
650
+ .luxe-loader__dot {
651
+ position: absolute;
652
+ left: 50%;
653
+ top: 3px;
654
+ height: 9px;
655
+ width: 9px;
656
+ border-radius: 9999px;
657
+ transform: translateX(-50%);
658
+ background: radial-gradient(
659
+ circle at 30% 30%,
660
+ rgba(255, 255, 255, 1) 0%,
661
+ rgba(167, 243, 255, 0.95) 35%,
662
+ rgba(34, 211, 238, 0.95) 100%
663
+ );
664
+ box-shadow:
665
+ 0 0 10px rgba(34, 211, 238, 0.8),
666
+ 0 0 18px rgba(56, 189, 248, 0.5);
667
+ }
668
+
669
+ .aurora-frame {
670
+ pointer-events: none;
671
+ position: absolute;
672
+ inset: 0;
673
+ z-index: 20;
674
+ }
675
+
676
+ .aurora-edge {
677
+ position: absolute;
678
+ opacity: 0.95;
679
+ will-change: background-position, opacity;
680
+ }
681
+
682
+ .aurora-edge.is-glow {
683
+ filter: blur(16px);
684
+ opacity: 0.75;
685
+ }
686
+
687
+ .aurora-edge.top,
688
+ .aurora-edge.bottom {
689
+ left: 0;
690
+ right: 0;
691
+ height: 4px;
692
+ background-image: linear-gradient(
693
+ 90deg,
694
+ rgba(16, 185, 129, 0.2),
695
+ rgba(34, 211, 238, 1),
696
+ rgba(59, 130, 246, 1),
697
+ rgba(16, 185, 129, 0.2)
698
+ );
699
+ background-size: 320% 100%;
700
+ box-shadow:
701
+ 0 0 8px rgba(34, 211, 238, 0.9),
702
+ 0 0 22px rgba(59, 130, 246, 0.65);
703
+ animation:
704
+ aurora-shift-x 5.2s linear infinite,
705
+ aurora-breathe 2.6s ease-in-out infinite;
706
+ }
707
+
708
+ .aurora-edge.top {
709
+ top: 0;
710
+ }
711
+
712
+ .aurora-edge.bottom {
713
+ bottom: 0;
714
+ animation-direction: reverse, normal;
715
+ }
716
+
717
+ .aurora-edge.left,
718
+ .aurora-edge.right {
719
+ top: 0;
720
+ bottom: 0;
721
+ width: 4px;
722
+ background-image: linear-gradient(
723
+ 180deg,
724
+ rgba(16, 185, 129, 0.2),
725
+ rgba(34, 211, 238, 1),
726
+ rgba(59, 130, 246, 1),
727
+ rgba(16, 185, 129, 0.2)
728
+ );
729
+ background-size: 100% 320%;
730
+ box-shadow:
731
+ 0 0 8px rgba(34, 211, 238, 0.85),
732
+ 0 0 20px rgba(59, 130, 246, 0.6);
733
+ animation:
734
+ aurora-shift-y 5.8s linear infinite,
735
+ aurora-breathe 3s ease-in-out infinite;
736
+ }
737
+
738
+ .aurora-edge.left {
739
+ left: 0;
740
+ }
741
+
742
+ .aurora-edge.right {
743
+ right: 0;
744
+ animation-direction: reverse, normal;
745
+ }
746
+
747
+ @media (prefers-reduced-motion: reduce) {
748
+ .nebula-orb,
749
+ .nebula-grain,
750
+ .luxe-loader__ring--outer,
751
+ .luxe-loader__ring--inner,
752
+ .luxe-loader__core,
753
+ .luxe-loader__orbit {
754
+ animation: none;
755
+ }
756
+
757
+ .aurora-edge {
758
+ animation: none;
759
+ opacity: 0.75;
760
+ }
761
+ }
431
762
  }
432
763
 
433
764
  html[data-preview-shield="on"] nextjs-portal,