Compare commits

...

7 Commits

Author SHA1 Message Date
Cole Miller
151926af56 Revert "git: Mark entries as pending when staging a files making the staged highlighting more "optimistic" (#43434)"
This reverts commit 74b4013e67.
2025-12-17 19:09:17 -05:00
Cole Miller
6750de3830 Update zed.rs 2025-12-17 19:02:59 -05:00
Kingsword
0c9992c5e9 terminal: Forward Ctrl+V when clipboard contains images (#42258)
When running Codex CLI, Claude Code, or other TUI agents in Zed’s
terminal, pasting images wasn’t supported — Zed
treated all clipboard content as plain text and simply pushed it into
the PTY, so the agent never saw the image data.
This change makes terminal pastes behave like they do in a native
terminal: if the clipboard contains an image, Zed now emits a raw Ctrl+V
to the PTY so the agent can read the system clipboard itself.

Release Notes:

- Fixed terminal-launched Codex/Claude sessions by forwarding Ctrl+V for
clipboard images so agents can attach them
2025-12-17 20:42:47 -03:00
Mayank Verma
cec46079fe git_ui: Preserve newlines in commit messages (#45167)
Closes #44982

Release Notes:

- Fixed Git panel to preserve newlines in commit messages
2025-12-17 22:52:10 +00:00
Ben Kunkle
f9b69aeff0 Fix Wayland platform resize resulting in non-interactive window (#45153)
Closes  #40361

Release Notes:

- Linux(Wayland): Fixed an issue where the settings window would not
respond to user interaction until resized
2025-12-17 17:44:25 -05:00
Nathan Sobo
f00cb371f4 macOS: Bundle placeholder Document.icns so Finder can display Zed file icons (#44833)
Generated by AI.

`DocumentTypes.plist` declares `CFBundleTypeIconFile` as `Document` for
Zed’s document types, but the macOS bundle did not include
`Contents/Resources/Document.icns`, causing Finder to fall back to
generic icons.

This PR:
- Adds `crates/zed/resources/Document.icns` as a placeholder document
icon (currently derived from the app icon).
- Updates `script/bundle-mac` to copy it into the `.app` at
`Contents/Resources/Document.icns` during bundling.
- Adds `script/verify-macos-document-icon` for one-command validation.

## How to test (CLI)
1. Build a debug bundle:
   - `./script/bundle-mac -d aarch64-apple-darwin`
2. Verify the bundle contains the referenced icon:
- `./script/verify-macos-document-icon
"target/aarch64-apple-darwin/debug/bundle/osx/Zed Dev.app"`

## Optional visual validation in Finder
- Pick a file (e.g. `.rs`), Get Info → Open with: Zed Dev → Change
All...
- Restart Finder: `killall Finder` (or log out/in)

@JosephTLyons — would you mind running the steps above and confirming
Finder shows Zed’s icon for source files after "Change All" + Finder
restart?

@danilo-leal — this PR ships a placeholder `Document.icns`. When the
real document icon is ready, replace
`crates/zed/resources/Document.icns` and the bundling script will
include it automatically.


Closes #44403.

Release Notes:

- TODO

---------

Co-authored-by: Matt Miller <mattrx@gmail.com>
2025-12-17 16:42:31 -06:00
Ben Kunkle
25e1e2ecdd Don't trigger autosave on focus change in modals (#45166)
Closes #28732

Release Notes:

- Opening the command palette or other modals no longer triggers
auto-save with the `{ "autosave": "on_focus_change" }` setting. This
reduces the chance of unwanted format changes when executing actions,
and fixes a race condition with `:w` in Vim mode
2025-12-17 17:42:18 -05:00
9 changed files with 148 additions and 96 deletions

View File

@@ -15,6 +15,7 @@ use askpass::AskPassDelegate;
use cloud_llm_client::CompletionIntent;
use collections::{BTreeMap, HashMap, HashSet};
use db::kvp::KEY_VALUE_STORE;
use editor::RewrapOptions;
use editor::{
Direction, Editor, EditorElement, EditorMode, MultiBuffer, MultiBufferOffset,
actions::ExpandAllDiffHunks,
@@ -2180,7 +2181,13 @@ impl GitPanel {
let editor = cx.new(|cx| Editor::for_buffer(buffer, None, window, cx));
let wrapped_message = editor.update(cx, |editor, cx| {
editor.select_all(&Default::default(), window, cx);
editor.rewrap(&Default::default(), window, cx);
editor.rewrap_impl(
RewrapOptions {
override_language_settings: false,
preserve_existing_whitespace: true,
},
cx,
);
editor.text(cx)
});
if wrapped_message.trim().is_empty() {

View File

@@ -1025,13 +1025,26 @@ impl PlatformWindow for WaylandWindow {
fn resize(&mut self, size: Size<Pixels>) {
let state = self.borrow();
let state_ptr = self.0.clone();
let dp_size = size.to_device_pixels(self.scale_factor());
// Keep window geometry consistent with configure handling. On Wayland, window geometry is
// surface-local: resizing should not attempt to translate the window; the compositor
// controls placement. We also account for client-side decoration insets and tiling.
let window_geometry = inset_by_tiling(
Bounds {
origin: Point::default(),
size,
},
state.inset(),
state.tiling,
)
.map(|v| v.0 as i32)
.map_size(|v| if v <= 0 { 1 } else { v });
state.surface_state.set_geometry(
state.bounds.origin.x.0 as i32,
state.bounds.origin.y.0 as i32,
dp_size.width.0,
dp_size.height.0,
window_geometry.origin.x,
window_geometry.origin.y,
window_geometry.size.width,
window_geometry.size.height,
);
state

View File

@@ -1672,59 +1672,6 @@ impl GitStore {
}
}
fn mark_entries_pending_by_project_paths(
&mut self,
project_paths: &[ProjectPath],
stage: bool,
cx: &mut Context<Self>,
) {
let buffer_store = &self.buffer_store;
for project_path in project_paths {
let Some(buffer) = buffer_store.read(cx).get_by_path(project_path) else {
continue;
};
let buffer_id = buffer.read(cx).remote_id();
let Some(diff_state) = self.diffs.get(&buffer_id) else {
continue;
};
diff_state.update(cx, |diff_state, cx| {
let Some(uncommitted_diff) = diff_state.uncommitted_diff() else {
return;
};
let buffer_snapshot = buffer.read(cx).text_snapshot();
let file_exists = buffer
.read(cx)
.file()
.is_some_and(|file| file.disk_state().exists());
let all_hunks: Vec<_> = uncommitted_diff
.read(cx)
.hunks_intersecting_range(
text::Anchor::MIN..text::Anchor::MAX,
&buffer_snapshot,
cx,
)
.collect();
if !all_hunks.is_empty() {
uncommitted_diff.update(cx, |diff, cx| {
diff.stage_or_unstage_hunks(
stage,
&all_hunks,
&buffer_snapshot,
file_exists,
cx,
);
});
}
});
}
}
pub fn git_clone(
&self,
repo: String,
@@ -4253,28 +4200,6 @@ impl Repository {
save_futures
}
fn mark_entries_pending_for_stage(
&self,
entries: &[RepoPath],
stage: bool,
cx: &mut Context<Self>,
) {
let Some(git_store) = self.git_store() else {
return;
};
let mut project_paths = Vec::new();
for repo_path in entries {
if let Some(project_path) = self.repo_path_to_project_path(repo_path, cx) {
project_paths.push(project_path);
}
}
git_store.update(cx, move |git_store, cx| {
git_store.mark_entries_pending_by_project_paths(&project_paths, stage, cx);
});
}
pub fn stage_entries(
&mut self,
entries: Vec<RepoPath>,
@@ -4283,9 +4208,6 @@ impl Repository {
if entries.is_empty() {
return Task::ready(Ok(()));
}
self.mark_entries_pending_for_stage(&entries, true, cx);
let id = self.id;
let save_tasks = self.save_buffers(&entries, cx);
let paths = entries
@@ -4351,9 +4273,6 @@ impl Repository {
if entries.is_empty() {
return Task::ready(Ok(()));
}
self.mark_entries_pending_for_stage(&entries, false, cx);
let id = self.id;
let save_tasks = self.save_buffers(&entries, cx);
let paths = entries

View File

@@ -8,8 +8,8 @@ mod terminal_slash_command;
use assistant_slash_command::SlashCommandRegistry;
use editor::{EditorSettings, actions::SelectAll, blink_manager::BlinkManager};
use gpui::{
Action, AnyElement, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
KeyContext, KeyDownEvent, Keystroke, MouseButton, MouseDownEvent, Pixels, Render,
Action, AnyElement, App, ClipboardEntry, DismissEvent, Entity, EventEmitter, FocusHandle,
Focusable, KeyContext, KeyDownEvent, Keystroke, MouseButton, MouseDownEvent, Pixels, Render,
ScrollWheelEvent, Styled, Subscription, Task, WeakEntity, actions, anchored, deferred, div,
};
use persistence::TERMINAL_DB;
@@ -687,10 +687,30 @@ impl TerminalView {
///Attempt to paste the clipboard into the terminal
fn paste(&mut self, _: &Paste, _: &mut Window, cx: &mut Context<Self>) {
if let Some(clipboard_string) = cx.read_from_clipboard().and_then(|item| item.text()) {
self.terminal
.update(cx, |terminal, _cx| terminal.paste(&clipboard_string));
let Some(clipboard) = cx.read_from_clipboard() else {
return;
};
if clipboard.entries().iter().any(|entry| match entry {
ClipboardEntry::Image(image) => !image.bytes.is_empty(),
_ => false,
}) {
self.forward_ctrl_v(cx);
return;
}
if let Some(text) = clipboard.text() {
self.terminal
.update(cx, |terminal, _cx| terminal.paste(&text));
}
}
/// Emits a raw Ctrl+V so TUI agents can read the OS clipboard directly
/// and attach images using their native workflows.
fn forward_ctrl_v(&self, cx: &mut Context<Self>) {
self.terminal.update(cx, |term, _| {
term.input(vec![0x16]);
});
}
fn send_text(&mut self, text: &SendText, _: &mut Window, cx: &mut Context<Self>) {

View File

@@ -886,8 +886,12 @@ impl<T: Item> ItemHandle for Entity<T> {
// Only trigger autosave if focus has truly left the item.
// If focus is still within the item's hierarchy (e.g., moved to a context menu),
// don't trigger autosave to avoid unwanted formatting and cursor jumps.
// Also skip autosave if focus moved to a modal (e.g., command palette),
// since the user is still interacting with the workspace.
let focus_handle = item.item_focus_handle(cx);
if !focus_handle.contains_focused(window, cx) {
if !focus_handle.contains_focused(window, cx)
&& !workspace.has_active_modal(window, cx)
{
Pane::autosave_item(&item, workspace.project.clone(), window, cx)
.detach_and_log_err(cx);
}

Binary file not shown.

View File

@@ -1,7 +1,4 @@
mod app_menus;
pub mod component_preview;
pub mod edit_prediction_registry;
#[cfg(target_os = "macos")]
pub(crate) mod mac_only_instance;
mod migrate;
mod open_listener;

View File

@@ -106,6 +106,17 @@ mv Cargo.toml.backup Cargo.toml
popd
echo "Bundled ${app_path}"
# DocumentTypes.plist references CFBundleTypeIconFile "Document", so the bundle must contain Document.icns.
# We use the app icon as a placeholder document icon for now.
document_icon_source="crates/zed/resources/Document.icns"
document_icon_target="${app_path}/Contents/Resources/Document.icns"
if [[ -f "${document_icon_source}" ]]; then
mkdir -p "$(dirname "${document_icon_target}")"
cp "${document_icon_source}" "${document_icon_target}"
else
echo "cargo::warning=Missing ${document_icon_source}; macOS document icons may not appear in Finder."
fi
if [[ -n "${MACOS_CERTIFICATE:-}" && -n "${MACOS_CERTIFICATE_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_KEY:-}" && -n "${APPLE_NOTARIZATION_KEY_ID:-}" && -n "${APPLE_NOTARIZATION_ISSUER_ID:-}" ]]; then
can_code_sign=true

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'USAGE'
Usage:
script/verify-macos-document-icon /path/to/Zed.app
Verifies that the given macOS app bundle's Info.plist references a document icon
named "Document" and that the corresponding icon file exists in the bundle.
Specifically checks:
- CFBundleDocumentTypes[*].CFBundleTypeIconFile includes "Document"
- Contents/Resources/Document.icns exists
Exit codes:
0 - success
1 - verification failed
2 - invalid usage / missing prerequisites
USAGE
}
fail() {
echo "error: $*" >&2
exit 1
}
if [[ $# -ne 1 ]]; then
usage >&2
exit 2
fi
app_path="$1"
if [[ ! -d "${app_path}" ]]; then
fail "app bundle not found: ${app_path}"
fi
info_plist="${app_path}/Contents/Info.plist"
if [[ ! -f "${info_plist}" ]]; then
fail "missing Info.plist: ${info_plist}"
fi
if ! command -v plutil >/dev/null 2>&1; then
fail "plutil not found (required on macOS to read Info.plist)"
fi
# Convert to JSON for robust parsing. plutil outputs JSON to stdout in this mode.
info_json="$(plutil -convert json -o - "${info_plist}")"
# Check that CFBundleDocumentTypes exists and that at least one entry references "Document".
# We use Python for JSON parsing; macOS ships with Python 3 on many setups, but not all.
# If python3 isn't available, fall back to a simpler grep-based check.
has_document_icon_ref="false"
if command -v python3 >/dev/null 2>&1; then
has_document_icon_ref="$(python3 -c "import json,sys; d=json.load(sys.stdin); types=d.get('CFBundleDocumentTypes', []); vals=[t.get('CFBundleTypeIconFile') for t in types if isinstance(t, dict)]; print('true' if 'Document' in vals else 'false')" <<<"${info_json}")"
else
# This is a best-effort fallback. It may produce false negatives if the JSON formatting differs.
if echo "${info_json}" | grep -q '"CFBundleTypeIconFile"[[:space:]]*:[[:space:]]*"Document"'; then
has_document_icon_ref="true"
fi
fi
if [[ "${has_document_icon_ref}" != "true" ]]; then
echo "Verification failed for: ${app_path}" >&2
echo "Expected Info.plist to reference CFBundleTypeIconFile \"Document\" in CFBundleDocumentTypes." >&2
echo "Tip: This bundle may be missing DocumentTypes.plist extensions or may have different icon naming." >&2
exit 1
fi
document_icon_path="${app_path}/Contents/Resources/Document.icns"
if [[ ! -f "${document_icon_path}" ]]; then
echo "Verification failed for: ${app_path}" >&2
echo "Expected document icon to exist: ${document_icon_path}" >&2
echo "Tip: The bundle script should copy crates/zed/resources/Document.icns into Contents/Resources/Document.icns." >&2
exit 1
fi
echo "OK: ${app_path}"
echo " - Info.plist references CFBundleTypeIconFile \"Document\""
echo " - Found ${document_icon_path}"