Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ac1844e
Add structural tree mode + redesign function detail page layout
mayagore Mar 9, 2026
0e4afb3
Add 3D Thinker Council hero scene + function detail page redesign
mayagore Mar 11, 2026
cc8ea15
Add chat input bar + task prompts in tree + homepage hero
mayagore Mar 11, 2026
1896658
Integrate .glb thinker model into hero scene
mayagore Mar 11, 2026
b07d5ed
WIP: v2 hero layout + PromptBlock (pre-B&W checkpoint)
mayagore Mar 22, 2026
da4615c
Rebuild landing page: live positioning, dual CTAs, Buttondown email c…
mayagore Mar 25, 2026
7989b05
Function tree visual overhaul: infra design language + ensemble LLM n…
mayagore Mar 26, 2026
f70584a
Landing page: scroll reveal, copy feedback, browse section, compact P…
mayagore Apr 1, 2026
aff0ade
Rewrite CLAUDE.md: landing page as design reference, accurate CSS var…
mayagore Apr 1, 2026
da6435b
Warm carbon + copper design system, agent/swarm copy, function tree w…
mayagore Apr 3, 2026
1fa1f39
Copper spectrum design system, live voting landing page, dead code cl…
mayagore Apr 4, 2026
6321e8e
Initialize objectiveai-web-2: fresh Next.js 16 app with SDK dependency
mayagore Apr 5, 2026
cb5dce8
Add .gitignore file to exclude build artifacts and dependencies
mayagore Apr 5, 2026
20ba2b7
Function browser, tree visualization, swarms page, full component system
mayagore Apr 5, 2026
3a65984
Profiles page, swarm detail, simulated execution, cross-linking
mayagore Apr 5, 2026
55c45d2
Collapsible tree layout, 5-profile showcase with tier visualization
mayagore Apr 5, 2026
081a154
Replace custom tree renderer with @objectiveai/function-tree canvas p…
mayagore Apr 5, 2026
30205b0
Merge remote-tracking branch 'origin/main' into maya/web-v2
mayagore Apr 7, 2026
8dd878e
Landing page, /functions route, Shell nav fix
mayagore Apr 7, 2026
206077c
Merge remote-tracking branch 'origin/main' into maya/web-v2
mayagore Apr 8, 2026
b29d749
Side-by-side execution comparison, SDK hooks, merge main
mayagore Apr 8, 2026
4e263c2
Landing page polish, error handling for function pages
mayagore Apr 8, 2026
2450d4d
Error boundaries + friendly errors for swarms/profiles, bridging text
mayagore Apr 8, 2026
170b99d
Merge remote-tracking branch 'origin/main' into maya/web-v2
mayagore Apr 9, 2026
4e7708c
Function tree renderer overhaul: copper palette, LOD detail bars, dyn…
mayagore Apr 9, 2026
3d76784
Execution visualization, profile cards, GitHub fallback, evolving emp…
mayagore Apr 9, 2026
a3aa34c
Merge remote-tracking branch 'origin/main' into maya/web-v2
mayagore Apr 10, 2026
5afaeca
Merge remote-tracking branch 'origin/main' into maya/web-v2
mayagore Apr 10, 2026
85c2eb0
Add shipping soon landing page
mayagore Apr 10, 2026
5886e83
Merge remote-tracking branch 'origin/main' into maya/web-v2
mayagore Apr 11, 2026
6bd3e51
Consolidate web projects: delete old web, rename web-2 → web
mayagore Apr 11, 2026
cebbc1c
Revert objectiveai-js to match main
mayagore Apr 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
tmpclaude-*
/target
node_modules
.env
.env*
env
.claude/
**/.DS_Store
Expand All @@ -27,6 +27,15 @@ bin/
# ignore logs
.logs/

# Screenshots (Claude working files)
/*.png

# Playwright MCP
.playwright-mcp/

# Next.js build output (root level)
.next/

# ignore embedded binaries
**/embed/
target-*/
5 changes: 5 additions & 0 deletions objectiveai-function-tree/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./core": {
"types": "./dist/core.d.ts",
"import": "./dist/core.js",
"require": "./dist/core.cjs"
},
"./styles": "./dist/function-tree.css"
},
"files": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function createMockCanvas(width = 800, height = 600) {
restore: vi.fn(),
clip: vi.fn(),
roundRect: vi.fn(),
setLineDash: vi.fn(),
createLinearGradient: vi.fn(() => ({
addColorStop: vi.fn(),
})),
Expand All @@ -38,6 +39,8 @@ export function createMockCanvas(width = 800, height = 600) {
textAlign: "left" as CanvasTextAlign,
textBaseline: "top" as CanvasTextBaseline,
globalAlpha: 1,
lineCap: "butt" as CanvasLineCap,
lineJoin: "miter" as CanvasLineJoin,
};

const canvas = {
Expand Down
6 changes: 3 additions & 3 deletions objectiveai-function-tree/src/__tests__/lod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ describe("getLodLevel", () => {
describe("getLodParams", () => {
it("full: all visual features enabled", () => {
const p = getLodParams("full");
expect(p.curvedEdges).toBe(true);
expect(p.curvedEdges).toBe(false);
expect(p.showLabels).toBe(true);
expect(p.showStreamingText).toBe(true);
expect(p.showScoreBars).toBe(true);
expect(p.showEdges).toBe(true);
expect(p.maxLabelLength).toBe(0);
expect(p.cornerRadius).toBe(8);
expect(p.cornerRadius).toBe(4);
expect(p.dotSize).toBe(0);
});

Expand All @@ -49,7 +49,7 @@ describe("getLodParams", () => {
expect(p.showStreamingText).toBe(false);
expect(p.showScoreBars).toBe(false);
expect(p.maxLabelLength).toBe(12);
expect(p.cornerRadius).toBe(4);
expect(p.cornerRadius).toBe(3);
expect(p.showEdges).toBe(true);
});

Expand Down
44 changes: 22 additions & 22 deletions objectiveai-function-tree/src/__tests__/score-color.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@ import { describe, it, expect } from "vitest";
import { scoreColor, SCORE_COLORS } from "../types";

describe("scoreColor", () => {
it("returns green for scores >= 0.66", () => {
expect(scoreColor(0.66)).toBe(SCORE_COLORS.green);
expect(scoreColor(0.8)).toBe(SCORE_COLORS.green);
expect(scoreColor(1.0)).toBe(SCORE_COLORS.green);
it("returns high for scores >= 0.5", () => {
expect(scoreColor(0.5)).toBe(SCORE_COLORS.high);
expect(scoreColor(0.8)).toBe(SCORE_COLORS.high);
expect(scoreColor(1.0)).toBe(SCORE_COLORS.high);
});

it("returns yellow for scores in [0.33, 0.66)", () => {
expect(scoreColor(0.33)).toBe(SCORE_COLORS.yellow);
expect(scoreColor(0.5)).toBe(SCORE_COLORS.yellow);
expect(scoreColor(0.659)).toBe(SCORE_COLORS.yellow);
it("returns midHigh for scores in [0.3, 0.5)", () => {
expect(scoreColor(0.3)).toBe(SCORE_COLORS.midHigh);
expect(scoreColor(0.4)).toBe(SCORE_COLORS.midHigh);
expect(scoreColor(0.499)).toBe(SCORE_COLORS.midHigh);
});

it("returns orange for scores in [0.15, 0.33)", () => {
expect(scoreColor(0.15)).toBe(SCORE_COLORS.orange);
expect(scoreColor(0.25)).toBe(SCORE_COLORS.orange);
expect(scoreColor(0.329)).toBe(SCORE_COLORS.orange);
it("returns midLow for scores in [0.15, 0.3)", () => {
expect(scoreColor(0.15)).toBe(SCORE_COLORS.midLow);
expect(scoreColor(0.2)).toBe(SCORE_COLORS.midLow);
expect(scoreColor(0.299)).toBe(SCORE_COLORS.midLow);
});

it("returns red for scores < 0.15", () => {
expect(scoreColor(0.0)).toBe(SCORE_COLORS.red);
expect(scoreColor(0.1)).toBe(SCORE_COLORS.red);
expect(scoreColor(0.149)).toBe(SCORE_COLORS.red);
it("returns low for scores < 0.15", () => {
expect(scoreColor(0.0)).toBe(SCORE_COLORS.low);
expect(scoreColor(0.1)).toBe(SCORE_COLORS.low);
expect(scoreColor(0.149)).toBe(SCORE_COLORS.low);
});

it("boundary: exactly 0.66 is green", () => {
expect(scoreColor(0.66)).toBe(SCORE_COLORS.green);
it("boundary: exactly 0.5 is high", () => {
expect(scoreColor(0.5)).toBe(SCORE_COLORS.high);
});

it("boundary: exactly 0.33 is yellow", () => {
expect(scoreColor(0.33)).toBe(SCORE_COLORS.yellow);
it("boundary: exactly 0.3 is midHigh", () => {
expect(scoreColor(0.3)).toBe(SCORE_COLORS.midHigh);
});

it("boundary: exactly 0.15 is orange", () => {
expect(scoreColor(0.15)).toBe(SCORE_COLORS.orange);
it("boundary: exactly 0.15 is midLow", () => {
expect(scoreColor(0.15)).toBe(SCORE_COLORS.midLow);
});
});
232 changes: 232 additions & 0 deletions objectiveai-function-tree/src/__tests__/structural-tree-data.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { describe, it, expect } from "vitest";
import { buildStructuralTree } from "../core/structural-tree-data";
import type {
InputFunctionDefinition,
InputTaskDefinition,
FunctionNodeData,
VectorCompletionNodeData,
} from "../types";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function vcTask(responses: unknown[] = ["A", "B", "C"]): InputTaskDefinition {
return {
type: "vector.completion",
responses,
messages: [{ role: "user", content: "test" }],
};
}

function scalarFuncTask(owner: string, repo: string): InputTaskDefinition {
return {
type: "scalar.function",
owner,
repository: repo,
commit: "abc123",
};
}

function vectorFuncTask(owner: string, repo: string): InputTaskDefinition {
return {
type: "vector.function",
owner,
repository: repo,
commit: "def456",
};
}

function placeholderScalar(): InputTaskDefinition {
return { type: "placeholder.scalar.function" };
}

function placeholderVector(): InputTaskDefinition {
return { type: "placeholder.vector.function" };
}

function makeDef(
type: "scalar.function" | "vector.function",
tasks: InputTaskDefinition[],
): InputFunctionDefinition {
return { type, tasks };
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe("buildStructuralTree", () => {
it("returns null for null input", () => {
expect(buildStructuralTree(null)).toBeNull();
});

it("builds a tree with VC tasks only (leaf function)", () => {
const def = makeDef("scalar.function", [
vcTask(["Good", "Average", "Poor"]),
vcTask(["Yes", "No"]),
]);

const tree = buildStructuralTree(def, "my-scorer");
expect(tree).not.toBeNull();
expect(tree!.mode).toBe("structural");
expect(tree!.rootId).toBe("root");

// Root node
const root = tree!.nodes.get("root")!;
expect(root.kind).toBe("function");
expect(root.label).toBe("my-scorer");
expect(root.state).toBe("pending");
expect(root.children).toEqual(["vc-0", "vc-1"]);
const rootData = root.data as FunctionNodeData;
expect(rootData.taskCount).toBe(2);
expect(rootData.functionType).toBe("scalar");
expect(rootData.output).toBeNull();

// VC task 0
const vc0 = tree!.nodes.get("vc-0")!;
expect(vc0.kind).toBe("vector-completion");
expect(vc0.label).toBe("Task 0");
expect(vc0.state).toBe("pending");
expect(vc0.parentId).toBe("root");
const vc0Data = vc0.data as VectorCompletionNodeData;
expect(vc0Data.responseCount).toBe(3);
expect(vc0Data.scores).toBeNull();
expect(vc0Data.voteCount).toBe(0);

// VC task 1
const vc1 = tree!.nodes.get("vc-1")!;
const vc1Data = vc1.data as VectorCompletionNodeData;
expect(vc1Data.responseCount).toBe(2);
});

it("builds a tree with nested function tasks", () => {
const def = makeDef("scalar.function", [
scalarFuncTask("objective-ai", "is-spam"),
vcTask(["A", "B"]),
]);

const tree = buildStructuralTree(def, "parent-fn");
expect(tree).not.toBeNull();

const root = tree!.nodes.get("root")!;
expect(root.children).toEqual(["func-0", "vc-1"]);

// Function task node
const func0 = tree!.nodes.get("func-0")!;
expect(func0.kind).toBe("function");
expect(func0.label).toBe("is-spam");
const func0Data = func0.data as FunctionNodeData;
expect(func0Data.ownerRepo).toBe("objective-ai/is-spam");
expect(func0Data.functionType).toBe("scalar");
expect(func0Data.taskCount).toBe(0); // Not resolved
});

it("recursively expands resolved sub-functions", () => {
const parentDef = makeDef("scalar.function", [
scalarFuncTask("org", "child-fn"),
]);

const childDef = makeDef("scalar.function", [
vcTask(["X", "Y", "Z"]),
vcTask(["P", "Q"]),
]);

const resolved = new Map<string, InputFunctionDefinition>();
resolved.set("org/child-fn", childDef);

const tree = buildStructuralTree(parentDef, "parent", resolved);
expect(tree).not.toBeNull();

// Root → func-0 → vc-0-0, vc-0-1
const func0 = tree!.nodes.get("func-0")!;
expect(func0.children).toEqual(["vc-0-0", "vc-0-1"]);
const func0Data = func0.data as FunctionNodeData;
expect(func0Data.taskCount).toBe(2);

// Child VC nodes
const vc00 = tree!.nodes.get("vc-0-0")!;
expect(vc00.parentId).toBe("func-0");
const vc00Data = vc00.data as VectorCompletionNodeData;
expect(vc00Data.responseCount).toBe(3);

const vc01 = tree!.nodes.get("vc-0-1")!;
const vc01Data = vc01.data as VectorCompletionNodeData;
expect(vc01Data.responseCount).toBe(2);
});

it("handles placeholder tasks", () => {
const def = makeDef("vector.function", [
placeholderScalar(),
placeholderVector(),
]);

const tree = buildStructuralTree(def);
expect(tree).not.toBeNull();

const root = tree!.nodes.get("root")!;
expect(root.children).toEqual(["vc-0", "vc-1"]);

const ph0 = tree!.nodes.get("vc-0")!;
expect(ph0.label).toBe("Placeholder (scalar)");

const ph1 = tree!.nodes.get("vc-1")!;
expect(ph1.label).toBe("Placeholder (vector)");
});

it("handles mixed task types", () => {
const def = makeDef("scalar.function", [
vcTask(["A", "B"]),
scalarFuncTask("org", "scorer"),
placeholderScalar(),
vectorFuncTask("org", "ranker"),
]);

const tree = buildStructuralTree(def, "mixed");
expect(tree).not.toBeNull();

const root = tree!.nodes.get("root")!;
expect(root.children).toEqual(["vc-0", "func-1", "vc-2", "func-3"]);
expect(tree!.nodes.size).toBe(5); // root + 4 children
});

it("uses 'Function' as default label", () => {
const def = makeDef("vector.function", [vcTask()]);
const tree = buildStructuralTree(def);
expect(tree!.nodes.get("root")!.label).toBe("Function");
});

it("sets vector function type correctly", () => {
const def = makeDef("vector.function", [vcTask()]);
const tree = buildStructuralTree(def, "ranker");
const rootData = tree!.nodes.get("root")!.data as FunctionNodeData;
expect(rootData.functionType).toBe("vector");
});

it("node IDs match execution tree scheme", () => {
// This is critical for animated transitions between structural and execution mode
const def = makeDef("scalar.function", [
vcTask(),
vcTask(),
scalarFuncTask("org", "sub"),
]);

const tree = buildStructuralTree(def);
expect(tree).not.toBeNull();

// These IDs must match what buildTree() produces for the same structure
expect(tree!.nodes.has("root")).toBe(true);
expect(tree!.nodes.has("vc-0")).toBe(true);
expect(tree!.nodes.has("vc-1")).toBe(true);
expect(tree!.nodes.has("func-2")).toBe(true);
});

it("handles empty tasks array", () => {
const def = makeDef("scalar.function", []);
const tree = buildStructuralTree(def);
expect(tree).not.toBeNull();
expect(tree!.nodes.size).toBe(1); // Just root
const rootData = tree!.nodes.get("root")!.data as FunctionNodeData;
expect(rootData.taskCount).toBe(0);
});
});
Loading