From 9150e9d31295e231797792274dc6062081c048fa Mon Sep 17 00:00:00 2001 From: AAA <35992542+TNTcraftHIM@users.noreply.github.com> Date: Sun, 13 Sep 2026 18:32:24 +0800 Subject: [PATCH] feat(app): improve startup, diagnostics and direct connections (#397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem and outcome / 问题与结果 Machines with several local addresses could fail before opening Piik, and startup or connection failures were difficult to diagnose. This phase adds Local invitation-address selection and accessible diagnostics, preserves useful startup errors, and repairs media preparation and recovery without changing room authority or routing policy. It also aligns language handling, playback, public downloads and contributor documentation with the resulting behavior. ## Release notes ### 中文 - **多网卡也能正常选择启动方式。** 本地房间在需要时显示网卡名称与局域网地址,供你选择朋友所在的网络;公网邀请和连接已有站点不再因多个局域网地址而无法启动。 - **更容易排查启动和连接问题。** 启动页新增“诊断启动”,同时记录 App 与网页诊断;网页版也有独立诊断入口。启动失败会显示具体原因和重试方法,Windows 双击启动后的错误会停留等待确认。诊断模式下异常退出会保存报告,ZIP 由用户自行保留或清理。 - **减少连接准备中的无谓等待。** 普通候选发现不再等待可选端口映射;修正连接重启时旧候选与完成通知的处理,以及部分准备失败后的反馈。Native 媒体连接可在网络支持时使用 IPv6 直连,同时保留 IPv4 路径。 - **修正分享恢复中的状态问题。** 切换画面后重新计算首播保护所需的新帧;SFU 恢复时应用最新画质设置;一次应答发送失败不再使整个会话失去 Native 接收能力。密码错误提示也进行了去重。 - **改善播放与全屏交互。** 同步 Safari 原生全屏中的播放、音量与退出状态;可操作的视频画面显示手形光标。 - **统一语言匹配并完善翻译贡献。** 简体、繁体中文系统均默认显示简体中文,其余系统默认英文;保留中英文与纯视觉模式快捷切换,提供中英文社区翻译教程和额外语言接入方式。 - **整理下载与官网说明。** Release 附件集中展示三个平台的 App ZIP 和 Server 程序包,源码版本与校验值收进更新说明的折叠区。官网保留内嵌短片,并提供独立观看链接;教程、下载文案和运维指南同步更新。 **升级:** App 请完整解压对应平台的 ZIP 并保留目录结构。服务端按部署指南升级,保留现有配置与 SQLite 数据。此次更新不改变现有通信协议和存储格式。 **测试范围:** 主要验证 Windows App 与桌面浏览器。macOS、Linux 实机采集和移动端、跨公网 IPv6 的更广泛验证仍待完成,欢迎试用反馈。 ### English - **Choose how to start on machines with multiple network addresses.** Local rooms show interface names and invitation addresses when a choice is needed. Public invitations and connections to an existing Site no longer fail because several LAN addresses are available. - **Diagnose startup and connection problems more easily.** The launcher now offers Debug launch for both App and browser diagnostics, alongside a separate browser diagnostics control. Startup failures include their cause and retry guidance; Windows errors remain visible for confirmation after a double-click launch. Failed exits in diagnostic mode save a report. Exported ZIPs remain available until you move or delete them. - **Remove unnecessary connection setup waits.** Ordinary candidate discovery no longer waits for optional gateway mapping. Fixes cover stale candidates and completion signals during ICE restarts, and feedback from failed connection preparation. Native media can use direct IPv6 connections where available while retaining IPv4 paths. - **Keep sharing recovery aligned with the current source and settings.** Source changes count new frames for startup protection, SFU recovery uses the latest quality preferences, and one failed answer submission no longer disables Native receiving for the entire session. Duplicate password-error feedback has also been removed. - **Improve playback and fullscreen interaction.** Safari native fullscreen now synchronizes playback, volume and exit state. Interactive video surfaces show a pointer cursor. - **Align language selection and community translation guidance.** Simplified and Traditional Chinese system locales select Simplified Chinese; other systems default to English. Chinese, English and visual-mode shortcuts remain, with bilingual translation guides and a registration path for additional languages. - **Simplify release downloads and refresh the website.** Release attachments contain the three App ZIPs and the Server runtime archive; source identity and checksums appear in a collapsed section of the release notes. The website keeps its embedded introduction and adds a standalone viewing link, with updated setup, download and operations guidance. **Upgrading:** Extract the complete App ZIP for your platform and keep its directory structure. Follow the deployment guide for Server upgrades, preserving existing configuration and SQLite data. Existing wire contracts and storage formats are unchanged. **Test coverage:** Windows App and desktop browsers are the primary validated paths. Physical macOS/Linux capture, broader mobile coverage and IPv6 across separate public networks remain open; testing and feedback are welcome. ## Verification / 验证 - `npm run check`: PASS, including TypeScript, 807 tests in 60 files and the production Web build. - `npm run check:client`: PASS on Windows, including Go formatting/tests/vet, Windows/Linux core builds and native Windows capture compile/probe. Darwin requires the native macOS CI runner. - `npm run gate:client-media`: PASS with Chrome 152 and a real captured test window on Windows. Two receivers obtained video and audio; live and paused profile changes, recovery frames and cleanup passed. This is a single-machine test, not a public-NAT success-rate measurement. - `npm run gate:client-link`: PASS using the newly packaged Windows App. A separate remote machine fetched its temporary public page and completed the WebSocket upgrade; App exit closed the link and cleanup passed. - Server runtime and Windows App candidate packaging: PASS, including archive identity/checksums and execution from the extracted App ZIP. Native Linux/macOS package checks still run in the publishing pipeline before publication. - `node scripts/check-docs.mjs`, `scripts/check-project-state.ps1`, `git diff --check` and `npm run build:website`: PASS. Seven documentation-size gardening advisories and the existing Vite chunk-size advisory remain non-blocking. - Browser review: English/Chinese website setup and direct-download links, narrow layout and diagnostics/language controls checked. Native popover accessibility correctly exposes expanded/collapsed state without a duplicate attribute owner. - Independent audit found no P0/P1/P2 issues; focused race checks for portmapping and mediaedge passed. Diagnostic archive accumulation is retained by the owner's explicit choice. The currently unreachable language menu placement issue is a first-community-language prerequisite in TODO. Physical macOS/Linux capture, broader mobile behavior, restricted-network/endurance coverage and cross-network Native IPv6 remain explicitly outside this candidate's evidence. ## Impact / 影响 Compatible feature release, planned as v1.2.0 from v1.1.2. Browser/server v23, Native control v9, capture v7 and SQLite schema 2 remain unchanged. No new telemetry service, retry policy or configuration requirement is introduced. Public Demo remains open-entry and P2P-only. NAT-PMP dependency corrections are explicitly deferred to the next release and are not claimed here. The existing product, configuration, translation, research and operations owners were updated. Raw audits, handoffs, diagnostic logs and experimental sources are excluded from the PR. --- .github/workflows/repository-hygiene.yml | 4 +- CONTRIBUTING.md | 21 +++ README.md | 8 +- README.zh-CN.md | 8 +- cmd/piik-app/README.md | 30 ++-- cmd/piik-app/exit_other.go | 5 + cmd/piik-app/exit_windows.go | 30 ++++ cmd/piik-app/exit_windows_test.go | 87 +++++++++ cmd/piik-app/main.go | 39 ++-- cmd/piik-app/main_test.go | 23 +++ docs/README.md | 3 +- docs/deployment.md | 6 + docs/design/public-introduction.md | 3 +- docs/design/visual-language.md | 11 ++ docs/guide/getting-started.md | 6 +- docs/guide/getting-started.zh-CN.md | 5 +- docs/guide/translating.md | 169 ++++++++++++++++++ docs/guide/translating.zh-CN.md | 139 ++++++++++++++ docs/maintenance.md | 1 + docs/operations/github.md | 18 +- docs/operations/website.md | 6 +- docs/product/presentation-lifecycle.md | 21 ++- docs/product/routing-transport.md | 6 +- docs/reference/configuration.md | 51 ++++-- docs/reference/engineering.md | 5 + docs/reference/naming.md | 2 +- docs/reference/versioning.md | 26 ++- docs/research/nat-traversal.md | 70 +++++++- docs/research/realtime-quality-adaptation.md | 22 ++- docs/todo.md | 47 +++-- docs/verification-status.md | 4 +- internal/app/app.go | 116 ++++++++---- internal/app/app_test.go | 43 +++++ internal/app/console.go | 2 +- internal/app/console_language_other.go | 31 ++++ internal/app/console_language_other_test.go | 32 ++++ internal/app/console_language_windows.go | 18 ++ internal/app/console_language_windows_test.go | 25 +++ internal/app/console_test.go | 11 ++ internal/app/lan/addresses.go | 39 +++- internal/app/lan/addresses_test.go | 17 +- internal/app/launcher/server.go | 92 ++++++++-- internal/app/launcher/server_test.go | 132 +++++++++++++- internal/app/mediaedge/candidate_gathering.go | 76 +++++--- .../app/mediaedge/candidate_gathering_test.go | 168 +++++++++++++++++ internal/app/mediaedge/edge.go | 6 +- internal/app/mediaedge/edge_test.go | 13 +- internal/app/mediaedge/engine.go | 97 +++++----- internal/app/mediaedge/engine_test.go | 154 ++++++++++++++++ internal/app/mediaedge/publication.go | 2 +- internal/app/mediaedge/receiver.go | 6 +- internal/app/mediaedge/receiver_test.go | 2 +- internal/app/portmapping/mapping.go | 47 ++++- internal/app/portmapping/mapping_test.go | 70 +++++++- internal/app/publictunnel/process.go | 47 +++-- internal/app/publictunnel/process_test.go | 11 ++ internal/server/config/local.go | 28 ++- internal/server/config/local_test.go | 26 +++ internal/server/signal/router_sfu_signal.go | 6 + .../server/signal/router_sfu_signal_test.go | 6 + internal/server/signal/router_test.go | 52 ++++++ scripts/build-website.mjs | 10 +- scripts/client-link-gate.ts | 15 +- scripts/publish-release.mjs | 33 +++- scripts/release-artifacts.mjs | 8 +- site/downloads.ts | 1 - site/film/README.md | 5 +- site/film/ui/main.tsx | 1 - site/index.html | 19 +- site/main.js | 1 + site/styles.css | 4 +- src/client/App.tsx | 7 +- src/client/components/living/Header.tsx | 67 ++++--- .../components/living/LanguageControl.tsx | 139 ++++++++++++++ src/client/components/living/LauncherForm.tsx | 40 +++-- .../components/living/PlaybackControls.tsx | 58 +++--- .../components/living/comic-presentation.ts | 1 + src/client/components/living/hints/index.tsx | 1 + .../components/living/hints/playback.tsx | 9 +- .../components/living/playback-controls.css | 1 + .../components/living/video-fullscreen.ts | 41 +++++ src/client/lib/debug-webrtc.ts | 27 ++- src/client/lib/debug.ts | 10 ++ src/client/lib/session.ts | 14 +- src/client/locales/en.ts | 20 ++- src/client/locales/index.ts | 35 ++-- src/client/locales/zh.ts | 20 ++- src/client/media/browser-encoding-producer.ts | 2 +- src/client/media/host-provisional-child.ts | 10 +- src/client/native/native-viewer-peer.ts | 15 +- src/client/pages/AppLauncherPage.tsx | 71 +++++--- src/client/pages/HostPage.tsx | 39 +++- src/client/pages/TooltipPreviewPage.tsx | 2 + src/client/pages/ViewerPage.tsx | 28 ++- src/client/sfu/publisher.ts | 100 +++++++---- src/client/styles.css | 106 +++++++++-- src/client/ui/copy.ts | 26 ++- src/client/ui/visual-kinds.ts | 1 + src/client/webrtc/host-peer.ts | 38 ++-- src/client/webrtc/nat-prediction.ts | 4 +- src/client/webrtc/stats.ts | 10 +- src/client/webrtc/viewer-relay.ts | 35 ++-- tests/browser-debug.test.ts | 59 ++++++ tests/copy.test.ts | 74 +++++++- tests/host-peer-tracks.test.ts | 152 +++++++++++++++- tests/nat-prediction.test.ts | 41 +++++ tests/native-viewer-peer.test.ts | 122 +++++++++++++ tests/publish-release.test.ts | 68 ++++++- tests/release-automation.test.ts | 6 + tests/sfu-client.test.ts | 90 ++++++++++ tests/video-fullscreen.test.ts | 88 +++++++++ tests/website-downloads.test.ts | 1 - vite.config.ts | 2 + 113 files changed, 3453 insertions(+), 575 deletions(-) create mode 100644 cmd/piik-app/exit_other.go create mode 100644 cmd/piik-app/exit_windows.go create mode 100644 cmd/piik-app/exit_windows_test.go create mode 100644 cmd/piik-app/main_test.go create mode 100644 docs/guide/translating.md create mode 100644 docs/guide/translating.zh-CN.md create mode 100644 internal/app/console_language_other.go create mode 100644 internal/app/console_language_other_test.go create mode 100644 internal/app/console_language_windows.go create mode 100644 internal/app/console_language_windows_test.go create mode 100644 internal/app/mediaedge/candidate_gathering_test.go create mode 100644 internal/app/mediaedge/engine_test.go create mode 100644 src/client/components/living/LanguageControl.tsx create mode 100644 src/client/components/living/video-fullscreen.ts create mode 100644 tests/video-fullscreen.test.ts diff --git a/.github/workflows/repository-hygiene.yml b/.github/workflows/repository-hygiene.yml index 4182138..dc6f9f8 100644 --- a/.github/workflows/repository-hygiene.yml +++ b/.github/workflows/repository-hygiene.yml @@ -218,7 +218,9 @@ jobs: pattern: piik-app-*-${{ github.sha }} path: ${{ runner.temp }}/publish-release merge-multiple: true - - name: Publish verified release + # Local descriptors/manifests/checksums are validated before publishing. + # Release assets contain packages only; checksums appear in the release body. + - name: Publish packages with release notes and checksums run: >- node scripts/publish-release.mjs "$RUNNER_TEMP/publish-release" "$PIIK_RELEASE_VERSION" "$GITHUB_SHA" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 568dce0..b7a62a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,26 @@ # Contributing +## Ways To Help + +Corrections, translations, bug reports and code contributions are welcome. +English and Chinese are both welcome in issues and PRs. +欢迎用中文或英文参与讨论,从一处文案修正或一次问题反馈开始也很好。 + +| Contribution / 参与方式 | Start here / 从这里开始 | +| --- | --- | +| Translate or improve wording / 翻译、校对文案 | [Translation guide](./docs/guide/translating.md) · [中文翻译教程](./docs/guide/translating.zh-CN.md) | +| Report a bug / 反馈问题 | [Issue templates](https://github.com/TNTcraftHIM/Piik/issues/new/choose); include version, environment, steps and actual result / 附版本、环境、步骤与实际结果 | +| Improve a guide / 改进教程 | [Documentation map](./docs/README.md); identify the confusing step and explain the expected result / 指出不清楚的步骤及预期结果 | +| Suggest a feature / 功能建议 | Search [existing issues](https://github.com/TNTcraftHIM/Piik/issues) first, then describe the use case / 先搜索已有讨论,再说明使用场景 | +| Contribute code / 参与开发 | [Run from source](./docs/README.md#run-from-source), then follow the workflow below / 从源码启动,按下文流程修改 | + +Keep feedback specific and respectful. Explain a wording or design disagreement +with context and a suggested improvement. Review screenshots and diagnostics for +private content before attaching them. If you cannot run a check, say so in the +PR; maintainers can help identify what still needs verification. +讨论时请结合实际场景提出建议,尊重不同意见;分享截图或日志前移除隐私内容。 +无法运行的检查请如实说明,便于维护者协助验证。 + ## Lean Workflow This is the default for an ordinary scoped implementation or bug fix. diff --git a/README.md b/README.md index 77eafa8..115918a 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ picture does not start automatically. 1. Select **Start sharing**, then choose the picture and audio to share. 2. Select **Copy invite link** and send it to your friends. Keep the sharing tab open. -Piik uses icon controls by default; select **EN** in the header to show labels. +Piik follows your system language: Chinese or English, with English as the fallback. +Use **中 / EN / ✦** in the header to switch language or try the visual mode. The App offers a local room, a temporary public link, or a connection to your own site. Keep the App running while sharing through it. @@ -99,8 +100,9 @@ domain, HTTPS reverse proxy and STUN address. Room data is stored in SQLite. ## Contributing -Bug reports, documentation improvements and pull requests are welcome. -Start with [CONTRIBUTING.md](./CONTRIBUTING.md), or see the +Bug reports, translations, documentation improvements and pull requests are welcome. +See [ways to contribute](./CONTRIBUTING.md#ways-to-help) or +[help translate Piik](./docs/guide/translating.md). Use the [repository layout](./docs/reference/engineering.md#repository-layout) to find your way around. ## License diff --git a/README.zh-CN.md b/README.zh-CN.md index e2f0f91..8078009 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -61,7 +61,8 @@ Piik 是一个免费开源的屏幕共享工具,适合游戏围观、一起看 1. 点击**开始分享**,选择要分享的画面和声音。 2. 点击**复制邀请链接**,发给朋友。分享期间保持标签页打开。 -Piik 默认使用图示按钮;点击顶部的**中**可显示文字。 +Piik 默认跟随系统语言,中文系统显示简体中文,其他语言显示英文。 +顶部的 **中 / EN / ✦** 可切换中英文或体验纯视觉模式。 客户端提供本地房间、临时公网邀请和连接已有站点三种模式,分享期间需保持 App 运行。 下载以 **`piik-app`** 开头的 ZIP 压缩包,再按文件名中的平台标识选择: @@ -95,8 +96,9 @@ Piik Server 是一个内置网页界面的独立程序。 ## 参与贡献 -欢迎提交问题、改进文档或参与开发。 -修改前请阅读[贡献指南](./CONTRIBUTING.md),也可以从 +欢迎反馈问题、参与翻译、改进文档或贡献代码。 +从[社区参与方式](./CONTRIBUTING.md#ways-to-help)或 +[翻译教程](./docs/guide/translating.zh-CN.md)开始,也可以从 [项目目录说明](./docs/reference/engineering.md#repository-layout)了解代码结构。 ## 许可 diff --git a/cmd/piik-app/README.md b/cmd/piik-app/README.md index 592111b..80e563a 100644 --- a/cmd/piik-app/README.md +++ b/cmd/piik-app/README.md @@ -28,8 +28,8 @@ publication are separate steps in [deployment](../../docs/deployment.md). origin; later manually opened Host pages may reuse the running App. - The `--site`, `--local`, and `--link` flags select a mode for CI and development. - The default launcher checks official releases after it opens, using GitHub - first and Gitee if GitHub is unavailable. It links to an available release - without installing or replacing the App. + first and Gitee if GitHub is unavailable. It opens the matching platform ZIP + when available, falling back to the release page. It does not install or replace the App. Local mode uses memory-only rooms, P2P relay, no SFU listener, and no public discovery. Ordinary Local works on a reachable LAN. The **Public invite** mode @@ -39,8 +39,14 @@ uses one ordinary public STUN destination plus two bounded public survey destinations. Browser and Native edges share the same prediction rule; Native STUN, ICE checks, and media use one Pion UDP mux. Media does not travel through the HTTP tunnel, and a difficult media path still has no SFU or -TURN fallback. The App chooses a sole private LAN IPv4 automatically. Use -`--lan-address
` only when multiple real LAN interfaces are active. +TURN fallback. + +For Local invitations, the App automatically selects a sole active address or +sole private IPv4. If several addresses remain possible, choose an interface +and IP in the launcher. Public invite and Site mode need no LAN selection. +When bypassing the launcher with `--local`, use `--lan-address
` to +resolve an ambiguous choice. The selected address is checked again at startup; +it affects Local invitation links, while ICE selects media paths independently. The system Browser remains the Host UI. An App-launched Host offers the Browser's standard capture picker and a list of exact platform capture targets; @@ -86,6 +92,9 @@ follows the launcher and App-enabled pages. Press `o` to reopen the Browser, or `q` / Ctrl+C to end Local rooms and stop the local server and temporary public link. Plain-text output uses Ctrl+C. The App keeps running after a Site tab closes. +When launched in its own Windows console, a startup or runtime error leaves the +error visible until Enter is pressed. Normal shutdown, existing terminals and +redirected or automated runs exit directly. For one-link Internet sharing, open the App launcher, choose **Public invite**, create a room in the opened Browser, and send its normal invitation link. The @@ -118,11 +127,12 @@ and the [verified policy mechanism and field case](../../docs/research/native-cl ### Diagnostics -Start the packaged executable with `--debug`, reproduce the problem, then press -`D` in the interactive terminal to export a local ZIP. This does not stop the -share or upload the archive. Browser diagnostics are separate: open the page with -`?debug=1` and use its header download button. For cooperation failures, include -both reports from the same reproduction. The +Enable **Debug launch** in the mode selector before opening Piik, reproduce the +problem, then press `D` in the interactive terminal to export a local ZIP. +Use `--debug` for failures before the selector opens. Exporting does not stop +the share or upload the archive. For Browser diagnostics, click the **Debug** +chip icon after the theme control, confirm the reload, then use **Web report** to download. +For cooperation failures, include both reports from the same reproduction. The [diagnostic reference](../../docs/reference/configuration.md#diagnostics) owns log locations, export commands, retention and privacy boundaries. @@ -197,7 +207,7 @@ Supported targets are `windows-amd64`, `linux-amd64`, and `darwin-arm64`. Use the matching target name in the command above. Darwin assembly requires a native macOS runner with its SDK and enables cgo; Windows and Linux assembly keep cgo disabled. -The result is a `tar.gz` bundle and SHA-256 file. Manual sidecar assembly, +The result is a ZIP bundle and SHA-256 file. Manual sidecar assembly, explicit CI packaging, Release publication and updates are documented in [deployment](../../docs/deployment.md); creating a candidate does not publish it. diff --git a/cmd/piik-app/exit_other.go b/cmd/piik-app/exit_other.go new file mode 100644 index 0000000..0e4c9a5 --- /dev/null +++ b/cmd/piik-app/exit_other.go @@ -0,0 +1,5 @@ +//go:build !windows + +package main + +func pauseOnError() {} diff --git a/cmd/piik-app/exit_windows.go b/cmd/piik-app/exit_windows.go new file mode 100644 index 0000000..b5fc7f6 --- /dev/null +++ b/cmd/piik-app/exit_windows.go @@ -0,0 +1,30 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "syscall" + "unsafe" + + "github.com/charmbracelet/x/term" +) + +func pauseOnError() { + if os.Getenv("PIIK_CLIENT_GATE_NO_BROWSER") == "true" || + !term.IsTerminal(os.Stdin.Fd()) || !term.IsTerminal(os.Stdout.Fd()) || !term.IsTerminal(os.Stderr.Fd()) { + return + } + // A shell sharing this console already preserves its output. Only keep a + // standalone window alive, after App resources and terminal mode are restored. + // https://learn.microsoft.com/en-us/windows/console/getconsoleprocesslist + var processID uint32 + count, _, _ := syscall.NewLazyDLL("kernel32.dll").NewProc("GetConsoleProcessList").Call( + uintptr(unsafe.Pointer(&processID)), 1, + ) + if count != 1 { + return + } + fmt.Fprintln(os.Stderr, "\nPress Enter to close this window. / 按回车关闭此窗口。") + _, _ = bufio.NewReader(os.Stdin).ReadString('\n') +} diff --git a/cmd/piik-app/exit_windows_test.go b/cmd/piik-app/exit_windows_test.go new file mode 100644 index 0000000..37cf555 --- /dev/null +++ b/cmd/piik-app/exit_windows_test.go @@ -0,0 +1,87 @@ +package main + +import ( + "context" + "os" + "os/exec" + "syscall" + "testing" + "time" + "unsafe" +) + +func TestStandaloneConsoleErrorPause(t *testing.T) { + const helper = "PIIK_TEST_STANDALONE_CONSOLE" + if os.Getenv(helper) == "" { + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) + defer cancel() + command := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestStandaloneConsoleErrorPause$") + command.WaitDelay = time.Second + command.Env = append(os.Environ(), helper+"=true") + command.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x10, HideWindow: true} // CREATE_NEW_CONSOLE + if output, err := command.CombinedOutput(); err != nil { + t.Fatalf("standalone console: %v\n%s", err, output) + } + return + } + // exec captures the test result; the real console handles model double-click + // launch without touching the parent terminal or a user's App configuration. + input, err := os.OpenFile("CONIN$", os.O_RDWR, 0) + if err != nil { + t.Fatal(err) + } + defer input.Close() + output, err := os.OpenFile("CONOUT$", os.O_RDWR, 0) + if err != nil { + t.Fatal(err) + } + defer output.Close() + stdin, stdout, stderr := os.Stdin, os.Stdout, os.Stderr + os.Stdin, os.Stdout, os.Stderr = input, output, output + defer func() { os.Stdin, os.Stdout, os.Stderr = stdin, stdout, stderr }() + t.Setenv("PIIK_CLIENT_GATE_NO_BROWSER", "false") + if os.Getenv(helper) == "shared" { + pauseOnError() + return + } + done := make(chan struct{}) + go func() { pauseOnError(); close(done) }() + select { + case <-done: + t.Fatal("the standalone error window closed before confirmation") + case <-time.After(150 * time.Millisecond): + } + // INPUT_RECORD containing a key-down Enter event. + event := struct { + kind, padding uint16 + down int32 + repeat, key uint16 + scan, char uint16 + control uint32 + }{kind: 1, down: 1, repeat: 1, key: 13, char: 13} + var written uint32 + ok, _, callErr := syscall.NewLazyDLL("kernel32.dll").NewProc("WriteConsoleInputW").Call( + input.Fd(), uintptr(unsafe.Pointer(&event)), 1, uintptr(unsafe.Pointer(&written)), + ) + if ok == 0 || written != 1 { + t.Fatalf("confirm close: %v", callErr) + } + select { + case <-done: + case <-time.After(2 * time.Second): + t.Fatal("Enter did not release the error window") + } + ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second) + defer cancel() + shared := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestStandaloneConsoleErrorPause$") + shared.Env = append(os.Environ(), helper+"=shared") + shared.WaitDelay = time.Second + if result, err := shared.CombinedOutput(); err != nil { + t.Fatalf("an existing terminal must exit directly: %v\n%s", err, result) + } + t.Setenv("PIIK_CLIENT_GATE_NO_BROWSER", "true") + pauseOnError() // Automation must not wait even when it owns a console. + os.Stdin, os.Stdout, os.Stderr = stdin, stdout, stderr + t.Setenv("PIIK_CLIENT_GATE_NO_BROWSER", "false") + pauseOnError() // Redirected output must not wait. +} diff --git a/cmd/piik-app/main.go b/cmd/piik-app/main.go index e55bca0..0e782ae 100644 --- a/cmd/piik-app/main.go +++ b/cmd/piik-app/main.go @@ -17,22 +17,36 @@ import ( ) func main() { + code := run(os.Args[1:]) + if code != 0 { + pauseOnError() + } + os.Exit(code) +} + +func run(args []string) int { var options app.Options - flag.Func("site", "save and open a Piik Site origin", func(value string) error { + flags := flag.NewFlagSet("piik-app", flag.ContinueOnError) + flags.Func("site", "save and open a Piik Site origin", func(value string) error { options.Site = value options.SiteSet = true return nil }) - flag.BoolVar(&options.Local, "local", false, "use the self-contained Local room authority") - flag.BoolVar(&options.Link, "link", false, "create one public Viewer invitation link") - flag.BoolVar(&options.Debug, "debug", false, "save opt-in App diagnostics to rotated files") - flag.StringVar(&options.LogDir, "log-dir", "", "diagnostic directory (overrides PIIK_LOG_DIR)") - flag.StringVar(&options.CaptureProcess, "capture-process", "", "path to the platform native capture process") - flag.StringVar(&options.TunnelProcess, "tunnel-process", "", "path to the packaged public tunnel process") - flag.StringVar(&options.ConfigPath, "config", "", "path to the App configuration file") - flag.StringVar(&options.LANAddress, "lan-address", "", "LAN IPv4 address used in Local invitations") - flag.IntVar(&options.Port, "port", app.DefaultLocalPort, "Local Piik server port") - flag.Parse() + flags.BoolVar(&options.Local, "local", false, "use the self-contained Local room authority") + flags.BoolVar(&options.Link, "link", false, "create one public Viewer invitation link") + flags.BoolVar(&options.Debug, "debug", false, "save opt-in App diagnostics to rotated files") + flags.StringVar(&options.LogDir, "log-dir", "", "diagnostic directory (overrides PIIK_LOG_DIR)") + flags.StringVar(&options.CaptureProcess, "capture-process", "", "path to the platform native capture process") + flags.StringVar(&options.TunnelProcess, "tunnel-process", "", "path to the packaged public tunnel process") + flags.StringVar(&options.ConfigPath, "config", "", "path to the App configuration file") + flags.StringVar(&options.LANAddress, "lan-address", "", "LAN IPv4 address used in Local invitations") + flags.IntVar(&options.Port, "port", app.DefaultLocalPort, "Local Piik server port") + if err := flags.Parse(args); err != nil { + if err == flag.ErrHelp { + return 0 + } + return 2 + } options.DisableBrowser = os.Getenv("PIIK_CLIENT_GATE_NO_BROWSER") == "true" ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) @@ -49,6 +63,7 @@ func main() { if options.DisableBrowser { fmt.Fprintln(os.Stderr, err) } - os.Exit(1) + return 1 } + return 0 } diff --git a/cmd/piik-app/main_test.go b/cmd/piik-app/main_test.go new file mode 100644 index 0000000..c4f7162 --- /dev/null +++ b/cmd/piik-app/main_test.go @@ -0,0 +1,23 @@ +package main + +import "testing" + +func TestCommandExitStatus(t *testing.T) { + for _, test := range []struct { + name string + args []string + want int + }{ + {"help", []string{"--help"}, 0}, + {"unknown flag", []string{"--not-a-piik-option"}, 2}, + {"invalid port", []string{"--port", "invalid"}, 2}, + {"startup failure", []string{"--local", "--link"}, 1}, + } { + t.Run(test.name, func(t *testing.T) { + t.Setenv("PIIK_CLIENT_GATE_NO_BROWSER", "true") + if got := run(test.args); got != test.want { + t.Fatalf("exit status = %d, want %d", got, test.want) + } + }) + } +} diff --git a/docs/README.md b/docs/README.md index 1b7e90c..ef49dee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,9 +18,10 @@ | Update my server | [Server updates](./operations/self-hosting.md#keep-it-running-and-update) | | Build, publish or recover a maintained release | [Maintainer release tooling](./deployment.md) | | Send a useful bug report | [Diagnostics and export](./reference/configuration.md#diagnostics) | +| Translate Piik or improve wording | [Translation guide](./guide/translating.md) · [中文翻译教程](./guide/translating.zh-CN.md) | | Check release and platform readiness | [Current status](./status.md) | -The introduction, getting-started and self-hosting guides are available in English and Chinese. +The introduction, getting-started, self-hosting and translation guides are available in English and Chinese. Technical references keep one shared version. ## Run from source diff --git a/docs/deployment.md b/docs/deployment.md index 2e25fdb..5e9cabc 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -48,6 +48,12 @@ extracts its own artifact to verify it. The archive contains exactly `piik-server`, `LICENSE`, `THIRD-PARTY-NOTICES.txt`, and `REVISION`. Upload the archive, manifest, and descriptor together to `/opt/piik/uploads`. +Public Release pages expose the runnable packages and put package checksums in +the release body. Retain the original complete build output outside the checkout +for later managed deployment or mirror retries: descriptors and manifests are +available in CI artifacts for 14 days and are not public Release attachments. +Do not rebuild an existing published version to replace missing metadata or bytes. + The same application descriptor is also the App assembly input. On each target platform, provide that platform's Go toolchain: diff --git a/docs/design/public-introduction.md b/docs/design/public-introduction.md index 50025bb..b71b261 100644 --- a/docs/design/public-introduction.md +++ b/docs/design/public-introduction.md @@ -135,7 +135,8 @@ The [website operations guide](../operations/website.md) owns Pages publication, public destinations and the separate P2P-only demo. The primary action leads to the platform download section. Each supported platform has a direct ZIP link using GitHub's native latest-release asset route. Build verified Gitee links from -the matching release and source revision, showing the mirror's specific version. +the matching release and source revision. Keep build-time versions out of +download captions. Keep release notes separate; when a mirror package is unverified, label its fallback as a release-page link. Verify destinations as part of publication. Demo, App and existing-site walkthroughs diff --git a/docs/design/visual-language.md b/docs/design/visual-language.md index c5c518c..31e000c 100644 --- a/docs/design/visual-language.md +++ b/docs/design/visual-language.md @@ -39,6 +39,17 @@ order and interaction ownership consistent across languages, themes and viewport sizes. Reflow or bounded scrolling must not hide a primary action, truncate an essential value, overlap controls or change meaning. +Keep the application language control's segmented Chinese, English and visual +shortcuts. When additional catalogs exist, insert one language menu +after the visual shortcut; all extra languages share that slot. Show the +selected language's short label there and native names inside the menu. Reuse +the sliding selection, rounded paper panels and control motion. The native +popover owns visibility, outside-click dismissal and Escape; the language +control owns menu navigation, focus and viewport placement. Keep touch rows +large and long lists scrollable. Locale labels belong to the shared registry. +Use a globe and chevron for the unselected picker, and each language's own full +name in the menu, following [W3C language-navigation guidance](https://www.w3.org/International/questions/qa-navigation-select). + Help-only indicators toggle their guidance on click or tap and dismiss on a second activation, outside tap, Escape or focus leaving. Action controls keep their direct click action and offer touch guidance on long-press. `Tooltip` diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 0e2a4ce..cd968dd 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -22,6 +22,9 @@ used below. **中** switches to Chinese; **✦** returns to the illustrations. 3. Move the pointer over the picture or tap it to show the playback bar. The playback bar controls only your own picture and sound. +On a computer, click the picture to pause or resume; double-click to enter or +leave fullscreen where supported. On a phone, tap the picture to show the bar +and use its buttons. Safari may show system controls in video fullscreen.
Playback controls, at a glance @@ -86,7 +89,7 @@ See the [App guide](../../cmd/piik-app/README.md) for package and runtime detail | Mode | When to choose it | What to keep in mind | | --- | --- | --- | -| **Local room** | Friends are on the same local network. | Your computer must be reachable from theirs. A local site passphrase is optional. | +| **Local room** | Friends are on the same local network. | If several **Local invitation addresses** appear, choose the interface and IP on your friends' network. Your computer must be reachable from theirs. A local site passphrase is optional. | | **Public invite** | Friends are elsewhere and you want a temporary invitation. | Needs Internet access. The public address lasts for this App run. | | **Connect to Site** | Your group already has a Piik site. | Enter its address; the App adds capture capabilities to the site's sharing page. | @@ -121,6 +124,7 @@ Invitation links grant access to that room; share them with the people you want | --- | --- | | Picture but no sound | Unmute the video. The host should choose a source with shareable audio; if sound was disabled, stop and start sharing with it enabled. App window/screen capture keeps that setting when switching sources. | | No screen picker | Allow the browser or App to record the screen when the OS asks. Browser capture needs HTTPS or `localhost`; try sharing from a desktop computer. | +| App startup fails | Read the reason on the page and in the terminal. Reopen the App and enable the chip-shaped **Debug launch** control after the theme button before trying again. A failed startup then exports a report; its path appears in the terminal. | | Local invitation will not open | Check that both devices are on the same network and can reach each other. Guest Wi-Fi or firewall rules can block local access. | | Page opens but video will not connect | Choose **Reconnect** in the playback bar. If it still fails, check [WebRTC connection settings](../../cmd/piik-app/README.md#chromium-webrtc-connections) or try a site with media fallback. | | Sharing stops after sleep or suspension | Wake the device and return to the sharing tab; start sharing again if needed. Browser and OS suspension can interrupt capture or playback. | diff --git a/docs/guide/getting-started.zh-CN.md b/docs/guide/getting-started.zh-CN.md index 5193627..5fa3278 100644 --- a/docs/guide/getting-started.zh-CN.md +++ b/docs/guide/getting-started.zh-CN.md @@ -21,6 +21,8 @@ Piik 支持一位房主分享屏幕,最多 20 位受邀观众通过浏览器 3. 将鼠标移到画面上,或轻点画面,显示播放控制栏。 播放控制栏只改变你自己看到的画面和听到的声音。 +电脑上单击画面可暂停或继续,双击可进入或退出全屏(浏览器支持时)。 +手机上轻点画面唤出控制栏,再使用其中的按钮。Safari 的视频全屏可能使用系统播控。
播放按钮分别做什么? @@ -79,7 +81,7 @@ Linux 原生采集还需要[系统组件](../../native/capture/linux/README.md) | 模式 | 什么时候用 | 需要知道 | | --- | --- | --- | -| **本地房间** | 朋友和你在同一个局域网。 | 对方需要能连接到你的电脑,可以设置本地站点口令。 | +| **本地房间** | 朋友和你在同一个局域网。 | 出现多个「局域网邀请地址」时,选择与朋友同一网络的网卡和 IP。对方需要能连接到你的电脑,可以设置本地站点口令。 | | **公网邀请** | 朋友在别处,想临时发个邀请。 | 需要互联网连接;公网地址只在本次 App 运行期间有效。 | | **连接站点** | 你们已经有一个 Piik 站点。 | 输入站点地址,在原有分享页面使用 App 的采集能力。 | @@ -110,6 +112,7 @@ Linux 原生采集还需要[系统组件](../../native/capture/linux/README.md) | --- | --- | | 有画面,没声音 | 先取消视频静音。房主应选择支持分享声音的来源;如果开始时关闭了声音,请停止后重新开始并打开声音。通过 App 采集窗口或屏幕时,切换来源会保留原来的声音设置。 | | 没有屏幕选择弹窗 | 按系统提示允许浏览器或 App 录制屏幕。浏览器采集需要 HTTPS 或 `localhost`,请在电脑上尝试分享。 | +| App 启动失败 | 先查看页面和黑窗口中的具体原因。重新打开 App,在主题按钮后的芯片图标开启「诊断启动」再重试。启动失败时会自动导出报告,保存位置见黑窗口。 | | 本地邀请打不开 | 确认两台设备在同一网络且可以互相访问。访客 Wi-Fi 或防火墙规则可能阻止本地连接。 | | 网页能打开,画面连不上 | 点击播放控制栏的 **重新连接**;仍然失败时,检查 [WebRTC 连接设置](../../cmd/piik-app/README.md#chromium-webrtc-connections),或尝试有媒体服务器兜底的站点。 | | 休眠或挂起后分享中断 | 唤醒设备,回到分享标签页,必要时重新开始分享。浏览器或系统挂起可能中断采集和播放。 | diff --git a/docs/guide/translating.md b/docs/guide/translating.md new file mode 100644 index 0000000..2c0b0a2 --- /dev/null +++ b/docs/guide/translating.md @@ -0,0 +1,169 @@ +# Help translate Piik + +[中文](./translating.zh-CN.md) · [Contributing](../../CONTRIBUTING.md) + +Piik currently includes Simplified Chinese and English. You can improve a label, +proofread a guide, or contribute another language. Translations are reviewed +through GitHub issues and pull requests. + +For a wording suggestion, [open an issue](https://github.com/TNTcraftHIM/Piik/issues/new/choose) +with the language, page or control, current text, proposed text and a brief reason. +A screenshot or the steps to reach the text help establish context. Remove +private invitations, passwords and personal content from screenshots. A wording +suggestion does not require a development environment. + +## Make your first correction + +1. Try the affected screen and read the nearby controls. Check both existing + languages when helpful; the [terminology and voice guide](../reference/naming.md#voice-and-terminology) + owns names and writing style. +2. Fork Piik and work on a branch from current `main`, following + [Contributing](../../CONTRIBUTING.md#lean-workflow). Small text edits can also + be proposed through GitHub's file editor. +3. Find the text in the files below. In a locale file, change the string value + after the colon. Keep the key and its placeholders. Search the key in the + source to see where it appears; for example: + + ```sh + rg -n -F 'host.title' src/client + ``` + +4. Preview the affected screen and run the [relevant checks](#preview-and-check). + For a typo in one language, edit that language. If the underlying meaning + changes, update the corresponding messages in every registered language. +5. Open a PR to `main`. Explain the wording problem, the language and surfaces + changed, and what you checked. Mention anything you could not preview; + maintainers can help with technical validation. PR format and publication + follow [Contributing](../../CONTRIBUTING.md#pull-request-scope). + +## Find the right text + +| Surface | Editable source | +| --- | --- | +| App launcher and shared Browser/App UI, including tooltips and accessible labels | [English](../../src/client/locales/en.ts), [Chinese](../../src/client/locales/zh.ts) | +| Welcome lines and changing browser-tab titles | `welcome.*` messages and the title-frame catalog at the end of those same files | +| Website | [Page markup](../../site/index.html) and [interactive labels](../../site/main.js); text is paired by `en` and `zh-CN` | +| Introduction film | [Film sources](../../site/film/README.md); captions, artwork and playback labels have their own bilingual text | +| App console window | `consoleCopy` in [console.go](../../internal/app/console.go); entries are ordered English, Chinese, visual; retain the third slot even when empty | +| README and entry guides | [README pair](../../README.md), [getting started](./getting-started.md) and [self-hosting](../operations/self-hosting.md), each with a `.zh-CN.md` counterpart | + +The App and Server serve the same web UI. Edit source files; build output and +release packages are generated. Translating an App message does not also +translate a website caption. Update related instructions when an action name +changes. Technical references keep one shared version; see +[documentation ownership](../maintenance.md). + +## Preserve meaning and syntax + +Write naturally for the target audience. Use the shared glossary for role and +password names. Introductions may be light; actions, errors and deployment steps +must be clear. Preserve the conditions on performance, platform and connection +claims. A cultural reference should still make sense to someone unfamiliar with it. +Machine-assisted drafts need proofreading by someone who understands the language +and the screen; raise unclear source text in the PR instead of guessing. + +Locale files are TypeScript objects. These are existing examples: + +```ts +// en.ts +"host.title": "{name}'s screen", +// zh.ts +"host.title": "{name} 的屏幕", +``` + +`host.title` is the lookup key; `{name}` is supplied by the application. Move the +placeholder to fit the sentence, preserving its spelling, braces and number of +occurrences. The current formatter substitutes each supplied variable once; +repeating `{name}` would leave the second occurrence visible. It accepts plain +strings, not HTML or ICU plural expressions. If a language needs plural forms +or different sentence composition, describe the actual case so the caller and +formatter can be changed together. + +Keep quotes, escapes and commas valid. Preserve commands, config keys, URLs, +download filenames and format markers such as `%s` in Go console text. Translate +link labels and image descriptions; repair internal anchors when translating +Markdown headings. Keep legal license text and third-party notices intact. + +## Add a language + +Search existing issues and PRs for the language first. For a larger translation, +start an issue with its language tag, intended coverage and any terminology or +layout questions, so contributors can coordinate work and review. + +1. Copy `src/client/locales/en.ts` to a file such as `fr.ts`. Translate the + messages and title frames, rename the two exports, and retain the + `Record` and `TitleFrameCatalog` types imported from `zh.ts`. + Chinese currently defines the key set; either existing language can provide + context. Complete all keys rather than spreading an English catalog over + missing translations. +2. Import the exports in [locales/index.ts](../../src/client/locales/index.ts) + and add one registry entry. For example, **after translating** the two exports: + + ```ts + fr: { name: "Français", short: "FR", tag: "fr", copy: fr, titleFrames: frTitleFrames }, + ``` + + Use an ASCII locale filename/key, the language's own name in `name`, a compact + button label in `short`, and a matching language tag in `tag`. Keep the short + label recognizable in one to three characters, such as `中`, `EN` or `FR`; + distinguish regional/script variants when they coexist. The registry supplies + the language controls, document language, saved selection and App launch handoff. + Chinese, English and visual mode retain their direct buttons; additional + languages share one dropdown slot after visual mode. The selected extra + language's short label appears on that slot, with its full name in the menu. + Initial selection matches a registered tag or key, then its base language; + unregistered languages use English. A saved choice takes precedence. + The shared [system-language policy](../product/presentation-lifecycle.md#visual-language) + also covers Chinese script and region variants across App, Server UI and website. +3. Check complete screens and the launch flow. New UI languages use English in + the App console until its own translation is added. Console additions also + require updating the launcher and loopback accepted-language checks together; + use the [App module map](../reference/engineering.md#module-map). +4. State coverage in the PR. Website, film and documentation translations can be + contributed separately; their existing language controls must be updated when + adding a language there. For right-to-left scripts, include text direction and + layout verification; a translated catalog alone does not establish RTL support. + +Pure-visual mode is an optional presentation, not another language to translate. +The small visual scenes are shared with text modes; translate their labels and +explanations in the normal catalog. Keep the visual option available. + +## Preview and check + +Use the Node/npm versions and local setup in [Run from source](../README.md#run-from-source). +For UI catalog edits, run from the repository root: + +```sh +npm ci +npm run typecheck +npm test -- tests/copy.test.ts +``` + +Type checking catches missing keys. The copy tests check registered catalogs for +empty text and mismatched placeholders, plus language selection and handoff. +Tests do not judge translation quality. + +Run the local UI and server as described in the source guide. Choose the language +from the top-right controls, open the affected screen and inspect long names or counts +in context. Check narrow windows, keyboard focus, tooltips and accessible labels. +With extra languages registered, verify that the dropdown stays usable near +viewport edges and exposes its expanded/collapsed state to assistive technology. +For new languages, also check a reload, App launch, switching back to English, +and the optional visual mode. Keep meaningful phrases together instead of adding +spaces or hard line breaks to force one desktop layout. + +Website and film changes use the [website preview](../operations/website.md#preview). +For Markdown changes, run `node scripts/check-docs.mjs` after staging new files, +then review GitHub's rendered Markdown and translated links. Additional checks +for code changes follow [Contributing](../../CONTRIBUTING.md#verification-entrypoints). + +## Further reading + +Reviewed 2026-09-13. These resources inform context-based proofreading and syntax +preservation; Piik's contribution steps above use its own repository workflow. + +- [OBS translation guide](https://github.com/obsproject/obs-studio/wiki/How-To-Contribute-Translations-For-OBS): proofread in the running application and understand technical terms. +- [Godot translation guide](https://contributing.godotengine.org/en/latest/other/translations.html): locate source context and coordinate terminology. +- [Weblate translation guidance](https://docs.weblate.org/en/latest/user/translating.html#translating-special-text-safely): handle placeholders and markup carefully. +- [W3C language-tag guidance](https://www.w3.org/International/questions/qa-choosing-language-tags): use BCP 47 tags to identify languages and their variants. +- [Unicode CLDR language names](https://cldr.unicode.org/translation/displaynames/languagelocale-names): reference names for menus. Browser `Intl.DisplayNames` can help look up native names. Piik's one-to-three-character button labels are editorial choices, not standardized CLDR language names; keep them in the registry with each reviewed translation. diff --git a/docs/guide/translating.zh-CN.md b/docs/guide/translating.zh-CN.md new file mode 100644 index 0000000..5ea9cc9 --- /dev/null +++ b/docs/guide/translating.zh-CN.md @@ -0,0 +1,139 @@ +# 参与 Piik 翻译 + +[English](./translating.md) · [贡献指南](../../CONTRIBUTING.md) + +Piik 目前提供简体中文和英文。欢迎改进按钮措辞、校对教程,或参与其他语言的翻译。 +翻译建议和修改通过 GitHub Issue 与 Pull Request(PR)讨论、审核。 + +只想改一句话,可以直接[提交建议](https://github.com/TNTcraftHIM/Piik/issues/new/choose): +写明语言、页面或控件、原文、建议译文及理由。附上截图或进入该页面的步骤,便于理解上下文; +截图中请移除私人邀请链接、密码和个人内容。提交措辞建议无需搭建开发环境。 + +## 从一处修改开始 + +1. 先试用对应界面,看看附近有哪些操作。必要时对照中英文理解含义; + 角色名称、术语与文风以[命名和文案规范](../reference/naming.md#voice-and-terminology)为准。 +2. Fork 仓库,从当前 `main` 创建工作分支,按[贡献流程](../../CONTRIBUTING.md#lean-workflow)修改。 + 少量文字也可以通过 GitHub 文件编辑器提出修改。 +3. 在下表对应的文件里查找原文。语言文件中只修改冒号后的译文,保留键名和占位符。 + 找到键名后,可以搜索它在哪些界面使用,例如: + + ```sh + rg -n -F 'host.title' src/client + ``` + +4. 预览对应界面并运行[相关检查](#预览与检查)。仅修正某种语言的错字时,修改该语言即可; + 如果调整的是功能含义,需要同步所有已登记语言中的对应内容。 +5. 向 `main` 提交 PR,说明原文的问题、修改的语言和页面,以及检查结果。 + 没有条件预览的部分请如实说明,维护者可以协助验证。 + PR 格式及发布流程见[贡献指南](../../CONTRIBUTING.md#pull-request-scope)。 + +## 文案在哪里 + +| 内容 | 修改位置 | +| --- | --- | +| App 启动页、网页端与 App 共用的操作界面,含 tooltip 和无障碍标签 | [英文词库](../../src/client/locales/en.ts)、[中文词库](../../src/client/locales/zh.ts) | +| 随机欢迎语、浏览器标签页的轮换标题 | 同一语言文件中的 `welcome.*` 及文件末尾的标题词库 | +| 官网 | [页面正文](../../site/index.html)和[交互文案](../../site/main.js),按 `en`、`zh-CN` 成对组织 | +| 宣传短片 | [短片源码说明](../../site/film/README.md),字幕、插画和播放控件有各自的中英文内容 | +| App 黑窗口 | [console.go](../../internal/app/console.go) 中的 `consoleCopy`,每项按英文、中文、纯视觉排列;第三项即使为空也需保留 | +| README 与入门教程 | [README](../../README.md)、[使用指南](./getting-started.md)、[部署指南](../operations/self-hosting.md),均有对应的 `.zh-CN.md` 文件 | + +App 与 Server 使用同一套网页操作界面。请修改源码,构建目录和发布包由工具生成。 +应用词库中的修改不会自动翻译官网文案;操作名称变化时,也要检查相关教程。 +技术参考文档保留一份公共版本,详见[文档职责](../maintenance.md)。 + +## 翻译时保留什么 + +按目标语言的习惯表达,角色和密码名称沿用公共术语表。介绍可以轻松,操作、错误和部署步骤要清楚。 +性能、平台、连接条件等限制应保留原意。梗和彩蛋应让不懂出处的人也能理解。 +机器辅助翻译需要由理解目标语言和使用场景的人校对;原文本身有歧义时,在 PR 中说明。 + +语言文件使用 TypeScript 对象。下面是现有的一组内容: + +```ts +// en.ts +"host.title": "{name}'s screen", +// zh.ts +"host.title": "{name} 的屏幕", +``` + +`host.title` 是程序查找文案的键名,`{name}` 由程序填入。 +可以调整占位符的位置,但要保留拼写、花括号和出现次数。 +当前格式化函数对每个变量替换一次;重复写 `{name}` 会让第二处仍显示占位符。 +词库使用普通字符串,不解析 HTML 或 ICU 复数表达式。 +如果目标语言需要复数变化或不同的句子组合,请给出具体例子,便于连同调用处一起调整。 + +保留代码的引号、转义和逗号。命令、配置项、URL、下载文件名,以及 Go 黑窗口文案中的 `%s` +等格式标记保持原样。链接显示文字和图片说明可以翻译;Markdown 标题变化后,要修正对应的页内链接。 +许可证原文及第三方声明保持完整。 + +## 新增一种语言 + +先搜索是否已有相关 Issue 或 PR。准备翻译较多内容时,可以开一个 Issue,说明语言代码、 +翻译范围,以及术语或排版疑问,方便大家分工和校对。 + +1. 复制 `src/client/locales/en.ts`,例如命名为 `fr.ts`。翻译正文和轮换标题,修改两个导出变量名, + 保留从 `zh.ts` 引入的 `Record`、`TitleFrameCatalog` 类型。 + 中文词库目前定义键名集合,理解内容时可以参考任一现有语言。 + 请补全全部键名,不要用展开英文词库的方式填充缺失译文。 +2. 在 [locales/index.ts](../../src/client/locales/index.ts) 中导入两个导出变量,并添加一项登记。 + 例如,**完成这两个词库的翻译后**: + + ```ts + fr: { name: "Français", short: "FR", tag: "fr", copy: fr, titleFrames: frTitleFrames }, + ``` + + 文件名和键名使用 ASCII 语言代码,`name` 填写该语言自己的名称,`short` 填写按钮简称, + `tag` 填写对应语言标签。简称采用一至三个易识别的字符,例如 `中`、`EN`、`FR`; + 同时提供地区或文字变体时,简称也应能区分。 + 此登记供语言控件、页面语言、偏好保存及 App 启动跳转共用。 + 中文、英文和纯视觉保留直接切换按钮;有其他语言时,在纯视觉后增加一个共用的下拉入口。 + 选中额外语言后,该格显示它的简称,菜单内显示全名。 + 首次使用先匹配已登记的标签或键名,再匹配基础语言;没有对应翻译时使用英文。 + 用户保存的选择优先。 + App、Server 网页界面及官网对中文地区和文字变体的默认选择,遵循 + [统一的系统语言规则](../product/presentation-lifecycle.md#visual-language)。 +3. 检查完整界面与启动流程。新增的界面语言在 App 黑窗口中暂用英文; + 翻译黑窗口时,还需同步启动服务和 loopback 对语言的校验,相关位置见 + [App 模块地图](../reference/engineering.md#module-map)。 +4. 在 PR 中写清覆盖范围。官网、短片和文档可以分别翻译,新增语言时需同步各自的语言切换入口。 + 从右向左书写的语言还需要验证文字方向与布局;仅有词库并不代表已支持 RTL 排版。 + +纯视觉模式是可选的呈现方式,不是另一种待翻译语言。小漫画和文字模式共用, +它们的文字标签与说明在普通词库中翻译,保留现有纯视觉选项即可。 + +## 预览与检查 + +Node/npm 版本及本地启动方法见[从源码运行](../README.md#run-from-source)。 +修改应用词库后,在仓库根目录执行: + +```sh +npm ci +npm run typecheck +npm test -- tests/copy.test.ts +``` + +类型检查能发现缺少的键名;文案测试会检查已登记词库的空文案、占位符不一致, +以及语言选择和启动跳转。测试不能代替语言质量的校对。 + +按源码指南启动本地界面与服务端,通过右上角控件选择语言,进入被修改的页面。 +结合长昵称、数量等实际内容,检查窄窗口、键盘焦点、tooltip 和无障碍标签。 +登记额外语言后,检查下拉菜单在视口边缘仍可操作,并能向辅助技术提供展开与收起状态。 +新增语言还应检查刷新、App 启动、切回英文及纯视觉模式。 +按完整语义断句,避免用空格或强制换行去适配单一桌面尺寸。 + +官网与短片使用[官网预览流程](../operations/website.md#preview)。 +Markdown 修改在暂存新文件后执行 `node scripts/check-docs.mjs`,并检查 GitHub 渲染结果与翻译后的链接。 +涉及代码时,额外检查按[贡献指南](../../CONTRIBUTING.md#verification-entrypoints)执行。 + +## 延伸阅读 + +核对日期:2026-09-13。以下资料提供上下文校对与语法保留的通用方法, +Piik 的提交流程以本页和贡献指南为准。 + +- [OBS 翻译指南](https://github.com/obsproject/obs-studio/wiki/How-To-Contribute-Translations-For-OBS):在运行中的界面校对,理解技术术语。 +- [Godot 翻译指南](https://contributing.godotengine.org/en/latest/other/translations.html):查找源码上下文,协作统一术语。 +- [Weblate 翻译说明](https://docs.weblate.org/en/latest/user/translating.html#translating-special-text-safely):处理占位符与标记语法。 +- [W3C 语言标签指南](https://www.w3.org/International/questions/qa-choosing-language-tags):用 BCP 47 标签区分语言及其变体。 +- [Unicode CLDR 语言名称](https://cldr.unicode.org/translation/displaynames/languagelocale-names):菜单名称的参考来源,也可以用浏览器的 `Intl.DisplayNames` 查找语言本名。Piik 按钮上的一至三个字符由项目选定,不是 CLDR 规定的标准简称;随经过校对的译文一起维护在语言登记表中。 diff --git a/docs/maintenance.md b/docs/maintenance.md index 5942301..4992d9d 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -14,6 +14,7 @@ verification procedures stay in [CONTRIBUTING.md](../CONTRIBUTING.md). | Branches, review, validation, PRs, releases and cleanup | `CONTRIBUTING.md` | Executable workflow | | Module boundaries, interface discipline and ablation | [Engineering](./reference/engineering.md) | Current responsibility map and shared coding rules | | Names, terminology and voice | [Naming and copy](./reference/naming.md) | Product/tool names, role labels and Chinese/English writing conventions | +| Translation contributions | [Translation guide](./guide/translating.md) and its Chinese counterpart | Catalog editing, new-language registration and translation review | | Illustration, palette, motion and accessible layout | [Visual language](./design/visual-language.md) | Shared Browser/App design grammar | | Status projection | [Media status](./design/media-status.md) | Which facts each display surface may express | | Release identity and compatibility | [Versioning](./reference/versioning.md) | Version roles, protected interfaces and public-readiness boundary | diff --git a/docs/operations/github.md b/docs/operations/github.md index 1528339..337861e 100644 --- a/docs/operations/github.md +++ b/docs/operations/github.md @@ -74,14 +74,24 @@ Rerun the failed publishing job to verify existing files and upload only missing ones. GitHub's published release remains unchanged. Both publishers reject changing a published package; use a corrected new version if its bytes are wrong. -If the original Actions artifacts have expired, download that release's files -from GitHub into an external working directory, then run: +For a local retry, use the original complete build output retained outside the +checkout. While the publishing run's artifacts are available, download all four +into one directory (replace `RUN_ID`, `FULL_SOURCE_SHA` and `VERSION`): ```sh -gh release download v1.0.0 --repo TNTcraftHIM/Piik --dir /tmp/piik-mirror -node scripts/mirror-release.mjs /tmp/piik-mirror v1.0.0 FULL_SOURCE_SHA +for artifact in piik-server piik-app-windows-amd64 piik-app-linux-amd64 piik-app-darwin-arm64; do + gh run download RUN_ID --repo TNTcraftHIM/Piik \ + --name "$artifact-FULL_SOURCE_SHA" --dir /tmp/piik-mirror +done +node scripts/mirror-release.mjs /tmp/piik-mirror VERSION FULL_SOURCE_SHA ``` +Use a fresh directory. Public Release downloads contain only runtime archives; +mirror verification also needs the original descriptors, manifest and checksum +files. If CI artifacts have expired, recover that complete output from the +operator's retained copy. Rebuilding a published version is not a metadata +recovery procedure. See [artifact retention](../deployment.md#build-host). + Use the release's actual version and full `target_commitish` SHA, with `GITEE_TOKEN` supplied through the publisher environment. `--dry-run` after the three arguments checks local identity, checksums and attachment sizes without diff --git a/docs/operations/website.md b/docs/operations/website.md index 227c2eb..ca8ec19 100644 --- a/docs/operations/website.md +++ b/docs/operations/website.md @@ -81,10 +81,10 @@ a website rebuild. `PIIK_WEBSITE_RELEASE_DATA` points the build at temporary JSO containing the published GitHub release and same-tag Gitee attachment metadata. CI supplies this input; the builder validates source identity, uploaded ZIPs, sizes and platform URLs. A missing/incomplete GitHub release fails deployment, -preserving the current site. Verified Gitee links show their specific version +preserving the current site. Verified Gitee links target the matched release and refresh after publication. Without matching metadata, Gitee retains its -labeled release-page link. The page makes no visitor API request or inferred -version claim when metadata is absent. +labeled release-page link. Download captions omit build-time version snapshots; +the page makes no visitor API request. ## Publish diff --git a/docs/product/presentation-lifecycle.md b/docs/product/presentation-lifecycle.md index ad6eb08..2633853 100644 --- a/docs/product/presentation-lifecycle.md +++ b/docs/product/presentation-lifecycle.md @@ -62,11 +62,16 @@ commands and responses retain their strict protocol contract. ## Visual Language Chinese, English, and pure-visual modes are three expressions of the same typed -state and command catalog. On first use, the App and Server UI follow the -Browser's primary system language: Chinese uses Chinese text, and other -languages use English. Pure-visual mode remains an explicit choice. An explicit -later choice is persisted and takes precedence over that default. Text modes -use concise copy. +state and command catalog. On first use, the App and Server UI match the +Browser's primary system language against registered locales, using English +when none matches. Current catalogs cover Simplified Chinese and English. +Both Simplified and Traditional Chinese system locales select the current +Chinese catalog across App, Server UI and website; this includes region and +script tags such as `zh-CN`, `zh-TW`, `zh-HK`, `zh-Hans` and `zh-Hant`. +Pure-visual mode remains an explicit choice. An explicit later choice is +persisted and takes precedence over that default, including App launch handoff. +The App console uses its own supported presentation languages and falls back +to English for additional UI languages. Text modes use concise copy. The [visual language](../design/visual-language.md) is the single owner of illustrative roles and objects, semantic colour, panel grammar, motion, @@ -77,8 +82,10 @@ accessible input and responsive hierarchy. One `