Commit Graph

119 Commits

Author SHA1 Message Date
Jeff Emmett 29332a5023 feat(rsocials): threadable drafts — vertical tweet chain + [+] in overlay
Post detail overlay now renders content as a tweet chain:
- Each tweet is a spine-connected row (dot + connecting line) with
  tweet number, per-platform char counter, and inline textarea in
  edit mode
- [+ Add tweet] button at the bottom appends a new empty tweet to
  the chain; × per tweet removes it (hidden when only one tweet)
- Save writes threadPosts[] when len > 1, else plain content (and
  clears threadPosts), so single drafts stay flat
- View mode shows tweets stacked vertically reading top-to-bottom,
  edit mode keeps the same layout with editable boxes
- Per-platform limits (x/twitter 280, bluesky 300, threads 500, li
  3000, ig 2200, yt 5000) drive live char counters; over-limit
  goes red bold
- Grid cards gain a 🧵 N badge when threadPosts.length > 1
- Fix FLIP animation replaying on every re-render (now guarded with
  an instance flag, plays once per open)

Bump folk-thread-gallery cache to v=5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:15:04 -04:00
Jeff Emmett 729570c48b feat(rsocials): progressive-zoom post detail overlay with inline edit
Clicking a post card expands it into a FLIP-animated detail overlay
instead of navigating away. Overlay has two modes:

View mode — full post content, schedule, hashtags, campaign, char
count. Actions: Open in Campaign (external link), Edit.

Edit mode — textarea for content, status picker (Draft/Scheduled/
Published), datetime-local for scheduled date, space-separated
hashtag input. Saves via offline runtime changeDoc so the post list
updates live everywhere else the doc is subscribed. Hashtag strings
are auto-prefixed with # if missing. ESC cancels edit; ESC again
(or backdrop click) closes the overlay. Backdrop blur + smooth
translate+scale entry from the clicked card's rect.

Threads still use the dedicated thread-editor since they have
multi-tweet rich editing. Card anchors become buttons with
data-post-id; cursor/focus-visible styling preserved. Cache bump
folk-thread-gallery to v=4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 12:24:33 -04:00
Jeff Emmett 076f4f170f feat(rsocials): drop sub-nav, center Drafts/Scheduled/Published toggles
Remove the top-right Campaigns / Campaign Canvas / Newsletter sub-nav
from /rsocials landing. Gallery filter chips become the primary toggle:
Drafts / Scheduled / Published only (no "All"), centered above the grid.
Default filter is drafts. Threads render alongside published posts.
Bump folk-thread-gallery cache to v=3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 11:46:07 -04:00
Jeff Emmett 91837fd1d4 feat(rsocials): landing shows posts gallery with draft/scheduled/published filter
/rsocials now lands on the content gallery instead of the nav hub.
Gallery surfaces all campaign posts (not just drafts/scheduled) and
adds filter chips for All / Drafts / Scheduled / Published with live
count badges. Last-selected filter persists in localStorage. Cards use
per-status left-border accent (amber/blue/green) and a published badge.

Top-right sub-nav keeps Campaigns / Campaign Canvas / Newsletter one
click away. Bump folk-thread-gallery cache to v=2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:16:47 -04:00
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 02889ce3ec merge(dev): campaign canvas pan/zoom fix
CI/CD / deploy (push) Successful in 3m48s Details
2026-04-17 14:37:35 -04:00
Jeff Emmett 04e38088b7 fix(rsocials): campaign canvas wheel = pan, Ctrl/pinch = zoom
Align /rsocials/campaign canvas with main rspace canvas behavior:
two-finger scroll now pans; Ctrl/Cmd+wheel or trackpad pinch
(ctrlKey) zooms at cursor. Previously any wheel event zoomed.
Sibling folk-campaign-workflow.ts already used this pattern.
Bump asset cache to v=4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 14:37:31 -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 06b84bf2d7 fix(rsocials): list-and-match fallback to recover postizPostId after create
Postiz's POST /public/v1/posts response shape doesn't reliably include the
new post's id (varies by version). When parsing fails, query listPosts in a
narrow window right after create and match by integration+content to recover
the real id. Without this, reconcile can't find queued posts back in Postiz.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:08:47 -04:00
Jeff Emmett 6a335e4dba fix(rsocials): include Postiz platform-specific settings defaults
Postiz's POST /public/v1/posts validates required per-platform fields on
settings (e.g. X needs who_can_reply_post). Added defaultSettingsFor()
covering x, linkedin, instagram, youtube.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:03:21 -04:00
Jeff Emmett b2fbe7ded4 fix(rsocials): postiz sweep skips nodes with any postizStatus set
Earlier version only skipped when postizPostId was set. If a send attempt
failed (no postizPostId but postizStatus='failed'), the sweep retried every
60s and hammered Postiz's throttler. Retries now require the user to clear
postizStatus — same gate as a fresh node.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:56:23 -04:00
Jeff Emmett 1cde95f3fc fix(rsocials): Postiz createPost/createThread use nested posts[] shape
Postiz's /public/v1/posts expects a nested envelope:
  { type, date, shortLink, tags, posts: [{ integration:{id}, value:[{content}], settings:{__type} }] }

The earlier shim's flat {content, integrationIds, type, scheduledAt} was
rejected with "All posts must have an integration id" (the field is named
integration, not integrationIds; it's a nested object, not an array of strings).

Also: Postiz treats PostItem.value[] as thread segments, so createThread now
sends ONE request with multiple value entries rather than N grouped posts.

Callers now pass full {id, identifier} tuples so settings.__type is set
correctly (otherwise Postiz can't route to the provider handler).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:49:18 -04:00
Jeff Emmett 878646a399 fix(rsocials): Postiz public API uses raw key + /api prefix, not Bearer
Postiz's Nest backend expects Authorization: <apiKey> (no "Bearer"
prefix) and the public API is reverse-proxied at
<hostname>/api/public/v1/*. Our client was hitting /public/v1/*
directly and using Bearer, which 401'd.

Tolerate both config.url shapes (with or without /api) so older
module settings that point at the root host keep working.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:40:17 -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 43d68fd521 fix(rsocials+spaces): merge Posts button; redact password settings from module GET
- rsocials: collapse dead "posts" outputPath into "threads" entry (💬 Posts)
  so the sub-nav shows one button linking to the thread gallery + builder
- spaces GET /:slug/modules: redact settingsSchema fields of type 'password'
  to '********' for non-owner callers; PATCH preserves existing password
  when the sentinel is sent back unchanged

Closes the leak where unauth'd GET returned Listmonk/Postiz credentials in plaintext.
2026-04-17 10:17:10 -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 fe3e3621af feat(canvas): rApplet Phase 2 — port chips, data flow, toolbar palette, 15 module applets
- Replace port-indicator dots with port-chip pills (colored border + dot + name)
- Add setPortValue override bridging FolkArrow piping to onInputReceived/emitOutput
- Add Applets toolbar group with dynamic palette + template section
- 15 new module applet files: rtasks, rtime, rcal, rchats, rdata, rdocs,
  rnotes, rphotos, rmaps, rnetwork, rchoices, rinbox, rsocials, rbooks, rexchange
- 20 total applet cards across 18 modules (was 5 across 3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 13:08:49 -04:00
Jeff Emmett 0f6b5ecd8d fix: disable all feature tours — shell welcome tour, TourEngine, landing links
Tours were demoing stale features and auto-triggering annoyingly.
TourEngine.start() now returns immediately (no-ops). Shell welcome
tour JS/CSS/HTML removed. "Start Guided Tour" links stripped from
all 27 landing pages. Tour CSS selectors removed from info panel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 11:12:00 -04:00
Jeff Emmett 53c757e68e fix: comprehensive memory leak and performance fixes across 44 files
Browser-side:
- Fix switchSpace() to LRU-evict idle space WebSocket connections (cap: 3)
- Add runtime.unsubscribe() to disconnectedCallback in 24 components
- Fix DocSyncManager.unsubscribe() to clean up syncStates, timers, listeners
- Fix 14 components leaking RAF loops, ResizeObservers, MutationObservers,
  document/window listeners, setIntervals, MapLibre WebGL contexts, and
  AbortControllers on disconnect
- Deduplicate Automerge WASM: module builds now use global shim from
  shell-offline instead of bundling ~2.5MB each (8 modules affected)

Server-side:
- Add LRU eviction to SyncServer.#docs (cap: 500, evicts idle docs with
  no subscribers, persists to disk before eviction)
- registerWatcher() now returns unsubscribe function

Data:
- Cap unbounded CRDT arrays: rexchange chatMessages (200), rcart events (200)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 22:26:24 -04:00
Jeff Emmett 2e8e702d75 feat(mcp): 101 MCP tools across all 35 rApps + security hardening + MI integration
- Add centralized auth helper (_auth.ts) with resolveAccess() enforcing
  space visibility (public/permissioned/private) and role-based access
- Retrofit 5 existing tool groups (rcal, rnotes, rtasks, rwallet, spaces)
  with resolveAccess gates
- Add 30 new MCP tool files covering all remaining rApps:
  rsocials, rnetwork, rinbox, rtime, rfiles, rschedule, rvote, rchoices,
  rtrips, rcart, rexchange, rbnb, rvnb, crowdsurf, rbooks, rpubs, rmeets,
  rtube, rswag, rdesign, rsplat, rphotos, rflows, rdocs, rdata, rforum,
  rchats, rmaps, rsheet, rgov
- Add ForMI data exports to all module mod.ts files
- Wire 6 core modules into MI context (mi-data-queries.ts, mi-routes.ts)
- forceAuth for sensitive modules (rinbox, rchats)
- Omit sensitive fields (storagePath, fileHash, bodyHtml) from responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 10:25:17 -04:00
Jeff Emmett 4a54e6af16 fix: enforce subdomain routing — spaces are subdomains, never path segments
Audit and fix ~60 violations across 47 files where URLs were constructed
as /{space}/moduleId instead of /moduleId (subdomain provides the space).

- Add getModuleApiBase() helper to shared/url-helpers.ts
- Fix client components: use getModuleApiBase() for fetch URLs,
  rspaceNavUrl() for navigation
- Fix server routes: use c.get("isSubdomain") for redirects and
  JSON response URLs
- Fix OAuth callbacks (notion, google, clickup): subdomain-aware redirects
- Fix email notification URLs (rvote): use {space}.rspace.online format
- Fix webhook registration (rtasks/clickup): subdomain-aware endpoint URL
- Replace broken #getSpaceSlug() methods in folk-feed, folk-splat
- Replace NODE_ENV checks with proper isSubdomain checks (rdata, rnetwork)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 14:09:53 -07:00
Jeff Emmett c8e4aede02 feat(canvas): module-aware canvas architecture — shapes, tools, and toolbar gated by module
Modules now declare their canvas shapes and AI tools (canvasShapes/canvasToolIds
on RSpaceModule), creating a single source of truth that the canvas enforces:

- Phase 1: Extended RSpaceModule + ModuleInfo, added moduleId to CanvasToolDefinition
  with getToolsForModules() filter, added moduleOf() to ShapeRegistry, populated
  declarations in 9 modules, fixed 8 ungated toolbar buttons (rchoices, rwallet, rsocials)
- Phase 2: AI prompt sends enabledModules, server filters Gemini tool declarations
- Phase 3: folk-commitment-pool and folk-task-request show lock overlay when rtime disabled
- Phase 4: Extracted MODULE_META into shared lib/module-display.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 15:19:12 -07:00
Jeff Emmett 2cbff8925d fix(layout): viewport-filling rApp layout — eliminate body scroll
Make #app a flex column with height:100vh so all rApps fill the viewport
exactly. Wrap module body in .rapp-content flex child. Replace all
hardcoded calc(100vh - Npx) with height:100% across 20+ components.
Remove sticky positioning from subnav/tabbar (now flex items).
Generalize mobile body-flex to all pages (not just canvas-layout).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 10:57:09 -07:00
Jeff Emmett d865da32a7 fix(rsocials): campaign wizard timeout + faster content generation
- Add 120s AbortController timeout to apiFetch so wizard can't hang
  forever on slow networks (shows "Request timed out" instead of stuck)
- Switch content generation from gemini-2.5-pro to gemini-2.5-flash
  to avoid Cloudflare/Traefik proxy timeouts (pro model took 30-60s+)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 22:48:57 -07:00
Jeff Emmett 9084de7adb feat(rsocials): wire social shapes into all cross-rApp systems
- Add GET /api/threads, /api/threads/:id, /api/campaigns, /api/campaigns/:id
  REST endpoints so other rApps can fetch rsocials data
- Register folk-social-thread/campaign/newsletter in MI triage panel,
  tool schema, system prompt, and KNOWN_TRIAGE_SHAPES
- Add rsocials MODULE_PORTS (threads-out, campaigns-out, post-published,
  campaign-data) and WIDGET_API to folk-rapp for embed/widget mode
- Add rsocials to folk-feed FEED_ENDPOINTS (threads, campaigns, newsletter)
  and normalize response arrays (threads, campaigns, drafts)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 16:25:26 -07:00
Jeff Emmett 25abb0266b feat(rsocials): newsletter drafts tab + remove Listmonk setup wall
Drafts tab shown by default with Automerge-backed CRUD. Listmonk tabs
conditionally appear when configured. Info banner for unconfigured state.
Draft editor with HTML preview, status dropdown, subscriber management.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 20:15:46 -07:00
Jeff Emmett 77ac0c1e32 feat(rsocials): newsletter drafts, subscribers + MI route updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 20:11:31 -07:00
Jeff Emmett 3add66b5ef chore(rsocials): add NewsletterDraft and NewsletterSubscriber types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 20:04:15 -07:00
Jeff Emmett f966f02909 feat(spaces,rsocials): invite-based member adds + clickable campaign content
Space invites: Convert both username-add and email-invite (existing user)
paths from direct-add to invite flow — creates space_invite via EncryptID,
sends invite email with Accept button, dispatches space_invite notification
with Accept/Decline buttons in the notification bell. No one gets forcefully
added to a space anymore.

rSocials content linking: All generated campaign content now links through
to actual editable content. Draft post cards in thread gallery are clickable
(thread editor or campaign view). Campaign manager post cards expand on click
and thread badges link to thread editor. Wizard success screen shows
individual thread links. CampaignPost schema gains threadId field, stored on
commit so posts maintain their thread association.

Also includes canvas social media tools, social shape components
(folk-social-thread, folk-social-campaign, folk-social-newsletter),
and MI context-aware suggestion registry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 20:03:52 -07:00
Jeff Emmett 4d7d2c0108 fix(rsocials): campaign wizard auth — read correct session storage key
Was reading `encryptid-token` (doesn't exist), now reads `encryptid_session`
and extracts `.accessToken` matching the pattern used by all other modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:01:10 -07:00
Jeff Emmett 06a58f1bba fix(ui): theme-adaptive rSocials buttons + device link textbox
- campaign-wizard.css: fix undefined --rs-surface-hover → --rs-bg-hover
- campaign-workflow.css: replace hardcoded blue colors with theme vars
  (--rs-primary-hover, --rs-bg-active, --rs-border, --rs-primary)
- rstack-identity.ts: device link URL input --rs-bg-inset → --rs-input-bg
  (--rs-bg-inset was never defined in theme.css, dark fallback always won)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:39:14 -07:00
Jeff Emmett 193588443e fix(routing): eliminate /{space} from paths on subdomain routing
On subdomain routing (e.g. demo.rspace.online), the space slug belongs
only in the subdomain — never in the URL path. This fixes all server
and client code that was generating /{space}/module paths on subdomains.

Server fixes:
- index.ts: notification actionUrls, template/disabled-module redirects,
  subdomain passthrough (now redirects HTML, rewrites API), WS notifications
- output-list.ts: subdomain-aware path prefix for hrefs and fetch URLs
- shell.ts: dashboard pushState URL

Client fixes (basePath getter pattern):
- folk-book-shelf.ts, folk-splat-viewer.ts: _basePath getter
- folk-campaign-wizard.ts: basePath subdomain check
- folk-trips-planner.ts: use __rspaceNavUrl for canvas export
- rschedule/landing.ts: onclick handlers use __rspaceNavUrl
- rsplat/mod.ts: legacy view redirect subdomain check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:45:16 -07: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 5f25ae02e1 fix(rsocials): use checkboxes for campaign wizard platform selectors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 14:22:49 -07:00
Jeff Emmett bf0661fab2 feat(cad): LLM-orchestrated MCP tool-calling for KiCad and FreeCAD
Add Gemini Flash agentic loop that converts natural language prompts
into real MCP tool call sequences for PCB design (KiCad) and parametric
CAD (FreeCAD). Dynamic schema conversion from MCP tools to Gemini
function declarations, 8-turn/60s loop with real execution and result
feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 14:07:19 -07:00
Jeff Emmett 90d511077c fix: campaign wizard using wrong localStorage key for auth token
Was reading `auth_token`, should be `encryptid-token`. Also removes
the premature client-side auth guard that blocked signed-in users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 13:04:57 -07:00
Jeff Emmett 7f327eb07a fix: whitelist rvote GET API as public + guard campaign wizard auth client-side
1. Add GET /rvote/api/* to public endpoint whitelist so proposal
   listings work on private/permissioned spaces without auth.
2. Campaign wizard now checks for auth token before POSTing,
   showing "Please sign in" instead of a cryptic 401.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 12:58:21 -07:00
Jeff Emmett 7618433498 refactor(auth): replace @encryptid/sdk imports with local auth module
Consolidates token verification into server/auth.ts, removing the
external SDK dependency. All modules now import from the local module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:41:59 -07:00
Jeff Emmett 524356d233 feat(tours+solo): add tours to remaining modules and solo mode toggle
Add guided tours to 6 modules that were missing them:
- Shadow DOM: rsocials dashboard, crowdsurf, rdata (TourEngine)
- Light DOM: rsplat, rbnb, rvnb (new LightTourEngine class)

Add solo mode toggle to collab overlay — click the presence badge
to hide your cursor/presence from others. Persists via localStorage,
dispatches event to canvas PresenceManager.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 17:26:57 -07:00
Jeff Emmett 7c29ccea41 feat(rinbox): bridge Listmonk newsletter campaigns with multisig approval
Newsletter campaigns now require N-of-M team member approval via rInbox
before Listmonk starts sending. The send-newsletter workflow node creates
a draft campaign and gates it through the team inbox; a manual submit-
approval route is also available. The multisig email UI shows a green
"Newsletter Approval" badge with campaign ID for newsletter approvals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:03:06 -07:00
Jeff Emmett 57e03f3049 feat(rsocials): add Campaign Wizard button to campaigns dashboard
Prominent teal gradient button in header and empty state of the
campaigns tab, linking to the AI-guided wizard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 14:49:28 -07:00
Jeff Emmett 5ad6c6ff77 feat(rsocials): add Campaign Wizard with 5-step AI-guided creation flow
Progressive approval workflow: paste brief → AI extracts structure →
AI generates per-platform posts → review with per-post regen →
commit (saves campaign, creates threads, drafts newsletters, builds workflow DAG).

Includes MI integration for Cmd+K campaign creation and vite build entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 14:41:45 -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 ac9bd302d1 fix(rsocials): replace hardcoded dark-mode colors with CSS variables for light/dark theme support
Buttons, badges, focus states, avatars, links, inputs, and surfaces across
all rSocials components now use --rs-primary, --rs-error, --rs-success,
--rs-accent, --rs-bg-surface, --rs-input-bg, etc. with dark-mode fallbacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 13:18:13 -07:00
Jeff Emmett df489d698c feat(rsocials): newsletter editor with Listmonk integration
Full campaign editor with HTML body textarea, live iframe preview,
list selector with subscriber counts, save draft, send now, and
schedule send. Added edit/delete actions on draft campaigns and
GET/PUT/DELETE single-campaign proxy routes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 15:53:35 -07:00