Commit Graph

19 Commits

Author SHA1 Message Date
Jeff Emmett e34eec649f feat(canvas): unified rApp canvas interaction — keyboard + space-to-grab
Phase A.5: bring rApp mini-canvases to parity with the main rSpace canvas
on keyboard shortcuts and Space-to-grab behavior, plus ship reusable
primitives for fit-view and zoom chrome.

shared/canvas-interaction.ts — extend CanvasInteractionController:
  • enableSpaceToGrab — Space sets grab cursor and flips isSpaceHeld()
  • enableKeyboardShortcuts — 0 fit, +/- zoom, arrows pan, Ctrl+Z/Y
    undo/redo, Delete/Backspace delete (with shadow-DOM-aware text
    input detection so inline editors still work)
  • zoomByFactor() helper for chrome +/- buttons

shared/canvas-viewport.ts (new):
  • fitViewToRects / fitViewToNodes — standard fit algorithm matching
    the main canvas (40px padding, 0.1–1.5 clamp default)
  • persistViewport / restoreViewport — localStorage under
    rspace_viewport:<key> with finite-value validation

shared/components/rspace-canvas-chrome.ts (new):
  • <rspace-canvas-chrome> web component — zoom out/in/fit buttons +
    percent indicator + optional grid toggle. Emits canvas-zoom-*
    events. Available as drop-in for new/future canvases.

Migrate 8 rApp canvases to opt into space-to-grab + keyboard fit:
rsocials planner (keeps existing isEnabled gate), rsocials workflow,
rminders automation, rflows, rgov circuit, rnetwork CRM graph,
applet-circuit-canvas. rTime skips enableSpaceToGrab because it has
its own Space tracking integrated with node-drag state.

Bump asset cache versions so browsers pick up the new JS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 18:18:49 -04:00
Jeff Emmett cd8878d925 feat(rsocials): board view + planner UX fixes
- Fix inline post editor: pointerdown + wheel on SVG now bail when
  target is inside .cp-inline-config so textarea/select focus and
  native scrolling work. Grow post panel to 300×460 with 380px body
  so the Done/Delete toolbar isn't clipped.
- Restyle channel palette chips as vertical tldraw-style cards (big
  tinted icon, centered label, constraint tags). 2-column grid.
- Add Board view (Drafts / Scheduled / Published) as a 4th switcher
  option. Each column toggleable (min one must stay on). Drag a post
  card between columns: Drafts→Scheduled opens a date/time picker
  modal, Scheduled→Drafts reverts status (blocked if already queued
  on Postiz). Published is terminal (no drops). Card shows platform
  badge, 3-line preview, char bar + limit, scheduled/published
  timestamp, Postiz error badge, release link, Edit button that
  jumps back into the canvas editor.

Bump planner assets: css?v=5, js?v=6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 17:53:28 -04:00
Jeff Emmett d3e65a0522 feat(canvas): shared CanvasInteractionController for rApp mini-canvases
CI/CD / deploy (push) Successful in 3m38s Details
Extract the rSpace main canvas wheel + touch interaction model into a
single shared controller (shared/canvas-interaction.ts). Wheel defaults
to pan, Ctrl/Cmd+wheel or trackpad pinch (ctrlKey) zooms at cursor,
two-finger touch pans + pinch-zooms at gesture center.

Migrate 8 rApp mini-canvases to use it, replacing 8 slightly-different
hand-rolled wheel handlers:
- rsocials campaign-planner (the bug report — was zoom-only)
- rsocials campaign-workflow
- rminders automation canvas (was zoom-only)
- rtime timebank weave
- rflows canvas
- rgov circuit
- rnetwork CRM graph (was zoom-only)
- lib/applet-circuit-canvas (was zoom-only)

Fixes the "wheel = zoom" regression everywhere and gives each rApp
canvas two-finger touch pan + pinch for free. Pointer-based pan and
marquee selection remain per-rApp because they depend on per-rApp
hit-testing. Bump asset cache versions for all affected routes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:07:32 -04:00
Jeff Emmett dc87eeb91f feat(rsocials): two-way planner↔Postiz sync with drift detection
Outbound: forward per-node platformSettings + title through
createPost/createThread via new PostizIntegrationRef.settings/title
merged on top of defaultSettingsFor. Record postizSyncedHash on the
node after each successful send.

Inbound: postizReconcile pulls remote content/publishDate onto queued
and draft nodes when the local hash matches postizSyncedHash, so
Postiz-side edits surface in the planner without clobbering pending
local edits. Published posts always take remote content as final.

Drift→Resync: inline editor shows a yellow "Local edits not yet on
Postiz" row when client SHA-1 (normalized content+title+schedule+tags+
platformSettings) drifts from postizSyncedHash. New
/resync-postiz route deletes the Postiz-side post (via new deletePost
helper, 404-tolerant) then re-creates with current content. Rejected
once published.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 14:36:56 -04:00
Jeff Emmett 98f554888c feat(rsocials): per-platform post fields, media uploads, auth on campaigns dashboard
Extend campaign planner with platform-aware post composition: optional
title (YouTube/TikTok/Reddit/Pinterest), mediaUrls attachments, and
per-platform settings overrides merged into Postiz __type. New
platform-specs.ts library encodes platform constraints. Bump asset
cache version to v=3.

Campaigns dashboard now sends Authorization headers on flow fetch/create
and surfaces 401/403 with a clear message instead of silently failing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 14:16:12 -04:00
Jeff Emmett 5c32119946 feat(rsocials): auto-push scheduled posts to Postiz + reconcile published state
Per-post "Schedule to Postiz" button on Post nodes (rSpace → Postiz), a 60s
server sweep that auto-pushes Scheduled posts within a 10min lead window,
and a Postiz → rSpace reconcile poll that flips postizStatus to
'published' (or 'failed') and records publishedAt + releaseURL.

- schemas: PostNodeData gains postizPostId, postizIntegrationId,
  postizStatus, postizError, postizSentAt, postizCheckedAt,
  postizReleaseURL, publishedAt.
- postiz-client: listPosts(startDate, endDate) for reconciliation.
- mod.ts: sendCampaignNodeToPostiz() helper, POST /api/campaign/flows/
  :flowId/nodes/:nodeId/send-postiz, postizSweep() + postizReconcile()
  wired into onInit via startPostizScheduler.
- folk-campaign-planner: button + status badge in Post inspector,
  timeline/table dot colors reflect postizStatus (queued=purple,
  published=green, failed=red). Timeline bucket stays by scheduledAt.
- campaign-planner.css: postiz state pill styles.
- Bump campaign-planner JS/CSS to ?v=2 to bust CF cache.

Mock-Postiz smoke against the client lib passes 20/20 assertions.
Live Postiz round-trip pending deploy to Netcup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:28:26 -04:00
Jeff Emmett 68648608a9 test(rsocials): Playwright smoke suite + planner reliability fixes
Adds e2e/tests/rsocials-campaign-flow.spec.ts — 13 tests covering the
unified campaign flow UX: dashboard → planner navigation, brief canvas
node (+ preview banner), markdown import modal, wizard handoff, and
API shape. 36 passed / 3 AI-skipped across chromium/firefox/mobile.

Bug fixes uncovered by the suite:
- markDownstreamStale only redraws when a node actually flips stale,
  so typing in an input node no longer destroys the open inline-edit
  overlay.
- executeSave wraps the local-first write in try/catch and nulls the
  client on failure, so a half-initialised client (WS down, IDB
  unavailable) falls through to localStorage instead of throwing
  "Document not open".
- init-failure path also nulls the client so the first save after a
  failed subscribe doesn't hit a doc that was never opened.

Test infra:
- server/security.ts + server/index.ts honour DISABLE_RATE_LIMIT=1
  (and NODE_ENV=test) to bypass HTTP rate limiter and anon WS-per-IP
  cap so the suite can run under 8 parallel workers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 16:59:59 -04:00
Jeff Emmett 5e5fcd7681 feat(rsocials): campaign planner polish
Expanded folk-campaign-planner with richer live state, styled dashboard
refresh, and schema + mod updates to match the new planner flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 16:13:20 -04:00
Jeff Emmett d49b4ea2dc feat(rsocials): graph-based campaign creation with brief-to-canvas pipeline
Add campaign planner as primary creation tool. Brief inputs in collapsible
sidebar generate editable node graph via Gemini 2.5 Flash. New input node
types (goal, message, tone) with feeds edges to downstream post/thread nodes.
Stale tracking propagates when inputs change; batch regen + single-node AI fill.

- schemas: goal/message/tone node types, feeds edge, stale tracking, schema v8
- mod: 3 API endpoints (from-brief, regen-nodes, ai-fill-node) + /campaign-flow page
- planner: ports, wiring, rendering, inline config, brief sidebar, context menu
- css: brief panel, platform chips, stale badges, feeds edge styles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 08:45:22 -04:00
Jeff Emmett df8631360e feat(collab): unified presence system across all 27 rApps
Harmonize the two disconnected presence systems into one:
- New shared/collab-presence.ts utility (broadcastPresence, startPresenceHeartbeat)
- Collab overlay now listens to custom presence messages, shows module context in people panel
- Fixed Shadow DOM focus tracking using composedPath() for focus rings through shadow boundaries
- Replaced rNotes custom presence with shared utility (kept sidebar dots)
- Added presence heartbeat to all 27 rApp components with dynamic context strings
- Bumped cache versions in all modified mod.ts files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:56:06 -07:00
Jeff Emmett aca0e6b353 feat(rsocials): connect drafted threads to campaign flows via picker dropdowns
Replace manual thread ID entry with select dropdowns in both campaign planner
and workflow components. Server-side publish-thread handler now resolves
linked threadId from Automerge doc when inline content is empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 14:13:31 -07:00
Jeff Emmett 72100c0922 feat: migrate hardcoded colors to --rs-* CSS variables across 18 modules
Replace structural UI colors with theme-aware CSS custom properties so
all rApp modules respond correctly to light/dark theme switching.

Covers: folk-social-post, folk-forum-dashboard, folk-video-player,
folk-video-chat, folk-thread-gallery, folk-campaign-manager,
folk-wallet-viewer, folk-vote-dashboard, folk-swag-designer,
folk-cart-shop, folk-workflow-block, folk-choices-dashboard,
folk-pubs-editor, folk-book-shelf, folk-flows-app, folk-analytics-view,
folk-campaign-planner, and flows.css canvas background.

Intentionally preserved: platform brand colors, chain colors,
data-viz/chart colors, video player black, call action buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 18:34:13 -07:00
Jeff Emmett e1bdc98b98 feat(rsocials): multi-view campaign planner (timeline, platform, table)
Add three alternative views to the campaign planner canvas:
- Timeline: horizontal chronological layout with day columns and phase bars
- Platform: kanban columns grouped by platform with post cards
- Table: compact sortable table with status, platform, content, dates

View switcher in toolbar preserves canvas state when switching. Clicking
any post in alt views navigates back to canvas with that node selected
and centered. Keyboard shortcuts guarded to canvas-only view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:21:59 -07:00
Jeff Emmett 932151aa88 fix(rsocials): campaign planner loads with fit-to-view on initial visit
Previously the planner restored a stale zoomed-out viewport from
localStorage, and fitView() could fail silently if the SVG had zero
dimensions during shadow DOM layout. Now: skip viewport restore on
initial load, retry fitView up to 3 rAFs, and clamp min zoom to 50%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:58:27 -07:00
Jeff Emmett a5c7bb784e fix(rsocials): subdomain-aware link generation
Links on subdomain routing (e.g. jeff.rspace.online) were including
the space in the path (/demo/rsocials/campaigns) instead of just
/rsocials/campaigns. Added basePath getter to all components and
detect subdomain in the server-rendered hub page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:31:45 -07:00
Jeff Emmett a689445af5 feat(rcal): add swipe/pan navigation and pinch-to-zoom gestures
Add pointer-based gesture handling to the calendar for lateral navigation
(drag/swipe left/right) and pinch-to-zoom between temporal layers. Also
fix dark mode across 30+ canvas shape components by replacing hardcoded
white backgrounds and input styles with --rs-* CSS custom property
references (with light-mode fallbacks).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:12:47 -07:00
Jeff Emmett c73635dcd3 fix(rsocials): remove redundant /demo space prefix from internal URLs
Subdomain routing (demo.rspace.online) already prepends the space,
so links should use /rsocials/... not /demo/rsocials/...

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:38:57 -07:00
Jeff Emmett 6173e17bb9 feat(rsocials): hub navigation page, route renames, and campaigns listing
Restructure rSocials landing to a hub with navigation cards for Campaigns,
Threads, and Thread Editor. Rename /thread routes to /thread-editor for
clarity. Render campaigns listing inline instead of redirecting. Also
improve rNotes notebook creation to auto-open first note for editing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:31:23 -07:00
Jeff Emmett ec83f5c9c1 feat: replace rSocials canvas with n8n-style campaign planner
Add flow-based campaign planner with draggable Post/Thread/Platform/
Audience/Phase nodes, port wiring, 3 edge types (publish/sequence/
target), inline config panels, context menu, keyboard shortcuts,
touch gestures, and Automerge local-first persistence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:18:30 -08:00