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
9 changes: 3 additions & 6 deletions apps/desktop/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/desktop/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "hugeicons",
"iconLibrary": "phosphor",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
Expand Down
3 changes: 1 addition & 2 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@
"@blocknote/react": "^0.25.0",
"@dnd-kit/react": "0.5.0",
"@excalidraw/excalidraw": "0.18.1",
"@hugeicons/core-free-icons": "^4.3.0",
"@hugeicons/react": "^1.1.10",
"@noble/hashes": "^2.2.0",
"@phosphor-icons/react": "^2.1.10",
"@petx/react": "0.2.0",
"@shikijs/monaco": "^4.4.1",
"@xterm/addon-fit": "^0.10.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5480,7 +5480,7 @@ export default function App() {
id: slug(skillDraft.name),
name: skillDraft.name.trim(),
description: "",
icon: "✦",
icon: null,
payload: { kind: "fragment", text: skillDraft.text },
});
setSkillDraft(null);
Expand Down
30 changes: 15 additions & 15 deletions apps/desktop/src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1594,11 +1594,11 @@ export function providerDisplayName(providerId: string): string {
}

const FALLBACK_SKILLS: SkillInfo[] = [
{ id: "reviewer", name: "Code Reviewer", description: "Meticulous reviewer", icon: "🔍", kind: "fragment", source: null },
{ id: "test-writer", name: "Test Writer", description: "Thorough tests", icon: "🧪", kind: "fragment", source: null },
{ id: "security-audit", name: "Security Audit", description: "Find vulns", icon: "🛡️", kind: "fragment", source: null },
{ id: "reviewer", name: "Code Reviewer", description: "Meticulous reviewer", icon: null, kind: "fragment", source: null },
{ id: "test-writer", name: "Test Writer", description: "Thorough tests", icon: null, kind: "fragment", source: null },
{ id: "security-audit", name: "Security Audit", description: "Find vulns", icon: null, kind: "fragment", source: null },
{
id: "commit-macro", name: "Commit Message", description: "Commit macro", icon: "📝", kind: "macro", source: null,
id: "commit-macro", name: "Commit Message", description: "Commit macro", icon: null, kind: "macro", source: null,
macro_template: "Write a {{style}} commit message for changes to {{scope}}.",
macro_slots: [
{ id: "style", label: "Style", kind: "select", options: ["conventional", "descriptive"], required: true },
Expand Down Expand Up @@ -2167,7 +2167,7 @@ export async function readVisualization(path: string): Promise<string> {
<div><p class="text-small text-muted">Renderer build</p><p class="viz-stat-value">Passed</p></div>
</div>
<div class="viz-row" style="margin-top:14px">
<button class="btn btn-primary" onclick="window.openai.sendFollowUpMessage({prompt:'Show the failed checks only',title:'Filter verification results'})"><svg class="viz-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M8.85746 12.5061C6.36901 10.6456 4.59564 8.59915 3.62734 7.44867C3.3276 7.09253 3.22938 6.8319 3.17033 6.3728C2.96811 4.8008 2.86701 4.0148 3.32795 3.5074C3.7889 3 4.60404 3 6.23433 3H17.7657C19.396 3 20.2111 3 20.672 3.5074C21.133 4.0148 21.0319 4.8008 20.8297 6.37281C20.7706 6.83191 20.6724 7.09254 20.3726 7.44867C19.403 8.60062 17.6261 10.6507 15.1326 12.5135C14.907 12.6821 14.7583 12.9567 14.7307 13.2614C14.4837 15.992 14.2559 17.4876 14.1141 18.2442C13.8853 19.4657 12.1532 20.2006 11.226 20.8563C10.6741 21.2466 10.0043 20.782 9.93278 20.1778C9.79643 19.0261 9.53961 16.6864 9.25927 13.2614C9.23409 12.9539 9.08486 12.6761 8.85746 12.5061Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.7"/></svg>Filter results</button>
<button class="btn btn-primary" onclick="window.openai.sendFollowUpMessage({prompt:'Show the failed checks only',title:'Filter verification results'})">Filter results</button>
<span class="text-small text-muted">Updated just now</span>
</div>
</section>`;
Expand Down Expand Up @@ -2967,9 +2967,9 @@ export interface MarketItem {
}

const FALLBACK_MARKET: MarketItem[] = [
{ id: "architect", name: "System Architect", description: "Design before coding.", author: "codetwo", tags: ["design"], icon: "🏛️", kind: "fragment", installed: false },
{ id: "test-suite", name: "Test Suite Author", description: "Thorough deterministic tests.", author: "codetwo", tags: ["testing"], icon: "🧪", kind: "fragment", installed: false },
{ id: "browser-tool", name: "Browser Tool (MCP)", description: "Give the agent a browser.", author: "codetwo", tags: ["mcp", "browser"], icon: "🌐", kind: "mcp", installed: false },
{ id: "architect", name: "System Architect", description: "Design before coding.", author: "codetwo", tags: ["design"], icon: null, kind: "fragment", installed: false },
{ id: "test-suite", name: "Test Suite Author", description: "Thorough deterministic tests.", author: "codetwo", tags: ["testing"], icon: null, kind: "fragment", installed: false },
{ id: "browser-tool", name: "Browser Tool (MCP)", description: "Give the agent a browser.", author: "codetwo", tags: ["mcp", "browser"], icon: null, kind: "mcp", installed: false },
];

export async function marketCatalog(): Promise<MarketItem[]> {
Expand Down Expand Up @@ -4380,18 +4380,18 @@ export async function onAutoSceneChanged(
/** Browser-preview stand-ins (same convention as FALLBACK_SKILLS): the five builtin scenes. */
const FALLBACK_SCENES: SceneInfo[] = (
[
["research", "Research", "调研", "🔎", "read_only", "Survey the problem space read-only and produce a cited research report."],
["develop", "Develop", "开发", "🛠️", "auto_edit", "Plan-first implementation in an isolated worktree."],
["test", "Test", "测试", "🧪", "auto_edit", "Exercise the change against its acceptance criteria."],
["fix", "Fix", "修复", "🩹", "auto_edit", "Resolve reported failures one by one."],
["acceptance", "Acceptance", "验收", "✅", "read_only", "Read-only verification against the original acceptance criteria."],
["research", "Research", "调研", "read_only", "Survey the problem space read-only and produce a cited research report."],
["develop", "Develop", "开发", "auto_edit", "Plan-first implementation in an isolated worktree."],
["test", "Test", "测试", "auto_edit", "Exercise the change against its acceptance criteria."],
["fix", "Fix", "修复", "auto_edit", "Resolve reported failures one by one."],
["acceptance", "Acceptance", "验收", "read_only", "Read-only verification against the original acceptance criteria."],
] as const
).map(([name, title, zh, icon, mode, description]) => ({
).map(([name, title, zh, mode, description]) => ({
reference: `builtin:${name}`,
name,
title,
description,
icon,
icon: null,
source: "builtin" as const,
keywords: [],
has_brief: true,
Expand Down
94 changes: 94 additions & 0 deletions apps/desktop/src/components/ui/icons.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { afterEach, describe, expect, test } from "bun:test";
import { createRef } from "react";
import { activateDom, dom, mount } from "../../../tests/domTestHarness";
import * as iconExports from "./icons";
import type { AppIcon } from "./icons";

activateDom();

const icons = Object.entries(iconExports) as Array<[string, AppIcon]>;

afterEach(() => {
dom.document.body.replaceChildren();
});

describe("rounded icon adapter", () => {
test("renders every public icon as a configurable Phosphor SVG", () => {
const view = mount(
<div>
{icons.map(([name, Icon]) => (
<Icon
key={name}
aria-label={name}
className="adapter-icon"
data-icon-name={name}
/>
))}
</div>,
);

const rendered = [...view.container.querySelectorAll("svg")];
expect(icons.length).toBeGreaterThan(100);
expect(rendered).toHaveLength(icons.length);

for (const svg of rendered) {
expect(svg.getAttribute("viewBox")).toBe("0 0 256 256");
expect(svg.getAttribute("fill")).toBe("currentColor");
expect(svg.getAttribute("width")).toBe("1em");
expect(svg.getAttribute("height")).toBe("1em");
expect(svg.classList.contains("adapter-icon")).toBe(true);
expect(svg.getAttribute("aria-label")).toBe(svg.getAttribute("data-icon-name"));
}

view.unmount();
});

test("defaults to regular weight, forwards refs, and preserves directional defaults", () => {
const ref = createRef<SVGSVGElement>();
const view = mount(
<div>
<iconExports.Search ref={ref} data-search="default" alt="Search" />
<iconExports.Search data-search="regular" weight="regular" />
<iconExports.ChevronDown data-caret="fallback">▼</iconExports.ChevronDown>
<iconExports.PanelRight data-panel="right" />
</div>,
);

const defaultSearch = view.container.querySelector<SVGSVGElement>('[data-search="default"]');
const regularSearch = view.container.querySelector<SVGSVGElement>('[data-search="regular"]');
const fallbackCaret = view.container.querySelector<SVGSVGElement>('[data-caret="fallback"]');
const rightPanel = view.container.querySelector<SVGSVGElement>('[data-panel="right"]');

expect(ref.current).toBe(defaultSearch);
expect(defaultSearch?.querySelector("path")?.getAttribute("d"))
.toBe(regularSearch?.querySelector("path")?.getAttribute("d"));
expect(defaultSearch?.querySelector("title")?.textContent).toBe("Search");
expect(fallbackCaret?.textContent).toBe("");
expect(rightPanel?.getAttribute("transform")).toBe("scale(-1, 1)");

view.unmount();
});

test("keeps action-oriented exports visually distinct from their base concepts", () => {
const view = mount(
<div>
<iconExports.File data-icon="file" />
<iconExports.FileClock data-icon="file-clock" />
<iconExports.FolderOpen data-icon="folder-open" />
<iconExports.FolderDown data-icon="folder-down" />
<iconExports.Package data-icon="package" />
<iconExports.PackageCheck data-icon="package-check" />
<iconExports.PackagePlus data-icon="package-plus" />
</div>,
);
const markup = (name: string) =>
view.container.querySelector<SVGSVGElement>(`[data-icon="${name}"]`)?.innerHTML;

expect(markup("file-clock")).not.toBe(markup("file"));
expect(markup("folder-down")).not.toBe(markup("folder-open"));
expect(markup("package-check")).not.toBe(markup("package"));
expect(markup("package-plus")).not.toBe(markup("package"));

view.unmount();
});
});
Loading