From a689445af5227692976e6868b2ab6a96093b3e70 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 9 Mar 2026 15:12:47 -0700 Subject: [PATCH] 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 --- lib/folk-blender.ts | 7 +- lib/folk-booking.ts | 3 +- lib/folk-budget.ts | 7 +- lib/folk-calendar.ts | 3 +- lib/folk-chat.ts | 14 ++-- lib/folk-choice-conviction.ts | 15 ++-- lib/folk-choice-rank.ts | 15 ++-- lib/folk-choice-spider.ts | 11 ++- lib/folk-choice-vote.ts | 15 ++-- lib/folk-destination.ts | 3 +- lib/folk-drawfast.ts | 3 +- lib/folk-embed.ts | 7 +- lib/folk-freecad.ts | 3 +- lib/folk-google-item.ts | 3 +- lib/folk-image-gen.ts | 9 ++- lib/folk-image-studio.ts | 3 +- lib/folk-itinerary.ts | 7 +- lib/folk-kicad.ts | 3 +- lib/folk-map.ts | 3 +- lib/folk-markdown.ts | 3 +- lib/folk-obs-note.ts | 3 +- lib/folk-packing-list.ts | 3 +- lib/folk-prompt.ts | 22 +++--- lib/folk-slide.ts | 4 +- lib/folk-social-post.ts | 3 +- lib/folk-token-ledger.ts | 3 +- lib/folk-token-mint.ts | 3 +- lib/folk-transcription.ts | 3 +- lib/folk-video-gen.ts | 7 +- lib/folk-workflow-block.ts | 3 +- lib/folk-wrapper.ts | 12 +-- lib/folk-zine-gen.ts | 53 ++++++++------ modules/rcal/components/folk-calendar-view.ts | 73 ++++++++++++++++++- modules/rflows/components/flows.css | 2 +- .../components/folk-campaign-manager.ts | 2 +- .../components/folk-campaign-planner.ts | 2 +- .../components/folk-thread-builder.ts | 2 +- .../components/folk-thread-gallery.ts | 69 +++++++++--------- modules/rsocials/mod.ts | 4 +- 39 files changed, 276 insertions(+), 134 deletions(-) diff --git a/lib/folk-blender.ts b/lib/folk-blender.ts index ad95fd9..a89f9ba 100644 --- a/lib/folk-blender.ts +++ b/lib/folk-blender.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 380px; @@ -63,12 +64,14 @@ const styles = css` .prompt-input { width: 100%; padding: 10px 12px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 8px; font-size: 13px; resize: none; outline: none; font-family: inherit; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .prompt-input:focus { diff --git a/lib/folk-booking.ts b/lib/folk-booking.ts index 8d03289..1cd7ab2 100644 --- a/lib/folk-booking.ts +++ b/lib/folk-booking.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 280px; diff --git a/lib/folk-budget.ts b/lib/folk-budget.ts index 1e599db..3a804e5 100644 --- a/lib/folk-budget.ts +++ b/lib/folk-budget.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 280px; @@ -147,10 +148,12 @@ const styles = css` .add-form input { flex: 1; padding: 6px 8px; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 4px; font-size: 12px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .add-form input:focus { diff --git a/lib/folk-calendar.ts b/lib/folk-calendar.ts index ef8febd..81e9da7 100644 --- a/lib/folk-calendar.ts +++ b/lib/folk-calendar.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 280px; diff --git a/lib/folk-chat.ts b/lib/folk-chat.ts index 1481624..5767aef 100644 --- a/lib/folk-chat.ts +++ b/lib/folk-chat.ts @@ -4,7 +4,8 @@ import { SpeechDictation } from "./speech-dictation"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 300px; @@ -65,7 +66,8 @@ const styles = css` } .message { - background: #f1f5f9; + background: var(--rs-bg-surface-sunken, #f1f5f9); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; padding: 8px 12px; max-width: 85%; @@ -94,16 +96,18 @@ const styles = css` display: flex; gap: 8px; padding: 12px; - border-top: 1px solid #e2e8f0; + border-top: 1px solid var(--rs-border, #e2e8f0); } .message-input { flex: 1; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 8px 12px; font-size: 14px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .message-input:focus { @@ -126,7 +130,7 @@ const styles = css` .mic-btn { background: transparent; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-border, #e2e8f0); border-radius: 6px; padding: 8px 10px; cursor: pointer; diff --git a/lib/folk-choice-conviction.ts b/lib/folk-choice-conviction.ts index f1d2e24..702a258 100644 --- a/lib/folk-choice-conviction.ts +++ b/lib/folk-choice-conviction.ts @@ -6,7 +6,8 @@ const USER_NAME_KEY = "folk-choice-username"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 340px; @@ -197,11 +198,13 @@ const styles = css` .add-input { flex: 1; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .add-input:focus { border-color: #d97706; } @@ -231,11 +234,13 @@ const styles = css` } .username-input { - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 8px 12px; font-size: 13px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); width: 100%; box-sizing: border-box; margin-bottom: 8px; @@ -255,7 +260,7 @@ const styles = css` .wrapper { position: relative; height: 100%; } .results-drawer { position: absolute; top: 0; left: 100%; width: 300px; height: 100%; - background: white; border-radius: 0 8px 8px 0; + background: var(--rs-bg-surface, #fff); border-radius: 0 8px 8px 0; box-shadow: 4px 0 12px rgba(0,0,0,0.08); overflow-y: auto; display: none; flex-direction: column; font-size: 12px; z-index: 10; @@ -283,7 +288,7 @@ const styles = css` .settings-open .body { display: none !important; } .settings-open .results-drawer { display: none !important; } .settings-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #94a3b8; margin-bottom: 6px; } - .settings-input { width: 100%; box-sizing: border-box; border: 1px solid #e2e8f0; border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; } + .settings-input { width: 100%; box-sizing: border-box; border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; background: var(--rs-input-bg, #fff); color: var(--rs-input-text, inherit); } .settings-input:focus { border-color: #d97706; } .settings-item { display: flex; align-items: center; gap: 6px; padding: 4px 0; } .settings-item input[type="text"] { flex: 1; border: 1px solid #e2e8f0; border-radius: 4px; padding: 4px 6px; font-size: 11px; outline: none; min-width: 0; } diff --git a/lib/folk-choice-rank.ts b/lib/folk-choice-rank.ts index b050a3b..33287e9 100644 --- a/lib/folk-choice-rank.ts +++ b/lib/folk-choice-rank.ts @@ -6,7 +6,8 @@ const USER_NAME_KEY = "folk-choice-username"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 340px; @@ -228,11 +229,13 @@ const styles = css` .add-input { flex: 1; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .add-input:focus { border-color: #4f46e5; } @@ -262,11 +265,13 @@ const styles = css` } .username-input { - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 8px 12px; font-size: 13px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); width: 100%; box-sizing: border-box; margin-bottom: 8px; @@ -293,7 +298,7 @@ const styles = css` .wrapper { position: relative; height: 100%; } .results-drawer { position: absolute; top: 0; left: 100%; width: 300px; height: 100%; - background: white; border-radius: 0 8px 8px 0; + background: var(--rs-bg-surface, #fff); border-radius: 0 8px 8px 0; box-shadow: 4px 0 12px rgba(0,0,0,0.08); overflow-y: auto; display: none; flex-direction: column; font-size: 12px; z-index: 10; @@ -321,7 +326,7 @@ const styles = css` .settings-open .body { display: none !important; } .settings-open .results-drawer { display: none !important; } .settings-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #94a3b8; margin-bottom: 6px; } - .settings-input { width: 100%; box-sizing: border-box; border: 1px solid #e2e8f0; border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; } + .settings-input { width: 100%; box-sizing: border-box; border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; background: var(--rs-input-bg, #fff); color: var(--rs-input-text, inherit); } .settings-input:focus { border-color: #4f46e5; } .settings-item { display: flex; align-items: center; gap: 6px; padding: 4px 0; } .settings-item input[type="text"] { flex: 1; border: 1px solid #e2e8f0; border-radius: 4px; padding: 4px 6px; font-size: 11px; outline: none; min-width: 0; } diff --git a/lib/folk-choice-spider.ts b/lib/folk-choice-spider.ts index 05d988c..eac2fef 100644 --- a/lib/folk-choice-spider.ts +++ b/lib/folk-choice-spider.ts @@ -6,7 +6,8 @@ const USER_NAME_KEY = "folk-choice-username"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 400px; @@ -231,11 +232,13 @@ const styles = css` } .username-input { - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 8px 12px; font-size: 13px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); width: 100%; box-sizing: border-box; margin-bottom: 8px; @@ -262,7 +265,7 @@ const styles = css` .wrapper { position: relative; height: 100%; } .results-drawer { position: absolute; top: 0; left: 100%; width: 300px; height: 100%; - background: white; border-radius: 0 8px 8px 0; + background: var(--rs-bg-surface, #fff); border-radius: 0 8px 8px 0; box-shadow: 4px 0 12px rgba(0,0,0,0.08); overflow-y: auto; display: none; flex-direction: column; font-size: 12px; z-index: 10; @@ -290,7 +293,7 @@ const styles = css` .settings-open .body { display: none !important; } .settings-open .results-drawer { display: none !important; } .settings-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #94a3b8; margin-bottom: 6px; } - .settings-input { width: 100%; box-sizing: border-box; border: 1px solid #e2e8f0; border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; } + .settings-input { width: 100%; box-sizing: border-box; border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; background: var(--rs-input-bg, #fff); color: var(--rs-input-text, inherit); } .settings-input:focus { border-color: #059669; } .settings-item { display: flex; align-items: center; gap: 6px; padding: 4px 0; } .settings-item input[type="text"] { flex: 1; border: 1px solid #e2e8f0; border-radius: 4px; padding: 4px 6px; font-size: 11px; outline: none; min-width: 0; } diff --git a/lib/folk-choice-vote.ts b/lib/folk-choice-vote.ts index 9c34843..0e40c1a 100644 --- a/lib/folk-choice-vote.ts +++ b/lib/folk-choice-vote.ts @@ -6,7 +6,8 @@ const USER_NAME_KEY = "folk-choice-username"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 320px; @@ -213,11 +214,13 @@ const styles = css` .add-input { flex: 1; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .add-input:focus { border-color: #0d9488; } @@ -247,11 +250,13 @@ const styles = css` } .username-input { - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 8px 12px; font-size: 13px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); width: 100%; box-sizing: border-box; margin-bottom: 8px; @@ -271,7 +276,7 @@ const styles = css` .wrapper { position: relative; height: 100%; } .results-drawer { position: absolute; top: 0; left: 100%; width: 300px; height: 100%; - background: white; border-radius: 0 8px 8px 0; + background: var(--rs-bg-surface, #fff); border-radius: 0 8px 8px 0; box-shadow: 4px 0 12px rgba(0,0,0,0.08); overflow-y: auto; display: none; flex-direction: column; font-size: 12px; z-index: 10; @@ -299,7 +304,7 @@ const styles = css` .settings-open .body { display: none !important; } .settings-open .results-drawer { display: none !important; } .settings-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #94a3b8; margin-bottom: 6px; } - .settings-input { width: 100%; box-sizing: border-box; border: 1px solid #e2e8f0; border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; } + .settings-input { width: 100%; box-sizing: border-box; border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; padding: 6px 10px; font-size: 12px; outline: none; background: var(--rs-input-bg, #fff); color: var(--rs-input-text, inherit); } .settings-input:focus { border-color: #0d9488; } .settings-item { display: flex; align-items: center; gap: 6px; padding: 4px 0; } .settings-item input[type="text"] { flex: 1; border: 1px solid #e2e8f0; border-radius: 4px; padding: 4px 6px; font-size: 11px; outline: none; min-width: 0; } diff --git a/lib/folk-destination.ts b/lib/folk-destination.ts index 0e72123..a104ebc 100644 --- a/lib/folk-destination.ts +++ b/lib/folk-destination.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 260px; diff --git a/lib/folk-drawfast.ts b/lib/folk-drawfast.ts index b8dbcec..7258ffe 100644 --- a/lib/folk-drawfast.ts +++ b/lib/folk-drawfast.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 360px; diff --git a/lib/folk-embed.ts b/lib/folk-embed.ts index 1f8cf65..bc2fb4e 100644 --- a/lib/folk-embed.ts +++ b/lib/folk-embed.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 300px; @@ -74,10 +75,12 @@ const styles = css` width: 100%; max-width: 400px; padding: 12px 16px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 8px; font-size: 14px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .url-input:focus { diff --git a/lib/folk-freecad.ts b/lib/folk-freecad.ts index 740a692..4554108 100644 --- a/lib/folk-freecad.ts +++ b/lib/folk-freecad.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 360px; diff --git a/lib/folk-google-item.ts b/lib/folk-google-item.ts index 9c1d5d8..239e4a2 100644 --- a/lib/folk-google-item.ts +++ b/lib/folk-google-item.ts @@ -13,7 +13,8 @@ const SERVICE_ICONS: Record = { const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 180px; diff --git a/lib/folk-image-gen.ts b/lib/folk-image-gen.ts index 90939ed..1ae43e8 100644 --- a/lib/folk-image-gen.ts +++ b/lib/folk-image-gen.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 320px; @@ -57,18 +58,20 @@ const styles = css` .prompt-area { padding: 12px; - border-bottom: 1px solid #e2e8f0; + border-bottom: 1px solid var(--rs-border, #e2e8f0); } .prompt-input { width: 100%; padding: 10px 12px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 8px; font-size: 13px; resize: none; outline: none; font-family: inherit; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .prompt-input:focus { diff --git a/lib/folk-image-studio.ts b/lib/folk-image-studio.ts index 18d7185..671356f 100644 --- a/lib/folk-image-studio.ts +++ b/lib/folk-image-studio.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 420px; diff --git a/lib/folk-itinerary.ts b/lib/folk-itinerary.ts index 6dc385e..107d6d9 100644 --- a/lib/folk-itinerary.ts +++ b/lib/folk-itinerary.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 300px; @@ -127,10 +128,12 @@ const styles = css` .add-form input { width: 100%; padding: 6px 8px; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-input-border, #e2e8f0); border-radius: 4px; font-size: 12px; outline: none; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .add-form input:focus { diff --git a/lib/folk-kicad.ts b/lib/folk-kicad.ts index eeb7ecd..517578e 100644 --- a/lib/folk-kicad.ts +++ b/lib/folk-kicad.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 380px; diff --git a/lib/folk-map.ts b/lib/folk-map.ts index 6f18743..9d11b06 100644 --- a/lib/folk-map.ts +++ b/lib/folk-map.ts @@ -26,7 +26,8 @@ const DEFAULT_STYLE = { const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 400px; diff --git a/lib/folk-markdown.ts b/lib/folk-markdown.ts index fe963f8..5671363 100644 --- a/lib/folk-markdown.ts +++ b/lib/folk-markdown.ts @@ -4,7 +4,8 @@ import { SpeechDictation } from "./speech-dictation"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 80px; diff --git a/lib/folk-obs-note.ts b/lib/folk-obs-note.ts index a9b2e5a..d3a102b 100644 --- a/lib/folk-obs-note.ts +++ b/lib/folk-obs-note.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 350px; diff --git a/lib/folk-packing-list.ts b/lib/folk-packing-list.ts index c1264bc..2413873 100644 --- a/lib/folk-packing-list.ts +++ b/lib/folk-packing-list.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 260px; diff --git a/lib/folk-prompt.ts b/lib/folk-prompt.ts index 797d5a8..c9922f7 100644 --- a/lib/folk-prompt.ts +++ b/lib/folk-prompt.ts @@ -4,7 +4,8 @@ import { SpeechDictation } from "./speech-dictation"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 400px; @@ -82,13 +83,13 @@ const styles = css` .message.assistant { align-self: flex-start; - background: #f1f5f9; - color: #1e293b; + background: var(--rs-bg-surface-sunken, #f1f5f9); + color: var(--rs-text-primary, #1e293b); border-bottom-left-radius: 4px; } .message.streaming { - background: #f1f5f9; + background: var(--rs-bg-surface-sunken, #f1f5f9); } .message.streaming::after { @@ -107,7 +108,7 @@ const styles = css` flex-direction: column; align-items: center; justify-content: center; - color: #94a3b8; + color: var(--rs-text-muted, #94a3b8); text-align: center; gap: 8px; padding: 24px; @@ -120,7 +121,7 @@ const styles = css` .input-area { padding: 12px; - border-top: 1px solid #e2e8f0; + border-top: 1px solid var(--rs-border, #e2e8f0); display: flex; flex-direction: column; gap: 8px; @@ -128,10 +129,11 @@ const styles = css` .model-select { padding: 6px 10px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; font-size: 12px; - background: white; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); cursor: pointer; width: fit-content; } @@ -144,12 +146,14 @@ const styles = css` .prompt-input { flex: 1; padding: 10px 14px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 8px; font-size: 13px; resize: none; outline: none; font-family: inherit; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .prompt-input:focus { diff --git a/lib/folk-slide.ts b/lib/folk-slide.ts index ff3d235..2043200 100644 --- a/lib/folk-slide.ts +++ b/lib/folk-slide.ts @@ -26,8 +26,8 @@ const styles = css` font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; font-weight: 600; - color: #64748b; - background: white; + color: var(--rs-text-secondary, #64748b); + background: var(--rs-bg-surface, #fff); padding: 2px 8px; border-radius: 4px; } diff --git a/lib/folk-social-post.ts b/lib/folk-social-post.ts index 9f11ae6..d380b97 100644 --- a/lib/folk-social-post.ts +++ b/lib/folk-social-post.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); min-width: 280px; diff --git a/lib/folk-token-ledger.ts b/lib/folk-token-ledger.ts index ef62638..284bf49 100644 --- a/lib/folk-token-ledger.ts +++ b/lib/folk-token-ledger.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 320px; diff --git a/lib/folk-token-mint.ts b/lib/folk-token-mint.ts index f9b6b6c..c12823f 100644 --- a/lib/folk-token-mint.ts +++ b/lib/folk-token-mint.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 280px; diff --git a/lib/folk-transcription.ts b/lib/folk-transcription.ts index b47dbec..3d752bd 100644 --- a/lib/folk-transcription.ts +++ b/lib/folk-transcription.ts @@ -4,7 +4,8 @@ import { SpeechDictation } from "./speech-dictation"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 350px; diff --git a/lib/folk-video-gen.ts b/lib/folk-video-gen.ts index fdfef1c..7befdbb 100644 --- a/lib/folk-video-gen.ts +++ b/lib/folk-video-gen.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 400px; @@ -124,9 +125,11 @@ const styles = css` .prompt-input { width: 100%; padding: 10px 12px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 8px; font-size: 13px; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); resize: none; outline: none; font-family: inherit; diff --git a/lib/folk-workflow-block.ts b/lib/folk-workflow-block.ts index ff1a7fc..6dca731 100644 --- a/lib/folk-workflow-block.ts +++ b/lib/folk-workflow-block.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); min-width: 200px; diff --git a/lib/folk-wrapper.ts b/lib/folk-wrapper.ts index 3edaa36..bf41780 100644 --- a/lib/folk-wrapper.ts +++ b/lib/folk-wrapper.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); min-width: 80px; @@ -109,13 +110,13 @@ const styles = css` .tags { padding: 8px 12px; - border-top: 1px solid #e0e0e0; + border-top: 1px solid var(--rs-border, #e0e0e0); display: flex; flex-wrap: wrap; gap: 4px; align-items: center; min-height: 32px; - background: #f8f9fa; + background: var(--rs-bg-surface-sunken, #f8f9fa); flex-shrink: 0; } @@ -168,14 +169,15 @@ const styles = css` } .tag-input { - border: 1px solid #9ca3af; + border: 1px solid var(--rs-input-border, #9ca3af); border-radius: 12px; padding: 2px 6px; font-size: 10px; outline: none; min-width: 60px; flex: 1; - background: white; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } `; diff --git a/lib/folk-zine-gen.ts b/lib/folk-zine-gen.ts index 5c433d7..27acb18 100644 --- a/lib/folk-zine-gen.ts +++ b/lib/folk-zine-gen.ts @@ -3,7 +3,8 @@ import { css, html } from "./tags"; const styles = css` :host { - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, #1e293b); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 480px; @@ -66,18 +67,20 @@ const styles = css` .ideation h3 { margin: 0; font-size: 14px; - color: #1e293b; + color: var(--rs-text-primary, #1e293b); } .topic-input { width: 100%; padding: 10px 12px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 8px; font-size: 13px; resize: none; outline: none; font-family: inherit; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .topic-input:focus { @@ -91,10 +94,11 @@ const styles = css` select { padding: 6px 10px; - border: 2px solid #e2e8f0; + border: 2px solid var(--rs-input-border, #e2e8f0); border-radius: 6px; font-size: 12px; - background: white; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); cursor: pointer; flex: 1; } @@ -127,21 +131,22 @@ const styles = css` align-items: center; justify-content: space-between; padding: 8px 12px; - border-bottom: 1px solid #e2e8f0; + border-bottom: 1px solid var(--rs-border, #e2e8f0); font-size: 12px; - color: #64748b; + color: var(--rs-text-secondary, #64748b); } .page-nav button { padding: 4px 10px; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-border, #e2e8f0); border-radius: 4px; - background: white; + background: var(--rs-bg-surface, #fff); + color: var(--rs-text-primary, inherit); cursor: pointer; font-size: 12px; } - .page-nav button:hover { background: #f1f5f9; } + .page-nav button:hover { background: var(--rs-bg-hover, #f1f5f9); } .page-nav button:disabled { opacity: 0.3; cursor: not-allowed; } .page-dots { @@ -178,7 +183,7 @@ const styles = css` .section { position: relative; margin-bottom: 12px; - border: 1px solid #e2e8f0; + border: 1px solid var(--rs-border, #e2e8f0); border-radius: 8px; overflow: hidden; transition: border-color 0.2s; @@ -193,10 +198,10 @@ const styles = css` align-items: center; justify-content: space-between; padding: 4px 8px; - background: #f8fafc; - border-bottom: 1px solid #e2e8f0; + background: var(--rs-bg-surface-sunken, #f8fafc); + border-bottom: 1px solid var(--rs-border, #e2e8f0); font-size: 10px; - color: #94a3b8; + color: var(--rs-text-muted, #94a3b8); text-transform: uppercase; letter-spacing: 0.5px; } @@ -213,12 +218,12 @@ const styles = css` cursor: pointer; font-size: 11px; border-radius: 3px; - color: #64748b; + color: var(--rs-text-secondary, #64748b); } .section-actions button:hover { - background: #e2e8f0; - color: #1e293b; + background: var(--rs-bg-hover, #e2e8f0); + color: var(--rs-text-primary, #1e293b); } .section-body { @@ -232,7 +237,7 @@ const styles = css` font-family: inherit; font-size: 13px; line-height: 1.5; - color: #1e293b; + color: var(--rs-text-primary, #1e293b); background: transparent; resize: none; min-height: 24px; @@ -247,14 +252,14 @@ const styles = css` .section-text.subhead { font-size: 14px; font-weight: 500; - color: #475569; + color: var(--rs-text-secondary, #475569); } .section-text.pullquote { font-style: italic; border-left: 3px solid #f59e0b; padding-left: 10px; - color: #64748b; + color: var(--rs-text-secondary, #64748b); } .section-image { @@ -268,9 +273,9 @@ const styles = css` align-items: center; justify-content: center; height: 120px; - background: #f1f5f9; + background: var(--rs-bg-surface-sunken, #f1f5f9); border-radius: 4px; - color: #94a3b8; + color: var(--rs-text-muted, #94a3b8); font-size: 12px; } @@ -286,11 +291,13 @@ const styles = css` .feedback-input { flex: 1; padding: 6px 8px; - border: 1px solid #fde68a; + border: 1px solid var(--rs-input-border, #fde68a); border-radius: 4px; font-size: 11px; outline: none; font-family: inherit; + background: var(--rs-input-bg, #fff); + color: var(--rs-input-text, inherit); } .feedback-input:focus { border-color: #f59e0b; } diff --git a/modules/rcal/components/folk-calendar-view.ts b/modules/rcal/components/folk-calendar-view.ts index fb52192..b9960b0 100644 --- a/modules/rcal/components/folk-calendar-view.ts +++ b/modules/rcal/components/folk-calendar-view.ts @@ -139,6 +139,14 @@ class FolkCalendarView extends HTMLElement { private _transitionActive = false; private viewVariant = 0; + // Gesture state (pan/swipe + pinch-to-zoom) + private _pointerCache: PointerEvent[] = []; + private _panStartX: number | null = null; + private _panStartY: number | null = null; + private _initialPinchDist: number | null = null; + private _gestureMode: 'none' | 'pan' | 'pinch' = 'none'; + private _gestureFired = false; + // Leaflet map (preserved across re-renders) private leafletMap: any = null; private mapContainer: HTMLDivElement | null = null; @@ -168,6 +176,10 @@ class FolkCalendarView extends HTMLElement { this.boundKeyHandler = null; } if (this._wheelTimer) { clearTimeout(this._wheelTimer); this._wheelTimer = null; } + this._pointerCache = []; + this._panStartX = this._panStartY = this._initialPinchDist = null; + this._gestureMode = 'none'; + this._gestureFired = false; if (this.leafletMap) { this.leafletMap.remove(); this.leafletMap = null; @@ -1736,6 +1748,65 @@ class FolkCalendarView extends HTMLElement { this._wheelTimer = null; }, 120); }, { passive: false }); + + // Pan/swipe + pinch-to-zoom on calendar pane + calPane.addEventListener("pointerdown", (e: PointerEvent) => { + const mapPanel = this.shadow.getElementById("map-panel"); + if (mapPanel && mapPanel.contains(e.target as Node)) return; + this._pointerCache.push(e); + if (this._pointerCache.length === 1) { + this._panStartX = e.clientX; + this._panStartY = e.clientY; + this._gestureMode = 'none'; + this._gestureFired = false; + } else if (this._pointerCache.length === 2) { + this._gestureMode = 'pinch'; + this._gestureFired = false; + const dx = this._pointerCache[0].clientX - this._pointerCache[1].clientX; + const dy = this._pointerCache[0].clientY - this._pointerCache[1].clientY; + this._initialPinchDist = Math.hypot(dx, dy); + } + }); + + calPane.addEventListener("pointermove", (e: PointerEvent) => { + // Update pointer in cache + const idx = this._pointerCache.findIndex(p => p.pointerId === e.pointerId); + if (idx < 0) return; + this._pointerCache[idx] = e; + + if (this._gestureMode === 'pinch' && this._pointerCache.length === 2 && this._initialPinchDist && !this._gestureFired) { + const dx = this._pointerCache[0].clientX - this._pointerCache[1].clientX; + const dy = this._pointerCache[0].clientY - this._pointerCache[1].clientY; + const dist = Math.hypot(dx, dy); + const ratio = dist / this._initialPinchDist; + if (ratio > 1.3) { this._gestureFired = true; this.zoomIn(); } + else if (ratio < 0.7) { this._gestureFired = true; this.zoomOut(); } + return; + } + + // Single-pointer pan + if (this._pointerCache.length === 1 && this._panStartX !== null && this._panStartY !== null && !this._gestureFired) { + const dx = e.clientX - this._panStartX; + const dy = e.clientY - this._panStartY; + // Only trigger if horizontal movement dominates + if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy) * 1.5) { + this._gestureFired = true; + this._gestureMode = 'pan'; + this.navigate(dx < 0 ? 1 : -1); + } + } + }); + + const pointerEnd = (e: PointerEvent) => { + this._pointerCache = this._pointerCache.filter(p => p.pointerId !== e.pointerId); + if (this._pointerCache.length === 0) { + this._panStartX = this._panStartY = this._initialPinchDist = null; + this._gestureMode = 'none'; + this._gestureFired = false; + } + }; + calPane.addEventListener("pointerup", pointerEnd); + calPane.addEventListener("pointercancel", pointerEnd); } } @@ -1906,7 +1977,7 @@ class FolkCalendarView extends HTMLElement { /* ── Main Layout ── */ .main-layout { position: relative; min-height: 400px; } .main-layout--docked { display: grid; grid-template-columns: 1fr 400px; gap: 8px; min-height: 500px; } - .calendar-pane { overflow: auto; min-width: 0; } + .calendar-pane { overflow: auto; min-width: 0; touch-action: pan-y; user-select: none; } /* ── Map Panel ── */ .map-panel { background: var(--rs-bg-surface-sunken); border: 1px solid var(--rs-border-strong); border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; } diff --git a/modules/rflows/components/flows.css b/modules/rflows/components/flows.css index 6847511..a6b3bc0 100644 --- a/modules/rflows/components/flows.css +++ b/modules/rflows/components/flows.css @@ -283,7 +283,7 @@ /* HTML card nodes (foreignObject) */ .node-card { - background: white; border-radius: 12px; overflow: hidden; + background: var(--rs-bg-surface, #fff); color: var(--rs-text-primary, #1e293b); border-radius: 12px; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; height: 100%; box-sizing: border-box; } diff --git a/modules/rsocials/components/folk-campaign-manager.ts b/modules/rsocials/components/folk-campaign-manager.ts index 041b147..8b9c0f6 100644 --- a/modules/rsocials/components/folk-campaign-manager.ts +++ b/modules/rsocials/components/folk-campaign-manager.ts @@ -131,7 +131,7 @@ export class FolkCampaignManager extends HTMLElement {
- Open Thread Builder + Open Thread Editor
${phaseHTML} diff --git a/modules/rsocials/components/folk-campaign-planner.ts b/modules/rsocials/components/folk-campaign-planner.ts index 3812248..34787b9 100644 --- a/modules/rsocials/components/folk-campaign-planner.ts +++ b/modules/rsocials/components/folk-campaign-planner.ts @@ -738,7 +738,7 @@ class FolkCampaignPlanner extends HTMLElement { - + `; break; } diff --git a/modules/rsocials/components/folk-thread-builder.ts b/modules/rsocials/components/folk-thread-builder.ts index 4e1ec03..89f1386 100644 --- a/modules/rsocials/components/folk-thread-builder.ts +++ b/modules/rsocials/components/folk-thread-builder.ts @@ -307,7 +307,7 @@ export class FolkThreadBuilder extends HTMLElement {