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,