Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions apps/chrome-extension/camera-permission.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Camera access - Cap</title>
<title>摄像头权限 - Cap</title>
<script type="module" src="/src/permission/camera-permission.tsx"></script>
</head>
<body>
Expand Down
4 changes: 2 additions & 2 deletions apps/chrome-extension/camera-preview.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cap Camera Preview</title>
<title>Cap 摄像头预览</title>
<script type="module" src="/src/preview/camera-preview.tsx"></script>
</head>
<body>
Expand Down
4 changes: 2 additions & 2 deletions apps/chrome-extension/e2e/overlay-ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ test("clicking X in the panel closes the panel and the camera preview", async ()
const panelFrame = frameWithUrl(targetPage, "popup.html");
if (!panelFrame) throw new Error("panel frame missing");
await panelFrame
.locator('button[aria-label="Close Cap and hide all recorder UI"]')
.locator('button[aria-label="关闭 Cap 并隐藏所有录制界面"]')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These e2e selectors are now coupled to translated aria-label copy. That tends to be brittle as soon as copy tweaks happen (or more locales get added). Consider adding a non-localized hook (e.g. data-testid or a stable id) to the close button and target that in tests instead.

.click();

// The panel and the camera preview should both tear down.
Expand Down Expand Up @@ -419,7 +419,7 @@ test("a failed recording start reopens the panel with the error", async () => {
.toBe(true);
const panelFrame = frameWithUrl(targetPage, "popup.html");
if (!panelFrame) throw new Error("panel frame missing");
await expect(panelFrame.getByText("Recording failed.")).toBeVisible({
await expect(panelFrame.getByText("录制失败。")).toBeVisible({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same idea here: asserting exact localized sentences (getByText("录制失败。")) makes the test fragile. If possible, assert on a stable error state/hook (test id, role + status attribute, or an error code) and keep the copy free to change per-locale.

timeout: 10_000,
});
await targetPage.screenshot({
Expand Down
37 changes: 15 additions & 22 deletions apps/chrome-extension/how-it-works.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>How it works - Cap</title>
<title>使用说明 - Cap</title>
</head>
<body>
<main class="stage">
Expand Down Expand Up @@ -81,21 +81,19 @@
></path>
</g>
</svg>
<h1>How Cap works</h1>
<h1>Cap 如何工作</h1>
<p class="lede">
Instant Mode uploads while you record, so your share link is ready the
moment you stop.
即时模式会在录制过程中同步上传,因此停止录制时分享链接已准备就绪。
</p>
<ol class="steps">
<li class="step">
<svg class="step-doodle" viewBox="0 0 48 48" aria-hidden="true">
<circle class="doodle-stroke-sm" cx="24" cy="24" r="14"></circle>
<circle class="record-dot" cx="24" cy="24" r="5.5"></circle>
</svg>
<span class="step-count">Step 1</span>
<span class="step-count">第 1 步</span>
<p>
Click the Cap icon, pick a tab, window, screen or camera, and press
start.
点击 Cap 图标,选择标签页、窗口、屏幕或摄像头,然后开始录制。
</p>
</li>
<li class="step">
Expand All @@ -110,10 +108,9 @@ <h1>How Cap works</h1>
d="M 18 29 L 24 23 L 30 29"
></path>
</svg>
<span class="step-count">Step 2</span>
<span class="step-count">第 2 步</span>
<p>
Your video streams to the cloud while you record. No exports, no
waiting at the end.
视频会在录制过程中传输到云端,无需导出,结束后也不用等待。
</p>
</li>
<li class="step">
Expand All @@ -128,36 +125,32 @@ <h1>How Cap works</h1>
></path>
<path class="doodle-stroke-sm link-bar" d="M 20 37 L 37 20"></path>
</svg>
<span class="step-count">Step 3</span>
<span class="step-count">第 3 步</span>
<p>
Stop the recording and the share link is live instantly. Paste it
anywhere.
停止录制后,分享链接会立即生效,可粘贴到任意位置。
</p>
</li>
</ol>
<div class="card tips-card">
<h2>Good to know</h2>
<h2>实用提示</h2>
<ul class="tips">
<li>
<span class="tip-dot" aria-hidden="true"></span>
Drag the camera preview anywhere on the page, resize it, or pop it
out with Picture in Picture.
可将摄像头预览拖到页面任意位置、调整大小,或使用画中画单独显示。
</li>
<li>
<span class="tip-dot" aria-hidden="true"></span>
Pause and resume from the floating recording bar without losing
your take.
可通过悬浮录制栏暂停和继续,不会丢失已录内容。
</li>
<li>
<span class="tip-dot" aria-hidden="true"></span>
Click the Cap icon in your toolbar while recording to stop and jump
straight to your video.
录制时点击工具栏中的 Cap 图标,即可停止并直接打开视频。
</li>
</ul>
</div>
</main>
<p class="footnote">
Click the Cap icon in your toolbar whenever you're ready to record.
准备好后,随时点击工具栏中的 Cap 图标开始录制。
</p>
<script type="module" src="/src/how-it-works/main.ts"></script>
</body>
Expand Down
4 changes: 2 additions & 2 deletions apps/chrome-extension/offscreen.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cap Recorder Offscreen</title>
<title>Cap 后台录制器</title>
</head>
<body>
<script type="module" src="/src/offscreen/recorder.ts"></script>
Expand Down
4 changes: 2 additions & 2 deletions apps/chrome-extension/options.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Options - Cap</title>
<title>设置 - Cap</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions apps/chrome-extension/popup-window.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cap Recorder</title>
<title>Cap 录制器</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions apps/chrome-extension/popup.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cap Recorder</title>
<title>Cap 录制器</title>
</head>
<body>
<div id="root"></div>
Expand Down
6 changes: 3 additions & 3 deletions apps/chrome-extension/public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"manifest_version": 3,
"name": "Cap - Screen Recorder & Screen Capture",
"name": "Cap - 屏幕录制与截图",
"short_name": "Cap",
"description": "Free, open source screen recorder. Capture your screen, tab, camera & mic in Chrome and share a video link the moment you stop.",
"description": "免费开源的屏幕录制工具。在 Chrome 中录制屏幕、标签页、摄像头和麦克风,停止录制后即可分享视频链接。",
"version": "1.0.2",
"homepage_url": "https://cap.so",
"minimum_chrome_version": "116",
Expand All @@ -13,7 +13,7 @@
"128": "icons/icon-128.png"
},
"action": {
"default_title": "Record your screen with Cap",
"default_title": "使用 Cap 录制屏幕",
"default_icon": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
Expand Down
49 changes: 18 additions & 31 deletions apps/chrome-extension/src/background/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,7 @@ const createTab = (url: string) =>
new Promise<chrome.tabs.Tab>((resolve, reject) => {
chrome.tabs.create({ url, active: true }, (tab) => {
if (chrome.runtime.lastError) {
reject(
new Error(chrome.runtime.lastError.message ?? "Failed to open tab"),
);
reject(new Error(chrome.runtime.lastError.message ?? "打开标签页失败"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fallback error strings are now Chinese. If the extension isn’t actually locale-switched yet, this will surface Chinese for everyone and makes logs/debugging harder to search. Consider sourcing user-facing copy via chrome.i18n.getMessage(...) (with an English fallback) and keeping thrown/logged messages stable.

return;
}
resolve(tab);
Expand All @@ -160,9 +158,7 @@ const updateTab = (tabId: number, url: string) =>
chrome.tabs.update(tabId, { url, active: true }, (tab) => {
if (chrome.runtime.lastError || !tab) {
reject(
new Error(
chrome.runtime.lastError?.message ?? "Failed to update tab",
),
new Error(chrome.runtime.lastError?.message ?? "更新标签页失败"),
);
return;
}
Expand Down Expand Up @@ -219,7 +215,7 @@ const createOffscreenDocument = () =>
{
url: OFFSCREEN_URL,
reasons: ["USER_MEDIA", "DISPLAY_MEDIA", "BLOBS", "AUDIO_PLAYBACK"],
justification: "Record and upload Cap videos from an extension page.",
justification: "从扩展页面录制并上传 Cap 视频。",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chrome.offscreen.createDocument’s justification can show up in review / debugging contexts; hardcoding one locale may be confusing. If you want this localized, pulling it from chrome.i18n keeps it consistent and lets you keep a safe English default.

},
() => {
const error = chrome.runtime.lastError;
Expand All @@ -228,7 +224,7 @@ const createOffscreenDocument = () =>
return;
}

const message = error.message ?? "Failed to create offscreen document";
const message = error.message ?? "创建后台录制文档失败";
if (message.toLowerCase().includes("single offscreen document")) {
resolve();
return;
Expand Down Expand Up @@ -267,7 +263,7 @@ const sendOffscreenRuntimeMessage = (message: OffscreenRequest) =>
new Promise<OffscreenResponse>((resolve, reject) => {
chrome.runtime.sendMessage(message, (response) => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message ?? "Message failed"));
reject(new Error(chrome.runtime.lastError.message ?? "消息发送失败"));
return;
}
resolve(response as OffscreenResponse);
Expand Down Expand Up @@ -312,11 +308,7 @@ const getTabStreamId = (tabId: number) =>
new Promise<string>((resolve, reject) => {
chrome.tabCapture.getMediaStreamId({ targetTabId: tabId }, (streamId) => {
if (chrome.runtime.lastError) {
reject(
new Error(
chrome.runtime.lastError.message ?? "Failed to capture tab",
),
);
reject(new Error(chrome.runtime.lastError.message ?? "录制标签页失败"));
return;
}
resolve(streamId);
Expand Down Expand Up @@ -596,9 +588,7 @@ const updateActionForStatus = (nextStatus: RecordingStatus) => {
// The recorder renders inside the page, so the action never opens a popup.
setActionPopup(""),
setActionBadgeText(isCapturing ? "REC" : ""),
setActionTitle(
isCapturing ? "Stop Cap recording" : "Record your screen with Cap",
),
setActionTitle(isCapturing ? "停止 Cap 录制" : "使用 Cap 录制屏幕"),
]).then(() => undefined);
};

Expand Down Expand Up @@ -849,7 +839,7 @@ const waitForWebcamPreviewReady = (tabId: number) => {
previewReadyWaiters,
tabId,
START_PREVIEW_READY_TIMEOUT_MS,
"Camera preview did not become ready before recording started.",
"开始录制前摄像头预览未准备就绪。",
);
};

Expand Down Expand Up @@ -1133,7 +1123,7 @@ const loadSignedInState = async () => {
const requireSignedInState = async () => {
const state = await loadSignedInState();
if (!state.auth || !state.bootstrap) {
throw new Error("Sign in to Cap first");
throw new Error("请先登录 Cap");
}
return state as {
settings: ExtensionSettings;
Expand Down Expand Up @@ -1230,7 +1220,7 @@ const startRecording = async (mode: RecordingMode) => {
await saveSettings(recordingSettings);
}
if (mode === "camera" && !recordingSettings.webcam.deviceId) {
throw new Error("Select a camera before recording.");
throw new Error("请先选择摄像头再开始录制。");
}
if (isWebcamPreviewEnabled(recordingSettings)) {
await saveWebcamPreviewDismissed(false);
Expand All @@ -1249,7 +1239,7 @@ const startRecording = async (mode: RecordingMode) => {
return {
ok: false,
canceled: true,
error: "Recording canceled",
error: "录制已取消",
} satisfies OffscreenResponse;
}
}
Expand Down Expand Up @@ -1396,10 +1386,7 @@ const launchWebAuthFlow = (url: string) =>
(responseUrl) => {
if (chrome.runtime.lastError || !responseUrl) {
reject(
new Error(
chrome.runtime.lastError?.message ??
"The sign-in window was closed",
),
new Error(chrome.runtime.lastError?.message ?? "登录窗口已关闭"),
);
return;
}
Expand Down Expand Up @@ -1492,7 +1479,7 @@ const handlePreviewError = async (
settleTabWaiters(
previewReadyWaiters,
tabId,
new Error("Camera preview did not become ready."),
new Error("摄像头预览未准备就绪。"),
);

const fallbackTabId =
Expand Down Expand Up @@ -1552,7 +1539,7 @@ const handleRequest = async (
const status = error instanceof ApiRequestError ? error.status : null;
if (status === null || status >= 500) {
throw new Error(
"Could not reach Cap to revoke this sign-in. Check your connection and try again.",
"无法连接 Cap 以撤销本次登录,请检查网络连接后重试。",
);
}
}
Expand Down Expand Up @@ -1741,7 +1728,7 @@ const handleRequest = async (
// excluded so a web-accessible page embedded by a hostile site cannot
// authorise itself.
if (!isWebPageSender(sender)) {
return { ok: false, error: "Unauthorized" };
return { ok: false, error: "未授权" };
}
await registerOverlayToken(message.token);
return { ok: true };
Expand All @@ -1757,7 +1744,7 @@ const handleRequest = async (
message.sessionId,
);
if (!allowed) {
return { ok: false, error: "Camera preview is not authorized." };
return { ok: false, error: "摄像头预览未获授权。" };
}
const response = await sendOffscreen({
target: "offscreen",
Expand Down Expand Up @@ -1789,7 +1776,7 @@ const handleRequest = async (
tabId === undefined ||
!(await isCameraPreviewEventAllowed(sender, message.token))
) {
return { ok: false, error: "Unauthorized" };
return { ok: false, error: "未授权" };
}
chrome.tabs.sendMessage(
tabId,
Expand Down Expand Up @@ -1840,7 +1827,7 @@ const handleRequest = async (
return { ok: true };
}

return { ok: false, error: "Unknown request" };
return { ok: false, error: "未知请求" };
};

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
Expand Down
Loading