New habit tracking module: define habits (emoji, color, daily target), tap to log with timestamp, view streaks and 7-day charts. Includes workbench ListView with inline creation, full-page detail view, and drag/drop entity integration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Window buttons now live in the drag handle bar alongside the move arrows,
appearing on hover. Header row is simplified to just icon + title.
Layout: ◀ ···drag··· [−] [□] [✕] ▶
All buttons appear on hover, close button has red hover state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Arrow buttons appear on hover at the left/right edges of the drag bar
to quickly reorder pages without dragging. Hidden when the page is
already first (no left arrow) or last (no right arrow).
- PageShell: onMoveLeft/onMoveRight props, CaretLeft/CaretRight icons
- AppPage: pass through move callbacks
- +page.svelte: handleMoveLeft/handleMoveRight with array swap + persist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DragPreview now accepts a resolveEntity callback that maps drag type
+ data to display info (title, app color, app name). Dragging a task
shows "Meeting mit Team · Todo" instead of just "task".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Drag handle bar spans full width (not just the icon)
- Entire bar is draggable with grab cursor
- Subtle background color differentiates it from content (border-bottom)
- Hover/active states for visual feedback
- DotsSixVertical icon rotated 90° (horizontal grip lines)
- Dark mode support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
First panel no longer sticks to the left edge. Left padding set to
calc(50vw - 240px) so the carousel starts with generous breathing room.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After a successful drag-and-drop, the browser fires a click event on
the source element. This was opening the detail view overlay instead of
completing the drop. Now a one-time click blocker is added after drag
ends to swallow the spurious click.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Break out of the layout's max-w-7xl px-4 container using negative
margins so the workbench carousel fills the entire viewport width.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Page reorder drag now only starts from the drag-handle icon, not from
anywhere in the page. This allows dragSource on list items (tasks,
events, contacts) to work without being intercepted by page reorder.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Making guestMode reactive with $state() triggered an infinite effect
cascade: guestMode.notifications being accessed in the template created
a reactive dependency that, combined with 8+ AppPages each running
$effect for module loading, exceeded Svelte's effect update depth limit.
Root cause: GuestMode object has reactive getters that return new
references on each access. Wrapping it in $state() made every template
access trigger a re-render, which triggered more effects, creating an
unbounded cascade.
Fix: Keep guestMode as plain variable (non-reactive). The warning about
non_reactive_update is acceptable — guestMode is set once during init
and its properties are accessed imperatively, not reactively.
Also:
- Remove debug console.logs from AppPage, +page, +layout
- Keep onMount for workbench state loading (replaces $effect)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete all 25 apps/*/apps/web-archived/ directories (superseded by unified ManaCore app)
- Remove stale +page.server.ts stubs from teams, organizations, settings (always returned empty data)
- Simplify teams and organizations pages to static empty-state (no server load dependency)
- Delete empty apps/context/apps/mobile/components/variants/index.ts
- Remove commented-out apps-archived entries from pnpm-workspace.yaml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make entity registration lazy (ensureEntitiesRegistered) to avoid
circular imports at module load time
- Re-enable cross-module drop target in AppPage
- Fix Component type in app-registry to accept any props (AnyComponent)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce EntityDescriptor pattern: each module declares how its items
can be displayed, dragged, dropped, and created from other modules.
- Entity types + registry with executeDrop orchestration
- Entity descriptors for todo, calendar, contacts
- List items are now draggable (dragSource) across pages
- Pages accept cross-module drops (event→todo, contact→calendar, etc.)
- Drops create new items + bidirectional manaLinks via shared-links
- LinkedItems component shows cross-module links in DetailViews
- Visual feedback: page glows purple on valid hover, green on success
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
$derived cannot reference variables declared later in the script.
Moved the calculation below the isTagStripVisible $state declaration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dev env vars (_CLIENT suffixed) are empty, so localhost:3001 (auth),
localhost:3050 (sync), localhost:3060 (api) were blocked by CSP.
Added http://localhost:* and ws://localhost:* to connect-src when
NODE_ENV !== 'production'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bind:clientHeight + $effect setting CSS variable caused infinite reflow
loop: height change → CSS var → padding change → height change.
Now using $derived calculation from state (isCollapsed, isTagStripVisible)
instead of DOM measurement. No reflow loop possible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per-app SSE connections exhaust the browser's 6-connection-per-origin
limit, causing the app to hang on load. Reverted to HTTP polling for
both eager and lazy apps.
SSE server endpoint remains available for future use as a single unified
stream (one connection for all apps, similar to the old unified WebSocket).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@const can only be used inside {#if}, {#each}, etc. — not directly in
a <div>. Reverted ActionZone and AuthGateModal back to <svelte:component>
which works correctly (the deprecation warning is less important than
a broken app).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Times ListView: inline timer with start/stop, name input, live elapsed display
- Times DetailView: editable entry with duration (h/m/s), project, billable, delete
- Zitare ListView: tap-to-cycle quotes, inline fav button, tag drag-and-drop support
- Zitare DetailView: full quote view with category, author bio, share, favorite
- Homepage: remove tag bar (available via PillNav), carousel uses full width
- Register both DetailViews in app-registry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 15 individual dev:*:server scripts with single dev:api command.
Update all dev:*:full, dev:*:app, dev:*:local scripts to start the
unified API server (apps/api) instead of archived individual servers.
dev:manacore:servers now starts: auth + sync + api (3 processes instead of 8)
dev:manacore:full now works without errors.
Removed: wisekeep scripts (project archived), broken server references.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These are secondary pages accessible via settings/profile, not primary navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Display assigned tags as colored pills in todo, calendar, contacts
DetailViews. Clicking a tag pill removes it from the item. Hover turns
the X icon red for clear affordance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All app clicks in the PillNav app drawer now open in a new tab via
window.open('_blank'). Previously internal URLs used window.location.href
(same tab navigation) which was confusing in the unified app context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace tiny 6px tag dots with readable pills showing colored dot +
tag name. Uses color-mix for subtle tinted background per tag color.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
useAllTags() returns a Svelte 5 runes object with .value, not an
Observable with .subscribe(). Also fix getTagsByIds() calls to pass
the allTags array as first argument.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add rightAction snippet prop to QuickInputBar (InputBar.svelte)
- Move toggle from leftAction to rightAction (renders after submit button)
- Increase toggle size from 28px to 36px for better tap target
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Mount DragPreview + draggable tag bar on workbench homepage
- Add dropTarget on list items in todo, calendar, contacts ListViews
- Show assigned tags as colored dots on list items
- Tags can be dragged from the tag bar onto any item to assign them
- Drop target highlights with module-colored outline on hover
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Toggle button now renders inside the InputBar pill (left side) instead of
as a separate element next to it. Uses the existing leftAction snippet prop.
- Button is 28px circle, subtle background, no border/shadow (lives inside pill)
- Remove bottom-stack-row wrapper (no longer needed)
- Cleaner visual: toggle is part of the input bar, not floating beside it
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Tags pill moved to first position in nav items (leftmost)
- Toggle button: 44px round (matches InputBar height), same border-radius
(9999px), same backdrop-filter blur, same box-shadow, theme-aware colors
- Button sits directly next to QuickInputBar with no gap, vertically centered
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bottom-stack container now exports its height as --bottom-chrome-height
CSS custom property (via bind:clientHeight + $effect on :root). All elements
that need to position above the bottom chrome read this variable:
- PageCarousel minimized-tabs: bottom: var(--bottom-chrome-height)
- NotificationBar: bottom: var(--bottom-chrome-height)
- Main content padding: calc(var(--bottom-chrome-height) + 2rem)
This ensures tabs, notifications, and content never overlap with the
bottom stack regardless of PillNav collapse state or TagStrip visibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 4 independent position:fixed elements with one flex container that
stacks them naturally from bottom to top. Elements push each other
automatically — no more hardcoded offsets or z-index conflicts.
Stack order (bottom → top):
1. PillNavigation (collapsible)
2. TagStrip (togglable)
3. QuickInputBar + toggle button row
Shared-UI changes:
- PillNavigation: add positioning='fixed'|'static' prop
- QuickInputBar: add positioning='fixed'|'static' prop
- TagStrip: add positioning='fixed'|'static' prop
- All default to 'fixed' for backward compatibility
Layout changes:
- Wrap all bottom elements in .bottom-stack (position:fixed, flex-column)
- Remove hardcoded bottomOffset calculations
- Toggle button is now inline next to QuickInputBar (not separately positioned)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When heightPx is not yet set, use the element's actual offsetHeight
instead of 0 so the first drag correctly adjusts height.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend PageShell resize handle to support diagonal drag for both
width and height. Height is persisted per page in workbench settings.
Resize cursor changed from ew-resize to nwse-resize.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Small floating button (▼/▲) at the bottom-right that toggles PillNav
visibility. QuickInputBar's bottomOffset adjusts dynamically when nav
is collapsed (70px → 12px), reclaiming vertical space.
- Button uses existing handleCollapsedChange() with localStorage persistence
- Smooth transitions on position and rotation
- Glass-morphism style (blur + semi-transparent background)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consistent naming: ListView.svelte + DetailView.svelte as a pair.
Update app-registry.ts and splitscreen/registry.ts imports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug 1: NotifyUser() early-returned when no WebSocket clients existed,
skipping SSE subscriber notifications entirely. Fixed by restructuring
to check WS clients and SSE subscribers independently.
Bug 2: SSE stream cursor defaulted to client's `since` parameter when
no initial data existed. If `since` was in the future (or very recent),
live updates had created_at < cursor and were silently filtered out.
Fixed by defaulting cursor to now() when no initial data is returned.
Bug 3: NotifyUser used original sseSubs slice instead of sseSubsCopy
after releasing the read lock (race condition).
Verified E2E: Push from client A → SSE stream on client B receives
live change event with correct data within ~1 second.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add inline-editable DetailViews with auto-save for:
- cards: deck details with color, description, public toggle
- storage: file details with rename, favorite, size/type info
- presi: presentation deck details with slide count
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add inline-editable DetailViews with auto-save for:
planta, inventar, skilltree, memoro, questions, uload, mukke, citycorners
Wire AppView list items to open overlay via navigate() with sibling
navigation support. Fix citycorners table names (cityLocations→ccLocations).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove /dashboard (redundant), merge /home into / (app root)
- Update all redirects and navigation references accordingly
- Add panel navigation stack with overlay detail views for workbench
- Implement inline-editable DetailViews for todo, calendar, contacts
- Auto-save on blur, prev/next navigation with sibling arrows
- Fix minimized tabs z-index behind quick input bar
- Fix showNewEvent undefined error in calendar AppView
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Client now connects to GET /sync/{appId}/stream via fetch + ReadableStream
instead of WebSocket + HTTP pull. Each app gets its own SSE connection that
delivers initial sync data + live updates in one persistent stream.
Changes:
- Remove WebSocket connection (connectUnifiedWs)
- Add connectSSE() per app using fetch + ReadableStream
- Parse SSE events (changes, heartbeat) from streamed response
- Auto-reconnect on disconnect with WS_RECONNECT_DELAY
- Fallback to polling if SSE endpoint not available
- ensureAppSynced() connects SSE for lazy apps on first visit
- handleOnline() reconnects all active SSE streams
- handleOffline() aborts all SSE connections
Benefits: 1 connection instead of 2 (WS + HTTP), data delivered instantly
without notification → pull round-trip, works through HTTP proxies/CDN.
Push (POST /sync/{appId}) remains unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New endpoint GET /sync/{appId}/stream sends Server-Sent Events with
change data directly, replacing the WebSocket notification + HTTP pull
round-trip pattern.
Server (Go):
- HandleStream() in handler.go: SSE endpoint with initial sync + live streaming
- Hub.Subscribe()/Unsubscribe() in hub.go: channel-based SSE subscriber system
- Notification type for type-safe SSE events
- convertChanges() helper extracted from duplicated code
- WriteTimeout set to 0 for SSE long-lived connections
Protocol: Client connects to /sync/{appId}/stream?collections=a,b&since=...
Server sends initial changes, then streams live changes as other clients sync.
Heartbeat every 30s keeps connection alive. Push still uses POST /sync/{appId}.
WebSocket remains available as fallback (not removed).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only sync eager apps at startup (manacore, todo, calendar, contacts,
tags, links — needed for dashboard widgets). All other apps are lazy:
their collections sync on first module route visit.
Reduces startup pull requests from ~108 to ~20-30. Lazy apps get
synced when the user navigates to their module via ensureAppSynced().
- Add EAGER_APPS config set in sync.ts
- startAll() only starts pull for eager apps
- ensureAppSynced() starts pull + periodic sync for lazy apps
- Route-based trigger in +layout.svelte $effect
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>