Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ file within the `src/apps` directory to learn how to get started.
⚠️ **Encountering 429 errors?** You may have hit a rate limit for the Etherspot service. PillarX will keep trying but it might be easier for you to register your own API keys for the Etherspot Bundler API and Etherspot Data Service API via the [Portal](https://portal.etherspot.io).

⚠️ **Privy encountering login limits?** You may have hit the maximum user limit for free accounts on Privy. Feel free to [register for Privy](https://dashboard.privy.io) and create an app. This will give you your own App ID which you can use in the `.env` file.

⚠️ **ReOwn Account Needed For WalletConnect features** Sign up for a [ReOwn account](https://reown.com/), then copy your Project ID into the `.env` file.
34 changes: 34 additions & 0 deletions nixpacks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
providers = ["node"]

# Global environment variables for low-memory builds/runtime
[variables]
NODE_ENV = "production"
NODE_OPTIONS = "--max-old-space-size=2048"
# Keep npm lean and quiet
NPM_CONFIG_FUND = "false"
NPM_CONFIG_AUDIT = "false"
CI = "true"

# Tools available in the image
[phases.setup]
nixPkgs = [
"nodejs_20", # Correct Nix package name (underscore, not dash)
"caddy" # Lightweight static file server for runtime
]

# Deterministic, memory-friendly install
[phases.install]
cmds = [
# Use npm ci (respects package-lock), avoid extra network/audit noise
"npm ci --prefer-offline --no-audit --no-fund"
]

# Build the static site (outputs to ./build via vite.config.mjs)
[phases.build]
cmds = [
"npm run build"
]

# Serve the built assets with Caddy (very low memory footprint)
[start]
cmd = "sh -c 'caddy file-server --root build --listen :${PORT:-3000}'"
67 changes: 67 additions & 0 deletions src/apps/pillardao/components/AnimatedTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { useEffect, useState } from 'react';
import { animated, useTrail } from '@react-spring/web';
import styled from 'styled-components';

type AnimatedTitleProps = {
text: string;
};

const AnimatedTitle: React.FC<AnimatedTitleProps> = ({ text }) => {
const [isDisplaying, setIsDisplaying] = useState(true);
const letters = text.split('');

const trail = useTrail(letters.length, {
from: { opacity: 0, transform: 'translateY(32px)' },
to: {
opacity: isDisplaying ? 1 : 0,
transform: isDisplaying ? 'translateY(0px)' : 'translateY(32px)',
},
config: { tension: 210, friction: 20, mass: 1, duration: 25 },
});

useEffect(() => {
const timeout = setTimeout(() => setIsDisplaying(false), 1250);
return () => clearTimeout(timeout);
}, []);

return (
<Overlay>
<div className="title">
{trail.map((styles, i) => (
// eslint-disable-next-line react/no-array-index-key
<animated.span key={i} style={styles} className="letter">
{letters[i] === ' ' ? '\u00A0' : letters[i]}
</animated.span>
))}
</div>
</Overlay>
);
};

const Overlay = styled.div`
position: fixed;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
background: ${({ theme }) => theme.color.background.body};
z-index: 9999;

.title {
font-size: 48px;
font-weight: 800;
color: #fff;
}

.letter {
display: inline-block;
}

@media (max-width: 640px) {
.title {
font-size: 24px;
}
}
`;

export default AnimatedTitle;
93 changes: 93 additions & 0 deletions src/apps/pillardao/components/CopyHelp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React from 'react';
import styled from 'styled-components';
import walletConnectImage from '../images/wallet-connect-example.png';

type CopyHelpProps = {
imageSrc?: string;
overlayCollapsed?: string;
overlayExpanded?: string;
};

// Displays the provided screenshot with an overlay arrow pointing at the copy icon
const CopyHelp: React.FC<CopyHelpProps> = ({
imageSrc = walletConnectImage,
overlayCollapsed = 'How to copy the WC URI',
overlayExpanded = 'Tap to collapse',
}) => {
const [expanded, setExpanded] = React.useState(false);

return (
<Container>
<Figure
type="button"
$expanded={expanded}
onClick={() => setExpanded((v) => !v)}
aria-pressed={expanded}
>
<Img src={imageSrc} alt="WalletConnect screenshot" $expanded={expanded} />

{/* In-overlay toggle helper */}
<OverlayPill>{expanded ? overlayExpanded : overlayCollapsed}</OverlayPill>
</Figure>
</Container>
);
};

const Container = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;

const Figure = styled.button<{ $expanded: boolean }>`
position: relative;
display: inline-block;
width: auto;
height: ${({ $expanded }) => ($expanded ? 'auto' : '146px')};
max-width: ${({ $expanded }) => ($expanded ? '640px' : '260px')};
border-radius: 12px;
background: ${({ theme }) => theme.color.background.card};
border: 1px solid ${({ theme }) => theme.color.border.alertOutline};
cursor: pointer;
transition: max-width 0.2s ease-in-out, height 0.2s ease-in-out, transform 0.1s ease-in-out;
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
overflow: hidden; /* keep overlays clipped to the figure */
padding: 0;

&:active {
transform: scale(0.995);
}
`;

const Img = styled.img<{ $expanded: boolean }>`
display: block;
width: ${({ $expanded }) => ($expanded ? '100%' : 'auto')};
height: ${({ $expanded }) => ($expanded ? 'auto' : '100%')};
object-fit: contain; /* ensure full image is visible */
border-radius: 12px;
filter: ${({ $expanded }) => ($expanded ? 'none' : 'grayscale(100%)')};
transition: filter 0.2s ease-in-out;
`;

/* Arrow and highlight intentionally removed per request */

const OverlayPill = styled.div`
position: absolute;
left: 12px;
right: 12px; /* constrain within figure */
bottom: 12px;
background: rgba(0, 0, 0, 0.6);
color: #fff;
padding: 4px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 600;
pointer-events: none;
white-space: nowrap;
max-width: calc(100% - 24px);
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
`;

export default CopyHelp;
Loading
Loading