diff options
Diffstat (limited to 'lib/utils.ts')
| -rw-r--r-- | lib/utils.ts | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/lib/utils.ts b/lib/utils.ts index 33b5a0c2..cb15e830 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -194,4 +194,84 @@ export function formatBytes(bytes: number, decimals: number = 2): string { const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i] -}
\ No newline at end of file +} + +export async function copyTextToClipboard(text: string) { + // 안전 가드: 브라우저 & API 지원 체크 + if (typeof navigator !== "undefined" && navigator.clipboard) { + try { + await navigator.clipboard.writeText(text) + return true + } catch { + /* fall through to fallback */ + } + } + + // --- Fallback (execCommand) --- + try { + const textarea = document.createElement("textarea") + textarea.value = text + textarea.style.position = "fixed" // iOS + textarea.style.opacity = "0" + document.body.appendChild(textarea) + textarea.focus() + textarea.select() + const ok = document.execCommand("copy") + document.body.removeChild(textarea) + return ok + } catch { + return false + } +} + +export function formatHtml(html: string): string { + if (!html.trim()) return html; + + // 한 줄짜리 HTML인지 확인 + const lineCount = html.split('\n').length; + const tagCount = (html.match(/<[^>]+>/g) || []).length; + + // 태그가 많은데 줄이 적으면 포맷팅 필요 + if (tagCount <= 3 || lineCount >= tagCount / 2) { + return html; // 이미 포맷팅된 것으로 보임 + } + + let formatted = html + // 태그 앞뒤 공백 정리 + .replace(/>\s*</g, '><') + // 블록 요소들 앞에 줄바꿈 + .replace(/(<\/?(div|p|h[1-6]|table|tr|td|th|ul|ol|li|header|footer|section|article)[^>]*>)/gi, '\n$1') + // 자체 닫는 태그 뒤에 줄바꿈 + .replace(/(<(br|hr|img|input)[^>]*\/?>)/gi, '$1\n') + // 여러 줄바꿈을 하나로 + .replace(/\n+/g, '\n') + .trim(); + + // 간단한 들여쓰기 + const lines = formatted.split('\n'); + let indent = 0; + const result: string[] = []; + + for (let line of lines) { + line = line.trim(); + if (!line) continue; + + // 닫는 태그면 들여쓰기 감소 + if (line.startsWith('</')) { + indent = Math.max(0, indent - 2); + } + + result.push(' '.repeat(indent) + line); + + // 여는 태그면 들여쓰기 증가 + if (line.startsWith('<') && !line.startsWith('</') && !line.endsWith('/>')) { + const voidTags = ['br', 'hr', 'img', 'input', 'meta', 'link']; + const tagName = line.match(/<(\w+)/)?.[1]?.toLowerCase(); + if (tagName && !voidTags.includes(tagName) && !line.includes(`</${tagName}>`)) { + indent += 2; + } + } + } + + return result.join('\n'); +} |
