diff --git a/public/css/assistant.css b/public/css/assistant.css index 9a31ffd4..35940968 100644 --- a/public/css/assistant.css +++ b/public/css/assistant.css @@ -123,7 +123,9 @@ .assistant-image-modal-card img { max-width:100%; max-height:92vh; border-radius:14px; background:white; box-shadow:0 24px 80px rgba(0,0,0,.35); } .assistant-image-modal-close { position:absolute; top:8px; right:8px; z-index:1; width:38px; height:38px; border:0; border-radius:999px; background:white; color:var(--g800); font-size:24px; line-height:1; cursor:pointer; box-shadow:var(--shadow); } .assistant-image-modal-cancel { justify-self:center; border:0; border-radius:999px; background:white; color:var(--g800); font-weight:700; padding:9px 14px; box-shadow:var(--shadow); cursor:pointer; } -.assistant-sources { padding:10px 12px; display:grid; gap:8px; flex:1 1 auto; min-height:0; overflow-y:auto; } +/* One column capped at the panel's width: an auto grid column grows to its + widest unbreakable content, so a long file name pushed every card off-screen. */ +.assistant-sources { padding:10px 12px; display:grid; grid-template-columns:minmax(0,1fr); gap:8px; flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; } .assistant-saved-chats { flex:1 1 auto; overflow-y:auto; min-height:0; padding:10px 12px; display:flex; flex-direction:column; gap:8px; } /* Open WebUI-style recency headings above each run of chats. */ /* Composed empty state: a title, one status pill and the composer — nothing @@ -158,7 +160,9 @@ .assistant-saved-chat-title { font-size:13px; font-weight:500; color:var(--g800); line-height:1.35; display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .assistant-saved-chat-meta { font-size:11px; color:var(--g500); display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .assistant-save-actions { display:flex; gap:6px; flex-wrap:wrap; } -.assistant-source { border:1px solid var(--g200); border-radius:10px; padding:10px 12px; background:white; box-shadow:var(--shadow); font-size:12px; line-height:1.5; transition:border-color .12s ease; } +/* Long names break onto new lines instead of running past the card + (word-break is the fallback for iOS Safari before 15.4). */ +.assistant-source { min-width:0; overflow-wrap:anywhere; word-break:break-word; border:1px solid var(--g200); border-radius:10px; padding:10px 12px; background:white; box-shadow:var(--shadow); font-size:12px; line-height:1.5; transition:border-color .12s ease; } .assistant-source:hover { border-color:var(--blue-light); } .assistant-source:target { border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-light); } .assistant-source strong { color:var(--g800); } @@ -211,7 +215,10 @@ border:none; box-shadow:none; border-radius:0; } .assistant-side .card-header { flex:0 0 auto; } /* The band is fixed height, so the list inside it is what scrolls. */ - .assistant-sources { flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch; + /* On a touchscreen the list is one smooth vertical scroll: no sideways pan, + and no excerpt scrolling inside it. */ + .assistant-source-excerpt { max-height:none; overflow:visible; } + .assistant-sources { flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; touch-action:pan-y; -webkit-overflow-scrolling:touch; padding-bottom:calc(12px + env(safe-area-inset-bottom)); } /* Slim toolbar: icon-only actions in one row */ /* Saved chats: a fixed drawer overlaying the chat */ diff --git a/test/assistant-mobile.test.js b/test/assistant-mobile.test.js index 6e878ba7..0771576e 100644 --- a/test/assistant-mobile.test.js +++ b/test/assistant-mobile.test.js @@ -140,3 +140,15 @@ test('on a phone the menu follows the view: chat history in the assistant', () = assert.match(mobile, /#assistant-saved-chats \{ flex:1 1 auto; min-height:0; overflow-y:auto;/, 'the chat list does'); assert.match(mobile, /\.assistant-drawer-close \{ display:none !important; \}/, 'one close control, the « in the head row'); }); + +test('source cards fit the phone: long names wrap, nothing scrolls sideways or inside', () => { + const css = read('public/css/assistant.css'); + // An auto grid column grew to the longest unbroken file name, so every card + // ran off the right edge of an iPhone. + assert.match(css, /^\.assistant-sources \{[^}]*grid-template-columns:minmax\(0,1fr\);[^}]*overflow-x:hidden;/m); + assert.match(css, /^\.assistant-source \{ min-width:0; overflow-wrap:anywhere; word-break:break-word;/m, + 'long names break onto new lines, with the pre-15.4 Safari fallback'); + const mobile = css.slice(css.indexOf('@media (max-width: 640px)')); + assert.match(mobile, /\.assistant-source-excerpt \{ max-height:none; overflow:visible; \}/, 'no scroll box inside the scrolling list'); + assert.match(mobile, /\.assistant-sources \{[^}]*overflow-x:hidden; touch-action:pan-y;/, 'and no sideways pan'); +});