feat: stable version before virtual monitor timing fix

Current status includes:
- Virtual monitor surface and components
- Monitor store for state management
- Tool call animations and transitions
- Liquid glass shader integration

Known issue to fix: Tool status display timing - "正在xx" appears
after tool execution completes instead of when tool call starts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JOJO 2025-12-13 17:12:12 +08:00
parent d97955fdc1
commit 2f75c1c8bb
206 changed files with 46776 additions and 145 deletions

View File

@ -13,6 +13,7 @@
| **单用户-单容器** | `modules/user_container_manager.py` 会在登录时启动专属容器并在退出或空闲超时后自动销毁。CLI/Web/Toolbox 复用同一容器,资源配额(默认 0.5 vCPU / 1GB RAM / 2GB 磁盘)由 `.env` 控制。 |
| **容器内文件代理** | `modules/container_file_proxy.py` 通过 `docker exec` 调用内置 Python 脚本,对 `create/read/search/write/modify` 等操作进行沙箱化处理,宿主机不再直写用户代码。 |
| **实时监控面板** | Web “用量统计”抽屉实时展示 Token 消耗、容器 CPU/内存、网络上下行速率0.5s 刷新以及项目存储占用5s 刷新。CLI `/status` 命令也会附带容器状态。 |
| **管理员监控面板** | 管理员身份可在「个人空间」中打开 `/admin/monitor`,集中查看用户用量、容器状态、项目存储与上传审计,支持一键刷新与自动轮询。 |
| **联网能力 + 最小工具集** | 终端镜像改为 `bridge` 网络并预装 `iputils-ping`,方便验证网络连通性;遇到受限环境可以随时在 `.env` 中切换网络模式。 |
---
@ -121,7 +122,7 @@ python web_server.py
## 开发建议
1. **安全**:新增模块前请优先考虑是否可以在容器中实现,避免回退宿主机;如需联网,务必评估外部依赖。
1. **安全**:新增模块前请优先考虑是否可以在容器中实现,避免回退宿主机;如需联网,务必评估外部依赖。上传检测现阶段默认关闭 ClamAV`UPLOAD_CLAMAV_ENABLED=0`),以免 1GB+ 的常驻内存占用,必要时可以在环境变量中显式打开。
2. **日志**:尽量使用 `utils.logger.setup_logger`,便于统一收集。
3. **测试**`users/<username>/project/test_scripts/` 提供内存压测脚本,可验证容器限制是否生效;可在 `test/` 下添加更多集成测试。
4. **文档**:第二阶段总结见 `doc/phases/phase2_summary.md`,安全基线更新见 `doc/security/security_review.md`

View File

@ -42,7 +42,8 @@ def _parse_bool(value: str, default: bool = True) -> bool:
return str(value).strip().lower() not in {"0", "false", "no", "off"}
UPLOAD_CLAMAV_ENABLED = _parse_bool(os.environ.get("UPLOAD_CLAMAV_ENABLED", "1"), default=True)
# 默认关闭 ClamAV 扫描以降低常驻内存占用,可通过设置环境变量重新开启
UPLOAD_CLAMAV_ENABLED = _parse_bool(os.environ.get("UPLOAD_CLAMAV_ENABLED", "0"), default=False)
UPLOAD_CLAMAV_BIN = os.environ.get("UPLOAD_CLAMAV_BIN", "clamdscan")
UPLOAD_CLAMAV_ARGS = tuple(
shlex.split(os.environ.get("UPLOAD_CLAMAV_ARGS", "--fdpass --no-summary --stdout"))

View File

@ -336,7 +336,9 @@ class WebTerminal(MainTerminal):
def broadcast(self, event_type: str, data: Dict):
"""广播事件到WebSocket"""
if self.message_callback:
self.message_callback(event_type, data)
payload = dict(data or {})
payload.setdefault('conversation_id', self.context_manager.current_conversation_id)
self.message_callback(event_type, payload)
# ===========================================
# 覆盖父类方法添加Web特有的广播功能
@ -481,10 +483,16 @@ class WebTerminal(MainTerminal):
# 特殊处理某些错误类型
if not success:
error_msg = result_data.get('error', '执行失败')
error_msg = result_data.get('error')
if not error_msg:
error_msg = result_data.get('message')
if not error_msg:
error_msg = '执行失败'
if not isinstance(error_msg, str):
error_msg = str(error_msg)
# 检查是否是参数预检查失败
if '参数过大' in error_msg or '内容过长' in error_msg:
if error_msg and ('参数过大' in error_msg or '内容过长' in error_msg):
self.broadcast('tool_execution_end', {
'tool': tool_name,
'success': False,
@ -493,7 +501,7 @@ class WebTerminal(MainTerminal):
'error_type': 'parameter_too_long',
'suggestion': result_data.get('suggestion', '建议分块处理')
})
elif 'JSON解析' in error_msg or '参数解析失败' in error_msg:
elif error_msg and ('JSON解析' in error_msg or '参数解析失败' in error_msg):
self.broadcast('tool_execution_end', {
'tool': tool_name,
'success': False,

View File

@ -152,6 +152,10 @@ class UserManager:
def list_invite_codes(self):
return list(self._invites.values())
def list_users(self) -> Dict[str, UserRecord]:
"""返回当前注册用户的浅拷贝字典,键为用户名。"""
return dict(self._users)
# ------------------------------------------------------------------
# Internal helpers
# ------------------------------------------------------------------

75
package-lock.json generated
View File

@ -8,6 +8,8 @@
"name": "ai-agent-frontend",
"version": "4.1.0",
"dependencies": {
"@types/html2canvas": "^0.5.35",
"html2canvas": "^1.4.1",
"katex": "^0.16.9",
"marked": "^11.1.0",
"pinia": "^3.0.4",
@ -1303,6 +1305,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/html2canvas": {
"version": "0.5.35",
"resolved": "https://registry.npmjs.org/@types/html2canvas/-/html2canvas-0.5.35.tgz",
"integrity": "sha512-1A2dtWZbOIZ+rUK8jmAx1We/EiNV+5vScpphU3AF14Vby6COIazi/9StosrvlVCqlQegRhsEgZf7QYOuWbwuuA==",
"license": "MIT",
"dependencies": {
"@types/jquery": "*"
}
},
"node_modules/@types/jquery": {
"version": "3.5.33",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz",
"integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==",
"license": "MIT",
"dependencies": {
"@types/sizzle": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -1327,6 +1347,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/sizzle": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz",
"integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
@ -1866,6 +1892,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/birpc": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz",
@ -2021,6 +2056,15 @@
"node": ">= 8"
}
},
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -2747,6 +2791,19 @@
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"license": "MIT",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -3727,6 +3784,15 @@
"url": "https://opencollective.com/synckit"
}
},
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@ -3824,6 +3890,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"license": "MIT",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
},
"node_modules/vite": {
"version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",

View File

@ -11,6 +11,8 @@
"preview": "vite preview"
},
"dependencies": {
"@types/html2canvas": "^0.5.35",
"html2canvas": "^1.4.1",
"katex": "^0.16.9",
"marked": "^11.1.0",
"pinia": "^3.0.4",

2
scratch_test/empty.txt Normal file
View File

@ -0,0 +1,2 @@
hello
world

100
scripts/kimi_cache_test.py Normal file
View File

@ -0,0 +1,100 @@
"""Moonshot Kimi 缓存命中测试脚本。
This script replays the three-turn conversation described in the QA instructions
to inspect whether the `cached_tokens` field grows across requests. 结果会被写入
`logs/kimi_cache_usage.json`方便后续排查缓存命中情况
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any, Dict, List
from openai import OpenAI
SYSTEM_PROMPT = (
"你是 Kimi由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。"
"你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,"
"种族歧视黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。"
)
QUERIES = ["你好!", "地球的自转周期是多少?", "月球呢?"]
ENV_PATH = Path(".env")
OUTPUT_PATH = Path("logs/kimi_cache_usage.json")
def load_env_values(path: Path) -> Dict[str, str]:
"""Load simple KEY=VALUE pairs from `.env`."""
data: Dict[str, str] = {}
for line in path.read_text(encoding="utf-8").splitlines():
stripped = line.strip()
if not stripped or stripped.startswith("#") or "=" not in stripped:
continue
key, value = stripped.split("=", 1)
data[key.strip()] = value.strip()
return data
def ensure_required(keys: List[str], env: Dict[str, str]) -> None:
"""Guard that required env variables exist."""
missing = [key for key in keys if not env.get(key)]
if missing:
raise RuntimeError(f"Missing required env vars: {', '.join(missing)}")
def main() -> None:
env = load_env_values(ENV_PATH)
ensure_required(["AGENT_API_KEY", "AGENT_API_BASE_URL", "AGENT_MODEL_ID"], env)
client = OpenAI(api_key=env["AGENT_API_KEY"], base_url=env["AGENT_API_BASE_URL"])
model = env["AGENT_MODEL_ID"]
history: List[Dict[str, str]] = [{"role": "system", "content": SYSTEM_PROMPT}]
records: List[Dict[str, Any]] = []
for idx, query in enumerate(QUERIES, start=1):
history.append({"role": "user", "content": query})
completion = client.chat.completions.create(
model=model,
messages=history,
temperature=0.6,
)
answer = completion.choices[0].message.content
history.append({"role": "assistant", "content": answer})
payload = completion.model_dump()
usage_dict = payload.get("usage", {})
records.append(
{
"round": idx,
"query": query,
"response": answer,
"usage": usage_dict,
}
)
usage_round1 = records[0]["usage"]
usage_round2 = records[1]["usage"]
usage_round3 = records[2]["usage"]
analysis = {
"round2_cached_equals_round1_total": usage_round2.get("cached_tokens")
== usage_round1.get("total_tokens"),
"round3_cached_equals_round2_total": usage_round3.get("cached_tokens")
== usage_round2.get("total_tokens"),
"cached_tokens_sequence": [
usage_round1.get("cached_tokens"),
usage_round2.get("cached_tokens"),
usage_round3.get("cached_tokens"),
],
}
OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_PATH.write_text(
json.dumps({"details": records, "analysis": analysis}, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"Wrote results to {OUTPUT_PATH}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,68 @@
"""Official Moonshot sample adapted to print raw JSON responses."""
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any, Dict
from openai import OpenAI
def resolve_env_value(keys: tuple[str, ...], *, default: str | None = None) -> str:
"""Try process env first, then fall back to `.env` file."""
for key in keys:
value = os.environ.get(key)
if value:
return value
env_path = Path(".env")
if env_path.exists():
for line in env_path.read_text(encoding="utf-8").splitlines():
stripped = line.strip()
if not stripped or stripped.startswith("#") or "=" not in stripped:
continue
env_key, env_value = stripped.split("=", 1)
if env_key in keys and env_value.strip():
return env_value.strip()
if default is not None:
return default
raise RuntimeError(f"Missing required environment variable from {keys}.")
client = OpenAI(
api_key=resolve_env_value(("MOONSHOT_API_KEY", "AGENT_API_KEY")),
base_url=resolve_env_value(
("MOONSHOT_BASE_URL", "AGENT_API_BASE_URL"), default="https://api.moonshot.cn/v1"
),
)
history = [
{
"role": "system",
"content": (
"你是 Kimi由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。"
"你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,"
"种族歧视黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。"
),
}
]
def chat(query: str, history_list: list[Dict[str, str]]) -> Dict[str, Any]:
history_list.append({"role": "user", "content": query})
completion = client.chat.completions.create(
model="kimi-k2-turbo-preview",
messages=history_list,
temperature=0.6,
)
result_content = completion.choices[0].message.content
history_list.append({"role": "assistant", "content": result_content})
return completion.model_dump()
if __name__ == "__main__":
earth = chat("地球的自转周期是多少?", history)
moon = chat("月球呢?", history)
print(json.dumps(earth, ensure_ascii=False, indent=2))
print(json.dumps(moon, ensure_ascii=False, indent=2))

View File

@ -0,0 +1,54 @@
"""Minimal Moonshot sample that also prints the usage payload."""
from __future__ import annotations
import json
from typing import Dict, List, Tuple
from openai import OpenAI
client = OpenAI(
api_key="sk-xW0xjfQM6Mp9ZCWMLlnHiRJcpEOIZPTkXcN0dQ15xpZSuw2y",
base_url="https://api.moonshot.cn/v1",
)
history: List[Dict[str, str]] = [
{
"role": "system",
"content": (
"你是 Kimi由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。"
"你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,"
"种族歧视黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。"
),
}
]
def chat(query: str, history_list: List[Dict[str, str]]) -> Tuple[str, Dict[str, int]]:
history_list.append({"role": "user", "content": query})
completion = client.chat.completions.create(
model="kimi-k2-0905-preview",
messages=history_list,
temperature=0.6,
)
result = completion.choices[0].message.content
history_list.append({"role": "assistant", "content": result})
usage_dict: Dict[str, int] = (
completion.usage.model_dump() if completion.usage else {}
)
return result, usage_dict
def main() -> None:
earth_answer, earth_usage = chat("地球的自转周期是多少?", history)
moon_answer, moon_usage = chat("月球呢?", history)
print(earth_answer)
print(json.dumps(earth_usage, ensure_ascii=False, indent=2))
print(moon_answer)
print(json.dumps(moon_usage, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,21 @@
{
"pages": {
"/layout": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/css/app/layout.css",
"static/chunks/app/layout.js"
],
"/page": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/css/app/page.css",
"static/chunks/app/page.js"
],
"/_not-found/page": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/chunks/app/_not-found/page.js"
]
}
}

View File

@ -0,0 +1,20 @@
{
"polyfillFiles": [
"static/chunks/polyfills.js"
],
"devFiles": [],
"ampDevFiles": [],
"lowPriorityFiles": [
"static/development/_buildManifest.js",
"static/development/_ssgManifest.js"
],
"rootMainFiles": [
"static/chunks/webpack.js",
"static/chunks/main-app.js"
],
"rootMainFilesTree": {},
"pages": {
"/_app": []
},
"ampFirstPages": []
}

View File

@ -0,0 +1 @@
{"encryption.key":"nIrtn86Hm0WlHEfVtm35NcUkmbNTI0U+eXfpVfP5kSI=","encryption.expire_at":1765854859753}

View File

@ -0,0 +1 @@
{"type": "commonjs"}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,4 @@
{
"/_not-found/page": "app/_not-found/page.js",
"/page": "app/page.js"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST="[]"

View File

@ -0,0 +1,22 @@
globalThis.__BUILD_MANIFEST = {
"polyfillFiles": [
"static/chunks/polyfills.js"
],
"devFiles": [],
"ampDevFiles": [],
"lowPriorityFiles": [],
"rootMainFiles": [
"static/chunks/webpack.js",
"static/chunks/main-app.js"
],
"rootMainFilesTree": {},
"pages": {
"/_app": []
},
"ampFirstPages": []
};
globalThis.__BUILD_MANIFEST.lowPriorityFiles = [
"/static/" + process.env.__NEXT_BUILD_ID + "/_buildManifest.js",
,"/static/" + process.env.__NEXT_BUILD_ID + "/_ssgManifest.js",
];

View File

@ -0,0 +1,6 @@
{
"version": 3,
"middleware": {},
"functions": {},
"sortedMiddleware": []
}

View File

@ -0,0 +1 @@
self.__REACT_LOADABLE_MANIFEST="{}"

View File

@ -0,0 +1 @@
self.__NEXT_FONT_MANIFEST="{\"pages\":{},\"app\":{\"/Users/jojo/Desktop/agents/正在修复中/agents/scripts/liquid-glass-demo/app/layout\":[\"static/media/e4af272ccee01ff0-s.p.woff2\"],\"/Users/jojo/Desktop/agents/正在修复中/agents/scripts/liquid-glass-demo/app/page\":[\"static/media/e4af272ccee01ff0-s.p.woff2\"]},\"appUsingSizeAdjust\":true,\"pagesUsingSizeAdjust\":false}"

View File

@ -0,0 +1 @@
{"pages":{},"app":{"/Users/jojo/Desktop/agents/正在修复中/agents/scripts/liquid-glass-demo/app/layout":["static/media/e4af272ccee01ff0-s.p.woff2"],"/Users/jojo/Desktop/agents/正在修复中/agents/scripts/liquid-glass-demo/app/page":["static/media/e4af272ccee01ff0-s.p.woff2"]},"appUsingSizeAdjust":true,"pagesUsingSizeAdjust":false}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY\"\n}"

View File

@ -0,0 +1,5 @@
{
"node": {},
"edge": {},
"encryptionKey": "nIrtn86Hm0WlHEfVtm35NcUkmbNTI0U+eXfpVfP5kSI="
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,75 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
exports.id = "vendor-chunks/@swc";
exports.ids = ["vendor-chunks/@swc"];
exports.modules = {
/***/ "(rsc)/./node_modules/@swc/helpers/esm/_interop_require_default.js":
/*!*******************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_interop_require_default.js ***!
\*******************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_default)\n/* harmony export */ });\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanMiLCJtYXBwaW5ncyI6Ijs7OztBQUFBO0FBQ0EsMkNBQTJDO0FBQzNDO0FBQ3lDIiwic291cmNlcyI6WyIvVXNlcnMvam9qby9EZXNrdG9wL2FnZW50cy/mraPlnKjkv67lpI3kuK0vYWdlbnRzL3NjcmlwdHMvbGlxdWlkLWdsYXNzLWRlbW8vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9pbnRlcm9wX3JlcXVpcmVfZGVmYXVsdChvYmopIHtcbiAgICByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBkZWZhdWx0OiBvYmogfTtcbn1cbmV4cG9ydCB7IF9pbnRlcm9wX3JlcXVpcmVfZGVmYXVsdCBhcyBfIH07XG4iXSwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbMF0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/@swc/helpers/esm/_interop_require_default.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js":
/*!**************************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js ***!
\**************************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _class_private_field_loose_base)\n/* harmony export */ });\nfunction _class_private_field_loose_base(receiver, privateKey) {\n if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {\n throw new TypeError(\"attempted to use private field on non-instance\");\n }\n\n return receiver;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9iYXNlLmpzIiwibWFwcGluZ3MiOiI7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ2dEIiwic291cmNlcyI6WyIvVXNlcnMvam9qby9EZXNrdG9wL2FnZW50cy/mraPlnKjkv67lpI3kuK0vYWdlbnRzL3NjcmlwdHMvbGlxdWlkLWdsYXNzLWRlbW8vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX2NsYXNzX3ByaXZhdGVfZmllbGRfbG9vc2VfYmFzZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBfY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9iYXNlKHJlY2VpdmVyLCBwcml2YXRlS2V5KSB7XG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocmVjZWl2ZXIsIHByaXZhdGVLZXkpKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJhdHRlbXB0ZWQgdG8gdXNlIHByaXZhdGUgZmllbGQgb24gbm9uLWluc3RhbmNlXCIpO1xuICAgIH1cblxuICAgIHJldHVybiByZWNlaXZlcjtcbn1cbmV4cG9ydCB7IF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2Jhc2UgYXMgXyB9O1xuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6WzBdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js":
/*!*************************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js ***!
\*************************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _class_private_field_loose_key)\n/* harmony export */ });\nvar id = 0;\n\nfunction _class_private_field_loose_key(name) {\n return \"__private_\" + id++ + \"_\" + name;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9rZXkuanMiLCJtYXBwaW5ncyI6Ijs7OztBQUFBOztBQUVBO0FBQ0E7QUFDQTtBQUMrQyIsInNvdXJjZXMiOlsiL1VzZXJzL2pvam8vRGVza3RvcC9hZ2VudHMv5q2j5Zyo5L+u5aSN5LitL2FnZW50cy9zY3JpcHRzL2xpcXVpZC1nbGFzcy1kZW1vL25vZGVfbW9kdWxlcy9Ac3djL2hlbHBlcnMvZXNtL19jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2tleS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgaWQgPSAwO1xuXG5mdW5jdGlvbiBfY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9rZXkobmFtZSkge1xuICAgIHJldHVybiBcIl9fcHJpdmF0ZV9cIiArIGlkKysgKyBcIl9cIiArIG5hbWU7XG59XG5leHBvcnQgeyBfY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9rZXkgYXMgXyB9O1xuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6WzBdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_interop_require_default.js":
/*!*******************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_interop_require_default.js ***!
\*******************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_default)\n/* harmony export */ });\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanMiLCJtYXBwaW5ncyI6Ijs7OztBQUFBO0FBQ0EsMkNBQTJDO0FBQzNDO0FBQ3lDIiwic291cmNlcyI6WyIvVXNlcnMvam9qby9EZXNrdG9wL2FnZW50cy/mraPlnKjkv67lpI3kuK0vYWdlbnRzL3NjcmlwdHMvbGlxdWlkLWdsYXNzLWRlbW8vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9pbnRlcm9wX3JlcXVpcmVfZGVmYXVsdChvYmopIHtcbiAgICByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBkZWZhdWx0OiBvYmogfTtcbn1cbmV4cG9ydCB7IF9pbnRlcm9wX3JlcXVpcmVfZGVmYXVsdCBhcyBfIH07XG4iXSwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbMF0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_interop_require_default.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_interop_require_wildcard.js":
/*!********************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_interop_require_wildcard.js ***!
\********************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_wildcard)\n/* harmony export */ });\nfunction _getRequireWildcardCache(nodeInterop) {\n if (typeof WeakMap !== \"function\") return null;\n\n var cacheBabelInterop = new WeakMap();\n var cacheNodeInterop = new WeakMap();\n\n return (_getRequireWildcardCache = function(nodeInterop) {\n return nodeInterop ? cacheNodeInterop : cacheBabelInterop;\n })(nodeInterop);\n}\nfunction _interop_require_wildcard(obj, nodeInterop) {\n if (!nodeInterop && obj && obj.__esModule) return obj;\n if (obj === null || typeof obj !== \"object\" && typeof obj !== \"function\") return { default: obj };\n\n var cache = _getRequireWildcardCache(nodeInterop);\n\n if (cache && cache.has(obj)) return cache.get(obj);\n\n var newObj = { __proto__: null };\n var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;\n\n for (var key in obj) {\n if (key !== \"default\" && Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;\n if (desc && (desc.get || desc.set)) Object.defineProperty(newObj, key, desc);\n else newObj[key] = obj[key];\n }\n }\n\n newObj.default = obj;\n\n if (cache) cache.set(obj, newObj);\n\n return newObj;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX3dpbGRjYXJkLmpzIiwibWFwcGluZ3MiOiI7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQSx1RkFBdUY7O0FBRXZGOztBQUVBOztBQUVBLG1CQUFtQjtBQUNuQjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBO0FBQzBDIiwic291cmNlcyI6WyIvVXNlcnMvam9qby9EZXNrdG9wL2FnZW50cy/mraPlnKjkv67lpI3kuK0vYWdlbnRzL3NjcmlwdHMvbGlxdWlkLWdsYXNzLWRlbW8vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX2ludGVyb3BfcmVxdWlyZV93aWxkY2FyZC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBfZ2V0UmVxdWlyZVdpbGRjYXJkQ2FjaGUobm9kZUludGVyb3ApIHtcbiAgICBpZiAodHlwZW9mIFdlYWtNYXAgIT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIG51bGw7XG5cbiAgICB2YXIgY2FjaGVCYWJlbEludGVyb3AgPSBuZXcgV2Vha01hcCgpO1xuICAgIHZhciBjYWNoZU5vZGVJbnRlcm9wID0gbmV3IFdlYWtNYXAoKTtcblxuICAgIHJldHVybiAoX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlID0gZnVuY3Rpb24obm9kZUludGVyb3ApIHtcbiAgICAgICAgcmV0dXJuIG5vZGVJbnRlcm9wID8gY2FjaGVOb2RlSW50ZXJvcCA6IGNhY2hlQmFiZWxJbnRlcm9wO1xuICAgIH0pKG5vZGVJbnRlcm9wKTtcbn1cbmZ1bmN0aW9uIF9pbnRlcm9wX3JlcXVpcmVfd2lsZGNhcmQob2JqLCBub2RlSW50ZXJvcCkge1xuICAgIGlmICghbm9kZUludGVyb3AgJiYgb2JqICYmIG9iai5fX2VzTW9kdWxlKSByZXR1cm4gb2JqO1xuICAgIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gXCJvYmplY3RcIiAmJiB0eXBlb2Ygb2JqICE9PSBcImZ1bmN0aW9uXCIpIHJldHVybiB7IGRlZmF1bHQ6IG9iaiB9O1xuXG4gICAgdmFyIGNhY2hlID0gX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlKG5vZGVJbnRlcm9wKTtcblxuICAgIGlmIChjYWNoZSAmJiBjYWNoZS5oYXMob2JqKSkgcmV0dXJuIGNhY2hlLmdldChvYmopO1xuXG4gICAgdmFyIG5ld09iaiA9IHsgX19wcm90b19fOiBudWxsIH07XG4gICAgdmFyIGhhc1Byb3BlcnR5RGVzY3JpcHRvciA9IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSAmJiBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yO1xuXG4gICAgZm9yICh2YXIga2V5IGluIG9iaikge1xuICAgICAgICBpZiAoa2V5ICE9PSBcImRlZmF1bHRcIiAmJiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBrZXkpKSB7XG4gICAgICAgICAgICB2YXIgZGVzYyA9IGhhc1Byb3BlcnR5RGVzY3JpcHRvciA/IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqLCBrZXkpIDogbnVsbDtcbiAgICAgICAgICAgIGlmIChkZXNjICYmIChkZXNjLmdldCB8fCBkZXNjLnNldCkpIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShuZXdPYmosIGtleSwgZGVzYyk7XG4gICAgICAgICAgICBlbHNlIG5ld09ialtrZXldID0gb2JqW2tleV07XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBuZXdPYmouZGVmYXVsdCA9IG9iajtcblxuICAgIGlmIChjYWNoZSkgY2FjaGUuc2V0KG9iaiwgbmV3T2JqKTtcblxuICAgIHJldHVybiBuZXdPYmo7XG59XG5leHBvcnQgeyBfaW50ZXJvcF9yZXF1aXJlX3dpbGRjYXJkIGFzIF8gfTtcbiJdLCJuYW1lcyI6W10sImlnbm9yZUxpc3QiOlswXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_interop_require_wildcard.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js":
/*!*************************************************************************!*\
!*** ./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js ***!
\*************************************************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _tagged_template_literal_loose)\n/* harmony export */ });\nfunction _tagged_template_literal_loose(strings, raw) {\n if (!raw) raw = strings.slice(0);\n\n strings.raw = raw;\n\n return strings;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWxfbG9vc2UuanMiLCJtYXBwaW5ncyI6Ijs7OztBQUFBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUMrQyIsInNvdXJjZXMiOlsiL1VzZXJzL2pvam8vRGVza3RvcC9hZ2VudHMv5q2j5Zyo5L+u5aSN5LitL2FnZW50cy9zY3JpcHRzL2xpcXVpZC1nbGFzcy1kZW1vL25vZGVfbW9kdWxlcy9Ac3djL2hlbHBlcnMvZXNtL190YWdnZWRfdGVtcGxhdGVfbGl0ZXJhbF9sb29zZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBfdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWxfbG9vc2Uoc3RyaW5ncywgcmF3KSB7XG4gICAgaWYgKCFyYXcpIHJhdyA9IHN0cmluZ3Muc2xpY2UoMCk7XG5cbiAgICBzdHJpbmdzLnJhdyA9IHJhdztcblxuICAgIHJldHVybiBzdHJpbmdzO1xufVxuZXhwb3J0IHsgX3RhZ2dlZF90ZW1wbGF0ZV9saXRlcmFsX2xvb3NlIGFzIF8gfTtcbiJdLCJuYW1lcyI6W10sImlnbm9yZUxpc3QiOlswXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js\n");
/***/ })
};
;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,25 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
exports.id = "vendor-chunks/clsx";
exports.ids = ["vendor-chunks/clsx"];
exports.modules = {
/***/ "(ssr)/./node_modules/clsx/dist/clsx.mjs":
/*!*****************************************!*\
!*** ./node_modules/clsx/dist/clsx.mjs ***!
\*****************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ clsx: () => (/* binding */ clsx),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nfunction r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (clsx);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvY2xzeC9kaXN0L2Nsc3gubWpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsY0FBYyxhQUFhLCtDQUErQyxnREFBZ0QsZUFBZSxRQUFRLElBQUksMENBQTBDLHlDQUF5QyxTQUFnQixnQkFBZ0Isd0NBQXdDLElBQUksbURBQW1ELFNBQVMsaUVBQWUsSUFBSSIsInNvdXJjZXMiOlsiL1VzZXJzL2pvam8vRGVza3RvcC9hZ2VudHMv5q2j5Zyo5L+u5aSN5LitL2FnZW50cy9zY3JpcHRzL2xpcXVpZC1nbGFzcy1kZW1vL25vZGVfbW9kdWxlcy9jbHN4L2Rpc3QvY2xzeC5tanMiXSwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gcihlKXt2YXIgdCxmLG49XCJcIjtpZihcInN0cmluZ1wiPT10eXBlb2YgZXx8XCJudW1iZXJcIj09dHlwZW9mIGUpbis9ZTtlbHNlIGlmKFwib2JqZWN0XCI9PXR5cGVvZiBlKWlmKEFycmF5LmlzQXJyYXkoZSkpe3ZhciBvPWUubGVuZ3RoO2Zvcih0PTA7dDxvO3QrKyllW3RdJiYoZj1yKGVbdF0pKSYmKG4mJihuKz1cIiBcIiksbis9Zil9ZWxzZSBmb3IoZiBpbiBlKWVbZl0mJihuJiYobis9XCIgXCIpLG4rPWYpO3JldHVybiBufWV4cG9ydCBmdW5jdGlvbiBjbHN4KCl7Zm9yKHZhciBlLHQsZj0wLG49XCJcIixvPWFyZ3VtZW50cy5sZW5ndGg7ZjxvO2YrKykoZT1hcmd1bWVudHNbZl0pJiYodD1yKGUpKSYmKG4mJihuKz1cIiBcIiksbis9dCk7cmV0dXJuIG59ZXhwb3J0IGRlZmF1bHQgY2xzeDsiXSwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbMF0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/clsx/dist/clsx.mjs\n");
/***/ })
};
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,215 @@
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({});
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ loaded: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ var threw = true;
/******/ try {
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ threw = false;
/******/ } finally {
/******/ if(threw) delete __webpack_module_cache__[moduleId];
/******/ }
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/create fake namespace object */
/******/ (() => {
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
/******/ var leafPrototypes;
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 16: return value when it's Promise-like
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = this(value);
/******/ if(mode & 8) return value;
/******/ if(typeof value === 'object' && value) {
/******/ if((mode & 4) && value.__esModule) return value;
/******/ if((mode & 16) && typeof value.then === 'function') return value;
/******/ }
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ var def = {};
/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
/******/ }
/******/ def['default'] = () => (value);
/******/ __webpack_require__.d(ns, def);
/******/ return ns;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks and sibling chunks for the entrypoint
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("54c87fec7e7388bb")
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/node module decorator */
/******/ (() => {
/******/ __webpack_require__.nmd = (module) => {
/******/ module.paths = [];
/******/ if (!module.children) module.children = [];
/******/ return module;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/startup entrypoint */
/******/ (() => {
/******/ __webpack_require__.X = (result, chunkIds, fn) => {
/******/ // arguments: chunkIds, moduleId are deprecated
/******/ var moduleId = chunkIds;
/******/ if(!fn) chunkIds = result, fn = () => (__webpack_require__(__webpack_require__.s = moduleId));
/******/ chunkIds.map(__webpack_require__.e, __webpack_require__)
/******/ var r = fn();
/******/ return r === undefined ? result : r;
/******/ }
/******/ })();
/******/
/******/ /* webpack/runtime/require chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded chunks
/******/ // "1" means "loaded", otherwise not loaded yet
/******/ var installedChunks = {
/******/ "webpack-runtime": 1
/******/ };
/******/
/******/ // no on chunks loaded
/******/
/******/ var installChunk = (chunk) => {
/******/ var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;
/******/ for(var moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) runtime(__webpack_require__);
/******/ for(var i = 0; i < chunkIds.length; i++)
/******/ installedChunks[chunkIds[i]] = 1;
/******/
/******/ };
/******/
/******/ // require() chunk loading for javascript
/******/ __webpack_require__.f.require = (chunkId, promises) => {
/******/ // "1" is the signal for "already loaded"
/******/ if(!installedChunks[chunkId]) {
/******/ if("webpack-runtime" != chunkId) {
/******/ installChunk(require("./" + __webpack_require__.u(chunkId)));
/******/ } else installedChunks[chunkId] = 1;
/******/ }
/******/ };
/******/
/******/ module.exports = __webpack_require__;
/******/ __webpack_require__.C = installChunk;
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/ })();
/******/
/************************************************************************/
/******/
/******/
/******/ })()
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
/*!*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
!*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[13].oneOf[2].use[1]!./node_modules/next/dist/build/webpack/loaders/next-font-loader/index.js??ruleSet[1].rules[13].oneOf[2].use[2]!./node_modules/next/font/google/target.css?{"path":"app/fonts.ts","import":"Inter","arguments":[{"subsets":["latin"],"display":"swap","variable":"--font-inter"}],"variableName":"inter"} ***!
\*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/ba9851c3c22cd980-s.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/21350d82a1f187e9-s.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/c5fe6dc8356a8c31-s.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/19cfc7226ec3afaa-s.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/df0a9ae256c0569c-s.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/8e9860b6e62d6359-s.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url(/_next/static/media/e4af272ccee01ff0-s.p.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}@font-face {font-family: 'Inter Fallback';src: local("Arial");ascent-override: 90.44%;descent-override: 22.52%;line-gap-override: 0.00%;size-adjust: 107.12%
}.__className_f367f3 {font-family: 'Inter', 'Inter Fallback';font-style: normal
}.__variable_f367f3 {--font-inter: 'Inter', 'Inter Fallback'
}

View File

@ -0,0 +1 @@
self.__BUILD_MANIFEST = (function(a){return {__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},__routerFilterStatic:a,__routerFilterDynamic:a,sortedPages:["\u002F_app"]}}(void 0));self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()

View File

@ -0,0 +1 @@
self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()

View File

@ -0,0 +1 @@
{"c":[],"r":[],"m":[]}

View File

@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./app/globals.css":
/*!*************************!*\
!*** ./app/globals.css ***!
\*************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"cb68c59db2ce\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiOzs7O0FBQUEsaUVBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIi9Vc2Vycy9qb2pvL0Rlc2t0b3AvYWdlbnRzL+ato+WcqOS/ruWkjeS4rS9hZ2VudHMvc2NyaXB0cy9saXF1aWQtZ2xhc3MtZGVtby9hcHAvZ2xvYmFscy5jc3MiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJjYjY4YzU5ZGIyY2VcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
/***/ })
});

View File

@ -0,0 +1 @@
{"c":["webpack"],"r":[],"m":[]}

View File

@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("d9fe63931496764a")
/******/ })();
/******/
/******/ }
);

View File

@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("da9c7a21bb145999")
/******/ })();
/******/
/******/ }
);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,84 @@
// File: /Users/jojo/Desktop/agents/正在修复中/agents/scripts/liquid-glass-demo/app/layout.tsx
import * as entry from '../../../app/layout.js'
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
type TEntry = typeof import('../../../app/layout.js')
type SegmentParams<T extends Object = any> = T extends Record<string, any>
? { [K in keyof T]: T[K] extends string ? string | string[] | undefined : never }
: T
// Check that the entry is a valid entry
checkFields<Diff<{
default: Function
config?: {}
generateStaticParams?: Function
revalidate?: RevalidateRange<TEntry> | false
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
dynamicParams?: boolean
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
maxDuration?: number
metadata?: any
generateMetadata?: Function
viewport?: any
generateViewport?: Function
experimental_ppr?: boolean
}, TEntry, ''>>()
// Check the prop type of the entry function
checkFields<Diff<LayoutProps, FirstArg<TEntry['default']>, 'default'>>()
// Check the arguments and return type of the generateMetadata function
if ('generateMetadata' in entry) {
checkFields<Diff<LayoutProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
}
// Check the arguments and return type of the generateViewport function
if ('generateViewport' in entry) {
checkFields<Diff<LayoutProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
}
// Check the arguments and return type of the generateStaticParams function
if ('generateStaticParams' in entry) {
checkFields<Diff<{ params: SegmentParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
}
export interface PageProps {
params?: Promise<SegmentParams>
searchParams?: Promise<any>
}
export interface LayoutProps {
children?: React.ReactNode
params?: Promise<SegmentParams>
}
// =============
// Utility types
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
function checkFields<_ extends { [k in keyof any]: never }>() {}
// https://github.com/sindresorhus/type-fest
type Numeric = number | bigint
type Zero = 0 | 0n
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'

View File

@ -0,0 +1,84 @@
// File: /Users/jojo/Desktop/agents/正在修复中/agents/scripts/liquid-glass-demo/app/page.tsx
import * as entry from '../../../app/page.js'
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
type TEntry = typeof import('../../../app/page.js')
type SegmentParams<T extends Object = any> = T extends Record<string, any>
? { [K in keyof T]: T[K] extends string ? string | string[] | undefined : never }
: T
// Check that the entry is a valid entry
checkFields<Diff<{
default: Function
config?: {}
generateStaticParams?: Function
revalidate?: RevalidateRange<TEntry> | false
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
dynamicParams?: boolean
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
maxDuration?: number
metadata?: any
generateMetadata?: Function
viewport?: any
generateViewport?: Function
experimental_ppr?: boolean
}, TEntry, ''>>()
// Check the prop type of the entry function
checkFields<Diff<PageProps, FirstArg<TEntry['default']>, 'default'>>()
// Check the arguments and return type of the generateMetadata function
if ('generateMetadata' in entry) {
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
}
// Check the arguments and return type of the generateViewport function
if ('generateViewport' in entry) {
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
}
// Check the arguments and return type of the generateStaticParams function
if ('generateStaticParams' in entry) {
checkFields<Diff<{ params: SegmentParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
}
export interface PageProps {
params?: Promise<SegmentParams>
searchParams?: Promise<any>
}
export interface LayoutProps {
children?: React.ReactNode
params?: Promise<SegmentParams>
}
// =============
// Utility types
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
function checkFields<_ extends { [k in keyof any]: never }>() {}
// https://github.com/sindresorhus/type-fest
type Numeric = number | bigint
type Zero = 0 | 0n
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'

View File

@ -0,0 +1,141 @@
// Type definitions for Next.js cacheLife configs
declare module 'next/cache' {
export { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cache'
export {
revalidateTag,
revalidatePath,
unstable_expireTag,
unstable_expirePath,
} from 'next/dist/server/web/spec-extension/revalidate'
export { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store'
/**
* Cache this `"use cache"` for a timespan defined by the `"default"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 900 seconds (15 minutes)
* expire: never
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 15 minutes, start revalidating new values in the background.
* It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request.
*/
export function unstable_cacheLife(profile: "default"): void
/**
* Cache this `"use cache"` for a timespan defined by the `"seconds"` profile.
* ```
* stale: 0 seconds
* revalidate: 1 seconds
* expire: 60 seconds (1 minute)
* ```
*
* This cache may be stale on clients for 0 seconds before checking with the server.
* If the server receives a new request after 1 seconds, start revalidating new values in the background.
* If this entry has no traffic for 1 minute it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "seconds"): void
/**
* Cache this `"use cache"` for a timespan defined by the `"minutes"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 60 seconds (1 minute)
* expire: 3600 seconds (1 hour)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 minute, start revalidating new values in the background.
* If this entry has no traffic for 1 hour it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "minutes"): void
/**
* Cache this `"use cache"` for a timespan defined by the `"hours"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 3600 seconds (1 hour)
* expire: 86400 seconds (1 day)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 hour, start revalidating new values in the background.
* If this entry has no traffic for 1 day it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "hours"): void
/**
* Cache this `"use cache"` for a timespan defined by the `"days"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 86400 seconds (1 day)
* expire: 604800 seconds (1 week)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 day, start revalidating new values in the background.
* If this entry has no traffic for 1 week it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "days"): void
/**
* Cache this `"use cache"` for a timespan defined by the `"weeks"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 604800 seconds (1 week)
* expire: 2592000 seconds (30 days)
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 1 week, start revalidating new values in the background.
* If this entry has no traffic for 30 days it will expire. The next request will recompute it.
*/
export function unstable_cacheLife(profile: "weeks"): void
/**
* Cache this `"use cache"` for a timespan defined by the `"max"` profile.
* ```
* stale: 300 seconds (5 minutes)
* revalidate: 2592000 seconds (30 days)
* expire: never
* ```
*
* This cache may be stale on clients for 5 minutes before checking with the server.
* If the server receives a new request after 30 days, start revalidating new values in the background.
* It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request.
*/
export function unstable_cacheLife(profile: "max"): void
/**
* Cache this `"use cache"` using a custom timespan.
* ```
* stale: ... // seconds
* revalidate: ... // seconds
* expire: ... // seconds
* ```
*
* This is similar to Cache-Control: max-age=`stale`,s-max-age=`revalidate`,stale-while-revalidate=`expire-revalidate`
*
* If a value is left out, the lowest of other cacheLife() calls or the default, is used instead.
*/
export function unstable_cacheLife(profile: {
/**
* This cache may be stale on clients for ... seconds before checking with the server.
*/
stale?: number,
/**
* If the server receives a new request after ... seconds, start revalidating new values in the background.
*/
revalidate?: number,
/**
* If this entry has no traffic for ... seconds it will expire. The next request will recompute it.
*/
expire?: number
}): void
export { cacheTag as unstable_cacheTag } from 'next/dist/server/use-cache/cache-tag'
}

View File

@ -0,0 +1 @@
{"type": "module"}

View File

@ -0,0 +1,11 @@
.custom-slider {
@apply w-full h-1 bg-white/20;
}
.custom-slider .custom-slider-thumb {
@apply w-0.5 h-4 bg-white/50;
}
.custom-slider .custom-slider-track {
@apply h-full bg-white/50;
}

View File

@ -0,0 +1,21 @@
import { Inter } from "next/font/google"
import type { NextFontWithVariable } from "next/dist/compiled/@next/font"
import type { NextFont } from "next/font"
const ppEditorialNewUltralightItalic: NextFontWithVariable & NextFont = {
className: "font-pp-editorial",
style: {
fontFamily: "PPEditorialNew-UltralightItalic",
fontWeight: "200",
fontStyle: "italic",
},
variable: "--font-pp-editorial",
}
const inter = Inter({
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
})
export { ppEditorialNewUltralightItalic, inter }

View File

@ -0,0 +1,43 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: "PPEditorialNew-UltralightItalic";
src: url("https://raw.githubusercontent.com/oeneco/file-thing/9f9223f211387b3764942fcff87f57523870916d/PPEditorialNew-UltralightItalic.ttf")
format("truetype");
font-weight: 200;
font-style: italic;
font-display: swap;
}
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
@layer base {
body {
color: rgb(var(--foreground-rgb));
background: rgb(var(--background-start-rgb));
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "PPEditorialNew-UltralightItalic", sans-serif;
font-weight: 200;
font-style: italic;
letter-spacing: -0.025em;
}
p {
font-family: var(--font-inter);
font-weight: 300;
opacity: 0.6;
}
}

View File

@ -0,0 +1,25 @@
import "./globals.css"
import { ppEditorialNewUltralightItalic, inter } from "./fonts"
import type React from "react"
import Script from "next/script"
export const metadata = {
title: "Dynamic Frame Layout",
description: "A dynamic frame layout with custom fonts",
generator: 'v0.app'
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={`${ppEditorialNewUltralightItalic.variable} ${inter.variable}`}>
<body className={inter.className}>
{children}
<Script src="/liquid-glass.js" strategy="afterInteractive" />
</body>
</html>
)
}

View File

@ -0,0 +1,18 @@
import localFont from "next/font/local"
export const customFont = localFont({
src: [
{
path: "../public/fonts/your-font-regular.woff2",
weight: "300",
style: "normal",
},
{
path: "../public/fonts/your-font-italic.woff2",
weight: "400",
style: "italic",
},
// Add more variations as needed
],
display: "swap",
})

View File

@ -0,0 +1,83 @@
"use client"
import { useState } from "react"
import DynamicFrameLayout from "../components/DynamicFrameLayout"
import { ppEditorialNewUltralightItalic, inter } from "./fonts"
import Image from "next/image"
import Link from "next/link"
export default function Home() {
const [headerSize] = useState(1.2) // 120% is the default size
const [textSize] = useState(0.8) // 80% is the default size
return (
<div
className={`min-h-screen bg-[#141414] flex items-center justify-center p-8 ${ppEditorialNewUltralightItalic.variable} ${inter.variable}`}
>
<div className="w-full h-full flex flex-col md:flex-row items-start gap-8 md:gap-8">
{/* Left Content */}
<div className="w-full md:w-[260px] flex-shrink-0 flex flex-col justify-between h-full">
<div className="flex flex-col gap-16">
<h1
className={`${ppEditorialNewUltralightItalic.className} text-4xl md:text-6xl font-light italic text-white/80 tracking-tighter leading-[130%]`}
style={{ fontSize: `${4 * headerSize}rem` }}
>
Brand
<br />
Designer
<br />
at Luma?
</h1>
<div
className={`${inter.className} flex flex-col gap-12 text-white/50 text-sm font-light max-w-[300px]`}
style={{ fontSize: `${0.875 * textSize}rem` }}
>
<div className="space-y-6">
<div className="h-px bg-white/10 w-full" />
<p>
Luma is looking to hire a multi-disciplinary Brand Designer to develop and maintain the brand identity
and communicate the story of Luma to the world. Alongside members of the design team using product and
research insights, you will help shape and define Luma's brand across product surfaces, social media,
merchandise, marketing website, launch campaigns as well as other new channels.
</p>
<p>
You will use a combination of graphic design, motion design, web design and video production/editing
skills across traditional and innovative tools to communicate in visually compelling and impactful
ways.
</p>
<p>Here are some of our favorite works so far.</p>
<div className="h-px bg-white/10 w-full" />
</div>
</div>
<Link
href="https://lumalabs.ai"
target="_blank"
rel="noopener noreferrer"
className="w-8 h-8 relative opacity-80 hover:opacity-100 transition-opacity"
>
<Image
src="https://hebbkx1anhila5yf.public.blob.vercel-storage.com/LumaLogo%201-MA3upjPymxFHKoHJgpdAUfZMeKGq3i.png"
alt="Luma Logo"
fill
className="object-contain"
/>
</Link>
</div>
<a
href="https://lumalabs.ai/join?role=5d274587-f8fd-4f53-a5b6-8f85d586e1aa"
className="inline-block px-6 py-3 text-white/70 border border-white/20 rounded-full font-medium hover:bg-white/5 transition-colors text-center w-full max-w-[260px] text-sm mt-16"
target="_blank"
rel="noopener noreferrer"
>
Apply
</a>
</div>
{/* Right Content */}
<div className="w-full md:flex-grow h-[60vh] md:h-[80vh]">
<DynamicFrameLayout />
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@ -0,0 +1,322 @@
"use client"
import { useState } from "react"
import { motion } from "framer-motion"
import { FrameComponent } from "./FrameComponent"
import { Slider } from "@/components/ui/slider"
import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
const GRID_SIZE = 12
const CELL_SIZE = 60 // pixels per grid cell
interface Frame {
id: number
video: string
defaultPos: { x: number; y: number; w: number; h: number }
corner: string
edgeHorizontal: string
edgeVertical: string
mediaSize: number
borderThickness: number
borderSize: number
autoplayMode: "all" | "hover"
isHovered: boolean
}
const initialFrames: Frame[] = [
{
id: 1,
video: "https://static.cdn-luma.com/files/981e483f71aa764b/Company%20Thing%20Exported.mp4",
defaultPos: { x: 0, y: 0, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/bcf576df9c38b05f/1_corner_update.png",
edgeHorizontal: "https://static.cdn-luma.com/files/bcf576df9c38b05f/1_vert_update.png",
edgeVertical: "https://static.cdn-luma.com/files/bcf576df9c38b05f/1_hori_update.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 2,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/WebGL%20Exported%20(1).mp4",
defaultPos: { x: 4, y: 0, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/bcf576df9c38b05f/2_corner_update.png",
edgeHorizontal: "https://static.cdn-luma.com/files/bcf576df9c38b05f/2_vert_update.png",
edgeVertical: "https://static.cdn-luma.com/files/bcf576df9c38b05f/2_hori_update.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 3,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Jitter%20Exported%20Poster.mp4",
defaultPos: { x: 8, y: 0, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/3d36d1e0dba2476c/3_Corner_update.png",
edgeHorizontal: "https://static.cdn-luma.com/files/3d36d1e0dba2476c/3_hori_update.png",
edgeVertical: "https://static.cdn-luma.com/files/3d36d1e0dba2476c/3_Vert_update.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 4,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Exported%20Web%20Video.mp4",
defaultPos: { x: 0, y: 4, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/9e67e05f37e52522/4_corner_update.png",
edgeHorizontal: "https://static.cdn-luma.com/files/9e67e05f37e52522/4_hori_update.png",
edgeVertical: "https://static.cdn-luma.com/files/9e67e05f37e52522/4_vert_update.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 5,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Logo%20Exported.mp4",
defaultPos: { x: 4, y: 4, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/9e67e05f37e52522/5_corner_update.png",
edgeHorizontal: "https://static.cdn-luma.com/files/9e67e05f37e52522/5_hori_update.png",
edgeVertical: "https://static.cdn-luma.com/files/9e67e05f37e52522/5_verti_update.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 6,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Animation%20Exported%20(4).mp4",
defaultPos: { x: 8, y: 4, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/1199340587e8da1d/6_corner.png",
edgeHorizontal: "https://static.cdn-luma.com/files/1199340587e8da1d/6_corner-1.png",
edgeVertical: "https://static.cdn-luma.com/files/1199340587e8da1d/6_vert.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 7,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Illustration%20Exported%20(1).mp4",
defaultPos: { x: 0, y: 8, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/b80b5aa00ccc33bd/7_corner.png",
edgeHorizontal: "https://static.cdn-luma.com/files/b80b5aa00ccc33bd/7_hori.png",
edgeVertical: "https://static.cdn-luma.com/files/b80b5aa00ccc33bd/7_vert.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 8,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Art%20Direction%20Exported.mp4",
defaultPos: { x: 4, y: 8, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/981e483f71aa764b/8_corner.png",
edgeHorizontal: "https://static.cdn-luma.com/files/981e483f71aa764b/8_hori.png",
edgeVertical: "https://static.cdn-luma.com/files/981e483f71aa764b/8_verticle.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
{
id: 9,
video: "https://static.cdn-luma.com/files/58ab7363888153e3/Product%20Video.mp4",
defaultPos: { x: 8, y: 8, w: 4, h: 4 },
corner: "https://static.cdn-luma.com/files/981e483f71aa764b/9_corner.png",
edgeHorizontal: "https://static.cdn-luma.com/files/981e483f71aa764b/9_hori.png",
edgeVertical: "https://static.cdn-luma.com/files/981e483f71aa764b/9_vert.png",
mediaSize: 1,
borderThickness: 0,
borderSize: 80,
autoplayMode: "all",
isHovered: false,
},
]
export default function DynamicFrameLayout() {
const [frames, setFrames] = useState<Frame[]>(initialFrames)
const [hovered, setHovered] = useState<{ row: number; col: number } | null>(null)
const [hoverSize, setHoverSize] = useState(6)
const [gapSize, setGapSize] = useState(4)
const [showControls, setShowControls] = useState(false)
const [cleanInterface, setCleanInterface] = useState(true)
const [showFrames, setShowFrames] = useState(false) // Update: showFrames starts as false
const [autoplayMode, setAutoplayMode] = useState<"all" | "hover">("all")
const getRowSizes = () => {
if (hovered === null) {
return "4fr 4fr 4fr"
}
const { row } = hovered
const nonHoveredSize = (12 - hoverSize) / 2
return [0, 1, 2].map((r) => (r === row ? `${hoverSize}fr` : `${nonHoveredSize}fr`)).join(" ")
}
const getColSizes = () => {
if (hovered === null) {
return "4fr 4fr 4fr"
}
const { col } = hovered
const nonHoveredSize = (12 - hoverSize) / 2
return [0, 1, 2].map((c) => (c === col ? `${hoverSize}fr` : `${nonHoveredSize}fr`)).join(" ")
}
const getTransformOrigin = (x: number, y: number) => {
const vertical = y === 0 ? "top" : y === 4 ? "center" : "bottom"
const horizontal = x === 0 ? "left" : x === 4 ? "center" : "right"
return `${vertical} ${horizontal}`
}
const updateFrameProperty = (id: number, property: keyof Frame, value: number) => {
setFrames(frames.map((frame) => (frame.id === id ? { ...frame, [property]: value } : frame)))
}
const toggleControls = () => {
setShowControls(!showControls)
}
const toggleCleanInterface = () => {
setCleanInterface(!cleanInterface)
if (!cleanInterface) {
setShowControls(false)
}
}
const updateCodebase = () => {
console.log("Updating codebase with current values:")
console.log("Hover Size:", hoverSize)
console.log("Gap Size:", gapSize)
console.log("Frames:", frames)
// Here you would typically make an API call to update the codebase
// For now, we'll just log the values
}
return (
<div className="space-y-4 w-full h-full">
<div className="flex justify-between items-center mb-4">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<Switch id="frame-toggle" checked={showFrames} onCheckedChange={setShowFrames} />
<label htmlFor="frame-toggle" className="text-sm text-white/70">
{showFrames ? "Hide Frames" : "Show Frames"}
</label>
</div>
<div className="flex items-center space-x-2">
<Switch
id="autoplay-toggle"
checked={autoplayMode === "all"}
onCheckedChange={(checked) => setAutoplayMode(checked ? "all" : "hover")}
/>
<label htmlFor="autoplay-toggle" className="text-sm text-white/70">
{autoplayMode === "all" ? "Autoplay All" : "Hover Autoplay"}
</label>
</div>
</div>
</div>
{!cleanInterface && (
<div className="flex justify-between items-center">
<h2 className="text-2xl font-bold text-white">Dynamic Frame Layout</h2>
<div className="space-x-2">
<Button onClick={toggleControls}>{showControls ? "Hide Controls" : "Show Controls"}</Button>
<Button onClick={updateCodebase}>Update Codebase</Button>
<Button onClick={toggleCleanInterface}>{cleanInterface ? "Show UI" : "Hide UI"}</Button>
</div>
</div>
)}
{!cleanInterface && showControls && (
<>
<div className="space-y-2">
<label htmlFor="hover-size" className="block text-sm font-medium text-gray-200">
Hover Size: {hoverSize}
</label>
<Slider
id="hover-size"
min={4}
max={8}
step={0.1}
value={[hoverSize]}
onValueChange={(value) => setHoverSize(value[0])}
/>
</div>
<div className="space-y-2">
<label htmlFor="gap-size" className="block text-sm font-medium text-gray-200">
Gap Size: {gapSize}px
</label>
<Slider
id="gap-size"
min={0}
max={20}
step={1}
value={[gapSize]}
onValueChange={(value) => setGapSize(value[0])}
/>
</div>
</>
)}
<div
className="relative w-full h-full"
style={{
display: "grid",
gridTemplateRows: getRowSizes(),
gridTemplateColumns: getColSizes(),
gap: `${gapSize}px`,
transition: "grid-template-rows 0.4s ease, grid-template-columns 0.4s ease",
}}
>
{frames.map((frame) => {
const row = Math.floor(frame.defaultPos.y / 4)
const col = Math.floor(frame.defaultPos.x / 4)
const transformOrigin = getTransformOrigin(frame.defaultPos.x, frame.defaultPos.y)
return (
<motion.div
key={frame.id}
className="relative"
style={{
transformOrigin,
transition: "transform 0.4s ease",
}}
onMouseEnter={() => setHovered({ row, col })}
onMouseLeave={() => setHovered(null)}
>
<FrameComponent
video={frame.video}
width="100%"
height="100%"
className="absolute inset-0"
corner={frame.corner}
edgeHorizontal={frame.edgeHorizontal}
edgeVertical={frame.edgeVertical}
mediaSize={frame.mediaSize}
borderThickness={frame.borderThickness}
borderSize={frame.borderSize}
onMediaSizeChange={(value) => updateFrameProperty(frame.id, "mediaSize", value)}
onBorderThicknessChange={(value) => updateFrameProperty(frame.id, "borderThickness", value)}
onBorderSizeChange={(value) => updateFrameProperty(frame.id, "borderSize", value)}
showControls={showControls && !cleanInterface}
label={`Frame ${frame.id}`}
showFrame={showFrames}
autoplayMode={autoplayMode}
isHovered={
hovered?.row === Math.floor(frame.defaultPos.y / 4) &&
hovered?.col === Math.floor(frame.defaultPos.x / 4)
}
/>
</motion.div>
)
})}
</div>
</div>
)
}

View File

@ -0,0 +1,222 @@
"use client"
import { Slider } from "@/components/ui/slider"
import { useEffect, useRef } from "react"
interface FrameComponentProps {
video: string
width: number | string
height: number | string
className?: string
corner: string
edgeHorizontal: string
edgeVertical: string
mediaSize: number
borderThickness: number
borderSize: number
onMediaSizeChange: (value: number) => void
onBorderThicknessChange: (value: number) => void
onBorderSizeChange: (value: number) => void
showControls: boolean
label: string
showFrame: boolean
autoplayMode: "all" | "hover"
isHovered: boolean
}
export function FrameComponent({
video,
width,
height,
className = "",
corner,
edgeHorizontal,
edgeVertical,
mediaSize,
borderThickness,
borderSize,
onMediaSizeChange,
onBorderThicknessChange,
onBorderSizeChange,
showControls,
label,
showFrame,
autoplayMode,
isHovered,
}: FrameComponentProps) {
const videoRef = useRef<HTMLVideoElement>(null)
useEffect(() => {
if (autoplayMode === "all") {
videoRef.current?.play()
} else if (autoplayMode === "hover") {
if (isHovered) {
videoRef.current?.play()
} else {
videoRef.current?.pause()
}
}
}, [isHovered, autoplayMode])
return (
<div
className={`relative ${className}`}
style={{
width,
height,
transition: "width 0.3s ease-in-out, height 0.3s ease-in-out",
}}
>
<div className="relative w-full h-full overflow-hidden">
{/* Video with Border */}
<div
className="absolute inset-0 flex items-center justify-center"
style={{
zIndex: 1,
transition: "all 0.3s ease-in-out",
padding: showFrame ? `${borderThickness}px` : "0",
width: showFrame ? `${borderSize}%` : "100%",
height: showFrame ? `${borderSize}%` : "100%",
left: showFrame ? `${(100 - borderSize) / 2}%` : "0",
top: showFrame ? `${(100 - borderSize) / 2}%` : "0",
}}
>
<div
className="w-full h-full overflow-hidden"
style={{
transform: `scale(${mediaSize})`,
transformOrigin: "center",
transition: "transform 0.3s ease-in-out",
}}
>
<video
className="w-full h-full object-cover"
src={video}
loop
muted
playsInline
autoPlay={autoplayMode === "all" || (autoplayMode === "hover" && isHovered)}
ref={videoRef}
onMouseEnter={(e) => {
if (autoplayMode === "hover") {
e.currentTarget.play()
}
}}
onMouseLeave={(e) => {
if (autoplayMode === "hover") {
e.currentTarget.pause()
}
}}
/>
</div>
</div>
{/* Frame Elements (Higher z-index) */}
{showFrame && (
<div className="absolute inset-0" style={{ zIndex: 2 }}>
{/* Corners */}
<div
className="absolute top-0 left-0 w-16 h-16 bg-contain bg-no-repeat"
style={{ backgroundImage: `url(${corner})` }}
/>
<div
className="absolute top-0 right-0 w-16 h-16 bg-contain bg-no-repeat"
style={{ backgroundImage: `url(${corner})`, transform: "scaleX(-1)" }}
/>
<div
className="absolute bottom-0 left-0 w-16 h-16 bg-contain bg-no-repeat"
style={{ backgroundImage: `url(${corner})`, transform: "scaleY(-1)" }}
/>
<div
className="absolute bottom-0 right-0 w-16 h-16 bg-contain bg-no-repeat"
style={{ backgroundImage: `url(${corner})`, transform: "scale(-1, -1)" }}
/>
{/* Edges */}
<div
className="absolute top-0 left-16 right-16 h-16"
style={{
backgroundImage: `url(${edgeHorizontal})`,
backgroundSize: "auto 64px",
backgroundRepeat: "repeat-x",
}}
/>
<div
className="absolute bottom-0 left-16 right-16 h-16"
style={{
backgroundImage: `url(${edgeHorizontal})`,
backgroundSize: "auto 64px",
backgroundRepeat: "repeat-x",
transform: "rotate(180deg)",
}}
/>
<div
className="absolute left-0 top-16 bottom-16 w-16"
style={{
backgroundImage: `url(${edgeVertical})`,
backgroundSize: "64px auto",
backgroundRepeat: "repeat-y",
}}
/>
<div
className="absolute right-0 top-16 bottom-16 w-16"
style={{
backgroundImage: `url(${edgeVertical})`,
backgroundSize: "64px auto",
backgroundRepeat: "repeat-y",
transform: "scaleX(-1)",
}}
/>
</div>
)}
</div>
{/* Controls */}
{showControls && (
<div className="absolute bottom-0 left-0 right-0 p-2 bg-black bg-opacity-50 z-10">
<div className="text-white font-bold mb-2">{label}</div>
<div className="space-y-2">
<div>
<label htmlFor={`media-size-${label}`} className="block text-sm font-medium text-white">
Media Size: {mediaSize.toFixed(2)}
</label>
<Slider
id={`media-size-${label}`}
min={0.5}
max={3}
step={0.01}
value={[mediaSize]}
onValueChange={(value) => onMediaSizeChange(value[0])}
/>
</div>
<div>
<label htmlFor={`border-thickness-${label}`} className="block text-sm font-medium text-white">
Border Thickness: {borderThickness}px
</label>
<Slider
id={`border-thickness-${label}`}
min={0}
max={20}
step={1}
value={[borderThickness]}
onValueChange={(value) => onBorderThicknessChange(value[0])}
/>
</div>
<div>
<label htmlFor={`border-size-${label}`} className="block text-sm font-medium text-white">
Border Size: {borderSize}%
</label>
<Slider
id={`border-size-${label}`}
min={50}
max={100}
step={1}
value={[borderSize]}
onValueChange={(value) => onBorderSizeChange(value[0])}
/>
</div>
</div>
</div>
)}
</div>
)
}

View File

@ -0,0 +1,11 @@
'use client'
import * as React from 'react'
import {
ThemeProvider as NextThemesProvider,
type ThemeProviderProps,
} from 'next-themes'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}

View File

@ -0,0 +1,58 @@
'use client'
import * as React from 'react'
import * as AccordionPrimitive from '@radix-ui/react-accordion'
import { ChevronDown } from 'lucide-react'
import { cn } from '@/lib/utils'
const Accordion = AccordionPrimitive.Root
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn('border-b', className)}
{...props}
/>
))
AccordionItem.displayName = 'AccordionItem'
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
className,
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn('pb-4 pt-0', className)}>{children}</div>
</AccordionPrimitive.Content>
))
AccordionContent.displayName = AccordionPrimitive.Content.displayName
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

View File

@ -0,0 +1,141 @@
'use client'
import * as React from 'react'
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'
const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className,
)}
{...props}
ref={ref}
/>
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className,
)}
{...props}
/>
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className,
)}
{...props}
/>
)
AlertDialogHeader.displayName = 'AlertDialogHeader'
const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...props}
/>
)
AlertDialogFooter.displayName = 'AlertDialogFooter'
const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold', className)}
{...props}
/>
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName
const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: 'outline' }),
'mt-2 sm:mt-0',
className,
)}
{...props}
/>
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}

View File

@ -0,0 +1,59 @@
import * as React from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const alertVariants = cva(
'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
{
variants: {
variant: {
default: 'bg-background text-foreground',
destructive:
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
},
},
defaultVariants: {
variant: 'default',
},
},
)
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = 'Alert'
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
{...props}
/>
))
AlertTitle.displayName = 'AlertTitle'
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('text-sm [&_p]:leading-relaxed', className)}
{...props}
/>
))
AlertDescription.displayName = 'AlertDescription'
export { Alert, AlertTitle, AlertDescription }

View File

@ -0,0 +1,7 @@
'use client'
import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'
const AspectRatio = AspectRatioPrimitive.Root
export { AspectRatio }

View File

@ -0,0 +1,50 @@
'use client'
import * as React from 'react'
import * as AvatarPrimitive from '@radix-ui/react-avatar'
import { cn } from '@/lib/utils'
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
className,
)}
{...props}
/>
))
Avatar.displayName = AvatarPrimitive.Root.displayName
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn('aspect-square h-full w-full', className)}
{...props}
/>
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
'flex h-full w-full items-center justify-center rounded-full bg-muted',
className,
)}
{...props}
/>
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
export { Avatar, AvatarImage, AvatarFallback }

View File

@ -0,0 +1,36 @@
import * as React from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const badgeVariants = cva(
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
default:
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
secondary:
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
destructive:
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
},
)
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}
export { Badge, badgeVariants }

View File

@ -0,0 +1,115 @@
import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { ChevronRight, MoreHorizontal } from 'lucide-react'
import { cn } from '@/lib/utils'
const Breadcrumb = React.forwardRef<
HTMLElement,
React.ComponentPropsWithoutRef<'nav'> & {
separator?: React.ReactNode
}
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
Breadcrumb.displayName = 'Breadcrumb'
const BreadcrumbList = React.forwardRef<
HTMLOListElement,
React.ComponentPropsWithoutRef<'ol'>
>(({ className, ...props }, ref) => (
<ol
ref={ref}
className={cn(
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
className,
)}
{...props}
/>
))
BreadcrumbList.displayName = 'BreadcrumbList'
const BreadcrumbItem = React.forwardRef<
HTMLLIElement,
React.ComponentPropsWithoutRef<'li'>
>(({ className, ...props }, ref) => (
<li
ref={ref}
className={cn('inline-flex items-center gap-1.5', className)}
{...props}
/>
))
BreadcrumbItem.displayName = 'BreadcrumbItem'
const BreadcrumbLink = React.forwardRef<
HTMLAnchorElement,
React.ComponentPropsWithoutRef<'a'> & {
asChild?: boolean
}
>(({ asChild, className, ...props }, ref) => {
const Comp = asChild ? Slot : 'a'
return (
<Comp
ref={ref}
className={cn('transition-colors hover:text-foreground', className)}
{...props}
/>
)
})
BreadcrumbLink.displayName = 'BreadcrumbLink'
const BreadcrumbPage = React.forwardRef<
HTMLSpanElement,
React.ComponentPropsWithoutRef<'span'>
>(({ className, ...props }, ref) => (
<span
ref={ref}
role="link"
aria-disabled="true"
aria-current="page"
className={cn('font-normal text-foreground', className)}
{...props}
/>
))
BreadcrumbPage.displayName = 'BreadcrumbPage'
const BreadcrumbSeparator = ({
children,
className,
...props
}: React.ComponentProps<'li'>) => (
<li
role="presentation"
aria-hidden="true"
className={cn('[&>svg]:w-3.5 [&>svg]:h-3.5', className)}
{...props}
>
{children ?? <ChevronRight />}
</li>
)
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator'
const BreadcrumbEllipsis = ({
className,
...props
}: React.ComponentProps<'span'>) => (
<span
role="presentation"
aria-hidden="true"
className={cn('flex h-9 w-9 items-center justify-center', className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More</span>
</span>
)
BreadcrumbEllipsis.displayName = 'BreadcrumbElipssis'
export {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
}

View File

@ -0,0 +1,83 @@
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
import { Separator } from '@/components/ui/separator'
const buttonGroupVariants = cva(
"flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
{
variants: {
orientation: {
horizontal:
'[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none',
vertical:
'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none',
},
},
defaultVariants: {
orientation: 'horizontal',
},
},
)
function ButtonGroup({
className,
orientation,
...props
}: React.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>) {
return (
<div
role="group"
data-slot="button-group"
data-orientation={orientation}
className={cn(buttonGroupVariants({ orientation }), className)}
{...props}
/>
)
}
function ButtonGroupText({
className,
asChild = false,
...props
}: React.ComponentProps<'div'> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : 'div'
return (
<Comp
className={cn(
"bg-muted shadow-xs flex items-center gap-2 rounded-md border px-4 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
className,
)}
{...props}
/>
)
}
function ButtonGroupSeparator({
className,
orientation = 'vertical',
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot="button-group-separator"
orientation={orientation}
className={cn(
'bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto',
className,
)}
{...props}
/>
)
}
export {
ButtonGroup,
ButtonGroupSeparator,
ButtonGroupText,
buttonGroupVariants,
}

View File

@ -0,0 +1,56 @@
import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button'
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
},
)
Button.displayName = 'Button'
export { Button, buttonVariants }

View File

@ -0,0 +1,213 @@
'use client'
import * as React from 'react'
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from 'lucide-react'
import { DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker'
import { cn } from '@/lib/utils'
import { Button, buttonVariants } from '@/components/ui/button'
function Calendar({
className,
classNames,
showOutsideDays = true,
captionLayout = 'label',
buttonVariant = 'ghost',
formatters,
components,
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>['variant']
}) {
const defaultClassNames = getDefaultClassNames()
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn(
'bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent',
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className,
)}
captionLayout={captionLayout}
formatters={{
formatMonthDropdown: (date) =>
date.toLocaleString('default', { month: 'short' }),
...formatters,
}}
classNames={{
root: cn('w-fit', defaultClassNames.root),
months: cn(
'flex gap-4 flex-col md:flex-row relative',
defaultClassNames.months,
),
month: cn('flex flex-col w-full gap-4', defaultClassNames.month),
nav: cn(
'flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between',
defaultClassNames.nav,
),
button_previous: cn(
buttonVariants({ variant: buttonVariant }),
'size-[--cell-size] aria-disabled:opacity-50 p-0 select-none',
defaultClassNames.button_previous,
),
button_next: cn(
buttonVariants({ variant: buttonVariant }),
'size-[--cell-size] aria-disabled:opacity-50 p-0 select-none',
defaultClassNames.button_next,
),
month_caption: cn(
'flex items-center justify-center h-[--cell-size] w-full px-[--cell-size]',
defaultClassNames.month_caption,
),
dropdowns: cn(
'w-full flex items-center text-sm font-medium justify-center h-[--cell-size] gap-1.5',
defaultClassNames.dropdowns,
),
dropdown_root: cn(
'relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md',
defaultClassNames.dropdown_root,
),
dropdown: cn(
'absolute bg-popover inset-0 opacity-0',
defaultClassNames.dropdown,
),
caption_label: cn(
'select-none font-medium',
captionLayout === 'label'
? 'text-sm'
: 'rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5',
defaultClassNames.caption_label,
),
table: 'w-full border-collapse',
weekdays: cn('flex', defaultClassNames.weekdays),
weekday: cn(
'text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none',
defaultClassNames.weekday,
),
week: cn('flex w-full mt-2', defaultClassNames.week),
week_number_header: cn(
'select-none w-[--cell-size]',
defaultClassNames.week_number_header,
),
week_number: cn(
'text-[0.8rem] select-none text-muted-foreground',
defaultClassNames.week_number,
),
day: cn(
'relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none',
defaultClassNames.day,
),
range_start: cn(
'rounded-l-md bg-accent',
defaultClassNames.range_start,
),
range_middle: cn('rounded-none', defaultClassNames.range_middle),
range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end),
today: cn(
'bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none',
defaultClassNames.today,
),
outside: cn(
'text-muted-foreground aria-selected:text-muted-foreground',
defaultClassNames.outside,
),
disabled: cn(
'text-muted-foreground opacity-50',
defaultClassNames.disabled,
),
hidden: cn('invisible', defaultClassNames.hidden),
...classNames,
}}
components={{
Root: ({ className, rootRef, ...props }) => {
return (
<div
data-slot="calendar"
ref={rootRef}
className={cn(className)}
{...props}
/>
)
},
Chevron: ({ className, orientation, ...props }) => {
if (orientation === 'left') {
return (
<ChevronLeftIcon className={cn('size-4', className)} {...props} />
)
}
if (orientation === 'right') {
return (
<ChevronRightIcon
className={cn('size-4', className)}
{...props}
/>
)
}
return (
<ChevronDownIcon className={cn('size-4', className)} {...props} />
)
},
DayButton: CalendarDayButton,
WeekNumber: ({ children, ...props }) => {
return (
<td {...props}>
<div className="flex size-[--cell-size] items-center justify-center text-center">
{children}
</div>
</td>
)
},
...components,
}}
{...props}
/>
)
}
function CalendarDayButton({
className,
day,
modifiers,
...props
}: React.ComponentProps<typeof DayButton>) {
const defaultClassNames = getDefaultClassNames()
const ref = React.useRef<HTMLButtonElement>(null)
React.useEffect(() => {
if (modifiers.focused) ref.current?.focus()
}, [modifiers.focused])
return (
<Button
ref={ref}
variant="ghost"
size="icon"
data-day={day.date.toLocaleDateString()}
data-selected-single={
modifiers.selected &&
!modifiers.range_start &&
!modifiers.range_end &&
!modifiers.range_middle
}
data-range-start={modifiers.range_start}
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
className={cn(
'data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square w-full min-w-[--cell-size] flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70',
defaultClassNames.day,
className,
)}
{...props}
/>
)
}
export { Calendar, CalendarDayButton }

View File

@ -0,0 +1,79 @@
import * as React from 'react'
import { cn } from '@/lib/utils'
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
'rounded-lg border bg-card text-card-foreground shadow-sm',
className,
)}
{...props}
/>
))
Card.displayName = 'Card'
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('flex flex-col space-y-1.5 p-6', className)}
{...props}
/>
))
CardHeader.displayName = 'CardHeader'
const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
'text-2xl font-semibold leading-none tracking-tight',
className,
)}
{...props}
/>
))
CardTitle.displayName = 'CardTitle'
const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
))
CardDescription.displayName = 'CardDescription'
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
))
CardContent.displayName = 'CardContent'
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('flex items-center p-6 pt-0', className)}
{...props}
/>
))
CardFooter.displayName = 'CardFooter'
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@ -0,0 +1,262 @@
'use client'
import * as React from 'react'
import useEmblaCarousel, {
type UseEmblaCarouselType,
} from 'embla-carousel-react'
import { ArrowLeft, ArrowRight } from 'lucide-react'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps = {
opts?: CarouselOptions
plugins?: CarouselPlugin
orientation?: 'horizontal' | 'vertical'
setApi?: (api: CarouselApi) => void
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: boolean
canScrollNext: boolean
} & CarouselProps
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
function useCarousel() {
const context = React.useContext(CarouselContext)
if (!context) {
throw new Error('useCarousel must be used within a <Carousel />')
}
return context
}
const Carousel = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & CarouselProps
>(
(
{
orientation = 'horizontal',
opts,
setApi,
plugins,
className,
children,
...props
},
ref,
) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === 'horizontal' ? 'x' : 'y',
},
plugins,
)
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) {
return
}
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}, [])
const scrollPrev = React.useCallback(() => {
api?.scrollPrev()
}, [api])
const scrollNext = React.useCallback(() => {
api?.scrollNext()
}, [api])
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'ArrowLeft') {
event.preventDefault()
scrollPrev()
} else if (event.key === 'ArrowRight') {
event.preventDefault()
scrollNext()
}
},
[scrollPrev, scrollNext],
)
React.useEffect(() => {
if (!api || !setApi) {
return
}
setApi(api)
}, [api, setApi])
React.useEffect(() => {
if (!api) {
return
}
onSelect(api)
api.on('reInit', onSelect)
api.on('select', onSelect)
return () => {
api?.off('select', onSelect)
}
}, [api, onSelect])
return (
<CarouselContext.Provider
value={{
carouselRef,
api: api,
opts,
orientation:
orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}}
>
<div
ref={ref}
onKeyDownCapture={handleKeyDown}
className={cn('relative', className)}
role="region"
aria-roledescription="carousel"
{...props}
>
{children}
</div>
</CarouselContext.Provider>
)
},
)
Carousel.displayName = 'Carousel'
const CarouselContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { carouselRef, orientation } = useCarousel()
return (
<div ref={carouselRef} className="overflow-hidden">
<div
ref={ref}
className={cn(
'flex',
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
className,
)}
{...props}
/>
</div>
)
})
CarouselContent.displayName = 'CarouselContent'
const CarouselItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { orientation } = useCarousel()
return (
<div
ref={ref}
role="group"
aria-roledescription="slide"
className={cn(
'min-w-0 shrink-0 grow-0 basis-full',
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
className,
)}
{...props}
/>
)
})
CarouselItem.displayName = 'CarouselItem'
const CarouselPrevious = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
'absolute h-8 w-8 rounded-full',
orientation === 'horizontal'
? '-left-12 top-1/2 -translate-y-1/2'
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
className,
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">Previous slide</span>
</Button>
)
})
CarouselPrevious.displayName = 'CarouselPrevious'
const CarouselNext = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
const { orientation, scrollNext, canScrollNext } = useCarousel()
return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
'absolute h-8 w-8 rounded-full',
orientation === 'horizontal'
? '-right-12 top-1/2 -translate-y-1/2'
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
className,
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight className="h-4 w-4" />
<span className="sr-only">Next slide</span>
</Button>
)
})
CarouselNext.displayName = 'CarouselNext'
export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
}

View File

@ -0,0 +1,365 @@
'use client'
import * as React from 'react'
import * as RechartsPrimitive from 'recharts'
import { cn } from '@/lib/utils'
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: '', dark: '.dark' } as const
export type ChartConfig = {
[k in string]: {
label?: React.ReactNode
icon?: React.ComponentType
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> }
)
}
type ChartContextProps = {
config: ChartConfig
}
const ChartContext = React.createContext<ChartContextProps | null>(null)
function useChart() {
const context = React.useContext(ChartContext)
if (!context) {
throw new Error('useChart must be used within a <ChartContainer />')
}
return context
}
const ChartContainer = React.forwardRef<
HTMLDivElement,
React.ComponentProps<'div'> & {
config: ChartConfig
children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer
>['children']
}
>(({ id, className, children, config, ...props }, ref) => {
const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`
return (
<ChartContext.Provider value={{ config }}>
<div
data-chart={chartId}
ref={ref}
className={cn(
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
className,
)}
{...props}
>
<ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer>
{children}
</RechartsPrimitive.ResponsiveContainer>
</div>
</ChartContext.Provider>
)
})
ChartContainer.displayName = 'Chart'
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(
([_, config]) => config.theme || config.color,
)
if (!colorConfig.length) {
return null
}
return (
<style
dangerouslySetInnerHTML={{
__html: Object.entries(THEMES)
.map(
([theme, prefix]) => `
${prefix} [data-chart=${id}] {
${colorConfig
.map(([key, itemConfig]) => {
const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
itemConfig.color
return color ? ` --color-${key}: ${color};` : null
})
.join('\n')}
}
`,
)
.join('\n'),
}}
/>
)
}
const ChartTooltip = RechartsPrimitive.Tooltip
const ChartTooltipContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<'div'> & {
hideLabel?: boolean
hideIndicator?: boolean
indicator?: 'line' | 'dot' | 'dashed'
nameKey?: string
labelKey?: string
}
>(
(
{
active,
payload,
className,
indicator = 'dot',
hideLabel = false,
hideIndicator = false,
label,
labelFormatter,
labelClassName,
formatter,
color,
nameKey,
labelKey,
},
ref,
) => {
const { config } = useChart()
const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) {
return null
}
const [item] = payload
const key = `${labelKey || item.dataKey || item.name || 'value'}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value =
!labelKey && typeof label === 'string'
? config[label as keyof typeof config]?.label || label
: itemConfig?.label
if (labelFormatter) {
return (
<div className={cn('font-medium', labelClassName)}>
{labelFormatter(value, payload)}
</div>
)
}
if (!value) {
return null
}
return <div className={cn('font-medium', labelClassName)}>{value}</div>
}, [
label,
labelFormatter,
payload,
hideLabel,
labelClassName,
config,
labelKey,
])
if (!active || !payload?.length) {
return null
}
const nestLabel = payload.length === 1 && indicator !== 'dot'
return (
<div
ref={ref}
className={cn(
'grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl',
className,
)}
>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || 'value'}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
return (
<div
key={item.dataKey}
className={cn(
'flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground',
indicator === 'dot' && 'items-center',
)}
>
{formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload)
) : (
<>
{itemConfig?.icon ? (
<itemConfig.icon />
) : (
!hideIndicator && (
<div
className={cn(
'shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]',
{
'h-2.5 w-2.5': indicator === 'dot',
'w-1': indicator === 'line',
'w-0 border-[1.5px] border-dashed bg-transparent':
indicator === 'dashed',
'my-0.5': nestLabel && indicator === 'dashed',
},
)}
style={
{
'--color-bg': indicatorColor,
'--color-border': indicatorColor,
} as React.CSSProperties
}
/>
)
)}
<div
className={cn(
'flex flex-1 justify-between leading-none',
nestLabel ? 'items-end' : 'items-center',
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{item.value && (
<span className="font-mono font-medium tabular-nums text-foreground">
{item.value.toLocaleString()}
</span>
)}
</div>
</>
)}
</div>
)
})}
</div>
</div>
)
},
)
ChartTooltipContent.displayName = 'ChartTooltip'
const ChartLegend = RechartsPrimitive.Legend
const ChartLegendContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<'div'> &
Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
hideIcon?: boolean
nameKey?: string
}
>(
(
{ className, hideIcon = false, payload, verticalAlign = 'bottom', nameKey },
ref,
) => {
const { config } = useChart()
if (!payload?.length) {
return null
}
return (
<div
ref={ref}
className={cn(
'flex items-center justify-center gap-4',
verticalAlign === 'top' ? 'pb-3' : 'pt-3',
className,
)}
>
{payload.map((item) => {
const key = `${nameKey || item.dataKey || 'value'}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
return (
<div
key={item.value}
className={
'flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground'
}
>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
)}
{itemConfig?.label}
</div>
)
})}
</div>
)
},
)
ChartLegendContent.displayName = 'ChartLegend'
// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(
config: ChartConfig,
payload: unknown,
key: string,
) {
if (typeof payload !== 'object' || payload === null) {
return undefined
}
const payloadPayload =
'payload' in payload &&
typeof payload.payload === 'object' &&
payload.payload !== null
? payload.payload
: undefined
let configLabelKey: string = key
if (
key in payload &&
typeof payload[key as keyof typeof payload] === 'string'
) {
configLabelKey = payload[key as keyof typeof payload] as string
} else if (
payloadPayload &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'
) {
configLabelKey = payloadPayload[
key as keyof typeof payloadPayload
] as string
}
return configLabelKey in config
? config[configLabelKey]
: config[key as keyof typeof config]
}
export {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
ChartLegend,
ChartLegendContent,
ChartStyle,
}

View File

@ -0,0 +1,28 @@
'use client'
import * as React from 'react'
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
import { Check } from 'lucide-react'
import { cn } from '@/lib/utils'
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
'peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
className,
)}
{...props}
>
<CheckboxPrimitive.Indicator className="flex items-center justify-center text-current">
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }

View File

@ -0,0 +1,11 @@
'use client'
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'
const Collapsible = CollapsiblePrimitive.Root
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
export { Collapsible, CollapsibleTrigger, CollapsibleContent }

Some files were not shown because too many files have changed in this diff Show More