This commit is contained in:
admin 2025-05-14 14:41:51 -06:00
parent 844f1c6550
commit 5a196804d7
74 changed files with 5333 additions and 0 deletions

View File

@ -0,0 +1,83 @@
# Feature Tracking ✨
> Keeping track of Change Maker's evolution. Stay updated on what's coming next!
## Currently in Development 🚀
Change Maker V4 is actively being developed by the bnkops team. Here's what's on the horizon:
### Core Infrastructure Updates 🏗️
- **🌐 DNS Integration**
- Seamless domain hosting under bnkops.com #not started
- Simplified domain management #in progress
- Integrated DNS configuration tools #not started
- **⚡ One-Click Installation**
- Streamlined public access deployment #in progress
- Simplified setup process #in progress
- Reduced technical barriers #in progress
### Enhanced Editor Experience 📝
- **🎨 Expanded Frontend Editor**
- Advanced markdown editing capabilities #Open
- Improved content management interface #Open
- Enhanced preview functionality #Open
- Real-time collaboration features #In Progress
### Mobile Optimization 📱
- **📱 Enhanced Mobile Features**
- Streamlined content uploading #Not Started
- Improved mobile UI/UX #In Progress
- Touch-optimized controls #Not Started
- Responsive design improvements #In Progress
### Communication & Automation 🤖
- **📨 Local Communication Suite**
- Newsletter system integration #done
- Email targeting capabilities #done
- Advanced automation workflows #in progress
- Database integration options #in progress
### Content Management 📊
- **🏷️ Meta Properties System**
- Document state tracking (draft/published) #in progress
- Author attribution #done
- Publication timestamps #in progress
- Version control #in progresss
- Content lifecycle management #in pprgoress
---
## Current Features Overview 🌟
For reference, here are some of our current notable features:
- **📱 Mobile-First Design**
- Responsive Web Build
- Responsive layout
- Cross-device compatibility
- **🔍 Search Capabilities**
- Real-time suggestions
- Keyword highlighting
- Instant results
- **🎨 Accessibility**
- Dark/Light mode support
- Screen reader optimization
- Accessibility-first design
- **🔒 Privacy & Security**
- Self-hosted platform
- No external dependencies
- Complete data control
- VPN compatibility
---
> 💡 **Want to stay updated?** [Subscribe to our newsletter](https://changemaker.bnkops.com) for development updates and release notifications.

View File

@ -0,0 +1,30 @@
# Free(ish)
Free(ish) is our way of saying that Changemaker does require an exchange; we ask that you voluntarily provide an email address. We do this because in political work, system security is paramount. If we uncover security vulnerabilities, it's crucial that we can contact you.
We maintain a single mailing list and send updates approximately bi-weekly. Please join us!
<div class="subscription-container" style="background: #424242; padding: 2rem; border-radius: 8px; max-width: 500px; margin: 2rem auto;">
<form method="post" action="https://listmonk.bnkops.com/subscription/form" class="listmonk-form" style="color: #fff;">
<h3 style="color: #ffb300; margin-bottom: 1.5rem;">Subscribe to Updates</h3>
<input type="hidden" name="nonce" />
<div style="margin-bottom: 1rem;">
<input type="email" name="email" required placeholder="E-mail" style="width: 100%; padding: 8px; border: 2px solid #ffb300; border-radius: 4px; background: #303030; color: #fff;" />
</div>
<div style="margin-bottom: 1.5rem;">
<input type="text" name="name" placeholder="Name (optional)" style="width: 100%; padding: 8px; border: 2px solid #ffb300; border-radius: 4px; background: #303030; color: #fff;" />
</div>
<div style="margin-bottom: 1.5rem;">
<input id="038eb" type="checkbox" name="l" checked value="038eb469-e141-435d-86eb-2ab4df20cf9c" />
<label for="038eb" style="color: #ffb300;">~Weekly Updates</label>
<p style="font-size: 0.9rem; color: #ccc; margin-top: 0.5rem;">
Hi Friend! Subscribing here will get you weekly updates from The Bunker Ops Admin (Bunker Admin) Reed Larsen (thatreallyblondehuman). Expect personal, code, and political updates.
</p>
</div>
<button type="submit" style="background: #ffb300; color: #424242; padding: 8px 20px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Subscribe</button>
</form>
</div>

View File

@ -0,0 +1,61 @@
# Who is Bnkops?
## We Are a Co-operative
The Bunker Operations is a co-operative based in amiskwaciy-wâskahikan (Edmonton). The administration team is comprised of settlers and we are grateful for all that the earth has provided us on this land.
Our organized group comprises trusted collaborators, comrades, and co-conspirators who share common goals in maintaining, growing, and thriving in community 🤝🏡💜. We provide comprehensive consulting services, web infrastructure, and resources to various organizations.
You can see another of our projects here:
[repo.bnkops.com](https://repo.bnkops.com){ .md-button }
You have seen our Featured Partner banner above. We have a number of partners who we work with to provide services and support to our community.
**Want to work with us in partnership?**
Contact us:
[Email](mailto:admin@bnkops.ca){ .md-button }
---
## Members of Bnkops Change Maker Team
### The Bunker Admin - Reed
![Reed](image.png){ width="200" style="border-radius: 50%" align="right" }
The Bunker Admin is the primary administrator of the Change Maker system. They are responsible for the overall operation of the system and the development of the software.
Reed is a settler living in amiskwaciy-wâskahikan (Edmonton). Their family has been living between Treaty 6 and 7 lands for 3 generations. Blessed with the opportunity to live and work on this land, they are grateful for all that the earth has provided them.
Reed is a human of many talents. They love to learn; often sporadically and in great bursts of excitment for a new topic, thing, or idea. They have a dry sense of humor and a love for the absurd. Catch them at a the dingiest dvd rental store, or the most obscure of bookstores, or sipping oat milk lattes at a local cafe.
They have enjoyed many different labors in their time, including:
- 🏠🔨 **Working** in the construction industry, specifically in housing
- 📚👨‍🏫 **Thriving** as a teacher, specifically with children and youth teaching english
- 🎓📊 **Studying** as a student, in the field of political science and Alberta political identity
- 🎓👔 **Laboring** as a Student leader, including as a President to the University of Alberta Students' Union
- 🌱🗣️ **Lobbying** as a political advocate, specifically for students, green energy, education, and the environment
- 🌈💪 **Organizing** as a community organizer, with groups like Pride Corner and Tawaw outreach
- 💻🌐 **Coding** as developer, specifically in web development and design
- 🎨🖌️ **Creating** art, specifically in the form of painting and drawing
**Known Aliases:**
- thatreallyblondehuman 👱‍♀️👱‍♂️
- canadianforsure 🇨🇦
- bunker admin 🏢
[Learn more about Reed in depth](https://repo.bnkops.com/thatreallyblondehuman/whomst.html){ .md-button }
[Contact Reed](mailto:reed@bnkops.ca){ .md-button }
### The Bunker Strategist - Shayla Breen
The Bunker Strategist is responsible for the strategic direction of the Change Maker project. They are the supporting role to the Bunker Admin and help to guide the project in the right direction.
[Contact Shayla](mailto:shayla@bnkops.ca){ .md-button }

View File

@ -0,0 +1,840 @@
# Why Change Maker
<!DOCTYPE html>
<html>
<head>
<style>
.power-container {
background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
border-radius: 15px;
padding: 1rem;
color: white;
font-family: system-ui, -apple-system, sans-serif;
width: 100%;
margin: 1rem auto;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
overflow: hidden;
position: relative;
}
.cards-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin: 2rem auto;
max-width: 1200px;
padding: 0 1rem;
}
@media (max-width: 768px) {
.cards-container {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.cards-container {
grid-template-columns: 1fr;
}
}
@media (max-width: 680px) {
.cards-container {
grid-template-columns: 1fr;
gap: 0.8rem;
}
.power-card {
padding: 1rem !important;
}
.power-message {
font-size: 1.4rem !important;
margin: 1rem 0 !important;
}
.power-icon {
font-size: 2rem !important;
margin-bottom: 0.5rem !important;
}
.power-card p {
margin: 0.5rem 0;
font-size: 0.9rem;
}
}
.power-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 1.5rem;
text-align: center;
transform-style: preserve-3d;
border: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.power-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
.power-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.power-card h3 {
color: #FFD700;
margin: 0.5rem 0;
font-size: 1.2rem;
}
.power-message {
font-size: 1.8rem;
font-weight: bold;
text-align: center;
margin: 1.5rem 0;
color: #FFD700;
opacity: 0;
transform: translateY(20px);
animation: slideUp 0.8s ease forwards;
}
@keyframes slideUp {
to {
opacity: 1;
transform: translateY(0);
}
}
.power-beam {
position: absolute;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, #FFD700, transparent);
animation: beam 3s linear infinite;
}
@keyframes beam {
0% { transform: translateX(-100%); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateX(100%); opacity: 0; }
}
.emphasis-text {
color: #FFD700;
font-weight: bold;
animation: glow 2s ease-in-out infinite;
}
@keyframes glow {
0%, 100% { text-shadow: 0 0 5px rgba(255, 215, 0, 0.5); }
50% { text-shadow: 0 0 20px rgba(255, 215, 0, 0.8); }
}
.particles {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
}
.particle {
position: absolute;
width: 3px;
height: 3px;
background: #FFD700;
border-radius: 50%;
animation: particleRise 2s ease-out infinite;
opacity: 0;
}
@keyframes particleRise {
0% { transform: translateY(50px); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(-50px); opacity: 0; }
}
.evil-container {
background: linear-gradient(135deg, #1a0505, #2d0808);
border: 1px solid #ff0000;
border-radius: 0;
padding: 1rem;
width: 100%; /* Changed from 800px to 100% */
max-width: 800px; /* Added max-width */
margin: 1rem auto;
color: #ffffff;
position: relative;
overflow: hidden;
animation: evilPulse 4s ease-in-out infinite;
box-shadow:
0 0 20px rgba(255, 0, 0, 0.2),
inset 0 0 20px rgba(255, 0, 0, 0.1);
}
/* Add media query for mobile devices */
@media (max-width: 768px) {
.evil-container {
padding: 0.75rem;
margin: 0.5rem;
font-size: 0.9em;
}
.evil-container h2 {
font-size: 1.5rem;
}
.evil-container p {
margin: 0.5rem 0;
}
.evil-container a {
word-break: break-word;
}
}
@keyframes evilPulse {
0%, 100% {
box-shadow:
0 0 20px rgba(255, 0, 0, 0.2),
inset 0 0 20px rgba(255, 0, 0, 0.1);
}
50% {
box-shadow:
0 0 30px rgba(255, 0, 0, 0.4),
inset 0 0 30px rgba(255, 0, 0, 0.2);
}
}
.evil-container::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 0, 0, 0.1),
transparent
);
animation: evilScan 8s linear infinite;
}
@keyframes evilScan {
0% { left: -100%; }
100% { left: 100%; }
}
.evil-container h2 {
color: #ff3333;
text-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
}
.evil-container a {
color: #ff6666;
text-decoration: none;
}
.evil-container a:hover {
color: #ff3333;
text-shadow: 0 0 8px rgba(255, 0, 0, 0.4);
}
.change-container {
background: #a8d5ba; /* Soft pastel green */
border: 2px solid #c9e6d6;
border-radius: 15px;
padding: 1rem;
max-width: 800px;
margin: 1rem auto;
color: #f8f9fa; /* Near white text */
position: relative;
overflow: hidden;
box-shadow: 0 4px 20px rgba(168, 213, 186, 0.3);
}
.change-container h2 {
color: white;
font-size: 2rem;
margin-bottom: 1.5rem;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
}
.change-container h3 {
color: white;
font-size: 1.4rem;
margin-top: 1.5rem;
font-style: italic;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
.change-container p {
color: #f8f9fa;
line-height: 1.8;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
background: rgba(0, 0, 0, 0.1);
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
}
.change-container iframe {
border: 8px solid white;
border-radius: 20px;
box-shadow:
0 4px 15px rgba(0, 0, 0, 0.1),
0 0 0 2px #c9e6d6;
margin: 1.5rem auto;
display: block;
max-width: 100%;
background: white;
padding: 4px;
}
.theory-container {
background: linear-gradient(135deg, #2c3e50, #3498db);
border: 2px solid #3498db;
border-radius: 15px;
padding: 1rem;
max-width: 800px;
margin: 1rem auto;
color: white;
box-shadow: 0 4px 20px rgba(52, 152, 219, 0.3);
}
.theory-container h3 {
font-size: 1.8rem;
margin-bottom: 1.5rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.theory-container ul {
list-style: none;
padding: 0;
}
.theory-container li {
margin: 1rem 0;
padding: 0.8rem;
background: rgba(255,255,255,0.1);
border-radius: 8px;
transition: transform 0.2s;
}
.theory-container li:hover {
transform: translateX(10px);
}
.theory-container a {
color: #fff;
text-decoration: none;
display: block;
padding: 0.5rem;
border-left: 3px solid #3498db;
}
.free-container {
background: linear-gradient(135deg, #2ecc71, #27ae60);
border: 2px solid #2ecc71;
border-radius: 15px;
padding: 1rem;
max-width: 800px;
margin: 1rem auto;
color: white;
box-shadow: 0 4px 20px rgba(46, 204, 113, 0.3);
}
.free-container h2 {
font-size: 2rem;
text-align: center;
margin-bottom: 1.5rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.free-container img {
display: block;
margin: 1rem auto;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.free-container p {
background: rgba(255,255,255,0.1);
padding: 1rem;
border-radius: 8px;
line-height: 1.6;
margin: 1rem 0;
}
@keyframes pulseGlow {
0%, 100% { box-shadow: 0 0 20px rgba(46, 204, 113, 0.4); }
50% { box-shadow: 0 0 30px rgba(46, 204, 113, 0.8); }
}
</style>
</head>
<body>
<div class="power-container">
<div class="power-beam"></div>
<div class="particles" id="particles"></div>
<!-- Power header styles with metallic effect -->
<style>
.power-header.entry-header h3 {
/* Welcome to text */
color: rgba(255, 255, 255, 0.9) !important;
font-size: 1.5rem !important;
font-weight: normal !important;
text-shadow: none !important;
background: none !important;
-webkit-background-clip: initial !important;
background-clip: initial !important;
animation: none !important;
margin-bottom: 0.5rem !important;
}
.power-header.entry-header h1.highlight-text {
/* Metallic gold effect for main title */
background: linear-gradient(
135deg,
#ffd700 0%,
#fff6a9 10%,
#d4af37 20%,
#fff6a9 30%,
#ffd700 40%,
#fff6a9 50%,
#d4af37 60%,
#fff6a9 70%,
#ffd700 80%,
#fff6a9 90%,
#d4af37 100%
) !important;
-webkit-background-clip: text !important;
background-clip: text !important;
color: transparent !important;
text-shadow:
0 0 1px rgba(255, 215, 0, 0.2),
0 0 15px rgba(255, 215, 0, 0.3),
0 0 30px rgba(255, 215, 0, 0.1) !important;
font-weight: 800 !important;
font-size: 4rem !important;
letter-spacing: -0.02em !important;
animation: metallicShine 8s linear infinite !important;
background-size: 200% auto !important;
}
@keyframes metallicShine {
to {
background-position: 200% center;
}
}
</style>
<div class="power-header entry-header" style="text-align: center;">
<h1 class="highlight-text">Build your power, don't pay for it.</h1>
</div>
<div class="cards-container">
<div class="power-card">
<div class="power-icon">💾</div>
<h3>Your Data, Your Rules</h3>
<p>Keep your digital footprint truly private. Your site and content are built locally, on your machine. That way, we can't peek, we can't leak, we can't sell what we can't see. Simple as that.</p>
</div>
<div class="power-card">
<div class="power-icon">📚</div>
<h3>Community Library</h3>
<p>Write your site in the same manner as a repository. Over time, your repository gets bigger. Like a library with good sustainable funding, your repository can become a beacon for community information.</p>
</div>
<div class="power-card">
<div class="power-icon"></div>
<h3>Digital Independence</h3>
<p>Why fund the competition? Most software companies are digital mercenaries, working for the highest bidder. Take control of your digital destiny without feeding the corporate beast.</p>
</div>
<div class="power-card">
<div class="power-icon"></div>
<h3>Lightning Fast</h3>
<p>Got thoughts? Put 'em online faster than you can say "corporate overlords." No tech degree required.</p>
</div>
<div class="power-card">
<div class="power-icon">🤔</div>
<h3>Level Up</h3>
<p>Start with basic text, end up mastering web development. It's like a game, but the final boss is digital dependency.</p>
</div>
<div class="power-card">
<div class="power-icon">💫</div>
<div style="text-align: center; margin: 1rem 0;">
<a href="/Free(ish)" style="
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #FFD700;
color: #1a1a1a;
text-decoration: none;
border-radius: 8px;
font-weight: bold;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s ease, transform 0.3s ease;
" onmouseover="this.style.backgroundColor='#FFC107'; this.style.transform='translateY(-2px)';" onmouseout="this.style.backgroundColor='#FFD700'; this.style.transform='translateY(0)';">
Free(ish)!
</a>
</div>
<p>Just drop us your email and get full access. No catches, no surprise bills, no selling your soul to Silicon Valley.</p>
</div>
</div>
<div class="power-message" style="font-size: 1.2rem;">
Do you own your data, or are you just renting it?
</div>
</div>
<div class="power-header entry-header" style="text-align: center; margin-top: 2rem;">
<h1 class="highlight-text">"Oh so its kinda like nexopia for politics however all on my computer?"</h1>
<h3>- Change Maker's First User</h3>
</div>
<script>
function createParticles() {
const particlesContainer = document.getElementById('particles');
const particleCount = window.innerWidth < 680 ? 8 : 15;
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 2 + 's';
particlesContainer.appendChild(particle);
setTimeout(() => {
particle.remove();
}, 2000);
}
}
createParticles();
setInterval(createParticles, 4000);
</script>
</body>
</html>
<div class="power-container">
<h2>The Original Internet Promise</h2>
<p>Remember when the internet was meant to be everyone's digital commons? That vision still lives, though it's been overshadowed by corporate platforms. What began as an open space has largely transformed into a marketplace, with companies repackaging freely available solutions behind sleek marketing.</p>
<p>Enter Software-as-a-Service (SaaS), the modern equivalent of cure-all tonics 🐍🎩. "Subscribe to solve all your problems!" they say - just keep paying monthly until those fees stack higher than a mortgage.</p>
<p>At <a href="https://repo.bnkops.com/Home.html">bnkops</a>, we believe in community power. By sharing our activist toolkit, we're supporting our communities, organizations, and fellow changemakers in building a more equitable world. These tools can help us organize against the rise of facism in ways we haven't even imagined yet.</p>
<p>This project returns to the internet's original promise - putting digital power back in the hands of everyday people on everyday computers. While we might not have flashy features or marketing budgets, we offer something better: a as secure as we can make it, functional activist platform available, and it's free(ish).</p>
<p>The best part? Your data stays completely yours. The system runs locally, so we can't access your information or suddenly start charging fees. Your movement, your data, your control. ✊</p>
</div>
<style>
.timeline-container {
background: rgba(68, 51, 85, 0.3);
border: 1px solid #9333ea;
border-radius: 15px;
padding: 1rem;
max-width: 800px;
margin: 2rem auto;
}
.timeline-header {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 1rem;
text-align: center;
margin-bottom: 1rem;
}
.time-period h3 {
color: #FFD700;
margin: 0;
font-size: 0.9rem;
}
.time-period p {
font-size: 0.8rem;
margin: 0.25rem 0;
}
.fuse-box {
background: rgba(0, 0, 0, 0.3);
height: 60px;
position: relative;
border-radius: 4px;
overflow: hidden;
}
.fuse-line {
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 2px;
background: #444;
transform: translateY(-50%);
}
.burning-point {
position: absolute;
top: 50%;
left: 0;
width: 8px;
height: 8px;
background: #FFD700;
border-radius: 50%;
transform: translateY(-50%);
box-shadow:
0 0 10px #FFD700,
0 0 20px #FF4500,
0 0 30px #FF0000;
animation: burnFuse 15s linear infinite;
z-index: 2;
}
.spark {
position: absolute;
width: 3px;
height: 3px;
background: #FFD700;
border-radius: 50%;
animation: sparkle 0.6s linear infinite;
}
.spark:nth-child(2) {
animation-delay: 0.2s;
}
.spark:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes burnFuse {
from {
left: 0;
box-shadow:
0 0 10px #FFD700,
0 0 20px #FF4500,
0 0 30px #FF0000;
}
to {
left: 100%;
box-shadow:
0 0 15px #FFD700,
0 0 25px #FF4500,
0 0 35px #FF0000;
}
}
@keyframes sparkle {
0% {
transform: translate(-50%, -50%) rotate(0deg) translateX(6px);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) rotate(360deg) translateX(12px);
opacity: 0;
}
}
.burned-path {
position: absolute;
top: 50%;
left: 0;
height: 2px;
background: #9333ea;
transform: translateY(-50%);
animation: burnPath 15s linear infinite;
}
@keyframes burnPath {
from {
width: 0;
}
to {
width: 100%;
}
}
.monitor-text {
position: absolute;
bottom: 5px;
right: 10px;
color: #9333ea;
font-family: monospace;
font-size: 0.8rem;
}
</style>
<div class="timeline-container">
<div class="timeline-header">
<div class="time-period">
<h3>1990s</h3>
<p>Free Internet</p>
<small>Open protocols</small>
</div>
<div class="time-period">
<h3>2000s</h3>
<p>Big Tech Rise</p>
<small>Centralization</small>
</div>
<div class="time-period">
<h3>2010s</h3>
<p>SaaS Trap</p>
<small>Subscriptions</small>
</div>
<div class="time-period">
<h3>2020s</h3>
<p>Surveillance Era</p>
<small>Data sold</small>
</div>
<div class="time-period">
<h3>NOW</h3>
<p>Time to Act</p>
<small>Take control</small>
</div>
</div>
<div class="fuse-box">
<div class="fuse-line"></div>
<div class="burned-path"></div>
<div class="burning-point">
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
</div>
<div class="monitor-text">CORPORATE EVIL LEVEL: CRITICAL</div>
</div>
</div>
<div class="evil-container">
<h2>Corporate Evil is the Default Selection</h2>
<p>We will not defeat capitalism and the march towards facism by giving our data and dollars to companies that would sell us to the highest bidder. Nearly all software corporations are already selling out your movements data.</p>
<strong>A brief list of examples:</strong>
<p><a href="https://www.aljazeera.com/news/2024/4/23/what-is-project-nimbus-and-why-are-google-workers-protesting-israel-deal">Google has billion dollar contracts with Israel to serve the technology that enables genocide.</a></p>
<p><a href="https://www.yahoo.com/news/microsoft-palantir-announce-major-ai-114252184.html?guccounter=1&guce_referrer=aHR0cHM6Ly9kdWNrZHVja2dvLmNvbS8&guce_referrer_sig=AQAAAN3D8nDOlxecFTDaNyOhCteW9Px_88vBP8XZf5d43NZyk3lR5glFF06bwwQP8u0yL_9HEVsZ5PTc2rtc53TKHqF-O6qPvGHODb8tiMFRRLrNa0MGtBsBeNN7M-wW11If6qx8B4JZ36FdSzJ77IKETfLVhZxrkos-yrI_x3vZpTQ9">Microsoft is the technology provider of the US military.</a></p>
<p><a href="https://www.washingtonpost.com/technology/2024/02/22/pentagon-ai-symposium-chatgpt-uses/">OpenAI (chatGPT) is used by intelligence agencies</a></p>
<p><a href="https://www.yourtango.com/self/intuit-ceo-laid-off-1800-workers-claimed-most-were-underperformers">Mailchimp, owned by Inuit, fired nearly 1,800 employees and slanderd them all</a></p>
<p><a href="https://www.nytimes.com/2024/08/13/technology/elon-musk-x-donald-trump.html">Twitter is owned my a manic billionaire that is a maga weirdo</a></p>
<p><a href="https://www.nbcnews.com/tech/tech-news/facebook-turned-chat-messages-mother-daughter-now-charged-abortion-rcna42185">Facebook chats have been used to imprison abortion rights advocates</a></p>
<p><a href="https://en.wikipedia.org/wiki/NationBuilder">A Nationbuilder founder Robert Greenwald is close with Mark Zuckerberg and its initial founders include prominent republicans. Plus the service is effectively banned in Europe over privacy concerns</a></p>
</div>
<div class="theory-container">
<h3>Sharing is caring. Knowledge is power. Creation is a community process.</h3>
<p>Want to read more theory on this? Read some papers:</p>
<ul>
<li><a href="https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/Distributed%20Digital%20Organizing%20is%20The%20Way%20Out.html">Distribute Digital Organizing is the Way Out</a></li>
<li><a href="https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/If%20you%20do%20politics%20who%20is%20reading%20your%20secrets%20-%20why%20you%20should%20de-corp%20your%20software%20stack.html">If you do politics who is reading your secrets</a></li>
<li><a href="https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/How%20not%20to%20get%20got%20making%20content%20v2.html">How to not get got making content</a></li>
<li><a href="https://repo.bnkops.com/What%20is%20a%20Repo%20and%20Why%20Make%20One%20%F0%9F%92%AD%20%F0%9F%8D%84.html">What is a Repo and Why Make One 💭 🍄</a></li>
</ul>
</div>
<div class="free-container">
<h2>It's Truly Free(ish)</h2>
<h3 style="text-align: center;">All we ask in return is your email.</h3>
<p>By joining our platform, you gain access to a system that bnkops excels in. We are a collective dedicated to positive change, and while we do charge for our expertise, your email helps us stay connected and grow together. Consider partnering with us in the future to collaborate with like-minded individuals who are committed to building a better tomorrow.</p>
<p>Our platform is designed to be accessible to everyone, regardless of their financial situation. We believe that everyone should have access to the tools they need to make a difference in the world, and we're committed to providing those tools to you for free.</p>
<p> We ask for your email also so we can reach you with updates; the only way to stay secure is to keep developing and updating our systems. We promise not to spam you, and you can unsubscribe at any time.</p>
</div>
<form method="post" action="https://listmonk.bnkops.com/subscription/form" class="listmonk-form">
<div style="
background-color: #fbbf24;
padding: 1.25rem;
padding-top: 0.5rem;
border-radius: 0.5rem;
color: #1f2937;
width: 100%;
max-width: 800px;
margin: 1rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
animation: glow 2s ease-in-out infinite;
position: relative;
left: 50%;
transform: translateX(-50%);
display: inline-block;
">
<style>
@keyframes glow {
0% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
50% { box-shadow: 0 0 20px rgba(251, 191, 36, 0.4); }
100% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
}
@media (max-width: 1024px) {
.listmonk-form > div {
width: 90% !important;
max-width: 600px !important;
}
}
@media (max-width: 640px) {
.listmonk-form > div {
width: 95% !important;
max-width: none !important;
}
}
</style>
<h3 style="font-size: 1.25rem; font-weight: bold; margin-bottom: 0.75rem; color: #1f2937;">Subscribe</h3>
<input type="hidden" name="nonce" />
<div style="display: flex; gap: 1rem; margin-bottom: 0.5rem;">
<p style="margin: 0; flex: 1;">
<input type="email"
name="email"
required
placeholder="E-mail"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
<p style="margin: 0; flex: 1;">
<input type="text"
name="name"
placeholder="Name (optional)"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
</div>
<p style="display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.75rem 0;">
<input id="038eb"
type="checkbox"
name="l"
checked
value="038eb469-e141-435d-86eb-2ab4df20cf9c"
style="width: 1rem; height: 1rem; accent-color: #1f2937;" />
<label for="038eb" style="font-size: 0.875rem;">Weekly(ish) Update</label>
</p>
<p style="margin: 0;">
<input type="submit"
value="Subscribe"
style="width: 100%; padding: 0.75rem; background-color: #1f2937; color: white; border: none; border-radius: 0.375rem; font-weight: 500; cursor: pointer; transition: all 0.2s; hover: { background-color: #374151; transform: translateY(-1px); }" />
</p>
</div>
</form>

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

View File

@ -0,0 +1,3 @@
# Blog
This is the archive of blog posts from previous bnkops changemaker deployments.

View File

@ -0,0 +1,108 @@
---
author: The Bunker Admin
date:
created: 2024-11-26
pin: true
---
# Admin Logs
The admin log is a one stop shop for all the updates and changes made to the Change Maker system.
<!-- more -->
# Development Path
This app is under intensive development. Updates will be pushed on completion. V4 will aim to be a stable release.
Priority development right now:
- Install script that simplifies public deployment
- Nocodb integration for database
bnkops is working on V4 right now, which will also be free. Core features coming include:
- DNS (domain) hosting services under bnkops.com
- One click install script for public access
- Expanded editor with more standard markdown editing features and content management
- Expanded mobile features, such as content uploading and improved ui
- Options to install local newsletter, email targeter, automation systems, and database integrations
- Meta properties for documentation for states like draft, publish, author, publication time, etc.
## Features for Fun
Time allows we will also work to release some updates that are not core components however are "nice to have".
- Emoji integration with editor
# Updates
## Change Maker V3.8.3 28/11/2024
- More input commands finished up.
- Lots of random bug fixes; line spacing and stuff like that.
- Added drag and drop functionality for the file manager.
## Change Maker V3.8.2 - 26/11/2024
- Fixed a series of bugs and overhauled the insert function for links. Moved it to a static bar at the top of the editor. Like this more for sure. Needs further updates yet however working well.
- Fixed a bunch of lesser bugs; reload triggers and whatnot where generally broken.
## Change Maker V3.8.1 - 25/11/2024
Holy cow a lot of updates done.
- Listmonk up and config files for up and down fixed
- Big rework of Changemaker website, including new landing page and new features
- Lots of bug fixing and general improvements
- wrote some fun special things, like the custom code for things like the music player and newsletter signup.
## Listmonk Integration - 19/11/2024
Version 3.5 ships with Listmonk. Going to integrate a home page for the system; a sort of login page. Listmonk is a fully featured messaging app; allowing messaging across several mediums. Simplicity combined with power; everything we like.
[Listmonk](https://listmonk.app/)
## MkDocs Config Save Button Addition - 04/11/2024
Added manual save functionality for mkdocs.yml to prevent accidental publishing:
- Removed auto-save for config file only
- Added save button (only visible when editing mkdocs.yml)
- Added save status indicators and feedback
- Regular markdown files still auto-save as before
Modified files:
- Editor.js: Added save button + disabled config auto-save
- No backend changes required
Button appears next to settings icon when editing mkdocs.yml.
## Admin Log: Preview URL Toggle Update - 2024/04/11
Added local/production preview toggle in editor UI:
- Added getMkdocsUrl() to constants.js
- Created PreviewToggle component with Local/Prod indicator
- Updated preview panel to use dynamic URL switching
- Default: prod (changemaker.bnkops.com)
- Toggle: local (localhost:4000)
Files modified:
- constants.js: Added URL switcher
- Editor.js: Added toggle + URL handling
- New: PreviewToggle.js
No breaking changes. Maintains all existing functionality.
## Change Maker V3.2 - Nov. 4 2024
V3.2 updates:
- Reworking of position of link suggestions and improved speed in search.
## Change Maker V3.1 - Nov. 4 2024
Change Maker V3.1 includes the following updates:
- Improved linking systems; changed from using wikilinks to standard markdown links for compatibility with site builder
- General bug fix's qr

View File

@ -0,0 +1,95 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2025-01-14
---
# Newsletter 1.2
Hay! It's Reed. A rant, some media, and a ask inside; whatcha think? check it out? 🤔🤷‍♂️
<!-- more -->
Yoooooooooo,
Suh friend! In this update I rant about Facebook, provide some media, and have a ask for you. Check it out below!
# Facebook Deleted!
<div style="text-align: center;">
<img src="https://listmonk.bnkops.com/uploads/Mark-Zuckerberg-1_JsUXea.webp" alt="NocoDB" style="max-width: 100%; height: auto; margin: 20px 0;">
</div>
Facebook is swiftly going to follow Twitter in toxicity. Invest elsewhere or fall to the trap of arguing with conservative bots. Like legit, [Facebook is leaning into bots](https://www.theverge.com/2025/1/3/24334946/meta-ai-profiles-instagram-facebook-bots), and for sure bad actors are salivating at the chance to unleash programmatic content. The same company that caused BREXIT WITH JUST MEMES now has AI posting on it's own. They tried to "sell" it to you. Its joever. Every and any account could be a bot. It is simple to take your likeness. They don't respect your data; well proven. There comes a day when Facebook sells digital ghosts; the ability to talk to a dead loved one. Literal demon daemons.
**Dead! Dusted! Deleted!** My personal account down.
Well, in 30 days. Facebook knows it's a addictive thing and it tries super hard to impress the value of it's service as you leave. It took like a dozen clicks, confirmations, lists of things, surveys, and bunch of other stuff just to be told my info won't be up for deletion for 30 days. So will see if we go back 🤷‍♂️
<div style="text-align: center;">
<img src="https://listmonk.bnkops.com/uploads/Facebook-Matrix_D5HYY5.webp" alt="NocoDB" style="max-width: 100%; height: auto; margin: 20px 0;">
</div>
Anyway, it actually did feel something doing this. Right on the delete button, I could feel my stomach somewhat drop. Some sort of anxiety, like the same feeling coming off a drug. Take it from a addict, those sweaty palms for sure a sign of how addictive this thing is.
To combat that, here I am sharing. Hope to hear from you. Legit is me over here typing away.
<div style="text-align: center;">
<img src="https://listmonk.bnkops.com/uploads/20241102_224555_9IXgDf.jpg" alt="NocoDB" style="max-width: 100%; height: auto; margin: 20px 0;">
</div>
---
Try out [Change Maker](https://changemaker.bnkops.com/manuals/)? I use the system to make this newsletter :)
### [Code Releases](https://drive.proton.me/urls/Q83BEPSPHC#sP5y9Zh8rWlp)
### Password: Hurray!
---
# Found Media
I love old, weird, odd, and often times horrific media. Like gory, rough, catholically evil things.
<div style="text-align: center;">
<img src="https://listmonk.bnkops.com/uploads/giphy-2378776604_Ru91Jr.gif" alt="NocoDB" style="max-width: 100%; height: auto; margin: 20px 0;">
</div>
**[Berserk](https://archive.org/details/berserk-episode-16)** is one of these kinds of show. It tortures. It is evil. It is gross. Toxic. Male gazy, because shocker, the leading characters are men. I project transness onto the character Griffith and probably will do a cosplay sometime. Death is prolific throughout. However the absolute destruction of the characters, the way it builds them up for the evil, is next to none. It leaves a solid slime of 'wtf' on the brain.
Talking about catholically evil things; this take-down by **[CJ the X of Jordan Peterson suits](https://www.youtube.com/watch?v=LpHFcylNGqg)** is sublime. Plus you can learn a bunch about suits while watching it.
Okay for one more evil recommendation; have you seen squid game? Did you like it?
You would love **[Psycho Pass](https://www.youtube.com/results?search_query=pyschopass)**. This show passes itself off as a copy buddy show at the start, and has plenty of tropes, however, the representation of the absolute nature of a surveillance state combined with out-right "brain-in-a-jar" facist thinking is a combination that is surreal. I am 2/3rds done and best guess is that the show argues how if we live in the surveillance [panopticon](https://en.wikipedia.org/wiki/Panopticon) and the state still has authority; it will abuse its power. It grapples with the question; what happens when the state is the only remaining source of violence?
Season two also has a wonderful line about medical care for mental health; healthcare, without thought for the individual, is also violent in nature. For each, their own, is the only way. This is why we must protect peoples rights to self-determiniation, even those we consider criminal.
---
# Help!
Friend, I need your help. I need to get out and more and chat with people. I have already written up a bunch of content that I am hoping to chat with people about. I need a hand sharing it. Do you think anyone in your network would like to chat more about:
- [What distributed digital organizing is](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/Distributed%20Digital%20Organizing%20is%20The%20Way%20Out.html)
- [How to do politics online without supporting billionaire oligarchs who want your every secret](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/If%20you%20do%20politics%20who%20is%20reading%20your%20secrets%20-%20why%20you%20should%20de-corp%20your%20software%20stack.html)
- [Ways to build followings and not get got in the age of surveillance capitalism](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/How%20not%20to%20get%20got%20making%20content%20v2.html)
This email is tied to my personal inbox. If you share this and introduce me to a colleague I would be thrilled and will owe you a solid.
If you would like to chat any of the above, or just be pen pals through email, it would be wonderful. I am embracing slower and thoughtful communication; come along with me?
---
Anyway, I think that is all for now folks! Wishing y'all peace and prosperity.
<div style="text-align: center;"><img src="https://listmonk.bnkops.com/uploads/20250105_144528_yJaDNl.jpg" alt="image" style="max-width: 100%; height: auto; margin: 20px 0;"></div>

View File

@ -0,0 +1,523 @@
---
Author: The Bunker Admin
date:
created: 2024-11-20
---
# The Revoltion Will Not Get Likes
<!DOCTYPE html>
<html>
<head>
<style>
:root {
--neon-purple: #b026ff;
--neon-blue: #00fff2;
--neon-pink: #ff2d82;
}
.glitch-container {
width: 300px;
height: 300px;
position: relative;
margin: 20px 0;
background: #1a1b1e;
overflow: hidden;
cursor: none;
box-shadow:
0 0 20px var(--neon-purple),
inset 0 0 30px rgba(176, 38, 255, 0.3);
border: 2px solid rgba(176, 38, 255, 0.2);
}
.logo {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: left;
align-items: left;
color: var(--neon-blue);
font-size: 120px;
opacity: 0;
animation: cycle 8s infinite;
filter: drop-shadow(0 0 5px var(--neon-blue));
}
.target-cursor {
width: 80px;
height: 80px;
position: absolute;
pointer-events: none;
transform: translate(-50%, -50%);
animation: cursorMove 8s infinite;
z-index: 100;
mix-blend-mode: screen;
}
.target-cursor::before,
.target-cursor::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
animation: targetPulse 1.5s infinite;
}
.target-cursor::before {
width: 60px;
height: 60px;
border: 2px solid var(--neon-pink);
box-shadow: 0 0 10px var(--neon-pink);
}
.target-cursor::after {
width: 20px;
height: 20px;
background: var(--neon-pink);
box-shadow: 0 0 15px var(--neon-pink);
}
.target-cursor .crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80px;
height: 80px;
}
.target-cursor .crosshair::before,
.target-cursor .crosshair::after {
content: '';
position: absolute;
background: var(--neon-pink);
box-shadow: 0 0 5px var(--neon-pink);
}
.target-cursor .crosshair::before {
width: 2px;
height: 80px;
left: 50%;
transform: translateX(-50%);
}
.target-cursor .crosshair::after {
width: 80px;
height: 2px;
top: 50%;
transform: translateY(-50%);
}
.logo:nth-child(1) { animation-delay: 0s; }
.logo:nth-child(2) { animation-delay: 2s; }
.logo:nth-child(3) { animation-delay: 4s; }
.logo:nth-child(4) { animation-delay: 6s; }
@keyframes targetPulse {
0%, 100% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
50% {
opacity: 0.5;
transform: translate(-50%, -50%) scale(1.2);
}
}
@keyframes cursorMove {
0%, 20% {
top: 80%;
left: 20%;
}
21% {
opacity: 1;
}
22% {
opacity: 0.3;
transform: translate(-50%, -50%) rotate(45deg) scale(1.2);
}
23% {
opacity: 1;
transform: translate(-50%, -50%) rotate(-45deg) scale(1);
}
24%, 45% {
top: 50%;
left: 50%;
}
46% {
opacity: 1;
transform: translate(-50%, -50%) rotate(0deg);
}
47% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.8) rotate(90deg);
}
48% {
opacity: 1;
transform: translate(-50%, -50%) scale(1.1) rotate(-90deg);
}
49%, 70% {
top: 50%;
left: 50%;
}
71% {
opacity: 1;
}
72% {
opacity: 0;
transform: translate(-50%, -50%) skew(10deg, 10deg);
}
73% {
opacity: 1;
transform: translate(-50%, -50%) skew(-10deg, -10deg);
}
74%, 95% {
top: 50%;
left: 50%;
}
100% {
top: 80%;
left: 20%;
}
}
@keyframes cycle {
0%, 5% {
opacity: 0;
transform: translateY(-20px) skew(0deg);
filter: brightness(1) hue-rotate(0deg);
}
8% {
opacity: 0.8;
transform: translateY(0) skew(-5deg);
filter: brightness(1.5) hue-rotate(45deg);
}
9% {
transform: skew(5deg);
filter: brightness(2) hue-rotate(-45deg);
}
10%, 15% {
opacity: 0.8;
transform: skew(0deg);
filter: brightness(1.2) hue-rotate(0deg);
}
16% {
opacity: 0.8;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
17% {
clip-path: polygon(0 10%, 100% 0, 100% 90%, 0 100%);
transform: translateX(5px);
filter: brightness(2) hue-rotate(90deg);
}
18% {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transform: translateX(-5px);
}
19% {
clip-path: polygon(0 5%, 100% 10%, 100% 85%, 0 90%);
transform: translateY(5px);
filter: brightness(2.5) hue-rotate(-90deg);
}
20% {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transform: translateY(0);
}
20.1%, 25% {
opacity: 0.8;
}
26% {
opacity: 0;
transform: scale(1.2);
filter: brightness(2);
}
27%, 100% {
opacity: 0;
transform: scale(1);
}
}
.emoji-rain {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.emoji-column {
position: absolute;
top: -20px;
font-family: 'Apple Color Emoji', 'Segoe UI Emoji', sans-serif;
font-size: 24px;
line-height: 1;
white-space: nowrap;
transform: translateY(0);
opacity: 0;
filter: drop-shadow(0 0 3px var(--neon-purple));
animation: emojiRain var(--fall-duration) linear infinite;
animation-delay: var(--fall-delay);
}
.emoji-column span {
display: block;
margin: 5px 0;
transform: scale(var(--scale));
opacity: var(--opacity);
transition: all 0.3s ease;
}
@keyframes emojiRain {
0% {
opacity: 0;
transform: translateY(-20px) rotate(-10deg);
}
10% {
opacity: 0.8;
}
90% {
opacity: 0.8;
}
100% {
opacity: 0;
transform: translateY(120px) rotate(10deg);
}
}
.scanner {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
transparent 0%,
rgba(176, 38, 255, 0.1) 50%,
transparent 100%
);
animation: scan 4s linear infinite;
}
@keyframes scan {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(100%);
}
}
</style>
</head>
<body>
<div class="glitch-container">
<div class="target-cursor">
<div class="crosshair"></div>
</div>
<div class="logo">
<i class="fab fa-twitter"></i>
</div>
<div class="logo">
<i class="fab fa-github"></i>
</div>
<div class="logo">
<i class="fab fa-linkedin"></i>
</div>
<div class="logo">
<i class="fab fa-discord"></i>
</div>
<div class="scanner"></div>
<div class="emoji-rain">
<script>
const emojis = ['👍', '❤️', '🔥', '💯', '⭐', '🙌', '💫', '✨'];
const container = document.querySelector('.emoji-rain');
function createEmojiColumn() {
const column = document.createElement('div');
column.className = 'emoji-column';
column.style.setProperty('--fall-duration', `${2 + Math.random() * 2}s`);
column.style.setProperty('--fall-delay', `${Math.random() * 2}s`);
column.style.left = `${Math.random() * 100}%`;
// Add 3-4 random emojis to each column
const numEmojis = 3 + Math.floor(Math.random() * 2);
for (let i = 0; i < numEmojis; i++) {
const span = document.createElement('span');
span.textContent = emojis[Math.floor(Math.random() * emojis.length)];
span.style.setProperty('--scale', 0.8 + Math.random() * 0.4);
span.style.setProperty('--opacity', 0.7 + Math.random() * 0.3);
column.appendChild(span);
}
container.appendChild(column);
// Remove the column after animation
setTimeout(() => {
column.remove();
}, 4000);
}
// Create new emoji columns periodically
setInterval(createEmojiColumn, 500);
</script>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
</body>
</html>
<!-- more -->
## The Social Media Harm Machine
There isn't a single activist in my social circles that actually _wants_ to be on social media. Being a social media manager for a movement is like being voluntarily assigned to digital self-harm duty. Getting doxxed has become such a rite of passage for leftists, we might as well start handing out merit badges. Meanwhile, platforms are openly throttling progressive content while boosting blonde trad wives explaining why women shouldn't have opinions. X has basically become 4chan's corporate cousin, and yet here we all are, still posting.
## Why Though? Like, Seriously, Why?
Why do our movements keep feeding energy into platforms owned by techbro capitalists who are literally selling our DMs to cops? Why give free content to companies that see our existence as a Terms of Service violation?
Let's call it what it is: **Addiction**.
I (your friendly neighborhood bnkops admin) am straight up admitting it I'm an addict. The doomscroll is real. My amygdala is basically playing Russian roulette every time I refresh: will it be a happy puppy or literal war crimes? Vegas slots probably have better odds for my mental health.
## Breaking Free (No Judgment Zone)
Like any addiction, people need to find their own path out. I've gone cold turkey on mobile apps. Started writing on platforms I actually control. Pro tip: try going phone-free for just one hour a day. A 15-minute walk without your digital pacifier hits different clarity comes through like [*chef's kiss*].
No shade though addiction is addiction. And like any vulnerability, oppressors know exactly how to weaponize it. The right wing troll swarms are like digital locusts, except locusts eventually leave. Try blocking the endless wave of MAGA bots it's like playing whack-a-mole with a broken mallet.
## The Dystopia Is Not Coming - It's Livestreaming
These platforms are increasingly becoming real-world threat multipliers. Elon Musk and Trump have basically built digital brownshirt armies. Coming soon to a Twitter near you: Musk playing innocent about how his "metaphorical" AI-generated hate posts led to actual violence.
We have written at length about how leftists are making themselves vulnerable to attack by being on these platforms. The time to transition off was yesterday.
## The Liberal Delusion
Liberals be like "don't worry, privacy laws will save us!" while we're already living in cyberpunk minus the cool aesthetics. Surveillance capitalism has pulled off the ultimate protection racket pay us or you're not safe (but also you're not safe if you do pay us).
The liberal brain rot runs so deep they're trusting Elon "Definitely Not A Bond Villain" Musk with global satellite communications. Local activists using Gmail might as well CC their strategy docs directly to the opposition. Bonus points: your entire contact list gets implicated too!
## It's Getting Worse, Fast
With the subtlety of a brick through a Starbucks window, these platforms are ramping up suppression of any non-capitalist thought. Today it's shadowbans, tomorrow it's sharing your location with "concerned authorities," and next week it's "predictive threat assessment" (aka pre-crime).
Straight bet: when mass deportations hit the US, every major social platform will have their data ready to go faster than you can say "just following orders."
## Your Digital Exit Strategy (You're Gonna Need It)
If you're running an org heavy on social, here's your homework:
- Export your followers/following lists NOW. When (not if) you get banned, you'll want that rolodex
- Get those emails and phone numbers like you're a club promoter in 2005
- Meet your actual neighbors (yes, the ones behind those scary doors called "outside")
- Return to analog: Flyers, stickers, graffiti. Make the streets your timeline
## Money Talks, So Stop Paying Your Oppressors
Stop throwing money at social media ads. You're basically paying your executioner for a slightly sharper axe. If you must spend cash on promotion:
- Plaster every telephone pole in a 5-mile radius
- Become the person coffee shop bulletin boards fear
- Make your car a rolling billboard
- Find local podcasters who aren't Joe Rogan clones
- Turn the subway into your personal art gallery
## The Offline Revolution
Remember: The revolution will not be monetized. It won't have sponsored content. It won't be algorithm-optimized. And it definitely won't fit in your Instagram grid.
But it might just start with a conversation at your local coffee shop, a flyer in someone's hand, or a really spicy piece of bathroom graffiti.
## Read More
Want more info to covince you to leave these platforms:
- [Twitter is Dead, Long Live X](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/Twitter%20is%20Dead%2C%20Long%20Live%20X%20---%20Why%20You%20Should%20Abandon%20X.html)
- [Are you a leftist? Who is reading your secrets?](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/If%20you%20do%20politics%20who%20is%20reading%20your%20secrets%20-%20why%20you%20should%20de-corp%20your%20software%20stack.html)
- [Distributed Digital Organizing is the Way Out](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/Distributed%20Digital%20Organizing%20is%20The%20Way%20Out.html)
- [How To Not Get Got Making Content](https://repo.bnkops.com/thatreallyblondehuman/Thoughts%20%F0%9F%A4%94/Distributed%20Digital%20Organizing%20is%20The%20Way%20Out.html)
_"The master's tools will never dismantle the master's house, but they might help us build our own somewhere else."_
Audre Lorde (but make it digital resistance).
<div class="conclusion-container" style="background: #1a1b1e; padding: 2rem; border-radius: 8px; margin: 2rem 0;">
<div class="glitch-text" style="color: #00fff2; font-size: 2rem; margin-bottom: 1.5rem; text-shadow: 2px 2px 8px rgba(0, 255, 242, 0.5); animation: glitch 3s infinite;">
Remember: You Are Not Your Likes
</div>
<div class="fade-in-text" style="color: #fff; line-height: 1.6; animation: fadeIn 2s ease-in;">
<p>The revolution will not be monetized.</p>
<p>The revolution will not have sponsored content.</p>
<p>The revolution will not be algorithm-optimized.</p>
<p>The revolution will not be in your feed.</p>
</div>
<div class="action-box" style="background: rgba(176, 38, 255, 0.1); border: 1px solid #b026ff; padding: 1.5rem; margin: 2rem 0; border-radius: 4px; box-shadow: 0 0 15px rgba(176, 38, 255, 0.2);">
<h3 style="color: #ff2d82; margin-bottom: 1rem;">Immediate Actions:</h3>
<ul style="color: #fff; list-style-type: none; padding: 0;">
<li style="margin: 0.5rem 0; display: flex; align-items: center;">
<span style="color: #00fff2; margin-right: 10px;"></span>
Export your follower data today
</li>
<li style="margin: 0.5rem 0; display: flex; align-items: center;">
<span style="color: #00fff2; margin-right: 10px;"></span>
Start building your physical mailing list
</li>
<li style="margin: 0.5rem 0; display: flex; align-items: center;">
<span style="color: #00fff2; margin-right: 10px;"></span>
Set up local communication networks
</li>
<li style="margin: 0.5rem 0; display: flex; align-items: center;">
<span style="color: #00fff2; margin-right: 10px;"></span>
Plan your platform exit strategy
</li>
</ul>
</div>
<div class="final-quote" style="color: #ff2d82; font-size: 1.2rem; text-align: center; margin: 2rem 0; font-style: italic; text-shadow: 2px 2px 8px rgba(255, 45, 130, 0.3);">
"The master's tools will never dismantle the master's house."<br>
- Audre Lorde
</div>
</div>
<style>
@keyframes glitch {
0% {
transform: translate(0);
text-shadow: 2px 2px 8px rgba(0, 255, 242, 0.5);
}
20% {
transform: translate(-2px, 2px);
text-shadow: -2px -2px 8px rgba(255, 45, 130, 0.5);
}
40% {
transform: translate(2px, -2px);
text-shadow: 2px -2px 8px rgba(176, 38, 255, 0.5);
}
60% {
transform: translate(-2px, -2px);
text-shadow: 2px 2px 8px rgba(0, 255, 242, 0.5);
}
80% {
transform: translate(2px, 2px);
text-shadow: -2px 2px 8px rgba(255, 45, 130, 0.5);
}
100% {
transform: translate(0);
text-shadow: 2px 2px 8px rgba(0, 255, 242, 0.5);
}
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>

View File

@ -0,0 +1,139 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2024-11-29
---
# Update
This is a test
<!-- more -->
![image.png](image.png)
[Why Change Maker](Why Change Maker.md)
[Test](https://repo.bnkops.com/Home.html){ .md-button }
!!! note "Test"
Hello this is a test note
test (1)
{ .annotate }
1. this is a test annotation
**bold**
<div class="grid cards" markdown>
- __Test__
---
Test
- __Test__
---
Test
- __Test__
---
Test
- __TEst__
---
TEst
</div>
<form method="post" action="https://listmonk.bnkops.com/subscription/form" class="listmonk-form">
<div style="
background-color: #fbbf24;
padding: 1.25rem;
padding-top: 0.5rem;
border-radius: 0.5rem;
color: #1f2937;
width: 100%;
max-width: 800px;
margin: 1rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
animation: glow 2s ease-in-out infinite;
position: relative;
left: 50%;
transform: translateX(-50%);
display: inline-block;
">
<style>
@keyframes glow {
0% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
50% { box-shadow: 0 0 20px rgba(251, 191, 36, 0.4); }
100% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
}
@media (max-width: 1024px) {
.listmonk-form > div {
width: 90% !important;
max-width: 600px !important;
}
}
@media (max-width: 640px) {
.listmonk-form > div {
width: 95% !important;
max-width: none !important;
}
}
</style>
<h3 style="font-size: 1.25rem; font-weight: bold; margin-bottom: 0.75rem; color: #1f2937;">Subscribe</h3>
<input type="hidden" name="nonce" />
<div style="display: flex; gap: 1rem; margin-bottom: 0.5rem;">
<p style="margin: 0; flex: 1;">
<input type="email"
name="email"
required
placeholder="E-mail"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
<p style="margin: 0; flex: 1;">
<input type="text"
name="name"
placeholder="Name (optional)"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
</div>
<p style="display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.75rem 0;">
<input id="038eb"
type="checkbox"
name="l"
checked
value="038eb469-e141-435d-86eb-2ab4df20cf9c"
style="width: 1rem; height: 1rem; accent-color: #1f2937;" />
<label for="038eb" style="font-size: 0.875rem;">Weekly(ish) Update</label>
</p>
<p style="margin: 0;">
<input type="submit"
value="Subscribe"
style="width: 100%; padding: 0.75rem; background-color: #1f2937; color: white; border: none; border-radius: 0.375rem; font-weight: 500; cursor: pointer; transition: all 0.2s; hover: { background-color: #374151; transform: translateY(-1px); }" />
</p>
</div>
</form>
Hay send this email!
Imagine the email content here!
[test](mailto:admin@thebunkerops.ca){ .md-button }

View File

@ -0,0 +1,116 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2024-12-04
---
# Change Maker Update V3.8.7
Okay so we have a decent new update! Introducing
- **Landing Page** - Change Maker landing page for navigating application.
- **NocoDB** - Database management for all and any data.
<!-- more -->
# Landing Page
![landing1](landing1.png)
The landing page is our newest custom built addition to the Change Maker system. It is going to be the central hub for managing the applications of Change Maker.
The User Management grants different users access to the Landing Page and is only visible to the account administrator.
!!! Warning "Security"
The landing page is not yet secure for production use. Make sure to set access rules inside each application based on documentation. We also recommend using Cloudflare Zero Trust access rules to secure applications for two-factor authentication.
# [NocoDB]( https://docs.nocodb.com/)
![database](database.png)
### "NocoDB is a no-code database platform that allows teams to collaborate and build applications with ease of a familiar and intuitive spreadsheet interface. This allows even non-developers or business users to become software creators." - NocoDB
This is such a powerful app! Import data from any source *cough Nationbuilder cough* and start building value based on that data. You can make forms, kabans, and move data between Change Maker databases with ease. As we grow out the network of apps, this database center will be what our automation systems reference and Change Maker dashboards are built on. A great way to save a tonne of dollars is to reduce how many contacts you store elsewhere and start storing them for free locally.
[NocoDB Manual](/manuals/NocoDB/){ .md-button }
---
So if you are on the mailing list these features are in Change-Maker-V3.8.7. Sign up for the newsletter to be kept up to date with releases:
<form method="post" action="https://listmonk.bnkops.com/subscription/form" class="listmonk-form">
<div style="
background-color: #fbbf24;
padding: 1.25rem;
padding-top: 0.5rem;
border-radius: 0.5rem;
color: #1f2937;
width: 100%;
max-width: 800px;
margin: 1rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
animation: glow 2s ease-in-out infinite;
position: relative;
left: 50%;
transform: translateX(-50%);
display: inline-block;
">
<style>
@keyframes glow {
0% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
50% { box-shadow: 0 0 20px rgba(251, 191, 36, 0.4); }
100% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
}
@media (max-width: 1024px) {
.listmonk-form > div {
width: 90% !important;
max-width: 600px !important;
}
}
@media (max-width: 640px) {
.listmonk-form > div {
width: 95% !important;
max-width: none !important;
}
}
</style>
<h3 style="font-size: 1.25rem; font-weight: bold; margin-bottom: 0.75rem; color: #1f2937;">Subscribe</h3>
<input type="hidden" name="nonce" />
<div style="display: flex; gap: 1rem; margin-bottom: 0.5rem;">
<p style="margin: 0; flex: 1;">
<input type="email"
name="email"
required
placeholder="E-mail"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
<p style="margin: 0; flex: 1;">
<input type="text"
name="name"
placeholder="Name (optional)"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
</div>
<p style="display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.75rem 0;">
<input id="038eb"
type="checkbox"
name="l"
checked
value="038eb469-e141-435d-86eb-2ab4df20cf9c"
style="width: 1rem; height: 1rem; accent-color: #1f2937;" />
<label for="038eb" style="font-size: 0.875rem;">Weekly(ish) Update</label>
</p>
<p style="margin: 0;">
<input type="submit"
value="Subscribe"
style="width: 100%; padding: 0.75rem; background-color: #1f2937; color: white; border: none; border-radius: 0.375rem; font-weight: 500; cursor: pointer; transition: all 0.2s; hover: { background-color: #374151; transform: translateY(-1px); }" />
</p>
</div>
</form>

View File

@ -0,0 +1,78 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2025-01-09
---
# Update 1.0
Hello new friends! Thanks for joining us on this journey. It has been awhile since we provided a update and so we have a lot of exciting updates to share with you. These include:
- Development
- AI Model Hosting
- User Management System
- Updated User Interface
- Simple Button
<!-- more -->
## Development
The next quarter we are focused on two things: fast value adds and stable production ready installation scripts. This means we are going to be adding new, already secure, and production ready applications to the Change Maker system and updating the current applications to be more stable and secure on installation.
Following the addtion of the fast value applications and a stable installation script, we will begin the process of integratting all the applications across the system. The original peices of Change Maker, like the tech running the [repo](https://repo.bnkops.com/Home.html), will also be updated to more stable and secure systems.
### Fast Value
We have currently flagged the followng applications for integration into Change Maker:
- **HiEvents** is a event management software. They offer effortless event management for conferences, nightclubs, and everything in between.
- **Mattermost** is a full slack alternative. They offer full data sovereignty and security for your teams sensitive communications.
- **BluSky** is a federated social media platform. Using your existing Change Maker domain, you can host your own BluSky server for complete control over your social media presence.
- **Budibase** is a low-code app builder. It serves as a comprehensive alternative to Google Forms and Sites, enabling the creation of applications like Email Targeter.
- **n8n** is a low-code automation platform. As the leading automation software for technical teams, it may enable seamless automation across your entire Change Maker installation.
- **Monica** is a relationship-focused CRM system. It excels in politics by prioritizing genuine connections and offering clear visualization of households and community relationships.
- **Answers** is a team-oriented Q&A platform. It streamlines volunteer organization and helps teams quickly understand various campaign roles and responsibilities.
### Production Ready Installation Scripts
General development is focused getting the current build-out to production ready with a one command install on most standard operating systems. This will include a new configuration script and a new start script with more detailed instructions on how to up Change Maker.
### Timing
We suspect this round of development to take 3-4 months, depending on other workloads. If anyone is interested in helping out, please reach out to us at [admin@bnkops.ca](mailto:admin@bnkops.ca).
## AI Model Hosting
We are also working to move all our systems of of Meta products; this includes this the models running [Daisy AI](https://repo.bnkops.com/Daisy%20AI%20%F0%9F%8C%BB/Daisy.html).
We are also working on building our first server to host AI models so we can move our entire development stack to a secure and private environment.
## User Management System
Right now we ask users to create access rules for the Change Maker network of apps using [Cloudflare Zero Trust](https://www.cloudflare.com/en-gb/products/zero-trust/). As a alternative to this cloud system, we are going to be integrating [Keycloak](https://www.keycloak.org/) into the Change Maker system ASAP. This will allow for a more secure and robust user management system that is localized to the installation device and can be used across all Change Maker applications.
## Updated User Interface
The system also now ships with a updated login page for the landing page, landing page, and introduces dark mode to the editor.
![alt text](image-1.png)
![alt text](image-2.png)
![alt text](image-3.png)
## Simple Button
Simple Button is now available! You can find it under the [**Simple Button**](https://changemaker.bnkops.com/manuals/button) page on our manuals site. We are going to integrate the Simple Button into the Change Maker system to allow for easy campaign button integration.

View File

@ -0,0 +1,28 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2025-01-09
---
# Update 4.0
Okay taking notes as I go this time around. Follow along as I do a clean up of the Change Maker system.
<!-- more -->
## Development
Okay starting off by doing a file clean-up. Lots of baggage in the system right now.
1. Cleaned up the code and home.html file.
2. Creating a stremalined home page.
DS

View File

@ -0,0 +1,48 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2025-02-19
---
# Changemaker 3.9.4
We have jumped several updates! A quick overview:
# New Functionality
## Monica CRM
Changemaker now bundles with Monica! This CRM is geared towards personal and familiar relations; way more useful for mapping connections between people then a sales based CRM.
## Annoucement Bar Editor
Users can now edit the annoucement bar from inside changemaker itself.
![alt text](image-4.png)
Also updated the editor panels for the home.html and the mkdocs.yml for consitent styling across all editor panels.
## Simple Button Updates
Simple Buttons now have a `copy` features for all text components, making them more versatile and usable even if the user doesnt have a emial client configured. Clicking the `send email` button now also automatically copies email body text to clipboard.
![alt text](image-5.png)
## Onboarding Flow
A all new first build experience is completed, which steps the user through deploying their first Changemaker
![alt text](image-6.png)
## General Bug Fixes
- Cleaned up landing page and env files
- Moved a lot of configuration to root
- Fixed bug for saving the home.html
- Moved the home.html template editor to main panel.
- Updated all the editor panels.

View File

@ -0,0 +1,33 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2025-03-03
---
# Changemaker 3.9.9
Big update! We have officially gotten [Syncthing](https://syncthing.net/) integrated! Rigorous testing is still needed to find out hitches however any docs written with this system now can be shared directly and built with another ChangeMaker system; also opening the door for connected & communicative systems.
[The original theory is coming together](https://repo.bnkops.com/What%20is%20a%20Repo%20and%20Why%20Make%20One%20%F0%9F%92%AD%20%F0%9F%8D%84.html).
## Next Objectives
**Video Server**
I have decided to park finding a video hosting solution for the time being; this may require that bnkops just hosts a dedicated web server node. Trying to build this using the changemaker script would be tricky however hosting it on a dedicated machine a lot easier.
Looking into MediaCMS: https://github.com/mediacms-io/mediacms
And also looking into Owncast: https://owncast.online/
**Pangolin**
A open sourced self-hosted alternative to cloudflared tunnels has been released: https://github.com/fosrl/pangolin?tab=readme-ov-file

View File

@ -0,0 +1,23 @@
---
# For full documentation on blog post settings, see:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-a-blog/#usage
draft: false
authors:
- The Bunker Admin
date:
created: 2025-03-20
---
# Changemaker 3.9.9.1
Okay latest update is Apache Answers added to the system.
Tonnes of backend support stuff and bug fixes:
- new system for domain updating (still needs some work)

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -0,0 +1,11 @@
---
date:
created: 2023-12-31
---
# Learning the Blog System
Just now learning how the blog system works in mkdocs.
<!-- more -->
Never actually had to use this before; was not creating content like this. I think this is how we will integrate the changelog and other updates. Further, want to make any posts made in the listmonk to show up as blog posts, if wanted.

View File

@ -0,0 +1,25 @@
---
title: Welcome to Bnkops Change Maker V3
template: home-cm-archive.html
---

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -0,0 +1,7 @@
# Known Bugs
## Blog in Editor
- A person can open the blog folder and its contents however the live preview is not able to update.
- we need a backup instructional page.

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,320 @@
# Backend & Editor
<!DOCTYPE html>
<html>
<head>
<style>
.features-container {
background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
border-radius: 16px;
padding: 2rem;
color: #e0e0e0;
font-family: system-ui, -apple-system, sans-serif;
width: 100%;
max-width: 1200px;
margin: 1rem auto;
}
.title {
color: #FFD700;
font-size: 2rem;
margin-bottom: 1rem;
font-weight: bold;
}
.subtitle {
color: #e0e0e0;
font-size: 1.1rem;
margin-bottom: 2rem;
line-height: 1.6;
}
.features-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
max-width: 1000px;
margin: 0 auto;
}
.feature-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 1.2rem;
border-left: 3px solid #FFD700;
transition: transform 0.3s ease, box-shadow 0.3s ease;
position: relative;
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.feature-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(255, 215, 0, 0.05), transparent);
transform: translateX(-100%);
transition: transform 0.5s ease;
}
.feature-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.feature-card:hover::before {
transform: translateX(100%);
}
.feature-icon {
font-size: 1.2rem;
margin-bottom: 0.8rem;
color: #FFD700;
display: flex;
align-items: center;
gap: 0.5rem;
}
.feature-title {
color: #FFD700;
font-size: 1rem;
margin-bottom: 0.5rem;
font-weight: 600;
}
.feature-description {
color: #b0b0b0;
font-size: 0.85rem;
line-height: 1.4;
flex-grow: 1;
}
@media (max-width: 1000px) {
.features-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 640px) {
.features-grid {
grid-template-columns: 1fr;
}
.features-container {
padding: 1.5rem;
}
.title {
font-size: 1.5rem;
}
.subtitle {
font-size: 1rem;
}
}
</style>
</head>
<body>
<div class="features-container">
<p class="subtitle">
A powerful documentation system that puts you in control. Edit your content locally or remotely with plain text and Markdown, while seeing your changes instantly.
</p>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">📝 Local-First Editing</div>
<p class="feature-description">All content is stored locally in plain text and Markdown format. Edit with your favorite tools, no vendor lock-in.</p>
</div>
<div class="feature-card">
<div class="feature-icon">👁️ Live Preview</div>
<p class="feature-description">See your changes instantly with built-in preview. No need to save or publish - everything updates in real-time.</p>
</div>
<div class="feature-card">
<div class="feature-icon">📱 Multi-Environment</div>
<p class="feature-description">Work seamlessly across desktop and mobile environments. Your workflow, your choice.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔧 Full Customization</div>
<p class="feature-description">Access and modify any part of the system. Your site, your rules, your customizations.</p>
</div>
</div>
</div>
</body>
</html>
## Desktop Environment
Upon first build, bnkops Change Maker V3 comes bundled with the current theme, documentation, and front-page. All can be updated within the app in plain text and notes are written using [Markdown](Simple Markdown Guide.md).
![backend.png](https://changemaker.bnkops.com/backend.png)
### Live Preview
Click the double headed arrow to open and close live preview panel. The live preview panel shows your site updates inside the editor live. All changes to .md files are automatically pushed to site; no need to save or manage publication details. V4 will ship with state management (publish, draft, etc.) however if you want to work with drafts we suggest that users write files in [obsidian](obsidian.md) and then copy/paste content to site.
![alt text](image-11.png)
### Files
You will have a files section, which allows you to create, delete, and organize your websites structure.
!!! note "Hidden Content"
Folders that start with a period (.example) are hidden from navigation in cmeditor and public site. If using [obsidian](obsidian.md) as content management system, this allows for folders to contain files, images, gifs, and website organization essentials without making them public. V4 will come with more fine grain control of what files are served to public and cmeditor.
![](files2.png)
### Editor
The editor is where you can write and edit your content. The editor is a plain text editor that supports markdown. [Markdown](Simple Markdown Guide.md) is a simple way to format text that is easy to read and write.
![alt text](image.png)
### Backlinks
The backlinks panel on the right hand side is where your you can monitor your internal linking. See where all your files are being referenced and if internal linking is working properly.
![Backlinks](backlinks.png)
### Settings
Along the top bar are the settings. The double arrows opens/closes the live preview. The cog icon opens the `mkdocs.yml` which is how you set your sites features, plugins, and layout. The table icon opens the `home.html` in which you can edit your websites home page.
![alt text](image-1.png)
### mkdocs.yml
![alt text](image-2.png)
The mkdocs.yml is accessed by clicking the cog icon or by opening the mkdocs.yml file inside your top level /Change Maker V3 folder. This is a plain text file. Edits made to this file, upon save, reload site and update universally. If preview shows site down, it is because syntax for your .yml file is incorrect. To fix, you can check the guides, or you can ask any capable LLM for assistance. See [Customize Your Landing Page](Customize%20Your%20Landing%20Page.md) for prompting manual.
Change Maker ships with several dozen features pre-installed and a navigation tree. Features, plugins, and references can be found on the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/setup/) site. Many future features will ship as features that are enabled through this plain text system.
[Check out Material for MkDocs Documentation](https://squidfunk.github.io/mkdocs-material/setup/){ .md-button }
#### Change Maker mkdocs.yml
??? "mkdocs.yml Configuration"
```yaml
site_name: Change Maker # The name of the site
site_description: A powerful documentation system combining simplicity with professional features. # A brief description of the site
site_url: https://changemaker.bnkops.com/ # The URL where the site is hosted
site_author: Bunker Ops # The author or organization responsible for the site
site_dir: docs # The directory where the documentation files are stored
theme:
logo: logo.svg # The logo file for the site
name: material # The theme name, in this case, 'material'
custom_dir: docs/overrides # Directory for custom theme overrides
palette:
scheme: slate # The color scheme for the theme
primary: amber # The primary color for the theme
accent: deep purple # The accent color for the theme
features:
- navigation.sections # Enable navigation sections
- navigation.instant # Enable instant navigation
- navigation.instant.progress # Show progress in instant navigation
- navigation.instant.preview # Enable instant preview in navigation
- navigation.tracking # Enable navigation tracking
- navigation.indexes # Enable navigation indexes
- toc.integrate # Integrate table of contents
- content.code.copy # Enable copy button for code blocks
- navigation.path # Show navigation path
- navigation.top # Enable top navigation
- navigation.footer # Enable footer navigation
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true # Enable line numbers in code blocks
line_spans: __span # Add spans to lines in code blocks
pygments_lang_class: true # Use Pygments language class for code blocks
- pymdownx.inlinehilite # Enable inline highlighting
- pymdownx.snippets # Enable snippets
- pymdownx.superfences # Enable superfences for code blocks
- admonition # Enable admonitions
- pymdownx.details # Enable details extension
- attr_list # Enable attribute lists
extra_css:
- stylesheets/extra.css # Additional CSS file for custom styles
copyright: Copyright &copy; 2024 The Bunker Operations # Copyright information
extra:
generator: false # Disable generator meta tag
social:
- icon: material/web # Icon for the social link
link: https://repo.bnkops.com/Home.html # URL for the social link
plugins:
- social # Enable social plugin
- search # Enable search plugin
nav:
- Welcome to Bnkops Change Maker: index.md # Navigation link to the welcome page
- Why Change Maker: Why Change Maker.md # Navigation link to the 'Why Change Maker' page
- Bnkops Deploy: bnkops Deploy.md # Navigation link to the 'Bnkops Deploy' page
- Manuals: # Navigation section for manuals
- Getting Started: manuals/index.md # Link to the 'Getting Started' manual
- Prerequisites: manuals/prerequisites.md # Link to the 'Prerequisites' manual
- Installation: manuals/Installation.md # Link to the 'Installation' manual
- Backend & Editor: manuals/Backend & Editor.md # Link to the 'Backend & Editor' manual
- Simple Markdown Guide: manuals/Simple Markdown Guide.md # Link to the 'Simple Markdown Guide' manual
- Use Obsidian as Site Editor: manuals/obsidian.md # Link to the 'Use Obsidian as Site Editor' manual
- Customize Your Landing Page: manuals/Customize Your Landing Page.md # Link to the 'Customize Your Landing Page' manual
- Example Political Apps: manuals/Examples.md # Link to the 'Example Political Apps' manual
- Get Online: manuals/Get Site Online.md # Link to the 'Get Online' manual
- Visit repo.bnkops.com for more!: https://repo.bnkops.com/Home.html # External link to the repository
- Admin Log: Admin Log.md # Navigation link to the 'Admin Log' page
```
### Navigation
Site wide navigation can be set purposefully by declaring it inside the .yml file or can be left to automatic alphabetical rendering. To switch to alphabetical approach, delete nav section in .yml file. Hybrid approaches are possible for things like blogs; see [Material for MkDocs ](https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/?h=navigation)for more.
![alt text](image-3.png)
### QR Code Maker
Qr code maker is a feature that allows you to generate QR codes for your site. This is useful for sharing your site with others. You can access the feature by clicking on the icon above the editor or by typing [[qr]] in the editor.
![alt text](image-5.png)
![alt text](image-6.png)
### Landing Page HTML Panel
Click on the table icon to open the landing page HTML panel. See [Customize Your Landing Page](Customize%20Your%20Landing%20Page.md)
for more instruction for updating this section.
![alt text](image-4.png)
## Mobile Environment
The mobile development currently deploys folded on load. This can be intimidating on first glance; trust us it is okay. To get to editor smoothly:
1. Select a file and collapse the file menu
2. Close the preview menu by clicking the double header arrow icon
3. Collapse the backlinks section
#### Editing on Mobile Browser
![MobileScreen2](MobileScreen2.png)
## Diving Further
The entire site is built locally, which means all your files are available for updates locally. You can use any editor, such as VSCode, Obsidian, Texteditor, or your preferred platform to do site wide updates. As there are no blackbox code sections, you can access and modify any part of the system.
![alt text](image-13.png)
### extra.css
Want to spruce up your site even further? Access the extra.css file `docs/stylesheets/extra.css` and add in any site wide css styling you would like to add. Shipped by default are code block improvements.
![alt text](image-12.png)
### Again, Learn More At Mkdocs-Material
The system uses [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) as the backend that builds your site. Material for Mkdocs is also a open-source project that has a massive community behind it. They have extensive documentation to setup a vast array of [features and customization](https://squidfunk.github.io/mkdocs-material/setup/).
Exciting to us, and coming to you all soon, is support for site creation in over [60 languages.](https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/?h=languag) and built in features like [data privacy](https://squidfunk.github.io/mkdocs-material/setup/ensuring-data-privacy/).
[Check out Material for MkDocs Documentation](https://squidfunk.github.io/mkdocs-material/setup/){ .md-button }
![](thumbsup.gif)

View File

@ -0,0 +1,165 @@
# Building Your Custom Site
Getting started with Change Maker is simple and flexible. Here are the steps to create your own custom site from scratch.
## Starting from Scratch
Your site only needs a few things to get started:
**Docs folder:** This is where all your content will live. You can organize your content into subfolders and files as needed.
**Configuration file:** This file contains the settings for your site, such as the site title and description.
**Navigation:** This text defines the structure of your site, including the order of pages and the hierarchy of the navigation.
### Docs
1. **Backup the Docs Folder**: Before making any changes, it's a good idea to backup the `docs` folder. This ensures you have a copy of the original content and all these manuals.
2. **Create a New Folder**: Create a new folder called `docs` in the root directory. This will be the root folder for your custom site.
3. **Add Your Content**: Add your content to the `docs` folder. You can create subfolders and organize your content as needed.
### Configuration
The most basic configuration for your site is the `mkdocs.yml` file. This file contains the site title, description, and other settings.
???+ "Basic mkdocs.yml Configuration"
```yaml
title: My Custom Site
description: This is my custom site.
```
You can find all the configuration options on the mkdocs documentation page:
[MkDocs documentation](https://squidfunk.github.io/mkdocs-material/setup/){ .md-button }
**The Bunker Operations Current Configuration:**
??? "Bunker Operations mkdocs.yml Configuration"
```yaml
site_name: Change Maker
site_description: A powerful documentation system combining simplicity with professional features. Own your documentation, data, and design; don't rent it.
site_url: https://changemaker.bnkops.com/
site_author: Bunker Ops
site_dir: docs
theme:
logo: logo.svg
name: material
custom_dir: docs/overrides
palette:
scheme: slate
primary: amber
accent: deep purple
features:
- navigation.sections
- navigation.instant
- navigation.instant.progress
- navigation.instant.preview
- navigation.tracking
- navigation.indexes
- toc.integrate
- content.code.copy
- navigation.path
- navigation.top
- navigation.footer
- header.autohide
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
- admonition
- pymdownx.details
- attr_list
- md_in_html
- footnotes
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
extra_css:
- stylesheets/extra.css
- https://fonts.googleapis.com/icon?family=Material+Icons
copyright: Copyright &copy; 2024 The Bunker Operations - Built with Change Maker
extra:
generator: false
social:
- icon: material/web
link: https://repo.bnkops.com/Home.html
plugins:
- social
- search
- blog:
draft_on_serve: false
nav:
- Welcome to Bnkops Change Maker: index.md
- Why Change Maker: Why Change Maker.md
- Bnkops Deploy: bnkops Deploy.md
- Admin Log: blog/index.md
- Install & Manuals:
- Getting Started: manuals/index.md
- Prerequisites: manuals/prerequisites.md
- Installation: manuals/Installation.md
- Reset & Build: manuals/Build Site.md
- Backend & Editor: manuals/Backend & Editor.md
- Simple Markdown Guide: manuals/Simple Markdown Guide.md
- Commands: manuals/Commands.md
- Use Obsidian as Site Editor: manuals/obsidian.md
- Customize Your Landing Page: manuals/Customize Your Landing Page.md
- Example Political Apps: manuals/Examples.md
- Get Online: manuals/Get Site Online.md
- Visit repo.bnkops.com for more!: https://repo.bnkops.com/Home.html
- Feature Tracking: Feature Tracking.md
- Who is bnkops: Who is bnkops.md
```
### Navigation
The navigation for your site is defined in the mkdocs.yml file. This file contains the structure of your site, including the order of pages and the hierarchy of the navigation. You have two options for defining the navigation:
1. **Manual Navigation**: You can manually define the navigation in the `mkdocs.yml` file. This gives you full control over the structure of your site. For example
???+ "Manual Navigation Example"
```yaml
nav:
- Home: index.md
- About: about.md
- Contact: contact.md
```
2. **Automatic Navigation**: You can let MkDocs automatically generate the navigation based on the structure of your `docs` folder. This is the default behavior if you don't define the navigation in the `mkdocs.yml` file. Mkdocs will generates navigation via alphabetical order of the files in the `docs` folder.
For example, if you have the following files in your `docs` folder:
???+ "Example Docs Folder Structure"
```
docs/
index.md
about.md
contact.md
```
These will load up in the navigation as follows:
???+ "Generated Navigation Example"
```
- About
- Contact
- Home
```
!!! tip "index.md"
The `index.md` file is always the first file in the navigation. The root index.md file is always the first file in the navigation. It works as the home page with it is located in the root of the `docs` folder. If you create a index.md file in a subfolder, it will be the first file in the navigation of that subfolder.
### More Customization
Follow the rest of the guide to build up your custom site with more advanced features and customization options.

View File

@ -0,0 +1,817 @@
# Code Snippets
Find below a list of code snippets that can be used in your projects. These are all snippets that we already use in our projects and are tested.
## Email Button
A simple email button that allows users to email their selected city councillor.
<!-- Begin Edmonton Council Emailer -->
<div id="edmonton-council-emailer" class="ece-container">
<style>
#edmonton-council-emailer,
#edmonton-council-emailer * {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: inherit;
}
#edmonton-council-emailer.ece-container {
position: relative;
width: 100%;
max-width: 1200px;
margin: 20px auto;
padding: 30px;
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
color: #fff;
background: #1e2124;
border-radius: 12px;
isolation: isolate;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
#edmonton-council-emailer .ece-button-container {
display: flex;
align-items: center;
gap: 20px;
background: #2a2d31;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
#edmonton-council-emailer .ece-button {
padding: 15px 30px;
font-size: 18px;
background-color: #ffc107;
color: #000;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
}
#edmonton-council-emailer .ece-button:hover {
background-color: #ffcd38;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3);
}
#edmonton-council-emailer .ece-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
z-index: 2147483647; /* Set to maximum value */
}
#edmonton-council-emailer .ece-modal-content {
position: relative;
background-color: #1e2124;
margin: 5% auto;
padding: 30px;
width: 80%;
max-width: 1200px;
max-height: 80vh;
overflow-y: auto;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
border: 1px solid #ffc107;
}
#edmonton-council-emailer .ece-close {
position: absolute;
right: 20px;
top: 20px;
font-size: 28px;
cursor: pointer;
color: #ffc107;
text-decoration: none;
line-height: 1;
transition: all 0.3s ease;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #2a2d31;
}
#edmonton-council-emailer .ece-close:hover {
background: #ffc107;
color: #000;
transform: rotate(90deg);
}
#edmonton-council-emailer .ece-councillor-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
#edmonton-council-emailer .ece-councillor-card {
border: 1px solid #2a2d31;
border-radius: 12px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
background-color: #2a2d31;
}
#edmonton-council-emailer .ece-councillor-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(255, 193, 7, 0.2);
border-color: #ffc107;
}
#edmonton-council-emailer .ece-councillor-card img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
transition: all 0.3s ease;
}
#edmonton-council-emailer .ece-councillor-card:hover img {
transform: scale(1.02);
}
#edmonton-council-emailer .ece-councillor-card h3 {
margin: 15px 0 5px;
color: #fff;
font-size: 1.2em;
font-weight: bold;
}
#edmonton-council-emailer .ece-councillor-card p {
margin: 5px 0;
color: #ffc107;
font-size: 1em;
}
#edmonton-council-emailer .ece-modal h2 {
color: #ffc107;
font-size: 2em;
margin-bottom: 30px;
font-weight: bold;
text-align: center;
}
@keyframes countUp {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
#edmonton-council-emailer {
position: static; /* Remove any positioning if unnecessary */
z-index: auto; /* Reset z-index */
}
</style>
<div class="ece-button-container">
<button class="ece-button" onclick="ECE.openModal()">Email Your Councillor</button>
</div>
<div class="ece-modal" id="ece-modal">
<div class="ece-modal-content">
<span class="ece-close" onclick="ECE.closeModal()">&times;</span>
<h2>Select Your City Councillor</h2>
<div class="ece-councillor-grid" id="ece-councillor-grid"></div>
</div>
</div>
<script>
const ECE = {
count: 154,
councillors: [
{
name: "Mayor Amarjeet Sohi",
title: "City-Wide Mayor",
email: "mayorsoffice@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Mayor-Sohi-800x494.jpg",
ward: "City-Wide"
},
{
name: "Councillor Erin Rutherford",
title: "Ward Anirniq Councillor",
email: "erin.rutherford@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Anirniq-councillor_800x494.jpg",
ward: "Anirniq"
},
{
name: "Councillor Aaron Paquette",
title: "Ward Dene Councillor",
email: "aaron.paquette@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Dene-councillor_800x494.jpg",
ward: "Dene"
},
{
name: "Councillor Jennifer Rice",
title: "Ward Ipiihkoohkanipiaohtsi Councillor",
email: "jennifer.rice@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Ipiihkoohkanipiaohtsi-councillor_800x494.jpg",
ward: "Ipiihkoohkanipiaohtsi"
},
{
name: "Councillor Keren Tang",
title: "Ward Karhiio Councillor",
email: "keren.tang@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Karhiio-councillor_800x494.jpg",
ward: "Karhiio"
},
{
name: "Councillor Ashley Salvador",
title: "Ward Métis Councillor",
email: "ashley.salvador@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Métis-councillor_800x494.jpg",
ward: "Métis"
},
{
name: "Councillor Andrew Knack",
title: "Ward Nakota Isga Councillor",
email: "andrew.knack@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Nakota-Isga-councillor_800x494.jpg",
ward: "Nakota Isga"
},
{
name: "Councillor Anne Stevenson",
title: "Ward O-day'min Councillor",
email: "anne.stevenson@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/O-day'min-councillor_800x494.jpg",
ward: "O-day'min"
},
{
name: "Councillor Michael Janz",
title: "Ward papastew Councillor",
email: "michael.janz@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/papastew-councillor_800x494.jpg",
ward: "papastew"
},
{
name: "Councillor Tim Cartmell",
title: "Ward pihêsiwin Councillor",
email: "tim.cartmell@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/pihêsiwin-councillor_800x494.jpg",
ward: "pihêsiwin"
},
{
name: "Councillor Sarah Hamilton",
title: "Ward sipiwiyiniwak Councillor",
email: "sarah.hamilton@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/sipiwiyiniwak-councillor_800x494.jpg",
ward: "sipiwiyiniwak"
},
{
name: "Councillor Jo-Anne Wright",
title: "Ward Sspomitapi Councillor",
email: "jo-anne.wright@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Sspomitapi-councillor_800x494.jpg",
ward: "Sspomitapi"
},
{
name: "Councillor Karen Principe",
title: "Ward tastawiyiniwak Councillor",
email: "karen.principe@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/tastawiyiniwak-councillor_800x494.jpg",
ward: "tastawiyiniwak"
}
],
init: function() {
if (window.ECEInitialized) return;
window.ECEInitialized = true;
document.getElementById('ece-modal').addEventListener('click', function(event) {
if (event.target === this) {
ECE.closeModal();
}
});
},
createCouncillorCards: function() {
const grid = document.getElementById('ece-councillor-grid');
if (!grid) return;
this.councillors.forEach(councillor => {
const card = document.createElement('div');
card.className = 'ece-councillor-card';
card.innerHTML = `
<img src="${councillor.image}" alt="${councillor.name}">
<h3>${councillor.name}</h3>
<p>${councillor.ward}</p>
`;
card.onclick = () => this.emailCouncillor(councillor);
grid.appendChild(card);
});
},
openModal: function() {
const modal = document.getElementById('ece-modal');
if (!modal) return;
modal.style.display = 'block';
if (!document.getElementById('ece-councillor-grid').children.length) {
this.createCouncillorCards();
}
},
closeModal: function() {
const modal = document.getElementById('ece-modal');
if (modal) modal.style.display = 'none';
},
emailCouncillor: function(councillor) {
this.closeModal();
const subject = "Constituent Feedback - [Your Issue]";
const body = `Dear ${councillor.name},
I am a constituent in ${councillor.ward} and I am writing to you regarding [describe your issue].
[Describe how this issue affects you and your community]
I would appreciate if you could [describe your requested action].
Thank you for your time and consideration.
Sincerely,
[Your Name]
[Your Address]
[Your Phone Number]`;
const encodedSubject = encodeURIComponent(subject);
const encodedBody = encodeURIComponent(body);
window.location.href = `mailto:${councillor.email}?subject=${encodedSubject}&body=${encodedBody}`;
}
};
// Initialize the component when the document is ready
document.addEventListener('DOMContentLoaded', function() {
ECE.init();
});
</script>
</div>
<!-- End Edmonton Council Emailer -->
### Basic Email Structure
The email template is defined in the `emailCouncillor` function using two main components:
```javascript
const subject = "Constituent Feedback - [Your Issue]";
const body = `Dear ${councillor.name},
// ... rest of the email content
`;
```
### How to Modify the Template
#### 1. Changing the Subject Line
Locate the `subject` constant in the `emailCouncillor` function:
```javascript
const subject = "Constituent Feedback - [Your Issue]";
```
Replace the text inside the quotes with your desired subject line.
#### 2. Modifying the Email Body
Find the `body` template string (marked with backticks `) and modify its content:
```javascript
const body = `Dear ${councillor.name},
Your new email template goes here...`;
```
#### 3. Available Variables
The following variables are available for use in your template:
- `${councillor.name}` - Councillor's full name
- `${councillor.ward}` - Ward name
- `${councillor.title}` - Councillor's title
- `${councillor.email}` - Councillor's email address
#### 4. Special Characters and Formatting
- Use `\n` for line breaks
- Avoid using special characters like `"` or `'` directly - escape them if needed
- Remember that HTML formatting will not work in email clients
### Example Templates
#### General Inquiry Template
```javascript
const subject = "General Inquiry from Constituent";
const body = `Dear ${councillor.name},
I am a constituent from ${councillor.ward} seeking information about...`;
```
#### Specific Issue Template
```javascript
const subject = "Urgent: Traffic Safety Concern";
const body = `Dear ${councillor.name},
I am writing regarding a safety concern at the intersection of...`;
```
#### Meeting Request Template
```javascript
const subject = "Meeting Request from ${councillor.ward} Constituent";
const body = `Dear ${councillor.name},
I would like to schedule a meeting to discuss...`;
```
### Implementation Tips
1. Keep subject lines concise and specific
2. Include clear calls to action in the body
3. Maintain professional formatting
4. Test the template with various email clients
5. Consider mobile device compatibility
### Technical Notes
- The template uses `encodeURIComponent()` to properly encode special characters
- Maximum email length may vary by email client
- Some email clients may have limitations on mailto link functionality
??? "Edmonton Council Emailer Code"
```html
<!-- Begin Edmonton Council Emailer -->
<div id="edmonton-council-emailer" class="ece-container">
<style>
#edmonton-council-emailer,
#edmonton-council-emailer * {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: inherit;
}
#edmonton-council-emailer.ece-container {
position: relative;
width: 100%;
max-width: 1200px;
margin: 20px auto;
padding: 30px;
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
color: #fff;
background: #1e2124;
border-radius: 12px;
isolation: isolate;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
#edmonton-council-emailer .ece-button-container {
display: flex;
align-items: center;
gap: 20px;
background: #2a2d31;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
#edmonton-council-emailer .ece-button {
padding: 15px 30px;
font-size: 18px;
background-color: #ffc107;
color: #000;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
}
#edmonton-council-emailer .ece-button:hover {
background-color: #ffcd38;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3);
}
#edmonton-council-emailer .ece-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
z-index: 2147483647; /* Set to maximum value */
}
#edmonton-council-emailer .ece-modal-content {
position: relative;
background-color: #1e2124;
margin: 5% auto;
padding: 30px;
width: 80%;
max-width: 1200px;
max-height: 80vh;
overflow-y: auto;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
border: 1px solid #ffc107;
}
#edmonton-council-emailer .ece-close {
position: absolute;
right: 20px;
top: 20px;
font-size: 28px;
cursor: pointer;
color: #ffc107;
text-decoration: none;
line-height: 1;
transition: all 0.3s ease;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #2a2d31;
}
#edmonton-council-emailer .ece-close:hover {
background: #ffc107;
color: #000;
transform: rotate(90deg);
}
#edmonton-council-emailer .ece-councillor-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
#edmonton-council-emailer .ece-councillor-card {
border: 1px solid #2a2d31;
border-radius: 12px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
background-color: #2a2d31;
}
#edmonton-council-emailer .ece-councillor-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(255, 193, 7, 0.2);
border-color: #ffc107;
}
#edmonton-council-emailer .ece-councillor-card img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
transition: all 0.3s ease;
}
#edmonton-council-emailer .ece-councillor-card:hover img {
transform: scale(1.02);
}
#edmonton-council-emailer .ece-councillor-card h3 {
margin: 15px 0 5px;
color: #fff;
font-size: 1.2em;
font-weight: bold;
}
#edmonton-council-emailer .ece-councillor-card p {
margin: 5px 0;
color: #ffc107;
font-size: 1em;
}
#edmonton-council-emailer .ece-modal h2 {
color: #ffc107;
font-size: 2em;
margin-bottom: 30px;
font-weight: bold;
text-align: center;
}
@keyframes countUp {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
#edmonton-council-emailer {
position: static; /* Remove any positioning if unnecessary */
z-index: auto; /* Reset z-index */
}
</style>
<div class="ece-button-container">
<button class="ece-button" onclick="ECE.openModal()">Email Your Councillor</button>
</div>
<div class="ece-modal" id="ece-modal">
<div class="ece-modal-content">
<span class="ece-close" onclick="ECE.closeModal()">&times;</span>
<h2>Select Your City Councillor</h2>
<div class="ece-councillor-grid" id="ece-councillor-grid"></div>
</div>
</div>
<script>
const ECE = {
count: 0,
councillors: [
{
name: "Mayor Amarjeet Sohi",
title: "City-Wide Mayor",
email: "mayorsoffice@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Mayor-Sohi-800x494.jpg",
ward: "City-Wide"
},
{
name: "Councillor Erin Rutherford",
title: "Ward Anirniq Councillor",
email: "erin.rutherford@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Anirniq-councillor_800x494.jpg",
ward: "Anirniq"
},
{
name: "Councillor Aaron Paquette",
title: "Ward Dene Councillor",
email: "aaron.paquette@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Dene-councillor_800x494.jpg",
ward: "Dene"
},
{
name: "Councillor Jennifer Rice",
title: "Ward Ipiihkoohkanipiaohtsi Councillor",
email: "jennifer.rice@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Ipiihkoohkanipiaohtsi-councillor_800x494.jpg",
ward: "Ipiihkoohkanipiaohtsi"
},
{
name: "Councillor Keren Tang",
title: "Ward Karhiio Councillor",
email: "keren.tang@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Karhiio-councillor_800x494.jpg",
ward: "Karhiio"
},
{
name: "Councillor Ashley Salvador",
title: "Ward Métis Councillor",
email: "ashley.salvador@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Métis-councillor_800x494.jpg",
ward: "Métis"
},
{
name: "Councillor Andrew Knack",
title: "Ward Nakota Isga Councillor",
email: "andrew.knack@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Nakota-Isga-councillor_800x494.jpg",
ward: "Nakota Isga"
},
{
name: "Councillor Anne Stevenson",
title: "Ward O-day'min Councillor",
email: "anne.stevenson@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/O-day'min-councillor_800x494.jpg",
ward: "O-day'min"
},
{
name: "Councillor Michael Janz",
title: "Ward papastew Councillor",
email: "michael.janz@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/papastew-councillor_800x494.jpg",
ward: "papastew"
},
{
name: "Councillor Tim Cartmell",
title: "Ward pihêsiwin Councillor",
email: "tim.cartmell@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/pihêsiwin-councillor_800x494.jpg",
ward: "pihêsiwin"
},
{
name: "Councillor Sarah Hamilton",
title: "Ward sipiwiyiniwak Councillor",
email: "sarah.hamilton@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/sipiwiyiniwak-councillor_800x494.jpg",
ward: "sipiwiyiniwak"
},
{
name: "Councillor Jo-Anne Wright",
title: "Ward Sspomitapi Councillor",
email: "jo-anne.wright@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Sspomitapi-councillor_800x494.jpg",
ward: "Sspomitapi"
},
{
name: "Councillor Karen Principe",
title: "Ward tastawiyiniwak Councillor",
email: "karen.principe@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/tastawiyiniwak-councillor_800x494.jpg",
ward: "tastawiyiniwak"
}
],
init: function() {
if (window.ECEInitialized) return;
window.ECEInitialized = true;
document.getElementById('ece-modal').addEventListener('click', function(event) {
if (event.target === this) {
ECE.closeModal();
}
});
},
createCouncillorCards: function() {
const grid = document.getElementById('ece-councillor-grid');
if (!grid) return;
this.councillors.forEach(councillor => {
const card = document.createElement('div');
card.className = 'ece-councillor-card';
card.innerHTML = `
<img src="${councillor.image}" alt="${councillor.name}">
<h3>${councillor.name}</h3>
<p>${councillor.ward}</p>
`;
card.onclick = () => this.emailCouncillor(councillor);
grid.appendChild(card);
});
},
openModal: function() {
const modal = document.getElementById('ece-modal');
if (!modal) return;
modal.style.display = 'block';
if (!document.getElementById('ece-councillor-grid').children.length) {
this.createCouncillorCards();
}
},
closeModal: function() {
const modal = document.getElementById('ece-modal');
if (modal) modal.style.display = 'none';
},
emailCouncillor: function(councillor) {
this.closeModal();
const subject = "Constituent Feedback - [Your Issue]";
const body = `Dear ${councillor.name},
I am a constituent in ${councillor.ward} and I am writing to you regarding [describe your issue].
[Describe how this issue affects you and your community]
I would appreciate if you could [describe your requested action].
Thank you for your time and consideration.
Sincerely,
[Your Name]
[Your Address]
[Your Phone Number]`;
const encodedSubject = encodeURIComponent(subject);
const encodedBody = encodeURIComponent(body);
window.location.href = `mailto:${councillor.email}?subject=${encodedSubject}&body=${encodedBody}`;
}
};
// Initialize the component when the document is ready
document.addEventListener('DOMContentLoaded', function() {
ECE.init();
});
</script>
</div>
<!-- End Edmonton Council Emailer -->
```

View File

@ -0,0 +1,126 @@
# Quick Link & Command System Guide
# Using Command Functions in the Editor
The Editor provides a set of command functions that make it easy to insert various elements into your documents. This guide will walk you through how to use these commands and how to update or add new ones.
Commands are triggered by typing [[ and a key letter. For example, to insert a button, you would type `[[btn]]` in the editor. This will trigger the button command and prompt you to enter the button text and URL. This same fuctionality can insert files, images, and other elements into your documents.
The available commands are defined in the `commands.js` file, where you can customize or add new commands as needed.
This same functionality can be used in the Obsidian editor, allowing you to quickly insert elements into your documents with minimal effort.
## Available Commands
### Insert Button
- **Trigger**: `btn`
- **Description**: Inserts a styled button.
- **How to Use**:
1. Type `[[btn]]` in the editor.
2. A prompt will appear asking for the button text and URL.
3. Fill in the details and the button will be inserted.
### Insert QR Code
- **Trigger**: `qr`
- **Description**: Inserts a QR code image.
- **How to Use**:
1. Type `[[qr]]` in the editor.
2. A QR code will be generated and inserted at the cursor position.
### Insert Admonition
- **Trigger**: `adm`
- **Description**: Inserts an admonition block (e.g., note, warning).
- **How to Use**:
1. Type `[[adm]]` in the editor.
2. A prompt will appear asking for the type, title, and content of the admonition.
3. Fill in the details and the admonition block will be inserted.
### Insert Annotation
- **Trigger**: `ann`
- **Description**: Inserts an annotation with Markdown support.
- **How to Use**:
1. Type `[[ann]]` in the editor.
2. A prompt will appear asking for the text to annotate and the annotation content.
3. Fill in the details and the annotation will be inserted.
### Insert 4-Card Grid
- **Trigger**: `grid4`
- **Description**: Inserts a grid with 4 cards.
- **How to Use**:
1. Type `[[grid4]]` in the editor.
2. A prompt will appear asking for the titles and contents of the four cards.
3. Fill in the details and the grid will be inserted.
### Insert Blog Front Matter
- **Trigger**: `blogfm`
- **Description**: Inserts a basic blog front matter template.
- **How to Use**:
1. Type `[[blogfm]]` in the editor.
2. A prompt will appear asking for the author's name.
3. Fill in the details and the front matter will be inserted.
## Updating Commands
To update or add new commands, follow these steps:
**Open the 'commands.js' File**:
- Navigate to the commands.js file in your project.
**Add or Modify Commands**:
- Each command is defined as an object within the `defaultCommands` array.
- To add a new command, create a new object with the following structure:
```javascript
{
trigger: "newTrigger",
name: "New Command Name",
template: "Template for the new command",
description: "Description of the new command",
params: [
{ name: "param1", prompt: "Prompt for parameter 1" },
{ name: "param2", prompt: "Prompt for parameter 2" }
]
}
```
- Example:
```javascript
{
trigger: "example",
name: "Insert Example",
template: "Example content with $param1$ and $param2$",
description: "Inserts example content",
params: [
{ name: "param1", prompt: "Enter first parameter" },
{ name: "param2", prompt: "Enter second parameter" }
]
}
```
### **Save the File**:
Save the changes to the `commands.js` file.
**Reload the Editor**:
- Reload the editor to apply the changes to the command functions.
- To reload, you need to down and rebuild the docker container labeled change-maker-v[insert version #]-frontend.
- This will apply the changes to the editor and make the new commands available for use.
**Rebuild Application**:
If reloading editor does not push updates, you can rebuild application using `./start.sh` in the root directory.
!!! Warning "Backup Your Data Before Rebuilding"
Do not forget to backup your data before rebuilding the application. Critical is that you backup your listmonk data and all your customizations in the Listmonk container. Specifically ensure that your subscribers are backed up before proceeding.
Run commands in root folder:
```
docker-compose down
./start.sh
```

View File

@ -0,0 +1,134 @@
# Customize Your Landing Page
## Basic Set Up
To return to base configuration for landing page, navigate to your index.md file, and delete the properties at top of file:
```
// Delete this text from your index.md file
---
title: Welcome to Bnkops Change Maker V3
template: home.html
---
```
This will allow you to write your home page in standard markdown.
## Custom Set Up
Alternatively you can edit your home pages html directly in editor. To view your front/home page html, click on the table icon next to the gear icon. Edit content and click save to upload and update landing page.
### But bnkops I don't read or write html 😧
**Don't panic! That is what this guide is for. Learn how to update your front page effortlessly.**
## Prompting for Updating
The code presented is standard html, which is easy to update by large language models (LLMs), often referred to as Ai. bnkops has written a [manual for creating a local llm system](https://repo.bnkops.com/Daisy%20AI%20%F0%9F%8C%BB/Daisy.html) that can do this work entirely for free. We also supply access to [Daisy](https://repo.bnkops.com/Daisy%20AI%20%F0%9F%8C%BB/Daisy.html), our own local, ethical, and custom LLM upon request. You can use any LLM to write this page; including freely accessible systems like chatgpt, claude, llama, or even whatsapp chat.
These same techniques can be used on the site settings (accessed through the cog icon). All documentation on site options is available [here](https://squidfunk.github.io/mkdocs-material/).
When asking an LLM to help customize your landing page, follow these key principles:
1. **Provide Context**: Let the LLM know about the existing structure
```
I'm using MkDocs Material theme with a custom landing page.
The template extends main.html and uses a dark theme.
I am want to fully update my landing page.
```
2. **Specify Requirements**: Clearly state what elements you want
```
I need:
- A hero section with a gradient background
- Feature cards with hover effects
- Smooth scroll animations
- Custom CSS styling that matches the dark theme
```
3. **Reference Existing Code**: If you're modifying an existing design
```
I'm starting with the default home.html template. Here's the current code:
[paste your current home.html content]
```
## Example Prompts
### For Basic Modifications
```
Please update my home.html template to add a new feature card section about [topic].
Keep the existing dark theme (#1e2127) and yellow accents (#ffd700).
```
### For Animation Effects
```
Add smooth scroll animations to my home.html template. I want:
- Elements to fade in as they scroll into view
- Subtle hover effects on cards
- No flashy or distracting animations
- Animations should complement the dark theme
```
### For Complete Redesigns
```
Create a custom home.html template for MkDocs Material with:
1. Dark theme matching [#1e2127](http://localhost:4000/tags/1e2127) background
2. Yellow (#ffd700) accent colors
3. [List your specific sections]
4. [List your desired animations]
Keep the template extending from main.html and ensure all styling is contained within the content block.
```
## Important Reminders
- **Template Structure**: Always remind the LLM to use `{% extends "main.html" %}` and proper block structure
- **Color Scheme**: Specify the exact colors (#1e2127 for background, [#ffd700] for accents)
- **Compatibility**: Ask for self-contained CSS and JavaScript (no external dependencies)
- **Accessibility**: Request that animations respect user preferences (`prefers-reduced-motion`)
## Example Complete Prompt
Here's a full example of a well-structured prompt:
```
Please help me update my MkDocs Material landing page. I need:
1. Template Structure:
- Extend from main.html
- Keep all CSS in a style block
- Keep JavaScript in a script block
2. Design Requirements:
- Dark theme (#1e2127 background)
- Yellow accents (#ffd700)
- Responsive layout
- Smooth animations on scroll
3. Specific Sections:
- Hero section with gradient
- Feature cards with hover effects
- Documentation section
- Getting started guide
4. Animations:
- Fade-in on scroll
- Subtle hover effects
- No aggressive animations
Please provide the complete home.html template with all necessary CSS and JavaScript included.
```
## Testing the Results
After receiving the HTML:
1. Save it to `docs/overrides/home.html`. You can do this directly in editor and click the save button to upload. You may receive a error reading; remember to reload page after saving. Error messages often will automatically resolve after a few minutes and reoload.
2. Check the template extends properly
3. Verify all styles are contained within the template
4. Test responsiveness at different screen sizes
5. Ensure animations work smoothly
## Iterative Improvements
If you need adjustments:
1. Specify exactly what needs changing
2. Provide the current code
3. Describe the desired outcome
4. Ask for specific sections to be updated
Remember: LLMs can be creative with designs while maintaining your core requirements. Be specific about what must be preserved (like color schemes and structure) but allow flexibility for creative elements.

View File

@ -0,0 +1,812 @@
# Example Political Apps
In addition to Change Maker, bnkops has already completed development on plugins for newsletters, email targeting, and micro sites. These technologies are also highly customizable, free to deploy, and actively maintained by bnkops.
bnkops will bake these plugins directly into the installation process for Change Maker (V4 here we come). For now, manuals for installation, hosting, and deployment can be found at [repo.bnkops.com](https://repo.bnkops.com/Free%20Office%20Software%20Stack%20%F0%9F%A4%AF/The%20Bunker%20Ops%20Server%20Build-Out.html).
# Email Button
Insert a counter button to email local council members in Edmonton. Example:
<!-- Begin Edmonton Council Emailer -->
<div id="edmonton-council-emailer" class="ece-container">
<style>
#edmonton-council-emailer,
#edmonton-council-emailer * {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: inherit;
}
#edmonton-council-emailer.ece-container {
position: relative;
width: 100%;
max-width: 1200px;
margin: 20px auto;
padding: 30px;
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
color: #fff;
background: #1e2124;
border-radius: 12px;
isolation: isolate;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
#edmonton-council-emailer .ece-button-container {
display: flex;
align-items: center;
gap: 20px;
background: #2a2d31;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
#edmonton-council-emailer .ece-button {
padding: 15px 30px;
font-size: 18px;
background-color: #ffc107;
color: #000;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
}
#edmonton-council-emailer .ece-button:hover {
background-color: #ffcd38;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3);
}
#edmonton-council-emailer .ece-counter {
font-size: 24px;
font-weight: bold;
color: #ffc107;
transition: all 0.3s ease;
padding: 10px 20px;
border-radius: 8px;
background: #2a2d31;
border: 2px solid #ffc107;
min-width: 200px;
text-align: center;
}
#edmonton-council-emailer .ece-counter.animate {
transform: scale(1.1);
background: #ffc107;
color: #000;
}
#edmonton-council-emailer .ece-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
z-index: 999999;
}
#edmonton-council-emailer .ece-modal-content {
position: relative;
background-color: #1e2124;
margin: 5% auto;
padding: 30px;
width: 80%;
max-width: 1200px;
max-height: 80vh;
overflow-y: auto;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
border: 1px solid #ffc107;
}
#edmonton-council-emailer .ece-close {
position: absolute;
right: 20px;
top: 20px;
font-size: 28px;
cursor: pointer;
color: #ffc107;
text-decoration: none;
line-height: 1;
transition: all 0.3s ease;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #2a2d31;
}
#edmonton-council-emailer .ece-close:hover {
background: #ffc107;
color: #000;
transform: rotate(90deg);
}
#edmonton-council-emailer .ece-councillor-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
#edmonton-council-emailer .ece-councillor-card {
border: 1px solid #2a2d31;
border-radius: 12px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
background-color: #2a2d31;
}
#edmonton-council-emailer .ece-councillor-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(255, 193, 7, 0.2);
border-color: #ffc107;
}
#edmonton-council-emailer .ece-councillor-card img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
transition: all 0.3s ease;
}
#edmonton-council-emailer .ece-councillor-card:hover img {
transform: scale(1.02);
}
#edmonton-council-emailer .ece-councillor-card h3 {
margin: 15px 0 5px;
color: #fff;
font-size: 1.2em;
font-weight: bold;
}
#edmonton-council-emailer .ece-councillor-card p {
margin: 5px 0;
color: #ffc107;
font-size: 1em;
}
#edmonton-council-emailer .ece-modal h2 {
color: #ffc107;
font-size: 2em;
margin-bottom: 30px;
font-weight: bold;
text-align: center;
}
@keyframes countUp {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
</style>
<div class="ece-button-container">
<button class="ece-button" onclick="ECE.openModal()">Email Your Councillor</button>
<div class="ece-counter" id="ece-counter">0 emails sent</div>
</div>
<div class="ece-modal" id="ece-modal">
<div class="ece-modal-content">
<span class="ece-close" onclick="ECE.closeModal()">&times;</span>
<h2>Select Your City Councillor</h2>
<div class="ece-councillor-grid" id="ece-councillor-grid"></div>
</div>
</div>
<script>
const ECE = {
count: 0,
councillors: [
{
name: "Mayor Amarjeet Sohi",
title: "City-Wide Mayor",
email: "mayorsoffice@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Mayor-Sohi-800x494.jpg",
ward: "City-Wide"
},
{
name: "Councillor Erin Rutherford",
title: "Ward Anirniq Councillor",
email: "erin.rutherford@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Anirniq-councillor_800x494.jpg",
ward: "Anirniq"
},
{
name: "Councillor Aaron Paquette",
title: "Ward Dene Councillor",
email: "aaron.paquette@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Dene-councillor_800x494.jpg",
ward: "Dene"
},
{
name: "Councillor Jennifer Rice",
title: "Ward Ipiihkoohkanipiaohtsi Councillor",
email: "jennifer.rice@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Ipiihkoohkanipiaohtsi-councillor_800x494.jpg",
ward: "Ipiihkoohkanipiaohtsi"
},
{
name: "Councillor Keren Tang",
title: "Ward Karhiio Councillor",
email: "keren.tang@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Karhiio-councillor_800x494.jpg",
ward: "Karhiio"
},
{
name: "Councillor Ashley Salvador",
title: "Ward Métis Councillor",
email: "ashley.salvador@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Métis-councillor_800x494.jpg",
ward: "Métis"
},
{
name: "Councillor Andrew Knack",
title: "Ward Nakota Isga Councillor",
email: "andrew.knack@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Nakota-Isga-councillor_800x494.jpg",
ward: "Nakota Isga"
},
{
name: "Councillor Anne Stevenson",
title: "Ward O-day'min Councillor",
email: "anne.stevenson@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/O-day'min-councillor_800x494.jpg",
ward: "O-day'min"
},
{
name: "Councillor Michael Janz",
title: "Ward papastew Councillor",
email: "michael.janz@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/papastew-councillor_800x494.jpg",
ward: "papastew"
},
{
name: "Councillor Tim Cartmell",
title: "Ward pihêsiwin Councillor",
email: "tim.cartmell@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/pihêsiwin-councillor_800x494.jpg",
ward: "pihêsiwin"
},
{
name: "Councillor Sarah Hamilton",
title: "Ward sipiwiyiniwak Councillor",
email: "sarah.hamilton@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/sipiwiyiniwak-councillor_800x494.jpg",
ward: "sipiwiyiniwak"
},
{
name: "Councillor Jo-Anne Wright",
title: "Ward Sspomitapi Councillor",
email: "jo-anne.wright@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/Sspomitapi-councillor_800x494.jpg",
ward: "Sspomitapi"
},
{
name: "Councillor Karen Principe",
title: "Ward tastawiyiniwak Councillor",
email: "karen.principe@edmonton.ca",
image: "https://www.edmonton.ca/sites/default/files/public-files/feature-images/tastawiyiniwak-councillor_800x494.jpg",
ward: "tastawiyiniwak"
}
],
init: function() {
if (window.ECEInitialized) return;
window.ECEInitialized = true;
document.getElementById('ece-modal').addEventListener('click', function(event) {
if (event.target === this) {
ECE.closeModal();
}
});
},
createCouncillorCards: function() {
const grid = document.getElementById('ece-councillor-grid');
if (!grid) return;
this.councillors.forEach(councillor => {
const card = document.createElement('div');
card.className = 'ece-councillor-card';
card.innerHTML = `
<img src="${councillor.image}" alt="${councillor.name}">
<h3>${councillor.name}</h3>
<p>${councillor.ward}</p>
`;
card.onclick = () => this.emailCouncillor(councillor);
grid.appendChild(card);
});
},
openModal: function() {
const modal = document.getElementById('ece-modal');
if (!modal) return;
modal.style.display = 'block';
if (!document.getElementById('ece-councillor-grid').children.length) {
this.createCouncillorCards();
}
},
closeModal: function() {
const modal = document.getElementById('ece-modal');
if (modal) modal.style.display = 'none';
},
animateCount: function(from, to) {
const counter = document.getElementById('ece-counter');
const duration = 1000; // 1 second animation
const steps = 20;
const increment = (to - from) / steps;
let current = from;
let step = 0;
counter.classList.add('animate');
const interval = setInterval(() => {
current += increment;
step++;
if (step >= steps) {
clearInterval(interval);
current = to;
setTimeout(() => {
counter.classList.remove('animate');
}, 300);
}
counter.textContent = `${Math.round(current)} emails sent`;
}, duration / steps);
},
emailCouncillor: function(councillor) {
const oldCount = this.count;
this.count++;
this.animateCount(oldCount, this.count);
this.closeModal();
const subject = "Constituent Feedback - [Your Issue]";
const body = `Dear ${councillor.name},
I am a constituent in ${councillor.ward} and I am writing to you regarding [describe your issue].
[Describe how this issue affects you and your community]
I would appreciate if you could [describe your requested action].
Thank you for your time and consideration.
Sincerely,
[Your Name]
[Your Address]
[Your Phone Number]`;
const encodedSubject = encodeURIComponent(subject);
const encodedBody = encodeURIComponent(body);
window.location.href = `mailto:${councillor.email}?subject=${encodedSubject}&body=${encodedBody}`;
}
};
// Initialize the component when the document is ready
document.addEventListener('DOMContentLoaded', function() {
ECE.init();
});
</script>
</div>
<!-- End Edmonton Council Emailer -->
[See Code Snippets For More](Code Snippets.md){ .md-button }
# Embed Any iframe
Any iframe embedding will work and render on site allowing for adding audio/visuals from thousands of different sources. For example:
<!DOCTYPE html>
<html>
<head>
<style>
.playlist-container {
background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
border-radius: 16px;
padding: 2rem;
color: #e0e0e0;
font-family: system-ui, -apple-system, sans-serif;
margin: 1rem 0;
position: relative;
overflow: hidden;
}
.playlist-frame {
position: relative;
border-radius: 12px;
overflow: hidden;
background: rgba(255, 255, 255, 0.05);
padding: 1px;
z-index: 1;
}
.playlist-frame::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #FFD700, transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.playlist-quote {
text-align: center;
margin-top: 1.5rem;
font-style: italic;
color: #FFD700;
font-size: 1.2rem;
opacity: 0;
transform: translateY(10px);
animation: fadeIn 0.5s ease forwards 0.5s;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.musical-note {
display: inline-block;
animation: float 3s ease-in-out infinite;
}
.musical-note:nth-child(2) {
animation-delay: 0.2s;
}
.musical-note:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-10px) rotate(5deg); }
}
@keyframes fadeIn {
to {
opacity: 1;
transform: translateY(0);
}
}
.magical-sparkle {
position: absolute;
pointer-events: none;
animation: sparkle 2s linear infinite;
color: #FFD700;
opacity: 0;
font-size: 1.2rem;
}
@keyframes sparkle {
0% { transform: translate(0, 0) rotate(0deg); opacity: 0; }
50% { opacity: 1; }
100% { transform: translate(var(--tx), var(--ty)) rotate(360deg); opacity: 0; }
}
.musical-decoration {
position: absolute;
font-size: 2rem;
opacity: 0.15;
pointer-events: none;
}
.top-left-music {
top: 10px;
left: 10px;
animation: pulse 3s ease-in-out infinite;
}
.bottom-right-music {
bottom: 10px;
right: 10px;
animation: pulse 3s ease-in-out infinite 1.5s;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.15; }
50% { transform: scale(1.1); opacity: 0.3; }
}
@media (max-width: 600px) {
.playlist-container {
padding: 1rem;
}
.playlist-quote {
font-size: 1rem;
flex-wrap: wrap;
}
.musical-decoration {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<div class="playlist-container">
<div class="musical-decoration top-left-music">🎼</div>
<div class="musical-decoration bottom-right-music">🎵</div>
<div class="playlist-frame">
<iframe style="border-radius:12px"
src="https://open.spotify.com/embed/playlist/5ZY7xLj2TqX5DUrMW89zfq?utm_source=generator&theme=0"
width="100%"
height="352"
frameBorder="0"
allowfullscreen=""
allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
loading="lazy">
</iframe>
</div>
<div class="playlist-quote">
<span class="musical-note">🎵</span>
The revolution will be sung before it is written
<span class="musical-note"></span>
</div>
</div>
<script>
function createSparkle() {
const container = document.querySelector('.playlist-container');
const sparkle = document.createElement('div');
sparkle.className = 'magical-sparkle';
sparkle.textContent = ['✨', '🎵', '🎶'][Math.floor(Math.random() * 3)];
const x = Math.random() * container.offsetWidth;
const y = Math.random() * container.offsetHeight;
const tx = (Math.random() - 0.5) * 100;
const ty = -100 - Math.random() * 50;
sparkle.style.left = `${x}px`;
sparkle.style.top = `${y}px`;
sparkle.style.setProperty('--tx', `${tx}px`);
sparkle.style.setProperty('--ty', `${ty}px`);
container.appendChild(sparkle);
setTimeout(() => sparkle.remove(), 2000);
}
// Create sparkles periodically
setInterval(createSparkle, 500);
</script>
</body>
</html>
## Newsletter
bnkops uses [listmonk](https://listmonk.app/) for its newsletter management. An example deployment:
<!DOCTYPE html>
<html>
<head>
<style>
.newsletter-container {
background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
border-radius: 16px;
padding: 2rem;
color: #e0e0e0;
font-family: system-ui, -apple-system, sans-serif;
width: 100%;
max-width: 600px;
margin: 2rem auto;
position: relative;
overflow: hidden;
}
.newsletter-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #FFD700, transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.form-title {
color: #FFD700;
font-size: 1.5rem;
margin-bottom: 1.5rem;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.form-title-icon {
font-size: 1.8rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
color: #FFD700;
font-size: 0.9rem;
}
.form-input {
width: 100%;
padding: 0.8rem;
border: 1px solid rgba(255, 215, 0, 0.3);
border-radius: 8px;
background: rgba(255, 255, 255, 0.05);
color: #fff;
font-size: 1rem;
transition: all 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: #FFD700;
box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2);
}
.form-input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.checkbox-group {
display: flex;
align-items: center;
gap: 0.5rem;
margin: 1rem 0;
}
.checkbox-input {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid rgba(255, 215, 0, 0.3);
border-radius: 4px;
background: rgba(255, 255, 255, 0.05);
cursor: pointer;
position: relative;
}
.checkbox-input:checked::before {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #FFD700;
font-size: 14px;
}
.checkbox-label {
color: #e0e0e0;
font-size: 0.9rem;
}
.submit-button {
width: 100%;
padding: 1rem;
background: #FFD700;
color: #1a1a1a;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.submit-button:hover {
background: #ffd900;
transform: translateY(-2px);
}
@media (max-width: 480px) {
.newsletter-container {
padding: 1.5rem;
}
.form-title {
font-size: 1.3rem;
}
}
</style>
</head>
<body>
<div class="newsletter-container">
<h3 class="form-title">
<span class="form-title-icon">📫</span>
Subscribe for Updates
</h3>
<form method="post" action="https://listmonk.bnkops.com/subscription/form">
<input type="hidden" name="nonce" />
<div class="form-group">
<label class="form-label" for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
class="form-input"
placeholder="your@email.com"
required
/>
</div>
<div class="form-group">
<label class="form-label" for="name">Name (Optional)</label>
<input
type="text"
id="name"
name="name"
class="form-input"
placeholder="Your name"
/>
</div>
<div class="checkbox-group">
<input
type="checkbox"
id="updates"
name="l"
class="checkbox-input"
checked
value="038eb469-e141-435d-86eb-2ab4df20cf9c"
/>
<label class="checkbox-label" for="updates">
Periodic Updates (~1 Weekly)
</label>
</div>
<button type="submit" class="submit-button">
Subscribe
</button>
</form>
</div>
</body>
</html>
## Simple Target
Simple Target is a bnkops app that facilitates a email campaign pointed at a inbox. These can be embedded with ease.
<iframe width="800" height="2000" frameborder="0" allow="clipboard-write;camera;geolocation;fullscreen" src="https://budibase.bnkops.com/embed/simple-target-latest"></iframe>

View File

@ -0,0 +1,292 @@
# Get Online
!!! note "Update"
Change Maker V3 online access requires customized deployment package. Change Maker V4 will include a automated process for getting online. If wanting to deploy asap email [admin@thebunkerops.ca](mailto:admin@thebunkerops.ca) for partnership opportunities or follow manual.
This guide will help you get your site online quickly and easily. By following these steps, you can have your site up and running in no time. We are recommending the service that we use to get online, which is Cloudflare. There are dozens of ways to host your site and application online. This guide will walk you through the process of setting up Cloudflare Tunnel to get your site online.
[Cloudflare](https://www.cloudflare.com/){ .md-button }
### Alternatives to Cloudflare Tunnels
If Cloudflare Tunnels don't meet your needs, here are some other options to consider:
**NGINX Reverse Proxy**
- Use NGINX to route traffic to your local server.
- Provides load balancing, SSL termination, and caching.
**Apache Reverse Proxy**
- Similar to NGINX, but uses Apache HTTP Server.
- Offers extensive configuration options and modules.
**Localtunnel**
- Quickly expose your local server to the internet.
- Easy to set up with minimal configuration.
**Ngrok**
- Provides secure introspectable tunnels to localhost.
- Offers additional features like custom subdomains and webhooks.
**Serveo**
- Another tool to expose local servers to the internet.
- No installation required, works over SSH.
Each of these alternatives has its own set of features and benefits, so you can choose the one that best fits your requirements.
### What is [Cloudflare?](https://www.cloudflare.com/)
[Cloudflared Docmentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/){ .md-button }
Cloudflare is a service that provides a range of features to improve the security, performance, and reliability of your website. It acts as a reverse proxy between your website visitors and your web hosting provider. Why we use Cloudlfare:
- **Security**: Protects your site from malicious attacks, including DDoS attacks, by filtering traffic before it reaches your server.
- **Performance**: Speeds up your website by caching content and serving it from data centers around the world.
- **Reliability**: Ensures your site remains available even if your server goes down by using its global network to keep your site online.
The Cloudflare backend and admin tools are also quite powerfull; allowing you to manage your site, monitor traffic, and configure security settings all in one spot. We quite enjoy the free analytics that are not pervasive trackers; just simple info about where people are visiting your site from.
### Before You Start
- You need a domain name (like yoursite.com)
- Your domain should be using Cloudflare (they have a free plan)
- Make sure Change Maker is working on your computer first
### Installing Cloudflare Tunnel
1. **Download Cloudflared**
- Windows: Download the latest release from [https://github.com/cloudflare/cloudflared/releases](https://github.com/cloudflare/cloudflared/releases)
- Mac: `brew install cloudflare/cloudflare/cloudflared`
- Linux:
```bash
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
```
2. **Login to Cloudflare**
```bash
cloudflared tunnel login
```
Follow the browser prompts to authenticate.
### Setting Up Your Tunnel
**Create the Tunnel**
- Open Terminal (Command Prompt on Windows)
- Type: `cloudflared tunnel create change-maker-site`
- Copy the Tunnel ID it gives you (you'll need this)
**Create the Settings File**
- On Windows, create this folder: `C:\Users\YOUR-USERNAME\.cloudflared`
- On Mac/Linux, it's in: `~/.cloudflared`
- Create a file called `config-site.yml` in that folder
- Open it with Notepad (Windows) or any text editor
**Add These Settings**
```yaml
tunnel: YOUR-TUNNEL-ID
credentials-file: ~/.cloudflared/YOUR-TUNNEL-ID.json
ingress:
- hostname: yoursite.com
service: http://localhost:4000
- hostname: button.yoursite.com
service: http://localhost:5001
- hostname: cmeditor.yoursite.com
service: http://localhost:3000
- hostname: listmonk.yoursite.com
service: http://localhost:9000
- service: http_status:404
```
Replace:
- `YOUR-TUNNEL-ID` with the ID from step 1
- `yoursite.com` with your actual website address
**Connect to Cloudflare**
- Go to cloudflare.com and log in
- Click on your domain
- Click "DNS"
- Add a new record:
- Type: CNAME
- Name: @ (or leave blank)
- Target: YOUR-TUNNEL-ID.cfargotunnel.com
- Proxy status: Proxied (orange cloud)
## Secure Site
If you are publically serving your site, it is recommended to secure your cmeditor and listmonk sites. This can be done by setting up Cloudflare Access.
[Cloudlfare Access](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/){ .md-button }
### Starting Your Tunnel
!!! Warning "Secure Your CMeditor"
Make sure your CMEditor is secure and only accessible by you. Access to the system backend can be a security risk.
#### Method 1: Running Manually (Easiest to Start With)
1. **Start the Tunnel**
- Open Terminal/Command Prompt
- Type: `cloudflared tunnel --config ~/.cloudflared/config-site.yml run`
- Leave this window open
#### Method 2: Running as Background Services (More Advanced)
On Windows:
```cmd
cloudflared service install --config C:\Users\YOUR-USERNAME\.cloudflared\config-site.yml
```
On Mac/Linux:
```bash
sudo cloudflared service install --config ~/.cloudflared/config-site.yml
```
### Checking If Everything Works
1. Start Change Maker:
```bash
docker compose down
docker system prune -f
docker compose up --build
```
2. Make sure your tunnels are running (you should see messages in those windows)
3. Try visiting your sites:
- Main website: `https://yoursite.com`
- Editor: `https://cmeditor.yoursite.com`
- Listmonk: `https://listmonk.yoursite.com`
## Making Changes Manually (if needed)
If you need to change things by hand, here's what to do:
### 1. Change Your Website Address
Open these files and update the website address:
In `mkdocs.yml`:
```yaml
site_name: Your Site Name
site_url: https://yoursite.com/
```
In `docker-compose.yml`:
```yaml
# Look for lines with:
SITE_URL=https://yoursite.com
```
In `constants.js`:
```javascript
export const MKDOCS_URL = 'https://yoursite.com';
```
### 2. Change Editor Address
Look for `cmeditor.yoursite.com` in these files:
- `docker-compose.yml`
- `constants.js`
- `server.js`
## Troubleshooting
### Common Problems and Solutions
**"Something's already using that port"**
- Change the port numbers in the setup
- Or close other programs that might be using those ports
**"Can't connect to the website"**
- Make sure Change Maker is running
- Check that your Cloudflare tunnels are running
- Try opening the site on your computer first (localhost)
**"Editor won't save changes"**
- Check that all addresses match in your setup
- Make sure you're using https:// for web addresses
- Restart Change Maker if needed
### Common Tunnel Problems
**"Tunnel Not Connected"**
- Check that both tunnel windows are still open
- Make sure you see "connected" messages
- Try running the tunnel command again
**"Can't Find Certificate"**
- Your settings files might be in the wrong place
- Double-check the folder names and locations
- Run `cloudflared tunnel login` and try again
**"Wrong Address"**
- Make sure all your domain names match exactly
- Check Cloudflare DNS settings
- Look for typos in config files
**"Editor Not Working"**
- Make sure both tunnels are running
- Check that Change Maker is running
- Verify all your addresses match in the files we changed earlier
### Keeping Your Editor Private (Recommended)
1. Go to Cloudflare Zero Trust
2. Create an Access Policy:
- Click "Applications"
- Add new application
- Choose "Self-hosted"
- Enter `https://cmeditor.yoursite.com`
- Add your email under "Allowed Users"
3. Now only you can access the editor
### Need Help?
If things aren't working:
1. Check that Change Maker is running
2. Make sure your tunnels are running
3. Look for error messages in the terminal windows
4. Try restarting everything
### Safety Tips
1. Keep your editor address private
2. Use strong passwords
3. Regular backups are a good idea
4. Keep your computer's security up to date
## Backup Your Work
### Quick Backup
- Find your Change Maker folder
- Copy the whole `docs` folder somewhere safe
- That's your content backed up!
### Restore from Backup
- Just copy your backed-up `docs` folder back
- Restart Change Maker
- Your content should be back
## After Setup
Remember to:
1. Keep the program running when you want your site available
2. Make regular backups of your content
3. Keep your domain and Cloudflare account active
4. Update your software regularly
Need more help? Check the technical manual for detailed instructions or ask in the community forums.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,409 @@
# Change Maker Simple Button
<div style="background: linear-gradient(135deg, rgba(255,215,0,0.05), rgba(255,246,169,0.05)); padding: 2rem; border-radius: 15px; margin: 1.5rem 0;">
<p style="font-size: 1.5rem; text-align: center; margin: 0; background: linear-gradient(135deg, #ffd700, #fff6a9); -webkit-background-clip: text; background-clip: text; color: transparent; font-weight: bold; line-height: 1.4;">
Transform any website into a powerful catalyst for change with our seamless, one-click solution.
</p>
<p style="font-size: 1.1rem; text-align: center; margin: 1rem 0 0; color: #666; text-shadow: 0 1px 2px rgba(255,215,0,0.1);">
Free. Open. Built by activists, for activists.
</p>
</div>
<style>
@media (max-width: 768px) {
.hero-flex-container {
flex-direction: column !important;
padding: 0.5rem !important;
margin: 1rem 0 !important;
}
.hero-flex-container > div {
width: 100% !important;
margin: 0.25rem 0 !important;
}
.feature-grid {
grid-template-columns: 1fr !important;
gap: 0.25rem !important;
margin: 0.5rem 0 !important;
}
.feature-card {
display: flex !important;
align-items: center !important;
text-align: left !important;
padding: 0.5rem !important;
}
.feature-card > div:first-child {
margin-right: 0.5rem !important;
}
.iframe-container {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important; /* Center the iframe container */
}
.iframe-container iframe {
min-height: 600px !important;
width: 100% !important;
max-width: 400px !important;
margin: 0 !important; /* Remove margins to align properly */
}
.iframe-title {
margin-bottom: 0.25rem !important;
}
}
</style>
<div class="hero-flex-container" style="display: flex; gap: 2rem; align-items: start; background: linear-gradient(135deg, rgba(255,215,0,0.1), rgba(255,246,169,0.1)); padding: 2rem; border-radius: 20px; margin: 2rem 0;">
<div class="hero-container" style="background: linear-gradient(135deg, #1a1a1a, #2d2d2d); border-radius: 15px; padding: 1.5rem; color: white; flex: 1;">
<h1 style="background: linear-gradient(135deg, #ffd700, #fff6a9); -webkit-background-clip: text; background-clip: text; color: transparent; font-size: 2rem; text-align: center; margin: 0;">Simple Button</h1>
<p style="text-align: center; font-size: 1rem; margin: 1rem 0 1.5rem; color: #e0e0e0;">Empower your community with zero technical hassle.</p>
<div class="feature-grid" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin: 1rem 0;">
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">📧</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Direct Impact</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">Real emails that work</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;"></div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Instant Setup</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">From idea to action in minutes</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">🔒</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Privacy First</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">Your data stays yours</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">💝</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Always Free</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">No hidden costs</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">🤝</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Community Built</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">By organizers, for organizers</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">🌐</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Instant Pages</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">Built-in landing pages</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">🔌</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Embed Anywhere</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">Works on any website</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">🛟</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Ongoing Support</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">Community backed help</p>
</div>
</div>
<div class="feature-card" style="background: rgba(255,255,255,0.05); padding: 0.6rem; border-radius: 8px; text-align: center;">
<div style="font-size: 1.5rem;">👩‍💻</div>
<div>
<h3 style="margin: 0.3rem 0; font-size: 0.8rem; color: #ffd700;">Open Code</h3>
<p style="margin: 0; font-size: 0.7rem; color: #e0e0e0;">Transparent & secure</p>
</div>
</div>
</div>
</div>
<div class="iframe-container" style="flex: 1; min-width: 300px; display: flex; flex-direction: column; align-items: center;">
<h2 class="iframe-title" style="text-align: center; font-size: 1.2rem; margin: 0 0 0.5rem; background: linear-gradient(135deg, #ffd700, #fff6a9); -webkit-background-clip: text; background-clip: text; color: transparent;">Live Demo</h2>
<iframe
id="emailButtonIframe-change"
src="https://button.bnkops.com/embed/change"
style="width: 100%; max-width: 450px; height: 580px; border: none; overflow: hidden; border-radius: 10px; margin: 0 auto;"
scrolling="no"
frameborder="0"
></iframe>
</div>
</div>
## Why Use Simple Change Button?
- ✨ **Free Forever** - No hidden costs, no premium features
- 🚀 **5-Minute Setup** - From zero to action in minutes
- 📧 **Real Impact** - Emails come from real people's accounts
- 🔒 **Privacy First** - No invasive tracking of users
- 💪 **Community Owned** - Built by organizers, for organizers
Install Simple Change Button on your website and start making a difference today!
## Installation Guide
Simple Change Button is a self-hosted tool that requires Docker & Docker Compose to run. Follow these steps to get started:
Install Docker & Docker Composer
[Docker Installation Instructions](Installation.md){ .md-button }
Releases of the latest version of Simple Change Button are released with the newsletter. To get the latest version, subscribe to the newsletter below:
<form method="post" action="https://listmonk.bnkops.com/subscription/form" class="listmonk-form">
<div style="
background-color: #fbbf24;
padding: 1.25rem;
padding-top: 0.5rem;
border-radius: 0.5rem;
color: #1f2937;
width: 100%;
max-width: 800px;
margin: 1rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
animation: glow 2s ease-in-out infinite;
position: relative;
left: 50%;
transform: translateX(-50%);
display: inline-block;
">
<style>
@keyframes glow {
0% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
50% { box-shadow: 0 0 20px rgba(251, 191, 36, 0.4); }
100% { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
}
@media (max-width: 1024px) {
.listmonk-form > div {
width: 90% !important;
max-width: 600px !important;
}
}
@media (max-width: 640px) {
.listmonk-form > div {
width: 95% !important;
max-width: none !important;
}
}
</style>
<h3 style="font-size: 1.25rem; font-weight: bold; margin-bottom: 0.75rem; color: #1f2937;">Subscribe</h3>
<input type="hidden" name="nonce" />
<div style="display: flex; gap: 1rem; margin-bottom: 0.5rem;">
<p style="margin: 0; flex: 1;">
<input type="email"
name="email"
required
placeholder="E-mail"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
<p style="margin: 0; flex: 1;">
<input type="text"
name="name"
placeholder="Name (optional)"
style="width: 100%; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.375rem; background: white; color: #4b5563; ::placeholder { color: #9ca3af; }" />
</p>
</div>
<p style="display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.75rem 0;">
<input id="038eb"
type="checkbox"
name="l"
checked
value="038eb469-e141-435d-86eb-2ab4df20cf9c"
style="width: 1rem; height: 1rem; accent-color: #1f2937;" />
<label for="038eb" style="font-size: 0.875rem;">Weekly(ish) Update</label>
</p>
<p style="margin: 0;">
<input type="submit"
value="Subscribe"
style="width: 100%; padding: 0.75rem; background-color: #1f2937; color: white; border: none; border-radius: 0.375rem; font-weight: 500; cursor: pointer; transition: all 0.2s; hover: { background-color: #374151; transform: translateY(-1px); }" />
</p>
</div>
</form>
Or you can email the admin directly and request access:
<div style="width: 350px; margin: 0 auto;">
<iframe
id="emailButtonIframe-email-admin"
src="https://button.bnkops.com/embed/email-admin"
style="width: 100%; height: 120px; border: none; overflow: hidden;"
scrolling="no"
frameborder="0"
></iframe>
</div>
## Install
### Independent Installation
#### Download and Unzip the Latest Release
#### Update .env Files
In the Simple Change Button directory, update the `.env` files with your desired settings. Theese two files can be found in the `backend` and `frontend` directories.
!!! example ".frontend/.env"
```
REACT_APP_API_URL=http://localhost:5000
REACT_APP_PRODUCTION_URL=https://button.bnkops.com // Change this to your domain
NODE_ENV=development // change to production for production
PORT=5001
```
!!! example ".backend/.env"
```
JWT_SECRET=your-very-secure-and-very-long-secret-key-here // Change this to a secure key
MONGODB_URI=mongodb://127.0.0.1:27017/emailcounter
PORT=5000
NODE_ENV=development // change to production for production
CORS_ORIGIN=http://localhost:5001
```
Run `docker-compose up` in the Simple Change Button directory.
Your Simple Change Button instance will be available at `http://localhost:5001` in your browser.
### Install With Changemaker
If you are using Changemaker, you can install Simple Change Button during installation.
Run `./start.sh` in the root directory and select Simple Change Button from the list of available tools.
## First Open
On first open, you will be prompted to create an admin account. Fill in the details and click "Create Account". You will then be redirected to the dashboard.
### Quick Start
1. Create a button in the dashboard
2. Copy the embed code
3. Paste it anywhere on your site
4. Track real engagement
## Dashboard
![alt text](image-21.png)
The dashboard is where you can create, manage, and track your buttons. You can also manage users and view statistics.
### Dashboard Features
- Total emails sent
- Per-button metrics
- Usage trends
- Real-time statistics
- Export capabilities
- User Management
## Analytics
![alt text](image-23.png)
Analytics are available in real-time and provide insights into button performance. You can also export data for further analysis.
## User Management
![alt text](image-24.png)
User management allows you to add, remove, and edit user accounts. Future updates will include assign roles and permissions to users.
## Creating a Button
![alt text](image-22.png)
1. Log into admin dashboard
2. Click "Create New Button"
3. Fill in button details
4. Optional: Add CC/BCC
5. Save and get embed code
## Editing a Button
![alt text](image-25.png)
Buttons are listed according to time of creation and can be edited at any time. You can change the button text, email body, and other settings.
## Standalone Button Page
![alt text](image-26.png)
All buttons also generate a standalone page that can be shared directly. This page includes the button, email body, and other details. Useful for sharing on social media or in emails.
## Privacy & Security
- No personal data collection
- GDPR-compliant design
- Encrypted connections
- Local data storage only
## Deployment
Bnkops uses Cloudflare for secure, fast, and reliable hosting. You can also deploy Simple Change Button on your own server.
We have instructions for deployment through Cloudflare for the whole Changemaker suite.
[Cloudflare Deployment](Get Site Online.md){ .md-button }
However, you can also deploy Simple Change Button independently. Here is a sample configuration file for Cloudflare Tunnel:
!!! example "Cloudflare Tunnel Configuration"
```yaml
tunnel: [YOUR-TUNNEL-ID]
credentials-file: [path-to-credentials-file]
ingress:
- hostname: [button.your-domain.com]
service: http://localhost:5001
- service: http_status:404
```
## Best Practices
!!! tip "Design Recommendations"
- Keep email body concise
- Use clear call-to-action text
- Test buttons before deployment
- Monitor performance regularly
## Troubleshooting
!!! question "Common Issues"
**Button not tracking:**
- Check internet connection
- Verify proper embedding
- Clear browser cache
**Stats not updating:**
- Refresh dashboard
- Check server connection
- Verify permissions
## Support
Contact system administrator for:
- Technical issues
- Feature requests
- Account problems
- General assistance
---
Built with ♥️ by communities, for communities

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -0,0 +1,6 @@
# Archive
The following is content for the archive.
It is highly likely that a lot of links will be broken in this page, so please check it carefully!