From 456d0de9c1c58a460a74fdfc7a1a49cf474d2221 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Thu, 12 Mar 2026 00:20:01 -0700 Subject: [PATCH] fix(rsocials): proper canvas sizing, SVG zoom icons, category-styled nodes - Fix height to account for shell header (92px not 60px) - Add min-height:0 on flex children to prevent overflow - Replace text zoom buttons with SVG icons matching rFlows pattern - Add fit-to-view icon (corner brackets) with separators - Add category class per node (trigger/delay/condition/action) with tinted backgrounds and category badge chips - Add keyboard shortcuts: F=fit, +/-=zoom Co-Authored-By: Claude Opus 4.6 --- .../rsocials/components/campaign-workflow.css | 78 ++++++++++++++++--- .../components/folk-campaign-workflow.ts | 35 +++++++-- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/modules/rsocials/components/campaign-workflow.css b/modules/rsocials/components/campaign-workflow.css index 09c4950..1c4e0b7 100644 --- a/modules/rsocials/components/campaign-workflow.css +++ b/modules/rsocials/components/campaign-workflow.css @@ -1,7 +1,7 @@ /* rSocials Campaign Workflow — n8n-style workflow builder */ folk-campaign-workflow { display: block; - height: calc(100vh - 60px); + height: calc(100vh - 92px); } .cw-root { @@ -22,6 +22,7 @@ folk-campaign-workflow { border-bottom: 1px solid var(--rs-border, #2d2d44); background: var(--rs-bg-surface, #1a1a2e); z-index: 10; + flex-shrink: 0; } .cw-toolbar__title { @@ -106,6 +107,7 @@ folk-campaign-workflow { display: flex; overflow: hidden; position: relative; + min-height: 0; } /* ── Left sidebar — node palette ── */ @@ -172,6 +174,7 @@ folk-campaign-workflow { overflow: hidden; cursor: grab; background: var(--rs-canvas-bg, #0f0f23); + min-width: 0; } .cw-canvas.grabbing { @@ -330,42 +333,57 @@ folk-campaign-workflow { /* ── Zoom controls ── */ .cw-zoom-controls { position: absolute; - bottom: 12px; - right: 12px; + bottom: 14px; + right: 14px; display: flex; align-items: center; - gap: 4px; + gap: 0; background: var(--rs-bg-surface, #1a1a2e); - border: 1px solid var(--rs-border-strong, #3d3d5c); + border: 1px solid var(--rs-border, #2d2d44); border-radius: 8px; - padding: 4px 6px; + box-shadow: 0 2px 8px rgba(0,0,0,0.12); + overflow: hidden; z-index: 5; } .cw-zoom-btn { - width: 28px; - height: 28px; + width: 32px; + height: 32px; border: none; - border-radius: 6px; + border-radius: 0; background: transparent; color: var(--rs-text-primary, #e2e8f0); - font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.15s; + padding: 0; } .cw-zoom-btn:hover { background: var(--rs-bg-surface-raised, #252545); } +.cw-zoom-btn svg { + width: 16px; + height: 16px; +} + +.cw-zoom-sep { + width: 1px; + height: 18px; + background: var(--rs-border, #2d2d44); + margin: 0 2px; +} + .cw-zoom-level { font-size: 11px; - color: var(--rs-text-muted, #94a3b8); - min-width: 36px; + font-weight: 600; + color: var(--rs-text-secondary, #94a3b8); + min-width: 40px; text-align: center; + user-select: none; } /* ── Node styles in SVG ── */ @@ -388,6 +406,24 @@ folk-campaign-workflow { border-color: #4f46e5 !important; } +/* Category-specific node backgrounds */ +.cw-node--trigger foreignObject > div { + border-color: #3b82f644; + background: linear-gradient(135deg, #1a1a2e 0%, #1e2a3e 100%); +} +.cw-node--delay foreignObject > div { + border-color: #a855f744; + background: linear-gradient(135deg, #1a1a2e 0%, #251e3e 100%); +} +.cw-node--condition foreignObject > div { + border-color: #f59e0b44; + background: linear-gradient(135deg, #1a1a2e 0%, #2e2a1e 100%); +} +.cw-node--action foreignObject > div { + border-color: #10b98144; + background: linear-gradient(135deg, #1a1a2e 0%, #1e2e24 100%); +} + .cw-node-header { display: flex; align-items: center; @@ -411,6 +447,20 @@ folk-campaign-workflow { text-overflow: ellipsis; } +.cw-node-cat { + font-size: 9px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + padding: 1px 5px; + border-radius: 3px; + flex-shrink: 0; +} +.cw-node-cat--trigger { color: #60a5fa; background: #3b82f622; } +.cw-node-cat--delay { color: #c084fc; background: #a855f722; } +.cw-node-cat--condition { color: #fbbf24; background: #f59e0b22; } +.cw-node-cat--action { color: #34d399; background: #10b98122; } + .cw-node-status { width: 8px; height: 8px; @@ -494,6 +544,10 @@ folk-campaign-workflow { /* ── Mobile ── */ @media (max-width: 768px) { + folk-campaign-workflow { + height: calc(100vh - 56px); + } + .cw-palette { width: 160px; min-width: 160px; diff --git a/modules/rsocials/components/folk-campaign-workflow.ts b/modules/rsocials/components/folk-campaign-workflow.ts index 614ba34..85da827 100644 --- a/modules/rsocials/components/folk-campaign-workflow.ts +++ b/modules/rsocials/components/folk-campaign-workflow.ts @@ -348,10 +348,19 @@ class FolkCampaignWorkflow extends HTMLElement {
- + + ${Math.round(this.canvasZoom * 100)}% - - + + + +
@@ -400,12 +409,13 @@ class FolkCampaignWorkflow extends HTMLElement { } return ` - +
${def.icon} ${esc(node.label)} + ${def.category}
@@ -960,13 +970,28 @@ class FolkCampaignWorkflow extends HTMLElement { } private handleKeyDown(e: KeyboardEvent) { + const tag = (e.target as Element)?.tagName; + const isEditing = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'; + if (e.key === 'Escape') { if (this.wiringActive) this.cancelWiring(); } if ((e.key === 'Delete' || e.key === 'Backspace') && this.selectedNodeId) { - if ((e.target as Element)?.tagName === 'INPUT' || (e.target as Element)?.tagName === 'TEXTAREA') return; + if (isEditing) return; this.deleteNode(this.selectedNodeId); } + if (isEditing) return; + if (e.key === 'f' || e.key === 'F') { + this.fitView(); + } + if (e.key === '=' || e.key === '+') { + const svg = this.shadow.getElementById('cw-svg'); + if (svg) { const r = svg.getBoundingClientRect(); this.zoomAt(r.width / 2, r.height / 2, 1.2); } + } + if (e.key === '-') { + const svg = this.shadow.getElementById('cw-svg'); + if (svg) { const r = svg.getBoundingClientRect(); this.zoomAt(r.width / 2, r.height / 2, 0.8); } + } } }