diff --git a/website/canvas.html b/website/canvas.html
index 9fecdb5a..cd129fe0 100644
--- a/website/canvas.html
+++ b/website/canvas.html
@@ -2053,8 +2053,9 @@
}
});
- // Create a shape, position it without overlapping others, add to canvas, and register for sync
- // atPosition: optional { x, y } in canvas coordinates to place near
+ // Create a shape, add to canvas, and register for sync.
+ // atPosition: optional { x, y } in canvas coordinates — places shape centered there exactly.
+ // Without atPosition, uses findFreePosition to auto-place without overlapping.
function newShape(tagName, props = {}, atPosition) {
const id = `shape-${Date.now()}-${++shapeCounter}`;
const defaults = SHAPE_DEFAULTS[tagName] || { width: 300, height: 200 };
@@ -2062,9 +2063,14 @@
const shape = document.createElement(tagName);
shape.id = id;
- const pos = atPosition
- ? findFreePosition(defaults.width, defaults.height, atPosition.x, atPosition.y)
- : findFreePosition(defaults.width, defaults.height);
+ let pos;
+ if (atPosition) {
+ // Explicit click-to-place: honor exact position (centered on click)
+ pos = { x: atPosition.x - defaults.width / 2, y: atPosition.y - defaults.height / 2 };
+ } else {
+ // Auto-place: find free spot near viewport center
+ pos = findFreePosition(defaults.width, defaults.height);
+ }
shape.x = pos.x;
shape.y = pos.y;
shape.width = defaults.width;
@@ -2280,48 +2286,49 @@
};
const flowKind = moduleFlowKinds[sourceModule] || "data";
- const shape = newShape("folk-feed", {
+ setPendingTool("folk-feed", {
sourceModule,
sourceLayer: "layer-" + sourceModule,
feedId: "",
flowKind,
maxItems: 10,
refreshInterval: 30000,
+ __postCreate: (shape) => {
+ // Auto-register a LayerFlow in Automerge if layers exist
+ if (sync.getLayers) {
+ const layers = sync.getLayers();
+ const currentLayer = layers.find(l => l.moduleId === "rspace") || layers[0];
+ const sourceLayer = layers.find(l => l.moduleId === sourceModule);
+
+ if (currentLayer && sourceLayer) {
+ const flowId = `flow-auto-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
+ sync.addFlow({
+ id: flowId,
+ kind: flowKind,
+ sourceLayerId: sourceLayer.id,
+ targetLayerId: currentLayer.id,
+ targetShapeId: shape.id,
+ label: sourceModule + " feed",
+ strength: 0.5,
+ active: true,
+ });
+ }
+
+ // Also ensure source module has a layer (add if missing)
+ if (!sourceLayer) {
+ sync.addLayer({
+ id: "layer-" + sourceModule,
+ moduleId: sourceModule,
+ label: sourceModule,
+ order: layers.length,
+ color: "",
+ visible: true,
+ createdAt: Date.now(),
+ });
+ }
+ }
+ },
});
-
- // Auto-register a LayerFlow in Automerge if layers exist
- if (shape && sync.getLayers) {
- const layers = sync.getLayers();
- const currentLayer = layers.find(l => l.moduleId === "rspace") || layers[0];
- const sourceLayer = layers.find(l => l.moduleId === sourceModule);
-
- if (currentLayer && sourceLayer) {
- const flowId = `flow-auto-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
- sync.addFlow({
- id: flowId,
- kind: flowKind,
- sourceLayerId: sourceLayer.id,
- targetLayerId: currentLayer.id,
- targetShapeId: shape.id,
- label: sourceModule + " feed",
- strength: 0.5,
- active: true,
- });
- }
-
- // Also ensure source module has a layer (add if missing)
- if (!sourceLayer) {
- sync.addLayer({
- id: "layer-" + sourceModule,
- moduleId: sourceModule,
- label: sourceModule,
- order: layers.length,
- color: "",
- visible: true,
- createdAt: Date.now(),
- });
- }
- }
});
// Arrow connection mode
@@ -2411,18 +2418,17 @@
document.getElementById("wb-pencil")?.addEventListener("click", () => setWbTool("pencil"));
document.getElementById("wb-sticky")?.addEventListener("click", () => {
- // Create a sticky note as a markdown shape with yellow background
setWbTool(null);
- const shape = newShape("folk-markdown", {
- content: "# Sticky Note\n\nClick to edit..."
+ setPendingTool("folk-markdown", {
+ content: "# Sticky Note\n\nClick to edit...",
+ __postCreate: (shape) => {
+ shape.width = 200;
+ shape.height = 200;
+ shape.style.background = "#fef08a";
+ shape.style.borderRadius = "4px";
+ shape.style.boxShadow = "2px 2px 8px rgba(0,0,0,0.15)";
+ },
});
- if (shape) {
- shape.width = 200;
- shape.height = 200;
- shape.style.background = "#fef08a";
- shape.style.borderRadius = "4px";
- shape.style.boxShadow = "2px 2px 8px rgba(0,0,0,0.15)";
- }
});
document.getElementById("wb-rect")?.addEventListener("click", () => setWbTool("rect"));
document.getElementById("wb-circle")?.addEventListener("click", () => setWbTool("circle"));
@@ -2694,7 +2700,7 @@
if (!btn) return;
// Keep open for connect, memory, group toggles, collapse, whiteboard tools
const keepOpen = ["new-arrow", "toggle-memory", "toggle-theme", "zoom-in", "zoom-out", "reset-view", "toolbar-collapse",
- "wb-pencil", "wb-sticky", "wb-rect", "wb-circle", "wb-line", "wb-eraser"];
+ "wb-pencil", "wb-rect", "wb-circle", "wb-line", "wb-eraser"];
if (btn.classList.contains("toolbar-group-toggle")) return;
if (!keepOpen.includes(btn.id)) {
toolbarEl.classList.remove("mobile-open");