Merge branch 'dev'
CI/CD / deploy (push) Waiting to run
Details
CI/CD / deploy (push) Waiting to run
Details
This commit is contained in:
commit
007df8a3bd
|
|
@ -14,6 +14,8 @@ export interface CanvasToolDefinition {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
tagName: string;
|
tagName: string;
|
||||||
|
/** Module that owns this tool (omit for core/always-available tools) */
|
||||||
|
moduleId?: string;
|
||||||
buildProps: (args: Record<string, any>) => Record<string, any>;
|
buildProps: (args: Record<string, any>) => Record<string, any>;
|
||||||
actionLabel: (args: Record<string, any>) => string;
|
actionLabel: (args: Record<string, any>) => string;
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +37,7 @@ const registry: CanvasToolDefinition[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-map",
|
tagName: "folk-map",
|
||||||
|
moduleId: "rmaps",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
center: [args.longitude, args.latitude],
|
center: [args.longitude, args.latitude],
|
||||||
zoom: args.zoom || 12,
|
zoom: args.zoom || 12,
|
||||||
|
|
@ -161,6 +164,7 @@ const registry: CanvasToolDefinition[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-destination",
|
tagName: "folk-destination",
|
||||||
|
moduleId: "rtrips",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
destName: args.destName,
|
destName: args.destName,
|
||||||
...(args.country ? { country: args.country } : {}),
|
...(args.country ? { country: args.country } : {}),
|
||||||
|
|
@ -186,6 +190,7 @@ const registry: CanvasToolDefinition[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-itinerary",
|
tagName: "folk-itinerary",
|
||||||
|
moduleId: "rtrips",
|
||||||
buildProps: (args) => {
|
buildProps: (args) => {
|
||||||
let items: any[] = [];
|
let items: any[] = [];
|
||||||
try { items = JSON.parse(args.itemsJson); } catch { items = []; }
|
try { items = JSON.parse(args.itemsJson); } catch { items = []; }
|
||||||
|
|
@ -217,6 +222,7 @@ const registry: CanvasToolDefinition[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-booking",
|
tagName: "folk-booking",
|
||||||
|
moduleId: "rtrips",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
bookingType: args.bookingType,
|
bookingType: args.bookingType,
|
||||||
provider: args.provider,
|
provider: args.provider,
|
||||||
|
|
@ -244,6 +250,7 @@ const registry: CanvasToolDefinition[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-budget",
|
tagName: "folk-budget",
|
||||||
|
moduleId: "rtrips",
|
||||||
buildProps: (args) => {
|
buildProps: (args) => {
|
||||||
let expenses: any[] = [];
|
let expenses: any[] = [];
|
||||||
try { expenses = JSON.parse(args.expensesJson); } catch { expenses = []; }
|
try { expenses = JSON.parse(args.expensesJson); } catch { expenses = []; }
|
||||||
|
|
@ -268,6 +275,7 @@ const registry: CanvasToolDefinition[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-packing-list",
|
tagName: "folk-packing-list",
|
||||||
|
moduleId: "rtrips",
|
||||||
buildProps: (args) => {
|
buildProps: (args) => {
|
||||||
let items: any[] = [];
|
let items: any[] = [];
|
||||||
try { items = JSON.parse(args.itemsJson); } catch { items = []; }
|
try { items = JSON.parse(args.itemsJson); } catch { items = []; }
|
||||||
|
|
@ -320,6 +328,7 @@ registry.push(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-social-post",
|
tagName: "folk-social-post",
|
||||||
|
moduleId: "rsocials",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
platform: args.platform || "x",
|
platform: args.platform || "x",
|
||||||
content: args.content,
|
content: args.content,
|
||||||
|
|
@ -346,6 +355,7 @@ registry.push(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-social-thread",
|
tagName: "folk-social-thread",
|
||||||
|
moduleId: "rsocials",
|
||||||
buildProps: (args) => {
|
buildProps: (args) => {
|
||||||
let tweets: string[] = [];
|
let tweets: string[] = [];
|
||||||
try { tweets = JSON.parse(args.tweetsJson || "[]"); } catch { tweets = []; }
|
try { tweets = JSON.parse(args.tweetsJson || "[]"); } catch { tweets = []; }
|
||||||
|
|
@ -374,6 +384,7 @@ registry.push(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-social-campaign",
|
tagName: "folk-social-campaign",
|
||||||
|
moduleId: "rsocials",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
title: args.title,
|
title: args.title,
|
||||||
description: args.description || "",
|
description: args.description || "",
|
||||||
|
|
@ -398,6 +409,7 @@ registry.push(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-social-newsletter",
|
tagName: "folk-social-newsletter",
|
||||||
|
moduleId: "rsocials",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
subject: args.subject,
|
subject: args.subject,
|
||||||
listName: args.listName || "",
|
listName: args.listName || "",
|
||||||
|
|
@ -423,6 +435,7 @@ registry.push(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-commitment-pool",
|
tagName: "folk-commitment-pool",
|
||||||
|
moduleId: "rtime",
|
||||||
buildProps: (args) => ({
|
buildProps: (args) => ({
|
||||||
spaceSlug: args.spaceSlug || "demo",
|
spaceSlug: args.spaceSlug || "demo",
|
||||||
}),
|
}),
|
||||||
|
|
@ -443,6 +456,7 @@ registry.push(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-task-request",
|
tagName: "folk-task-request",
|
||||||
|
moduleId: "rtime",
|
||||||
buildProps: (args) => {
|
buildProps: (args) => {
|
||||||
let needs: Record<string, number> = {};
|
let needs: Record<string, number> = {};
|
||||||
try { needs = JSON.parse(args.needsJson || "{}"); } catch { needs = {}; }
|
try { needs = JSON.parse(args.needsJson || "{}"); } catch { needs = {}; }
|
||||||
|
|
@ -470,6 +484,7 @@ registry.push({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tagName: "folk-design-agent",
|
tagName: "folk-design-agent",
|
||||||
|
moduleId: "rdesign",
|
||||||
buildProps: (args) => ({ brief: args.brief || "" }),
|
buildProps: (args) => ({ brief: args.brief || "" }),
|
||||||
actionLabel: (args) => `Opened design agent${args.brief ? `: ${args.brief.slice(0, 50)}` : ""}`,
|
actionLabel: (args) => `Opened design agent${args.brief ? `: ${args.brief.slice(0, 50)}` : ""}`,
|
||||||
});
|
});
|
||||||
|
|
@ -485,3 +500,11 @@ export function findTool(name: string): CanvasToolDefinition | undefined {
|
||||||
export function registerCanvasTool(def: CanvasToolDefinition): void {
|
export function registerCanvasTool(def: CanvasToolDefinition): void {
|
||||||
CANVAS_TOOLS.push(def);
|
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));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,48 @@ export class FolkCommitmentPool extends FolkShape {
|
||||||
this.styles = sheet;
|
this.styles = sheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Module-disabled gating ──
|
||||||
|
static #enabledModules: Set<string> | null = null;
|
||||||
|
static #instances = new Set<FolkCommitmentPool>();
|
||||||
|
|
||||||
|
static setEnabledModules(ids: string[] | null) {
|
||||||
|
FolkCommitmentPool.#enabledModules = ids ? new Set(ids) : null;
|
||||||
|
for (const inst of FolkCommitmentPool.#instances) inst.#syncDisabledState();
|
||||||
|
}
|
||||||
|
|
||||||
|
#isModuleDisabled(): boolean {
|
||||||
|
const enabled = FolkCommitmentPool.#enabledModules;
|
||||||
|
if (!enabled) return false;
|
||||||
|
return !enabled.has("rtime");
|
||||||
|
}
|
||||||
|
|
||||||
|
#syncDisabledState() {
|
||||||
|
if (!this.#wrapper) return;
|
||||||
|
const disabled = this.#isModuleDisabled();
|
||||||
|
const wasDisabled = this.hasAttribute("data-module-disabled");
|
||||||
|
if (disabled && !wasDisabled) {
|
||||||
|
this.#showDisabledOverlay();
|
||||||
|
} else if (!disabled && wasDisabled) {
|
||||||
|
this.removeAttribute("data-module-disabled");
|
||||||
|
this.#wrapper.querySelector(".disabled-overlay")?.remove();
|
||||||
|
this.#fetchCommitments();
|
||||||
|
this.#startAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#showDisabledOverlay() {
|
||||||
|
this.setAttribute("data-module-disabled", "");
|
||||||
|
if (this.#animFrame) { cancelAnimationFrame(this.#animFrame); this.#animFrame = 0; }
|
||||||
|
let overlay = this.#wrapper?.querySelector(".disabled-overlay") as HTMLElement;
|
||||||
|
if (!overlay && this.#wrapper) {
|
||||||
|
overlay = document.createElement("div");
|
||||||
|
overlay.className = "disabled-overlay";
|
||||||
|
overlay.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px;background:rgba(15,23,42,0.85);border-radius:inherit;z-index:10;color:#94a3b8;font-size:13px;";
|
||||||
|
overlay.innerHTML = '<span style="font-size:24px">🔒</span><span>rTime is disabled</span>';
|
||||||
|
this.#wrapper.appendChild(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#spaceSlug = "demo";
|
#spaceSlug = "demo";
|
||||||
#canvas!: HTMLCanvasElement;
|
#canvas!: HTMLCanvasElement;
|
||||||
#ctx!: CanvasRenderingContext2D;
|
#ctx!: CanvasRenderingContext2D;
|
||||||
|
|
@ -234,13 +276,20 @@ export class FolkCommitmentPool extends FolkShape {
|
||||||
this.#canvas.addEventListener("pointerdown", this.#onPointerDown);
|
this.#canvas.addEventListener("pointerdown", this.#onPointerDown);
|
||||||
this.#canvas.addEventListener("pointerleave", () => { this.#hoveredOrb = null; });
|
this.#canvas.addEventListener("pointerleave", () => { this.#hoveredOrb = null; });
|
||||||
|
|
||||||
this.#fetchCommitments();
|
FolkCommitmentPool.#instances.add(this);
|
||||||
this.#startAnimation();
|
|
||||||
|
if (this.#isModuleDisabled()) {
|
||||||
|
this.#showDisabledOverlay();
|
||||||
|
} else {
|
||||||
|
this.#fetchCommitments();
|
||||||
|
this.#startAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
FolkCommitmentPool.#instances.delete(this);
|
||||||
if (this.#animFrame) cancelAnimationFrame(this.#animFrame);
|
if (this.#animFrame) cancelAnimationFrame(this.#animFrame);
|
||||||
this.#animFrame = 0;
|
this.#animFrame = 0;
|
||||||
this.#removeGhost();
|
this.#removeGhost();
|
||||||
|
|
|
||||||
|
|
@ -648,6 +648,7 @@ export class FolkPrompt extends FolkShape {
|
||||||
})),
|
})),
|
||||||
model: this.#model,
|
model: this.#model,
|
||||||
...(useTools ? { useTools: true, systemPrompt: this.#systemPrompt || undefined } : {}),
|
...(useTools ? { useTools: true, systemPrompt: this.#systemPrompt || undefined } : {}),
|
||||||
|
...(useTools && (window as any).__rspaceEnabledModules ? { enabledModules: (window as any).__rspaceEnabledModules } : {}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { FolkShape } from "./folk-shape";
|
import { FolkShape } from "./folk-shape";
|
||||||
import { css, html } from "./tags";
|
import { css, html } from "./tags";
|
||||||
import { rspaceNavUrl } from "../shared/url-helpers";
|
import { rspaceNavUrl } from "../shared/url-helpers";
|
||||||
|
import { MODULE_META } from "./module-display";
|
||||||
import type { PortDescriptor } from "./data-types";
|
import type { PortDescriptor } from "./data-types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,35 +17,6 @@ import type { PortDescriptor } from "./data-types";
|
||||||
* iframe → parent: { source: "rspace-rapp", type: "navigate", moduleId }
|
* iframe → parent: { source: "rspace-rapp", type: "navigate", moduleId }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Module metadata for header display (subset of rstack-app-switcher badges)
|
|
||||||
const MODULE_META: Record<string, { badge: string; color: string; name: string; icon: string }> = {
|
|
||||||
rnotes: { badge: "rN", color: "#fcd34d", name: "rNotes", icon: "📝" },
|
|
||||||
rphotos: { badge: "rPh", color: "#f9a8d4", name: "rPhotos", icon: "📸" },
|
|
||||||
rbooks: { badge: "rB", color: "#fda4af", name: "rBooks", icon: "📚" },
|
|
||||||
rpubs: { badge: "rP", color: "#fda4af", name: "rPubs", icon: "📖" },
|
|
||||||
rfiles: { badge: "rFi", color: "#67e8f9", name: "rFiles", icon: "📁" },
|
|
||||||
rtasks: { badge: "rTa", color: "#cbd5e1", name: "rTasks", icon: "📋" },
|
|
||||||
rforum: { badge: "rFo", color: "#fcd34d", name: "rForum", icon: "💬" },
|
|
||||||
rinbox: { badge: "rI", color: "#a5b4fc", name: "rInbox", icon: "📧" },
|
|
||||||
rtube: { badge: "rTu", color: "#f9a8d4", name: "rTube", icon: "🎬" },
|
|
||||||
rflows: { badge: "rFl", color: "#bef264", name: "rFlows", icon: "🌊" },
|
|
||||||
rwallet: { badge: "rW", color: "#fde047", name: "rWallet", icon: "💰" },
|
|
||||||
rvote: { badge: "rV", color: "#c4b5fd", name: "rVote", icon: "🗳️" },
|
|
||||||
rcart: { badge: "rCt", color: "#fdba74", name: "rCart", icon: "🛒" },
|
|
||||||
rdata: { badge: "rD", color: "#d8b4fe", name: "rData", icon: "📊" },
|
|
||||||
rnetwork: { badge: "rNe", color: "#93c5fd", name: "rNetwork", icon: "🌍" },
|
|
||||||
rsplat: { badge: "r3", color: "#d8b4fe", name: "rSplat", icon: "🔮" },
|
|
||||||
rswag: { badge: "rSw", color: "#fda4af", name: "rSwag", icon: "🎨" },
|
|
||||||
rchoices: { badge: "rCo", color: "#f0abfc", name: "rChoices", icon: "🤔" },
|
|
||||||
rcal: { badge: "rC", color: "#7dd3fc", name: "rCal", icon: "📅" },
|
|
||||||
rtrips: { badge: "rT", color: "#6ee7b7", name: "rTrips", icon: "✈️" },
|
|
||||||
rmaps: { badge: "rM", color: "#86efac", name: "rMaps", icon: "🗺️" },
|
|
||||||
rmeets: { badge: "rMe", color: "#6ee7b7", name: "rMeets", icon: "📹" },
|
|
||||||
rschedule: { badge: "rSc", color: "#93c5fd", name: "rSchedule", icon: "⏰" },
|
|
||||||
rsocials: { badge: "rSo", color: "#f9a8d4", name: "rSocials", icon: "📱" },
|
|
||||||
rdesign: { badge: "rDe", color: "#7c3aed", name: "rDesign", icon: "🎨" },
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = css`
|
const styles = css`
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,45 @@ export class FolkTaskRequest extends FolkShape {
|
||||||
this.styles = sheet;
|
this.styles = sheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Module-disabled gating ──
|
||||||
|
static #enabledModules: Set<string> | null = null;
|
||||||
|
static #instances = new Set<FolkTaskRequest>();
|
||||||
|
|
||||||
|
static setEnabledModules(ids: string[] | null) {
|
||||||
|
FolkTaskRequest.#enabledModules = ids ? new Set(ids) : null;
|
||||||
|
for (const inst of FolkTaskRequest.#instances) inst.#syncDisabledState();
|
||||||
|
}
|
||||||
|
|
||||||
|
#isModuleDisabled(): boolean {
|
||||||
|
const enabled = FolkTaskRequest.#enabledModules;
|
||||||
|
if (!enabled) return false;
|
||||||
|
return !enabled.has("rtime");
|
||||||
|
}
|
||||||
|
|
||||||
|
#syncDisabledState() {
|
||||||
|
if (!this.#wrapper) return;
|
||||||
|
const disabled = this.#isModuleDisabled();
|
||||||
|
const wasDisabled = this.hasAttribute("data-module-disabled");
|
||||||
|
if (disabled && !wasDisabled) {
|
||||||
|
this.#showDisabledOverlay();
|
||||||
|
} else if (!disabled && wasDisabled) {
|
||||||
|
this.removeAttribute("data-module-disabled");
|
||||||
|
this.#wrapper.querySelector(".disabled-overlay")?.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#showDisabledOverlay() {
|
||||||
|
this.setAttribute("data-module-disabled", "");
|
||||||
|
let overlay = this.#wrapper?.querySelector(".disabled-overlay") as HTMLElement;
|
||||||
|
if (!overlay && this.#wrapper) {
|
||||||
|
overlay = document.createElement("div");
|
||||||
|
overlay.className = "disabled-overlay";
|
||||||
|
overlay.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px;background:rgba(15,23,42,0.85);border-radius:inherit;z-index:10;color:#94a3b8;font-size:13px;";
|
||||||
|
overlay.innerHTML = '<span style="font-size:24px">🔒</span><span>rTime is disabled</span>';
|
||||||
|
this.#wrapper.appendChild(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#taskId = "";
|
#taskId = "";
|
||||||
#spaceSlug = "demo";
|
#spaceSlug = "demo";
|
||||||
#taskName = "New Task";
|
#taskName = "New Task";
|
||||||
|
|
@ -234,10 +273,14 @@ export class FolkTaskRequest extends FolkShape {
|
||||||
document.addEventListener("commitment-drag-move", this.#onDragMove as EventListener);
|
document.addEventListener("commitment-drag-move", this.#onDragMove as EventListener);
|
||||||
document.addEventListener("commitment-drag-end", this.#onDragEnd as EventListener);
|
document.addEventListener("commitment-drag-end", this.#onDragEnd as EventListener);
|
||||||
|
|
||||||
|
FolkTaskRequest.#instances.add(this);
|
||||||
|
if (this.#isModuleDisabled()) this.#showDisabledOverlay();
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
FolkTaskRequest.#instances.delete(this);
|
||||||
document.removeEventListener("commitment-drag-start", this.#onDragStart as EventListener);
|
document.removeEventListener("commitment-drag-start", this.#onDragStart as EventListener);
|
||||||
document.removeEventListener("commitment-drag-move", this.#onDragMove as EventListener);
|
document.removeEventListener("commitment-drag-move", this.#onDragMove as EventListener);
|
||||||
document.removeEventListener("commitment-drag-end", this.#onDragEnd as EventListener);
|
document.removeEventListener("commitment-drag-end", this.#onDragEnd as EventListener);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Module display metadata — badge colors, names, icons for canvas UI.
|
||||||
|
* Extracted from folk-rapp.ts so both folk-rapp and future display code
|
||||||
|
* can share a single source of truth.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface ModuleDisplayMeta {
|
||||||
|
badge: string;
|
||||||
|
color: string;
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MODULE_META: Record<string, ModuleDisplayMeta> = {
|
||||||
|
rnotes: { badge: "rN", color: "#fcd34d", name: "rNotes", icon: "📝" },
|
||||||
|
rphotos: { badge: "rPh", color: "#f9a8d4", name: "rPhotos", icon: "📸" },
|
||||||
|
rbooks: { badge: "rB", color: "#fda4af", name: "rBooks", icon: "📚" },
|
||||||
|
rpubs: { badge: "rP", color: "#fda4af", name: "rPubs", icon: "📖" },
|
||||||
|
rfiles: { badge: "rFi", color: "#67e8f9", name: "rFiles", icon: "📁" },
|
||||||
|
rtasks: { badge: "rTa", color: "#cbd5e1", name: "rTasks", icon: "📋" },
|
||||||
|
rforum: { badge: "rFo", color: "#fcd34d", name: "rForum", icon: "💬" },
|
||||||
|
rinbox: { badge: "rI", color: "#a5b4fc", name: "rInbox", icon: "📧" },
|
||||||
|
rtube: { badge: "rTu", color: "#f9a8d4", name: "rTube", icon: "🎬" },
|
||||||
|
rflows: { badge: "rFl", color: "#bef264", name: "rFlows", icon: "🌊" },
|
||||||
|
rwallet: { badge: "rW", color: "#fde047", name: "rWallet", icon: "💰" },
|
||||||
|
rvote: { badge: "rV", color: "#c4b5fd", name: "rVote", icon: "🗳️" },
|
||||||
|
rcart: { badge: "rCt", color: "#fdba74", name: "rCart", icon: "🛒" },
|
||||||
|
rdata: { badge: "rD", color: "#d8b4fe", name: "rData", icon: "📊" },
|
||||||
|
rnetwork: { badge: "rNe", color: "#93c5fd", name: "rNetwork", icon: "🌍" },
|
||||||
|
rsplat: { badge: "r3", color: "#d8b4fe", name: "rSplat", icon: "🔮" },
|
||||||
|
rswag: { badge: "rSw", color: "#fda4af", name: "rSwag", icon: "🎨" },
|
||||||
|
rchoices: { badge: "rCo", color: "#f0abfc", name: "rChoices", icon: "🤔" },
|
||||||
|
rcal: { badge: "rC", color: "#7dd3fc", name: "rCal", icon: "📅" },
|
||||||
|
rtrips: { badge: "rT", color: "#6ee7b7", name: "rTrips", icon: "✈️" },
|
||||||
|
rmaps: { badge: "rM", color: "#86efac", name: "rMaps", icon: "🗺️" },
|
||||||
|
rmeets: { badge: "rMe", color: "#6ee7b7", name: "rMeets", icon: "📹" },
|
||||||
|
rschedule: { badge: "rSc", color: "#93c5fd", name: "rSchedule", icon: "⏰" },
|
||||||
|
rsocials: { badge: "rSo", color: "#f9a8d4", name: "rSocials", icon: "📱" },
|
||||||
|
rdesign: { badge: "rDe", color: "#7c3aed", name: "rDesign", icon: "🎨" },
|
||||||
|
rtime: { badge: "rTi", color: "#a78bfa", name: "rTime", icon: "⏳" },
|
||||||
|
};
|
||||||
|
|
@ -19,12 +19,23 @@ export interface ShapeRegistration {
|
||||||
|
|
||||||
class ShapeRegistry {
|
class ShapeRegistry {
|
||||||
#registrations = new Map<string, ShapeRegistration>();
|
#registrations = new Map<string, ShapeRegistration>();
|
||||||
|
#moduleMap = new Map<string, string>();
|
||||||
|
|
||||||
/** Register a shape type. */
|
/** Register a shape type. */
|
||||||
register(tagName: string, elementClass: ShapeRegistration["elementClass"]): void {
|
register(tagName: string, elementClass: ShapeRegistration["elementClass"]): void {
|
||||||
this.#registrations.set(tagName, { tagName, elementClass });
|
this.#registrations.set(tagName, { tagName, elementClass });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Associate a shape tag with a module ID. */
|
||||||
|
setModule(tagName: string, moduleId: string): void {
|
||||||
|
this.#moduleMap.set(tagName, moduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the owning module ID for a shape tag, or undefined if core/always-available. */
|
||||||
|
moduleOf(tagName: string): string | undefined {
|
||||||
|
return this.#moduleMap.get(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
/** Get registration for a tag name. */
|
/** Get registration for a tag name. */
|
||||||
getRegistration(tagName: string): ShapeRegistration | undefined {
|
getRegistration(tagName: string): ShapeRegistration | undefined {
|
||||||
return this.#registrations.get(tagName);
|
return this.#registrations.get(tagName);
|
||||||
|
|
|
||||||
|
|
@ -1082,6 +1082,7 @@ export const calModule: RSpaceModule = {
|
||||||
name: "rCal",
|
name: "rCal",
|
||||||
icon: "📅",
|
icon: "📅",
|
||||||
description: "Temporal coordination calendar with lunar, solar, and seasonal systems",
|
description: "Temporal coordination calendar with lunar, solar, and seasonal systems",
|
||||||
|
canvasShapes: ["folk-calendar"],
|
||||||
scoping: { defaultScope: 'global', userConfigurable: true },
|
scoping: { defaultScope: 'global', userConfigurable: true },
|
||||||
docSchemas: [{ pattern: '{space}:cal:events', description: 'Calendar events and sources', init: calendarSchema.init }],
|
docSchemas: [{ pattern: '{space}:cal:events', description: 'Calendar events and sources', init: calendarSchema.init }],
|
||||||
routes,
|
routes,
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,7 @@ export const choicesModule: RSpaceModule = {
|
||||||
name: "rChoices",
|
name: "rChoices",
|
||||||
icon: "☑",
|
icon: "☑",
|
||||||
description: "Polls, rankings, and multi-criteria scoring",
|
description: "Polls, rankings, and multi-criteria scoring",
|
||||||
|
canvasShapes: ["folk-choice-vote", "folk-choice-rank", "folk-choice-spider", "folk-spider-3d", "folk-choice-conviction"],
|
||||||
scoping: { defaultScope: 'space', userConfigurable: false },
|
scoping: { defaultScope: 'space', userConfigurable: false },
|
||||||
routes,
|
routes,
|
||||||
standaloneDomain: "rchoices.online",
|
standaloneDomain: "rchoices.online",
|
||||||
|
|
|
||||||
|
|
@ -673,6 +673,8 @@ export const designModule: RSpaceModule = {
|
||||||
name: "rDesign",
|
name: "rDesign",
|
||||||
icon: "🎨",
|
icon: "🎨",
|
||||||
description: "AI-powered DTP workspace — text in, design out",
|
description: "AI-powered DTP workspace — text in, design out",
|
||||||
|
canvasShapes: ["folk-design-agent"],
|
||||||
|
canvasToolIds: ["create_design_agent"],
|
||||||
scoping: { defaultScope: 'global', userConfigurable: false },
|
scoping: { defaultScope: 'global', userConfigurable: false },
|
||||||
publicWrite: true,
|
publicWrite: true,
|
||||||
routes,
|
routes,
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,8 @@ export const mapsModule: RSpaceModule = {
|
||||||
name: "rMaps",
|
name: "rMaps",
|
||||||
icon: "🗺",
|
icon: "🗺",
|
||||||
description: "Real-time collaborative location sharing and indoor/outdoor maps",
|
description: "Real-time collaborative location sharing and indoor/outdoor maps",
|
||||||
|
canvasShapes: ["folk-map"],
|
||||||
|
canvasToolIds: ["create_map"],
|
||||||
scoping: { defaultScope: 'global', userConfigurable: false },
|
scoping: { defaultScope: 'global', userConfigurable: false },
|
||||||
routes,
|
routes,
|
||||||
landingPage: renderLanding,
|
landingPage: renderLanding,
|
||||||
|
|
|
||||||
|
|
@ -2270,6 +2270,8 @@ export const socialsModule: RSpaceModule = {
|
||||||
name: "rSocials",
|
name: "rSocials",
|
||||||
icon: "📢",
|
icon: "📢",
|
||||||
description: "Federated social feed aggregator for communities",
|
description: "Federated social feed aggregator for communities",
|
||||||
|
canvasShapes: ["folk-social-post", "folk-social-thread", "folk-social-campaign", "folk-social-newsletter"],
|
||||||
|
canvasToolIds: ["create_social_post", "create_social_thread", "create_campaign_card", "create_newsletter_card"],
|
||||||
scoping: { defaultScope: "space", userConfigurable: true },
|
scoping: { defaultScope: "space", userConfigurable: true },
|
||||||
docSchemas: [{ pattern: "{space}:socials:data", description: "Threads and campaigns", init: socialsSchema.init }],
|
docSchemas: [{ pattern: "{space}:socials:data", description: "Threads and campaigns", init: socialsSchema.init }],
|
||||||
routes,
|
routes,
|
||||||
|
|
|
||||||
|
|
@ -863,6 +863,7 @@ export const splatModule: RSpaceModule = {
|
||||||
name: "rSplat",
|
name: "rSplat",
|
||||||
icon: "🔮",
|
icon: "🔮",
|
||||||
description: "3D Gaussian splat viewer",
|
description: "3D Gaussian splat viewer",
|
||||||
|
canvasShapes: ["folk-splat"],
|
||||||
publicWrite: true,
|
publicWrite: true,
|
||||||
scoping: { defaultScope: 'global', userConfigurable: true },
|
scoping: { defaultScope: 'global', userConfigurable: true },
|
||||||
docSchemas: [{ pattern: '{space}:splat:scenes', description: 'Splat scene metadata', init: splatScenesSchema.init }],
|
docSchemas: [{ pattern: '{space}:splat:scenes', description: 'Splat scene metadata', init: splatScenesSchema.init }],
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,8 @@ export const timeModule: RSpaceModule = {
|
||||||
name: "rTime",
|
name: "rTime",
|
||||||
icon: "⏳",
|
icon: "⏳",
|
||||||
description: "Timebank commitment pool & weaving dashboard",
|
description: "Timebank commitment pool & weaving dashboard",
|
||||||
|
canvasShapes: ["folk-commitment-pool", "folk-task-request"],
|
||||||
|
canvasToolIds: ["create_commitment_pool", "create_task_request"],
|
||||||
scoping: { defaultScope: 'space', userConfigurable: false },
|
scoping: { defaultScope: 'space', userConfigurable: false },
|
||||||
docSchemas: [
|
docSchemas: [
|
||||||
{ pattern: '{space}:rtime:commitments', description: 'Commitment pool', init: commitmentsSchema.init },
|
{ pattern: '{space}:rtime:commitments', description: 'Commitment pool', init: commitmentsSchema.init },
|
||||||
|
|
|
||||||
|
|
@ -754,6 +754,8 @@ export const tripsModule: RSpaceModule = {
|
||||||
name: "rTrips",
|
name: "rTrips",
|
||||||
icon: "✈️",
|
icon: "✈️",
|
||||||
description: "Collaborative trip planner with itinerary, bookings, and expense splitting",
|
description: "Collaborative trip planner with itinerary, bookings, and expense splitting",
|
||||||
|
canvasShapes: ["folk-itinerary", "folk-destination", "folk-budget", "folk-packing-list", "folk-booking"],
|
||||||
|
canvasToolIds: ["create_destination", "create_itinerary", "create_booking", "create_budget", "create_packing_list"],
|
||||||
scoping: { defaultScope: 'global', userConfigurable: true },
|
scoping: { defaultScope: 'global', userConfigurable: true },
|
||||||
docSchemas: [{ pattern: '{space}:trips:trips:{tripId}', description: 'Trip with destinations and itinerary', init: tripSchema.init }],
|
docSchemas: [{ pattern: '{space}:trips:trips:{tripId}', description: 'Trip with destinations and itinerary', init: tripSchema.init }],
|
||||||
routes,
|
routes,
|
||||||
|
|
|
||||||
|
|
@ -1294,6 +1294,7 @@ export const walletModule: RSpaceModule = {
|
||||||
name: "rWallet",
|
name: "rWallet",
|
||||||
icon: "💰",
|
icon: "💰",
|
||||||
description: "Multichain Safe wallet visualization and treasury management",
|
description: "Multichain Safe wallet visualization and treasury management",
|
||||||
|
canvasShapes: ["folk-token-mint", "folk-token-ledger", "folk-transaction-builder"],
|
||||||
scoping: { defaultScope: 'global', userConfigurable: false },
|
scoping: { defaultScope: 'global', userConfigurable: false },
|
||||||
routes,
|
routes,
|
||||||
standaloneDomain: "rwallet.online",
|
standaloneDomain: "rwallet.online",
|
||||||
|
|
|
||||||
|
|
@ -1899,7 +1899,7 @@ After creating shapes, give a brief summary of what you placed. Only create shap
|
||||||
For text-only questions (explanations, coding help, math), respond with text — don't create shapes unless asked.`;
|
For text-only questions (explanations, coding help, math), respond with text — don't create shapes unless asked.`;
|
||||||
|
|
||||||
app.post("/api/prompt", async (c) => {
|
app.post("/api/prompt", async (c) => {
|
||||||
const { messages, model = "gemini-flash", useTools = false, systemPrompt } = await c.req.json();
|
const { messages, model = "gemini-flash", useTools = false, systemPrompt, enabledModules } = await c.req.json();
|
||||||
if (!messages?.length) return c.json({ error: "messages required" }, 400);
|
if (!messages?.length) return c.json({ error: "messages required" }, 400);
|
||||||
|
|
||||||
// Determine provider
|
// Determine provider
|
||||||
|
|
@ -1912,8 +1912,9 @@ app.post("/api/prompt", async (c) => {
|
||||||
// Build model config with optional tools
|
// Build model config with optional tools
|
||||||
const modelConfig: any = { model: GEMINI_MODELS[model] };
|
const modelConfig: any = { model: GEMINI_MODELS[model] };
|
||||||
if (useTools) {
|
if (useTools) {
|
||||||
const { CANVAS_TOOL_DECLARATIONS } = await import("../lib/canvas-tools");
|
const { getToolsForModules } = await import("../lib/canvas-tools");
|
||||||
modelConfig.tools = [{ functionDeclarations: CANVAS_TOOL_DECLARATIONS }];
|
const tools = getToolsForModules(enabledModules ?? null);
|
||||||
|
modelConfig.tools = [{ functionDeclarations: tools.map(t => t.declaration) }];
|
||||||
modelConfig.systemInstruction = systemPrompt || CANVAS_TOOLS_SYSTEM_PROMPT;
|
modelConfig.systemInstruction = systemPrompt || CANVAS_TOOLS_SYSTEM_PROMPT;
|
||||||
}
|
}
|
||||||
const geminiModel = genAI.getGenerativeModel(modelConfig);
|
const geminiModel = genAI.getGenerativeModel(modelConfig);
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,11 @@ export interface RSpaceModule {
|
||||||
|
|
||||||
/** Per-module settings schema for space-level configuration */
|
/** Per-module settings schema for space-level configuration */
|
||||||
settingsSchema?: ModuleSettingField[];
|
settingsSchema?: ModuleSettingField[];
|
||||||
|
|
||||||
|
/** Canvas shape tag names this module owns (e.g. ["folk-commitment-pool"]) */
|
||||||
|
canvasShapes?: string[];
|
||||||
|
/** Canvas AI tool IDs this module owns (e.g. ["create_commitment_pool"]) */
|
||||||
|
canvasToolIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Registry of all loaded modules */
|
/** Registry of all loaded modules */
|
||||||
|
|
@ -210,6 +215,8 @@ export interface ModuleInfo {
|
||||||
subPageInfos?: Array<{ path: string; title: string }>;
|
subPageInfos?: Array<{ path: string; title: string }>;
|
||||||
settingsSchema?: ModuleSettingField[];
|
settingsSchema?: ModuleSettingField[];
|
||||||
onboardingActions?: OnboardingAction[];
|
onboardingActions?: OnboardingAction[];
|
||||||
|
canvasShapes?: string[];
|
||||||
|
canvasToolIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModuleInfoList(): ModuleInfo[] {
|
export function getModuleInfoList(): ModuleInfo[] {
|
||||||
|
|
@ -230,5 +237,7 @@ export function getModuleInfoList(): ModuleInfo[] {
|
||||||
...(m.subPageInfos ? { subPageInfos: m.subPageInfos.map(s => ({ path: s.path, title: s.title })) } : {}),
|
...(m.subPageInfos ? { subPageInfos: m.subPageInfos.map(s => ({ path: s.path, title: s.title })) } : {}),
|
||||||
...(m.settingsSchema ? { settingsSchema: m.settingsSchema } : {}),
|
...(m.settingsSchema ? { settingsSchema: m.settingsSchema } : {}),
|
||||||
...(m.onboardingActions ? { onboardingActions: m.onboardingActions } : {}),
|
...(m.onboardingActions ? { onboardingActions: m.onboardingActions } : {}),
|
||||||
|
...(m.canvasShapes ? { canvasShapes: m.canvasShapes } : {}),
|
||||||
|
...(m.canvasToolIds ? { canvasToolIds: m.canvasToolIds } : {}),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2032,7 +2032,7 @@
|
||||||
<button id="new-bookmark" title="Bookmark">🔖 Bookmark</button>
|
<button id="new-bookmark" title="Bookmark">🔖 Bookmark</button>
|
||||||
<button id="new-google-item" title="Google">📎 Google</button>
|
<button id="new-google-item" title="Google">📎 Google</button>
|
||||||
<button id="new-map" title="Map" data-requires-module="rmaps">🗺️ Map</button>
|
<button id="new-map" title="Map" data-requires-module="rmaps">🗺️ Map</button>
|
||||||
<button id="new-social-post" title="Social Post">📱 Social Post</button>
|
<button id="new-social-post" title="Social Post" data-requires-module="rsocials">📱 Social Post</button>
|
||||||
<button id="embed-notes" title="Embed rNotes" data-requires-module="rnotes">📝 rNotes</button>
|
<button id="embed-notes" title="Embed rNotes" data-requires-module="rnotes">📝 rNotes</button>
|
||||||
<button id="embed-books" title="Embed rBooks" data-requires-module="rbooks">📚 rBooks</button>
|
<button id="embed-books" title="Embed rBooks" data-requires-module="rbooks">📚 rBooks</button>
|
||||||
<button id="embed-forum" title="Embed rForum" data-requires-module="rforum">💬 rForum</button>
|
<button id="embed-forum" title="Embed rForum" data-requires-module="rforum">💬 rForum</button>
|
||||||
|
|
@ -2091,12 +2091,12 @@
|
||||||
<button class="toolbar-group-toggle" title="Decide"><span class="tg-icon">📊</span><span class="tg-label">Decide</span></button>
|
<button class="toolbar-group-toggle" title="Decide"><span class="tg-icon">📊</span><span class="tg-label">Decide</span></button>
|
||||||
<div class="toolbar-dropdown">
|
<div class="toolbar-dropdown">
|
||||||
<div class="toolbar-dropdown-header">Decide</div>
|
<div class="toolbar-dropdown-header">Decide</div>
|
||||||
<button id="new-choice-vote" title="Poll">☑ Poll</button>
|
<button id="new-choice-vote" title="Poll" data-requires-module="rchoices">☑ Poll</button>
|
||||||
<button id="new-choice-rank" title="Ranking">📊 Ranking</button>
|
<button id="new-choice-rank" title="Ranking" data-requires-module="rchoices">📊 Ranking</button>
|
||||||
<button id="new-choice-spider" title="Scoring">🕸 Scoring</button>
|
<button id="new-choice-spider" title="Scoring" data-requires-module="rchoices">🕸 Scoring</button>
|
||||||
<button id="new-spider-3d" title="3D Spider">📊 3D Spider</button>
|
<button id="new-spider-3d" title="3D Spider" data-requires-module="rchoices">📊 3D Spider</button>
|
||||||
<button id="new-conviction" title="Conviction">⏳ Conviction</button>
|
<button id="new-conviction" title="Conviction" data-requires-module="rchoices">⏳ Conviction</button>
|
||||||
<button id="new-token" title="Token">🪙 Token</button>
|
<button id="new-token" title="Token" data-requires-module="rwallet">🪙 Token</button>
|
||||||
<button id="new-commitment-pool" title="Commitment Pool" data-requires-module="rtime">🧺 Commitments</button>
|
<button id="new-commitment-pool" title="Commitment Pool" data-requires-module="rtime">🧺 Commitments</button>
|
||||||
<button id="new-task-request" title="Task Request" data-requires-module="rtime">📋 Task Request</button>
|
<button id="new-task-request" title="Task Request" data-requires-module="rtime">📋 Task Request</button>
|
||||||
<button id="new-multisig-email" title="Multi-Sig Email">✉️ Multi-Sig Email</button>
|
<button id="new-multisig-email" title="Multi-Sig Email">✉️ Multi-Sig Email</button>
|
||||||
|
|
@ -2111,7 +2111,7 @@
|
||||||
<div class="toolbar-dropdown-header">Spend</div>
|
<div class="toolbar-dropdown-header">Spend</div>
|
||||||
<button id="embed-wallet" title="rWallet" data-requires-module="rwallet">💰 rWallet</button>
|
<button id="embed-wallet" title="rWallet" data-requires-module="rwallet">💰 rWallet</button>
|
||||||
<button id="embed-flows" title="rFlows" data-requires-module="rflows">🌊 rFlows</button>
|
<button id="embed-flows" title="rFlows" data-requires-module="rflows">🌊 rFlows</button>
|
||||||
<button id="new-tx-builder" title="Transaction Builder">🔐 Tx Builder</button>
|
<button id="new-tx-builder" title="Transaction Builder" data-requires-module="rwallet">🔐 Tx Builder</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -2575,6 +2575,15 @@
|
||||||
const FolkRApp = customElements.get("folk-rapp");
|
const FolkRApp = customElements.get("folk-rapp");
|
||||||
if (FolkRApp?.setEnabledModules) FolkRApp.setEnabledModules(enabledIds);
|
if (FolkRApp?.setEnabledModules) FolkRApp.setEnabledModules(enabledIds);
|
||||||
});
|
});
|
||||||
|
// Initialize rTime shape disabled states
|
||||||
|
customElements.whenDefined("folk-commitment-pool").then(() => {
|
||||||
|
const cls = customElements.get("folk-commitment-pool");
|
||||||
|
if (cls?.setEnabledModules) cls.setEnabledModules(enabledIds);
|
||||||
|
});
|
||||||
|
customElements.whenDefined("folk-task-request").then(() => {
|
||||||
|
const cls = customElements.get("folk-task-request");
|
||||||
|
if (cls?.setEnabledModules) cls.setEnabledModules(enabledIds);
|
||||||
|
});
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
|
||||||
// React to runtime module toggling from app switcher
|
// React to runtime module toggling from app switcher
|
||||||
|
|
@ -2591,6 +2600,12 @@
|
||||||
const FolkRApp = customElements.get("folk-rapp");
|
const FolkRApp = customElements.get("folk-rapp");
|
||||||
if (FolkRApp?.setEnabledModules) FolkRApp.setEnabledModules(enabledModules);
|
if (FolkRApp?.setEnabledModules) FolkRApp.setEnabledModules(enabledModules);
|
||||||
|
|
||||||
|
// Update rTime shape disabled states
|
||||||
|
const FolkCommitmentPool = customElements.get("folk-commitment-pool");
|
||||||
|
if (FolkCommitmentPool?.setEnabledModules) FolkCommitmentPool.setEnabledModules(enabledModules);
|
||||||
|
const FolkTaskRequest = customElements.get("folk-task-request");
|
||||||
|
if (FolkTaskRequest?.setEnabledModules) FolkTaskRequest.setEnabledModules(enabledModules);
|
||||||
|
|
||||||
document.querySelectorAll("[data-requires-module]").forEach(el => {
|
document.querySelectorAll("[data-requires-module]").forEach(el => {
|
||||||
el.style.display = enabledSet.has(el.dataset.requiresModule) ? "" : "none";
|
el.style.display = enabledSet.has(el.dataset.requiresModule) ? "" : "none";
|
||||||
});
|
});
|
||||||
|
|
@ -2790,6 +2805,13 @@
|
||||||
shapeRegistry.register("folk-holon", FolkHolon);
|
shapeRegistry.register("folk-holon", FolkHolon);
|
||||||
shapeRegistry.register("folk-holon-browser", FolkHolonBrowser);
|
shapeRegistry.register("folk-holon-browser", FolkHolonBrowser);
|
||||||
|
|
||||||
|
// Wire shape→module affiliations from module declarations
|
||||||
|
for (const mod of window.__rspaceAllModules || []) {
|
||||||
|
for (const tag of mod.canvasShapes || []) {
|
||||||
|
shapeRegistry.setModule(tag, mod.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Zoom and pan state — declared early to avoid TDZ errors
|
// Zoom and pan state — declared early to avoid TDZ errors
|
||||||
// (event handlers reference these before awaits yield execution)
|
// (event handlers reference these before awaits yield execution)
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue