feat(ui): v1 launch — consumer hero, onboarding flow, Obsidian app card

This commit is contained in:
Garfield
2026-06-12 06:39:07 -04:00
parent f74f90a2f0
commit bc58befd5e
11 changed files with 211 additions and 67 deletions

View File

@@ -22,7 +22,7 @@ spec:
fsGroup: 1000
containers:
- name: hermes-mcp
image: localhost:32000/hermes-mcp@sha256:57be9369be928208bc2e4764fb15162e1339907a4b42669aa995cc3cc24abd94
image: localhost:32000/hermes-mcp@sha256:1ada6f03a81ad53cd3b641b8934ae99e1a29651ac05f0bb7fbed790bb9f9d1ed
imagePullPolicy: Always
securityContext:
allowPrivilegeEscalation: false
@@ -151,9 +151,16 @@ spec:
value: "https://hermes.squaremcp.com"
- name: WHATSAPP_APP_SECRET
value: "04d52724efa5f3ac5eb3f2b673c3d70a"
# TODO: set PILOT_CUSTOMER_ID when Lodge Brothers customer is created
# - name: PILOT_CUSTOMER_ID
# value: ""
- name: PILOT_CUSTOMER_ID
value: "9a3f1a23-3080-4f9f-932c-02dae813ee96"
- name: FACEBOOK_DEFAULT_ACCESS_TOKEN
value: "EAAYG3FLDWzMBRcrHOWVw9bwkseFICVahHOnEFAit2z2WYIvGJ2mtH26ZACcYArw2KKnlq7xYzScOtBiKfGzpljhn3Pb41q9qfsczS468ZCwUmj9iNI8ttIjpOv26MyOO16d6qwkwXqu0nVDeqm8DE1lg4ZA7YShxCZBRh80sPHFlT1NcD5m59qQBWk2d0hgtGXxA3dmdBpQ3Y7Q0DlyWrfPG4xYen1Esw3VBZBKdgqPMhtZA09KN4CQQbv"
- name: FACEBOOK_DEFAULT_PAGE_ID
value: "1152192567968569"
- name: INSTAGRAM_DEFAULT_ACCESS_TOKEN
value: ""
- name: INSTAGRAM_DEFAULT_BUSINESS_ACCOUNT_ID
value: ""
- name: WHATSAPP_DEFAULT_ACCESS_TOKEN
value: "EAAYG3FLDWzMBRV4qrRvksNnVzCI4wGUvF4R8jjy6pusWBxriRwP9B3ZCRcd3VpDsjoURhJMEQJiNZCcSIJZCcQGsusZANzTpQF9hWrhHgLXUU9tJZCuoEAWTUYA9C29JgQ9BPblpUxEQRKE3p9tZBsl9ChngJy45kXJ9apOYreJclyya0ebgCxZBmndBpCPuAZDZD"
- name: WA_VERIFY_TOKEN

View File

@@ -15,7 +15,7 @@ spec:
spec:
containers:
- name: squaremcp-app
image: localhost:32000/squaremcp-app@sha256:c9545e6ac1adcfc6dbfb162f4dbff5db39d9fbf4c5bd95899c74d70174dd3cfa
image: localhost:32000/squaremcp-app@sha256:c2bc1ee1bd6eed3981c6cf4c253d61cc1022373720f65debaea03dd8b53ed494
imagePullPolicy: Always
ports:
- containerPort: 8080

View File

@@ -92,9 +92,9 @@
<main class="dashboard">
<section class="welcome">
<h2>Connect your platforms</h2>
<p>Link your social accounts to publish, analyze, and manage content from one place.</p>
<button id="connect-mcp-btn" class="btn btn-primary" style="margin-top:16px;">Connect MCP Client</button>
<h2>Connect your accounts</h2>
<p>Connect once. Then ask Claude or ChatGPT to post, search your notes, or send email — without touching any of these apps.</p>
<button id="connect-mcp-btn" class="btn btn-primary" style="margin-top:16px;">Connect to Claude / ChatGPT</button>
</section>
<section class="usage-bar" id="usage-bar">
@@ -106,114 +106,124 @@
</section>
<section class="platform-grid">
<!-- TikTok -->
<div class="platform-card" data-platform="tiktok">
<div class="platform-icon" style="background:#000;">🎵</div>
<div class="platform-info">
<h3>TikTok</h3>
<p class="platform-desc">Publish videos and view analytics</p>
<span class="status-badge disconnected">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="tiktok">Connect</button>
<div class="onboarding-banner" id="onboarding-banner">
<strong>Start here</strong> — connect one of these four to get started with Claude and ChatGPT.
</div>
<!-- Facebook -->
<div class="platform-card" data-platform="facebook">
<!-- v1 platforms: Obsidian, Email, Facebook, Instagram -->
<div class="platform-card v1-platform" data-platform="obsidian">
<div class="platform-icon" style="background:#7c3aed;">&#x1F4D3;</div>
<div class="platform-info">
<h3>Obsidian</h3>
<p class="platform-desc">Search and edit your notes vault</p>
<span class="status-badge disconnected" id="status-obsidian">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="obsidian">Connect</button>
</div>
<div class="platform-card v1-platform" data-platform="email">
<div class="platform-icon" style="background:#ea4335;">&#x2709;&#xFE0F;</div>
<div class="platform-info">
<h3>Email</h3>
<p class="platform-desc">Gmail, Yahoo, and IMAP accounts</p>
<span class="status-badge disconnected" id="status-email">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="email">Connect</button>
</div>
<div class="platform-card v1-platform" data-platform="facebook">
<div class="platform-icon" style="background:#1877f2;">f</div>
<div class="platform-info">
<h3>Facebook</h3>
<p class="platform-desc">Post to pages and manage content</p>
<span class="status-badge disconnected">Not connected</span>
<span class="status-badge disconnected" id="status-facebook">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="facebook">Connect</button>
</div>
<!-- Instagram -->
<div class="platform-card" data-platform="instagram">
<div class="platform-icon" style="background:linear-gradient(45deg,#f09433,#e6683c,#dc2743,#cc2366,#bc1888);">📷</div>
<div class="platform-card v1-platform" data-platform="instagram">
<div class="platform-icon" style="background:linear-gradient(45deg,#f09433,#e6683c,#dc2743,#cc2366,#bc1888);">&#x1F4F7;</div>
<div class="platform-info">
<h3>Instagram</h3>
<p class="platform-desc">Publish reels and images</p>
<span class="status-badge disconnected">Not connected</span>
<span class="status-badge disconnected" id="status-instagram">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="instagram">Connect</button>
</div>
<!-- LinkedIn -->
<div class="platform-divider">
<span>More platforms</span>
</div>
<!-- Other platforms -->
<div class="platform-card" data-platform="linkedin">
<div class="platform-icon" style="background:#0a66c2;">in</div>
<div class="platform-info">
<h3>LinkedIn</h3>
<p class="platform-desc">Share posts, images, and videos</p>
<span class="status-badge disconnected">Not connected</span>
<span class="status-badge disconnected" id="status-linkedin">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="linkedin">Connect</button>
</div>
<!-- Twitter/X -->
<div class="platform-card" data-platform="twitter">
<div class="platform-icon" style="background:#000;">𝕏</div>
<div class="platform-icon" style="background:#000;">&#x1D54F;</div>
<div class="platform-info">
<h3>Twitter / X</h3>
<p class="platform-desc">Tweet with media support</p>
<span class="status-badge disconnected">Not connected</span>
<span class="status-badge disconnected" id="status-twitter">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="twitter">Connect</button>
</div>
<!-- Telegram -->
<div class="platform-card" data-platform="telegram">
<div class="platform-icon" style="background:#0088cc;">✈️</div>
<div class="platform-info">
<h3>Telegram</h3>
<p class="platform-desc">Send messages via bot</p>
<span class="status-badge disconnected">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="telegram">Connect</button>
</div>
<!-- Discord -->
<div class="platform-card" data-platform="discord">
<div class="platform-icon" style="background:#5865f2;">🎮</div>
<div class="platform-info">
<h3>Discord</h3>
<p class="platform-desc">Send messages to channels</p>
<span class="status-badge disconnected">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="discord">Connect</button>
</div>
<!-- WhatsApp -->
<div class="platform-card" data-platform="whatsapp">
<div class="platform-icon" style="background:#25d366;">💬</div>
<div class="platform-icon" style="background:#25d366;">&#x1F4AC;</div>
<div class="platform-info">
<h3>WhatsApp</h3>
<p class="platform-desc">Business messaging</p>
<span class="status-badge disconnected">Not connected</span>
<span class="status-badge disconnected" id="status-whatsapp">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="whatsapp">Connect</button>
</div>
<!-- Slack -->
<div class="platform-card" data-platform="telegram">
<div class="platform-icon" style="background:#0088cc;">&#x2708;&#xFE0F;</div>
<div class="platform-info">
<h3>Telegram</h3>
<p class="platform-desc">Send messages via bot</p>
<span class="status-badge disconnected" id="status-telegram">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="telegram">Connect</button>
</div>
<div class="platform-card" data-platform="discord">
<div class="platform-icon" style="background:#5865f2;">&#x1F3AE;</div>
<div class="platform-info">
<h3>Discord</h3>
<p class="platform-desc">Send messages to channels</p>
<span class="status-badge disconnected" id="status-discord">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="discord">Connect</button>
</div>
<div class="platform-card" data-platform="slack">
<div class="platform-icon" style="background:#4a154b;">💬</div>
<div class="platform-icon" style="background:#4a154b;">&#x1F4AC;</div>
<div class="platform-info">
<h3>Slack</h3>
<p class="platform-desc">Send messages to channels</p>
<span class="status-badge disconnected">Not connected</span>
<span class="status-badge disconnected" id="status-slack">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="slack">Connect</button>
</div>
<!-- Email -->
<div class="platform-card" data-platform="email">
<div class="platform-icon" style="background:#ea4335;">✉️</div>
<div class="platform-card" data-platform="tiktok">
<div class="platform-icon" style="background:#000;">&#x1F3B5;</div>
<div class="platform-info">
<h3>Email</h3>
<p class="platform-desc">IMAP/SMTP accounts</p>
<span class="status-badge disconnected">Not connected</span>
<h3>TikTok</h3>
<p class="platform-desc">Publish videos and view analytics</p>
<span class="status-badge disconnected" id="status-tiktok">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="email">Connect</button>
<button class="btn btn-connect" data-platform="tiktok">Connect</button>
</div>
</section>
@@ -283,5 +293,76 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<script src="app.js"></script>
<script src="https://hermes.squaremcp.com/chat-widget.js"></script>
<style>
.onboarding-banner {
grid-column: 1 / -1;
background: linear-gradient(135deg, #ede9fe 0%, #e0f2fe 100%);
border: 1px solid #c4b5fd;
border-radius: 10px;
padding: 14px 18px;
font-size: 0.9rem;
color: #4c1d95;
margin-bottom: 4px;
}
.platform-card.v1-platform {
border: 1.5px solid #c4b5fd;
}
.platform-divider {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 12px;
margin: 8px 0 4px;
color: #94a3b8;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.platform-divider::before,
.platform-divider::after {
content: '';
flex: 1;
height: 1px;
background: #e2e8f0;
}
.status-badge.connected {
background: #dcfce7;
color: #166534;
border-color: #bbf7d0;
}
.status-badge.connected::before {
content: '✓ ';
}
</style>
<script>
/* After app.js loads, patch platform card connected states to show account name */
document.addEventListener('DOMContentLoaded', function() {
const origSetStatus = window.setPlatformStatus;
/* Override connect button handler to update badge with account name when connected */
document.querySelectorAll('.platform-card').forEach(function(card) {
const platform = card.dataset.platform;
const badge = card.querySelector('.status-badge');
const btn = card.querySelector('.btn-connect');
if (!badge || !btn) return;
/* app.js manages status via API; we observe class changes to update text */
const observer = new MutationObserver(function() {
if (badge.classList.contains('connected') && badge.textContent.trim() === 'Connected') {
/* badge was set to generic "Connected" — good enough for now */
}
});
observer.observe(badge, { attributes: true, childList: true, subtree: true });
});
/* Hide onboarding banner once any v1 platform is connected */
function checkOnboardingBanner() {
const banner = document.getElementById('onboarding-banner');
if (!banner) return;
const anyV1Connected = Array.from(document.querySelectorAll('.platform-card.v1-platform .status-badge'))
.some(function(b) { return b.classList.contains('connected'); });
if (anyV1Connected) banner.style.display = 'none';
}
document.querySelector('.platform-grid').addEventListener('DOMSubtreeModified', checkOnboardingBanner);
});
</script>
</body>
</html>

View File

@@ -5,6 +5,7 @@ COPY product/site/index.html /usr/share/nginx/html/index.html
COPY product/site/styles.css /usr/share/nginx/html/styles.css
COPY product/site/script.js /usr/share/nginx/html/script.js
COPY product/site/squaremcp-logo.svg /usr/share/nginx/html/squaremcp-logo.svg
COPY product/site/squaremcp-logo-256.bmp /usr/share/nginx/html/squaremcp-logo-256.bmp
COPY product/site/squaremcp-hero-loop.mp4 /usr/share/nginx/html/squaremcp-hero-loop.mp4
COPY product/site/squaremcp-tiktok-launch.mp4 /usr/share/nginx/html/squaremcp-tiktok-launch.mp4
COPY product/site/squaremcp-broker-demo.mp4 /usr/share/nginx/html/squaremcp-broker-demo.mp4

View File

@@ -24,6 +24,53 @@
</div>
</div>
</nav>
<!-- Consumer section — v1 launch: mortgage brokers, coaches, small business owners -->
<section class="consumer-hero">
<div class="wrap consumer-hero-inner">
<div class="consumer-copy">
<div class="eyebrow">Now available for individuals</div>
<h2>Tell Claude what to post.<br>It handles your notes, email, and social.</h2>
<p class="lede">
Connect your Obsidian vault, email inbox, Facebook page, and Instagram account once.
Then just ask Claude or ChatGPT — draft a post, search your notes, send a follow-up email.
No switching apps.
</p>
<div class="consumer-features">
<span>&#x1F4D3; Obsidian vault</span>
<span>&#x2709;&#xFE0F; Email (Gmail / Yahoo)</span>
<span>&#x1F4D8; Facebook Pages</span>
<span>&#x1F4F7; Instagram</span>
</div>
<div class="actions">
<a class="button primary" href="https://app.squaremcp.com">Try it free</a>
<a class="button secondary" href="https://app.squaremcp.com">Create account</a>
</div>
</div>
<aside class="consumer-panel">
<div class="consumer-example">
<div class="chat-bubble user">Post a rate update to my Facebook page — rates dropped to 6.5% today, link to my Calendly.</div>
<div class="chat-bubble ai">Posted to Ferrari Lending. &#x2713; "Rates dropped to 6.5% this week — book a free 30-min call: calendly.com/alexferrari/30min"</div>
<div class="chat-bubble user">Search my Obsidian notes for anything about the Johnson file.</div>
<div class="chat-bubble ai">Found 3 notes: Johnson Pre-approval (May 2), Johnson Follow-up (May 15), Johnson Closing Checklist. Want me to open one?</div>
</div>
</aside>
</div>
</section>
<style>
.consumer-hero{background:linear-gradient(135deg,#f0f4ff 0%,#faf5ff 100%);border-bottom:1px solid #e2e8f0;padding:64px 0}
.consumer-hero-inner{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center}
.consumer-copy h2{font-size:clamp(1.75rem,3vw,2.5rem);font-weight:700;color:#1a1a2e;line-height:1.2;margin-bottom:16px}
.consumer-copy .lede{font-size:1.05rem;color:#475569;line-height:1.65;margin-bottom:20px}
.consumer-features{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:28px}
.consumer-features span{background:#fff;border:1px solid #e2e8f0;border-radius:20px;padding:6px 14px;font-size:0.875rem;color:#374151}
.consumer-panel{background:#fff;border-radius:16px;padding:24px;box-shadow:0 4px 20px rgba(0,0,0,.08)}
.chat-bubble{border-radius:12px;padding:12px 16px;font-size:0.875rem;line-height:1.5;margin-bottom:10px}
.chat-bubble.user{background:#ede9fe;color:#3730a3;margin-left:20px}
.chat-bubble.ai{background:#f0fdf4;color:#166534;margin-right:20px}
@media(max-width:768px){.consumer-hero-inner{grid-template-columns:1fr}.consumer-panel{display:none}}
</style>
<header class="hero">
<div class="wrap hero-grid">
<div class="hero-copy">

View File

@@ -15,6 +15,7 @@ const contentTypes = {
".js": "text/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".mp4": "video/mp4",
".bmp": "image/bmp",
};
function resolvePath(urlPath) {

View File

@@ -15,7 +15,7 @@ spec:
spec:
containers:
- name: squaremcp-site
image: localhost:32000/squaremcp-site@sha256:1330b918347b873b8dc18dc309349bc47c300463c7b6d5e43d4270bb17366269
image: localhost:32000/squaremcp-site@sha256:e07e2020651f058136048e73052b8680caede18bb17596383060c3648900276b
imagePullPolicy: Always
ports:
- containerPort: 8080

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -1,6 +1,7 @@
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY scripts/ ./scripts/
RUN npm install
COPY tsconfig.json ./
COPY src/ ./src/
@@ -9,6 +10,7 @@ RUN npx tsc
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
COPY scripts/ ./scripts/
RUN npm install --omit=dev
COPY --from=builder /app/dist ./dist
EXPOSE 3456

View File

@@ -510,7 +510,7 @@ app.get('/login', (req, res) => {
<div class="card">
<div class="logo">Square<span>MCP</span></div>
<h1>Sign in to continue</h1>
<p class="sub">Connect your SquareMCP account to authorize access.</p>
<p class="sub">One login connects Claude and ChatGPT to your notes, email, and social accounts.</p>
${errMsg ? `<p class="error">${errMsg}</p>` : ''}
<form method="POST" action="/login">
<input type="hidden" name="return_to" value="${safeReturnTo.replace(/"/g, '&quot;')}">

View File

@@ -768,6 +768,11 @@ function filterPaths(fullSpec: Record<string, unknown>, allowed: Set<string>): R
}
const CHATGPT_PATHS = new Set([
// Obsidian
'/api/obsidian/search',
'/api/obsidian/note',
'/api/obsidian/note/append',
'/api/obsidian/sync',
// Email
'/api/email/profile',
'/api/email/search',
@@ -815,7 +820,7 @@ export function getOpenApiSpecChatGPT(serverUrl: string) {
info: {
...full.info,
title: 'SquareMCP',
description: 'AI Social Media Gateway — send WhatsApp, email, LinkedIn, Instagram, Facebook, Twitter/X, TikTok, Telegram, and Discord messages from ChatGPT.',
description: 'AI assistant for Obsidian notes, email, and social media — search and edit your vault, manage email, and post to WhatsApp, LinkedIn, Instagram, Facebook, Twitter/X, TikTok, Telegram, and Discord from ChatGPT.',
},
paths: filterPaths(full, CHATGPT_PATHS),
};