Skip to content

Commit fcaf1df

Browse files
luoxuanzaoQoder-AI
andcommitted
feat(composer): move selection chips below the textarea
The top context row now carries only the image preview. Editor, browser and canvas selection chips render in a new row between the textarea and the toolbar, so highlighting text no longer inserts a strip above the typing area. The overflow collapse and the has-content show/hide logic follow the chips to the new row; image changes refresh the top row directly. Co-authored-by: QoderAI (Qwen 3.8 Max) <qoder_ai@qoder.com>
1 parent a450edf commit fcaf1df

4 files changed

Lines changed: 35 additions & 16 deletions

File tree

‎src/features/chat/tabs/tab-dom.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export function buildTabDOM(contentEl: HTMLElement): TabDOMElements {
2121
dir: 'auto',
2222
},
2323
});
24+
const selectionRowEl = inputWrapper.createDiv({ cls: 'qoderian-selection-row' });
2425

2526
return {
2627
contentEl,
@@ -35,6 +36,7 @@ export function buildTabDOM(contentEl: HTMLElement): TabDOMElements {
3536
inputEl,
3637
navRowEl,
3738
contextRowEl,
39+
selectionRowEl,
3840
selectionIndicatorEl: null,
3941
browserIndicatorEl: null,
4042
canvasIndicatorEl: null,

‎src/features/chat/tabs/tab.ts‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { openReferenceChip } from '../../../shared/obsidian/compat';
1919
import { BrowserSelectionController } from '../controllers/browser-selection-controller';
2020
import { CanvasSelectionController } from '../controllers/canvas-selection-controller';
2121
import { ContextRowOverflowController } from '../controllers/context-row-overflow';
22+
import { updateContextRowHasContent } from '../controllers/context-row-visibility';
2223
import { ConversationController } from '../controllers/conversation-controller';
2324
import { InputController } from '../controllers/input-controller';
2425
import { NavigationController } from '../controllers/navigation-controller';
@@ -305,6 +306,7 @@ function initializeContextManagers(tab: TabData, plugin: QoderianPlugin): void {
305306
tab.controllers.selectionController?.updateContextRowVisibility();
306307
tab.controllers.browserSelectionController?.updateContextRowVisibility();
307308
tab.controllers.canvasSelectionController?.updateContextRowVisibility();
309+
updateContextRowHasContent(dom.contextRowEl);
308310
autoResizeTextarea(dom.inputEl);
309311
tab.renderer?.scrollToBottomIfNeeded();
310312
updateComposerSendAvailability(tab);
@@ -590,27 +592,27 @@ export function initializeTabUI(
590592
// Initialize context managers (file/image)
591593
initializeContextManagers(tab, plugin);
592594

593-
// Selection chips - add to contextRowEl (pill style: icon + label + remove)
595+
// Selection chips - add to selectionRowEl below the textarea (pill style: icon + label + remove)
594596
dom.selectionIndicatorEl = createSelectionChip(
595-
dom.contextRowEl,
597+
dom.selectionRowEl,
596598
'qoderian-selection-indicator',
597599
'text-select'
598600
);
599601

600602
dom.browserIndicatorEl = createSelectionChip(
601-
dom.contextRowEl,
603+
dom.selectionRowEl,
602604
'qoderian-browser-selection-indicator',
603605
'globe'
604606
);
605607

606608
dom.canvasIndicatorEl = createSelectionChip(
607-
dom.contextRowEl,
609+
dom.selectionRowEl,
608610
'qoderian-canvas-indicator',
609611
'network'
610612
);
611613

612614
// Collapse chips into "+N more" when the sidebar is too narrow.
613-
tab.controllers.contextRowOverflow = new ContextRowOverflowController(dom.contextRowEl);
615+
tab.controllers.contextRowOverflow = new ContextRowOverflowController(dom.selectionRowEl);
614616

615617
const catalogInfo = options.getQoderCatalogConfig?.() ?? null;
616618
initializeSlashCommands(
@@ -836,7 +838,7 @@ export function initializeTabControllers(
836838
plugin.app,
837839
dom.selectionIndicatorEl!,
838840
dom.inputEl,
839-
dom.contextRowEl,
841+
dom.selectionRowEl,
840842
() => autoResizeTextarea(dom.inputEl),
841843
[dom.contentEl, dom.inputComposerEl, ...getSharedSelectionFocusScopeEls(component)],
842844
);
@@ -845,15 +847,15 @@ export function initializeTabControllers(
845847
plugin.app,
846848
dom.browserIndicatorEl!,
847849
dom.inputEl,
848-
dom.contextRowEl,
850+
dom.selectionRowEl,
849851
() => autoResizeTextarea(dom.inputEl)
850852
);
851853

852854
tab.controllers.canvasSelectionController = new CanvasSelectionController(
853855
plugin.app,
854856
dom.canvasIndicatorEl!,
855857
dom.inputEl,
856-
dom.contextRowEl,
858+
dom.selectionRowEl,
857859
() => autoResizeTextarea(dom.inputEl)
858860
);
859861

‎src/features/chat/tabs/types.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,12 @@ export interface TabDOMElements {
158158
/** Nav row for tab badges and header icons (above input wrapper). */
159159
navRowEl: HTMLElement;
160160

161-
/** Context row for file chips and selection indicator (inside input wrapper). */
161+
/** Context row for the image preview (inside input wrapper, above textarea). */
162162
contextRowEl: HTMLElement;
163163

164+
/** Row for selection chips (inside input wrapper, below textarea). */
165+
selectionRowEl: HTMLElement;
166+
164167
selectionIndicatorEl: HTMLElement | null;
165168
browserIndicatorEl: HTMLElement | null;
166169
canvasIndicatorEl: HTMLElement | null;

‎src/style/components/input.css‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,33 +85,45 @@ body.qoderian-composer-resizing * {
8585
user-select: none;
8686
}
8787

88-
/* Context row (context chips: files, images, selections) - inside input wrapper at top */
88+
/* Context row (image preview) above the textarea and selection row (selection
89+
chips) below it - both inside the input wrapper */
8990
/* Collapsed by default; expanded via .has-content class; textarea fills remaining space */
90-
.qoderian-context-row {
91+
.qoderian-context-row,
92+
.qoderian-selection-row {
9193
display: none;
9294
position: relative;
9395
align-items: flex-start;
9496
justify-content: flex-start;
9597
flex-shrink: 0;
96-
padding: 6px 10px 0 10px;
9798
gap: 8px;
9899
}
99100

100-
/* Show context row when it has visible content */
101-
.qoderian-context-row.has-content {
101+
.qoderian-context-row {
102+
padding: 6px 10px 0 10px;
103+
}
104+
105+
.qoderian-selection-row {
106+
padding: 0 10px 6px 10px;
107+
}
108+
109+
/* Show a row when it has visible content */
110+
.qoderian-context-row.has-content,
111+
.qoderian-selection-row.has-content {
102112
display: flex;
103113
}
104114

105115
/* Overflow collapse: chips that do not fit a narrow row are parked behind a
106116
"+N more" pill. Hidden items stay measurable (absolute + invisible). */
107-
.qoderian-context-row > .qoderian-context-overflow-hidden {
117+
.qoderian-context-row > .qoderian-context-overflow-hidden,
118+
.qoderian-selection-row > .qoderian-context-overflow-hidden {
108119
position: absolute;
109120
visibility: hidden;
110121
pointer-events: none;
111122
}
112123

113124
/* Expanded state (pill clicked): wrap so every chip stays reachable. */
114-
.qoderian-context-row.qoderian-context-row--expanded {
125+
.qoderian-context-row.qoderian-context-row--expanded,
126+
.qoderian-selection-row.qoderian-context-row--expanded {
115127
flex-wrap: wrap;
116128
}
117129

0 commit comments

Comments
 (0)