vibespot 1.0.8 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -5
- package/dist/index.js +459 -197
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/ui/chat.js +70 -7
- package/ui/docs/index.html +157 -4
- package/ui/index.html +79 -3
- package/ui/plan.js +0 -0
- package/ui/settings.js +228 -1
- package/ui/setup.js +289 -1
- package/ui/styles.css +499 -2
- package/ui/vendor/marked.umd.js +46 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibespot",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "AI-powered HubSpot CMS landing page builder — vibe coding & React converter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"prepublishOnly": "npm run build"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@anthropic-ai/sdk": "^0.
|
|
22
|
+
"@anthropic-ai/sdk": "^0.91.1",
|
|
23
23
|
"@clack/prompts": "^0.9.1",
|
|
24
24
|
"@codemirror/lang-css": "^6.3.1",
|
|
25
25
|
"@codemirror/lang-html": "^6.4.11",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"busboy": "^1.6.0",
|
|
30
30
|
"chalk": "^5.4.1",
|
|
31
31
|
"codemirror": "^6.0.2",
|
|
32
|
-
"commander": "^
|
|
33
|
-
"execa": "^9.
|
|
32
|
+
"commander": "^14.0.3",
|
|
33
|
+
"execa": "^9.6.1",
|
|
34
34
|
"mammoth": "^1.11.0",
|
|
35
|
-
"marked": "^
|
|
35
|
+
"marked": "^18.0.2",
|
|
36
36
|
"pdf-parse": "^2.4.5",
|
|
37
|
-
"ws": "^8.
|
|
37
|
+
"ws": "^8.20.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/busboy": "^1.5.4",
|
package/ui/chat.js
CHANGED
|
@@ -113,6 +113,11 @@ function handleWsMessage(msg) {
|
|
|
113
113
|
if (historyBtn) {
|
|
114
114
|
historyBtn.style.display = msg.gitAvailable ? "" : "none";
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
// Hydrate plan-mode state (toggle + Plan pane content)
|
|
118
|
+
if (window.planController) {
|
|
119
|
+
window.planController.setInitialState(msg);
|
|
120
|
+
}
|
|
116
121
|
break;
|
|
117
122
|
|
|
118
123
|
case "stream":
|
|
@@ -188,6 +193,49 @@ function handleWsMessage(msg) {
|
|
|
188
193
|
case "brand_extraction_error":
|
|
189
194
|
appendSystemMessage("Brand extraction failed: " + (msg.message || "Unknown error"));
|
|
190
195
|
break;
|
|
196
|
+
|
|
197
|
+
case "figma_import_started":
|
|
198
|
+
startStreaming();
|
|
199
|
+
appendSystemMessage("Importing from Figma: " + (msg.fileName || "design") + "...");
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case "needs_setup":
|
|
203
|
+
// Clear stale UI if shown
|
|
204
|
+
messagesEl.innerHTML = "";
|
|
205
|
+
document.getElementById("module-items").innerHTML = "";
|
|
206
|
+
document.getElementById("module-count").textContent = "0";
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case "plan_updated":
|
|
210
|
+
if (window.planController) window.planController.onPlanUpdated(msg.plan || "");
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case "plan_choices":
|
|
214
|
+
if (window.planController) window.planController.renderChoices(msg.question, msg.options);
|
|
215
|
+
break;
|
|
216
|
+
|
|
217
|
+
case "plan_complete":
|
|
218
|
+
// Replace the streaming bubble's content with the cleaned message
|
|
219
|
+
// (vibespot-plan + vibespot-choices fenced blocks stripped). We
|
|
220
|
+
// also overwrite streamBuffer so finishStreaming's final render
|
|
221
|
+
// doesn't re-introduce trailing <br> tags from the stripped block.
|
|
222
|
+
if (typeof msg.cleanedContent === "string") {
|
|
223
|
+
streamBuffer = msg.cleanedContent.trim();
|
|
224
|
+
}
|
|
225
|
+
if (streamingMsgEl) {
|
|
226
|
+
if (streamBuffer) {
|
|
227
|
+
streamingMsgEl.innerHTML = renderMarkdown(streamBuffer);
|
|
228
|
+
} else {
|
|
229
|
+
streamingMsgEl.innerHTML = '<em class="message__placeholder">Updated the plan in the Plan pane.</em>';
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
finishStreaming();
|
|
233
|
+
clearStreamStatus();
|
|
234
|
+
break;
|
|
235
|
+
|
|
236
|
+
case "plan_discarded":
|
|
237
|
+
if (window.planController) window.planController.onPlanDiscarded();
|
|
238
|
+
break;
|
|
191
239
|
}
|
|
192
240
|
}
|
|
193
241
|
|
|
@@ -542,17 +590,26 @@ const DOC_TYPES = new Set([
|
|
|
542
590
|
"text/markdown", "text/plain",
|
|
543
591
|
]);
|
|
544
592
|
const SUPPORTED_TYPES = new Set([...IMAGE_TYPES, ...DOC_TYPES]);
|
|
593
|
+
// Browsers often report .md files as application/octet-stream or empty string
|
|
594
|
+
const EXT_MIME_MAP = { ".md": "text/markdown", ".txt": "text/plain", ".markdown": "text/markdown" };
|
|
545
595
|
|
|
546
596
|
function addPendingFile(file) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
597
|
+
let f = file;
|
|
598
|
+
if (!SUPPORTED_TYPES.has(f.type)) {
|
|
599
|
+
const ext = f.name.slice(f.name.lastIndexOf(".")).toLowerCase();
|
|
600
|
+
const mapped = EXT_MIME_MAP[ext];
|
|
601
|
+
if (mapped) {
|
|
602
|
+
f = new File([f], f.name, { type: mapped });
|
|
603
|
+
} else {
|
|
604
|
+
showToast(`Unsupported file type: ${f.name}`);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
550
607
|
}
|
|
551
|
-
if (
|
|
552
|
-
showToast(`File too large (>10MB): ${
|
|
608
|
+
if (f.size > MAX_FILE_SIZE) {
|
|
609
|
+
showToast(`File too large (>10MB): ${f.name}`);
|
|
553
610
|
return;
|
|
554
611
|
}
|
|
555
|
-
pendingFiles.push(
|
|
612
|
+
pendingFiles.push(f);
|
|
556
613
|
renderFileChips();
|
|
557
614
|
}
|
|
558
615
|
|
|
@@ -955,6 +1012,9 @@ function renderMarkdown(text) {
|
|
|
955
1012
|
text = text.replace(/```[\s\S]*?```/g, "");
|
|
956
1013
|
// Also strip unclosed code fences (truncated responses)
|
|
957
1014
|
text = text.replace(/```[\s\S]*$/g, "");
|
|
1015
|
+
// Collapse runs of blank lines left behind by stripped fences (otherwise
|
|
1016
|
+
// they become trailing <br> tags and produce visible empty space).
|
|
1017
|
+
text = text.replace(/\n{3,}/g, "\n\n").trim();
|
|
958
1018
|
|
|
959
1019
|
// Escape HTML to prevent XSS from AI/user content
|
|
960
1020
|
text = escapeHtml(text);
|
|
@@ -1657,7 +1717,10 @@ document.getElementById("responsive-toggle").addEventListener("click", (e) => {
|
|
|
1657
1717
|
|
|
1658
1718
|
const chrome = document.getElementById("browser-chrome");
|
|
1659
1719
|
const width = btn.dataset.width;
|
|
1660
|
-
|
|
1720
|
+
const isFullWidth = width === "100%";
|
|
1721
|
+
chrome.style.maxWidth = isFullWidth ? "none" : width;
|
|
1722
|
+
chrome.style.flex = isFullWidth ? "1" : "none";
|
|
1723
|
+
chrome.style.width = isFullWidth ? "" : width;
|
|
1661
1724
|
|
|
1662
1725
|
// Update browser URL bar with theme name
|
|
1663
1726
|
const urlEl = document.getElementById("browser-url");
|
package/ui/docs/index.html
CHANGED
|
@@ -76,6 +76,17 @@
|
|
|
76
76
|
<a class="doc-nav__link doc-nav__link--sub" href="#intent-types">Intents</a>
|
|
77
77
|
<a class="doc-nav__link doc-nav__link--sub" href="#prompt-tips">Prompt Tips</a>
|
|
78
78
|
<a class="doc-nav__link doc-nav__link--sub" href="#brand-assets">Brand Assets</a>
|
|
79
|
+
<a class="doc-nav__link" href="#plan-mode">Plan Mode</a>
|
|
80
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#plan-mode-when">When to Use</a>
|
|
81
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#plan-mode-flow">The Flow</a>
|
|
82
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#plan-mode-pane">Plan Pane & Editing</a>
|
|
83
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#plan-mode-approval">Approval & Discard</a>
|
|
84
|
+
<a class="doc-nav__link" href="#figma-import">Figma Import (Beta)</a>
|
|
85
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#figma-token">Figma Token Setup</a>
|
|
86
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#figma-extraction">What Gets Extracted</a>
|
|
87
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#figma-image-modes">Image Modes</a>
|
|
88
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#figma-pipeline">Translation Pipeline</a>
|
|
89
|
+
<a class="doc-nav__link doc-nav__link--sub" href="#figma-tips">Tips & Limits</a>
|
|
79
90
|
<a class="doc-nav__link" href="#editing">Editing & Customization</a>
|
|
80
91
|
<a class="doc-nav__link doc-nav__link--sub" href="#field-editor">Field Editor</a>
|
|
81
92
|
<a class="doc-nav__link doc-nav__link--sub" href="#design-system">Design System</a>
|
|
@@ -557,18 +568,20 @@ vibespot</code></pre>
|
|
|
557
568
|
<div class="mock-setup__action"><div class="mock-setup__action-icon">+</div><div class="mock-setup__action-label">New Theme</div><div class="mock-setup__action-desc">Start from scratch</div></div>
|
|
558
569
|
<div class="mock-setup__action"><div class="mock-setup__action-icon">▶</div><div class="mock-setup__action-label">Continue</div><div class="mock-setup__action-desc">Resume saved project</div></div>
|
|
559
570
|
<div class="mock-setup__action"><div class="mock-setup__action-icon">↓</div><div class="mock-setup__action-label">From HubSpot</div><div class="mock-setup__action-desc">Fetch existing theme</div></div>
|
|
560
|
-
<div class="mock-setup__action"><div class="mock-setup__action-icon">&#
|
|
571
|
+
<div class="mock-setup__action"><div class="mock-setup__action-icon">◆</div><div class="mock-setup__action-label">From Figma</div><div class="mock-setup__action-desc">Translate a design<br><span style="font-size:9px;color:var(--doc-yellow)">Beta</span></div></div>
|
|
572
|
+
<div class="mock-setup__action"><div class="mock-setup__action-icon">↻</div><div class="mock-setup__action-label">From React</div><div class="mock-setup__action-desc">Import React project<br><span style="font-size:9px;color:var(--doc-yellow)">Beta</span></div></div>
|
|
561
573
|
</div>
|
|
562
574
|
</div>
|
|
563
575
|
</div>
|
|
564
576
|
</div>
|
|
565
577
|
|
|
566
|
-
<p>The setup screen presents
|
|
578
|
+
<p>The setup screen presents five options:</p>
|
|
567
579
|
<ul>
|
|
568
580
|
<li><strong>New Theme</strong> — Creates a fresh HubSpot theme directory at <code>~/vibespot-themes/your-theme-name</code>. Initializes the standard folder structure (<code>modules/</code>, <code>templates/</code>, <code>css/</code>, <code>js/</code>) and a git repository for version tracking.</li>
|
|
569
581
|
<li><strong>Continue (Resume)</strong> — Reopens an existing project from the sidebar list. Your chat history, modules, and all templates are preserved exactly where you left off.</li>
|
|
570
582
|
<li><strong>From HubSpot (Fetch)</strong> — Downloads an existing theme from your HubSpot portal. vibeSpot scans the fetched modules and imports them into a session so you can modify them with AI or by hand.</li>
|
|
571
|
-
<li><strong>
|
|
583
|
+
<li><strong>From Figma (Beta)</strong> — Paste a Figma URL and vibeSpot extracts design tokens, section structure, copy, and image assets, then translates each section into a HubSpot module. See the dedicated <a href="#figma-import">Figma Import</a> section for the full walkthrough.</li>
|
|
584
|
+
<li><strong>From React (Beta)</strong> — Clones a React or Lovable project from a Git URL and converts its components into native HubSpot modules. The conversion uses the built-in conversion guide to map React patterns to HubL equivalents.</li>
|
|
572
585
|
</ul>
|
|
573
586
|
|
|
574
587
|
<h3 id="first-page">Your First Page</h3>
|
|
@@ -714,6 +727,133 @@ vibespot</code></pre>
|
|
|
714
727
|
</ul>
|
|
715
728
|
</div>
|
|
716
729
|
|
|
730
|
+
<!-- ============================================================
|
|
731
|
+
Section: Plan Mode
|
|
732
|
+
============================================================ -->
|
|
733
|
+
<div class="doc-section" id="plan-mode">
|
|
734
|
+
<h2 id="plan-mode-heading">Plan Mode <a href="#plan-mode" class="doc-anchor">#</a></h2>
|
|
735
|
+
<p class="doc-subtitle">A deliberation phase that runs <em>before</em> any code is generated. Plan mode turns vague briefs into well-scoped plans through guided dialogue, then runs the agentic pipeline against the approved plan as a high-fidelity design brief.</p>
|
|
736
|
+
|
|
737
|
+
<p>Without plan mode, vibeSpot generates immediately on every chat message — fast, but unforgiving on vague prompts. The AI guesses at audience, content, and structure, and you find out what it guessed only after a full generation cycle. Plan mode flips the order: the AI asks targeted questions first, surfaces what's missing, and only generates after you've reviewed and approved a written plan. The plan is markdown, persisted to <code>{theme}/.vibespot/plan.md</code>, and reviewable in a dedicated tab in the right pane.</p>
|
|
738
|
+
|
|
739
|
+
<h3 id="plan-mode-when">When to Use Plan Mode</h3>
|
|
740
|
+
<p>Plan mode is opt-in via a prominent <strong>Plan Mode</strong> pill above the chat input. The toggle persists across sessions in <code>~/.vibespot/config.json</code> as <code>planMode</code>.</p>
|
|
741
|
+
<p>Use plan mode when:</p>
|
|
742
|
+
<ul>
|
|
743
|
+
<li><strong>You're starting from a vague brief.</strong> "Build a landing page for our new SaaS product" benefits hugely from elicitation before code is written.</li>
|
|
744
|
+
<li><strong>Multiple stakeholders need to review.</strong> The plan is a markdown document that can be reviewed, edited, and shared before it becomes a generated theme.</li>
|
|
745
|
+
<li><strong>You're doing exploratory design work.</strong> Iterating on the plan is much cheaper than iterating on generated modules.</li>
|
|
746
|
+
<li><strong>You want explicit control.</strong> The plan documents every section, content choice, and CTA decision before the AI commits to code.</li>
|
|
747
|
+
</ul>
|
|
748
|
+
<p>Skip plan mode when:</p>
|
|
749
|
+
<ul>
|
|
750
|
+
<li>You're making small changes to an existing page (use the regular chat — the agentic pipeline handles modifications well).</li>
|
|
751
|
+
<li>You already have a detailed brief; type it directly and let the agentic pipeline run.</li>
|
|
752
|
+
<li>You're using a Figma design as the source of truth (use <a href="#figma-import">Figma Import</a> instead).</li>
|
|
753
|
+
</ul>
|
|
754
|
+
|
|
755
|
+
<h3 id="plan-mode-flow">The Flow</h3>
|
|
756
|
+
<p>Plan mode follows three implicit phases. The AI tracks which phase to be in based on conversation history.</p>
|
|
757
|
+
<ol class="doc-steps">
|
|
758
|
+
<li><strong>Understand.</strong> Your first message in plan mode triggers an "ask first" response. The AI poses two to four high-leverage gap-filling questions: who is this for, what's the primary CTA, what content sections do you envision, what's the brand tone. The plan in the right pane is mostly TBD markers and an "Open questions" list.</li>
|
|
759
|
+
<li><strong>Research & Draft.</strong> Once you've answered the most important questions, the AI proposes a concrete plan: page goal, audience, primary CTA, a numbered module list with brief descriptions, brand and tone notes. It references existing modules and brand assets in the theme so you don't have to re-state them. It may ask one or two narrow follow-ups to fill remaining gaps.</li>
|
|
760
|
+
<li><strong>Refine.</strong> Subsequent messages tweak the plan. "Move the FAQ above the pricing." "Change the primary CTA to 'Book a demo'." The AI confirms what changed in chat and updates the plan in the pane. It only asks clarifying questions when your edit creates a new ambiguity.</li>
|
|
761
|
+
</ol>
|
|
762
|
+
|
|
763
|
+
<h3 id="plan-mode-pane">The Plan Pane & Inline Editing</h3>
|
|
764
|
+
<p>The <strong>Plan</strong> tab in the right pane (between Preview and Code) is the source of truth for the current plan. While plan mode is on, this tab auto-selects when you send a message so the latest plan is always visible.</p>
|
|
765
|
+
<p>Two modes inside the pane:</p>
|
|
766
|
+
<ul>
|
|
767
|
+
<li><strong>Read mode</strong> (default) — the markdown plan is rendered with proper headings, lists, tables, and emphasis. A persistent footer holds <strong>Approve plan</strong> and <strong>Discard & start over</strong> buttons.</li>
|
|
768
|
+
<li><strong>Edit mode</strong> (pencil icon) — the rendered view becomes a textarea containing the raw markdown. Edit any section directly: rename headers, swap copy, reorder modules. <strong>Save</strong> persists your edits to <code>plan.md</code> and the next AI turn picks them up as context. <strong>Cancel</strong> reverts.</li>
|
|
769
|
+
</ul>
|
|
770
|
+
<p>Inline editing is the fastest path for small fixes (typo, copy tweak, section rename). Use chat for substantive changes that need the AI to revisit assumptions.</p>
|
|
771
|
+
|
|
772
|
+
<h4>Choice chips</h4>
|
|
773
|
+
<p>When the AI asks a question with discrete answers (e.g. "What's the primary goal?"), it can emit clickable answer chips below its message: <code>[Lead capture] [Sign-ups] [Demo bookings] [Brand awareness]</code>. Clicking a chip sends that value as the next user message. You can always type a free-text answer instead — the chips are a shortcut, not a constraint.</p>
|
|
774
|
+
|
|
775
|
+
<h3 id="plan-mode-approval">Approval & Discard</h3>
|
|
776
|
+
<p>vibeSpot enforces a <strong>hard write-gate</strong> while plan mode is active: the agentic pipeline is refused, even if the AI mistakenly tries to emit a module-generation block. Generation is reachable only via two explicit actions:</p>
|
|
777
|
+
<ul>
|
|
778
|
+
<li><strong>Approve plan</strong> — The plan markdown is prepended to a synthesized "Implement the approved plan." message and the agentic pipeline runs against it. The Plan tab automatically switches to Preview so you watch modules generate in place. Plan mode flips off after approval; turn it back on for the next plan.</li>
|
|
779
|
+
<li><strong>Discard & start over</strong> — Clears <code>plan.md</code>, exits plan mode, returns the chat to normal generation. Used when you want to scrap the current direction entirely.</li>
|
|
780
|
+
</ul>
|
|
781
|
+
<p>The plan persists across vibeSpot restarts. Open the same theme weeks later, toggle plan mode on, and your in-progress plan is right where you left it.</p>
|
|
782
|
+
|
|
783
|
+
<div class="doc-callout doc-callout--tip">
|
|
784
|
+
<div class="doc-callout__label">✨ Plan mode + brand assets</div>
|
|
785
|
+
<p>If your theme has a styleguide, brand voice, or product context loaded, the plan-mode AI uses them automatically. The plan will reference the right colors and tone without you re-stating them. After approval, the agentic pipeline gets the plan <em>and</em> the brand assets — double the alignment.</p>
|
|
786
|
+
</div>
|
|
787
|
+
|
|
788
|
+
<div class="doc-callout doc-callout--tip">
|
|
789
|
+
<div class="doc-callout__label">🔎 Web search during planning</div>
|
|
790
|
+
<p>Plan mode auto-engages Web Search when you've toggled it on in <a href="#settings-ai">Settings → AI → AI Capabilities</a>. The AI can look up competitor pages, industry references, or example landing pages while drafting your plan. Works on Anthropic API/OAuth and Claude Code engines.</p>
|
|
791
|
+
</div>
|
|
792
|
+
</div>
|
|
793
|
+
|
|
794
|
+
<!-- ============================================================
|
|
795
|
+
Section: Figma Import
|
|
796
|
+
============================================================ -->
|
|
797
|
+
<div class="doc-section" id="figma-import">
|
|
798
|
+
<h2 id="figma-import-heading">Figma Import (Beta) <a href="#figma-import" class="doc-anchor">#</a></h2>
|
|
799
|
+
<p class="doc-subtitle">Translate a Figma design into a HubSpot CMS theme without re-implementing it. The pipeline preserves the design's exact colors, typography, copy, and section order — the AI is used only to translate each section to HubL, not to make creative decisions.</p>
|
|
800
|
+
|
|
801
|
+
<p>Most AI tools that "import from Figma" treat the design as a vague reference and let the model regenerate everything. vibeSpot does the opposite: design tokens are extracted mechanically and mapped directly to CSS variables; section structure becomes module structure 1:1; per-section text is fed to the AI as field defaults. The AI's only job is to convert each section's layout into HubL syntax.</p>
|
|
802
|
+
|
|
803
|
+
<h3 id="figma-token">Figma Token Setup</h3>
|
|
804
|
+
<ol class="doc-steps">
|
|
805
|
+
<li>Open Figma and go to <strong>Account Settings → Personal access tokens → Generate new token</strong>. Give it a descriptive name like "vibeSpot import." Scopes needed: <strong>File content (read-only)</strong>.</li>
|
|
806
|
+
<li>Copy the token (it's shown only once).</li>
|
|
807
|
+
<li>In vibeSpot, open <strong>Settings → Figma</strong> and paste the token. Click <strong>Test</strong> to verify it works. The token is stored locally in <code>~/.vibespot/config.json</code> as <code>figmaToken</code> and is never sent anywhere except the Figma API.</li>
|
|
808
|
+
</ol>
|
|
809
|
+
<p>You can also test the connection per-import: the import dialog accepts a one-off token if you don't want to save one globally.</p>
|
|
810
|
+
|
|
811
|
+
<h3 id="figma-extraction">What Gets Extracted</h3>
|
|
812
|
+
<p>Click <strong>From Figma</strong> on the setup screen, paste a Figma file URL (or a specific frame URL, which scopes the import to that frame and its descendants), and click <strong>Extract</strong>. Progress streams via Server-Sent Events as the pipeline pulls data from Figma's REST API.</p>
|
|
813
|
+
<p>The extractor produces:</p>
|
|
814
|
+
<ul>
|
|
815
|
+
<li><strong>Design tokens</strong> — Colors with usage counts (most-used fill becomes <code>--theme-color-bg</code>, top non-bg fill becomes <code>--theme-color-primary</code>, etc.); typography styles grouped by role (heading vs body, with exact px sizes); spacing values (sorted unique scale: xs, sm, md, lg, xl, section); effects (box shadows and border radii ready as <code>cssValue</code>).</li>
|
|
816
|
+
<li><strong>Section structure</strong> — Top-level frames in the file or selected frame become modules, in their original Figma order. Each section keeps its name (kebab-cased to a module name), dimensions, layout direction (horizontal/vertical/grid), item spacing, and padding.</li>
|
|
817
|
+
<li><strong>Per-section content</strong> — Every text node is captured with its role (headline, subhead, body, CTA, eyebrow, etc.), font size, font weight, and verbatim text. This becomes the field defaults in the generated module.</li>
|
|
818
|
+
<li><strong>Children structure</strong> — A summary of each section's children (e.g., "3 cards each containing: image, headline, body, button") so the AI knows the layout shape without trying to over-engineer it.</li>
|
|
819
|
+
<li><strong>Image assets</strong> — Every embedded image is downloaded from Figma's image-export API at 2× resolution as PNG. Filenames are sanitized; the full set is shown to you for confirmation before generation.</li>
|
|
820
|
+
</ul>
|
|
821
|
+
<p>After extraction, you see a summary screen: section count, color count, font families, and asset count. Name the theme (this becomes the kebab-case directory name and CSS-variable prefix), choose your image mode, and click <strong>Generate Page</strong>.</p>
|
|
822
|
+
|
|
823
|
+
<h3 id="figma-image-modes">Image Modes</h3>
|
|
824
|
+
<p>A toggle on the import screen controls how images are handled in the generated modules. Both modes copy the extracted PNGs to the theme's <code>assets/</code> directory.</p>
|
|
825
|
+
<ul>
|
|
826
|
+
<li><strong>Import images as assets</strong> <em>(default)</em> — Modules reference images via <code>get_asset_url("{theme}/assets/{filename}")</code>. The page renders pixel-identically to Figma out of the box. Best when the Figma assets are the final assets you want on the live page.</li>
|
|
827
|
+
<li><strong>Image fields with placeholders</strong> — Modules expose HubSpot image fields with <code>placehold.co</code> URLs as defaults. The Figma assets are still in <code>assets/</code>, but the modules don't reference them; instead, content editors swap in their own images via HubSpot's editor. Best when Figma images are mockups (stock photography, lorem ipsum imagery) and the real images will be different.</li>
|
|
828
|
+
</ul>
|
|
829
|
+
|
|
830
|
+
<h3 id="figma-pipeline">The Translation Pipeline</h3>
|
|
831
|
+
<p>Figma import runs a streamlined pipeline (separate from the agentic pipeline used by chat-driven generation) optimized for fidelity:</p>
|
|
832
|
+
<ol class="doc-steps">
|
|
833
|
+
<li><strong>Generate shared CSS</strong> — Deterministic mapping of design tokens to <code>:root</code> CSS variables and utility classes (<code>{theme}-container</code>, <code>{theme}-section</code>, <code>{theme}-grid</code>, <code>{theme}-btn</code>, etc.). Lightness analysis on the dominant color auto-selects light vs dark mode defaults. Standard responsive breakpoints (mobile and tablet) are emitted automatically. <strong>No AI involved</strong> — the design tokens are the spec.</li>
|
|
834
|
+
<li><strong>Map sections to module specs</strong> — Each Figma section becomes one module spec: name (kebab-cased), description (inferred from name + content), content brief (text grouped by role with exact strings), and layout notes (layout mode, gap, padding, background color, children summary).</li>
|
|
835
|
+
<li><strong>Translate each module</strong> — For each spec, the AI is invoked once with a system prompt that explicitly forbids invention: "TRANSLATE this design to HubL. Use these exact texts as field defaults. Match colors, spacing, and layout from the design tokens." Modules are generated in parallel (concurrency limited via the same <code>agenticConcurrency</code> setting; default 20). Structured output via the Module Developer JSON schema ensures reliable parsing.</li>
|
|
836
|
+
<li><strong>Validate & auto-fix</strong> — The same quality-check stage as the agentic pipeline: balances HubL tags, normalizes reserved field names (<code>name</code> → <code>item_name</code>), upgrades deprecated field types, strips CDN imports.</li>
|
|
837
|
+
</ol>
|
|
838
|
+
<p>Progress streams to the chat panel as standard pipeline events: a "Mapped N modules" decision, then per-module "Generating…" / "Module complete" entries. After the run, the live preview shows the page with the Figma colors, copy, and section order intact.</p>
|
|
839
|
+
|
|
840
|
+
<h3 id="figma-tips">Tips & Limits</h3>
|
|
841
|
+
<ul>
|
|
842
|
+
<li><strong>Use top-level frames as sections.</strong> The importer treats top-level frames in your Figma file (or in the selected frame) as the section boundary. If your design has everything in one giant frame, only that one frame becomes a module. Break your design into logical sections (Hero, Features, Pricing, Footer, etc.) as separate top-level frames for best results.</li>
|
|
843
|
+
<li><strong>Name your frames meaningfully.</strong> Frame names become module names. A frame called <code>Section 1</code> turns into a module called <code>section-1</code>; rename it to <code>Hero</code> so you get <code>hero</code>.</li>
|
|
844
|
+
<li><strong>Auto-layout produces better results.</strong> Frames with auto-layout export their layout direction, gap, and padding cleanly. Free-form positioning is supported but the AI has to infer layout from coordinates, which is less reliable.</li>
|
|
845
|
+
<li><strong>Figma rate limits apply.</strong> The image-export endpoint has limits; very asset-heavy files (50+ images) may take a minute or two to extract. Errors with status <code>429</code> indicate rate-limiting — wait and retry.</li>
|
|
846
|
+
<li><strong>Permissions matter.</strong> The token must have access to the file. <code>403</code> errors mean the file isn't shared with the token's owner or doesn't have view permission.</li>
|
|
847
|
+
<li><strong>Effects support is limited.</strong> Box shadows and border radii are extracted; gradients, blur effects, blend modes, and complex masks are ignored (HubL preview wouldn't render them faithfully anyway).</li>
|
|
848
|
+
<li><strong>Re-importing replaces modules.</strong> Importing a Figma URL into an existing theme clears the active template's modules first — the import is treated as a full page replacement, not an additive merge.</li>
|
|
849
|
+
</ul>
|
|
850
|
+
|
|
851
|
+
<div class="doc-callout doc-callout--tip">
|
|
852
|
+
<div class="doc-callout__label">✨ Workflow tip</div>
|
|
853
|
+
<p>For the highest-fidelity output: use auto-layout in Figma, name your frames as kebab-friendly section names ("Hero", "Trust Bar", "Final CTA"), and extract real copy + final imagery into the design before importing. The result is a HubSpot theme that's ready to deploy with no manual cleanup — content editors get a fully-fielded theme they just need to plug content into.</p>
|
|
854
|
+
</div>
|
|
855
|
+
</div>
|
|
856
|
+
|
|
717
857
|
<!-- ============================================================
|
|
718
858
|
Section 6: Editing & Customization
|
|
719
859
|
============================================================ -->
|
|
@@ -973,11 +1113,24 @@ vibespot</code></pre>
|
|
|
973
1113
|
<li><strong>Engine selector</strong> — Seven buttons, one for each supported engine. The active engine is highlighted in orange. Unavailable engines are grayed out — API engines require a key, CLI engines must first be toggled on in the CLI Tools section below.</li>
|
|
974
1114
|
<li><strong>Model dropdown</strong> — A dynamic model catalog populated from the selected engine's available models. You can also type a custom model identifier for newly released models.</li>
|
|
975
1115
|
<li><strong>Agentic pipeline toggle</strong> — An iOS-style toggle switch that enables or disables the 4-stage pipeline. When off, vibeSpot falls back to single-call mode. A sub-label shows the current status (Active, Disabled, or Not configured). On first use, vibeSpot prompts you to choose.</li>
|
|
1116
|
+
<li><strong>AI Capabilities section</strong> — Surfaces SDK feature toggles per active engine:
|
|
1117
|
+
<ul>
|
|
1118
|
+
<li><em>Prompt Caching</em> — auto-active on Anthropic API, "Auto (CLI-managed)" on Claude Code, "Anthropic only" elsewhere. No knob; informational.</li>
|
|
1119
|
+
<li><em>Extended Thinking</em> — toggle on Anthropic API/OAuth with a Low / Medium / High budget select (~4k / 16k / 32k thinking tokens). Page Architect's two substages opt in when enabled. On Claude Code the badge reads "Auto (CLI-managed)" because the CLI handles thinking internally and doesn't expose a budget knob.</li>
|
|
1120
|
+
<li><em>Web Search</em> — real toggle. On Anthropic API/OAuth it appends the <code>web_search_20250305</code> server-side tool to non-structured-output calls (chat, plan-mode deliberation). On Claude Code it passes <code>--allowedTools=WebSearch</code> so the agent's tool allowlist permits it. Plan mode opts in automatically — research is highest-value during deliberation.</li>
|
|
1121
|
+
<li><em>Citations</em> — "Coming soon".</li>
|
|
1122
|
+
</ul>
|
|
1123
|
+
</li>
|
|
976
1124
|
<li><strong>API keys section</strong> — Input fields for Anthropic, OpenAI, and Google AI API keys. Keys are displayed masked (only last 4 characters visible) after saving.</li>
|
|
977
1125
|
<li><strong>Claude OAuth</strong> — A section to paste the OAuth token generated by <code>claude setup-token</code>. Shows the token status (valid/expired).</li>
|
|
978
1126
|
<li><strong>CLI tools</strong> — Toggle switches for Claude Code, Gemini CLI, and Codex CLI. CLI tools must be toggled <strong>on</strong> before they appear as selectable engines in the Engine section above. This lazy-detection approach means disabled tools add zero startup overhead. Once toggled on, vibeSpot checks if the CLI is installed and authenticated. If not installed, an install button appears. If installed but not authenticated, an auth button appears.</li>
|
|
979
1127
|
</ul>
|
|
980
1128
|
|
|
1129
|
+
<div class="doc-callout doc-callout--tip">
|
|
1130
|
+
<div class="doc-callout__label">Claude Code uses <code>stream-json</code></div>
|
|
1131
|
+
<p>vibeSpot invokes Claude Code with <code>--output-format stream-json --include-partial-messages --verbose</code>, parsing the line-delimited JSON event stream rather than raw text. This preserves the live token-typing UX <em>and</em> lets vibeSpot show concrete agent activity in the status pane — "Reading hero/module.html", "Searching: 'pricing best practices'", "Editing styleguide.md" — instead of generic "thinking…" placeholders. Tool calls (<code>Read</code>, <code>Edit</code>, <code>Bash</code>, <code>WebSearch</code>, <code>WebFetch</code>, <code>Grep</code>, <code>Glob</code>) are extracted from the event stream and rendered as live status lines.</p>
|
|
1132
|
+
</div>
|
|
1133
|
+
|
|
981
1134
|
<h3 id="settings-hubspot">HubSpot Tab</h3>
|
|
982
1135
|
<ul>
|
|
983
1136
|
<li><strong>Upload mode toggle</strong> — Switch between API (recommended) and CLI (legacy) upload modes.</li>
|
|
@@ -987,7 +1140,7 @@ vibespot</code></pre>
|
|
|
987
1140
|
</ul>
|
|
988
1141
|
|
|
989
1142
|
<h3 id="settings-github">GitHub Tab</h3>
|
|
990
|
-
<p>The GitHub tab configures authentication for source imports (used by the "
|
|
1143
|
+
<p>The GitHub tab configures authentication for source imports (used by the "From React" flow).</p>
|
|
991
1144
|
<ul>
|
|
992
1145
|
<li><strong>GitHub CLI authentication</strong> — Uses <code>gh auth</code> to authenticate with GitHub. Required for cloning private repositories during React-to-HubSpot conversion.</li>
|
|
993
1146
|
</ul>
|
package/ui/index.html
CHANGED
|
@@ -96,10 +96,15 @@
|
|
|
96
96
|
<span class="setup__action-icon">↓</span>
|
|
97
97
|
<span>From HubSpot</span>
|
|
98
98
|
</button>
|
|
99
|
+
<button class="setup__action-btn" data-action="figma">
|
|
100
|
+
<span class="setup__action-icon">◆</span>
|
|
101
|
+
<span>From Figma</span>
|
|
102
|
+
<span class="setup__action-badge">Beta</span>
|
|
103
|
+
</button>
|
|
99
104
|
<button class="setup__action-btn" data-action="convert">
|
|
100
105
|
<span class="setup__action-icon">↺</span>
|
|
101
|
-
<span>
|
|
102
|
-
<span class="setup__action-badge">
|
|
106
|
+
<span>From React</span>
|
|
107
|
+
<span class="setup__action-badge">Beta</span>
|
|
103
108
|
</button>
|
|
104
109
|
</div>
|
|
105
110
|
|
|
@@ -132,6 +137,36 @@
|
|
|
132
137
|
</div>
|
|
133
138
|
</div>
|
|
134
139
|
|
|
140
|
+
<div class="setup__panel hidden" id="panel-figma">
|
|
141
|
+
<div id="figma-token-prompt" class="hidden">
|
|
142
|
+
<p class="setup__hint">A Figma Personal Access Token is required. <a href="#" id="figma-open-settings">Add one in Settings</a> or enter it below:</p>
|
|
143
|
+
<div class="setup__row">
|
|
144
|
+
<input type="password" class="setup__input setup__input--wide" id="figma-inline-token" placeholder="figd_..." />
|
|
145
|
+
<button class="btn btn--primary" id="figma-save-token">Save</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
<div id="figma-url-section">
|
|
149
|
+
<div class="setup__row">
|
|
150
|
+
<input type="text" class="setup__input setup__input--wide" id="figma-url" placeholder="https://www.figma.com/design/..." />
|
|
151
|
+
<button class="btn btn--primary" id="figma-extract-btn">Extract</button>
|
|
152
|
+
</div>
|
|
153
|
+
<p class="setup__hint">Paste a Figma design URL to extract structure, text, and assets</p>
|
|
154
|
+
<div class="figma-progress hidden" id="figma-progress"></div>
|
|
155
|
+
</div>
|
|
156
|
+
<div id="figma-summary" class="hidden"></div>
|
|
157
|
+
<div id="figma-generate" class="hidden">
|
|
158
|
+
<div class="setup__row">
|
|
159
|
+
<input type="text" class="setup__input setup__input--wide" id="figma-theme-name" placeholder="my-landing-page" />
|
|
160
|
+
<button class="btn btn--primary" id="figma-generate-btn">Generate Page</button>
|
|
161
|
+
</div>
|
|
162
|
+
<label class="figma-toggle" id="figma-image-toggle">
|
|
163
|
+
<input type="checkbox" id="figma-use-assets" checked />
|
|
164
|
+
<span class="figma-toggle__label">Import images as assets</span>
|
|
165
|
+
<span class="figma-toggle__hint" id="figma-image-hint">Images uploaded to HubSpot, no manual replacement needed</span>
|
|
166
|
+
</label>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
135
170
|
<div class="setup__panel hidden" id="panel-convert">
|
|
136
171
|
<div class="setup__row">
|
|
137
172
|
<input type="text" class="setup__input setup__input--wide" id="import-url" placeholder="https://github.com/user/repo or Lovable URL" />
|
|
@@ -423,6 +458,15 @@
|
|
|
423
458
|
</div>
|
|
424
459
|
</div>
|
|
425
460
|
<div class="chat__input-area">
|
|
461
|
+
<div class="chat__mode-bar">
|
|
462
|
+
<button class="plan-toggle" id="plan-mode-toggle" title="Plan mode — deliberate before generating" aria-pressed="false">
|
|
463
|
+
<span class="plan-toggle__indicator">
|
|
464
|
+
<svg class="plan-toggle__icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/></svg>
|
|
465
|
+
</span>
|
|
466
|
+
<span class="plan-toggle__label">Plan mode</span>
|
|
467
|
+
<span class="plan-toggle__state">Off</span>
|
|
468
|
+
</button>
|
|
469
|
+
</div>
|
|
426
470
|
<div class="chat__file-chips" id="file-chips"></div>
|
|
427
471
|
<div class="chat__input-wrapper">
|
|
428
472
|
<textarea class="chat__input" id="chat-input" placeholder="Describe your landing page..." rows="1"></textarea>
|
|
@@ -430,7 +474,7 @@
|
|
|
430
474
|
<button class="chat__input-icon" id="btn-attach-file" title="Attach files (images, PDFs, docs)">
|
|
431
475
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>
|
|
432
476
|
</button>
|
|
433
|
-
<input type="file" id="file-input" multiple accept="image/png,image/jpeg,image/svg+xml,image/webp,image/gif,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/markdown,text/plain" style="display:none">
|
|
477
|
+
<input type="file" id="file-input" multiple accept="image/png,image/jpeg,image/svg+xml,image/webp,image/gif,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/markdown,text/plain,.md,.markdown,.txt" style="display:none">
|
|
434
478
|
<button class="chat__input-icon" id="btn-starter-templates" title="Templates">
|
|
435
479
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>
|
|
436
480
|
</button>
|
|
@@ -454,6 +498,7 @@
|
|
|
454
498
|
<main class="panel panel--right" id="panel-right">
|
|
455
499
|
<div class="view-toggle" id="view-toggle">
|
|
456
500
|
<button class="view-toggle__btn active" data-view="preview">Preview</button>
|
|
501
|
+
<button class="view-toggle__btn" data-view="plan" id="view-toggle-plan">Plan</button>
|
|
457
502
|
<button class="view-toggle__btn" data-view="code">Code</button>
|
|
458
503
|
</div>
|
|
459
504
|
|
|
@@ -471,6 +516,35 @@
|
|
|
471
516
|
</div>
|
|
472
517
|
</div>
|
|
473
518
|
|
|
519
|
+
<!-- Plan pane (deliberation phase) -->
|
|
520
|
+
<div class="plan-pane hidden" id="plan-pane">
|
|
521
|
+
<div class="plan-pane__scroll">
|
|
522
|
+
<div class="plan-pane__header">
|
|
523
|
+
<h2 class="plan-pane__title">Plan</h2>
|
|
524
|
+
<div class="plan-pane__header-actions">
|
|
525
|
+
<button class="plan-pane__icon-btn" id="plan-edit-toggle" title="Edit plan">
|
|
526
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
|
527
|
+
<span>Edit</span>
|
|
528
|
+
</button>
|
|
529
|
+
</div>
|
|
530
|
+
</div>
|
|
531
|
+
<div class="plan-pane__content" id="plan-content">
|
|
532
|
+
<div class="plan-pane__empty">
|
|
533
|
+
<p>No plan yet.</p>
|
|
534
|
+
<p class="plan-pane__empty-hint">Toggle plan mode in the chat (icon next to send) and describe what you want to build. The assistant will ask clarifying questions and draft a plan here.</p>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
<textarea class="plan-pane__editor hidden" id="plan-editor" spellcheck="false"></textarea>
|
|
538
|
+
</div>
|
|
539
|
+
<div class="plan-pane__footer">
|
|
540
|
+
<button class="btn btn--secondary plan-pane__discard" id="plan-discard-btn">Discard & start over</button>
|
|
541
|
+
<div class="plan-pane__footer-spacer"></div>
|
|
542
|
+
<button class="btn btn--secondary hidden" id="plan-edit-cancel">Cancel</button>
|
|
543
|
+
<button class="btn btn--primary hidden" id="plan-edit-save">Save</button>
|
|
544
|
+
<button class="btn btn--primary plan-pane__approve" id="plan-approve-btn">Approve plan</button>
|
|
545
|
+
</div>
|
|
546
|
+
</div>
|
|
547
|
+
|
|
474
548
|
<div class="code-view hidden" id="code-view">
|
|
475
549
|
<div class="code-view__sidebar">
|
|
476
550
|
<div class="code-view__sidebar-header">Files</div>
|
|
@@ -529,6 +603,7 @@
|
|
|
529
603
|
<div class="settings__tabs" id="settings-tabs">
|
|
530
604
|
<button class="settings__tab active" data-tab="ai">AI</button>
|
|
531
605
|
<button class="settings__tab" data-tab="hubspot">HubSpot</button>
|
|
606
|
+
<button class="settings__tab" data-tab="figma">Figma</button>
|
|
532
607
|
<button class="settings__tab" data-tab="github">GitHub</button>
|
|
533
608
|
<button class="settings__tab" data-tab="vibespot">vibeSpot</button>
|
|
534
609
|
</div>
|
|
@@ -552,6 +627,7 @@
|
|
|
552
627
|
<script src="/settings.js"></script>
|
|
553
628
|
<script src="/upload-panel.js"></script>
|
|
554
629
|
<script src="/chat.js"></script>
|
|
630
|
+
<script src="/plan.js"></script>
|
|
555
631
|
<script src="/preview.js"></script>
|
|
556
632
|
<script src="/field-editor.js"></script>
|
|
557
633
|
<script src="/code-editor.js"></script>
|
package/ui/plan.js
ADDED
|
Binary file
|