From f3db92c52d19f8487b88d000690e0708c550e9c0 Mon Sep 17 00:00:00 2001 From: whereareiam Date: Sun, 1 Nov 2026 12:24:51 +0100 Subject: [PATCH] fix(settings): join fields to their buttons, space searches, restyle the runner setup The search over a list on the settings and administration pages touched the list's box and came apart from its button: the settings form style rounded and bordered the field's right side, which belongs to the button. It now joins its button and sits 16px above the list, as GitHub's filter bar does. Other joined groups came apart the same way, each through a theme rule that outranked the join: the administration's package type dropdown, the pull request merge button's arrow, the wiki's clone field and its HTTP/SSH switch, the empty repository's clone field, and the sign-up username prefix. Code inside a pre block no longer takes inline code's padding, which indented its first line. Subhead buttons wrap under a long title on phones instead of covering it. The runner setup page shown after creating a runner follows GitHub's one-time credential pages: a warning flash, the UUID and token in a box with a copy button each, and the snippets in code blocks with a copy button that copies the commands without their prompts. Co-Authored-By: Claude Opus 5.5 --- components/common/form/form.css | 9 ++ components/common/settings/settings.css | 16 +++ styles/components/issues/pull_review.ts | 4 + styles/components/signin.ts | 12 ++ styles/pages/settings.css | 116 ++++++++++++++++++- styles/pages/wiki.css | 5 +- styles/public/base.ts | 4 +- styles/public/button.ts | 3 +- styles/public/dropdown/selection_dropdown.ts | 11 ++ templates/shared/actions/runner_setup.tmpl | 52 +++++++++ 10 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 templates/shared/actions/runner_setup.tmpl diff --git a/components/common/form/form.css b/components/common/form/form.css index 944bc488..1a738294 100644 --- a/components/common/form/form.css +++ b/components/common/form/form.css @@ -76,6 +76,15 @@ box-shadow: inset 0 0 0 1px var(--github-bgColor-accent-emphasis); } } + /* A field joined to its button (Forgejo's search, .ui.action.input) keeps the join the border above would undo: its + right side is the button's, square and without a line of its own until it has focus. */ + & .ui.action.input:not([class*="left action"]) > input { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + &:not(:focus) { + border-right-color: transparent; + } + } /* A field the server rejected keeps GitHub's red border, which the border above would otherwise replace. */ & .field.error > :is(input, textarea) { border-color: var(--color-error-border); diff --git a/components/common/settings/settings.css b/components/common/settings/settings.css index 25bb7846..f218d8c1 100644 --- a/components/common/settings/settings.css +++ b/components/common/settings/settings.css @@ -130,7 +130,11 @@ font-weight: 500; line-height: 18px; } + /* Forgejo places the right side absolutely, over a long title on a phone; in the row it wraps under it. */ + flex-wrap: wrap; & > :is(.ui.right, .right) { + position: static; + flex: none; display: flex; gap: 8px; margin-left: auto; @@ -236,6 +240,18 @@ } } } + /* A search over a list (runners, the site administration's lists) sits 16px above the list's box, as GitHub's + filter bar does; the repositories' filter menu brings its own margin and divider instead. Unadopted repositories' + field has no search type, and a form that only holds a search field (the token form) has no list under it. */ + & .ui.attached.segment:has(input[type="search"], > form > .ui.action.input):has(+ .ui.attached.segment) { + margin-bottom: 16px; + & > .filter.menu { + margin-bottom: 0; + } + & > .divider { + display: none; + } + } /* An add form below its list (Forgejo's bottom attached segment) sits 16px under the box. */ & .ui.attached.segment + .ui.bottom.attached.segment { margin-top: 16px; diff --git a/styles/components/issues/pull_review.ts b/styles/components/issues/pull_review.ts index e4f79925..4ab15975 100644 --- a/styles/components/issues/pull_review.ts +++ b/styles/components/issues/pull_review.ts @@ -340,6 +340,10 @@ export default css` & svg { color: inherit; } + /* The style's full border would double the seam with the arrow; its border is the line. */ + &:not(:last-child) { + border-inline-end-width: 0; + } } /* GitHub keeps the badge and border neutral for blocked, conflicting, draft and closed pull requests. */ & > .timeline-avatar:is(.red, .grey, .yellow) { diff --git a/styles/components/signin.ts b/styles/components/signin.ts index 9019c289..aa8f59dd 100644 --- a/styles/components/signin.ts +++ b/styles/components/signin.ts @@ -98,12 +98,24 @@ const signIn = css` /* A site-wide username prefix sits in a box joined to the start of the field, with its note below. */ .ui.labeled.input { width: 100%; + /* The site's 32px input height would leave the 40px field hanging below the prefix. */ + height: auto; > .ui.label { display: flex; align-items: center; + margin: 0; + padding: 0 12px; + border: 1px solid ${themeVars.color.input.border}; + border-right: 0; + border-radius: ${otherThemeVars.border.radius} 0 0 ${otherThemeVars.border.radius}; + background: ${themeVars.color.box.header}; font-size: 14px; font-weight: 400; } + > input { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } } .help { display: block; diff --git a/styles/pages/settings.css b/styles/pages/settings.css index 7eb6b365..cbfd7e54 100644 --- a/styles/pages/settings.css +++ b/styles/pages/settings.css @@ -1,5 +1,5 @@ /* Settings sub-pages that need more than the shared settings layout (components/common/settings): two-factor - enrolment, access tokens, webhook deliveries and events, branch protection rules and repository units. */ + enrolment, access tokens, runner setup, webhook deliveries and events, branch protection rules and repository units. */ /* Two-factor enrolment: the QR code in a bordered box, the secret as inline code. */ .page-content > .ui.container.flex-container.theme-settings > .flex-container-main .theme-settings-qr { @@ -94,6 +94,120 @@ } } +/* Runner setup, shown once after a runner is created: the warning as a flash, the UUID and token as rows of a box with + a copy button each, and each snippet in a code block with its copy button in the corner. */ +.page-content > .ui.container.flex-container.theme-settings > .flex-container-main .theme-runner-setup { + & .ui.warning.message { + display: flex; + align-items: flex-start; + gap: 8px; + margin: 0 0 16px; + text-align: start; + & > .svg { + flex: none; + margin-top: 2px; + } + & > p { + margin: 0; + } + } + & .theme-runner-credentials { + margin: 0 0 24px; + border: 1px solid var(--color-light-border); + border-radius: var(--border-radius); + background: var(--color-box-body); + } + & .theme-runner-credential { + display: flex; + align-items: center; + gap: 16px; + min-height: 48px; + padding: 8px 8px 8px 16px; + & + .theme-runner-credential { + border-top: 1px solid var(--color-light-border); + } + & > dt { + flex: none; + width: 64px; + font-weight: 600; + } + & > dd { + display: flex; + flex: 1; + align-items: center; + gap: 8px; + min-width: 0; + margin: 0; + } + & > dd > code { + flex: 1; + min-width: 0; + padding: 0; + background: transparent; + font-size: 12px; + line-height: 20px; + overflow-wrap: anywhere; + } + } + & .theme-runner-setup-heading { + margin: 24px 0 8px; + font-size: 16px; + font-weight: 600; + line-height: 24px; + } + /* The block is drawn by the wrapper, and only the pre scrolls inside it, so a long line passes under the copy button's + column, never under the button. */ + & .theme-runner-snippet { + position: relative; + margin: 0 0 8px; + padding-right: 48px; + border-radius: var(--border-radius); + background: var(--color-markup-code-block); + & > pre { + margin: 0; + padding: 16px 0 16px 16px; + overflow: auto; + border: 0; + border-radius: 0; + background: transparent; + font-family: var(--fonts-monospace); + font-size: 12px; + line-height: 20px; + } + /* Commands and tokens never break: Forgejo wraps code and pre on admin pages with an id selector + (:is(.admin, #detail-modal)), which only !important outranks. */ + & > pre, + & > pre > code { + white-space: pre !important; + overflow-wrap: normal !important; + } + & > pre > code { + padding: 0; + background: transparent; + font: inherit; + } + & > .theme-icon-button { + position: absolute; + top: 8px; + right: 8px; + } + } + & .theme-runner-prompt { + color: var(--color-text-light-2); + user-select: none; + } + & .ui.attached.segment > p { + margin: 0 0 16px; + color: var(--color-text-light-1); + } + & .ui.attached.segment > p > code { + padding: 2px 6px; + border-radius: var(--border-radius); + background: var(--color-markup-code-inline); + font-size: 12px; + } +} + /* Webhook form: the events to choose sit under their radio, GitHub's two columns of checkboxes. */ .page-content.new.webhook > .ui.container.flex-container.theme-settings diff --git a/styles/pages/wiki.css b/styles/pages/wiki.css index 0421ebd4..58cd9ec8 100644 --- a/styles/pages/wiki.css +++ b/styles/pages/wiki.css @@ -351,7 +351,8 @@ margin: 0; padding: 0 8px; border: 1px solid transparent; - border-radius: var(--border-radius); + /* Forgejo squares the first switch for a joined row with !important; here the switches stand apart. */ + border-radius: var(--border-radius) !important; background: transparent; box-shadow: none; color: var(--color-text); @@ -376,6 +377,8 @@ width: 100%; min-width: 0; height: 32px; + /* Forgejo's -1px overlap is for a single row; on its own row it opened a gap before the copy button. */ + margin: 0; padding: 5px 8px; border: 1px solid var(--color-light-border); border-radius: var(--border-radius) 0 0 var(--border-radius); diff --git a/styles/public/base.ts b/styles/public/base.ts index 8c605294..e76c3544 100644 --- a/styles/public/base.ts +++ b/styles/public/base.ts @@ -69,8 +69,8 @@ const body = css` text-underline-position: unset; } } - /* 默认代码块 */ - code:not(.code-inner) { + /* 默认代码块; the code of a block (pre > code) takes its padding from the pre, as on GitHub, so its first line is not indented. */ + code:not(.code-inner, pre > code) { padding: 0.2em 0.4em; } /* 快捷键提示(输入框内) */ diff --git a/styles/public/button.ts b/styles/public/button.ts index 8bb42809..bab92306 100644 --- a/styles/public/button.ts +++ b/styles/public/button.ts @@ -153,7 +153,8 @@ const fixButtonHeight = css` height: 32px; } /* 修复仓库页仓库操作按钮高度对齐和修正 */ - .repo-button-row .ui.button { + .repo-button-row .ui.button, + .repo-button-row .clone-panel > input { min-height: 32px; height: 32px; } diff --git a/styles/public/dropdown/selection_dropdown.ts b/styles/public/dropdown/selection_dropdown.ts index 747d83ce..0f33277f 100644 --- a/styles/public/dropdown/selection_dropdown.ts +++ b/styles/public/dropdown/selection_dropdown.ts @@ -90,6 +90,17 @@ const selectionDropdown = css` .ui.action.input > .dropdown.small:not(:first-child) { border-radius: 0; } + /* The radius rule above counts :has(> #uid) as an id, so a dropdown joined after a field (the admin package type) + repeats it to stay square on its joined sides; before a button, the button's border is the line between them. */ + .ui.action.input > .ui.selection.dropdown:not(:first-child):not(:has(> #uid)) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + &:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right-color: transparent; + } + } /* 排除一些小按钮, 例如软件包类型, 通常相邻有元素 */ .ui.selection.dropdown.active:not(.small) { border-bottom-left-radius: ${otherThemeVars.border.radius} !important; diff --git a/templates/shared/actions/runner_setup.tmpl b/templates/shared/actions/runner_setup.tmpl new file mode 100644 index 00000000..a583e418 --- /dev/null +++ b/templates/shared/actions/runner_setup.tmpl @@ -0,0 +1,52 @@ +{{/* Forgejo's runner setup, shown once after a runner is created, in the shape GitHub gives a credential it shows only +once: the warning as a flash, the UUID and token in a box with a copy button each, and each snippet in a code block +with its own copy button. The copy buttons copy the commands without their "$ " prompts. */}} +
+

+ {{ctx.Locale.Tr "actions.runners.runner_setup.title" .Runner.Name}} + +

+
+
+ {{svg "octicon-alert"}} +

{{ctx.Locale.Tr "actions.runners.runner_setup.last_chance_copying_token"}}

+
+
+
+
{{ctx.Locale.Tr "actions.runners.uuid"}}
+
+ {{.Runner.UUID}} + +
+
+
+
{{ctx.Locale.Tr "actions.runners.token"}}
+
+ {{.Runner.Token}} + +
+
+
+

{{ctx.Locale.Tr "actions.runners.runner_setup.heading_using_configuration"}}

+ {{- $configuration := printf "server:\n connections:\n forgejo:\n url: %s\n uuid: %s\n token: %s\n" .AppURL .Runner.UUID .Runner.Token}} +
+
{{$configuration}}
+ +
+

{{ctx.Locale.Tr "actions.runners.runner_setup.instruction_replace_connection_name"}}

+

{{ctx.Locale.Tr "actions.runners.runner_setup.heading_using_options"}}

+ {{- $token := printf "echo -n \"%s\" > /path/to/runner-token" .Runner.Token}} + {{- $daemon := printf "forgejo-runner daemon \\\n --url %s \\\n --uuid %s \\\n --token-url file:///path/to/runner-token \\\n --label docker:docker://node:lts" .AppURL .Runner.UUID}} +
+
{{$token}}
+{{$daemon}}
+
+ +
+

{{ctx.Locale.Tr "actions.runners.runner_setup.instruction_advanced_configurations" (AppDocsURL "admin/actions/")}}

+
+