diff --git a/src/tui/components/QRCode.tsx b/src/tui/components/QRCode.tsx index e437abd..020a3cb 100644 --- a/src/tui/components/QRCode.tsx +++ b/src/tui/components/QRCode.tsx @@ -47,6 +47,8 @@ interface QRCodeProps { dialogTitle?: string; /** Whether to display the raw encoded value as copyable text above the QR code. */ showValue?: boolean; + /** Optional subtitle to display below the QR code. */ + subtitle?: React.ReactNode; } /** @@ -155,6 +157,7 @@ export function QRCode({ dialog = false, dialogTitle = 'QR Code', showValue = false, + subtitle = null, }: QRCodeProps): React.ReactElement { const { rows, moduleCount } = useMemo(() => { const matrix = generateMatrix(value); @@ -190,6 +193,7 @@ export function QRCode({ return ( {qrContent} + {subtitle} ); } diff --git a/src/tui/screens/WalletState.tsx b/src/tui/screens/WalletState.tsx index 4ceaa4f..f0662a0 100644 --- a/src/tui/screens/WalletState.tsx +++ b/src/tui/screens/WalletState.tsx @@ -29,6 +29,7 @@ import { type HistoryDisplayRow, type HistoryColorName, } from '../../utils/history-utils.js'; +import { copyToClipboard } from '../utils/clipboard.js'; /** * Map history color name to theme color. @@ -75,7 +76,10 @@ type HistoryListItem = ListItemData; function QRDialogOverlay({ address, onClose }: { address: string; onClose: () => void }): React.ReactElement { useInputLayer('qr-dialog'); - useLayeredInput('qr-dialog', (_input, key) => { + useLayeredInput('qr-dialog', (input, key) => { + if (input === 'c' || input === 'C') { + copyToClipboard(address); + } if (key.escape || key.return) { onClose(); } @@ -93,10 +97,13 @@ function QRDialogOverlay({ address, onClose }: { address: string; onClose: () => dialog dialogTitle="Receive Address" showValue + subtitle={ + + Press C to copy to clipboard + Press Enter or Esc to close + + } /> - - Press Enter or Esc to close - ); } diff --git a/src/utils/invitation-flow.ts b/src/utils/invitation-flow.ts index bb2bf59..016a33d 100644 --- a/src/utils/invitation-flow.ts +++ b/src/utils/invitation-flow.ts @@ -10,6 +10,7 @@ export interface SelectableUtxoLike { selected: boolean; } +// TODO: Move to engine export const hasMissingRequirements = (missingRequirements: { variables?: string[]; inputs?: string[]; @@ -32,6 +33,7 @@ export const isInvitationRequirementsComplete = async ( return !hasMissingRequirements(missingRequirements); }; +// TODO: Move to engine in templates.ts export const resolveActionRoles = ( template: XOTemplate | undefined, actionIdentifier: string | undefined, @@ -51,6 +53,7 @@ export const resolveActionRoles = ( return [...new Set(roleIds)]; }; +// TODO: Move to engine export const roleRequiresInputs = ( template: XOTemplate | undefined, actionIdentifier: string | undefined, @@ -75,6 +78,7 @@ export const roleRequiresInputs = ( return (roleInputs?.length ?? 0) > 0; }; + export const getTransactionOutputIdentifier = ( output: XOTemplateTransactionOutput, ): string | undefined => { @@ -121,6 +125,7 @@ export const tryCashAddressToLockingBytecodeHex = ( return binToHex(result.bytecode); }; +// Replace with libauth compiler in the engine export const resolveProvidedLockingBytecodeHex = ( template: XOTemplate, outputIdentifier: string,