Synced from monorepo

Synced from monorepo

Changes:
- Workspace server: report `/ready` as failed with dwell on hub connect failure
- Refresh OIDC token for the Grok agent in the shell
- ACP terminal output recorder
- Cross-platform provider auth commands in the shell
- Default `/resume` to Grok sessions with a hint for hidden external sessions
- Resume sessions by title with `--resume`
- Limit app-builder archive size
- Data-driven tag labels for slash commands
- Doctor fixes for tmux
- Custom provider gateways and subprocess environment policy in the shell
- `/tutorial` — opt-in onboarding tour of Grok Build
- Soft and required CLI version checks in the shell
- Privacy banner env overrides survive live settings updates
- Add remote flag to override the image-edit model
- Return profile fields from auth info even when the access token is expired
- Add edit control on queued prompt rows
- Keep fail-closed policy when clearing orphans with no team
- Setting to disable the Ctrl+Space/F8 voice shortcut
- Pass `--raw` to pw-record so Linux dictation works on older PipeWire
- Validate git URLs when adding marketplace entries
- Stop shipping stale tool-doc parameter and tool names
- Re-point dashboard attach after `/fork` only when the parent was attached
- Surface Grok Computer media-generation results as file-path chunks
- Clear web background-task tray on kill and keep the task description
- Show privacy upsell banner in agent view until acted on
- Add tools-server client callback surface
- Protect persistent global hook sources

Source-Revision: 95d84f443eddcbed6cbfd6eed22e2eafe6b3939d
This commit is contained in:
grokkybara[bot] 2026-07-23 17:12:33 +00:00
commit 69f0ba880a
286 changed files with 22939 additions and 9624 deletions

View file

@ -0,0 +1,169 @@
//! Coding-data sharing upsell banner (Figma "Data Sharing Upsell",
//! node 8698:3690). Shared by the welcome tip slot and the agent-view
//! banner slot; visibility is gated by `AppView::privacy_banner_should_show`.
use crate::theme::Theme;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Paragraph, Widget};
/// Legal line copy — used for both render spans and mouse hit width.
const PRIVACY_BANNER_LEGAL: &str = "Learn more and read Terms and Privacy Policy.";
/// Click target for the legal line links.
pub(crate) const PRIVACY_BANNER_LEGAL_URL: &str = "https://x.ai/legal";
/// Hit rects returned by [`render`] for mouse handling.
pub(crate) struct PrivacyBannerRects {
pub accept: Rect,
pub customize: Rect,
pub legal: Rect,
}
/// Render the banner: copy left, `[Customize in settings]` / `[Accept]`
/// right, legal links on the second row. Needs `area.height >= 2`.
/// Hover styling mirrors the plugin CTA buttons.
pub(crate) fn render(
area: Rect,
buf: &mut Buffer,
theme: &Theme,
mouse_pos: Option<(u16, u16)>,
) -> PrivacyBannerRects {
let customize_label = "[Customize in settings]";
let accept_label = "[Accept]";
let right_w = (customize_label.len() + 1 + accept_label.len()) as u16;
// Buttons render whole or not at all: a clipped/overflowing [Accept]
// must never leave a click target in the blank margin (a stray click
// there would silently opt the user in).
let buttons_fit = area.width > right_w;
let left_w = if buttons_fit {
area.width - right_w - 1
} else {
area.width
};
let left = Rect {
x: area.x,
y: area.y,
width: left_w,
height: area.height.min(2),
};
let right = Rect {
x: area.x + left_w + 1,
y: area.y,
width: right_w,
height: 1,
};
let hovered = |r: Rect| {
mouse_pos.is_some_and(|(mx, my)| r.contains(ratatui::layout::Position::new(mx, my)))
};
let legal_w = if left.width as usize >= PRIVACY_BANNER_LEGAL.len() {
PRIVACY_BANNER_LEGAL.len()
} else {
"Learn more".len().min(left.width as usize)
};
// The legal line only exists when the slot really has a second row —
// otherwise its rect would make the blank row below clickable.
let legal_rect = if area.height >= 2 {
Rect {
x: left.x,
y: left.y.saturating_add(1),
width: legal_w as u16,
height: 1,
}
} else {
Rect::default()
};
// Figma node 8698:3806: title fg/primary, description fg/secondary,
// legal line fg/tertiary with underlined links in the same color.
// The whole legal line is one click target, so its links brighten together.
let link_fg = if hovered(legal_rect) {
theme.gray_bright
} else {
theme.gray
};
let link = Style::default()
.fg(link_fg)
.add_modifier(Modifier::UNDERLINED);
let gray = Style::default().fg(theme.gray);
let title = Span::styled("Help improve Grok", Style::default().fg(theme.text_primary));
let desc = "Allow your sessions to improve SpaceXAI's models.";
// Drop trailing spans whole rather than clipping mid-word when narrow.
let line1 = if left.width as usize >= "Help improve Grok ".len() + desc.len() {
Line::from(vec![
title,
Span::raw(" "),
Span::styled(desc, Style::default().fg(theme.gray_bright)),
])
} else {
Line::from(title)
};
// Span pieces must reassemble to PRIVACY_BANNER_LEGAL.
let line2 = if left.width as usize >= PRIVACY_BANNER_LEGAL.len() {
Line::from(vec![
Span::styled("Learn more", link),
Span::styled(" and read ", gray),
Span::styled("Terms", link),
Span::styled(" and ", gray),
Span::styled("Privacy Policy", link),
Span::styled(".", gray),
])
} else {
Line::from(Span::styled("Learn more", link))
};
Paragraph::new(vec![line1, line2]).render(left, buf);
if !buttons_fit {
return PrivacyBannerRects {
accept: Rect::default(),
customize: Rect::default(),
legal: legal_rect,
};
}
let customize_rect = Rect {
x: right.x,
y: right.y,
width: customize_label.len() as u16,
height: 1,
};
let accept_rect = Rect {
x: right.x + customize_label.len() as u16 + 1,
y: right.y,
width: accept_label.len() as u16,
height: 1,
};
let customize_style = if hovered(customize_rect) {
Style::default().fg(theme.text_primary).bg(theme.bg_hover)
} else {
Style::default().fg(theme.gray_bright)
};
let accept_style = if hovered(accept_rect) {
Style::default().fg(theme.link_fg).bg(theme.bg_hover)
} else {
Style::default().fg(theme.text_primary)
};
buf.set_stringn(
customize_rect.x,
customize_rect.y,
customize_label,
customize_rect.width as usize,
customize_style,
);
buf.set_stringn(
accept_rect.x,
accept_rect.y,
accept_label,
accept_rect.width as usize,
accept_style,
);
PrivacyBannerRects {
accept: accept_rect,
customize: customize_rect,
legal: legal_rect,
}
}