diff --git a/packages/app/src/composer/editor/editor.tsx b/packages/app/src/composer/editor/editor.tsx index ea0220fe706..22b91bdcb4d 100644 --- a/packages/app/src/composer/editor/editor.tsx +++ b/packages/app/src/composer/editor/editor.tsx @@ -841,8 +841,8 @@ export function ComposerEditorSubmitButton(props: { disabled={!props.stopping && props.disabled} tabIndex={props.mode === "normal" ? undefined : -1} icon={} - variant="contrast" - class="size-7 rounded-md p-[6px] disabled:opacity-50" + variant="submit" + class="size-7 rounded-md p-[6px]" aria-label={props.stopping ? props.stopLabel : props.sendLabel} onClick={(event) => { event.preventDefault() diff --git a/packages/app/src/session/requests/session-permission-dock.tsx b/packages/app/src/session/requests/session-permission-dock.tsx index 4657d61c9d7..db54fc57c52 100644 --- a/packages/app/src/session/requests/session-permission-dock.tsx +++ b/packages/app/src/session/requests/session-permission-dock.tsx @@ -45,7 +45,7 @@ export function SessionPermissionDock(props: { > {language.t("ui.permission.allowAlways")} - props.onDecide("once")} disabled={props.responding}> + props.onDecide("once")} disabled={props.responding}> {language.t("ui.permission.allowOnce")} diff --git a/packages/app/src/session/requests/session-question-dock.tsx b/packages/app/src/session/requests/session-question-dock.tsx index d298766ad01..24b76e28c7c 100644 --- a/packages/app/src/session/requests/session-question-dock.tsx +++ b/packages/app/src/session/requests/session-question-dock.tsx @@ -523,7 +523,7 @@ export const SessionQuestionDock: Component<{ request: FormInfo; onSubmit: () => { const selected = props.model.selected() diff --git a/packages/ui/src/actions/button/button.css b/packages/ui/src/actions/button/button.css index f96d804913f..13230546728 100644 --- a/packages/ui/src/actions/button/button.css +++ b/packages/ui/src/actions/button/button.css @@ -1,3 +1,5 @@ +@import "../submit.css"; + * { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; diff --git a/packages/ui/src/actions/button/button.stories.tsx b/packages/ui/src/actions/button/button.stories.tsx index e065f133015..f96b20c9b02 100644 --- a/packages/ui/src/actions/button/button.stories.tsx +++ b/packages/ui/src/actions/button/button.stories.tsx @@ -5,7 +5,8 @@ const docs = `### Overview Button v2 with visual variants and three sizes. ### API -- \`variant\`: "neutral" | "danger" | "warning" | "contrast" | "ghost" | "ghost-muted" | "ghost-faint" | "loading". +- \`variant\`: "neutral" | "danger" | "warning" | "contrast" | "submit" | "ghost" | "ghost-muted" | "ghost-faint" | "loading". +- \`submit\` shares the brighter contrast icon-button styling for Composer submission actions. - \`size\`: "small" | "normal" | "large". - \`icon\`: Optional icon name. - Inherits Kobalte Button props and native button attributes. @@ -40,7 +41,7 @@ export default { }, variant: { control: "select", - options: ["neutral", "danger", "warning", "contrast", "ghost", "ghost-muted", "ghost-faint", "loading"], + options: ["neutral", "danger", "warning", "contrast", "submit", "ghost", "ghost-muted", "ghost-faint", "loading"], }, size: { control: "select", @@ -65,6 +66,7 @@ export const Variants = { Danger Warning Contrast + Submit Ghost Ghost muted @@ -127,6 +129,7 @@ export const AllStates = { "danger", "warning", "contrast", + "submit", "ghost", "ghost-muted", "ghost-faint", diff --git a/packages/ui/src/actions/button/button.tsx b/packages/ui/src/actions/button/button.tsx index 3d04fb1e01f..f8206a8b1e5 100644 --- a/packages/ui/src/actions/button/button.tsx +++ b/packages/ui/src/actions/button/button.tsx @@ -13,6 +13,7 @@ export interface ButtonProps | "warning" | "outline" | "contrast" + | "submit" | "ghost" | "ghost-muted" | "ghost-faint" diff --git a/packages/ui/src/actions/icon-button/icon-button.css b/packages/ui/src/actions/icon-button/icon-button.css index dfee295bb46..dc4cd2c4bf7 100644 --- a/packages/ui/src/actions/icon-button/icon-button.css +++ b/packages/ui/src/actions/icon-button/icon-button.css @@ -1,3 +1,5 @@ +@import "../submit.css"; + * { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -69,61 +71,6 @@ cursor: not-allowed; } -/* Contrast */ -[data-component="icon-button-v2"][data-variant="contrast"] { - --icon-button-contrast-highlight: var(--v2-alpha-light-20); - --icon-button-contrast-hover: var(--v2-overlay-simple-overlay-contrast-hover); - - background-image: - linear-gradient(180deg, var(--icon-button-contrast-highlight) 0%, var(--v2-alpha-light-0) 100%), - linear-gradient( - 90deg, - var(--v2-background-bg-icon-button-contrast) 0%, - var(--v2-background-bg-icon-button-contrast) 100% - ); - color: var(--v2-text-text-contrast); - text-shadow: 0 0.5px 0.5px rgba(0, 0, 0, 0.3); - box-shadow: var(--v2-elevation-button-contrast); -} - -[data-color-scheme="dark"] [data-component="icon-button-v2"][data-variant="contrast"] { - --icon-button-contrast-highlight: var(--v2-alpha-light-60); - --icon-button-contrast-hover: var(--v2-alpha-dark-8); - - color: var(--v2-icon-icon-inverse); -} - -[data-component="icon-button-v2"][data-variant="contrast"]:is(:hover, [data-state="hover"]):not(:disabled) { - background-image: - linear-gradient(90deg, var(--icon-button-contrast-hover) 0%, var(--icon-button-contrast-hover) 100%), - linear-gradient(180deg, var(--icon-button-contrast-highlight) 0%, var(--v2-alpha-light-0) 100%), - linear-gradient( - 90deg, - var(--v2-background-bg-icon-button-contrast) 0%, - var(--v2-background-bg-icon-button-contrast) 100% - ); -} - -[data-component="icon-button-v2"][data-variant="contrast"]:is(:active, [data-state="pressed"]):not(:disabled) { - background-image: - linear-gradient( - 90deg, - var(--v2-overlay-simple-overlay-contrast-pressed) 0%, - var(--v2-overlay-simple-overlay-contrast-pressed) 100% - ), - linear-gradient(180deg, var(--icon-button-contrast-highlight) 0%, var(--v2-alpha-light-0) 100%), - linear-gradient( - 90deg, - var(--v2-background-bg-icon-button-contrast) 0%, - var(--v2-background-bg-icon-button-contrast) 100% - ); -} - -[data-component="icon-button-v2"][data-variant="contrast"]:is(:disabled, [data-state="disabled"]) { - opacity: 0.4; - cursor: not-allowed; -} - /* Ghost */ [data-component="icon-button-v2"][data-variant="ghost"] { background-color: transparent; diff --git a/packages/ui/src/actions/icon-button/icon-button.stories.tsx b/packages/ui/src/actions/icon-button/icon-button.stories.tsx index 37bfee00161..3acbf697b45 100644 --- a/packages/ui/src/actions/icon-button/icon-button.stories.tsx +++ b/packages/ui/src/actions/icon-button/icon-button.stories.tsx @@ -3,11 +3,12 @@ import { IconButton } from "./icon-button" import { Icon } from "@opencode/ui/icon" const docs = `### Overview -Square icon-only button with three visual variants and three sizes. +Square icon-only button with visual variants and three sizes. ### API - \`icon\`: Icon content. -- \`variant\`: "neutral" | "contrast" | "ghost". +- \`variant\`: "neutral" | "contrast" | "submit" | "ghost". +- \`submit\` shares the contrast styling with regular Composer submit buttons. - \`size\`: "small" | "normal" | "large". - Inherits Kobalte Button props and native button attributes. @@ -41,7 +42,7 @@ export default { }, variant: { control: "select", - options: ["neutral", "contrast", "ghost"], + options: ["neutral", "contrast", "submit", "ghost"], }, size: { control: "select", @@ -57,6 +58,7 @@ export const Variants = { } variant="neutral" /> } variant="contrast" /> + } variant="submit" /> } variant="ghost" /> ), @@ -74,7 +76,7 @@ export const Sizes = { export const AllStates = { render: () => { - const variants = ["neutral", "contrast", "ghost"] as const + const variants = ["neutral", "contrast", "submit", "ghost"] as const const states = ["default", "hover", "pressed", "focus", "disabled"] as const return ( diff --git a/packages/ui/src/actions/icon-button/icon-button.tsx b/packages/ui/src/actions/icon-button/icon-button.tsx index b515126dbd2..6c6a5817ac4 100644 --- a/packages/ui/src/actions/icon-button/icon-button.tsx +++ b/packages/ui/src/actions/icon-button/icon-button.tsx @@ -8,7 +8,7 @@ export interface IconButtonProps Pick, "class" | "classList"> { icon?: JSX.Element size?: "small" | "normal" | "large" - variant?: "neutral" | "contrast" | "ghost" | "ghost-muted" + variant?: "neutral" | "contrast" | "submit" | "ghost" | "ghost-muted" state?: "rest" | "hover" | "pressed" } diff --git a/packages/ui/src/actions/submit.css b/packages/ui/src/actions/submit.css new file mode 100644 index 00000000000..fd521c3a629 --- /dev/null +++ b/packages/ui/src/actions/submit.css @@ -0,0 +1,74 @@ +/* Shared by Composer submit actions and contrast icon buttons. */ +:is( + [data-component="button-v2"][data-variant="submit"], + [data-component="icon-button-v2"]:is([data-variant="submit"], [data-variant="contrast"]) +) { + --submit-highlight: var(--v2-alpha-light-20); + --submit-hover: var(--v2-overlay-simple-overlay-contrast-hover); + + background-image: + linear-gradient(180deg, var(--submit-highlight) 0%, var(--v2-alpha-light-0) 100%), + linear-gradient( + 90deg, + var(--v2-background-bg-icon-button-contrast) 0%, + var(--v2-background-bg-icon-button-contrast) 100% + ); + color: var(--v2-text-text-contrast); + text-shadow: 0 0.5px 0.5px rgba(0, 0, 0, 0.3); + box-shadow: var(--v2-elevation-button-contrast); +} + +[data-color-scheme="dark"] + :is( + [data-component="button-v2"][data-variant="submit"], + [data-component="icon-button-v2"]:is([data-variant="submit"], [data-variant="contrast"]) + ) { + --submit-highlight: var(--v2-alpha-light-60); + --submit-hover: var(--v2-alpha-dark-8); + + color: var(--v2-text-text-inverse); +} + +[data-color-scheme="dark"] [data-component="icon-button-v2"]:is([data-variant="submit"], [data-variant="contrast"]) { + color: var(--v2-icon-icon-inverse); +} + +:is( + [data-component="button-v2"][data-variant="submit"], + [data-component="icon-button-v2"]:is([data-variant="submit"], [data-variant="contrast"]) + ):is(:hover, [data-state="hover"]):not(:disabled) { + background-image: + linear-gradient(90deg, var(--submit-hover) 0%, var(--submit-hover) 100%), + linear-gradient(180deg, var(--submit-highlight) 0%, var(--v2-alpha-light-0) 100%), + linear-gradient( + 90deg, + var(--v2-background-bg-icon-button-contrast) 0%, + var(--v2-background-bg-icon-button-contrast) 100% + ); +} + +:is( + [data-component="button-v2"][data-variant="submit"], + [data-component="icon-button-v2"]:is([data-variant="submit"], [data-variant="contrast"]) + ):is(:active, [data-state="pressed"]):not(:disabled) { + background-image: + linear-gradient( + 90deg, + var(--v2-overlay-simple-overlay-contrast-pressed) 0%, + var(--v2-overlay-simple-overlay-contrast-pressed) 100% + ), + linear-gradient(180deg, var(--submit-highlight) 0%, var(--v2-alpha-light-0) 100%), + linear-gradient( + 90deg, + var(--v2-background-bg-icon-button-contrast) 0%, + var(--v2-background-bg-icon-button-contrast) 100% + ); +} + +:is( + [data-component="button-v2"][data-variant="submit"], + [data-component="icon-button-v2"]:is([data-variant="submit"], [data-variant="contrast"]) +):is(:disabled, [data-state="disabled"]) { + opacity: 0.4; + cursor: not-allowed; +}