managarten/packages/website-blocks
Till JS ac44d51363 feat(calendar): M4.a — events adopt the unified visibility system
Third consumer of @mana/shared-privacy. Calendar events now carry a
VisibilityLevel the owner flips from the EventDetailModal via
<VisibilityPicker>; a new calendar.events embed source lets the user
drop a moduleEmbed block on their website that pulls their public
events in.

This unblocks concrete use-cases the Website-Builder audit surfaced:
band tour dates, public workshops, public rehearsals on a team-space
website, meeting-with-the-host pages.

Changes:
- calendar/types: visibility + unlistedToken + visibilityChangedAt +
  visibilityChangedBy on LocalEvent; CalendarEvent (UI type) requires
  visibility. timeBlockToCalendarEvent forwards the field; cross-module
  TimeBlocks (tasks, habits, time entries) without an owning
  LocalEvent fall back to 'space' so they stay off the public embed
- calendar/stores/events: createEvent stamps
  defaultVisibilityFor(activeSpace.type); createDraftEvent seeds a
  'private' draft until the user explicitly opts in; new
  setVisibility(id, level) mints/clears the unlisted token on the
  transition boundary and emits cross-module VisibilityChanged
- calendar/components/EventDetailModal: <VisibilityPicker compact>
  sits in the modal-actions row left of copy/edit/delete

website embed:
- website-blocks/moduleEmbed/schema: EmbedSourceSchema adds
  'calendar.events'; the filter shape gains optional `upcomingDays`
  (1-365) and `tagIds` (up to 16). Old filters (isFavorite/status/kind)
  remain — each source uses only its own subset
- website/embeds: resolveCalendarEvents gates hard on
  canEmbedOnWebsite(event.visibility ?? 'private'), joins each event
  to its LocalTimeBlock for the real start/end, applies the optional
  upcomingDays window and tag-id AND-filter, sorts upcoming-first with
  id as stable tiebreaker

Redaction is whitelist-per-design (plan §2): the inlined snapshot
carries only title, formatted date range, and location — NOT
description, reminders, tag labels, or the guest list. Fields that
typically hold private context stay out of the public blob regardless
of the visibility toggle.

Verified:
- pnpm check (web): 7450 files, 0 errors
- pnpm test calendar + website: 26/26
- pnpm run validate:all green

Next: M4.b — Todo, M4.c — Goals. Same pattern; split out because
goals lives under $lib/companion/goals/ with its own structure and
Todo has a complex view-column/filter surface that warrants its own PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 02:32:25 +02:00
..
src feat(calendar): M4.a — events adopt the unified visibility system 2026-04-24 02:32:25 +02:00
package.json feat(website): M3 — 5 more blocks, containers, upload, themes 2026-04-23 14:27:49 +02:00
tsconfig.json feat(webapp): wire isParallelSafe in Companion chat + Mission runner 2026-04-23 14:11:24 +02:00