diff --git a/apps/showcase-bot/src/listeners/commands/showcase.ts b/apps/showcase-bot/src/listeners/commands/showcase.ts index 7df4069..c6b6ecb 100644 --- a/apps/showcase-bot/src/listeners/commands/showcase.ts +++ b/apps/showcase-bot/src/listeners/commands/showcase.ts @@ -228,6 +228,97 @@ export function register(app: App) { ], }, }, + { + type: 'input', + label: { type: 'plain_text', text: 'Department' }, + element: { + type: 'static_select', + action_id: 'showcase_modal_department', + placeholder: { + type: 'plain_text', + text: 'Select a department', + }, + options: [ + { + text: { type: 'plain_text', text: 'Engineering' }, + value: 'engineering', + }, + { + text: { type: 'plain_text', text: 'Design' }, + value: 'design', + }, + { + text: { type: 'plain_text', text: 'Marketing' }, + value: 'marketing', + }, + { + text: { type: 'plain_text', text: 'Sales' }, + value: 'sales', + }, + ], + }, + }, + { + type: 'input', + label: { type: 'plain_text', text: 'Skills' }, + element: { + type: 'multi_static_select', + action_id: 'showcase_modal_skills', + placeholder: { + type: 'plain_text', + text: 'Select your skills', + }, + options: [ + { + text: { type: 'plain_text', text: 'JavaScript' }, + value: 'javascript', + }, + { + text: { type: 'plain_text', text: 'TypeScript' }, + value: 'typescript', + }, + { + text: { type: 'plain_text', text: 'Python' }, + value: 'python', + }, + { + text: { type: 'plain_text', text: 'Rust' }, + value: 'rust', + }, + { + text: { type: 'plain_text', text: 'Go' }, + value: 'go', + }, + ], + }, + }, + { + type: 'input', + label: { type: 'plain_text', text: 'Attachments' }, + element: { + type: 'file_input', + action_id: 'showcase_modal_attachments', + max_files: 3, + }, + }, + { + type: 'input', + label: { type: 'plain_text', text: 'Additional Notes' }, + hint: { + type: 'plain_text', + text: 'Any extra details or comments', + }, + optional: true, + element: { + type: 'plain_text_input', + action_id: 'showcase_modal_notes', + multiline: true, + placeholder: { + type: 'plain_text', + text: 'Enter any additional notes here...', + }, + }, + }, ], }, }) diff --git a/apps/showcase-bot/src/messages/blocks/16-search-results.json b/apps/showcase-bot/src/messages/blocks/16-search-results.json new file mode 100644 index 0000000..f09812f --- /dev/null +++ b/apps/showcase-bot/src/messages/blocks/16-search-results.json @@ -0,0 +1,157 @@ +{ + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "We found *205 Hotels* in New Orleans, LA from *12/14 to 12/17*" + }, + "accessory": { + "type": "overflow", + "action_id": "showcase_search_results_filter", + "options": [ + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Option One" + }, + "value": "value-0" + }, + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Option Two" + }, + "value": "value-1" + }, + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Option Three" + }, + "value": "value-2" + }, + { + "text": { + "type": "plain_text", + "emoji": true, + "text": "Option Four" + }, + "value": "value-3" + } + ] + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "**\n★★★★★\n$340 per night\nRated: 9.4 - Excellent" + }, + "accessory": { + "type": "image", + "image_url": "https://api.slack.com/img/blocks/bkb_template_images/tripAgent_1.png", + "alt_text": "Windsor Court Hotel thumbnail" + } + }, + { + "type": "context", + "elements": [ + { + "type": "image", + "image_url": "https://api.slack.com/img/blocks/bkb_template_images/tripAgentLocationMarker.png", + "alt_text": "Location Pin Icon" + }, + { + "type": "plain_text", + "emoji": true, + "text": "Location: Central Business District" + } + ] + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "**\n★★★★★\n$340 per night\nRated: 9.1 - Excellent" + }, + "accessory": { + "type": "image", + "image_url": "https://api.slack.com/img/blocks/bkb_template_images/tripAgent_2.png", + "alt_text": "Ritz-Carlton New Orleans thumbnail" + } + }, + { + "type": "context", + "elements": [ + { + "type": "image", + "image_url": "https://api.slack.com/img/blocks/bkb_template_images/tripAgentLocationMarker.png", + "alt_text": "Location Pin Icon" + }, + { + "type": "plain_text", + "emoji": true, + "text": "Location: French Quarter" + } + ] + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "**\n★★★★★\n$419 per night\nRated: 8.8 - Excellent" + }, + "accessory": { + "type": "image", + "image_url": "https://api.slack.com/img/blocks/bkb_template_images/tripAgent_3.png", + "alt_text": "Omni Royal Orleans Hotel thumbnail" + } + }, + { + "type": "context", + "elements": [ + { + "type": "image", + "image_url": "https://api.slack.com/img/blocks/bkb_template_images/tripAgentLocationMarker.png", + "alt_text": "Location Pin Icon" + }, + { + "type": "plain_text", + "emoji": true, + "text": "Location: French Quarter" + } + ] + }, + { + "type": "divider" + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "emoji": true, + "text": "Next 2 Results" + }, + "action_id": "showcase_search_results_next", + "value": "click_me_123" + } + ] + } + ] +} diff --git a/apps/ui/src/app.css b/apps/ui/src/app.css index 8520868..56e0cdf 100644 --- a/apps/ui/src/app.css +++ b/apps/ui/src/app.css @@ -3,6 +3,10 @@ @custom-variant dark (&:where(.dark, .dark *)); +button { + cursor: pointer; +} + @theme inline { /* Typography */ --font-sans: diff --git a/apps/ui/src/components/Message.svelte b/apps/ui/src/components/Message.svelte index 0dd9e15..8515aed 100644 --- a/apps/ui/src/components/Message.svelte +++ b/apps/ui/src/components/Message.svelte @@ -354,9 +354,7 @@
{#if isEphemeral}
- import { X } from '@lucide/svelte' + import { X, Sparkles } from '@lucide/svelte' import { simulatorState } from '../lib/state.svelte' import { submitView, @@ -41,6 +41,16 @@ // Track whether the user has attempted to submit (enables live revalidation) let hasSubmitted = $state(false) + // Track whether modal content is scrolled + let contentScrolled = $state(false) + + // Look up the bot that opened the modal + const modalBot = $derived.by(() => { + const botId = simulatorState.activeModal?.botId + if (!botId) return undefined + return simulatorState.connectedBots.get(botId) + }) + /** * Extract initial values from modal blocks to pre-populate formValues */ @@ -148,6 +158,7 @@ fileFormValues = {} validationErrors = {} hasSubmitted = false + contentScrolled = false } }) @@ -324,9 +335,31 @@ >
- @@ -370,7 +408,7 @@ {#if modal.view.submit} @@ -380,3 +418,9 @@
{/if} + + diff --git a/apps/ui/src/components/blockkit/blocks/InputBlock.svelte b/apps/ui/src/components/blockkit/blocks/InputBlock.svelte index ef23d4a..93bfa07 100644 --- a/apps/ui/src/components/blockkit/blocks/InputBlock.svelte +++ b/apps/ui/src/components/blockkit/blocks/InputBlock.svelte @@ -82,12 +82,16 @@ function getFiles(actionId: string): UploadedFile[] { return fileValues[blockId]?.[actionId] ?? [] } + + const inputId = $derived(`input-${blockId}-${block.element.action_id}`)
{#if block.element.type !== 'file_input'} - -