cdrop — 跨 OS 剪贴板与文件传输服务
Commilitia Drop:自托管的跨设备剪贴板同步与点对点文件传输。 - 后端 Go(chi / SQLite WAL / SSE Hub / WebRTC signaling + 状态机 / Relay ring buffer),编译进单个 distroless 镜像(前端 go:embed)。 - 前端 React + TanStack Router + Zustand,自实现 SSE + WebRTC P2P,NAT 受阻时回退服务端中继;聚珍(Juzhen)CJK 综合排版。 - 桌面端 Wails v2(macOS / Windows),瘦客户端复用 web。 - 鉴权 OIDC PKCE(自建 Casdoor 等),refresh_token 信封加密存系统密钥库;iOS Shortcut 用 HS256 scoped token。 架构文档与变更记录见 docs 分支(PROJECT_BRIEF / FRONTEND_DESIGN / CHANGELOG)。 本次为公开发布初始提交:完整开发历史(含部署细节)留存于私有归档,公开仓库自此干净起步。
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
.row
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-card);
|
||||
/* 描边:1px divider 全包,左侧 3px 状态色覆盖。 */
|
||||
border: 1px solid var(--divider);
|
||||
border-left: 3px solid var(--divider);
|
||||
box-shadow: var(--shadow-card);
|
||||
transition: border-color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.rowSunken
|
||||
{
|
||||
background: var(--surface-sunken);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 状态色编码:左色条按当前状态着色。
|
||||
- rowActive 传输中 → info (cyan)
|
||||
- rowStalled 卡住 → warn (yellow)
|
||||
- rowFailed 失败 / 取消 → error (red)
|
||||
- rowDone 完成 → success (green) */
|
||||
.rowActive { border-left-color: var(--info); }
|
||||
.rowStalled { border-left-color: var(--warn); }
|
||||
.rowFailed { border-left-color: var(--error); }
|
||||
.rowDone { border-left-color: var(--success); }
|
||||
|
||||
.head
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.arrow
|
||||
{
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fileName
|
||||
{
|
||||
font-size: var(--fs-13);
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.peer
|
||||
{
|
||||
font-size: var(--fs-12);
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.barRow
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.bar
|
||||
{
|
||||
flex: 1;
|
||||
height: 3px;
|
||||
background: var(--divider);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.barFill
|
||||
{
|
||||
position: absolute;
|
||||
inset: 0 auto 0 0;
|
||||
background: var(--accent);
|
||||
border-radius: 999px;
|
||||
transition: width 220ms linear;
|
||||
}
|
||||
|
||||
.barFillStalled { background: var(--state-warn); }
|
||||
.barFillError { background: var(--state-error); }
|
||||
.barFillDone { background: var(--state-online); }
|
||||
|
||||
.pct
|
||||
{
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-12);
|
||||
color: var(--text-muted);
|
||||
min-width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.meta
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-12);
|
||||
color: var(--text-muted);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.metaItem { white-space: nowrap; }
|
||||
.dot { color: var(--divider); }
|
||||
|
||||
.footer
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.actions
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.actionBtn
|
||||
{
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--fs-11);
|
||||
cursor: pointer;
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-control);
|
||||
transition: color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.actionBtn:hover { color: var(--accent); }
|
||||
.actionDanger:hover { color: var(--state-error); }
|
||||
|
||||
.detailToggle
|
||||
{
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--fs-11);
|
||||
cursor: pointer;
|
||||
padding: 2px 4px;
|
||||
border-radius: var(--radius-control);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.detailToggle:hover { color: var(--accent); }
|
||||
|
||||
.detail
|
||||
{
|
||||
background: var(--surface-sunken);
|
||||
border-radius: var(--radius-control);
|
||||
padding: var(--space-3);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-11);
|
||||
color: var(--text-muted);
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
gap: 4px var(--space-3);
|
||||
line-height: var(--lh-ui);
|
||||
}
|
||||
|
||||
.detail strong { color: var(--text); font-weight: 500; }
|
||||
|
||||
.errMsg
|
||||
{
|
||||
color: var(--state-error);
|
||||
font-size: var(--fs-12);
|
||||
}
|
||||
|
||||
.stalled .meta { color: var(--state-warn); }
|
||||
Reference in New Issue
Block a user