Pin estilo fix/menu-item-danger (759a8ff): End meeting for everyone in the negative tone
The row menu's destructive item uses estilo-menu-item="danger", the correction this adoption made to estilo, in place of an app class. Native hand-off pages show no account menu: they keep no-referrer, which a chained sign-out cannot pass. The window scenario reads the new words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8 files changed,
+23
−14
go.mod+1 −1go.sum+2 −0internal/calls/chrome.go+5 −3internal/calls/native_auth.go+5 −2internal/calls/static/calls-shell.css+0 −1internal/calls/templates/index.html+1 −1internal/callstest/chrome_test.go+1 −1test/browser/call.mjs+8 −5
CI cancelled — superseded by a newer push — run details
diff --git a/go.mod b/go.mod| index 9d1c5e6..9f8c78d 100644 |
| --- a/go.mod |
| +++ b/go.mod |
| @@ -3,7 +3,7 @@ module amadan.net/oficina/calls |
| go 1.26.0 |
| require ( |
| - amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0 |
| + amadan.net/oficina/estilo.git v0.0.0-20261001081853-759a8ff71c5a |
| amadan.net/oficina/home/connect v0.0.0-20260930200947-7a0f19c3a8a7 |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 |
| amadan.net/rastrillo/rastrillo v0.26.1-0.20260908075707-29e4491631bd |
diff --git a/go.sum b/go.sum| index bfb810b..0f8bbb5 100644 |
| --- a/go.sum |
| +++ b/go.sum |
| @@ -1,5 +1,7 @@ |
| amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0 h1:Qww+xGIYJaj+6TIX4Knr71ghxeYJjtgFm6WukX6NkGg= |
| amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw= |
| +amadan.net/oficina/estilo.git v0.0.0-20261001081853-759a8ff71c5a h1:7eP/rgQjU0KAtYbhiaFSplK5VF2s49NeUKjki/li3zg= |
| +amadan.net/oficina/estilo.git v0.0.0-20261001081853-759a8ff71c5a/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw= |
| amadan.net/oficina/home/connect v0.0.0-20260930200947-7a0f19c3a8a7 h1:FVzfiViGRUtl/enMtf34LL2ECLCLJx5V2JJmsOxuOEI= |
| amadan.net/oficina/home/connect v0.0.0-20260930200947-7a0f19c3a8a7/go.mod h1:YroavXe43Wietec+po8mtSk4a9Bir2h31Q3iAf4MWxI= |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 h1:RPKtyry/71i06o8L+gjAaUyexbmG56kO1YPs601URAM= |
diff --git a/internal/calls/chrome.go b/internal/calls/chrome.go| index 7019ffc..5f1169f 100644 |
| --- a/internal/calls/chrome.go |
| +++ b/internal/calls/chrome.go |
| @@ -45,9 +45,11 @@ type frame struct { |
| parts []string |
| // context is the open meeting's title, shown in the bar. |
| context string |
| - // signinPage is one of the sign-in, sent and signed-out pages: the |
| - // corner is empty and the brand is plain text, since everything it |
| - // could link to is behind the session gate. |
| + // signinPage is one of the sign-in, sent and signed-out pages, or a |
| + // native app hand-off page: the corner is empty and the brand is |
| + // plain text, since everything it could link to is behind the session |
| + // gate (and a hand-off page keeps no-referrer, which a chained |
| + // sign-out could not pass through). |
| signinPage bool |
| // public is a page reached through a capability (the invitation |
| // link /j/{token}): "Sign in" carries no return path, because the |
diff --git a/internal/calls/native_auth.go b/internal/calls/native_auth.go| index 1ce7d2f..be19dc7 100644 |
| --- a/internal/calls/native_auth.go |
| +++ b/internal/calls/native_auth.go |
| @@ -242,7 +242,10 @@ func (a *app) nativeAuthorize(w http.ResponseWriter, r *http.Request) { |
| if person.Anonymous { |
| title = "Continue to the Calls app" |
| } |
| - render(w, "native", a.page(r, titled(title), map[string]any{"CancelHref": cancelHref, "Guest": person.Anonymous, "Title": title, "Action": "/native/authorize?id=" + id, "Done": false, "Code": protocol.AuthenticationCode(id)})) |
| + // The hand-off pages keep no-referrer (nativeHeaders) and so show no |
| + // account menu: an empty corner, as the sign-in pages have (spec §9.5, |
| + // "not a chrome page"). |
| + render(w, "native", a.page(r, frame{parts: []string{title}, signinPage: true}, map[string]any{"CancelHref": cancelHref, "Guest": person.Anonymous, "Title": title, "Action": "/native/authorize?id=" + id, "Done": false, "Code": protocol.AuthenticationCode(id)})) |
| } |
| func (a *app) nativeApprove(w http.ResponseWriter, r *http.Request) { |
| nativeHeaders(w) |
| @@ -271,7 +274,7 @@ func (a *app) nativeComplete(w http.ResponseWriter, r *http.Request) { |
| if person.Anonymous { |
| title = "Calls is ready" |
| } |
| - render(w, "native", a.page(r, titled(title), map[string]any{"Title": title, "Done": true})) |
| + render(w, "native", a.page(r, frame{parts: []string{title}, signinPage: true}, map[string]any{"Title": title, "Done": true})) |
| } |
| // nativeStartMeeting is POST /native/meetings/start: the app's half of |
diff --git a/internal/calls/static/calls-shell.css b/internal/calls/static/calls-shell.css| index 91f71f0..517d6d6 100644 |
| --- a/internal/calls/static/calls-shell.css |
| +++ b/internal/calls/static/calls-shell.css |
| @@ -30,4 +30,3 @@ body { background: var(--rst-bg); } |
| .calls-holding-more { display: grid; gap: var(--rst-sp-3); justify-items: start; margin-block-start: var(--rst-sp-5); } |
| .calls-holding-more p { margin: 0; } |
| .calls-holding-more form { margin: 0; } |
| -.calls-menu-item-danger { color: var(--rst-tone-negative-fg); } |
diff --git a/internal/calls/templates/index.html b/internal/calls/templates/index.html| index e174840..ec405a8 100644 |
| --- a/internal/calls/templates/index.html |
| +++ b/internal/calls/templates/index.html |
| @@ -25,7 +25,7 @@ |
| <a estilo-menu-item href="/meetings/{{.Sqid}}/settings"><span estilo-menu-label>Meeting settings</span></a> |
| <a estilo-menu-item href="/meetings/{{.Sqid}}/links/replace"><span estilo-menu-label>Replace the link</span></a> |
| <hr> |
| - <a estilo-menu-item class="calls-menu-item-danger" href="/meetings/{{.Sqid}}/end"><span estilo-menu-label>End meeting for everyone</span></a> |
| + <a estilo-menu-item="danger" href="/meetings/{{.Sqid}}/end"><span estilo-menu-label>End meeting for everyone</span></a> |
| </div> |
| </details> |
| </div> |
diff --git a/internal/callstest/chrome_test.go b/internal/callstest/chrome_test.go| index 144976e..ee754b6 100644 |
| --- a/internal/callstest/chrome_test.go |
| +++ b/internal/callstest/chrome_test.go |
| @@ -290,7 +290,7 @@ func TestMeetingsAreListRowsWithOneMenu(t *testing.T) { |
| `<span rst-row-main><a href="/m/` + sqid + `/join">Board meeting</a>`, |
| `<details rst-row-menu name="estilo-menus">`, |
| `<summary aria-label="Actions for Board meeting">`, |
| - `<a estilo-menu-item class="calls-menu-item-danger" href="/meetings/` + sqid + `/end"><span estilo-menu-label>End meeting for everyone</span></a>`, |
| + `<a estilo-menu-item="danger" href="/meetings/` + sqid + `/end"><span estilo-menu-label>End meeting for everyone</span></a>`, |
| } { |
| if !strings.Contains(body, want) { |
| t.Errorf("the meeting list lacks %q", want) |
diff --git a/test/browser/call.mjs b/test/browser/call.mjs| index ea14e9e..e376005 100644 |
| --- a/test/browser/call.mjs |
| +++ b/test/browser/call.mjs |
| @@ -5080,7 +5080,7 @@ const run = { |
| 'no script: the room is still the inset, rounded window, with no page scroll (' + JSON.stringify(gs) + ')'); |
| const bareText = await still.locator('.calls-window').innerText(); |
| check(bareText.includes('The call needs JavaScript') && bareText.includes('Back to the holding screen') && |
| - bareText.includes('joining as a member'), |
| + bareText.includes('You’re in this call as paul.'), |
| 'no script: the title, the standing, the sentence and the way back are inside the window'); |
| await still.locator('#call-nav summary').click(); |
| const menu = await still.locator('.calls-nav-menu').innerText(); |
| @@ -5130,15 +5130,18 @@ const run = { |
| 'the title pill says the title and "In the meeting", as the Mac\'s does (' + JSON.stringify(pill) + ')'); |
| await pageA.locator('#call-nav summary').click(); |
| const standing = (await pageA.locator('.calls-nav-standing').innerText()).trim(); |
| - check(standing === 'You are paul, joining as a member.' && |
| + // The standing line opens the menu; then the account menu's order |
| + // (shared chrome R2): who is signed in, Meetings, Settings, Help. |
| + check(standing === 'You’re in this call as paul.' && |
| await pageA.evaluate(() => { |
| const p = document.querySelector('.calls-nav-standing'); |
| - return !!p.nextElementSibling && p.nextElementSibling.textContent === 'Meetings'; |
| - }), 'the standing line is in the title\'s menu, above Meetings (' + standing + ')'); |
| + const meetings = [...document.querySelectorAll('.calls-nav-menu a')].find((a) => a.textContent === 'Meetings'); |
| + return p === p.parentElement.firstElementChild && !!meetings && !!(p.compareDocumentPosition(meetings) & Node.DOCUMENT_POSITION_FOLLOWING); |
| + }), 'the standing line opens the title\'s menu, above Meetings (' + standing + ')'); |
| await pageA.keyboard.press('Escape'); |
| check(!(await pageA.locator('#call-nav').evaluate((d) => d.open)), 'Escape closes the menu'); |
| const floorText = await pageA.locator('#notices #floor-notice').innerText().catch(() => ''); |
| - check(await pageA.locator('#floor-notice').isVisible() && floorText.includes('no relay yet'), |
| + check(await pageA.locator('#floor-notice').isVisible() && floorText.includes('Calls outside your network may not connect yet.'), |
| 'the relay notice is a notice of its own under the title, not in it'); |
| await shot(pageA, '1440-relay-notice'); |
| await pageA.locator('#floor-dismiss').click(); |