// AUTO-GENERATED by scaffold-stacks. DO NOT EDIT.
// Re-run `stacksdapp generate` to update.
"use client";
import { useState } from 'react';
import { Cl } from '@stacks/transactions';
import Message from '@/components/Message';
{% for contract in contracts %}
{% for fn in contract.functions %}
{% if fn.access == "public" or fn.access == "read_only" %}
import { use{{ contract.contract_name | upper_camel }}_{{ fn.name | upper_camel }} } from './hooks';
{% endif %}
{% endfor %}
{% endfor %}
{% raw %}
const S = {
page: { padding: '24px', display: 'flex', flexDirection: 'column' as const, gap: '24px', alignItems: 'center'},
header: { display: 'flex', alignItems: 'center', gap: '25px', paddingBottom: '16px', width: '428px' },
headerDot: { width: '8px', height: '8px', borderRadius: '50%', background: '#34d399' },
headerTitle: { fontSize: '12px', fontWeight: 500, margin: 0, fontFamily: 'JetBrains Mono' },
tab: {
fontSize: '12px',
margin: 0,
fontFamily: 'JetBrains Mono',
background: 'transparent',
border: '1px solid transparent',
padding: 0,
cursor: 'pointer',
transition: 'all 0.15s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
tabActive: {
background: '#434242',
width: '135px',
height: '40px',
borderRadius: '25px',
border: '1px solid #1F1E1F',
color: '#F4F3EF',
lineHeight: '1',
fontWeight: 400 // font-mono usually maps to normal weight unless specified
},
tabIdle: {
color: '#908E8E',
width: '135px', // Keep width consistent so tabs don't jump when switching
height: '40px'
},
tabHover: {
color: '#FFFFFF'
},
headerTag: { fontSize: '12px', color: '#6b7280', fontFamily: 'monospace', marginLeft: 'auto' },
panel: { borderRadius: '12px', border: '1px solid #1f2937', overflow: 'hidden', width: '428px', marginBottom: '12px' },
panelBtn: { width: '100%', textAlign: 'left' as const, padding: '16px 20px', background: '#1F1F1F', display: 'flex', alignItems: 'center', justifyContent: 'space-between', cursor: 'pointer', border: 'none' },
panelBtnL: { display: 'flex', alignItems: 'center' },
panelBar: { width: '6px', height: '20px', borderRadius: '3px', background: '#FF550E', marginRight: '12px' },
panelName: { fontWeight: 500, color: '#F4F3EF', fontSize: '14px', fontFamily: 'Instrument Sans' },
panelExt: { fontSize: '12px', color: '#374151', fontFamily: 'monospace' },
panelGrid: { padding: '20px', width:'100%' },
resLabel: { fontSize: '11px', color: '#9ca3af', fontWeight: 600, textTransform: 'uppercase' as const, letterSpacing: '0.05em', marginBottom: '4px' },
resPre: { fontSize: '12px', color: '#F4F3EF', overflowX: 'auto' as const, whiteSpace: 'pre-wrap' as const, wordBreak: 'break-word' as const, margin: 0 },
txBox: { borderRadius: '8px', background: 'rgba(6,78,59,0.2)', border: '1px solid #2E2D2E', padding: '8px 12px' },
txLabel: { fontSize: '11px', color: '#34d399', fontWeight: 600, marginBottom: '4px' },
txHash: { fontSize: '12px', color: '#F4F3EF', fontFamily: 'monospace', wordBreak: 'break-all' as const, margin: 0 },
fnNumber: { fontFamily: 'JetBrains Mono', fontSize: '12px', color: '#4B4B4B', marginRight: '12px' },
card: { borderRadius: '12px', border: '1px solid #1F1E1F', background: '#1A1B1E', padding: '24px', display: 'flex', flexDirection: 'column' as const, transition: 'all 0.3s ease' },
formContainer: { overflow: 'hidden', transition: 'max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease' },
cardHead: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: '4px'
},
fnName: {
fontFamily: 'Instrument Sans',
fontSize: '12px',
fontWeight: 500,
color: '#908E8E'
},
badgeRead: {
fontSize: '10px',
padding: '4px 10px',
borderRadius: '4px',
fontFamily: 'JetBrains Mono',
color: '#908E8E',
display: 'flex',
alignItems: 'center',
gap: '4px'
},
badgeWrite: {
fontSize: '10px',
padding: '4px 10px',
borderRadius: '4px',
fontFamily: 'JetBrains Mono',
color: '#908E8E',
display: 'flex',
alignItems: 'center',
gap: '4px'
},
form: { display: 'flex', flexDirection: 'column' as const, gap: '16px' },
argLabel: {
fontSize: '11px',
color: '#908E8E',
fontFamily: 'JetBrains Mono',
marginBottom: '8px',
display: 'block'
},
argType: {
fontSize: '11px',
color: '#4B4B4B',
marginLeft: '8px'
},
argInput: {
width: '100%',
borderRadius: '8px',
background: '#131416', // Deep dark input
border: '1px solid #2C2C2C',
padding: '12px 16px',
fontSize: '14px',
fontFamily: 'JetBrains Mono',
color: '#FFFFFF',
outline: 'none',
boxSizing: 'border-box' as const
},
btnRead: {
width: '100%',
padding: '12px',
borderRadius: '8px',
fontSize: '14px',
fontWeight: 600,
border: 'none',
background: '#FF550E',
color: '#fff',
fontFamily: 'Instrument Sans',
marginTop: '8px'
},
btnWrite: {
width: '100%',
padding: '12px',
borderRadius: '8px',
fontSize: '14px',
fontWeight: 600,
border: 'none',
background: '#FF550E',
color: '#fff',
fontFamily: 'Instrument Sans',
marginTop: '8px'
},
resBox: {
marginTop: '12px',
borderRadius: '8px',
background: '#131416',
border: '1px solid #3B82F6', // Outline matches the action type
padding: '12px'
},
badgeDot: {
width: '6px',
height: '6px',
borderRadius: '50%',
background: 'currentColor',
marginRight: '6px'
},
} as const;
// Convert a raw string value to the appropriate ClarityValue based on the type hint
function toClarityValue(value: string, typeHint: string): import('@stacks/transactions').ClarityValue {
const v = value.trim();
const t = typeHint.toLowerCase();
if (t === 'bool') return Cl.bool(v === 'true' || v === '1');
if (t.startsWith('uint')) return Cl.uint(BigInt(v));
if (t.startsWith('int')) return Cl.int(BigInt(v));
if (t === 'principal') return Cl.principal(v);
if (t.startsWith('string-utf8')) return Cl.stringUtf8(v);
if (t.startsWith('string-ascii')) return Cl.stringAscii(v);
if (t.startsWith('buff')) return Cl.bufferFromHex(v);
// fallback: try uint, then string
if (/^\d+$/.test(v)) return Cl.uint(BigInt(v));
return Cl.stringAscii(v);
}
{% endraw %}
export default function DebugContracts() {
const [activeTab, setActiveTab] = useState<'write' | 'read'>('write');
const [hoveredTab, setHoveredTab] = useState<'write' | 'read' | null>(null);
// Initialize with the first contract name to drop it down on load
const [openContract, setOpenContract] = useState<string | null>("{{ contracts[0].contract_name }}");
const [openFunction, setOpenFunction] = useState<string | null>(null);
return (
<div style={S.page}>
<div style={S.header}>
<button type="button" onMouseEnter={() => setHoveredTab('write')}
onMouseLeave={() => setHoveredTab(null)} style={Object.assign({}, S.tab, activeTab === 'write' ? S.tabActive : S.tabIdle, (hoveredTab === 'write' && activeTab !== 'write') ? S.tabHover : {})} onClick={() => setActiveTab('write')}>
Write Contracts
</button>
<button onMouseEnter={() => setHoveredTab('read')}
onMouseLeave={() => setHoveredTab(null)} type="button" style={Object.assign({}, S.tab, activeTab === 'read' ? S.tabActive : S.tabIdle, (hoveredTab === 'read' && activeTab !== 'read') ? S.tabHover : {})} onClick={() => setActiveTab('read')}>
Read Contracts
</button>
</div>
{% for contract in contracts %}
<div style={S.panel}>
<button
style={S.panelBtn}
onClick={() => {
setOpenContract(openContract === "{{ contract.contract_name }}" ? null : "{{ contract.contract_name }}");
setOpenFunction(null); // Close functions when switching contracts
}}
>
<div style={Object.assign({ display: 'flex', alignItems: 'center' })}>
<div style={S.panelBar} />
<span style={S.panelName}>{{ contract.contract_name | capitalize }}</span>
<span style={S.panelExt}>.clar</span>
</div>
<span style={S.headerTag}>{openContract === "{{ contract.contract_name }}" ? '▲' : '▼'}</span>
</button>
{openContract === "{{ contract.contract_name }}" && (
<div style={Object.assign({ padding: '20px', width:'100%' })} className='space-y-4'>
{% for fn in contract.functions %}
{% if fn.access == "public" or fn.access == "read_only" %}
{% set fn_id = contract.contract_name ~ "_" ~ fn.name %}
{% set current_idx = loop.index0 %}
{# Manual padding logic #}
{% if current_idx < 10 %}
{% set padded_idx = "0" ~ current_idx %}
{% else %}
{% set padded_idx = current_idx %}
{% endif %}
{% if fn.access == "read_only" %}
{activeTab === 'read' && (
<FunctionCard_{{ contract.contract_name | upper_camel }}_{{ fn.name | upper_camel }}
num="{{ padded_idx }}"
isOpen={openFunction === "{{ fn_id }}"}
onToggle={() => setOpenFunction(openFunction === "{{ fn_id }}" ? null : "{{ fn_id }}")}
/>
)}
{% else %}
{activeTab === 'write' && (
<FunctionCard_{{ contract.contract_name | upper_camel }}_{{ fn.name | upper_camel }}
num="{{ padded_idx }}"
isOpen={openFunction === "{{ fn_id }}"}
onToggle={() => setOpenFunction(openFunction === "{{ fn_id }}" ? null : "{{ fn_id }}")}
/>
)}
{% endif %}
{% endif %}
{% endfor %}
</div>
)}
</div>
{% endfor %}
<Message />
</div>
);
}
function ContractPanel({ name, children }: { name: string; children: React.ReactNode }) {
const [open, setOpen] = useState(true);
return (
<div style={S.panel}>
<button style={S.panelBtn} onClick={() => setOpen(o => !o)}>
<div style={S.panelBtnL}>
<div style={S.panelBar} />
<span style={S.panelName}>{name}</span>
<span style={S.panelExt}>.clar</span>
</div>
<span style={S.headerTag}>{open ? '▲' : '▼'}</span>
</button>
{open && <div style={S.panelGrid} className='space-y-4'>{children}</div>}
</div>
);
}
{% for contract in contracts %}
{% for fn in contract.functions %}
{% if fn.access == "public" or fn.access == "read_only" %}
function FunctionCard_{{ contract.contract_name | upper_camel }}_{{ fn.name | upper_camel }}({ num, isOpen, onToggle }: { num: string, isOpen: boolean, onToggle: () => void }) {
const { data, loading, call } = use{{ contract.contract_name | upper_camel }}_{{ fn.name | upper_camel }}();
const isReadOnly = {% if fn.access == "read_only" %}true{% else %}false{% endif %};
{% if fn.args %}
const [args, setArgs] = useState<Record<string, string>>({
{% for arg in fn.args %}'{{ arg.name }}': '', {% endfor %}
});
const setArg = (name: string, value: string) => setArgs(a => Object.assign({}, a, { [name]: value }));
{% endif %}
return (
<div style={Object.assign({}, S.card, { border: isOpen ? '1px solid #FF550E' : S.card.border })}>
<div style={Object.assign({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', cursor: 'pointer' })} onClick={onToggle}>
<div style={Object.assign({ display: 'flex', alignItems: 'center' })}>
<span style={S.fnNumber}>{num}</span>
<span style={S.fnName}>{{ fn.name | upper }}</span>
</div>
<div style={isReadOnly ? S.badgeRead : S.badgeWrite}>
<div style={S.badgeDot} />
{isReadOnly ? 'READ' : 'WRITE'}
<span style={Object.assign({ marginLeft: '8px', transition: '0.3s' }, { transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)' })}>▼</span>
</div>
</div>
<div style={Object.assign({}, S.formContainer, { maxHeight: isOpen ? '1000px' : '0px', opacity: isOpen ? 1 : 0, marginTop: isOpen ? '20px' : '0px' })}>
<form style={S.form} onSubmit={async (e) => { e.preventDefault(); await call([{% if fn.args %}{% for arg in fn.args %}toClarityValue(args['{{ arg.name }}'] ?? '', '{{ arg.type_str }}'),{% endfor %}{% endif %}]); }}>
{% if fn.args %}
{% for arg in fn.args %}
<div>
<label style={S.argLabel}>{{ arg.name }} <span style={S.argType}>{{ arg.type_str | upper }}</span></label>
<input style={S.argInput} value={args['{{ arg.name }}']} onChange={e => setArg('{{ arg.name }}', e.target.value)} />
</div>
{% endfor %}
{% endif %}
<button type="submit" disabled={loading} style={Object.assign({}, isReadOnly ? S.btnRead : S.btnWrite, { opacity: loading ? 0.5 : 1 })}>
{loading ? 'PROCESSING...' : (isReadOnly ? 'READ' : 'CALL')}
</button>
{!!data && (
<div style={Object.assign({}, S.resBox, { borderColor: isReadOnly ? '#2E2D2E' : '#2E2D2E' })}>
<p style={S.resLabel}>RESULT</p>
<pre style={S.resPre}>{JSON.stringify(data, null, 2)}</pre>
</div>
)}
</form>
</div>
</div>
);
}
{% endif %}
{% endfor %}
{% endfor %}