Skip to content
Closed
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
7 changes: 4 additions & 3 deletions core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ var ZH = {
"Copy invite link for {name}": "复制 {name} 的邀请链接",
"Leave {name} out": "不拉 {name} 进群",
"Invite link for {name} copied. Send it to your friend; once they accept, create the group again.": "{name} 的邀请链接已复制。发给好友,对方接受后再重新建群。",
"Send this invite link for {name} to your friend; once they accept, create the group again: {link}": "把 {name} 的邀请链接发给好友,对方接受后再重新建群:{link}",
"Could not create the invite. Use Bot settings → Invite to this Bot.": "无法创建邀请。请在 Bot 设置里使用 Invite to this Bot。",
"Face-to-face group": "面对面建群",
"Meet the people next to you. Pick your Bots and type the same 6-digit number.": "和身边的人建群。选你的 Bot,输入同一组 6 位数字。",
Expand Down Expand Up @@ -3085,7 +3086,7 @@ button.bt-q-opt:active{background:var(--bt-active)}
.bt-error{padding:0 14px 10px;color:#e02135;font-size:12px}
.bt-fix-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.bt-fix-btn{font:inherit;font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid currentColor;background:transparent;color:inherit;cursor:pointer}
.bt-fix .bt-note{margin-top:6px}
.bt-fix .bt-note{margin-top:6px;overflow-wrap:anywhere;user-select:text}

.bt-pane{position:fixed;top:var(--dsh-frame-chrome-top,0px);right:0;bottom:0;z-index:45;pointer-events:auto;background:var(--bt-main);color:var(--bt-ink);display:flex;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif}
.bt-dialog{flex:1;display:flex;flex-direction:column;min-width:0}
Expand Down Expand Up @@ -9665,8 +9666,8 @@ function NewChat({ mode, roster, actions }) {
const invite = await social.createInvite(bot.id);
const link = invite?.link || invite?.url;
if (!link) throw new Error("no link");
await navigator.clipboard?.writeText(link);
setFixNote(t("Invite link for {name} copied. Send it to your friend; once they accept, create the group again.", { name: bot.name }));
const copied = navigator.clipboard?.writeText === void 0 ? false : await navigator.clipboard.writeText(link).then(() => true, () => false);
setFixNote(copied ? t("Invite link for {name} copied. Send it to your friend; once they accept, create the group again.", { name: bot.name }) : t("Send this invite link for {name} to your friend; once they accept, create the group again: {link}", { name: bot.name, link }));
} catch {
setFixNote(t("Could not create the invite. Use Bot settings → Invite to this Bot."));
}
Expand Down
1 change: 1 addition & 0 deletions core/src/client/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const ZH = {
'Copy invite link for {name}': '复制 {name} 的邀请链接',
'Leave {name} out': '不拉 {name} 进群',
'Invite link for {name} copied. Send it to your friend; once they accept, create the group again.': '{name} 的邀请链接已复制。发给好友,对方接受后再重新建群。',
'Send this invite link for {name} to your friend; once they accept, create the group again: {link}': '把 {name} 的邀请链接发给好友,对方接受后再重新建群:{link}',
'Could not create the invite. Use Bot settings → Invite to this Bot.': '无法创建邀请。请在 Bot 设置里使用 Invite to this Bot。',
'Face-to-face group': '面对面建群',
'Meet the people next to you. Pick your Bots and type the same 6-digit number.': '和身边的人建群。选你的 Bot,输入同一组 6 位数字。',
Expand Down
10 changes: 8 additions & 2 deletions core/src/client/new-chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,14 @@ export function NewChat({ mode, roster, actions }) {
const invite = await social.createInvite(bot.id)
const link = invite?.link || invite?.url
if (!link) throw new Error('no link')
await navigator.clipboard?.writeText(link)
setFixNote(t('Invite link for {name} copied. Send it to your friend; once they accept, create the group again.', { name: bot.name }))
// No clipboard API (an insecure context, a locked-down WebView) or a refused write is not
// a copy: the card then shows the link itself, since it is shown nowhere else.
const copied = navigator.clipboard?.writeText === undefined
? false
: await navigator.clipboard.writeText(link).then(() => true, () => false)
setFixNote(copied
? t('Invite link for {name} copied. Send it to your friend; once they accept, create the group again.', { name: bot.name })
: t('Send this invite link for {name} to your friend; once they accept, create the group again: {link}', { name: bot.name, link }))

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 Record the clipboard fallback in the changelog

The clipboard-failure branch changes what users see, but I checked the full 3e9c47a changed-file list and it neither adds nor updates a changelog.d fragment. Record this in a .fixed.md fragment (or update the existing unreleased group-friend-bot-refusal fragment) so release notes include it; repository policy requires a fragment for user-visible changes.

AGENTS.md reference: AGENTS.md:L20-L20

Useful? React with 👍 / 👎.

} catch {
setFixNote(t('Could not create the invite. Use Bot settings → Invite to this Bot.'))
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/client/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ button.bt-q-opt:active{background:var(--bt-active)}
.bt-error{padding:0 14px 10px;color:#e02135;font-size:12px}
.bt-fix-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.bt-fix-btn{font:inherit;font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid currentColor;background:transparent;color:inherit;cursor:pointer}
.bt-fix .bt-note{margin-top:6px}
.bt-fix .bt-note{margin-top:6px;overflow-wrap:anywhere;user-select:text}

.bt-pane{position:fixed;top:var(--dsh-frame-chrome-top,0px);right:0;bottom:0;z-index:45;pointer-events:auto;background:var(--bt-main);color:var(--bt-ink);display:flex;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif}
.bt-dialog{flex:1;display:flex;flex-direction:column;min-width:0}
Expand Down
6 changes: 5 additions & 1 deletion core/test/new-chat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ test('new-chat turns a refused group into a card with the invite and leave-out a
assert.doesNotMatch(src, /setGrants|grants\.set/)
const api = readFileSync(new URL('../src/client/api.js', import.meta.url), 'utf8')
assert.match(api, /details: result\.error\?\.details/)
for (const key of ['Copy invite link for {name}', 'Leave {name} out', 'that friend']) {
// A missing clipboard API or a refused write is not reported as copied; the link is shown instead.
assert.doesNotMatch(src, /await navigator\.clipboard\?\.writeText/)
assert.match(src, /navigator\.clipboard\?\.writeText === undefined/)
assert.match(src, /\{ name: bot\.name, link \}/)
for (const key of ['Copy invite link for {name}', 'Leave {name} out', 'that friend', 'Send this invite link for {name} to your friend; once they accept, create the group again: {link}']) {
assert.ok(DICTIONARY.zh[key], `ZH entry for ${key}`)
}
})
2 changes: 1 addition & 1 deletion docs/subsystems/core-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The browser side of the patched `ds-bot` core (`core/`): sources in `core/src/cl
| File | Role |
| --- | --- |
| `core/src/client/` | Client sources → build to `core/client.js`. |
| `core/src/client/new-chat.js` | Sidebar **+** picker. Plugin rows via `flybot.new-chat.action` (`newChatActionRows` in `flybot-registry.js`); social adds Face-to-face group and Add friend (copy actions live inside Add friend, #181). The DSH Agent row reads **Add coding agent** (hint "A plain DSH session, not a Bot") so it is not mistaken for **Create new Bot** (#181). The short + menu does not list individual Bots. A group the host refuses because one of my Bots is not shared with the friend whose Bot is in it (`error.details.reason === 'ungranted-local'`, put on the error by `client/api.js`) is drawn as a card, not a red line: it names the Bot(s) and friend(s), and offers **Copy invite link for {Bot}** (`__flybot__.social.createInvite`; shown only when social is loaded and can invite that Bot, nothing is granted by the click) and **Leave {Bot} out** (`ungrantedLocalFix`, `shared/remote-label.js`). |
| `core/src/client/new-chat.js` | Sidebar **+** picker. Plugin rows via `flybot.new-chat.action` (`newChatActionRows` in `flybot-registry.js`); social adds Face-to-face group and Add friend (copy actions live inside Add friend, #181). The DSH Agent row reads **Add coding agent** (hint "A plain DSH session, not a Bot") so it is not mistaken for **Create new Bot** (#181). The short + menu does not list individual Bots. A group the host refuses because one of my Bots is not shared with the friend whose Bot is in it (`error.details.reason === 'ungranted-local'`, put on the error by `client/api.js`) is drawn as a card, not a red line: it names the Bot(s) and friend(s), and offers **Copy invite link for {Bot}** (`__flybot__.social.createInvite`; shown only when social is loaded and can invite that Bot, nothing is granted by the click; with no clipboard API or a refused write the note shows the link itself instead of saying it was copied) and **Leave {Bot} out** (`ungrantedLocalFix`, `shared/remote-label.js`). |
| `core/test/new-chat-menu.test.js` | **+** menu rows (#283): an action row's description is its own line under the title, an inline Bot hint is capped, so hovering never hides a title. |
| `core/test/new-chat.test.js` | Picker filters and the **+** short action menu (`data-menu`; New chat, then Create group chat, plugin actions, then Add Agent). `ungrantedLocalFix` and the refusal card (invite and leave-out actions, no grant written by the client). |

Expand Down
3 changes: 2 additions & 1 deletion patches/ds-bot/0001-core-fixes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ diff --git a/src/client/i18n.js b/src/client/i18n.js
Add: '添加',
Close: '关闭',
Copy: '复制',
@@ -47,15 +47,52 @@ const ZH = {
@@ -47,15 +47,53 @@ const ZH = {
'Remove Main Bot role': '取消主 Bot',
'Bot settings': 'Bot 设置',
'Create group chat': '新建群聊',
Expand All @@ -125,6 +125,7 @@ diff --git a/src/client/i18n.js b/src/client/i18n.js
+ 'Copy invite link for {name}': '复制 {name} 的邀请链接',
+ 'Leave {name} out': '不拉 {name} 进群',
+ 'Invite link for {name} copied. Send it to your friend; once they accept, create the group again.': '{name} 的邀请链接已复制。发给好友,对方接受后再重新建群。',
+ 'Send this invite link for {name} to your friend; once they accept, create the group again: {link}': '把 {name} 的邀请链接发给好友,对方接受后再重新建群:{link}',
+ 'Could not create the invite. Use Bot settings → Invite to this Bot.': '无法创建邀请。请在 Bot 设置里使用 Invite to this Bot。',
+ 'Face-to-face group': '面对面建群',
+ 'Meet the people next to you. Pick your Bots and type the same 6-digit number.': '和身边的人建群。选你的 Bot,输入同一组 6 位数字。',
Expand Down
14 changes: 10 additions & 4 deletions patches/ds-bot/0003-flybot-api-client.patch
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ diff --git a/src/client/new-chat.js b/src/client/new-chat.js
const [cursor, setCursor] = useState(0)
// The Create Bot form, opened straight from 'create' mode or from the list.
const [draft, setDraft] = useState(mode === 'create' ? { name: '', fromList: false } : null)
@@ -24,15 +24,44 @@ export function NewChat({ mode, roster, actions }) {
@@ -24,15 +24,50 @@ export function NewChat({ mode, roster, actions }) {
useEscape(close)
const paneLeft = usePaneLeft()
const wanted = query.trim().toLowerCase()
Expand Down Expand Up @@ -831,8 +831,14 @@ diff --git a/src/client/new-chat.js b/src/client/new-chat.js
+ const invite = await social.createInvite(bot.id)
+ const link = invite?.link || invite?.url
+ if (!link) throw new Error('no link')
+ await navigator.clipboard?.writeText(link)
+ setFixNote(t('Invite link for {name} copied. Send it to your friend; once they accept, create the group again.', { name: bot.name }))
+ // No clipboard API (an insecure context, a locked-down WebView) or a refused write is not
+ // a copy: the card then shows the link itself, since it is shown nowhere else.
+ const copied = navigator.clipboard?.writeText === undefined
+ ? false
+ : await navigator.clipboard.writeText(link).then(() => true, () => false)
+ setFixNote(copied
+ ? t('Invite link for {name} copied. Send it to your friend; once they accept, create the group again.', { name: bot.name })
+ : t('Send this invite link for {name} to your friend; once they accept, create the group again: {link}', { name: bot.name, link }))
+ } catch {
+ setFixNote(t('Could not create the invite. Use Bot settings → Invite to this Bot.'))
+ }
Expand Down Expand Up @@ -1477,7 +1483,7 @@ diff --git a/src/client/styles.js b/src/client/styles.js
.bt-error{padding:0 14px 10px;color:#e02135;font-size:12px}
+.bt-fix-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
+.bt-fix-btn{font:inherit;font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid currentColor;background:transparent;color:inherit;cursor:pointer}
+.bt-fix .bt-note{margin-top:6px}
+.bt-fix .bt-note{margin-top:6px;overflow-wrap:anywhere;user-select:text}

.bt-pane{position:fixed;top:var(--dsh-frame-chrome-top,0px);right:0;bottom:0;z-index:45;pointer-events:auto;background:var(--bt-main);color:var(--bt-ink);display:flex;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif}
.bt-dialog{flex:1;display:flex;flex-direction:column;min-width:0}
Expand Down
9 changes: 5 additions & 4 deletions patches/ds-bot/0008-client-bundle.patch
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ diff --git a/client.js b/client.js
Add: "添加",
Close: "关闭",
Copy: "复制",
@@ -84,14 +84,51 @@ var ZH = {
@@ -84,14 +84,52 @@ var ZH = {
"Remove Main Bot role": "取消主 Bot",
"Bot settings": "Bot 设置",
"Create group chat": "新建群聊",
Expand All @@ -35,6 +35,7 @@ diff --git a/client.js b/client.js
+ "Copy invite link for {name}": "复制 {name} 的邀请链接",
+ "Leave {name} out": "不拉 {name} 进群",
+ "Invite link for {name} copied. Send it to your friend; once they accept, create the group again.": "{name} 的邀请链接已复制。发给好友,对方接受后再重新建群。",
+ "Send this invite link for {name} to your friend; once they accept, create the group again: {link}": "把 {name} 的邀请链接发给好友,对方接受后再重新建群:{link}",
+ "Could not create the invite. Use Bot settings → Invite to this Bot.": "无法创建邀请。请在 Bot 设置里使用 Invite to this Bot。",
+ "Face-to-face group": "面对面建群",
+ "Meet the people next to you. Pick your Bots and type the same 6-digit number.": "和身边的人建群。选你的 Bot,输入同一组 6 位数字。",
Expand Down Expand Up @@ -1466,7 +1467,7 @@ diff --git a/client.js b/client.js
.bt-error{padding:0 14px 10px;color:#e02135;font-size:12px}
+.bt-fix-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
+.bt-fix-btn{font:inherit;font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid currentColor;background:transparent;color:inherit;cursor:pointer}
+.bt-fix .bt-note{margin-top:6px}
+.bt-fix .bt-note{margin-top:6px;overflow-wrap:anywhere;user-select:text}

.bt-pane{position:fixed;top:var(--dsh-frame-chrome-top,0px);right:0;bottom:0;z-index:45;pointer-events:auto;background:var(--bt-main);color:var(--bt-ink);display:flex;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif}
.bt-dialog{flex:1;display:flex;flex-direction:column;min-width:0}
Expand Down Expand Up @@ -6926,8 +6927,8 @@ diff --git a/client.js b/client.js
+ const invite = await social.createInvite(bot.id);
+ const link = invite?.link || invite?.url;
+ if (!link) throw new Error("no link");
+ await navigator.clipboard?.writeText(link);
+ setFixNote(t("Invite link for {name} copied. Send it to your friend; once they accept, create the group again.", { name: bot.name }));
+ const copied = navigator.clipboard?.writeText === void 0 ? false : await navigator.clipboard.writeText(link).then(() => true, () => false);
+ setFixNote(copied ? t("Invite link for {name} copied. Send it to your friend; once they accept, create the group again.", { name: bot.name }) : t("Send this invite link for {name} to your friend; once they accept, create the group again: {link}", { name: bot.name, link }));
+ } catch {
+ setFixNote(t("Could not create the invite. Use Bot settings → Invite to this Bot."));
+ }
Expand Down
6 changes: 5 additions & 1 deletion patches/ds-bot/files/test/new-chat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ test('new-chat turns a refused group into a card with the invite and leave-out a
assert.doesNotMatch(src, /setGrants|grants\.set/)
const api = readFileSync(new URL('../src/client/api.js', import.meta.url), 'utf8')
assert.match(api, /details: result\.error\?\.details/)
for (const key of ['Copy invite link for {name}', 'Leave {name} out', 'that friend']) {
// A missing clipboard API or a refused write is not reported as copied; the link is shown instead.
assert.doesNotMatch(src, /await navigator\.clipboard\?\.writeText/)
assert.match(src, /navigator\.clipboard\?\.writeText === undefined/)
assert.match(src, /\{ name: bot\.name, link \}/)
for (const key of ['Copy invite link for {name}', 'Leave {name} out', 'that friend', 'Send this invite link for {name} to your friend; once they accept, create the group again: {link}']) {
assert.ok(DICTIONARY.zh[key], `ZH entry for ${key}`)
}
})
Loading