Implement chopping

This commit is contained in:
DrHaid
2025-12-13 20:59:48 +01:00
parent 5a8dc3b057
commit 40872adafe
3 changed files with 77 additions and 7 deletions

View File

@@ -1,6 +1,7 @@
import { StickyNote, Image } from "@mirohq/websdk-types";
import { getStickiesToChopInTree } from "./todo-tree-helper";
export async function init() {
export const init = async () => {
miro.board.ui.on('icon:click', async () => {
await miro.board.ui.openPanel({ url: 'app.html' });
});
@@ -42,19 +43,18 @@ const playAxeAnimation = async ({axe, stickyNote, onChopped}: AxeAnimationArgs)
for (let i = 0; i < 3; i++) {
axe.rotation = 90;
await axe.sync();
await new Promise(resolve => setTimeout(resolve, 200));
await new Promise(resolve => setTimeout(resolve, 300));
axe.rotation = 0;
await axe.sync();
await new Promise(resolve => setTimeout(resolve, 200));
await new Promise(resolve => setTimeout(resolve, 300));
}
onChopped();
}
async function postInfoNotification(message: string) {
const postInfoNotification = async (message: string) =>
await miro.board.notifications.showInfo(message);
}
const handleChoppingAction = async ({items}: {items:StickyNote[]}) => {
const axe = await findExistingAxe();
@@ -73,15 +73,22 @@ const handleChoppingAction = async ({items}: {items:StickyNote[]}) => {
playAxeAnimation({
axe,
stickyNote: target,
onChopped: async () => await miro.board.remove(target)
onChopped: () => removeTree(target)
});
};
const removeTree = async (root: StickyNote) => {
const stickiesToRemove = await getStickiesToChopInTree(root, ["light_green", "red"]);
for (const item of stickiesToRemove) {
await miro.board.remove(item);
}
};
export const findExistingAxe = async () => {
const items = await miro.board.get({ type: 'image' });
const axe = items.find(item => item.title === 'todo-tree-axe');
return axe;
};
init();

58
src/todo-tree-helper.ts Normal file
View File

@@ -0,0 +1,58 @@
import { Connector, Item, StickyNote } from "@mirohq/websdk-types";
const getTypedMiroItem = async <T extends Item>(id: string, type: T['type']): Promise<T | undefined> => {
const item = await miro.board.getById(id);
return item?.type === type ? item as T : undefined;
};
interface ConnectorDirection {
parentId?: string;
childId?: string;
}
const getConnectorDirection = (connector: Connector): ConnectorDirection | undefined => {
const hasEndArrow = connector.style.endStrokeCap !== 'none';
const hasStartArrow = connector.style.startStrokeCap !== 'none';
if (hasEndArrow && !hasStartArrow) {
return { parentId: connector.start?.item, childId: connector.end?.item };
}
if (hasStartArrow && !hasEndArrow) {
return { parentId: connector.end?.item, childId: connector.start?.item };
}
return;
};
export const getStickiesToChopInTree = async (root: StickyNote, removableColors: string[]): Promise<StickyNote[]> => {
if (!removableColors.includes(root.style.fillColor)) return [];
const toRemove: StickyNote[] = [];
const visitedIds = new Set<string>();
const traverseTree = async (node: StickyNote) => {
if (visitedIds.has(node.id)){
// prevent infinite loops of circular arrows
return;
}
visitedIds.add(node.id);
for (const connectorId of node.connectorIds || []) {
const connector = await getTypedMiroItem<Connector>(connectorId, 'connector');
if (!connector) continue;
const direction = getConnectorDirection(connector);
if (!direction || !direction.childId || direction.parentId !== node.id) continue;
const childSticky = await getTypedMiroItem<StickyNote>(direction.childId, 'sticky_note');
if (childSticky) {
if (removableColors.includes(childSticky.style.fillColor)) {
toRemove.push(childSticky);
}
await traverseTree(childSticky);
}
}
};
await traverseTree(root);
return toRemove;
};