@opencode/ui 2.0.2 → 2.0.3

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.
@@ -14,6 +14,10 @@ export interface ScrollViewProps extends ComponentProps<"div"> {
14
14
  thumbContainer?: HTMLElement;
15
15
  /** Element whose hover reveals the thumb. Defaults to the ScrollView root when unset. */
16
16
  thumbHoverTarget?: HTMLElement;
17
+ /** Reconcile native scroll geometry before keyboard or scrollbar navigation reads it. */
18
+ onBeforeScroll?: () => void;
19
+ /** Offset/extent correction for a virtualized vertical scrollbar. */
20
+ verticalScrollAdjustment?: number;
17
21
  }
18
22
  export declare const scrollKey: (event: Pick<KeyboardEvent, "key" | "altKey" | "ctrlKey" | "metaKey" | "shiftKey">) => "end" | "home" | "page-down" | "page-up" | "up" | "down" | undefined;
19
23
  export declare function canScrollKey(element: HTMLElement, key: NonNullable<ReturnType<typeof scrollKey>>): boolean;
@@ -162,6 +162,14 @@ declare const icons: {
162
162
  viewBox: string;
163
163
  body: string;
164
164
  };
165
+ "graduation-cap": {
166
+ viewBox: string;
167
+ body: string;
168
+ };
169
+ "code-slash": {
170
+ viewBox: string;
171
+ body: string;
172
+ };
165
173
  trash: {
166
174
  viewBox: string;
167
175
  body: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencode/ui",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,6 +20,10 @@ export interface ScrollViewProps extends ComponentProps<"div"> {
20
20
  thumbContainer?: HTMLElement
21
21
  /** Element whose hover reveals the thumb. Defaults to the ScrollView root when unset. */
22
22
  thumbHoverTarget?: HTMLElement
23
+ /** Reconcile native scroll geometry before keyboard or scrollbar navigation reads it. */
24
+ onBeforeScroll?: () => void
25
+ /** Offset/extent correction for a virtualized vertical scrollbar. */
26
+ verticalScrollAdjustment?: number
23
27
  }
24
28
 
25
29
  export const scrollKey = (event: Pick<KeyboardEvent, "key" | "altKey" | "ctrlKey" | "metaKey" | "shiftKey">) => {
@@ -124,6 +128,8 @@ export function ScrollView(props: ScrollViewProps) {
124
128
  "thumbVisibility",
125
129
  "thumbContainer",
126
130
  "thumbHoverTarget",
131
+ "onBeforeScroll",
132
+ "verticalScrollAdjustment",
127
133
  "style",
128
134
  ],
129
135
  [
@@ -189,17 +195,19 @@ export function ScrollView(props: ScrollViewProps) {
189
195
  const minThumbSize = 32
190
196
 
191
197
  if (vertical()) {
198
+ const adjustment = local.verticalScrollAdjustment ?? 0
199
+ const scrollHeight = viewportRef.scrollHeight + adjustment
192
200
  const trackSize = Math.max(0, (thumbMount()?.clientHeight || viewportRef.clientHeight) - trackPadding * 2)
193
201
  const size = trackSize
194
- ? Math.min(trackSize, Math.max((viewportRef.clientHeight / viewportRef.scrollHeight) * trackSize, minThumbSize))
202
+ ? Math.min(trackSize, Math.max((viewportRef.clientHeight / scrollHeight) * trackSize, minThumbSize))
195
203
  : 0
196
- const maxScroll = viewportRef.scrollHeight - viewportRef.clientHeight
204
+ const maxScroll = scrollHeight - viewportRef.clientHeight
197
205
  const maxStart = trackSize - size
198
206
  setState("showVerticalThumb", maxScroll > 0)
199
207
  setState("verticalThumbSize", size)
200
208
  setState(
201
209
  "verticalThumbStart",
202
- trackPadding + (maxScroll > 0 ? (viewportRef.scrollTop / maxScroll) * maxStart : 0),
210
+ trackPadding + (maxScroll > 0 ? ((viewportRef.scrollTop + adjustment) / maxScroll) * maxStart : 0),
203
211
  )
204
212
  } else {
205
213
  setState("showVerticalThumb", false)
@@ -263,9 +271,17 @@ export function ScrollView(props: ScrollViewProps) {
263
271
  })
264
272
  })
265
273
 
274
+ const prepareScroll = () => {
275
+ if (local.onBeforeScroll) {
276
+ local.onBeforeScroll()
277
+ updateThumb()
278
+ }
279
+ }
280
+
266
281
  const onThumbPointerDown = (axis: "vertical" | "horizontal", e: PointerEvent) => {
267
282
  e.preventDefault()
268
283
  e.stopPropagation()
284
+ prepareScroll()
269
285
  setState("dragging", axis)
270
286
  const thumb = axis === "vertical" ? verticalThumbRef : horizontalThumbRef
271
287
  const grabOffset =
@@ -277,6 +293,7 @@ export function ScrollView(props: ScrollViewProps) {
277
293
  thumb.setPointerCapture(e.pointerId)
278
294
 
279
295
  const onPointerMove = (e: PointerEvent) => {
296
+ prepareScroll()
280
297
  const vertical = axis === "vertical"
281
298
  const rtl = !vertical && getComputedStyle(viewportRef).direction === "rtl"
282
299
  const offset = scrollOffsetFromThumbPointer({
@@ -355,10 +372,23 @@ export function ScrollView(props: ScrollViewProps) {
355
372
  return
356
373
  }
357
374
  const next = scrollKey(e)
358
- if (!next) return
359
- if (!isScrollKeyTarget(e.target, next)) return
360
- if (scrollKeyOwner(viewportRef, e.target, next) !== viewportRef) return
375
+ // Modified navigation (for example Ctrl+Home) stays native, but must read
376
+ // the same reconciled geometry as the keys handled by this component.
377
+ const intent =
378
+ next ??
379
+ scrollKey({
380
+ key: e.key,
381
+ shiftKey: e.key === " " && e.shiftKey,
382
+ altKey: false,
383
+ ctrlKey: false,
384
+ metaKey: false,
385
+ })
386
+ if (!intent) return
387
+ if (!isScrollKeyTarget(e.target, intent)) return
388
+ if (scrollKeyOwner(viewportRef, e.target, intent) !== viewportRef) return
361
389
 
390
+ prepareScroll()
391
+ if (!next) return
362
392
  const scrollAmount = viewportRef.clientHeight * 0.8
363
393
  const lineAmount = 40
364
394
 
@@ -164,6 +164,14 @@ const icons = {
164
164
  viewBox: "0 0 16 16",
165
165
  body: `<path d="M14.5 9.8333V13.5H1.5V2.5H7.1667M9.5 2.5V7.5H14.5V2.5H9.5Z" stroke="currentColor" stroke-miterlimit="10" stroke-linecap="square"/>`,
166
166
  },
167
+ "graduation-cap": {
168
+ viewBox: "0 0 16 16",
169
+ body: `<path d="M12.3327 7.50065V11.0007L7.99935 13.6673L3.66602 11.0007V7.50065M14.3327 9.83398V6.16732M7.99935 2.33398L1.66602 6.00065L7.99935 10.0007L14.3327 6.00065L7.99935 2.33398Z" stroke="currentColor" stroke-linecap="square"/>`,
170
+ },
171
+ "code-slash": {
172
+ viewBox: "0 0 16 16",
173
+ body: `<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0812 2.10803L6.55974 14.0809L5.92016 13.8928L9.44161 1.91992L10.0812 2.10803ZM4.13793 4.97275L1.44666 8.00045L4.13793 11.0281L3.63966 11.471L0.554688 8.00045L3.63966 4.52984L4.13793 4.97275ZM12.3617 4.52984L15.4467 8.00045L12.3617 11.471L11.8634 11.0281L14.5547 8.00045L11.8634 4.97275L12.3617 4.52984Z" fill="currentColor"/>`,
174
+ },
167
175
  trash: {
168
176
  viewBox: "0 0 20 20",
169
177
  body: `<path d="M4.58342 17.9134L4.58369 17.4134L4.22787 17.5384L4.22766 18.0384H4.58342V17.9134ZM15.4167 17.9134V18.0384H15.7725L15.7723 17.5384L15.4167 17.9134ZM2.08342 3.95508V3.45508H1.58342V3.95508H2.08342V4.45508V3.95508ZM17.9167 4.45508V4.95508H18.4167V4.45508H17.9167V3.95508V4.45508ZM4.16677 4.58008L3.66701 4.5996L4.22816 17.5379L4.72792 17.4934L5.22767 17.4489L4.66652 4.54055L4.16677 4.58008ZM4.58342 18.0384V17.9134H15.4167V18.0384V18.5384H4.58342V18.0384ZM15.4167 17.9134L15.8332 17.5379L16.2498 4.5996L15.7501 4.58008L15.2503 4.56055L14.8337 17.4989L15.4167 17.9134ZM15.8334 4.58008V4.08008H4.16677V4.58008V5.08008H15.8334V4.58008ZM2.08342 4.45508V4.95508H4.16677V4.58008V4.08008H2.08342V4.45508ZM15.8334 4.58008V5.08008H17.9167V4.45508V3.95508H15.8334V4.58008ZM6.83951 4.35149L7.432 4.55047C7.79251 3.47701 8.80699 2.70508 10.0001 2.70508V2.20508V1.70508C8.25392 1.70508 6.77335 2.83539 6.24702 4.15251L6.83951 4.35149ZM10.0001 2.20508V2.70508C11.1932 2.70508 12.2077 3.47701 12.5682 4.55047L13.1607 4.35149L13.7532 4.15251C13.2269 2.83539 11.7463 1.70508 10.0001 1.70508V2.20508Z" fill="currentColor"/>`,