Skip to content
Merged
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
41 changes: 38 additions & 3 deletions edit.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ body {
flex: 1;
padding: 48px;
overflow-y: auto;
position: relative;
}

.tab-pane {
Expand Down Expand Up @@ -405,7 +406,7 @@ input[type="color"]::-webkit-color-swatch {

/* Top Right Editor Controls */
.editor-controls-topright {
position: fixed;
position: absolute;
top: 24px;
right: 24px;
z-index: 998;
Expand Down Expand Up @@ -483,8 +484,8 @@ input[type="color"]::-webkit-color-swatch {
position: fixed;
width: max-content;
top: 24px;
left: 50%;
transform: translateX(-50%);
right: 25px;
width: 400px;
padding: 8px 14px;
border-radius: var(--radius);
margin-bottom: 24px;
Expand All @@ -501,3 +502,37 @@ input[type="color"]::-webkit-color-swatch {
font-size: 18px;
}

/* Preview Panel styles */
.preview-panel {
width: 450px;
border-left: 1px solid var(--border);
background: var(--bg);
display: flex;
align-items: center;
justify-content: center;
/* padding: 10px; */
}

.phone-simulator {
width: 100%;
height: 100%;
min-height: 500px;
/* border: 4px solid var(--border); */
/* border: 12px solid #1c1b1f; */
/* border-radius: 24px; */
/* box-shadow: 0 24px 24px rgba(0,0,0,0.15); */
overflow: hidden;
background: #fff;
}

.phone-simulator iframe {
width: 100%;
height: 100%;
border: none;
}

@media (max-width: 1024px) {
.preview-panel {
display: none;
}
}
30 changes: 19 additions & 11 deletions edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,6 @@ <h2>Bio Link Editor</h2>
</nav>
</aside>

<!-- Top Right Editor Controls -->
<div class="editor-controls-topright">
<button class="btn-discard-pill" id="btn-discard-draft-top" style="display: none;">
<span class="material-symbols-outlined">delete</span> Discard Changes
</button>
<div class="draft-status-pill saved" id="draft-status-indicator">
<span class="material-symbols-outlined status-icon">check_circle</span>
<span class="status-text">Saved</span>
</div>
</div>

<!-- Main Content Area -->
<main class="main-content">

Expand All @@ -70,6 +59,17 @@ <h2>Bio Link Editor</h2>
<span>You are editing an unsaved local draft. Deploy or discard changes.</span>
</div>

<!-- Top Right Editor Controls -->
<div class="editor-controls-topright">
<button class="btn-discard-pill" id="btn-discard-draft-top" style="display: none;">
<span class="material-symbols-outlined">delete</span> Discard Changes
</button>
<div class="draft-status-pill saved" id="draft-status-indicator">
<span class="material-symbols-outlined status-icon">check_circle</span>
<span class="status-text">Saved</span>
</div>
</div>

<!-- Profile Tab -->
<div id="profile" class="tab-pane active">
<header class="pane-header">
Expand Down Expand Up @@ -326,6 +326,14 @@ <h3>GitHub Integration</h3>
</div>

</main>

<!-- Preview Panel -->
<div class="preview-panel">
<div class="phone-simulator">
<iframe id="preview-iframe" src="index.html"></iframe>
</div>
</div>

</div>

<script src="edit.js"></script>
Expand Down
16 changes: 16 additions & 0 deletions edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,22 @@ initSortable('links-list', 'links', renderLinks);
initSortable('ctas-list', 'ctas', renderCtas);
initSortable('featured-list', 'featured', renderFeatured);

// --- Live Preview Polling Loop ---
let lastPreviewStr = JSON.stringify(currentConfig);
setInterval(() => {
const currentStr = JSON.stringify(currentConfig);
if (currentStr !== lastPreviewStr) {
lastPreviewStr = currentStr;
const iframe = document.getElementById('preview-iframe');
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage({
type: 'UPDATE_CONFIG',
config: JSON.parse(currentStr)
}, '*');
}
}
}, 300);
Comment on lines +1125 to +1139

// --- Auto-Save Polling Loop ---
setInterval(() => {
const currentStr = JSON.stringify(currentConfig);
Expand Down
142 changes: 83 additions & 59 deletions script.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
'use strict';

// --- 1. Populate Content from Configuration ---
const config = window.APP_CONFIG;
if (!config) {
console.error('Configuration data (config.js) not found.');
return;
}
function renderPage(config) {
if (!config) {
console.error('Configuration data (config.js) not found.');
return;
}
Comment on lines +11 to +15

// Check if the page is loaded inside an iframe (preview mode)
if (window !== window.parent) {
document.documentElement.classList.add('in-preview');
}

// --- Theme Presets & Loading ---
const THEME_PRESETS = {
Expand Down Expand Up @@ -482,6 +487,49 @@
`;
}

initScrollAnimations();
}

let scrollObserver = null;
function initScrollAnimations() {
if ('IntersectionObserver' in window) {
if (scrollObserver) scrollObserver.disconnect();
scrollObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.style.animationPlayState = 'running';
scrollObserver.unobserve(entry.target);
}
});
},
{
root: null,
rootMargin: '0px 0px -40px 0px',
threshold: 0.05,
}
);

const animatedElements = document.querySelectorAll('.link-tile, .footer');
animatedElements.forEach((el) => scrollObserver.observe(el));
} else {
const animatedElements = document.querySelectorAll('.link-tile, .footer');
animatedElements.forEach((el) => {
el.style.animationPlayState = 'running';
});
}
}

// Initial Load
renderPage(window.APP_CONFIG);

// Live Preview Support
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'UPDATE_CONFIG') {
window.APP_CONFIG = event.data.config;
renderPage(window.APP_CONFIG);
}
});
Comment on lines +527 to +532

// --- 2. Ambient Background Cursor Parallax ---
const blobs = document.querySelectorAll('.blob');
Expand Down Expand Up @@ -513,62 +561,38 @@
animateBackground();
}

// --- 3. Staggered Scroll-Entrance Animation ---
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.style.animationPlayState = 'running';
observer.unobserve(entry.target);
// (Scroll animations are now initialized inside renderPage)
// --- 4. Promo Popup Trigger (3 seconds after page load) ---
if (window === window.parent) {
setTimeout(() => {
const promoWrapper = document.getElementById('promo-popup-wrapper');
const promoClose = document.getElementById('promo-close');

if (promoWrapper) {
promoWrapper.classList.add('show');
promoWrapper.setAttribute('aria-hidden', 'false');

const dismissPopup = () => {
promoWrapper.classList.remove('show');
promoWrapper.setAttribute('aria-hidden', 'true');
};

if (promoClose) {
promoClose.addEventListener('click', dismissPopup);
}

const promoCta = promoWrapper.querySelector('.btn-promo');
if (promoCta) {
promoCta.addEventListener('click', dismissPopup);
}

// Close when clicking the backdrop overlay
promoWrapper.addEventListener('click', (e) => {
if (e.target === promoWrapper) {
dismissPopup();
}
});
},
{
root: null,
rootMargin: '0px 0px -40px 0px',
threshold: 0.05,
}
);

const animatedElements = document.querySelectorAll('.link-tile, .footer');
animatedElements.forEach((el) => observer.observe(el));
} else {
const animatedElements = document.querySelectorAll('.link-tile, .footer');
animatedElements.forEach((el) => {
el.style.animationPlayState = 'running';
});
}, 3000);
}

// --- 4. Promo Popup Trigger (3 seconds after page load) ---
setTimeout(() => {
const promoWrapper = document.getElementById('promo-popup-wrapper');
const promoClose = document.getElementById('promo-close');

if (promoWrapper) {
promoWrapper.classList.add('show');
promoWrapper.setAttribute('aria-hidden', 'false');

const dismissPopup = () => {
promoWrapper.classList.remove('show');
promoWrapper.setAttribute('aria-hidden', 'true');
};

if (promoClose) {
promoClose.addEventListener('click', dismissPopup);
}

const promoCta = promoWrapper.querySelector('.btn-promo');
if (promoCta) {
promoCta.addEventListener('click', dismissPopup);
}

// Close when clicking the backdrop overlay
promoWrapper.addEventListener('click', (e) => {
if (e.target === promoWrapper) {
dismissPopup();
}
});
}
}, 3000);
})();
11 changes: 11 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ body {
-webkit-font-smoothing: antialiased;
}

/* Hide scrollbar for iframe preview */
html.in-preview::-webkit-scrollbar,
html.in-preview body::-webkit-scrollbar {
display: none;
}
html.in-preview,
html.in-preview body {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}

.text-muted, .text-secondary {
color: var(--text-muted) !important;
}
Expand Down