diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
| commit | 4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch) | |
| tree | c296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /src/style.chat.css | |
| download | relic-4f49b16079b27d7ce633619e4cddfdf2bffbc952.tar.gz | |
Diffstat (limited to 'src/style.chat.css')
| -rw-r--r-- | src/style.chat.css | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/src/style.chat.css b/src/style.chat.css new file mode 100644 index 0000000..e251887 --- /dev/null +++ b/src/style.chat.css @@ -0,0 +1,710 @@ +/* Chat-related */ +body.chat span.at-name { /* for @USERNAME references */ + text-decoration: underline; + font-weight: bold; +} +/* A wrapper for a single single chat message (one row of the UI) */ +body.chat .message-widget { + margin-bottom: 0.75em; + border: none; + display: flex; + flex-direction: column; + border: none; + align-items: flex-start; +} +body.chat button, +body.chat input[type=button] { + line-height: inherit/*undo skin-specific funkiness*/; +} +body.chat .message-widget:last-of-type { + /* Latest message: reduce bottom gap */ + margin-bottom: 0.1em; +} +body.chat.my-messages-right .message-widget.mine { + /* Right-aligns a user's own chat messages, similar to how + most/some mobile messaging apps do it. */ + align-items: flex-end; +} +body.chat.my-messages-right .message-widget.notification { + /* Center-aligns a system-level notification message. */ + align-items: center; +} +/* The content area of a message. */ +body.chat .message-widget-content { + border-radius: 0.25em; + border: 1px solid rgba(0,0,0,0.2); + box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29); + padding: 0.25em 0.5em; + margin-top: 0; + min-width: 9em /*avoid unsightly "underlap" with the neighboring + .message-widget-tab element*/; + white-space: normal; + word-break: break-word /* so that full hashes wrap on narrow screens */; +} + +body.chat .message-widget-content.wide { + /* Special case for when embedding content which we really want to + expand, namely iframes. */ + width: 98%; +} +body.chat .message-widget-content label[for] { + margin-left: 0.25em; + cursor: pointer; +} +body.chat .message-widget-content > .attachment-link { + display: flex; + flex-direction: row; +} +body.chat .message-widget-content > .attachment-link > a { + margin-right: 1em; +} +body.chat .message-widget-content > iframe { + width: 100%; + max-width: 100%; + resize: both; +} +body.chat .message-widget-content> a { + /* Cosmetic: keep skin-induced on-hover underlining from shifting + content placed below this. */ + border-bottom: 1px transparent; +} +body.chat.monospace-messages .message-widget-content, +body.chat.monospace-messages .chat-input-field{ + font-family: monospace; +} +body.chat .message-widget-content > * { + margin: 0; + padding: 0; +} +body.chat .message-widget-content > pre { + white-space: pre-wrap; +} +body.chat .message-widget-content > .markdown > *:first-child { + margin-top: 0; +} +body.chat .message-widget-content > .markdown > *:last-child { + margin-bottom: 0; +} +body.chat .message-widget-content.error .buttons { + display: flex; + flex-direction: row; + justify-content: space-around; + flex-wrap: wrap; +} +body.chat .message-widget-content.error .buttons > button { + margin: 0.25em; +} + +body.chat .message-widget-content.error a { + color: inherit; +} +body.chat .message-widget-content.error .failed-message { + display: flex; + flex-direction: column; +} +body.chat .message-widget-content.error .failed-message textarea { + min-height: 5rem; +} + +/* User name and timestamp (a LEGEND-like element) */ +body.chat .message-widget .message-widget-tab { + border-radius: 0.25em 0.25em 0 0; + margin: 0 0.25em 0em 0.15em; + padding: 0 0.5em 0.15em 0.5em; + cursor: pointer; + white-space: nowrap; +} +body.chat .fossil-tooltip.help-buttonlet-content { + font-size: 80%; +} + +body.chat .message-widget .message-widget-tab { + /* Element which renders the main metadata for a given message. */ +} +body.chat .message-widget .message-widget-tab .xfrom { + /* xfrom part of the message tab */ + font-style: italic; + font-weight: bold; +} + +body.chat .message-widget .message-widget-tab .mtime { + /* mtime part of the message tab */ +} + +body.chat .message-widget .message-widget-tab .msgid { + /* msgid part of the message tab */ +} + +body.chat .message-widget .match { + font-weight: bold; + background-color: yellow; +} + +body.chat.fossil-dark-style .message-widget .match { + background-color: #ff4800; +} + +/* The popup element for displaying message timestamps + and deletion controls. */ +body.chat .chat-message-popup { + font-family: monospace; + font-size: 0.9em; + text-align: left; + display: flex; + flex-direction: column; + align-items: stretch; + padding: 0.25em; + margin-top: 0.25em; + border: 1px outset; + border-radius: 0.5em; +} +/* Full message timestamps. */ +body.chat .chat-message-popup > span { white-space: nowrap; } +/* Container for the message deletion buttons. */ +body.chat .chat-message-popup > .toolbar { + padding: 0; + margin: 0; + border: 2px inset rgba(0,0,0,0.3); + border-radius: 0.25em; + display: flex; + flex-direction: row; + justify-content: stretch; + flex-wrap: wrap; + align-items: center; +} +body.chat .chat-message-popup > .toolbar > * { + margin: 0.35em; +} +body.chat .chat-message-popup > .toolbar > button { + flex: 1 1 auto; +} +/* The widget for loading more/older chat messages. */ +body.chat #load-msg-toolbar { + border-radius: 0.25em; + padding: 0.1em 0.2em; + margin-bottom: 1em; +} +/* .all-done is set when chat has loaded all of the available + historical messages */ +body.chat #load-msg-toolbar.all-done { + opacity: 0.5; +} +body.chat #load-msg-toolbar > div { + display: flex; + flex-direction: row; + justify-content: stretch; + flex-wrap: wrap; +} +body.chat #load-msg-toolbar > div > button { + flex: 1 1 auto; +} +/* "Chat-only mode" hides the site header/footer, showing only + the chat app. */ +body.chat.chat-only-mode{ + padding: 0; + margin: 0 auto; +} +body.chat #chat-button-settings {} + +/** Container for the list of /chat messages. */ +body.chat #chat-messages-wrapper { + overflow: auto; + padding: 0 0.25em; +} +body.chat #chat-messages-wrapper.loading > * { + /* An attempt at reducing flicker when loading lots of messages. */ + visibility: hidden; +} + +/* Provide a visual cue when polling is offline. */ +body.chat.connection-error #chat-input-line-wrapper { + border-top: medium dotted red; +} +body.chat.fossil-dark-style.connection-error #chat-input-line-wrapper { + border-color: yellow; +} + +body.chat div.content { + margin: 0; + padding: 0; + display: flex; + flex-direction: column-reverse; + /* ^^^^ In order to get good automatic scrolling of new messages on + the BOTTOM in bottom-up chat mode, such that they scroll up + instead of down, we have to use column-reverse layout, which + changes #chat-messages-wrapper's "gravity" for purposes of + scrolling! If we instead use flex-direction:column then each new + message pushes #chat-input-area down further off the screen! + */ + align-items: stretch; +} +/* Wrapper for /chat user input controls */ +body.chat #chat-input-area { + display: flex; + flex-direction: column; + padding: 0; + margin: 0; + flex: 0 1 auto; +} +body.chat:not(.chat-only-mode) #chat-input-area{ + /* Safari user reports that 2em is necessary to keep the file selection + widget from overlapping the page footer, whereas a margin of 0 is fine + for FF/Chrome (and 2em is a *huge* waste of space for those). */ + margin-bottom: 0; +} + +body.chat .chat-input-field { + flex: 10 1 auto; + margin: 0; +} +body.chat #chat-input-field-x, +body.chat #chat-input-field-multi { + overflow: auto; + resize: vertical; +} +body.chat #chat-input-field-x { + display: inline-block/*supposed workaround for Chrome weirdness*/; + padding: 0.2em; + background-color: rgba(156,156,156,0.3); + white-space: pre-wrap; + /* ^^^ Firefox, when pasting plain text into a contenteditable field, + loses all newlines unless we explicitly set this. Chrome does not. */ + cursor: text; + /* ^^^ In some browsers the cursor may not change for a contenteditable + element until it has focus, causing potential confusion. */ +} +body.chat #chat-input-field-x:empty::before { + content: attr(data-placeholder); + opacity: 0.6; +} +body.chat .chat-input-field:not(:focus){ + border-width: 1px; + border-style: solid; + border-radius: 0.25em; +} +body.chat .chat-input-field:focus{ + /* This transparent border helps avoid the text shifting around + when the contenteditable attribute causes a border (which we + apparently cannot style) to be added. */ + border-width: 1px; + border-style: solid; + border-color: transparent; + border-radius: 0.25em; +} +/* Widget holding the chat message input field, send button, and + settings button. */ +body.chat #chat-input-line-wrapper { + display: flex; + flex-direction: row; + align-items: stretch; + flex-wrap: nowrap; +} +body.chat.chat-only-mode #chat-input-line-wrapper { + padding: 0 0.25em; +} + +/*body.chat #chat-input-line-wrapper:not(.compact) { + flex-wrap: nowrap; +}*/ +body.chat #chat-input-line-wrapper.compact { + /* "The problem" with wrapping, together with a contenteditable input + field, is that the latter grows as the user types, so causes + wrapping to happen while they type, then to unwrap as soon as the + input field is cleared (when the message is sent). When we stay + wrapped in compact mode, the wrapped buttons simply take up too + much space. */ + /*flex-wrap: wrap; + justify-content: flex-end;*/ + flex-direction: column; + /** + We "really do" need column orientation here because it's the + only way to eliminate the possibility that (A) the buttons + get truncated in very narrow windows and (B) that they keep + stable positions. + */ +} +body.chat #chat-input-line-wrapper.compact #chat-input-field-x { +} + +body.chat #chat-buttons-wrapper { + flex: 0 1 auto; + display: flex; + flex-direction: column; + align-items: center; + min-width: 4em; + min-height: 1.5em; + align-self: flex-end + /*keep buttons stable at bottom/right even when input field + resizes */; +} +body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper { + flex-direction: row; + flex: 1 1 auto; + align-self: stretch; + justify-content: flex-end; + /*flex-wrap: wrap;*/ + /* Wrapping would be ideal except that the edit widget + grows in width as the user types, moving the buttons + around */ +} +body.chat #chat-buttons-wrapper > .cbutton { + padding: 0; + display: inline-block; + border-width: 1px; + border-style: solid; + border-radius: 0.25em; + min-width: 4ex; + max-width: 4ex; + min-height: 3ex; + max-height: 3ex; + margin: 0.125em; + display: inline-flex; + justify-content: center; + align-items: center; + cursor: pointer; + font-size: 130%; +} +body.chat #chat-buttons-wrapper > .cbutton:hover { + background-color: rgba(200,200,200,0.3); +} +body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper > .cbutton { + margin: 2px 0.125em 0 0.125em; + min-width: 4.5ex; + max-width: 4.5ex; + min-height: 2.3ex; + max-height: 2.3ex; + font-size: 120%; +} +body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper #chat-button-submit { + min-width: 10ex; +} +.chat-input-field { + font-family: inherit +} +body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-multi, +body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-x { + min-height: 4rem; +/* + Problems related to max-height: + + - If we do NOT set a max-height then pasting/typing a large amount + of text can cause this element to grow without bounds, larger than + the window, and there's no way to navigate it sensibly. In this + case, manually resizing the element (desktop only - mobile doesn't + offer that) will force it to stay at the selected size even if more + content is added to it later. + + - If we DO set a max-height then its growth is bounded but it also + cannot manually expanded by the user. + + The lesser of the two evils seems to be to rely on the browser + feature that a manual resize of the element will pin its size. +*/ +} + +body.chat #chat-input-line-wrapper > #chat-button-settings{ + margin: 0 0 0 0.25em; + max-width: 2em; +} +body.chat #chat-input-line-wrapper > input[type=text], +body.chat #chat-input-line-wrapper > textarea { + flex: 20 1 auto; + max-width: revert; + min-width: 20em; +} +body.chat #chat-input-line-wrapper.compact > input[type=text] { + margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/; +} +/* Widget holding the file selection control and preview */ +body.chat #chat-input-file-area { + display: flex; + flex-direction: row; + margin: 0; +} +body.chat #chat-input-file-area > .file-selection-wrapper { + align-self: flex-start; + margin-right: 0.5em; + flex: 0 1 auto; + padding: 0.25em 0.5em; + white-space: nowrap; +} +body.chat #chat-input-file { + border:1px solid rgba(0,0,0,0);/*avoid UI shift during drop-targeting*/ + border-radius: 0.25em; + padding: 0.25em; +} +body.chat #chat-input-file > input { + flex: 1 0 auto; +} +/* Indicator when a drag/drop is in progress */ +body.chat #chat-input-file.dragover { + border: 1px dashed green; +} +/* Widget holding the details of a selected/dropped file/image. */ +body.chat #chat-drop-details { + padding: 0 1em; + white-space: pre; + font-family: monospace; + margin: auto; + flex: 0; +} +body.chat #chat-drop-details:empty { + padding: 0; + margin: 0; +} +body.chat #chat-drop-details img { + max-width: 45%; + max-height: 45%; +} +body.chat .chat-view { + flex: 20 1 auto + /*ensure that these grow more than the non-.chat-view elements. + Note that setting flex shrink to 0 breaks/disables scrolling!*/; + margin-bottom: 0.2em; +} +body.chat #chat-config, +body.chat #chat-search, +body.chat #chat-preview, +body.chat #chat-zoom { + /* /chat configuration widget */ + display: flex; + flex-direction: column; + overflow: auto; + padding: 0; + margin: 0; + align-items: stretch; + min-height: 6em; +} +body.chat #chat-zoom { + justify-content: space-between; +} +body.chat #chat-zoom-content { + display: flex; + overflow: auto; +} +body.chat #chat-zoom-content > .message-widget { + flex-grow: 1; +} +body.chat #chat-zoom-content > .message-widget > .message-widget-content { + width: 99%; +} +body.chat #chat-zoom-content > .message-widget .toolbar.hide-in-zoom { + /* The various Delete buttons misinteract with zoom mode's moving-around + of message widgets, so hide them in zoom mode. */ + position: absolute !important; + opacity: 0 !important; + pointer-events: none !important; + display: none !important; +} +body.chat #chat-config #chat-config-options { + /* /chat config options go here */ + flex: 1 1 auto; + display: flex; + flex-direction: column; + overflow: auto; + align-items: stretch; +} +body.chat #chat-config #chat-config-options .menu-entry { + display: flex; + align-items: center; + flex-direction: row-reverse; + flex-wrap: nowrap; + padding: 1em; + flex: 1 1 auto; + align-self: stretch; +} +body.chat #chat-config #chat-config-options .menu-entry.parent{ + border-radius: 1em 1em 0 1em; + margin-top: 1em; +} +body.chat #chat-config #chat-config-options .menu-entry.child { + /*padding-left: 2.5em;*/ + margin-left: 2em; +} +body.chat #chat-config #chat-config-options .menu-entry:nth-of-type(even){ + background-color: rgba(175,175,175,0.15); +} +body.chat #chat-config #chat-config-options .menu-entry:nth-of-type(odd){ + background-color: rgba(175,175,175,0.35); +} +body.chat #chat-config #chat-config-options .menu-entry:first-child { + /* Config list header */ + border-radius: 0 0 1em 1em; +} +body.chat #chat-config #chat-config-options .menu-entry:first-child .label-wrapper { + align-items: start; +} +body.chat #chat-config #chat-config-options .menu-entry > .toggle-wrapper { + /* Holder for a checkbox, if any */ + min-width: 1.5rem; + margin-right: 1rem; +} +body.chat #chat-config #chat-config-options .menu-entry .label-wrapper { + /* Wrapper for a LABEL and a .hint element. */ + display: flex; + flex-direction: column; + align-self: baseline; + flex: 1 1 auto; +} +body.chat #chat-config #chat-config-options .menu-entry label { + /* Config option label. */ + font-weight: bold; + white-space: initial; +} +body.chat #chat-config #chat-config-options .menu-entry label[for] { + cursor: pointer; +} +body.chat #chat-config #chat-config-options .menu-entry .hint { + /* Config menu hint text */ + font-size: 85%; + font-weight: normal; + white-space: pre-wrap; + display: inline-block; + opacity: 0.85; +} +body.chat #chat-config #chat-config-options .menu-entry select { +} +body.chat #chat-preview #chat-preview-content, +body.chat #chat-search #chat-search-content { + overflow: auto; + flex: 1 1 auto; + padding: 0.5em; + border: 1px dotted; +} + +body.chat #chat-preview #chat-preview-content > * { + margin: 0; + padding: 0; +} +body.chat .chat-view .button-bar { + flex: 0 1 auto; + display: flex; + flex-direction: column; +} +body.chat .chat-view .button-bar button { + padding: 0.5em; + flex: 1 1 auto; + margin: 0.25em 0; +} + +body.chat #chat-search .button-bar { + flex: 0 1 auto; + display: flex; + flex-direction: row; +} + +body.chat #chat-user-list-wrapper { + /* Safari can't do fieldsets right, so we emulate one. */ + border-radius: 0.5em; + margin: 1em 0 0.2em 0; + padding: 0 0.5em; + border-style: inset; + border-width: 0 1px 1px 1px/*else collides with the LEGEND*/; +} +body.chat #chat-user-list-wrapper.collapsed { + padding: 0; +} +body.chat #chat-user-list-wrapper > .legend { + font-weight: initial; + padding: 0 0.5em 0 0.5em; + position: relative; + top: -1.75ex/* place it like a fieldset legend */; + cursor: pointer; +} +body.chat #chat-user-list-wrapper > .legend > * { + vertical-align: middle; +} +body.chat #chat-user-list-wrapper > .legend > *:nth-child(2){ + /* Title label */ + opacity: 0.6; + font-size: 0.8em; +} +body.chat #chat-user-list-wrapper.collapsed > .legend > *:nth-child(2)::after { + content: " (tap to toggle)"; +} +body.chat #chat-user-list-wrapper .help-buttonlet { + margin: 0; +} +body.chat #chat-user-list-wrapper.collapsed #chat-user-list { + position: absolute !important; + opacity: 0 !important; + pointer-events: none !important; + display: none !important; +} +body.chat #chat-user-list { + margin-top: -1.25ex; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; +} +body.chat #chat-user-list .chat-user { + margin: 0.2em; + padding: 0.1em 0.5em 0.2em 0.5em; + border-radius: 0.5em; + cursor: pointer; + text-align: center; + white-space: pre; +} +body.chat #chat-user-list .timestamp { + font-size: 85%; + font-family: monospace; +} +body.chat #chat-user-list:not(.timestamps) .timestamp { + display: none; +} +body.chat #chat-user-list .chat-user.selected { + font-weight: bold; + text-decoration: underline; +} + +body.chat .searchForm { + margin-top: 1em; +} +body.chat .spacer-widget button { + margin-left: 1ex; + margin-right: 1ex; + display: block; + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +body.chat .anim-rotate-360 { + animation: rotate-360 750ms linear; +} +@keyframes rotate-360 { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} +body.chat .anim-flip-h { + animation: flip-h 750ms linear; +} +@keyframes flip-h{ + from { transform: rotateY(0deg); } + to { transform: rotateY(360deg); } +} +body.chat .anim-flip-v { + animation: flip-v 750ms linear; +} +@keyframes flip-v{ + from { transform: rotateX(0deg); } + to { transform: rotateX(360deg); } +} +body.chat .anim-fade-in { + animation: fade-in 750ms linear; +} +body.chat .anim-fade-in-fast { + animation: fade-in 350ms linear; +} +@keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} +body.chat .anim-fade-out-fast { + animation: fade-out 250ms linear; +} +@keyframes fade-out { + from { opacity: 1; } + to { opacity: 0; } +} + |
