Commit Graph

129 Commits

Author SHA1 Message Date
xarmian edcf2ae8b0 Board view improvements: independent scrolling, unified cards, lane reorder, new-item modal (#61)
* feat: independent board scrolling, unified card style, lane reordering, and new-item modal

- BoardView: switch from CSS grid to flex layout with independent per-column
  scrolling, matching the Roles board UX
- ItemCard: redesign to match Roles board card style — top row with optional
  collection badge + ref, compact meta row with colored status/priority text
- Roles board: replace inline card markup with shared ItemCard component,
  add HTML5 drag-and-drop lane reordering (persisted via new API endpoint),
  rename "Highlight Mine" to "Mine", add "+ New" button with collection
  picker modal
- Backend: add PUT /roles/board/lane-order endpoint and UpdateAgentRoleOrder
  store method for batch role sort_order updates
- Collection page: board view now fills viewport height so columns have
  bounded scroll areas

* fix: resolve svelte-check type error and remove unused CSS selectors

- Add null guard on lane.role in openEditModal onclick
- Remove unused .role-edit-actions, .role-btn-create, .role-btn-cancel CSS

* fix: correct lane reorder insert index when dragging forward

After splicing out the source lane, downstream indices shift left by one.
Adjust the insert index when srcIdx < dstIdx to place the lane at the
correct drop target position.
2026-04-04 13:02:12 -04:00
xarmian 2f9e61ad9c fix: within-lane reorder now persists sort order
The early return for same-lane drops (oldKey === key) was skipping
the sort persistence code entirely. Restructured handleDndFinalize
so the cross-lane role update is a nested conditional, and the sort
order persistence always runs regardless of whether a cross-lane
move occurred.
2026-04-04 14:10:13 +00:00
xarmian 034f169a02 fix: keep isDragging true until lanes state is updated
The $effect that syncs laneData from orderedLanes was firing when
isDragging was set to false, overwriting the optimistic sort order
with stale data. Now isDragging stays true until after lanes state
is updated with the new sort order, so the $effect sees correct data.
2026-04-04 14:07:00 +00:00
xarmian 8b1b46ef22 feat: persistent card ordering within role board lanes
Add role_sort_order column to items for independent ordering in the
role board, separate from the collection sort_order.

Backend:
- Migration 020: role_sort_order INTEGER column on items
- Item model, all SELECT/INSERT/Scan queries updated
- PUT /roles/board/reorder endpoint for batch sort updates
- Board API sorts items by role_sort_order within each lane

Frontend:
- Within-lane drag reorder persists via reorder API
- Cross-lane moves also persist new sort order
- Both operations are optimistic (no page refresh)
2026-04-04 14:00:21 +00:00
xarmian 64ab4fe1d3 fix: remove assignee pills from lane headers, show on cards only
Assigned user names were shown both in the lane header as pills and
on individual item cards. Remove the lane header pills — the card
already shows the assigned user, and duplicating it in the header
added clutter without information.
2026-04-04 13:26:46 +00:00
xarmian 0cf8f34496 fix: center role dialog on mobile, shrink add-role button
- Dialog uses fixed positioning with translate(-50%, -50%) for
  reliable viewport centering on all screen sizes
- Add Role column no longer stretches to full lane width/height —
  sized to its content with align-self: flex-start
2026-04-04 13:24:37 +00:00
xarmian a3e8ccc4ca refactor: per-lane edit buttons and add-column replaces manage modal
Remove the ⚙ Manage button and its full-list modal. Instead:

- Each role lane header has a ✎ edit button (appears on hover)
  that opens a focused dialog for that role's fields + delete
- A dashed "+" column at the far right of the board opens the
  same dialog in create mode
- Empty state has a "Create your first role" CTA
- Dialog is simpler: one role at a time with name, icon,
  description, tools fields, save/cancel/delete
2026-04-04 12:19:30 +00:00
xarmian e958abc20a fix: optimistic drag-and-drop — no page refresh on role reassignment
Replace the loadData() round-trip after dropping with an optimistic
local update. The item moves instantly in lanes state with updated
role fields, and the API call fires in the background. Only reloads
from server on error (revert). No scroll reset, no flash.
2026-04-04 12:06:49 +00:00
xarmian 2635823cd2 fix: role board drop zones extend to bottom of tallest column
Change lanes-container from align-items: flex-start to stretch so all
lanes match the height of the tallest column. Combined with flex: 1 on
lane-items, the drop target area fills the full remaining lane height.
2026-04-04 12:03:32 +00:00
xarmian a2b51693e1 fix: drag-and-drop now shows item in new column immediately
Keep isDragging true during the API call so the $effect doesn't
revert laneData from stale orderedLanes. The item stays visually
in the target lane while the server processes the role change,
then isDragging releases after loadData() refreshes from the server.
2026-04-04 11:59:35 +00:00
xarmian edd259b1b0 feat: role board — cross-collection view, dashboard breakdown, agent bindings (#60)
* feat: role board — cross-collection view, dashboard breakdown, agent bindings (#PHASE-11)

Add a standalone role board page showing all work organized by agent
role across every collection. This is the "human orchestrator" view —
see at a glance what's queued for each capability and who's working it.

Agent bindings:
- Add `tools` text field to agent_roles table (migration 019)
- CLI: `pad role create "Implementer" --tools "Claude Code + Sonnet"`
- Lightweight notes about preferred tools — no per-user binding table

Dashboard role breakdown:
- `pad project dashboard` now includes `by_role` section
- Shows item count, assigned users, and tools per role
- CLI renders role summary table with icons

Role board API:
- `GET /workspaces/{ws}/roles/board` — items from all collections grouped by role
- Filters terminal-status items (done, cancelled, etc.)
- Supports `?assigned_user_id=X` for "my work" filtering
- Returns role info, items, and assigned user list per lane

Web UI:
- New page at /{workspace}/roles with horizontal lane layout
- Collection badges on cards (items span collections)
- "My Work" toggle to filter by current user
- Empty states for no roles and empty lanes
- Sidebar nav: 🎭 Roles link added
- Responsive: stacks vertically on mobile

Skill:
- References role board in greeting and "who's working on what" patterns

* feat: add assignment picker to item detail page

Replace read-only assignment display with interactive dropdowns for
assigning users and roles directly from the item detail page.

- User dropdown populated from workspace members
- Role dropdown populated from agent roles
- Either can be set or cleared independently
- Saves immediately on change via PATCH API
- Added assigned_user_id/agent_role_id/clear_* to ItemUpdate type

* feat: add role management UI to roles page

Add a "Manage" toggle in the role board header that reveals an inline
panel for creating, editing, and deleting roles directly from the UI.

- Role cards show icon, name, description, tools, and item count
- Edit inline: name, icon, description, tools
- Create new roles with a dashed card form
- Delete with confirmation dialog
- Board auto-refreshes after changes

* refactor: replace inline role management with dialog modal

The inline horizontal card grid was cramped and hard to use. Replace
with a proper <dialog> modal that opens from the ⚙ Manage button.

- Vertical list of role rows with icon, name, description, tools, item count
- Inline edit mode per row with labeled fields
- Create new role form at the bottom with clear field labels
- Click backdrop or ✕ to close, board refreshes on close
- Native dialog handles backdrop, escape key, and focus trapping

* fix: role board mobile layout matches collection kanban, unassigned first

- Unassigned lane now appears first (before role lanes)
- Mobile: horizontal swipe with scroll-snap at 75vw columns, matching
  the collection BoardView pattern (no vertical stacking)

* feat: add drag-and-drop between role board lanes

Items can now be dragged between role lanes to reassign their role.
Uses svelte-dnd-action matching the collection BoardView pattern.

- Drag items between role lanes to change role assignment
- Drag to Unassigned lane to clear role
- Drop target highlight on hover
- Touch support with 500ms delay (same as collection board)
- Haptic feedback on mobile drag start
- Board refreshes after drop to sync server state

* fix: auto-assign user on drag to role lane, show unassigned in My Work

- When dragging an unassigned item into a role lane, automatically
  assign the current user alongside the role
- "My Work" filter now shows items assigned to you OR items with no
  user assignment, so unassigned work remains visible and claimable

* fix: three-state filter on role board — All, My Work, Unassigned

Replace the My Work toggle with a segmented button group offering
three filter modes:
- All: show everything (default)
- My Work: items explicitly assigned to the current user
- Unassigned: items with no user assignment

* fix: replace filter buttons with Highlight Mine toggle

Remove the three-state filter (All/My Work/Unassigned) and replace
with a single "Highlight Mine" toggle that dims cards not assigned
to the current user. All items remain visible and draggable — your
items just visually pop while others fade to 35% opacity (hovering
restores to 70%).

* fix: resolve undefined loadBoard and myWorkOnly in role board page

Replace 6 references to nonexistent `loadBoard()` with `loadData()`
(the actual data-loading function), and replace `myWorkOnly` with
`highlightMine` (the actual state variable). Fixes svelte-check errors
that caused CI Web Build to fail.

* fix: role breakdown pointer aliasing and terminal status filtering

P1: Copy role.ID to a local variable before taking its address in
GetRoleBreakdown, avoiding potential pointer aliasing from the range
variable (safe in Go 1.22+ but clearer with an explicit copy).

P2: Add terminal status exclusion to the GetRoleBreakdown SQL query
so dashboard counts match the board view. Previously, done/completed/
cancelled items were included in role counts, inflating active load.

Addresses Codex review comments on PR #60.
2026-04-04 07:48:33 -04:00
xarmian 9fedbe4ad6 feat: skill role awareness + role-specific conventions (#59)
* feat: skill role awareness + role-specific conventions (#PHASE-10)

Make the /pad skill role-aware so agents know what role they're acting
as and load conventions scoped to that role. Role context lives in the
conversation — no server state, no files, no new CLI commands.

Skill changes:
- Ask for role on first invocation when roles exist
- Parse "as <role>" inline: /pad as implementer, /pad what's next as reviewer
- Auto-filter work queue by active (user, role) pair
- Role-aware greeting: "Working as 🔨 Implementer. Your queue: ..."
- Load role-specific + global conventions before performing work
- Support mid-session role switching
- Updated CLI reference: --role/--assign flags, pad role commands, --comment best practice

Convention schema:
- Migration 018: add optional `role` field to Conventions collection
- Conventions with a role value apply only to that role
- Conventions without a role apply to all (backward compatible)
- Updated defaults.go with role field

* fix: use json_insert for conventions role field migration

Replace fragile REPLACE() on exact JSON string literal with SQLite's
json_insert(schema, '$.fields[#]', ...) which appends the role field
regardless of field order or custom fields in the schema. Adds a
NOT EXISTS guard via json_each() to skip if role already present.

Also adds role field to shared conventions template for non-default
workspace templates, and regression tests for schema seeding.

Addresses Codex review comment on PR #59.
2026-04-03 21:50:04 -04:00
xarmian be576d9e24 feat: agent roles — role-based (user, role) assignment for items (#58)
* feat: agent roles — role-based (user, role) assignment for items (#PHASE-9)

Introduce agent roles as a first-class concept for human-agent work
assignment. Roles describe capability specializations (Planner,
Implementer, Reviewer, etc.) and items can be assigned to a (user, role)
pair, enabling natural handoff workflows between different AI tools.

Migration:
- New `agent_roles` table (workspace-scoped, slug-unique)
- `assigned_user_id` + `agent_role_id` columns on `items` with FKs
- Removed legacy `assignee` text field from Tasks schema

Backend:
- AgentRole model + full CRUD store/API
- All item queries updated with LEFT JOINs to resolve assignment
- Item list filtering by assigned_user_id and agent_role_id
- Role transitions tracked in activity feed metadata

CLI:
- `pad role list/create/delete` commands
- `--role` and `--assign` flags on item create/update/list
- Assignment displayed in `pad item show` output

Web:
- TypeScript types + API client for agent roles
- Role badge on item cards in list/board views
- Assignment display on item detail page

* fix: enforce workspace-scoped assignments and fail fast on unresolved --assign filter

Addresses code review feedback from PR #58:

P1: Add validateAssignmentScope() to the store layer, called by both
CreateItem and UpdateItem. Verifies that assigned_user_id belongs to
the workspace (via IsWorkspaceMember) and agent_role_id exists in the
workspace (via GetAgentRole) before writing. Prevents cross-workspace
assignment leaks.

P2: The CLI `pad item list --assign <name>` now errors instead of
silently returning unfiltered results when the member lookup fails or
no workspace member matches the provided name.
2026-04-03 21:16:38 -04:00
xarmian 481527de02 fix: server-side timeline pagination and reaction toggle (#57)
* fix: server-side timeline pagination and reaction toggle (#55, #56)

Issue #55: Replace in-memory pagination with cursor-based approach.
The /timeline endpoint now accepts `before` (RFC3339 timestamp) and
`limit` params, fetching a small window from each source (comments,
activities, versions) instead of loading everything into memory.
Frontend gets a "Load more" button that passes the oldest entry's
timestamp as the cursor.

Issue #56: Plumb current user ID to reaction toggle. ItemTimeline
fetches the auth session on mount to get the user ID, passes it to
TimelineCommentCard. toggleReaction now checks if the user already
reacted (by matching reaction.user_id) and calls onRemoveReaction
to un-react. Own reaction chips are visually highlighted.

* fix: address review findings for PR #57 (iteration 1)

- Use <= with ID tie-breaker in cursor queries to prevent skipping
  entries at timestamp boundaries; use consistent RFC3339 format
- Treat orphaned replies (parent on different page) as top-level
  entries instead of silently dropping them
- Deduplicate by ID on "Load more" to handle boundary overlap
- SSE reload now prepends new entries and updates existing ones
  instead of clobbering all paginated state
- Parse before cursor with RFC3339Nano fallback for sub-second
  precision
- Fix dangling doc comment on ListItemVersions

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: add global auth store, remove per-component session fetch

Create authStore (web/src/lib/stores/auth.svelte.ts) following the
same pattern as workspaceStore. The root layout populates it on mount.
ItemTimeline now reads currentUserId via $derived(authStore.userId)
instead of making a separate api.auth.session() call on every mount.

* fix: address review findings for PR #57 (iteration 2)

- Add beforeID tie-breaker to all cursor queries to prevent infinite
  Load More loop when entries share the same timestamp
- Over-fetch per source (limit*3) to avoid skipping filtered entries
- SSE refresh now detects deleted entries and removes them from the
  first-page window instead of keeping stale data
- Auth store re-throws on fetch errors so layout can distinguish
  "not authenticated" from "server unreachable"
- Sidebar reads from authStore instead of making redundant session fetch

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: address review findings for PR #57 (iteration 3)

- Add ID tie-breaker to buildTimeline sort to match SQL cursor ordering
  (prevents same-second entries from being skipped on Load More)
- Refresh authStore after login so Sidebar and reaction toggle have
  correct user identity without requiring a page reload
- SSE merge now tracks first-page IDs explicitly to detect deletions
  without incorrectly removing entries from older pages that share
  boundary timestamps

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-03 09:39:40 -04:00
xarmian 998716ae49 feat: unified item timeline with comment-on-update, threading, and reactions (#54)
* feat: unified item timeline with comment-on-update, threading, and reactions (IDEA-115)

Replace the separate Comments section and Version History modal on the
item detail page with a single chronological timeline that interleaves
comments, activities, and content versions.

Key changes:
- Add --comment flag to `pad item update` so agents/users can explain
  status changes inline (creates a comment linked to the activity)
- Add threaded replies (parent_id on comments) with inline reply UI
- Add emoji reactions on comments (new comment_reactions table)
- New /timeline API endpoint merges comments, activities, and versions
  server-side with dedup and collapsing of rapid edits
- New Svelte timeline components: ItemTimeline, TimelineCommentCard,
  TimelineActivityCard, TimelineVersionCard, ReactionPicker
- Remove Implementation Notes and Decision Log inputs from web UI
- Update skill docs to encourage --comment on status changes

* fix: address review findings for PR #54 (iteration 1)

- Use ListItemVersions instead of ListVersions in timeline endpoint
  so item content history renders correctly
- Add workspace validation to reply and reaction handlers to prevent
  cross-workspace comment mutation
- Raise activity cap from 500 to 10000 to avoid silently truncating
  long timelines
- Fix toggleReaction to always POST (idempotent) instead of incorrectly
  matching other users' reactions for DELETE
- Await onReply promise before clearing draft to prevent duplicate
  submissions and lost drafts on failure
- Register reaction_added/reaction_removed in SSE ITEM_EVENTS so
  reactions from other sessions appear in real-time

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: address review findings for PR #54 (iteration 2)

- Fix nil pointer dereference in timeline handler when item not found
- Store empty string instead of NULL for reaction user_id so UNIQUE
  constraint works correctly in SQLite
- Add workspace validation to handleDeleteComment (cross-workspace
  deletion was possible)
- Fix SKILL.md duplicate numbering (4. appeared twice)
- Remove || true debug artifacts from reaction conditionals
- Replace SvelteMap with plain Map in non-reactive groupReactions
- Use != null checks for timeline API params to handle offset=0
- Log warning on comment creation failure during item update

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-03 07:25:56 -04:00
xarmian 7a7e666659 fix(items): resolve relation refs in list filters for BUG-19 (#53) 2026-04-02 22:08:55 -04:00
xarmian 89db556e29 feat(server): add info command for TASK-134 (#52) 2026-04-02 21:44:08 -04:00
xarmian 35f3dd1da4 feat(conventions): add structured metadata for TASK-133 (#51)
* feat(conventions): add structured metadata for TASK-133

* fix(web): add workspace update type for CI
2026-04-02 18:39:51 -04:00
xarmian 9650c0ec0b feat(workspaces): populate context during onboarding for TASK-132 (#50)
* feat(web): add workspace context editor for TASK-131

* feat(workspaces): populate context during onboarding for TASK-132
2026-04-02 16:24:21 -04:00
xarmian 64654846be feat(web): add workspace context editor for TASK-131 (#49) 2026-04-02 16:23:47 -04:00
xarmian 23a7fc2be1 feat(workspaces): add CLI and API context support for TASK-130 (#46) 2026-04-02 16:10:09 -04:00
xarmian c1a9cd1533 feat(workspaces): add typed context schema for TASK-129 (#47) 2026-04-02 16:09:10 -04:00
xarmian f5649b912e refactor(cli): group first-release commands for TASK-127 (#45) 2026-04-02 15:28:16 -04:00
xarmian f1e4618013 feat(cli): add agent query commands for TASK-126 (#44) 2026-04-02 14:04:04 -04:00
xarmian c61f4cdaaa feat(items): add structured notes for TASK-125 (#43) 2026-04-02 13:48:00 -04:00
xarmian 43f0e5ca9a feat(cli): add reconcile workflow for TASK-124 (#42) 2026-04-02 11:29:35 -04:00
xarmian bd9f281c81 feat(items): add first-class code metadata for TASK-123 (#41) 2026-04-02 11:15:38 -04:00
xarmian 0596ed07f4 feat(lineage): surface derived closure for TASK-122 (#40) 2026-04-02 10:46:09 -04:00
xarmian c9ba893650 feat(links): add lineage relationships for TASK-121 (#39) 2026-04-02 09:45:41 -04:00
xarmian 1205fd1757 feat(web): unify setup-required auth guidance for TASK-119 (#38) 2026-04-02 05:35:20 -04:00
xarmian b59f50982f feat(auth): add local bootstrap setup for TASK-118 (#37)
* feat(auth): add local bootstrap setup for TASK-118

* fix(auth): honor setup-required bootstrap flow
2026-04-02 05:13:17 -04:00
xarmian a2a25b176a refactor(auth): make setup state explicit for TASK-117 (#36) 2026-04-01 22:21:26 -04:00
xarmian 5db077a2a3 refactor(cli): limit local server autostart to local mode for TASK-116 (#35) 2026-04-01 21:56:18 -04:00
xarmian 123a7aec98 feat(cli): add client configure flow for TASK-115 (#34) 2026-04-01 21:32:55 -04:00
xarmian 20b1260d76 chore(repo): ignore local agent artifacts for TASK-113 (#33) 2026-04-01 15:12:59 -04:00
xarmian eb4e41ad0b build(embed): stop rewriting embed.go for TASK-112 (#32) 2026-04-01 15:00:18 -04:00
xarmian 4dea8ddc83 docs(docker): default host publishing to localhost for TASK-114 (#31) 2026-04-01 14:23:00 -04:00
xarmian 193d694995 feat(editor): add mobile list nesting controls for TASK-111 (#30) 2026-04-01 12:38:00 -04:00
xarmian 172004f002 [codex] Fix BUG-16 quick-create button behavior and BUG-15 phase task refresh (#29)
* fix(collection): align quick-create button with Enter for BUG-16

* fix(phases): refresh linked tasks live for BUG-15
2026-04-01 12:15:27 -04:00
xarmian 056beba12b fix: run npm ci before web build to ensure dependencies are installed
Prevents "vite: not found" errors on fresh clones or after node_modules
is removed, by installing dependencies before building.
2026-03-31 16:49:34 -04:00
xarmian d136d78cfd fix(web): route empty collection new CTA to create form (BUG-14) (#27) 2026-03-31 16:43:41 -04:00
xarmian 0972347cf0 Fix svelte warnings and add build version info (#26)
* Fix all 17 svelte-check warnings across 7 components

- Add tabindex to toolbar role elements (BoardView, Editor)
- Replace nested buttons with div[role=button] in ListView group headers
- Add role="none" to click-to-close backdrop overlays (Editor, slug page)
- Fix label→span for non-input field labels (CreateWorkspaceModal)
- Add keyboard handlers to interactive divs (CreateWorkspaceModal drop zone, slug page modal)
- Remove unused .slash-backdrop CSS (Editor) and input[type=text] selector (CreateWorkspaceModal)
- Fix state_referenced_locally in RawMarkdownEditor ($state init)
- Add svelte-ignore for conditional tabindex false positive (ToastContainer)

* feat: add build version, commit hash, and timestamp to CLI and web UI

Inject version info via ldflags during build (Makefile, GoReleaser,
Dockerfile). Expose version/commit/build_time in the health API
endpoint and display it in the sidebar footer. Dev builds show
"dev (abc1234 ...)", releases show "v1.2.3 (abc1234 ...)".
2026-03-30 11:54:45 -04:00
xarmian 07ff6faed7 feat: global skill installation registry and detection fixes (#25)
Track all skill installations in ~/.pad/installations.json so
`pad install --update` can update stale skill files across every
project in one shot. Also fixes false Copilot detection on projects
that have .github/ for CI but don't use Copilot.

- Add Installation registry (internal/cli/registry.go) with
  record, prune, status, and update-all operations
- Record installations from all install code paths (install,
  init, skills install, interactive, --all, --update)
- `pad install --list` / `pad skills status` now show tracked
  installations across all projects with freshness indicators
- `pad install --update` / `pad skills update` now update stale
  files globally, not just the current directory
- `pad skills update` and `pad skills status` delegate to the
  same logic as `pad install --update` and `pad install --list`
- Fix Copilot detection: use .github/copilot or .github/instructions
  instead of .github (which exists on most projects for CI)
- Add .codex to agents detection directories
- `pad init` on already-linked workspaces now records existing
  installations in the registry
2026-03-30 11:31:05 -04:00
xarmian a6befde8bf feat: email-based password reset flow (#24)
* feat: email-based password reset flow (IDEA-81)

Add full password reset flow: forgot password request, time-limited
reset tokens (1hr, single-use, SHA-256 hashed), new password form,
and automatic session creation after reset.

* fix: use all: prefix in go:embed to include _-prefixed files

Go's embed package excludes files starting with _ or . when recursing
directories. SvelteKit/Vite occasionally generates chunk filenames with
_ prefixes (e.g. _VLZtjCJ.js), causing them to be silently dropped
from the embedded filesystem and served as HTML by the SPA fallback.

The all: prefix includes everything regardless of filename prefix.
Fixed in both embed.go and the Makefile which regenerates it.

* fix: address PR review — atomic token consumption, error handling, log reset URL

- Replace ValidatePasswordReset + MarkPasswordResetUsed with atomic
  ConsumePasswordReset using UPDATE ... WHERE ... RETURNING to prevent
  race conditions where two concurrent requests consume the same token
- Handle DeleteUserSessions errors (log instead of silently ignoring)
- Log the full reset URL when email is not configured so the admin
  CLI fallback is actually usable
2026-03-29 19:31:16 -04:00
xarmian d94a28c3e8 feat: account settings, email infrastructure, and UX polish (#23)
- Add Account tab to settings: profile editing, password change, API token management
- Add PATCH /api/v1/auth/me endpoint for profile updates with password verification
- Add email sending infrastructure via Maileroo with contextual sender names
- Add Platform settings tab (admin-only) for email configuration with test send
- Add platform_settings table for instance-wide configuration
- Add tab visibility refresh: silently sync data when browser tab regains focus
- Fix filters icon: replace broken Unicode character with proper SVG funnel
- Add cancel invitation support, TypeScript User/APIToken types
2026-03-29 18:29:52 -04:00
xarmian 1da91c4cff feat: reorganize settings page with tabbed layout (IDEA-67) (#22)
Replace the single long-scroll settings page with a 4-tab layout:
General (workspace + theme), Members, Collections, and Danger Zone.
Tab selection persists in the URL hash and survives page refreshes.
2026-03-29 10:55:14 -04:00
xarmian 992690f77d fix: split item detail meta section into two rows for mobile readability (#21)
The meta area (timestamps + action buttons) was a single flex row that
wrapped chaotically on narrow screens. Split it into a `.meta-info` row
for timestamps/save status and a separate `.meta-actions` row for buttons,
both with flex-wrap. Renamed `.history-btn` to `.action-btn` with
consistent sizing (min-width, white-space: nowrap).

Closes IDEA-71
2026-03-29 08:21:52 -04:00
xarmian a49d7606dd fix: prevent layout jump from save status transitions on mobile (#20)
Replace conditional {#if} rendering of save status with always-present
span using opacity transitions. Element stays in the DOM flow so the
flex row never reflows when state changes.
2026-03-29 07:58:35 -04:00
xarmian c7dc653c10 fix: debounce activity entries to prevent autosave flood (#19)
Rapid saves to the same item by the same user within 5 minutes now
coalesce into a single activity entry instead of creating one per save.
SSE events still fire every time for real-time UI updates.
2026-03-29 07:53:36 -04:00
xarmian f5691fd8f6 fix: show activity history on item detail page, not just content versions (#18)
The "History" panel on item detail pages was blank because it only showed
content version snapshots (stored in item_versions). Field changes like
status updates, title edits, and moves generate activity entries but not
content versions, so the panel appeared empty for most items.

Changes:
- Add GET /items/{itemSlug}/activity endpoint to fetch per-item activity
- Update VersionHistory component to fetch both versions AND activity,
  merging them into a unified timeline sorted newest-first
- Deduplicate entries where a version and activity share the same timestamp
- Collapse rapid content autosave bursts (within 5 min) to reduce noise
- Content version entries are expandable with diff view and restore
- Activity entries show field changes, status transitions, moves, etc.
2026-03-28 19:11:44 -04:00