fix: canvas tools place shapes exactly where the ghost placeholder shows
- newShape() with explicit position now places directly at click point instead of routing through findFreePosition spiral which could nudge the shape away from the cursor - Sticky note converted to setPendingTool so it shows dotted placeholder before placement instead of instantly spawning at viewport center - Feed tool converted to setPendingTool with __postCreate for the same click-to-place UX - Removed wb-sticky from mobile keepOpen list since it's now a placement tool that should close the menu Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
47b585665d
commit
8d77c6eee8
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Reference in New Issue