Conversation
…to state - Add optional icon_emoji and icon_url fields to SlackAppConfig.app - Add getBotForShortcut(callbackId) method to EmulatorState for resolving shortcut ownership
…rgeted - Return full shortcut arrays and icon fields (iconEmoji, iconUrl) per bot in getConnectedBots - Resolve owning bot by callback_id via getBotForShortcut before dispatching shortcuts - Add optional targetBotId parameter to dispatchShortcut with broadcast fallback
…context menu - Change ConnectedBotInfo.shortcuts from number to Shortcut[] with iconEmoji/iconUrl fields - Replace getMessageShortcut() with getAllMessageShortcuts() grouped by bot - Export BotShortcutGroup type from barrel file - Update SSE handler and bot type to pass full shortcut data and icon fields
- Replace hardcoded Generate Image menu item with grouped multi-bot submenu - Remove onGenerateImage prop chain from Message, MessagePanel, and ThreadPanel - Shortcuts dispatched directly from Message via triggerMessageShortcut - Context menu works on all messages, not just image messages - Each submenu item shows bot icon + shortcut name (bold) + app name (muted)
Add "Showcase message context" message shortcut to the showcase bot that opens a modal showing message details including images from both file attachments and Block Kit blocks. Fix shortcut payload type from 'shortcut' to 'message_action' to match Bolt's expected type for message shortcuts. Enrich shortcut payload with full message data (text, files, blocks) from emulator state.
…utton Add no-drag to ImagePreviewModal overlay so -webkit-app-region: drag on headers behind the modal doesn't intercept mouse events.
Replace custom dropdown with shadcn-svelte DropdownMenu so the hover menu matches the right-click context menu. Also fix submenu close animation flash in both dropdown and context menu sub-content.
…e detection - Reuse ImageBlock component for file-attached images instead of duplicate rendering code, giving consistent collapse triangle styling and size display - Constrain file image thumbnails to max-height 360px to match real Slack - Extract image URLs from Block Kit image blocks into files array when dispatching shortcuts so connected bots can detect them - Fix image-size endpoint returning wrong size (35 B) for local files by supporting HEAD requests on file serving route and checking response status - Add no-store cache headers to image-size responses
|
No actionable comments were generated in the recent review. 🎉 📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used📓 Path-based instructions (4)**/*.svelte📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/ui/src/**/*.{ts,tsx,svelte}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
packages/mrkdwn/src/**/*.ts📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (5)📚 Learning: 2026-02-22T01:14:27.310ZApplied to files:
📚 Learning: 2026-01-14T21:36:07.933ZApplied to files:
📚 Learning: 2026-02-20T00:31:35.696ZApplied to files:
📚 Learning: 2026-02-20T00:31:35.696ZApplied to files:
📚 Learning: 2026-02-20T00:31:35.696ZApplied to files:
🧬 Code graph analysis (1)packages/mrkdwn/src/mrkdwn-to-html.ts (1)
🔇 Additional comments (10)
📝 WalkthroughWalkthroughAdds a message-action shortcut for the showcase bot and its handler to open a "Message Context" modal; propagates richer shortcut and icon metadata through UI state/types and server APIs; refactors message UI to use a DropdownMenu/ImageBlock system and grouping logic; adds client-side modal validation and collapsible image controls; and enables targeted server-side shortcut dispatch. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as Message Component
participant ClientState as Client Dispatcher/State
participant WebAPI as Server Web API
participant Socket as SocketModeServer
participant Bot as Connected Bot
User->>UI: Invoke message action "Showcase message context"
UI->>UI: Collect message context (ts, channel, user, text, files, blocks)
UI->>ClientState: triggerMessageShortcut(callback_id, context)
ClientState->>WebAPI: POST shortcut (callback_id, trigger_id, message payload)
WebAPI->>WebAPI: getBotForShortcut(callback_id) -> targetBot
WebAPI->>Socket: dispatchShortcut(message_action payload, targetBotId)
Socket->>Bot: Send envelope to target bot connection
Bot->>User: Open modal with assembled message context
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/ui/src/components/MessagePanel.svelte (1)
42-50:⚠️ Potential issue | 🟡 MinorUse
$libalias for lib imports.
This import targetssrc/lib, so switch to$libto keep paths stable.Suggested change
- import { clearChannelMessages, deleteMessage } from '../lib/dispatcher.svelte' + import { clearChannelMessages, deleteMessage } from '$lib/dispatcher.svelte'As per coding guidelines,
apps/ui/src/**/*.{ts,tsx,svelte}: Use$libpath alias when importing fromsrc/lib/in Svelte applications.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/MessagePanel.svelte` around lines 42 - 50, The import from '../lib/dispatcher.svelte' in MessagePanel.svelte should use the Svelte $lib alias instead of a relative path; update the import that brings in clearChannelMessages and deleteMessage to '$lib/dispatcher.svelte' so the module resolution is stable and matches the project's apps/ui/src/**/*.{ts,tsx,svelte} guideline.apps/ui/src/components/blockkit/blocks/InputBlock.svelte (1)
2-20:⚠️ Potential issue | 🟡 MinorUse
$libalias for lib imports.
The types import targetssrc/lib, so it should use the$libalias.Suggested change
- } from '../../../lib/types' + } from '$lib/types'As per coding guidelines,
apps/ui/src/**/*.{ts,tsx,svelte}: Use$libpath alias when importing fromsrc/lib/in Svelte applications.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/blockkit/blocks/InputBlock.svelte` around lines 2 - 20, The import of many types (e.g., SlackInputBlock, SlackPlainTextInputElement, SlackStaticSelectElement, SlackFileInputElement, SlackCheckboxesElement, SlackNumberInputElement, SlackEmailInputElement, SlackUrlInputElement, SlackRadioButtonsElement, SlackDatePickerElement, SlackTimePickerElement, SlackDateTimePickerElement, SlackWorkspaceSelectElement, SlackOption, UploadedFile) currently uses a relative path ('../../../lib/types'); update that import to use the Svelte $lib alias (import from '$lib/types') so it follows the apps/ui Svelte path-alias guideline and resolves the src/lib module correctly.apps/ui/src/lib/state/bots.svelte.ts (1)
6-14:⚠️ Potential issue | 🟡 MinorUse the
$libalias forsrc/libimports.
Please switch these imports to the alias to match the project convention.Proposed fix
-import type { - SlashCommand, - Shortcut, - SlackView, - SlackAppConfig, - ConnectedBotInfo, -} from '../types' -import { BOT_USER_ID } from '../types' +import type { + SlashCommand, + Shortcut, + SlackView, + SlackAppConfig, + ConnectedBotInfo, +} from '$lib/types' +import { BOT_USER_ID } from '$lib/types'As per coding guidelines:
apps/ui/src/**/*.{ts,tsx,svelte}: Use$libpath alias when importing fromsrc/lib/in Svelte applications.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/lib/state/bots.svelte.ts` around lines 6 - 14, The imports at the top of bots.svelte.ts currently reference relative paths from src/lib; change them to use the Svelte $lib alias (e.g., replace imports that bring in SlashCommand, Shortcut, SlackView, SlackAppConfig, ConnectedBotInfo, BOT_USER_ID and simulatorState from '../types' and '../state.svelte' to import from '$lib/types' and '$lib/state.svelte' respectively) so the file follows the project's `$lib` import convention.apps/ui/src/components/Message.svelte (1)
555-564:⚠️ Potential issue | 🟡 MinorSame trailing separator issue in ContextMenu.
The ContextMenu has the identical problem: the separator on line 557 renders even when
onDeleteis undefined.🛠️ Proposed fix
</ContextMenu.SubContent> </ContextMenu.Sub> - <ContextMenu.Separator /> {/if} {`#if` onDelete} + {`#if` hasShortcuts} + <ContextMenu.Separator /> + {/if} <ContextMenu.Item variant="destructive" onclick={handleDelete}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/Message.svelte` around lines 555 - 564, The trailing ContextMenu.Separator is rendered unconditionally and shows even when onDelete is undefined; update the JSX in Message.svelte so the separator is only rendered when onDelete is truthy — e.g., move or wrap <ContextMenu.Separator /> inside the same conditional that renders the destructive <ContextMenu.Item> (the block using onDelete, handleDelete, and <ContextMenu.Item variant="destructive">) so the separator and the delete item are gated together.
🧹 Nitpick comments (1)
apps/ui/src/components/Message.svelte (1)
358-398: Consider extracting duplicated shortcut menu rendering into a Svelte snippet.The shortcut group iteration (icon, name, bot label) is repeated almost identically in both
DropdownMenuandContextMenu. Svelte 5 snippets could reduce this duplication and simplify future changes.Also applies to: 532-564
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/Message.svelte` around lines 358 - 398, Extract the repeated shortcut-group rendering into a reusable Svelte snippet/component and use it in both DropdownMenu and ContextMenu to remove duplication: create a small component/snippet (e.g., ShortcutList or ShortcutItem) that accepts shortcutGroups and handleShortcut as props and renders the {`#each` group.shortcuts as shortcut (shortcut.callback_id)} loop with the same markup (using group.botIcon, group.botName.charAt(0).toUpperCase(), shortcut.name, and shortcut.callback_id for keys), then replace the inline loops in the DropdownMenu.SubContent and the ContextMenu content with that snippet/component to centralize markup and behavior.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (39)
apps/showcase-bot/config.yamlapps/showcase-bot/src/listeners/index.tsapps/showcase-bot/src/listeners/shortcuts/showcase-shortcuts.tsapps/ui/src/components/ImagePreviewModal.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/MessagePanel.svelteapps/ui/src/components/ModalOverlay.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/blockkit/BlockKitRenderer.svelteapps/ui/src/components/blockkit/blocks/ImageBlock.svelteapps/ui/src/components/blockkit/blocks/InputBlock.svelteapps/ui/src/lib/components/ui/context-menu/context-menu-sub-content.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu.svelteapps/ui/src/lib/components/ui/dropdown-menu/index.tsapps/ui/src/lib/dispatcher.svelte.tsapps/ui/src/lib/state.svelte.tsapps/ui/src/lib/state/bots.svelte.tsapps/ui/src/lib/types.tspackages/slack/src/server/index.tspackages/slack/src/server/socket-mode.tspackages/slack/src/server/state.tspackages/slack/src/server/types.tspackages/slack/src/server/web-api.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript code
No semicolons in code, enforced by Prettier
Use single quotes instead of double quotes
Use trailing commas in es5 format
Prefix unused variables with underscore (_)
Files:
packages/slack/src/server/types.tsapps/ui/src/lib/state.svelte.tsapps/ui/src/lib/types.tspackages/slack/src/server/state.tspackages/slack/src/server/socket-mode.tsapps/ui/src/lib/state/bots.svelte.tsapps/ui/src/lib/dispatcher.svelte.tspackages/slack/src/server/web-api.tsapps/showcase-bot/src/listeners/index.tsapps/showcase-bot/src/listeners/shortcuts/showcase-shortcuts.tspackages/slack/src/server/index.tsapps/ui/src/lib/components/ui/dropdown-menu/index.ts
packages/*/src/server/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
packages/*/src/server/*.ts: Platform emulator plugins must implement WebSocket protocol for event broadcasting to the frontend
Organize platform plugin packages withserver/state.tsfor in-memory state,server/web-api.tsfor API handlers, andserver/persistence.tsfor storage
Files:
packages/slack/src/server/types.tspackages/slack/src/server/state.tspackages/slack/src/server/socket-mode.tspackages/slack/src/server/web-api.tspackages/slack/src/server/index.ts
packages/slack/src/server/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Files:
packages/slack/src/server/types.tspackages/slack/src/server/state.tspackages/slack/src/server/socket-mode.tspackages/slack/src/server/web-api.tspackages/slack/src/server/index.ts
**/*.svelte
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.svelte: Use{@html}in Svelte for mrkdwn rendering (ESLint rule disabled for this specific use case)
Use Svelte 5 runes ($state,$derived,$effect) for reactivity instead of legacy stores
Files:
apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelteapps/ui/src/components/ImagePreviewModal.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelteapps/ui/src/components/MessagePanel.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelteapps/ui/src/lib/components/ui/context-menu/context-menu-sub-content.svelteapps/ui/src/components/blockkit/blocks/InputBlock.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/blockkit/blocks/ImageBlock.svelteapps/ui/src/components/blockkit/BlockKitRenderer.svelteapps/ui/src/components/ModalOverlay.svelteapps/ui/src/components/Message.svelte
apps/ui/src/**/*.{ts,tsx,svelte}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
$libpath alias when importing fromsrc/lib/in Svelte applications
Files:
apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelteapps/ui/src/lib/state.svelte.tsapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelteapps/ui/src/lib/types.tsapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelteapps/ui/src/components/ImagePreviewModal.svelteapps/ui/src/lib/state/bots.svelte.tsapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelteapps/ui/src/components/MessagePanel.svelteapps/ui/src/lib/dispatcher.svelte.tsapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelteapps/ui/src/lib/components/ui/context-menu/context-menu-sub-content.svelteapps/ui/src/components/blockkit/blocks/InputBlock.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/blockkit/blocks/ImageBlock.svelteapps/ui/src/components/blockkit/BlockKitRenderer.svelteapps/ui/src/lib/components/ui/dropdown-menu/index.tsapps/ui/src/components/ModalOverlay.svelteapps/ui/src/components/Message.svelte
packages/slack/src/server/socket-mode.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Slack API emulator must implement Socket Mode protocol for bot communication
Files:
packages/slack/src/server/socket-mode.ts
🧠 Learnings (4)
📚 Learning: 2026-01-14T21:36:07.933Z
Learnt from: tyom
Repo: tyom/botarium PR: 1
File: apps/ui/src/components/MessagePanel.svelte:10-23
Timestamp: 2026-01-14T21:36:07.933Z
Learning: In the client-only Botarium UI (Vite + Svelte), do not add or rely on SSR-specific guards (e.g., checks for sessionStorage) in components. Since the app runs only in Electron or the browser, code that accesses browser APIs will execute in a non-SSR environment, so SSR-unsafe checks are unnecessary and can be removed for maintainability.
Applied to files:
apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelteapps/ui/src/components/ImagePreviewModal.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelteapps/ui/src/components/MessagePanel.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelteapps/ui/src/lib/components/ui/context-menu/context-menu-sub-content.svelteapps/ui/src/components/blockkit/blocks/InputBlock.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelteapps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/blockkit/blocks/ImageBlock.svelteapps/ui/src/components/blockkit/BlockKitRenderer.svelteapps/ui/src/components/ModalOverlay.svelteapps/ui/src/components/Message.svelte
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/socket-mode.ts : Slack API emulator must implement Socket Mode protocol for bot communication
Applied to files:
packages/slack/src/server/state.tspackages/slack/src/server/socket-mode.tspackages/slack/src/server/web-api.ts
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/**/*.ts : Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Applied to files:
packages/slack/src/server/socket-mode.tsapps/ui/src/lib/dispatcher.svelte.tspackages/slack/src/server/index.ts
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to apps/ui/src/**/*.{ts,tsx,svelte} : Use `$lib` path alias when importing from `src/lib/` in Svelte applications
Applied to files:
apps/ui/src/lib/components/ui/dropdown-menu/index.ts
🧬 Code graph analysis (5)
apps/ui/src/lib/types.ts (1)
apps/showcase-bot/src/config/loader.ts (1)
Shortcut(70-75)
packages/slack/src/server/state.ts (1)
packages/slack/src/server/types.ts (1)
ConnectedBot(354-360)
packages/slack/src/server/socket-mode.ts (2)
packages/slack/src/server/types.ts (1)
MessageShortcutPayload(125-140)packages/slack/src/lib/logger.ts (1)
socketModeLogger(35-35)
apps/ui/src/lib/state/bots.svelte.ts (1)
apps/ui/src/lib/types.ts (1)
Shortcut(54-59)
packages/slack/src/server/web-api.ts (2)
packages/slack/src/server/index.ts (1)
message(565-567)packages/slack/src/lib/logger.ts (1)
webApiLogger(34-34)
🔇 Additional comments (43)
apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte (1)
1-14: Looks good — clean wrapper and props forwarding.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte (1)
1-20: LGTM!Clean wrapper component following established patterns. Uses Svelte 5 runes (
$bindable,$props) correctly, properly imports via the$libalias as per coding guidelines, and the Tailwind v4 classes includingorigin-(--bits-dropdown-menu-content-transform-origin)are correctly formatted.apps/ui/src/lib/components/ui/context-menu/context-menu-sub-content.svelte (1)
1-20: LGTM!The component correctly uses Svelte 5 runes (
$props,$bindable) and the$libpath alias as per coding guidelines. The removal of closing-state animations (animate-out,fade-out-0,zoom-out-95) is a valid UX choice that can make submenu transitions feel snappier and avoid potential animation timing conflicts.apps/showcase-bot/src/listeners/index.ts (1)
4-9: Shortcut listener registration is correctly wired.Nice, clean addition that aligns with existing listener setup.
apps/showcase-bot/src/listeners/shortcuts/showcase-shortcuts.ts (1)
28-63: Image extraction from files and Block Kit blocks looks solid.Good coverage for both attachment files and Block Kit image blocks.
apps/showcase-bot/config.yaml (1)
14-18: Shortcut configuration matches the handler.Looks consistent with the new listener.
apps/ui/src/components/ImagePreviewModal.svelte (1)
202-202: LGTM!The
no-dragclass addition correctly prevents Electron's drag region from interfering with the modal backdrop interactions, aligning with the PR objective to fix the image preview close button issue.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte (1)
1-16: LGTM!Clean wrapper component following the established pattern. Properly uses Svelte 5 runes with
$bindablefor two-way binding ofrefandvalue, and correctly forwards all remaining props to the primitive.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte (1)
1-10: LGTM!Minimal and clean root wrapper component. Correctly uses
$bindable(false)for theopenstate and properly forwards all other props to the primitive.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte (1)
1-17: LGTM!Well-structured separator component with sensible default styling. Correctly uses the
$libpath alias for thecnutility import and properly merges custom class names.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte (1)
1-7: LGTM!Minimal portal wrapper that correctly forwards all props to the underlying primitive. No additional logic needed for this pass-through component.
packages/slack/src/server/index.ts (3)
472-475: LGTM!Adding HEAD method support for the file serving endpoint is appropriate. This allows clients to check file metadata without downloading the full file body.
500-520: Good defensive fix for HEAD response handling.The change to check
res.okbefore extractingcontent-length(line 504) correctly handles cases where the HEAD request fails with an error status code. Previously, a failed HEAD response could still return a content-length header, leading to incorrect size values.The addition of
Cache-Control: no-storeis appropriate for this dynamic proxy endpoint.
521-531: LGTM!Consistent error response formatting with the
Cache-Control: no-storeheader to prevent caching of error states.packages/slack/src/server/state.ts (1)
928-939: LGTM!The
getBotForShortcutmethod follows the established pattern ofgetBotForCommand(lines 918-926). It correctly filters for connected bots and uses efficient short-circuit evaluation with.some()to find the matching shortcut bycallback_id.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte (1)
1-29: LGTM!Well-structured submenu trigger component with comprehensive styling for various states (highlighted, open, disabled). The chevron icon correctly indicates a submenu, and the optional
insetprop provides layout flexibility. Properly uses the$libpath alias and Svelte 5 runes.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte (1)
1-16: LGTM!Clean wrapper component following the established bits-ui pattern. Correctly uses Svelte 5 runes (
$bindable,$props) and maintains consistency with the other dropdown menu components.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte (1)
1-14: LGTM!Consistent wrapper implementation matching the established component pattern. Proper use of Svelte 5 runes and data-slot attribution.
apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte (1)
1-10: LGTM!Minimal wrapper for submenu state management. The bindable
openprop with defaultfalseenables proper two-way binding for submenu visibility control.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte (1)
1-33: LGTM!Well-structured radio item component with proper checked state indication using the CircleIcon. The snippet pattern for conditional rendering (
{#snippetchildren}with@render) follows Svelte 5 conventions correctly.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte (1)
1-22: LGTM!Clean implementation with the
insetprop providing flexible padding control via thedata-[inset]:ps-8Tailwind variant. The type composition usingComponentProps<typeof ...> & { inset?: boolean }is a good pattern for extending primitive props.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte (1)
1-31: LGTM!Proper composition of portal and content primitives. The Tailwind v4 syntax for CSS variables (
max-h-(--bits-dropdown-menu-content-available-height)) andoutline-hiddenare correctly applied.packages/slack/src/server/types.ts (2)
92-93: LGTM!The
icon_emojiandicon_urlfields align with Slack's bot configuration API, enabling proper bot icon display in the multi-bot shortcuts UI.
125-140: Payload type correction is accurate.Changing
typeto'message_action'correctly aligns with@slack/bolt'sMessageShortcutinterface as noted in the comment. The addition of optionalblocksenables passing Block Kit content through the shortcut payload.apps/ui/src/lib/types.ts (1)
576-586: Type changes are consistent with the PR objectives.The
shortcutsfield change fromnumbertoShortcut[]enables the UI to display full shortcut details (name, description, callback_id) in the multi-bot submenu. All consumers—including the filtering logic ingetAllMessageShortcuts()and component iterations inMessage.svelte—properly handle the new array type. TheiconEmojiandiconUrlfields use frontend camelCase convention, correctly mapping from the backend'sicon_emoji/icon_url.apps/ui/src/lib/state.svelte.ts (1)
102-116: Export surface update looks sound.apps/ui/src/components/ThreadPanel.svelte (1)
93-95: Message rendering update looks good.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte (1)
1-20: Neat, minimal wrapper component.apps/ui/src/components/blockkit/blocks/InputBlock.svelte (1)
95-199: Error state wiring and visuals look good.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte (1)
1-24: Clean label wrapper, nicely aligned with the other primitives.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte (1)
1-43: Solid checkbox item wrapper.apps/ui/src/components/blockkit/blocks/ImageBlock.svelte (1)
9-65: Collapsible handling looks sound.
The gated fetch and conditional UI keep the preview behaviour consistent across collapsible and non‑collapsible contexts.apps/ui/src/components/blockkit/BlockKitRenderer.svelte (1)
52-103: Error propagation and image‑collapsibility wiring look good.
Passing per‑block errors and the collapsible flag through to the child blocks is tidy and consistent.apps/ui/src/components/ModalOverlay.svelte (2)
39-248: Validation flow is cohesive.
Required‑field detection plus live revalidation after the first submit reads cleanly.
323-354: Modal layout and renderer wiring look good.
The width adjustment and passingerrors/imageCollapsibleintoBlockKitRendererare neatly integrated.apps/ui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte (1)
1-27: Dropdown menu item wrapper looks good.
Prop forwarding and class composition are clear and consistent.apps/ui/src/lib/dispatcher.svelte.ts (1)
294-306: Bot shortcut metadata propagation looks good.
Icons and shortcut arrays are now surfaced consistently in the SSE payload.Also applies to: 423-436
packages/slack/src/server/web-api.ts (1)
1409-1413: Connected‑bot payload enrichment looks good.
Including icon fields and shortcut arrays in the response aligns cleanly with the updated UI.apps/ui/src/lib/components/ui/dropdown-menu/index.ts (1)
1-54: Dropdown menu re‑exports look tidy.
The consolidated export surface is clear for consumers.apps/ui/src/components/Message.svelte (4)
319-325: LGTM!The
handleShortcutfunction correctly dispatches the shortcut with the required message context (ts,text,file).
453-472: LGTM!Good approach reusing
ImageBlockfor file attachments. The inline block object construction is clean and the max-height constraint via Tailwind is appropriate for thumbnails.
69-70: LGTM!Proper use of Svelte 5
$derivedrunes for reactive shortcut state.
39-40: LGTM!Imports correctly use the
$libpath alias as per coding guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/showcase-bot/src/listeners/shortcuts/showcase-shortcuts.ts`:
- Around line 13-26: The section block creation in blocks uses message.text
directly which can exceed Slack's 3000-char mrkdwn limit and cause views.open to
fail; before building fields/blocks (where message.text is referenced), truncate
message.text to at most 3000 characters (preserving the code-fence/backtick
formatting if you wrap it with ```...```), then use the truncated value when
constructing fields and blocks (keep the existing '_empty_' fallback), and
ensure the final mrkdwn string passed into blocks (in the map that builds
KnownBlock[]) is under 3000 chars so views.open cannot fail due to oversized
text.
In `@apps/ui/src/components/Message.svelte`:
- Around line 386-398: The DropdownMenu.Separator is rendered unconditionally
which leaves an orphaned divider when hasShortcuts is true but onDelete is
falsy; update the conditional rendering so the separator
(DropdownMenu.Separator) is only included when there will be a following item
(i.e., wrap or gate the Separator with the same condition that renders the
Delete item or change the outer condition to require both hasShortcuts and
onDelete), ensuring handleDelete / onDelete logic remains unchanged.
In `@apps/ui/src/components/ThreadPanel.svelte`:
- Around line 1-5: Update the imports in ThreadPanel.svelte to use the $lib
alias instead of a relative path: replace imports that reference
'../lib/dispatcher.svelte' (including MockApp type, deleteMessage, sendMessage)
with imports from '$lib/dispatcher.svelte' so the file follows the Svelte app
aliasing convention; ensure any other references to '../lib' in this file are
similarly switched to '$lib'.
In `@packages/slack/src/server/socket-mode.ts`:
- Around line 705-723: The code silently returns when a bot is marked
'connected' but this.connections.get(bot.connectionId) returns falsy; add a
warning log in that branch to surface missing active socket details. Inside the
targetBotId handling block (where you call this.state.getBot(targetBotId) and
then const conn = this.connections.get(bot.connectionId)), when conn is falsy
emit socketModeLogger.warn including targetBotId, bot.connectionId,
envelope.envelope_id and payload.callback_id so operators can see why the
shortcut was dropped; keep the existing early return behavior after logging.
In `@packages/slack/src/server/web-api.ts`:
- Around line 1960-2008: When building the files array in the Block Kit image
handling loop (see storedMessage, files, and the loop over
storedMessage.blocks), do not hard-code mimetype to 'image/png'; instead either
omit the mimetype field for these entries or attempt to infer it from the
image_url (e.g., via file extension or a content-type HEAD request) so the
metadata is accurate before calling this.socketMode.dispatchShortcut with the
constructed message; update the block branch that currently pushes { mimetype:
'image/png', url_private: b.image_url } to push either { url_private:
b.image_url } or an inferred mimetype when available.
---
Outside diff comments:
In `@apps/ui/src/components/blockkit/blocks/InputBlock.svelte`:
- Around line 2-20: The import of many types (e.g., SlackInputBlock,
SlackPlainTextInputElement, SlackStaticSelectElement, SlackFileInputElement,
SlackCheckboxesElement, SlackNumberInputElement, SlackEmailInputElement,
SlackUrlInputElement, SlackRadioButtonsElement, SlackDatePickerElement,
SlackTimePickerElement, SlackDateTimePickerElement, SlackWorkspaceSelectElement,
SlackOption, UploadedFile) currently uses a relative path
('../../../lib/types'); update that import to use the Svelte $lib alias (import
from '$lib/types') so it follows the apps/ui Svelte path-alias guideline and
resolves the src/lib module correctly.
In `@apps/ui/src/components/Message.svelte`:
- Around line 555-564: The trailing ContextMenu.Separator is rendered
unconditionally and shows even when onDelete is undefined; update the JSX in
Message.svelte so the separator is only rendered when onDelete is truthy — e.g.,
move or wrap <ContextMenu.Separator /> inside the same conditional that renders
the destructive <ContextMenu.Item> (the block using onDelete, handleDelete, and
<ContextMenu.Item variant="destructive">) so the separator and the delete item
are gated together.
In `@apps/ui/src/components/MessagePanel.svelte`:
- Around line 42-50: The import from '../lib/dispatcher.svelte' in
MessagePanel.svelte should use the Svelte $lib alias instead of a relative path;
update the import that brings in clearChannelMessages and deleteMessage to
'$lib/dispatcher.svelte' so the module resolution is stable and matches the
project's apps/ui/src/**/*.{ts,tsx,svelte} guideline.
In `@apps/ui/src/lib/state/bots.svelte.ts`:
- Around line 6-14: The imports at the top of bots.svelte.ts currently reference
relative paths from src/lib; change them to use the Svelte $lib alias (e.g.,
replace imports that bring in SlashCommand, Shortcut, SlackView, SlackAppConfig,
ConnectedBotInfo, BOT_USER_ID and simulatorState from '../types' and
'../state.svelte' to import from '$lib/types' and '$lib/state.svelte'
respectively) so the file follows the project's `$lib` import convention.
---
Nitpick comments:
In `@apps/ui/src/components/Message.svelte`:
- Around line 358-398: Extract the repeated shortcut-group rendering into a
reusable Svelte snippet/component and use it in both DropdownMenu and
ContextMenu to remove duplication: create a small component/snippet (e.g.,
ShortcutList or ShortcutItem) that accepts shortcutGroups and handleShortcut as
props and renders the {`#each` group.shortcuts as shortcut (shortcut.callback_id)}
loop with the same markup (using group.botIcon,
group.botName.charAt(0).toUpperCase(), shortcut.name, and shortcut.callback_id
for keys), then replace the inline loops in the DropdownMenu.SubContent and the
ContextMenu content with that snippet/component to centralize markup and
behavior.
Show icon_url in message avatars, sidebar, and DM header instead of the hardcoded Sparkles icon. Falls back to Sparkles when no icon is provided.
Consecutive messages from the same user within 10 minutes are visually grouped — hiding the avatar and name, reducing vertical padding, and showing a 24h timestamp on hover in the avatar gutter.
- Truncate message text to stay within Slack's 3000-char mrkdwn limit in showcase shortcut modal - Gate menu separators on onDelete to prevent orphaned dividers - Add warning log when bot socket is missing during shortcut dispatch - Infer image mimetype from URL extension instead of hard-coding png
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/ui/src/components/Message.svelte (1)
9-39:⚠️ Potential issue | 🟡 MinorUse
$libalias forsrc/libimports.These imports should use the
$libalias instead of relative paths.♻️ Suggested change
- } from '../lib/types' + } from '$lib/types' ... - } from '../lib/state.svelte' + } from '$lib/state.svelte' ... - } from '../lib/dispatcher.svelte' + } from '$lib/dispatcher.svelte' ... - } from '../lib/time' + } from '$lib/time'As per coding guidelines,
apps/ui/src/**/*.{ts,tsx,svelte}: Use$libpath alias when importing fromsrc/lib/in Svelte applications.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/Message.svelte` around lines 9 - 39, Replace the relative imports that target the app's src/lib directory with the $lib path alias: update the imports that currently reference '../lib/types' (symbols: SimulatorMessage, Shortcut, SlackBlock, SlackOption, SlackSectionBlock, SlackActionsBlock, SlackContextActionsBlock, SlackInputBlock), '../lib/state.svelte' (functions: getAllMessageShortcuts, getChannelDisplayName, simulatorState, isBotUserId, getBotByUserId), '../lib/dispatcher.svelte' (sendMessageBlockAction, triggerMessageShortcut), and '../lib/time' (formatTimestamp, formatTimestampShort, formatRelativeTime, formatFullDate) to use '$lib/...' instead; leave local component imports like './blockkit/BlockKitRenderer.svelte', './blockkit/blocks/ImageBlock.svelte', and './blockkit/context' unchanged. Ensure only the path prefix changes so imports resolve via the $lib alias.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/showcase-bot/src/listeners/shortcuts/showcase-shortcuts.tsapps/ui/src/components/BotAboutHeader.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/MessagePanel.svelteapps/ui/src/components/Sidebar.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/lib/time.tspackages/slack/src/server/socket-mode.tspackages/slack/src/server/web-api.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/slack/src/server/socket-mode.ts
- apps/showcase-bot/src/listeners/shortcuts/showcase-shortcuts.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.svelte
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.svelte: Use{@html}in Svelte for mrkdwn rendering (ESLint rule disabled for this specific use case)
Use Svelte 5 runes ($state,$derived,$effect) for reactivity instead of legacy stores
Files:
apps/ui/src/components/MessagePanel.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/BotAboutHeader.svelteapps/ui/src/components/Sidebar.svelte
apps/ui/src/**/*.{ts,tsx,svelte}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
$libpath alias when importing fromsrc/lib/in Svelte applications
Files:
apps/ui/src/components/MessagePanel.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/lib/time.tsapps/ui/src/components/Message.svelteapps/ui/src/components/BotAboutHeader.svelteapps/ui/src/components/Sidebar.svelte
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript code
No semicolons in code, enforced by Prettier
Use single quotes instead of double quotes
Use trailing commas in es5 format
Prefix unused variables with underscore (_)
Files:
apps/ui/src/lib/time.tspackages/slack/src/server/web-api.ts
packages/*/src/server/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
packages/*/src/server/*.ts: Platform emulator plugins must implement WebSocket protocol for event broadcasting to the frontend
Organize platform plugin packages withserver/state.tsfor in-memory state,server/web-api.tsfor API handlers, andserver/persistence.tsfor storage
Files:
packages/slack/src/server/web-api.ts
packages/slack/src/server/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Files:
packages/slack/src/server/web-api.ts
🧠 Learnings (3)
📚 Learning: 2026-01-14T21:36:07.933Z
Learnt from: tyom
Repo: tyom/botarium PR: 1
File: apps/ui/src/components/MessagePanel.svelte:10-23
Timestamp: 2026-01-14T21:36:07.933Z
Learning: In the client-only Botarium UI (Vite + Svelte), do not add or rely on SSR-specific guards (e.g., checks for sessionStorage) in components. Since the app runs only in Electron or the browser, code that accesses browser APIs will execute in a non-SSR environment, so SSR-unsafe checks are unnecessary and can be removed for maintainability.
Applied to files:
apps/ui/src/components/MessagePanel.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/BotAboutHeader.svelteapps/ui/src/components/Sidebar.svelte
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to apps/ui/src/**/*.{ts,tsx,svelte} : Use `$lib` path alias when importing from `src/lib/` in Svelte applications
Applied to files:
apps/ui/src/components/ThreadPanel.svelte
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/socket-mode.ts : Slack API emulator must implement Socket Mode protocol for bot communication
Applied to files:
packages/slack/src/server/web-api.ts
🔇 Additional comments (12)
apps/ui/src/components/Sidebar.svelte (1)
208-220: Looks good — conditional bot avatar rendering is clean.
The fallback to Sparkles keeps the UI consistent when no icon URL is present.apps/ui/src/components/BotAboutHeader.svelte (1)
13-26: Avatar rendering update looks good.Conditional rendering is clear and consistent with the intended layout.
apps/ui/src/lib/time.ts (1)
69-92: Looks good: concise helper additions.Clear, self-contained utilities with consistent formatting and behaviour.
apps/ui/src/components/Message.svelte (4)
43-82: Props and derived state updates look solid.
isGroupedand timestampShort integration are clean and consistent with the new grouping UI.
338-452: DropdownMenu + grouped header rendering looks consistent.The grouped timestamp/identity logic and menu gating read well.
479-500: ImageBlock integration is clean.Nice consolidation of image rendering and preview routing.
557-586: Context menu parity is tidy.The shortcut submenu and delete gating match the main menu behaviour.
apps/ui/src/components/ThreadPanel.svelte (1)
94-100: Grouping logic is clear and minimal.The 10‑minute window with user matching is straightforward.
apps/ui/src/components/MessagePanel.svelte (1)
266-279: Day separator and grouping flow is coherent.The
isDaySeparatorandisGroupedlogic reads cleanly and should be easy to maintain.packages/slack/src/server/web-api.ts (3)
1974-1996: Nice improvement: MIME inference for Block Kit images.The extension-based inference avoids the hard-coded PNG assumption and keeps the payload more accurate.
1998-2022: Targeted shortcut dispatch looks solid.The switch to
message_actionand targeted routing viatargetBotaligns with the intended multi-bot behaviour.
1409-1413: No action required. Theshortcutsfield correctly returns an array.The code at line 1410 returns
shortcutsas an array (bot.appConfig.shortcuts ?? []), which is the intended design. The type definition inapps/ui/src/lib/types.ts(line 583) confirms this:shortcuts: Shortcut[]. The frontend consumer (apps/ui/src/lib/dispatcher.svelte.ts, line 433) expects and handlesshortcutsas an array, and the server-side state logic (packages/slack/src/server/state.ts, line 934) also treats it as an array. This is not a breaking change; it's consistent with the established contract.Likely an incorrect or invalid review comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/ui/src/components/Message.svelte`:
- Around line 322-327: The shortcut payload sent from handleShortcut should
include the message's Block Kit blocks so actions receive full content; update
the call to triggerMessageShortcut (in function handleShortcut) to add
message.blocks to the payload alongside ts, text, and file (use
shortcut.callback_id as before) so the payload contains { ts: message.ts, text:
message.text, file: message.file, blocks: message.blocks }.
In `@apps/ui/src/components/MessagePanel.svelte`:
- Around line 50-60: Update imports in MessagePanel.svelte to use the $lib alias
instead of relative '../lib' paths: replace imports for clearChannelMessages,
deleteMessage (from dispatcher), getChannelDisplayName, getChannelMessages,
getParentMessages, getReplyCount, hasThreadDraft, simulatorState (from state),
the Channel type (from types), and formatDateLabel, getDateKey, isWithinMinutes
(from time) so they all import from '$lib/...' counterparts; ensure named
imports and type imports remain unchanged and update any import specifiers to
the matching $lib module names.
In `@packages/slack/src/server/web-api.ts`:
- Around line 1960-1973: The current logic that builds the files array drops
other attachments when storedMessage.file exists; update the files construction
(the storedMessage and files variables) to merge storedMessage.file metadata
into the original message.files instead of replacing it: take message.files (or
empty array), map over it and for any file whose url_private matches
storedMessage.file.url_private (or other identifying field) replace/merge its
properties with storedMessage.file so stored metadata is preferred, and if
storedMessage.file exists but isn’t present in message.files append it; ensure
the resulting files array preserves all original attachments while using
storedMessage.file fields where available.
---
Outside diff comments:
In `@apps/ui/src/components/Message.svelte`:
- Around line 9-39: Replace the relative imports that target the app's src/lib
directory with the $lib path alias: update the imports that currently reference
'../lib/types' (symbols: SimulatorMessage, Shortcut, SlackBlock, SlackOption,
SlackSectionBlock, SlackActionsBlock, SlackContextActionsBlock,
SlackInputBlock), '../lib/state.svelte' (functions: getAllMessageShortcuts,
getChannelDisplayName, simulatorState, isBotUserId, getBotByUserId),
'../lib/dispatcher.svelte' (sendMessageBlockAction, triggerMessageShortcut), and
'../lib/time' (formatTimestamp, formatTimestampShort, formatRelativeTime,
formatFullDate) to use '$lib/...' instead; leave local component imports like
'./blockkit/BlockKitRenderer.svelte', './blockkit/blocks/ImageBlock.svelte', and
'./blockkit/context' unchanged. Ensure only the path prefix changes so imports
resolve via the $lib alias.
---
Duplicate comments:
In `@apps/ui/src/components/ThreadPanel.svelte`:
- Around line 3-12: Update the import paths in ThreadPanel.svelte to use the
$lib alias instead of relative ../lib paths: replace imports that reference
dispatcher.svelte (deleteMessage, sendMessage), state.svelte (getReplyCount,
getThreadDraft, getThreadMessages, setThreadDraft, simulatorState) and time
(isWithinMinutes) with their corresponding $lib/... module paths so the module
resolution follows the app guideline for Svelte files.
…in section blocks Mrkdwn links without protocol (e.g. <google.com|label>) were rendered as raw text instead of clickable links. Section blocks now use <br> for line breaks matching real Slack spacing. Added three new Block Kit template samples with action_ids.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/ui/src/components/Message.svelte (1)
1-1:⚠️ Potential issue | 🟡 MinorAddress Prettier formatting issue.
The CI pipeline reports a code style issue. Run
prettier --writeon this file to resolve.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/Message.svelte` at line 1, Prettier formatting in the Message.svelte component is failing CI; run the formatter (e.g., prettier --write) on this file so the <script lang="ts"> block and the rest of the Svelte markup are reformatted to project style and the code style check passes.
🧹 Nitpick comments (4)
packages/mrkdwn/src/mrkdwn-to-html.ts (1)
37-45: Protocol-less link regex may be overly permissive for the labelled case.The regex on line 39 (
[^@#!][^|]*?) matches any labelled link not starting with@,#, or!, regardless of whether it contains a dot. This could incorrectly convert non-URL text like<foo|bar>into<a href="https://foo">bar</a>.Consider aligning with the unlabelled regex (line 43) by requiring a dot to validate it looks like a domain:
💡 Suggested fix to require a dot in labelled links
// Links without protocol: <domain.com|label> and <domain.com> text = text.replace( - /<([^@#!][^|]*?)\|(.*?)>/g, + /<([^@#!][^|]*?\.[^|]*?)\|(.*?)>/g, '<a href="https://$1" target="_blank" rel="noopener noreferrer">$2</a>' )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mrkdwn/src/mrkdwn-to-html.ts` around lines 37 - 45, The labelled protocol-less link regex in mrkdwnToHtml (currently using the pattern starting with [^@#!][^|]*?) is too permissive and will convert tags like <foo|bar> into links; update the labelled-link replacement to mirror the unlabelled pattern by requiring a dot in the captured link portion (i.e., require a hostname-like token) so the replacement in mrkdwnToHtml only creates <a href="https://..."> for domain-like values; locate the two replacements (the function/variable performing the two text.replace calls) and change the first regex to include the dot requirement used by the second.apps/ui/src/components/blockkit/blocks/SectionBlock.svelte (1)
2-12: Use$libpath alias for imports fromsrc/lib/.The type imports use a relative path
../../../lib/typesinstead of the$libalias. As per coding guidelines, imports fromsrc/lib/in Svelte applications should use the path alias.♻️ Suggested fix
import type { SlackSectionBlock, SlackButtonElement, SlackStaticSelectElement, SlackOverflowElement, SlackRadioButtonsElement, SlackCheckboxesElement, SlackDatePickerElement, SlackTimePickerElement, SlackWorkspaceSelectElement, -} from '../../../lib/types' +} from '$lib/types'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/blockkit/blocks/SectionBlock.svelte` around lines 2 - 12, The import statement in SectionBlock.svelte uses a relative path '../../../lib/types' for types like SlackSectionBlock, SlackButtonElement, SlackStaticSelectElement, etc.; update that import to use the Svelte path alias by replacing the module source with '$lib/types' so all referenced types (SlackSectionBlock, SlackButtonElement, SlackStaticSelectElement, SlackOverflowElement, SlackRadioButtonsElement, SlackCheckboxesElement, SlackDatePickerElement, SlackTimePickerElement, SlackWorkspaceSelectElement) are imported from '$lib/types'.apps/ui/src/components/blockkit/context.ts (1)
3-7: Use$libpath alias for imports fromsrc/lib/.The import on line 7 uses a relative path instead of the
$libalias. As per coding guidelines, imports fromsrc/lib/should use the path alias.♻️ Suggested fix
import { mrkdwnToHtml, resolveEmoji } from '@botarium/mrkdwn' import DOMPurify from 'dompurify' import type { SlackViewTextObject, SlackOption, UploadedFile, -} from '../../lib/types' +} from '$lib/types'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/blockkit/context.ts` around lines 3 - 7, The import statement in apps/ui/src/components/blockkit/context.ts uses a relative path to '../../lib/types'; update it to use the $lib alias instead by replacing that import with one that imports SlackViewTextObject, SlackOption, and UploadedFile from '$lib/types' so the module resolution follows the project's path-aliasing convention.apps/ui/src/components/Message.svelte (1)
363-394: Consider extracting duplicated shortcut menu rendering.The shortcut submenu content (bot icon, shortcut name, app name) is duplicated between the
DropdownMenu(lines 369–394) andContextMenu(lines 567–588). Extracting this into a Svelte snippet or shared component would reduce duplication and simplify future maintenance.💡 Example using Svelte 5 snippet
{`#snippet` shortcutItem(group, shortcut)} <span class="flex items-center gap-2 w-full"> {`#if` group.botIcon?.startsWith('http')} <img src={group.botIcon} alt="" class="size-4 rounded object-cover shrink-0" /> {:else} <span class="shrink-0 text-sm">{group.botIcon || group.botName.charAt(0).toUpperCase()}</span> {/if} <span class="font-semibold">{shortcut.name}</span> <span class="text-slack-text-muted ml-auto text-xs">{group.botName}</span> </span> {/snippet}Then use
{@rendershortcutItem(group, shortcut)}in both menus.Also applies to: 563-588
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/Message.svelte` around lines 363 - 394, The shortcut menu item markup is duplicated between the DropdownMenu and ContextMenu rendering; extract the repeated span block into a reusable Svelte snippet or small component and use it in both places to reduce duplication. Locate the repeated code inside the loops over shortcutGroups/shortcut (references: shortcutGroups, group, shortcut, DropdownMenu.Item, handleShortcut) and replace the inner content with a call to the shared snippet/component (e.g., a Svelte 5 {`#snippet` ...} or a <ShortcutItem /> component) that accepts group and shortcut props/params, keeping onclick={() => handleShortcut(shortcut)} on the menu item wrappers.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
apps/showcase-bot/src/messages/blocks/13-template-approval.jsonapps/showcase-bot/src/messages/blocks/14-template-notification.jsonapps/showcase-bot/src/messages/blocks/15-template-vote.jsonapps/ui/src/components/Message.svelteapps/ui/src/components/MessagePanel.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/blockkit/blocks/SectionBlock.svelteapps/ui/src/components/blockkit/context.tspackages/mrkdwn/src/mrkdwn-to-html.test.tspackages/mrkdwn/src/mrkdwn-to-html.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript code
No semicolons in code, enforced by Prettier
Use single quotes instead of double quotes
Use trailing commas in es5 format
Prefix unused variables with underscore (_)
Files:
packages/mrkdwn/src/mrkdwn-to-html.test.tspackages/mrkdwn/src/mrkdwn-to-html.tsapps/ui/src/components/blockkit/context.ts
**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.ts: Test files should be colocated with source code using*.test.tsnaming convention
Use Bun's built-in test runner (bun:test) for all tests
Files:
packages/mrkdwn/src/mrkdwn-to-html.test.ts
packages/mrkdwn/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Slack mrkdwn converter must provide bidirectional conversion (mrkdwn to HTML and Markdown to mrkdwn)
Files:
packages/mrkdwn/src/mrkdwn-to-html.test.tspackages/mrkdwn/src/mrkdwn-to-html.ts
apps/ui/src/**/*.{ts,tsx,svelte}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
$libpath alias when importing fromsrc/lib/in Svelte applications
Files:
apps/ui/src/components/blockkit/context.tsapps/ui/src/components/blockkit/blocks/SectionBlock.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/MessagePanel.svelte
**/*.svelte
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.svelte: Use{@html}in Svelte for mrkdwn rendering (ESLint rule disabled for this specific use case)
Use Svelte 5 runes ($state,$derived,$effect) for reactivity instead of legacy stores
Files:
apps/ui/src/components/blockkit/blocks/SectionBlock.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/MessagePanel.svelte
🧠 Learnings (6)
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/mrkdwn/src/**/*.ts : Slack mrkdwn converter must provide bidirectional conversion (mrkdwn to HTML and Markdown to mrkdwn)
Applied to files:
packages/mrkdwn/src/mrkdwn-to-html.test.tspackages/mrkdwn/src/mrkdwn-to-html.tsapps/ui/src/components/blockkit/context.tsapps/ui/src/components/blockkit/blocks/SectionBlock.svelte
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to **/*.svelte : Use `{html}` in Svelte for mrkdwn rendering (ESLint rule disabled for this specific use case)
Applied to files:
packages/mrkdwn/src/mrkdwn-to-html.test.tspackages/mrkdwn/src/mrkdwn-to-html.tsapps/ui/src/components/blockkit/context.tsapps/ui/src/components/blockkit/blocks/SectionBlock.svelte
📚 Learning: 2026-01-14T21:36:07.933Z
Learnt from: tyom
Repo: tyom/botarium PR: 1
File: apps/ui/src/components/MessagePanel.svelte:10-23
Timestamp: 2026-01-14T21:36:07.933Z
Learning: In the client-only Botarium UI (Vite + Svelte), do not add or rely on SSR-specific guards (e.g., checks for sessionStorage) in components. Since the app runs only in Electron or the browser, code that accesses browser APIs will execute in a non-SSR environment, so SSR-unsafe checks are unnecessary and can be removed for maintainability.
Applied to files:
apps/ui/src/components/blockkit/blocks/SectionBlock.svelteapps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/Message.svelteapps/ui/src/components/MessagePanel.svelte
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to apps/ui/src/**/*.{ts,tsx,svelte} : Use `$lib` path alias when importing from `src/lib/` in Svelte applications
Applied to files:
apps/ui/src/components/ThreadPanel.svelteapps/ui/src/components/MessagePanel.svelte
📚 Learning: 2026-02-22T01:14:27.310Z
Learnt from: tyom
Repo: tyom/botarium PR: 13
File: packages/slack/src/server/web-api.ts:1960-1973
Timestamp: 2026-02-22T01:14:27.310Z
Learning: The Botarium simulator (packages/slack/src/server) only supports single file uploads, so `storedMessage.file` (singular) is the correct data structure and can replace `message.files` when present.
Applied to files:
apps/ui/src/components/Message.svelte
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to **/*.svelte : Use Svelte 5 runes (`$state`, `$derived`, `$effect`) for reactivity instead of legacy stores
Applied to files:
apps/ui/src/components/MessagePanel.svelte
🧬 Code graph analysis (2)
packages/mrkdwn/src/mrkdwn-to-html.test.ts (1)
packages/mrkdwn/src/mrkdwn-to-html.ts (1)
mrkdwnToHtml(102-215)
apps/ui/src/components/blockkit/context.ts (1)
packages/mrkdwn/src/mrkdwn-to-html.ts (1)
mrkdwnToHtml(102-215)
🪛 GitHub Actions: CI
apps/ui/src/components/Message.svelte
[warning] 1-1: Code style issues found in the above file. Run Prettier with --write to fix.
🔇 Additional comments (13)
packages/mrkdwn/src/mrkdwn-to-html.ts (1)
102-105: Good addition of theuseBroption for flexible line-break rendering.The optional
optionsparameter withuseBrprovides a clean way to toggle between<br>tags and styled spans without breaking existing callers.packages/mrkdwn/src/mrkdwn-to-html.test.ts (1)
47-63: Good test coverage for protocol-less link handling.These tests validate the new link forms well. Consider adding a negative test case for inputs like
<foo|bar>(no dot) to document expected behaviour and guard against unintended matches.apps/ui/src/components/blockkit/context.ts (1)
48-63: Clean integration of theuseBroption.The signature change properly threads the
optionsparameter through tomrkdwnToHtml, allowing callers to control line-break rendering. The plain text branch correctly bypasses mrkdwn parsing.apps/ui/src/components/blockkit/blocks/SectionBlock.svelte (1)
40-47: Correct use ofuseBr: truefor compact line breaks in section blocks.Passing
{ useBr: true }torenderMrkdwnaligns with the PR objective to use actual<br>elements for more compact rendering in section blocks. The{@html}directive is correctly used for mrkdwn rendering.apps/ui/src/components/Message.svelte (6)
1-41: LGTM!Imports are correctly structured using the
$libpath alias and appropriate type imports.
43-82: LGTM!Props and derived state correctly use Svelte 5 runes (
$props,$derived,$derived.by). The grouping and shortcut-related derived values are well-structured.
322-328: LGTM!The
handleShortcutfunction correctly passes the essential message identifiers. The server enriches the payload with full message data including blocks.
397-399: LGTM!The separator is now correctly gated by
onDelete, preventing an orphaned divider when only shortcuts are present.Also applies to: 592-594
414-442: LGTM!The grouped message rendering correctly displays a compact timestamp on hover when
isGroupedis true, and falls back to the full avatar/name display otherwise. The conditional class application for bot icons is well-handled.
481-504: LGTM!Good use of the
ImageBlockcomponent for file attachments, maintaining consistency with Block Kit image rendering. The max-height constraint on the wrapper ensures thumbnails don't dominate the message area.apps/ui/src/components/ThreadPanel.svelte (2)
1-14: LGTM!Imports correctly use the
$libpath alias and include the necessary utilities for grouping logic.
94-103: LGTM!The reply grouping logic is well-implemented. Using
parentMessageas the reference for the first reply (index 0) ensures continuity from the thread root, whilst subsequent replies correctly compare against the previous reply in the list.apps/ui/src/components/MessagePanel.svelte (1)
266-278: LGTM!The day separator and message grouping logic is well-structured:
- Short-circuit evaluation in
isDaySeparatorsafely handles the first message case before accessingprevMessage.ts- The
isGroupedcomputation correctly combines day boundary, user continuity, and the 10-minute window check
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/showcase-bot/src/messages/blocks/13-template-approval.json`:
- Around line 65-77: Fix the user-facing typos inside the JSON block values:
update the "text" value for the "*When:*" entry (currently "Submitted Aut 10")
to "Submitted Aug 10" and remove the stray trailing double-quote from the
"*Specs:*" value (currently "\"Cheetah Pro 15\" - Fast, really fast\"") so it
becomes "\"Cheetah Pro 15\" - Fast, really fast". Locate these strings in the
13-template-approval.json block entries for "When" and "Specs" and update the
text fields accordingly.
In `@apps/showcase-bot/src/messages/blocks/15-template-vote.json`:
- Around line 44-47: Update the user-facing alt text in the image block: replace
the incorrect "Pam Beasely" value for the "alt_text" property in the image
object (the block containing "type": "image" and "image_url":
"https://api.slack.com/img/blocks/bkb_template_images/profile_3.png") with the
correct surname "Pam Beesly".
---
Outside diff comments:
In `@apps/ui/src/components/Message.svelte`:
- Line 1: Prettier formatting in the Message.svelte component is failing CI; run
the formatter (e.g., prettier --write) on this file so the <script lang="ts">
block and the rest of the Svelte markup are reformatted to project style and the
code style check passes.
---
Duplicate comments:
In `@apps/ui/src/components/MessagePanel.svelte`:
- Around line 50-60: No changes required: the imports in MessagePanel.svelte
correctly use the $lib path alias (symbols: clearChannelMessages, deleteMessage,
getChannelDisplayName, getChannelMessages, getParentMessages, getReplyCount,
hasThreadDraft, simulatorState, formatDateLabel, getDateKey, isWithinMinutes),
so leave these import statements as-is and no code edits are necessary.
---
Nitpick comments:
In `@apps/ui/src/components/blockkit/blocks/SectionBlock.svelte`:
- Around line 2-12: The import statement in SectionBlock.svelte uses a relative
path '../../../lib/types' for types like SlackSectionBlock, SlackButtonElement,
SlackStaticSelectElement, etc.; update that import to use the Svelte path alias
by replacing the module source with '$lib/types' so all referenced types
(SlackSectionBlock, SlackButtonElement, SlackStaticSelectElement,
SlackOverflowElement, SlackRadioButtonsElement, SlackCheckboxesElement,
SlackDatePickerElement, SlackTimePickerElement, SlackWorkspaceSelectElement) are
imported from '$lib/types'.
In `@apps/ui/src/components/blockkit/context.ts`:
- Around line 3-7: The import statement in
apps/ui/src/components/blockkit/context.ts uses a relative path to
'../../lib/types'; update it to use the $lib alias instead by replacing that
import with one that imports SlackViewTextObject, SlackOption, and UploadedFile
from '$lib/types' so the module resolution follows the project's path-aliasing
convention.
In `@apps/ui/src/components/Message.svelte`:
- Around line 363-394: The shortcut menu item markup is duplicated between the
DropdownMenu and ContextMenu rendering; extract the repeated span block into a
reusable Svelte snippet or small component and use it in both places to reduce
duplication. Locate the repeated code inside the loops over
shortcutGroups/shortcut (references: shortcutGroups, group, shortcut,
DropdownMenu.Item, handleShortcut) and replace the inner content with a call to
the shared snippet/component (e.g., a Svelte 5 {`#snippet` ...} or a <ShortcutItem
/> component) that accepts group and shortcut props/params, keeping onclick={()
=> handleShortcut(shortcut)} on the menu item wrappers.
In `@packages/mrkdwn/src/mrkdwn-to-html.ts`:
- Around line 37-45: The labelled protocol-less link regex in mrkdwnToHtml
(currently using the pattern starting with [^@#!][^|]*?) is too permissive and
will convert tags like <foo|bar> into links; update the labelled-link
replacement to mirror the unlabelled pattern by requiring a dot in the captured
link portion (i.e., require a hostname-like token) so the replacement in
mrkdwnToHtml only creates <a href="https://..."> for domain-like values; locate
the two replacements (the function/variable performing the two text.replace
calls) and change the first regex to include the dot requirement used by the
second.
apps/showcase-bot/src/messages/blocks/13-template-approval.json
Outdated
Show resolved
Hide resolved
…wn regex Fix typos in showcase bot templates (Aut→Aug, stray quote, Beasely→Beesly), use $lib alias in SectionBlock import, extract duplicated shortcut menu item markup into a Svelte snippet, and tighten protocol-less labelled link regex to require a dot in the URL portion.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes