mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
refactor(ui): share per-message affordance CSS via chat.css (#549)
The per-message rewind / edit / retry affordance — the icon glyphs (.icon-edit / .icon-rewind / .icon-retry) plus the inline edit-in-place form (.msg-edit-*) and the [data-busy] / .msg-editing states — was duplicated verbatim in both pane stylesheets: ui/static/style.css (interactive) and console/static/coordinator/coordinator.css (coordinator). PR #598 deferred consolidating them to keep that coord-only change off the shipped interactive stylesheet's cascade. Move the block into shared_static/chat.css, immediately after the .msg-actions / .msg-action-btn primitives both panes already share, and delete both copies (including coordinator.css's now-obsolete FOLLOW-UP note describing the duplication). Both index.html files load chat.css before their pane stylesheet, so the rules land earlier in the cascade; the selectors are unique (defined nowhere else, confirmed repo-wide) so it is a visual no-op. The block is moved verbatim — chat.css's sibling rules use a different variable vocabulary (--r-sm=4px / --font-mono) than the affordance block (--radius-sm=3px / --font-ui), so renaming would change radii/fonts. Verified pixel-identical via a headless-Chrome render-diff of both panes, before vs after, across all four affordance states (edit+rewind, retry, editing-open, busy): 0 differing pixels.
This commit is contained in:
@@ -647,176 +647,3 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Per-message rewind / edit / retry affordance (#549)
|
||||
The .msg-actions / .msg-action-btn primitives live in shared/chat.css; the
|
||||
icon glyphs + inline edit-in-place form currently live only in interactive's
|
||||
ui/static/style.css, so they're ported here verbatim (same CSS variables
|
||||
from shared/base.css).
|
||||
FOLLOW-UP (deferred from PR B Phase 2): this duplicates ui/static/style.css.
|
||||
Both copies should be promoted into shared/chat.css (alongside .msg-actions)
|
||||
so the two panes share one source of truth. Deferred to keep this coord-only
|
||||
phase from re-touching the shipped interactive stylesheet's cascade; do it as
|
||||
a focused both-pane change with its own browser smoke.
|
||||
========================================================================== */
|
||||
|
||||
/* Icon: retry (circular arrow) */
|
||||
.icon-retry {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 50%;
|
||||
border-bottom-color: transparent;
|
||||
position: relative;
|
||||
}
|
||||
.icon-retry::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
right: -1px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid transparent;
|
||||
border-top: 3px solid currentColor;
|
||||
transform: rotate(-30deg);
|
||||
}
|
||||
|
||||
/* Icon: edit (pencil) */
|
||||
.icon-edit {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
.icon-edit::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 3px;
|
||||
width: 6px;
|
||||
height: 8px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 1px 1px 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.icon-edit::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-top: 3px solid currentColor;
|
||||
}
|
||||
|
||||
/* Icon: rewind (chevrons pointing left) */
|
||||
.icon-rewind {
|
||||
width: 14px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
}
|
||||
.icon-rewind::before,
|
||||
.icon-rewind::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-left: 1.5px solid currentColor;
|
||||
border-bottom: 1.5px solid currentColor;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.icon-rewind::before {
|
||||
left: 1px;
|
||||
}
|
||||
.icon-rewind::after {
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
/* Edit-in-place form */
|
||||
.msg-edit-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
.msg-edit-textarea {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
max-height: 200px;
|
||||
background: var(--bg);
|
||||
color: var(--fg-bright);
|
||||
border: 1px solid var(--accent-dim);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 8px 10px;
|
||||
font-family: var(--font-ui);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: border-color 0.12s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.msg-edit-textarea:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.msg-edit-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.msg-edit-btn {
|
||||
padding: 4px 14px;
|
||||
font-size: 12px;
|
||||
font-family: var(--font-ui);
|
||||
font-weight: 500;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
transition:
|
||||
background 0.1s ease,
|
||||
border-color 0.1s ease,
|
||||
color 0.1s ease;
|
||||
}
|
||||
.msg-edit-btn:hover {
|
||||
background: var(--bg-highlight);
|
||||
}
|
||||
.msg-edit-btn-send {
|
||||
background: var(--accent-dim);
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.msg-edit-btn-send:hover {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Edit-in-place active state */
|
||||
.msg-editing {
|
||||
background: var(--bg-surface);
|
||||
border-color: var(--accent-dim);
|
||||
}
|
||||
.msg-editing .msg-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Busy-state disables action buttons */
|
||||
[data-busy="true"] .msg-action-btn {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.msg-edit-textarea,
|
||||
.msg-edit-btn {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,6 +1250,175 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Per-message rewind / edit / retry affordance (#549)
|
||||
Icon glyphs rendered inside the .msg-action-btn toolbar above, plus the
|
||||
inline edit-in-place form. Shared by the interactive pane (ui/static) and
|
||||
the coordinator dashboard (console/static/coordinator); both load this file
|
||||
before their page stylesheet, so this is the single source of truth.
|
||||
Vars resolve from shared/base.css (loaded by both panes).
|
||||
========================================================================== */
|
||||
|
||||
/* Icon: retry (circular arrow) */
|
||||
.icon-retry {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 50%;
|
||||
border-bottom-color: transparent;
|
||||
position: relative;
|
||||
}
|
||||
.icon-retry::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
right: -1px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid transparent;
|
||||
border-top: 3px solid currentColor;
|
||||
transform: rotate(-30deg);
|
||||
}
|
||||
|
||||
/* Icon: edit (pencil) */
|
||||
.icon-edit {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
.icon-edit::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 3px;
|
||||
width: 6px;
|
||||
height: 8px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 1px 1px 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.icon-edit::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-top: 3px solid currentColor;
|
||||
}
|
||||
|
||||
/* Icon: rewind (chevrons pointing left) */
|
||||
.icon-rewind {
|
||||
width: 14px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
}
|
||||
.icon-rewind::before,
|
||||
.icon-rewind::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-left: 1.5px solid currentColor;
|
||||
border-bottom: 1.5px solid currentColor;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.icon-rewind::before {
|
||||
left: 1px;
|
||||
}
|
||||
.icon-rewind::after {
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
/* Edit-in-place form */
|
||||
.msg-edit-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
.msg-edit-textarea {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
max-height: 200px;
|
||||
background: var(--bg);
|
||||
color: var(--fg-bright);
|
||||
border: 1px solid var(--accent-dim);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 8px 10px;
|
||||
font-family: var(--font-ui);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: border-color 0.12s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.msg-edit-textarea:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.msg-edit-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.msg-edit-btn {
|
||||
padding: 4px 14px;
|
||||
font-size: 12px;
|
||||
font-family: var(--font-ui);
|
||||
font-weight: 500;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
transition:
|
||||
background 0.1s ease,
|
||||
border-color 0.1s ease,
|
||||
color 0.1s ease;
|
||||
}
|
||||
.msg-edit-btn:hover {
|
||||
background: var(--bg-highlight);
|
||||
}
|
||||
.msg-edit-btn-send {
|
||||
background: var(--accent-dim);
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.msg-edit-btn-send:hover {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Edit-in-place active state */
|
||||
.msg-editing {
|
||||
background: var(--bg-surface);
|
||||
border-color: var(--accent-dim);
|
||||
}
|
||||
.msg-editing .msg-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Busy-state disables action buttons */
|
||||
[data-busy="true"] .msg-action-btn {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.msg-edit-textarea,
|
||||
.msg-edit-btn {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Per-workstream status bar — pinned above the composer.
|
||||
Rendered by both the interactive pane (ui/static/app.js) and the
|
||||
|
||||
@@ -1197,172 +1197,6 @@ body {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Action button icons — glyphs rendered inside .msg-action-btn (toolbar
|
||||
chrome + hover/focus styling lives in the .msg-actions/.msg-action-btn
|
||||
rules in shared_static/chat.css).
|
||||
========================================================================== */
|
||||
|
||||
/* Icon: retry (circular arrow) */
|
||||
.icon-retry {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 50%;
|
||||
border-bottom-color: transparent;
|
||||
position: relative;
|
||||
}
|
||||
.icon-retry::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
right: -1px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid transparent;
|
||||
border-top: 3px solid currentColor;
|
||||
transform: rotate(-30deg);
|
||||
}
|
||||
|
||||
/* Icon: edit (pencil) */
|
||||
.icon-edit {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
.icon-edit::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 3px;
|
||||
width: 6px;
|
||||
height: 8px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 1px 1px 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.icon-edit::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-top: 3px solid currentColor;
|
||||
}
|
||||
|
||||
/* Icon: rewind (chevrons pointing left) */
|
||||
.icon-rewind {
|
||||
width: 14px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
}
|
||||
.icon-rewind::before,
|
||||
.icon-rewind::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-left: 1.5px solid currentColor;
|
||||
border-bottom: 1.5px solid currentColor;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.icon-rewind::before {
|
||||
left: 1px;
|
||||
}
|
||||
.icon-rewind::after {
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
/* Edit-in-place form */
|
||||
.msg-edit-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
.msg-edit-textarea {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
max-height: 200px;
|
||||
background: var(--bg);
|
||||
color: var(--fg-bright);
|
||||
border: 1px solid var(--accent-dim);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 8px 10px;
|
||||
font-family: var(--font-ui);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: border-color 0.12s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.msg-edit-textarea:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.msg-edit-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.msg-edit-btn {
|
||||
padding: 4px 14px;
|
||||
font-size: 12px;
|
||||
font-family: var(--font-ui);
|
||||
font-weight: 500;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
transition:
|
||||
background 0.1s ease,
|
||||
border-color 0.1s ease,
|
||||
color 0.1s ease;
|
||||
}
|
||||
.msg-edit-btn:hover {
|
||||
background: var(--bg-highlight);
|
||||
}
|
||||
.msg-edit-btn-send {
|
||||
background: var(--accent-dim);
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.msg-edit-btn-send:hover {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Edit-in-place active state */
|
||||
.msg-editing {
|
||||
background: var(--bg-surface);
|
||||
border-color: var(--accent-dim);
|
||||
}
|
||||
.msg-editing .msg-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Busy-state disables action buttons */
|
||||
[data-busy="true"] .msg-action-btn {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.msg-edit-textarea,
|
||||
.msg-edit-btn {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Input area — composer DOM + styling lives in /shared/composer.js +
|
||||
/shared/chat.css. No page-specific overrides needed.
|
||||
|
||||
Reference in New Issue
Block a user