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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ exclude = [
"OpenBitFun-Installer/src-tauri",
"third_party/eioc",
"third_party/tao",
"third_party/wry",
]

resolver = "2"
Expand Down Expand Up @@ -308,6 +309,9 @@ eioc = { path = "third_party/eioc" }
# Restore macOS traffic-light geometry after the queued native style reset.
# See third_party/tao/OPENBITFUN-PATCH.md for the upstream source and removal condition.
tao = { path = "third_party/tao" }
# Keep macOS HTML drags in WebKit while preserving native file-drop paths.
# See third_party/wry/OPENBITFUN-PATCH.md for source and removal conditions.
wry = { path = "third_party/wry" }
# Tauri 2.11 loses keyboard focus for child webviews after Windows reactivates
# an `unstable` multi-webview window. Use the upstream fix until released;
# this revision also adopts tao 0.36, which includes the Windows input deadlock fix.
Expand Down
9 changes: 9 additions & 0 deletions OpenBitFun-Installer/src/i18n/generatedLocaleContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ export const SHARED_TERMS_BY_APP_LANGUAGE = {
"done": "Done",
"failed": "Failed",
"cancelled": "Cancelled"
},
"sessions": {
"new": "New Session"
}
},
"zh-CN": {
Expand Down Expand Up @@ -155,6 +158,9 @@ export const SHARED_TERMS_BY_APP_LANGUAGE = {
"done": "已完成",
"failed": "失败",
"cancelled": "已取消"
},
"sessions": {
"new": "新会话"
}
},
"zh-TW": {
Expand Down Expand Up @@ -208,6 +214,9 @@ export const SHARED_TERMS_BY_APP_LANGUAGE = {
"done": "已完成",
"failed": "失敗",
"cancelled": "已取消"
},
"sessions": {
"new": "新會話"
}
}
} as const satisfies Record<AppLanguage, SharedI18nTerms>;
19 changes: 17 additions & 2 deletions design-system/packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ dismissal. Floating menus and dialogs within either pane continue to use the
document overlay coordinator. Owners control visibility with `mode` and persist
split and overlay sizes independently when those preferences differ.

For movable tab hosts, pass a flat keyed deck through `surfaces` and render each
view inside `SplitViewSurface pane="primary" | "secondary"`. Changing `pane` keeps
the same React/DOM host. Inactive surfaces stay mounted but are inert and hidden
from accessibility; F6 navigates visible surfaces. Do not reparent portals between
pane containers to simulate a move.

Use the `header` render function for chrome aligned with the divider. Its
`columnClassName` supplies layout-owned columns tied to live resizing and clamping,
not a second stored width. Apply it to a three-column header (primary, separator,
secondary); keep padding inside those columns. Header layout works without CSS
subgrid. The primitive remains independent of product tabs and stores.

Place `SplitViewPrimaryDock` inside the primary content's positioned container
for an absolute input or toolbar that must avoid the secondary overlay. Its
width and placement follow the displayed pane size, including pointer drags and
Expand Down Expand Up @@ -401,7 +413,7 @@ focus ring. Use it for toolbar, dialog, and row utilities. `fill` and `primary`
keep an opaque backing surface for persistent emphasis. Disabled quiet actions
remain transparent and do not show hover or pressed feedback.

`IconButton variant="glass"` is a translucent, blurred action over existing
`Button variant="glass"` and `IconButton variant="glass"` share a translucent, blurred action over existing
content. It uses a low-opacity tint of the shared raised surface, a softened
subtle border and subtle blur, without an outer shadow. Hover and pressed states
retain translucency. Unsupported blur, increased contrast and reduced transparency
Expand Down Expand Up @@ -488,7 +500,10 @@ and ref interface, using the same Lucide MessageCircle glyph.
## Advanced selection and menus

Use `Select` for simple options; its hidden native control preserves form
participation. `Combobox` adds searchable single
participation. `width="content"` fits the collapsed control to the selected label;
`popupWidth="content"` independently fits the expanded surface to its options,
within the viewport. The defaults remain a full-width control and a popup that
matches the trigger. `Combobox` adds searchable single
selection, grouped options, explicit custom-value creation and async loading
states. `MultiSelect` owns multiple selection, removable tags and select-all.
Controlled values are authoritative; option discovery remains host-owned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { ComponentMeta } from "../../registry.types";

export const buttonMeta = {
category: "action",
description: "An action with transparent outline and text variants, neutral fill, secondary, and primary presentation.",
description: "An action with transparent outline and text variants, floating glass, neutral fill, secondary, and primary presentation.",
maturity: "stable",
name: "Button",
props: [
{ defaultValue: "outline", name: "variant", type: "outline | fill | secondary | primary | text" },
{ defaultValue: "outline", name: "variant", type: "outline | glass | fill | secondary | primary | text" },
{ defaultValue: "md", name: "size", type: "xs | sm | md | lg" },
{ defaultValue: "neutral", name: "tone", type: "neutral | danger" },
{ defaultValue: "false", name: "loading", type: "boolean" },
Expand All @@ -16,6 +16,9 @@ export const buttonMeta = {
],
states: ["default", "hover", "active", "disabled"],
tokens: [
"color.surface.raised",
"color.border.subtle",
"effect.blur.subtle",
"opacity.iconArtwork",
"component.button.content",
"component.button.outlineBorder",
Expand Down
11 changes: 11 additions & 0 deletions design-system/packages/ui/src/components/Button/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@
--_button-border-active: transparent;
}

/* GlassAction owns the material, including contrast/transparency fallbacks. */
.button[data-openbitfun-variant="glass"] {
--_button-border: transparent;
--_button-border-hover: transparent;
--_button-border-active: transparent;
}

.button[data-openbitfun-variant="glass"]::before {
content: none;
}

.button[data-openbitfun-variant="primary"] {
--_button-background: var(--openbitfun-component-button-primary-background);
--_button-background-hover: var(--openbitfun-component-button-primary-background-hover);
Expand Down
5 changes: 3 additions & 2 deletions design-system/packages/ui/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from "react";
import { classNames } from "../../internal/classNames";
import { OverflowText } from "../../primitives/OverflowText";
import glassStyles from "../../internal/GlassAction.module.css";
import styles from "./Button.module.css";

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
Expand All @@ -16,7 +17,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
size?: "xs" | "sm" | "md" | "lg";
tone?: "danger" | "neutral";
trailingIcon?: ReactNode;
variant?: "fill" | "outline" | "primary" | "secondary" | "text";
variant?: "fill" | "glass" | "outline" | "primary" | "secondary" | "text";
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button({
Expand All @@ -37,7 +38,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
<button data-overflow-trigger
{...props}
aria-busy={loading || undefined}
className={classNames(styles.button, className)}
className={classNames(styles.button, variant === "glass" && glassStyles.surface, className)}
data-openbitfun-component="button"
data-openbitfun-part="root"
data-openbitfun-tone={tone}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const selectMeta = {
{ name: "leading", type: "ReactNode" },
{ defaultValue: "md", name: "size", type: "sm | md | lg" },
{ defaultValue: "bottom", name: "placement", type: "top | bottom" },
{ defaultValue: "full", name: "width", type: "full | content" },
{ defaultValue: "trigger", name: "popupWidth", type: "trigger | content" },
{ name: "open / defaultOpen", type: "boolean" },
{ name: "onOpenChange", type: "(open: boolean) => void" },
{ defaultValue: "false", name: "invalid", type: "boolean" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
--_select-height: var(--openbitfun-control-height-sm);
}

.root[data-width="content"] {
inline-size: max-content;
max-inline-size: 100%;
}

.root[data-size="lg"],
.popover[data-size="lg"] {
--_select-height: var(--openbitfun-control-height-lg);
Expand Down Expand Up @@ -183,6 +188,10 @@
border-color: var(--openbitfun-color-status-danger-border);
}

.popover[data-width="content"] {
inline-size: max-content;
}

.popover[data-placement="top"] {
flex-direction: column-reverse;
transform-origin: bottom center;
Expand Down
10 changes: 9 additions & 1 deletion design-system/packages/ui/src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ export interface SelectProps
options: readonly SelectOption[];
placement?: SelectPlacement;
placeholder?: string;
/** Size the expanded surface to its trigger or the full option labels. */
popupWidth?: "trigger" | "content";
size?: SelectSize;
value?: SelectValue;
/** Fill the available field width or fit the currently selected label. */
width?: "full" | "content";
}

function assignRef<T>(ref: ForwardedRef<T>, value: T | null) {
Expand Down Expand Up @@ -140,12 +144,14 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select
options,
placement = "bottom",
placeholder,
popupWidth = "trigger",
required = false,
size = "md",
style,
tabIndex,
title,
value,
width = "full",
...nativeProps
}, forwardedRef) {
const designSystem = useDesignSystem();
Expand Down Expand Up @@ -299,7 +305,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select
const layout = useAnchoredLayer({
anchorRef: triggerRef,
layerRef: popoverRef,
matchWidth: true,
matchWidth: popupWidth === "trigger",
open: resolvedOpen,
overlapAnchor: true,
placement,
Expand Down Expand Up @@ -368,6 +374,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select
data-invalid={resolvedInvalid ? "true" : "false"}
data-placement={layout?.placement ?? placement}
data-size={size}
data-width={popupWidth}
ref={popoverRef}
style={layout?.style ?? { position: "fixed", visibility: "hidden" }}
>
Expand Down Expand Up @@ -431,6 +438,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select
data-open={resolvedOpen ? "true" : "false"}
data-placement={layout?.placement ?? placement}
data-size={size}
data-width={width}
ref={rootRef}
>
<select
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const splitViewMeta = {
props: [
{ name: 'primary', type: 'ReactNode' },
{ name: 'secondary', type: 'ReactNode' },
{ name: 'header', type: 'ReactNode | (geometry: SplitViewHeaderGeometry) => ReactNode' },
{ name: 'surfaces', type: 'ReactNode' },
{ name: 'mode', type: 'split | primary | secondary', defaultValue: 'split' },
{ name: 'layout', type: 'split | overlay', defaultValue: 'split' },
{ name: 'secondarySide', type: 'left | right', defaultValue: 'right' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
pointer-events: none;
}

.root[data-layout="overlay"][data-mode="split"] > .primary .primaryDock {
.root[data-layout="overlay"][data-mode="split"] > .primary .primaryDock,
.root[data-layout="overlay"][data-mode="split"] .surface.primary .primaryDock {
inline-size: max(0px, calc(100% - var(--_split-view-right-size) - var(--openbitfun-border-width-default)));
}

.root[data-layout="overlay"][data-mode="split"][data-secondary-side="left"] > .primary .primaryDock {
.root[data-layout="overlay"][data-mode="split"][data-secondary-side="left"] > .primary .primaryDock,
.root[data-layout="overlay"][data-mode="split"][data-secondary-side="left"] .surface.primary .primaryDock {
inset-inline-start: calc(var(--_split-view-right-size) + var(--openbitfun-border-width-default));
}

Expand Down Expand Up @@ -72,6 +74,26 @@
inset-inline-end: auto;
}

.root .header > .headerColumns {
grid-template-columns: minmax(0, 1fr) var(--openbitfun-border-width-default) minmax(0, var(--_split-view-right-size));
}
.root[data-layout="overlay"][data-secondary-side="left"][data-mode="split"] .headerColumns {
grid-template-columns: minmax(0, var(--_split-view-right-size)) var(--openbitfun-border-width-default) minmax(0, 1fr);
}
.header { grid-area: header; min-inline-size: 0; z-index: var(--openbitfun-layer-sticky); }
.root[data-has-header] {
grid-template-rows: auto minmax(0, 1fr);
grid-template-areas: "header header header" "primary divider secondary";
}
.root[data-has-header][data-secondary-side="left"] { grid-template-areas: "header header header" "secondary divider primary"; }
.root[data-has-header][data-mode="primary"],
.root[data-has-header][data-layout="overlay"]:not([data-mode="secondary"]) { grid-template-areas: "header" "primary"; }
.root[data-has-header][data-mode="secondary"] { grid-template-areas: "header" "secondary"; }
.root[data-has-header][data-layout="overlay"][data-mode="split"] .secondary,
.root[data-has-header][data-layout="overlay"][data-mode="split"] .divider {
grid-row: 2;
}

.pane {
display: flex;
flex-direction: column;
Expand All @@ -80,6 +102,9 @@
overflow: hidden;
}

.surface { position: relative; }
.surface[data-split-active="false"] { visibility: hidden; opacity: 0; pointer-events: none; }
.root[data-layout="overlay"][data-mode="split"] .secondary { z-index: 2; }
.primary { grid-area: primary; }
.secondary { grid-area: secondary; }
.pane[hidden], .divider[hidden] { display: none; }
Expand Down
Loading
Loading