fad9760265
- 新增 Node 服务端(Hono + Hocuspocus + node:sqlite),数据保存在服务器 - 基于 Yjs 的多端实时同步,本机 IndexedDB 离线缓存,撤销只针对本机操作 - 空间(标签)有独立地址,增删改通过接口同步到所有设备 - 口令登录(签名 Cookie 会话、登录限流、同源校验) - 图片上传到服务器,过大照片上传前自动压缩 - 历史版本(自动/手动保存、一键恢复)、跨空间搜索、在线设备 - 随机油画加载动画(Painting Loaders,本地收录 p5 / p5.brush) - 选中样式与拖拽性能优化:拖动结束后才同步位置 - Docker / docker-compose 部署,README 与 CLAUDE.md 更新
100 lines
3.1 KiB
HTML
100 lines
3.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<link rel="icon" href="/favicon.ico">
|
|
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
|
<link rel="apple-touch-icon" href="/icon.svg">
|
|
<link rel="manifest" href="/manifest.webmanifest">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<meta name="theme-color" content="#f4f3ee">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-title" content="无界笺">
|
|
<meta name="description" content="无界笺是一张可以无限延伸、在所有设备间同步的灵感平面。">
|
|
<title>无界笺 · Boundless</title>
|
|
<style>
|
|
/* 启动画面:在主程序下载完成之前就显示,样式内联避免闪烁 */
|
|
#boot {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 1000;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 22px;
|
|
background: #f4f3ee;
|
|
color: #7b7b73;
|
|
font-family: Georgia, 'Songti SC', 'STSong', serif;
|
|
transition: opacity 480ms ease;
|
|
}
|
|
#boot.boot--gone {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
#boot .boot__tile {
|
|
position: relative;
|
|
width: min(232px, 58vw);
|
|
aspect-ratio: 1;
|
|
overflow: hidden;
|
|
border: 1px solid rgba(29, 30, 29, 0.08);
|
|
border-radius: 14px;
|
|
background: #efebe2;
|
|
box-shadow: 0 1px 2px rgba(27, 26, 23, 0.04), 0 18px 40px -22px rgba(27, 26, 23, 0.32);
|
|
transition: background 600ms ease;
|
|
}
|
|
#boot iframe {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 0;
|
|
opacity: 0;
|
|
transition: opacity 600ms ease;
|
|
}
|
|
#boot.boot--painting iframe {
|
|
opacity: 1;
|
|
}
|
|
#boot .boot__text {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 13px;
|
|
letter-spacing: 0.06em;
|
|
}
|
|
#boot .boot__caption {
|
|
min-height: 1.2em;
|
|
color: #a3a39a;
|
|
font-size: 11px;
|
|
font-style: italic;
|
|
letter-spacing: 0.04em;
|
|
opacity: 0;
|
|
transition: opacity 600ms ease;
|
|
}
|
|
#boot.boot--painting .boot__caption {
|
|
opacity: 1;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="boot" role="status" aria-live="polite">
|
|
<div class="boot__tile" aria-hidden="true">
|
|
<iframe src="/painterly/index.html" title="加载动画" tabindex="-1"></iframe>
|
|
</div>
|
|
<div class="boot__text">
|
|
<span class="boot__status">正在展开你的平面…</span>
|
|
<span class="boot__caption"></span>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// 保护:页面被嵌进 iframe 时不再嵌套加载动画,避免无限递归
|
|
if (window.top !== window.self) {
|
|
document.querySelector('#boot iframe')?.remove()
|
|
}
|
|
</script>
|
|
<div id="app"></div>
|
|
<script type="module" src="/src/main.ts"></script>
|
|
</body>
|
|
</html>
|