Private
Public Access
Adding to a message that already had reactions meant going back up to the hover row's 🙂. A "+🙂" button now sits at the end of the row of reaction pills, always visible once the message has at least one reaction, and opens the same emoji picker. The picker anchors to the left edge of the pill row rather than the button: the button moves right with every added reaction, and a picker hanging off it ran past the edge of a phone screen. Both buttons share the picker's open state, so only one picker is ever open at a time. MessageList had no tests; this adds a few covering the new button. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
455 lines
8.4 KiB
CSS
455 lines
8.4 KiB
CSS
.message-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: var(--sp-4);
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.message-row {
|
|
position: relative;
|
|
display: flex;
|
|
gap: var(--sp-2);
|
|
padding: 1px var(--sp-2);
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.message-row-start {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.message-row:hover {
|
|
background: var(--ds-surface);
|
|
}
|
|
|
|
.message-avatar-slot {
|
|
width: 36px;
|
|
flex: none;
|
|
}
|
|
|
|
.message-content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.message-header {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 8px;
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.message-author {
|
|
/* Lets a very long display name shrink/wrap instead of pushing the
|
|
(now longer, e.g. "Yesterday 4:04 PM") timestamp out of the row. */
|
|
min-width: 0;
|
|
font-size: 0.86rem;
|
|
font-weight: 700;
|
|
color: var(--ds-text);
|
|
}
|
|
|
|
.message-time {
|
|
flex: none;
|
|
white-space: nowrap;
|
|
font-size: 0.68rem;
|
|
color: var(--ds-muted);
|
|
font-family: var(--mono);
|
|
}
|
|
|
|
.message-image {
|
|
display: block;
|
|
/* #71: rem, not px -- scales with the text-size setting (see
|
|
lib/theme.ts's applyTextScale), same as every other size in this app.
|
|
min(...) still caps against the viewport in absolute px, since a
|
|
percentage-of-viewport constraint isn't something a root font-size
|
|
change should affect. */
|
|
max-width: min(20rem, 100%);
|
|
max-height: 15rem;
|
|
object-fit: contain;
|
|
border-radius: var(--radius);
|
|
border: 1px solid var(--ds-border);
|
|
cursor: zoom-in;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.message-video-wrap {
|
|
position: relative;
|
|
display: inline-block;
|
|
max-width: min(20rem, 100%);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.message-video {
|
|
display: block;
|
|
width: 100%;
|
|
max-height: 15rem;
|
|
border-radius: var(--radius);
|
|
border: 1px solid var(--ds-border);
|
|
background: var(--ds-void);
|
|
}
|
|
|
|
.message-video-expand {
|
|
position: absolute;
|
|
top: 6px;
|
|
right: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 5px;
|
|
background: rgba(0, 0, 0, 0.6);
|
|
border: none;
|
|
border-radius: 6px;
|
|
color: white;
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.message-video-wrap:hover .message-video-expand,
|
|
.message-video-expand:focus-visible {
|
|
opacity: 1;
|
|
}
|
|
|
|
.message-file-attachment {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
background: var(--ds-surface-2);
|
|
border: 1px solid var(--ds-border);
|
|
border-radius: var(--radius);
|
|
padding: 8px 12px;
|
|
margin-bottom: 4px;
|
|
color: var(--ds-text);
|
|
text-decoration: none;
|
|
max-width: min(20rem, 100%);
|
|
}
|
|
|
|
.message-file-attachment:hover {
|
|
border-color: var(--ds-accent);
|
|
}
|
|
|
|
.message-file-attachment svg {
|
|
flex: none;
|
|
color: var(--ds-muted);
|
|
}
|
|
|
|
.message-file-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.message-file-name {
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.message-file-size {
|
|
font-size: 0.72rem;
|
|
color: var(--ds-muted);
|
|
font-family: var(--mono);
|
|
}
|
|
|
|
.message-text {
|
|
font-size: 0.88rem;
|
|
line-height: 1.45;
|
|
color: var(--ds-text);
|
|
word-break: break-word;
|
|
}
|
|
|
|
.message-text p {
|
|
margin: 0 0 0.4em;
|
|
}
|
|
|
|
.message-text p:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.message-mention {
|
|
background: color-mix(in srgb, var(--ds-highlight) 18%, transparent);
|
|
color: var(--ds-highlight);
|
|
border-radius: 5px;
|
|
padding: 0 4px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.message-room-reference {
|
|
background: color-mix(in srgb, var(--ds-accent) 18%, transparent);
|
|
color: var(--ds-accent);
|
|
border-radius: 5px;
|
|
padding: 0 4px;
|
|
font-weight: 700;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.message-room-reference:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.message-text code {
|
|
background: var(--ds-surface-2);
|
|
padding: 1px 5px;
|
|
border-radius: 5px;
|
|
font-family: var(--mono);
|
|
font-size: 0.85em;
|
|
}
|
|
|
|
.message-text pre {
|
|
background: var(--ds-surface-2);
|
|
padding: 8px 10px;
|
|
border-radius: 8px;
|
|
overflow-x: auto;
|
|
margin: 0.3em 0;
|
|
}
|
|
|
|
.message-text pre code {
|
|
background: none;
|
|
padding: 0;
|
|
font-size: 0.82em;
|
|
}
|
|
|
|
.message-text blockquote {
|
|
border-left: 3px solid var(--ds-border);
|
|
margin: 0.3em 0;
|
|
padding: 0 0.7em;
|
|
color: var(--ds-muted);
|
|
}
|
|
|
|
.message-text ul,
|
|
.message-text ol {
|
|
margin: 0.2em 0;
|
|
padding-left: 1.3em;
|
|
}
|
|
|
|
.message-text a {
|
|
color: var(--ds-accent);
|
|
word-break: break-all;
|
|
}
|
|
|
|
.message-text h1,
|
|
.message-text h2,
|
|
.message-text h3,
|
|
.message-text h4,
|
|
.message-text h5,
|
|
.message-text h6 {
|
|
font-weight: 700;
|
|
margin: 0.3em 0;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
/* Capped well below normal HTML heading sizes so a heading in chat doesn't
|
|
dwarf the message row, but each level still steps down from the last so
|
|
the hierarchy stays visible. */
|
|
.message-text h1 {
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
.message-text h2 {
|
|
font-size: 1.2em;
|
|
}
|
|
|
|
.message-text h3 {
|
|
font-size: 1.1em;
|
|
}
|
|
|
|
.message-text h4 {
|
|
font-size: 1.02em;
|
|
}
|
|
|
|
.message-text h5 {
|
|
font-size: 0.95em;
|
|
}
|
|
|
|
.message-text h6 {
|
|
font-size: 0.88em;
|
|
color: var(--ds-muted);
|
|
}
|
|
|
|
.message-text table {
|
|
border-collapse: collapse;
|
|
margin: 0.3em 0;
|
|
font-size: 0.85em;
|
|
max-width: 100%;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.message-text th,
|
|
.message-text td {
|
|
border: 1px solid var(--ds-border);
|
|
padding: 4px 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
.message-text th {
|
|
background: var(--ds-surface-2);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.message-text mark {
|
|
background: color-mix(in srgb, var(--ds-accent) 35%, transparent);
|
|
color: var(--ds-text);
|
|
padding: 0 2px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.message-text li:has(> input[type='checkbox']) {
|
|
list-style: none;
|
|
margin-left: -1.3em;
|
|
}
|
|
|
|
.message-text input[type='checkbox'] {
|
|
accent-color: var(--ds-accent);
|
|
margin-right: 4px;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.message-text footer {
|
|
margin-top: 0.4em;
|
|
padding-top: 0.3em;
|
|
border-top: 1px solid var(--ds-border);
|
|
font-size: 0.8em;
|
|
color: var(--ds-muted);
|
|
}
|
|
|
|
.message-text footer div {
|
|
margin: 2px 0;
|
|
}
|
|
|
|
.message-edited {
|
|
font-style: italic;
|
|
color: var(--ds-muted);
|
|
font-size: 0.78rem;
|
|
}
|
|
|
|
.message-row-actions {
|
|
display: none;
|
|
position: absolute;
|
|
top: 2px;
|
|
right: var(--sp-2);
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.message-row:hover .message-row-actions {
|
|
display: flex;
|
|
}
|
|
|
|
.message-edit-link {
|
|
background: var(--ds-surface-2);
|
|
border: 1px solid var(--ds-border);
|
|
color: var(--ds-muted);
|
|
font-size: 0.68rem;
|
|
cursor: pointer;
|
|
padding: 2px 8px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.message-edit-link:hover {
|
|
color: var(--ds-text);
|
|
border-color: var(--ds-accent);
|
|
}
|
|
|
|
.message-delete-link {
|
|
background: var(--ds-surface-2);
|
|
border: 1px solid var(--ds-border);
|
|
color: var(--ds-muted);
|
|
font-size: 0.68rem;
|
|
cursor: pointer;
|
|
padding: 2px 8px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.message-delete-link:hover {
|
|
color: var(--ds-danger);
|
|
border-color: var(--ds-danger);
|
|
}
|
|
|
|
.message-deleted-text {
|
|
color: var(--ds-muted);
|
|
}
|
|
|
|
.message-reaction-wrap {
|
|
position: relative;
|
|
}
|
|
|
|
.message-reaction-trigger {
|
|
background: var(--ds-surface-2);
|
|
border: 1px solid var(--ds-border);
|
|
color: var(--ds-muted);
|
|
font-size: 0.78rem;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
padding: 2px 6px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.message-reaction-trigger:hover {
|
|
border-color: var(--ds-accent);
|
|
}
|
|
|
|
.message-reaction-pills {
|
|
position: relative;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 4px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.message-reaction-pill {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
background: var(--ds-surface-2);
|
|
border: 1px solid var(--ds-border);
|
|
border-radius: var(--radius-pill);
|
|
color: var(--ds-muted);
|
|
font-size: 0.76rem;
|
|
padding: 2px 8px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.message-reaction-pill:hover {
|
|
border-color: var(--ds-accent);
|
|
}
|
|
|
|
.message-reaction-pill-mine {
|
|
border-color: var(--ds-accent);
|
|
color: var(--ds-text);
|
|
}
|
|
|
|
.message-edit-input {
|
|
background: var(--ds-surface-2);
|
|
border: 1px solid var(--ds-accent);
|
|
border-radius: 6px;
|
|
padding: 6px 10px;
|
|
font-size: 0.88rem;
|
|
color: var(--ds-text);
|
|
font-family: var(--sans);
|
|
width: 100%;
|
|
resize: vertical;
|
|
}
|
|
|
|
/* The picker anchors to the row of pills, not to this button: the button's
|
|
position moves right with every added reaction, and a picker hanging
|
|
off it ran past the edge of a phone screen. (No `position` here on
|
|
purpose -- .message-reaction-wrap's relative would re-anchor it.) */
|
|
.message-reaction-add-wrap {
|
|
display: contents;
|
|
}
|
|
|
|
/* #78: always visible once a message has reactions, so adding to the pile
|
|
doesn't need the hover-row button. Same shape as the pills it sits
|
|
beside, kept quieter so it doesn't read as another reaction. */
|
|
.message-reaction-add {
|
|
gap: 2px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.message-reaction-add:hover,
|
|
.message-reaction-add:focus-visible {
|
|
opacity: 1;
|
|
}
|