mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-15 09:04:07 -08:00
dcf12867b3
Fixes: - Refactor connection snapping when dragging and enable it also for non-main connection types - Fix propagation of errors from sub-nodes - Fix chat scrolling when sending/receiving messages - Prevent empty chat messages - Fix sub-node selected styles - Fix output names text overflow Usability improvements: - Auto-add manual chat trigger for agents & chain nodes - Various labels and description updates - Make the output parser input optional for Basic LLM Chain - Summarization Chain V2 with a simplified document loader & text chunking mode #### How to test the change: Example workflow showcasing different operation mode of the new summarization chain: [Summarization_V2.json](https://github.com/n8n-io/n8n/files/13599901/Summarization_V2.json) ## Issues fixed Include links to Github issue or Community forum post or **Linear ticket**: > Important in order to close automatically and provide context to reviewers - https://www.notion.so/n8n/David-Langchain-Posthog-notes-7a9294938420403095f4508f1a21d31d - https://linear.app/n8n/issue/N8N-7070/ux-fixes-batch - https://linear.app/n8n/issue/N8N-7071/ai-sub-node-bugs ## Review / Merge checklist - [x] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [x] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [ ] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. > > *(internal)* You can use Slack commands to trigger [e2e tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227) or [deploy test instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce) or [deploy early access version on Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e). --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Elias Meire <elias@meire.dev>
80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
/**
|
|
* Creates event listeners for `data-action` attribute to allow for actions to be called from locale without using
|
|
* unsafe onclick attribute
|
|
*/
|
|
import { reactive, computed, onMounted, onUnmounted } from 'vue';
|
|
import { globalLinkActionsEventBus } from '@/event-bus';
|
|
|
|
const state = reactive({
|
|
customActions: {} as Record<string, Function>,
|
|
delegatedClickHandler: null as null | ((e: MouseEvent) => void),
|
|
});
|
|
|
|
export default () => {
|
|
function registerCustomAction({ key, action }: { key: string; action: Function }) {
|
|
state.customActions[key] = action;
|
|
}
|
|
function unregisterCustomAction(key: string) {
|
|
const { [key]: _, ...rest } = state.customActions;
|
|
state.customActions = rest;
|
|
}
|
|
function getElementAttributes(element: Element) {
|
|
const attributesObject: Record<string, string> = {};
|
|
|
|
for (let i = 0; i < element.attributes.length; i++) {
|
|
const attr = element.attributes[i];
|
|
if (attr.name.startsWith('data-action-parameter-')) {
|
|
attributesObject[attr.name.replace('data-action-parameter-', '')] = attr.value;
|
|
}
|
|
}
|
|
return attributesObject;
|
|
}
|
|
|
|
function delegateClick(e: MouseEvent) {
|
|
const clickedElement = e.target;
|
|
if (!(clickedElement instanceof Element) || clickedElement.tagName !== 'A') return;
|
|
|
|
const actionAttribute = clickedElement.getAttribute('data-action');
|
|
if (actionAttribute && typeof availableActions.value[actionAttribute] === 'function') {
|
|
e.preventDefault();
|
|
// Extract and parse `data-action-parameter-` attributes and pass them to the action
|
|
const elementAttributes = getElementAttributes(clickedElement);
|
|
availableActions.value[actionAttribute](elementAttributes);
|
|
}
|
|
}
|
|
|
|
function reload() {
|
|
if (window.top) {
|
|
window.top.location.reload();
|
|
} else {
|
|
window.location.reload();
|
|
}
|
|
}
|
|
|
|
const availableActions = computed<{ [key: string]: Function }>(() => ({
|
|
reload,
|
|
...state.customActions,
|
|
}));
|
|
|
|
onMounted(() => {
|
|
if (state.delegatedClickHandler) return;
|
|
|
|
state.delegatedClickHandler = delegateClick;
|
|
window.addEventListener('click', delegateClick);
|
|
|
|
globalLinkActionsEventBus.on('registerGlobalLinkAction', registerCustomAction);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('click', delegateClick);
|
|
state.delegatedClickHandler = null;
|
|
|
|
globalLinkActionsEventBus.off('registerGlobalLinkAction', registerCustomAction);
|
|
});
|
|
|
|
return {
|
|
registerCustomAction,
|
|
unregisterCustomAction,
|
|
};
|
|
};
|