Skip to content

Fall back for blank sidebar icons - #5037

Merged
lawrencecchen merged 8 commits into
mainfrom
task-icons-disappearing
May 31, 2026
Merged

lawrencecchen merged 8 commits into
mainfrom
task-icons-disappearing

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Validate workspace group SF Symbols before rendering and fall back to folder.fill.
  • Normalize file preview tab symbols before publishing them to Bonsplit so blank or invalid values fall back to doc.text.

Testing

  • ./scripts/reload.sh --tag iconfix --swift-frontend-workaround
  • Not run locally: cmuxTests, because local xcodebuild test actions are forbidden on this Mac.

Dogfood


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Note

Low Risk
UI-only icon normalization and layout tweaks; no auth, data, or security-sensitive paths.

Overview
Adds RenderableSystemSymbol to trim names, check SF Symbol availability via NSImage, cache results, and fall back to folder.fill (workspace groups) or doc.text (surface/file-preview tabs).

Workspace groups: Sidebar headers and config resolution use the helper instead of raw strings; setWorkspaceGroupIcon stores only renderable symbols (or nil) and returns the stored value; the v2 API echoes that normalized icon_symbol.

File preview tabs: Bonsplit create/update paths, drag transfer payloads, and live icon updates all publish resolved tab icons so invalid or empty names don’t render blank.

UI polish: PanelFilePathHeader uses a fixed 14×14 icon slot and tighter horizontal padding; group header icons are semibold in a 14×14 frame and hidden from accessibility as decorative.

Tests: Coverage for resolution, invalid symbol rejection, and surface tab fallbacks.

Reviewed by Cursor Bugbot for commit 62ca9a8. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes disappearing sidebar and file‑preview icons by validating SF Symbols with a cached main‑actor renderability check and falling back to safe defaults. Aligns file‑path header icon size/spacing with tabs; invalid or blank names resolve to "folder.fill" (groups) or "doc.text" (tabs).

  • Bug Fixes
    • Cache SF Symbol renderability on the main actor in RenderableSystemSymbol and use it for all icon resolution.
    • Workspace groups: normalize on set and when resolved against config; store only renderable symbols or nil; API echoes the stored value; fallback to "folder.fill".
    • Surfaces/tabs: normalize icons for Bonsplit creates/updates and drag payloads; fallback to "doc.text".
    • UI/tests: 14pt icon slot with tighter spacing and 6pt header padding; 14×14 semibold group glyph marked decorative; regression tests cover resolution and fallbacks.

Written for commit 70c7261. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced icon symbol validation and normalization for workspace groups and file preview tabs, with improved fallback handling for invalid or whitespace-padded symbols.
  • Tests

    • Added unit tests verifying icon symbol resolution and storage behavior across workspace groups and tab surfaces.
  • Chores

    • Refactored layout constants for improved maintainability.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented May 31, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 31, 2026 8:00am
cmux-staging Building Building Preview, Comment May 31, 2026 8:00am

@coderabbitai

coderabbitai Bot commented May 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c935b018-10dd-468c-b8ca-b319ae3b6178

📥 Commits

Reviewing files that changed from the base of the PR and between f88a092 and 70c7261.

📒 Files selected for processing (1)
  • Sources/RenderableSystemSymbol.swift

📝 Walkthrough

Walkthrough

This PR introduces RenderableSystemSymbol utility to validate and normalize macOS SF Symbol strings, then applies symbol resolution across workspace-group icon storage/display, file-preview tab rendering, and related UI components. Tests and Xcode project entries are added; panel layout constants are refactored.

Changes

Icon symbol normalization and integration

Layer / File(s) Summary
RenderableSystemSymbol utility
Sources/RenderableSystemSymbol.swift
New @MainActor utility enum providing symbol validation via isRenderable() with memoized cache, trimming and normalization helpers, and fallback-chain resolution for workspace-group (folder.fill default) and surface-tab (doc.text default) icons. Includes test-only cache reset method.
Tests and project setup
cmuxTests/WorkspaceGroupTests.swift, cmux.xcodeproj/project.pbxproj
Three new unit tests validate resolvedWorkspaceGroupIcon fallback logic, setWorkspaceGroupIcon normalization with trimming, and resolvedSurfaceTabIcon invalid-input handling. Xcode project entries register the new source file for compilation.
Workspace-group icon storage and display
Sources/TabManager.swift, Sources/VerticalTabsSidebar+WorkspaceGroups.swift, Sources/SidebarWorkspaceGroupHeaderView.swift, Sources/TerminalController.swift
TabManager.setWorkspaceGroupIcon normalizes input via RenderableSystemSymbol.normalized(), stores the normalized value, and returns it. VerticalTabsSidebar computes effective icon using resolvedWorkspaceGroupIcon(explicit:configured:). SidebarWorkspaceGroupHeaderView resolves and renders the display icon. TerminalController uses the stored normalized symbol in API responses.
File-preview tab icon resolution
Sources/Workspace.swift
File-preview tab icons are resolved via RenderableSystemSymbol.resolvedSurfaceTabIcon() in subscription updates and during tab creation in both newFilePreviewSurface and splitPaneWithFilePreview.
Panel header layout constants
Sources/Panels/PanelContentView.swift
Replace hardcoded values in PanelFilePathHeader with file-scoped private constants for HStack spacing, icon slot size, and horizontal padding.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A symbol needs a home, so we built it one,
With validation and cache, the work is nearly done.
Icons fall back gracefully, from explicit to default,
Workspace groups shine bright—no more symbol fault! ✨


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Actor Isolation ❌ Error RenderableSystemSymbol.trimmed() is not marked @MainActor but is called from @MainActor-marked normalized(), violating Swift 6 actor isolation rules. Mark trimmed(_ raw: String?) -> String? with @MainActor attribute to match its caller and usage context.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fall back for blank sidebar icons' clearly describes the main change: adding fallback behavior for blank/invalid sidebar icons.
Description check ✅ Passed The description covers the main changes and includes a Testing section, but the Demo Video and Checklist sections are incomplete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Blocking Runtime ✅ Passed PR introduces no blocking/timing-based synchronization primitives; new RenderableSystemSymbol properly uses @MainActor for concurrency, existing v2MainSync pattern is unchanged.
Cmux No Hacky Sleeps ✅ Passed Check applies to TypeScript, JavaScript, shell, and non-Swift scripts. PR modifies only Swift source files and Xcode project files, which are out of scope for this check.
Cmux Algorithmic Complexity ✅ Passed Memoized SF Symbol validation, fixed 2-element resolution loops, single-pass group lookups, no batch iterations—all conform to algorithmic complexity requirements.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns; new RenderableSystemSymbol uses modern @MainActor for cache sync, avoiding DispatchQueue, Combine, or fire-and-forget Task usage.
Cmux Swift @Concurrent ✅ Passed AppKit NSImage calls in RenderableSystemSymbol are @MainActor; all callers properly isolated on @MainActor class or SwiftUI View; no async or @concurrent violations.
Cmux Swift File And Package Boundaries ✅ Passed 58-line RenderableSystemSymbol utility has single responsibility. Minimal additions to oversized files (+3/+1/+3 lines, under 250-line threshold). Meets allowed cases for focused bug fixes.
Cmux Swift Logging ✅ Passed No print, debugPrint, dump, NSLog, or new Logger statements added. RenderableSystemSymbol.swift contains only AppKit import and validation logic with no logging.
Cmux User-Facing Error Privacy ✅ Passed PR adds icon validation utility and modifies layout constants. No user-facing errors, credentials, vendor names, or sensitive data exposed to users.
Cmux Full Internationalization ✅ Passed PR adds RenderableSystemSymbol utility (SF Symbol names only, not user text) and modifies icon handling without introducing any user-facing strings, localization keys, or string catalog changes.
Cmux Swiftui State Layout ✅ Passed No SwiftUI state layout violations: no new @ObservableObject/@published, no GeometryReader measurement changes (pre-existing), no lazy/list store violations, no render-time mutations.
Cmux Architecture Rethink ✅ Passed Pure utility and model updates for icon validation with clear single ownership, no timing repairs, no observers, pure functional resolution, and test coverage.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR is about icon validation and UI component updates only; does not create or materially change any NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup declarations.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-icons-disappearing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 31, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds RenderableSystemSymbol — an @MainActor-isolated enum with a per-symbol renderability cache backed by NSImage(systemSymbolName:) — and threads it through every icon-publishing path to replace blank or unrecognised SF Symbol names with folder.fill (workspace groups) or doc.text (file-preview tabs).

  • RenderableSystemSymbol: normalized trims whitespace and rejects non-renderable names; resolvedWorkspaceGroupIcon and resolvedSurfaceTabIcon cascade through candidates before falling back to the respective defaults; results are cached on @MainActor and validated at all call sites.
  • Storage & API: setWorkspaceGroupIcon now stores only validated symbols (or nil) and returns the stored value; v2WorkspaceGroupSetIcon echoes that stored value in its response, fixing the previously reported response-mismatch.
  • Tab creation / live updates / drag payloads: All three Bonsplit publishing paths in Workspace.swift now resolve the display icon before publishing, preventing blank tab icons.

Confidence Score: 5/5

Safe to merge — changes are confined to icon normalisation on the main actor, with no data-loss, auth, or persistence side effects.

All call sites are on @MainActor-isolated types (TabManager, Workspace) or SwiftUI views, so the new @mainactor cache is accessed correctly throughout. The one redundant re-validation in the view body is harmless (cache hit every time). The API response fix and three new unit tests give solid confidence the feature behaves as intended.

No files require special attention; SidebarWorkspaceGroupHeaderView.swift has a minor redundancy worth tidying.

Important Files Changed

Filename Overview
Sources/RenderableSystemSymbol.swift New utility enum with @MainActor-isolated renderability cache; clean design, no issues found.
Sources/TabManager.swift setWorkspaceGroupIcon now normalizes and validates before storing, returns the stored value; correct since TabManager is @mainactor.
Sources/TerminalController.swift API response now echoes storedIconSymbol (the post-normalization value) rather than the pre-normalization input; fixes the previously noted response-mismatch issue.
Sources/SidebarWorkspaceGroupHeaderView.swift displayedIconSymbol re-runs resolvedWorkspaceGroupIcon on an iconSymbol that is already pre-resolved by the parent; redundant cache lookups on every body evaluation.
Sources/VerticalTabsSidebar+WorkspaceGroups.swift Switches from raw string fallback to RenderableSystemSymbol.resolvedWorkspaceGroupIcon; correctly incorporates both explicit and configured symbol candidates.
Sources/Workspace.swift Tab creation and live-icon-update paths now resolve icons via RenderableSystemSymbol before publishing to Bonsplit; valid since Workspace is @mainactor.
Sources/Panels/PanelContentView.swift PanelFilePathHeader icon slot reduced from 16→14pt and HStack padding from 12→6pt via named constants; UI-only, no logic changes.
cmuxTests/WorkspaceGroupTests.swift Three new tests covering fallback resolution, invalid symbol rejection on storage, and surface tab icon fallback; good coverage of the new behaviour.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Raw icon string] --> B{RenderableSystemSymbol.normalized}
    B --> C{trimmed?}
    C -- nil/empty --> D[return nil]
    C -- non-empty --> E{isRenderable?\nNSImage check + cache}
    E -- false --> D
    E -- true --> F[return trimmed string]

    F --> G1[setWorkspaceGroupIcon\nstores valid symbol or nil]
    F --> G2[Workspace.swift\nBonsplit tab create/update]
    F --> G3[SidebarWorkspaceGroupHeaderView\ndisplayedIconSymbol]

    D --> H1[group.iconSymbol = nil\nfallback → folder.fill]
    D --> H2[tab icon fallback → doc.text]
Loading

Reviews (4): Last reviewed commit: "Use main actor for symbol renderability ..." | Re-trigger Greptile

Comment on lines +29 to +31
static func normalized(_ raw: String?) -> String? {
RenderableSystemSymbol.trimmed(raw)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 WorkspaceGroupIconSymbol.normalized skips the renderability check

normalized calls RenderableSystemSymbol.trimmed — which only strips whitespace — not RenderableSystemSymbol.normalized, which is the method that also calls isRenderable. As a result, setWorkspaceGroupIcon(groupId:symbol:) stores any non-blank string (e.g. "not.an.sf.symbol") directly into the model without validating that it resolves to a real SF Symbol. The bad value persists until resolved() discards it at render time via RenderableSystemSymbol.normalized, but the authoritative iconSymbol property on the group carries stale invalid data. Rename the wrapper or change its body to RenderableSystemSymbol.normalized(raw) so storage and display share the same validation semantics.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by making WorkspaceGroupIconSymbol.normalized use the same renderability validation as the render path, and added a regression test that invalid group symbols are dropped before storage.

— Claude Code

Comment on lines +4 to +50
enum RenderableSystemSymbol {
static func trimmed(_ raw: String?) -> String? {
guard let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines),
!trimmed.isEmpty else {
return nil
}
return trimmed
}

static func normalized(_ raw: String?) -> String? {
guard let trimmed = trimmed(raw),
isRenderable(trimmed) else {
return nil
}
return trimmed
}

static func isRenderable(_ symbol: String) -> Bool {
NSImage(systemSymbolName: symbol, accessibilityDescription: nil) != nil
}
}

enum WorkspaceGroupIconSymbol {
static let defaultSymbol = "folder.fill"

static func normalized(_ raw: String?) -> String? {
RenderableSystemSymbol.trimmed(raw)
}

static func resolved(explicit: String?, configured: String?) -> String {
for candidate in [explicit, configured] {
guard let normalized = RenderableSystemSymbol.normalized(candidate) else { continue }
return normalized
}
return defaultSymbol
}
}

enum SurfaceTabIconSymbol {
static let defaultSymbol = "doc.text"

static func resolved(_ raw: String?, fallback: String = defaultSymbol) -> String {
RenderableSystemSymbol.normalized(raw)
?? RenderableSystemSymbol.normalized(fallback)
?? defaultSymbol
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Symbol utility enums defined in a view file but consumed across the model layer

RenderableSystemSymbol, WorkspaceGroupIconSymbol, and SurfaceTabIconSymbol are referenced from TabManager.swift, Workspace.swift, VerticalTabsSidebar+WorkspaceGroups.swift, and FilePreviewPanel.swift — yet they live in SidebarWorkspaceGroupHeaderView.swift, which is a SwiftUI view file. This mixes shared, independently-testable icon-validation logic into a UI component file, making the dependency graph read backwards (the model layer importing from a view file). A dedicated Sources/IconSymbol.swift (or similar) would give these helpers the right home without adding file count.

Rule Used: Flag Swift changes that add too much unrelated res... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread Sources/TabManager.swift Outdated
Comment on lines 6587 to 6592
func setWorkspaceGroupIcon(groupId: UUID, symbol: String?) {
let normalized = WorkspaceGroupIconSymbol.normalized(symbol)
guard let index = workspaceGroups.firstIndex(where: { $0.id == groupId }) else { return }
guard workspaceGroups[index].iconSymbol != symbol else { return }
workspaceGroups[index].iconSymbol = symbol
guard workspaceGroups[index].iconSymbol != normalized else { return }
workspaceGroups[index].iconSymbol = normalized
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 API response echoes unvalidated symbol after normalization

setWorkspaceGroupIcon now silently rejects unrenderable symbols (storing nil instead), but the only existing caller — v2WorkspaceGroupSetIcon in TerminalController.swift — still echoes its own pre-normalization normalized variable in the success response body ("icon_symbol": v2OrNull(normalized)). A caller that sends "not.an.sf.symbol" receives a success response claiming that exact string was applied, but the group actually stores nil and displays folder.fill. Before this PR that was harmless because every symbol was accepted; now the response can misrepresent the stored state for any non-renderable input. setWorkspaceGroupIcon should either return the effective stored symbol so callers can reflect it, or the TerminalController call-site should run WorkspaceGroupIconSymbol.normalized before composing its response.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/VerticalTabsSidebar`+WorkspaceGroups.swift:
- Around line 16-19: RenderableSystemSymbol.resolvedWorkspaceGroupIcon is
calling isRenderable(_:) on each candidate every render, which repeatedly
constructs NSImage(systemSymbolName:) — add memoization inside
RenderableSystemSymbol (e.g., a static [String: Bool] cache keyed by the symbol
name) and have isRenderable(_) consult and populate that cache instead of always
instantiating NSImage; update resolvedWorkspaceGroupIcon to use the cached
result and ensure simple thread-safety (DispatchQueue or atomic access) for
cache reads/writes and an option to invalidate if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 810b8332-5fb8-491d-bc8f-cafab0bc05ba

📥 Commits

Reviewing files that changed from the base of the PR and between 9e9a343 and f88a092.

📒 Files selected for processing (10)
  • Sources/Panels/FilePreviewPanel.swift
  • Sources/Panels/PanelContentView.swift
  • Sources/RenderableSystemSymbol.swift
  • Sources/SidebarWorkspaceGroupHeaderView.swift
  • Sources/TabManager.swift
  • Sources/TerminalController.swift
  • Sources/VerticalTabsSidebar+WorkspaceGroups.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/WorkspaceGroupTests.swift

Comment thread Sources/VerticalTabsSidebar+WorkspaceGroups.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes May 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/RenderableSystemSymbol.swift`:
- Around line 3-20: The current RenderableSystemSymbolCache uses NSLock and
`@unchecked` Sendable; rewrite it as an actor named RenderableSystemSymbolCache
that owns the values dictionary and exposes an async method value(for:compute:)
(or value(for:) that accepts a synchronous closure but is called via await) so
locking is handled by actor isolation, remove `@unchecked` Sendable and NSLock,
and update all call sites (e.g., isRenderable(_:), any callers that invoke
RenderableSystemSymbolCache.value(for:compute:)) to await the new actor method,
preserving the same semantics of caching the computed Bool result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 704096b1-976f-4110-ba41-f0fbb034022b

📥 Commits

Reviewing files that changed from the base of the PR and between f88a092 and 62ca9a8.

📒 Files selected for processing (1)
  • Sources/RenderableSystemSymbol.swift

Comment thread Sources/RenderableSystemSymbol.swift Outdated
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lawrencecchen
lawrencecchen dismissed coderabbitai[bot]’s stale review May 31, 2026 09:01

Stale CodeRabbit review. The reported NSLock cache issue was fixed in 70c7261 with a MainActor-isolated cache; the inline thread is resolved and the current CodeRabbit check is passing.

@lawrencecchen
lawrencecchen merged commit fc11995 into main May 31, 2026
21 checks passed
@lawrencecchen
lawrencecchen deleted the task-icons-disappearing branch May 31, 2026 09:01

This branch was successfully deployed

1 active deployment
Preview – cmux — 70c7261d Deployed May 31, 2026 by vercel[bot]
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