764 lines
28 KiB
TypeScript
764 lines
28 KiB
TypeScript
/**
|
|
* Canvas Tool Registry — shared by server (Gemini function declarations) and client (shape spawning).
|
|
* Pure TypeScript, no DOM or server dependencies.
|
|
*/
|
|
|
|
export interface CanvasToolDefinition {
|
|
declaration: {
|
|
name: string;
|
|
description: string;
|
|
parameters: {
|
|
type: "object";
|
|
properties: Record<string, { type: string; description: string; enum?: string[] }>;
|
|
required: string[];
|
|
};
|
|
};
|
|
tagName: string;
|
|
/** Module that owns this tool (omit for core/always-available tools) */
|
|
moduleId?: string;
|
|
buildProps: (args: Record<string, any>) => Record<string, any>;
|
|
actionLabel: (args: Record<string, any>) => string;
|
|
}
|
|
|
|
const registry: CanvasToolDefinition[] = [
|
|
{
|
|
declaration: {
|
|
name: "create_map",
|
|
description: "Create an interactive map centered on a location. Use when the user wants to see a place, get directions, or explore a geographic area.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
latitude: { type: "number", description: "Latitude of the center point" },
|
|
longitude: { type: "number", description: "Longitude of the center point" },
|
|
zoom: { type: "number", description: "Zoom level (1-18, default 12)" },
|
|
location_name: { type: "string", description: "Human-readable name of the location" },
|
|
},
|
|
required: ["latitude", "longitude", "location_name"],
|
|
},
|
|
},
|
|
tagName: "folk-map",
|
|
moduleId: "rmaps",
|
|
buildProps: (args) => ({
|
|
center: [args.longitude, args.latitude],
|
|
zoom: args.zoom || 12,
|
|
}),
|
|
actionLabel: (args) => `Created map: ${args.location_name}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_note",
|
|
description: "Create a markdown note on the canvas. Use for text content, lists, summaries, instructions, or any written information.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
content: { type: "string", description: "Markdown content for the note" },
|
|
title: { type: "string", description: "Optional title for the note" },
|
|
},
|
|
required: ["content"],
|
|
},
|
|
},
|
|
tagName: "folk-markdown",
|
|
buildProps: (args) => ({
|
|
value: args.title ? `# ${args.title}\n\n${args.content}` : args.content,
|
|
}),
|
|
actionLabel: (args) => `Created note${args.title ? `: ${args.title}` : ""}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_embed",
|
|
description: "Embed a webpage or web app on the canvas. Use for websites, search results, booking sites, videos, or any URL the user wants to view inline.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
url: { type: "string", description: "The URL to embed" },
|
|
title: { type: "string", description: "Descriptive title for the embed" },
|
|
},
|
|
required: ["url"],
|
|
},
|
|
},
|
|
tagName: "folk-embed",
|
|
buildProps: (args) => ({
|
|
url: args.url,
|
|
}),
|
|
actionLabel: (args) => `Embedded: ${args.title || args.url}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_image",
|
|
description: "Display an image on the canvas from a URL. Use when showing an existing image, photo, diagram, or any direct image link.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
src: { type: "string", description: "Image URL" },
|
|
alt: { type: "string", description: "Alt text describing the image" },
|
|
},
|
|
required: ["src"],
|
|
},
|
|
},
|
|
tagName: "folk-image",
|
|
buildProps: (args) => ({
|
|
src: args.src,
|
|
alt: args.alt || "",
|
|
}),
|
|
actionLabel: (args) => `Created image${args.alt ? `: ${args.alt}` : ""}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_bookmark",
|
|
description: "Create a bookmark card for a URL. Use when the user wants to save or reference a link without embedding the full page.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
url: { type: "string", description: "The URL to bookmark" },
|
|
},
|
|
required: ["url"],
|
|
},
|
|
},
|
|
tagName: "folk-bookmark",
|
|
buildProps: (args) => ({
|
|
url: args.url,
|
|
}),
|
|
actionLabel: (args) => `Bookmarked: ${args.url}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_image_gen",
|
|
description: "Generate an AI image from a text prompt. Use when the user wants to create, generate, or imagine a new image that doesn't exist yet.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
prompt: { type: "string", description: "Text prompt describing the image to generate" },
|
|
style: {
|
|
type: "string",
|
|
description: "Visual style for the generated image",
|
|
enum: ["photorealistic", "illustration", "painting", "sketch", "punk-zine", "collage", "vintage", "minimalist"],
|
|
},
|
|
},
|
|
required: ["prompt"],
|
|
},
|
|
},
|
|
tagName: "folk-image-gen",
|
|
buildProps: (args) => ({
|
|
prompt: args.prompt,
|
|
style: args.style || "photorealistic",
|
|
}),
|
|
actionLabel: (args) => `Generating image: ${args.prompt.slice(0, 50)}${args.prompt.length > 50 ? "..." : ""}`,
|
|
},
|
|
// ── Trip Planning Tools ──
|
|
{
|
|
declaration: {
|
|
name: "create_destination",
|
|
description: "Create a destination card for a trip location. Use when the user mentions a city, place, or stop on their trip.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
destName: { type: "string", description: "Name of the destination (city or place)" },
|
|
country: { type: "string", description: "Country name" },
|
|
lat: { type: "number", description: "Latitude coordinate" },
|
|
lng: { type: "number", description: "Longitude coordinate" },
|
|
arrivalDate: { type: "string", description: "Arrival date in YYYY-MM-DD format" },
|
|
departureDate: { type: "string", description: "Departure date in YYYY-MM-DD format" },
|
|
notes: { type: "string", description: "Additional notes about this destination" },
|
|
},
|
|
required: ["destName"],
|
|
},
|
|
},
|
|
tagName: "folk-destination",
|
|
moduleId: "rtrips",
|
|
buildProps: (args) => ({
|
|
destName: args.destName,
|
|
...(args.country ? { country: args.country } : {}),
|
|
...(args.lat != null ? { lat: args.lat } : {}),
|
|
...(args.lng != null ? { lng: args.lng } : {}),
|
|
...(args.arrivalDate ? { arrivalDate: args.arrivalDate } : {}),
|
|
...(args.departureDate ? { departureDate: args.departureDate } : {}),
|
|
...(args.notes ? { notes: args.notes } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created destination: ${args.destName}${args.country ? `, ${args.country}` : ""}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_itinerary",
|
|
description: "Create an itinerary card with a list of activities/events organized by date. Use when planning a schedule or day-by-day plan.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
tripTitle: { type: "string", description: "Title for the itinerary" },
|
|
itemsJson: { type: "string", description: 'JSON array of items. Each: {"id":"<uuid>","title":"...","date":"YYYY-MM-DD","startTime":"HH:MM","category":"ACTIVITY|TRANSPORT|MEAL|FREE_TIME|FLIGHT"}' },
|
|
},
|
|
required: ["tripTitle", "itemsJson"],
|
|
},
|
|
},
|
|
tagName: "folk-itinerary",
|
|
moduleId: "rtrips",
|
|
buildProps: (args) => {
|
|
let items: any[] = [];
|
|
try { items = JSON.parse(args.itemsJson); } catch { items = []; }
|
|
return { tripTitle: args.tripTitle, items };
|
|
},
|
|
actionLabel: (args) => `Created itinerary: ${args.tripTitle}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_booking",
|
|
description: "Create a booking card for a flight, hotel, transport, activity, or restaurant reservation.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
bookingType: {
|
|
type: "string",
|
|
description: "Type of booking",
|
|
enum: ["FLIGHT", "HOTEL", "CAR_RENTAL", "TRAIN", "BUS", "FERRY", "ACTIVITY", "RESTAURANT", "OTHER"],
|
|
},
|
|
provider: { type: "string", description: "Provider/company name (e.g. airline, hotel name)" },
|
|
cost: { type: "number", description: "Cost amount" },
|
|
currency: { type: "string", description: "ISO currency code (e.g. USD, EUR)" },
|
|
startDate: { type: "string", description: "Start/check-in date in YYYY-MM-DD format" },
|
|
endDate: { type: "string", description: "End/check-out date in YYYY-MM-DD format" },
|
|
bookingStatus: { type: "string", description: "Booking status", enum: ["PENDING", "CONFIRMED", "CANCELLED"] },
|
|
details: { type: "string", description: "Additional booking details or notes" },
|
|
},
|
|
required: ["bookingType", "provider"],
|
|
},
|
|
},
|
|
tagName: "folk-booking",
|
|
moduleId: "rtrips",
|
|
buildProps: (args) => ({
|
|
bookingType: args.bookingType,
|
|
provider: args.provider,
|
|
...(args.cost != null ? { cost: args.cost } : {}),
|
|
...(args.currency ? { currency: args.currency } : {}),
|
|
...(args.startDate ? { startDate: args.startDate } : {}),
|
|
...(args.endDate ? { endDate: args.endDate } : {}),
|
|
...(args.bookingStatus ? { bookingStatus: args.bookingStatus } : {}),
|
|
...(args.details ? { details: args.details } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created booking: ${args.bookingType} — ${args.provider}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_budget",
|
|
description: "Create a budget tracker card with total budget and expense line items. Use when the user wants to track trip costs.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
budgetTotal: { type: "number", description: "Total budget amount" },
|
|
currency: { type: "string", description: "ISO currency code (e.g. USD, EUR)" },
|
|
expensesJson: { type: "string", description: 'JSON array of expenses. Each: {"id":"<uuid>","category":"TRANSPORT|ACCOMMODATION|FOOD|ACTIVITY|SHOPPING|OTHER","description":"...","amount":123,"date":"YYYY-MM-DD"}' },
|
|
},
|
|
required: ["budgetTotal"],
|
|
},
|
|
},
|
|
tagName: "folk-budget",
|
|
moduleId: "rtrips",
|
|
buildProps: (args) => {
|
|
let expenses: any[] = [];
|
|
try { expenses = JSON.parse(args.expensesJson); } catch { expenses = []; }
|
|
return {
|
|
budgetTotal: args.budgetTotal,
|
|
...(args.currency ? { currency: args.currency } : {}),
|
|
expenses,
|
|
};
|
|
},
|
|
actionLabel: (args) => `Created budget: ${args.currency || "USD"} ${args.budgetTotal}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_packing_list",
|
|
description: "Create a packing list card with checkable items organized by category. Use when the user needs help with what to pack.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
itemsJson: { type: "string", description: 'JSON array of packing items. Each: {"id":"<uuid>","name":"...","category":"CLOTHING|FOOTWEAR|ELECTRONICS|GEAR|PERSONAL|DOCUMENTS|SAFETY|SUPPLIES","quantity":1,"packed":false}' },
|
|
},
|
|
required: ["itemsJson"],
|
|
},
|
|
},
|
|
tagName: "folk-packing-list",
|
|
moduleId: "rtrips",
|
|
buildProps: (args) => {
|
|
let items: any[] = [];
|
|
try { items = JSON.parse(args.itemsJson); } catch { items = []; }
|
|
return { items };
|
|
},
|
|
actionLabel: (args) => {
|
|
let count = 0;
|
|
try { count = JSON.parse(args.itemsJson).length; } catch {}
|
|
return `Created packing list (${count} items)`;
|
|
},
|
|
},
|
|
];
|
|
|
|
// ── Mermaid Diagram Tool ──
|
|
registry.push({
|
|
declaration: {
|
|
name: "create_mermaid_diagram",
|
|
description: "Create a mermaid diagram on the canvas. Use when the user wants to create flowcharts, sequence diagrams, class diagrams, state diagrams, ER diagrams, Gantt charts, or any diagram that can be expressed in Mermaid syntax.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
prompt: { type: "string", description: "Description of the diagram to generate (e.g. 'CI/CD pipeline with build, test, deploy stages')" },
|
|
},
|
|
required: ["prompt"],
|
|
},
|
|
},
|
|
tagName: "folk-mermaid-gen",
|
|
buildProps: (args) => ({
|
|
prompt: args.prompt,
|
|
}),
|
|
actionLabel: (args) => `Creating diagram: ${args.prompt.slice(0, 50)}${args.prompt.length > 50 ? "..." : ""}`,
|
|
});
|
|
|
|
// ── Social Media / Campaign Tools ──
|
|
registry.push(
|
|
{
|
|
declaration: {
|
|
name: "create_social_post",
|
|
description: "Create a social media post card for scheduling across platforms.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
platform: { type: "string", description: "Target platform", enum: ["x", "linkedin", "instagram", "youtube", "threads", "bluesky", "tiktok", "facebook"] },
|
|
content: { type: "string", description: "Post text content" },
|
|
postType: { type: "string", description: "Format", enum: ["text", "image", "video", "carousel", "thread", "article"] },
|
|
scheduledAt: { type: "string", description: "ISO datetime to schedule" },
|
|
hashtags: { type: "string", description: "Comma-separated hashtags" },
|
|
},
|
|
required: ["platform", "content"],
|
|
},
|
|
},
|
|
tagName: "folk-social-post",
|
|
moduleId: "rsocials",
|
|
buildProps: (args) => ({
|
|
platform: args.platform || "x",
|
|
content: args.content,
|
|
postType: args.postType || "text",
|
|
scheduledAt: args.scheduledAt || "",
|
|
hashtags: args.hashtags ? args.hashtags.split(",").map((t: string) => t.trim()).filter(Boolean) : [],
|
|
status: "draft",
|
|
}),
|
|
actionLabel: (args) => `Created ${args.platform || "social"} post`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_social_thread",
|
|
description: "Create a tweet thread card on the canvas. Use when the user wants to draft a multi-tweet thread.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Thread title" },
|
|
platform: { type: "string", description: "Target platform", enum: ["x", "bluesky", "threads"] },
|
|
tweetsJson: { type: "string", description: "JSON array of tweet strings" },
|
|
status: { type: "string", description: "Thread status", enum: ["draft", "ready", "published"] },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-social-thread",
|
|
moduleId: "rsocials",
|
|
buildProps: (args) => {
|
|
let tweets: string[] = [];
|
|
try { tweets = JSON.parse(args.tweetsJson || "[]"); } catch { tweets = []; }
|
|
return {
|
|
title: args.title,
|
|
platform: args.platform || "x",
|
|
tweets,
|
|
status: args.status || "draft",
|
|
};
|
|
},
|
|
actionLabel: (args) => `Created thread: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_campaign_card",
|
|
description: "Create a campaign dashboard card on the canvas. Use when the user wants to plan or track a social media campaign.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Campaign title" },
|
|
description: { type: "string", description: "Campaign description" },
|
|
platforms: { type: "string", description: "Comma-separated platform names" },
|
|
duration: { type: "string", description: "Campaign duration (e.g. '4 weeks')" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-social-campaign",
|
|
moduleId: "rsocials",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
description: args.description || "",
|
|
platforms: args.platforms ? args.platforms.split(",").map((p: string) => p.trim().toLowerCase()).filter(Boolean) : [],
|
|
duration: args.duration || "",
|
|
}),
|
|
actionLabel: (args) => `Created campaign: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_newsletter_card",
|
|
description: "Create a newsletter/email campaign card on the canvas. Use when the user wants to draft or schedule an email newsletter.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
subject: { type: "string", description: "Email subject line" },
|
|
listName: { type: "string", description: "Mailing list name" },
|
|
status: { type: "string", description: "Newsletter status", enum: ["draft", "scheduled", "sent"] },
|
|
scheduledAt: { type: "string", description: "ISO datetime to schedule" },
|
|
},
|
|
required: ["subject"],
|
|
},
|
|
},
|
|
tagName: "folk-social-newsletter",
|
|
moduleId: "rsocials",
|
|
buildProps: (args) => ({
|
|
subject: args.subject,
|
|
listName: args.listName || "",
|
|
status: args.status || "draft",
|
|
scheduledAt: args.scheduledAt || "",
|
|
}),
|
|
actionLabel: (args) => `Created newsletter: ${args.subject}`,
|
|
},
|
|
);
|
|
|
|
// ── rTime Commitment/Task Tools ──
|
|
registry.push(
|
|
{
|
|
declaration: {
|
|
name: "create_commitment_pool",
|
|
description: "Create a commitment pool basket on the canvas. Shows floating orbs representing community time pledges that can be dragged onto task cards.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
spaceSlug: { type: "string", description: "The space slug to load commitments from" },
|
|
},
|
|
required: ["spaceSlug"],
|
|
},
|
|
},
|
|
tagName: "folk-commitment-pool",
|
|
moduleId: "rtime",
|
|
buildProps: (args) => ({
|
|
spaceSlug: args.spaceSlug || "demo",
|
|
}),
|
|
actionLabel: (args) => `Created commitment pool for ${args.spaceSlug || "demo"}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_task_request",
|
|
description: "Create a task request card on the canvas with skill slots. Commitments can be dragged from the pool onto matching skill slots.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
taskName: { type: "string", description: "Name of the task" },
|
|
spaceSlug: { type: "string", description: "The space slug this task belongs to" },
|
|
needsJson: { type: "string", description: 'JSON object of skill needs, e.g. {"facilitation":3,"design":2}' },
|
|
},
|
|
required: ["taskName"],
|
|
},
|
|
},
|
|
tagName: "folk-task-request",
|
|
moduleId: "rtime",
|
|
buildProps: (args) => {
|
|
let needs: Record<string, number> = {};
|
|
try { needs = JSON.parse(args.needsJson || "{}"); } catch { needs = {}; }
|
|
return {
|
|
taskName: args.taskName,
|
|
spaceSlug: args.spaceSlug || "demo",
|
|
needs,
|
|
};
|
|
},
|
|
actionLabel: (args) => `Created task request: ${args.taskName}`,
|
|
},
|
|
);
|
|
|
|
// ── ASCII Art Tool ──
|
|
registry.push({
|
|
declaration: {
|
|
name: "create_ascii_art",
|
|
description: "Generate ASCII art from patterns like plasma, mandelbrot, spiral, waves, nebula, kaleidoscope, aurora, lava, crystals, or fractal_tree.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
prompt: { type: "string", description: "Pattern name or description of what to generate" },
|
|
pattern: {
|
|
type: "string",
|
|
description: "Pattern type",
|
|
enum: ["plasma", "mandelbrot", "spiral", "waves", "nebula", "kaleidoscope", "aurora", "lava", "crystals", "fractal_tree", "random"],
|
|
},
|
|
palette: {
|
|
type: "string",
|
|
description: "Character palette to use",
|
|
enum: ["classic", "blocks", "braille", "dots", "shades", "emoji", "cosmic", "runes", "geometric", "kanji", "hieroglyph", "alchemical"],
|
|
},
|
|
width: { type: "number", description: "Width in characters (default 80)" },
|
|
height: { type: "number", description: "Height in characters (default 40)" },
|
|
},
|
|
required: ["prompt"],
|
|
},
|
|
},
|
|
tagName: "folk-ascii-gen",
|
|
buildProps: (args) => ({
|
|
prompt: args.prompt,
|
|
...(args.pattern ? { pattern: args.pattern } : {}),
|
|
...(args.palette ? { palette: args.palette } : {}),
|
|
...(args.width ? { width: args.width } : {}),
|
|
...(args.height ? { height: args.height } : {}),
|
|
}),
|
|
actionLabel: (args) => `Generating ASCII art: ${args.prompt?.slice(0, 50) || args.pattern || "random"}`,
|
|
});
|
|
|
|
// ── Design Agent Tool ──
|
|
registry.push({
|
|
declaration: {
|
|
name: "create_design_agent",
|
|
description: "Open the design agent to create print layouts in Scribus. Use when the user wants to design a poster, flyer, brochure, or any print-ready document.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
brief: { type: "string", description: "Design brief describing what to create (e.g. 'A4 event poster for Mushroom Festival with title, date, and image area')" },
|
|
},
|
|
required: ["brief"],
|
|
},
|
|
},
|
|
tagName: "folk-design-agent",
|
|
moduleId: "rdesign",
|
|
buildProps: (args) => ({ brief: args.brief || "" }),
|
|
actionLabel: (args) => `Opened design agent${args.brief ? `: ${args.brief.slice(0, 50)}` : ""}`,
|
|
});
|
|
|
|
// ── rGov Governance Circuit Tools ──
|
|
registry.push(
|
|
{
|
|
declaration: {
|
|
name: "create_binary_gate",
|
|
description: "Create a Yes/No signoff gate on the canvas. Use when a decision requires someone's explicit approval or sign-off.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Title for the signoff gate (e.g. 'Proprietor Approval')" },
|
|
assignee: { type: "string", description: "Who must sign off (leave empty for 'anyone')" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-binary",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.assignee ? { assignee: args.assignee } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created binary gate: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_threshold",
|
|
description: "Create a numeric threshold gate on the canvas. Use when a decision requires accumulating a target amount (hours, dollars, signatures, etc.).",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Title for the threshold (e.g. 'Capital Required')" },
|
|
target: { type: "number", description: "Target value to reach" },
|
|
unit: { type: "string", description: "Unit of measurement (e.g. '$', 'hours', 'signatures')" },
|
|
},
|
|
required: ["title", "target"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-threshold",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
target: args.target,
|
|
...(args.unit ? { unit: args.unit } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created threshold: ${args.title} (${args.target} ${args.unit || ""})`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_gov_knob",
|
|
description: "Create an adjustable parameter knob on the canvas. Use when a governance parameter needs to be tunable (e.g. quorum percentage, budget cap).",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Parameter name (e.g. 'Quorum %')" },
|
|
min: { type: "number", description: "Minimum value" },
|
|
max: { type: "number", description: "Maximum value" },
|
|
value: { type: "number", description: "Initial value" },
|
|
unit: { type: "string", description: "Unit label (e.g. '%', '$', 'hours')" },
|
|
cooldown: { type: "number", description: "Cooldown in seconds before value propagates (0 for instant)" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-knob",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.min != null ? { min: args.min } : {}),
|
|
...(args.max != null ? { max: args.max } : {}),
|
|
...(args.value != null ? { value: args.value } : {}),
|
|
...(args.unit ? { unit: args.unit } : {}),
|
|
...(args.cooldown != null ? { cooldown: args.cooldown } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created knob: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_gov_project",
|
|
description: "Create a governance project aggregator on the canvas. It automatically tracks all upstream gates wired to it and shows overall completion progress.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Project title (e.g. 'Build a Climbing Wall')" },
|
|
description: { type: "string", description: "Project description" },
|
|
status: { type: "string", description: "Initial status", enum: ["draft", "active", "completed", "archived"] },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-project",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.description ? { description: args.description } : {}),
|
|
...(args.status ? { status: args.status } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created project: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_amendment",
|
|
description: "Create a governance amendment proposal on the canvas. An amendment proposes replacing one gate with another (e.g. converting a dollar threshold into a binary checkbox).",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Amendment title" },
|
|
targetShapeId: { type: "string", description: "ID of the shape to modify" },
|
|
replacementType: { type: "string", description: "Type of replacement shape (e.g. 'folk-gov-binary')" },
|
|
approvalMode: { type: "string", description: "How approval works", enum: ["single", "majority", "unanimous"] },
|
|
description: { type: "string", description: "Description of what the amendment changes" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-amendment",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.targetShapeId ? { targetShapeId: args.targetShapeId } : {}),
|
|
...(args.replacementType ? { replacementType: args.replacementType } : {}),
|
|
...(args.approvalMode ? { approvalMode: args.approvalMode } : {}),
|
|
...(args.description ? { description: args.description } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created amendment: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_quadratic_transform",
|
|
description: "Create a quadratic weight transformer on the canvas. Accepts raw weights and applies sqrt/log/linear dampening — useful for reducing whale dominance in voting.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Transform title (e.g. 'Vote Weight Dampener')" },
|
|
mode: { type: "string", description: "Transform mode", enum: ["sqrt", "log", "linear"] },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-quadratic",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.mode ? { mode: args.mode } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created quadratic transform: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_conviction_gate",
|
|
description: "Create a conviction accumulator on the canvas. Accumulates time-weighted conviction from stakes. Gate mode triggers at threshold; tuner mode continuously emits score.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Gate title (e.g. 'Community Support')" },
|
|
convictionMode: { type: "string", description: "Operating mode", enum: ["gate", "tuner"] },
|
|
threshold: { type: "number", description: "Conviction threshold for gate mode" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-conviction",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.convictionMode ? { convictionMode: args.convictionMode } : {}),
|
|
...(args.threshold != null ? { threshold: args.threshold } : {}),
|
|
}),
|
|
actionLabel: (args) => `Created conviction gate: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_multisig_gate",
|
|
description: "Create an M-of-N multisig gate on the canvas. Requires M named signers before passing. Signers can sign manually or auto-populate from upstream binary gates.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Multisig title (e.g. 'Council Approval')" },
|
|
requiredM: { type: "number", description: "Number of required signatures (M)" },
|
|
signerNames: { type: "string", description: "Comma-separated signer names" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-multisig",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
...(args.requiredM != null ? { requiredM: args.requiredM } : {}),
|
|
...(args.signerNames ? {
|
|
signers: args.signerNames.split(",").map((n: string) => ({
|
|
name: n.trim(), signed: false, timestamp: 0,
|
|
})),
|
|
} : {}),
|
|
}),
|
|
actionLabel: (args) => `Created multisig: ${args.title}`,
|
|
},
|
|
{
|
|
declaration: {
|
|
name: "create_sankey_visualizer",
|
|
description: "Create a governance flow Sankey visualizer on the canvas. Auto-discovers all nearby gov shapes and renders an animated flow diagram. No ports — purely visual.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
title: { type: "string", description: "Visualizer title (e.g. 'Governance Flow')" },
|
|
},
|
|
required: ["title"],
|
|
},
|
|
},
|
|
tagName: "folk-gov-sankey",
|
|
moduleId: "rgov",
|
|
buildProps: (args) => ({
|
|
title: args.title,
|
|
}),
|
|
actionLabel: (args) => `Created Sankey visualizer: ${args.title}`,
|
|
},
|
|
);
|
|
|
|
export const CANVAS_TOOLS: CanvasToolDefinition[] = [...registry];
|
|
|
|
export const CANVAS_TOOL_DECLARATIONS = CANVAS_TOOLS.map((t) => t.declaration);
|
|
|
|
export function findTool(name: string): CanvasToolDefinition | undefined {
|
|
return CANVAS_TOOLS.find((t) => t.declaration.name === name);
|
|
}
|
|
|
|
export function registerCanvasTool(def: CanvasToolDefinition): void {
|
|
CANVAS_TOOLS.push(def);
|
|
}
|
|
|
|
/** Return tools available for the given set of enabled modules.
|
|
* If enabledIds is null/undefined, all tools are returned (all modules enabled). */
|
|
export function getToolsForModules(enabledIds: string[] | null | undefined): CanvasToolDefinition[] {
|
|
if (!enabledIds) return CANVAS_TOOLS;
|
|
const enabled = new Set(enabledIds);
|
|
return CANVAS_TOOLS.filter(t => !t.moduleId || enabled.has(t.moduleId));
|
|
}
|