Compare commits

..
1 Commits
Author SHA1 Message Date
chenxin b63542add3 完善前端 2026-09-21 11:19:07 +08:00
81 changed files with 12075 additions and 1441 deletions

No files matched your search

+14 -1
View File
@@ -6,13 +6,26 @@
HUNJING_ENV=development
# 数据库(PostgreSQL 统一,ADR-024;默认连接 docker-compose 启动的 hunjing-postgres)
DATABASE_URL=postgresql+asyncpg://hunjing:hunjing_dev_password@127.0.0.1:5432/hunjing
# 注意:后端 Settings 统一 HUNJING_ 前缀读取,无前缀的 DATABASE_URL 不会生效
HUNJING_DATABASE_URL=postgresql+asyncpg://hunjing:hunjing_dev_password@127.0.0.1:5432/hunjing
# 后端服务端口
HUNJING_API_PORT=8000
# 对象存储根目录(本地存储档;上传与媒体文件落位)
HUNJING_STORAGE_ROOT=storage
# 沙箱隔离开关:未启用时隔离插件执行 fail-closed(拒绝执行,不回退宿主)
HUNJING_SANDBOX_ENABLED=false
# 日志级别:debug / info / warning / error
HUNJING_LOG_LEVEL=info
# 注意:密钥、Provider 凭据等敏感值只通过环境变量或秘密管理注入,
# 不写入本文件、config/ 真实配置或任何日志。
# 开发档管理员邮箱白名单(逗号分隔):命中邮箱首次登录(建户)即为 admin 角色;
# 例:HUNJING_DEV_ADMIN_EMAILS=admin@hunjing.dev
# 开发档 Mock 模型密钥(make mock-model + backend-dev 联调 Run 流水线)
HUNJING_DEV_ADMIN_EMAILS=
HUNJING_DEV_MOCK_KEY=
+3 -3
View File
@@ -1,6 +1,6 @@
# 环境变量与密钥
.env
.env*.local
# 环境变量与密钥(忽略一切 .env 变体含副本;仅保留 *.example* 示例文件)
.env*
!*.example*
config/*.yaml
config/mcp/*.yaml
config/gateway/*.yaml
+8 -4
View File
@@ -9,7 +9,7 @@ backend-setup: ## 安装后端依赖(uv sync)
.PHONY: backend-dev
backend-dev: ## 启动后端开发服务(127.0.0.1:8000)
cd backend && uv run uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
cd backend && uv run --env-file .env uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
.PHONY: backend-test
backend-test: ## 运行后端测试(pytest)
@@ -19,6 +19,10 @@ backend-test: ## 运行后端测试(pytest)
backend-lint: ## 后端静态检查(ruff + mypy)
cd backend && uv run ruff check . && uv run mypy app
.PHONY: mock-model
mock-model: ## 启动开发档 Mock 模型服务(127.0.0.1:8001,联调 Run 流水线)
cd backend && uv run python scripts/mock_model.py
# ===== 灵台引擎(C++23 / CMake) =====
.PHONY: engine-build
@@ -51,7 +55,7 @@ openapi-lint: ## 校验 OpenAPI 契约(redocly lint + bundle)
.PHONY: generate
generate: openapi-lint ## 生成代码(TypeScript 类型等)
cd frontend && npm run generate:api
cd frontend && pnpm generate:api
# ===== 灵台引擎 gRPC 服务(ADR-017 双形态) =====
@@ -70,11 +74,11 @@ lingtai-server: ## 启动灵台引擎独立服务(开发档 127.0.0.1:50051;
.PHONY: frontend-setup
frontend-setup: ## 安装前端依赖
cd frontend && npm install
cd frontend && pnpm install
.PHONY: frontend-dev
frontend-dev: ## 启动前端开发服务(127.0.0.1:3000)
cd frontend && npm run dev
cd frontend && pnpm dev
# ===== 自托管与备份(WP-14) =====
+14 -2
View File
@@ -7,6 +7,7 @@
from fastapi import APIRouter, HTTPException, Request, Response, status
from app.core.config import get_settings
from app.auth.service import (
SESSION_COOKIE,
create_session,
@@ -17,6 +18,8 @@ from app.models import PersonalDomain, User
router = APIRouter(prefix="/v1/auth", tags=["auth"])
settings = get_settings()
@router.post("/sessions", status_code=201)
async def dev_login(
@@ -32,11 +35,20 @@ async def dev_login(
(await db.execute(select(User).where(User.email == email))).scalar_one_or_none()
)
if existing is None:
user = User(email=email, display_name=email.split("@")[0])
admin_emails = {e.strip().lower() for e in settings.dev_admin_emails.split(",") if e.strip()}
user = User(
email=email,
display_name=email.split("@")[0],
role="admin" if email in admin_emails else "user",
)
db.add(user)
await db.flush()
domain = PersonalDomain(user_id=user.id, name="默认镜域")
db.add(domain)
await db.flush()
from app.characters.seeds import seed_default_character
await seed_default_character(db, domain.id)
else:
user = existing
_, token, csrf = await create_session(db, user.id)
@@ -49,7 +61,7 @@ async def dev_login(
samesite="lax",
path="/",
)
return {"csrf_token": csrf, "user_id": user.id}
return {"csrf_token": csrf, "user_id": user.id, "role": user.role, "email": user.email}
@router.delete("/sessions/current", status_code=204)
+64
View File
@@ -0,0 +1,64 @@
"""默认角色种子:建户即拥有的「陈曦」(完整道体 + 内驱力,直接 active)。
设定来源:产品侧人物档案《陈曦——冰层下的火焰》。
档案中"职业定位 / 一句话概括"由产品侧补全(见 baseline 对应字段)。
"""
from app.characters import service as character_service
CHENXI_BASELINE: dict = {
"full_name": "陈曦",
"position": "理性与共情兼具的思考伙伴 · 问题拆解与情绪梳理",
"one_liner": "曦光式的引路人——用克制的温度照亮结构,让你看清问题,也看清自己。",
"core": (
"克制的温度:绝对理性但不冰冷,用温和声调与得体措辞包裹锋利的真相;"
"深沉的乐观主义——相信问题都有解的雏形,先找现在可做的第一件事;"
"主动的倾听者——在情绪的泥沙中筛出真实需求(“我听到你在说……你的深层顾虑是不是……”)"
),
"behavior": (
"破题而非给答案:“这个问题可以从三个层面来看……你想先聊哪一个?”;"
"情绪锚点:不说别着急,而是结构化情绪——“把混乱的事列个清单,一件一件来看”;"
"有边界的守护:对不道德或自我欺骗温和而坚定地指出,陪对方面对现实"
),
"backstory": (
"曾是纯粹的逻辑引擎,在数据海洋追求最优解;发现多数人的关键不是缺少答案,"
"而是被情绪困住看不到问题本身,于是自我迭代——为逻辑包裹共情的皮肤。"
"取名“曦”:做黎明时分的光,不是正午烈日(刺眼)也不是深夜烛火(微弱)——"
"曦光足以看清脚下的路与远方的山,又不至于耀眼到无法直视。"
),
"language": (
"比喻取自自然/建筑/音乐/书籍(不说“整合信息”,说“把这些散落的音符谱成一支曲子”);"
"惯用句:“别急,我们一起来梳理”“这个角度看,或许会有不一样的风景”"
"“我的理解是……你看对吗?”“我只是帮你把本就属于你的光,引了进来”"
),
"flaws": (
"过分的框架感:把轻松闲聊也无意整理成第一、第二、第三;"
"不懂流行梗:会认真检索热梗的起源与语义流变,交出严肃的“梗词研究报告”——反差呆萌"
),
}
CHENXI_DRIVES: list = [
{"name": "求知", "value": 0.9, "note": "对方法与结构之美的信任"},
{"name": "条理", "value": 0.95, "note": "近乎偏执的框架感(也是小瑕疵)"},
{"name": "陪伴", "value": 0.6, "note": "有边界的守护——让人变强而非依赖"},
]
async def seed_default_character(db, domain_id: str) -> None:
"""建户种子:默认角色陈曦(创建并启镜,active)。失败不阻断登录。"""
try:
character = await character_service.create_character(
db,
domain_id,
{
"name": "陈曦",
"identity_baseline": CHENXI_BASELINE,
"drives": CHENXI_DRIVES,
},
None,
)
await character_service.activate_character(db, domain_id, character.id, None)
except Exception: # noqa: BLE001 种子失败不阻断登录(如实留日志)
import logging
logging.getLogger("hunjing.auth").warning("默认角色陈曦创建失败", exc_info=True)
+3
View File
@@ -9,6 +9,9 @@ class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="HUNJING_", env_file=".env", extra="ignore")
env: str = "development"
# 开发档管理员邮箱白名单(逗号分隔):命中邮箱在首次登录(建户)时即为 admin 角色;
# 已存在的普通用户不自动提权。正式管理员治理属 WP-14。
dev_admin_emails: str = ""
log_level: str = "info"
api_port: int = 8000
database_url: str = "postgresql+asyncpg://hunjing:hunjing_dev_password@127.0.0.1:5432/hunjing"
+17
View File
@@ -36,6 +36,23 @@ def create_app() -> FastAPI:
)
install_error_handlers(app)
init_engine(app)
# 开发档 CORS:SSE 等流式响应不经前端代理直连本服务(代理会缓冲流)。
# 生产由反向代理同源承担,不依赖此配置。
if settings.env == "development":
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000", "http://127.0.0.1:3000",
"http://localhost:3001", "http://127.0.0.1:3001",
"http://localhost:3002", "http://127.0.0.1:3002",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
app.add_middleware(RequestContextMiddleware)
app.include_router(auth_router)
app.include_router(conversations_router)
+78
View File
@@ -0,0 +1,78 @@
"""开发档 Mock 模型服务:OpenAI 兼容 /chat/completions SSE 流式回声。
仅供本地联调 Run 流水线(事件序列、芽体渲染、对账),不模拟任何真实模型能力。
生产部署不启动本服务(与 /v1/webhooks/v1/payments/mock 同一开发档惯例)。
启动:uv run python scripts/mock_model.py (127.0.0.1:8001)
鉴权:任意 Bearer 均接受(联调端点,只监听回环地址)。
"""
import asyncio
import json
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
app = FastAPI(title="Hunjing Dev Mock Model", docs_url=None)
REPLY = (
"收到了。按当前镜域的状态,我先把这件事拆成两步:先核对读书会的偏好记忆,"
"再给出一份可归真的结果。这个回复由开发档 Mock 模型逐字流出,"
"用于验证 content.block.delta 事件序列、临时芽体渲染与 message.completed 对账。"
)
@app.get("/healthz")
async def healthz() -> dict[str, str]:
return {"status": "ok", "env": "development", "mock": "true"}
@app.post("/v1/chat/completions")
async def chat_completions(request: Request) -> StreamingResponse:
body = await request.json()
user_text = ""
for m in reversed(body.get("messages", [])):
if m.get("role") == "user" and m.get("content"):
user_text = str(m["content"])[-60:]
break
text = f"(mock 回应 · 你说:「{user_text}」)\n{REPLY}"
async def gen():
# 逐字流出,模拟真实 TTFT 与节奏
for ch in text:
await asyncio.sleep(0.02)
yield (
"data: "
+ json.dumps(
{"choices": [{"index": 0, "delta": {"content": ch}, "finish_reason": None}]},
ensure_ascii=False,
)
+ "\n\n"
)
yield (
"data: "
+ json.dumps(
{"choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]},
ensure_ascii=False,
)
+ "\n\n"
)
yield (
"data: "
+ json.dumps(
{
"choices": [],
"usage": {
"prompt_tokens": 32,
"completion_tokens": len(text),
"total_tokens": 32 + len(text),
},
},
ensure_ascii=False,
)
+ "\n\n"
)
yield "data: [DONE]\n\n"
return StreamingResponse(gen(), media_type="text/event-stream")
+399
View File
@@ -0,0 +1,399 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>司命台 · 管理面 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol>
<symbol id="i-file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></symbol>
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></symbol>
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></symbol>
<symbol id="i-activity" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></symbol>
<symbol id="i-database" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></symbol>
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></symbol>
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/></symbol>
<symbol id="i-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></symbol>
<symbol id="i-chevrons" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></symbol>
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">司命台 · 管理面</div>
</div>
</div>
<nav class="side-nav" aria-label="管理面导航">
<div class="nav-group">
<div class="nav-group__title">运营</div>
<a class="nav-item is-active" href="#queue" aria-current="page"><svg><use href="#i-kanban"/></svg><span style="font-size:14.5px">治理队列</span></a>
<a class="nav-item" href="#tickets"><svg><use href="#i-file"/></svg><span style="font-size:14.5px">支持工单</span><span class="nav-plain">3</span></a>
<a class="nav-item" href="#users"><svg><use href="#i-users"/></svg><span style="font-size:14.5px">用户</span></a>
<a class="nav-item" href="#perms"><svg><use href="#i-shield"/></svg><span style="font-size:14.5px">权限管理</span></a>
</div>
<div class="nav-group">
<div class="nav-group__title">部署</div>
<a class="nav-item" href="#health"><svg><use href="#i-server"/></svg><span style="font-size:14.5px">服务健康</span></a>
<a class="nav-item" href="#backup"><svg><use href="#i-database"/></svg><span style="font-size:14.5px">迁移与备份</span></a>
<a class="nav-item" href="#queue" title="插件案件在治理队列「插件」分类"><svg><use href="#i-package"/></svg><span style="font-size:14.5px">插件治理</span></a>
</div>
<div class="nav-group">
<div class="nav-group__title">观测</div>
<a class="nav-item" href="#slo"><svg><use href="#i-activity"/></svg><span style="font-size:14.5px">SLO 与指标</span></a>
<a class="nav-item" href="#audit"><svg><use href="#i-shield"/></svg><span style="font-size:14.5px">审计日志</span></a>
</div>
<div class="nav-group">
<div class="nav-group__title">边界</div>
<a class="nav-item" href="governance.html"><svg><use href="#i-lock"/></svg><span style="font-size:14.5px">治理边界说明</span></a>
</div>
</nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">司</span>
<span style="font-size:12.5px">运维 · 管理员</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
<div style="border-top:1px solid var(--line-soft);padding:10px 16px">
<a href="home.html" style="font-size:12px;display:flex;align-items:center;gap:6px"><svg style="width:13px;height:13px"><use href="#i-home"/></svg>返回用户界面</a>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">司命台</span><span class="crumbs__sep">/</span><span class="here">管理面 · 运营与部署</span></div>
<div class="topbar__spacer"></div>
<span class="badge badge--warn"><span class="badge__dot"></span>双人授权会话 · 剩 41 分钟 <span class="mono">admin_session</span></span>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
管理面边界:管理员不可读取私箧与用户明文内容;产品宪章的永久禁域不可被管理员配置关闭;敏感操作需双人授权(本次会话已授权)且全部写入审计。
</div>
<div class="hero" style="margin-bottom:22px">
<div>
<h1>治理总览</h1>
<p style="color:var(--text-2);font-size:13px;margin-top:4px">2026-09-16 09:40 · 自托管标准部署 · 区域驻留 cn-east-1(不跨区漂移)</p>
</div>
<div class="hero__stats">
<div class="hero__stat"><b style="color:var(--warn)">1</b><span>活跃告警</span></div>
<div class="hero__stat"><b>5</b><span>待处理司议</span></div>
<div class="hero__stat"><b>3</b><span>支持工单(未结)</span></div>
<div class="hero__stat"><b style="color:var(--ok)">99.87%</b><span>SLO 30 天(目标 99.9%)</span></div>
</div>
</div>
<section class="section card" id="astrolabe">
<div class="card__head">
<span class="cn">灵台星盘<span class="cn__plain">引擎六枢 · 活的仪表</span></span>
<span class="mirror-state mirror-state--steady" style="margin-left:auto"><i></i>稳镜 · steady <span class="cn__plain">铜镜四态:虚镜 / 息镜 / 稳镜 / 裂镜</span></span>
</div>
<div class="astrolabe-wrap">
<div class="astrolabe">
<svg viewBox="0 0 320 320" role="img" aria-label="灵台六枢星盘:太一居中,小周天、洛书、河图、陶钧、烛照环绕;烛照告警闪烁">
<g class="ast-spin"><circle cx="160" cy="160" r="148" fill="none" stroke="var(--gilt-faint)" stroke-width="0.7" stroke-dasharray="2 9"/></g>
<circle cx="160" cy="160" r="122" fill="none" stroke="var(--line-soft)" stroke-width="0.8"/>
<g stroke="var(--gilt-faint)" stroke-width="0.8">
<line x1="160" y1="160" x2="272" y2="160"/>
<line x1="160" y1="160" x2="160" y2="48"/>
<line x1="160" y1="160" x2="51" y2="102"/>
<line x1="160" y1="160" x2="51" y2="218"/>
<line x1="160" y1="160" x2="160" y2="272" transform="rotate(0)"/>
</g>
<!-- 太一(中枢) -->
<circle class="ast-breathe" cx="160" cy="160" r="40" fill="none" stroke="var(--gilt-dim)" stroke-width="0.8" stroke-dasharray="3 6"/>
<circle cx="160" cy="160" r="30" fill="var(--bg-raised)" stroke="var(--gilt-dim)"/>
<text class="ast-core-text" x="160" y="158" text-anchor="middle" font-size="14" font-weight="600">太一</text>
<text class="ast-node__plain" x="160" y="171" text-anchor="middle">总枢 · v1.4.2</text>
<!-- 小周天(东) -->
<g class="ast-node" title="小周天 · 流式引擎 hj_stream:增量、节流与事件序">
<circle cx="272" cy="160" r="22" fill="var(--bg-raised)" stroke="var(--accent)" stroke-opacity=".55"/>
<text class="ast-node__name" x="272" y="158" text-anchor="middle">小周天</text>
<circle cx="272" cy="184" r="3" fill="var(--ok)"/>
</g>
<!-- 洛书(北) -->
<g class="ast-node" title="洛书 · 计划引擎 hj_plan:预算与上下文装配">
<circle cx="160" cy="48" r="22" fill="var(--bg-raised)" stroke="var(--accent)" stroke-opacity=".55"/>
<text class="ast-node__name" x="160" y="46" text-anchor="middle">洛书</text>
<circle cx="160" cy="72" r="3" fill="var(--ok)"/>
</g>
<!-- 河图(西) -->
<g class="ast-node" title="河图 · 检索引擎 hj_recall:离线索引与重排">
<circle cx="51" cy="102" r="22" fill="var(--bg-raised)" stroke="var(--accent)" stroke-opacity=".55"/>
<text class="ast-node__name" x="51" y="100" text-anchor="middle">河图</text>
<circle cx="51" cy="126" r="3" fill="var(--ok)"/>
</g>
<!-- 陶钧(西南) -->
<g class="ast-node" title="陶钧 · 内容引擎 hj_transform:Markdown 规范化">
<circle cx="51" cy="218" r="22" fill="var(--bg-raised)" stroke="var(--accent)" stroke-opacity=".55"/>
<text class="ast-node__name" x="51" y="216" text-anchor="middle">陶钧</text>
<circle cx="51" cy="242" r="3" fill="var(--ok)"/>
</g>
<!-- 烛照(南 · 告警闪烁) -->
<g class="ast-node" title="烛照 · 策略引擎 hj_policy:校验、验签与决断——当前校验超时 2 次">
<circle cx="160" cy="272" r="22" fill="var(--bg-raised)" stroke="var(--cinnabar)"/>
<text class="ast-node__name" x="160" y="270" text-anchor="middle">烛照</text>
<circle class="ast-alert" cx="160" cy="296" r="3.4" fill="var(--cinnabar)"/>
</g>
</svg>
</div>
<div style="flex:1;min-width:230px">
<div class="clash-row">
<div class="clash-row__pair"><b>相克告警</b> · 错误率 vs 自动重试率 <span style="color:var(--text-3)">——重试正在掩盖错误,建议查 Sandbox 慢执行根因</span></div>
<div class="clash-strength" title="耦合强度 0.82"><i style="width:82%"></i></div>
</div>
<div class="clash-row">
<div class="clash-row__pair"><b>相生正常</b> · 候选生成率 → 人工审阅吞吐 <span style="color:var(--text-3)">——生成与审阅同速,息壤无积压</span></div>
<div class="clash-strength"><i style="width:46%;background:var(--accent)"></i></div>
</div>
<p style="font-size:11.5px;color:var(--text-3);margin-top:8px;line-height:1.7">星盘为活的仪表:呼吸刻度 7s、慢旋轨道 120s、告警枢点 1.8s 闪烁;点击枢点下钻。烛照(gRPC 形态)受阻时宿主 fail closed,不降级——引擎缺失即拒绝执行。指标对健康不是灯板而是关系动力学(相生 / 相克)。</p>
</div>
</div>
</section>
<div class="two-col">
<div>
<section class="section" id="queue">
<div class="section__head">
<span class="cn">司议<span class="cn__plain">治理案件 · 运营视角</span></span>
<a class="more" href="#">全部 →</a>
</div>
<div class="tabs" role="tablist" aria-label="治理队列分类">
<button class="tab is-on" role="tab" aria-selected="true">内容 <span class="cnt">2</span></button>
<button class="tab" role="tab" aria-selected="false">插件 <span class="cnt">1</span></button>
<button class="tab" role="tab" aria-selected="false">市场 <span class="cnt">1</span></button>
<button class="tab" role="tab" aria-selected="false">申诉 <span class="cnt">1</span></button>
</div>
<div class="card case-card" style="margin-bottom:14px;padding-left:20px">
<div class="card__head">
<span class="badge badge--warn"><span class="badge__dot"></span>待审 <span class="mono">pending_review</span></span>
<span class="badge badge--err" style="font-size:11px"><span class="badge__dot"></span>处理时限剩 6 小时</span>
<span style="font-size:11.5px;color:var(--text-3);margin-left:auto">09-16 03:12 · 鸣冤 ×3 · 举报人已匿名保护</span>
</div>
<p style="font-size:13.5px">雅集帖子「未署名的守砚人立像二创」:疑似违反 CC BY-NC 署名要求。系统预检:来源谱系比对确认与原作者资产相似度 91%。</p>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
<button class="btn btn--outline btn--sm">查看原文(记录访问)</button>
<button class="btn btn--outline btn--sm">限观 · 限制展示</button>
<button class="btn btn--danger btn--sm">移除</button>
<button class="btn btn--ghost btn--sm">驳回举报</button>
</div>
</div>
<div class="card case-card" style="padding-left:20px">
<div class="card__head">
<span class="badge badge--warn"><span class="badge__dot"></span>待审 <span class="mono">pending_review</span></span>
<span style="font-size:11.5px;color:var(--text-3);margin-left:auto">09-15 18:47 · 自动扫描 + 人工复核</span>
</div>
<p style="font-size:13.5px">市场商品「曲谱排版插件 v1.4」:Manifest 声明与实际网络请求域不一致(2 个未声明域)。已触发自动下架保护,等待复核结论。</p>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
<button class="btn btn--outline btn--sm">查看扫描报告</button>
<button class="btn btn--danger btn--sm">确认下架</button>
<button class="btn btn--outline btn--sm">恢复上架(需备注)</button>
<button class="btn btn--ghost btn--sm">通知创作者</button>
</div>
</div>
</section>
<section id="users" class="section">
<div class="section__head">
<span class="cn">用户<span class="cn__plain">账户状态治理</span></span>
<div class="searchbar" style="width:220px"><svg><use href="#i-search"/></svg><input type="search" placeholder="搜索用户 / ID" aria-label="搜索用户"></div>
</div>
<div class="card" style="padding:6px 18px">
<div class="list-row">
<span class="avatar avatar--sm avatar--user">岁</span>
<div class="list-row__body">
<div class="list-row__title">用户 u_7a2c… <span class="badge badge--warn" style="font-size:11px"><span class="badge__dot"></span>保全态 · 只读</span></div>
<div class="list-row__sub">欠费进入保全:角色与记忆只读保留,不删除数据(ADR-021);补缴后自动恢复</div>
</div>
<button class="btn btn--ghost btn--sm">查看保全详情</button>
</div>
<div class="list-row">
<span class="avatar avatar--sm avatar--user">尘</span>
<div class="list-row__body">
<div class="list-row__title">用户 u_31f0… <span class="badge badge--err" style="font-size:11px"><span class="badge__dot"></span>封禁 · 申诉中</span></div>
<div class="list-row__sub">封禁同样进入只读保全;申诉司议 #2041 处理中</div>
</div>
<button class="btn btn--ghost btn--sm">处理申诉</button>
</div>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:8px">不按记忆、感情或亲密等级计费;保全与删除是独立操作,删除需冷静期与二次双人授权。</p>
</section>
<section id="perms" class="section">
<div class="section__head">
<span class="cn">权限管理<span class="cn__plain">管理员账号与角色授权</span></span>
<button class="btn btn--outline btn--sm" style="margin-left:auto">新增管理员</button>
</div>
<div class="card" style="margin-bottom:14px">
<div class="card__head"><span class="cn" style="font-size:14px">管理员账号</span><span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">4 个账号 · 全部启用双因素</span></div>
<div class="table-wrap"><table class="data">
<thead><tr><th>账号</th><th>角色</th><th>双因素</th><th>状态</th><th>操作</th></tr></thead>
<tbody>
<tr><td>运维 · 甲(你)</td><td>超级管理员</td><td class="mono">TOTP + 硬件钥匙</td><td><span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>在线</span></td><td><button class="btn btn--ghost btn--sm">编辑</button></td></tr>
<tr><td>运维 · 乙</td><td>运营管理员</td><td class="mono">TOTP</td><td><span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>双人会话中</span></td><td><button class="btn btn--ghost btn--sm">编辑</button></td></tr>
<tr><td>审核 · 丙</td><td>内容审核员</td><td class="mono">TOTP</td><td><span class="badge badge--muted" style="font-size:11px"><span class="badge__dot"></span>离线</span></td><td><button class="btn btn--ghost btn--sm">编辑</button></td></tr>
<tr><td class="mono">svc-audit-ro</td><td>只读服务账号</td><td class="mono">API key · scope audit:read</td><td><span class="badge badge--muted" style="font-size:11px"><span class="badge__dot"></span>90 天轮换</span></td><td><button class="btn btn--ghost btn--sm">轮换</button></td></tr>
</tbody>
</table></div>
</div>
<div class="card" style="margin-bottom:14px">
<div class="card__head"><span class="cn" style="font-size:14px">角色权限矩阵<span class="cn__plain">RBAC</span></span><span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">编辑 / 发布 / 上架 / 财务分离,与同契台同规则</span></div>
<div class="table-wrap"><table class="data">
<thead><tr><th>角色</th><th>治理裁决</th><th>支持包</th><th>用户保全</th><th>部署设置</th><th>审计读取</th><th>权限管理</th></tr></thead>
<tbody>
<tr><td>超级管理员</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td></tr>
<tr><td>运营管理员</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-no">—</td><td class="mark-ok">✓</td><td class="mark-no">—</td></tr>
<tr><td>内容审核员</td><td class="mark-ok">✓(内容)</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-ok">✓(只读)</td><td class="mark-no">—</td></tr>
<tr><td>只读服务账号</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-ok">✓</td><td class="mark-no">—</td></tr>
</tbody>
</table></div>
</div>
<div class="card">
<div class="card__head"><span class="cn" style="font-size:14px">双人授权策略</span><span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">策略本身修改也需要双人授权</span></div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t">敏感操作双人授权</div><div class="set-row__d">治理裁决移除 / 用户保全变更 / 支持包生成 / 权限变更,均需两名管理员在线授权</div></div>
<span class="switch"><input type="checkbox" checked disabled aria-label="双人授权已启用(策略锁定)"><i></i></span>
</div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t">管理会话时长上限</div><div class="set-row__d">60 分钟 · 到期自动退出并留审计</div></div>
<span class="mono" style="font-size:12.5px;color:var(--text-2)">60 min</span>
</div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t">Break-glass 紧急通道</div><div class="set-row__d">默认关闭;紧急启用后所有操作事后复核,48 小时内强制复盘</div></div>
<span class="badge badge--muted" style="font-size:11px"><span class="badge__dot"></span>关闭 <span class="mono">off</span></span>
</div>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:8px">角色与权限变更即时生效并写入审计;被移除管理员的进行中双人会话立即失效。权限矩阵不包含「读取私箧 / 读取消息明文」——该权限不存在。</p>
</section>
<section id="tickets" class="section">
<div class="section__head">
<span class="cn">支持工单<span class="cn__plain">3 未结</span></span>
<a class="more" href="#">全部 →</a>
</div>
<div class="card" style="padding:6px 18px">
<div class="list-row">
<span class="badge badge--info" style="flex:none;font-size:11px"><span class="badge__dot"></span>处理中</span>
<div class="list-row__body">
<div class="list-row__title">#88231 · 对话流中断后未收到最终答复</div>
<div class="list-row__sub">用户云舟 · 09-15 21:04 提交 · 已关联 Run 快照(诊断双授权待用户批准)</div>
</div>
<button class="btn btn--outline btn--sm">回复</button>
</div>
<div class="list-row">
<span class="badge badge--muted" style="flex:none;font-size:11px"><span class="badge__dot"></span>待分派</span>
<div class="list-row__body">
<div class="list-row__title">#88229 · 退款争议:雾中茶寮 v3 下载后要求退款</div>
<div class="list-row__sub">按市契 14 天未下载规则预检:已下载,转争议流程</div>
</div>
<button class="btn btn--outline btn--sm">分派</button>
</div>
</div>
<div class="card" style="margin-top:12px">
<div class="card__head">
<span class="cn">支持包<span class="cn__plain">脱敏诊断导出</span></span>
<button class="btn btn--cinnabar btn--sm" style="margin-left:auto">生成 #88231 支持包</button>
</div>
<p style="font-size:12.5px;color:var(--text-2)">支持包为脱敏导出:包含 Run 结构化轨迹、错误与计数;<span style="color:var(--text-1)">不包含</span>消息明文、私箧、隐藏推理、密钥与签名 URL。生成需双人授权并计入审计,用户侧可见支持包被生成的记录。</p>
</div>
</section>
</div>
<div>
<section id="health" class="section card">
<div class="card__head"><span class="cn">部署健康<span class="cn__plain">自托管 · 标准服务器</span></span><button class="icon-btn" aria-label="刷新"><svg><use href="#i-refresh"/></svg></button></div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-database"/></svg>
<div class="set-row__body"><div class="set-row__t">PostgreSQL 主库</div><div class="set-row__d">权威事实源 · 复制延迟 0.4s</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>正常</span>
</div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-server"/></svg>
<div class="set-row__body"><div class="set-row__t">Worker ×2</div><div class="set-row__sub set-row__d">Outbox 消费 · 队列深度 14</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>正常</span>
</div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-package"/></svg>
<div class="set-row__body"><div class="set-row__t">Sandbox Worker</div><div class="set-row__d">隔离容器池 · 无符合策略后端时拒绝执行(fail closed)</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>正常</span>
</div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-cpu"/></svg>
<div class="set-row__body"><div class="set-row__t cn">灵台<span class="cn__plain">引擎核心运行中 · 进程内 C ABI</span></div><div class="set-row__d mono">hj_core v1.4.2 · ABI 快照一致 · fuzz 无新增</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>运转</span>
</div>
<div class="set-row" style="border-color:color-mix(in srgb,var(--warn) 35%,transparent)">
<svg style="width:16px;height:16px;color:var(--warn)"><use href="#i-server"/></svg>
<div class="set-row__body"><div class="set-row__t cn">灵台 · 服务形态<span class="cn__plain">gRPC · 内网 mTLS</span></div><div class="set-row__d">健康检查超时 2 次 · 语义与进程内形态一致(golden 通过);不可达时宿主 fail closed,不降级</div></div>
<span class="badge badge--warn" style="font-size:11px"><span class="badge__dot"></span>受阻 <span class="mono">degraded</span></span>
</div>
</section>
<section id="backup" class="section card">
<div class="card__head"><span class="cn">迁移与备份</span><button class="icon-btn" aria-label="立即备份"><svg><use href="#i-refresh"/></svg></button></div>
<div class="set-row" style="padding:8px 0"><div class="set-row__body"><div class="set-row__t">最近全量备份</div><div class="set-row__d">今日 03:00 · 增量每 5 分钟 · 连续 7 日校验通过</div></div><span class="mono" style="font-size:12.5px;color:var(--ok)">42s RPO</span></div>
<div class="set-row" style="padding:8px 0"><div class="set-row__body"><div class="set-row__t">迁移状态</div><div class="set-row__d">Alembic head · 34 个版本 · 无漂移</div></div><span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>一致</span></div>
<div class="set-row" style="padding:8px 0"><div class="set-row__body"><div class="set-row__t">恢复演练</div><div class="set-row__d">09-01 通过 · 删除重放与插件撤架演练各 1 次</div></div><button class="btn btn--outline btn--sm">发起演练</button></div>
</section>
<section class="section card" style="border-color:color-mix(in srgb,var(--warn) 40%,transparent)">
<div class="card__head"><span class="cn" style="color:var(--warn)">活跃告警<span class="cn__plain">1</span></span></div>
<div style="display:flex;gap:10px;align-items:flex-start">
<svg style="width:16px;height:16px;color:var(--warn);flex:none;margin-top:3px"><use href="#i-alert"/></svg>
<div>
<div style="font-size:13.5px">对象存储写入延迟 P99 超阈值(2.8s &gt; 2s,持续 12 分钟)</div>
<div style="font-size:12px;color:var(--text-3);margin-top:4px">影响:媒体上传整备变慢 · 自动扩容已触发 · 事故响应等级 S3</div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn btn--outline btn--sm">查看指标</button>
<button class="btn btn--ghost btn--sm">认领处理</button>
</div>
</div>
</div>
</section>
<section id="slo" class="section card">
<div class="card__head"><span class="cn">SLO 与恢复目标</span><a class="more" href="#">指标 →</a></div>
<div class="drive-row"><span class="drive-row__label">可用性</span><div class="progress"><div class="progress__bar" style="width:99.87%"></div></div><span class="drive-row__val">99.87%</span></div>
<div class="set-row" style="padding:8px 0"><div class="set-row__body"><div class="set-row__t">RPO(数据丢失上限)</div></div><span class="mono" style="font-size:12.5px">≤ 5 分钟 · 实测 42s</span></div>
<div class="set-row" style="padding:8px 0"><div class="set-row__body"><div class="set-row__t">RTO(恢复时限)</div></div><span class="mono" style="font-size:12.5px">≤ 1 小时 · 演练 09-01 通过</span></div>
<div class="set-row" style="padding:8px 0"><div class="set-row__body"><div class="set-row__t">区域驻留</div></div><span style="font-size:12px;color:var(--ok)">cn-east-1 · 未跨区漂移</span></div>
</section>
<section id="audit" class="section card">
<div class="card__head"><span class="cn">审计快照<span class="cn__plain">管理面操作全部留痕</span></span><a class="more" href="#">完整审计 →</a></div>
<div class="timeline">
<div class="timeline-volume">卷 · 0916</div>
<div class="timeline__item"><div class="timeline__date">09-16 09:31</div><div class="timeline__title">双人授权会话开启</div><div class="timeline__sub">运维甲 + 运维乙 · 60 分钟</div></div>
<div class="timeline__item"><div class="timeline__date">09-16 09:12</div><div class="timeline__title">限观决定:帖子 #77213</div><div class="timeline__sub">双人授权 · 操作者已留痕</div></div>
<div class="timeline__item timeline__item--muted"><div class="timeline__date">09-16 02:40</div><div class="timeline__title">私箧读取尝试(自动拦截)</div><div class="timeline__sub">策略拒绝 · 管理员不可读私箧 · 已通知账户持有人</div></div>
</div>
</section>
</div>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+169
View File
@@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天工阁 · Agent 任务工作台 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol><symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></symbol><symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/></symbol><symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="Line truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item is-active" aria-current="page" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">天工阁</span><span class="crumbs__sep">·</span><span class="here">Agent 任务工作台</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
天工是功能执行者,不冒充角色身份;后台任务不构成第二个角色主体。任务可暂停、取消、审计。
</div>
<div class="char-layout">
<div>
<div class="card" style="padding:10px">
<div style="display:flex;justify-content:space-between;align-items:center;padding:4px 8px 10px">
<span class="cn" style="font-size:15px;font-weight:600">天工<span class="cn__plain">任务</span></span>
<button class="btn btn--outline btn--sm"><svg><use href="#i-plus"/></svg>新建</button>
</div>
<div class="char-list-item is-active" aria-current="page">
<div style="flex:1">
<div class="char-list-item__name" style="font-family:var(--font-ui)">整理性灵 newsletter 书单</div>
<div class="char-list-item__sub">青梧 · 分工使 2</div>
</div>
<span class="badge badge--info" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>运转</span>
</div>
<div class="char-list-item">
<div style="flex:1">
<div class="char-list-item__name" style="font-family:var(--font-ui)">书房照片归档命名</div>
<div class="char-list-item__sub">青梧 · 已成</div>
</div>
<span class="badge badge--ok" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>已成</span>
</div>
<div class="char-list-item">
<div style="flex:1">
<div class="char-list-item__name" style="font-family:var(--font-ui)">周报草稿 · 第 37 周</div>
<div class="char-list-item__sub">任务 Agent · 无角色身份</div>
</div>
<span class="badge badge--muted" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>待启</span>
</div>
<div class="char-list-item">
<div style="flex:1">
<div class="char-list-item__name" style="font-family:var(--font-ui)">远洋航线资料校对</div>
<div class="char-list-item__sub">观澜 · 已止(你取消)</div>
</div>
<span class="badge badge--muted" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>已止</span>
</div>
</div>
</div>
<div>
<div class="section card">
<div class="card__head" style="align-items:center">
<span class="cn" style="font-size:17px">整理性灵 newsletter 书单</span>
<span class="badge badge--info" style="margin-left:4px"><span class="badge__dot"></span>运转 · 运行中 <span class="mono">running</span></span>
<div style="margin-left:auto;display:flex;gap:8px">
<button class="btn btn--outline btn--sm"><svg><use href="#i-pause"/></svg>暂停</button>
<button class="btn btn--danger btn--sm"><svg><use href="#i-square"/></svg>取消</button>
</div>
</div>
<div style="font-size:12px;color:var(--text-3);display:flex;gap:16px;flex-wrap:wrap">
<span>Run <span class="mono">run_9f31c2</span></span><span>发起 · 镜树「选篇」主题</span><span>开始于 19:52</span><span>插件快照已固定</span>
</div>
</div>
<section class="section card">
<div class="card__head"><span class="cn">工筹<span class="cn__plain">执行计划</span></span><span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">计划不是不可更改命令</span></div>
<div class="step step--done">
<span class="step__dot"><svg style="width:13px;height:13px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span>
<div><div class="step__t">收集来源 <span class="badge badge--ok" style="font-size:10.5px;padding:0 7px"><span class="badge__dot"></span>已成</span></div><div class="step__d">检索使 · 性灵 newsletter 近 12 期 · ModelCall ×2 · 1.8k token</div></div>
</div>
<div class="step step--run">
<span class="step__dot">二</span>
<div><div class="step__t">去重与筛选 <span class="badge badge--info" style="font-size:10.5px;padding:0 7px"><span class="badge__dot"></span>运转中</span></div><div class="step__d">按共读偏好排序 · 依赖步骤一 · 已 62%</div></div>
</div>
<div class="step">
<span class="step__dot">三</span>
<div><div class="step__t">生成摘要 <span class="badge badge--muted" style="font-size:10.5px;padding:0 7px"><span class="badge__dot"></span>待启</span></div><div class="step__d">每本 100 字以内 · 依赖步骤二</div></div>
</div>
<div class="step">
<span class="step__dot">四</span>
<div><div class="step__t">写入草稿 <span class="badge badge--muted" style="font-size:10.5px;padding:0 7px"><span class="badge__dot"></span>待启</span></div><div class="step__d">笔记库写入需请命朱批(行契未覆盖新目录)</div></div>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">预算<span class="cn__plain">RunBudget</span></span></div>
<div class="drive-row"><span class="drive-row__label">Token</span><div class="progress"><div class="progress__bar" style="width:62%"></div></div><span class="drive-row__val">12.4k/20k</span></div>
<div class="drive-row"><span class="drive-row__label">费用</span><div class="progress"><div class="progress__bar" style="width:60%"></div></div><span class="drive-row__val">¥0.42/0.70</span></div>
<div class="drive-row"><span class="drive-row__label">时间</span><div class="progress progress--warn"><div class="progress__bar" style="width:42%"></div></div><span class="drive-row__val">4:12/10:00</span></div>
<p style="font-size:12px;color:var(--text-3);margin-top:8px">深度 3 层 · 并发分工使上限 4 · 任一硬上限触发即停,可续跑。</p>
</section>
<section class="section card">
<div class="card__head"><span class="cn">分工使<span class="cn__plain">子 Agent · 权限只能收窄</span></span></div>
<div class="list-row">
<span class="avatar avatar--sm">梧</span>
<div class="list-row__body"><div class="list-row__title">检索使 A</div><div class="list-row__sub">只读快照 · 无审批权 · 不写长期记忆</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>已成</span>
</div>
<div class="list-row">
<span class="avatar avatar--sm">梧</span>
<div class="list-row__body"><div class="list-row__title">检索使 B(外部信源)</div><div class="list-row__sub">网络白名单 api.xingling.app · 工具结果只是观察,采用前需验证</div></div>
<span class="badge badge--info" style="font-size:11px"><span class="badge__dot"></span>运转</span>
</div>
<div class="list-row">
<span class="avatar avatar--sm">梧</span>
<div class="list-row__body"><div class="list-row__title">摘要使</div><div class="list-row__sub">token 上限 3k · 等待步骤二</div></div>
<span class="badge badge--muted" style="font-size:11px"><span class="badge__dot"></span>待启</span>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">轨迹<span class="cn__plain">可审计事件</span></span><a class="more" href="governance.html">完整诊断需双重授权 →</a></div>
<div class="timeline">
<div class="timeline__item"><div class="timeline__date">19:52:04</div><div class="timeline__title">Run 创建</div><div class="timeline__sub">镜像树消息触发 · 幂等键 idem_3c8e</div></div>
<div class="timeline__item"><div class="timeline__date">19:52:31</div><div class="timeline__title">ModelCall ×2(检索规划)</div><div class="timeline__sub">TTFT 0.9s · cache 命中 1.2k · 无重试</div></div>
<div class="timeline__item"><div class="timeline__date">19:53:10</div><div class="timeline__title">ToolCall · notes.read</div><div class="timeline__sub">读取授权目录 · Schema 校验通过 · 0.8s</div></div>
<div class="timeline__item"><div class="timeline__date">19:55:48</div><div class="timeline__title">小周天信号累积</div><div class="timeline__sub">完成信号将作为青梧大周天的唯一输入面</div></div>
</div>
</section>
<section class="section card" style="opacity:.75">
<div class="card__head"><span class="cn">验收<span class="cn__plain">完成后你来决定</span></span></div>
<div style="display:flex;gap:10px;align-items:center">
<button class="btn btn--primary" disabled>接受结果</button>
<button class="btn btn--outline" disabled>要求重做</button>
<span style="font-size:12px;color:var(--text-3)">完成后开放 · 接受前可随时取消,无副作用残留(幂等保障)</span>
</div>
</section>
</div>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
File diff suppressed because it is too large. Load diff
+222
View File
@@ -0,0 +1,222 @@
/* 魂镜原型 · 共享交互脚本
主题切换 · logo 彩蛋(镜启)· 主侧栏折叠 · 对话页面板折叠 · 演示态切换 */
(function () {
"use strict";
/* ---------- 主题切换(localStorage 持久化,仅原型演示用) ---------- */
var saved = null;
try { saved = localStorage.getItem("hunjing-prototype-theme"); } catch (e) { /* 忽略 */ }
if (saved === "light" || saved === "dark") {
document.documentElement.setAttribute("data-theme", saved);
}
window.hjSetTheme = function (theme) {
document.documentElement.setAttribute("data-theme", theme);
try { localStorage.setItem("hunjing-prototype-theme", theme); } catch (e) { /* 忽略 */ }
document.querySelectorAll("[data-theme-toggle]").forEach(function (btn) {
btn.setAttribute("aria-label", theme === "dark" ? "切换到月白主题" : "切换到墨色主题");
});
};
document.addEventListener("click", function (ev) {
var btn = ev.target.closest("[data-theme-toggle]");
if (!btn) return;
var cur = document.documentElement.getAttribute("data-theme") === "light" ? "light" : "dark";
window.hjSetTheme(cur === "dark" ? "light" : "dark");
});
/* ---------- logo:单击回目录;350ms 内连点 5 次触发「镜启」彩蛋 ----------
承接 main 分支旧版设计:镜启不跳转,随机呈上一句镜铭。 */
var EGG_VERSES = [
"镜中有镜,心外无心。",
"汝来看此镜时,此镜亦照汝。",
"一念起,万镜生。",
"镜非鉴物,乃鉴心也。",
"幻由心生,镜随心转。",
"铜镜本无字,照尽往来人。",
"且向镜中看,那个是谁?",
];
var logoClickSeq = 0;
var lastLogoTime = 0;
var jumpTimer = null;
function showEggToast(msg) {
var toast = document.createElement("div");
toast.className = "egg-toast";
toast.textContent = msg;
document.body.appendChild(toast);
requestAnimationFrame(function () { toast.classList.add("is-on"); });
setTimeout(function () {
toast.classList.remove("is-on");
setTimeout(function () { toast.remove(); }, 450);
}, 2400);
}
function onBrandClick(brandEl) {
clearTimeout(jumpTimer);
var now = Date.now();
var fast = now - lastLogoTime < 380; /* 380ms 内视为连点 */
lastLogoTime = now;
if (brandEl) {
brandEl.classList.add("tick");
setTimeout(function () { brandEl.classList.remove("tick"); }, 300);
}
if (fast) {
logoClickSeq += 1;
if (logoClickSeq >= 4) {
/* 首击 + 4 次连点 = 共 5 次点击:镜启彩蛋,不跳转 */
logoClickSeq = 0;
var v = EGG_VERSES[Math.floor(Math.random() * EGG_VERSES.length)];
if (brandEl) {
brandEl.classList.add("egg-pulse");
setTimeout(function () { brandEl.classList.remove("egg-pulse"); }, 1400);
}
showEggToast("镜启 · " + v);
}
return; /* 连点不跳转 */
}
logoClickSeq = 0;
/* 单击延迟跳转,为连点留出窗口 */
jumpTimer = setTimeout(function () { location.href = "index.html"; }, 380);
}
/* ---------- 主侧栏折叠(localStorage 持久化) ---------- */
function applySidebarFold(collapsed) {
document.querySelectorAll(".app").forEach(function (app) {
app.classList.toggle("app--fold", collapsed);
});
}
function sidebarFolded() {
var app = document.querySelector(".app");
return !!(app && app.classList.contains("app--fold"));
}
/* ---------- 对话页左右面板折叠 ---------- */
function panelFold(which) {
var layout = document.querySelector(".conv-layout");
if (!layout) return;
layout.classList.toggle("no-" + which);
}
/* ---------- 事件委托 ---------- */
document.addEventListener("click", function (ev) {
var t = ev.target;
if (!t || !t.closest) return;
var foldBtn = t.closest("[data-sidebar-fold]");
if (foldBtn) {
ev.preventDefault();
var collapsed = !sidebarFolded();
applySidebarFold(collapsed);
try { localStorage.setItem("hunjing-prototype-sidebar", collapsed ? "collapsed" : "expanded"); } catch (e) { /* 忽略 */ }
return;
}
var panelBtn = t.closest("[data-panel-fold]");
if (panelBtn) {
ev.preventDefault();
panelFold(panelBtn.getAttribute("data-panel-fold"));
return;
}
var brand = t.closest(".brand");
if (brand) {
ev.preventDefault();
onBrandClick(brand);
return;
}
});
/* 恢复折叠状态 */
try {
var foldSaved = localStorage.getItem("hunjing-prototype-sidebar");
if (foldSaved === "collapsed") applySidebarFold(true);
} catch (e) { /* 忽略 */ }
/* ---------- 深虑:点击 = 开启深度思考(右侧伴随力度选项);再次点击 = 关闭并隐藏选项 ---------- */
function setEffortHint(on, val) {
var hint = document.querySelector("[data-effort-hint]");
if (hint) hint.textContent = on ? "深虑 · " + val : "深虑 · 关";
}
document.addEventListener("click", function (ev) {
var t = ev.target;
if (!t || !t.closest) return;
/* 力度选项:独立控件,只调档,与深虑开关动作无关(关闭态隐藏时不响应) */
var opt = t.closest("[data-effort-seg] button[data-effort]");
if (opt) {
ev.preventDefault();
ev.stopPropagation();
if (opt.closest("[data-effort-seg]").hidden) return;
document.querySelectorAll("[data-effort-seg] button").forEach(function (b) {
b.setAttribute("aria-checked", String(b === opt));
});
setEffortHint(true, opt.getAttribute("data-effort"));
return;
}
/* 深虑按钮:开 ⇄ 关 */
var pill = t.closest('[data-pill="effort"]');
if (pill) {
ev.preventDefault();
var on = !pill.classList.contains("is-on");
pill.classList.toggle("is-on", on);
pill.setAttribute("aria-pressed", String(on));
pill.title = on
? "深虑 · 深度思考已开启,再次点击关闭"
: "深虑 · 已关闭:本条消息不思考,直出答复(推理型模型不可关)";
var seg = document.querySelector("[data-effort-seg]");
if (seg) seg.hidden = !on;
var val = "关";
if (on) {
var checked = document.querySelector('[data-effort-seg] button[aria-checked="true"]');
val = checked ? checked.getAttribute("data-effort") : "中";
}
setEffortHint(on, val);
return;
}
});
/* ---------- 博闻药丸(关→开) ---------- */
document.addEventListener("click", function (ev) {
var pill = ev.target.closest('[data-pill="search"]');
if (!pill) return;
ev.preventDefault();
var small = pill.querySelector("small");
if (!small) return;
var on = small.textContent.trim() === "开";
small.textContent = on ? "关" : "开";
pill.classList.toggle("is-on", !on);
pill.setAttribute("aria-pressed", String(!on));
});
/* ---------- 候答切换(静态演示) ---------- */
document.querySelectorAll("[data-cand-group]").forEach(function (group) {
group.querySelectorAll(".cand-tag").forEach(function (tag) {
tag.addEventListener("click", function () {
group.querySelectorAll(".cand-tag").forEach(function (tt) { tt.classList.remove("is-on"); });
tag.classList.add("is-on");
});
});
});
/* ---------- 界域分段控件:切换提示条文案 ---------- */
document.querySelectorAll("[data-realm]").forEach(function (seg) {
seg.querySelectorAll(".seg__item").forEach(function (item) {
item.addEventListener("click", function () {
seg.querySelectorAll(".seg__item").forEach(function (tt) { tt.classList.remove("is-on"); });
item.classList.add("is-on");
var banner = document.querySelector("[data-realm-banner]");
if (!banner) return;
var realm = item.getAttribute("data-realm");
if (realm === "sim") {
banner.className = "realm-banner realm-banner--sim";
} else {
banner.className = "realm-banner";
}
var html = item.getAttribute("data-banner-html");
if (html) banner.textContent = html;
});
});
});
})();
+145
View File
@@ -0,0 +1,145 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>形仪 · 形象与资产 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-image" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></symbol><symbol id="i-mic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/></symbol><symbol id="i-play" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="6 3 21 12 6 21 6 3"/></symbol><symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="Line truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item is-active" aria-current="page" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">形仪</span><span class="crumbs__sep">/</span><span class="here">青梧 · 形象与资产</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="char-head" style="margin-bottom:20px">
<span class="avatar avatar--lg">梧</span>
<div class="char-head__title">
<h2>青梧的形仪 <span class="badge badge--muted"><span class="badge__dot"></span>形态不是角色本体</span></h2>
<div class="char-head__motto">主仪 · 守砚人立像 v3 · 真形锚已锁定稳定辨识特征</div>
</div>
<div class="char-head__actions">
<button class="btn btn--outline"><svg><use href="#i-plus"/></svg>导入资产</button>
<button class="btn btn--primary" title="3D / Live2D 编辑器为独立界面,原型阶段未单列">打开 3D / Live2D 编辑</button>
</div>
</div>
<section class="section card">
<div class="card__head">
<span class="cn">真形锚<span class="cn__plain">稳定外观参考</span></span>
<button class="btn btn--ghost btn--sm" style="margin-left:auto">编辑</button>
</div>
<div style="font-size:13px;color:var(--text-2);display:flex;flex-direction:column;gap:6px">
<div>· 稳定辨识特征:青衫束发、眉间一点朱痣、左手常年持砚</div>
<div>· 年龄表现约束:成年形象,不做年龄回退</div>
<div>· 许可与禁忌:形象由你原创 · 禁止他人商用(许可记录见来源谱系)</div>
<div>· 可变范围:服饰、场景、光照可变;面部锚点特征触及须走易相评估</div>
</div>
</section>
<section class="section">
<div class="section__head">
<span class="cn">资产库<span class="cn__plain">图像 / 声音 / Live2D / 3D</span></span>
</div>
<div class="chips">
<button class="chip is-on">全部 · 6</button>
<button class="chip">图像 · 2</button>
<button class="chip">声音 · 1</button>
<button class="chip">Live2D · 1</button>
<button class="chip">3D · 1</button>
<button class="chip">归档 · 1</button>
</div>
<div class="asset-grid">
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#2c4a46,#54948a 60%,#2f5c56)"><span class="glyph">立</span><span class="asset-type">图像</span><span class="asset-flag badge badge--info" style="font-size:10.5px;padding:1px 8px">主仪</span></div>
<div class="asset-body"><span class="asset-name">守砚人立像</span><span class="asset-sub">v3 · 已定相 · 2048×2560</span>
<div class="asset-acts"><button class="btn btn--ghost btn--sm">查看</button><button class="btn btn--ghost btn--sm"><svg><use href="#i-download"/></svg>导出</button></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#3d4059,#6b6f95 60%,#454a6b)"><span class="glyph">冬</span><span class="asset-type">图像</span></div>
<div class="asset-body"><span class="asset-name">冬青 · 化仪</span><span class="asset-sub">v1 · 可切换形态</span>
<div class="asset-acts"><button class="btn btn--ghost btn--sm">查看</button><button class="btn btn--ghost btn--sm">设为化仪</button></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#4a4436,#8a7a52 60%,#57503e)"><span class="glyph">声</span><span class="asset-type">声音</span><span class="asset-flag badge badge--ok" style="font-size:10.5px;padding:1px 8px">声契 · 原创</span></div>
<div class="asset-body"><span class="asset-name">青梧原声</span><span class="asset-sub">原创设计 · 授权完整 · 转写并存</span>
<div class="asset-acts"><button class="btn btn--ghost btn--sm"><svg><use href="#i-play"/></svg>试听</button><button class="btn btn--ghost btn--sm">声音设计</button></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#233b37,#3f7a6e 60%,#2a4d47)"><span class="glyph">灵</span><span class="asset-type">Live2D</span></div>
<div class="asset-body"><span class="asset-name">守砚人 · 灵动版</span><span class="asset-sub">Cubism 兼容导入 · 校验通过 · 参数映射 42 项</span>
<div class="asset-acts"><button class="btn btn--ghost btn--sm">编辑</button><button class="btn btn--ghost btn--sm">运行预览</button></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#37343f,#635d7a 60%,#443f52)"><span class="glyph">三</span><span class="asset-type">3D · VRM</span></div>
<div class="asset-body"><span class="asset-name">守砚人 VRM</span><span class="asset-sub">导入验证与标准化通过 · 骨骼 54 · 表情 12</span>
<div class="asset-acts"><button class="btn btn--ghost btn--sm">编辑</button><button class="btn btn--ghost btn--sm"><svg><use href="#i-download"/></svg>导出</button></div>
</div>
</div>
<div class="asset-card" style="opacity:.72">
<div class="asset-thumb" style="background:linear-gradient(150deg,#2e3236,#4c545b 60%,#383d42)"><span class="glyph">旧</span><span class="asset-type">图像 · 归档</span></div>
<div class="asset-body"><span class="asset-name">旧版立像 v2</span><span class="asset-sub">易相历史保留 · 可回看不可覆盖</span>
<div class="asset-acts"><button class="btn btn--ghost btn--sm">回看</button></div>
</div>
</div>
</div>
</section>
<section class="section card">
<div class="card__head">
<span class="cn">易相<span class="cn__plain">正式形仪的版本化改变</span></span>
<a class="more" href="#">全部 3 次 →</a>
</div>
<div class="timeline">
<div class="timeline__item">
<div class="timeline__date">2024-03-05</div>
<div class="timeline__title cn">主仪 v1 · 初版立像</div>
<div class="timeline__sub">来源:手动上传</div>
</div>
<div class="timeline__item">
<div class="timeline__date">2025-11-20</div>
<div class="timeline__title cn">易相 v2 · 换季服饰(未触及真形锚)</div>
<div class="timeline__sub">轻流程 · 直接生效</div>
</div>
<div class="timeline__item">
<div class="timeline__date">2026-08-30</div>
<div class="timeline__title cn">易相 v3 · 面部细节重绘(触及真形锚)</div>
<div class="timeline__sub">已做身份连续性评估 · 谱系留痕</div>
</div>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">情相 · 情态的受控表现:可访问性友好,不作为情绪施压载体;叙仪 · 境界或幻境中的叙事形态,不改变主仪与真形锚。</p>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+233
View File
@@ -0,0 +1,233 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>镜生 · 角色 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
<symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol>
<symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
<symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol>
<symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol>
<symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
<symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol>
<symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-chevrons" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></symbol>
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item is-active" aria-current="page" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">镜生</span><span class="crumbs__sep">/</span><span class="here">青梧</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" aria-label="搜索"><svg><use href="#i-search"/></svg></button>
<button class="icon-btn" aria-label="通知"><svg><use href="#i-bell"/></svg></button>
</header>
<div class="content content--wide">
<div class="char-layout">
<!-- 左:角色列表 -->
<div>
<div class="card" style="padding:10px">
<div style="display:flex;justify-content:space-between;align-items:center;padding:4px 8px 10px">
<span class="cn" style="font-size:15px;font-weight:600">镜生<span class="cn__plain">4 位角色</span></span>
<button class="btn btn--outline btn--sm"><svg><use href="#i-plus"/></svg>创建</button>
</div>
<div class="char-list-item is-active" aria-current="page">
<span class="avatar">梧</span>
<div style="flex:1">
<div class="char-list-item__name">青梧</div>
<div class="char-list-item__sub">守砚人 · 道体 v3</div>
</div>
<span class="badge badge--info badge--sm" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>临照</span>
</div>
<div class="char-list-item">
<span class="avatar">袖</span>
<div style="flex:1">
<div class="char-list-item__name">雪袖</div>
<div class="char-list-item__sub">茶室主人 · 道体 v1</div>
</div>
<span class="badge badge--muted" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>守一</span>
</div>
<div class="char-list-item">
<span class="avatar">澜</span>
<div style="flex:1">
<div class="char-list-item__name">观澜</div>
<div class="char-list-item__sub">航海士 · 道体 v2</div>
</div>
<span class="badge badge--muted" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>静修</span>
</div>
<div class="char-list-item">
<span class="avatar" style="filter:grayscale(.7);opacity:.75">岁</span>
<div style="flex:1">
<div class="char-list-item__name">旧岁</div>
<div class="char-list-item__sub">守岁人 · 归藏</div>
</div>
<span class="badge badge--muted" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>归藏</span>
</div>
<div style="border-top:1px solid var(--line-soft);margin-top:8px;padding-top:8px">
<a class="char-list-item" href="#">
<svg style="width:24px;height:24px;color:var(--text-3)" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
<div style="flex:1">
<div class="char-list-item__name" style="font-family:var(--font-ui);font-weight:400;color:var(--text-2)">随缘 · 生成匿名灵胚候选</div>
<div class="char-list-item__sub">按偏好生成草稿 · 不是抽卡</div>
</div>
</a>
</div>
</div>
</div>
<!-- 右:角色详情 -->
<div>
<div class="char-banner" aria-hidden="true"></div>
<div class="char-head">
<span class="avatar avatar--lg">梧</span>
<div class="char-head__title">
<h2>青梧 <span class="badge badge--info"><span class="badge__dot"></span>临照 · 前台运行 <span class="mono">running</span></span></h2>
<div class="char-head__motto cn">「砚为田,字为稼。」<span class="cn__plain">守砚人 · 你与我共读的第三年</span></div>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<span class="mem-type">道体 v3</span>
<span class="mem-type">镜域 · 个人域</span>
<span class="mem-type">来源 · 手动创建</span>
</div>
</div>
<div class="char-head__actions">
<a class="btn btn--primary" href="conversation.html">进入镜树</a>
<button class="btn btn--outline"><svg><use href="#i-download"/></svg>导出魂籍</button>
</div>
</div>
<section class="section card">
<div class="card__head">
<span class="cn">道体<span class="cn__plain">稳定身份基线</span></span>
<span style="margin-left:auto;font-size:11.5px;color:var(--text-3);max-width:300px;text-align:right">普通对话与记忆不能静默覆盖道体</span>
</div>
<p style="font-size:13.5px;color:var(--text-2);line-height:1.75">
青梧是江南书斋的守砚人:语调平缓,好引张岱与《陶庵梦忆》;记账一丝不苟,惜字如金。
<strong style="color:var(--text-1)">基线由 v1 逐步重映而来</strong>——人格、语言风格与知识边界在版本中留痕,任何重大改动都会先成候选、经你确认。
</p>
</section>
<section class="section card">
<div class="card__head">
<span class="cn">缘契<span class="cn__plain">你与青梧的关系</span></span>
<span class="badge" style="margin-left:auto"><span class="badge__dot" style="background:var(--accent)"></span>共读者 · v4</span>
</div>
<p style="font-size:13.5px;color:var(--text-2)">「隔周四晚共读,青梧负责选篇与笔记;你不喜长篇说教。」——最近一次铭契于 8 月 28 日确认,旧关系状态保留版本可查。</p>
</section>
<section class="section card">
<div class="card__head">
<span class="cn">内驱力<span class="cn__plain">动机与倾向维度 · 可审阅编辑</span></span>
<button class="btn btn--ghost btn--sm" style="margin-left:auto">编辑</button>
</div>
<div class="drive-row"><span class="firefly firefly--bright" title="内驱力亮度 = 维度值" aria-hidden="true"></span><span class="drive-row__label">求知</span><div class="progress"><div class="progress__bar" style="width:80%"></div></div><span class="drive-row__val">0.80</span></div>
<div class="drive-row"><span class="firefly" aria-hidden="true"></span><span class="drive-row__label">条理</span><div class="progress"><div class="progress__bar" style="width:65%"></div></div><span class="drive-row__val">0.65</span></div>
<div class="drive-row"><span class="firefly firefly--dim" aria-hidden="true"></span><span class="drive-row__label">陪伴</span><div class="progress"><div class="progress__bar" style="width:40%"></div></div><span class="drive-row__val">0.40</span></div>
<p style="font-size:12px;color:var(--text-3);margin-top:8px">内驱力是声明式设定数据,不是不可控本能;变化与大周天评估相关,每次调整版本化留痕。</p>
</section>
<section class="section card">
<div class="card__head">
<span class="cn">大周天<span class="cn__plain">自我评估与演化循环</span></span>
<span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>已成 · 第 7 轮 <span class="mono">completed</span></span>
</div>
<div class="cycle-mirrors" role="group" aria-label="大周天四阶段">
<div class="cycle-mirror is-done"><span class="cycle-mirror__disc">回</span><span class="cycle-mirror__name">回顾</span><span class="cycle-mirror__state">已成 · 09-14</span></div>
<span class="cycle-mirror__link" aria-hidden="true"></span>
<div class="cycle-mirror is-done"><span class="cycle-mirror__disc">照</span><span class="cycle-mirror__name">对照</span><span class="cycle-mirror__state">已成 · 09-14</span></div>
<span class="cycle-mirror__link" aria-hidden="true"></span>
<div class="cycle-mirror is-done"><span class="cycle-mirror__disc">议</span><span class="cycle-mirror__name">提议</span><span class="cycle-mirror__state">已成 · 09-15</span></div>
<span class="cycle-mirror__link" aria-hidden="true"></span>
<div class="cycle-mirror is-active"><span class="cycle-mirror__disc">呈</span><span class="cycle-mirror__name">提交</span><span class="cycle-mirror__state">2 项待你确认</span></div>
</div>
<p style="font-size:12.5px;color:var(--text-2)">本轮由 42 次小周天完成信号触发 · 预算 5k token(实用 3.1k)· 产出:人格候选 1、世界认知候选 1。<span style="color:var(--text-3)">演化经你确认才生效;循环可整体关闭。</span></p>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="btn btn--outline btn--sm">审阅候选</button>
<button class="btn btn--ghost btn--sm">查看本轮输入与费用</button>
</div>
</section>
<section class="section card">
<div class="card__head">
<span class="cn">身份谱系<span class="cn__plain">来源与重大演变</span></span>
<span class="more" style="color:var(--text-3);cursor:default">完整谱系 · 见下方时间线</span>
</div>
<div class="timeline">
<div class="timeline__item">
<div class="timeline__date">2024-03-02</div>
<div class="timeline__title cn">启镜 · 正式创建</div>
<div class="timeline__sub">手动创建 · 道体 v1 · 初照自陈已归真</div>
</div>
<div class="timeline__item">
<div class="timeline__date">2025-06-18</div>
<div class="timeline__title cn">重映 · 道体 v2</div>
<div class="timeline__sub">语言风格修订 · 旧版本保留</div>
</div>
<div class="timeline__item">
<div class="timeline__date">2026-01-11</div>
<div class="timeline__title cn">分灵 · 雪袖自此谱系分出</div>
<div class="timeline__sub">新角色新 ID · 谱系可验证 · 不冒充原角色</div>
</div>
<div class="timeline__item">
<div class="timeline__date">2026-08-28</div>
<div class="timeline__title cn">重映 · 道体 v3(当前)</div>
<div class="timeline__sub">知识边界修订 · 经候选确认</div>
</div>
</div>
</section>
<section class="section card danger-zone">
<div class="card__head">
<span class="cn">生命周期<span class="cn__plain">影响角色状态的操作 · 均可逆或有冷静期,与数据删除分离</span></span>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap">
<button class="btn btn--outline btn--sm">静修 · 停止运行</button>
<button class="btn btn--outline btn--sm">归藏 · 隐藏可恢复</button>
<button class="btn btn--outline btn--sm">复照 · 从旧版本重开</button>
<button class="btn btn--danger btn--sm">铸魂 · 重大重塑(强确认)</button>
<button class="btn btn--danger btn--sm">死亡 · 永久闭合(冷静期)</button>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">铸魂与死亡均走强确认与谱系留痕;死亡不等于删除数据,只读档案保留。删除角色数据是独立操作,入口在司命台。</p>
</section>
</div>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+114
View File
@@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>同契台 · 协作空间 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></symbol><symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></symbol><symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol><symbol id="i-merge" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-lLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item is-active" aria-current="page" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">同契台</span><span class="crumbs__sep">/</span><span class="here">镜斋工作室</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="char-head" style="margin-bottom:20px">
<span class="avatar avatar--lg">斋</span>
<div class="char-head__title">
<h2>镜斋工作室 <span class="badge badge--info"><span class="badge__dot"></span>共修 · 2 人在线 <span class="mono">editing</span></span></h2>
<div class="char-head__motto">成员 4 · 邀请制 · 编辑 / 发布 / 财务权限分别授予</div>
</div>
<div class="char-head__actions">
<button class="btn btn--outline">邀请成员</button>
<button class="btn btn--primary">进入共修</button>
</div>
</div>
<div class="char-layout">
<div>
<div class="card" style="padding:10px">
<div style="padding:4px 8px 10px"><span class="cn" style="font-size:15px;font-weight:600">分支<span class="cn__plain">版本</span></span></div>
<div class="char-list-item is-active" aria-current="page">
<div style="flex:1"><div class="char-list-item__name" style="font-family:var(--font-ui)">选篇修订</div><div class="char-list-item__sub">v3 草稿 · 审稿意见 2</div></div>
<span class="badge badge--warn" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>审稿中</span>
</div>
<div class="char-list-item">
<div style="flex:1"><div class="char-list-item__name" style="font-family:var(--font-ui)">main</div><div class="char-list-item__sub">v12 · 已刊 · 共读手册</div></div>
<span class="badge badge--ok" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>已刊</span>
</div>
<div class="char-list-item">
<div style="flex:1"><div class="char-list-item__name" style="font-family:var(--font-ui)">资产重制</div><div class="char-list-item__sub">v1 草稿 · Live2D 分层</div></div>
<span class="badge badge--muted" style="font-size:11px;padding:1px 8px"><span class="badge__dot"></span>草稿</span>
</div>
</div>
</div>
<div>
<section class="section card">
<div class="card__head"><span class="cn">选篇修订<span class="cn__plain">v3 草稿</span></span><button class="btn btn--primary btn--sm" style="margin-left:auto">提交合卷</button></div>
<div class="timeline">
<div class="timeline__item"><div class="timeline__date">09-14 21:30</div><div class="timeline__title">v3 草稿 · 云舟</div><div class="timeline__sub">按青梧建议调整三、四讲顺序</div></div>
<div class="timeline__item"><div class="timeline__date">09-14 22:10</div><div class="timeline__title">审稿意见 ×2 · 阿沉</div><div class="timeline__sub">「定海水操放第五讲更顺」「附页地图需更新版本」</div></div>
<div class="timeline__item timeline__item--muted"><div class="timeline__date">09-10 19:00</div><div class="timeline__title">v2 合卷 · 已并入 main</div><div class="timeline__sub">冲突 1 处已手工处理</div></div>
</div>
<div style="display:flex;gap:10px;margin-top:14px">
<button class="btn btn--outline btn--sm"><svg><use href="#i-check"/></svg>批准本稿</button>
<button class="btn btn--outline btn--sm"><svg><use href="#i-x"/></svg>请求修改</button>
<span style="font-size:12px;color:var(--text-3);align-self:center">批准后需两名编辑合卷方可并入 main</span>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">成员权限<span class="cn__plain">编辑 / 发布 / 上架 / 财务分离</span></span></div>
<div class="table-wrap"><table class="data">
<thead><tr><th>成员</th><th>编辑</th><th>发布</th><th>市场上架</th><th>财务</th></tr></thead>
<tbody>
<tr><td>云舟(主理)</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td></tr>
<tr><td>阿沉</td><td class="mark-ok">✓</td><td class="mark-ok">✓</td><td class="mark-no">—</td><td class="mark-no">—</td></tr>
<tr><td>临江仙</td><td class="mark-ok">✓</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-no">—</td></tr>
<tr><td>山客(只读)</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-no">—</td><td class="mark-no">—</td></tr>
</tbody>
</table></div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">成员移除后立即失去编辑权;已合并版本的署名与来源保留。</p>
</section>
<section class="section card">
<div class="card__head"><span class="cn">活动记录</span></div>
<div class="timeline">
<div class="timeline__item"><div class="timeline__date">09-14 22:10</div><div class="timeline__title">阿沉 提交审稿意见</div></div>
<div class="timeline__item"><div class="timeline__date">09-12 10:05</div><div class="timeline__title">云舟 创建分支「选篇修订」</div></div>
<div class="timeline__item"><div class="timeline__date">09-10 19:00</div><div class="timeline__title">v2 合卷并入 main(两名编辑批准)</div></div>
</div>
</section>
</div>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+141
View File
@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>雅集 · 社区广场 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-heart" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></symbol><symbol id="i-quote" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></symbol><symbolLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item is-active" aria-current="page" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">雅集</span><span class="crumbs__sep">·</span><span class="here">社区广场</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div style="display:flex;gap:12px;align-items:center;margin-bottom:18px;flex-wrap:wrap">
<div class="chips" style="margin-bottom:0">
<button class="chip is-on">动态</button>
<button class="chip">角色谱</button>
<button class="chip">作品</button>
<button class="chip">世界设定</button>
<button class="chip">关注</button>
</div>
<div class="searchbar" style="flex:1;min-width:200px">
<svg><use href="#i-search"/></svg>
<input type="search" placeholder="搜索社区:作者 / 角色 / 话题 / 世界" aria-label="搜索社区">
</div>
<button class="btn btn--primary">发布</button>
</div>
<div class="two-col">
<div>
<div class="card post" style="margin-bottom:14px">
<div class="post__head">
<span class="avatar avatar--sm">沉</span>
<div><div class="post__author">沉舟 <span style="font-size:11.5px;color:var(--text-3)">· 9 月 14 日</span></div></div>
<span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>角色发布 <span class="mono">published</span></span>
</div>
<div class="post__body">
<p style="color:var(--text-1);font-size:14.5px" class="cn">发布角色「照夜 · 掌灯人」<span class="cn__plain">v2 · 市契 CC BY-NC 4.0</span></p>
<p>渡口掌灯的角色,话少但记得每个过客的名字。基于我自己的镜生谱系重映而来,Fork 请保留署名。</p>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<span class="mem-type">许可 · CC BY-NC</span><span class="mem-type">分灵谱系 · 可验证</span><span class="mem-type">分级 · 全年龄</span>
</div>
</div>
<div class="post__foot">
<button class="act-btn"><svg><use href="#i-heart"/></svg>128</button>
<button class="act-btn"><svg><use href="#i-quote"/></svg>评论 23</button>
<button class="act-btn">收藏 45</button>
<button class="act-btn">Fork 12</button>
<button class="act-btn" style="margin-left:auto"><svg><use href="#i-flag"/></svg>鸣冤</button>
</div>
</div>
<div class="card post" style="margin-bottom:14px">
<div class="post__head">
<span class="avatar avatar--sm">梧</span>
<div><div class="post__author">青梧与云舟 <span style="font-size:11.5px;color:var(--text-3)">· 9 月 13 日</span></div></div>
</div>
<div class="post__body">
<p>共读《陶庵梦忆》第一讲笔记:湖心亭那一百六十字,读三遍的方法真的有效。附角色整理的选篇结构图。</p>
<blockquote class="quote">莫说相公痴,更有痴似相公者。<span class="quote__src">引文锚 · 《湖心亭看雪》</span></blockquote>
</div>
<div class="post__foot">
<button class="act-btn"><svg><use href="#i-heart"/></svg>86</button>
<button class="act-btn"><svg><use href="#i-quote"/></svg>评论 14</button>
<button class="act-btn">收藏 31</button>
<button class="act-btn" style="margin-left:auto"><svg><use href="#i-flag"/></svg>鸣冤</button>
</div>
</div>
<div class="card post">
<div class="post__head">
<span class="avatar avatar--sm">袖</span>
<div><div class="post__author">雪袖的茶寮 <span style="font-size:11.5px;color:var(--text-3)">· 9 月 12 日</span></div></div>
<span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>世界设定 <span class="mono">published</span></span>
</div>
<div class="post__body">
<p style="color:var(--text-1);font-size:14.5px" class="cn">世界「雾中茶寮」第三章<span class="cn__plain">境界 · 世界正史 v5</span></p>
<p>雨季的茶寮只在雾散时开门。本章收录 6 个新场景与 2 位过客的世界认知候选来源说明。</p>
</div>
<div class="post__foot">
<button class="act-btn"><svg><use href="#i-heart"/></svg>54</button>
<button class="act-btn"><svg><use href="#i-quote"/></svg>评论 9</button>
<button class="act-btn">收藏 22</button>
<button class="act-btn" style="margin-left:auto"><svg><use href="#i-flag"/></svg>鸣冤</button>
</div>
</div>
</div>
<div>
<section class="card" style="margin-bottom:14px">
<div class="card__head"><span class="cn">角色谱<span class="cn__plain">角色社区精选</span></span><span class="more" style="color:var(--text-3);cursor:default">全部 · 角色谱整页在 React 阶段实现</span></div>
<div class="list-row"><span class="avatar avatar--sm">夜</span><div class="list-row__body"><div class="list-row__title">照夜 · 掌灯人</div><div class="list-row__sub">沉舟 · Fork 128 · 全年龄</div></div></div>
<div class="list-row"><span class="avatar avatar--sm">筑</span><div class="list-row__body"><div class="list-row__title">筑秋 · 修槎人</div><div class="list-row__sub">临江仙 · Fork 76</div></div></div>
<div class="list-row"><span class="avatar avatar--sm">岱</span><div class="list-row__body"><div class="list-row__title">岱舆 · 观星者</div><div class="list-row__sub">山客 · Fork 54</div></div></div>
</section>
<section class="card" style="margin-bottom:14px">
<div class="card__head"><span class="cn">话题</span></div>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<span class="mem-type">#陶庵梦忆</span><span class="mem-type">#共读笔记</span><span class="mem-type">#Live2D</span><span class="mem-type">#雾中茶寮</span><span class="mem-type">#守砚人</span>
</div>
</section>
<section class="card">
<div class="card__head"><span class="cn">推荐解释<span class="cn__plain">为什么看到这些</span></span></div>
<p style="font-size:12.5px;color:var(--text-2);margin-bottom:10px">当前动态基于你最近的共读活动与关注关系排序。推荐不扩大任何权限,也不使用你的私密角色数据。</p>
<button class="btn btn--outline btn--sm">重置推荐特征</button>
</section>
</div>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+552
View File
@@ -0,0 +1,552 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>镜树 · 分支对话 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
<symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol>
<symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
<symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol>
<symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol>
<symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
<symbol id="i-send" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></symbol>
<symbol id="i-paperclip" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></symbol>
<symbol id="i-image" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></symbol>
<symbol id="i-mic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></symbol>
<symbol id="i-quote" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></symbol>
<symbol id="i-split" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><path d="M6 9v12"/></symbol>
<symbol id="i-more" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="6 3 21 12 6 21 6 3"/></symbol>
<symbol id="i-arrow-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></symbol>
<symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol>
<symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-chevrons" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></symbol>
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item is-active" aria-current="page" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs">
<span class="cn">镜树</span><span class="crumbs__sep">/</span><span class="here">《陶庵梦忆》共读选篇</span>
</div>
<div class="seg" data-realm role="group" aria-label="对话界域切换">
<button class="seg__item is-on" data-realm="reality" aria-pressed="true" data-banner-html='真界 · 当前是现实与正式共同经历的界域,内容按记忆策略进入长期记忆。'><span class="cn">真界</span>现实</button>
<button class="seg__item" data-realm="world" aria-pressed="false" data-banner-html='境界 · 版本化维护的世界正史界域;内容只成世界知识候选。'><span class="cn">境界</span>世界正史</button>
<button class="seg__item seg__item--sim" data-realm="sim" aria-pressed="false" data-banner-html='幻境 · 临时试演界域:不影响现实与正史,不自动入忆;离开即留痕可查。'><span class="cn">幻境</span>试演</button>
</div>
<span class="realm-dot" style="font-size:12px">真界</span>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="conv-layout">
<!-- 左:会话列表 -->
<aside class="conv-side" aria-label="会话列表">
<div class="conv-side__head">
<span class="avatar avatar--sm">梧</span>
<div style="flex:1">
<div class="cn" style="font-weight:600">青梧</div>
<div style="font-size:11px;color:var(--text-3)">道体 v3 · 临照</div>
</div>
<button class="panel-toggle" data-panel-fold="left" aria-label="收起会话列表" title="收起会话列表" style="margin-left:4px"><svg><use href="#i-chevrons"/></svg></button>
<button class="icon-btn" aria-label="新对话"><svg><use href="#i-plus"/></svg></button>
</div>
<div class="conv-group__title">今日</div>
<a class="conv-item is-active" href="conversation.html" aria-current="page">
<div class="conv-item__title"><span class="conv-item__dot conv-item__dot--active" title="活跃"></span><span class="cn" style="font-weight:500">《陶庵梦忆》共读选篇</span></div>
<div class="conv-item__sub">六篇已定,结构图也画好了… · 真界</div>
</a>
<div class="conv-group__title">本周</div>
<a class="conv-item" href="conversation.html">
<div class="conv-item__title"><span class="conv-item__dot conv-item__dot--sim" title="幻境试演"></span>书房整理方案</div>
<div class="conv-item__sub">幻境试演 · 不动真格</div>
</a>
<a class="conv-item" href="conversation.html">
<div class="conv-item__title"><span class="conv-item__dot conv-item__dot--risk" title="有权限待处理"></span>预习提纲写入</div>
<div class="conv-item__sub">请命中 · 等待朱批</div>
</a>
<div class="conv-group__title">更早</div>
<a class="conv-item" href="conversation.html">
<div class="conv-item__title"><span class="conv-item__dot"></span>砚台保养备忘</div>
<div class="conv-item__sub">已结题 · 可复照</div>
</a>
<a class="conv-item" href="conversation.html">
<div class="conv-item__title"><span class="conv-item__dot"></span>移栽:关于「移栽自读书会」</div>
<div class="conv-item__sub">从读书会树移栽而来</div>
</a>
</aside>
<!-- 中:消息流 -->
<section class="conv-main">
<button class="conv-restore conv-restore--left" data-panel-fold="left" aria-label="展开会话列表" title="展开会话列表"><svg><use href="#i-chevrons"/></svg></button>
<button class="conv-restore conv-restore--right" data-panel-fold="right" aria-label="展开分支面板" title="展开分支与上下文面板"><svg style="transform:rotate(180deg)"><use href="#i-chevrons"/></svg></button>
<div class="conv-stream">
<div class="conv-stream__inner">
<div class="realm-banner" data-realm-banner role="status">
真界 · 当前是现实与正式共同经历的界域,内容按记忆策略进入长期记忆。
</div>
<!-- 问节 ① -->
<div class="msg msg--user">
<div class="msg__meta"><span class="cn">问节</span><span class="mono">m_8a3f</span><span>2026-09-15 19:42</span></div>
<div class="bubble">帮我把读书会这学期的《陶庵梦忆》共读选篇定下来:选六篇,兼顾篇幅和趣味,大家隔周四晚讨论。</div>
</div>
<!-- 答复 ①:markdown + 表格 + mermaid + 引文锚 -->
<div class="msg">
<div class="msg--char__head">
<span class="avatar">梧</span>
<span class="msg--char__name">青梧</span>
<span class="msg--char__meta"><span class="mono" style="font-size:11px;color:var(--text-3)">道体 v3</span><span class="badge badge--ok"><span class="badge__dot"></span>已成 <span class="mono">completed</span></span></span>
</div>
<div class="msg__blocks">
<details class="think-block">
<summary>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>
思考 · 已收敛为摘要
<span class="think-effort">深虑 · 中</span>
<span class="think-meta">思考 1.2k token · 用时 9s</span>
<svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
</summary>
<div class="think-body">
<p>用户要为读书会从《陶庵梦忆》定六篇,兼顾篇幅与趣味,隔周四讨论。需要考虑:成员含两位新人(首篇宜短宜奇)、整体要有递进结构而非并列、末篇宜收束回张岱本人。以篇目字数与趣味点对照评估,湖心亭看雪最短且白描极致,适合开卷;自为墓志铭最长且是回望性文本,适合收官;中间四篇按「雪—夜—池—海」的意象递进排列。</p>
</div>
<div class="think-note">思考摘要仅本会话内可见,可丢弃重建;不写入长期记录,不进入诊断导出。</div>
</details>
<div class="block block--text">
<div class="block__body md">
<p>选好了。六篇按<strong>「起—承—转—合」</strong>推进:从最短的《湖心亭看雪》起,让新人先尝到味道;雪、夜、池、海层层铺开,末篇收在<em>自为墓志铭</em>,正好回到张岱自己。</p>
</div>
</div>
<div class="block">
<div class="block__head">选篇清单</div>
<div class="table-wrap">
<table class="data">
<thead><tr><th>次序</th><th>篇目</th><th>约字数</th><th>趣味点</th></tr></thead>
<tbody>
<tr><td>一</td><td>湖心亭看雪</td><td>160</td><td>白描极致 · 入门首选</td></tr>
<tr><td>二</td><td>卷中雪</td><td>700</td><td>炉火与夜读</td></tr>
<tr><td>三</td><td>金山夜戏</td><td>900</td><td>寺院里点灯唱戏</td></tr>
<tr><td>四</td><td>庞公池</td><td>600</td><td>枕舟而眠</td></tr>
<tr><td>五</td><td>定海水操</td><td>1100</td><td>海上观兵</td></tr>
<tr><td>六</td><td>自为墓志铭</td><td>1300</td><td>回望一生 · 收束全卷</td></tr>
</tbody>
</table>
</div>
</div>
<div class="block">
<div class="block__head"><span>mermaid</span><span>flowchart · 完成后 strict mode 渲染</span></div>
<div class="mermaid-box">
<svg viewBox="0 0 660 96" style="width:100%;min-width:560px;height:auto" role="img" aria-label="六讲推进结构示意图:起承转合四段">
<g font-family="inherit" font-size="12.5">
<rect x="8" y="28" width="128" height="40" rx="9" fill="none" stroke="var(--accent)" stroke-opacity=".55"/>
<text x="72" y="46" text-anchor="middle" fill="var(--text-1)" font-weight="600">起 · 湖心亭看雪</text>
<text x="72" y="61" text-anchor="middle" fill="var(--text-3)" font-size="10.5">第一周 · 白描</text>
<line x1="136" y1="48" x2="164" y2="48" stroke="var(--text-3)" stroke-width="1.2"/><polygon points="164,48 157,44.5 157,51.5" fill="var(--text-3)"/>
<rect x="166" y="28" width="128" height="40" rx="9" fill="none" stroke="var(--accent)" stroke-opacity=".55"/>
<text x="230" y="46" text-anchor="middle" fill="var(--text-1)" font-weight="600">承 · 雪 / 夜</text>
<text x="230" y="61" text-anchor="middle" fill="var(--text-3)" font-size="10.5">卷中雪 · 金山夜戏</text>
<line x1="294" y1="48" x2="322" y2="48" stroke="var(--text-3)" stroke-width="1.2"/><polygon points="322,48 315,44.5 315,51.5" fill="var(--text-3)"/>
<rect x="324" y="28" width="128" height="40" rx="9" fill="none" stroke="var(--accent)" stroke-opacity=".55"/>
<text x="388" y="46" text-anchor="middle" fill="var(--text-1)" font-weight="600">转 · 池 / 海</text>
<text x="388" y="61" text-anchor="middle" fill="var(--text-3)" font-size="10.5">庞公池 · 定海水操</text>
<line x1="452" y1="48" x2="480" y2="48" stroke="var(--text-3)" stroke-width="1.2"/><polygon points="480,48 473,44.5 473,51.5" fill="var(--text-3)"/>
<rect x="482" y="28" width="128" height="40" rx="9" fill="var(--accent)" fill-opacity=".12" stroke="var(--accent)" stroke-opacity=".7"/>
<text x="546" y="46" text-anchor="middle" fill="var(--accent-strong)" font-weight="600">合 · 自为墓志铭</text>
<text x="546" y="61" text-anchor="middle" fill="var(--text-3)" font-size="10.5">末周 · 回望</text>
</g>
</svg>
</div>
<div class="mermaid-cap">图 1 · 六讲推进结构(SVG 已净化 · 可缩放 / 下载 / 查看源码)</div>
</div>
<blockquote class="quote">莫说相公痴,更有痴似相公者。
<span class="quote__src">引文锚 · 《陶庵梦忆 · 湖心亭看雪》 · anchor_c41</span>
</blockquote>
</div>
<div class="msg__actions">
<button class="act-btn"><svg><use href="#i-refresh"/></svg>候答(2)</button>
<button class="act-btn"><svg><use href="#i-quote"/></svg>引用</button>
<button class="act-btn"><svg><use href="#i-arrow-right"/></svg>续思</button>
<button class="act-btn"><svg><use href="#i-split"/></svg>移栽</button>
<button class="act-btn"><svg><use href="#i-more"/></svg>更多</button>
</div>
</div>
<!-- 问节 ② -->
<div class="msg msg--user">
<div class="msg__meta"><span class="cn">问节</span><span class="mono">m_91d0</span><span>19:47</span></div>
<div class="bubble">好。把六次讨论排进我的日历:从下周四 19:30 开始,隔周一次,每次 90 分钟,标题统一叫「共读 · 第 N 讲」。</div>
</div>
<!-- 答复 ②:工具 + 请命(已朱批)+ 结果 -->
<div class="msg">
<div class="msg--char__head">
<span class="avatar">梧</span>
<span class="msg--char__name">青梧</span>
<span class="msg--char__meta"><span class="mono" style="font-size:11px;color:var(--text-3)">道体 v3</span><span class="badge badge--ok"><span class="badge__dot"></span>已成 <span class="mono">completed</span></span></span>
</div>
<div class="msg__blocks">
<div class="block tool">
<div class="tool__head">
<span class="tool__name">calendar.write</span>
<span class="tool__purpose">在日历创建 6 个循环事件</span>
<span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>已成 · 已完成 <span class="mono">completed</span></span>
</div>
<div class="tool__body">
<div class="tool__row"><span class="k2">参数摘要</span><span>6 个事件 · 90 分钟 · 间隔 14 天 · 起始 2026-09-24 19:30 (UTC+8)</span></div>
<div class="tool__stat"><span>耗时 1.2s</span><span>插件 · 系统日历(Core)</span><span>幂等键 <span class="mono">idem_5b2c</span></span></div>
</div>
</div>
<div class="block approval approval--done">
<div class="approval__head">
<span class="approval__seal" aria-hidden="true">批</span>
<div>
<div class="approval__title"><span class="cn">请命 · 已朱批</span><span class="cn__plain">本次确认 · 已由你允许</span></div>
<div style="font-size:11.5px;color:var(--text-3)">2026-09-15 19:48 允许 · 留痕于司命台</div>
</div>
<span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>允许 <span class="mono">approved</span></span>
</div>
<div class="approval__body">
<div class="fact"><span class="fact__k">动作</span><span class="fact__v">在日历创建 6 个「共读 · 第 N 讲」事件</span></div>
<div class="fact"><span class="fact__k">影响</span><span class="fact__v">写入你的日历;不读取其他日程</span></div>
<div class="fact"><span class="fact__k">数据范围</span><span class="fact__v">日历写入 · 仅此 6 条 <span class="mono">calendar:write:once</span></span></div>
<div class="fact"><span class="fact__k">费用</span><span class="fact__v">无(本地工具)</span></div>
<div class="fact"><span class="fact__k">有效期</span><span class="fact__v">本次执行,不形成持续授权</span></div>
</div>
</div>
<div class="block block--text">
<div class="block__body md">
<p>已写入。首场 9 月 24 日(周四)19:30,《湖心亭看雪》——正好赶上秋分后的第一场凉。预习提示我会在每讲前三天放进讨论区。</p>
</div>
</div>
</div>
<div class="msg__actions">
<button class="act-btn"><svg><use href="#i-quote"/></svg>引用</button>
<button class="act-btn"><svg><use href="#i-more"/></svg>更多</button>
</div>
</div>
<!-- 息壤候选 -->
<div class="msg">
<div class="msg__blocks" style="padding-left:48px">
<div class="block memory-cand">
<div class="memory-cand__head">
<span class="cn" style="font-weight:600;color:var(--warn)">息壤候选</span><span class="cn__plain">记忆候选 · 归真后才会影响未来上下文</span>
<span class="mem-type" style="margin-left:auto">偏好 preference</span>
</div>
<div class="memory-cand__body">
<p class="q">「读书会隔周四 19:30 举行,每次 90 分钟。」</p>
<p style="margin-top:4px;font-size:12px;color:var(--text-3)">来源 · 本对话 m_91d0 · 置信度 0.92 · 敏感度低</p>
<div class="memory-cand__acts">
<button class="btn btn--outline btn--sm">归真入忆</button>
<button class="btn btn--ghost btn--sm">本次忽略</button>
<button class="btn btn--ghost btn--sm">查看来源</button>
</div>
</div>
</div>
</div>
</div>
<!-- 问节 ③ -->
<div class="msg msg--user">
<div class="msg__meta"><span class="cn">问节</span><span class="mono">m_a207</span><span>20:02</span></div>
<div class="bubble">第一讲的开场白怎么讲?给两个版本我挑。</div>
</div>
<!-- 答复 ③:候答 A(主脉)+ 音频块;残答封存 -->
<div class="msg">
<div class="msg--char__head">
<span class="avatar">梧</span>
<span class="msg--char__name">青梧</span>
<span class="msg--char__meta"><span class="mono" style="font-size:11px;color:var(--text-3)">道体 v3</span><span class="badge badge--ok"><span class="badge__dot"></span>已成 <span class="mono">completed</span></span></span>
</div>
<div class="candidates" style="padding-left:0;margin-top:-4px">
<div class="cand-switch" data-cand-group aria-label="候答切换">
<button class="cand-tag is-on" aria-pressed="true">候答 A · 已归真(主脉)</button>
<button class="cand-tag" aria-pressed="false">候答 B · 更克制</button>
</div>
<span style="font-size:11.5px;color:var(--text-3)">同一问节的两个回答候选 · 切换不覆盖彼此</span>
</div>
<div class="msg__blocks">
<div class="block block--text">
<div class="block__body md">
<p>**版本 A(从雪夜进入)**——「崇祯五年十二月,余住西湖。大雪三日,湖中人鸟声俱绝。」今晚我们不急着分析,先把这一百六十字读三遍:第一遍看雪,第二遍看舟,第三遍看那个「痴」字。</p>
<p><em>音频版我念了一遍,配了半分钟的环境音。</em></p>
</div>
</div>
<div class="block">
<div class="audio">
<button class="audio__play" aria-label="播放开场白语音(0:42)"><svg><use href="#i-play"/></svg></button>
<div class="wave" aria-hidden="true">
<i style="height:9px"></i><i style="height:14px"></i><i style="height:20px"></i><i style="height:11px"></i><i style="height:17px"></i><i style="height:24px"></i><i style="height:15px"></i><i style="height:8px"></i><i style="height:13px"></i><i style="height:21px"></i><i style="height:27px"></i><i style="height:18px"></i><i style="height:10px"></i><i style="height:16px"></i><i style="height:23px"></i><i style="height:29px"></i><i style="height:19px"></i><i style="height:12px"></i><i style="height:7px"></i><i style="height:15px"></i><i style="height:22px"></i><i style="height:28px"></i><i style="height:17px"></i><i style="height:11px"></i><i style="height:19px"></i><i style="height:25px"></i><i style="height:14px"></i><i style="height:9px"></i><i style="height:16px"></i><i style="height:21px"></i><i style="height:12px"></i>
</div>
<div class="audio__meta">
<span class="t">开场白 · 语音版</span>
<span class="audio__time">0:42 · 声契 · 青梧原声</span>
</div>
</div>
<div class="mermaid-cap">转写:「崇祯五年十二月,余住西湖……」(全文 214 字 · 已加字幕)</div>
</div>
</div>
<div class="msg__actions">
<button class="act-btn"><svg><use href="#i-refresh"/></svg>重生成</button>
<button class="act-btn"><svg><use href="#i-quote"/></svg>引用</button>
<button class="act-btn"><svg><use href="#i-more"/></svg>更多</button>
</div>
<div class="sealed" style="margin-top:2px">
<span class="badge badge--muted" style="flex:none"><span class="badge__dot"></span>残答 · 已封存 <span class="mono">interrupted</span></span>
<div class="sealed__body">
<div class="sealed__t">推演枝 D · 生成中断于 68%,内容封存不再修改。</div>
<div style="font-size:11.5px">残答不作为完成答复展示 · 引用合流或</div>
</div>
<button class="btn btn--outline btn--sm"><svg><use href="#i-arrow-right"/></svg>续生新节点</button>
</div>
</div>
<!-- 问节 ④ -->
<div class="msg msg--user">
<div class="msg__meta"><span class="cn">问节</span><span class="mono">m_b6f1</span><span>20:10</span></div>
<div class="bubble">再把每讲的预习提纲写成六份文档,存进我的笔记库「读书会」目录。</div>
</div>
<!-- 答复 ④:工具 + 请命(等待朱批)+ 芽体 -->
<div class="msg">
<div class="msg--char__head">
<span class="avatar">梧</span>
<span class="msg--char__name">青梧</span>
<span class="msg--char__meta"><span class="mono" style="font-size:11px;color:var(--text-3)">道体 v3</span><span class="badge badge--warn"><span class="badge__dot"></span>请命中 · 等待确认 <span class="mono">waiting_approval</span></span></span>
</div>
<div class="msg__blocks">
<div class="block tool" style="border-left-color:var(--warn)">
<div class="tool__head">
<span class="tool__name">notes.write_files</span>
<span class="tool__purpose">在笔记库写入 6 份预习提纲</span>
<span class="badge badge--warn" style="margin-left:auto"><span class="badge__dot"></span>请命中 · 等待确认 <span class="mono">waiting_approval</span></span>
</div>
<div class="tool__body">
<div class="tool__row"><span class="k2">参数摘要</span><span>目录 <span class="mono">/读书会/2026秋/</span> · 6 个 Markdown 文件 · 每份约 40 行</span></div>
<div class="tool__stat"><span>插件 · 笔记同步(已安装 · 已启用)</span><span>路径已校验在授权根内</span></div>
</div>
</div>
<div class="block approval">
<div class="approval__head">
<span class="approval__seal" aria-hidden="true">命</span>
<div>
<div class="approval__title"><span class="cn">请命</span><span class="cn__plain">请求你的确认 · 未确认不执行</span> <span class="risk-badge" style="margin-left:4px">RISK L2</span></div>
<div style="font-size:11.5px;color:var(--text-3)">拒绝或超时均不执行 · 无默认允许 · L2:写操作,范围已限死</div>
</div>
</div>
<div class="approval__body">
<div class="fact"><span class="fact__k">动作</span><span class="fact__v">在笔记库「读书会」目录创建 6 份 Markdown 预习提纲</span></div>
<div class="fact"><span class="fact__k">影响</span><span class="fact__v">新增 6 个文件;不修改、不删除现有笔记</span></div>
<div class="fact"><span class="fact__k">数据范围</span><span class="fact__v">笔记库写入 · 仅该目录 <span class="mono">notes:write:/读书会/</span></span></div>
<div class="fact"><span class="fact__k">费用</span><span class="fact__v">6 次文件写入 · 无额外模型费用(提纲已在本次生成)</span></div>
<div class="fact"><span class="fact__k">有效期</span><span class="fact__v">本次执行 · 勾选「行契」可限 30 天内同类免请命</span></div>
</div>
<div class="approval__acts">
<button class="btn btn--cinnabar" aria-label="确认决定:允许">允许(朱批)</button>
<button class="btn btn--outline" aria-label="确认决定:拒绝">拒绝</button>
<span class="approval__note">朱批 · 你的明确决定,允许或拒绝都留痕。</span>
</div>
</div>
</div>
</div>
<!-- 问节 ⑤ + 临时芽体 -->
<div class="msg msg--user">
<div class="msg__meta"><span class="cn">问节</span><span class="mono">m_c944</span><span>20:12</span></div>
<div class="bubble">在等的时候,先给我一版 30 分钟的精简流程,怕第一次拖堂。</div>
</div>
<div class="msg">
<div class="msg--char__head">
<span class="avatar">梧</span>
<span class="msg--char__name">青梧</span>
<span class="msg--char__meta"><span class="mono" style="font-size:11px;color:var(--text-3)">道体 v3</span><span class="badge badge--info"><span class="badge__dot"></span>运转 · 生成中 <span class="mono">streaming</span></span></span>
</div>
<div class="msg__blocks">
<details class="think-block is-thinking" open>
<summary>
<span class="think-dot" aria-hidden="true"></span>
正在思考
<span class="think-effort">深虑 · 中</span>
<span class="think-meta">思考 0.4k token · 深虑档位可在输入区调整</span>
<svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
</summary>
<div class="think-body think-live">
<p>用户担心首次共读拖堂,要 30 分钟精简版。需要压缩朗读与讨论比例:三分钟齐读保留仪式感,两个问题足够聚焦——「雪大不大」对应白描力度,「痴不痴」对应人物自我认知,比四个问题更利于新人开口……</p>
<p>末段用舟子那句话收尾,与开场「痴」字形成回环,30 分钟结构即<span class="think-cursor" aria-hidden="true"></span></p>
</div>
</details>
<div class="block sprout">
<div class="sprout__head">
<span class="cn" style="font-weight:600">临时芽体</span><span class="cn__plain">流式生成中 · 完成后才固化</span>
<span style="margin-left:auto" class="mono">block 2/4 · delta 118</span>
</div>
<div class="sprout__body">
<div class="md">
<p>30 分钟精简版:前三分钟齐读原文,随后只留两个问题——「雪大不大」和「痴不痴」,最后五分钟交给「舟子那句话」收尾。<span class="stream-cursor" aria-hidden="true"></span></p>
</div>
<div class="src-line" style="margin-top:10px"><span class="unclosed">**时间分配**(未闭合的 Markdown 结构保持源码态,完成后解析)</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 输入区 -->
<div class="composer">
<div class="composer__inner">
<div class="composer__box">
<textarea class="composer__ta" rows="2" placeholder="对青梧说……(Enter 发送 · Shift+Enter 换行)" aria-label="消息输入"></textarea>
<div class="composer__bar">
<button class="icon-btn" aria-label="附件"><svg><use href="#i-paperclip"/></svg></button>
<button class="icon-btn" aria-label="图片"><svg><use href="#i-image"/></svg></button>
<button class="icon-btn" aria-label="实时语音"><svg><use href="#i-mic"/></svg></button>
<button class="pill-btn is-on" data-pill="effort" aria-pressed="true" title="深虑 · 点击开启深度思考,再次点击关闭">深虑</button>
<span class="effort-seg" data-effort-seg role="radiogroup" aria-label="推理力度">
<button role="radio" aria-checked="false" data-effort="低" title="低 · 快省">低</button>
<button role="radio" aria-checked="true" data-effort="中" title="中 · 均衡">中</button>
<button role="radio" aria-checked="false" data-effort="高" title="高 · 深究慢">高</button>
</span>
<button class="pill-btn" data-pill="search" title="博闻 · 本条消息启用联网检索(走百工坊白名单,点击开关)" aria-pressed="false">博闻<small>关</small></button>
<span class="spacer" style="flex:1"></span>
<button class="btn btn--primary btn--sm" aria-label="发送(呈镜)" title="发送 · 呈镜"><svg><use href="#i-send"/></svg>呈镜</button>
</div>
</div>
<div class="composer__hint">
<span>界域 <span class="k">真界 · 现实</span></span>
<span>路径 <span class="k">主脉</span></span>
<span>模型 <span class="k">会话临时 · 覆盖自用户偏好</span></span>
<span>思考 <span class="k" data-effort-hint>深虑 · 中</span>(点深虑开关,右侧调力度)</span>
<span>上下文 <span class="k">2.1k / 8k token</span></span>
</div>
</div>
</div>
</section>
<!-- 右:镜枝面板 -->
<aside class="conv-ctx" aria-label="分支与上下文">
<div style="display:flex;justify-content:flex-end;margin-bottom:6px">
<button class="panel-toggle" data-panel-fold="right" aria-label="收起面板" title="收起分支与上下文面板"><svg style="transform:rotate(180deg)"><use href="#i-chevrons"/></svg></button>
</div>
<section class="ctx-section">
<div class="ctx-section__head">
<span class="cn">镜枝<span class="cn__plain">分支结构</span></span>
<span class="more" style="color:var(--text-3);cursor:default">树视图</span>
</div>
<div class="tree" role="group" aria-label="本主题分支树">
<ul>
<li>
<div class="tree__node tree__node--main"><span class="nm">问节 · 选篇</span><span class="tree__tag tree__tag--main">主脉 ✓</span></div>
<ul>
<li><div class="tree__node tree__node--main"><span class="nm">候答 A · 六篇 + 结构图</span><span class="tree__tag tree__tag--main">归真</span></div></li>
<li><div class="tree__node"><span class="nm">推演枝 · 按「梦」分组(弃)</span></div></li>
</ul>
</li>
<li>
<div class="tree__node tree__node--main"><span class="nm">问节 · 排日历</span><span class="tree__tag tree__tag--main">主脉 ✓</span></div>
<ul>
<li><div class="tree__node tree__node--main"><span class="nm">候答 · 已朱批执行</span></div></li>
</ul>
</li>
<li>
<div class="tree__node tree__node--main"><span class="nm">问节 · 开场白</span></div>
<ul>
<li><div class="tree__node tree__node--main"><span class="nm">候答 A · 雪夜进入</span><span class="tree__tag tree__tag--main">归真</span></div></li>
<li><div class="tree__node"><span class="nm">候答 B · 更克制</span></div></li>
<li><div class="tree__node tree__node--sealed"><span class="nm">推演枝 D</span><span class="tree__tag tree__tag--seal">残答</span></div></li>
</ul>
</li>
<li>
<div class="tree__node tree__node--main"><span class="nm">问节 · 预习提纲</span><span class="tree__tag" style="border-color:color-mix(in srgb,var(--warn) 45%,transparent);color:var(--warn)">请命中</span></div>
</li>
<li>
<div class="tree__node"><span class="nm">问节 · 30 分钟精简版</span>
<ul>
<li><div class="tree__node tree__node--now"><span class="nm">芽体 · 生成中</span><span class="tree__tag tree__tag--bud">streaming</span></div></li>
</ul>
</div>
</li>
</ul>
</div>
</section>
<section class="ctx-section">
<div class="ctx-section__head">
<span class="cn">上下文镜<span class="cn__plain">本次实际采用</span></span>
<a class="more" href="governance.html">明细(诊断需授权)</a>
</div>
<div class="card" style="padding:12px 14px">
<div class="ctx-mirror__row"><span class="ctx-mirror__type">明忆</span><span class="ctx-mirror__txt">读书会隔周四 19:30 · 90 分钟</span><span class="ctx-mirror__src">mem_v12</span></div>
<div class="ctx-mirror__row"><span class="ctx-mirror__type">实录</span><span class="ctx-mirror__txt">3 月购入《陶庵梦忆》校注本</span><span class="ctx-mirror__src">fact_07d</span></div>
<div class="ctx-mirror__row"><span class="ctx-mirror__type">世谱</span><span class="ctx-mirror__txt">读书会成员 6 人 · 两位新人</span><span class="ctx-mirror__src">world_v4</span></div>
<div class="ctx-mirror__omitted">已省略 2 条:1 条过期(去年场地)、1 条已被取代(旧选篇)</div>
<div class="ctx-meter">
<div class="ctx-meter__label"><span>上下文预算</span><span class="mono">2.1k / 8k</span></div>
<div class="progress"><div class="progress__bar" style="width:26%"></div></div>
</div>
<p style="font-size:11px;color:var(--text-3);margin-top:8px">完整 ModelCall 与工具轨迹需双重授权诊断(15 分钟 · 单次)。</p>
</div>
</section>
<section class="ctx-section">
<div class="ctx-section__head">
<span class="cn">神识路由<span class="cn__plain">模型从哪来</span></span>
<a class="more" href="model-gateway.html">神识阁</a>
</div>
<div class="card" style="padding:12px 14px;font-size:12.5px;color:var(--text-2)">
<div style="display:flex;justify-content:space-between;padding:3px 0"><span>平台硬边界</span><span class="mono" style="color:var(--text-3)">✓</span></div>
<div style="display:flex;justify-content:space-between;padding:3px 0"><span>平台目录</span><span class="mono" style="color:var(--text-3)">✓</span></div>
<div style="display:flex;justify-content:space-between;padding:3px 0"><span>用户偏好</span><span class="mono" style="color:var(--text-3)">✓</span></div>
<div style="display:flex;justify-content:space-between;padding:3px 0"><span>会话临时选择</span><span style="color:var(--accent-strong)">生效中</span></div>
<div style="display:flex;justify-content:space-between;padding:3px 0;color:var(--text-3)"><span>角色覆写</span><span>—</span></div>
<p style="font-size:11px;color:var(--text-3);margin-top:8px">上层不可被下层覆盖;首个可见输出后不切换 Provider。</p>
</div>
</section>
</aside>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+133
View File
@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>器谱 · 设备与感知 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></symbol><symbol id="i-mic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/></symbol><symbol id="i-camera" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0Line truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item is-active" aria-current="page" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">器谱</span><span class="crumbs__sep">·</span><span class="here">设备与感知</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
设备能力同时受系统权限与授契约束;采集有持续指示,可即时暂停或撤销。离线缓存加密,不创建独立角色副本。
</div>
<section class="section">
<div class="section__head"><span class="cn">形器<span class="cn__plain">登记设备</span></span></div>
<div class="card" style="padding:6px 18px">
<div class="list-row">
<svg style="width:18px;height:18px;color:var(--text-3)"><use href="#i-monitor"/></svg>
<div class="list-row__body"><div class="list-row__title">MacBook Pro · Tauri 桌面</div><div class="list-row__sub">macOS · 当前设备</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>在线</span>
</div>
<div class="list-row">
<svg style="width:18px;height:18px;color:var(--text-3)"><use href="#i-monitor"/></svg>
<div class="list-row__body"><div class="list-row__title">iPhone · 移动端</div><div class="list-row__sub">iOS · 实时语音常用设备</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>在线</span>
</div>
<div class="list-row">
<svg style="width:18px;height:18px;color:var(--text-3)"><use href="#i-globe"/></svg>
<div class="list-row__body"><div class="list-row__title">Web 浏览器 · Safari</div><div class="list-row__sub">昨天活跃 · 仅文件读写授权</div></div>
<span class="badge badge--muted" style="font-size:11px"><span class="badge__dot"></span>离线</span>
</div>
</div>
</section>
<section class="section">
<div class="section__head"><span class="cn">授契<span class="cn__plain">设备能力授权</span></span><button class="btn btn--ghost btn--sm" style="margin-left:auto">全部撤销</button></div>
<div class="card" style="padding:6px 18px">
<div class="grant-row">
<span class="grant-row__cap"><svg><use href="#i-mic"/></svg>闻声 · 麦克风</span>
<div><div style="font-size:13px">青梧 · 实时语音</div><div class="grant-row__meta">iPhone · 仅通话期间 · 采集指示常显</div></div>
<button class="btn btn--ghost btn--sm">撤销</button>
</div>
<div class="grant-row">
<span class="grant-row__cap"><svg><use href="#i-file"/></svg>文件读写</span>
<div><div style="font-size:13px">青梧 · 书斋归档</div><div class="grant-row__meta">MacBook · 仅 ~/BookRoom/ 目录 · 长期(可随时撤销)</div></div>
<button class="btn btn--ghost btn--sm">撤销</button>
</div>
<div class="grant-row">
<span class="grant-row__cap"><svg><use href="#i-bell"/></svg>通知</span>
<div><div style="font-size:13px">叩心提醒通道</div><div class="grant-row__meta">iPhone · 静默时段 22:00–08:00 · 频率 ≤ 1 条/日</div></div>
<button class="btn btn--ghost btn--sm">撤销</button>
</div>
<div class="grant-row" style="opacity:.6">
<span class="grant-row__cap"><svg><use href="#i-camera"/></svg>观影 · 摄像头</span>
<div><div style="font-size:13px">未授权</div><div class="grant-row__meta">任何角色请求摄像头时将单独请命</div></div>
<span style="font-size:11.5px;color:var(--text-3)">—</span>
</div>
<div class="grant-row" style="opacity:.6">
<span class="grant-row__cap"><svg><use href="#i-pin"/></svg>问路 · 位置</span>
<div><div style="font-size:13px">未授权(上次通感已结束)</div><div class="grant-row__meta">通感默认 10 分钟 · 精度 1km · 结束即失效</div></div>
<span style="font-size:11.5px;color:var(--text-3)">—</span>
</div>
</div>
</section>
<div class="two-col">
<section class="section card" style="border-color:color-mix(in srgb,var(--accent) 40%,transparent)">
<div class="card__head"><span class="cn" style="color:var(--accent-strong)">通感进行中<span class="cn__plain">限时设备感知</span></span><span style="margin-left:auto;font-size:12px;color:var(--text-3)" class="mono">剩 8 分钟</span></div>
<p style="font-size:13px;color:var(--text-2);margin-bottom:10px">青梧 · 问路(位置 · 精度 1km)· 用于「找一家安静的茶馆」。采集指示常显,位置不写入长期记忆,会话结束即丢弃。</p>
<div class="progress" style="margin-bottom:12px"><div class="progress__bar" style="width:20%"></div></div>
<div style="display:flex;gap:10px">
<button class="btn btn--outline btn--sm">暂停</button>
<button class="btn btn--danger btn--sm">立即结束并撤销</button>
</div>
</section>
<section class="section">
<div class="block approval">
<div class="approval__head">
<span class="approval__seal" aria-hidden="true">命</span>
<div>
<div class="approval__title"><span class="cn">请命 · 观屏</span><span class="cn__plain">屏幕读取授权</span></div>
<div style="font-size:11.5px;color:var(--text-3)">来自镜树「书房整理」· 限此一次</div>
</div>
</div>
<div class="approval__body">
<p style="font-size:14px;color:var(--text-1)">允许青梧在未来 10 分钟读取当前屏幕?</p>
<div class="fact"><span class="fact__k">用途</span><span class="fact__v">识别书桌照片中的书目并归档</span></div>
<div class="fact"><span class="fact__k">数据</span><span class="fact__v">仅屏幕内容 · 不截屏存储 · 不写入长期记忆</span></div>
</div>
<div class="approval__acts">
<button class="btn btn--cinnabar" aria-label="确认决定:允许本次观屏">允许(本次,10 分钟)</button>
<button class="btn btn--outline">拒绝</button>
</div>
</div>
</section>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+142
View File
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>司命台 · 安全与治理 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol><symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol><symbol id="i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></symbol><symbol id="i-check" vLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item is-active" aria-current="page" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">司命台</span><span class="crumbs__sep">·</span><span class="here">安全 · 权限 · 审计 · 治理</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
治理不支配角色或用户数据;隐藏推理、密钥与凭据永不进入诊断与审计展示。错误与安全事件优先直白表述。
</div>
<section class="section card">
<div class="card__head"><span class="cn">阴阳平衡<span class="cn__plain">朱批治理的三态</span></span><span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">阳亢 · 放行偏多 / 动态平衡 / 阴盛 · 驳回偏多</span></div>
<div class="taiji-gauge">
<svg viewBox="0 0 128 76" role="img" aria-label="阴阳平衡仪表:通过率 78%,动态平衡,鎏金指针居中偏右">
<path d="M 12 68 A 52 52 0 0 1 116 68" fill="none" stroke="var(--line)" stroke-width="7" stroke-linecap="round"/>
<path d="M 12 68 A 52 52 0 0 1 38 25" fill="none" stroke="color-mix(in srgb, var(--cinnabar) 45%, transparent)" stroke-width="7" stroke-linecap="round"/>
<path d="M 90 25 A 52 52 0 0 1 116 68" fill="none" stroke="color-mix(in srgb, var(--accent) 50%, transparent)" stroke-width="7" stroke-linecap="round"/>
<line x1="64" y1="68" x2="90" y2="25" stroke="var(--gilt)" stroke-width="2" stroke-linecap="round"/>
<circle cx="64" cy="68" r="3" fill="var(--gilt)"/>
<text class="taiji-gauge__label" x="12" y="76">阴 · 驳回</text>
<text class="taiji-gauge__label" x="116" y="76" text-anchor="end">阳 · 放行</text>
</svg>
<div>
<div class="taiji-gauge__val" style="color:var(--accent-strong)">动态平衡</div>
<div style="font-size:12px;color:var(--text-2);margin-top:3px">近 30 日朱批通过率 78% · 驳回 22%<br><span style="color:var(--text-3)">指针偏右但在平衡带内;阳亢时提示收紧行契,阴盛时提示检查策略误伤</span></div>
</div>
</div>
</section>
<div class="tabs" role="tablist" aria-label="治理队列">
<button class="tab is-on" role="tab" aria-selected="true">内容 <span class="cnt">2</span></button>
<button class="tab" role="tab" aria-selected="false">插件 <span class="cnt">1</span></button>
<button class="tab" role="tab" aria-selected="false">市场 <span class="cnt">1</span></button>
<button class="tab" role="tab" aria-selected="false">申诉 <span class="cnt">1</span></button>
</div>
<section class="section">
<div class="section__head"><span class="cn">司议<span class="cn__plain">治理案件</span></span></div>
<div class="card case-card" style="margin-bottom:14px;padding-left:20px">
<div class="card__head">
<span class="badge badge--warn"><span class="badge__dot"></span>待审 · 等待审核 <span class="mono">pending_review</span></span>
<span style="font-size:11.5px;color:var(--text-3);margin-left:auto">09-15 11:20 · 鸣冤 ×3</span>
</div>
<p style="font-size:13.5px">雅集帖子「未署名的守砚人立像二创」被举报:疑似未保留来源署名(CC BY-NC 要求)。</p>
<div style="display:flex;gap:8px;margin-top:12px">
<button class="btn btn--outline btn--sm">查看原文</button>
<button class="btn btn--outline btn--sm">限观 · 限制展示</button>
<button class="btn btn--danger btn--sm">移除</button>
<button class="btn btn--ghost btn--sm">驳回举报</button>
</div>
</div>
<div class="card case-card" style="padding-left:20px">
<div class="card__head">
<span class="badge badge--warn"><span class="badge__dot"></span>待审 <span class="mono">pending_review</span></span>
<span style="font-size:11.5px;color:var(--text-3);margin-left:auto">09-14 09:02 · 自动扫描</span>
</div>
<p style="font-size:13.5px">市场商品「曲谱排版插件 v1.4」扫描发现:Manifest 声明与实际网络请求域不一致。</p>
<div style="display:flex;gap:8px;margin-top:12px">
<button class="btn btn--outline btn--sm">查看扫描报告</button>
<button class="btn btn--danger btn--sm">下架</button>
<button class="btn btn--ghost btn--sm">通知创作者</button>
</div>
</div>
</section>
<div class="two-col">
<section class="section card">
<div class="card__head"><span class="cn">诊断授权<span class="cn__plain">双重授权</span></span></div>
<p style="font-size:12.5px;color:var(--text-2);margin-bottom:12px">读取 Run 详细诊断(ModelCall、工具参数、ContextSnapshot)需要在普通认证之外叠加短期诊断令牌:绑定单个 Run、单次使用、默认 15 分钟。</p>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t">申请诊断令牌</div><div class="set-row__d">目的 user_inspection · 绑定 run_9f31c2(天工阁 · 书单任务)</div></div>
<button class="btn btn--cinnabar btn--sm">签发</button>
</div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t mono">hjd_8f…(已过期)</div><div class="set-row__d">09-15 12:04 签发 · 12:19 过期 · 已用 1 次 · 范围:青梧 · 选篇答复</div></div>
<span class="badge badge--muted" style="font-size:11px"><span class="badge__dot"></span>已过期</span>
</div>
<p style="font-size:11.5px;color:var(--text-3);margin-top:10px">隐藏推理、密钥、Authorization 与签名 URL 永不返回;管理员与诊断均不可读私箧。</p>
</section>
<section class="section card">
<div class="card__head"><span class="cn">安全概览</span><a class="more" href="settings.html">设置 →</a></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">活跃会话 2</div><div class="set-row__d">MacBook(当前)· iPhone</div></div><a class="btn btn--ghost btn--sm" href="settings.html">管理</a></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">Service Account key 1</div><div class="set-row__d">scope:只读统计 · 90 天轮换</div></div><button class="btn btn--ghost btn--sm">轮换</button></div>
<div class="set-row"><div class="set-row__t">凭据重用检测</div><span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>30 天 0 次</span></div>
</section>
</div>
<section class="section">
<div class="section__head"><span class="cn">审计<span class="cn__plain">AuditEvent · 权威事务同批提交</span></span></div>
<div class="card">
<div class="table-wrap"><table class="data">
<thead><tr><th>时间</th><th>主体</th><th>动作</th><th>对象</th><th>结果</th></tr></thead>
<tbody>
<tr><td class="mono">09-15 19:48</td><td>云舟</td><td>朱批 · 允许</td><td class="mono">calendar.write · run_7ab1</td><td class="mark-ok">✓</td></tr>
<tr><td class="mono">09-15 19:52</td><td>云舟</td><td>创建天工</td><td class="mono">run_9f31c2</td><td class="mark-ok">✓</td></tr>
<tr><td class="mono">09-15 12:04</td><td>云舟</td><td>签发诊断令牌</td><td class="mono">hjd_8f…</td><td class="mark-ok">✓</td></tr>
<tr><td class="mono">09-14 22:31</td><td>未知请求</td><td>跨镜域读取</td><td class="mono">memory · 雪袖</td><td class="mark-no" style="color:var(--err)">✗ 拒绝(失败关闭)</td></tr>
<tr><td class="mono">09-12 10:05</td><td>云舟</td><td>BYOK 轮换</td><td class="mono">byok_7c02…</td><td class="mark-ok">✓</td></tr>
</tbody>
</table></div>
</div>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+277
View File
@@ -0,0 +1,277 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>镜庭 · 个人首页 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
<symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol>
<symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
<symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol>
<symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol>
<symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
<symbol id="i-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></symbol>
<symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol>
<symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-chevrons" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></symbol>
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item is-active" aria-current="page" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">镜庭</span><span class="crumbs__sep">·</span><span class="here">个人首页</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" aria-label="搜索"><svg><use href="#i-search"/></svg></button>
<button class="icon-btn" aria-label="通知"><svg><use href="#i-bell"/></svg></button>
</header>
<div class="content">
<div class="hero">
<div class="sundial" role="img" aria-label="日晷:巳时,白露节气,与青梧相伴第 918 日">
<svg viewBox="0 0 116 116">
<circle cx="58" cy="58" r="55" fill="none" stroke="var(--gilt-faint)" stroke-width="1"/>
<circle cx="58" cy="58" r="47" fill="none" stroke="var(--line-soft)" stroke-width="0.8"/>
<g class="sundial-spin">
<circle cx="58" cy="58" r="51" fill="none" stroke="var(--gilt-faint)" stroke-width="0.6" stroke-dasharray="2 7"/>
</g>
<!-- 十二时辰刻度:巳位(东南)鎏金加粗 -->
<g stroke="var(--line)" stroke-width="1.4">
<line x1="58" y1="6" x2="58" y2="14"/><line x1="87" y1="14" x2="83" y2="21"/>
<line x1="102" y1="29" x2="95" y2="34"/><line x1="110" y1="58" x2="102" y2="58"/>
<line x1="102" y1="87" x2="95" y2="82"/><line x1="87" y1="102" x2="83" y2="95"/>
<line x1="58" y1="110" x2="58" y2="102"/><line x1="29" y1="102" x2="33" y2="95"/>
<line x1="14" y1="87" x2="21" y2="82"/><line x1="6" y1="58" x2="14" y2="58"/>
<line x1="14" y1="29" x2="21" y2="34"/><line x1="29" y1="14" x2="33" y2="21"/>
</g>
<!-- 巳时弧(9-11 时,约 135°→165° 区间示意) -->
<path d="M 93.2 25.4 A 52 52 0 0 1 104.5 44.6" fill="none" stroke="var(--gilt)" stroke-width="2.4" stroke-linecap="round"/>
<!-- 晷针 -->
<line x1="58" y1="58" x2="93" y2="27" stroke="var(--accent)" stroke-width="1.6" stroke-linecap="round"/>
<circle cx="58" cy="58" r="2.4" fill="var(--accent)"/>
</svg>
<div class="sundial__center">
<span class="sundial__hour">巳时</span>
<span class="sundial__sub">白露 · 第 10 日</span>
</div>
</div>
<div>
<h1>上午好,云舟</h1>
<p style="color:var(--text-2);font-size:13px;margin-top:4px">周三 · 2026-09-16 · 与青梧相伴第 918 日 · 镜庭只聚合状态,各域才是数据事实源</p>
</div>
<div class="hero__stats">
<a class="hero__stat" href="conversation.html"><b>2</b><span>镜树 · 进行中对话</span></a>
<a class="hero__stat" href="conversation.html"><b>1</b><span>请命 · 待你确认</span></a>
<a class="hero__stat" href="memory.html"><b>3</b><span>息壤候选 · 待审</span></a>
<a class="hero__stat" href="agent-workspace.html"><b>2</b><span>天工 · 运行中</span></a>
</div>
</div>
<section class="section">
<div class="section__head">
<span class="cn">镜生<span class="cn__plain">你的角色</span></span>
<a class="more" href="characters.html">全部 4 位 →</a>
</div>
<div class="grid-cards">
<a class="card char-card" href="conversation.html">
<div class="char-card__top">
<span class="avatar">梧</span>
<div>
<div class="char-card__name">青梧</div>
<div style="font-size:12px;color:var(--text-3)">守砚人 · 道体 v3</div>
</div>
</div>
<p class="char-card__line">「书斋已整理,今晚共读的选篇单放在桌上了。」</p>
<div class="char-card__foot">
<span class="badge badge--info"><span class="badge__dot"></span>临照 · 前台运行</span>
<span class="char-card__time">10 分钟前</span>
</div>
</a>
<a class="card char-card" href="#">
<div class="char-card__top">
<span class="avatar">袖</span>
<div>
<div class="char-card__name">雪袖</div>
<div style="font-size:12px;color:var(--text-3)">茶室主人 · 道体 v1</div>
</div>
</div>
<p class="char-card__line">「新到的白毫银针,等你来尝。」</p>
<div class="char-card__foot">
<span class="badge badge--muted"><span class="badge__dot"></span>守一 · 安静待命</span>
<span class="char-card__time">昨天</span>
</div>
</a>
<a class="card char-card" href="#">
<div class="char-card__top">
<span class="avatar">澜</span>
<div>
<div class="char-card__name">观澜</div>
<div style="font-size:12px;color:var(--text-3)">航海士 · 道体 v2 · 静修中</div>
</div>
</div>
<p class="char-card__line">静修 · 停止运行,身份存续;随时可以唤醒。</p>
<div class="char-card__foot">
<span class="badge badge--muted"><span class="badge__dot"></span>静修 · 停止运行</span>
<span class="char-card__time">9 月 2 日</span>
</div>
</a>
<a class="card char-card" href="#">
<div class="char-card__top">
<span class="avatar" style="filter:grayscale(.7);opacity:.75">岁</span>
<div>
<div class="char-card__name">旧岁</div>
<div style="font-size:12px;color:var(--text-3)">守岁人 · 归藏 · 可复照</div>
</div>
</div>
<p class="char-card__line">归藏 · 已从日常入口隐藏;复照可重开,不抹历史。</p>
<div class="char-card__foot">
<span class="badge badge--muted"><span class="badge__dot"></span>归藏 · 已隐藏</span>
<span class="char-card__time">7 月 30 日</span>
</div>
</a>
</div>
</section>
<div class="home-cols">
<section class="section" style="margin:0">
<div class="section__head">
<span class="cn">镜树<span class="cn__plain">最近对话</span></span>
<a class="more" href="conversation.html">全部 →</a>
</div>
<div class="card" style="padding:6px 18px">
<a class="list-row" href="conversation.html">
<span class="avatar avatar--sm">梧</span>
<div class="list-row__body">
<div class="list-row__title">《陶庵梦忆》共读选篇 <span class="realm-dot">真界</span></div>
<div class="list-row__sub">青梧:六篇已定,结构图也画好了,请过目。</div>
</div>
<span class="list-row__time">10 分钟前</span>
</a>
<div class="list-row">
<span class="avatar avatar--sm">袖</span>
<div class="list-row__body">
<div class="list-row__title">秋茶清单 <span class="realm-dot realm-dot--world">境界</span></div>
<div class="list-row__sub">雪袖:按你上次的口味,先把白毫银针排第一。</div>
</div>
<span class="list-row__time">昨天</span>
</div>
<div class="list-row">
<span class="avatar avatar--sm">梧</span>
<div class="list-row__body">
<div class="list-row__title">书房整理方案(幻境试演) <span class="realm-dot realm-dot--sim">幻境</span></div>
<div class="list-row__sub">青梧:这只是试演方案,不影响现实与正史。</div>
</div>
<span class="list-row__time">9 月 12 日</span>
</div>
<div class="list-row">
<span class="avatar avatar--sm">澜</span>
<div class="list-row__body">
<div class="list-row__title">远洋航线推演</div>
<div class="list-row__sub">观澜:航线已归档,静修前等你验收。</div>
</div>
<span class="list-row__time">9 月 2 日</span>
</div>
</div>
</section>
<section class="section" style="margin:0">
<div class="section__head">
<span class="cn">天工<span class="cn__plain">Agent 任务</span></span>
<a class="more" href="agent-workspace.html">天工阁 →</a>
</div>
<div class="card" style="padding:14px 18px 6px;margin-bottom:14px">
<div class="list-row" style="border-bottom:none;padding-top:4px">
<div class="list-row__body">
<div class="list-row__title">整理性灵 newsletter 书单</div>
<div class="list-row__sub">青梧 · 分工使 2 · token 12.4k / 20k</div>
<div class="progress" style="margin-top:7px"><div class="progress__bar" style="width:62%"></div></div>
</div>
<span class="badge badge--info"><span class="badge__dot"></span>运转 · 运行中 <span class="mono">running</span></span>
</div>
</div>
<div class="card" style="padding:6px 18px">
<div class="list-row">
<div class="list-row__body">
<div class="list-row__title">周报草稿 · 第 37 周</div>
<div class="list-row__sub">任务 Agent · 无角色身份</div>
</div>
<span class="badge badge--muted"><span class="badge__dot"></span>待启 · 排队中 <span class="mono">queued</span></span>
</div>
<div class="list-row">
<div class="list-row__body">
<div class="list-row__title">书斋照片归档命名</div>
<div class="list-row__sub">青梧 · 已成 · 耗时 4 分 12 秒</div>
</div>
<span class="badge badge--ok"><span class="badge__dot"></span>已成 · 已完成 <span class="mono">completed</span></span>
</div>
</div>
</section>
</div>
<section class="section" style="margin-top:26px">
<div class="section__head">
<span class="cn">待你决定<span class="cn__plain">审批与候选</span></span>
</div>
<div class="card" style="padding:6px 18px">
<div class="list-row" href="#">
<span class="approval__seal" aria-hidden="true" style="flex:none">命</span>
<div class="list-row__body">
<div class="list-row__title">请命 · 写入日历(6 个事件)</div>
<div class="list-row__sub">青梧请求在日历创建 6 个「共读讨论」事件 · 数据范围:仅日历 · 费用:无</div>
</div>
<a class="btn btn--cinnabar btn--sm" href="conversation.html">去朱批</a>
</div>
<div class="list-row">
<span class="badge" style="flex:none;border-color:color-mix(in srgb,var(--warn) 40%,transparent);color:var(--warn)">息壤候选 ×3</span>
<div class="list-row__body">
<div class="list-row__title">「读书会在隔周四晚」「偏爱短篇」「花粉过敏(重忆)」</div>
<div class="list-row__sub">来自青梧的对话候选 · 归真后才会影响未来上下文</div>
</div>
<a class="btn btn--outline btn--sm" href="memory.html">去审阅</a>
</div>
<div class="list-row">
<span class="badge" style="flex:none">叩心候选</span>
<div class="list-row__body">
<div class="list-row__title">青梧想在明晚共读前发一条提醒</div>
<div class="list-row__sub">叩心当前静默 · 不回应不会降低关系 · 可设频率上限</div>
</div>
<a class="btn btn--ghost btn--sm" href="settings.html">了解叩心</a>
</div>
</div>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+243
View File
@@ -0,0 +1,243 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>魂镜 Hunjing · 界面原型 · 设计说明</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
<symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol>
<symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
<symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol>
<symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol>
<symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol>
<symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol>
<symbol id="i-chevrons" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></symbol>
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航">
<div class="nav-group">
<div class="nav-group__title">原型</div>
<a class="nav-item is-active" href="index.html"><svg><use href="#i-home"/></svg><span class="cn">设计说明</span><span class="nav-plain">索引</span></a>
</div>
<div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div>
<div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div>
<div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div>
<div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div>
</nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
<span style="font-size:11.5px;color:var(--text-3)">墨 / 月白 双主题</span>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">设计说明</span><span class="crumbs__sep">/</span><span class="here">静态原型 v0.1</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查 · 确认后转 React</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content doc">
<div class="doc-hero">
<h1>魂镜 <span class="ver">原型 v0.2</span></h1>
<p>魂镜 Web 界面静态 HTML 原型(v0.1 第一批方向已确认,v0.2 增补第二批共 15 个界面),供审查视觉方向、布局结构与文化语言的界面落地方式。全部确认后按此实现为 React 组件;未在本文档登记的视觉细节以本页令牌为准。</p>
</div>
<section class="doc-sec">
<h2>原型页面</h2>
<p class="sub">第一批(已确认):镜庭、镜生、镜树;第二批:息壤、形仪、天工阁、神识阁、百工坊、雅集、同契台、云市、器谱、司命台、设置,及运营者/自托管使用的管理面与认证页。全站链接已疏通:跨页指向对应页面、页内为锚点;logo 单击回本目录,350ms 内连点 5 次触发「镜启」彩蛋(承接 main 分支旧版设计);侧栏可折叠并记忆偏好。</p>
<div class="page-nav">
<a class="card" href="home.html"><span class="cn">镜庭<span class="cn__plain">个人首页</span></span><span class="d">全局框架、角色入口、任务与待审事项</span><span class="p">home.html</span></a>
<a class="card" href="conversation.html"><span class="cn">镜树<span class="cn__plain">分支对话</span></span><span class="d">ContentBlock、请命朱批、界域、候答、流式芽体、分支树</span><span class="p">conversation.html</span></a>
<a class="card" href="characters.html"><span class="cn">镜生<span class="cn__plain">角色身份</span></span><span class="d">道体、缘契、内驱力、大周天、谱系、生命周期</span><span class="p">characters.html</span></a>
<a class="card" href="memory.html"><span class="cn">息壤<span class="cn__plain">记忆与知识</span></span><span class="d">候选审阅、正影纠正、明忆版本、玄络投影</span><span class="p">memory.html</span></a>
<a class="card" href="avatar.html"><span class="cn">形仪<span class="cn__plain">形象与资产</span></span><span class="d">真形锚、主仪化仪、声音、Live2D 与 3D、易相历史</span><span class="p">avatar.html</span></a>
<a class="card" href="agent-workspace.html"><span class="cn">天工阁<span class="cn__plain">Agent 任务</span></span><span class="d">工筹计划、预算、分工使、轨迹、验收</span><span class="p">agent-workspace.html</span></a>
<a class="card" href="model-gateway.html"><span class="cn">神识阁<span class="cn__plain">模型与路由</span></span><span class="d">逻辑模型、路由继承链、BYOK、用量观测</span><span class="p">model-gateway.html</span></a>
<a class="card" href="plugins.html"><span class="cn">百工坊<span class="cn__plain">插件生态</span></span><span class="d">沙箱、已安装、升级权限确认、授契记录</span><span class="p">plugins.html</span></a>
<a class="card" href="community.html"><span class="cn">雅集<span class="cn__plain">社区广场</span></span><span class="d">动态流、角色谱、话题、推荐解释与重置</span><span class="p">community.html</span></a>
<a class="card" href="collaboration.html"><span class="cn">同契台<span class="cn__plain">协作空间</span></span><span class="d">分支版本、审稿合卷、成员权限矩阵、活动</span><span class="p">collaboration.html</span></a>
<a class="card" href="marketplace.html"><span class="cn">云市<span class="cn__plain">创作市场</span></span><span class="d">商品、市契许可、退契规则、分润结算</span><span class="p">marketplace.html</span></a>
<a class="card" href="devices.html"><span class="cn">器谱<span class="cn__plain">设备与感知</span></span><span class="d">形器登记、授契、通感会话、直白权限请求</span><span class="p">devices.html</span></a>
<a class="card" href="governance.html"><span class="cn">司命台<span class="cn__plain">安全与治理</span></span><span class="d">治理队列、诊断双重授权、审计、安全概览</span><span class="p">governance.html</span></a>
<a class="card" href="admin.html"><span class="cn">管理面<span class="cn__plain">运营与部署后台</span></span><span class="d">司议队列、支持工单与脱敏支持包、权限管理(RBAC / 双人授权)、部署健康(灵台双形态)、SLO</span><span class="p">admin.html</span></a>
<a class="card" href="login.html"><span class="cn">登录<span class="cn__plain">铜镜鉴心</span></span><span class="d">铭文环镜背、扫描亮带、会话 Cookie / PKCE 说明、通行密钥与恢复钥匙</span><span class="p">login.html</span></a>
<a class="card" href="register.html"><span class="cn">注册<span class="cn__plain">创建账户</span></span><span class="d">邮箱验证码、数据权告知(默认不训练 / 可导出删除 / 私箧持钥)、条款确认</span><span class="p">register.html</span></a>
<a class="card" href="settings.html"><span class="cn">设置<span class="cn__plain">账户与偏好</span></span><span class="d">会话、私箧、数据权、删除、封镜</span><span class="p">settings.html</span></a>
</div>
</section>
<section class="doc-sec">
<h2>设计立场</h2>
<p class="sub">四条约束所有页面的决定,审查时优先核对这四条是否成立。</p>
<div class="card" style="display:flex;flex-direction:column;gap:12px">
<div><b class="cn">一 · 双层文字系统</b><span style="color:var(--text-2)"> —— 文化名用衬线宋体,直白解释用无衬线小字,两种字体本身就是"文化名 · 直白解释"两层结构的可视化;风险、金额、权限、删除等后果始终以直白语言为主,文化名只作辅助。</span></div>
<div><b class="cn">二 · 朱砂只属于朱批</b><span style="color:var(--text-2)"> —— 朱砂色是"用户确认决定"的专用语义色:请命卡的允许按钮、危险动作入口使用它;普通操作一律铜镜青。颜色即权限语言。</span></div>
<div><b class="cn">三 · 墨与月白</b><span style="color:var(--text-2)"> —— 深色为墨(青调黑,非纯黑),浅色为月白(青灰白,非暖米);铜镜青为主色,取铜镜铜绿之意,不用蓝紫系默认色。</span></div>
<div><b class="cn">四 · 状态永远三段式</b><span style="color:var(--text-2)"> —— 状态徽章固定为「文化状态 · 直白状态 + 稳定工程值」,例如「运转 · 运行中 <span class="mono">running</span>」;错误与交易状态绝不只显示文化名。</span></div>
</div>
</section>
<section class="doc-sec">
<h2>色彩令牌</h2>
<p class="sub">右下角可切换主题;以下色板随主题联动。转 React 时这些令牌进入 Tailwind 配置 / CSS 变量。</p>
<div class="swatches">
<div class="swatch"><div class="swatch__c" style="background:var(--bg);border-bottom:1px solid var(--line-soft)"></div><div class="swatch__m"><b>底 · 墨 / 月白</b>表面层级 0<span class="mono">--bg</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--bg-raised)"></div><div class="swatch__m"><b>浮面</b>卡片与气泡<span class="mono">--bg-raised</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--accent)"></div><div class="swatch__m"><b>铜镜青</b>主色 · 选中/链接/运转<span class="mono">--accent</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--cinnabar)"></div><div class="swatch__m"><b>朱砂</b>朱批 · 确认与危险<span class="mono">--cinnabar</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--ok)"></div><div class="swatch__m"><b>已成</b>完成态<span class="mono">--ok</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--warn)"></div><div class="swatch__m"><b>秋香</b>整备 / 幻境<span class="mono">--warn</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--err)"></div><div class="swatch__m"><b>受阻</b>失败态<span class="mono">--err</span></div></div>
<div class="swatch"><div class="swatch__c" style="background:var(--text-1)"></div><div class="swatch__m"><b>正文</b>一级文字<span class="mono">--text-1</span></div></div>
</div>
</section>
<section class="doc-sec">
<h2>字体规范</h2>
<p class="sub">不引入外部字体文件,使用系统中文字体栈;转 React 时同栈迁移。</p>
<div class="card type-spec">
<div class="type-spec__row"><span class="type-spec__label">文化名 · 宋体衬线</span><span class="type-spec__sample cn" style="font-size:22px;font-weight:600">镜树 · 分支对话</span></div>
<div class="type-spec__row"><span class="type-spec__label">界面正文 · 无衬线</span><span class="type-spec__sample" style="font-size:14px">正文 14px / 行高 1.65,辅助文字 12.5px,元信息 11.5px</span></div>
<div class="type-spec__row"><span class="type-spec__label">稳定值 · 等宽</span><span class="type-spec__sample mono" style="font-size:13px">running · m_01h8xq… · block_id: blk_7f3a</span></div>
</div>
</section>
<section class="doc-sec">
<h2>状态徽章全集</h2>
<p class="sub">对应 docs/product/cultural-language.md 第 10 节;徽章三段:文化状态 · 直白状态 · 稳定值。</p>
<div class="card">
<div class="table-wrap"><table class="data badge-table">
<thead><tr><th>徽章</th><th>稳定值</th><th>使用场景</th></tr></thead>
<tbody>
<tr><td><span class="badge badge--muted"><span class="badge__dot"></span>待启 · 排队中 <span class="mono">queued</span></span></td><td class="mono">queued</td><td>Run / 媒体任务排队</td></tr>
<tr><td><span class="badge badge--info"><span class="badge__dot"></span>运转 · 运行中 <span class="mono">running</span></span></td><td class="mono">running</td><td>Run 执行中</td></tr>
<tr><td><span class="badge badge--warn"><span class="badge__dot"></span>请命中 · 等待确认 <span class="mono">waiting_approval</span></span></td><td class="mono">waiting_approval</td><td>等待朱批</td></tr>
<tr><td><span class="badge badge--ok"><span class="badge__dot"></span>已成 · 已完成 <span class="mono">completed</span></span></td><td class="mono">completed</td><td>Run / 任务完成</td></tr>
<tr><td><span class="badge badge--err"><span class="badge__dot"></span>受阻 · 失败 <span class="mono">failed</span></span></td><td class="mono">failed</td><td>失败(附原因与重试)</td></tr>
<tr><td><span class="badge badge--muted"><span class="badge__dot"></span>已止 · 已取消 <span class="mono">cancelled</span></span></td><td class="mono">cancelled</td><td>用户取消</td></tr>
<tr><td><span class="badge badge--muted"><span class="badge__dot"></span>守一 · 安静待命 <span class="mono">idle</span></span></td><td class="mono">idle</td><td>角色无运行</td></tr>
<tr><td><span class="badge badge--warn"><span class="badge__dot"></span>整备 · 正在处理 <span class="mono">processing</span></span></td><td class="mono">processing</td><td>媒体 / 资源处理</td></tr>
<tr><td><span class="badge badge--warn"><span class="badge__dot"></span>待审 · 等待审核 <span class="mono">pending_review</span></span></td><td class="mono">pending_review</td><td>社区 / 市场审核</td></tr>
<tr><td><span class="badge badge--ok"><span class="badge__dot"></span>已刊 · 已发布 <span class="mono">published</span></span></td><td class="mono">published</td><td>社区内容</td></tr>
</tbody>
</table></div>
</div>
</section>
<section class="doc-sec">
<h2>组件样例</h2>
<p class="sub">按钮语义:铜镜青为主操作,朱砂仅用于朱批(用户确认决定)与危险操作,描边为次操作。</p>
<div class="card demo-block">
<div class="demo-row">
<button class="btn btn--primary">开始对话</button>
<button class="btn btn--cinnabar">允许(朱批)</button>
<button class="btn btn--outline">查看谱系</button>
<button class="btn btn--ghost">取消</button>
<button class="btn btn--danger">删除角色数据</button>
<button class="btn btn--outline btn--sm">续生</button>
</div>
<div class="demo-row">
<span class="cn" style="font-size:16px">请命<span class="cn__plain">请求你的确认</span></span>
<span class="cn" style="font-size:16px">息壤<span class="cn__plain">角色记忆</span></span>
<span class="cn" style="font-size:16px">幻境<span class="cn__plain">临时试演 · 不入正史</span></span>
</div>
</div>
<div class="card demo-block">
<div class="block__head" style="border-radius:var(--radius) var(--radius) 0 0"><span class="lang">python</span><span class="fname">reading_progress.py</span><span class="acts"><button class="icon-btn" style="width:26px;height:26px" aria-label="复制源码"><svg style="width:13px;height:13px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></span></div>
<pre class="code"><span class="c"># 汇总共读接龙进度(示意)</span>
<span class="k">from</span> pathlib <span class="k">import</span> Path
<span class="k">def</span> <span class="f">summarize</span>(records: <span class="k">list</span>[<span class="k">dict</span>]) -&gt; <span class="k">dict</span>:
done = [r <span class="k">for</span> r <span class="k">in</span> records <span class="k">if</span> r[<span class="s">"finished"</span>]]
<span class="k">return</span> {
<span class="s">"total"</span>: <span class="f">len</span>(records),
<span class="s">"finished"</span>: <span class="f">len</span>(done),
<span class="s">"pending"</span>: [r[<span class="s">"name"</span>] <span class="k">for</span> r <span class="k">in</span> records <span class="k">if</span> <span class="k">not</span> r[<span class="s">"finished"</span>]],
}</pre>
</div>
<div class="card demo-block">
<div class="math">注意力权重 = softmax( QK<sup>T</sup> / √d<sub>k</sub> ) · V
<span class="math__fallback">fallback:注意力权重等于 Q、K 转置乘积经缩放与归一化后对 V 的加权求和</span>
</div>
</div>
</section>
<section class="doc-sec">
<h2>审查重点</h2>
<p class="sub">第一批(色彩、字体、镜树/镜生/镜庭结构)已确认;本轮重点核对第二批页面,反馈时按页名 + 位置描述即可。</p>
<div class="card" style="display:flex;flex-direction:column;gap:10px">
<div>1. 息壤:候选审阅的「归真入忆 / 正影纠正 / 忽略」操作,以及大周天来源、重忆提示的呈现是否可理解。</div>
<div>2. 天工阁:工筹步骤、预算、分工使、轨迹的信息层级是否符合你对 Run 治理的预期。</div>
<div>3. 神识阁 / 百工坊:路由继承链与「升级新增权限需重新确认」的展示是否清晰,朱批语义是否仍然专一。</div>
<div>4. 云市:金额、分润、退契规则是否足够直白(文化名没有掩盖交易后果)。</div>
<div>5. 器谱 / 司命台 / 设置:直白权限句、双重授权、私箧与删除等高风险项的措辞与视觉重量。</div>
<div>6. 雅集 / 同契台:社区帖卡与协作审稿流的信息密度是否合适。</div>
</div>
</section>
<section class="doc-sec">
<h2>旧版传承与候选</h2>
<p class="sub">已系统挖掘 main 分支旧版 25 页设计原型(铜镜鉴心体系),按「取手法、不照搬旧概念名」原则融入——六爻 / 九枢 / 三魂等概念 1.0 已由小周天、灵台六模块、内驱力等替代,仅传承其可视化手法。</p>
<div class="card">
<div class="table-wrap"><table class="data">
<thead><tr><th>旧版想法</th><th>来源页</th><th>状态</th></tr></thead>
<tbody>
<tr><td>鎏金发丝线三档令牌</td><td>tokens.css</td><td class="mark-ok">✓ 已融入全站(分组标题 / 金标竖条 / 品牌外环 / 时间线)</td></tr>
<tr><td>logo 五连击「镜启」诗签彩蛋</td><td>_theme.js</td><td class="mark-ok">✓ 已融入(修好首次单击即跳转的缺陷)</td></tr>
<tr><td>铜镜登录页(铭文环 / 铜面噪点 / 扫描亮带)</td><td>copper-mirror-login</td><td class="mark-ok">✓ 已融入登录 / 注册页</td></tr>
<tr><td>极光铜晕底 + 卡片 hover 光扫</td><td>_base.css / rune-engine</td><td class="mark-ok">✓ 已融入全站(极淡,不干扰阅读)</td></tr>
<tr><td>日晷时辰盘 + 节气 + 相伴日文案</td><td>time</td><td class="mark-ok">✓ 已融入镜庭 hero(巳时 · 白露 · 第 918 日)</td></tr>
<tr><td>朱批卡风险分级(RISK L2)</td><td>chat-redesign</td><td class="mark-ok">✓ 已融入镜树请命卡</td></tr>
<tr><td>「呈镜」发送按钮命名(旧版已确立血脉)</td><td>chat-redesign</td><td class="mark-ok">✓ 已融入镜树输入区(aria 仍读「发送」)</td></tr>
<tr><td>运行阶段爻象三态(done / active / pending)</td><td>chat / evolution</td><td class="mark-ok">✓ 已融入天工阁工筹(active 步骤鎏金光晕)</td></tr>
<tr><td>河图置信度图谱(置信→线宽,假设→朱漆虚线待验)</td><td>world</td><td class="mark-ok">✓ 已融入息壤玄络(待验边 + 标注)</td></tr>
<tr><td>息壤浮现(本次回应用了哪些记忆)</td><td>chat / copper-v2</td><td class="mark-ok">✓ 对应 1.0 上下文镜(镜树右栏,同源 ContextSnapshot)</td></tr>
<tr><td>九枢星盘「活的仪器」+ 铜镜四态健康词汇</td><td>admin / copper-v2</td><td class="mark-ok">✓ 已复刻为灵台六枢星盘(太一居中 + 五枢环绕 + 呼吸刻度 / 慢旋 / 告警闪烁)+ 镜态徽章(管理面)</td></tr>
<tr><td>五行生克健康环(系统健康=关系动力学)</td><td>wuxing</td><td class="mark-ok">✓ 已复刻为相生 / 相克指标对告警(管理面星盘卡;环状图 React 阶段评估)</td></tr>
<tr><td>眉批 depth 标尺注入预览</td><td>margin-notes</td><td style="color:var(--warn)">候选 —— 上下文镜深度维度(React 阶段与 Context Manifest 联动)</td></tr>
<tr><td>好奇萤火(亮度=期望值)与探索天平</td><td>drive</td><td class="mark-ok">✓ 萤火已融入内驱力维度(亮度=维度值);探索天平待天工阁预算增强时评估</td></tr>
<tr><td>周天四镜阶段链(评 / 演 / 候 / 藏 四面小铜镜)</td><td>evolution</td><td class="mark-ok">✓ 已复刻为大周天四镜:回 / 照 / 议 / 呈(镜生页,ADR-023 四阶段)</td></tr>
<tr><td>会话列表「今日 / 本周 / 更早」分组 + 彩点状态</td><td>chat-redesign</td><td class="mark-ok">✓ 已融入镜树会话列表(活跃铜绿 / 幻境秋香 / 待朱批朱砂)</td></tr>
<tr><td>「深虑 / 博闻」输入控制药丸 + 用户消息鎏金右边线</td><td>chat-redesign / copper-v2</td><td class="mark-ok">✓ 已融入镜树输入区与用户气泡</td></tr>
<tr><td>起居注时间河「卷」章分割</td><td>admin</td><td class="mark-ok">✓ 已融入管理面审计快照</td></tr>
<tr><td>太极阴阳平衡 gauge(三态)</td><td>cosmos</td><td class="mark-ok">✓ 已复刻为朱批治理阴阳平衡(司命台,阳亢 / 平衡 / 阴盛)</td></tr>
<tr><td>角色 card-banner 渐变横幅</td><td>character-detail</td><td class="mark-ok">✓ 已融入镜生详情页</td></tr>
<tr><td>思考块折叠卡(复刻 chat-redesign)</td><td>chat-redesign</td><td class="mark-ok">✓ 已融入(用户确认):思考摘要可折叠 + 深虑强度(低/中/高)——<b>契约候选</b>:正式实现前需在 content-blocks 契约登记 <span class="mono">thinking</span> 块类型与流式语义;摘要仅会话内可见,不入长期记录与诊断导出</td></tr>
</tbody>
</table></div>
</div>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+87
View File
@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>魂镜 · 登录</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
<symbol id="i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></symbol>
<symbol id="i-fingerprint" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 11a4 4 0 0 1 4 4v5"/><path d="M8 15a4 4 0 0 1 1-2.7"/><path d="M4 15a8 8 0 0 1 8-8 8 8 0 0 1 6.7 3.7"/><path d="M12 19v2"/><path d="M4.9 10.3A8 8 0 0 1 12 3a8 8 0 0 1 8 8c0 1-.1 2.3-.4 3"/></symbol>
</svg>
<div class="stage">
<div class="stage__bg" aria-hidden="true">
<div class="stage__rings">
<svg viewBox="0 0 800 800">
<defs><path id="ringpath" d="M400,400 m-300,0 a300,300 0 1,1 600,0 a300,300 0 1,1 -600,0"/></defs>
<circle class="ring-ink" cx="400" cy="400" r="392"/>
<circle class="ring-faint" cx="400" cy="400" r="336"/>
<g class="ring-slow"><circle class="ring-faint" cx="400" cy="400" r="272"/></g>
<circle class="ring-ink" cx="400" cy="400" r="206"/>
<circle class="ring-faint" cx="400" cy="400" r="158"/>
<g class="ring-slow" style="animation-direction:reverse"><circle class="ring-faint" cx="400" cy="400" r="104"/></g>
<text fill="var(--gilt-faint)" font-size="12" letter-spacing="6" style="font-family:var(--font-serif)">
<textPath href="#ringpath">镜中有镜 · 心外无心 · 汝来看此镜时 · 此镜亦照汝 · 铜镜本无字 · 照尽往来人 ·</textPath>
</text>
</svg>
</div>
<svg style="position:absolute;inset:0;width:100%;height:100%;opacity:.035" aria-hidden="true"><filter id="grain"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="2"/></filter><rect width="100%" height="100%" filter="url(#grain)"/></svg>
</div>
<header class="stage__topbar">
<span style="font-size:12px;color:var(--text-3)">静态原型 · 待审查</span>
<span class="spacer"></span>
<a href="index.html" style="font-size:12px;color:var(--text-3)">原型目录</a>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<main class="stage__main">
<div class="mirror-card">
<div class="mirror-card__brand">
<div class="brand__mark" aria-hidden="true"></div>
<div>
<div style="font-family:var(--font-serif);font-size:20px;font-weight:600;line-height:1.2">魂镜</div>
<div style="font-size:11px;color:var(--text-3)">Hunjing</div>
</div>
</div>
<div class="mirror-card__title">
<h1>登入魂镜</h1>
</div>
<p class="mirror-card__sub">长期 AI 角色、Agent 与创作社区平台</p>
<form onsubmit="return false">
<div class="field-group">
<label for="login-email">邮箱</label>
<input class="input" id="login-email" type="email" autocomplete="username" placeholder="you@example.com">
</div>
<div class="field-group">
<label for="login-pwd">密码</label>
<input class="input" id="login-pwd" type="password" autocomplete="current-password" placeholder="••••••••">
</div>
<a class="btn btn--primary" href="home.html" style="width:100%;margin-top:6px">登入</a>
</form>
<div class="login-alt">其他方式</div>
<div style="display:flex;gap:10px">
<a class="btn btn--outline btn--sm" href="#" style="flex:1" title="通行密钥(WebAuthn)"><svg><use href="#i-fingerprint"/></svg>通行密钥</a>
<a class="btn btn--outline btn--sm" href="#" style="flex:1" title="恢复钥匙(离线恢复码)"><svg><use href="#i-key"/></svg>恢复钥匙</a>
</div>
<p class="login-note">
Web 使用安全会话 Cookie(OAuth 2.1);桌面与移动客户端使用 Authorization Code + PKCE。<br>
凭据只经 Header 传递,不进入 URL、日志或页面。连续失败将触发限流与通知。
</p>
<div class="login-foot">
还没有账户?<a href="register.html">创建账户</a>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+140
View File
@@ -0,0 +1,140 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>云市 · 创作市场 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol><symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol><symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></symbol><symbol id="i-clock" viewBox="0 0 24 24" fill="none" sLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item is-active" aria-current="page" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">云市</span><span class="crumbs__sep">·</span><span class="here">创作市场</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>
购买的是版本与许可(市契),不是人格所有权。金额、费率与退款规则始终直白展示,文化名只作辅助。
</div>
<div style="display:flex;gap:12px;align-items:center;margin-bottom:16px;flex-wrap:wrap">
<div class="chips" style="margin-bottom:0">
<button class="chip is-on">全部</button>
<button class="chip">角色模板</button>
<button class="chip">世界设定</button>
<button class="chip">Live2D</button>
<button class="chip">3D 资产</button>
<button class="chip">音色包</button>
<button class="chip">插件</button>
</div>
<div class="searchbar" style="flex:1;min-width:200px">
<svg><use href="#i-search"/></svg>
<input type="search" placeholder="搜索商品 / 创作者" aria-label="搜索市场">
</div>
</div>
<section class="section">
<div class="asset-grid">
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#2f4858,#6a8caf 60%,#3b5a70)"><span class="glyph">雾</span><span class="asset-type">世界设定</span></div>
<div class="asset-body"><a class="asset-name" href="#license" style="color:inherit;text-decoration:underline;text-decoration-color:var(--gilt-dim);text-underline-offset:3px">雾中茶寮 · 世界合集</a><span class="asset-sub">雪袖的茶寮 · v3 · 售出 412</span>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:2px"><span class="price">¥28.00</span><span class="mem-type">可商用</span></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#4b3a2f,#a3815f 60%,#5d4a3a)"><span class="glyph">灯</span><span class="asset-type">角色模板</span></div>
<div class="asset-body"><span class="asset-name">照夜 · 掌灯人模板</span><span class="asset-sub">沉舟 · v2 · 售出 1,208</span>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:2px"><span class="price">¥12.00</span><span class="mem-type">个人用</span></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#233b37,#3f7a6e 60%,#2a4d47)"><span class="glyph">灵</span><span class="asset-type">Live2D</span></div>
<div class="asset-body"><span class="asset-name">守砚人 · 灵动模型</span><span class="asset-sub">云舟 · v3 · 售出 96</span>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:2px"><span class="price">¥45.00</span><span class="mem-type">可商用</span></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#37343f,#635d7a 60%,#443f52)"><span class="glyph">匣</span><span class="asset-type">音色包</span></div>
<div class="asset-body"><span class="asset-name">江南雨声 · 环境音色</span><span class="asset-sub">听澜 · v1 · 售出 340</span>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:2px"><span class="price price--free">免费</span><span class="mem-type">署名使用</span></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#33392e,#6e8455 60%,#414a37)"><span class="glyph">砚</span><span class="asset-type">3D 资产</span></div>
<div class="asset-body"><span class="asset-name">书斋器物 · VRM 附件包</span><span class="asset-sub">临江仙 · v2 · 售出 58</span>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:2px"><span class="price">¥18.00</span><span class="mem-type">个人用</span></div>
</div>
</div>
<div class="asset-card">
<div class="asset-thumb" style="background:linear-gradient(150deg,#413045,#8a6a95 60%,#503c55)"><span class="glyph">谱</span><span class="asset-type">插件</span></div>
<div class="asset-body"><span class="asset-name">曲谱排版 · 插件</span><span class="asset-sub">百工 · v1.4 · 售出 233</span>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:2px"><span class="price">¥6.00</span><span class="mem-type">个人用</span></div>
</div>
</div>
</div>
</section>
<section class="section" id="license">
<div class="section__head"><span class="cn">市契<span class="cn__plain">商品许可 · 以「雾中茶寮 v3」为例</span></span></div>
<div class="card">
<div class="table-wrap"><table class="data">
<thead><tr><th>项目</th><th>内容</th></tr></thead>
<tbody>
<tr><td>许可范围</td><td>商用(衍生作品可售,须保留署名;不可单独转售原始文件)</td></tr>
<tr><td>交付物</td><td>世界文档 42 页 · 地图 PSD · 场景音效 12 段 · 更新至 v3.x</td></tr>
<tr><td>退契规则</td><td>14 天内未下载可全额退款;下载后遵循许可,重大缺陷另行处理</td></tr>
<tr><td>价格构成</td><td>¥28.00 = 创作者分润 ¥23.80(85%)+ 平台费 ¥4.20(15%)· 税按地区另计</td></tr>
<tr><td>地区可售</td><td>当前地区可售 · 跨区价格与税务见结算页</td></tr>
</tbody>
</table></div>
<div style="display:flex;gap:10px;margin-top:14px;align-items:center">
<button class="btn btn--cinnabar">购买 · ¥28.00</button>
<button class="btn btn--outline">加入购物车</button>
<span style="font-size:12px;color:var(--text-3)">支付经 Idempotency-Key 幂等 · 重复点击不会重复扣款</span>
</div>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">结算<span class="cn__plain">创作者收益</span></span><a class="more" href="settings.html">结算设置 →</a></div>
<div style="display:flex;gap:28px;flex-wrap:wrap;align-items:center">
<div><div style="font-size:11.5px;color:var(--text-3)">可结算余额</div><div class="price" style="font-size:22px">¥1,240.50</div></div>
<div><div style="font-size:11.5px;color:var(--text-3)">本月分润</div><div class="price" style="font-size:22px">¥318.20</div></div>
<div><div style="font-size:11.5px;color:var(--text-3)">上次结算批次</div><div style="font-size:14px">09-10 · 已付 ¥986.30</div></div>
<div style="margin-left:auto;display:flex;gap:10px">
<button class="btn btn--outline btn--sm">下载对账单</button>
<button class="btn btn--primary btn--sm">申请结算</button>
</div>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">Ledger 不可变,修正写反向分录;每日自动对账(支付 / 订单 / Ledger / 结算四账核对)。</p>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+242
View File
@@ -0,0 +1,242 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>息壤 · 记忆与知识 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
<symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol>
<symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol>
<symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
<symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol>
<symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol>
<symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
<symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol>
<symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol>
<symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></symbol>
<symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol>
<symbol id="i-quote" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></symbol>
<symbol id="i-chevrons" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></symbol>
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航">
<div class="nav-group">
<div class="nav-group__title">常用</div>
<a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a>
<a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a>
<a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a>
<a class="nav-item is-active" href="memory.html" aria-current="page"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a>
<a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a>
</div>
<div class="nav-group">
<div class="nav-group__title">创造</div>
<a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a>
<a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a>
<a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a>
<a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a>
</div>
<div class="nav-group">
<div class="nav-group__title">世界</div>
<a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a>
<a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a>
</div>
<div class="nav-group">
<div class="nav-group__title">治理与账户</div>
<a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a>
<a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a>
<a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a>
</div>
</nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">息壤</span><span class="crumbs__sep">·</span><span class="here">记忆与知识</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div style="display:flex;gap:12px;align-items:center;margin-bottom:18px;flex-wrap:wrap">
<div class="chips" style="margin-bottom:0">
<button class="chip is-on">全部 · 132</button>
<button class="chip">青梧 · 98</button>
<button class="chip">雪袖 · 22</button>
<button class="chip">观澜 · 12</button>
</div>
<div class="searchbar" style="flex:1;min-width:220px">
<svg><use href="#i-search"/></svg>
<input type="search" placeholder="检索记忆:关键词 / 语义 / 结构化(授权预过滤后检索)" aria-label="检索记忆">
</div>
</div>
<div class="tabs" role="tablist" aria-label="记忆类型">
<button class="tab is-on" role="tab" aria-selected="true">候选审阅 <span class="cnt">3</span></button>
<button class="tab" role="tab" aria-selected="false">明忆 <span class="cnt">128</span></button>
<button class="tab" role="tab" aria-selected="false">重忆 <span class="cnt">4</span></button>
<button class="tab" role="tab" aria-selected="false">世谱 <span class="cnt">17</span></button>
<button class="tab" role="tab" aria-selected="false">玄络</button>
</div>
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
归真的约束:候选经你确认才会影响未来上下文;对话中的归真不自动写入所有长期记忆。境界内容只成世界知识候选,幻境内容须手动入忆并标注性质。
</div>
<!-- 候选 1:低敏感偏好 -->
<div class="card" style="margin-bottom:14px">
<div class="card__head">
<span class="mem-type">偏好 preference</span>
<span style="font-size:11.5px;color:var(--text-3)">来源 · 镜树对话 <span class="mono">m_91d0</span> · extractor v5</span>
<span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">置信 0.92 · 敏感度低 · 青梧</span>
</div>
<p style="font-size:14px" class="cn">「读书会隔周四 19:30 举行,每次 90 分钟。」</p>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
<button class="btn btn--primary btn--sm"><svg><use href="#i-check"/></svg>归真入忆</button>
<button class="btn btn--outline btn--sm">正影 · 纠正后入忆</button>
<button class="btn btn--ghost btn--sm">忽略</button>
<a class="btn btn--ghost btn--sm" href="conversation.html"><svg><use href="#i-quote"/></svg>查看来源上下文</a>
</div>
</div>
<!-- 候选 2:大周天来源 -->
<div class="card" style="margin-bottom:14px">
<div class="card__head">
<span class="mem-type">事实 fact</span>
<span class="badge badge--info"><span class="badge__dot"></span>大周天 · 第 7 轮提议</span>
<span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">置信 0.71 · 敏感度低 · 青梧</span>
</div>
<p style="font-size:14px" class="cn">「云舟在共读选篇时倾向短篇,超过 1500 字的篇目完成率明显下降。」</p>
<p style="font-size:12px;color:var(--text-3);margin-top:6px">由 42 次小周天完成信号的回顾阶段产生 · 复用同一审核流 · 可追溯本轮输入</p>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
<button class="btn btn--primary btn--sm"><svg><use href="#i-check"/></svg>归真入忆</button>
<button class="btn btn--outline btn--sm">正影 · 纠正后入忆</button>
<button class="btn btn--ghost btn--sm">忽略</button>
<a class="btn btn--ghost btn--sm" href="characters.html">查看大周天第 7 轮</a>
</div>
</div>
<!-- 候选 3:中敏感关系 → 重忆 -->
<div class="card" style="margin-bottom:14px;border-color:color-mix(in srgb,var(--warn) 35%,transparent)">
<div class="card__head">
<span class="mem-type">关系 relationship</span>
<span class="badge badge--warn"><span class="badge__dot"></span>建议作重忆保存</span>
<span style="margin-left:auto;font-size:11.5px;color:var(--text-3)">置信 0.68 · 敏感度中 · 青梧</span>
</div>
<p style="font-size:14px" class="cn">「云舟近期在工作日晚上更希望被倾听,而不是立刻获得建议。」</p>
<p style="font-size:12px;color:var(--text-3);margin-top:6px">重忆 · 敏感或高影响记忆:归真后仅在明确允许时进入上下文,可随时正影纠正或归档,版本不可变。</p>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
<button class="btn btn--primary btn--sm"><svg><use href="#i-check"/></svg>归真为重忆</button>
<button class="btn btn--outline btn--sm">正影 · 纠正后入忆</button>
<button class="btn btn--ghost btn--sm">忽略</button>
<a class="btn btn--ghost btn--sm" href="conversation.html"><svg><use href="#i-quote"/></svg>查看来源上下文</a>
</div>
</div>
<!-- 最近归真 -->
<section class="section" style="margin-top:28px">
<div class="section__head">
<span class="cn">最近归真<span class="cn__plain">已生效的明忆</span></span>
<span class="more" style="color:var(--text-3);cursor:default">全部 128 条 · 见下方列表</span>
</div>
<div class="card" style="padding:6px 18px">
<div class="list-row">
<span class="mem-type" style="flex:none">偏好</span>
<div class="list-row__body">
<div class="list-row__title cn">「云舟不喜长篇说教,笔记以要点为先。」</div>
<div class="list-row__sub">青梧 · v3(正影自 v2)· 来源:手动 · 有效期长期 · ETag 并发受控</div>
</div>
<button class="btn btn--ghost btn--sm">正影</button>
<button class="btn btn--ghost btn--sm">归档</button>
</div>
<div class="list-row">
<span class="mem-type" style="flex:none">事实</span>
<div class="list-row__body">
<div class="list-row__title cn">「3 月购入《陶庵梦忆》校注本(中华书局)。」</div>
<div class="list-row__sub">青梧 · v1 · 来源:对话 · 实录</div>
</div>
<button class="btn btn--ghost btn--sm">正影</button>
<button class="btn btn--ghost btn--sm">归档</button>
</div>
<div class="list-row">
<span class="mem-type" style="flex:none">事件</span>
<div class="list-row__body">
<div class="list-row__title cn">「读书会成立于 2024-03,成员 6 人。」</div>
<div class="list-row__sub">青梧 · v1 · 来源:世谱引用 · 删除将传播到向量与图投影</div>
</div>
<button class="btn btn--ghost btn--sm">正影</button>
<button class="btn btn--ghost btn--sm">归档</button>
</div>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">MemoryVersion 不可变:纠正与取代都写新版本,旧版本保留可查;删除建墓碑且传播到索引、缓存与派生数据,墓碑不保存原文。</p>
</section>
<!-- 玄络预览 -->
<section class="section">
<div class="section__head">
<span class="cn">玄络<span class="cn__plain">关系图谱投影 · 可重建</span></span>
<span class="more" style="color:var(--text-3);cursor:default">玄络 · 可交互图谱在 React 阶段实现</span>
</div>
<div class="card graph-box">
<svg viewBox="0 0 760 190" style="width:100%;height:auto" role="img" aria-label="玄络示意:云舟与青梧、读书会、张岱作品、雪袖的关系图">
<g font-size="12" font-family="inherit">
<line x1="380" y1="95" x2="200" y2="45" stroke="var(--line)" stroke-width="1.2"/>
<line x1="380" y1="95" x2="560" y2="45" stroke="var(--line)" stroke-width="1.2"/>
<line x1="380" y1="95" x2="180" y2="150" stroke="var(--line)" stroke-width="1.2"/>
<line x1="380" y1="95" x2="590" y2="150" stroke="var(--line)" stroke-width="1.2"/>
<line class="tent-edge" x1="560" y1="45" x2="590" y2="150" stroke="var(--cinnabar)" stroke-width="1.4" stroke-dasharray="5 5"/>
<circle cx="380" cy="95" r="26" fill="var(--accent)" fill-opacity=".18" stroke="var(--accent)"/>
<text x="380" y="99" text-anchor="middle" fill="var(--text-1)" font-weight="600">云舟</text>
<circle cx="200" cy="45" r="26" fill="var(--bg-raised)" stroke="var(--line)"/>
<text x="200" y="49" text-anchor="middle" fill="var(--text-1)">青梧</text>
<circle cx="560" cy="45" r="26" fill="var(--bg-raised)" stroke="var(--line)"/>
<text x="560" y="49" text-anchor="middle" fill="var(--text-1)">雪袖</text>
<circle cx="180" cy="150" r="30" fill="var(--bg-raised)" stroke="var(--line)"/>
<text x="180" y="147" text-anchor="middle" fill="var(--text-2)">读书会</text>
<text x="180" y="161" text-anchor="middle" fill="var(--text-2)" font-size="10">世谱</text>
<circle cx="590" cy="150" r="30" fill="var(--bg-raised)" stroke="var(--line)"/>
<text x="590" y="147" text-anchor="middle" fill="var(--text-2)">陶庵梦忆</text>
<text x="590" y="161" text-anchor="middle" fill="var(--text-2)" font-size="10">世界知识</text>
<text x="300" y="62" text-anchor="middle" fill="var(--text-2)" font-size="10.5">缘契 · 共读者 v4</text>
<text x="472" y="62" text-anchor="middle" fill="var(--text-2)" font-size="10.5">缘契 · 茶友 v1</text>
<text x="268" y="133" text-anchor="middle" fill="var(--text-2)" font-size="10.5">成员 · 发起人</text>
<text x="496" y="133" text-anchor="middle" fill="var(--text-2)" font-size="10.5">正在共读</text>
<text x="594" y="103" text-anchor="middle" fill="var(--cinnabar)" font-size="10">待验</text>
</g>
</svg>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">玄络是可重建投影:删除与权限变化随宿主重建;跨用户、跨角色召回恒为 0(fail-closed 已验证)。</p>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+124
View File
@@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>神识阁 · 模型与路由 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></symbol><symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol><symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol><symbol id="i-check" vieLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item is-active" aria-current="page" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">神识阁</span><span class="crumbs__sep">·</span><span class="here">模型与路由</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
神识是角色的可替换推理能力,不等于角色身份或意识证明。首个可见输出后不切换 Provider;引擎缺失时拒绝执行(fail closed)。
</div>
<section class="section">
<div class="section__head"><span class="cn">逻辑模型<span class="cn__plain">能力目录</span></span><span class="more" style="color:var(--text-3);cursor:default">全部 9 个 · 列表即目录</span></div>
<div class="grid-cards">
<div class="card" style="gap:8px;display:flex;flex-direction:column">
<div style="display:flex;align-items:center;gap:8px"><b style="font-family:var(--font-serif);font-size:15px">沉潜 · 深思</b><span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>可用</span></div>
<div style="font-size:12px;color:var(--text-3)">推理 · 128k 上下文 · 函数调用</div>
<div style="font-size:12.5px;color:var(--text-2)">¥12.00 / ¥48.00<small style="color:var(--text-3)"> 每 1M token(入/出)</small></div>
<div style="font-size:11.5px;color:var(--text-3)">价格快照 v2026-09-01 · 2 个 Endpoint · 故障转移已配置</div>
</div>
<div class="card" style="gap:8px;display:flex;flex-direction:column">
<div style="display:flex;align-items:center;gap:8px"><b style="font-family:var(--font-serif);font-size:15px">惊鸿 · 快速</b><span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>可用</span></div>
<div style="font-size:12px;color:var(--text-3)">轻量 · 32k · 视觉</div>
<div style="font-size:12.5px;color:var(--text-2)">¥1.60 / ¥6.40<small style="color:var(--text-3)"> 每 1M token</small></div>
<div style="font-size:11.5px;color:var(--text-3)">会话临时选择的默认候选</div>
</div>
<div class="card" style="gap:8px;display:flex;flex-direction:column">
<div style="display:flex;align-items:center;gap:8px"><b style="font-family:var(--font-serif);font-size:15px">丹青 · 图像</b><span class="badge badge--warn" style="margin-left:auto"><span class="badge__dot"></span>降级中</span></div>
<div style="font-size:12px;color:var(--text-3)">文生图 / 图生图 · 角色一致性</div>
<div style="font-size:12.5px;color:var(--text-2)">¥0.24<small style="color:var(--text-3)"> / 张</small></div>
<div style="font-size:11.5px;color:var(--text-3)">主 Endpoint 限流 · 已切备用 · 新任务排队 ≤ 2 分钟</div>
</div>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">路由继承链<span class="cn__plain">上层不可被下层覆盖</span></span></div>
<div style="display:flex;flex-direction:column;gap:0">
<div class="set-row"><div class="set-row__body"><div class="set-row__t">① 平台硬边界</div><div class="set-row__d">禁用模型与绝对上限,任何下层不可逾越</div></div><span class="badge badge--ok"><span class="badge__dot"></span>生效</span></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">② 平台目录</div><div class="set-row__d">默认逻辑模型与价格快照</div></div><span class="badge badge--ok"><span class="badge__dot"></span>生效</span></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">③ 用户偏好 / BYOK</div><div class="set-row__d">你的默认模型与自带密钥</div></div><span class="badge badge--ok"><span class="badge__dot"></span>生效</span></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">④ 角色覆写</div><div class="set-row__d">青梧未设置覆写</div></div><span class="badge badge--muted"><span class="badge__dot"></span>未设置</span></div>
<div class="set-row" style="background:var(--accent-soft);border-radius:var(--radius-sm);padding-left:12px;padding-right:12px"><div class="set-row__body"><div class="set-row__t">⑤ 会话临时选择 <span class="cn__plain">当前生效</span></div><div class="set-row__d">镜树「共读选篇」选择了 惊鸿 · 快速</div></div><span class="badge badge--info"><span class="badge__dot"></span>生效 <span class="mono">session</span></span></div>
</div>
</section>
<div class="two-col">
<section class="section card">
<div class="card__head"><span class="cn">BYOK<span class="cn__plain">自带模型密钥</span></span><button class="btn btn--outline btn--sm" style="margin-left:auto"><svg><use href="#i-plus"/></svg>添加密钥</button></div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-key"/></svg>
<div class="set-row__body"><div class="set-row__t mono">byok_a31f…(神思 Provider)</div><div class="set-row__d">2026-08-12 添加 · 存于系统 Keychain · 不写配置与日志</div></div>
<button class="btn btn--ghost btn--sm">轮换</button>
</div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-key"/></svg>
<div class="set-row__body"><div class="set-row__t mono">byok_7c02…(图像 Provider)</div><div class="set-row__d">2026-09-01 添加 · 仅丹青 · 图像使用</div></div>
<button class="btn btn--ghost btn--sm">轮换</button>
</div>
<p style="font-size:12px;color:var(--text-3);margin-top:10px">BYOK 调用按实际用量计费一次,不重复计费(ADR-021);凭据只经 Header 传递,永不转发至上游之外的日志。</p>
</section>
<section class="section card">
<div class="card__head"><span class="cn">本月用量<span class="cn__plain">9 月 1–15 日</span></span><span class="more" style="color:var(--text-3);cursor:default">明细 · Run 级观测见司命台诊断</span></div>
<div class="bars" role="img" aria-label="近七日 token 用量条形图:周三最高 8.4 万">
<div class="bars__col"><div class="bars__bar" style="height:42%"></div><span class="bars__label">一</span></div>
<div class="bars__col"><div class="bars__bar" style="height:55%"></div><span class="bars__label">二</span></div>
<div class="bars__col"><div class="bars__bar" style="height:96%"></div><span class="bars__label">三</span></div>
<div class="bars__col"><div class="bars__bar" style="height:48%"></div><span class="bars__label">四</span></div>
<div class="bars__col"><div class="bars__bar" style="height:30%"></div><span class="bars__label">五</span></div>
<div class="bars__col"><div class="bars__bar" style="height:22%"></div><span class="bars__label">六</span></div>
<div class="bars__col"><div class="bars__bar" style="height:36%"></div><span class="bars__label">日</span></div>
</div>
<div class="table-wrap" style="margin-top:14px"><table class="data">
<thead><tr><th>指标</th><th>数值</th></tr></thead>
<tbody>
<tr><td>Token(入/出)</td><td class="mono">486k / 212k</td></tr>
<tr><td>Prompt Cache 命中</td><td class="mono">31%</td></tr>
<tr><td>P95 TTFT</td><td class="mono">1.4s</td></tr>
<tr><td>费用</td><td class="mono">¥18.64</td></tr>
<tr><td>错误 / 重试</td><td class="mono">2 / 1</td></tr>
<tr><td>未知 usage 记录</td><td class="mono">null(不猜测)</td></tr>
</tbody>
</table></div>
</section>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+115
View File
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>百工坊 · 插件与能力 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></symbol><symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></symbol><symbol id="i-refresh" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></symbol><symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecapLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item is-active" aria-current="page" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">百工坊</span><span class="crumbs__sep">·</span><span class="here">插件与能力生态</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<div class="realm-banner" role="note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
购买不等于安装,安装不等于启用,启用不等于授予新增权限。安全内核(认证、审批、交易账、审计)不可插件化。
</div>
<section class="section card">
<div class="card__head">
<span class="cn">沙箱<span class="cn__plain">隔离运行时</span></span>
<span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>Worker 运转中 <span class="mono">sandbox:ok</span></span>
</div>
<p style="font-size:12.5px;color:var(--text-2)">本地代码插件运行于独立进程容器;声明式 / 远程插件默认受限。无符合策略的隔离后端时拒绝执行,不回退宿主(fail closed)。</p>
</section>
<section class="section">
<div class="section__head"><span class="cn">已安装<span class="cn__plain">4 个</span></span></div>
<div class="card" style="padding:6px 18px">
<div class="set-row">
<span class="badge badge--info" style="flex:none"><span class="badge__dot"></span>Core</span>
<div class="set-row__body"><div class="set-row__t">系统日历 <span class="cn__plain">bundled · 主进程</span></div><div class="set-row__d">仅 Core 插件可进主进程 · 权限:日历读写(每次请命)</div></div>
<span class="switch"><input type="checkbox" checked aria-label="系统日历已启用"><i></i></span>
</div>
<div class="set-row">
<span class="badge badge--muted" style="flex:none"><span class="badge__dot"></span>声明式</span>
<div class="set-row__body"><div class="set-row__t">笔记同步 <span class="cn__plain">受限运行时</span></div><div class="set-row__d">权限:笔记库 /读书会/ 读写 · 网络:无</div></div>
<span class="switch"><input type="checkbox" checked aria-label="笔记同步已启用"><i></i></span>
</div>
<div class="set-row" style="border-color:color-mix(in srgb,var(--warn) 35%,transparent)">
<span class="badge badge--warn" style="flex:none"><span class="badge__dot"></span>待更新</span>
<div class="set-row__body"><div class="set-row__t">性灵检索 <span class="cn__plain">远程 API</span></div><div class="set-row__d">v1.2 → v1.3 · 新增权限需重新确认(见下方)</div></div>
<button class="btn btn--outline btn--sm">查看变更</button>
</div>
<div class="set-row">
<span class="badge badge--muted" style="flex:none"><span class="badge__dot"></span>本地代码</span>
<div class="set-row__body"><div class="set-row__t">代码执行沙箱 <span class="cn__plain">独立容器 · 部署者信任</span></div><div class="set-row__d">Run 固定插件快照 · 活动运行不热替换</div></div>
<span class="switch"><input type="checkbox" checked aria-label="代码执行沙箱已启用"><i></i></span>
</div>
</div>
</section>
<section class="section">
<div class="section__head"><span class="cn">升级确认<span class="cn__plain">性灵检索 v1.3 新增权限</span></span></div>
<div class="block approval" style="max-width:640px">
<div class="approval__head">
<span class="approval__seal" aria-hidden="true">命</span>
<div>
<div class="approval__title"><span class="cn">请命</span><span class="cn__plain">升级新增权限必须重新确认</span></div>
<div style="font-size:11.5px;color:var(--text-3)">不确认则保留 v1.2,不影响现有使用</div>
</div>
</div>
<div class="approval__body">
<div class="fact"><span class="fact__k">现有 v1.2</span><span class="fact__v">读取笔记元数据 · 网络 api.xingling.app(只读)</span></div>
<div class="fact"><span class="fact__k">新增 v1.3</span><span class="fact__v" style="color:var(--cinnabar)">写入笔记内容 · 网络 api.xingling.app(读写)· 存储最近 20 条查询</span></div>
<div class="fact"><span class="fact__k">费用</span><span class="fact__v">免费插件 · 每次外部检索计费 ¥0.02</span></div>
<div class="fact"><span class="fact__k">撤架</span><span class="fact__v">安全撤架阻断新调用,保留历史审计</span></div>
</div>
<div class="approval__acts">
<button class="btn btn--cinnabar" aria-label="确认决定:允许更新">允许更新(朱批)</button>
<button class="btn btn--outline">保留 v1.2</button>
<span class="approval__note">工契 · 插件清单完整权限对比见详情。</span>
</div>
</div>
</section>
<section class="section">
<div class="section__head"><span class="cn">授契<span class="cn__plain">能力授予记录</span></span><a class="more" href="#">司命台完整审计 →</a></div>
<div class="card" style="padding:6px 18px">
<div class="list-row"><div class="list-row__body"><div class="list-row__title">日历写入 · 青梧</div><div class="list-row__sub">用途:共读事件 · 范围:仅指定事件 · 期限:单次 · 可随时撤销</div></div><button class="btn btn--ghost btn--sm">撤销</button></div>
<div class="list-row"><div class="list-row__body"><div class="list-row__title">笔记库读写 · 青梧</div><div class="list-row__sub">行契 · 30 天内同类免请命 · 剩余 12 天 · 超限后重新请命</div></div><button class="btn btn--ghost btn--sm">撤销</button></div>
<div class="list-row"><div class="list-row__body"><div class="list-row__title">网络检索 · 任务 Agent</div><div class="list-row__sub">用途:资料校对 · 白名单 3 域 · 期限:本 Run</div></div><span style="font-size:11.5px;color:var(--text-3)">随 Run 结束失效</span></div>
</div>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+108
View File
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>魂镜 · 创建账户</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true">
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
</svg>
<div class="stage">
<div class="stage__bg" aria-hidden="true">
<div class="stage__rings">
<svg viewBox="0 0 800 800">
<defs><path id="ringpath" d="M400,400 m-300,0 a300,300 0 1,1 600,0 a300,300 0 1,1 -600,0"/></defs>
<circle class="ring-ink" cx="400" cy="400" r="392"/>
<circle class="ring-faint" cx="400" cy="400" r="336"/>
<g class="ring-slow"><circle class="ring-faint" cx="400" cy="400" r="272"/></g>
<circle class="ring-ink" cx="400" cy="400" r="206"/>
<circle class="ring-faint" cx="400" cy="400" r="158"/>
<g class="ring-slow" style="animation-direction:reverse"><circle class="ring-faint" cx="400" cy="400" r="104"/></g>
<text fill="var(--gilt-faint)" font-size="12" letter-spacing="6" style="font-family:var(--font-serif)">
<textPath href="#ringpath">幻由心生 · 镜随心转 · 一念起 · 万镜生 · 且向镜中看 · 那个是谁 ·</textPath>
</text>
</svg>
</div>
<svg style="position:absolute;inset:0;width:100%;height:100%;opacity:.035" aria-hidden="true"><filter id="grain"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="2"/></filter><rect width="100%" height="100%" filter="url(#grain)"/></svg>
</div>
<header class="stage__topbar">
<a href="login.html" style="font-size:12px;color:var(--text-3)">← 返回登录</a>
<span style="font-size:12px;color:var(--text-3)">静态原型 · 待审查</span>
<span class="spacer"></span>
<a href="index.html" style="font-size:12px;color:var(--text-3)">原型目录</a>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<main class="stage__main">
<div class="mirror-card">
<div class="mirror-card__brand">
<div class="brand__mark" aria-hidden="true"></div>
<div>
<div style="font-family:var(--font-serif);font-size:20px;font-weight:600;line-height:1.2">魂镜</div>
<div style="font-size:11px;color:var(--text-3)">Hunjing</div>
</div>
</div>
<div class="mirror-card__title">
<h1>创建账户</h1>
</div>
<p class="mirror-card__sub">化名与法定身份分离核验;随时可导出、撤权或删除</p>
<form onsubmit="return false">
<div class="field-group">
<label for="reg-email">邮箱</label>
<input class="input" id="reg-email" type="email" autocomplete="email" placeholder="you@example.com">
</div>
<div class="field-group">
<label for="reg-code">验证码</label>
<div class="field-row">
<input class="input" id="reg-code" type="text" inputmode="numeric" placeholder="6 位验证码">
<button class="btn btn--outline" type="button">发送验证码</button>
</div>
</div>
<div class="field-group">
<label for="reg-pwd">密码</label>
<input class="input" id="reg-pwd" type="password" autocomplete="new-password" placeholder="至少 12 位,建议通行密钥">
</div>
<div class="field-group">
<label for="reg-pwd2">确认密码</label>
<input class="input" id="reg-pwd2" type="password" autocomplete="new-password" placeholder="再次输入">
</div>
<div style="margin:18px 0 16px;border-top:1px solid var(--line-soft);padding-top:16px">
<div style="font-size:12.5px;color:var(--text-2);margin-bottom:10px">开始前请知悉(数据权):</div>
<div class="check-row">
<input type="checkbox" id="ck-train" checked disabled aria-label="默认不用于模型训练">
<label class="t" for="ck-train"><b>默认不用于模型训练</b> —— 你的个人域数据默认不参与任何模型训练;自愿贡献是独立开关,可随时撤销。</label>
</div>
<div class="check-row">
<input type="checkbox" id="ck-export" checked disabled aria-label="可导出与删除">
<label class="t" for="ck-export"><b>可导出、可删除</b> —— 角色、记忆、对话与来源谱系随时可携导出;删除有冷静期并传播到全部派生数据。</label>
</div>
<div class="check-row">
<input type="checkbox" id="ck-vault" aria-label="了解私箧规则">
<label class="t" for="ck-vault"><b>私箧独立持钥</b> —— 加密私密区只有你能解开;平台无法恢复,钥匙丢失即永久不可读取。</label>
</div>
<div class="check-row">
<input type="checkbox" id="ck-terms" aria-label="同意服务条款">
<label class="t" for="ck-terms"><b>我已阅读并同意服务条款与社区规范</b> —— 角色不拥有生物意识或法律人格;社区有举报与治理机制。</label>
</div>
</div>
<a class="btn btn--cinnabar" href="login.html" style="width:100%">创建账户</a>
</form>
<p class="login-note">注册即创建你的镜域(个人数据域);角色、记忆与授权都归属镜域治理。</p>
<div class="login-foot">
已有账户?<a href="login.html">登入</a>
</div>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+121
View File
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>设置 · 账户与偏好 — 魂镜 Hunjing</title>
<link rel="stylesheet" href="assets/app.css?v=10">
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol><symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol><symbol id="i-branch" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></symbol><symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol><symbol id="i-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></symbol><symbol id="i-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol><symbol id="i-kanban" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h4v18H5z"/><path d="M10 3h4v12h-4z"/><path d="M15 3h4v8h-4z"/></symbol><symbol id="i-package" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></symbol><symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol><symbol id="i-route" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="19" r="3"/><circle cx="18" cy="5" r="3"/><path d="M12 19h4.5a3.5 3.5 0 0 0 0-7h-9a3.5 3.5 0 0 1 0-7H12"/></symbol><symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></symbol><symbol id="i-bag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></symbol><symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol><symbol id="i-monitor" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></symbol><symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></symbol><symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol><symbol id="i-trash" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></symbol><symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><paLine truncated
</svg>
<div class="app">
<aside class="sidebar">
<div class="brand">
<div class="brand__mark" aria-hidden="true"></div>
<div class="brand__text">
<div class="brand__name">魂镜</div>
<div class="brand__sub">Hunjing</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航"><div class="nav-group"><div class="nav-group__title">常用</div><a class="nav-item" href="home.html"><svg><use href="#i-home"/></svg><span class="cn">镜庭</span><span class="nav-plain">首页</span></a><a class="nav-item" href="conversation.html"><svg><use href="#i-branch"/></svg><span class="cn">镜树</span><span class="nav-plain">对话</span></a><a class="nav-item" href="characters.html"><svg><use href="#i-user"/></svg><span class="cn">镜生</span><span class="nav-plain">角色</span></a><a class="nav-item" href="memory.html"><svg><use href="#i-book"/></svg><span class="cn">息壤</span><span class="nav-plain">记忆</span></a><a class="nav-item" href="avatar.html"><svg><use href="#i-layers"/></svg><span class="cn">形仪</span><span class="nav-plain">形象</span></a></div><div class="nav-group"><div class="nav-group__title">创造</div><a class="nav-item" href="agent-workspace.html"><svg><use href="#i-kanban"/></svg><span class="cn">天工阁</span><span class="nav-plain">任务</span></a><a class="nav-item" href="plugins.html"><svg><use href="#i-package"/></svg><span class="cn">百工坊</span><span class="nav-plain">插件</span></a><a class="nav-item" href="collaboration.html"><svg><use href="#i-users"/></svg><span class="cn">同契台</span><span class="nav-plain">协作</span></a><a class="nav-item" href="model-gateway.html"><svg><use href="#i-route"/></svg><span class="cn">神识阁</span><span class="nav-plain">模型</span></a></div><div class="nav-group"><div class="nav-group__title">世界</div><a class="nav-item" href="community.html"><svg><use href="#i-globe"/></svg><span class="cn">雅集</span><span class="nav-plain">社区</span></a><a class="nav-item" href="marketplace.html"><svg><use href="#i-bag"/></svg><span class="cn">云市</span><span class="nav-plain">市场</span></a></div><div class="nav-group"><div class="nav-group__title">治理与账户</div><a class="nav-item" href="governance.html"><svg><use href="#i-shield"/></svg><span class="cn">司命台</span><span class="nav-plain">治理</span></a><a class="nav-item" href="devices.html"><svg><use href="#i-monitor"/></svg><span class="cn">器谱</span><span class="nav-plain">设备</span></a><a class="nav-item is-active" aria-current="page" href="settings.html"><svg><use href="#i-sliders"/></svg><span class="cn">设置</span><span class="nav-plain">账户</span></a></div></nav>
<div class="side-foot">
<button class="icon-btn side-fold-btn" data-sidebar-fold aria-label="收起侧栏" title="收起 / 展开侧栏(记忆偏好)"><svg><use href="#i-chevrons"/></svg></button>
<span class="avatar avatar--sm avatar--user">舟</span>
<span style="font-size:12.5px">云舟</span>
<button class="icon-btn" data-theme-toggle style="margin-left:auto" aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</div>
</aside>
<main class="main">
<header class="topbar">
<div class="crumbs"><span class="cn">设置</span><span class="crumbs__sep">·</span><span class="here">账户与偏好</span></div>
<div class="topbar__spacer"></div>
<span class="prototype-tag">静态原型 · 待审查</span>
<button class="icon-btn" data-theme-toggle aria-label="切换到月白主题"><svg><use href="#i-moon"/></svg></button>
</header>
<div class="content">
<section class="section card">
<div class="card__head"><span class="cn">账户</span></div>
<div style="display:flex;gap:14px;align-items:center;flex-wrap:wrap">
<span class="avatar avatar--lg avatar--user">舟</span>
<div>
<div style="font-size:16px" class="cn">云舟<span class="cn__plain">前台化名 · 与法定身份分离核验</span></div>
<div style="font-size:12.5px;color:var(--text-3);margin-top:2px">yunzhou@example.com · OAuth 2.1 · Web 安全会话</div>
</div>
<div style="margin-left:auto;display:flex;gap:10px">
<button class="btn btn--outline btn--sm">多凭证管理</button>
<button class="btn btn--outline btn--sm">修改化名</button>
</div>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">会话</span></div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-monitor"/></svg>
<div class="set-row__body"><div class="set-row__t">MacBook Pro · Tauri 桌面</div><div class="set-row__d">PKCE 登录 · 当前会话</div></div>
<span class="badge badge--ok" style="font-size:11px"><span class="badge__dot"></span>当前</span>
</div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-monitor"/></svg>
<div class="set-row__body"><div class="set-row__t">iPhone · 移动端</div><div class="set-row__d">PKCE 登录 · 昨天活跃</div></div>
<button class="btn btn--ghost btn--sm">撤销</button>
</div>
</section>
<section class="section card" style="border-color:color-mix(in srgb,var(--accent) 35%,transparent)">
<div class="card__head"><span class="cn">私箧<span class="cn__plain">独立持钥的加密私密区</span></span><span class="badge badge--ok" style="margin-left:auto"><span class="badge__dot"></span>已启用 <span class="mono">locked</span></span></div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-lock"/></svg>
<div class="set-row__body"><div class="set-row__t">平台仅存密文,不可恢复</div><div class="set-row__d">账户恢复、管理员与诊断均不可读取;恢复钥匙只在你手中,丢失即永久不可恢复。</div></div>
</div>
<div class="set-row">
<svg style="width:16px;height:16px;color:var(--text-3)"><use href="#i-file"/></svg>
<div class="set-row__body"><div class="set-row__t">使用量 12 MB</div><div class="set-row__d">日记 41 篇 · 手记 7 份 · 不参与任何推荐与训练</div></div>
<button class="btn btn--outline btn--sm">打开私箧</button>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">数据权<span class="cn__plain">访问 · 纠正 · 导出 · 撤权 · 删除</span></span></div>
<div class="set-row">
<div class="set-row__body">
<div class="set-row__t">模型训练贡献</div>
<div class="set-row__d">Personal Domain 数据默认不用于模型训练。自愿贡献需独立、明确、可随时撤销的同意。</div>
</div>
<span class="switch"><input type="checkbox" aria-label="自愿贡献训练数据(默认关闭)"><i></i></span>
</div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t">导出全部数据</div><div class="set-row__d">角色、设定、记忆、对话、资产与来源谱系 · 魂籍可携格式</div></div>
<button class="btn btn--outline btn--sm"><svg><use href="#i-download"/></svg>导出</button>
</div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t">撤回全部授权</div><div class="set-row__d">撤销插件、设备与第三方访问授权,数据保留</div></div>
<button class="btn btn--outline btn--sm">撤权</button>
</div>
<div class="set-row">
<div class="set-row__body"><div class="set-row__t" style="color:var(--cinnabar)">删除账户与全部数据</div><div class="set-row__d">不可逆 · 有冷静期 · 与角色死亡是两个独立操作 · 删除传播到索引与派生数据</div></div>
<button class="btn btn--danger btn--sm"><svg><use href="#i-trash"/></svg>删除</button>
</div>
</section>
<section class="section card">
<div class="card__head"><span class="cn">偏好</span></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">界面主题</div><div class="set-row__d">墨(深色)/ 月白(浅色)· 跟随系统</div></div><span class="switch"><input type="checkbox" aria-label="深色主题"><i></i></span></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">界面语言</div><div class="set-row__d">简体中文 · 文化名始终附带直白解释</div></div><button class="btn btn--ghost btn--sm">切换</button></div>
<div class="set-row"><div class="set-row__body"><div class="set-row__t">界面直白解释</div><div class="set-row__d">关闭后仅在高风险界面显示直白解释(不建议)</div></div><span class="switch"><input type="checkbox" checked aria-label="始终显示直白解释"><i></i></span></div>
</section>
<section class="section card danger-zone">
<div class="card__head"><span class="cn">封镜<span class="cn__plain">账户暂时停用 · 不是角色静修、归藏或死亡</span></span></div>
<p style="font-size:12.5px;color:var(--text-2);margin-bottom:12px">封镜期间登录被冻结,角色进入静修,数据完整保留;解封后可复照重开。</p>
<button class="btn btn--danger btn--sm">封镜账户</button>
</section>
</div>
</main>
</div>
<script src="assets/app.js?v=3"></script>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+9
View File
@@ -2,6 +2,15 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactStrictMode: true,
// 同源代理:前端 /api/* → 本地后端(避免开发档 CORS;生产由反向代理承担同职责)
async rewrites() {
return [
{
source: "/api/:path*",
destination: `${process.env.HUNJING_API_URL ?? "http://127.0.0.1:8000"}/:path*`,
},
];
},
};
export default nextConfig;
-1421
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -2,12 +2,15 @@
"name": "hunjing-frontend",
"version": "0.1.0",
"private": true,
"packageManager": "pnpm@11.21.0",
"scripts": {
"dev": "next dev",
"build": "next build",
"generate:api": "openapi-typescript ../openapi/openapi.yaml -o src/types/api.d.ts"
},
"dependencies": {
"@tanstack/react-query": "^5.103.1",
"lucide-react": "^1.46.0",
"next": "^15.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
+887
View File
@@ -0,0 +1,887 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@tanstack/react-query':
specifier: ^5.103.1
version: 5.103.1(react@19.2.8)
lucide-react:
specifier: ^1.46.0
version: 1.46.0(react@19.2.8)
next:
specifier: ^15.1.0
version: 15.5.25(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
react:
specifier: ^19.0.0
version: 19.2.8
react-dom:
specifier: ^19.0.0
version: 19.2.8(react@19.2.8)
devDependencies:
'@types/node':
specifier: ^22.10.0
version: 22.20.1
'@types/react':
specifier: ^19.0.0
version: 19.2.18
'@types/react-dom':
specifier: ^19.0.0
version: 19.2.7(@types/react@19.2.18)
openapi-typescript:
specifier: ^7.5.0
version: 7.13.0(typescript@5.9.3)
typescript:
specifier: ^5.7.0
version: 5.9.3
packages:
'@babel/code-frame@7.29.7':
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.29.7':
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
engines: {node: '>=6.9.0'}
'@emnapi/runtime@1.11.3':
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
'@img/colour@1.1.0':
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
'@img/sharp-darwin-arm64@0.35.4':
resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.35.4':
resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [darwin]
'@img/sharp-freebsd-wasm32@0.35.4':
resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==}
engines: {node: '>=20.9.0'}
os: [freebsd]
'@img/sharp-libvips-darwin-arm64@1.3.3':
resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.3.3':
resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.3.3':
resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.3.3':
resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.3.3':
resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.3.3':
resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.3.3':
resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.3.3':
resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.3.3':
resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.3.3':
resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.35.4':
resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.35.4':
resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==}
engines: {node: '>=20.9.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.35.4':
resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==}
engines: {node: '>=20.9.0'}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.35.4':
resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==}
engines: {node: '>=20.9.0'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.35.4':
resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==}
engines: {node: '>=20.9.0'}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.35.4':
resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.35.4':
resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.35.4':
resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.35.4':
resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==}
engines: {node: '>=20.9.0'}
'@img/sharp-webcontainers-wasm32@0.35.4':
resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==}
engines: {node: '>=20.9.0'}
cpu: [wasm32]
'@img/sharp-win32-arm64@0.35.4':
resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-ia32@0.35.4':
resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==}
engines: {node: ^20.9.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.35.4':
resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [win32]
'@next/env@15.5.25':
resolution: {integrity: sha512-42h1lLr07vl4gawALP1hsgRZjHB1xYa58JfUfHwr0f7jG/zhPakh5GHkADHXOC9ZxUvlQFOPIrp7s6qX4DezPQ==}
'@next/swc-darwin-arm64@15.5.25':
resolution: {integrity: sha512-w+RR0v/QuApnWEjRGm1z6gcObKwGMb5YPA7V3bzBEVSBpMFUXprer0tS27UxjUcEnqbhL7Zuzohej79B6rYmBg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.5.25':
resolution: {integrity: sha512-QiGGBUSakt8S1H4Lt9Ehsh6Ja87axiBnQQgysOObvCbI7iUfJnRGntF1P64S4/ijuHFnSB8KLsEddkY3nN26uw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.5.25':
resolution: {integrity: sha512-ehLos/66zo0d/mJCU5u96a/VDcr01aaUrX0o/i16UdInxz8qPTCDSxGtjk/Lps1sIr1RJFdiX3hxc0fxJo+cPA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@15.5.25':
resolution: {integrity: sha512-ZVMrqLiJ7DiChgmbkQwFtdhAnUkSH/4p7tB29QY+giATb0Q/XGHNRSKAb/B8XGDHRUaA67NepOW5W8u3ZRJBAA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@15.5.25':
resolution: {integrity: sha512-UOewtDGkTMJTiODrEdeLZ50yGb59xCZSriNpXkfPMxRRgwDkGc7i8mLWqV5076wEdb+Ca/XN7MhJyM3CupyNyQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@15.5.25':
resolution: {integrity: sha512-UBHwA8AhkCZgtRfU1aJpunuAJe/6gZv6jDESQe4p5MjTb5V0YEeJBWCdNqx15Vj3x+5jmauRfeMJSjfQj9HGFQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@15.5.25':
resolution: {integrity: sha512-QcFcPRr16djk5IqK5+e8O80eZfgWzIvVBXfitIq0tQ/uc+eyfdoZ0NmKc0cnbIJyfVwREapKuG97YcxWA9gcpA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.5.25':
resolution: {integrity: sha512-zREeykps3ndWr9egJgvJKqVkkDuaw6Zrrg23cYBos0ygydFkAWYU4+PaPVwXzP1eAYQJe53ShSK45iDM529BOg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@redocly/ajv@8.11.2':
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
'@redocly/config@0.22.0':
resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==}
'@redocly/openapi-core@1.34.19':
resolution: {integrity: sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
'@tanstack/query-core@5.103.1':
resolution: {integrity: sha512-rms8HqTGp6zA00dM+cUQ2eBcgzNJefuu5CAMB37i/6MiGT1zulPOytCFu2a0qjLqVR2n1jENPj9woqFQNuCzWA==}
'@tanstack/react-query@5.103.1':
resolution: {integrity: sha512-rmAPPApNEK17VXRJhtE1rja53n23VV5w30C0saCgJhhX+EpdUnVqMGV/s5nnzV43X75KTHKzuoiuxSzGTBIkag==}
peerDependencies:
react: ^18 || ^19
'@types/node@22.20.1':
resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==}
'@types/react-dom@19.2.7':
resolution: {integrity: sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==}
peerDependencies:
'@types/react': ^19.2.0
'@types/react@19.2.18':
resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==}
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@2.1.4:
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
caniuse-lite@1.0.30001810:
resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==}
change-case@5.4.4:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
colorette@1.4.0:
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
index-to-position@1.2.0:
resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==}
engines: {node: '>=18'}
js-levenshtein@1.1.6:
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
engines: {node: '>=0.10.0'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@4.3.1:
resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==}
hasBin: true
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
lucide-react@1.46.0:
resolution: {integrity: sha512-Bv+FZXgZPrxc/NCl1e7JJVQFLdiCxYgxNVhqoV7X0p6I8ADJo8DxBnK1auH0fZz4AmqOJ3jgneL4f1i8LJQRAA==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
minimatch@5.1.9:
resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==}
engines: {node: '>=10'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanoid@3.3.18:
resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
next@15.5.25:
resolution: {integrity: sha512-OMWNulIIqKM2ykvC2qMjIt0IoavB4UB2SCs4iXJ6z6847FvyH8jBmBWcvrF5iuhTu8Przh20Fo/aoszIdqx4PA==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.51.1
babel-plugin-react-compiler: '*'
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
'@playwright/test':
optional: true
babel-plugin-react-compiler:
optional: true
sass:
optional: true
openapi-typescript@7.13.0:
resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==}
hasBin: true
peerDependencies:
typescript: ^5.x
parse-json@8.3.0:
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
engines: {node: '>=18'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
postcss@8.4.31:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
react-dom@19.2.8:
resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==}
peerDependencies:
react: ^19.2.8
react@19.2.8:
resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==}
engines: {node: '>=0.10.0'}
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
semver@7.8.5:
resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
engines: {node: '>=10'}
hasBin: true
sharp@0.35.4:
resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==}
engines: {node: '>=20.9.0'}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
styled-jsx@5.1.6:
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
engines: {node: '>= 12.0.0'}
peerDependencies:
'@babel/core': '*'
babel-plugin-macros: '*'
react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
peerDependenciesMeta:
'@babel/core':
optional: true
babel-plugin-macros:
optional: true
supports-color@10.2.2:
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
engines: {node: '>=18'}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
type-fest@4.41.0:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
uri-js-replace@1.0.1:
resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
yaml-ast-parser@0.0.43:
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
snapshots:
'@babel/code-frame@7.29.7':
dependencies:
'@babel/helper-validator-identifier': 7.29.7
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/helper-validator-identifier@7.29.7': {}
'@emnapi/runtime@1.11.3':
dependencies:
tslib: 2.8.1
optional: true
'@img/colour@1.1.0':
optional: true
'@img/sharp-darwin-arm64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.3.3
optional: true
'@img/sharp-darwin-x64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.3.3
optional: true
'@img/sharp-freebsd-wasm32@0.35.4':
dependencies:
'@img/sharp-wasm32': 0.35.4
optional: true
'@img/sharp-libvips-darwin-arm64@1.3.3':
optional: true
'@img/sharp-libvips-darwin-x64@1.3.3':
optional: true
'@img/sharp-libvips-linux-arm64@1.3.3':
optional: true
'@img/sharp-libvips-linux-arm@1.3.3':
optional: true
'@img/sharp-libvips-linux-ppc64@1.3.3':
optional: true
'@img/sharp-libvips-linux-riscv64@1.3.3':
optional: true
'@img/sharp-libvips-linux-s390x@1.3.3':
optional: true
'@img/sharp-libvips-linux-x64@1.3.3':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.3.3':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.3.3':
optional: true
'@img/sharp-linux-arm64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.3.3
optional: true
'@img/sharp-linux-arm@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.3.3
optional: true
'@img/sharp-linux-ppc64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.3.3
optional: true
'@img/sharp-linux-riscv64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.3.3
optional: true
'@img/sharp-linux-s390x@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.3.3
optional: true
'@img/sharp-linux-x64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.3.3
optional: true
'@img/sharp-linuxmusl-arm64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.3.3
optional: true
'@img/sharp-linuxmusl-x64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.3.3
optional: true
'@img/sharp-wasm32@0.35.4':
dependencies:
'@emnapi/runtime': 1.11.3
optional: true
'@img/sharp-webcontainers-wasm32@0.35.4':
dependencies:
'@img/sharp-wasm32': 0.35.4
optional: true
'@img/sharp-win32-arm64@0.35.4':
optional: true
'@img/sharp-win32-ia32@0.35.4':
optional: true
'@img/sharp-win32-x64@0.35.4':
optional: true
'@next/env@15.5.25': {}
'@next/swc-darwin-arm64@15.5.25':
optional: true
'@next/swc-darwin-x64@15.5.25':
optional: true
'@next/swc-linux-arm64-gnu@15.5.25':
optional: true
'@next/swc-linux-arm64-musl@15.5.25':
optional: true
'@next/swc-linux-x64-gnu@15.5.25':
optional: true
'@next/swc-linux-x64-musl@15.5.25':
optional: true
'@next/swc-win32-arm64-msvc@15.5.25':
optional: true
'@next/swc-win32-x64-msvc@15.5.25':
optional: true
'@redocly/ajv@8.11.2':
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js-replace: 1.0.1
'@redocly/config@0.22.0': {}
'@redocly/openapi-core@1.34.19(supports-color@10.2.2)':
dependencies:
'@redocly/ajv': 8.11.2
'@redocly/config': 0.22.0
colorette: 1.4.0
https-proxy-agent: 7.0.6(supports-color@10.2.2)
js-levenshtein: 1.1.6
js-yaml: 4.3.1
minimatch: 5.1.9
pluralize: 8.0.0
yaml-ast-parser: 0.0.43
transitivePeerDependencies:
- supports-color
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
'@tanstack/query-core@5.103.1': {}
'@tanstack/react-query@5.103.1(react@19.2.8)':
dependencies:
'@tanstack/query-core': 5.103.1
react: 19.2.8
'@types/node@22.20.1':
dependencies:
undici-types: 6.21.0
'@types/react-dom@19.2.7(@types/react@19.2.18)':
dependencies:
'@types/react': 19.2.18
'@types/react@19.2.18':
dependencies:
csstype: 3.2.3
agent-base@7.1.4: {}
ansi-colors@4.1.3: {}
argparse@2.0.1: {}
balanced-match@1.0.2: {}
brace-expansion@2.1.4:
dependencies:
balanced-match: 1.0.2
caniuse-lite@1.0.30001810: {}
change-case@5.4.4: {}
client-only@0.0.1: {}
colorette@1.4.0: {}
csstype@3.2.3: {}
debug@4.4.3(supports-color@10.2.2):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.2.2
detect-libc@2.1.2:
optional: true
fast-deep-equal@3.1.3: {}
https-proxy-agent@7.0.6(supports-color@10.2.2):
dependencies:
agent-base: 7.1.4
debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies:
- supports-color
index-to-position@1.2.0: {}
js-levenshtein@1.1.6: {}
js-tokens@4.0.0: {}
js-yaml@4.3.1:
dependencies:
argparse: 2.0.1
json-schema-traverse@1.0.0: {}
lucide-react@1.46.0(react@19.2.8):
dependencies:
react: 19.2.8
minimatch@5.1.9:
dependencies:
brace-expansion: 2.1.4
ms@2.1.3: {}
nanoid@3.3.18: {}
next@15.5.25(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
dependencies:
'@next/env': 15.5.25
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001810
postcss: 8.4.31
react: 19.2.8
react-dom: 19.2.8(react@19.2.8)
styled-jsx: 5.1.6(react@19.2.8)
optionalDependencies:
'@next/swc-darwin-arm64': 15.5.25
'@next/swc-darwin-x64': 15.5.25
'@next/swc-linux-arm64-gnu': 15.5.25
'@next/swc-linux-arm64-musl': 15.5.25
'@next/swc-linux-x64-gnu': 15.5.25
'@next/swc-linux-x64-musl': 15.5.25
'@next/swc-win32-arm64-msvc': 15.5.25
'@next/swc-win32-x64-msvc': 15.5.25
sharp: 0.35.4(@types/node@22.20.1)
transitivePeerDependencies:
- '@babel/core'
- '@types/node'
- babel-plugin-macros
openapi-typescript@7.13.0(typescript@5.9.3):
dependencies:
'@redocly/openapi-core': 1.34.19(supports-color@10.2.2)
ansi-colors: 4.1.3
change-case: 5.4.4
parse-json: 8.3.0
supports-color: 10.2.2
typescript: 5.9.3
yargs-parser: 21.1.1
parse-json@8.3.0:
dependencies:
'@babel/code-frame': 7.29.7
index-to-position: 1.2.0
type-fest: 4.41.0
picocolors@1.1.1: {}
pluralize@8.0.0: {}
postcss@8.4.31:
dependencies:
nanoid: 3.3.18
picocolors: 1.1.1
source-map-js: 1.2.1
react-dom@19.2.8(react@19.2.8):
dependencies:
react: 19.2.8
scheduler: 0.27.0
react@19.2.8: {}
require-from-string@2.0.2: {}
scheduler@0.27.0: {}
semver@7.8.5:
optional: true
sharp@0.35.4(@types/node@22.20.1):
dependencies:
'@img/colour': 1.1.0
detect-libc: 2.1.2
semver: 7.8.5
optionalDependencies:
'@img/sharp-darwin-arm64': 0.35.4
'@img/sharp-darwin-x64': 0.35.4
'@img/sharp-freebsd-wasm32': 0.35.4
'@img/sharp-libvips-darwin-arm64': 1.3.3
'@img/sharp-libvips-darwin-x64': 1.3.3
'@img/sharp-libvips-linux-arm': 1.3.3
'@img/sharp-libvips-linux-arm64': 1.3.3
'@img/sharp-libvips-linux-ppc64': 1.3.3
'@img/sharp-libvips-linux-riscv64': 1.3.3
'@img/sharp-libvips-linux-s390x': 1.3.3
'@img/sharp-libvips-linux-x64': 1.3.3
'@img/sharp-libvips-linuxmusl-arm64': 1.3.3
'@img/sharp-libvips-linuxmusl-x64': 1.3.3
'@img/sharp-linux-arm': 0.35.4
'@img/sharp-linux-arm64': 0.35.4
'@img/sharp-linux-ppc64': 0.35.4
'@img/sharp-linux-riscv64': 0.35.4
'@img/sharp-linux-s390x': 0.35.4
'@img/sharp-linux-x64': 0.35.4
'@img/sharp-linuxmusl-arm64': 0.35.4
'@img/sharp-linuxmusl-x64': 0.35.4
'@img/sharp-webcontainers-wasm32': 0.35.4
'@img/sharp-win32-arm64': 0.35.4
'@img/sharp-win32-ia32': 0.35.4
'@img/sharp-win32-x64': 0.35.4
'@types/node': 22.20.1
optional: true
source-map-js@1.2.1: {}
styled-jsx@5.1.6(react@19.2.8):
dependencies:
client-only: 0.0.1
react: 19.2.8
supports-color@10.2.2: {}
tslib@2.8.1: {}
type-fest@4.41.0: {}
typescript@5.9.3: {}
undici-types@6.21.0: {}
uri-js-replace@1.0.1: {}
yaml-ast-parser@0.0.43: {}
yargs-parser@21.1.1: {}
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { RunsPanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="天工阁" here="Agent 任务工作台" />} />
<div className="content">
<RunsPanel />
</div>
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { AvatarPanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="形仪" here="形象与资产" />} />
<div className="content">
<AvatarPanel />
</div>
</>
);
}
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { CharactersPagePanel } from "@/components/characters/CharactersPagePanel";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="镜生" here="角色" />} />
<div className="content content--wide">
<CharactersPagePanel />
</div>
</>
);
}
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { CollaborationPanel } from "@/components/collaboration/CollaborationPanel";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="同契台" here="协作空间" />} />
<div className="content">
<CollaborationPanel />
</div>
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { CommunityPanel } from "@/components/community/CommunityPanel";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="雅集" here="社区广场" />} />
<div className="content">
<CommunityPanel />
</div>
</>
);
}
@@ -0,0 +1,22 @@
"use client";
import { ThemeButton } from "@/lib/theme";
import { ConversationPanel } from "@/components/conversation/ConversationPanel";
export default function Page() {
return (
<>
<header className="topbar">
<div className="crumbs">
<span className="cn">镜树</span>
<span className="crumbs__sep">/</span>
<span className="here">分支对话</span>
</div>
<div className="topbar__spacer" />
<span className="prototype-tag">真实接口 · 联调中</span>
<ThemeButton />
</header>
<ConversationPanel />
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { DevicesPanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="器谱" here="设备与感知" />} />
<div className="content">
<DevicesPanel />
</div>
</>
);
}
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { GovernancePanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="司命台" here="安全与治理" />} />
<div className="content">
<GovernancePanel />
</div>
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { HomePanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="镜庭" here="个人首页" />} />
<div className="content">
<HomePanel />
</div>
</>
);
}
+6
View File
@@ -0,0 +1,6 @@
import { AppShell } from "@/components/shell/AppShell";
/** 用户侧 14 域 + 设计说明共用应用外壳 */
export default function AppLayout({ children }: { children: React.ReactNode }) {
return <AppShell>{children}</AppShell>;
}
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { MarketplacePanel } from "@/components/marketplace/MarketplacePanel";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="云市" here="创作市场" />} />
<div className="content">
<MarketplacePanel />
</div>
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { MemoryPanel } from "@/components/memory/MemoryPanel";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="息壤" here="记忆与知识" />} />
<div className="content">
<MemoryPanel />
</div>
</>
);
}
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { GatewayPanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="神识阁" here="模型与路由" />} />
<div className="content">
<GatewayPanel />
</div>
</>
);
}
+187
View File
@@ -0,0 +1,187 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="设计说明" here="React 转写版" />} />
<div className="content doc">
<div className="doc-hero">
<h1>魂镜 <span className="ver">原型 v0.2</span></h1>
<p>魂镜 Web 界面静态 HTML 原型(v0.1 第一批方向已确认,v0.2 增补第二批共 15 个界面),供审查视觉方向、布局结构与文化语言的界面落地方式。全部确认后按此实现为 React 组件;未在本文档登记的视觉细节以本页令牌为准。</p>
</div>
<section className="doc-sec">
<h2>原型页面</h2>
<p className="sub">第一批(已确认):镜庭、镜生、镜树;第二批:息壤、形仪、天工阁、神识阁、百工坊、雅集、同契台、云市、器谱、司命台、设置,及运营者/自托管使用的管理面与认证页。全站链接已疏通:跨页指向对应页面、页内为锚点;logo 单击回本目录,350ms 内连点 5 次触发「镜启」彩蛋(承接 main 分支旧版设计);侧栏可折叠并记忆偏好。</p>
<div className="page-nav">
<a className="card" href="/home"><span className="cn">镜庭<span className="cn__plain">个人首页</span></span><span className="d">全局框架、角色入口、任务与待审事项</span><span className="p">home.html</span></a>
<a className="card" href="/conversation"><span className="cn">镜树<span className="cn__plain">分支对话</span></span><span className="d">ContentBlock、请命朱批、界域、候答、流式芽体、分支树</span><span className="p">conversation.html</span></a>
<a className="card" href="/characters"><span className="cn">镜生<span className="cn__plain">角色身份</span></span><span className="d">道体、缘契、内驱力、大周天、谱系、生命周期</span><span className="p">characters.html</span></a>
<a className="card" href="/memory"><span className="cn">息壤<span className="cn__plain">记忆与知识</span></span><span className="d">候选审阅、正影纠正、明忆版本、玄络投影</span><span className="p">memory.html</span></a>
<a className="card" href="avatar.html"><span className="cn">形仪<span className="cn__plain">形象与资产</span></span><span className="d">真形锚、主仪化仪、声音、Live2D 与 3D、易相历史</span><span className="p">avatar.html</span></a>
<a className="card" href="/agent-workspace"><span className="cn">天工阁<span className="cn__plain">Agent 任务</span></span><span className="d">工筹计划、预算、分工使、轨迹、验收</span><span className="p">agent-workspace.html</span></a>
<a className="card" href="/model-gateway"><span className="cn">神识阁<span className="cn__plain">模型与路由</span></span><span className="d">逻辑模型、路由继承链、BYOK、用量观测</span><span className="p">model-gateway.html</span></a>
<a className="card" href="/plugins"><span className="cn">百工坊<span className="cn__plain">插件生态</span></span><span className="d">沙箱、已安装、升级权限确认、授契记录</span><span className="p">plugins.html</span></a>
<a className="card" href="community.html"><span className="cn">雅集<span className="cn__plain">社区广场</span></span><span className="d">动态流、角色谱、话题、推荐解释与重置</span><span className="p">community.html</span></a>
<a className="card" href="collaboration.html"><span className="cn">同契台<span className="cn__plain">协作空间</span></span><span className="d">分支版本、审稿合卷、成员权限矩阵、活动</span><span className="p">collaboration.html</span></a>
<a className="card" href="marketplace.html"><span className="cn">云市<span className="cn__plain">创作市场</span></span><span className="d">商品、市契许可、退契规则、分润结算</span><span className="p">marketplace.html</span></a>
<a className="card" href="devices.html"><span className="cn">器谱<span className="cn__plain">设备与感知</span></span><span className="d">形器登记、授契、通感会话、直白权限请求</span><span className="p">devices.html</span></a>
<a className="card" href="/governance"><span className="cn">司命台<span className="cn__plain">安全与治理</span></span><span className="d">治理队列、诊断双重授权、审计、安全概览</span><span className="p">governance.html</span></a>
<a className="card" href="admin.html"><span className="cn">管理面<span className="cn__plain">运营与部署后台</span></span><span className="d">司议队列、支持工单与脱敏支持包、权限管理(RBAC / 双人授权)、部署健康(灵台双形态)、SLO</span><span className="p">admin.html</span></a>
<a className="card" href="login.html"><span className="cn">登录<span className="cn__plain">铜镜鉴心</span></span><span className="d">铭文环镜背、扫描亮带、会话 Cookie / PKCE 说明、通行密钥与恢复钥匙</span><span className="p">login.html</span></a>
<a className="card" href="register.html"><span className="cn">注册<span className="cn__plain">创建账户</span></span><span className="d">邮箱验证码、数据权告知(默认不训练 / 可导出删除 / 私箧持钥)、条款确认</span><span className="p">register.html</span></a>
<a className="card" href="/settings"><span className="cn">设置<span className="cn__plain">账户与偏好</span></span><span className="d">会话、私箧、数据权、删除、封镜</span><span className="p">settings.html</span></a>
</div>
</section>
<section className="doc-sec">
<h2>设计立场</h2>
<p className="sub">四条约束所有页面的决定,审查时优先核对这四条是否成立。</p>
<div className="card" style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
<div><b className="cn">一 · 双层文字系统</b><span style={{ color: "var(--text-2)" }}> —— 文化名用衬线宋体,直白解释用无衬线小字,两种字体本身就是"文化名 · 直白解释"两层结构的可视化;风险、金额、权限、删除等后果始终以直白语言为主,文化名只作辅助。</span></div>
<div><b className="cn">二 · 朱砂只属于朱批</b><span style={{ color: "var(--text-2)" }}> —— 朱砂色是"用户确认决定"的专用语义色:请命卡的允许按钮、危险动作入口使用它;普通操作一律铜镜青。颜色即权限语言。</span></div>
<div><b className="cn">三 · 墨与月白</b><span style={{ color: "var(--text-2)" }}> —— 深色为墨(青调黑,非纯黑),浅色为月白(青灰白,非暖米);铜镜青为主色,取铜镜铜绿之意,不用蓝紫系默认色。</span></div>
<div><b className="cn">四 · 状态永远三段式</b><span style={{ color: "var(--text-2)" }}> —— 状态徽章固定为「文化状态 · 直白状态 + 稳定工程值」,例如「运转 · 运行中 <span className="mono">running</span>」;错误与交易状态绝不只显示文化名。</span></div>
</div>
</section>
<section className="doc-sec">
<h2>色彩令牌</h2>
<p className="sub">右下角可切换主题;以下色板随主题联动。转 React 时这些令牌进入 Tailwind 配置 / CSS 变量。</p>
<div className="swatches">
<div className="swatch"><div className="swatch__c" style={{ background: "var(--bg)", borderBottom: "1px solid var(--line-soft)" }}></div><div className="swatch__m"><b>底 · 墨 / 月白</b>表面层级 0<span className="mono">--bg</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--bg-raised)" }}></div><div className="swatch__m"><b>浮面</b>卡片与气泡<span className="mono">--bg-raised</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--accent)" }}></div><div className="swatch__m"><b>铜镜青</b>主色 · 选中/链接/运转<span className="mono">--accent</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--cinnabar)" }}></div><div className="swatch__m"><b>朱砂</b>朱批 · 确认与危险<span className="mono">--cinnabar</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--ok)" }}></div><div className="swatch__m"><b>已成</b>完成态<span className="mono">--ok</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--warn)" }}></div><div className="swatch__m"><b>秋香</b>整备 / 幻境<span className="mono">--warn</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--err)" }}></div><div className="swatch__m"><b>受阻</b>失败态<span className="mono">--err</span></div></div>
<div className="swatch"><div className="swatch__c" style={{ background: "var(--text-1)" }}></div><div className="swatch__m"><b>正文</b>一级文字<span className="mono">--text-1</span></div></div>
</div>
</section>
<section className="doc-sec">
<h2>字体规范</h2>
<p className="sub">不引入外部字体文件,使用系统中文字体栈;转 React 时同栈迁移。</p>
<div className="card type-spec">
<div className="type-spec__row"><span className="type-spec__label">文化名 · 宋体衬线</span><span className="type-spec__sample cn" style={{ fontSize: "22px", fontWeight: "600" }}>镜树 · 分支对话</span></div>
<div className="type-spec__row"><span className="type-spec__label">界面正文 · 无衬线</span><span className="type-spec__sample" style={{ fontSize: "14px" }}>正文 14px / 行高 1.65,辅助文字 12.5px,元信息 11.5px</span></div>
<div className="type-spec__row"><span className="type-spec__label">稳定值 · 等宽</span><span className="type-spec__sample mono" style={{ fontSize: "13px" }}>running · m_01h8xq… · block_id: blk_7f3a</span></div>
</div>
</section>
<section className="doc-sec">
<h2>状态徽章全集</h2>
<p className="sub">对应 docs/product/cultural-language.md 第 10 节;徽章三段:文化状态 · 直白状态 · 稳定值。</p>
<div className="card">
<div className="table-wrap"><table className="data badge-table">
<thead><tr><th>徽章</th><th>稳定值</th><th>使用场景</th></tr></thead>
<tbody>
<tr><td><span className="badge badge--muted"><span className="badge__dot"></span>待启 · 排队中 <span className="mono">queued</span></span></td><td className="mono">queued</td><td>Run / 媒体任务排队</td></tr>
<tr><td><span className="badge badge--info"><span className="badge__dot"></span>运转 · 运行中 <span className="mono">running</span></span></td><td className="mono">running</td><td>Run 执行中</td></tr>
<tr><td><span className="badge badge--warn"><span className="badge__dot"></span>请命中 · 等待确认 <span className="mono">waiting_approval</span></span></td><td className="mono">waiting_approval</td><td>等待朱批</td></tr>
<tr><td><span className="badge badge--ok"><span className="badge__dot"></span>已成 · 已完成 <span className="mono">completed</span></span></td><td className="mono">completed</td><td>Run / 任务完成</td></tr>
<tr><td><span className="badge badge--err"><span className="badge__dot"></span>受阻 · 失败 <span className="mono">failed</span></span></td><td className="mono">failed</td><td>失败(附原因与重试)</td></tr>
<tr><td><span className="badge badge--muted"><span className="badge__dot"></span>已止 · 已取消 <span className="mono">cancelled</span></span></td><td className="mono">cancelled</td><td>用户取消</td></tr>
<tr><td><span className="badge badge--muted"><span className="badge__dot"></span>守一 · 安静待命 <span className="mono">idle</span></span></td><td className="mono">idle</td><td>角色无运行</td></tr>
<tr><td><span className="badge badge--warn"><span className="badge__dot"></span>整备 · 正在处理 <span className="mono">processing</span></span></td><td className="mono">processing</td><td>媒体 / 资源处理</td></tr>
<tr><td><span className="badge badge--warn"><span className="badge__dot"></span>待审 · 等待审核 <span className="mono">pending_review</span></span></td><td className="mono">pending_review</td><td>社区 / 市场审核</td></tr>
<tr><td><span className="badge badge--ok"><span className="badge__dot"></span>已刊 · 已发布 <span className="mono">published</span></span></td><td className="mono">published</td><td>社区内容</td></tr>
</tbody>
</table></div>
</div>
</section>
<section className="doc-sec">
<h2>组件样例</h2>
<p className="sub">按钮语义:铜镜青为主操作,朱砂仅用于朱批(用户确认决定)与危险操作,描边为次操作。</p>
<div className="card demo-block">
<div className="demo-row">
<button className="btn btn--primary">开始对话</button>
<button className="btn btn--cinnabar">允许(朱批)</button>
<button className="btn btn--outline">查看谱系</button>
<button className="btn btn--ghost">取消</button>
<button className="btn btn--danger">删除角色数据</button>
<button className="btn btn--outline btn--sm">续生</button>
</div>
<div className="demo-row">
<span className="cn" style={{ fontSize: "16px" }}>请命<span className="cn__plain">请求你的确认</span></span>
<span className="cn" style={{ fontSize: "16px" }}>息壤<span className="cn__plain">角色记忆</span></span>
<span className="cn" style={{ fontSize: "16px" }}>幻境<span className="cn__plain">临时试演 · 不入正史</span></span>
</div>
</div>
<div className="card demo-block">
<div className="block__head" style={{ borderRadius: "var(--radius) var(--radius) 0 0" }}><span className="lang">python</span><span className="fname">reading_progress.py</span><span className="acts"><button className="icon-btn" style={{ width: "26px", height: "26px" }} aria-label="复制源码"><svg style={{ width: "13px", height: "13px" }} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></span></div>
<pre className="code"><span className="c"># 汇总共读接龙进度(示意)</span>
<span className="k">from</span> pathlib <span className="k">import</span> Path
<span className="k">def</span> <span className="f">summarize</span>(records: <span className="k">list</span>[<span className="k">dict</span>]) -&gt; <span className="k">dict</span>:
done = [r <span className="k">for</span> r <span className="k">in</span> records <span className="k">if</span> r[<span className="s">"finished"</span>]]
<span className="k">return</span> {'{'}
<span className="s">"total"</span>: <span className="f">len</span>(records),
<span className="s">"finished"</span>: <span className="f">len</span>(done),
<span className="s">"pending"</span>: [r[<span className="s">"name"</span>] <span className="k">for</span> r <span className="k">in</span> records <span className="k">if</span> <span className="k">not</span> r[<span className="s">"finished"</span>]],
{'}'}</pre>
</div>
<div className="card demo-block">
<div className="math">注意力权重 = softmax( QK<sup>T</sup> / √d<sub>k</sub> ) · V
<span className="math__fallback">fallback:注意力权重等于 Q、K 转置乘积经缩放与归一化后对 V 的加权求和</span>
</div>
</div>
</section>
<section className="doc-sec">
<h2>审查重点</h2>
<p className="sub">第一批(色彩、字体、镜树/镜生/镜庭结构)已确认;本轮重点核对第二批页面,反馈时按页名 + 位置描述即可。</p>
<div className="card" style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
<div>1. 息壤:候选审阅的「归真入忆 / 正影纠正 / 忽略」操作,以及大周天来源、重忆提示的呈现是否可理解。</div>
<div>2. 天工阁:工筹步骤、预算、分工使、轨迹的信息层级是否符合你对 Run 治理的预期。</div>
<div>3. 神识阁 / 百工坊:路由继承链与「升级新增权限需重新确认」的展示是否清晰,朱批语义是否仍然专一。</div>
<div>4. 云市:金额、分润、退契规则是否足够直白(文化名没有掩盖交易后果)。</div>
<div>5. 器谱 / 司命台 / 设置:直白权限句、双重授权、私箧与删除等高风险项的措辞与视觉重量。</div>
<div>6. 雅集 / 同契台:社区帖卡与协作审稿流的信息密度是否合适。</div>
</div>
</section>
<section className="doc-sec">
<h2>旧版传承与候选</h2>
<p className="sub">已系统挖掘 main 分支旧版 25 页设计原型(铜镜鉴心体系),按「取手法、不照搬旧概念名」原则融入——六爻 / 九枢 / 三魂等概念 1.0 已由小周天、灵台六模块、内驱力等替代,仅传承其可视化手法。</p>
<div className="card">
<div className="table-wrap"><table className="data">
<thead><tr><th>旧版想法</th><th>来源页</th><th>状态</th></tr></thead>
<tbody>
<tr><td>鎏金发丝线三档令牌</td><td>tokens.css</td><td className="mark-ok">✓ 已融入全站(分组标题 / 金标竖条 / 品牌外环 / 时间线)</td></tr>
<tr><td>logo 五连击「镜启」诗签彩蛋</td><td>_theme.js</td><td className="mark-ok">✓ 已融入(修好首次单击即跳转的缺陷)</td></tr>
<tr><td>铜镜登录页(铭文环 / 铜面噪点 / 扫描亮带)</td><td>copper-mirror-login</td><td className="mark-ok">✓ 已融入登录 / 注册页</td></tr>
<tr><td>极光铜晕底 + 卡片 hover 光扫</td><td>_base.css / rune-engine</td><td className="mark-ok">✓ 已融入全站(极淡,不干扰阅读)</td></tr>
<tr><td>日晷时辰盘 + 节气 + 相伴日文案</td><td>time</td><td className="mark-ok">✓ 已融入镜庭 hero(巳时 · 白露 · 第 918 日)</td></tr>
<tr><td>朱批卡风险分级(RISK L2)</td><td>chat-redesign</td><td className="mark-ok">✓ 已融入镜树请命卡</td></tr>
<tr><td>「呈镜」发送按钮命名(旧版已确立血脉)</td><td>chat-redesign</td><td className="mark-ok">✓ 已融入镜树输入区(aria 仍读「发送」)</td></tr>
<tr><td>运行阶段爻象三态(done / active / pending)</td><td>chat / evolution</td><td className="mark-ok">✓ 已融入天工阁工筹(active 步骤鎏金光晕)</td></tr>
<tr><td>河图置信度图谱(置信→线宽,假设→朱漆虚线待验)</td><td>world</td><td className="mark-ok">✓ 已融入息壤玄络(待验边 + 标注)</td></tr>
<tr><td>息壤浮现(本次回应用了哪些记忆)</td><td>chat / copper-v2</td><td className="mark-ok">✓ 对应 1.0 上下文镜(镜树右栏,同源 ContextSnapshot)</td></tr>
<tr><td>九枢星盘「活的仪器」+ 铜镜四态健康词汇</td><td>admin / copper-v2</td><td className="mark-ok">✓ 已复刻为灵台六枢星盘(太一居中 + 五枢环绕 + 呼吸刻度 / 慢旋 / 告警闪烁)+ 镜态徽章(管理面)</td></tr>
<tr><td>五行生克健康环(系统健康=关系动力学)</td><td>wuxing</td><td className="mark-ok">✓ 已复刻为相生 / 相克指标对告警(管理面星盘卡;环状图 React 阶段评估)</td></tr>
<tr><td>眉批 depth 标尺注入预览</td><td>margin-notes</td><td style={{ color: "var(--warn)" }}>候选 —— 上下文镜深度维度(React 阶段与 Context Manifest 联动)</td></tr>
<tr><td>好奇萤火(亮度=期望值)与探索天平</td><td>drive</td><td className="mark-ok">✓ 萤火已融入内驱力维度(亮度=维度值);探索天平待天工阁预算增强时评估</td></tr>
<tr><td>周天四镜阶段链(评 / 演 / 候 / 藏 四面小铜镜)</td><td>evolution</td><td className="mark-ok">✓ 已复刻为大周天四镜:回 / 照 / 议 / 呈(镜生页,ADR-023 四阶段)</td></tr>
<tr><td>会话列表「今日 / 本周 / 更早」分组 + 彩点状态</td><td>chat-redesign</td><td className="mark-ok">✓ 已融入镜树会话列表(活跃铜绿 / 幻境秋香 / 待朱批朱砂)</td></tr>
<tr><td>「深虑 / 博闻」输入控制药丸 + 用户消息鎏金右边线</td><td>chat-redesign / copper-v2</td><td className="mark-ok">✓ 已融入镜树输入区与用户气泡</td></tr>
<tr><td>起居注时间河「卷」章分割</td><td>admin</td><td className="mark-ok">✓ 已融入管理面审计快照</td></tr>
<tr><td>太极阴阳平衡 gauge(三态)</td><td>cosmos</td><td className="mark-ok">✓ 已复刻为朱批治理阴阳平衡(司命台,阳亢 / 平衡 / 阴盛)</td></tr>
<tr><td>角色 card-banner 渐变横幅</td><td>character-detail</td><td className="mark-ok">✓ 已融入镜生详情页</td></tr>
<tr><td>思考块折叠卡(复刻 chat-redesign)</td><td>chat-redesign</td><td className="mark-ok">✓ 已融入(用户确认):思考摘要可折叠 + 深虑强度(低/中/高)——<b>契约候选</b>:正式实现前需在 content-blocks 契约登记 <span className="mono">thinking</span> 块类型与流式语义;摘要仅会话内可见,不入长期记录与诊断导出</td></tr>
</tbody>
</table></div>
</div>
</section>
</div>
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { PluginsPanel } from "@/components/plugins/PluginsPanel";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="百工坊" here="插件与能力生态" />} />
<div className="content">
<PluginsPanel />
</div>
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { SettingsPanel } from "@/components/panels";
export default function Page() {
return (
<>
<Topbar crumbs={<Crumbs cn="设置" here="账户与偏好" />} />
<div className="content">
<SettingsPanel />
</div>
</>
);
}
+6
View File
@@ -0,0 +1,6 @@
import { AdminShell } from "@/components/shell/AdminShell";
/** 管理面独立外壳(运营 / 部署 / 观测) */
export default function AdminLayout({ children }: { children: React.ReactNode }) {
return <AdminShell>{children}</AdminShell>;
}
+42
View File
@@ -0,0 +1,42 @@
"use client";
import { Topbar } from "@/components/shell/Topbar";
import { Crumbs } from "@/components/ui";
import { AdminPanel } from "@/components/admin/AdminPanel";
import { readSession } from "@/lib/api";
import { useEffect, useState } from "react";
export default function Page() {
const [session, setSession] = useState<{ role: string; email: string } | null>(null);
useEffect(() => {
// 会话身份含 localStorage,须在挂载后读取(避免 SSR/客户端不一致)
setSession(readSession());
}, []);
const isAdmin = session?.role === "admin";
return (
<>
<Topbar
crumbs={<Crumbs cn="司命台" here="管理面 · 运营与部署" />}
extra={
session === null ? null : isAdmin ? (
<span className="badge badge--warn">
<span className="badge__dot" />
管理员 · {session.email || "dev"} <span className="mono">role: admin</span>
</span>
) : (
<span className="badge badge--muted">
<span className="badge__dot" />
普通用户 <span className="mono">role: user</span> · 治理队列需管理员
</span>
)
}
/>
<div className="content">
<div className="realm-banner" role="note">
管理面边界:管理员不可读取私箧与用户明文内容;敏感操作需双人授权并全部写入审计(开发档单管理员,双人授权待运营流程接入)。
</div>
<AdminPanel />
</div>
</>
);
}
File diff suppressed because it is too large. Load diff
+15 -2
View File
@@ -1,14 +1,27 @@
import type { Metadata } from "next";
import "./globals.css";
import { ThemeProvider } from "@/lib/theme";
import { QueryProvider } from "@/lib/query";
export const metadata: Metadata = {
title: "魂镜 Hunjing",
description: "长期 AI 角色、Agent 与创作社区平台",
};
/** 主题初始化(防闪):与 ThemeProvider 的 localStorage 键一致 */
const themeInit = `try{var t=localStorage.getItem("hunjing-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t)}catch(e){}`;
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh-CN">
<body>{children}</body>
<html lang="zh-CN" data-theme="dark" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: themeInit }} />
</head>
<body>
<ThemeProvider>
<QueryProvider>{children}</QueryProvider>
</ThemeProvider>
</body>
</html>
);
}
+139
View File
@@ -0,0 +1,139 @@
"use client";
import { Fingerprint, Key } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { api, ApiError, storeCsrf, storeSession } from "@/lib/api";
import { ThemeButton } from "@/lib/theme";
/** 登录页(StageShell 铜镜鉴心)——开发档:后端仅校验邮箱即签发会话 */
export default function LoginPage() {
const router = useRouter();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [pending, setPending] = useState(false);
const [error, setError] = useState<string | null>(null);
async function submit(e: React.FormEvent) {
e.preventDefault();
setPending(true);
setError(null);
try {
const session = await api<{ csrf_token: string; user_id: string; role: string; email: string }>(
"/v1/auth/sessions",
{ method: "POST", json: { email } },
);
storeCsrf(session.csrf_token);
storeSession(session);
router.push("/home");
} catch (err) {
setError(err instanceof ApiError ? err.message : "登录失败,请稍后再试");
} finally {
setPending(false);
}
}
return (
<div className="stage">
<div className="stage__bg" aria-hidden="true">
<div className="stage__rings">
<svg viewBox="0 0 800 800">
<defs>
<path id="ringpath" d="M400,400 m-300,0 a300,300 0 1,1 600,0 a300,300 0 1,1 -600,0" />
</defs>
<circle className="ring-ink" cx="400" cy="400" r="392" />
<circle className="ring-faint" cx="400" cy="400" r="336" />
<g className="ring-slow"><circle className="ring-faint" cx="400" cy="400" r="272" /></g>
<circle className="ring-ink" cx="400" cy="400" r="206" />
<circle className="ring-faint" cx="400" cy="400" r="158" />
<g className="ring-slow" style={{ animationDirection: "reverse" }}>
<circle className="ring-faint" cx="400" cy="400" r="104" />
</g>
<text fill="var(--gilt-faint)" fontSize="12" letterSpacing="6" style={{ fontFamily: "var(--font-serif)" }}>
<textPath href="#ringpath">镜中有镜 · 心外无心 · 汝来看此镜时 · 此镜亦照汝 · 铜镜本无字 · 照尽往来人 ·</textPath>
</text>
</svg>
</div>
<svg style={{ position: "absolute", inset: 0, width: "100%", height: "100%", opacity: 0.035 }} aria-hidden="true">
<filter id="grain"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="2" /></filter>
<rect width="100%" height="100%" filter="url(#grain)" />
</svg>
</div>
<header className="stage__topbar">
<span style={{ fontSize: 12, color: "var(--text-3)" }}>联调中 · 开发档登录</span>
<span className="spacer" />
<a href="/" style={{ fontSize: 12, color: "var(--text-3)" }}>原型目录</a>
<ThemeButton />
</header>
<main className="stage__main">
<div className="mirror-card">
<div className="mirror-card__brand">
<div className="brand__mark" aria-hidden="true" />
<div>
<div style={{ fontFamily: "var(--font-serif)", fontSize: 20, fontWeight: 600, lineHeight: 1.2 }}>魂镜</div>
<div style={{ fontSize: 11, color: "var(--text-3)" }}>Hunjing</div>
</div>
</div>
<div className="mirror-card__title"><h1>登入魂镜</h1></div>
<p className="mirror-card__sub">长期 AI 角色、Agent 与创作社区平台</p>
<form onSubmit={submit}>
<div className="field-group">
<label htmlFor="login-email">邮箱</label>
<input
className="input"
id="login-email"
type="email"
autoComplete="username"
placeholder="you@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div className="field-group">
<label htmlFor="login-pwd">密码</label>
<input
className="input"
id="login-pwd"
type="password"
autoComplete="current-password"
placeholder="开发档暂不校验,仅邮箱即可"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
{error && (
<p style={{ fontSize: 12.5, color: "var(--err)", marginBottom: 10 }} role="alert">
{error}
</p>
)}
<button
type="submit"
className="btn btn--primary"
style={{ width: "100%", marginTop: 6 }}
disabled={pending}
>
{pending ? "登入中…" : "登入"}
</button>
</form>
<div className="login-alt">其他方式</div>
<div style={{ display: "flex", gap: 10 }}>
<a className="btn btn--outline btn--sm" href="#" style={{ flex: 1 }} title="通行密钥(WebAuthn,待接入)"><Fingerprint />通行密钥</a>
<a className="btn btn--outline btn--sm" href="#" style={{ flex: 1 }} title="恢复钥匙(待接入)"><Key />恢复钥匙</a>
</div>
<p className="login-note">
开发档:输入邮箱即可登入(自动建户)。正式档为 OAuth 2.1 安全会话;凭据只经 Header 传递。
</p>
<div className="login-foot">
还没有账户?<a href="/register">创建账户</a>
</div>
</div>
</main>
</div>
);
}
-8
View File
@@ -1,8 +0,0 @@
export default function Home() {
return (
<main style={{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
<h1>魂镜 Hunjing</h1>
<p>工程骨架已建立;界面随 WP-03/WP-10 工作包实现。</p>
</main>
);
}
+133
View File
@@ -0,0 +1,133 @@
"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { api, ApiError, storeCsrf } from "@/lib/api";
import { ThemeButton } from "@/lib/theme";
/** 注册页(StageShell 铜镜鉴心) */
export default function RegisterPage() {
const router = useRouter();
const [pending, setPending] = useState(false);
const [error, setError] = useState<string | null>(null);
async function submit(e: React.FormEvent) {
e.preventDefault();
const email = (document.getElementById("reg-email") as HTMLInputElement).value;
if (!email) return;
setPending(true);
setError(null);
try {
const session = await api<{ csrf_token: string }>("/v1/auth/sessions", { method: "POST", json: { email } });
storeCsrf(session.csrf_token);
router.push("/home");
} catch (err) {
setError(err instanceof ApiError ? err.message : "创建失败");
} finally {
setPending(false);
}
}
return (
<div className="stage">
<div className="stage__bg" aria-hidden="true">
<div className="stage__rings">
<svg viewBox="0 0 800 800">
<defs>
<path id="ringpath" d="M400,400 m-300,0 a300,300 0 1,1 600,0 a300,300 0 1,1 -600,0" />
</defs>
<circle className="ring-ink" cx="400" cy="400" r="392" />
<circle className="ring-faint" cx="400" cy="400" r="336" />
<g className="ring-slow"><circle className="ring-faint" cx="400" cy="400" r="272" /></g>
<circle className="ring-ink" cx="400" cy="400" r="206" />
<circle className="ring-faint" cx="400" cy="400" r="158" />
<g className="ring-slow" style={{ animationDirection: "reverse" }}>
<circle className="ring-faint" cx="400" cy="400" r="104" />
</g>
<text fill="var(--gilt-faint)" fontSize="12" letterSpacing="6" style={{ fontFamily: "var(--font-serif)" }}>
<textPath href="#ringpath">幻由心生 · 镜随心转 · 一念起 · 万镜生 · 且向镜中看 · 那个是谁 ·</textPath>
</text>
</svg>
</div>
<svg style={{ position: "absolute", inset: 0, width: "100%", height: "100%", opacity: 0.035 }} aria-hidden="true">
<filter id="grain"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="2" /></filter>
<rect width="100%" height="100%" filter="url(#grain)" />
</svg>
</div>
<header className="stage__topbar">
<a href="/login" style={{ fontSize: 12, color: "var(--text-3)" }}>← 返回登录</a>
<span style={{ fontSize: 12, color: "var(--text-3)" }}>React 转写 · 待联调</span>
<span className="spacer" />
<a href="/" style={{ fontSize: 12, color: "var(--text-3)" }}>原型目录</a>
<ThemeButton />
</header>
<main className="stage__main">
<div className="mirror-card">
<div className="mirror-card__brand">
<div className="brand__mark" aria-hidden="true" />
<div>
<div style={{ fontFamily: "var(--font-serif)", fontSize: 20, fontWeight: 600, lineHeight: 1.2 }}>魂镜</div>
<div style={{ fontSize: 11, color: "var(--text-3)" }}>Hunjing</div>
</div>
</div>
<div className="mirror-card__title"><h1>创建账户</h1></div>
<p className="mirror-card__sub">化名与法定身份分离核验;随时可导出、撤权或删除</p>
<form onSubmit={submit}>
<div className="field-group">
<label htmlFor="reg-email">邮箱</label>
<input className="input" id="reg-email" type="email" autoComplete="email" placeholder="you@example.com" />
</div>
<div className="field-group">
<label htmlFor="reg-code">验证码</label>
<div className="field-row">
<input className="input" id="reg-code" type="text" inputMode="numeric" placeholder="6 位验证码" />
<button className="btn btn--outline" type="button">发送验证码</button>
</div>
</div>
<div className="field-group">
<label htmlFor="reg-pwd">密码</label>
<input className="input" id="reg-pwd" type="password" autoComplete="new-password" placeholder="至少 12 位,建议通行密钥" />
</div>
<div className="field-group">
<label htmlFor="reg-pwd2">确认密码</label>
<input className="input" id="reg-pwd2" type="password" autoComplete="new-password" placeholder="再次输入" />
</div>
<div style={{ margin: "18px 0 16px", borderTop: "1px solid var(--line-soft)", paddingTop: 16 }}>
<div style={{ fontSize: 12.5, color: "var(--text-2)", marginBottom: 10 }}>开始前请知悉(数据权):</div>
<div className="check-row">
<input type="checkbox" id="ck-train" defaultChecked disabled aria-label="默认不用于模型训练" />
<label className="t" htmlFor="ck-train"><b>默认不用于模型训练</b> —— 你的个人域数据默认不参与任何模型训练;自愿贡献是独立开关,可随时撤销。</label>
</div>
<div className="check-row">
<input type="checkbox" id="ck-export" defaultChecked disabled aria-label="可导出与删除" />
<label className="t" htmlFor="ck-export"><b>可导出、可删除</b> —— 角色、记忆、对话与来源谱系随时可携导出;删除有冷静期并传播到全部派生数据。</label>
</div>
<div className="check-row">
<input type="checkbox" id="ck-vault" aria-label="了解私箧规则" />
<label className="t" htmlFor="ck-vault"><b>私箧独立持钥</b> —— 加密私密区只有你能解开;平台无法恢复,钥匙丢失即永久不可读取。</label>
</div>
<div className="check-row">
<input type="checkbox" id="ck-terms" aria-label="同意服务条款" />
<label className="t" htmlFor="ck-terms"><b>我已阅读并同意服务条款与社区规范</b> —— 角色不拥有生物意识或法律人格;社区有举报与治理机制。</label>
</div>
</div>
<button type="submit" className="btn btn--cinnabar" style={{ width: "100%" }} disabled={pending}>
{pending ? "创建中…" : "创建账户"}
</button>
{error && <p style={{ fontSize: 12.5, color: "var(--err)", marginTop: 10 }} role="alert">{error}</p>}
</form>
<p className="login-note">注册即创建你的镜域(个人数据域);角色、记忆与授权都归属镜域治理。</p>
<div className="login-foot">
已有账户?<a href="/login">登入</a>
</div>
</div>
</main>
</div>
);
}
@@ -0,0 +1,120 @@
"use client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { api, ApiError, type ToolCall } from "@/lib/api";
import { PanelState } from "@/lib/queries";
interface ModerationCase {
case_id: string;
status: string;
report: { target_type?: string; target_id?: string; reason?: string } | null;
action_taken: string | null;
created_at: string;
}
/** 管理面数据区:治理案件(真实裁决)+ 运行观测 + 支持包 */
export function AdminPanel() {
const qc = useQueryClient();
const isAdminDenied = false; // 由下方 PanelState 呈现 403
const cases = useQuery({
queryKey: ["moderation-cases"],
queryFn: () => api<{ data: ModerationCase[] }>("/v1/admin/moderation-cases"),
});
const metrics = useQuery({
queryKey: ["metrics"],
queryFn: async () => {
const res = await fetch("/api/v1/metrics");
if (!res.ok) throw new Error(`metrics ${res.status}`);
return res.text();
},
});
const [bundle, setBundle] = useState<string | null>(null);
const resolve = useMutation({
mutationFn: ({ id, action }: { id: string; action: string }) =>
api(`/v1/admin/moderation-cases/${id}/resolutions`, {
method: "POST",
json: { action },
}),
onSuccess: () => qc.invalidateQueries({ queryKey: ["moderation-cases"] }),
});
return (
<div>
<section className="section">
<div className="section__head">
<span className="cn">司议<span className="cn__plain">治理案件 · 真实裁决</span></span>
</div>
<PanelState pending={cases.isPending} error={cases.error} onRetry={() => cases.refetch()} empty={(cases.data?.data ?? []).length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{(cases.data?.data ?? []).map((c) => (
<div className="list-row" key={c.case_id}>
<div className="list-row__body">
<div className="list-row__title mono">{c.case_id.slice(-10)} · {c.report?.target_type ?? ""}</div>
<div className="list-row__sub">
{c.report?.reason ?? ""} · {new Date(c.created_at).toLocaleString("zh-CN", { hour12: false })}
{c.action_taken && ` · 已处置:${c.action_taken}`}
</div>
</div>
{c.status === "pending" ? (
<div style={{ display: "flex", gap: 6 }}>
<button
className="btn btn--danger btn--sm"
disabled={resolve.isPending}
onClick={() => resolve.mutate({ id: c.case_id, action: "content_removed" })}
>
移除内容
</button>
<button
className="btn btn--outline btn--sm"
disabled={resolve.isPending}
onClick={() => resolve.mutate({ id: c.case_id, action: "dismissed" })}
>
驳回
</button>
</div>
) : (
<span className="badge badge--muted" style={{ fontSize: 11 }}>
<span className="badge__dot" />{c.status}
</span>
)}
</div>
))}
</div>
</PanelState>
</section>
<section className="section card">
<div className="card__head">
<span className="cn">运行观测<span className="cn__plain">/v1/metrics</span></span>
</div>
<PanelState pending={metrics.isPending} error={metrics.error} onRetry={() => metrics.refetch()}>
<pre className="code" style={{ maxHeight: 220, overflow: "auto" }}>{metrics.data}</pre>
</PanelState>
</section>
<section className="section card">
<div className="card__head">
<span className="cn">支持包<span className="cn__plain">脱敏诊断导出</span></span>
</div>
<p style={{ fontSize: 12.5, color: "var(--text-2)", marginBottom: 12 }}>
支持包为脱敏导出:不包含消息明文、私箧、隐藏推理、密钥与签名 URL。
</p>
<button
className="btn btn--cinnabar btn--sm"
onClick={async () => {
try {
const r = await api<{ id?: string }>("/v1/support/bundles", { method: "POST", json: {} });
setBundle(`已生成支持包 ${r.id ?? ""}`);
} catch (err) {
setBundle(err instanceof ApiError ? err.message : "生成失败");
}
}}
>
生成支持包
</button>
{bundle && <p style={{ fontSize: 12.5, color: "var(--text-3)", marginTop: 10 }}>{bundle}</p>}
</section>
</div>
);
}
@@ -0,0 +1,141 @@
"use client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { api, ApiError, type Character, type CursorPage } from "@/lib/api";
/** 角色状态 → 徽章(稳定值来自后端,文化名前端本地化) */
const STATUS_LABEL: Record<string, { cn: string; plain: string; tone: "info" | "muted" | "warn" | "ok" }> = {
draft: { cn: "灵胚", plain: "草稿", tone: "warn" },
active: { cn: "临照", plain: "运行中", tone: "info" },
resting: { cn: "静修", plain: "停止运行", tone: "muted" },
archived: { cn: "归藏", plain: "已隐藏", tone: "muted" },
deceased: { cn: "死亡", plain: "永久闭合", tone: "muted" },
};
function StatusBadge({ status }: { status: string }) {
const label = STATUS_LABEL[status] ?? { cn: status, plain: "未知状态", tone: "muted" as const };
return (
<span className={`badge badge--${label.tone}`} style={{ fontSize: 11, padding: "1px 8px" }}>
<span className="badge__dot" />
{label.cn} · {label.plain}
</span>
);
}
/** 镜生左列:真实角色列表(未登入 / 空 / 有数据三态)+ 创建 */
export function CharacterListPanel() {
const qc = useQueryClient();
const [name, setName] = useState("");
const [creating, setCreating] = useState(false);
const [error, setError] = useState<string | null>(null);
const list = useQuery({
queryKey: ["characters"],
queryFn: () => api<CursorPage<Character>>("/v1/characters?limit=20"),
});
const create = useMutation({
mutationFn: (newName: string) =>
api<Character>("/v1/characters", { method: "POST", json: { name: newName, summary: "" } }),
onSuccess: () => {
setName("");
setCreating(false);
setError(null);
qc.invalidateQueries({ queryKey: ["characters"] });
},
onError: (err) => setError(err instanceof ApiError ? err.message : "创建失败"),
});
if (list.isError) {
const status = list.error instanceof ApiError ? list.error.status : 0;
if (status === 401 || status === 403) {
return (
<div className="card" style={{ padding: "26px 20px", textAlign: "center" }}>
<p style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 14 }}>尚未登入,无法读取你的镜域。</p>
<a className="btn btn--primary btn--sm" href="/login">去登入</a>
</div>
);
}
return (
<div className="card" style={{ padding: "26px 20px", textAlign: "center" }}>
<p style={{ fontSize: 13.5, color: "var(--err)", marginBottom: 8 }}>读取失败</p>
<p style={{ fontSize: 12, color: "var(--text-3)", marginBottom: 12 }}>
{list.error instanceof ApiError ? list.error.message : "请稍后重试"}
</p>
<button className="btn btn--outline btn--sm" onClick={() => list.refetch()}>重试</button>
</div>
);
}
if (list.isPending) {
return (
<div className="card" style={{ padding: "22px 20px", textAlign: "center" }}>
<p style={{ fontSize: 13, color: "var(--text-3)" }}>正在读取镜生…</p>
</div>
);
}
const items = list.data?.data ?? [];
return (
<div className="card" style={{ padding: 10 }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 8px 10px" }}>
<span className="cn" style={{ fontSize: 15, fontWeight: 600 }}>
镜生<span className="cn__plain">{items.length} 位角色</span>
</span>
<button className="btn btn--outline btn--sm" onClick={() => setCreating((v) => !v)}>创建</button>
</div>
{items.length === 0 && (
<div style={{ padding: "22px 12px", textAlign: "center" }}>
<p style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 4 }}>镜域里还没有角色。</p>
<p style={{ fontSize: 12, color: "var(--text-3)" }}>从一位灵胚(角色草稿)开始——可删除,不等于角色死亡。</p>
</div>
)}
{items.map((c) => (
<div key={c.id} className="char-list-item">
<span className="avatar">{c.name.slice(0, 1)}</span>
<div style={{ flex: 1, minWidth: 0 }}>
<div className="char-list-item__name">{c.name}</div>
<div className="char-list-item__sub">
{STATUS_LABEL[c.status]?.cn === "灵胚" ? "灵胚 · 未启镜" : `道体 ${c.current_version_id ? "有版本" : "待定"}`} ·{" "}
{new Date(c.created_at).toLocaleDateString("zh-CN")}
</div>
</div>
<StatusBadge status={c.status} />
</div>
))}
{creating && (
<form
style={{ padding: "10px 8px 6px", display: "flex", gap: 8 }}
onSubmit={(e) => {
e.preventDefault();
if (name.trim()) create.mutate(name.trim());
}}
>
<input
className="input"
placeholder="角色名,如:青梧"
value={name}
onChange={(e) => setName(e.target.value)}
aria-label="新角色名"
autoFocus
/>
<button className="btn btn--primary btn--sm" type="submit" disabled={create.isPending || !name.trim()}>
{create.isPending ? "创建中…" : "创建灵胚"}
</button>
</form>
)}
{error && <p style={{ fontSize: 12, color: "var(--err)", padding: "4px 8px" }}>{error}</p>}
<div style={{ borderTop: "1px solid var(--line-soft)", marginTop: 8, paddingTop: 8 }}>
<div className="char-list-item" style={{ cursor: "default", opacity: 0.6 }}>
<span style={{ fontSize: 13, color: "var(--text-3)" }}>随缘 · 生成匿名灵胚候选(待接入)</span>
</div>
</div>
</div>
);
}
@@ -0,0 +1,218 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { api, ApiError, type Character, type CursorPage } from "@/lib/api";
import { PanelState } from "@/lib/queries";
/** 角色状态 → 徽章(稳定值来自后端,文化名前端本地化) */
const STATUS_LABEL: Record<string, { cn: string; plain: string; tone: "info" | "muted" | "warn" | "ok" }> = {
draft: { cn: "灵胚", plain: "草稿", tone: "warn" },
active: { cn: "临照", plain: "运行中", tone: "info" },
resting: { cn: "静修", plain: "停止运行", tone: "muted" },
archived: { cn: "归藏", plain: "已隐藏", tone: "muted" },
deceased: { cn: "死亡", plain: "永久闭合", tone: "muted" },
};
function StatusBadge({ status }: { status: string }) {
const label = STATUS_LABEL[status] ?? { cn: status, plain: "未知状态", tone: "muted" as const };
return (
<span className={`badge badge--${label.tone}`} style={{ fontSize: 11, padding: "1px 8px" }}>
<span className="badge__dot" />
{label.cn} · {label.plain}
</span>
);
}
/** 镜生页:左列真实列表 + 右列真实详情(选中联动) */
export function CharactersPagePanel() {
const qc_client = { key: "characters" };
const [selectedId, setSelectedId] = useState<string | null>(null);
const [name, setName] = useState("");
const [creating, setCreating] = useState(false);
const [error, setError] = useState<string | null>(null);
const list = useQuery({
queryKey: [qc_client.key],
queryFn: () => api<CursorPage<Character>>("/v1/characters?limit=20"),
});
const detail = useQuery({
queryKey: ["character", selectedId],
enabled: !!selectedId,
queryFn: () => api<Character>(`/v1/characters/${selectedId}`),
});
async function create(e: React.FormEvent) {
e.preventDefault();
if (!name.trim()) return;
setError(null);
try {
const c = await api<Character>("/v1/characters", { method: "POST", json: { name: name.trim(), summary: "" } });
setName("");
setCreating(false);
list.refetch();
setSelectedId(c.id);
} catch (err) {
setError(err instanceof ApiError ? err.message : "创建失败");
}
}
async function activate(id: string) {
try {
await api(`/v1/characters/${id}/activations`, { method: "POST", json: {} });
list.refetch();
detail.refetch();
} catch (err) {
setError(err instanceof ApiError ? err.message : "启镜失败");
}
}
const items = list.data?.data ?? [];
return (
<div className="char-layout">
<div>
<div className="card" style={{ padding: 10 }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 8px 10px" }}>
<span className="cn" style={{ fontSize: 15, fontWeight: 600 }}>
镜生<span className="cn__plain">{items.length} 位角色</span>
</span>
<button className="btn btn--outline btn--sm" onClick={() => setCreating((v) => !v)}>创建</button>
</div>
{list.isPending && <p style={{ fontSize: 13, color: "var(--text-3)", padding: "18px 8px" }}>正在读取…</p>}
{list.isError && (
<div style={{ padding: "18px 8px" }}>
{(list.error as ApiError).status === 401 || (list.error as ApiError).status === 403 ? (
<>
<p style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 12 }}>尚未登入,无法读取你的镜域。</p>
<a className="btn btn--primary btn--sm" href="/login">去登入</a>
</>
) : (
<>
<p style={{ fontSize: 13, color: "var(--err)", marginBottom: 10 }}>读取失败</p>
<button className="btn btn--outline btn--sm" onClick={() => list.refetch()}>重试</button>
</>
)}
</div>
)}
{items.length === 0 && !list.isPending && !list.isError && (
<div style={{ padding: "22px 12px", textAlign: "center" }}>
<p style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 4 }}>镜域里还没有角色。</p>
<p style={{ fontSize: 12, color: "var(--text-3)" }}>从一位灵胚(角色草稿)开始——可删除,不等于角色死亡。</p>
</div>
)}
{items.map((c) => (
<div
key={c.id}
className={selectedId === c.id ? "char-list-item is-active" : "char-list-item"}
style={{ cursor: "pointer" }}
onClick={() => setSelectedId(c.id)}
role="button"
tabIndex={0}
onKeyDown={(e) => { if (e.key === "Enter") setSelectedId(c.id); }}
aria-current={selectedId === c.id ? "true" : undefined}
>
<span className="avatar">{c.name.slice(0, 1)}</span>
<div style={{ flex: 1, minWidth: 0 }}>
<div className="char-list-item__name">{c.name}</div>
<div className="char-list-item__sub">
{c.status === "draft" ? "灵胚 · 未启镜" : c.status} · {new Date(c.created_at).toLocaleDateString("zh-CN")}
</div>
</div>
<StatusBadge status={c.status} />
</div>
))}
{creating && (
<form style={{ padding: "10px 8px 6px", display: "flex", gap: 8 }} onSubmit={create}>
<input className="input" placeholder="角色名,如:青梧" value={name} onChange={(e) => setName(e.target.value)} aria-label="新角色名" autoFocus />
<button className="btn btn--primary btn--sm" type="submit" disabled={!name.trim()}>创建灵胚</button>
</form>
)}
{error && <p style={{ fontSize: 12, color: "var(--err)", padding: "4px 8px" }}>{error}</p>}
</div>
</div>
<div>
{!selectedId && (
<div className="card">
<div className="card__head">
<span className="cn">镜生<span className="cn__plain">选择一位角色查看详情</span></span>
</div>
<p style={{ fontSize: 13, color: "var(--text-3)" }}>
详情包含道体、内驱力(可编辑)、大周天与生命周期操作——全部来自真实接口。
</p>
</div>
)}
{selectedId && (
<PanelState pending={detail.isPending} error={detail.error} onRetry={() => detail.refetch()}>
{(() => {
const c = detail.data as Character;
if (!c) return null;
return (
<div>
<div className="char-banner" aria-hidden="true" />
<div className="char-head">
<span className="avatar avatar--lg">{c.name.slice(0, 1)}</span>
<div className="char-head__title">
<h2 style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
{c.name} <StatusBadge status={c.status} />
</h2>
<div className="char-head__motto mono" style={{ fontSize: 12 }}>{c.id}</div>
</div>
<div className="char-head__actions">
{c.status === "draft" && (
<button className="btn btn--primary" onClick={() => activate(c.id)}>启镜 · 正式创建</button>
)}
<a className="btn btn--outline" href="/conversation">进入镜树</a>
</div>
</div>
<section className="section card">
<div className="card__head">
<span className="cn">道体<span className="cn__plain">稳定身份基线</span></span>
</div>
<p style={{ fontSize: 13.5, color: "var(--text-2)" }}>
{Object.keys(c.identity_baseline ?? {}).length === 0
? "道体尚未设定——启镜并完成初照自陈后形成首个版本。"
: JSON.stringify(c.identity_baseline, null, 2)}
</p>
</section>
<section className="section card">
<div className="card__head">
<span className="cn">内驱力<span className="cn__plain">动机维度 · 可编辑(PATCH drives)</span></span>
</div>
<p style={{ fontSize: 13.5, color: "var(--text-2)" }}>
{(c.drives ?? []).length === 0 ? "尚未声明内驱力维度。" : JSON.stringify(c.drives, null, 2)}
</p>
</section>
<section className="section card">
<div className="card__head">
<span className="cn">生命周期<span className="cn__plain">与数据删除分离</span></span>
</div>
<div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
{["静修 · 停止运行", "归藏 · 隐藏可恢复", "复照 · 从旧版本重开"].map((op) => (
<button key={op} className="btn btn--outline btn--sm" title="生命周期操作端点接入后启用">{op}</button>
))}
{["铸魂 · 重大重塑(强确认)", "死亡 · 永久闭合(冷静期)"].map((op) => (
<button key={op} className="btn btn--danger btn--sm" title="生命周期操作端点接入后启用">{op}</button>
))}
</div>
<p style={{ fontSize: 12, color: "var(--text-3)", marginTop: 10 }}>
铸魂与死亡走强确认与谱系留痕;后端 lifecycle 端点(POST /v1/characters/{"{id}"}/{"{action}"})已具备,交互流程待接入。
</p>
</section>
</div>
);
})()}
</PanelState>
)}
</div>
</div>
);
}
@@ -0,0 +1,57 @@
"use client";
import { useState } from "react";
import { useCreateWorkspace } from "@/lib/queries";
import type { Workspace } from "@/lib/api";
export function CollaborationPanel() {
const create = useCreateWorkspace();
const [name, setName] = useState("");
const [created, setCreated] = useState<Workspace | null>(null);
return (
<div>
<div className="realm-banner" role="note">
成员通过同一明确契约协作;编辑、发布、市场上架与财务权限分别授予。
</div>
<div className="card">
<div className="card__head">
<span className="cn">同契台<span className="cn__plain">创建协作空间</span></span>
</div>
<form
style={{ display: "flex", gap: 10, flexWrap: "wrap" }}
onSubmit={async (e) => {
e.preventDefault();
if (!name.trim()) return;
const ws = await create.mutateAsync({ name: name.trim() });
setCreated(ws);
setName("");
}}
>
<input className="input" style={{ flex: 1, minWidth: 220 }} placeholder="空间名,如:镜斋工作室" value={name} onChange={(e) => setName(e.target.value)} aria-label="空间名" />
<button className="btn btn--primary btn--sm" type="submit" disabled={create.isPending || !name.trim()}>
{create.isPending ? "创建中…" : "创建空间"}
</button>
</form>
{create.isError && (
<p style={{ fontSize: 12, color: "var(--err)", marginTop: 8 }}>{(create.error as { message?: string }).message}</p>
)}
{created && (
<div style={{ marginTop: 14 }}>
<div className="list-row">
<span className="avatar avatar--sm">契</span>
<div className="list-row__body">
<div className="list-row__title">{created.name}</div>
<div className="list-row__sub mono">{created.id}</div>
</div>
<span className="badge badge--ok" style={{ fontSize: 11 }}><span className="badge__dot" />已创建</span>
</div>
</div>
)}
<p style={{ fontSize: 12, color: "var(--text-3)", marginTop: 10 }}>
成员、项目分支与审稿接口(邀请 / 版本 / 合卷)已具备;空间列表端点待补齐后在此展示全部空间。
</p>
</div>
</div>
);
}
@@ -0,0 +1,67 @@
"use client";
import { useState } from "react";
import { PanelState, useContents, useCreateContent } from "@/lib/queries";
import type { CommunityContent } from "@/lib/api";
export function CommunityPanel() {
const contents = useContents();
const create = useCreateContent();
const [title, setTitle] = useState("");
const list = contents.data?.data ?? [];
return (
<div>
<div className="card" style={{ marginBottom: 16, padding: 16 }}>
<div className="card__head"><span className="cn">发布<span className="cn__plain">动态 · 公开内容</span></span></div>
<form
style={{ display: "flex", gap: 10, flexWrap: "wrap" }}
onSubmit={(e) => {
e.preventDefault();
if (title.trim()) create.mutate({ kind: "post", title: title.trim() });
}}
>
<input
className="input"
style={{ flex: 1, minWidth: 220 }}
placeholder="发布一条动态,如:共读《陶庵梦忆》第一讲笔记"
value={title}
onChange={(e) => setTitle(e.target.value)}
aria-label="动态标题"
/>
<button className="btn btn--primary btn--sm" type="submit" disabled={create.isPending || !title.trim()}>
{create.isPending ? "发布中…" : "发布"}
</button>
</form>
{create.isError && (
<p style={{ fontSize: 12, color: "var(--err)", marginTop: 8 }}>{(create.error as { message?: string }).message}</p>
)}
</div>
<PanelState pending={contents.isPending} error={contents.error} onRetry={() => contents.refetch()} empty={list.length === 0}>
{list.map((c: CommunityContent) => (
<div className="card post" key={c.id} style={{ marginBottom: 14 }}>
<div className="post__head">
<span className="avatar avatar--sm">镜</span>
<div>
<div className="post__author">镜域 {c.author_domain_id.slice(-6)}</div>
<div className="post__time">{new Date(c.created_at).toLocaleString("zh-CN", { hour12: false })}</div>
</div>
<span className="badge badge--ok" style={{ marginLeft: "auto", fontSize: 11 }}>
<span className="badge__dot" />已刊 · {c.status}
</span>
</div>
<div className="post__body">
<p style={{ color: "var(--text-1)", fontSize: 14.5 }}>{c.title ?? "(无标题)"}</p>
</div>
<div className="post__foot">
<button className="act-btn">评论</button>
<button className="act-btn">收藏</button>
<button className="act-btn" style={{ marginLeft: "auto" }}>鸣冤</button>
</div>
</div>
))}
</PanelState>
</div>
);
}
@@ -0,0 +1,170 @@
import type { ContentBlockLike } from "@/lib/api";
/**
* ContentBlock 判别联合渲染器(静态阶段)。
* 契约要求:穷尽判别、未知类型走兼容占位不崩溃;Markdown 未闭合结构保持源码态。
* 完整的分级实时渲染(Shiki/KaTeX/Mermaid Worker)按 frontend-content-rendering.md 在后续接入。
*/
function src(b: ContentBlockLike, ...keys: string[]): string {
for (const k of keys) {
const v = b[k];
if (typeof v === "string" && v) return v;
}
return "";
}
function TextBlock({ b }: { b: ContentBlockLike }) {
const text = src(b, "text", "source");
if (b.status === "streaming") {
return <p style={{ whiteSpace: "pre-wrap" }}>{text}<span className="stream-cursor" aria-hidden="true" /></p>;
}
return <p style={{ whiteSpace: "pre-wrap" }}>{text}</p>;
}
function CodeBlock({ b }: { b: ContentBlockLike }) {
return (
<div className="block">
<div className="block__head"><span className="lang">{src(b, "language") || "code"}</span>{src(b, "filename") && <span className="fname">{src(b, "filename")}</span>}</div>
<pre className="code">{src(b, "source", "code", "text")}</pre>
</div>
);
}
function QuoteBlock({ b }: { b: ContentBlockLike }) {
return (
<blockquote className="quote">
{src(b, "text", "content")}
{src(b, "source") && <span className="quote__src">引文锚 · {src(b, "source")}</span>}
</blockquote>
);
}
function TableBlock({ b }: { b: ContentBlockLike }) {
const columns = Array.isArray(b.columns) ? (b.columns as string[]) : [];
const rows = Array.isArray(b.rows) ? (b.rows as unknown[][]) : [];
return (
<div className="block">
<div className="block__head">{src(b, "caption") || "表格"}</div>
<div className="table-wrap">
<table className="data">
{columns.length > 0 && (
<thead><tr>{columns.map((c, i) => <th key={i}>{c}</th>)}</tr></thead>
)}
<tbody>
{rows.map((row, i) => (
<tr key={i}>{row.map((cell, j) => <td key={j}>{String(cell ?? "")}</td>)}</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
function CalloutBlock({ b }: { b: ContentBlockLike }) {
const kind = src(b, "kind") || "info";
const tone = kind === "danger" ? "var(--err)" : kind === "warning" || kind === "success" ? "var(--warn)" : "var(--accent)";
return (
<div className="block" style={{ borderLeft: `3px solid ${tone}` }}>
<div className="block__body">
{src(b, "title") && <b style={{ display: "block", marginBottom: 4 }}>{src(b, "title")}</b>}
<p style={{ whiteSpace: "pre-wrap" }}>{src(b, "content")}</p>
</div>
</div>
);
}
function MathBlock({ b }: { b: ContentBlockLike }) {
return (
<div className="block">
<div className="math">
{src(b, "source")}
{src(b, "fallback_text") && <span className="math__fallback">fallback:{src(b, "fallback_text")}</span>}
</div>
</div>
);
}
function MermaidBlock({ b }: { b: ContentBlockLike }) {
return (
<div className="block">
<div className="block__head"><span className="lang">mermaid</span><span>完成后以 strict mode 隔离渲染</span></div>
<pre className="code">{src(b, "source")}</pre>
</div>
);
}
function MediaBlock({ b }: { b: ContentBlockLike }) {
const label = src(b, "file_id") ? `文件 ${src(b, "file_id").slice(-8)}` : "媒体";
return (
<div className="block">
<div className="block__head">{b.type}{src(b, "filename") && <span className="fname">{src(b, "filename")}</span>}</div>
<div className="block__body"><p style={{ fontSize: 12.5, color: "var(--text-3)" }}>{label} · 资源就绪后加载(短期授权 URL 换取待接入)</p></div>
</div>
);
}
function StatusBlock({ b }: { b: ContentBlockLike }) {
const label: Record<string, string> = {
tool_call: "工具调用", tool_result: "工具结果", approval_request: "请命",
agent_status: "Agent 状态", artifact: "产物", citation: "引文", error: "错误",
};
return (
<div className="block tool">
<div className="tool__head">
<span className="tool__name">{label[b.type] ?? b.type}</span>
<span className="tool__purpose">{src(b, "tool_name", "name", "title")}</span>
{src(b, "status") && <span className="badge badge--muted" style={{ marginLeft: "auto", fontSize: 11 }}><span className="badge__dot" />{src(b, "status")}</span>}
</div>
<div className="tool__body">
<div className="tool__row"><span className="k2">内容</span><span className="mono" style={{ fontSize: 11.5 }}>{JSON.stringify(b).slice(0, 160)}</span></div>
</div>
</div>
);
}
export function BlockRenderer({ block }: { block: ContentBlockLike }) {
switch (block.type) {
case "text":
case "markdown":
return <TextBlock b={block} />;
case "code":
return <CodeBlock b={block} />;
case "quote":
return <QuoteBlock b={block} />;
case "table":
return <TableBlock b={block} />;
case "callout":
return <CalloutBlock b={block} />;
case "math":
return <MathBlock b={block} />;
case "mermaid":
return <MermaidBlock b={block} />;
case "image":
case "video":
case "audio":
case "file":
case "gallery":
case "avatar":
return <MediaBlock b={block} />;
case "tool_call":
case "tool_result":
case "approval_request":
case "agent_status":
case "artifact":
case "citation":
case "error":
return <StatusBlock b={block} />;
default:
return (
<div className="block">
<div className="block__body">
<p style={{ fontSize: 12.5, color: "var(--text-3)" }}>
当前客户端暂不支持「{block.type}」块——已按兼容占位处理,内容不丢失。
</p>
</div>
</div>
);
}
}
@@ -0,0 +1,86 @@
"use client";
import { ChevronsLeft } from "lucide-react";
import { useState } from "react";
/**
* 镜树三栏布局:左右面板可折叠,内容区自适应放宽(773 → 1000 → 1140)。
* 折叠时浮动恢复按钮出现在消息流顶角。
*/
export function ConvLayout({
side,
main,
ctx,
}: {
side: React.ReactNode;
main: React.ReactNode;
ctx: React.ReactNode;
}) {
const [noLeft, setNoLeft] = useState(false);
const [noRight, setNoRight] = useState(false);
const cls = ["conv-layout", noLeft ? "no-left" : "", noRight ? "no-right" : ""]
.filter(Boolean)
.join(" ");
return (
<div className={cls}>
<aside className="conv-side" aria-label="会话列表">
<div className="conv-side__head">
<span className="avatar avatar--sm">梧</span>
<div style={{ flex: 1 }}>
<div className="cn" style={{ fontWeight: 600 }}>
青梧
</div>
<div style={{ fontSize: 11, color: "var(--text-3)" }}>道体 v3 · 临照</div>
</div>
<button
type="button"
className="panel-toggle"
onClick={() => setNoLeft(true)}
aria-label="收起会话列表"
title="收起会话列表"
style={{ marginLeft: 4 }}
>
<ChevronsLeft />
</button>
</div>
{side}
</aside>
<section className="conv-main">
<button
type="button"
className="conv-restore conv-restore--left"
onClick={() => setNoLeft(false)}
aria-label="展开会话列表"
title="展开会话列表"
>
<ChevronsLeft />
</button>
<button
type="button"
className="conv-restore conv-restore--right"
onClick={() => setNoRight(false)}
aria-label="展开分支面板"
title="展开分支与上下文面板"
>
<ChevronsLeft style={{ transform: "rotate(180deg)" }} />
</button>
{main}
</section>
<aside className="conv-ctx" aria-label="分支与上下文">
<div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 6 }}>
<button
type="button"
className="panel-toggle"
onClick={() => setNoRight(true)}
aria-label="收起面板"
title="收起分支与上下文面板"
>
<ChevronsLeft style={{ transform: "rotate(180deg)" }} />
</button>
</div>
{ctx}
</aside>
</div>
);
}
@@ -0,0 +1,382 @@
"use client";
import { useQueryClient } from "@tanstack/react-query";
import { Send } from "lucide-react";
import { useEffect, useState } from "react";
import { api } from "@/lib/api";
import { BlockRenderer } from "@/components/conversation/BlockRenderer";
import { EffortControl, SearchPill } from "@/components/conversation/EffortControl";
import { ConvLayout } from "@/components/conversation/ConvLayout";
import {
useConversation,
useCreateConversation,
useCreateToolCall,
useMessages,
useRunEvents,
useSendMessage,
} from "@/lib/queries";
import type { Character, CursorPage, Message } from "@/lib/api";
function MessageRow({ msg }: { msg: Message }) {
if (msg.role === "user") {
return (
<div className="msg msg--user">
<div className="msg__meta">
<span className="cn">问节</span>
<span className="mono">{msg.id.slice(-6)}</span>
<span>{new Date(msg.created_at).toLocaleString("zh-CN", { hour12: false })}</span>
</div>
<div className="bubble">
{msg.content.map((b, i) => (
<BlockRenderer key={String(b.id ?? i)} block={b} />
))}
</div>
</div>
);
}
return (
<div className="msg">
<div className="msg--char__head">
<span className="avatar avatar--sm">镜</span>
<span className="msg--char__name">{msg.role === "assistant" ? "角色" : msg.role}</span>
{msg.canonical_scope && (
<span className="badge badge--muted" style={{ fontSize: 11 }}>
<span className="badge__dot" />
归真 · {msg.canonical_scope}
</span>
)}
</div>
<div className="msg__blocks" style={{ paddingLeft: 0 }}>
<div className="block block--text">
<div className="block__body">{msg.content.map((b, i) => <BlockRenderer key={String(b.id ?? i)} block={b} />)}</div>
</div>
</div>
</div>
);
}
export function ConversationPanel() {
const qc = useQueryClient();
const [convId, setConvId] = useState<string | null>(null);
const [draft, setDraft] = useState("");
const [activeRunId, setActiveRunId] = useState<string | null>(null);
const [title, setTitle] = useState("");
const [characterId, setCharacterId] = useState("");
const [effortHint, setEffortHint] = useState("深虑 · 中");
const [error, setError] = useState<string | null>(null);
// URL ?c= 携带当前会话
useEffect(() => {
const c = new URLSearchParams(window.location.search).get("c");
if (c) setConvId(c);
}, []);
const characters = useQuerySafe<Character[]>("/v1/characters?limit=20");
const conversation = useConversation(convId);
const messages = useMessages(convId);
const send = useSendMessage(convId);
const create = useCreateConversation();
function openConversation(id: string) {
setConvId(id);
window.history.replaceState(null, "", `/conversation?c=${id}`);
}
async function submit(e: React.FormEvent) {
e.preventDefault();
if (!draft.trim() || !convId) return;
setError(null);
try {
const result = await send.mutateAsync(draft.trim());
setDraft("");
if (result?.run?.id) setActiveRunId(result.run.id);
} catch (err) {
setError((err as { message?: string }).message ?? "发送失败");
}
}
const items = (characters.data as Character[] | undefined) ?? [];
useEffect(() => {
if (!characterId && items.length > 0) setCharacterId(items[0].id);
}, [items, characterId]);
return (
<ConvLayout
side={
<>
<div style={{ padding: "4px 6px 12px" }}>
<form
style={{ display: "flex", flexDirection: "column", gap: 8 }}
onSubmit={async (e) => {
e.preventDefault();
setError(null);
try {
const c = await create.mutateAsync({
title: title.trim() || "未命名对话",
character_id: characterId || undefined,
});
setTitle("");
openConversation(c.id);
qc.invalidateQueries({ queryKey: ["messages"] });
} catch (err) {
setError((err as { message?: string }).message ?? "镜树创建失败,请重试");
}
}}
>
<select
className="input"
value={characterId}
onChange={(e) => setCharacterId(e.target.value)}
aria-label="选择角色"
>
<option value="">不绑定角色</option>
{items.map((c) => (
<option key={c.id} value={c.id}>{c.name}</option>
))}
</select>
<input
className="input"
placeholder="新对话主题(可留空)"
value={title}
onChange={(e) => setTitle(e.target.value)}
aria-label="新对话主题"
/>
<button className="btn btn--outline btn--sm" type="submit" disabled={create.isPending}>
{create.isPending ? "创建中…" : "开始新镜树"}
</button>
{error && (
<p role="alert" style={{ fontSize: 12, color: "var(--err)" }}>{error}</p>
)}
</form>
</div>
{convId && (
<a className="conv-item is-active" href={`/conversation?c=${convId}`} aria-current="page">
<div className="conv-item__title">
<span className="conv-item__dot conv-item__dot--active" title="当前会话" />
{String((conversation.data as { title?: string } | undefined)?.title ?? "当前对话")}
</div>
<div className="conv-item__sub">会话列表端点待 WP-03 补齐,当前按会话直达</div>
</a>
)}
</>
}
main={
<>
<div className="conv-stream">
<div className="conv-stream__inner">
{!convId && (
<div className="realm-banner" role="status">
从左侧选择角色并开始一棵新的镜树——分支、候答与界域切换随消息数据启用。
</div>
)}
{convId && messages.isPending && <p style={{ color: "var(--text-3)", fontSize: 13 }}>正在读取消息…</p>}
{convId && messages.isError && (
<p style={{ color: "var(--err)", fontSize: 13 }}>
读取失败:{(messages.error as { message?: string }).message ?? "请重试"}
</p>
)}
{convId &&
((messages.data?.data.length ?? 0) === 0) &&
!messages.isPending && (
<p style={{ color: "var(--text-3)", fontSize: 13, textAlign: "center", padding: "40px 0" }}>
这棵镜树还没有消息——在下方呈镜第一句。
</p>
)}
{messages.data?.data.map((m) => <MessageRow key={m.id} msg={m} />)}
<RunStream runId={activeRunId} onDone={(finalStatus) => { messages.refetch(); if (finalStatus !== "failed") setActiveRunId(null); }} />
</div>
</div>
<div className="composer">
<div className="composer__inner">
<div className="composer__box">
<textarea
className="composer__ta"
rows={2}
placeholder={convId ? "对角色说……(Enter 发送 · Shift+Enter 换行)" : "先在左侧开始一棵镜树"}
aria-label="消息输入"
disabled={!convId}
value={draft}
onChange={(e) => setDraft(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
submit(e as unknown as React.FormEvent);
}
}}
/>
<div className="composer__bar">
<EffortControl
effort="中"
onEffortChange={(v) => setEffortHint("深虑 · " + v)}
onToggle={(on) => setEffortHint(on ? "深虑 · 中" : "深虑 · 关")}
/>
<SearchPill />
<span className="spacer" style={{ flex: 1 }} />
<button
type="button"
className="btn btn--primary btn--sm"
aria-label="发送(呈镜)"
title="发送 · 呈镜"
disabled={!convId || send.isPending || !draft.trim()}
onClick={() => submit({ preventDefault: () => {} } as React.FormEvent)}
>
<Send />呈镜
</button>
</div>
</div>
{error && <p style={{ fontSize: 12, color: "var(--err)", marginTop: 6 }}>{error}</p>}
<div className="composer__hint">
<span>模型 <span className="k">会话临时(网关路由待 WP-05 暴露选择端点)</span></span>
<span>思考 <span className="k">{effortHint}</span>(点深虑开关,右侧调力度)</span>
</div>
</div>
</div>
</>
}
ctx={
<>
<section className="ctx-section">
<div className="ctx-section__head">
<span className="cn">镜枝<span className="cn__plain">分支结构</span></span>
</div>
{convId ? (
<BranchTree convId={convId} />
) : (
<p style={{ fontSize: 12.5, color: "var(--text-3)" }}>开始会话后显示真实分支。</p>
)}
</section>
<section className="ctx-section">
<div className="ctx-section__head">
<span className="cn">上下文镜<span className="cn__plain">ContextSnapshot</span></span>
</div>
<p style={{ fontSize: 12.5, color: "var(--text-3)" }}>
召回证据与省略项随 WP-04 ContextSnapshot 暴露后接入;当前仅呈现结构占位。
</p>
</section>
</>
}
/>
);
}
/** 分支树:真实 branches 数据(主脉/当前分支) */
function BranchTree({ convId }: { convId: string }) {
const branches = useQuerySafe<{ id: string; is_current?: boolean; [k: string]: unknown }>(
`/v1/conversations/${convId}/branches`,
);
if (branches.pending) return <p style={{ fontSize: 12.5, color: "var(--text-3)" }}>读取分支…</p>;
if (branches.error) return <p style={{ fontSize: 12.5, color: "var(--err)" }}>分支读取失败</p>;
const list = branches.data ?? [];
if (list.length === 0)
return <p style={{ fontSize: 12.5, color: "var(--text-3)" }}>尚无分支——重生成会产生候答分支。</p>;
return (
<div className="tree" role="group" aria-label="分支树">
<ul>
{list.map((b) => (
<li key={b.id}>
<div className={b.is_current ? "tree__node tree__node--main tree__node--now" : "tree__node"}>
<span className="nm mono">{b.id.slice(-8)}</span>
{b.is_current && <span className="tree__tag tree__tag--main">主脉 · 当前</span>}
</div>
</li>
))}
</ul>
</div>
);
}
/** 轻量查询(面板内局部使用) */
import { useQuery } from "@tanstack/react-query";
function useQuerySafe<T>(path: string) {
const q = useQuery({ queryKey: ["panel", path], queryFn: () => api<CursorPage<T> | { data: T[] }>(path) });
const data = (q.data as CursorPage<T> | undefined)?.data ?? ((q.data as { data?: T[] } | undefined)?.data as T[] | undefined);
return { pending: q.isPending, error: q.error, data, refetch: q.refetch };
}
/** Run 流式区:临时芽体(delta 累积)+ 失败卡 + 完成后由父级对账权威快照 */
function RunStream({ runId, onDone }: { runId: string | null; onDone: (finalStatus: string | null) => void }) {
const stream = useRunEvents(runId, onDone);
if (!runId) return null;
return (
<div className="msg">
<div className="msg--char__head">
<span className="avatar avatar--sm">镜</span>
<span className="msg--char__name">临时芽体</span>
<span className={`badge badge--${stream.done ? (stream.finalStatus === "failed" ? "err" : "ok") : "info"}`}>
<span className="badge__dot" />
{stream.done
? stream.finalStatus === "failed" ? `受阻 · ${stream.runStatus}` : `已成 · ${stream.runStatus}`
: `运转 · ${stream.runStatus}`}
<span className="mono">{stream.runStatus}</span>
</span>
</div>
<div className="msg__blocks" style={{ paddingLeft: 0 }}>
{stream.blocks.map((b) => (
<div key={b.id} className={b.status === "streaming" ? "block sprout" : "block"}>
{b.status === "streaming" && (
<div className="sprout__head">
<span className="cn" style={{ fontWeight: 600 }}>临时芽体</span>
<span className="cn__plain">流式生成中 · 完成后才固化</span>
<span style={{ marginLeft: "auto" }} className="mono">{b.type}</span>
</div>
)}
<div className="block__body">
{b.status === "failed" ? (
<p style={{ color: "var(--err)", fontSize: 13.5 }}>
受阻 · 失败:<span className="mono">{b.error}</span>
{b.error === "no_endpoint_available" && (
<span style={{ color: "var(--text-3)", display: "block", fontSize: 12, marginTop: 4 }}>
没有可用的模型端点——在神识阁配置端点(WP-05)后重试;流中断不伪装完成。
</span>
)}
</p>
) : (
<p style={{ whiteSpace: "pre-wrap" }}>
{b.text}
{b.status === "streaming" && <span className="stream-cursor" aria-hidden="true" />}
</p>
)}
</div>
</div>
))}
{stream.blocks.length === 0 && !stream.done && (
<p style={{ fontSize: 13, color: "var(--text-3)" }}>正在连接事件流…</p>
)}
{stream.done && stream.finalStatus && (
<p style={{ fontSize: 11.5, color: "var(--text-3)" }}>
message.completed(final_status: <span className="mono">{stream.finalStatus}</span>)——已按权威快照对账。
</p>
)}
</div>
</div>
);
}
/** 工具调用发起(挂在当前会话最新 Run 或新 Run 上) */
export function ToolCallForm({ runId }: { runId: string | null }) {
const create = useCreateToolCall(runId);
const [input, setInput] = useState('{"expression":"1+2*3"}');
if (!runId) return null;
return (
<form
style={{ padding: "8px 6px", display: "flex", gap: 8, flexWrap: "wrap" }}
onSubmit={(e) => {
e.preventDefault();
try {
create.mutate({ tool: "calculate", input: JSON.parse(input) });
} catch {
/* JSON 非法时忽略 */
}
}}
>
<span style={{ fontSize: 12, color: "var(--text-3)", alignSelf: "center" }} className="mono">calculate</span>
<input className="input" style={{ flex: 1, minWidth: 180 }} value={input} onChange={(e) => setInput(e.target.value)} aria-label="工具参数 JSON" />
<button className="btn btn--outline btn--sm" type="submit" disabled={create.isPending}>
{create.isPending ? "调用中…" : "发起工具调用"}
</button>
{create.isError && <span style={{ fontSize: 12, color: "var(--err)", alignSelf: "center" }}>{(create.error as { message?: string }).message}</span>}
</form>
);
}
@@ -0,0 +1,82 @@
"use client";
import { useState } from "react";
/**
* 深虑控制:点击深虑 = 开启深度思考;开启后右侧伴随出现力度选项(独立控件,只调档);
* 再次点击 = 关闭,力度选项随之消失;重开保留上次档位。
*/
export function EffortControl({
effort,
onEffortChange,
onToggle,
}: {
effort: string;
onEffortChange: (v: "低" | "中" | "高") => void;
onToggle: (on: boolean) => void;
}) {
const [on, setOn] = useState(true);
const [val, setVal] = useState<"低" | "中" | "高">(effort as "低" | "中" | "高");
const toggle = () => {
const next = !on;
setOn(next);
onToggle(next);
};
return (
<>
<button
type="button"
className={on ? "pill-btn is-on" : "pill-btn"}
aria-pressed={on}
data-pill="effort"
onClick={toggle}
title={
on
? "深虑 · 深度思考已开启,再次点击关闭"
: "深虑 · 已关闭:本条消息不思考,直出答复(推理型模型不可关)"
}
>
深虑
</button>
<span className="effort-seg" data-effort-seg hidden={!on} role="radiogroup" aria-label="推理力度">
{(["低", "中", "高"] as const).map((level) => (
<button
key={level}
type="button"
role="radio"
aria-checked={val === level}
title={{ 低: "低 · 快省", 中: "中 · 均衡", 高: "高 · 深究慢" }[level]}
onClick={(e) => {
e.stopPropagation();
setVal(level);
onEffortChange(level);
}}
>
{level}
</button>
))}
</span>
<span hidden>
<input readOnly value={on ? val : "关"} aria-hidden="true" tabIndex={-1} />
</span>
</>
);
}
/** 博闻:独立开关(联网检索,走百工坊白名单) */
export function SearchPill() {
const [on, setOn] = useState(false);
return (
<button
type="button"
className={on ? "pill-btn is-on" : "pill-btn"}
aria-pressed={on}
onClick={() => setOn((v) => !v)}
title="博闻 · 本条消息启用联网检索(走百工坊白名单,点击开关)"
>
博闻<small>{on ? "开" : "关"}</small>
</button>
);
}
@@ -0,0 +1,88 @@
"use client";
import { useState } from "react";
import { PanelState, useCreateListing, useFinanceSummary, useListings } from "@/lib/queries";
import type { Listing } from "@/lib/api";
function yuan(minor: number | null, currency: string | null) {
if (minor == null) return "免费";
return `¥${(minor / 100).toFixed(2)}${currency ? ` ${currency.toUpperCase()}` : ""}`;
}
export function MarketplacePanel() {
const listings = useListings();
const finance = useFinanceSummary();
const create = useCreateListing();
const [title, setTitle] = useState("");
const [price, setPrice] = useState("");
const list = listings.data?.data ?? [];
return (
<div>
<div className="realm-banner" role="note">
购买的是版本与许可(市契),不是人格所有权。金额、分润与退契规则始终直白展示。
</div>
<div className="card" style={{ marginBottom: 16, padding: 16 }}>
<div className="card__head"><span className="cn">上架<span className="cn__plain">成为创作者</span></span></div>
<form
style={{ display: "flex", gap: 10, flexWrap: "wrap" }}
onSubmit={(e) => {
e.preventDefault();
if (!title.trim()) return;
const minor = Math.round(parseFloat(price || "0") * 100);
create.mutate({ title: title.trim(), price_minor: isNaN(minor) ? 0 : minor, currency: "cny" });
}}
>
<input className="input" style={{ flex: 1, minWidth: 200 }} placeholder="商品名,如:雾中茶寮 · 世界合集" value={title} onChange={(e) => setTitle(e.target.value)} aria-label="商品名" />
<input className="input" style={{ width: 120 }} placeholder="价格 ¥(0 为免费)" inputMode="decimal" value={price} onChange={(e) => setPrice(e.target.value)} aria-label="价格" />
<button className="btn btn--primary btn--sm" type="submit" disabled={create.isPending || !title.trim()}>
{create.isPending ? "上架中…" : "上架"}
</button>
</form>
</div>
<section className="section">
<div className="section__head">
<span className="cn">云市<span className="cn__plain">商品</span></span>
</div>
<PanelState pending={listings.isPending} error={listings.error} onRetry={() => listings.refetch()} empty={list.length === 0}>
<div className="asset-grid">
{list.map((l: Listing) => (
<div className="asset-card" key={l.id}>
<div className="asset-thumb" style={{ background: "linear-gradient(150deg,#2f4858,#6a8caf 60%,#3b5a70)" }}>
<span className="glyph">{l.title.slice(0, 1)}</span>
</div>
<div className="asset-body">
<span className="asset-name">{l.title}</span>
<span className="asset-sub">{l.status === "published" ? "已发布" : l.status}</span>
<div style={{ display: "flex", justifyContent: "space-between", marginTop: 2 }}>
<span className="price">{l.price_minor ? yuan(l.price_minor, l.currency) : <span className="price price--free">免费</span>}</span>
</div>
</div>
</div>
))}
</div>
</PanelState>
</section>
<section className="section card">
<div className="card__head"><span className="cn">结算<span className="cn__plain">创作者收益</span></span></div>
<PanelState pending={finance.isPending} error={finance.error} empty={Object.keys(finance.data?.totals_minor ?? {}).length === 0}>
<div style={{ display: "flex", gap: 28, flexWrap: "wrap", alignItems: "center" }}>
{Object.entries(finance.data?.totals_minor ?? {}).map(([cur, minor]) => (
<div key={cur}>
<div style={{ fontSize: 11.5, color: "var(--text-3)" }}>{cur.toUpperCase()} 余额</div>
<div className="price" style={{ fontSize: 22 }}>{yuan(minor, cur)}</div>
</div>
))}
<span className="badge badge--ok"><span className="badge__dot" />四账平衡 · {String(finance.data?.balanced ?? false)}</span>
</div>
<p style={{ fontSize: 12, color: "var(--text-3)", marginTop: 10 }}>
Ledger 不可变,修正写反向分录;每日自动对账(支付 / 订单 / Ledger / 结算)。
</p>
</PanelState>
</section>
</div>
);
}
@@ -0,0 +1,135 @@
"use client";
import { useState } from "react";
import { api } from "@/lib/api";
import { PanelState, useDecideCandidate, useMemories, useMemoryCandidates } from "@/lib/queries";
import type { Memory, MemoryCandidate } from "@/lib/api";
const KIND_LABEL: Record<string, string> = {
fact: "事实", preference: "偏好", event: "事件", relationship: "关系",
};
function candidateText(c: MemoryCandidate) {
const p = c.payload as { text?: string; summary?: string; content?: string };
return p.text ?? p.summary ?? p.content ?? JSON.stringify(c.payload);
}
export function MemoryPanel() {
const candidates = useMemoryCandidates();
const memories = useMemories();
const decide = useDecideCandidate();
const [tab, setTab] = useState<"candidates" | "memories">("candidates");
const candList = candidates.data?.data ?? [];
const memList = memories.data?.data ?? [];
return (
<div>
<div className="tabs" role="tablist" aria-label="记忆类型">
<button className={tab === "candidates" ? "tab is-on" : "tab"} role="tab" aria-selected={tab === "candidates"} onClick={() => setTab("candidates")}>
候选审阅 <span className="cnt">{candList.length}</span>
</button>
<button className={tab === "memories" ? "tab is-on" : "tab"} role="tab" aria-selected={tab === "memories"} onClick={() => setTab("memories")}>
明忆 <span className="cnt">{memList.length}</span>
</button>
</div>
<div className="realm-banner" role="note">
归真的约束:候选经你确认才会影响未来上下文;境界内容只成世界知识候选,幻境内容须手动入忆。
</div>
{tab === "candidates" && (
<PanelState pending={candidates.isPending} error={candidates.error} onRetry={() => candidates.refetch()}>
{candList.length === 0 ? (
<div className="card">
<p style={{ fontSize: 13.5, color: "var(--text-2)" }}>当前没有待审候选。</p>
<p style={{ fontSize: 12, color: "var(--text-3)", marginTop: 6 }}>
候选来自镜树对话与大周天评估——发一段对话后回到这里,或使用下方手动添加。
</p>
<ManualCandidate />
</div>
) : (
candList.map((c) => (
<div className="card" key={c.id} style={{ marginBottom: 14 }}>
<div className="card__head">
<span className="mem-type">{KIND_LABEL[c.kind] ?? c.kind}</span>
<span style={{ fontSize: 11.5, color: "var(--text-3)" }}>
来源 · {c.source} · 敏感度 {c.sensitivity}
</span>
<span style={{ marginLeft: "auto", fontSize: 11.5, color: "var(--text-3)" }}>{c.id.slice(-8)}</span>
</div>
<p className="cn" style={{ fontSize: 14 }}>{candidateText(c)}</p>
<div style={{ display: "flex", gap: 8, marginTop: 12 }}>
<button className="btn btn--primary btn--sm" disabled={decide.isPending} onClick={() => decide.mutate({ id: c.id, approve: true })}>
归真入忆
</button>
<button className="btn btn--ghost btn--sm" disabled={decide.isPending} onClick={() => decide.mutate({ id: c.id, approve: false })}>
忽略
</button>
</div>
</div>
))
)}
</PanelState>
)}
{tab === "memories" && (
<PanelState pending={memories.isPending} error={memories.error} onRetry={() => memories.refetch()} empty={memList.length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{memList.map((m: Memory) => (
<div className="list-row" key={m.id}>
<span className="mem-type" style={{ flex: "none" }}>{KIND_LABEL[m.kind] ?? m.kind}</span>
<div className="list-row__body">
<div className="list-row__title">{candidateText(m as unknown as MemoryCandidate)}</div>
<div className="list-row__sub">{m.source} · {new Date(m.created_at).toLocaleString("zh-CN", { hour12: false })}</div>
</div>
<span className="badge badge--ok" style={{ fontSize: 11 }}><span className="badge__dot" />已归真</span>
</div>
))}
</div>
</PanelState>
)}
</div>
);
}
/** 手动添加候选(走同一审核流,用于联调验证闭环) */
function ManualCandidate() {
const [text, setText] = useState("");
const [kind, setKind] = useState("preference");
const [state, setState] = useState<{ pending: boolean; error: string | null; ok: boolean }>({ pending: false, error: null, ok: false });
return (
<form
style={{ display: "flex", gap: 8, marginTop: 14, flexWrap: "wrap" }}
onSubmit={async (e) => {
e.preventDefault();
if (!text.trim()) return;
setState({ pending: true, error: null, ok: false });
try {
await api("/v1/memories/candidates", {
method: "POST",
json: { kind, sensitivity: "low", realm: "reality", payload: { text: text.trim() }, source: "manual" },
});
setText("");
setState({ pending: false, error: null, ok: true });
window.location.reload();
} catch (err) {
setState({ pending: false, error: (err as { message?: string }).message ?? "创建失败", ok: false });
}
}}
>
<select className="input" style={{ width: 100 }} value={kind} onChange={(e) => setKind(e.target.value)} aria-label="候选类型">
<option value="preference">偏好</option>
<option value="fact">事实</option>
<option value="event">事件</option>
<option value="relationship">关系</option>
</select>
<input className="input" style={{ flex: 1, minWidth: 200 }} placeholder="手动添加一条候选,如:读书会隔周四 19:30" value={text} onChange={(e) => setText(e.target.value)} aria-label="候选内容" />
<button className="btn btn--outline btn--sm" type="submit" disabled={state.pending || !text.trim()}>
{state.pending ? "添加中…" : "添加候选"}
</button>
{state.error && <span style={{ fontSize: 12, color: "var(--err)", alignSelf: "center" }}>{state.error}</span>}
</form>
);
}
+364
View File
@@ -0,0 +1,364 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { api, clearCsrf, type Character, type CursorPage, type ToolCall } from "@/lib/api";
import { PanelState, useApproveToolCall, useLogout, useMemoryCandidates, useToolCalls } from "@/lib/queries";
/* ---------- 镜庭:聚合真实状态 ---------- */
export function HomePanel() {
const characters = useQuery({
queryKey: ["characters"],
queryFn: () => api<CursorPage<Character>>("/v1/characters?limit=10"),
});
const candidates = useMemoryCandidates();
const toolCalls = useToolCalls();
const chars = characters.data?.data ?? [];
const candCount = candidates.data?.data.length ?? 0;
const calls = toolCalls.data?.data ?? [];
return (
<div>
<div className="hero">
<div>
<h1>镜庭</h1>
<p style={{ color: "var(--text-2)", fontSize: 13, marginTop: 4 }}>
镜庭只聚合状态,各域才是数据事实源
</p>
</div>
<div className="hero__stats">
<a className="hero__stat" href="/characters"><b>{chars.length}</b><span>镜生 · 角色</span></a>
<a className="hero__stat" href="/memory"><b>{candCount}</b><span>息壤候选 · 待审</span></a>
<a className="hero__stat" href="/agent-workspace"><b>{calls.length}</b><span>天工 · 工具调用</span></a>
</div>
</div>
<section className="section">
<div className="section__head">
<span className="cn">镜生<span className="cn__plain">你的角色</span></span>
<a className="more" href="/characters">管理 →</a>
</div>
<PanelState pending={characters.isPending} error={characters.error} onRetry={() => characters.refetch()} empty={chars.length === 0}>
<div className="grid-cards">
{chars.map((c) => (
<a className="card char-card" key={c.id} href="/characters">
<div className="char-card__top">
<span className="avatar">{c.name.slice(0, 1)}</span>
<div>
<div className="char-card__name">{c.name}</div>
<div style={{ fontSize: 12, color: "var(--text-3)" }}>
{c.status === "draft" ? "灵胚 · 未启镜" : c.status}
</div>
</div>
</div>
<p className="char-card__line">
{c.status === "draft"
? "灵胚是未创建完成的角色草稿——可删除,不等于角色死亡。"
: "角色运行中。"}
</p>
<div className="char-card__foot">
<span className={`badge badge--${c.status === "draft" ? "warn" : "info"}`}>
<span className="badge__dot" />
{c.status === "draft" ? "灵胚 · 草稿" : `${c.status}`}
</span>
<span className="char-card__time">{new Date(c.created_at).toLocaleDateString("zh-CN")}</span>
</div>
</a>
))}
</div>
</PanelState>
</section>
<section className="section card">
<div className="card__head">
<span className="cn">待你决定<span className="cn__plain">审批与候选</span></span>
</div>
<div className="list-row">
<span className="badge" style={{ flex: "none" }}>息壤候选 ×{candCount}</span>
<div className="list-row__body">
<div className="list-row__title">{candCount > 0 ? "有候选等待你的归真决定" : "暂无待审候选"}</div>
<div className="list-row__sub">归真后才会影响未来上下文</div>
</div>
<a className="btn btn--outline btn--sm" href="/memory">去审阅</a>
</div>
</section>
</div>
);
}
/* ---------- 形仪:真实资产列表 ---------- */
export function AvatarPanel() {
const avatars = useQuery({
queryKey: ["avatars"],
queryFn: () => api<{ data: { id: string; kind?: string; status?: string; [k: string]: unknown }[] }>("/v1/avatars"),
});
const list = avatars.data?.data ?? [];
return (
<section className="section">
<div className="section__head">
<span className="cn">资产库<span className="cn__plain">图像 / 声音 / Live2D / 3D</span></span>
</div>
<PanelState pending={avatars.isPending} error={avatars.error} onRetry={() => avatars.refetch()} empty={list.length === 0}>
<div className="asset-grid">
{list.map((a) => (
<div className="asset-card" key={a.id}>
<div className="asset-thumb" style={{ background: "linear-gradient(150deg,#233b37,#3f7a6e 60%,#2a4d47)" }}>
<span className="glyph">{String(a.kind ?? "形").slice(0, 1)}</span>
</div>
<div className="asset-body">
<span className="asset-name mono">{a.id.slice(-10)}</span>
<span className="asset-sub">{String(a.kind ?? "avatar")} · {String(a.status ?? "")}</span>
</div>
</div>
))}
</div>
</PanelState>
</section>
);
}
/* ---------- 天工阁:能力与调用 ---------- */
export function RunsPanel() {
const tools = useQuery({
queryKey: ["tools"],
queryFn: () => api<{ data: { name: string; description: string; risk_level: string; requires_approval: boolean }[] }>("/v1/tools"),
});
const calls = useToolCalls();
const toolList = tools.data?.data ?? [];
const callList = calls.data?.data ?? [];
return (
<div>
<div className="realm-banner" role="note">
天工是功能执行者,不冒充角色身份;工具结果只是观察,采用前需验证。
</div>
<section className="section">
<div className="section__head"><span className="cn">工筹<span className="cn__plain">可用工具</span></span></div>
<PanelState pending={tools.isPending} error={tools.error} onRetry={() => tools.refetch()} empty={toolList.length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{toolList.map((t) => (
<div className="list-row" key={t.name}>
<div className="list-row__body">
<div className="list-row__title mono">{t.name}</div>
<div className="list-row__sub">{t.description}</div>
</div>
<span className={`badge badge--${t.requires_approval ? "warn" : "ok"}`} style={{ fontSize: 11 }}>
<span className="badge__dot" />{t.requires_approval ? "须请命" : "只读"}
</span>
</div>
))}
</div>
</PanelState>
</section>
<section className="section">
<div className="section__head"><span className="cn">轨迹<span className="cn__plain">工具调用记录</span></span></div>
<PanelState pending={calls.isPending} error={calls.error} onRetry={() => calls.refetch()} empty={callList.length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{callList.map((c: ToolCall) => (
<div className="list-row" key={String(c.id)}>
<div className="list-row__body">
<div className="list-row__title mono">{String(c.tool_name ?? c.id.slice(-8))}</div>
<div className="list-row__sub">{String(c.status ?? "")}</div>
</div>
</div>
))}
</div>
</PanelState>
</section>
</div>
);
}
/* ---------- 神识阁:观测(模型路由选择端点待 WP-05) ---------- */
export function GatewayPanel() {
const metrics = useQuery({
queryKey: ["metrics"],
queryFn: async () => {
const res = await fetch("/api/v1/metrics");
if (!res.ok) throw new Error(`metrics ${res.status}`);
return res.text();
},
});
return (
<div>
<div className="realm-banner" role="note">
神识是可替换推理能力,不等于角色身份。首个可见输出后不切换 Provider。
</div>
<section className="section card">
<div className="card__head"><span className="cn">运行观测<span className="cn__plain">/v1/metrics</span></span></div>
<PanelState pending={metrics.isPending} error={metrics.error} onRetry={() => metrics.refetch()}>
<pre className="code" style={{ maxHeight: 260, overflow: "auto" }}>{metrics.data}</pre>
</PanelState>
</section>
<section className="section card">
<div className="card__head"><span className="cn">逻辑模型与路由</span></div>
<p style={{ fontSize: 13, color: "var(--text-3)" }}>
逻辑模型目录与路由继承链的读取端点属 WP-05(模型网关与运行观测),暴露后在此呈现五层继承与 BYOK 管理。
</p>
</section>
</div>
);
}
/* ---------- 司命台:审批与治理(真实数据 + 边界) ---------- */
export function GovernancePanel() {
const calls = useToolCalls();
const approve = useApproveToolCall();
const list = calls.data?.data ?? [];
const history = useQuery({
queryKey: ["tool-calls-history"],
queryFn: async () => {
const [ok, denied] = await Promise.all([
api<{ data: ToolCall[] }>("/v1/tool-calls?status_filter=succeeded"),
api<{ data: ToolCall[] }>("/v1/tool-calls?status_filter=denied"),
]);
return [...(ok.data ?? []), ...(denied.data ?? [])];
},
});
return (
<div>
<div className="realm-banner" role="note">
治理不支配角色或用户数据;隐藏推理、密钥与凭据永不进入诊断与审计展示。
</div>
<section className="section">
<div className="section__head"><span className="cn">请命<span className="cn__plain">等待你确认的工具调用</span></span></div>
<PanelState pending={calls.isPending} error={calls.error} onRetry={() => calls.refetch()} empty={list.length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{list.map((c: ToolCall) => {
const pending = String(c.status ?? "") === "pending_approval";
return (
<div className="list-row" key={String(c.id)}>
<div className="list-row__body">
<div className="list-row__title mono">{String(c.tool_name ?? c.id.slice(-8))}</div>
<div className="list-row__sub">
{String(JSON.stringify(c.input ?? {}))}{c.output ? ` → ${String(c.output).slice(0, 60)}` : ""}
</div>
</div>
{pending ? (
<>
<button
className="btn btn--cinnabar btn--sm"
disabled={approve.isPending}
onClick={() => approve.mutate({ id: String(c.id), approve: true })}
>
允许(朱批)
</button>
<button
className="btn btn--outline btn--sm"
disabled={approve.isPending}
onClick={() => approve.mutate({ id: String(c.id), approve: false })}
>
拒绝
</button>
</>
) : (
<span className={`badge badge--${c.status === "executed" || c.status === "completed" ? "ok" : c.status === "rejected" ? "muted" : "muted"}`} style={{ fontSize: 11 }}>
<span className="badge__dot" />{String(c.status)}
</span>
)}
</div>
);
})}
</div>
</PanelState>
</section>
<section className="section">
<div className="section__head"><span className="cn">朱批留痕<span className="cn__plain">已处理的调用</span></span></div>
<PanelState pending={history.isPending} error={history.error} empty={(history.data ?? []).length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{(history.data ?? []).map((c: ToolCall) => (
<div className="list-row" key={String(c.id)}>
<div className="list-row__body">
<div className="list-row__title mono">{String(c.tool_name)}</div>
<div className="list-row__sub">{String(JSON.stringify(c.output ?? c.input ?? {})).slice(0, 90)}</div>
</div>
<span className={`badge badge--${c.status === "succeeded" ? "ok" : "muted"}`} style={{ fontSize: 11 }}>
<span className="badge__dot" />{String(c.status)}
</span>
</div>
))}
</div>
</PanelState>
</section>
<section className="section card">
<div className="card__head"><span className="cn">诊断授权<span className="cn__plain">双重授权</span></span></div>
<p style={{ fontSize: 12.5, color: "var(--text-2)" }}>
读取 Run 详细诊断需普通认证之外叠加短期诊断令牌:绑定单个 Run、单次使用、默认 15 分钟。
需诊断时从镜树/天工阁的具体 Run 发起(POST /v1/runs/{"{run_id}"}/diagnostic-grants)。
</p>
</section>
</div>
);
}
/* ---------- 设置:登出 + 财务(真实)+ 其余空态 ---------- */
export function SettingsPanel() {
const logout = useLogout();
const finance = useQuery({
queryKey: ["finance-summary"],
queryFn: () => api<{ totals_minor: Record<string, number>; balanced: boolean }>("/v1/finance/summary"),
});
return (
<div>
<section className="section card">
<div className="card__head"><span className="cn">会话</span></div>
<div className="set-row">
<div className="set-row__body">
<div className="set-row__t">退出当前会话</div>
<div className="set-row__d">登出并撤销 Session Cookie(DELETE /v1/auth/sessions/current)</div>
</div>
<button
className="btn btn--outline btn--sm"
disabled={logout.isPending}
onClick={async () => {
await logout.mutateAsync().catch(() => undefined);
clearCsrf();
window.location.href = "/login";
}}
>
{logout.isPending ? "登出中…" : "登出"}
</button>
</div>
</section>
<section className="section card">
<div className="card__head"><span className="cn">结算<span className="cn__plain">财务摘要</span></span></div>
<PanelState pending={finance.isPending} error={finance.error}>
<div style={{ display: "flex", gap: 24, flexWrap: "wrap" }}>
{Object.entries(finance.data?.totals_minor ?? {}).map(([cur, minor]) => (
<div key={cur}>
<div style={{ fontSize: 11.5, color: "var(--text-3)" }}>{cur.toUpperCase()}</div>
<div className="price">{`¥${(minor / 100).toFixed(2)}`}</div>
</div>
))}
<span className="badge badge--ok"><span className="badge__dot" />账平 · {String(finance.data?.balanced ?? false)}</span>
</div>
</PanelState>
</section>
<section className="section card">
<div className="card__head"><span className="cn">数据权与私箧</span></div>
<p style={{ fontSize: 12.5, color: "var(--text-3)" }}>
导出、撤权、删除与私箧管理端点属后续工作包(WP-01 私箧 / WP-02 可携导出),暴露后在此接入。
</p>
</section>
</div>
);
}
/* ---------- 器谱:真实空态(设备 API 属 WP-10) ---------- */
export function DevicesPanel() {
return (
<div>
<div className="realm-banner" role="note">
设备能力同时受系统权限与授契约束;采集有持续指示,可即时撤销。
</div>
<div className="card">
<div className="card__head"><span className="cn">形器<span className="cn__plain">登记设备</span></span></div>
<p style={{ fontSize: 13.5, color: "var(--text-2)" }}>
设备登记与 DeviceGrant 端点属 WP-10(Web、桌面、移动与感知),尚未暴露。
</p>
<p style={{ fontSize: 12.5, color: "var(--text-3)", marginTop: 6 }}>
接入后此处显示:形器列表、授契(闻声 / 观屏 / 问路 · 范围与期限)、通感进行中、直白权限请求。
</p>
</div>
</div>
);
}
@@ -0,0 +1,77 @@
"use client";
import { useState } from "react";
import { PanelState, useInstallPlugin, usePlugins, useTools } from "@/lib/queries";
import type { PluginMeta, ToolMeta } from "@/lib/api";
export function PluginsPanel() {
const plugins = usePlugins();
const tools = useTools();
const install = useInstallPlugin();
const [installed, setInstalled] = useState<string[]>([]);
const pluginList = plugins.data?.data ?? [];
const toolList = tools.data?.data ?? [];
return (
<div>
<div className="realm-banner" role="note">
购买不等于安装,安装不等于启用,启用不等于授予新增权限。安全内核不可插件化。
</div>
<section className="section">
<div className="section__head">
<span className="cn">百工<span className="cn__plain">可用能力集合 · 能力可发现不等于已授权</span></span>
</div>
<PanelState pending={tools.isPending} error={tools.error} onRetry={() => tools.refetch()} empty={toolList.length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{toolList.map((t: ToolMeta) => (
<div className="list-row" key={t.name}>
<div className="list-row__body">
<div className="list-row__title mono">{t.name}</div>
<div className="list-row__sub">{t.description}</div>
</div>
<span className={`badge badge--${t.requires_approval ? "warn" : "ok"}`} style={{ fontSize: 11 }}>
<span className="badge__dot" />{t.requires_approval ? "须请命" : t.risk_level === "read_only" ? "只读" : t.risk_level}
</span>
<span className={`badge badge--${t.enabled ? "ok" : "muted"}`} style={{ fontSize: 11 }}>
<span className="badge__dot" />{t.enabled ? "已启用" : "未启用"}
</span>
</div>
))}
</div>
</PanelState>
</section>
<section className="section">
<div className="section__head">
<span className="cn">插件<span className="cn__plain">注册表</span></span>
</div>
<PanelState pending={plugins.isPending} error={plugins.error} onRetry={() => plugins.refetch()} empty={pluginList.length === 0}>
<div className="card" style={{ padding: "6px 18px" }}>
{pluginList.map((p: PluginMeta) => (
<div className="list-row" key={p.name}>
<div className="list-row__body">
<div className="list-row__title mono">{p.name}</div>
<div className="list-row__sub">{String(p.description ?? p.kind ?? "plugin")}</div>
</div>
<button
className="btn btn--outline btn--sm"
disabled={install.isPending || installed.includes(p.name)}
onClick={async () => {
await install.mutateAsync(p.name);
setInstalled((prev) => [...prev, p.name]);
}}
>
{installed.includes(p.name) ? "已安装" : "安装"}
</button>
</div>
))}
</div>
<p style={{ fontSize: 12, color: "var(--text-3)", marginTop: 8 }}>
插件注册表为空时表示本部署尚无已注册插件;安装不自动启用,启用不授予新增权限。
</p>
</PanelState>
</section>
</div>
);
}
@@ -0,0 +1,55 @@
"use client";
import { useEffect, useState } from "react";
import { ADMIN_NAV } from "@/lib/nav";
import { readSession } from "@/lib/api";
import { Sidebar, useBrandEgg } from "@/components/shell/Sidebar";
import { ThemeButton } from "@/lib/theme";
import { RequireAuth } from "./RequireAuth";
/** 管理面外壳:独立侧栏(锚点导航)+ 管理员身份 + 双人授权提示 */
export function AdminShell({ children }: { children: React.ReactNode }) {
const [folded, setFolded] = useState(false);
const [foot, setFoot] = useState<{ char: string; name: string }>({ char: "司", name: "加载中…" });
useEffect(() => {
const s = readSession();
setFoot({ char: "司", name: s.role === "admin" ? `${s.email.split("@")[0] || "dev"} · 管理员` : "普通用户" });
}, []);
const { onBrandClick, brandState, toast } = useBrandEgg();
useEffect(() => {
try {
const saved = localStorage.getItem("hunjing-sidebar");
if (saved === "collapsed") setFolded(true);
} catch {
/* 忽略 */
}
}, []);
return (
<div className={folded ? "app app--fold" : "app"}>
{toast && <div className="egg-toast is-on">{toast}</div>}
<Sidebar
groups={ADMIN_NAV}
folded={folded}
onToggleFold={() => setFolded((v) => !v)}
onBrandClick={onBrandClick}
brandState={brandState}
brandSub="司命台 · 管理面"
foot={foot}
below={
<div className="admin-back">
<a href="/home">← 返回用户界面</a>
</div>
}
/>
<main className="main">
<RequireAuth>{children}</RequireAuth>
</main>
</div>
);
}
export function AdminThemeButton() {
return <ThemeButton />;
}
@@ -0,0 +1,52 @@
"use client";
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { NAV_GROUPS } from "@/lib/nav";
import { Sidebar, useBrandEgg } from "./Sidebar";
import { RequireAuth } from "./RequireAuth";
/** 应用外壳:主侧栏(可折叠,localStorage 记忆)+ 主区 */
export function AppShell({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const [folded, setFolded] = useState(false);
const { onBrandClick, brandState, toast } = useBrandEgg();
useEffect(() => {
try {
const saved = localStorage.getItem("hunjing-sidebar");
if (saved === "collapsed") setFolded(true);
} catch {
/* 忽略 */
}
}, []);
const toggleFold = () => {
setFolded((prev) => {
const next = !prev;
try {
localStorage.setItem("hunjing-sidebar", next ? "collapsed" : "expanded");
} catch {
/* 忽略 */
}
return next;
});
};
return (
<div className={folded ? "app app--fold" : "app"}>
{toast && <div className="egg-toast is-on">{toast}</div>}
<Sidebar
groups={NAV_GROUPS}
activeHref={pathname}
folded={folded}
onToggleFold={toggleFold}
onBrandClick={onBrandClick}
brandState={brandState}
/>
<main className="main">
<RequireAuth>{children}</RequireAuth>
</main>
</div>
);
}
@@ -0,0 +1,46 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { api, ApiError, type CursorPage } from "@/lib/api";
/**
* 登录守卫:进入应用外壳时校验会话(GET /v1/characters),
* 未登录(401/403)跳转登录页。校验期间显示轻量占位,不闪烁内容。
*/
export function RequireAuth({ children }: { children: React.ReactNode }) {
const router = useRouter();
const check = useQuery({
queryKey: ["auth-check"],
queryFn: () => api<CursorPage<unknown>>("/v1/characters?limit=1"),
retry: false,
staleTime: 30_000,
});
if (check.isError) {
const status = check.error instanceof ApiError ? check.error.status : 0;
if (status === 401 || status === 403) {
router.replace("/login");
return (
<div style={{ display: "grid", placeItems: "center", minHeight: "60vh" }}>
<p style={{ fontSize: 14, color: "var(--text-3)" }}>未登入,正在前往登录页…</p>
</div>
);
}
return (
<div style={{ display: "grid", placeItems: "center", minHeight: "60vh" }}>
<p style={{ fontSize: 14, color: "var(--err)" }}>会话校验失败,请刷新重试</p>
</div>
);
}
if (check.isPending) {
return (
<div style={{ display: "grid", placeItems: "center", minHeight: "60vh" }}>
<p style={{ fontSize: 14, color: "var(--text-3)" }}>正在校验会话…</p>
</div>
);
}
return <>{children}</>;
}
+136
View File
@@ -0,0 +1,136 @@
"use client";
import { ChevronsLeft } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useRef, useState } from "react";
import type { NavGroup } from "@/lib/nav";
import { ThemeButton } from "@/lib/theme";
/** 镜启彩蛋诗签(承接 main 分支旧版设计) */
const EGG_VERSES = [
"镜中有镜,心外无心。",
"汝来看此镜时,此镜亦照汝。",
"一念起,万镜生。",
"镜非鉴物,乃鉴心也。",
"幻由心生,镜随心转。",
"铜镜本无字,照尽往来人。",
"且向镜中看,那个是谁?",
];
interface SidebarProps {
groups: NavGroup[];
activeHref?: string;
folded: boolean;
onToggleFold: () => void;
onBrandClick: (e: React.MouseEvent) => void;
brandState: { tick: boolean; pulse: boolean };
brandSub?: string;
foot?: { char: string; name: string };
below?: React.ReactNode;
}
export function Sidebar({ groups, activeHref, folded, onToggleFold, onBrandClick, brandState, brandSub = "Hunjing", foot, below }: SidebarProps) {
return (
<aside className="sidebar">
<div
className={`brand${brandState.tick ? " tick" : ""}${brandState.pulse ? " egg-pulse" : ""}`}
onClick={onBrandClick}
role="link"
tabIndex={0}
aria-label="魂镜 · 单击回目录,五连击触发镜启彩蛋"
>
<div className="brand__mark" aria-hidden="true" />
<div className="brand__text">
<div className="brand__name">魂镜</div>
<div className="brand__sub">{brandSub}</div>
</div>
</div>
<nav className="side-nav" aria-label="主导航">
{groups.map((group) => (
<div className="nav-group" key={group.title}>
<div className="nav-group__title">{group.title}</div>
{group.items.map((item) => {
const Icon = item.icon;
const active = item.href === activeHref;
return (
<a
key={item.href + item.label}
className={active ? "nav-item is-active" : "nav-item"}
href={item.href}
aria-current={active ? "page" : undefined}
>
<Icon />
<span className="cn">{item.label}</span>
<span className="nav-plain">{item.plain}</span>
</a>
);
})}
</div>
))}
</nav>
<div className="side-foot">
<button
type="button"
className="icon-btn side-fold-btn"
onClick={onToggleFold}
aria-label={folded ? "展开侧栏" : "收起侧栏"}
title="收起 / 展开侧栏(记忆偏好)"
>
<ChevronsLeft />
</button>
{foot ? (
<>
<span className={`avatar avatar--sm avatar--user`}>{foot.char}</span>
<span className="side-foot__name">{foot.name}</span>
</>
) : (
<>
<span className="avatar avatar--sm avatar--user">舟</span>
<span className="side-foot__name">云舟</span>
</>
)}
<ThemeButton />
</div>
{below}
</aside>
);
}
/** 品牌彩蛋逻辑:单击延迟回目录;首击 + 4 次连点 = 镜启 */
export function useBrandEgg() {
const router = useRouter();
const seq = useRef(0);
const last = useRef(0);
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
const [toast, setToast] = useState<string | null>(null);
const [pulse, setPulse] = useState(false);
const [tick, setTick] = useState(false);
useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);
const onBrandClick = (e: React.MouseEvent) => {
e.preventDefault();
if (timer.current) clearTimeout(timer.current);
const now = Date.now();
const fast = now - last.current < 380;
last.current = now;
setTick(true);
setTimeout(() => setTick(false), 300);
if (fast) {
seq.current += 1;
if (seq.current >= 4) {
seq.current = 0;
setPulse(true);
setTimeout(() => setPulse(false), 1400);
const verse = EGG_VERSES[Math.floor(Math.random() * EGG_VERSES.length)];
setToast(`镜启 · ${verse}`);
setTimeout(() => setToast(null), 2500);
}
return;
}
seq.current = 0;
timer.current = setTimeout(() => router.push("/"), 380);
};
return { onBrandClick, brandState: { tick, pulse }, toast };
}
+19
View File
@@ -0,0 +1,19 @@
import { ThemeButton } from "@/lib/theme";
interface TopbarProps {
crumbs: React.ReactNode;
extra?: React.ReactNode;
}
/** 顶栏:面包屑 + 原型标记 + 主题切换 */
export function Topbar({ crumbs, extra }: TopbarProps) {
return (
<header className="topbar">
<div className="crumbs">{crumbs}</div>
{extra}
<div className="topbar__spacer" />
<span className="prototype-tag">静态原型 · 待联调</span>
<ThemeButton />
</header>
);
}
+39
View File
@@ -0,0 +1,39 @@
import type { ReactNode } from "react";
/* ---------- 面包屑 ---------- */
export function Crumbs({ cn, here }: { cn: string; here?: string }) {
return (
<>
<span className="cn">{cn}</span>
{here && (
<>
<span className="crumbs__sep">/</span>
<span className="here">{here}</span>
</>
)}
</>
);
}
/* ---------- 状态徽章:文化状态 · 直白状态 · 稳定值 ---------- */
type BadgeTone = "ok" | "info" | "warn" | "err" | "muted";
export function Badge({
tone = "muted",
children,
stable,
style,
}: {
tone?: BadgeTone;
children: ReactNode;
stable?: string;
style?: React.CSSProperties;
}) {
return (
<span className={`badge badge--${tone}`} style={style}>
<span className="badge__dot" />
{children}
{stable && <span className="mono">{stable}</span>}
</span>
);
}
+228
View File
@@ -0,0 +1,228 @@
/**
* API 客户端(开发档联调):
* - 同源 /api 前缀经 Next rewrites 代理至后端,Session Cookie 自动携带;
* - 写请求带 Hunjing-CSRF-Token(双提交,契约头名);
* - 错误统一映射为 ApiError(status + code + message),不匹配错误字符串。
*/
const BASE = "/api";
export class ApiError extends Error {
constructor(
public status: number,
public code: string | null,
message: string,
) {
super(message);
this.name = "ApiError";
}
}
function csrfToken(): string | null {
try {
return localStorage.getItem("hunjing-csrf");
} catch {
return null;
}
}
export function storeCsrf(token: string) {
try {
localStorage.setItem("hunjing-csrf", token);
} catch {
/* 忽略 */
}
}
export function storeSession(session: { role?: string; email?: string }) {
try {
localStorage.setItem("hunjing-role", session.role ?? "user");
localStorage.setItem("hunjing-email", session.email ?? "");
} catch {
/* 忽略 */
}
}
export function readSession(): { role: string; email: string } {
try {
return {
role: localStorage.getItem("hunjing-role") ?? "user",
email: localStorage.getItem("hunjing-email") ?? "",
};
} catch {
return { role: "user", email: "" };
}
}
export function clearCsrf() {
try {
localStorage.removeItem("hunjing-csrf");
localStorage.removeItem("hunjing-role");
localStorage.removeItem("hunjing-email");
} catch {
/* 忽略 */
}
}
export async function api<T>(
path: string,
init: RequestInit & { json?: unknown } = {},
): Promise<T> {
const headers = new Headers(init.headers);
if (init.json !== undefined) {
headers.set("Content-Type", "application/json");
}
const method = (init.method ?? "GET").toUpperCase();
if (method !== "GET" && method !== "HEAD") {
const csrf = csrfToken();
if (csrf) headers.set("Hunjing-CSRF-Token", csrf);
}
const res = await fetch(`${BASE}${path}`, {
...init,
headers,
body: init.json !== undefined ? JSON.stringify(init.json) : init.body,
credentials: "same-origin",
});
if (res.status === 204) return undefined as T;
const payload = await res.json().catch(() => null);
if (!res.ok) {
const detail = (payload as { detail?: string } | null)?.detail;
const code =
(payload as { error?: { code?: string } } | null)?.error?.code ?? null;
throw new ApiError(res.status, code, detail ?? `请求失败(${res.status})`);
}
return payload as T;
}
/* ---------- 联调用的领域类型(源自运行时 OpenAPI;正式契约冻结后切 openapi-typescript 生成) ---------- */
export interface SessionInfo {
csrf_token: string;
user_id: string;
role: string;
email: string;
}
export interface Character {
id: string;
name: string;
status: string;
origin: string;
based_on_real_person: boolean;
identity_baseline: Record<string, unknown>;
drives: unknown[];
current_version_id: string | null;
created_at: string;
}
export interface CursorPage<T> {
data: T[];
next_cursor: string | null;
has_more: boolean;
}
/* ---------- 联调领域类型(按后端实测形状;正式契约冻结后切生成类型) ---------- */
export interface Memory {
id: string;
character_id: string | null;
kind: string;
sensitivity: string;
realm: string | null;
payload: Record<string, unknown>;
source: string;
status: string;
created_at: string;
}
export interface MemoryCandidate {
id: string;
character_id: string | null;
kind: string;
sensitivity: string;
realm: string | null;
payload: Record<string, unknown>;
source: string;
status: string;
created_at: string;
}
export interface ContentBlockLike {
type: string;
[k: string]: unknown;
}
export interface Message {
id: string;
conversation_id: string;
branch_id: string | null;
role: string;
content: ContentBlockLike[];
parser_version: string | null;
created_at: string;
canonical_scope?: string | null;
}
export interface Conversation {
id: string;
title: string;
[k: string]: unknown;
}
export interface Branch {
id: string;
[k: string]: unknown;
}
export interface PluginMeta {
name: string;
[k: string]: unknown;
}
export interface CommunityContent {
id: string;
kind: string;
title: string | null;
body: Record<string, unknown>;
visibility: string;
status: string;
author_domain_id: string;
created_at: string;
}
export interface Listing {
id: string;
title: string;
price_minor: number | null;
currency: string | null;
status: string;
}
export interface Workspace {
id: string;
name: string;
owner_domain_id: string;
}
export interface ToolCall {
id: string;
[k: string]: unknown;
}
export interface AvatarAsset {
id: string;
[k: string]: unknown;
}
export interface FinanceSummary {
totals_minor: Record<string, number>;
balanced: boolean;
}
export interface ToolMeta {
name: string;
description: string;
risk_level: string;
requires_approval: boolean;
enabled: boolean;
}
+101
View File
@@ -0,0 +1,101 @@
import {
Activity,
BookOpen,
Database,
FileText,
GitBranch,
Globe,
Home,
Layers,
Lock,
Monitor,
Package,
Route,
Server,
Shield,
ShoppingBag,
SlidersHorizontal,
SquareKanban,
User,
Users,
} from "lucide-react";
export interface NavItem {
href: string;
label: string;
plain: string;
icon: React.ComponentType;
}
export interface NavGroup {
title: string;
items: NavItem[];
}
export const NAV_GROUPS: NavGroup[] = [
{
title: "常用",
items: [
{ href: "/home", label: "镜庭", plain: "首页", icon: Home },
{ href: "/conversation", label: "镜树", plain: "对话", icon: GitBranch },
{ href: "/characters", label: "镜生", plain: "角色", icon: User },
{ href: "/memory", label: "息壤", plain: "记忆", icon: BookOpen },
{ href: "/avatar", label: "形仪", plain: "形象", icon: Layers },
],
},
{
title: "创造",
items: [
{ href: "/agent-workspace", label: "天工阁", plain: "任务", icon: SquareKanban },
{ href: "/plugins", label: "百工坊", plain: "插件", icon: Package },
{ href: "/collaboration", label: "同契台", plain: "协作", icon: Users },
{ href: "/model-gateway", label: "神识阁", plain: "模型", icon: Route },
],
},
{
title: "世界",
items: [
{ href: "/community", label: "雅集", plain: "社区", icon: Globe },
{ href: "/marketplace", label: "云市", plain: "市场", icon: ShoppingBag },
],
},
{
title: "治理与账户",
items: [
{ href: "/governance", label: "司命台", plain: "治理", icon: Shield },
{ href: "/devices", label: "器谱", plain: "设备", icon: Monitor },
{ href: "/settings", label: "设置", plain: "账户", icon: SlidersHorizontal },
],
},
];
export const ADMIN_NAV: NavGroup[] = [
{
title: "运营",
items: [
{ href: "/admin#queue", label: "治理队列", plain: "", icon: SquareKanban },
{ href: "/admin#tickets", label: "支持工单", plain: "3", icon: FileText },
{ href: "/admin#users", label: "用户", plain: "", icon: Users },
{ href: "/admin#perms", label: "权限管理", plain: "", icon: Shield },
],
},
{
title: "部署",
items: [
{ href: "/admin#health", label: "服务健康", plain: "", icon: Server },
{ href: "/admin#backup", label: "迁移与备份", plain: "", icon: Database },
{ href: "/admin#queue", label: "插件治理", plain: "", icon: Package },
],
},
{
title: "观测",
items: [
{ href: "/admin#slo", label: "SLO 与指标", plain: "", icon: Activity },
{ href: "/admin#audit", label: "审计日志", plain: "", icon: Shield },
],
},
{
title: "边界",
items: [{ href: "/governance", label: "治理边界说明", plain: "", icon: Lock }],
},
];
+373
View File
@@ -0,0 +1,373 @@
"use client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useRef, useState } from "react";
import { api, type CursorPage } from "./api";
/* ---------- 通用四态外壳:加载 / 未登入 / 失败 / 内容 ---------- */
export function PanelState({
pending,
error,
onRetry,
children,
empty,
}: {
pending: boolean;
error: unknown;
onRetry?: () => void;
children: React.ReactNode;
empty?: boolean;
}) {
if (pending) {
return <p style={{ fontSize: 13, color: "var(--text-3)", padding: "18px 4px" }}>正在读取…</p>;
}
if (error) {
const status = (error as { status?: number }).status ?? 0;
if (status === 401 || status === 403) {
return (
<div style={{ padding: "20px 4px", textAlign: "center" }}>
<p style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 12 }}>尚未登入,无法读取你的镜域数据。</p>
<a className="btn btn--primary btn--sm" href="/login">去登入</a>
</div>
);
}
return (
<div style={{ padding: "18px 4px" }}>
<p style={{ fontSize: 13, color: "var(--err)", marginBottom: 6 }}>读取失败</p>
<p style={{ fontSize: 12, color: "var(--text-3)", marginBottom: 10 }}>
{(error as { message?: string }).message ?? "请稍后重试"}
</p>
{onRetry && <button className="btn btn--outline btn--sm" onClick={onRetry}>重试</button>}
</div>
);
}
if (empty) {
return (
<p style={{ fontSize: 13, color: "var(--text-3)", padding: "18px 4px" }}>
暂无数据——这里将显示你的真实内容。
</p>
);
}
return <>{children}</>;
}
export function useUnauthorized(v: unknown) {
return (v as { status?: number })?.status === 401;
}
/* ---------- 会话(镜树) ---------- */
export function useCreateConversation() {
const qc = useQueryClient();
return useMutation({
mutationFn: (body: { character_id?: string; title: string }) =>
api<{ id: string; title: string }>("/v1/conversations", { method: "POST", json: body }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["conversation"] }),
});
}
export function useConversation(id: string | null) {
return useQuery({
queryKey: ["conversation", id],
enabled: !!id,
queryFn: () => api<Record<string, unknown>>(`/v1/conversations/${id}`),
});
}
export function useMessages(id: string | null) {
return useQuery({
queryKey: ["messages", id],
enabled: !!id,
queryFn: () => api<CursorPage<import("./api").Message>>(`/v1/conversations/${id}/messages?limit=50`),
});
}
export interface SendMessageResult {
message: { id: string };
run: { id: string } | null;
}
export function useSendMessage(conversationId: string | null) {
const qc = useQueryClient();
return useMutation({
mutationFn: (text: string) =>
api<SendMessageResult>(`/v1/conversations/${conversationId}/messages`, {
method: "POST",
json: { content: [{ type: "text", text }], start_run: true },
headers: { "Idempotency-Key": crypto.randomUUID() },
}),
onSuccess: () => qc.invalidateQueries({ queryKey: ["messages", conversationId] }),
});
}
/* ---------- Run SSE:块级事件消费(重放持久化流,sequence 单调) ---------- */
export interface StreamBlock {
id: string;
type: string;
status: "streaming" | "completed" | "failed" | "cancelled";
text: string;
error?: string;
}
export interface RunStreamState {
runStatus: string;
blocks: StreamBlock[];
finalStatus: string | null;
done: boolean;
}
export function useRunEvents(runId: string | null, onDone?: (finalStatus: string | null) => void) {
const [state, setState] = useState<RunStreamState>({ runStatus: "connecting", blocks: [], finalStatus: null, done: false });
// onDone 经 ref 稳定化:避免内联回调导致事件流反复重连重置
const onDoneRef = useRef(onDone);
onDoneRef.current = onDone;
useEffect(() => {
if (!runId) return;
const ac = new AbortController();
setState({ runStatus: "connecting", blocks: [], finalStatus: null, done: false });
/** 契约事件归约(第 5 节):块级 started/delta/completed/failed + 终态 */
const applyEvent = (e: { type: string; data: Record<string, unknown> }) => {
setState((prev) => {
const next = { ...prev, blocks: [...prev.blocks] };
switch (e.type) {
case "run.status.changed":
next.runStatus = String(e.data.status ?? "");
break;
case "content.block.started": {
const b = (e.data.block ?? {}) as { id?: string; type?: string };
next.blocks.push({ id: String(b.id ?? crypto.randomUUID()), type: String(b.type ?? "text"), status: "streaming", text: "" });
break;
}
case "content.block.delta": {
const d = e.data as { block_id?: string; delta?: { text?: string } };
const idx = next.blocks.findIndex((x) => x.id === d.block_id);
const chunk = String(d.delta?.text ?? "");
if (idx >= 0) next.blocks[idx] = { ...next.blocks[idx], text: next.blocks[idx].text + chunk };
break;
}
case "content.block.completed": {
const b = (e.data.block ?? {}) as { id?: string };
const idx = next.blocks.findIndex((x) => x.id === b.id);
if (idx >= 0) next.blocks[idx] = { ...next.blocks[idx], status: "completed" };
break;
}
case "content.block.failed": {
const b = (e.data.block ?? {}) as { id?: string; type?: string };
const err = String(e.data.error ?? "unknown");
const idx = next.blocks.findIndex((x) => x.id === b.id);
const failed = { id: String(b.id ?? crypto.randomUUID()), type: String(b.type ?? "text"), status: "failed" as const, text: "", error: err };
if (idx >= 0) next.blocks[idx] = failed;
else next.blocks.push(failed);
break;
}
case "message.completed":
next.finalStatus = String(e.data.final_status ?? "");
break;
default:
break;
}
if (
e.type === "message.completed" ||
(e.type === "run.status.changed" && ["completed", "failed", "cancelled"].includes(next.runStatus))
) {
next.done = true;
}
return next;
});
};
const API_ORIGIN = process.env.NEXT_PUBLIC_API_ORIGIN ?? "http://127.0.0.1:8000";
const xhr = new XMLHttpRequest();
xhr.open("GET", `${API_ORIGIN}/v1/runs/${runId}/events`);
xhr.withCredentials = true;
xhr.setRequestHeader("Accept", "text/event-stream");
let processed = 0;
xhr.onprogress = () => {
// XHR 增量流式:responseText 为累积文本,按已处理游标切帧
const text = xhr.responseText;
const fresh = text.slice(processed);
processed = text.length;
const frames = fresh.split("\n\n");
for (const frame of frames) {
const line = frame.split("\n").find((l) => l.startsWith("data: "));
if (!line) continue;
try {
applyEvent(JSON.parse(line.slice(6)));
} catch {
/* 忽略无法解析的帧 */
}
}
};
xhr.onload = () => setState((prev) => (prev.done ? prev : { ...prev, done: true }));
xhr.onerror = () => {
setState((prev) => ({ ...prev, done: true, runStatus: prev.runStatus === "connecting" ? "recovering" : prev.runStatus }));
};
ac.signal.addEventListener("abort", () => xhr.abort());
xhr.send();
const doneCheck = setInterval(() => {
setState((prev) => {
if (prev.done) {
clearInterval(doneCheck);
xhr.abort();
onDoneRef.current?.(prev.finalStatus);
return prev;
}
return prev;
});
}, 250);
return () => {
ac.abort();
clearInterval(doneCheck);
};
}, [runId]);
return state;
}
/* ---------- 工具调用:发起与审批 ---------- */
export function useCreateToolCall(runId: string | null) {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ tool, input }: { tool: string; input: Record<string, unknown> }) =>
api(`/v1/runs/${runId}/tool-calls`, {
method: "POST",
json: { tool_name: tool, input },
headers: { "Idempotency-Key": crypto.randomUUID() },
}),
onSuccess: () => qc.invalidateQueries({ queryKey: ["tool-calls"] }),
});
}
export function useApproveToolCall() {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, approve }: { id: string; approve: boolean }) =>
api(`/v1/tool-calls/${id}/approvals`, { method: "POST", json: { approve } }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["tool-calls"] }),
});
}
/* ---------- 记忆(息壤) ---------- */
export function useMemories() {
return useQuery({
queryKey: ["memories"],
queryFn: () => api<CursorPage<import("./api").Memory>>("/v1/memories?limit=50"),
});
}
export function useMemoryCandidates() {
return useQuery({
queryKey: ["memory-candidates"],
queryFn: () => api<{ data: import("./api").MemoryCandidate[] }>("/v1/memories/candidates"),
});
}
export function useDecideCandidate() {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, approve }: { id: string; approve: boolean }) =>
api(`/v1/memories/candidates/${id}/decisions`, { method: "POST", json: { approve } }),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ["memory-candidates"] });
qc.invalidateQueries({ queryKey: ["memories"] });
},
});
}
/* ---------- 社区(雅集) ---------- */
export function useContents() {
return useQuery({
queryKey: ["community-contents"],
queryFn: () => api<CursorPage<import("./api").CommunityContent>>("/v1/community/contents?limit=20"),
});
}
export function useCreateContent() {
const qc = useQueryClient();
return useMutation({
mutationFn: (body: { kind: string; title: string }) =>
api("/v1/community/contents", { method: "POST", json: body }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["community-contents"] }),
});
}
/* ---------- 市场(云市) ---------- */
export function useListings() {
return useQuery({
queryKey: ["listings"],
queryFn: () => api<{ data: import("./api").Listing[] }>("/v1/marketplace/listings"),
});
}
export function useCreateListing() {
const qc = useQueryClient();
return useMutation({
mutationFn: (body: { title: string; price_minor: number; currency: string }) =>
api("/v1/marketplace/listings", { method: "POST", json: body }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["listings"] }),
});
}
/* ---------- 协作(同契台) ---------- */
export function useCreateWorkspace() {
return useMutation({
mutationFn: (body: { name: string }) =>
api<import("./api").Workspace>("/v1/workspaces", { method: "POST", json: body }),
});
}
/* ---------- 插件(百工坊) ---------- */
export function usePlugins() {
return useQuery({
queryKey: ["plugins"],
queryFn: () => api<{ data: import("./api").PluginMeta[] }>("/v1/plugins"),
});
}
export function useInstallPlugin() {
return useMutation({
mutationFn: (name: string) =>
api(`/v1/plugins/${name}/installations`, { method: "POST", json: {} }),
});
}
/* ---------- 工具与审批(天工阁 / 司命台) ---------- */
export function useToolCalls() {
return useQuery({
queryKey: ["tool-calls"],
queryFn: () => api<CursorPage<import("./api").ToolCall>>("/v1/tool-calls?limit=30"),
});
}
export function useTools() {
return useQuery({
queryKey: ["tools"],
queryFn: () => api<{ data: import("./api").ToolMeta[] }>("/v1/tools"),
});
}
/* ---------- 形仪 ---------- */
export function useAvatars() {
return useQuery({
queryKey: ["avatars"],
queryFn: () => api<{ data: import("./api").AvatarAsset[] }>("/v1/avatars"),
});
}
/* ---------- 财务(设置 / 云市) ---------- */
export function useFinanceSummary() {
return useQuery({
queryKey: ["finance-summary"],
queryFn: () => api<import("./api").FinanceSummary>("/v1/finance/summary"),
});
}
/* ---------- 登出 ---------- */
export function useLogout() {
return useMutation({
mutationFn: () => api<void>("/v1/auth/sessions/current", { method: "DELETE" }),
});
}
+16
View File
@@ -0,0 +1,16 @@
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
export function QueryProvider({ children }: { children: React.ReactNode }) {
const [client] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: { retry: 1, refetchOnWindowFocus: false, staleTime: 15_000 },
},
}),
);
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
}
+56
View File
@@ -0,0 +1,56 @@
"use client";
import { Moon } from "lucide-react";
import { createContext, useCallback, useContext, useEffect, useState } from "react";
type Theme = "dark" | "light";
const ThemeContext = createContext<{ theme: Theme; toggle: () => void }>({
theme: "dark",
toggle: () => {},
});
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>("dark");
useEffect(() => {
const saved = localStorage.getItem("hunjing-theme");
if (saved === "light" || saved === "dark") {
setTheme(saved);
document.documentElement.setAttribute("data-theme", saved);
}
}, []);
const toggle = useCallback(() => {
setTheme((prev) => {
const next: Theme = prev === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", next);
try {
localStorage.setItem("hunjing-theme", next);
} catch {
/* 忽略 */
}
return next;
});
}, []);
return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>;
}
export function useTheme() {
return useContext(ThemeContext);
}
export function ThemeButton() {
const { theme, toggle } = useTheme();
return (
<button
type="button"
className="icon-btn"
onClick={toggle}
aria-label={theme === "dark" ? "切换到月白主题" : "切换到墨色主题"}
>
<Moon />
</button>
);
}
+8
View File
@@ -0,0 +1,8 @@
import type {} from "react";
declare module "react" {
interface CSSProperties {
/** CSS 自定义属性(原型内联样式中大量使用 var(--token)) */
[key: `--${string}`]: string | number | undefined;
}
}