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
1 change: 1 addition & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ Mobile app tests live in `mobile/src/**/*.test.ts` and use Bun's built-in test r
- If a tool/agent provides an emoji string (e.g., todo-derived status or `displayStatus`), render via `EmojiIcon` (`src/browser/components/icons/EmojiIcon.tsx`) instead of rendering the emoji.
- If a new emoji appears in tool output, extend `EmojiIcon` to map it to an SVG icon.
- Colors defined in `src/browser/styles/globals.css` (`:root @theme` block). Reference via CSS variables (e.g., `var(--color-plan-mode)`), never hardcode hex values.
- Tooltips must use the shared `Tooltip`/`TooltipIfPresent` components, not native `title` attributes. Native titles create duplicate OS tooltips and cannot be z-indexed; shared tooltips portal above clipped containers.
- For incrementing numeric UI (costs, timers, token counts, percentages), use semantic numeric typography utilities (`counter-nums` / `counter-nums-mono`) to prevent width jitter.
- Choose `counter-nums-mono` only when monospace is an intentional visual style (e.g., terminal/telemetry), not merely as a workaround.
- Use `min-w-[Nch]` only when reserving layout width is intentional and separate from tabular numeral stability.
Expand Down
28 changes: 19 additions & 9 deletions src/browser/features/Memory/MemoryBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "lucide-react";

import { ConfirmationModal } from "@/browser/components/ConfirmationModal/ConfirmationModal";
import { TooltipIfPresent } from "@/browser/components/Tooltip/Tooltip";
import { useAPI } from "@/browser/contexts/API";
import { RowActionButton } from "@/browser/features/RightSidebar/GoalBoardSections";
import { isAbortError } from "@/browser/utils/isAbortError";
Expand Down Expand Up @@ -555,16 +556,25 @@ function ConsolidationFooter(props: {

return (
<div className="border-border-light flex items-center justify-between gap-2 border-t px-3 py-2 text-xs">
<div className="text-muted min-w-0 flex-1 space-y-0.5" title={summaryTitle}>
<div className="counter-nums truncate">
Workspace: {formatConsolidationRecord(status?.workspaceRecord ?? null)}
</div>
<div className="counter-nums truncate">Project: {projectLabel}</div>
<div className="counter-nums truncate">
Global: {formatConsolidationRecord(status?.globalRecord ?? null)}
<TooltipIfPresent
tooltip={
summaryTitle === "" ? null : <div className="whitespace-pre-line">{summaryTitle}</div>
}
side="top"
align="start"
>
{/* Use the shared, portaled tooltip only: a native title duplicates the UI tooltip and ignores z-index. */}
<div className="text-muted min-w-0 flex-1 space-y-0.5">
<div className="counter-nums truncate">
Workspace: {formatConsolidationRecord(status?.workspaceRecord ?? null)}
</div>
<div className="counter-nums truncate">Project: {projectLabel}</div>
<div className="counter-nums truncate">
Global: {formatConsolidationRecord(status?.globalRecord ?? null)}
</div>
{runError !== null && <div className="text-error truncate">{runError}</div>}
</div>
{runError !== null && <div className="text-error truncate">{runError}</div>}
</div>
</TooltipIfPresent>
<button
type="button"
className="border-border-light text-foreground hover:bg-hover shrink-0 rounded border px-2 py-0.5 disabled:opacity-50"
Expand Down
22 changes: 22 additions & 0 deletions src/browser/features/RightSidebar/Memory/MemoryTab.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,28 @@ describe("MemoryTab", () => {
expect(getByText(/^Project:/).textContent).not.toContain("manual");
});

test("consolidation summary does not leave a native title tooltip", async () => {
fake = createFakeMemoryApi([], {
consolidationStatus: {
workspaceRecord: {
...DEFAULT_CONSOLIDATION_RECORD,
summary: "workspace summary",
},
projectRecord: null,
globalRecord: null,
projectAvailable: true,
},
});
const { findByText } = render(<MemoryTab workspaceId="ws-1" />);

const workspaceLine = await findByText(/^Workspace: .*manual/);
const statusBlock = workspaceLine.parentElement;
expect(statusBlock).not.toBeNull();
// Native title tooltips are rendered by the OS and appear in addition to our
// portaled app tooltip, so this block must not expose one.
expect(statusBlock!.getAttribute("title")).toBeNull();
});

test("shows usage stats for used files and omits them for never-used files", async () => {
fake = createFakeMemoryApi([
fileInfo({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export const BUILTIN_SKILL_FILES: Record<string, Record<string, string>> = {
"- If a tool/agent provides an emoji string (e.g., todo-derived status or `displayStatus`), render via `EmojiIcon` (`src/browser/components/icons/EmojiIcon.tsx`) instead of rendering the emoji.",
"- If a new emoji appears in tool output, extend `EmojiIcon` to map it to an SVG icon.",
"- Colors defined in `src/browser/styles/globals.css` (`:root @theme` block). Reference via CSS variables (e.g., `var(--color-plan-mode)`), never hardcode hex values.",
"- Tooltips must use the shared `Tooltip`/`TooltipIfPresent` components, not native `title` attributes. Native titles create duplicate OS tooltips and cannot be z-indexed; shared tooltips portal above clipped containers.",
"- For incrementing numeric UI (costs, timers, token counts, percentages), use semantic numeric typography utilities (`counter-nums` / `counter-nums-mono`) to prevent width jitter.",
"- Choose `counter-nums-mono` only when monospace is an intentional visual style (e.g., terminal/telemetry), not merely as a workaround.",
"- Use `min-w-[Nch]` only when reserving layout width is intentional and separate from tabular numeral stability.",
Expand Down
Loading