🎉 Initial commit: PiAgentUI — AI Agent Desktop & Web UI
Build & Push Frontend Docker Image / build-and-push (push) Canceled after 0s
Build Validation / validate (push) Canceled after 6s
Build & Push Backend Docker Image / build-and-push (push) Failing after 10m10s
Build & Push Gateway Docker Image / build-and-push (push) Failing after 10m9s
Build & Push Frontend Docker Image / build-and-push (push) Canceled after 0s
Build Validation / validate (push) Canceled after 6s
Build & Push Backend Docker Image / build-and-push (push) Failing after 10m10s
Build & Push Gateway Docker Image / build-and-push (push) Failing after 10m9s
A professional workspace for AI coding agents with: - Real-time streaming chat with SSE - Session management (fork, clone, tree navigation) - MCP tools browser & metadata panel - Skills browser (global, project, package sources) - Voice input & STT provider configuration - Integrated terminal & file explorer - Model selector with capability metadata - Keyboard-first UI with command palette - 8 languages (en, pt-BR, es, ja, zh-CN, ar, bn, hi) - Tauri 2 desktop app - React 19 + TypeScript + Vite 8 + Tailwind CSS 4 - Pi Agent extension runtime Originally forked from OpenCodeUI, now an independent project. License: GPL-3.0-only
This commit is contained in:
commit
b3489e1ede
983 files changed
+158475
No files matched your search
@@ -0,0 +1,65 @@
|
||||
function fallbackCopyText(text: string): boolean {
|
||||
if (typeof document === 'undefined' || !document.body) {
|
||||
return false
|
||||
}
|
||||
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = text
|
||||
textArea.setAttribute('readonly', '')
|
||||
textArea.setAttribute('aria-hidden', 'true')
|
||||
textArea.style.position = 'fixed'
|
||||
textArea.style.top = '0'
|
||||
textArea.style.left = '-9999px'
|
||||
textArea.style.opacity = '0'
|
||||
textArea.style.pointerEvents = 'none'
|
||||
|
||||
const selection = document.getSelection()
|
||||
const savedRanges = selection
|
||||
? Array.from({ length: selection.rangeCount }, (_, index) => selection.getRangeAt(index).cloneRange())
|
||||
: []
|
||||
const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null
|
||||
|
||||
document.body.appendChild(textArea)
|
||||
textArea.focus()
|
||||
textArea.select()
|
||||
textArea.setSelectionRange(0, text.length)
|
||||
|
||||
const copied = typeof document.execCommand === 'function' && document.execCommand('copy')
|
||||
|
||||
document.body.removeChild(textArea)
|
||||
|
||||
if (selection) {
|
||||
selection.removeAllRanges()
|
||||
savedRanges.forEach(range => selection.addRange(range))
|
||||
}
|
||||
activeElement?.focus()
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
export async function copyTextToClipboard(text: string): Promise<void> {
|
||||
let clipboardError: unknown
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
return
|
||||
} catch (error) {
|
||||
clipboardError = error
|
||||
}
|
||||
}
|
||||
|
||||
if (fallbackCopyText(text)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw clipboardError instanceof Error ? clipboardError : new Error('Failed to copy text to clipboard')
|
||||
}
|
||||
|
||||
export async function readTextFromClipboard(): Promise<string> {
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard?.readText) {
|
||||
return navigator.clipboard.readText()
|
||||
}
|
||||
|
||||
throw new Error('Clipboard read API is not available')
|
||||
}
|
||||
Reference in new issue
Block a user