| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
chore: Use gpui-kit (#2929) ## Summary Zed owns the `gpui` crate names on crates.io and publishes them only occasionally. This PR adds `script/bump-gpui.ts` (Bun) to publish a snapshot of any Zed commit to crates.io under our own names, a weekly `Release GPUI` workflow that runs it, and switches the workspace, README and docs to those crates. | Zed crate | Published as | | --- | --- | | `gpui` | `gpui-pre` | | `gpui_platform` | `gpui-pre-platform` | | `gpui_macros` | `gpui-pre-macros` | | `reqwest_client` | `gpui-pre-reqwest-client` | | `gpui_<x>` / other internal crates | `gpui-pre-<x>` | What the script does: - Fetches the requested Zed revision (`--rev`, default `main`) into `target/gpui-pre/zed`, or reuses a checkout passed with `--zed`. - Walks the workspace path dependencies of the four root crates and publishes the whole closure (25 crates today) at one version, `<VERSION>.<N>` (e.g. `0.3.12`): the `VERSION` constant at the top of the script (`0.3`) plus a patch number that continues from the highest one crates.io already has (`0.3.0` first). A run that stopped part-way resumes the same number. A positional argument publishes an explicit version instead. - Keeps each crate's original name as `[lib] name`, so consumers write `gpui = { package = "gpui-pre", version = "0.3.0" }` and `use gpui::*` unchanged; `actions!` and `#[derive(Action)]` keep working. - Drops optional dependencies that come from git without a crates.io version (`proptest`, `async-tar`) together with the features that enable them, then removes optional dependencies nothing can enable any more. This keeps Zed's `util` crate (which needs Zed's git-patched `async-process`) out of the publish set. - Swaps the workspace `reqwest` dependency (Zed's git fork) for the hand-published `gpui-pre-reqwest` through `DEPENDENCY_OVERRIDES`. - Vendors the gpui source files that `gpui_apple`'s build script reads from `../gpui`, since a crate unpacked from crates.io has no such sibling. - Verifies everything with `cargo publish --workspace --dry-run`, then publishes with `cargo publish --workspace`, re-checking crates.io before each attempt and waiting out the new-crate rate limit so a run can be resumed. Workflow: - `.github/workflows/release-gpui.yml` runs the script every other Sunday (even ISO weeks, gated by a small `cadence` job since cron cannot express two weeks) at 18:00 Beijing time (`0 10 * * 0` UTC) and on `workflow_dispatch` with optional `rev` and `version` inputs, on `macos-latest` so `gpui_apple`'s Metal shader build script is verified. It uses the `CARGO_REGISTRY_TOKEN` secret and writes the published crate table to the job summary. Workspace and docs: - `Cargo.toml` now depends on `gpui-pre` 0.3.0 (`gpui`, `gpui_platform`, `gpui_web`, `gpui_macros`, `reqwest_client`, `sum-tree`) and `gpui-pre-reqwest` 0.12.15 instead of the Zed git repository, `zed-sum-tree` and Zed's reqwest fork. The `[profile.dev.package]` overrides follow the new package names. - README (en/zh-CN), the site docs (en/zh-CN), the gpui-component skill, and the crate READMEs show the `gpui-pre` dependency lines. - CONTRIBUTING documents the workflow and version scheme. ## gpui-kit `crates/kit` (published as `gpui-kit`, names already reserved on crates.io) is the one crate applications depend on. It pins the matching `gpui-pre-*` set and re-exports every layer: | Path | Crate | Feature | | --- | --- | --- | | `gpui_kit::gpui` | `gpui` | always | | `gpui_kit::platform` | `gpui_platform` | always | | `gpui_kit::base` | `gpui-base` | always | | `gpui_kit::component` | `gpui-component` | `component` (default) | | `gpui_kit::assets` | `gpui-kit-assets` | `assets` (default) | | `gpui_kit::shell` | `gpui-shell` | `shell` (default) | | `gpui_kit::webview` | `gpui-wry` | `webview` | `gpui_kit::application()` and `gpui_kit::init()` cover startup, and `gpui_kit::prelude::*` also brings the crate names into scope, so `gpui::…` paths and the `actions!` / `#[derive(Action)]` macros (which expand to `gpui::…`) work without a direct `gpui` dependency. The `gpui-component` features (`inspector`, `decimal`, `tree-sitter*`) are forwarded by name. README, the site docs, the skill, the crate READMEs and `hello_world` now show `gpui-kit = "0.6"` alone; the docs no longer link GPUI to gpui.rs. Workspace crates are bumped to 0.6.0. ## Breaking Changes `gpui-component-assets` is renamed `gpui-kit-assets`; the `links` key is unchanged so `gpui-component`'s build script still finds the icon directory. ```diff -gpui-component-assets = "0.5" +gpui-kit-assets = "0.6" ``` ```diff -use gpui_component_assets::Assets; +use gpui_kit_assets::Assets; ``` Applications that listed GPUI themselves can drop those lines: ```diff -gpui = { package = "gpui-pre", version = "0.3.0" } -gpui_platform = { package = "gpui-pre-platform", version = "0.3.0", features = ["font-kit"] } -gpui-component = "0.5" +gpui-kit = "0.6" ``` ## Release gate Before anything is uploaded, `script/bump-gpui.ts` builds and tests this repository against the staged crates (mirrored outside the checkout and injected with `--config patch.crates-io…`; `Cargo.lock` is scratch-updated and restored, and `cargo metadata` proves the patch resolved). CI's `check`, `clippy` and `test` must pass, so a Zed change that no longer fits `gpui-component` fails the release instead of reaching applications through their caret `gpui-pre` requirement on `cargo update`. `--skip-kit-check` bypasses it. The workflow installs the system dependencies for that step. The facade-aware macro rewrite now copies `#[cfg]` gates onto the wrapped bodies (the published 0.3.1 `gpui-pre-macros` breaks release builds without `inspector`; 0.3.2 fixes it) and the `actions!` rewrite is scoped to the `macro_rules!` block. `gpui-shell` is not published for now (its `llrt_*` dependencies are git-only) and is no longer part of `gpui-kit`; `gpui-fps` is. `release.yml` publishes our crates with one ordered `cargo publish -p …`. ## Verification - `./script/bump-gpui.ts --dry-run --zed <local zed checkout>`: all 25 crates package and build; `gpui-pre-reqwest` resolves from crates.io. - With a temporary `[patch.crates-io]` pointing at the staged workspace (not committed), `cargo check --workspace` passes for every crate, story, story-web and example. - A scratch consumer crate depending on the staged crates via `package = "gpui-pre"` passes `cargo check`, including `actions!`, `#[derive(Action)]` and `collections::` by its original lib name. - `gpui-kit`: `cargo check` with default, no, `base`-only, `component`-only and `webview,inspector,tree-sitter-rust` features, its doc tests, `cargo check --workspace`, the CI clippy set plus `gpui-kit` and `hello_world` with `--deny warnings`, and `cargo machete` all pass against the staged `gpui-pre`. - `tsc --strict` with `@types/bun`, `typos`, and a YAML parse of the workflow pass. `gpui-pre` 0.3.0 is not on crates.io yet, so CI cannot resolve dependencies until the `Release GPUI` workflow has run (by hand, or on the next even ISO week Sunday). A requirement of `0.3.0` accepts every later `0.3.x` as well. `Cargo.lock` is left for that first resolution. ## AI assistance The script, workflow, dependency switch and documentation were written with Claude Code and reviewed by hand. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01D2PVGzukYKuXnb3LB5s53T --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> | 30 天前 | |
i18n: extend component locales in story (#2563) ## Summary - upgrade to rust-i18n 4.2.0 and register Story locales with `extend!(gpui_component)` - add French Calendar and DatePicker translations as a Story-only extension - keep the component’s built-in Italian and Traditional Chinese locales unchanged - add French to the Story language menu and cover extension fallback behavior with a test - document custom I18n setup, namespace rules, override priority, and locale selection in English and Simplified Chinese This uses rust-i18n’s locale extension mechanism to address the customization requirement discussed in #2548 without adding every downstream locale to gpui-component itself. Refs #2548 ## Test Plan - `cargo test -p gpui-component-story` - `git diff --check` - `bun run build` *(blocked by the current Bun/Vite Vue runtime: `crypto.hash is not a function` while compiling the existing `contributors.md`)* --------- Co-authored-by: Codex <codex@openai.com> | 2 个月前 | |
speech: Add speech input (#3333) ## Summary Speech input for GPUI Component: capture audio, recognize it and hand the text to the application. - **`SpeechState`** runs a session (`Idle → Connecting → Recording → Stopping`) and tracks the transcript. It emits `SpeechEvent::Partial` / `Final` with the whole transcript so far, plus `Started`, `Cancelled` and `Error`. - **`SpeechButton`** starts and stops the session. It shows a microphone at rest and a pressed stop glyph while capturing. While the final result is pending it shows a spinner. - **`SpeechWaveform`** draws the recent input levels. - **Two seams:** - `SpeechRecognizer` / `RecognitionSession` / `SpeechSink` plug in any speech service, for example a cloud service over WebSocket. - `AudioInput` / `AudioSink` plug in any audio source. - **Recognizer order:** the application's recognizer, else the platform's `SystemRecognizer` (unless `.system_fallback(false)`), else none. With none, `has_recognizer()` is false and the button renders nothing, unless `show_when_unsupported(true)` is set. https://github.com/user-attachments/assets/8991cb40-7a83-4362-bcc8-7304f44859b7 ### Platforms (`speech` feature) | | Capture | System recognizer | | --- | --- | --- | | macOS | cpal (Core Audio) | `SFSpeechRecognizer`, on-device only. A language without offline support is unavailable. Needs `NSMicrophoneUsageDescription` and `NSSpeechRecognitionUsageDescription`; without the latter it reports unavailable instead of letting the system terminate the process. | | Windows | cpal (WASAPI) | `Windows.Media.SpeechRecognition` continuous dictation. Needs the language's speech pack and "Online speech recognition". The recognizer captures from the default device itself, so pushed PCM only drives the waveform. | | Linux | cpal (ALSA) | None. An application recognizer is required. Building needs `libasound2-dev`. | | wasm | — | Microphone and system recognizer are compiled out. | `SpeechAnalyzer` (macOS 26) is Swift-only and not reachable through objc2, so macOS uses `SFSpeechRecognizer` on every version. ### Also in this PR - **Story:** `DemoRecognizer` implements the trait with no service, and the story also shows the system recognizer. - **Docs:** `website/component/speech.md` and the zh-CN page. - **`examples/speech`:** a dictation notepad that doubles as a test bench. - The sidebar shows platform, input device, recognizer availability and session state, and a session log records every event. - `cargo run -p speech -- --check` prints the checks without opening a window. - `build.rs` links the usage descriptions into the macOS executable, so `cargo run` can ask for access without an app bundle. - Its demo recognizer needs no service: it types a scripted passage only while the input sounds like speech and ends a sentence after a 0.8 s pause. - **Dependencies:** cpal 0.15; optional objc2 Speech / AVFAudio / AVFoundation bindings on macOS; WinRT features of the existing `windows` dependency. All come in only with `speech`. cpal 0.15 brings its own older `windows` crate on Windows. - **Icons:** `Mic` and `Square` join the default icon set. - **Linux setup:** `libasound2-dev` is added to `script/install-linux.sh` and to the installation docs. ## Public API ### gpui-component (`gpui_component::speech`) - `SpeechState` — the session state. `EventEmitter<SpeechEvent>`. - `new(cx: &mut Context<Self>) -> Self` - `recognizer(self, recognizer: impl SpeechRecognizer) -> Self` — use this recognizer instead of the system's. - `input(self, input: impl AudioInput) -> Self` — capture from this input instead of the microphone. - `system_fallback(self, system_fallback: bool) -> Self` — fall back to `SystemRecognizer` (default `true`). - `stop_timeout(self, timeout: Duration) -> Self` — how long `stop` waits for the final result (default 3 s). - `status(&self) -> SpeechStatus`, `has_recognizer(&self) -> bool`, `is_available(&self, cx: &App) -> bool`, `transcript(&self) -> SharedString`, `levels(&self) -> impl ExactSizeIterator<Item = f32>` (one per 25 ms of audio, fast attack / slow release, noise floor) - `start` / `stop` / `cancel` / `toggle(&mut self, cx: &mut Context<Self>)` - `SpeechStatus` — `Idle`, `Connecting`, `Recording`, `Stopping`; `is_active(self) -> bool`, `is_capturing(self) -> bool`. - `SpeechEvent` — `Started`, `Partial(SharedString)`, `Final(SharedString)`, `Cancelled`, `Error(SpeechError)`. - `SpeechButton` — `new(state: &Entity<SpeechState>) -> Self`, `show_when_unsupported(self, show: bool) -> Self`; `Sizable`, `Disableable`, `Styled` (refines the inner `Button`). - `SpeechWaveform` — `new(state: &Entity<SpeechState>) -> Self`; `Sizable` (height), `Styled` (width, 96 px by default). Fills its width with bars centered on the midline, the newest at the trailing edge; redraws every frame and scrolls while capturing, steps only under reduced motion. - `trait SpeechRecognizer: 'static`, also implemented for `Rc<T>`: - `audio_format(&self) -> AudioFormat` (default 16 kHz mono) - `is_available(&self, cx: &App) -> bool` (default `true`) - `start(&self, sink: SpeechSink, cx: &mut App) -> Result<Box<dyn RecognitionSession>, SpeechError>` - `trait RecognitionSession: 'static` — `push_audio(&mut self, samples: &[i16], cx: &mut App)`, `finish(&mut self, cx: &mut App)`. Dropping it cancels the session. - `SpeechSink` (`Clone`) — `ready`, `hypothesis(text)`, `phrase(text)`, `finish`, `error(SpeechError)`. Calls are deferred and ignored once the session is over. - `trait AudioInput: 'static`, also implemented for `Rc<T>` — `start(&self, format: AudioFormat, sink: AudioSink, cx: &mut App) -> Result<Subscription, SpeechError>`. Capture runs until the `Subscription` is dropped. - `AudioSink` (`Clone`) — `push(samples: Vec<i16>, cx)`, `error(SpeechError, cx)`. - `AudioFormat` — `new(sample_rate: u32, channels: u16) -> Self`, `sample_rate()`, `channels()`, `Default` (16 kHz mono). - `SpeechError` — `PermissionDenied`, `NoInputDevice`, `Unsupported`, `Input(Arc<anyhow::Error>)`, `Recognizer(Arc<anyhow::Error>)`; `input(error)`, `recognizer(error)`; `Display` and `Error`. - `Microphone` (`speech`, native) — the default input device through cpal. `Default`, `AudioInput`. - `SystemRecognizer` (`speech`, native) — the platform recognizer. `new()`, `locale(self, locale: impl Into<SharedString>) -> Self`, `Default`, `SpeechRecognizer`. - Feature `speech`. - `IconName::Mic`, `IconName::Square` join the default component icon set. - Locale keys `Speech.Start`, `Speech.Stop`, `Speech.Unavailable` (en, zh-CN, zh-HK, zh-TW). ### gpui-kit - Feature `speech`, forwarding `gpui-component/speech`. ## Test plan - `cargo test -p gpui-component --features speech --lib speech` — 22 tests: - the state machine (6): full session, stop timeout, cancel with late results ignored, input error, failing start, unsupported; - input levels (6): 25 ms windows across pushes, fast attack / slow release, the noise floor; - the waveform geometry (3): bars centered on the midline, the trailing-edge scroll, clipping at the leading edge; - the resampler (3) across chunk boundaries; - the macOS recognizer (3): a revised utterance carries on, a restarted one is committed, phrase spacing by script; - the button builder (1). - `cargo test -p speech` — the example's demo recognizer tells speech from silence (2 tests). - `cargo test -p gpui-kit-assets --test icons` (106 default icons). - `cargo clippy -p gpui-component -p gpui-component-story -p gpui-kit -p speech --features gpui-kit/speech --all-targets -- -D warnings` - `cargo check -p gpui-component` without the feature. - `cargo check` / `clippy -D warnings` for `x86_64-pc-windows-msvc` with `--features speech`, cross-checked from macOS. - `cargo check -p gpui-component-story-web --target wasm32-unknown-unknown`. - `cargo run -p speech -- --check` on macOS 27: en-US and zh-CN are available; en-GB, zh-HK and ja-JP are not. - The example's window, idle and recording (demo recognizer with generated audio), was captured and reviewed. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 天前 | |
speech: Add speech input (#3333) ## Summary Speech input for GPUI Component: capture audio, recognize it and hand the text to the application. - **`SpeechState`** runs a session (`Idle → Connecting → Recording → Stopping`) and tracks the transcript. It emits `SpeechEvent::Partial` / `Final` with the whole transcript so far, plus `Started`, `Cancelled` and `Error`. - **`SpeechButton`** starts and stops the session. It shows a microphone at rest and a pressed stop glyph while capturing. While the final result is pending it shows a spinner. - **`SpeechWaveform`** draws the recent input levels. - **Two seams:** - `SpeechRecognizer` / `RecognitionSession` / `SpeechSink` plug in any speech service, for example a cloud service over WebSocket. - `AudioInput` / `AudioSink` plug in any audio source. - **Recognizer order:** the application's recognizer, else the platform's `SystemRecognizer` (unless `.system_fallback(false)`), else none. With none, `has_recognizer()` is false and the button renders nothing, unless `show_when_unsupported(true)` is set. https://github.com/user-attachments/assets/8991cb40-7a83-4362-bcc8-7304f44859b7 ### Platforms (`speech` feature) | | Capture | System recognizer | | --- | --- | --- | | macOS | cpal (Core Audio) | `SFSpeechRecognizer`, on-device only. A language without offline support is unavailable. Needs `NSMicrophoneUsageDescription` and `NSSpeechRecognitionUsageDescription`; without the latter it reports unavailable instead of letting the system terminate the process. | | Windows | cpal (WASAPI) | `Windows.Media.SpeechRecognition` continuous dictation. Needs the language's speech pack and "Online speech recognition". The recognizer captures from the default device itself, so pushed PCM only drives the waveform. | | Linux | cpal (ALSA) | None. An application recognizer is required. Building needs `libasound2-dev`. | | wasm | — | Microphone and system recognizer are compiled out. | `SpeechAnalyzer` (macOS 26) is Swift-only and not reachable through objc2, so macOS uses `SFSpeechRecognizer` on every version. ### Also in this PR - **Story:** `DemoRecognizer` implements the trait with no service, and the story also shows the system recognizer. - **Docs:** `website/component/speech.md` and the zh-CN page. - **`examples/speech`:** a dictation notepad that doubles as a test bench. - The sidebar shows platform, input device, recognizer availability and session state, and a session log records every event. - `cargo run -p speech -- --check` prints the checks without opening a window. - `build.rs` links the usage descriptions into the macOS executable, so `cargo run` can ask for access without an app bundle. - Its demo recognizer needs no service: it types a scripted passage only while the input sounds like speech and ends a sentence after a 0.8 s pause. - **Dependencies:** cpal 0.15; optional objc2 Speech / AVFAudio / AVFoundation bindings on macOS; WinRT features of the existing `windows` dependency. All come in only with `speech`. cpal 0.15 brings its own older `windows` crate on Windows. - **Icons:** `Mic` and `Square` join the default icon set. - **Linux setup:** `libasound2-dev` is added to `script/install-linux.sh` and to the installation docs. ## Public API ### gpui-component (`gpui_component::speech`) - `SpeechState` — the session state. `EventEmitter<SpeechEvent>`. - `new(cx: &mut Context<Self>) -> Self` - `recognizer(self, recognizer: impl SpeechRecognizer) -> Self` — use this recognizer instead of the system's. - `input(self, input: impl AudioInput) -> Self` — capture from this input instead of the microphone. - `system_fallback(self, system_fallback: bool) -> Self` — fall back to `SystemRecognizer` (default `true`). - `stop_timeout(self, timeout: Duration) -> Self` — how long `stop` waits for the final result (default 3 s). - `status(&self) -> SpeechStatus`, `has_recognizer(&self) -> bool`, `is_available(&self, cx: &App) -> bool`, `transcript(&self) -> SharedString`, `levels(&self) -> impl ExactSizeIterator<Item = f32>` (one per 25 ms of audio, fast attack / slow release, noise floor) - `start` / `stop` / `cancel` / `toggle(&mut self, cx: &mut Context<Self>)` - `SpeechStatus` — `Idle`, `Connecting`, `Recording`, `Stopping`; `is_active(self) -> bool`, `is_capturing(self) -> bool`. - `SpeechEvent` — `Started`, `Partial(SharedString)`, `Final(SharedString)`, `Cancelled`, `Error(SpeechError)`. - `SpeechButton` — `new(state: &Entity<SpeechState>) -> Self`, `show_when_unsupported(self, show: bool) -> Self`; `Sizable`, `Disableable`, `Styled` (refines the inner `Button`). - `SpeechWaveform` — `new(state: &Entity<SpeechState>) -> Self`; `Sizable` (height), `Styled` (width, 96 px by default). Fills its width with bars centered on the midline, the newest at the trailing edge; redraws every frame and scrolls while capturing, steps only under reduced motion. - `trait SpeechRecognizer: 'static`, also implemented for `Rc<T>`: - `audio_format(&self) -> AudioFormat` (default 16 kHz mono) - `is_available(&self, cx: &App) -> bool` (default `true`) - `start(&self, sink: SpeechSink, cx: &mut App) -> Result<Box<dyn RecognitionSession>, SpeechError>` - `trait RecognitionSession: 'static` — `push_audio(&mut self, samples: &[i16], cx: &mut App)`, `finish(&mut self, cx: &mut App)`. Dropping it cancels the session. - `SpeechSink` (`Clone`) — `ready`, `hypothesis(text)`, `phrase(text)`, `finish`, `error(SpeechError)`. Calls are deferred and ignored once the session is over. - `trait AudioInput: 'static`, also implemented for `Rc<T>` — `start(&self, format: AudioFormat, sink: AudioSink, cx: &mut App) -> Result<Subscription, SpeechError>`. Capture runs until the `Subscription` is dropped. - `AudioSink` (`Clone`) — `push(samples: Vec<i16>, cx)`, `error(SpeechError, cx)`. - `AudioFormat` — `new(sample_rate: u32, channels: u16) -> Self`, `sample_rate()`, `channels()`, `Default` (16 kHz mono). - `SpeechError` — `PermissionDenied`, `NoInputDevice`, `Unsupported`, `Input(Arc<anyhow::Error>)`, `Recognizer(Arc<anyhow::Error>)`; `input(error)`, `recognizer(error)`; `Display` and `Error`. - `Microphone` (`speech`, native) — the default input device through cpal. `Default`, `AudioInput`. - `SystemRecognizer` (`speech`, native) — the platform recognizer. `new()`, `locale(self, locale: impl Into<SharedString>) -> Self`, `Default`, `SpeechRecognizer`. - Feature `speech`. - `IconName::Mic`, `IconName::Square` join the default component icon set. - Locale keys `Speech.Start`, `Speech.Stop`, `Speech.Unavailable` (en, zh-CN, zh-HK, zh-TW). ### gpui-kit - Feature `speech`, forwarding `gpui-component/speech`. ## Test plan - `cargo test -p gpui-component --features speech --lib speech` — 22 tests: - the state machine (6): full session, stop timeout, cancel with late results ignored, input error, failing start, unsupported; - input levels (6): 25 ms windows across pushes, fast attack / slow release, the noise floor; - the waveform geometry (3): bars centered on the midline, the trailing-edge scroll, clipping at the leading edge; - the resampler (3) across chunk boundaries; - the macOS recognizer (3): a revised utterance carries on, a restarted one is committed, phrase spacing by script; - the button builder (1). - `cargo test -p speech` — the example's demo recognizer tells speech from silence (2 tests). - `cargo test -p gpui-kit-assets --test icons` (106 default icons). - `cargo clippy -p gpui-component -p gpui-component-story -p gpui-kit -p speech --features gpui-kit/speech --all-targets -- -D warnings` - `cargo check -p gpui-component` without the feature. - `cargo check` / `clippy -D warnings` for `x86_64-pc-windows-msvc` with `--features speech`, cross-checked from macOS. - `cargo check -p gpui-component-story-web --target wasm32-unknown-unknown`. - `cargo run -p speech -- --check` on macOS 27: en-US and zh-CN are available; en-GB, zh-HK and ja-JP are not. - The example's window, idle and recording (demo recognizer with generated audio), was captured and reviewed. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 天前 |