Commit Graph

192 Commits

Author SHA1 Message Date
Jeff Emmett 1d8fc2b23b feat: default selector tool with marquee multi-select, space+drag pan
Replace single-click-to-pan with selector as default tool. Left-click-drag
on canvas background draws a blue marquee rectangle to select multiple shapes.
Shift/Ctrl+click toggles additive selection. Panning now via Space+drag,
middle-click, or wheel/trackpad (unchanged). Delete/Backspace removes all
selected shapes. folk-shape highlighted state shows blue selection outline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:35:31 -08:00
Jeff Emmett 3faf44865e chore: update backlog — TASK-39 done, TASK-65 AC checked, TASK-69 created+done
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:30:01 -08:00
Jeff Emmett 645f1fc015 feat: SVG drawing persistence, click-to-edit markdown, quick-add rApps, dblclick pencil
- SVG whiteboard drawings now persist via Automerge (addShapeData for
  DOM-less shapes, wb-svg type in newShapeElement, eraser deletes from doc)
- folk-markdown: click preview to edit, edit-enter/edit-exit events sync
  with folk-shape editing state, refactored into enter/exitMarkdownEdit
- Desktop quick-add (+) button opens rApps popout panel directly
- Double-click empty canvas background activates pencil draw mode
- Canvas background click exits editing mode on all shapes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:25:44 -08:00
Jeff Emmett 9f3c9abf5b fix: canonicalize space URLs to subdomain pattern
rspace.online/{space}/{moduleId} now 301-redirects to
{space}.rspace.online/{moduleId} — spaces are subdomains,
not path segments. e.g. rspace.online/demo/rnotes →
demo.rspace.online/rnotes.

Landing pages (rspace.online/{moduleId}) unchanged.
API sub-path rewrite for demo "Try Demo" unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:20:36 -08:00
Jeff Emmett eee9cbed69 fix: shape overlap push-aside, coordinate persistence, toolbar panel clipping
- Collision: shapes now slide-off in movement direction by minimum
  penetration depth instead of flipping to the opposite side
- Coordinates: use nullish coalescing (??) so x=0/y=0 are preserved
  on reload instead of being replaced by falsy-check defaults
- Toolbar: remove overflow:hidden from #toolbar-panel so submenus
  render fully visible instead of being clipped/scrolled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:20:06 -08:00
Jeff Emmett 09d23f8fc1 fix: rApp shapes auto-derive space context and load as applets directly
- folk-rapp now auto-derives spaceSlug from the current URL path
  (/{space}/canvas → space) so embedded rApps always know their space
- Fixed race condition where createRenderRoot overwrote moduleId
  that was already set via JS property setter (showed picker instead
  of loading the module directly)
- newShapeElement always passes communitySlug as fallback spaceSlug
  when restoring folk-rapp shapes from sync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:12:55 -08:00
Jeff Emmett 0c00a69d8a feat: flow colors green=economic, purple=delegation, blue=data
Align flow kind colors to intuitive scheme:
- Economic: green (#4ade80)
- Governance → Delegation: purple (#a78bfa)
- Data: blue (#60a5fa)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:10:40 -08:00
Jeff Emmett d850a7615e feat: MI canvas bridge, action executor, and enhanced context
- Add MiCanvasBridge for deep canvas state awareness (shapes, selection, viewport)
- Add MiActionExecutor to create/update/delete/move/connect shapes from MI responses
- Add MI action parsing (create-shape, connect, update-shape, delete-shape, move-shape, transform)
- Add selection transforms (align, distribute, arrange, match-size)
- Add tool suggestion schema for contextual MI hints
- Enhanced MI system prompt with action markers and transform commands
- Richer canvas context in /api/mi/ask (positions, connections, viewport, shape groups)
- Refactored tab-bar I/O chips for cleaner feed port rendering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:08:20 -08:00
Jeff Emmett 32ee5a5ed0 feat: restructure all 22 landing pages with standardized template
- (You)r* becomes the h1 heading, old titles preserved as subtitles
- Standardized section order: Features → How It Works → Built on Open Source → Your Data Protected → CTA
- Each module lists its open source dependencies (Typst, Immich, MapLibre, Discourse, etc.)
- New "Your Data, Protected" section with E2E encryption + zero-knowledge (coming soon)
- Added .rl-subtitle CSS class for subtitle styling
- Module-specific sections preserved between How It Works and Built on Open Source

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:05:26 -08:00
Jeff Emmett bc5c9e608e feat: (You)r* hero branding, MI bar in landing headers, Try Demo repositioned
- Add (You)rModuleName tagline to all 22 module hero subtexts
- Move Try Demo button next to rApp dropdown in landing page header
- Add <rstack-mi> bar to landing page header center
- Create rNetwork landing page (was missing)
- Wire rNetwork landingPage into mod.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:45:57 -08:00
Jeff Emmett ed675d286d feat: embed rich landing pages for all 21 rSpace modules
Replace broken proxy-based landing pages with inline HTML for every
module. Each gets a landing.ts exporting renderLanding() with hero,
how-it-works, features, and CTA sections using shared .rl-* CSS.

12 ported from standalone repos (rPubs, rWork, rVote, rCal, rCart,
rTrips, rNotes, rMaps, rForum, rInbox, rSocials, rWallet) and 9 new
(rBooks, rChoices, rData, rFiles, rFunds, rPhotos, rSplat, rSwag, rTube).

Infrastructure: RICH_LANDING_CSS in shell.ts, landingPage field on
RSpaceModule, routing checks inline content before proxy fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:36:48 -08:00
Jeff Emmett 286f08fadb feat: space access request flow with notifications
Add "Request Access" flow for inaccessible spaces: authenticated users
see all spaces in the dropdown (categorized as Your/Public/Discover),
can request access to restricted spaces, and space owners get in-app
notification badges with inline approve/deny actions.

- API: GET /api/spaces returns accessible/relationship/pendingRequest fields
- API: POST/PATCH /api/spaces/:slug/access-requests + GET /notifications
- Space switcher: 3-section layout with Discover section + Request Access modal
- Identity: notification polling (30s), red badge on avatar, approve/deny in dropdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:45:15 -08:00
Jeff Emmett 9e4648be62 feat: feed-aware flow wiring + CSS 3D interactive layer view
Replace SVG stack view with CSS 3D perspective scene featuring
glassmorphism layer planes, animated flow particles, orbit controls,
and a time scrubber. Flow creation dialog now filters kind buttons
by source/target feed compatibility with count badges. Contained
feeds (no outgoing flow) shown with lock icons on layer planes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:36:51 -08:00
Jeff Emmett 683df43b95 fix: move toolbar collapse toggle to bottom as subtle pill
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:36:27 -08:00
Jeff Emmett f8bd09dbac feat: vertical toolbar, whiteboard tools, zoom dropdown, context-aware MI bar
- Convert canvas toolbar from horizontal (top center) to vertical (left side)
  with dropdowns opening to the right
- Add whiteboard "Draw" toolbar group: pencil, sticky note, rectangle, circle,
  line, eraser — renders SVG strokes on canvas overlay
- Nest zoom controls (in/out/reset) under a "Zoom" dropdown group
- Enhance rstack-mi to gather page context (open shapes, active tab, page title)
  and send to /api/mi/ask for context-aware responses
- Move FeedDefinition to lib/layer-types.ts, add feeds/acceptsFeeds to modules
- Extend rstack-tab-bar with feed compatibility helpers and 3D scene state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:29:24 -08:00
Jeff Emmett b4dc1f6f08 fix: replace unicode escapes in folk-splat html template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:15:58 -08:00
Jeff Emmett 303433fa49 fix: make shape content interactive + render emojis properly
- Scope pointer-events:none to .slot-container instead of all divs,
  so buttons/textareas/inputs inside shapes are clickable again
- Replace unicode escapes (\u{xxxx}) in html`` tagged templates with
  actual emoji characters — String.raw doesn't process escapes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:15:31 -08:00
Jeff Emmett 726ef43952 feat: categorized rApp dropdown in tab bar + button
- Tab bar + button now shows full rApp dropdown with names, icons, descriptions
- Grouped by category (Creating, Planning, Communicating, etc.)
- Only shows modules not already open as tabs
- Shell passes module list to tab bar via setModules()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:11:33 -08:00
Jeff Emmett 15e6a9b9ba fix: remove scrollbar arrows from shapes + add collision slide-off
- Change inner div overflow from scroll to hidden, removing browser
  scrollbar arrows that appeared on every canvas shape
- Add shape collision detection: shapes now slide off each other with
  an 8px gap instead of overlapping when dragged (pointer + touch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:07:10 -08:00
Jeff Emmett 0555b5fa7f feat: add rSocials module + redirect standalone domains to rspace.online landing pages
- New rSocials module (federated social feed aggregator) with demo timeline
- Standalone domain root (r*.online/) now 302 redirects to rspace.online/{moduleId}
- Self-fetch detection breaks circular proxy loop (User-Agent: rSpace-Proxy/1.0)
- Traefik label for rsocials.online

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:05:03 -08:00
Jeff Emmett 3f7c649b54 fix: remove distracting outlines and resize handles from canvas shapes
Handles now only appear during active move/resize, not on hover or focus.
Removes blue outline borders that cluttered the canvas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:03:51 -08:00
Jeff Emmett 3b6ea5afcd feat: creative tools suite — 7 tools in unified canvas toolbar
- Delete rProviders module (unused)
- Add hidden flag to module system, hide rSplat from app switcher
- Add fal.ai API proxies: image-gen (Flux Pro), video-gen t2v (WAN 2.1), i2v (Kling)
- New canvas shapes: folk-splat (3D viewer), folk-blender (3D gen), folk-drawfast (freehand drawing), folk-freecad (parametric CAD), folk-kicad (PCB design)
- Restructure canvas toolbar: new "Creative" group with all 7 tools, reduced "Media" group
- Add blender-gen, kicad, freecad REST-to-MCP bridge endpoints
- Fix standalone domain navigation to rspace.online landing pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:57:50 -08:00
Jeff Emmett 25643060e0 feat: proxy rich r*.online landing pages to rspace.online/{moduleId}
Fetches pre-rendered HTML from standalone domains at request time,
transforms with HTMLRewriter (strip scripts, rewrite asset URLs,
inject rSpace shell header), caches 10min with stale-on-error fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:31:24 -08:00
Jeff Emmett a2f0752fed feat: add user profile and encrypted address API endpoints
Server-side support for user profile management and zero-knowledge
postal address storage:

Schema:
- ALTER users table: add bio, avatar_url, profile_email,
  profile_email_is_recovery, wallet_address, updated_at columns
- CREATE encrypted_addresses table with composite PK (id, user_id),
  label CHECK constraint, and cleartext metadata for UI listing

DB layer:
- getUserProfile, updateUserProfile (dynamic column updates)
- getUserAddresses, getAddressById, saveUserAddress (upsert),
  deleteUserAddress
- Default-address logic: unsets all others when isDefault=true

API routes:
- GET/PUT /api/user/profile — bio validation (500 chars), email format
- GET/POST /api/user/addresses — max 10 addresses, label validation
- PUT/DELETE /api/user/addresses/:id — 404 if not found

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:13:32 -08:00
Jeff Emmett 92037610db feat: landing pages for rspace.online/{moduleId} with Try Demo CTA
Exact module paths (rspace.online/rtube) now show a generated landing
page with icon, name, description, and CTAs instead of immediately
loading the demo app. Sub-paths still rewrite to /demo/... as before.
Removes the iframe embed branch which caused CORS issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:49:59 -08:00
Jeff Emmett 763f897c11 fix: rspace.online/r* now renders same shell as r*.online
Instead of serving a special iframe landing page, bare-domain module
paths now rewrite to /demo/{moduleId} — identical to standalone domains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:37:05 -08:00
Jeff Emmett 5d19ad169c fix: unregister stale service workers + sandbox iframe on landing pages
Stale service workers from the old fetch-proxy approach were intercepting
cross-origin iframe requests, causing CORS and replaceState errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:02:07 -08:00
Jeff Emmett bd0916b60f security: remove hardcoded secrets, require env vars
Remove hardcoded encryption fallback and Postgres password defaults
flagged by GitGuardian. ENCRYPTION_SECRET and DATABASE_URL are now
required env vars that throw on missing rather than falling back to
insecure defaults.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:45:10 -08:00
Jeff Emmett bfdb09fc4b fix: iframe embed for standalone rApps, all links use rspace.online/r*
Replaced the fetch-and-proxy approach (which caused CORS errors for
cross-origin assets, fonts, and service workers) with full-page iframe
embedding. Modules with standalone domains are embedded in an iframe
below the rSpace header. Modules without get a simple generated landing
page. All "Try Demo" and app switcher links now consistently use
rspace.online/{moduleId} instead of demo.rspace.online.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:47:15 -08:00
Jeff Emmett f84b0b9914 refactor: remove rProviders from rApps, fix rSwag domain to rswag.online
rProviders (providers.mycofi.earth) is a separate project — removed
module registration, app switcher entry, tab bar badge, canvas embed
button, Traefik router, and standalone config. rSwag domain updated
from swag.mycofi.earth to rswag.online across all references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:39:43 -08:00
Jeff Emmett 19ce7080e8 feat: add missing standalone domain routing for all rApps
Added standaloneDomain to rsplat module (rsplat.online). Added 8 missing
Traefik routers: rnotes, rfiles, rphotos, rinbox, rcart, rsplat,
swag.mycofi.earth, providers.mycofi.earth. All 22 standalone-domain
modules now have matching Traefik routing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:34:25 -08:00
Jeff Emmett 138716660b feat: proxy standalone domain landing pages for rspace.online/{moduleId}
Instead of the generated landing page, rspace.online/rnotes now fetches
and serves the real page from rnotes.online (with <base> tag for asset
resolution). 5-minute in-memory cache avoids repeated fetches. Falls
back to the generated landing page for modules without a standalone
domain or when the fetch fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:14:07 -08:00
Jeff Emmett 2cfe2c744d feat: app switcher links to landing pages, demo uses standalone rApp builds
App switcher on demo/bare domain now links to rspace.online/{moduleId}
landing pages instead of jumping straight to demo. "Try Demo" buttons
use standalone domain builds (rnotes.online, rvote.online, etc.) which
have better styling and more updated features. Falls back to
demo.rspace.online for modules without a standalone domain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:09:10 -08:00
Jeff Emmett 7698e32774 feat: per-module landing pages on bare domain rspace.online/{moduleId}
Bare domain now serves a dedicated landing page for each rApp instead
of directly loading the demo. "Try Demo" links to demo.rspace.online
which loads the live app. Sub-paths still rewrite to demo for API compat.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:02:39 -08:00
Jeff Emmett 8b9ccff256 fix: use bare domain rspace.online/r* for rApp links instead of personal subdomain
App switcher fallback was "personal" causing all rApp links to resolve
to personal.rspace.online/r*. Changed to "demo" so links use the bare
domain which the server rewrites to demo mode. Updated landing page
CTAs and ecosystem links to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:19:51 -08:00
Jeff Emmett 74642a57ca brand: add (you)r* prefix to landing page title
Reinforces the r-suite "your tools" philosophy across all rApp landing pages.
Also normalizes title separators to em-dash (—) for consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 04:21:51 +00:00
Jeff Emmett 034e3b308a chore: update TASK-46 progress — postMessage bridge + module switcher
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:50:28 -08:00
Jeff Emmett c934ed3fd0 feat: folk-rapp postMessage bridge, module switcher, open-in-tab
Enhance the folk-rapp canvas shape with three improvements:

1. PostMessage bridge: parent sends context to iframe on load,
   listens for shape-updated events from CommunitySync. Green
   status dot indicates active connection.

2. Module switcher: header dropdown (⇄ button) lets users change
   which rApp is embedded without recreating the shape.

3. Open-in-tab: ↗ button navigates to the module page (adds a tab)
   instead of opening a new browser window.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:50:07 -08:00
Jeff Emmett 0a795006d3 feat: folk-rapp shape — embed live rApp modules on the canvas
POC for cross-app embedding (TASK-46). New folk-rapp shape type that
embeds any rApp module as a live iframe inside a canvas shape. Features:
- Module picker dropdown when no module selected
- Colored header with module badge/icon
- Open-in-tab action button
- Syncs moduleId + spaceSlug via Automerge CRDT
- Toolbar rApps section now creates folk-rapp (not generic folk-embed)
- Fixed stale "canvas" moduleId refs → "rspace" in canvas.html

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:45:22 -08:00
Jeff Emmett 69cb105758 chore: add backlog task TASK-HIGH.1 (bare-domain module routing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:34:52 -08:00
Jeff Emmett 1bedc4e504 feat: bare-domain module routing — rspace.online/{moduleId} as default
App dropdown links now go to rspace.online/r* (bare domain) instead of
demo.rspace.online/r*. Only the "Try Demo" button links to the explicit
demo subdomain. Server internally rewrites bare-domain module paths to
/demo/{moduleId} while preserving the browser URL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:22:06 -08:00
Jeff Emmett 92bec8243d feat: demo space improvements — description + sorted listing
- Add description to demo space seed for context in the UI
- Sort spaces API: user's own spaces first, then demo, then alphabetical

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:15:35 -08:00
Jeff Emmett 03c843254b feat: add "Try Demo" button to all headers + revert landing page copy
- Add teal "Try Demo" button to rstack-header (right side, before identity)
- Button links to demo.rspace.online/{currentModule} for context-aware demo entry
- Hidden when already on the demo space (server-side conditional)
- Revert website/index.html to original copy, only updating CTA link + adding demo button
- Add demo button CSS to shell.css

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:10:04 -08:00
Jeff Emmett a5f8389239 feat: r-prefix module slugs, landing page, clickable rStack header
- Rename all 23 module IDs to r-prefixed slugs (canvas→rspace, notes→rnotes, etc.)
- Root rspace.online/ now serves the landing page instead of redirecting to demo
- rStack header in app switcher dropdown is now a clickable link to rstack.online
- Update all internal navigation links, badge maps, and URL helpers
- Space root redirects to /rspace instead of /canvas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:04:22 -08:00
Jeff Emmett ae9bbebb4a chore: add backlog tasks for canvas tab bar and persistence features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:51:59 -08:00
Jeff Emmett abca93757b chore: add backlog task TASK-63 (remove iframe shell)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:46:37 -08:00
Jeff Emmett f9bda6c35d refactor: remove iframe shell — render all modules directly via web components
Every module except canvas was using renderIframeShell() to embed standalone
domains (rdata.online, rwork.online, etc.) via iframe. None of these domains
had independent deployments — they routed back to the same container, causing
infinite redirect loops or 404s.

Now all 22 modules render their web components directly inside renderShell(),
eliminating cross-origin failures, iframe loading spinners, and ~820 lines
of dead code. Standalone domain requests are internally rewritten to module
routes instead of 301 redirecting.

- Remove renderIframeShell(), renderStandaloneShell(), IframeShellOptions
- Remove keepStandalone set; rewrite standalone domains internally
- Convert all module GET / handlers to renderShell + <folk-*> components
- Delete 20 standalone.ts entry points (circular/broken)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:45:41 -08:00
Jeff Emmett a483cbe0af fix: tab bar persistence + iframe loading/error states
Tabs now persist in localStorage across page navigations so opening
a new rApp adds it alongside existing tabs instead of replacing them.
Iframe shell shows a loading spinner with 12s timeout and error panel
when standalone apps are unreachable. Converts rSwag to iframe shell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:19:07 -08:00
Jeff Emmett 0f7d4eb7bb feat: canvas tab bar + rApps toolbar + iframe shell for all modules
Add the missing tab bar to the canvas page so users can switch between
rApp layers (with full CommunitySync persistence). Add an "rApps"
toolbar group that embeds any of the 18 remaining modules as interactive
iframes directly on the canvas. Switch all module page routes to
renderIframeShell, loading standalone domains inside the unified shell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:03:29 -08:00
Jeff Emmett fcf350d40a chore: add backlog tasks TASK-59 and TASK-60
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:45:04 -08:00