nextworks 0.2.0-alpha.15 → 0.2.0-alpha.16

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.
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
 
3
3
  import React from "react";
4
- import { motion } from "motion/react";
5
4
  import { cn } from "@/lib/utils";
6
5
  import { DemoWindow } from "./DemoWindow";
7
6
  import { KnowledgePanel } from "./KnowledgePanel";
@@ -295,6 +294,19 @@ function getWindowShellClass(key: ProductDemoWindowKey) {
295
294
  }
296
295
  }
297
296
 
297
+ function getMobileWindowClass(key: ProductDemoWindowKey) {
298
+ switch (key) {
299
+ case "taskList":
300
+ return "h-[15rem] sm:h-[16rem] lg:h-full";
301
+ case "workflowStudio":
302
+ return "h-[18rem] sm:h-[19rem] lg:h-full";
303
+ case "runConsole":
304
+ return "h-[18rem] sm:h-[19rem] lg:h-full";
305
+ default:
306
+ return "lg:h-full";
307
+ }
308
+ }
309
+
298
310
  export function DemoStage({
299
311
  scenarios = [],
300
312
  initialScenarioIndex,
@@ -391,8 +403,8 @@ export function DemoStage({
391
403
  )}
392
404
  aria-label={ariaLabel}
393
405
  >
394
- <div className="relative z-10 flex h-[36rem] min-h-[36rem] flex-col gap-0 lg:h-full lg:min-h-0">
395
- <div className="grid gap-4 lg:hidden">
406
+ <div className="relative z-10 flex min-h-[36rem] flex-col gap-4 lg:h-full lg:min-h-0 lg:gap-0">
407
+ <div className="grid auto-rows-max gap-4 lg:hidden">
396
408
  {windows.map((windowData) => {
397
409
  if (getWindowShellClass(windowData.key) === "hidden") {
398
410
  return null;
@@ -412,6 +424,8 @@ export function DemoStage({
412
424
  showControls={false}
413
425
  showResizeHandle={false}
414
426
  showHeader={false}
427
+ className={getMobileWindowClass(windowData.key)}
428
+ bodyClassName="px-0 py-0 sm:px-0 sm:py-0"
415
429
  >
416
430
  {windowData.content}
417
431
  </DemoWindow>
@@ -472,20 +486,7 @@ export function DemoStage({
472
486
  windowData.key === "workflowStudio");
473
487
 
474
488
  return (
475
- <motion.div
476
- key={windowData.key}
477
- initial={enableMotion ? { opacity: 0 } : false}
478
- animate={{ opacity: 1 }}
479
- transition={
480
- enableMotion
481
- ? {
482
- type: "tween",
483
- duration: 0.24,
484
- }
485
- : { duration: 0 }
486
- }
487
- className={cn("min-h-0", shellClass)}
488
- >
489
+ <div key={windowData.key} className={cn("min-h-0", shellClass)}>
489
490
  <DemoWindow
490
491
  window={windowData.meta}
491
492
  active={activeWindow}
@@ -507,7 +508,7 @@ export function DemoStage({
507
508
  >
508
509
  {windowData.content}
509
510
  </DemoWindow>
510
- </motion.div>
511
+ </div>
511
512
  );
512
513
  })}
513
514
  </div>
@@ -7,6 +7,15 @@ export interface RunConsolePanelProps {
7
7
  }
8
8
 
9
9
  export function RunConsolePanel({ state }: RunConsolePanelProps) {
10
+ const scrollViewportRef = React.useRef<HTMLDivElement | null>(null);
11
+ const scrollbarTrackRef = React.useRef<HTMLDivElement | null>(null);
12
+ const dragStateRef = React.useRef<{
13
+ pointerId: number;
14
+ startY: number;
15
+ startScrollTop: number;
16
+ scrollRatio: number;
17
+ pointerOffsetY: number;
18
+ } | null>(null);
10
19
  const playbackMs = state.playbackMs ?? 1800;
11
20
  const playbackStepEntryIndices = state.playbackStepEntryIndices ?? [];
12
21
  const playbackStepVisibleLineCounts =
@@ -74,6 +83,11 @@ export function RunConsolePanel({ state }: RunConsolePanelProps) {
74
83
  const [visibleLineCount, setVisibleLineCount] = React.useState(
75
84
  Math.max(1, Math.min(2, activeCode.length || 1)),
76
85
  );
86
+ const [scrollMetrics, setScrollMetrics] = React.useState({
87
+ scrollTop: 0,
88
+ scrollHeight: 1,
89
+ clientHeight: 1,
90
+ });
77
91
 
78
92
  React.useEffect(() => {
79
93
  if (typeof state.playbackStep === "number") {
@@ -124,13 +138,179 @@ export function RunConsolePanel({ state }: RunConsolePanelProps) {
124
138
  state.playbackStep,
125
139
  ]);
126
140
 
141
+ React.useEffect(() => {
142
+ const viewport = scrollViewportRef.current;
143
+
144
+ if (!viewport) {
145
+ return;
146
+ }
147
+
148
+ const updateScrollMetrics = () => {
149
+ setScrollMetrics({
150
+ scrollTop: viewport.scrollTop,
151
+ scrollHeight: viewport.scrollHeight,
152
+ clientHeight: viewport.clientHeight,
153
+ });
154
+ };
155
+
156
+ updateScrollMetrics();
157
+
158
+ viewport.addEventListener("scroll", updateScrollMetrics, {
159
+ passive: true,
160
+ });
161
+
162
+ const resizeObserver = new ResizeObserver(() => {
163
+ updateScrollMetrics();
164
+ });
165
+
166
+ resizeObserver.observe(viewport);
167
+
168
+ if (viewport.firstElementChild instanceof HTMLElement) {
169
+ resizeObserver.observe(viewport.firstElementChild);
170
+ }
171
+
172
+ window.addEventListener("resize", updateScrollMetrics);
173
+
174
+ return () => {
175
+ viewport.removeEventListener("scroll", updateScrollMetrics);
176
+ resizeObserver.disconnect();
177
+ window.removeEventListener("resize", updateScrollMetrics);
178
+ };
179
+ }, [visibleLineCount, activeEntry?.id, activeCode.length]);
180
+
127
181
  const visibleCode = activeCode.slice(0, visibleLineCount);
182
+ const hasOverflow =
183
+ scrollMetrics.scrollHeight > scrollMetrics.clientHeight + 1;
184
+ const thumbHeight = hasOverflow
185
+ ? Math.max(
186
+ 36,
187
+ (scrollMetrics.clientHeight / scrollMetrics.scrollHeight) *
188
+ scrollMetrics.clientHeight,
189
+ )
190
+ : 0;
191
+ const maxThumbOffset = Math.max(scrollMetrics.clientHeight - thumbHeight, 0);
192
+ const maxScrollTop = Math.max(
193
+ scrollMetrics.scrollHeight - scrollMetrics.clientHeight,
194
+ 1,
195
+ );
196
+ const thumbOffset = hasOverflow
197
+ ? (scrollMetrics.scrollTop / maxScrollTop) * maxThumbOffset
198
+ : 0;
199
+
200
+ const updateScrollTopFromPointer = React.useCallback(
201
+ (clientY: number) => {
202
+ const viewport = scrollViewportRef.current;
203
+ const track = scrollbarTrackRef.current;
204
+
205
+ if (!viewport || !track || !hasOverflow) {
206
+ return;
207
+ }
208
+
209
+ const trackRect = track.getBoundingClientRect();
210
+ const nextThumbTop = Math.min(
211
+ Math.max(
212
+ clientY - trackRect.top - dragStateRef.current!.pointerOffsetY,
213
+ 0,
214
+ ),
215
+ maxThumbOffset,
216
+ );
217
+ const nextScrollTop =
218
+ maxThumbOffset > 0 ? (nextThumbTop / maxThumbOffset) * maxScrollTop : 0;
219
+
220
+ viewport.scrollTop = nextScrollTop;
221
+ },
222
+ [hasOverflow, maxScrollTop, maxThumbOffset],
223
+ );
224
+
225
+ const handleScrollbarPointerDown = React.useCallback(
226
+ (event: React.PointerEvent<HTMLDivElement>) => {
227
+ const viewport = scrollViewportRef.current;
228
+ const track = scrollbarTrackRef.current;
229
+
230
+ if (!viewport || !track || !hasOverflow) {
231
+ return;
232
+ }
233
+
234
+ const trackRect = track.getBoundingClientRect();
235
+ const targetElement = event.target as HTMLElement | null;
236
+ const clickedThumb = targetElement?.dataset.scrollbarThumb === "true";
237
+ const pointerOffsetY = clickedThumb
238
+ ? event.clientY - trackRect.top - thumbOffset
239
+ : thumbHeight / 2;
240
+
241
+ dragStateRef.current = {
242
+ pointerId: event.pointerId,
243
+ startY: event.clientY,
244
+ startScrollTop: viewport.scrollTop,
245
+ scrollRatio: maxThumbOffset > 0 ? maxScrollTop / maxThumbOffset : 0,
246
+ pointerOffsetY,
247
+ };
248
+
249
+ if (!clickedThumb) {
250
+ updateScrollTopFromPointer(event.clientY);
251
+ }
252
+
253
+ (event.currentTarget as HTMLDivElement).setPointerCapture(
254
+ event.pointerId,
255
+ );
256
+ event.preventDefault();
257
+ },
258
+ [
259
+ hasOverflow,
260
+ maxScrollTop,
261
+ maxThumbOffset,
262
+ thumbHeight,
263
+ thumbOffset,
264
+ updateScrollTopFromPointer,
265
+ ],
266
+ );
267
+
268
+ const handleScrollbarPointerMove = React.useCallback(
269
+ (event: React.PointerEvent<HTMLDivElement>) => {
270
+ const dragState = dragStateRef.current;
271
+
272
+ if (!dragState || dragState.pointerId !== event.pointerId) {
273
+ return;
274
+ }
275
+
276
+ const viewport = scrollViewportRef.current;
277
+ const track = scrollbarTrackRef.current;
278
+
279
+ if (!viewport || !track || !hasOverflow) {
280
+ return;
281
+ }
282
+
283
+ const deltaY = event.clientY - dragState.startY;
284
+ const nextScrollTop = Math.min(
285
+ Math.max(dragState.startScrollTop + deltaY * dragState.scrollRatio, 0),
286
+ maxScrollTop,
287
+ );
288
+
289
+ viewport.scrollTop = nextScrollTop;
290
+ event.preventDefault();
291
+ },
292
+ [hasOverflow, maxScrollTop],
293
+ );
294
+
295
+ const handleScrollbarPointerUp = React.useCallback(
296
+ (event: React.PointerEvent<HTMLDivElement>) => {
297
+ const dragState = dragStateRef.current;
298
+
299
+ if (dragState?.pointerId === event.pointerId) {
300
+ dragStateRef.current = null;
301
+ }
302
+ },
303
+ [],
304
+ );
128
305
 
129
306
  return (
130
307
  <div className="flex h-full min-h-0 flex-col text-[var(--demo-fg)] [font-synthesis:none] antialiased">
131
308
  <div className="flex min-h-0 flex-1 overflow-hidden rounded-none border border-[var(--demo-border)] bg-[var(--demo-code-bg)] shadow-none">
132
- <div className="flex min-h-0 min-w-0 flex-1 flex-col">
133
- <div className="grid min-h-0 flex-1 grid-cols-[3.5rem_minmax(0,1fr)] bg-[var(--demo-code-bg)]">
309
+ <div className="relative flex min-h-0 min-w-0 flex-1 flex-col">
310
+ <div
311
+ ref={scrollViewportRef}
312
+ className="grid min-h-0 flex-1 grid-cols-[3.5rem_minmax(0,1fr)] overflow-y-auto bg-[var(--demo-code-bg)] pr-5 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden lg:overflow-hidden lg:pr-0"
313
+ >
134
314
  <div className="border-r border-[var(--demo-border)] bg-[var(--demo-code-gutter-bg)] px-2 py-3 font-mono text-[11px] leading-7 text-[var(--demo-subtle-fg)]">
135
315
  {visibleCode.map((line, index) => {
136
316
  const isAdded = line.trimStart().startsWith("+");
@@ -151,7 +331,7 @@ export function RunConsolePanel({ state }: RunConsolePanelProps) {
151
331
  })}
152
332
  </div>
153
333
 
154
- <div className="relative flex h-full min-h-0 flex-col overflow-hidden bg-[var(--demo-code-bg)] px-3 py-3 font-mono text-[12px] leading-7 text-[var(--demo-fg)]">
334
+ <div className="relative min-h-full bg-[var(--demo-code-bg)] px-3 py-3 font-mono text-[12px] leading-7 text-[var(--demo-fg)]">
155
335
  <div>
156
336
  {visibleCode.map((line, index) => {
157
337
  const isAdded = line.trimStart().startsWith("+");
@@ -202,10 +382,29 @@ export function RunConsolePanel({ state }: RunConsolePanelProps) {
202
382
  </div>
203
383
  ) : null}
204
384
  </div>
205
-
206
- <div className="flex-1" />
207
385
  </div>
208
386
  </div>
387
+
388
+ {hasOverflow ? (
389
+ <div
390
+ ref={scrollbarTrackRef}
391
+ aria-hidden="true"
392
+ onPointerDown={handleScrollbarPointerDown}
393
+ onPointerMove={handleScrollbarPointerMove}
394
+ onPointerUp={handleScrollbarPointerUp}
395
+ onPointerCancel={handleScrollbarPointerUp}
396
+ className="absolute inset-y-3 right-1.5 w-[10px] cursor-pointer overflow-hidden rounded-full bg-[var(--demo-scroll-track)] lg:hidden"
397
+ >
398
+ <div
399
+ data-scrollbar-thumb="true"
400
+ className="absolute inset-x-1 rounded-full bg-[var(--demo-scroll-thumb)] shadow-[inset_0_1px_0_rgba(255,255,255,0.35)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.08)] will-change-transform"
401
+ style={{
402
+ height: `${thumbHeight}px`,
403
+ transform: `translateY(${thumbOffset}px)`,
404
+ }}
405
+ />
406
+ </div>
407
+ ) : null}
209
408
  </div>
210
409
 
211
410
  {state.metrics?.length ? (
@@ -9,6 +9,185 @@ export interface TaskListPanelProps {
9
9
  }
10
10
 
11
11
  export function TaskListPanel({ state, onSelect }: TaskListPanelProps) {
12
+ const scrollViewportRef = React.useRef<HTMLDivElement | null>(null);
13
+ const scrollbarTrackRef = React.useRef<HTMLDivElement | null>(null);
14
+ const dragStateRef = React.useRef<{
15
+ pointerId: number;
16
+ startY: number;
17
+ startScrollTop: number;
18
+ scrollRatio: number;
19
+ pointerOffsetY: number;
20
+ } | null>(null);
21
+ const [scrollMetrics, setScrollMetrics] = React.useState({
22
+ scrollTop: 0,
23
+ scrollHeight: 1,
24
+ clientHeight: 1,
25
+ });
26
+
27
+ React.useEffect(() => {
28
+ const viewport = scrollViewportRef.current;
29
+
30
+ if (!viewport) {
31
+ return;
32
+ }
33
+
34
+ const updateScrollMetrics = () => {
35
+ setScrollMetrics({
36
+ scrollTop: viewport.scrollTop,
37
+ scrollHeight: viewport.scrollHeight,
38
+ clientHeight: viewport.clientHeight,
39
+ });
40
+ };
41
+
42
+ updateScrollMetrics();
43
+
44
+ viewport.addEventListener("scroll", updateScrollMetrics, {
45
+ passive: true,
46
+ });
47
+
48
+ const resizeObserver = new ResizeObserver(() => {
49
+ updateScrollMetrics();
50
+ });
51
+
52
+ resizeObserver.observe(viewport);
53
+
54
+ if (viewport.firstElementChild instanceof HTMLElement) {
55
+ resizeObserver.observe(viewport.firstElementChild);
56
+ }
57
+
58
+ window.addEventListener("resize", updateScrollMetrics);
59
+
60
+ return () => {
61
+ viewport.removeEventListener("scroll", updateScrollMetrics);
62
+ resizeObserver.disconnect();
63
+ window.removeEventListener("resize", updateScrollMetrics);
64
+ };
65
+ }, [state.items.length, state.activeItemId]);
66
+
67
+ const hasOverflow =
68
+ scrollMetrics.scrollHeight > scrollMetrics.clientHeight + 1;
69
+ const thumbHeight = hasOverflow
70
+ ? Math.max(
71
+ 36,
72
+ (scrollMetrics.clientHeight / scrollMetrics.scrollHeight) *
73
+ scrollMetrics.clientHeight,
74
+ )
75
+ : 0;
76
+ const maxThumbOffset = Math.max(scrollMetrics.clientHeight - thumbHeight, 0);
77
+ const maxScrollTop = Math.max(
78
+ scrollMetrics.scrollHeight - scrollMetrics.clientHeight,
79
+ 1,
80
+ );
81
+ const thumbOffset = hasOverflow
82
+ ? (scrollMetrics.scrollTop / maxScrollTop) * maxThumbOffset
83
+ : 0;
84
+
85
+ const updateScrollTopFromPointer = React.useCallback(
86
+ (clientY: number) => {
87
+ const viewport = scrollViewportRef.current;
88
+ const track = scrollbarTrackRef.current;
89
+
90
+ if (!viewport || !track || !hasOverflow) {
91
+ return;
92
+ }
93
+
94
+ const trackRect = track.getBoundingClientRect();
95
+ const nextThumbTop = Math.min(
96
+ Math.max(
97
+ clientY - trackRect.top - dragStateRef.current!.pointerOffsetY,
98
+ 0,
99
+ ),
100
+ maxThumbOffset,
101
+ );
102
+ const nextScrollTop =
103
+ maxThumbOffset > 0 ? (nextThumbTop / maxThumbOffset) * maxScrollTop : 0;
104
+
105
+ viewport.scrollTop = nextScrollTop;
106
+ },
107
+ [hasOverflow, maxScrollTop, maxThumbOffset],
108
+ );
109
+
110
+ const handleScrollbarPointerDown = React.useCallback(
111
+ (event: React.PointerEvent<HTMLDivElement>) => {
112
+ const viewport = scrollViewportRef.current;
113
+ const track = scrollbarTrackRef.current;
114
+
115
+ if (!viewport || !track || !hasOverflow) {
116
+ return;
117
+ }
118
+
119
+ const trackRect = track.getBoundingClientRect();
120
+ const targetElement = event.target as HTMLElement | null;
121
+ const clickedThumb = targetElement?.dataset.scrollbarThumb === "true";
122
+ const pointerOffsetY = clickedThumb
123
+ ? event.clientY - trackRect.top - thumbOffset
124
+ : thumbHeight / 2;
125
+
126
+ dragStateRef.current = {
127
+ pointerId: event.pointerId,
128
+ startY: event.clientY,
129
+ startScrollTop: viewport.scrollTop,
130
+ scrollRatio: maxThumbOffset > 0 ? maxScrollTop / maxThumbOffset : 0,
131
+ pointerOffsetY,
132
+ };
133
+
134
+ if (!clickedThumb) {
135
+ updateScrollTopFromPointer(event.clientY);
136
+ }
137
+
138
+ (event.currentTarget as HTMLDivElement).setPointerCapture(
139
+ event.pointerId,
140
+ );
141
+ event.preventDefault();
142
+ },
143
+ [
144
+ hasOverflow,
145
+ maxScrollTop,
146
+ maxThumbOffset,
147
+ thumbHeight,
148
+ thumbOffset,
149
+ updateScrollTopFromPointer,
150
+ ],
151
+ );
152
+
153
+ const handleScrollbarPointerMove = React.useCallback(
154
+ (event: React.PointerEvent<HTMLDivElement>) => {
155
+ const dragState = dragStateRef.current;
156
+
157
+ if (!dragState || dragState.pointerId !== event.pointerId) {
158
+ return;
159
+ }
160
+
161
+ const viewport = scrollViewportRef.current;
162
+ const track = scrollbarTrackRef.current;
163
+
164
+ if (!viewport || !track || !hasOverflow) {
165
+ return;
166
+ }
167
+
168
+ const deltaY = event.clientY - dragState.startY;
169
+ const nextScrollTop = Math.min(
170
+ Math.max(dragState.startScrollTop + deltaY * dragState.scrollRatio, 0),
171
+ maxScrollTop,
172
+ );
173
+
174
+ viewport.scrollTop = nextScrollTop;
175
+ event.preventDefault();
176
+ },
177
+ [hasOverflow, maxScrollTop],
178
+ );
179
+
180
+ const handleScrollbarPointerUp = React.useCallback(
181
+ (event: React.PointerEvent<HTMLDivElement>) => {
182
+ const dragState = dragStateRef.current;
183
+
184
+ if (dragState?.pointerId === event.pointerId) {
185
+ dragStateRef.current = null;
186
+ }
187
+ },
188
+ [],
189
+ );
190
+
12
191
  return (
13
192
  <div className="flex h-full min-h-0 flex-col bg-[var(--demo-panel-muted-bg)] text-[var(--demo-fg)]">
14
193
  <div className="border-b border-[var(--demo-border)] px-3 py-2.5">
@@ -20,75 +199,103 @@ export function TaskListPanel({ state, onSelect }: TaskListPanelProps) {
20
199
  </div>
21
200
  </div>
22
201
 
23
- <div className="space-y-0 overflow-hidden">
24
- {state.items.map((item, index) => {
25
- const isActive = item.id === state.activeItemId;
26
-
27
- return (
28
- <button
29
- key={item.id}
30
- type="button"
31
- onClick={() => onSelect?.(item.id)}
32
- className={cn(
33
- "relative isolate w-full overflow-hidden rounded-none border-x-0 border-y border-b-0 px-3 py-3 text-left transition-colors duration-200 first:border-t-0",
34
- "border-[var(--demo-border)] bg-[var(--demo-panel-bg)] hover:border-[var(--demo-border-strong)] hover:bg-[var(--demo-shell-strong-bg)]",
35
- isActive &&
36
- "border-[var(--demo-border-strong)] bg-[var(--demo-shell-strong-bg)]",
37
- )}
38
- >
39
- {isActive ? (
40
- <span
41
- aria-hidden="true"
42
- className="pointer-events-none absolute inset-y-0 left-0 w-px bg-[var(--demo-accent)]"
43
- />
44
- ) : null}
45
-
46
- <div className="relative z-10 flex items-start gap-3">
47
- <span
202
+ <div className="relative min-h-0 flex-1">
203
+ <div
204
+ ref={scrollViewportRef}
205
+ className="h-full overflow-y-auto pr-5 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
206
+ >
207
+ <div className="space-y-0">
208
+ {state.items.map((item, index) => {
209
+ const isActive = item.id === state.activeItemId;
210
+
211
+ return (
212
+ <button
213
+ key={item.id}
214
+ type="button"
215
+ onClick={() => onSelect?.(item.id)}
48
216
  className={cn(
49
- "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full border text-[10px] font-semibold",
50
- isActive
51
- ? "border-[var(--demo-border-strong)] bg-[var(--demo-shell-strong-bg)] text-[var(--demo-fg)]"
52
- : "border-[var(--demo-border)] bg-[var(--demo-panel-subtle-bg)] text-[var(--demo-muted-fg)]",
217
+ "relative isolate w-full overflow-hidden rounded-none border-x-0 border-y border-b-0 px-3 py-3 text-left transition-colors duration-200 first:border-t-0",
218
+ "border-[var(--demo-border)] bg-[var(--demo-panel-bg)] hover:border-[var(--demo-border-strong)] hover:bg-[var(--demo-shell-strong-bg)]",
219
+ isActive &&
220
+ "border-[var(--demo-border-strong)] bg-[var(--demo-shell-strong-bg)]",
53
221
  )}
54
222
  >
55
- {index + 1}
56
- </span>
57
-
58
- <div className="min-w-0 flex-1">
59
- <div className="flex items-start justify-between gap-3">
60
- <div className="min-w-0">
61
- <div className="text-sm font-semibold text-[var(--demo-fg)]">
62
- {item.title}
63
- </div>
64
- </div>
65
- <div
223
+ {isActive ? (
224
+ <span
225
+ aria-hidden="true"
226
+ className="pointer-events-none absolute inset-y-0 left-0 w-px bg-[var(--demo-accent)]"
227
+ />
228
+ ) : null}
229
+
230
+ <div className="relative z-10 flex items-start gap-3">
231
+ <span
66
232
  className={cn(
67
- "flex h-5 w-[4.5rem] shrink-0 items-center justify-end text-[9px] uppercase tracking-[0.14em]",
233
+ "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full border text-[10px] font-semibold",
68
234
  isActive
69
- ? "text-[var(--demo-muted-fg)]"
70
- : "text-[var(--demo-subtle-fg)]",
235
+ ? "border-[var(--demo-border-strong)] bg-[var(--demo-shell-strong-bg)] text-[var(--demo-fg)]"
236
+ : "border-[var(--demo-border)] bg-[var(--demo-panel-subtle-bg)] text-[var(--demo-muted-fg)]",
71
237
  )}
72
238
  >
73
- {isActive ? "Open" : "Queued"}
239
+ {index + 1}
240
+ </span>
241
+
242
+ <div className="min-w-0 flex-1">
243
+ <div className="flex items-start justify-between gap-3">
244
+ <div className="min-w-0">
245
+ <div className="text-sm font-semibold text-[var(--demo-fg)]">
246
+ {item.title}
247
+ </div>
248
+ </div>
249
+ <div
250
+ className={cn(
251
+ "flex h-5 w-[4.5rem] shrink-0 items-center justify-end text-[9px] uppercase tracking-[0.14em]",
252
+ isActive
253
+ ? "text-[var(--demo-muted-fg)]"
254
+ : "text-[var(--demo-subtle-fg)]",
255
+ )}
256
+ >
257
+ {isActive ? "Open" : "Queued"}
258
+ </div>
259
+ </div>
260
+ {item.description && (
261
+ <p className="mt-1.5 max-w-[22ch] text-xs leading-relaxed text-[var(--demo-muted-fg)]">
262
+ {item.description}
263
+ </p>
264
+ )}
265
+ {item.meta && (
266
+ <div className="mt-2.5 flex items-center gap-2 text-[11px] text-[var(--demo-muted-fg)]">
267
+ <span className="h-1 w-1 rounded-full bg-[var(--demo-border-strong)]" />
268
+ <span>{item.meta}</span>
269
+ </div>
270
+ )}
74
271
  </div>
75
272
  </div>
76
- {item.description && (
77
- <p className="mt-1.5 max-w-[22ch] text-xs leading-relaxed text-[var(--demo-muted-fg)]">
78
- {item.description}
79
- </p>
80
- )}
81
- {item.meta && (
82
- <div className="mt-2.5 flex items-center gap-2 text-[11px] text-[var(--demo-muted-fg)]">
83
- <span className="h-1 w-1 rounded-full bg-[var(--demo-border-strong)]" />
84
- <span>{item.meta}</span>
85
- </div>
86
- )}
87
- </div>
88
- </div>
89
- </button>
90
- );
91
- })}
273
+ </button>
274
+ );
275
+ })}
276
+ </div>
277
+ </div>
278
+
279
+ {hasOverflow ? (
280
+ <div
281
+ ref={scrollbarTrackRef}
282
+ aria-hidden="true"
283
+ onPointerDown={handleScrollbarPointerDown}
284
+ onPointerMove={handleScrollbarPointerMove}
285
+ onPointerUp={handleScrollbarPointerUp}
286
+ onPointerCancel={handleScrollbarPointerUp}
287
+ className="absolute inset-y-3 right-1.5 w-[10px] cursor-pointer overflow-hidden rounded-full bg-[var(--demo-scroll-track)]"
288
+ >
289
+ <div
290
+ data-scrollbar-thumb="true"
291
+ className="absolute inset-x-1 rounded-full bg-[var(--demo-scroll-thumb)] shadow-[inset_0_1px_0_rgba(255,255,255,0.35)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.08)] will-change-transform"
292
+ style={{
293
+ height: `${thumbHeight}px`,
294
+ transform: `translateY(${thumbOffset}px)`,
295
+ }}
296
+ />
297
+ </div>
298
+ ) : null}
92
299
  </div>
93
300
  </div>
94
301
  );
@@ -13,9 +13,9 @@
13
13
  "next-themes": "^0.4.6",
14
14
  "motion": "^12.24.10",
15
15
  "sonner": "^2.0.7",
16
- "@nextworks/blocks-core": "0.2.0-alpha.15",
17
- "@nextworks/blocks-sections": "0.2.0-alpha.15",
18
- "@nextworks/blocks-templates": "0.2.0-alpha.15"
16
+ "@nextworks/blocks-core": "0.2.0-alpha.16",
17
+ "@nextworks/blocks-sections": "0.2.0-alpha.16",
18
+ "@nextworks/blocks-templates": "0.2.0-alpha.16"
19
19
  },
20
20
  "devDependencies": {
21
21
  "tailwindcss": "^4.1.12"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextworks",
3
- "version": "0.2.0-alpha.15",
3
+ "version": "0.2.0-alpha.16",
4
4
  "description": "Nextworks CLI",
5
5
  "main": "dist/index.js",
6
6
  "bin": {