feat: 统一 Commilitia Drop 全客户端命名与分发

This commit is contained in:
2026-07-31 22:56:10 +08:00
parent f7b0f04c9c
commit c480f0d1c2
77 changed files with 861 additions and 263 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
传输引擎。仅由原生 iOS 壳的离屏 WKWebView 从安全源(https)加载;原生须在本
模块执行前经 WKUserScript 注入 window.__CDROP_BOOT__session / device_name /
api_base / device_type:"ios"),见 src/engine/main.ts。 -->
<title>cdrop engine</title>
<title>Commilitia Drop</title>
</head>
<body>
<script type="module" src="/src/engine/main.ts"></script>
+2 -2
View File
@@ -19,8 +19,8 @@
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="cdrop" />
<meta name="application-name" content="cdrop" />
<meta name="apple-mobile-web-app-title" content="Commilitia Drop" />
<meta name="application-name" content="Commilitia Drop" />
<!-- 预取品牌 logo:登录跳转回来后首页 header 立即用到;早取 + 后端一周缓存,
避免那一刻才发起请求导致的闪烁重载。 -->
<link rel="preload" as="image" href="/logo-mark.png" />
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "/",
"name": "Commilitia Drop",
"short_name": "cdrop",
"short_name": "Commilitia Drop",
"description": "跨设备剪贴板同步与点对点文件传输",
"lang": "zh-CN",
"start_url": "/",
+1 -1
View File
@@ -72,7 +72,7 @@ self.addEventListener("push", (event) =>
try { data = event.data ? event.data.json() : {}; }
catch (_e) { data = {}; }
const title = data.title || "cdrop";
const title = data.title || "Commilitia Drop";
const options = {
body: data.body || "",
tag: data.tag || undefined, // same tag replaces a stale notification in place
+2 -2
View File
@@ -179,7 +179,7 @@ function subscribeStore(): void
});
}
// handleSendFile:原生把待发文件经 payload.urlcdrop-file://<id>)暴露。R-iOS-4:用
// handleSendFile:原生把待发文件经 payload.urlcommilitia-drop-file://<id>)暴露。R-iOS-4:用
// bridgeFileSource 经桥按块取片(原生据 [start,end) seek 文件、回 base64),整文件永不整体进
// WebView 内存——大文件不再有 jetsam 风险,传输层 p2p / relay 透明复用。改走桥而非跨 origin
// fetch,绕开自定义 scheme 的 CORS 拦截(否则发送在读文件即失败)。
@@ -520,7 +520,7 @@ function boot(): void
{
// 非 iOS 壳(误加载 engine.html)——保持惰性,不触发任何网络 / 引擎行为。
// eslint-disable-next-line no-console
console.warn("cdrop engine: not running inside the iOS shell, idle");
console.warn("Commilitia Drop engine: not running inside the iOS shell, idle");
return;
}
+3 -3
View File
@@ -24,7 +24,7 @@ import type { FileSource } from "./source";
// - iceServers: pulled from lib/iceServers (Cloudflare Realtime TURN over
// TLS when CDROP_CF_TURN_* is configured; STUN-only fallback otherwise).
// Refreshed on login by RootLayout.
// - DataChannel "cdrop-file", ordered:true
// - DataChannel "commilitia-drop-file", ordered:true
// - chunk size 64 KiB, backpressure thresholds 16 MiB / 4 MiB
// - 接收端流式写 OPFSlib/incomingSink),避免 iOS Safari 内存压力
// - control frames as JSON strings (head meta + tail done), payload bytes binary
@@ -36,7 +36,7 @@ import type { FileSource } from "./source";
// Same-LAN Restricted Cone NAT 排查依据:iceCandidatePoolSize=4 预热候选;
// 收集本/远端 host/mdns/srflx/prflx/relay 计数 + 选中候选对类型推到 store,
// 让 UI 暴露"实际通过哪条路径连上"host:host = 真直连,relay:* = 走了 TURN)。
const CHANNEL_NAME = "cdrop-file";
const CHANNEL_NAME = "commilitia-drop-file";
const CHUNK_SIZE = 64 * 1024;
const HIGH_WATERMARK = 16 * 1024 * 1024;
const LOW_WATERMARK = 4 * 1024 * 1024;
@@ -971,7 +971,7 @@ export function p2pStartOutgoing(
{
return nativeStartOutgoing(sessionId, receiverName, src.path, src.size);
}
// iOS 无头壳:源带 cdrop-file://<id> 引用(path)即走 libwebrtc 原生数据面(Swift 侧解析回沙盒
// iOS 无头壳:源带 commilitia-drop-file://<id> 引用(path)即走 libwebrtc 原生数据面(Swift 侧解析回沙盒
// 绝对路径直接读盘)。IOS_NATIVE 关闭时回退下方 JS SessionWebKit WebRTC)。
if (IOS_NATIVE && isIOSShell() && src.path)
{
+1 -1
View File
@@ -235,7 +235,7 @@ function register(s: NativeSess): void
idByPeer.set(s.peerName, s.sessionId);
}
// nativeIosStartOutgoing 经 Go 引擎发送 filePathcdrop-file://<id> 引用,原生解析回沙盒绝对
// nativeIosStartOutgoing 经 Go 引擎发送 filePathcommilitia-drop-file://<id> 引用,原生解析回沙盒绝对
// 路径)到 peerName。total 用于进度事件的分母(store 的 bytesTransferred 仍是 UI 真值源)。
export function nativeIosStartOutgoing(
sessionId: string, peerName: string, filePath: string, total: number,
+1 -1
View File
@@ -35,7 +35,7 @@ export function fileSource(file: File): FileSource
// rangeSource:经 HTTP Range 向原生 WKURLSchemeHandler 惰性拉取文件片(iOS 大文件流式发送)。
// 每次 slice 只取 [start, end) 一段,WebView 常驻内存上限即单块大小(p2p 的
// READ_BLOCK_SIZE = 4 MiB)。url 形如 `cdrop-file://<id>`;原生据 Range 头 seek 文件、
// READ_BLOCK_SIZE = 4 MiB)。url 形如 `commilitia-drop-file://<id>`;原生据 Range 头 seek 文件、
// 回 206 Partial Content 仅含该区间字节。
export function rangeSource(
url: string,
+4 -4
View File
@@ -123,7 +123,7 @@ export const enUS: Partial<TranslationDict> = {
"qr.show.nameHint": "Shown to your other devices. ASCII characters only.",
"qr.show.generate": "Generate code",
"qr.show.scanTitle": "Scan to approve",
"qr.show.howto1": "Open the camera or cdrop on another signed-in device.",
"qr.show.howto1": "Open the camera or Commilitia Drop on another signed-in device.",
"qr.show.howto2": "Scan the code above and approve this device.",
"qr.show.waiting": "Waiting for approval…",
"qr.show.approved": "Approved — signing you in…",
@@ -225,11 +225,11 @@ export const enUS: Partial<TranslationDict> = {
"notify.transfer.failedTitle": "Transfer failed",
"settings.account.title": "Account",
"settings.account.signOut": "Sign out",
"settings.desktop.title": "Desktop",
"settings.desktop.title": "App settings",
"settings.desktop.clipboardSync": "Auto-sync clipboard",
"settings.desktop.clipboardSyncHint": "Copies are uploaded to the cloud, and clipboard updates from your other devices are applied here.",
"settings.desktop.launchAtLogin": "Launch at login",
"settings.desktop.launchAtLoginHint": "Start cdrop in the background after you sign in (lives in the menu bar).",
"settings.desktop.launchAtLoginHint": "Start Commilitia Drop in the background after you sign in (lives in the menu bar or system tray).",
"settings.desktop.downloadDir": "Download folder",
"settings.desktop.downloadDirHint": "Received files are saved here. Leave unset to use the system Downloads folder.",
"settings.desktop.downloadDirChoose": "Change…",
@@ -344,7 +344,7 @@ export const enUS: Partial<TranslationDict> = {
"errors.messageOverflow": "Message exceeds 4 KB; send a file instead",
"errors.noReceiver": "No receiver selected",
"errors.devTokenMissing":
"Dev mode: VITE_CDROP_DEV_TOKEN is not set in .env.local",
"Dev mode: the development token is not configured in .env.local",
"errors.noAccessToken": "No access token; user must log in",
"errors.clipboardUnavailable": "Browser clipboard API unavailable (requires HTTPS + permission)",
"errors.clipboardOverflow": "Content too large; exceeds {{max}} byte limit",
+4 -4
View File
@@ -120,7 +120,7 @@ export const zhCN = {
"qr.show.nameHint": "此名称会展示给你的其他设备,仅可使用 ASCII 字符。",
"qr.show.generate": "生成二维码",
"qr.show.scanTitle": "用手机扫码批准",
"qr.show.howto1": "在另一台已登录的设备上打开相机或 cdrop。",
"qr.show.howto1": "在另一台已登录的设备上打开相机或 Commilitia Drop。",
"qr.show.howto2": "扫描上方二维码,按提示批准此设备。",
"qr.show.waiting": "等待批准…",
"qr.show.approved": "已批准,正在进入…",
@@ -221,11 +221,11 @@ export const zhCN = {
"notify.transfer.failedTitle": "传输失败",
"settings.account.title": "账号",
"settings.account.signOut": "退出登录",
"settings.desktop.title": "桌面",
"settings.desktop.title": "应用设置",
"settings.desktop.clipboardSync": "剪贴板自动同步",
"settings.desktop.clipboardSyncHint": "复制即上传到云端,并接收其他设备的剪贴板更新。",
"settings.desktop.launchAtLogin": "开机自启",
"settings.desktop.launchAtLoginHint": "登录系统后自动在后台启动 cdrop(菜单栏常驻)。",
"settings.desktop.launchAtLoginHint": "登录系统后自动在后台启动 Commilitia Drop常驻菜单栏或系统托盘)。",
"settings.desktop.downloadDir": "下载目录",
"settings.desktop.downloadDirHint": "接收到的文件保存到此目录;留空则使用系统下载目录。",
"settings.desktop.downloadDirChoose": "更改…",
@@ -340,7 +340,7 @@ export const zhCN = {
"errors.messageOverflow": "消息超过4 KB;请改用文件传输",
"errors.noReceiver": "未选择接收方",
"errors.devTokenMissing":
"Dev模式:未在.env.local设置VITE_CDROP_DEV_TOKEN",
"Dev 模式:未在 .env.local 配置开发令牌",
"errors.noAccessToken": "无访问令牌:用户必须先登录",
"errors.clipboardUnavailable": "浏览器不支持剪贴板API(需HTTPS与权限)",
"errors.clipboardOverflow": "内容过大,超过 {{max}} 字节上限",
+4 -4
View File
@@ -124,7 +124,7 @@ export const zhTW: Partial<TranslationDict> = {
"qr.show.nameHint": "此名稱會顯示給你的其他裝置,僅可使用 ASCII 字元。",
"qr.show.generate": "產生 QR 碼",
"qr.show.scanTitle": "用手機掃碼批准",
"qr.show.howto1": "在另一部已登入的裝置上開啟相機或 cdrop。",
"qr.show.howto1": "在另一部已登入的裝置上開啟相機或 Commilitia Drop。",
"qr.show.howto2": "掃描上方 QR 碼,依提示批准此裝置。",
"qr.show.waiting": "等待批准…",
"qr.show.approved": "已批准,正在進入…",
@@ -225,11 +225,11 @@ export const zhTW: Partial<TranslationDict> = {
"notify.transfer.failedTitle": "傳輸失敗",
"settings.account.title": "帳號",
"settings.account.signOut": "登出",
"settings.desktop.title": "桌面",
"settings.desktop.title": "應用程式設定",
"settings.desktop.clipboardSync": "剪貼簿自動同步",
"settings.desktop.clipboardSyncHint": "複製後即上傳雲端,並接收其他裝置的剪貼簿更新。",
"settings.desktop.launchAtLogin": "開機自動啟動",
"settings.desktop.launchAtLoginHint": "登入系統後自動在背景啟動 cdrop(常駐選單列)。",
"settings.desktop.launchAtLoginHint": "登入系統後自動在背景啟動 Commilitia Drop(常駐選單列或系統匣)。",
"settings.desktop.downloadDir": "下載資料夾",
"settings.desktop.downloadDirHint": "接收到的檔案會儲存至此資料夾;留空則使用系統下載資料夾。",
"settings.desktop.downloadDirChoose": "變更…",
@@ -344,7 +344,7 @@ export const zhTW: Partial<TranslationDict> = {
"errors.messageOverflow": "訊息超過4 KB;請改用檔案傳輸",
"errors.noReceiver": "未選擇接收方",
"errors.devTokenMissing":
"Dev模式:未在.env.local設定VITE_CDROP_DEV_TOKEN",
"Dev 模式:未在 .env.local 設定開發權杖",
"errors.noAccessToken": "無存取權杖:使用者必須先登入",
"errors.clipboardUnavailable": "瀏覽器不支援剪貼簿API(需HTTPS與權限)",
"errors.clipboardOverflow": "內容過大,超過 {{max}} 位元組上限",
+3 -3
View File
@@ -196,7 +196,7 @@ export async function abortIncomingDownloadIOS(sessionId: string): Promise<void>
const BRIDGE_SLICE_MAX = 512 * 1024;
// readFileSliceIOS:经桥让原生按 [start, end) seek 读暂存的待发文件、回 base64,再解回字节。
// 取代原先「fetch(cdrop-file://) + Range」——引擎在 https origin 下跨 origin fetch 自定义 scheme
// 取代原先「fetch(commilitia-drop-file://) + Range」——引擎在 https origin 下跨 origin fetch 自定义 scheme
// 被 CORS 拦死(Range 头触发预检、scheme handler 无 CORS 头)→ 发送在读文件即失败。改走桥与
// 接收落盘同形,绕开 CORS;整文件仍永不整体进 WebView 内存(按需取片)。
async function readFileSliceIOS(url: string, start: number, end: number): Promise<Uint8Array<ArrayBuffer>>
@@ -214,7 +214,7 @@ export function bridgeFileSource(url: string, name: string, size: number, type:
name,
size,
type: type || "application/octet-stream",
// path 带 cdrop-file://<id> 引用:iOS 原生数据面gomobile/pion据此让原生侧解析回沙盒
// path 带 commilitia-drop-file://<id> 引用:iOS 原生数据面据此让原生侧解析回沙盒
// 绝对路径、由 Go 直接读盘发送(见 p2pIos.ts / EngineController.p2pStartOutgoing)。JS 收发
// 路径不读 path,故对回退到 JS 引擎的传输无影响(与桌面 nativeFileSource 同范式)。
path: url,
@@ -246,7 +246,7 @@ export function bridgeFileSource(url: string, name: string, size: number, type:
// 四个入口走 callNativeRPC,原生回 resolve);进度 / 状态 / 出站信令 / 落盘 / 候选对经
// onNativeEvent 反向回来。原生侧契约见 ios/CDrop/Sources/Engine/EngineController.swift。
// nativeP2PStartOutgoing:发起原生发送。filePath 为 cdrop-file://<id> 引用,原生解析回沙盒
// nativeP2PStartOutgoing:发起原生发送。filePath 为 commilitia-drop-file://<id> 引用,原生解析回沙盒
// 绝对路径后交 Go 直接读盘(不经 base64 桥 / 不整文件进内存)。
export async function nativeP2PStartOutgoing(
sessionId: string, peerName: string, filePath: string, iceServersJSON: string,
+1 -1
View File
@@ -262,7 +262,7 @@ function suggestDeviceName(): string
else if (ua.includes("Chrome/")) { browser = "Chrome"; }
else if (ua.includes("Safari/")) { browser = "Safari"; }
let os = "Desktop";
let os = "Computer";
if (ua.includes("Mac")) { os = "macOS"; }
else if (ua.includes("Windows")) { os = "Windows"; }
else if (ua.includes("Linux")) { os = "Linux"; }
+1 -1
View File
@@ -104,7 +104,7 @@ function suggestDefault(): string
else if (ua.includes("Chrome/")) { browser = "Chrome"; }
else if (ua.includes("Safari/")) { browser = "Safari"; }
let os = "Desktop";
let os = "Computer";
if (ua.includes("Mac")) { os = "macOS"; }
else if (ua.includes("Windows")) { os = "Windows"; }
else if (ua.includes("Linux")) { os = "Linux"; }
+1 -1
View File
@@ -31,7 +31,7 @@ function StyleGuide()
<Container size="lg" py="lg">
<Stack gap="lg">
<Group justify="space-between" align="baseline">
<Title order={3} fw={600}>cdrop · UI </Title>
<Title order={3} fw={600}>Commilitia Drop · UI </Title>
<Group gap="xs">
<Text size="xs" c="dimmed"></Text>
<Tabs<ThemeMode>