mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08:00
feat(core): adjust the layout, style, and structure of the AI chat input (#12828)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for image uploads in the chat panel, including upload limits and user feedback when limits are exceeded. - Introduced a unified chat input preference menu for selecting AI models, toggling extended thinking, and enabling web search. - Menu buttons and menus now support test identifiers for improved testing. - **Improvements** - Updated chat input UI with enhanced styling, consolidated controls, and simplified feature toggling. - Improved layout and spacing for chat chips and image preview grids. - Chat abort icon now adapts to the current color theme. - **Refactor** - Replaced the separate AI model selection component with the new chat input preference menu. - Streamlined imports and custom element registrations for chat input preferences. - **Tests** - Enhanced test utilities to support the new chat input preference menu interactions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -11,6 +11,7 @@ import { property } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
import type { ClassInfo } from 'lit-html/directives/class-map.js';
|
||||
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
||||
|
||||
import { MenuFocusable } from './focusable.js';
|
||||
import type { Menu } from './menu.js';
|
||||
@@ -21,6 +22,7 @@ export type MenuButtonData = {
|
||||
class: ClassInfo;
|
||||
select: (ele: HTMLElement) => void | false;
|
||||
onHover?: (hover: boolean) => void;
|
||||
testId?: string;
|
||||
};
|
||||
|
||||
export class MenuButton extends MenuFocusable {
|
||||
@@ -97,7 +99,12 @@ export class MenuButton extends MenuFocusable {
|
||||
focused: this.isFocused$.value,
|
||||
...this.data.class,
|
||||
});
|
||||
return html` <div class="${classString}">${this.data.content()}</div>`;
|
||||
return html` <div
|
||||
class="${classString}"
|
||||
data-testid=${ifDefined(this.data.testId)}
|
||||
>
|
||||
${this.data.content()}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
@@ -157,7 +164,12 @@ export class MobileMenuButton extends MenuFocusable {
|
||||
focused: this.isFocused$.value,
|
||||
...this.data.class,
|
||||
});
|
||||
return html` <div class="${classString}">${this.data.content()}</div>`;
|
||||
return html` <div
|
||||
class="${classString}"
|
||||
data-testid=${ifDefined(this.data.testId)}
|
||||
>
|
||||
${this.data.content()}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
@@ -188,6 +200,7 @@ export const menuButtonItems = {
|
||||
onHover?: (hover: boolean) => void;
|
||||
class?: MenuClass;
|
||||
hide?: () => boolean;
|
||||
testId?: string;
|
||||
}) =>
|
||||
menu => {
|
||||
if (config.hide?.() || !menu.search(config.name)) {
|
||||
@@ -209,6 +222,7 @@ export const menuButtonItems = {
|
||||
'selected-item': config.isSelected ?? false,
|
||||
...config.class,
|
||||
},
|
||||
testId: config.testId,
|
||||
};
|
||||
return renderButton(data, menu);
|
||||
},
|
||||
@@ -220,6 +234,7 @@ export const menuButtonItems = {
|
||||
label?: () => TemplateResult;
|
||||
select: (checked: boolean) => boolean;
|
||||
class?: ClassInfo;
|
||||
testId?: string;
|
||||
}) =>
|
||||
menu => {
|
||||
if (!menu.search(config.name)) {
|
||||
@@ -240,6 +255,7 @@ export const menuButtonItems = {
|
||||
return false;
|
||||
},
|
||||
class: config.class ?? {},
|
||||
testId: config.testId,
|
||||
};
|
||||
return html`${keyed(config.name, renderButton(data, menu))}`;
|
||||
},
|
||||
@@ -247,10 +263,12 @@ export const menuButtonItems = {
|
||||
(config: {
|
||||
name: string;
|
||||
on: boolean;
|
||||
prefix?: TemplateResult;
|
||||
postfix?: TemplateResult;
|
||||
label?: () => TemplateResult;
|
||||
onChange: (on: boolean) => void;
|
||||
class?: ClassInfo;
|
||||
testId?: string;
|
||||
}) =>
|
||||
menu => {
|
||||
if (!menu.search(config.name)) {
|
||||
@@ -262,6 +280,7 @@ export const menuButtonItems = {
|
||||
|
||||
const data: MenuButtonData = {
|
||||
content: () => html`
|
||||
${config.prefix}
|
||||
<div class="affine-menu-action-text">
|
||||
${config.label?.() ?? config.name}
|
||||
</div>
|
||||
@@ -276,6 +295,7 @@ export const menuButtonItems = {
|
||||
return false;
|
||||
},
|
||||
class: config.class ?? {},
|
||||
testId: config.testId,
|
||||
};
|
||||
return html`${keyed(config.name, renderButton(data, menu))}`;
|
||||
},
|
||||
|
||||
@@ -23,6 +23,7 @@ export type MenuOptions = {
|
||||
placeholder?: string;
|
||||
};
|
||||
items: MenuConfig[];
|
||||
testId?: string;
|
||||
};
|
||||
|
||||
// Global menu open listener type
|
||||
@@ -72,6 +73,9 @@ export class Menu {
|
||||
? document.createElement('mobile-menu')
|
||||
: document.createElement('affine-menu');
|
||||
this.menuElement.menu = this;
|
||||
if (this.options.testId) {
|
||||
this.menuElement.dataset.testid = this.options.testId;
|
||||
}
|
||||
|
||||
// Call global menu open listeners
|
||||
menuOpenListeners.forEach(listener => {
|
||||
|
||||
Reference in New Issue
Block a user