From 6abe5b188265f9c573b8089c4382b3e0a9758399 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Tue, 3 Nov 2026 21:36:34 +0000 Subject: [PATCH] feat(github): rework the connection page of the GitHub add-on Token help now works: a button opens GitHub's fine-grained token page, the steps are open while there is no connection, and a table lists the permissions for read only and for write modes. The page says it does not answer mentions yet. Errors sit under their own field. The repository list splits on lines and commas only, so "not a repo" is one entry and is named in the message. The connection name defaults to the first repository. The write mode cards each carry one consequence line, and "Allowed" is now "Acts without asking". The Test result is drawn per repository in the row, with a tone, a busy line, and a plain text when GitHub refuses the token. The unit tests and a browser spec cover the page. Assisted-by: Claude:claude-sonnet-5-5 node playwright eslint --- addons/github/ui/src/addon.jsx | 141 +++++++++++++++----- addons/github/ui/src/github.css | 41 ++++++ addons/github/ui/src/i18n/github.en.json | 98 +++++++++++--- addons/github/ui/src/repos.js | 70 ++++++++-- addons/github/ui/src/results.js | 30 +++++ addons/github/ui/test/github.test.mjs | 57 +++++++- core/http/react-ui/e2e/github-addon.spec.js | 94 +++++++++++++ core/http/react-ui/e2e/studio-addon.js | 4 +- docs/content/features/chats-connectors.md | 12 +- 9 files changed, 472 insertions(+), 75 deletions(-) create mode 100644 addons/github/ui/src/github.css create mode 100644 addons/github/ui/src/results.js create mode 100644 core/http/react-ui/e2e/github-addon.spec.js diff --git a/addons/github/ui/src/addon.jsx b/addons/github/ui/src/addon.jsx index 6ad153b751..f86ac11761 100644 --- a/addons/github/ui/src/addon.jsx +++ b/addons/github/ui/src/addon.jsx @@ -1,44 +1,58 @@ /* eslint-disable no-unused-vars -- components used only inside JSX look unused to this config, which has no eslint-plugin-react */ +import { useEffect, useState } from 'react' import { RouteProvider } from 'react-router-dom' import { getSdk, } from '../../../internal/host-compat/src/runtime.js' import { registerLocales } from '../../../internal/host-compat/src/i18n.js' +import Icon from '../../../internal/host-compat/src/components/Icon' import ConnectorPage from '../../../internal/connector-ui/src/ConnectorPage' -import { Field, FieldError, Notice, RadioCards, TextInput } from '../../../internal/connector-ui/src/controls' +import { connectorsApi } from '../../../internal/connector-ui/src/api' +import { Field, Notice, RadioCards, TextInput } from '../../../internal/connector-ui/src/controls' import en from './i18n/github.en.json' -import { checkLine, extraBody, extraForm, extraValid, githubSecret, parseRepos, reposProblem, tokenProblem, WRITE_MODES, writeModeOf } from './repos' +import { checkFailure, checkItem, extraBody, extraForm, extraValid, followName, githubSecret, parseRepos, PERMISSIONS, reposIssue, tokenProblem, WRITE_MODES, writeModeOf } from './repos' +import { finishTest, resultOf, startTest, subscribe } from './results' +import './github.css' registerLocales({ github: { en } }) +const TOKEN_URL = 'https://github.com/settings/personal-access-tokens/new' + +function TokenLink({ t, className, label }) { + return ( + + {label || t('tokenLink')} + + ) +} + // The fine-grained token. It is typed only to replace it. function GitHubToken({ form, setForm, t }) { const problem = tokenProblem(form) + const hint = <>{t('tokenHelp')}{!form.id && <>{' '}} return ( - + {(p) => ( setForm({ ...form, token: e.target.value })} /> )} ) } -// The repositories the agent may read. The host refuses any other one before +// The repositories the agent may use. The host refuses any other one before // the add-on is called. function GitHubFields({ form, setForm, t }) { - const problem = reposProblem(form.repos_text) + const issue = reposIssue(form.repos_text) const count = parseRepos(form.repos_text).length + const change = (text) => setForm({ ...form, repos_text: text, name: followName(form.name, form.repos_text, text) }) return ( <> - {tokenProblem(form) && {t(tokenProblem(form))}} - + {(p) => (