Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions changelog.d/1177-inbox-ask.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Needs you cards in the Inbox show the whole ask. The title and the reason wrap, including on a phone, so a sentence is not cut off before the in-place answer (#1177).
3 changes: 3 additions & 0 deletions core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7888,6 +7888,8 @@ var INBOX_CSS = `
.bt-inbox-meta{min-width:0;font-size:12px;line-height:16px;color:var(--bt-ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bt-inbox-title{font-size:13.5px;line-height:19px;color:var(--bt-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bt-inbox-reason,.bt-inbox-hint,.bt-inbox-activity{font-size:12px;line-height:16px;color:var(--bt-ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Needs you is answered on the card, so its ask is the whole text (#1177). The phone sheet clamps these two classes to two lines at a lower specificity; this unsets that clamp. In progress and Done stay one line. */
.bt-inbox-card:is([data-status=approval],[data-status=held],[data-status=input],[data-status=result],[data-status=failed]) :is(.bt-inbox-title,.bt-inbox-reason){white-space:pre-wrap;overflow-wrap:anywhere;overflow:visible;text-overflow:clip;display:block;min-width:0;-webkit-line-clamp:unset;-webkit-box-orient:unset}
.bt-inbox-calls{font-size:12px;line-height:16px;color:var(--bt-ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.bt-inbox-activity{color:var(--bt-ink-3)}
.bt-inbox-error{font-size:12px;line-height:16px;color:#c21d2e;overflow-wrap:anywhere}
Expand Down Expand Up @@ -17924,6 +17926,7 @@ var MOBILE_CSS = `
.bt-flybot-pointer button{flex:none;min-height:28px;padding:4px 12px}
.bt-inbox-card{flex-wrap:wrap;row-gap:8px}
.bt-inbox-copy{flex-basis:calc(100% - 38px)}
/* In progress and Done only. A Needs you ask is the whole text: inbox.js unsets this clamp on those five statuses (#1177). */
.bt-inbox-title,.bt-inbox-reason{white-space:normal;overflow-wrap:anywhere;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
.bt-inbox-actions{flex:1 1 100%;justify-content:flex-end;flex-wrap:wrap;padding-left:38px}
.bt-inbox-actions :is(.bt-soft,.bt-send){height:36px;padding:0 16px;font-size:13px}
Expand Down
2 changes: 2 additions & 0 deletions core/src/client/inbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,8 @@ export const INBOX_CSS = `
.bt-inbox-meta{min-width:0;font-size:12px;line-height:16px;color:var(--bt-ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bt-inbox-title{font-size:13.5px;line-height:19px;color:var(--bt-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bt-inbox-reason,.bt-inbox-hint,.bt-inbox-activity{font-size:12px;line-height:16px;color:var(--bt-ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Needs you is answered on the card, so its ask is the whole text (#1177). The phone sheet clamps these two classes to two lines at a lower specificity; this unsets that clamp. In progress and Done stay one line. */
.bt-inbox-card:is([data-status=approval],[data-status=held],[data-status=input],[data-status=result],[data-status=failed]) :is(.bt-inbox-title,.bt-inbox-reason){white-space:pre-wrap;overflow-wrap:anywhere;overflow:visible;text-overflow:clip;display:block;min-width:0;-webkit-line-clamp:unset;-webkit-box-orient:unset}
.bt-inbox-calls{font-size:12px;line-height:16px;color:var(--bt-ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.bt-inbox-activity{color:var(--bt-ink-3)}
.bt-inbox-error{font-size:12px;line-height:16px;color:#c21d2e;overflow-wrap:anywhere}
Expand Down
1 change: 1 addition & 0 deletions core/src/client/mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const MOBILE_CSS = `
.bt-flybot-pointer button{flex:none;min-height:28px;padding:4px 12px}
.bt-inbox-card{flex-wrap:wrap;row-gap:8px}
.bt-inbox-copy{flex-basis:calc(100% - 38px)}
/* In progress and Done only. A Needs you ask is the whole text: inbox.js unsets this clamp on those five statuses (#1177). */
.bt-inbox-title,.bt-inbox-reason{white-space:normal;overflow-wrap:anywhere;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
.bt-inbox-actions{flex:1 1 100%;justify-content:flex-end;flex-wrap:wrap;padding-left:38px}
.bt-inbox-actions :is(.bt-soft,.bt-send){height:36px;padding:0 16px;font-size:13px}
Expand Down
55 changes: 54 additions & 1 deletion core/test/inbox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { test } from 'node:test'
import { fileURLToPath } from 'node:url'
import { runInNewContext } from 'node:vm'
import { build } from 'esbuild'
import { MOBILE_CSS } from '../src/client/mobile.js'

// The Agent inbox, step 1 (#349): three groups built from data the client already has,
// approvals answered with the chat card's own answer, and one Inbox row in the sidebar.
Expand All @@ -29,7 +30,7 @@ runInNewContext(bundle.outputFiles[0].text, {
return platform[name]
},
})
const { inboxModel, needsCount, InboxCard, InboxDialog, InboxRow, GROUPS, STATUSES, KEEP_MS } = module.exports
const { inboxModel, needsCount, InboxCard, InboxDialog, InboxRow, GROUPS, STATUSES, KEEP_MS, INBOX_CSS } = module.exports
const plain = value => JSON.parse(JSON.stringify(value))

// Draws the inbox's own components; Bot marks and icons stay as elements.
Expand Down Expand Up @@ -675,3 +676,55 @@ test('a card names where its decision is written back, and one line when that fa
assert.equal(quiet[0].props.role, undefined, 'only a failed comment is an alert')
assert.equal(textOf(quiet[0]), `Could not confirm the comment on ${href} went out`)
})

// Needs you is answered on the card. Those statuses are the whole group; the other
// groups stay a one-line title. The phone sheet still clamps .bt-inbox-title at two
// lines, so the Needs you rule has to unset that clamp or a 400px window cuts the ask again.
const NEEDS_STATUSES = ['approval', 'held', 'input', 'result', 'failed']
const askRule = (css) => {
const match = css.match(/\.bt-inbox-card:is\(([^)]*)\) :is\(\.bt-inbox-title,\.bt-inbox-reason\)\{([^}]*)\}/)
return { statuses: match?.[1] ?? '', body: match?.[2] ?? '' }
}

test('a Needs you card shows the whole ask, and the phone clamp cannot cut it to two lines (#1177)', () => {
for (const card of model.needs) assert.ok(NEEDS_STATUSES.includes(card.status), card.id)
for (const card of [...model.progress, ...model.done]) assert.equal(NEEDS_STATUSES.includes(card.status), false, card.id)

const { statuses, body } = askRule(INBOX_CSS)
for (const status of NEEDS_STATUSES) assert.match(statuses, new RegExp(`\\[data-status=${status}\\]`), status)
for (const status of ['running', 'task', 'done', 'cancelled']) assert.doesNotMatch(statuses, new RegExp(`\\[data-status=${status}\\]`), status)
assert.match(body, /white-space:pre-wrap/)
assert.match(body, /overflow-wrap:anywhere/)
assert.match(body, /overflow:visible/)
assert.match(body, /text-overflow:clip/)
assert.match(body, /display:block/)
assert.match(body, /-webkit-line-clamp:unset/)
assert.match(body, /-webkit-box-orient:unset/)
assert.match(INBOX_CSS, /\.bt-inbox-title\{[^}]*white-space:nowrap[^}]*text-overflow:ellipsis/)
assert.match(INBOX_CSS, /\.bt-inbox-reason,\.bt-inbox-hint,\.bt-inbox-activity\{[^}]*white-space:nowrap/)
const phone = MOBILE_CSS.match(/\.bt-inbox-title,\.bt-inbox-reason\{([^}]*)\}/)?.[1] ?? ''
assert.match(phone, /-webkit-line-clamp:2/)
assert.doesNotMatch(phone, /data-status/)

const ask = 'Review PR #1171 (the token is base64url 格式的 payload) before ScheduleService 刚 restarted, and the rest of the sentence a one-line card cuts off.'
const detail = `${ask}\nThe second paragraph stays on the card.`
const asked = inboxModel({
roster: {
...roster,
questions: {
s2: { id: 'q-long', question: ask, detail, options: [{ label: 'Ship it' }], askedAt: NOW, botId: 'b2' },
},
},
work, tasks, sessions, now: NOW,
})
const card = asked.needs.find(entry => entry.id === 'question:q-long')
assert.equal(card.status, 'input')
assert.equal(card.title, ask)
assert.equal(card.reason, detail)
const tree = draw(card)
assert.equal(textOf(byClass(tree, 'bt-inbox-title')[0]), ask)
assert.equal(textOf(byClass(tree, 'bt-inbox-reason')[0]), detail)
assert.equal(byClass(tree, 'bt-inbox-answer').length, 1)
assert.ok(buttons(tree).some(node => textOf(node) === 'Submit'))
assert.ok(buttons(tree).some(node => textOf(node) === 'Open'))
})
Loading
Loading