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
86 changes: 54 additions & 32 deletions apps/docs/src/components/doc/CopyPage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ const translations = {
</button>
</div>

<span class="copy-page-feedback sr-only" aria-live="polite" aria-atomic="true"></span>
<span class="copy-page-feedback sr-only" role="status" aria-live="polite" aria-atomic="true"></span>
</div>

<script is:inline define:vars={{ translations, editUrl: editUrl?.toString() ?? '' }}>
Expand Down Expand Up @@ -157,6 +157,8 @@ const translations = {

// Copy page as markdown
document.querySelectorAll('.copy-markdown').forEach((btn) => {
let resetTimer

btn.addEventListener('click', async (e) => {
e.preventDefault()
e.stopPropagation()
Expand All @@ -172,6 +174,44 @@ const translations = {
const container = btn.closest('.copy-page-container')
const feedback = container?.querySelector('.copy-page-feedback')

function updateFeedback(message) {
if (feedback instanceof HTMLElement) {
feedback.textContent = message
}
}

function resetFeedback() {
if (resetTimer) {
window.clearTimeout(resetTimer)
}

resetTimer = window.setTimeout(() => {
if (title) {
title.textContent = originalText || translations.copyPage
}
updateFeedback('')
}, 2000)
}

function fallbackCopy(value) {
const textarea = document.createElement('textarea')
textarea.value = value
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.focus()
textarea.select()

const legacyCopy = Reflect.get(document, 'execCommand')
const copied = typeof legacyCopy === 'function' ? legacyCopy.call(document, 'copy') : false

document.body.removeChild(textarea)

if (!copied) {
throw new Error('Fallback copy method also failed')
}
}
Comment thread
riderx marked this conversation as resolved.

try {
// Disable button during operation
btn.disabled = true
Expand Down Expand Up @@ -205,49 +245,31 @@ const translations = {
throw new Error('Failed to fetch markdown from any source')
}

// Copy to clipboard using the most reliable method
try {
await navigator.clipboard.writeText(content)
console.log('Copied to clipboard successfully')
} catch (clipboardError) {
console.error('Clipboard API failed, trying fallback:', clipboardError)

// Fallback method using textarea
const textarea = document.createElement('textarea')
textarea.value = content
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.focus()
textarea.select()
// @ts-expect-error execCommand is deprecated but used as fallback for older browsers
const success = document.execCommand('copy')
document.body.removeChild(textarea)

if (!success) {
throw new Error('Fallback copy method also failed')
if (!navigator.clipboard?.writeText) {
fallbackCopy(content)
} else {
try {
await navigator.clipboard.writeText(content)
} catch (clipboardError) {
console.error('Clipboard API failed, trying fallback:', clipboardError)
fallbackCopy(content)
}
}
console.log('Copied to clipboard successfully')

Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Show success feedback
updateFeedback(translations.copied)
if (title) {
title.textContent = translations.copied
if (feedback) feedback.textContent = translations.copied
setTimeout(() => {
title.textContent = originalText || translations.copyPage
if (feedback) feedback.textContent = ''
}, 2000)
}
resetFeedback()
} catch (error) {
console.error('Failed to copy:', error)
updateFeedback(translations.failedToCopy)
if (title) {
title.textContent = translations.failedToCopy
if (feedback) feedback.textContent = translations.failedToCopy
setTimeout(() => {
title.textContent = originalText || translations.copyPage
if (feedback) feedback.textContent = ''
}, 2000)
}
resetFeedback()
} finally {
btn.disabled = false
}
Expand Down
8 changes: 3 additions & 5 deletions apps/web/src/content.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { glob } from 'astro/loaders'
import { defineCollection, z } from 'astro:content'
import { z } from 'astro/zod'
import { defineCollection } from 'astro:content'
import { locales, type Locales } from './services/locale'

const localeSchema = z.custom<Locales>(
(value) => typeof value === 'string' && locales.includes(value as Locales),
{ message: 'Invalid locale' },
)
const localeSchema = z.custom<Locales>((value) => typeof value === 'string' && locales.includes(value as Locales), { message: 'Invalid locale' })

const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: 'src/content/blog', generateId: ({ entry }) => entry }),
Expand Down
20 changes: 16 additions & 4 deletions apps/web/src/pages/tools/ios-udid-finder/result.astro
Original file line number Diff line number Diff line change
Expand Up @@ -142,22 +142,34 @@ const signingDocsHref = getRelativeLocaleUrl(Astro.locals.locale, 'docs/cli/clou
}

async function copyText(value: string): Promise<void> {
try {
await navigator.clipboard.writeText(value)
} catch {
const fallbackCopy = (): void => {
const helper = document.createElement('textarea')
helper.value = value
helper.setAttribute('readonly', 'true')
helper.style.position = 'absolute'
helper.style.opacity = '0'
document.body.append(helper)
helper.select()
const copied = document.execCommand('copy')

const legacyCopy = Reflect.get(document, 'execCommand')
const copied = typeof legacyCopy === 'function' ? legacyCopy.call(document, 'copy') : false

helper.remove()
if (!copied) {
throw new Error('Copy failed')
}
}

if (!navigator.clipboard?.writeText) {
fallbackCopy()
return
}

try {
await navigator.clipboard.writeText(value)
} catch {
fallbackCopy()
}
}

function bindCopyButtons(): void {
Expand Down