diff --git a/frontend/src/chat.css b/frontend/src/chat.css
index 79b79d2..1a5f9d5 100644
--- a/frontend/src/chat.css
+++ b/frontend/src/chat.css
@@ -473,3 +473,6 @@ header h1 { font-size: 16px; font-weight: 700; letter-spacing: -.02em; margin: 0
.viewer-body { flex: 1; overflow: auto; padding: 12px; background: #fff; color: #111; }
.viewer-body table { border-collapse: collapse; font-size: 12px; }
.viewer-body td, .viewer-body th { border: 1px solid #ccc; padding: 3px 8px; white-space: nowrap; }
+
+/* === 用户消息:气泡 + 下方附件(同列右对齐) === */
+.msg-stack { display: flex; flex-direction: column; align-items: flex-end; max-width: 100%; }
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index 1818975..e1ff874 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -57,16 +57,33 @@ function renderPendingAttachment(): void {
}
function attachmentChipHtml(att: AttachmentRef): string {
- return '
';
+ return '';
}
function bindAttachChips(root: HTMLElement): void {
root.querySelectorAll('.attach-chip').forEach((btn) => {
btn.onclick = () => { void openAttachmentViewer(btn.dataset.ft || 'requirements', btn.dataset.name || ''); };
});
}
+// 用户消息:文字在气泡内,附件在气泡**下方**(同一列、右对齐)
function addUserMsg(text: string, att: AttachmentRef | null): void {
- const m = addMsg('user', esc(text) + (att ? attachmentChipHtml(att) : ''));
+ const m = document.createElement('div');
+ m.className = 'msg user';
+ const stack = document.createElement('div');
+ stack.className = 'msg-stack';
+ const bubble = document.createElement('div');
+ bubble.className = 'bubble';
+ bubble.innerHTML = esc(text);
+ stack.appendChild(bubble);
+ if (att) {
+ const line = document.createElement('div');
+ line.className = 'attach-line';
+ line.innerHTML = attachmentChipHtml(att);
+ stack.appendChild(line);
+ }
+ m.appendChild(stack);
+ chatEl.appendChild(m);
+ chatEl.scrollTop = chatEl.scrollHeight;
bindAttachChips(m);
}
diff --git a/src/genesis/server/static/chat.css b/src/genesis/server/static/chat.css
index a85cc7f..c70a817 100644
--- a/src/genesis/server/static/chat.css
+++ b/src/genesis/server/static/chat.css
@@ -1295,3 +1295,9 @@ header h1 {
padding: 3px 8px;
white-space: nowrap;
}
+.msg-stack {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ max-width: 100%;
+}
diff --git a/src/genesis/server/static/chat.js b/src/genesis/server/static/chat.js
index 756c5f1..135a1fc 100644
--- a/src/genesis/server/static/chat.js
+++ b/src/genesis/server/static/chat.js
@@ -26948,7 +26948,7 @@ function renderPendingAttachment() {
};
}
function attachmentChipHtml(att) {
- return '";
+ return '";
}
function bindAttachChips(root) {
root.querySelectorAll(".attach-chip").forEach((btn) => {
@@ -26958,7 +26958,23 @@ function bindAttachChips(root) {
});
}
function addUserMsg(text, att) {
- const m = addMsg("user", esc(text) + (att ? attachmentChipHtml(att) : ""));
+ const m = document.createElement("div");
+ m.className = "msg user";
+ const stack = document.createElement("div");
+ stack.className = "msg-stack";
+ const bubble = document.createElement("div");
+ bubble.className = "bubble";
+ bubble.innerHTML = esc(text);
+ stack.appendChild(bubble);
+ if (att) {
+ const line = document.createElement("div");
+ line.className = "attach-line";
+ line.innerHTML = attachmentChipHtml(att);
+ stack.appendChild(line);
+ }
+ m.appendChild(stack);
+ chatEl.appendChild(m);
+ chatEl.scrollTop = chatEl.scrollHeight;
bindAttachChips(m);
}
var pfName = must("pf-name");