Commit Graph

77 Commits

Author SHA1 Message Date
Bohan Jiang
bf6509be96 fix(issues): show labels in my-issues view + place chips after title (#1743)
- my-issues page lost labels because myIssuesViewStore cherry-picked
  name/storage/partialize from viewStorePersistOptions and dropped the
  cardProperties-aware merge. Persisted snapshots predating the labels
  toggle had cardProperties.labels = undefined, falsy-shorting the chip
  render. Extracted mergeViewStatePersisted as a generic and wired it
  into both stores.
- list-row chips now render right after the title (with a small left
  margin for breathing room) instead of in the right-aligned cluster.
2026-04-27 16:50:13 +08:00
Bohan Jiang
6620997503 feat(issues): render labels on list/board with bulk server-side fetch (#1741)
* feat(issues): render labels on list/board with bulk server-side fetch

ListIssues / ListOpenIssues / GetIssue now bulk-fetch labels per response
via a new ListLabelsForIssues query so the client gets labels in a single
round-trip instead of N requests per visible issue. List-row and board-card
read issue.labels directly; an issue_labels:changed WS handler patches the
list and detail caches in place so chips stay live across tabs, and
attach/detach mutations mirror their result into the same caches for
immediate same-tab feedback.

Adds a "Labels" toggle to the card properties dropdown (defaults on).

* fix(issues): preserve cached labels and refresh on label edit/delete

Three fixes from gpt-boy's review of #1741:

1. IssueResponse.Labels was a non-omitempty slice, so paths that didn't
   load labels (UpdateIssue, batch updates, the issue:updated WS broadcast)
   serialized labels:null. onIssueUpdated then merged that null into the
   list/detail caches, wiping chips on every other tab whenever any non-
   label field changed. Switched to *[]LabelResponse + omitempty: nil =
   field absent (client merge keeps existing labels); non-nil (incl. empty
   slice) = authoritative.

2. issue.labels is a denormalized snapshot, but useUpdateLabel /
   useDeleteLabel and the WS label:* prefix only touched labelKeys, leaving
   stale chips in list/board after rename/recolor/delete. Mutations now
   also invalidate issueKeys.all(wsId), and the realtime refreshMap maps
   the label prefix to both labels and issues invalidation for cross-tab.

3. Persisted cardProperties from before this branch lacks the new `labels`
   key. Render fell back to `?? true` but the dropdown switch read it raw
   and showed unchecked. Added a custom Zustand merge that deep-merges
   cardProperties so newly added toggles inherit defaults for existing
   users; dropped the `?? true` fallbacks now that the store guarantees
   the key.
2026-04-27 16:33:34 +08:00
Naiyuan Qing
e268ee3e71 refactor(views): centralize project icon rendering and fix nav active state (#1738)
Extract <ProjectIcon> with sm/md/lg sizes and a single 📁 fallback,
replacing 9 inline render sites that had drifted into 6 different
sizes and a mixed FolderKanban/emoji fallback.

Two visible fixes fall out of the centralization:
- ProjectPicker trigger now shows the selected project's icon (most
  visibly in the issue detail right Properties panel, where it had
  always been a generic FolderKanban).
- Sidebar parent nav (Projects, Issues, Settings, ...) now stays
  highlighted on child detail routes via a small isNavActive helper.
  Pinned items keep strict equality.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:42:56 +08:00
Ayman Alkurdi
e9d04ecfc1 feat(labels): ship issue labels (closes #1191) (#1233)
* feat(labels): add issue label CRUD + attach/detach handlers (#1191)

The issue_label and issue_to_label tables were scaffolded in 001_init.up.sql
but never wired to any code path. This commit ships the backend for #1191:

- Migration 048: adds created_at/updated_at timestamps + workspace-scoped
  case-insensitive unique index on label names
- sqlc queries for label CRUD + issue<->label attach/detach + batch list
  (ListLabelsByIssueIDs for board/list views)
- HTTP handlers: /api/labels CRUD, /api/issues/{id}/labels attach/detach
- Protocol events: label:{created,updated,deleted} + issue_labels:changed
- Handler tests covering CRUD, duplicate-name conflict, invalid-color,
  attach/detach idempotency, and cross-workspace isolation

* feat(cli): add label and issue label subcommands (#1191)

- multica label {list,get,create,update,delete}
- multica issue label {list,add,remove}

Both follow existing CLI conventions (JSON/table output, flag shapes)
and exercise the /api/labels endpoints shipped in the previous commit.

* feat(web): add labels UI — picker with inline create + management dialog (#1191)

Exposes the backend label feature to users via the existing issue-detail
sidebar.

- `@multica/core/types/label` — Label, CreateLabelRequest, UpdateLabelRequest,
  plus response envelopes
- `@multica/core/api/client` — 8 methods for label CRUD and issue↔label
  attach/detach
- `@multica/core/labels` — labelKeys, queryOptions, and mutation hooks with
  optimistic updates (matches the project/ module layout)
- WS event type literals extended for label:{created,updated,deleted} and
  issue_labels:changed

- `views/labels/label-chip.tsx` — colored pill; uses relative luminance
  (ITU-R BT.601) to pick #111827 or #f9fafb text so chips stay readable on
  both pastel and saturated backgrounds
- `views/issues/components/pickers/label-picker.tsx`
  - Multi-select combobox in the issue sidebar
  - When 0 labels: "Add label" trigger
  - When 1+ labels: the chips themselves are the trigger; × on each chip
    detaches without opening the picker
  - Inline create: typing a new name + Enter creates with a hash-derived
    color and attaches in one motion (matches Linear/GitHub)
  - "Manage labels…" footer opens a dialog containing the full workspace
    panel — users never leave the issue context to rename/recolor/delete
- `views/issues/components/labels-panel.tsx` — workspace labels manager.
  Single-row create form (color swatch + name + Add button). Each label
  row supports inline rename + recolor + delete (with confirm dialog).
  Color input uses the browser's native picker for full-gamut access —
  no preset palette clutter.

- `PropRow label="Labels"` added to the issue-detail sidebar below Project

Labels are issue metadata everyone uses — not admin configuration.
Putting them in Settings next to destructive workspace actions misframed
them; adding a top-level nav entry or a sibling tab to the Issues page
added surface area that wasn't earning its keep for a feature users
touch occasionally. Keeping management in a dialog launched from the
picker itself keeps users in their issue context and matches how GitHub
handles label editing from the label selector.
2026-04-27 14:23:42 +08:00
Naiyuan Qing
5bab95ad26 fix(issues): unify board card hover and active visual (#1603)
Hover and popup-open states now share the same bg-accent + border-accent
treatment. Drop the shadow-md hover (invisible in dark mode) and the
multi-property transition in favor of a single transition-colors.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:55:30 +08:00
Naiyuan Qing
0bd6ba9354 fix(issues): cleaner board card hover with shadow elevation (#1600)
Replace translucent tinted hover (border-accent/50 + bg-accent/20) with
a single-dimension shadow lift. The previous overlay was visually weak
because --accent is nearly identical to --card, so a 20% tint rendered
as almost no change. Active (popup-open) state now uses solid bg-accent
so hover and active are distinguished by different dimensions —
elevation vs color — instead of competing on the same axis.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:15:07 +08:00
Naiyuan Qing
d54daa62c5 feat(issues): right-click context menu + unified issue actions (#1594)
* feat(issues): add right-click context menu on list rows and board cards

Extract the detail page's ⋯ dropdown (~180 lines of inline JSX) into a
shared `useIssueActions` hook plus two thin wrappers so the same action
set (status / priority / assignee / due date / sub-issue ops / pin / copy
link / delete) can be mounted as both a DropdownMenu and a Base UI
ContextMenu. Right-click on any list row or board card now opens the
full action menu without entering the detail page.

Shell-level modals replace the detail-page-local state for set-parent /
add-child / delete-confirm / backlog-agent-hint, so any trigger (detail
page, list, board) can open them through `useModalStore`. Detail page
detects its own deletion via a query-transition effect, avoiding the
need to smuggle callbacks through the store.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(issues): hover and active styling on list rows and board cards

Mirror the sidebar's same-color/different-intensity pattern for the new
right-click context menu states. Base UI adds `data-popup-open` to the
ContextMenuTrigger when the menu is open; `hover:not-data-[popup-open]`
suppresses hover feedback on the already-active item.

List rows apply the pattern directly to background color (`accent/60`
hover, `accent` active). Board cards additionally modulate the card's
border and a lighter background tint (`accent/20` hover, `accent/40`
active) so the card's own bg/border/shadow identity stays intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(modals): show target issue banner in SetParent/AddChild pickers

When triggered from an issue's action menu, the IssuePickerModal now
displays a banner at the top showing "Setting parent of" / "Adding
sub-issue to" followed by the originating issue's status, identifier,
and title. Previously the operation target was only implied by the
modal's sr-only title.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(modals): create-issue gains ⋯ overflow menu with parent issue linkage

Add a dropdown-menu with "Set parent issue..." / "Remove parent" at the
end of the property pill row. The ⋯ button is always the last DOM child
of the row so it stays at the tail even when the row wraps to multiple
lines. Menu state reflects current selection — unset shows a single
"Set parent…" entry, set shows the current identifier plus a separate
Remove option.

When a parent is set (either via the new menu or via `data.parent_issue_id`
from a "Create sub-issue" trigger), a chip appears in the pill row
showing "Sub-issue of {identifier}" with the same click-to-change /
click-×-to-clear semantics. This replaces the old header breadcrumb
disclosure that was neither editable nor visible in the form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(issues): group relationship actions under "More" submenu

Nest Create sub-issue / Set parent issue / Add sub-issue inside a
`More >` submenu in the issue actions menu (both Dropdown and
Context variants). Top-level keeps Status/Priority/Assignee/Due date
category submenus plus Pin and Copy link; the relationship ops are
lower-frequency and will grow with future relation types (blocks,
duplicates, related) that fit the same category.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(modals): create-issue adds Add sub-issue with deferred linking

The create modal's ⋯ menu gains an "Add sub-issue..." entry that queues
existing issues as children of the new one. Picked issues appear as
chips in the pill row (downward arrow, distinct from the upward parent
chip), each individually removable.

Linking is deferred because the new issue's ID doesn't exist at pick
time. Once createIssueMutation resolves, we run updateIssueMutation
for every queued child in parallel and surface any partial failures
via toast — the new issue itself is already committed and never rolls
back. Parent and child pickers exclude each other so a single issue
can't occupy both relations simultaneously.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* polish(issues): add MoreHorizontal icon to "More" submenu trigger

The "More" label was visually misaligned because every other top-level
entry has a leading icon. Use MoreHorizontal (same icon as the outer ⋯
trigger — semantically "more options, nested") and drop the `inset`
padding hack.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* revert(modals): drop target-issue banner from IssuePickerModal

The banner sat directly above the search input and rendered the target
issue with bolder styling than the "Setting parent of" / "Adding sub-issue
to" caption, which made it read like a pre-selected search result rather
than a context label. Users opening the modal from a menu item already
carry the context, so the extra chrome was redundant.

Remove the contextIssue / contextLabel API from IssuePickerModal and
drop the now-unused issueDetailOptions query in SetParentIssueModal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* polish(modals): exclude current parent from create-issue parent picker

Re-opening the parent picker to change the already-set parent used to
show that parent in the results — picking it was a silent no-op. Mirror
the child picker's exclude-list construction so the current parent is
always filtered out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:48:46 +08:00
Jiayuan Zhang
977b0c0558 feat(agents): show profile card on agent avatar hover (#1577)
* feat(agents): show profile card on agent avatar hover

Hovering an agent avatar now opens a preview card with name, status,
runtime mode + connectivity, model, skills, and owner. Wired through
the shared ActorAvatar wrapper so every render site gets it; opt-out
via disableHoverCard in pickers and the agent's own detail header
where the card would be redundant or interfere with click selection.

* fix(agents): keyboard-focusable hover card + opt out on settings avatar

- Make the agent profile-card hover trigger focusable (tabIndex=0 with
  visible focus ring), so keyboard users can open the card. Drops
  cursor-default so the trigger inherits the parent control's cursor
  instead of fighting it.
- Disable the hover card on the agent settings avatar — it's a
  click-to-upload target on the agent's own settings page, where the
  card would be redundant and the trigger conflicted with the upload
  affordance.

* fix(agents): scope hover-card tab stop to standalone avatars only

Detect a focusable ancestor (link/button/role=button/tabindex>=0) at
mount and only flip the agent profile-card trigger to tabIndex=0 when
none exists. Avatars rendered inside an existing focusable parent (issue
list rows wrapped in AppLink, button-style cards, etc.) keep the trigger
unfocusable so they don't add redundant nested tab stops or bloat
keyboard navigation. Standalone avatars (e.g. comment author, issue
detail meta) remain keyboard-accessible with a focus-visible ring.

---------

Co-authored-by: Lambda <f252c2c5-7d1d-4f3c-b394-a61abfe673fc@users.noreply.multica.ai>
2026-04-24 00:53:55 +08:00
Naiyuan Qing
044d1443b5 fix(issues): keep reply editor expand icon muted on focus (#1565)
The expand button relied on the parent row's inherited color, which
flipped to text-foreground via group-focus-within while the editor was
focused. The attach and submit buttons set text-muted-foreground on
themselves and stayed muted regardless of focus, so expand was the only
one changing color — inconsistent with the "default muted" convention
the other icon-buttons in this editor follow.

Give expand its own text-muted-foreground and drop the now-unused color
classes from the button row container.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:05:36 +08:00
Naiyuan Qing
502add4bd1 fix(issues): restore compact single-line reply editor, keep expand overlap fix (#1562)
#1558 fixed the expand button covering trailing text, but also collapsed
the reply editor's "empty = 1 line, has content = 2 lines" behavior by
making the button row a permanent flex sibling below the editor.

Restore the original absolute-positioned button row on both editors:

- comment-input: back to `pb-8` container + `absolute bottom-1 right-1.5`
  buttons (pre-#1558 layout; never had the overlap bug).
- reply-input: absolute buttons + `pb-7` gated on `!isEmpty || isExpanded`.
  Empty → single-line compact; any content → two-row layout with buttons
  below text (no overlap by construction).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 17:37:36 +08:00
Naiyuan Qing
efc08a1e37 fix(issues): stop expand button from covering text in comment/reply editors (MUL-1297) (#1558)
The comment and reply editors positioned their three trailing buttons
(expand, attach, submit) with `absolute` and relied on `pr-14` /
`pb-8` magic numbers to reserve space. The reserved 56px is smaller
than the actual 80px button row, so the leftmost button (expand)
visibly overlaps the trailing characters of a long line of text.

Restructure the button row as a normal flex sibling below the editor.
Text can no longer flow under the buttons, and the layout no longer
needs the `pr-14` hack, `pb-8` padding, or the ResizeObserver that
toggled `pb-7` when content overflowed.

Also align the expand button in comment-input with the reply-input
version (`h-6 w-6` + `h-3.5 w-3.5` icon) so the two entry points
match.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:38:04 +08:00
Naiyuan Qing
f84d216794 fix(views): restore issue-mention class on <a> for mention card (#1516)
PR #1502's IssueChip extraction moved the `issue-mention` class from the
outer <a> into IssueChip's inner <span>, breaking three consumers that
select on `<a>.issue-mention` directly:

- `.rich-text-editor a.issue-mention` underline-exemption in
  content-editor.css (stopped matching -> mentions in editor gained a
  spurious underline).
- `link-hover-card.tsx` classList check that suppresses the URL preview
  on issue mentions (stopped matching -> hover card wrongly pops up
  over mention chips).
- Tailwind Typography prose (`prose a { text-decoration: underline }`)
  covers a separate path — markdown bubbles in chat. prose's specificity
  (0,1,1) beats `.no-underline` (0,1,0), so `not-prose` is the right
  escape hatch on the AppLink.

Put `issue-mention` back on the <a> in both wrappers (IssueMentionCard
and the editor's MentionView), and add `not-prose` only to the markdown
wrapper. IssueChip's BASE_CLASS keeps `issue-mention` too (inert on the
span; removing it is a separate scope that needs a full consumer audit).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:13:14 +08:00
Naiyuan Qing
2d0916ee38 feat(chat): focus mode — share current page as context (#1502)
* refactor(views): extract IssueChip shared primitive from mention card

IssueMention (in editor NodeView) and IssueMentionCard shared 95% of their
markup — StatusIcon + identifier + title inside a bordered chip. They drifted
into two parallel implementations so changes had to be made in two places.

Extract the presentational chip into IssueChip. The navigable variants
(IssueMentionCard, the editor NodeView) become thin shells that layer
routing + cmd/shift behaviour onto the shared chip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat): add focus mode to share current page as context

Adds a Focus button next to the chat submit. When on, the chat auto-attaches
whatever the user is viewing (issue, project, or inbox-selected issue) as a
context prefix on outgoing messages, so the agent knows what "this" refers
to without the user pasting ids.

The attached object is derived from the route + react-query cache on every
render — no separate copy in state. Only the boolean focusMode is persisted
(global to the user, not per-workspace), matching the "my preference"
mental model.

The button has three visual states driven by two dimensions (focusMode +
whether the current route resolves to an anchorable object):
  - off:         ghost + muted, click turns on
  - on  + anchor: secondary (bright), click turns off
  - on  + none:  disabled (nothing to attach here)

The derived anchor renders above the input as a chip — IssueChip for issues,
a new ProjectChip for projects — wrapped in AppLink so the visual target
matches the clickable target (mirrors IssueMentionCard's hover + navigation).

Prefix format reuses the editor's mention markdown:
  Context: [MUL-1](mention://issue/<uuid>) — "Fix login bug"
  Context: Project "Authentication"

so the agent sees an identical token whether the user @-mentioned inline or
focus-mode attached. Backend is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:25:58 +08:00
Ark
26a2db2540 feat(transcript): add multi-select tool filter to agent execution dialog (#1460)
* feat(transcript): add multi-select tool filter to agent execution dialog

Adds a Filter dropdown to AgentTranscriptDialog that lets users
multi-select tool types (e.g. tool:Bash, tool:Edit) to narrow down
the event list, timeline bar, and copy output. Non-tool items (text,
thinking, error) are also filterable. Clear filters is placed at the
bottom of the dropdown with a separator.

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

* fix(transcript): address review comments on tool filter

- Replace index-based selection with seq-based (selectedSeq) to fix
  highlight/scroll jumping when toggling filters
- Use full tool count for the "X tool calls" chip (task-level stat)
  instead of filtered count
- Title-case filter labels: Thinking, Error (was lowercase)
- "Copy all" → "Copy filtered" when filter is active
- Replace raw <button> with DropdownMenuItem for Clear filters
  so it participates in keyboard navigation
- Drop redundant idx from row key (seq is already unique/stable)

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

* fix(transcript): use proportional width in timeline bar

After removing segment grouping, the timeline bar lost proportional
widths. Restore proportional width per item (1/items.length * 100%)
so each event's width reflects its share of the timeline.

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

* fix(transcript): show individual event dividers in timeline bar

When filtering by a single type (e.g. Agent), all events share the
same color and blend into one solid bar. Split each event into its own
clickable button so users can see and click individual events, while
keeping proportional widths based on item count.

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

* fix(transcript): simplify timeline bar to segment-level buttons

Remove per-item nested buttons in timeline segments; each segment is now
a single clickable area. Reduces DOM nodes and aligns with the original
design where segments are coarse color blocks.

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

* refactor(transcript): reuse getEventLabel for filter option display

Replace the manual displayMap with getEventLabel() so filter option
labels stay in sync with row labels automatically.

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

* fix(transcript): address round-2 review comments

- Remove dead `onClick` prop from TranscriptEventRow (caused
  TS6133 under noUnusedParameters; row never wired a click handler)
- Align `itemFilterKey` guard with `filterOptions` derivation
  (tool_use/tool_result type check)
- Fix TimelineBar `isSelected` from seq range to actual membership
  via `.some()` — avoids false highlight when a filtered-out seq
  falls within a segment's range

Note: `DropdownMenuItem` uses `onClick` not `onSelect` because this
codebase uses Base UI, not Radix. Base UI's Item.Props has no
`onSelect`; the inbox/members-tab code uses `onClick` as the pattern.

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

---------

Co-authored-by: Ark <lifangzhou@shizhuang-inc.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 14:41:46 +08:00
Bohan Jiang
642844c736 feat(issues): paginate every status column, not just done (#1422)
* feat(issues): paginate every status column, not just done

Previously the workspace issues list fetched all non-done/cancelled
issues in a single unbounded `open_only=true` request and only
paginated the done column. In workspaces with many open issues this
ballooned the initial payload and skipped pagination entirely.

Restructure the issue list cache into per-status buckets
(`{ byStatus: { [status]: { issues, total } } }`) fetched in parallel,
generalize `useLoadMoreDoneIssues` into `useLoadMoreByStatus(status,
myIssuesOpts?)`, and render an infinite-scroll sentinel inside every
accordion group and kanban column. Sort and filter stay client-side,
matching the done column's existing behavior.

Backend `ListIssues` already supports per-status pagination, so no
API changes are required.

* fix(issues): handle project / hidden-column / lookup regressions from paginated list cache

After bucketing the issue list cache by status, three consumers that
treated `issueListOptions()` as a complete local index broke:

- `project-detail.tsx` filtered the workspace list by `project_id`
  client-side, so projects whose issues sat past the first 50-per-status
  page rendered empty. Switch to `myIssueListOptions(wsId,
  'project:<id>', { project_id })` so the server returns only this
  project's issues; add `project_id` to `ListIssuesParams` /
  `MyIssuesFilter` / api client.
- `board-view.tsx` HiddenColumnsPanel read counts from the in-memory
  `issues` array — a paginated fragment. Pass `myIssuesOpts` through to
  a per-row subcomponent that reads the real per-status total from the
  cache.
- `tasks-tab.tsx` and `search-command.tsx` used the list as a global
  lookup for task titles / Recent items / current-issue chrome. Switch
  both to per-id `issueDetailOptions` via `useQueries` so they're
  independent of which page the issue lands on.

Drop the now-redundant `doneTotal` override prop on BoardView/ListView
and the `allIssues` prop on BoardView (only HiddenColumnsPanel consumed
it).

Tests updated: tasks-tab now mocks `api.getIssue`; search-command mocks
`issueDetailOptions` + `useQueries`; project-issue-metrics drops the
`doneColumnCount` assertion.
2026-04-21 16:48:55 +08:00
Bohan Jiang
779c72e835 fix(views): clear agent live state when switching issues (#1389)
AgentLiveCard kept its taskStates map across issueId prop changes, and
its merge logic only added newly-fetched tasks without removing stale
ones. Navigating from Issue A (with a running agent) to Issue B via
cmd+k left A's sticky agent status card pinned on B's page.

Key AgentLiveCard and TaskRunHistory by issue id so React remounts
them when the issue changes, guaranteeing fresh state per issue.

Closes MUL-1147
2026-04-20 18:47:46 +08:00
Jiayuan Zhang
e830575efc feat(issues): add expand toggle to comment and reply editors (#1386)
Mirrors the new-issue modal's expand behavior on the inline comment and
reply editors so users can compose long text without feeling cramped.
2026-04-20 18:19:40 +08:00
Jiayuan Zhang
2ab89d4690 feat(editor): create sub-issue from selected text in bubble menu (#1348)
Adds a "Create sub-issue from selection" button to the editor bubble
menu. When an issue context is present (description editor, comment
input, reply input, comment edit), selecting text and clicking the
button creates a new issue parented under the current issue and
replaces the selection with a mention link to the new issue.
2026-04-20 12:40:20 +08:00
Jiayuan Zhang
a7e9801c83 feat(views): show issue title in detail page header (#1344)
Previously the issue detail top bar only showed 'workspace name > identifier'.
Add the issue title next to the identifier so users can see what issue they're
viewing without scrolling.
2026-04-20 00:36:10 +08:00
Jiayuan Zhang
488aed6abf feat(issues): show project and sub-issue progress as optional card properties (MUL-996) (#1258)
Adds two new toggleable card properties that surface issue context at a glance:
- Project: shows the parent project icon + title when the issue belongs to one.
- Sub-issue progress: gates the existing progress ring behind a card property
  so users can hide it when not useful.

Both default to on; toggled via the existing "Display" popover.
2026-04-17 15:25:45 +08:00
Naiyuan Qing
c15212c0e4 fix(views): align skeleton loading states with actual page layouts (#1251)
- Issues/MyIssues: remove incorrect border-b from toolbar skeleton, add
  viewMode-aware skeleton (list vs board)
- Issue Detail: fix content padding (max-w-4xl mx-auto) and sidebar
  width (w-80), remove independent reactions/subscribers/timeline
  skeleton flashes — components now render with empty defaults
- Agents/Skills: gate skeleton on data query isLoading instead of auth
  store isLoading so skeleton covers actual data fetch
- Projects/Autopilots: add sticky column header skeleton row
- Autopilot Detail: add PageHeader skeleton, flesh out section structure
- Invite: replace plain text with Card-shaped skeleton
- Chat: migrate ChatMessageSkeleton to use Skeleton component
- Workspace layout: show MulticaIcon loading indicator instead of blank

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:21:10 +08:00
Naiyuan Qing
0a1c82730f fix(editor): prevent duplicate image attachments showing as file cards (#1231)
Images pasted into comments could produce duplicate attachment records
(macOS/Chrome clipboard provides duplicate File entries), causing
AttachmentList to show a spurious file card below the inline image.

Three-layer fix:
- Dedup clipboard files by name+size+type in paste/drop handlers
- Track upload URL→ID mapping instead of accumulating IDs blindly;
  only send IDs for uploads still present in content on submit
- AttachmentList filters duplicate attachments (same file identity)
  where a sibling is already referenced inline — handles old data

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 10:49:29 +08:00
Jiayuan Zhang
7843da0315 refactor(issues): lighten board card styling (#1217)
Slimmer 0.5px border, 12/10 asymmetric padding, and a two-layer soft
drop shadow give the kanban card a more weightless look on the board.
2026-04-17 02:15:24 +08:00
Jiayuan Zhang
ec30e46947 feat(issues): persist comment collapse state (#1008)
* feat(issues): persist comment collapse state across page reloads

Store collapsed comment IDs in a workspace-scoped Zustand store backed
by localStorage, replacing the transient useState(true) default.
Comments now remember their collapsed/expanded state per issue.

* test(issues): add useCommentCollapseStore mock to issue-detail tests

The existing vi.mock for @multica/core/issues/stores didn't include the
newly exported useCommentCollapseStore, causing CommentCard to throw at
render time.
2026-04-17 00:14:00 +08:00
Bohan Jiang
8c518c350a feat(agent): add Pi agent runtime support (#1064)
* feat(agent): add Pi agent runtime support

Add Pi as a new agent runtime provider, following the established adapter
pattern. Pi CLI outputs JSONL events which are parsed for messages, tool
calls, and usage tracking.

Backend:
- New piBackend implementing the Backend interface (pi.go)
- Pi CLI discovery via MULTICA_PI_PATH env var or PATH lookup
- JSONL event stream parsing (agent_start, message_update, thinking_update,
  tool_execution_start/end, agent_end)
- Usage scanner for ~/.pi/sessions/*.jsonl files
- Runtime config injection via AGENTS.md
- Skill injection to .pi/agent/skills/

Frontend:
- Pi provider logo (teal π icon)
- Pi label in transcript dialog

Docs:
- Updated all provider lists in README, CLI_INSTALL, and docs

* fix(agent): filter Pi usage scanner to agent_end events only

Address review feedback: restrict usage parsing to agent_end events
which contain cumulative totals, preventing potential inaccuracy if
Pi adds usage fields to other event types in the future.

* fix(agent): align Pi runtime with real CLI flags, event schema, and custom_args

- Flags: Pi's CLI uses `--mode json` (not `--output-format jsonl`), has no
  `--yolo` (explicit `--tools` allowlist instead), takes the prompt as a
  positional argument (not `-p <prompt>`), splits model as
  `--provider <name> --model <id>`, and treats `--session` as a file path
  that must exist before spawn.
- Event parsing: rewrite the stream event struct to match Pi's actual
  JSON event schema (`message_update.assistantMessageEvent.delta`,
  `turn_end.message.usage.{input,output,cacheRead,cacheWrite}`, etc.).
- Sessions: generate/persist session files under ~/.multica/pi-sessions/
  and use the file path as the opaque SessionID returned to the daemon.
- Usage scanner: read assistant `message` events from the same session
  files (Pi's session-file schema, distinct from the stdout stream).
- Custom args: consume `ExecOptions.CustomArgs` via `filterCustomArgs`
  with a Pi-specific blocked set (`-p`, `--print`, `--mode`, `--session`)
  so Pi matches the pattern shared by every other agent backend.
2026-04-16 15:42:40 +08:00
Naiyuan Qing
fe358feff0 Reapply "feat: workspace URL refactor v2 + rollback-safe compat layer (#1138)" (#1139) (#1141)
This reverts commit b30fd98605.
2026-04-16 13:16:35 +08:00
Naiyuan Qing
b30fd98605 Revert "feat: workspace URL refactor v2 + rollback-safe compat layer (#1138)" (#1139)
This reverts commit 75d12c26c5.
2026-04-16 12:26:40 +08:00
Naiyuan Qing
75d12c26c5 feat: workspace URL refactor v2 + rollback-safe compat layer (#1138)
* Reapply "feat: workspace URL refactor + slug-first API identity (#1131)" (#1137)

This reverts commit 9b94914bc8.

* compat: legacy URL redirect + localStorage double-write for safe rollback

The first attempt at this refactor (#1131) was reverted because existing
users on old URLs (/issues, /projects, etc.) hit 404 immediately after
deploy, and rolling back left them with empty dashboards — the legacy
code reads localStorage["multica_workspace_id"] to attach a workspace
to API requests, but the new code had stopped writing that key.

Two compat layers added on top of the restored refactor:

1. proxy.ts now intercepts legacy route prefixes (/issues/*, /projects/*,
   /agents/*, /inbox/*, /my-issues/*, /autopilots/*, /runtimes/*,
   /skills/*, /settings/*). Logged-in users with a last_workspace_slug
   cookie are 302'd to /{slug}/{rest}, preserving their deep link. Users
   without the cookie bounce through / where the landing page picks a
   workspace client-side. Unauthenticated users go to /login.

2. Both layouts now double-write the workspace id to the legacy
   localStorage key on every workspace entry. New code ignores this key
   — it exists solely so that if this PR ever gets reverted again, the
   legacy build reading the key would still find the correct workspace
   and avoid the empty-dashboard symptom users saw during the rollback.

Net effect: any direction of deploy ↔ rollback is now cache-compatible,
and any direction of old bookmark → new route resolves without 404.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(platform): defer rehydrateAllWorkspaceStores to a microtask

Same React 19 render-phase restriction that forced setCurrentWorkspace
to defer its subscriber notifications. rehydrateAllWorkspaceStores
synchronously calls each persist store's rehydrate, which setState()s
the store, which schedules updates on any subscribed component. When
the workspace layout's render-phase ref guard invoked this, React
complained that SearchCommand (a store subscriber) couldn't be
re-rendered while WorkspaceLayout was still rendering.

Fix: queueMicrotask the rehydrate loop and add a pending-flag guard so
rapid workspace switches coalesce into one rehydrate on the final slug.
Persist stores tolerate one microtask of staleness — they hold UI
preferences, not correctness-critical state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:23:41 +08:00
Naiyuan Qing
9b94914bc8 Revert "feat: workspace URL refactor + slug-first API identity (#1131)" (#1137)
This reverts commit 59ace95a1e.
2026-04-16 11:56:15 +08:00
Naiyuan Qing
59ace95a1e feat: workspace URL refactor + slug-first API identity (#1131)
* feat: workspace URL refactor + slug-first API identity

Make the URL the single source of truth for workspace identity.
All workspace-scoped URLs now carry the workspace slug as the first
path segment (/{slug}/issues, /{slug}/projects, etc.), matching the
industry standard (Linear, Notion, Vercel, GitHub).

## Key architectural changes

**URL-driven workspace identity:**
- Web routes moved under app/[workspaceSlug]/(dashboard)/
- Desktop routes nested under /:workspaceSlug
- paths.ts builder centralises all URL construction
- reserved-slugs validation (backend + frontend + DB migration audit)

**Slug-first API contract:**
- Frontend sends X-Workspace-Slug header (from URL) instead of X-Workspace-ID (UUID)
- Backend middleware resolves slug → UUID via GetWorkspaceBySlug, falls back to
  X-Workspace-ID for CLI/daemon backwards compatibility
- WebSocket auth accepts ?workspace_slug query param with SlugResolver callback

**State cleanup:**
- Deleted: useWorkspaceStore (Zustand mirror), switchWorkspace/hydrateWorkspace/
  clearWorkspace, localStorage["multica_workspace_id"], api._workspaceId
- useCurrentWorkspace() derives from URL slug + React Query workspace list
- useWorkspaceId() is now a bridge hook (no Context, derives from useCurrentWorkspace)
- WorkspaceIdProvider removed from DashboardGuard
- Paired module vars (slug + UUID) in workspace-storage.ts for non-React consumers

**Layout simplified:**
- Render-phase ref guard sets workspace context synchronously (no async gate)
- DashboardGuard handles auth redirect, loading state, and workspace resolution
- Subscriber notifications deferred via queueMicrotask (React 19 compat)
- persist namespace uses slug (immutable) instead of UUID

## Issues resolved

MUL-43 (share links), MUL-509 (mobile workspace switch), MUL-723 (workspace in URL),
MUL-727 (create workspace flash), MUL-728 (delete workspace no-navigate),
MUL-820 (sidebar Join not switching)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve code review C3/C4/C5/C6 — desktop deadlock + hardcoded paths

C3: Desktop OnboardingGate was calling useCurrentWorkspace() outside
WorkspaceSlugProvider → always null → permanent onboarding deadlock.
Rewrite to use useQuery(workspaceListOptions()) which reads React Query
cache directly without slug context. Remove DashboardGuard from
DesktopShell (auth gating handled by AppContent, workspace routing by
WorkspaceRouteLayout per-tab).

C4: Landing page "Dashboard" links hardcoded /issues (no longer valid).
Changed to / — proxy handles redirect to /{lastSlug}/issues.

C5: autopilots-page.tsx had one hardcoded /autopilots/${id} link.
Changed to wsPaths.autopilotDetail(id).

C6: inbox-page.tsx hardcoded /inbox paths. Changed to wsPaths.inbox().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(desktop): wrap shell in WorkspaceSlugProvider from module var

AppSidebar calls useWorkspacePaths() → useRequiredWorkspaceSlug() which
throws outside WorkspaceSlugProvider. In the desktop shell, the sidebar
renders at the shell level (outside any tab's WorkspaceRouteLayout).

Fix: DesktopShell reads the current slug via useSyncExternalStore on
the workspace-storage singleton. When slug is available, wraps the
entire shell in WorkspaceSlugProvider. When null (first mount before
any tab's WorkspaceRouteLayout sets it), shows a loading spinner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(desktop): migrate old tab paths + fix shell slug deadlock

Tab store rehydration: old-format paths like "/issues/abc" (missing
workspace slug prefix) are reset to "/" so IndexRedirect picks the
correct workspace. Detection: if the first segment is a known route
name (issues, projects, etc.) rather than a workspace slug, it's an
old-format path.

Desktop shell: TabContent must always render (not gated behind slug
check) so WorkspaceRouteLayout can mount and call setCurrentWorkspace.
Only sidebar and shell-level UI (chat, modals, search) gate on slug
being present.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:53:09 +08:00
Naiyuan Qing
3c46c5baa3 fix(editor): add dirty check and allow clearing description (#1132)
Two editor bugs fixed:

1. Descriptions saved unnecessarily on every document change (no dirty
   check). Added onCreate baseline capture + string comparison in the
   debounced onUpdate handler so mutations only fire when content
   actually changes.

2. Clearing a description didn't persist — empty string was converted
   to undefined via `md || undefined`, causing the field to be omitted
   from the API request. Changed to `md` so empty strings reach the
   backend and clear the description via COALESCE.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:46:43 +08:00
Naiyuan Qing
c38af55a8e refactor(ui): comprehensive UI craft review — sidebar, headers, detail panels (#1087)
Sidebar:
- Pinned items: StatusIcon for issues, emoji for projects, sm size, mask gradient text fade
- Pinned items: inline X close button (hidden → flex on hover, desktop tab pattern)
- Pinned section: collapsible with chevron + hover count
- Remove unused canvas token

Global components:
- PageHeader: shared component with built-in mobile SidebarTrigger (md:hidden)
- Replace header divs in all 11 dashboard pages with PageHeader
- Remove standalone mobile trigger bar from DashboardLayout
- Tooltip: 200ms delay, remove arrow, popover/border style
- Search dialog: add finalFocus={false}
- SidebarInset: remove shadow-sm
- Button sizing: icon-xs → icon-sm across all non-editor contexts

Issue Detail:
- Simplify breadcrumb to workspace > identifier
- Extract sidebarContent variable shared between ResizablePanel and mobile Sheet
- All sidebar sections collapsible (Properties, Parent issue, Details, Token usage)
- Auto-close sidebar on mobile breakpoint
- Collapsible section headers: text before chevron, !size-3 stroke-[2.5], hover bg

Project Detail:
- Match Issue Detail layout pattern (header inside left ResizablePanel)
- Extract sidebarContent, add mobile Sheet support
- All sidebar sections collapsible (Properties, Progress, Description)
- Header: move three-dot menu to right button group, unified breadcrumb layout
- Auto-close sidebar on mobile breakpoint

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:50:00 +08:00
Jiayuan Zhang
d930bcaa18 feat(server): trigger agent when issue moves out of backlog (#1006)
* feat(server): trigger agent when issue moves out of backlog

When a member moves an agent-assigned issue from "backlog" to an active
status (e.g. "todo", "in_progress"), enqueue an agent task so the agent
starts working. This lets backlog act as a parking lot where issues can
be assigned to agents without immediately triggering execution.

Applies to both single and batch issue updates.

* fix(server): treat backlog as parking lot — no trigger on create/assign

Address review feedback: creating or assigning an agent to a backlog
issue no longer triggers immediate execution. Only moving out of backlog
to an active status triggers the agent, producing exactly one task.

- shouldEnqueueAgentTask now gates on backlog status
- backlog→active trigger uses isAgentAssigneeReady directly
- Added TestBacklogNoTriggerOnCreate test
- Updated TestBacklogToTodoTriggersAgent to assert exactly 1 task
  across the full create→move path (no manual cleanup)

* feat(ui): show toast hint when assigning agent to backlog issue

Users may not know that backlog issues won't trigger agent execution
until moved to an active status. Show an actionable toast with a
"Move to Todo" button when:

- Assigning an agent to a backlog issue in the detail page
- Creating a backlog issue with an agent assignee

* feat(ui): add "Don't show again" option to backlog agent toast

Users who understand the backlog parking lot behavior can dismiss the
hint permanently. Uses localStorage to persist the preference.

* feat(ui): replace backlog agent toast with AlertDialog

Use a modal dialog instead of a toast notification so users must
explicitly acknowledge the hint. The dialog offers three options:
- "Move to Todo" — changes status and triggers the agent
- "Keep in Backlog" — dismisses without action
- "Don't show again" — persists dismissal in localStorage

* fix(ui): improve backlog agent dialog

* fix(ui): close create dialog behind hint, use checkbox for don't-show-again

1. Create Issue dialog now closes when the backlog agent hint appears,
   so only the hint dialog is visible (not stacked behind).
2. "Don't show again" is now a checkbox instead of a separate button.
   When checked, clicking either "Keep in Backlog" or "Move to Todo"
   persists the preference.

* fix(ui): smooth backlog agent hint dialog

* fix(test): add useUpdateIssue mock to create-issue test

The test mock for @multica/core/issues/mutations was missing the
useUpdateIssue export that create-issue.tsx now imports, causing
CI failure.
2026-04-15 19:07:48 +08:00
Naiyuan Qing
0399e387f8 fix(views): resolve stale status in cmd+k recent issues list
Recent issues store was duplicating server data (title, status, identifier)
in Zustand, violating the single-source-of-truth architecture. Now the store
only tracks visit records (id + visitedAt), and the search command joins
fresh data from the TanStack Query issue list cache at render time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:23:27 +08:00
Bohan Jiang
653c0adeee Merge pull request #959 from multica-ai/agent/j/e52c9eda
fix(views): issue mentions missing status/title after page refresh
2026-04-14 15:18:06 +08:00
Jiang Bohan
b6a69c113e fix(views): fetch individual issue for mentions not in list cache
Issue mentions in comments showed only the identifier (no status icon
or title) after page refresh when the referenced issue wasn't in the
issueListOptions cache (e.g. done issues beyond the first 50).

Fall back to issueDetailOptions to fetch the individual issue when it's
not found in the list. The detail query is only enabled when the issue
is missing from the list, so it adds no overhead for the common case.
2026-04-14 15:03:42 +08:00
Jiang Bohan
ed1a1dc6b1 fix(issues): include done/cancelled issues in parent/sub-issue picker search
The IssuePickerDialog was not passing include_closed: true to searchIssues,
so done and cancelled issues were invisible in the picker.
2026-04-14 14:55:42 +08:00
Jiang Bohan
345cb984a9 feat(issues): add "Set parent issue" and "Add sub-issue" to More menu
Add two new options to the issue detail More dropdown that let users
link existing issues as parent or sub-issue via a search dialog.
2026-04-14 14:20:48 +08:00
Naiyuan Qing
6a451c1ce7 fix(editor): rewrite bubble menu and link preview with useFloating
Replace Tiptap's BubbleMenu plugin with @floating-ui/react-dom for
all floating editor UI (formatting toolbar, link preview cards).

Architecture:
- useFloating({ strategy:"fixed" }) + createPortal(body) escapes
  all overflow:hidden ancestors (Card component, scroll containers)
- autoUpdate + contextElement monitors all scroll ancestors for
  repositioning; manual update() on transaction for virtual ref changes
- open prop resets isPositioned on visibility change (no stale-position
  flash at 0,0)
- display:none for hiding (not return null which causes blur/focus
  cycle, not visibility:hidden which leaves transition artifacts)
- No blur listener — portal DOM updates cause false editor blurs;
  outside-click + scroll + resize + Escape handle all close cases

Bug fixes:
- BubbleMenu: remove all custom visibility hacks, let selection state
  drive show/hide
- Link preview: new shared card (Copy + Open) for editable editor and
  readonly markdown, portaled to body with fixed positioning
- TitleEditor: use JSON content format (not HTML interpolation that
  loses < > characters)
- Blob URLs: strip from getMarkdown output during upload
- Markdown paste: check clipboard.files first to avoid intercepting
  file paste events
- FileCard: escape HTML attributes in preprocessing
- Link extension: enable linkOnPaste, set defaultProtocol to https,
  switch URL normalization to protocol blocklist (only block
  javascript:/data:/vbscript:)

Dependencies: add @floating-ui/react-dom, remove @tiptap/extension-bubble-menu

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:00:17 +08:00
Bohan Jiang
a94c6481dd fix: compute sub-issue progress from database instead of paginated client cache (#865)
The sub-issue progress indicator (e.g. "0/2") was undercounting because
it was computed from the client-side issue list, which only loads the
first 50 done issues. Sub-issues marked as done beyond that page were
excluded from both the total and done counts.

Added a dedicated backend endpoint (GET /api/issues/child-progress) that
aggregates child issue counts directly from the database, ensuring
accurate totals regardless of client-side pagination or filtering.

Fixes MUL-702
2026-04-13 19:10:28 +08:00
Naiyuan Qing
b4de4c9e9f Merge pull request #861 from multica-ai/feat/chat-ui-improvements
feat(chat): overhaul chat UI — resize, animations, session history
2026-04-13 18:31:00 +08:00
Bohan Jiang
7cac8014c9 feat(views): add keyboard navigation to assignee picker (#857)
* feat(views): add keyboard navigation and auto-select to PropertyPicker

Add arrow key (up/down) navigation and Enter key selection to the
searchable PropertyPicker dropdown. When the search narrows results to
a single match, pressing Enter auto-selects it without needing to
arrow-navigate first. Fixes GitHub issue #793.

* fix(views): hide Unassigned option when search filter is active

When the user types a search query in the assignee picker, the
Unassigned option is no longer pinned at the top — it only shows
when there is no active filter.

* feat(views): auto-highlight first result when searching in picker
2026-04-13 18:30:48 +08:00
Naiyuan Qing
f4ce4c249d chore: remove redundant icon size classes on pin buttons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 18:21:44 +08:00
Naiyuan Qing
6097f7392e refactor(chat): migrate chat input to ContentEditor + unified SubmitButton
- Replace plain textarea in chat-input with ContentEditor (rich text, matches comment-input structure)
- Extract shared SubmitButton component (idle/loading/running states) to packages/ui/components/common
- Update comment-input to use icon-sm size
- Fix chat-fab Tooltip delay prop (not supported on Root, global 500ms applies)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 15:52:39 +08:00
Jiang Bohan
b271e8915e fix(ws): include issue_id in task:dispatch event to prevent cross-issue UI glitch
broadcastTaskDispatch was the only task event broadcast missing issue_id
in its WebSocket payload. The frontend task:dispatch handler had no way
to filter by issue, causing AgentLiveCard to briefly show activity for
the wrong issue when multiple tabs are open.

Closes https://github.com/multica-ai/multica/issues/791
2026-04-13 01:50:10 +08:00
tianrking
0c19f0d16f Fix workspace filter sync and align CLI docs (#722)
* Fix workspace filter sync and align CLI docs

* simplify workspace sync subscription in issues page

* docs(self-hosting): align supported agents and daemon env vars
2026-04-12 23:11:40 +08:00
Jiang Bohan
7312b5650c fix(server): fix ListRuntimeUsage to filter by date range instead of row count (#765)
Replace LIMIT $2 with AND date >= $2 in ListRuntimeUsage query. When a
runtime uses multiple models each day has multiple rows, so a row LIMIT
silently returns fewer days than requested.

Also fixes displayName warnings in issue-detail test mocks and adds
missing setOpen to useCallback deps in search-command.

Co-authored-by: jayavibhavnk <jaya11vibhav@gmail.com>
Closes #731
2026-04-12 22:46:07 +08:00
Manish Chauhan
ec586ebc25 fix(pins): scope cache by user and fix sidebar pin action (#664) 2026-04-12 15:02:20 +08:00
Jiayuan Zhang
02c9480f44 fix(views): show agent live card immediately without waiting for messages (#727)
When navigating to an issue where an agent is already working, the
"Agent is working" card was delayed because it waited for both
getActiveTasksForIssue() AND listTaskMessages() to complete before
rendering. Now the card renders immediately after active tasks are
fetched, and messages load progressively in the background. Also
properly merges HTTP-loaded messages with any WebSocket-delivered
messages to avoid race conditions.
2026-04-12 00:21:39 +08:00
Jiayuan Zhang
3e4ae17596 fix(views): display comment attachments uploaded via CLI (#726)
commentToTimelineEntry() was dropping the attachments field, and
comment-card never rendered entry.attachments. Attachments uploaded
through the CLI (not embedded in markdown) were invisible in the UI.

- Add attachments to commentToTimelineEntry() conversion
- Add AttachmentList component that renders standalone attachments
  (skipping those already referenced in the markdown content)
- Render AttachmentList in both CommentRow and CommentCard
2026-04-12 00:11:25 +08:00