Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/entities/http-dump/ui/preview-card/preview-card.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { copyTextToClipboard } from '@/shared/lib/clipboard'
import type { NormalizedEvent } from '@/shared/types'
import { PreviewCard, IconSvg } from '@/shared/ui'
import type { HttpDump } from '../../types'
Expand Down Expand Up @@ -78,7 +79,7 @@ const buildCurl = (): string => {

const copyCurl = () => {
isCopied.value = true
navigator.clipboard.writeText(buildCurl()).catch(console.error)
copyTextToClipboard(buildCurl()).catch(console.error)
setTimeout(() => {
isCopied.value = false
}, 1500)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { copyTextToClipboard } from '@/shared/lib/clipboard'
import { useProfiler } from '../../lib'
import type { ProfilerSummary } from '../../types'

Expand Down Expand Up @@ -29,7 +30,7 @@ const toggle = () => {
const copied = ref('')

const selectFunction = (fn: string) => {
navigator.clipboard.writeText(fn).then(() => {
copyTextToClipboard(fn).then(() => {
copied.value = fn
setTimeout(() => {
copied.value = ''
Expand Down
44 changes: 44 additions & 0 deletions src/shared/lib/clipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Clipboard utilities with fallback for non-secure (HTTP) contexts
* where navigator.clipboard API is not available.
*/

export function copyTextToClipboard(text: string): Promise<void> {
if (navigator.clipboard?.writeText) {
return navigator.clipboard.writeText(text)
}

return new Promise((resolve, reject) => {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()

try {
document.execCommand('copy')
resolve()
} catch (e) {
reject(e)
} finally {
document.body.removeChild(textarea)
}
})
}

export function copyBlobToClipboard(blob: Blob): Promise<void> {
if (navigator.clipboard?.write) {
return navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })])
}

// Fallback: convert image blob to a data URL and copy as text
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
copyTextToClipboard(reader.result as string).then(resolve, reject)
}
reader.onerror = () => reject(reader.error)
reader.readAsDataURL(blob)
})
}
17 changes: 16 additions & 1 deletion src/shared/lib/vendor/sf-dumper/dumper.js
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,22 @@ export function SfdumpWrap(doc) {
showSearch()
} else if (action === 'copy') {
var text = root.innerText || root.textContent || ''
if (navigator.clipboard) navigator.clipboard.writeText(text.trim())
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text.trim())
} else {
var textarea = document.createElement('textarea')
textarea.value = text.trim()
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
document.execCommand('copy')
} catch (e) {
/* noop */
}
document.body.removeChild(textarea)
}
} else if (depth) {
collapseAll(root)
expandToDepth(root, parseInt(depth, 10))
Expand Down
4 changes: 2 additions & 2 deletions src/shared/ui/code-snippet/code-snippet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import highlightPlugin from '@highlightjs/vue-plugin'
import isString from 'lodash.isstring'
import { ref, computed } from 'vue'
import { copyTextToClipboard } from '../../lib/clipboard'
import { logger } from '../../lib/logger'
import { IconSvg } from '../icon-svg'

Expand All @@ -26,8 +27,7 @@ const normalizedCode = computed(() =>
const copyCode = (): void => {
isCopied.value = true

navigator.clipboard
.writeText(normalizedCode.value)
copyTextToClipboard(normalizedCode.value)
.then(() => {
setTimeout(() => {
isCopied.value = false
Expand Down
3 changes: 2 additions & 1 deletion src/shared/ui/preview-card/preview-card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import download from 'downloadjs'
import { toBlob, toPng } from 'html-to-image'
import { ref, computed, onBeforeMount, onMounted } from 'vue'
import { copyBlobToClipboard } from '../../lib/clipboard'
import { REST_API_URL } from '../../lib/io'
import { logger } from '../../lib/logger'
import { useEvents } from '../../lib/use-events'
Expand Down Expand Up @@ -112,7 +113,7 @@ const copyCode = () => {
toBlob(eventRef.value as HTMLElement)
.then((blob) => {
if (blob) {
navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })])
copyBlobToClipboard(blob)
}
})
.catch((e) => logger(['UI: Operation failed', e]))
Expand Down
13 changes: 6 additions & 7 deletions src/widgets/ui/layout-preview-events/layout-preview-events.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { storeToRefs } from 'pinia'
import { computed, ref, watch, watchEffect } from 'vue'
import { useRouter } from 'vue-router'
import { PAGE_TYPES } from '@/shared/constants'
import { copyTextToClipboard, copyBlobToClipboard } from '@/shared/lib/clipboard'
import { useEvents } from '@/shared/lib/use-events'
import { useKeyboardNav, showToast, screenshotingEventId } from '@/shared/lib/use-keyboard-nav'
import { useEventsStore } from '@/shared/stores'
Expand All @@ -20,7 +21,7 @@ type Props = {

const props = withDefaults(defineProps<Props>(), {
title: '',
favoritesOnly: false,
favoritesOnly: false
})

const { events, cachedEvents, lockedIds } = useEvents()
Expand Down Expand Up @@ -117,9 +118,9 @@ const { focusedId } = useKeyboardNav(eventUuids, {
onCopyPayload: (id) => {
const event = visibleEvents.value.find((e) => e.uuid === id)
if (event) {
navigator.clipboard
.writeText(JSON.stringify(event.payload, null, 2))
.then(() => showToast('Payload copied'))
copyTextToClipboard(JSON.stringify(event.payload, null, 2)).then(() =>
showToast('Payload copied')
)
}
},
onScreenshot: (id) => {
Expand All @@ -131,9 +132,7 @@ const { focusedId } = useKeyboardNav(eventUuids, {
toBlob(el as HTMLElement)
.then((blob) => {
if (blob) {
navigator.clipboard
.write([new ClipboardItem({ [blob.type]: blob })])
.then(() => showToast('Screenshot copied'))
copyBlobToClipboard(blob).then(() => showToast('Screenshot copied'))
}
})
.finally(() => {
Expand Down
Loading