Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/197-social-ui-nits.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Social: a face-to-face room you left or that closed no longer shows a message box under "You left this room."; your own handle in the account menu is in normal, readable ink; messages no longer point to a "Friends' Bots" section that no longer exists, and a held message to a friend's Bot names the Auto-send setting the UI actually shows.
1 change: 1 addition & 0 deletions docs/subsystems/social.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Everything in `plugins/social/` (entry `plugins/social/index.js` and any helpers
- #194: a face room title shows one time: Bot Social's `created_at`, else the joining deadline minus the 10-minute window (`faceRoomCreatedAt`), the same before and after Enter chat and on every home.
- #195: the Add friend paste button reads **Accept invite**; an accepted Bot invite posts a toast and closes the panel at once.
- #191: a peer's leave reads `Bob left the room` (the person, from the member's owner name; the Bot label only when there is no owner name), and an ended face room shows a plain status line above the composer (`FaceRoomStatus` on `flybot.session.dock`, `data-flybot=face-room-end`: `Bob left the room. This room is closed.` / `You left this room.`). Joining by number opens the new room in the main pane at once, so the top bar never keeps the previous room's title.
- #197: while that status line shows, the composer is hidden (a CSS rule on the composer stack that has `data-flybot=face-room-end`), so a left or closed room is read-only. The account-menu handle uses normal ink. User-facing text no longer names a Friends' Bots section: `relationship_exists` reads `You're already friends. Their Bot is back in your list.`, and a held message to a friend's Bot says it waits for approval in this chat and names the Auto-send setting.
- My own handle (`@name.home`) shows with a **Copy** link at the top of the account menu (slot `flybot.account.header`, `MyHandle`) as well as in Add friend (#181).
- Sidebar **+** is a short action menu, not the To:/composer. Add friend has one-click **Copy invite link** (mint + clipboard) with a Bot picker when there is more than one Bot, and a `You are @your.home` line (#181). **Talk through** stays on one line (nowrap label). Bot Sharing puts the invite link on its own full-width row above **Copy link** / **Copy with instructions**. The social strip keeps the **N to approve** badge and opens that chat; the stacked jump list stays out of the column. Held `face_room_message` drafts show in that face chat with readable names (`Chief → the room`), never raw `bot_01…` or a `FLYBOT-FACE-HUMAN` envelope.

Expand Down
6 changes: 4 additions & 2 deletions plugins/social/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
return 'Social is not running: the plugin did not start. See Settings → Feedback.'
}
const message = String(e?.message ?? e ?? '')
if (/relationship_exists/i.test(message)) return "You're already friends. That Bot is in Friends' Bots again."
if (/relationship_exists/i.test(message)) return "You're already friends. Their Bot is back in your list."
if (/invite_unavailable|invite is used|invite_used|already used/i.test(message)) return 'This invite was already used. Ask your friend for a new one.'
return message
}
Expand Down Expand Up @@ -94,8 +94,10 @@
.fs-pending-tag{background:var(--bt-accent,#4d6bfe);color:#fff}
.fs-refresh{flex:none;font-size:11px;line-height:14px;color:var(--bt-ink-3,rgba(20,20,20,.36));font-weight:400}
.fs-room-end{margin:6px 12px;padding:8px 10px;border-radius:8px;background:var(--bt-hover,rgba(119,119,119,.09));color:var(--bt-ink-2,rgba(20,20,20,.6));font-size:12px;line-height:16px}
/* A left or closed face room is read-only (#197): its end line replaces the composer. */
[class*="_composerStack"]:has([data-flybot="face-room-end"]) [class*="_root"]:has(> [data-composer-card]){display:none!important}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the social subsystem doc or add the waiver

When checked against this commit's parent, cd core && ARCH_DOCS_BASE=129752c^ node scripts/check-arch-docs.mjs fails: these plugins/social/ edits are owned by docs/subsystems/social.md, but the commit changes neither that page nor includes a Docs-not-needed: trailer. Since npm run lint runs this gate, required CI cannot pass for this commit; document the changed UI contract or add the trailer if it is truly docs-neutral.

AGENTS.md reference: AGENTS.md:L94-L94

Useful? React with 👍 / 👎.

.fs-me{display:flex;align-items:center;gap:6px;padding:0 10px 4px;justify-content:space-between;font-size:11px;line-height:14px;color:var(--bt-ink-3,rgba(20,20,20,.36))}
.fs-me-handle{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--bt-ink-2,rgba(20,20,20,.6));font-size:12px}
.fs-me-handle{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--bt-ink,#141414);font-size:13px}
.bt-menu .fs-me button.fs-link{display:inline;flex:none;width:auto;min-height:0;padding:0 2px;border-radius:4px;font-size:12px;line-height:16px}
.fs-me .fs-link{flex:none}
.fs-pending{margin:4px 4px 6px;padding:6px 8px;border:1px solid var(--bt-line-2,rgba(20,20,20,.15));border-radius:10px;background:var(--bt-card,#fff);display:flex;flex-direction:column;gap:4px;min-width:0;max-width:100%}
Expand Down Expand Up @@ -1352,7 +1354,7 @@
statusTask ??= call('social/status', {}).then(value => {
const changed = JSON.stringify(value) !== JSON.stringify(status.value)
status = { at: Date.now(), value }
if (changed) for (const fn of [...statusListeners]) { try { fn() } catch (error) { console.error('flybot-social: status listener failed', error) } }

Check warning on line 1357 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-useless-spread)

Using a spread operator here creates a new array unnecessarily.
return value
}).finally(() => { statusTask = undefined })
return statusTask
Expand Down
4 changes: 2 additions & 2 deletions plugins/social/src/social.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@
const TEXTUAL = new Set(['text/plain', 'text/markdown', 'text/csv', 'text/tab-separated-values', 'application/json'])
const sizeLabel = n => (n < 1024 ? `${n} B` : n < 1048576 ? `${(n / 1024).toFixed(1)} KB` : `${(n / 1048576).toFixed(1)} MB`)
// Remote strings that reach model-visible text: one line, no brackets or markup, capped.
export const cleanText = (value, fallback = '') => String(value ?? '').replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, ' ').replace(/[[\]"<>`]/g, '').replace(/\s+/g, ' ').trim().slice(0, 60) || fallback

Check warning on line 84 in plugins/social/src/social.js

View workflow job for this annotation

GitHub Actions / plugins (social)

eslint(no-control-regex)

Unexpected control characters
// Owner-only draft body: keep punctuation the friend will see; strip controls and cap.
export const draftText = (value) => String(value ?? '').replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 800)

Check warning on line 86 in plugins/social/src/social.js

View workflow job for this annotation

GitHub Actions / plugins (social)

eslint(no-control-regex)

Unexpected control characters
// Owner cards never show the FLYBOT-FACE-HUMAN wire envelope (#159).
export function approvalDraftText(value) {
const raw = draftText(value)
Expand Down Expand Up @@ -136,7 +136,7 @@
}
const SEEN_KEPT = 1000
const CREDENTIAL_NAME = /(^id_(rsa|dsa|ecdsa|ed25519)|\.(pem|key|p12|pfx|kdbx|keychain)$|credential|secret|token|password|passwd|^flybot-social.*\.json$|^\.?env$|^\.netrc$|^known_hosts$|^authorized_keys$)/i
const safeName = name => String(name ?? 'file').replace(/[\u0000-\u001f\u007f/\\:]/g, '_').replace(/^\.+/, '_').slice(0, 120) || 'file'

Check warning on line 139 in plugins/social/src/social.js

View workflow job for this annotation

GitHub Actions / plugins (social)

eslint(no-control-regex)

Unexpected control characters
// The text Copy puts on the clipboard for a per-Bot invite: what to do with the link, for a
// person in FlyBot or for their Grok bot.
export function inviteText({ handle, name, home, url, expiresAt }) {
Expand Down Expand Up @@ -238,7 +238,7 @@
const code = String(error?.code ?? '')
const blob = `${code} ${msg}`
if (/relationship_exists/i.test(blob)) {
return Object.assign(new Error("You're already friends. That Bot is in Friends' Bots again."), {
return Object.assign(new Error("You're already friends. Their Bot is back in your list."), {
code: 'relationship_exists',
...(error?.peer ? { peer: error.peer } : {}),
...(error?.bot ? { bot: error.bot } : {}),
Expand Down Expand Up @@ -575,7 +575,7 @@
if (result?.unavailable) return
} catch (error) {
if (/invalid_avatar|invalid_color|invalid_request/i.test(`${error?.code ?? ''} ${error?.message ?? ''}`)) {
settings.lookSynced = { ...(settings.lookSynced ?? {}), [bot.id]: fingerprint }

Check warning on line 578 in plugins/social/src/social.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-useless-fallback-in-spread)

Empty fallbacks in spreads are unnecessary
saveSettings()
return
}
Expand Down Expand Up @@ -1129,7 +1129,7 @@
if (settings.via[target] !== bot.id) { settings.via[target] = bot.id; saveSettings() }
if (sent?.message?.status === 'pending_approval') {
pendingCache.at = 0
return `Sent, but Bot Social holds it until you approve it in Friends' Bots. Turn on Auto-accept for this friend to skip the hold on later messages.`
return `Sent, but it waits for your approval in this chat. Turn on Auto-send for this friend to skip that next time.`
}
return ''
} catch (error) {
Expand Down
13 changes: 13 additions & 0 deletions plugins/social/test/social.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4399,3 +4399,16 @@ test('#192: Open on a room the user left shows that room with its transcript, re
assert.equal(room.face.status, 'left')
assert.ok(room.log.some(line => /earlier line/.test(line.text)), 'the transcript is kept')
})

test('#197: a read-only face room hides the composer, the own handle uses normal ink, no Friends\' Bots section is named', () => {
const source = readFileSync(new URL('../client.js', import.meta.url), 'utf8')
// The end line of a left/closed room sits in the composer stack; the composer itself goes.
assert.match(source, /\[class\*="_composerStack"\]:has\(\[data-flybot="face-room-end"\]\) \[class\*="_root"\]:has\(> \[data-composer-card\]\)\{display:none!important\}/)
assert.match(source, /\.fs-me-handle\{[^}]*color:var\(--bt-ink,[^}]*\}/)
assert.doesNotMatch(source.match(/\.fs-me-handle\{[^}]*\}/)[0], /--bt-ink-2/)
const server = readFileSync(new URL('../src/social.js', import.meta.url), 'utf8')
for (const text of [source, server]) {
assert.doesNotMatch(text, /(?:return|Error\()[^\n]*in Friends' Bots/)
}
assert.equal(mapJoinInviteError(Object.assign(new Error('relationship_exists'), { code: 'relationship_exists' })).message, "You're already friends. Their Bot is back in your list.")
})
Loading