Skip to content

feat!: web UI, multi-binary split, worktrees, drag-and-drop, e2e tests#23

Merged
hmans merged 64 commits intonextfrom
beans-serve
Mar 8, 2026
Merged

feat!: web UI, multi-binary split, worktrees, drag-and-drop, e2e tests#23
hmans merged 64 commits intonextfrom
beans-serve

Conversation

@hmans
Copy link
Owner

@hmans hmans commented Dec 20, 2025

Summary

Major evolution of the beans web UI and project architecture. This PR started as a web server command and grew into a comprehensive set of features.

Multi-Binary Architecture

  • Split into three executables: beans, beans-serve, beans-tui
  • Each binary defaults to its primary subcommand (no need for beans-serve serve)
  • Deprecated command hints: beans serve and beans tui print migration messages
  • Removed wgo dependency

Web UI

  • Two-pane layout with hierarchical bean list and detail view
  • Tabbed views: Backlog (list) and Board (kanban)
  • Board view with drag-and-drop reordering and status changes
  • Fractional indexing for manual bean ordering
  • Bean create/edit form with route-based views and URL-persisted selection
  • Markdown rendering with Shiki syntax highlighting (essential languages only)
  • Real-time updates via GraphQL subscriptions over WebSockets
  • Custom Tailwind v4 theme (replaced DaisyUI)
  • Completed lane on board view
  • Optimistic drag-and-drop with no-layout-jump drop indicators
  • Clickable relationship beans in detail view

Git Worktree Support

  • "Start Work" button creates a git worktree for a bean
  • Worktree tabs appear in the nav bar
  • Close worktree button to clean up

GraphQL Enhancements

  • Subscriptions with includeInitial parameter and INITIAL_SYNC_COMPLETE event
  • BeanChangeEvent type: INITIAL, CREATED, UPDATED, DELETED
  • Configurable server port via CLI flag or .beans.yml

E2E Testing

  • Playwright test suite with page object model
  • Per-test isolation: each test spawns its own beans-serve with a unique port and temp directory
  • Parallelized with 4 workers
  • Tests cover backlog sorting, board column placement, real-time re-sorting on filesystem changes

Frontend Sorting Consistency

  • Frontend sortBeans() mirrors backend's SortByStatusPriorityAndType (status → order → priority → type → title)
  • Both backlog and board views sort consistently with the CLI

Test Plan

  • mise test — all Go tests pass
  • mise test:e2e — all 9 Playwright tests pass (parallelized)
  • beans serve / beans tui print deprecation messages
  • beans-serve and beans-tui default to their primary commands
  • Web UI loads, displays beans hierarchically, supports drag-and-drop
  • Real-time updates work via WebSocket subscriptions
  • Worktree creation and tab display work
  • Board view correctly places beans in columns and re-sorts on changes

hmans and others added 30 commits March 7, 2026 23:18
- Add cmd/serve.go with HTTP server serving GraphQL API
- GraphQL endpoint at POST /graphql
- GraphQL Playground at GET /graphql for interactive queries
- Supports --port/-p flag (default 8080)
- Graceful shutdown on SIGINT/SIGTERM
- Plan web UI feature with subtasks for future implementation
- Add internal/web/ package with go:embed for frontend assets
- Serve embedded SPA from beans serve at /
- Handle SPA routing (unknown paths serve index.html)
- Add mise tasks: build:frontend, build:embed
- Build pipeline now includes frontend compilation
- Use signal.NotifyContext for cleaner signal handling
- Properly deregister signal handlers on exit
- Add gin.Logger() middleware to log HTTP requests
- Add Subscription type with beanChanged field to schema
- Add BeanChangeEvent type and ChangeType enum (CREATED/UPDATED/DELETED)
- Implement subscription resolver using beancore.Subscribe()
- Add WebSocket transport to serve command for subscription support
- Start file watcher automatically when server starts

Usage: Subscribe to bean changes via GraphQL:
```graphql
subscription {
  beanChanged {
    type
    beanId
    bean { id title status }
  }
}
```

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- BeansStore class with $state for reactive beans map
- Load all beans via GraphQL query
- Helper methods: byStatus, byType, children, blockedBy
- Uses SvelteMap for proper reactivity
- Singleton export for app-wide usage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Display all beans in a card layout
- Show id, type, status, title, tags, and date
- Color-coded status and type badges
- Loading and error states

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add subscribe() method to BeansStore
- Handle CREATED/UPDATED/DELETED events from beanChanged subscription
- Show "Live" indicator when connected
- Clean up subscription on component destroy
- Add wonka dependency for urql streams

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Configure gqlgen with explicit transports (WebSocket first for upgrade handling)
- Add graphql-transport-ws subprotocol for graphql-ws client compatibility
- Frontend connects WebSocket directly to backend in dev (bypasses Vite proxy)
- Force-close connections if graceful shutdown times out
- Add CORS middleware for development
Add includeInitial argument to beanChanged subscription that emits all
current beans on connect as INITIAL events, followed by real-time
CREATED/UPDATED/DELETED changes. This eliminates race conditions between
loading and subscribing.

- Add includeInitial argument (default false) to beanChanged subscription
- Add INITIAL to ChangeType enum for initial state events
- Update resolver to emit all beans with INITIAL type when includeInitial=true
- Simplify frontend to use single subscribe() call instead of load()+subscribe()
Display beans in a tree structure matching CLI output:
- Top-level beans shown at root
- Children indented with visual tree lines
- Color-coded left border by type (milestone, epic, feature, bug, task)
- Shows child count on parent beans
- Recursive BeanItem component for unlimited nesting depth
- Two-pane layout: bean list on left, detail view on right
- Draggable separator with localStorage persistence (200-600px range)
- Compact bean list items with short ID, title, status badge
- Detail view shows: metadata, tags, relationships, body, timestamps
- Click bean to select and view details
- Selected bean stays in sync with real-time updates
- Add shiki package for syntax highlighting
- Create markdown.ts utility with lazy highlighter initialization
- Use github-dark theme for code blocks
- Add styling for code blocks and inline code
- Preload highlighter in layout for faster first render
- Remove header for cleaner layout
- Make separator more subtle with gray tones
- Fix HTML comment syntax
hmans added 5 commits March 7, 2026 23:19
- Add onSelect callback prop to BeanDetail component
- Render parent, children, blocking, and blocked-by as compact card-style
  buttons matching the left pane's BeanItem style
- Wire up selectBean in +page.svelte so clicking navigates to that bean
- Replace hardcoded port 22880 with BEANS_PORT env var (default: 22880)
- Replace hardcoded Vite port with VITE_PORT env var (default: 5173)
- Update vite.config.ts proxy target to read from BEANS_PORT
- Simplify graphqlClient.ts WebSocket URL to use window.location.host,
  routing through Vite's WS proxy instead of direct backend connection
- Update conductor.json to use $CONDUCTOR_PORT for backend and
  $CONDUCTOR_PORT+1 for frontend via mise dev
hmans added 15 commits March 7, 2026 23:26
- Add tab navigation to main view (Backlog / Board)
- Backlog tab: existing tree list + detail pane layout
- Board tab: kanban columns for Draft, Todo, In Progress
- Drag-and-drop between columns to change bean status via GraphQL mutation
- Visual feedback: drop target highlight, grab cursor, opacity on dragged card

Refs: beans-d91u
- Add daisyui v5 as a dependency
- Replace custom tab bar with DaisyUI tabs (tabs-border)
- Use DaisyUI badge variants for status/type/priority indicators
- Use DaisyUI card components on board view
- Use DaisyUI alert for error messages, btn for buttons, divider
- Replace hardcoded colors with semantic tokens (base-100, base-200, base-content)

Refs: beans-d91u
…isted selection

- Add BeanForm component (modal) for creating and editing beans
- Extract shared UI state into uiState.svelte.ts singleton
- Split Backlog and Board into separate SvelteKit routes (/ and /board)
- Reflect selected bean in URL as ?bean=<id> query param
- Preserve bean selection when switching between views
- Add Edit button to BeanDetail header
- Add "+ New Bean" button in nav bar
- Board cards use cursor-pointer
- Remove outdated page test

Refs: beans-z4ry
- Add internal/worktree package with Manager (List/Create/Remove + pub/sub)
- Extend GraphQL schema with Worktree type, query, mutations, subscription
- Implement worktree resolvers (createWorktree sets bean to in-progress)
- Add frontend WorktreeStore with real-time WebSocket subscription
- Add "Start Work" button to BeanDetail component
- Add worktree tabs to layout nav bar
- Create worktree route page (/worktree/[id]) with detail pane
- Configure adapter-static fallback for SPA routing

Refs: beans-dtaq
- Add removeWorktree method to WorktreeStore
- Add close (x) button on each worktree tab in nav bar
- Navigate back to Backlog when closing the active worktree

Refs: beans-dtaq
- Add internal/bean/fractional.go with base-62 fractional index generation
- Add Order field to Bean struct, frontmatter parsing, and rendering
- Update sort to use Order as primary key within each status group
- Add order field to GraphQL schema (Bean type + UpdateBeanInput)
- Add frontend fractional.ts (TypeScript port of the algorithm)
- Update BoardView with drag-and-drop reordering within and across columns
- Auto-assign order keys to unordered beans on first drag in a column
- Show drop position indicator line between cards

Refs: beans-vsfv
- Add optimisticUpdate method to BeansStore for instant UI feedback
- Make ensureOrdered/onDrop synchronous with optimistic store updates
- Replace drop indicator divs with box-shadow on cards (zero layout space)
- Use var(--color-primary) for DaisyUI v5 compatible drop indicator color

Refs: beans-vsfv
- Drain all event batches instead of expecting exactly one
- Verify final state from core rather than event snapshot
- Prevents flaky failures on slow CI where debounce fires between writes
- Remove daisyui dependency from package.json
- Define custom design tokens in layout.css via @theme (surface, border,
  text, accent, danger, warning, success, info)
- Replace all DaisyUI component classes (btn, card, badge, modal, tabs,
  form-control, input, select, textarea, alert, loading, divider) with
  plain Tailwind utility classes
- Replace DaisyUI color tokens (base-100/200/300, base-content, primary)
  with semantic custom tokens
Replace box-shadow drop indicators with permanent 2px bars between
cards that are transparent by default and colored on drop target.
Eliminates layout shifts entirely.
…s-tui)

- Move cmd/ to internal/commands/ as shared command package
- Convert init() auto-registration to explicit Register*Cmd(root) functions
- Create cmd/beans/, cmd/beans-serve/, cmd/beans-tui/ entrypoints
- Add internal/version/ package for shared version variables
- Update mise.toml build tasks and .goreleaser.yaml for three binaries
- Delete root main.go in favor of per-binary entrypoints

Refs: beans-unmv
- beans serve/tui now prints a message directing users to beans-serve/beans-tui
- beans-serve and beans-tui default to their respective subcommands (no need to type beans-serve serve)

Refs: beans-unmv
@hmans hmans changed the base branch from main to next March 8, 2026 15:17
hmans added 2 commits March 8, 2026 16:19
- Add sortBeans() to frontend store matching backend's SortByStatusPriorityAndType
- Add data-status attribute to board columns for reliable test selectors
- Set up Playwright with page objects (BacklogPage, BoardPage)
- Add 9 e2e tests: sorting, real-time re-sort, column placement, deletion
- Wire e2e tests into mise test via test:e2e task
- Fix build warnings: suppress state_referenced_locally in BeanForm,
  use role="slider" for worktree drag handle, raise chunk size limit

Refs: beans-hww2
- Each test spawns its own beans-serve on a unique port with an isolated temp dir
- Pre-build binaries once in run.sh instead of compiling per-test
- Pass baseURL through fixtures to page objects
- Bump workers to 4
@hmans hmans changed the title feat: add beans serve command with web UI feat!: web UI, multi-binary split, worktrees, drag-and-drop, e2e tests Mar 8, 2026
@hmans hmans merged commit 84b4afc into next Mar 8, 2026
1 check passed
@hmans hmans deleted the beans-serve branch March 8, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant