Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
29ae6e5
code added for the modify button functionality
qjhuangAWS Jul 16, 2025
cd6e9fc
Fix ESLint errors for modify button functionality
qjhuangAWS Jul 16, 2025
3fe72b5
Fix additional ESLint errors for modify button functionality
qjhuangAWS Jul 16, 2025
3fd78fc
Fix all remaining ESLint errors in tests
qjhuangAWS Jul 16, 2025
61dc562
Fix ESLint indentation errors with --fix
qjhuangAWS Jul 16, 2025
e697418
move the test files to _test_ folder
qjhuangAWS Jul 16, 2025
033d717
Resolve merge conflicts between feature/modify-button-functionality a…
qjhuangAWS Jul 16, 2025
0a8e977
fix: restore modify button functionality accidentally removed during …
qjhuangAWS Jul 17, 2025
e4f4554
fixing unit test failure
qjhuangAWS Jul 17, 2025
8728dee
fix: update e2e test snapshots for modify button functionality
qjhuangAWS Jul 17, 2025
b62f960
fix: resolve ESLint errors in test file
qjhuangAWS Jul 17, 2025
8645c68
Fix E2E test snapshots by downgrading Playwright to v1.50.0
qjhuangAWS Jul 17, 2025
4c7d308
Merge branch 'main' into feature/modify-button-functionality
qjhuangAWS Jul 17, 2025
e635558
Revert "Fix E2E test snapshots by downgrading Playwright to v1.50.0"
qjhuangAWS Jul 17, 2025
a673556
Revert "fix: update e2e test snapshots for modify button functionality"
qjhuangAWS Jul 17, 2025
6d2b373
commented code removed and datamodel.md updated
qjhuangAWS Jul 18, 2025
a2bc864
revert changes made to formatting
qjhuangAWS Jul 18, 2025
8145acd
removed live-server
qjhuangAWS Jul 18, 2025
b4e99fe
update snapshot on passing case "keep the content inside window bound…
qjhuangAWS Jul 18, 2025
62d77aa
removed the package-lock.json changes
qjhuangAWS Jul 19, 2025
a577973
removed the package-lock.json changes version fix
qjhuangAWS Jul 19, 2025
06aa15d
resolved comments in pr for reused code block, naming, and sample-dat…
qjhuangAWS Jul 21, 2025
1cba30d
Revert "update snapshot on passing case "keep the content inside wind…
qjhuangAWS Jul 22, 2025
131b318
Merge branch 'aws:main' into feature/modify-button-functionality
qjhuangAWS Jul 23, 2025
602bc0d
Merge branch 'feature/modify-button-functionality' of https://github.…
qjhuangAWS Jul 23, 2025
5b93259
removed uncessary added changes
qjhuangAWS Jul 24, 2025
4814b72
Merge branch 'aws:main' into feature/modify-button-functionality
qjhuangAWS Aug 5, 2025
47e07eb
Merge branch 'feature/modify-button-functionality' of https://github.…
qjhuangAWS Aug 5, 2025
2ba9398
Clean structure with all comments addressed
qjhuangAWS Aug 5, 2025
3a03c12
Fix main ESLint errors in implementation files
qjhuangAWS Aug 5, 2025
6fb5354
ESLlinting errors fixed for the test cases
qjhuangAWS Aug 5, 2025
d89ec05
removed uncessary change
qjhuangAWS Aug 5, 2025
68abfc7
removed duplicate notes on DATAMODEL.md
qjhuangAWS Aug 5, 2025
e490672
updated datamodel.md
qjhuangAWS Aug 5, 2025
5f3a1d0
Added dynamic resizing and background theme change to textarea
qjhuangAWS Aug 8, 2025
3e1d3aa
fixed ESlint issues for push
qjhuangAWS Aug 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/DATAMODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,7 @@ interface ChatItemContent {
interface ChatItem extends ChatItemContent {
type: ChatItemType;
messageId?: string;
editable?: boolean;
snapToTop?: boolean;
autoCollapse?: boolean;
contentHorizontalAlignment?: 'default' | 'center';
Expand Down Expand Up @@ -3323,6 +3324,61 @@ mynahUI.addChatItem(tabId, {
<img src="./img/data-model/chatItems/iconStatus.png" alt="icon" style="max-width:500px; width:100%;border: 1px solid #e0e0e0;">
</p>

## `editable` (default: `false`)
The `editable` property enables users to modify the content of chat items directly within the chat interface. When set to `true`, the chat item displays interactive controls that allow users to edit command text and see immediate changes.

**Key Features:**
- **Inline Editing**: Users can modify shell commands directly in the chat response
- **Button State Management**: Shows "Modify" button in normal state, "Save"/"Cancel" buttons in edit mode
- **Text Extraction**: Automatically extracts commands from markdown code blocks (e.g., `\`\`\`shell\inputted_command\n\`\`\``)
- **State Preservation**: Original commands are preserved during editing and restored on cancel
- **Event Integration**: Modified content is sent to backend via `editedText` parameter in button click events

**Usage Example:**
```typescript
const mynahUI = new MynahUI({
tabs: {
'tab-1': {
...
}
}
});

mynahUI.addChatItem('tab-1', {
type: ChatItemType.ANSWER,
messageId: 'editable-command-1',
editable: true,
body: '```shell\nnpm install react\n```',
buttons: [
{
id: 'run-shell-command',
text: 'Run',
status: 'primary'
},
{
id: 'reject-shell-command',
text: 'Reject',
status: 'clear'
}
]
});
```

**User Workflow:**
1. **Initial State**: Command appears with "Modify", "Run", "Reject" buttons
2. **Edit Mode**: Click "Modify" → UI switches to editable textarea with "Save"/"Cancel" buttons
3. **Editing**: User modifies command with auto-focus and text selection
4. **Save**: Click "Save" → Edited command sent to backend, UI returns to normal view
5. **Cancel**: Click "Cancel" → Original command restored, UI returns to normal view

**Technical Implementation:**
- **State Management**: Uses `isOnEdit` internal state for edit mode tracking
- **UI Switching**: Seamlessly transitions between CardBody display and textarea input
- **Event Handling**: Integrates with existing button click events, adding `editedText` parameter for backend processing
- **Accessibility**: Auto-focus and text selection in edit mode for improved user experience

**Note:** The `editable` property works best with shell command content but can be used with any text content. When combined with buttons, the modify workflow automatically manages button states and integrates with the existing event system.

---

## `followUp`
Expand Down
157 changes: 157 additions & 0 deletions example/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
mcpToolRunSampleCard,
mcpToolRunSampleCardInit,
sampleRulesList,
shellCommandWithModifyEditable,
accountDetailsTabData,
} from './samples/sample-data';
import escapeHTML from 'escape-html';
Expand Down Expand Up @@ -1384,6 +1385,157 @@ here to see if it gets cut off properly as expected, with an ellipsis through cs
},
},
});
} else if (action.id === 'modify-example-command') {
// Example: Show how to pass data for modify action
Log(`Modify bash command clicked for message ${messageId}`);
Log(`Example data passing: action=${action.id}, messageId=${messageId}, tabId=${tabId}`);

// Example: Demonstrate how to update chat item with new data
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This shows how to pass data to update a chat item body',
header: {
body: '**Example Header Update**',
buttons: [
{ id: 'save-example-command', text: 'Save', icon: MynahIcons.OK, status: 'clear' },
{ id: 'cancel-example-command', text: 'Cancel', icon: MynahIcons.CANCEL, status: 'dimmed-clear' },
],
},
});
return false;
} else if (action.id === 'save-example-command') {
// Example: Show how to pass data for save action
Log(`Save command clicked for message ${messageId}`);
Log(`Example: Passing save data to Mynah UI component`);

// Example: Demonstrate how to pass updated data back to chat item
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This shows how data flows back after save action',
header: {
body: '**Saved State Example**',
buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
});
return false;
} else if (action.id === 'cancel-example-command') {
// Example: Show how to pass data for cancel action
Log(`Cancel edit clicked for message ${messageId}`);
Log(`Example: Demonstrating data restoration in Mynah UI`);

// Example: Show how to restore original data
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This demonstrates how to restore original data on cancel',
header: {
body: '**Original State Restored**',
buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
});
return false;
} else if (action.id === 'reject-bash-command' || action.id === 'run-bash-command') {
// Example: Show how to pass different action data to Mynah UI
Log(`${action.id} clicked for message ${messageId}`);
Log(`Example: Demonstrating how to pass action-specific data to components`);

if (action.id === 'reject-bash-command') {
// Example: Show data passing for reject action
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This shows how to pass reject action data to Mynah UI',
header: {
body: '**Rejected State Example**',
buttons: [
{ id: 'restore-original-buttons', text: 'Try Again', icon: MynahIcons.REFRESH, status: 'clear' },
],
},
});
} else {
// Example: Show data passing for run action
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This demonstrates how to pass execution data to Mynah UI components',
header: {
body: '**Running State Example**',
buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
});
}
return false;
} else if (action.id === 'restore-original-buttons') {
// Example: Show how to restore original button state
Log(`Restore original buttons clicked for message ${messageId}`);
Log(`Example: Demonstrating how to restore original UI state`);

// Example: Restore original buttons after reject -> try again
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This shows how to restore the original button state',
header: {
body: '**Original Buttons Restored**',
buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
});
return false;
} else if (action.id === 'reject-bash-command' || action.id === 'run-bash-command') {
// Example: Show how to pass different action data to Mynah UI
Log(`${action.id} clicked for message ${messageId}`);
Log(`Example: Demonstrating how to pass action-specific data to components`);

if (action.id === 'reject-bash-command') {
// Example: Show data passing for reject action
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This shows how to pass reject action data to Mynah UI',
header: {
body: '**Rejected State Example**',
buttons: [
{ id: 'restore-original-buttons', text: 'Try Again', icon: MynahIcons.REFRESH, status: 'clear' },
],
},
});
} else {
// Example: Show data passing for run action
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This demonstrates how to pass execution data to Mynah UI components',
header: {
body: '**Running State Example**',
buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
});
}
return false;
} else if (action.id === 'restore-original-buttons') {
// Example: Show how to restore original button state
Log(`Restore original buttons clicked for message ${messageId}`);
Log(`Example: Demonstrating how to restore original UI state`);

// Example: Restore original buttons after reject
mynahUI.updateChatAnswerWithMessageId(tabId, messageId, {
body: '**Example:** This shows how to restore the original button state',
header: {
body: '**Original Buttons Restored**',
buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
});
return false;
} else if (action.id === 'quick-start') {
mynahUI.updateStore(tabId, {
tabHeaderDetails: null,
Expand Down Expand Up @@ -1620,6 +1772,11 @@ here to see if it gets cut off properly as expected, with an ellipsis through cs
break;
case Commands.HEADER_TYPES:
sampleHeaderTypes.forEach((ci) => mynahUI.addChatItem(tabId, ci));
// Add the shell command with modify button example
mynahUI.addChatItem(tabId, {
...shellCommandWithModifyEditable,
messageId: generateUID(),
});
break;
case Commands.SUMMARY_CARD:
const cardId = generateUID();
Expand Down
35 changes: 34 additions & 1 deletion example/src/samples/sample-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2547,4 +2547,37 @@ export const sampleMCPDetails = (title: string): DetailedList => {
export const sampleRulesList: DetailedList = {selectable: 'clickable', list: [{children: [{id: 'README', icon: MynahIcons.CHECK_LIST,
description: 'README',actions: [{ id: 'README.md', icon: MynahIcons.OK, status: 'clear' }]}]},
{groupName: '.amazonq/rules', childrenIndented: true, icon: MynahIcons.FOLDER , actions: [{ id: 'java-expert.md', icon: MynahIcons.OK, status: 'clear' }], children: [{id: 'java-expert.md', icon: MynahIcons.CHECK_LIST,
description: 'java-expert',actions: [{ id: 'java-expert.md', icon: MynahIcons.OK, status: 'clear' }]}]}]}
description: 'java-expert',actions: [{ id: 'java-expert.md', icon: MynahIcons.OK, status: 'clear' }]}]}]}

export const shellCommandWithModifyEditable: ChatItem = {
fullWidth: true,
padding: false,
type: ChatItemType.ANSWER,
messageId: 'shell-cmd-1',
body: ['```bash', 'ls', '```'].join('\n'),
editable: false, // start view-only
header: {
// pick an existing icon—let's use BLOCK as our "shell" glyph
icon: MynahIcons.BLOCK,

buttons: [
{ id: 'run-bash-command', text: 'Run', icon: MynahIcons.PLAY, status: 'primary' },
{ id: 'reject-bash-command', text: 'Reject', icon: MynahIcons.CANCEL, status: 'error' },
{ id: 'modify-example-command', text: 'Modify', icon: MynahIcons.PENCIL, status: 'clear' },
],
},
// these drive the little buttons that appear *in* the code block
codeBlockActions: {
'run-bash-command': {
id: 'run-bash-command',
label: 'Run', // ← was `text`
icon: MynahIcons.PLAY,
flash: 'infinite',
},
'reject-bash-command': {
id: 'reject-bash-command',
label: 'Reject', // ← was `text`
icon: MynahIcons.CANCEL,
},
},
};
Loading