Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ pub fn run() {
// on macOS/Windows.
linux_media::enable_media_capture(&webview);

#[cfg(target_os = "macos")]
if let Err(error) = webview
.set_background_color(Some(tauri::window::Color(0, 0, 0, 0)))
{
eprintln!("buzz-desktop: failed to make the macOS webview transparent: {error}");
}

// macOS applies the restored geometry asynchronously. Wait
// for several identical outer bounds and for React to
// commit the startup surface before revealing it.
Expand Down
4 changes: 4 additions & 0 deletions desktop/src/app/useTauriWindowDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export function useTauriWindowDrag() {
return;
}

// A native window drag replaces the browser's normal pointer gesture.
// Cancel that gesture before handing control to Tauri so moving from the
// titlebar across page copy cannot start a text selection.
event.preventDefault();
void getCurrentWindow().startDragging();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ function RelayMemberRow({
<Shield className="h-4 w-4 text-blue-500" />
) : null}
</div>
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<div
className="flex items-center gap-1.5 text-xs text-muted-foreground/70"
data-settings-subcopy
>
<span className="shrink-0 capitalize">{member.role}</span>
<span aria-hidden="true" className="shrink-0">
·
Expand Down Expand Up @@ -311,19 +314,18 @@ export function CommunityMembersSettingsCard({
description="Manage members and community access."
/>

<div className="overflow-hidden rounded-2xl border border-border/70 bg-background/70 shadow-xs">
<div className="overflow-hidden rounded-xl border border-border/70 bg-background/70 divide-y divide-border/55">
<div className="flex min-h-14 items-center px-4 py-3">
<h2 className="text-lg font-semibold tracking-tight">
Members
{members.length > 0 ? (
<span className="ml-1.5 text-sm font-normal text-muted-foreground">
{members.length}
</span>
) : null}
</h2>
</div>
<div className="space-y-3 p-4 sm:p-5">
<div className="flex items-center justify-between gap-3">
<h2 className="text-sm font-medium">
Members
{members.length > 0 ? (
<span className="ml-1.5 text-xs font-normal text-muted-foreground">
{members.length}
</span>
) : null}
</h2>
</div>

<div className="relative">
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
Expand Down
47 changes: 26 additions & 21 deletions desktop/src/features/custom-emoji/ui/CustomEmojiSettingsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,14 @@ export function CustomEmojiSettingsCard() {
if (canSubmit) void handleAdd();
}}
>
<SettingsOptionGroup>
<SettingsOptionGroup title="Add emoji">
<div className="flex flex-wrap items-center justify-between gap-3 px-4 py-3 text-sm">
<div className="min-w-0 flex-[1_1_22rem]">
<h4 className="text-sm font-medium">Upload an image</h4>
<p className="text-sm font-normal text-muted-foreground">
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
Square images work best. GIF, PNG, JPEG, and WebP files are
supported.
</p>
Expand Down Expand Up @@ -191,7 +194,10 @@ export function CustomEmojiSettingsCard() {
<div className="flex flex-wrap items-start justify-between gap-3 px-4 py-3 text-sm">
<div className="min-w-0 flex-[1_1_22rem]">
<h4 className="text-sm font-medium">Give it a name</h4>
<p className="text-sm font-normal text-muted-foreground">
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
This is what you’ll type to add this emoji to messages and
reactions.
</p>
Expand Down Expand Up @@ -221,12 +227,18 @@ export function CustomEmojiSettingsCard() {
Use only letters, numbers, hyphen, or underscore.
</p>
) : pendingUpload === null ? (
<p className="text-sm font-normal text-muted-foreground">
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
Choose an image first; Buzz will suggest a name from the
filename.
</p>
) : ownDuplicate ? (
<p className="text-sm font-normal text-muted-foreground">
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
You already have :{normalized}: — saving will replace its
image.
</p>
Expand Down Expand Up @@ -256,24 +268,21 @@ export function CustomEmojiSettingsCard() {
</SettingsOptionGroup>
</form>

<div className="space-y-3" data-testid="custom-emoji-mine">
<h2 className="text-lg font-semibold tracking-tight">
My emoji{own.length > 0 ? ` (${own.length})` : ""}
</h2>
<div data-testid="custom-emoji-mine">
{ownLoading ? (
<SettingsOptionGroup>
<SettingsOptionGroup title="My emoji">
<div className="px-4 py-3 text-sm font-normal text-muted-foreground">
Loading…
</div>
</SettingsOptionGroup>
) : own.length === 0 ? (
<SettingsOptionGroup>
<SettingsOptionGroup title="My emoji">
<div className="px-4 py-3 text-sm font-normal text-muted-foreground">
You haven&apos;t added any emoji yet. Add one above.
</div>
</SettingsOptionGroup>
) : (
<SettingsOptionGroup>
<SettingsOptionGroup title={`My emoji (${own.length})`}>
{own.map((e) => (
<div
key={e.shortcode}
Expand Down Expand Up @@ -304,15 +313,11 @@ export function CustomEmojiSettingsCard() {
</div>

{!communityLoading && othersEmoji.length > 0 ? (
<div className="space-y-3" data-testid="custom-emoji-community">
<h2 className="text-lg font-semibold tracking-tight">
Community emoji ({othersEmoji.length})
</h2>
<p className="text-sm font-normal text-muted-foreground">
Added by other members. You can use these, but only their owner
can remove them.
</p>
<SettingsOptionGroup>
<div data-testid="custom-emoji-community">
<SettingsOptionGroup
description="Added by other members. You can use these, but only their owner can remove them."
title={`Community emoji (${othersEmoji.length})`}
>
{othersEmoji.map((e) => (
<div
key={e.shortcode}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ function ObserverArchiveSection({
}: ObserverSectionProps) {
const toggleDisabled = toggling;
return (
<div className="space-y-3" data-testid="local-archive-observer-section">
<h2 className="text-lg font-semibold tracking-tight">
Agent observer feed
</h2>
<SettingsOptionGroup>
<div data-testid="local-archive-observer-section">
<SettingsOptionGroup title="Agent observer feed">
<SettingsOptionRow>
<div className="min-w-0 flex-1">
<label
Expand All @@ -90,7 +87,10 @@ function ObserverArchiveSection({
>
Archive my agents' observer frames
</label>
<p className="text-sm font-normal text-muted-foreground">
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
{`Saves kind ${KIND_AGENT_OBSERVER_FRAME} observer frames addressed to your pubkey. These are ephemeral — not stored by the relay — so local archiving is the only way to retain them.`}
</p>
</div>
Expand Down Expand Up @@ -121,11 +121,8 @@ function AgentMetricArchiveSection({
onToggle,
}: AgentMetricSectionProps) {
return (
<div className="space-y-3" data-testid="local-archive-agent-metric-section">
<h2 className="text-lg font-semibold tracking-tight">
Agent turn metrics
</h2>
<SettingsOptionGroup>
<div data-testid="local-archive-agent-metric-section">
<SettingsOptionGroup title="Agent turn metrics">
<SettingsOptionRow>
<div className="min-w-0 flex-1">
<label
Expand All @@ -134,7 +131,10 @@ function AgentMetricArchiveSection({
>
Archive my agents' turn metrics
</label>
<p className="text-sm font-normal text-muted-foreground">
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
Saves kind {KIND_AGENT_TURN_METRIC} turn-metric events addressed
to your pubkey. Stored as plaintext in your local archive so
token-usage calculators can read them directly.
Expand Down Expand Up @@ -240,7 +240,10 @@ function CustomKindsInput({ value, onChange }: CustomKindsInputProps) {
type="text"
value={value}
/>
<p className="mt-1 text-xs text-muted-foreground">
<p
className="mt-1 text-xs text-muted-foreground/70"
data-settings-subcopy
>
Space- or comma-separated non-negative integers. Kinds already in the
checklist above are ignored.
</p>
Expand All @@ -266,11 +269,17 @@ function CustomKindsInput({ value, onChange }: CustomKindsInputProps) {

type AddFormProps = {
channels: Array<{ id: string; name: string }>;
title?: React.ReactNode;
onSaved: () => void;
onCancel: () => void;
};

function AddSubscriptionForm({ channels, onSaved, onCancel }: AddFormProps) {
function AddSubscriptionForm({
channels,
title,
onSaved,
onCancel,
}: AddFormProps) {
const [selectedChannelId, setSelectedChannelId] = React.useState("");
const [checkedKinds, setCheckedKinds] = React.useState<Set<number>>(
new Set(),
Expand Down Expand Up @@ -316,7 +325,7 @@ function AddSubscriptionForm({ channels, onSaved, onCancel }: AddFormProps) {
};

return (
<SettingsOptionGroup>
<SettingsOptionGroup title={title}>
<div className="space-y-5 px-4 py-4">
{/* Channel picker */}
<div>
Expand Down Expand Up @@ -535,25 +544,23 @@ export function LocalArchiveSettingsCard() {
/>

{/* Channel subscriptions */}
<div className="space-y-3" data-testid="local-archive-subscriptions">
<h2 className="text-lg font-semibold tracking-tight">
Channel subscriptions
{channelSubs.length > 0 ? ` (${channelSubs.length})` : ""}
</h2>
<div data-testid="local-archive-subscriptions">
{isLoading ? (
<SettingsOptionGroup>
<SettingsOptionGroup title="Channel subscriptions">
<div className="px-4 py-3 text-sm font-normal text-muted-foreground">
Loading…
</div>
</SettingsOptionGroup>
) : channelSubs.length === 0 ? (
<SettingsOptionGroup>
<SettingsOptionGroup title="Channel subscriptions">
<div className="px-4 py-3 text-sm font-normal text-muted-foreground">
No channel subscriptions yet. Add one below.
</div>
</SettingsOptionGroup>
) : (
<SettingsOptionGroup>
<SettingsOptionGroup
title={`Channel subscriptions (${channelSubs.length})`}
>
{channelSubs.map((sub) => {
const key = `${sub.scopeType}:${sub.scopeValue}`;
return (
Expand All @@ -567,7 +574,10 @@ export function LocalArchiveSettingsCard() {
<p className="truncate text-sm font-medium">
{scopeLabel(sub, channelNameById)}
</p>
<p className="text-xs text-muted-foreground">
<p
className="text-xs text-muted-foreground/70"
data-settings-subcopy
>
{sub.scopeType} · kinds: {kindSummary(sub.kinds)}
</p>
</div>
Expand All @@ -590,10 +600,7 @@ export function LocalArchiveSettingsCard() {
</div>

{/* Add channel subscription */}
<div className="space-y-3" data-testid="local-archive-add">
<h2 className="text-lg font-semibold tracking-tight">
Add channel subscription
</h2>
<div data-testid="local-archive-add">
{isAddingOpen ? (
<AddSubscriptionForm
channels={joinedChannels}
Expand All @@ -602,13 +609,17 @@ export function LocalArchiveSettingsCard() {
setIsAddingOpen(false);
void reload();
}}
title="Add channel subscription"
/>
) : (
<SettingsOptionGroup>
<SettingsOptionGroup title="Add channel subscription">
<SettingsOptionRow>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">Subscribe to a channel</p>
<p className="text-xs text-muted-foreground">
<p
className="text-xs text-muted-foreground/70"
data-settings-subcopy
>
Choose a channel and select which event types to archive.
</p>
</div>
Expand Down
Loading
Loading