Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Mission Control — the table, the views and the board (part 2)

Part 2 of the Mission Control page: the Main Table and everything you can do to it — filters, grouping, the alternate views over the same items, the Kanban board's card actions, colouring and swimlanes, finding a teammate, and the per-board preferences you can set.

What it is

This is part 2 of the Mission Control page. It covers the board itself: the table, the views, and the controls around them.

Where to find it

Inside Mission Control, on any board — the view switcher and the filter toolbar sit at the top of it.

How it behaves

Main Table (Phase 2)

The board route (/board/:id) renders BoardView, which loads everything in one call — GET /boards/:id/full → { board, groups, columns, items, values } — and draws a custom grid (no heavy table lib): a column header (with a Column Center + to add any of the essential column types), then each group (colored strip, collapse) as item rows, an "+ Add item" row, and a group footer of per-column summaries. Each cell is driven by a frontend column registry (features/board/columns/) that maps a column type to a renderer/editor — the status cell is a colored pill with a label picker + Edit Labels; numbers/text/date/checkbox/timeline/people/dropdown have inline editors.

The backend's core primitive is the column-type registry (src/lib/column-types/ in amc-back): one handler per type owning defaultSettings / validate / serialize / toText / summarize. columns rows store type + settings (JSONB); column_values rows store value (JSONB) + a denormalized text. Writing a cell (PUT /items/:id/columns/:colId) validates the value through the registry (HTTP 400 on a bad value), so a status value must match one of its column's labels. New backend modules — groups, columns (the Column Center), items + column-values — are account-scoped through the board's workspace via a shared ownedBoard helper.

Board filter toolbar (post-Phase-4 fast-follow)

A pixel-near rebuild of monday's Main-Table toolbar, replacing the plain Phase-3c controls with monday's real filter surfaces (features/board/BoardControls.tsx + features/board/toolbar/). The row reads as one control set — New task · Search · Person · Filter · Sort · Hide — over the shared ToolbarButton shape (px-2.5 py-1 text-sm, 1px border, foreground text idle → accent when active). All logic is a pure module (features/board/boardQuickFilter.ts + codec), compiled to a predicate in BoardView and applied client-side over the loaded board data (no backend).

  • Board search — collapsed it's a plain toolbar button (search glyph + "Search"); clicking expands it in place into a ~240px "Search this board" input (accent border) that owns the query. Escape clears + collapses, an empty blur collapses, a non-empty value keeps it open.
  • Person filter — a toolbar button (accent-tint fill when active) opening a searchable list of teammates; multi-selecting members filters items by the people assigned to them. It originally rendered every member as a bare grid of avatars with no search box, which stopped being usable somewhere around a dozen people; it now uses the shared teammate picker, so it is searchable and keyboard-operable like every other place you pick a person.
  • Quick filters panel — the Filter split-button (funnel + "Filter", showing "Filter / {n}" once selections exist; accent-tint fill when the panel is open or n > 0) opens a wide panel of per-column facet columns, each a scrollable list of multi-select chips. Chips are OR within a column, AND across columns. The panel stays open across chip clicks (only a click-away closes it); a header "Showing X of Y tasks" count and a Clear all reset are always present.
  • Save as new view — both the Person popover and the Quick filters panel offer "Save as new view" (enabled only when a real handler exists and something is filtered). It persists the current filter set as a new saved view tab (a views row) via the existing view system. Edits to filters while viewing an already-saved view are session-only — they don't mutate the saved view until you save again as a new one. The save is guarded against double-submit.

Pure filter/codec logic is unit-tested (tests/unit/plugins/mission-control/board-quick-filter.test.ts); the toolbar components have component coverage (search-board-button, person-filter-button, quick-filters-panel under tests/unit/plugins/mission-control/).

Finding a teammate — one search, everywhere

Every place Mission Control asks you to pick a person — assigning an item, @-mentioning someone in an update, adding someone in Permissions, choosing an approver, and the toolbar's Person filter — now answers a query the same way.

They did not used to. Each of those five was written separately and they disagreed about what a search even searches: assigning matched a name, email or role; @-mentions matched the name only; Permissions matched the name only; the approver field matched name or email; and the Person filter had no search box at all. The visible consequence was that typing a teammate's email found them when you were assigning work and found nothing when you were mentioning them, and typing a role worked on exactly one screen.

All five now share one rule (matchTeammates in src/shared/teammate.ts): it searches display name, handle, email and role, and ranks exact matches above ones that start with what you typed, above ones that merely contain it. Every picker has a search box and responds to ↑ / ↓ / Enter / Escape. A picker whose member list failed to load now says so, instead of showing "No matching people" as though your search simply missed.

The same rule backs Team Chat's @-mention typeahead and a main-app picker (components/ui/TeammatePicker), so a feature that needs "let the user pick a teammate" has one to reach for rather than writing a sixth. Accent handling is passed in per surface rather than baked in, because the main app and the plugin fold accented characters differently on purpose.

The promises this locks in — and the one thing deliberately left out of it, the fact that Mission Control and Team Chat still identify the same human by two unreconciled ids — are written up in .claude/memory/contracts/teammate-primitive-contract.md.

Roadmap view (post-Phase-3 addition)

The Roadmap tracker view (RoadmapView.tsx) is a matrix of time-bucket columns (e.g. quarters/months) crossed with status rows. Each item lands in the cell where its date column's period meets its status value, giving a bird's-eye view of items across time horizons. Listed as { type: 'roadmap', fallback: 'Roadmap tracker' } in ADDABLE_VIEWS (features/board/views/ViewSwitcher.tsx). Like the other alternate views, it is a config-only view over the loaded board data — no backend change required.

Location column + Map view (Ollert Port WI-6)

The Location column type ('location' in PmColumnType) stores a geographic position as { lat, lng, label } (the LocationCellValue interface in columnValue.ts). Geocoding is powered by Nominatim/OpenStreetMap (free, no API key; User-Agent: Omniscio/1.0). The cell (LocationCell.tsx) shows a MapPin icon + truncated place name; clicking opens a search combobox (LocationForm.tsx) with debounced type-ahead, ARIA combobox pattern (arrow keys, Enter, Escape), and a "Remove location" action when a value is set. Listed in the Column Center's "Super useful" section.

The Map view (MapView.tsx, { type: 'map', fallback: 'Map' } in ADDABLE_VIEWS) plots items with location values as circle markers on an interactive Leaflet.js map with OpenStreetMap tiles. Finds the board's first location column and builds a LocatedItem[] from items+values. Markers use the board's accent color, show item title + place label as tooltips (XSS-safe via textContent), and clicking a marker opens the item detail. The viewport auto-fits to all markers on load (with signature-based dedup to avoid re-fitting on unrelated data changes). Two empty states: "no location column" (add one first) and "no items with locations" (overlay on the map). An SR-only item list provides keyboard/screen-reader access. CSP: connect-src includes nominatim.openstreetmap.org; img-src already covers https: for OSM tiles.

Feature catalog: col-location (column node) and view-map (view node, requires edge to col-location in the feature graph). PM feature catalog: view-map in the operations preset with dependencies: ['col-location'].

Workload view

The Workload view (WorkloadView.tsx, { type: 'workload' } in ViewSwitcher.tsx's ADDABLE_VIEWS) shows per-person capacity utilization across a board. It groups items by a People column (auto-detected or configurable via groupByColumnId), sums each person's effort from an optional numeric effort column (effortColumnId; a new Workload view seeds it from the board's first numbers column, and with no effort column set every item counts as 1), and renders a horizontal bar per person against a configurable capacity (capacityHours, default 40h/week). Over-capacity bars turn red (--status-error-rgb) with a translucent overflow overlay. A tick mark on each bar shows where the capacity threshold sits relative to the widest bar. The WorkloadSummary header (sticky) shows the capacity input and which column drives effort. PersonAvatar renders initials in accent-colored circles. Unassigned items (no people-column value) are grouped in a collapsible section at the bottom. Each person row is expandable to list individual items (clicking an item opens its detail via onOpenItem). Empty states: "no people column" (add one first) and "no items" (generic). Read-only mode hides the capacity input.

Number formatting, Time Tracking, and Checklist columns

Three column additions to the Column Center catalog. (1) Number formatting — the existing numbers column gained per-column format settings: plain (locale-aware decimals), currency (configurable symbol + before/after position), or percent; configured from a hover-revealed number-format button that opens the NumberFormatForm popover in NumbersCell.tsx. The pure formatting function formatNumber() lives in web/lib/columnValue-format.ts (re-exported by columnValue.ts). (2) Time Tracking ('time_tracking' in PmColumnType) — reads elapsed time from the time_entries table via session-bridge-time.ts (the cell imports the bridge directly, not via CellProps). Shows compact duration (e.g. 2h 15m) with a green dot when a timer is running; the running row ticks via a 1s setInterval (no grid-wide re-render). Start/stop toggle with a pending latch to prevent double-fire. (3) Checklist ('checklist' in PmColumnType) stores { items: [{text, done}] } as JSON. The grid shows a progress pill (mini bar + done/total); clicking opens an inline popover editor with checkable items, text editing, remove buttons, and an Enter-to-add input. Auto-commits on every change. Checklist registers a checklistSummary (aggregate completion %) in the frontend registry; Time Tracking registers no summary. All three render in render/mc-cell-renderers.ts for HTML snapshots. Frontend-only; backend column-type registry handlers are a separate follow-up.

Kanban card actions + "⋯" menu (post-Phase-4 fast-follow)

Hovering a Kanban card reveals monday's two actions in the top-right: a pencil (opens the item card) and a ⋯ overflow menu (features/board/views/KanbanCardMenu.tsx, on the shared Popover). The menu mirrors monday's real order: Open task · Move to ▸ · Duplicate ▸ · Copy name · Copy task link · Add subitem · Customize cards ▸ · Archive · Delete (Delete confirms first). "Move to" and "Duplicate" open right-side flyout submenus (features/board/CardMenuFlyout.tsx — SubmenuRow/FlyoutItem, portaled out of the menu so they aren't clipped, flipping left near the screen edge): Move to → Move to group (lists the board's other groups → patchItem({groupId})) or Move to board (lists other boards from /nav/tree → a new POST /items/:id/move-to-board, which reassigns the board + a target group and drops the board-scoped column values); Duplicate → without / with updates (the existing POST /items/:id/duplicate, with ?includeUpdates=true also cloning the conversation thread). Copy name / Copy task link write to the clipboard (the link is an omniscio://mission-control/board/<id>?item=<id> deep link; BoardView opens an item from a ?item= param on load).

Subitems are real item rows tagged with parent_item_id (no migration — the column already existed). GET /boards/:id/full now returns top-level rows in items and child rows separately in subitems, so every other view is unchanged; the Kanban card renders its subitems as nested name-only mini-cards with a footer count badge and an inline "+ Add sub-task" composer (POST /items/:id/subitems), which the menu's Add subitem row opens and focuses. Backend behavior is covered by amc-back's item-card-actions bun test (a package not tracked in this repo); the subitem grouping helper is unit-tested (groupSubitemsByParent in tests/unit/plugins/mission-control/kanban-data.test.ts).

Kanban card coloring by field value

Not wired in the app today. Cards render with no colour stripe. KanbanCard.tsx accepts an optional colorHex prop and, when it is set, draws a 4px left border from it — but nothing computes or passes that prop (KanbanBoard.tsx renders <KanbanCard> without it), and KanbanToolbar.tsx (a "Color by" Select over the board's status and priority columns) is exported but mounted by no view. ViewConfig in web/lib/types-board.ts carries no colorByColumnId key, so a per-view colour choice could not be persisted either.

Kanban swimlanes (second grouping axis)

The Kanban view gained a swimlane feature — a second row-level grouping axis perpendicular to the status columns. A "Swimlane by" dropdown in the Kanban toolbar (SwimlanePicker → the Select component, persists as ViewConfig.swimlaneColumnId) lets users group items horizontally by any status, priority, people, dropdown, or checkbox column; "None" disables swimlanes. When active, items stack into collapsible KanbanSwimlaneRow sections — each a filtered KanbanBoard with its own DnD context (namespaced via id prop, no cross-swimlane drag in V1). Items with no value land in a "No value" lane at the bottom; multi-assignee items (people column) appear in each assignee's lane. Subitems follow their parent's lane. The swimlane column picker excludes the current groupByColumnId to prevent the useless same-column-both-axes case. A newly created Kanban view is seeded with groupByColumnId pointing at the board's first status or dropdown column (useBoardViewActions.ts). Data helpers: computeSwimlanes() + SWIMLANE_GROUPABLE_TYPES in kanbanData.ts; unit-tested in tests/unit/plugins/mission-control/kanbanData.test.ts.

Grouped search (Phase 4a, extended)

Search Everything originally returned items only, grouped by board. A parity fast-follow broadened it to four result types — items, boards, dashboards, and people — grouped by type. Private boards you're not a member of are excluded (both the board results themselves and any items on them; dashboards and people are workspace/account-scoped and unaffected), consistent with the private-board enforcement above.

Board customization features (desktop-only)

Six board-level customization features shipped as part of the Ollert Port work items. All are desktop-only — their IPC channels are not exposed as CLI routes and are blocked from the mobile/web WS bridge.

  • Celebrations (pm:celebrations:get/set) — confetti animation on status completion
  • Accent color (pm:accent-color:get/set) — per-board accent color override
  • Voting (pm:vote:toggle, pm:vote:data) — item-level voting/polling
  • Board background (pm:board-background:set) — custom board background image/color
  • Card aging (pm:card-aging:get/set) — visual aging indicator on stale cards
  • Stickers (pm:item-stickers:list/add/update/remove, pm:item-stickers:changed push) — decorative item stickers

IPC: 14 channels total (see src/shared/ipc-channels/pm.ts), all desktop-only. CLI-parity: classified as feature-scoped-surface exemptions (board-level visual settings with no external integration need).

Board favorites (parity fast-follow)

The star in the board-info popover is now wired to real favorites — starring a board persists it as a favorite (it was previously a dead control).

Stub cleanup (no dead buttons)

Every affordance that did nothing was either wired up or removed, so a control the user can see is a control that works. Wired: the view menu's Share item now opens the board share modal (access is per-board, not per-view, so it's labelled "Share board"); the board nav-row's Add to favorites now toggles the shared FavoritesProvider; and User management gained a real Filter menu (features/users/UserFilterMenu.tsx) — client-side status + role facets over the already-loaded member list, where an empty facet means "no constraint" and the two facets AND together. Removed (each needs a subsystem nobody has built): Save as template (board nav + workspace ⋯), My Work's Customize, the Gantt toolbar's Baseline/filter/⋯ affordances, the dashboard widget's Dock this widget, the workspace Feedback button (the plugin has no host bridge to Omniscio's intake), and the board-info popover's fake "🔔 Everything" notifications row. The Apps widget left the Add-widget menu, but 'apps' stays in the persisted WidgetType union and WidgetCard still renders a placeholder body for it, so a dashboard that already saved one keeps working — it just can't be added again.

Sub-pages

Related

Last verified 2026-10-06