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 `