mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Fix and enable copying to clipboard in PiP (#15632)
Co-authored-by: autologie <suguru@n8n.io>
This commit is contained in:
@@ -39,9 +39,10 @@ const props = withDefaults(
|
|||||||
const ndvStore = useNDVStore();
|
const ndvStore = useNDVStore();
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const nodeHelpers = useNodeHelpers();
|
const nodeHelpers = useNodeHelpers();
|
||||||
const clipboard = useClipboard();
|
|
||||||
const { activeNode } = ndvStore;
|
const { activeNode } = ndvStore;
|
||||||
const pinnedData = usePinnedData(activeNode);
|
const pinnedData = usePinnedData(activeNode);
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { computed, inject, onBeforeUnmount, onMounted, ref, unref } from 'vue';
|
import { inject, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
import { useClipboard as useClipboardCore, useThrottleFn } from '@vueuse/core';
|
import { useClipboard as useClipboardCore, useThrottleFn } from '@vueuse/core';
|
||||||
import { IsInPiPWindowSymbol } from '@/constants';
|
import { PiPWindowSymbol } from '@/constants';
|
||||||
|
|
||||||
type ClipboardEventFn = (data: string, event?: ClipboardEvent) => void;
|
type ClipboardEventFn = (data: string, event?: ClipboardEvent) => void;
|
||||||
|
|
||||||
export function useClipboard(
|
export function useClipboard({
|
||||||
options: {
|
onPaste: onPasteFn = () => {},
|
||||||
onPaste: ClipboardEventFn;
|
}: {
|
||||||
} = {
|
onPaste?: ClipboardEventFn;
|
||||||
onPaste() {},
|
} = {}) {
|
||||||
},
|
const pipWindow = inject(PiPWindowSymbol, ref<Window | undefined>());
|
||||||
) {
|
const { copy, copied, isSupported, text } = useClipboardCore({
|
||||||
const isInPiPWindow = inject(IsInPiPWindowSymbol, false);
|
navigator: pipWindow?.value?.navigator ?? window.navigator,
|
||||||
const { copy, copied, isSupported, text } = useClipboardCore({ legacy: true });
|
legacy: true,
|
||||||
|
});
|
||||||
|
|
||||||
const ignoreClasses = ['el-messsage-box', 'ignore-key-press-canvas'];
|
const ignoreClasses = ['el-messsage-box', 'ignore-key-press-canvas'];
|
||||||
const initialized = ref(false);
|
const initialized = ref(false);
|
||||||
|
|
||||||
const onPasteCallback = ref<ClipboardEventFn | null>(options.onPaste || null);
|
const onPasteCallback = ref<ClipboardEventFn | null>(onPasteFn || null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles copy/paste events
|
* Handles copy/paste events
|
||||||
@@ -74,9 +75,7 @@ export function useClipboard(
|
|||||||
return {
|
return {
|
||||||
copy,
|
copy,
|
||||||
copied,
|
copied,
|
||||||
// When the `copy()` method is invoked from inside of the document picture-in-picture (PiP) window, it throws the error "Document is not focused".
|
isSupported,
|
||||||
// Therefore, we disable copying features in the PiP window for now.
|
|
||||||
isSupported: computed(() => isSupported && !unref(isInPiPWindow)),
|
|
||||||
text,
|
text,
|
||||||
onPaste: onPasteCallback,
|
onPaste: onPasteCallback,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
CanvasNodeHandleInjectionData,
|
CanvasNodeHandleInjectionData,
|
||||||
CanvasNodeInjectionData,
|
CanvasNodeInjectionData,
|
||||||
} from '@/types';
|
} from '@/types';
|
||||||
import type { InjectionKey, MaybeRefOrGetter } from 'vue';
|
import type { InjectionKey, Ref } from 'vue';
|
||||||
|
|
||||||
export const MAX_WORKFLOW_SIZE = 1024 * 1024 * 16; // Workflow size limit in bytes
|
export const MAX_WORKFLOW_SIZE = 1024 * 1024 * 16; // Workflow size limit in bytes
|
||||||
export const MAX_EXPECTED_REQUEST_SIZE = 2048; // Expected maximum workflow request metadata (i.e. headers) size in bytes
|
export const MAX_EXPECTED_REQUEST_SIZE = 2048; // Expected maximum workflow request metadata (i.e. headers) size in bytes
|
||||||
@@ -914,9 +914,7 @@ export const CanvasKey = 'canvas' as unknown as InjectionKey<CanvasInjectionData
|
|||||||
export const CanvasNodeKey = 'canvasNode' as unknown as InjectionKey<CanvasNodeInjectionData>;
|
export const CanvasNodeKey = 'canvasNode' as unknown as InjectionKey<CanvasNodeInjectionData>;
|
||||||
export const CanvasNodeHandleKey =
|
export const CanvasNodeHandleKey =
|
||||||
'canvasNodeHandle' as unknown as InjectionKey<CanvasNodeHandleInjectionData>;
|
'canvasNodeHandle' as unknown as InjectionKey<CanvasNodeHandleInjectionData>;
|
||||||
export const IsInPiPWindowSymbol = 'IsInPipWindow' as unknown as InjectionKey<
|
export const PiPWindowSymbol = 'PiPWindow' as unknown as InjectionKey<Ref<Window | undefined>>;
|
||||||
MaybeRefOrGetter<boolean>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/** Auth */
|
/** Auth */
|
||||||
export const APP_MODALS_ELEMENT_ID = 'app-modals';
|
export const APP_MODALS_ELEMENT_ID = 'app-modals';
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
const locale = useI18n();
|
const locale = useI18n();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ async function copySessionId() {
|
|||||||
@click="emit('clickHeader')"
|
@click="emit('clickHeader')"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<N8nTooltip v-if="clipboard.isSupported.value && !isReadOnly">
|
<N8nTooltip v-if="clipboard.isSupported && !isReadOnly">
|
||||||
<template #content>
|
<template #content>
|
||||||
{{ sessionId }}
|
{{ sessionId }}
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ async function handleOpenNdv(treeNode: LogEntry) {
|
|||||||
@resizeend="onChatPanelResizeEnd"
|
@resizeend="onChatPanelResizeEnd"
|
||||||
>
|
>
|
||||||
<ChatMessagesPanel
|
<ChatMessagesPanel
|
||||||
|
:key="`canvas-chat-${currentSessionId}${isPoppedOut ? '-pip' : ''}`"
|
||||||
data-test-id="canvas-chat"
|
data-test-id="canvas-chat"
|
||||||
:is-open="isOpen"
|
:is-open="isOpen"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import { type IRunDataDisplayMode, type NodePanelType } from '@/Interface';
|
|||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { waitingNodeTooltip } from '@/utils/executionUtils';
|
import { waitingNodeTooltip } from '@/utils/executionUtils';
|
||||||
import { N8nLink, N8nText } from '@n8n/design-system';
|
import { N8nLink, N8nText } from '@n8n/design-system';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, inject, ref } from 'vue';
|
||||||
import { I18nT } from 'vue-i18n';
|
import { I18nT } from 'vue-i18n';
|
||||||
|
import { PiPWindowSymbol } from '@/constants';
|
||||||
|
|
||||||
const { title, logEntry, paneType } = defineProps<{
|
const { title, logEntry, paneType } = defineProps<{
|
||||||
title: string;
|
title: string;
|
||||||
@@ -18,6 +19,8 @@ const { title, logEntry, paneType } = defineProps<{
|
|||||||
const locale = useI18n();
|
const locale = useI18n();
|
||||||
const ndvStore = useNDVStore();
|
const ndvStore = useNDVStore();
|
||||||
|
|
||||||
|
const pipWindow = inject(PiPWindowSymbol, ref<Window | undefined>());
|
||||||
|
|
||||||
const displayMode = ref<IRunDataDisplayMode>(paneType === 'input' ? 'schema' : 'table');
|
const displayMode = ref<IRunDataDisplayMode>(paneType === 'input' ? 'schema' : 'table');
|
||||||
const isMultipleInput = computed(
|
const isMultipleInput = computed(
|
||||||
() => paneType === 'input' && (logEntry.runData?.source.length ?? 0) > 1,
|
() => paneType === 'input' && (logEntry.runData?.source.length ?? 0) > 1,
|
||||||
@@ -65,6 +68,7 @@ function handleChangeDisplayMode(value: IRunDataDisplayMode) {
|
|||||||
<RunData
|
<RunData
|
||||||
v-if="runDataProps"
|
v-if="runDataProps"
|
||||||
v-bind="runDataProps"
|
v-bind="runDataProps"
|
||||||
|
:key="`run-data${pipWindow ? '-pip' : ''}`"
|
||||||
:workflow="logEntry.workflow"
|
:workflow="logEntry.workflow"
|
||||||
:workflow-execution="logEntry.execution"
|
:workflow-execution="logEntry.execution"
|
||||||
:too-much-data-title="locale.baseText('ndv.output.tooMuchData.title')"
|
:too-much-data-title="locale.baseText('ndv.output.tooMuchData.title')"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IsInPiPWindowSymbol } from '@/constants';
|
import { PiPWindowSymbol } from '@/constants';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { applyThemeToBody } from '@/stores/ui.utils';
|
import { applyThemeToBody } from '@/stores/ui.utils';
|
||||||
import { useProvideTooltipAppendTo } from '@n8n/design-system/composables/useTooltipAppendTo';
|
import { useProvideTooltipAppendTo } from '@n8n/design-system/composables/useTooltipAppendTo';
|
||||||
@@ -90,7 +90,7 @@ export function usePiPWindow({
|
|||||||
// Copy over dynamic styles to PiP window to support lazily imported modules
|
// Copy over dynamic styles to PiP window to support lazily imported modules
|
||||||
observer.observe(document.head, { childList: true, subtree: true });
|
observer.observe(document.head, { childList: true, subtree: true });
|
||||||
|
|
||||||
provide(IsInPiPWindowSymbol, isPoppedOut);
|
provide(PiPWindowSymbol, pipWindow);
|
||||||
useProvideTooltipAppendTo(tooltipContainer);
|
useProvideTooltipAppendTo(tooltipContainer);
|
||||||
|
|
||||||
async function showPip() {
|
async function showPip() {
|
||||||
|
|||||||
Reference in New Issue
Block a user