@opencode-ai/ui 1.18.3 → 1.18.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,8 @@ export interface ResizeHandleProps extends Omit<JSX.HTMLAttributes<HTMLDivElemen
7
7
  max: number;
8
8
  onResize: (size: number) => void;
9
9
  onCollapse?: () => void;
10
+ /** Called while dragging when size crosses `collapseThreshold`. */
11
+ onCollapseChange?: (collapsed: boolean) => void;
10
12
  collapseThreshold?: number;
11
13
  }
12
14
  export declare function ResizeHandle(props: ResizeHandleProps): JSX.Element;
@@ -4,7 +4,7 @@ import { type IconProps } from "./icon";
4
4
  import "./button-v2.css";
5
5
  export interface ButtonV2Props extends ComponentProps<typeof Kobalte>, Pick<ComponentProps<"button">, "class" | "classList" | "children"> {
6
6
  size?: "small" | "normal" | "large";
7
- variant?: "neutral" | "danger" | "outline" | "contrast" | "ghost" | "ghost-muted" | "loading";
7
+ variant?: "neutral" | "danger" | "warning" | "outline" | "contrast" | "ghost" | "ghost-muted" | "loading";
8
8
  icon?: IconProps["name"];
9
9
  }
10
10
  export declare function ButtonV2(props: ButtonV2Props): import("solid-js").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencode-ai/ui",
3
- "version": "1.18.3",
3
+ "version": "1.18.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -7,6 +7,20 @@
7
7
  overflow: clip;
8
8
  }
9
9
 
10
+ [data-dock-border-underlay] {
11
+ /* Later shadows paint underneath, so this solid ring masks overlapping surfaces without changing border geometry. */
12
+ box-shadow:
13
+ var(--dock-shell-visual-shadow, var(--shadow-xs-border)),
14
+ 0 0 0 var(--dock-shell-border-underlay-width, 1px)
15
+ var(--dock-shell-border-underlay-background, var(--surface-raised-stronger-non-alpha));
16
+ }
17
+
18
+ [data-dock-border-underlay="v2"] {
19
+ --dock-shell-visual-shadow: var(--v2-elevation-raised);
20
+ --dock-shell-border-underlay-width: 0.5px;
21
+ --dock-shell-border-underlay-background: var(--v2-background-bg-base);
22
+ }
23
+
10
24
  [data-dock-surface="tray"] {
11
25
  background-color: var(--background-base);
12
26
  border: 1px solid var(--border-weak-base);
@@ -8,6 +8,8 @@ export interface ResizeHandleProps extends Omit<JSX.HTMLAttributes<HTMLDivElemen
8
8
  max: number
9
9
  onResize: (size: number) => void
10
10
  onCollapse?: () => void
11
+ /** Called while dragging when size crosses `collapseThreshold`. */
12
+ onCollapseChange?: (collapsed: boolean) => void
11
13
  collapseThreshold?: number
12
14
  }
13
15
 
@@ -20,17 +22,26 @@ export function ResizeHandle(props: ResizeHandleProps) {
20
22
  "max",
21
23
  "onResize",
22
24
  "onCollapse",
25
+ "onCollapseChange",
23
26
  "collapseThreshold",
24
27
  "class",
25
28
  "classList",
26
29
  ])
27
30
 
28
31
  const handleMouseDown = (e: MouseEvent) => {
32
+ if (e.detail > 1) return
29
33
  e.preventDefault()
30
34
  const edge = local.edge ?? (local.direction === "vertical" ? "start" : "end")
31
35
  const start = local.direction === "horizontal" ? e.clientX : e.clientY
32
36
  const startSize = local.size
37
+ const min = local.min
38
+ const max = local.max
39
+ const threshold = local.collapseThreshold ?? 0
40
+ const onResize = local.onResize
41
+ const onCollapse = local.onCollapse
42
+ const onCollapseChange = local.onCollapseChange
33
43
  let current = startSize
44
+ let collapsed = false
34
45
 
35
46
  document.body.style.userSelect = "none"
36
47
  document.body.style.overflow = "hidden"
@@ -46,8 +57,12 @@ export function ResizeHandle(props: ResizeHandleProps) {
46
57
  ? start - pos
47
58
  : pos - start
48
59
  current = startSize + delta
49
- const clamped = Math.min(local.max, Math.max(local.min, current))
50
- local.onResize(clamped)
60
+ const nextCollapsed = threshold > 0 && current < threshold
61
+ if (nextCollapsed !== collapsed) {
62
+ collapsed = nextCollapsed
63
+ onCollapseChange?.(collapsed)
64
+ }
65
+ onResize(Math.min(max, Math.max(min, current)))
51
66
  }
52
67
 
53
68
  const onMouseUp = () => {
@@ -56,10 +71,11 @@ export function ResizeHandle(props: ResizeHandleProps) {
56
71
  document.removeEventListener("mousemove", onMouseMove)
57
72
  document.removeEventListener("mouseup", onMouseUp)
58
73
 
59
- const threshold = local.collapseThreshold ?? 0
60
- if (local.onCollapse && threshold > 0 && current < threshold) {
61
- local.onCollapse()
74
+ if (collapsed) {
75
+ onCollapse?.()
76
+ return
62
77
  }
78
+ onCollapseChange?.(false)
63
79
  }
64
80
 
65
81
  document.addEventListener("mousemove", onMouseMove)
@@ -723,7 +723,11 @@ body[data-new-layout] #review-panel [data-component="tabs"][data-variant="normal
723
723
  }
724
724
 
725
725
  &:has([data-slot="tabs-trigger-close-button"]) {
726
- padding-right: 10px;
726
+ padding-right: 8px;
727
+
728
+ [data-slot="tabs-trigger-close-button"] {
729
+ margin-left: 2px;
730
+ }
727
731
  }
728
732
 
729
733
  &:has([data-selected]) {
@@ -789,6 +793,62 @@ body[data-new-layout]
789
793
  [data-slot="tabs-list"]
790
794
  > .sticky {
791
795
  padding-right: 0;
796
+
797
+ [data-component="icon-button-v2"] {
798
+ position: relative;
799
+
800
+ &::after {
801
+ content: "";
802
+ position: absolute;
803
+ left: 100%;
804
+ top: 50%;
805
+ transform: translateY(-50%);
806
+ width: 20px;
807
+ height: 28px;
808
+ background-color: var(--v2-background-bg-base);
809
+ pointer-events: none;
810
+ }
811
+ }
812
+ }
813
+
814
+ body[data-new-layout]
815
+ #review-panel
816
+ [data-component="tabs"]
817
+ .session-review-v2-tabs-bar
818
+ [data-slot="tabs-list"]
819
+ > .session-review-v2-sidebar-toggle-slot.sticky {
820
+ padding-right: 0;
821
+
822
+ &::before {
823
+ content: "";
824
+ position: absolute;
825
+ top: 50%;
826
+ bottom: auto;
827
+ left: auto;
828
+ right: 100%;
829
+ transform: translateY(-50%);
830
+ width: 12px;
831
+ height: 28px;
832
+ background-color: var(--v2-background-bg-base);
833
+ background-image: none;
834
+ pointer-events: none;
835
+ }
836
+
837
+ &::after {
838
+ content: "";
839
+ position: absolute;
840
+ top: 50%;
841
+ left: 100%;
842
+ transform: translateY(-50%);
843
+ width: 8px;
844
+ height: 28px;
845
+ pointer-events: none;
846
+ background: linear-gradient(90deg, var(--v2-background-bg-base), transparent);
847
+ }
848
+
849
+ [data-component="icon-button-v2"]::after {
850
+ display: none;
851
+ }
792
852
  }
793
853
 
794
854
  body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app-slot {
@@ -113,6 +113,29 @@
113
113
  cursor: not-allowed;
114
114
  }
115
115
 
116
+ [data-component="button-v2"][data-variant="warning"] {
117
+ background-color: var(--v2-background-bg-button-neutral);
118
+ color: var(--v2-state-fg-warning);
119
+ box-shadow: var(--v2-elevation-button-neutral);
120
+ }
121
+
122
+ [data-component="button-v2"][data-variant="warning"]:is(:hover, [data-state="hover"]):not(:disabled) {
123
+ background-image:
124
+ linear-gradient(90deg, var(--v2-overlay-simple-overlay-hover) 0%, var(--v2-overlay-simple-overlay-hover) 100%),
125
+ linear-gradient(90deg, var(--v2-background-bg-button-neutral) 0%, var(--v2-background-bg-button-neutral) 100%);
126
+ }
127
+
128
+ [data-component="button-v2"][data-variant="warning"]:is(:active, [data-state="pressed"]):not(:disabled) {
129
+ background-image:
130
+ linear-gradient(90deg, var(--v2-overlay-simple-overlay-pressed) 0%, var(--v2-overlay-simple-overlay-pressed) 100%),
131
+ linear-gradient(90deg, var(--v2-background-bg-button-neutral) 0%, var(--v2-background-bg-button-neutral) 100%);
132
+ }
133
+
134
+ [data-component="button-v2"][data-variant="warning"]:is(:disabled, [data-state="disabled"]) {
135
+ opacity: 0.5;
136
+ cursor: not-allowed;
137
+ }
138
+
116
139
  /* Outline */
117
140
  [data-component="button-v2"][data-variant="outline"] {
118
141
  background-color: transparent;
@@ -7,7 +7,7 @@ export interface ButtonV2Props
7
7
  extends ComponentProps<typeof Kobalte>,
8
8
  Pick<ComponentProps<"button">, "class" | "classList" | "children"> {
9
9
  size?: "small" | "normal" | "large"
10
- variant?: "neutral" | "danger" | "outline" | "contrast" | "ghost" | "ghost-muted" | "loading"
10
+ variant?: "neutral" | "danger" | "warning" | "outline" | "contrast" | "ghost" | "ghost-muted" | "loading"
11
11
  icon?: IconProps["name"]
12
12
  }
13
13
 
@@ -101,7 +101,7 @@ export function Dialog(props: DialogProps) {
101
101
  const autofocusEl = target?.querySelector("[autofocus]") as HTMLElement | null
102
102
  if (autofocusEl) {
103
103
  e.preventDefault()
104
- autofocusEl.focus()
104
+ autofocusEl.focus({ preventScroll: true })
105
105
  }
106
106
  }}
107
107
  >
@@ -1,8 +1,8 @@
1
1
  [data-component="divider-v2"] {
2
2
  box-sizing: border-box;
3
3
  width: 100%;
4
- height: 0.5px;
5
- margin-block: 0.25px;
4
+ height: 1px;
5
+ transform: scaleY(0.5);
6
6
  border: none;
7
7
  background: var(--v2-border-border-strong);
8
8
  flex: none;
@@ -67,9 +67,14 @@
67
67
  gap: 4px;
68
68
  background: var(--v2-background-bg-layer-01);
69
69
  border-radius: 4px 0 0 4px;
70
+ cursor: default;
70
71
  transition: background 85ms ease-out;
71
72
  }
72
73
 
74
+ [data-component="inline-input-v2"]:where([data-disabled]) [data-slot="inline-input-v2-prefix"] {
75
+ cursor: not-allowed;
76
+ }
77
+
73
78
  [data-component="inline-input-v2"][data-label-width] [data-slot="inline-input-v2-prefix"] {
74
79
  width: var(--inline-input-v2-label-width);
75
80
  min-width: var(--inline-input-v2-label-width);
@@ -89,6 +94,7 @@
89
94
  letter-spacing: -0.04px;
90
95
  color: var(--v2-text-text-muted);
91
96
  font-variation-settings: "slnt" 0;
97
+ user-select: none;
92
98
  }
93
99
 
94
100
  [data-component="inline-input-v2"][data-numeric] [data-slot="inline-input-v2-prefix-text"] {
@@ -149,6 +155,7 @@
149
155
 
150
156
  [data-component="inline-input-v2"] [data-slot="inline-input-v2-input"]::placeholder {
151
157
  color: var(--v2-text-text-faint);
158
+ user-select: none;
152
159
  }
153
160
 
154
161
  [data-component="inline-input-v2"][data-numeric] [data-slot="inline-input-v2-input"] {
@@ -37,6 +37,8 @@ export function InlineInputV2(props: InlineInputV2Props) {
37
37
  "style",
38
38
  ])
39
39
 
40
+ let input: HTMLInputElement | undefined
41
+
40
42
  return (
41
43
  <div
42
44
  data-component="inline-input-v2"
@@ -59,7 +61,15 @@ export function InlineInputV2(props: InlineInputV2Props) {
59
61
  : {}),
60
62
  }}
61
63
  >
62
- <div data-slot="inline-input-v2-prefix">
64
+ <div
65
+ data-slot="inline-input-v2-prefix"
66
+ onMouseDown={(event) => {
67
+ if (local.disabled || event.button !== 0) return
68
+ // Keep focus on the input without using a native <label>, so external labels still work.
69
+ event.preventDefault()
70
+ input?.focus()
71
+ }}
72
+ >
63
73
  <span data-slot="inline-input-v2-prefix-text">{local.prefix}</span>
64
74
  </div>
65
75
  <div data-slot="inline-input-v2-divider" aria-hidden="true" />
@@ -67,6 +77,11 @@ export function InlineInputV2(props: InlineInputV2Props) {
67
77
  <div data-slot="inline-input-v2-value">
68
78
  <input
69
79
  {...inputProps}
80
+ ref={(el) => {
81
+ input = el
82
+ const ref = inputProps.ref
83
+ if (typeof ref === "function") ref(el)
84
+ }}
70
85
  type={inputProps.type ?? "text"}
71
86
  disabled={local.disabled}
72
87
  aria-invalid={local.invalid ? true : undefined}
@@ -8,6 +8,6 @@
8
8
  }
9
9
 
10
10
  [data-component="progress-circle-v2"] [data-slot="progress-circle-v2-progress"] {
11
- stroke: var(--v2-icon-icon-muted);
11
+ stroke: var(--v2-icon-icon-base);
12
12
  transition: stroke-dashoffset 0.35s cubic-bezier(0.65, 0, 0.35, 1);
13
13
  }
@@ -98,6 +98,7 @@
98
98
 
99
99
  [data-component="text-input-v2"] [data-slot="text-input-v2-input"]::placeholder {
100
100
  color: var(--v2-text-text-faint);
101
+ user-select: none;
101
102
  }
102
103
 
103
104
  [data-component="text-input-v2"] [data-slot="text-input-v2-input"][type="search"]::-webkit-search-cancel-button {