Compare commits

..

2 Commits

Author SHA1 Message Date
4fbda2cfc8 虚拟显示器 2025-12-14 04:20:57 +08:00
2f75c1c8bb 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>
2025-12-13 17:12:12 +08:00
211 changed files with 48282 additions and 147 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"))

File diff suppressed because one or more lines are too long

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

@ -0,0 +1,237 @@
使用统一 diff`@@` 块、`-`/`+`/空格行)对单个文件做精确编辑:追加、插入、替换、删除都可以在一次调用里完成。
硬性规则:
1) 补丁必须被 `*** Begin Patch` 与 `*** End Patch` 包裹。
2) 每个修改块必须以 `@@ [id:数字]` 开头。
3) 块内每一行只能是三类之一:
- 上下文行:以空格开头(` ␠`),表示“文件里必须原样存在”的锚点;
- 删除行:以 `-` 开头,表示要从文件中移除的原文;
- 新增行:以 `+` 开头,表示要写入的新内容。
4) 任何“想新增/想删除/想替换”的内容都必须逐行写 `+` 或 `-`;如果你把多行新内容直接贴上去却不加 `+`,它会被当成上下文锚点去匹配原文件,极易导致“未找到匹配的原文”。
5) 重要语义:一个块里如果完全没有上下文行(空格开头)也没有删除行(`-`那么它会被视为“仅追加append-only也就是把所有 `+` 行追加到文件末尾——这对“给空文件写正文”很合适,但对“插入到中间”是错误的。
正面案例(至少 5 个,且都包含多行原文/多处修改)
1) 给空文件写完整正文(追加到末尾;空文件=正确)
目标:新建 README.md 后一次性写入标题、安装、用法、FAQ多段落、多行
要点:没有上下文/删除行 → 追加模式;空文件时最常用。
*** Begin Patch
@@ [id:1]
+# 项目名称
+
+一个简短说明:这个项目用于……
+
+## 安装
+
+```bash
+pip install -r requirements.txt
+```
+
+## 快速开始
+
+```bash
+python main.py
+```
+
+## 常见问题
+
+- Q: 为什么会报 xxx
+ A: 先检查 yyy再确认 zzz。
+
*** End Patch
2) “不删除,直接插入内容”到函数内部(必须用上下文锚定插入位置)
目标:在 def build_prompt(...): 里插入日志与参数归一化,但不改动其它行。
要点:插入发生在“两个上下文行之间”,上下文必须精确(包含缩进)。
*** Begin Patch
@@ [id:1]
def build_prompt(user_text: str, system_text: str, tags: list):
prompt_parts = []
+ # 参数归一化:去掉首尾空白,避免模型误判
+ user_text = (user_text or "").strip()
+ system_text = (system_text or "").strip()
+
+ logger.debug("build_prompt: tags=%s, user_len=%d", tags, len(user_text))
prompt_parts.append(system_text)
prompt_parts.append(user_text)
if tags:
prompt_parts.append("TAGS: " + ",".join(tags))
*** End Patch
3) 复杂替换:整段函数重构(多行 old/new + 保留稳定上下文)
目标:把旧的 apply_patch()(弱校验)替换成新实现(多分支、异常信息更清晰)。
要点:替换不是“改一行”,而是“删一段、加一段”,并用函数签名/相邻代码作锚点。
*** Begin Patch
@@ [id:1]
class FilePatcher:
def __init__(self, root: Path):
self.root = root
def apply_patch(self, path: str, patch_text: str) -> dict:
- # old: naive replace
- content = (self.root / path).read_text(encoding="utf-8")
- content = content.replace("foo", "bar")
- (self.root / path).write_text(content, encoding="utf-8")
- return {"success": True}
+ full_path = (self.root / path).resolve()
+ if self.root not in full_path.parents and full_path != self.root:
+ return {"success": False, "error": "非法路径:越界访问"}
+
+ if "*** Begin Patch" not in patch_text or "*** End Patch" not in patch_text:
+ return {"success": False, "error": "补丁格式错误:缺少 Begin/End 标记"}
+
+ try:
+ original = full_path.read_text(encoding="utf-8")
+ except Exception as e:
+ return {"success": False, "error": f"读取失败: {e}"}
+
+ # 这里省略:解析 blocks、逐块应用、失败回滚等
+ updated = original
+ try:
+ full_path.write_text(updated, encoding="utf-8")
+ except Exception as e:
+ return {"success": False, "error": f"写入失败: {e}"}
+
+ return {"success": True, "message": "已应用补丁"}
*** End Patch
4) 复杂多块:同一文件里同时“加 import + 替换逻辑 + 插入新 helper + 删除旧函数”
目标:一次调用完成 4 种操作,且每块都有足够上下文,避免误匹配。
要点:不同区域用不同 @@ [id:n] 分块,互不干扰。
*** Begin Patch
@@ [id:1]
-import json
+import json
+import re
from pathlib import Path
@@ [id:2]
def normalize_user_input(text: str) -> str:
- return text
+ text = (text or "").strip()
+ # 压缩多余空白,减少提示词抖动
+ text = re.sub(r"\\s+", " ", text)
+ return text
@@ [id:3]
def load_config(path: str) -> dict:
cfg_path = Path(path)
if not cfg_path.exists():
return {}
data = cfg_path.read_text(encoding="utf-8")
return json.loads(data)
+
+def safe_get(cfg: dict, key: str, default=None):
+ if not isinstance(cfg, dict):
+ return default
+ return cfg.get(key, default)
@@ [id:4]
-def legacy_parse_flags(argv):
- # deprecated, kept for compatibility
- flags = {}
- for item in argv:
- if item.startswith("--"):
- k, _, v = item[2:].partition("=")
- flags[k] = v or True
- return flags
-
def main():
cfg = load_config("config.json")
# ...
*** End Patch
5) 删除示例:删除一整段“废弃配置块”,并顺手修正周围空行(多行删除 + 上下文)
目标:删掉 DEPRECATED_* 配置和旧注释,确保删除位置精确。
要点:删除行必须逐行 `-`;保留上下文行确保定位。
*** Begin Patch
@@ [id:1]
# ==============================
# Runtime Config
# ==============================
-DEPRECATED_TIMEOUT = 5
-DEPRECATED_RETRIES = 1
-# 注意:这些字段将在下个版本移除
-# 请迁移到 NEW_TIMEOUT / NEW_RETRIES
NEW_TIMEOUT = 30
NEW_RETRIES = 3
*** End Patch
如何写“带上下文”的正确姿势(要点)
- 上下文要选“稳定锚点”:函数签名、类名、关键注释、紧邻的两三行缩进代码。
- 不要用“容易变的行”当唯一锚点:时间戳、日志序号、随机 id、生成内容片段。
- 上下文必须字节级一致(空格/Tab/大小写/标点都算),否则会匹配失败。
反面案例(至少 3 个,且都是“真实会踩坑”的类型)
反例 A来自一次常见错误空文件时只有第一行加了 `+`,后面直接贴正文
这会让后面的正文变成“上下文锚点”,工具会去空文件里找这些原文,必然失败(常见报错:未找到匹配的原文)。
*** Begin Patch
@@ [id:1]
+
仰望U9X·电驭苍穹
银箭破空电光闪
三千马力云中藏
*** End Patch
正确做法:正文每一行都要写 `+`(包括空行也写 `+`)。
(对应的正确 patch 示例:向空文件追加多行)
*** Begin Patch
@@ [id:1]
+
+仰望U9X·电驭苍穹
+银箭破空电光闪
+三千马力云中藏
*** End Patch
反例 B想“插入到中间”却只写 `+`(没有任何上下文/删除行)
这种块会被当成“追加到文件末尾”,结果内容跑到文件最后,不会插入到你以为的位置。
*** Begin Patch
@@ [id:1]
+# 我以为会插到某个函数上面
+print("hello")
*** End Patch
正确做法:用上下文锚定插入点(见正面案例 2
(对应的正确 patch 示例:用上下文把内容插入到函数内部,而不是追加到文件末尾)
*** Begin Patch
@@ [id:1]
def main():
config = load_config("config.json")
+ # 这里插入:启动提示(不会移动到文件末尾)
+ print("hello")
run(config)
*** End Patch
反例 C补丁在第一个 `@@` 之前出现内容 / 或漏掉 Begin/End 标记
解析会直接报格式错误(例如:“在检测到第一个 @@ 块之前出现内容”、“缺少 Begin/End 标记”)。
(错误形态示意)
这里先写了一段说明文字(没有 @@
@@ [id:1]
+...
正确做法:确保第一段非空内容必须从 `@@ [id:n]` 开始,并且整体有 Begin/End。
(对应的正确 patch 示例:完整结构、第一段内容从 @@ 块开始)
*** Begin Patch
@@ [id:1]
# ==============================
# Runtime Config
# ==============================
+# 说明:此处新增一行注释作为示例
NEW_TIMEOUT = 30
*** End Patch

View File

@ -20,8 +20,15 @@ from config import (
SUB_AGENT_TASKS_BASE_DIR,
)
from utils.logger import setup_logger
import logging
# 静音子智能体日志(交由前端提示/brief_log处理
logger = setup_logger(__name__)
logger.setLevel(logging.CRITICAL)
logger.disabled = True
logger.propagate = False
for h in list(logger.handlers):
logger.removeHandler(h)
TERMINAL_STATUSES = {"completed", "failed", "timeout"}

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,
}

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