Skip to content

Commit

Permalink
Improve copy-to-clipboard button and ui.clipboard for non-secure en…
Browse files Browse the repository at this point in the history
…vironments (#3636)

* hide copy-to-clipboard button if clipboard API is unavailable

* add documentation

* avoid KeyError when a JavaScript function returns `undefined`

* add warnings and documentation to `ui.clipboard`
  • Loading branch information
falkoschindler committed Aug 29, 2024
1 parent 6ea6db4 commit ed6a955
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
2 changes: 1 addition & 1 deletion nicegui/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def handle_event(self, msg: Dict) -> None:

def handle_javascript_response(self, msg: Dict) -> None:
"""Store the result of a JavaScript command."""
JavaScriptRequest.resolve(msg['request_id'], msg['result'])
JavaScriptRequest.resolve(msg['request_id'], msg.get('result'))

def safe_invoke(self, func: Union[Callable[..., Any], Awaitable]) -> None:
"""Invoke the potentially async function in the client context and catch any exceptions."""
Expand Down
6 changes: 6 additions & 0 deletions nicegui/elements/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def __init__(self, content: str = '', *, language: Optional[str] = 'python') ->
This element displays a code block with syntax highlighting.
In secure environments (HTTPS or localhost), a copy button is displayed to copy the code to the clipboard.
:param content: code to display
:param language: language of the code (default: "python")
"""
Expand All @@ -34,6 +36,10 @@ def __init__(self, content: str = '', *, language: Optional[str] = 'python') ->
self.markdown.on('scroll', self._handle_scroll)
timer(0.1, self._update_copy_button)

self.client.on_connect(lambda: self.client.run_javascript(f'''
if (!navigator.clipboard) getElement({self.copy_button.id}).$el.style.display = 'none';
'''))

async def show_checkmark(self) -> None:
"""Show a checkmark icon for 3 seconds."""
self.copy_button.props('icon=check')
Expand Down
29 changes: 26 additions & 3 deletions nicegui/functions/clipboard.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
from .. import json
from ..logging import log
from .javascript import run_javascript


async def read() -> str:
"""Read text from the clipboard."""
return await run_javascript('navigator.clipboard.readText()')
"""Read text from the clipboard.
Note: This function only works in secure contexts (HTTPS or localhost).
"""
result = await run_javascript('''
if (navigator.clipboard) {
return navigator.clipboard.readText()
}
else {
console.error('Clipboard API is only available in secure contexts (HTTPS or localhost).')
}
''')
if result is None:
log.warning('Clipboard API is only available in secure contexts (HTTPS or localhost).')
return result or ''


def write(text: str) -> None:
"""Write text to the clipboard.
Note: This function only works in secure contexts (HTTPS or localhost).
:param text: text to write
"""
run_javascript(f'navigator.clipboard.writeText({json.dumps(text)})')
run_javascript(f'''
if (navigator.clipboard) {{
navigator.clipboard.writeText({json.dumps(text)})
}}
else {{
console.error('Clipboard API is only available in secure contexts (HTTPS or localhost).')
}}
''')

0 comments on commit ed6a955

Please sign in to comment.