@noya-app/noya-designsystem 0.1.63 → 0.1.64

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.
Files changed (112) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +13 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +169 -127
  5. package/dist/index.d.ts +169 -127
  6. package/dist/index.js +3131 -2642
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +2941 -2459
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +10 -22
  11. package/src/__tests__/validateDropIndicator.test.ts +26 -41
  12. package/src/__tests__/workspaceLayout.test.ts +281 -0
  13. package/src/components/ActionMenu.tsx +2 -0
  14. package/src/components/ActivityIndicator.tsx +5 -3
  15. package/src/components/AnimatePresence.tsx +6 -4
  16. package/src/components/Avatar.tsx +4 -2
  17. package/src/components/BaseToolbar.tsx +45 -34
  18. package/src/components/Breadcrumbs.tsx +2 -0
  19. package/src/components/Button.tsx +9 -3
  20. package/src/components/Chip.tsx +2 -0
  21. package/src/components/Collection.tsx +4 -1
  22. package/src/components/ColorSwatch.tsx +42 -22
  23. package/src/components/ColorSwatchControl.tsx +5 -3
  24. package/src/components/Combobox.tsx +2 -0
  25. package/src/components/CommandPalette.tsx +2 -0
  26. package/src/components/ContextMenu.tsx +3 -1
  27. package/src/components/Dialog.tsx +3 -1
  28. package/src/components/DimensionInput.tsx +2 -0
  29. package/src/components/Divider.tsx +10 -6
  30. package/src/components/DraggableMenuButton.tsx +2 -0
  31. package/src/components/Drawer.tsx +11 -3
  32. package/src/components/DropdownMenu.tsx +3 -1
  33. package/src/components/EditableText.tsx +2 -0
  34. package/src/components/FileUploadIndicator.tsx +108 -0
  35. package/src/components/FillPreviewBackground.tsx +2 -0
  36. package/src/components/FloatingWindow.tsx +2 -0
  37. package/src/components/GradientPicker.tsx +2 -0
  38. package/src/components/Grid.tsx +47 -38
  39. package/src/components/GridView.tsx +33 -11
  40. package/src/components/IconButton.tsx +2 -0
  41. package/src/components/Icons.tsx +1 -1
  42. package/src/components/InputField.tsx +2 -0
  43. package/src/components/Label.tsx +1 -1
  44. package/src/components/LabeledElementView.tsx +6 -2
  45. package/src/components/LabeledField.tsx +2 -0
  46. package/src/components/List.tsx +10 -2
  47. package/src/components/ListView.tsx +27 -14
  48. package/src/components/MediaThumbnail.tsx +31 -2
  49. package/src/components/Message.tsx +8 -9
  50. package/src/components/Popover.tsx +3 -1
  51. package/src/components/Progress.tsx +20 -7
  52. package/src/components/ScrollArea.tsx +3 -1
  53. package/src/components/SearchCompletionMenu.tsx +2 -0
  54. package/src/components/Section.tsx +2 -0
  55. package/src/components/SegmentedControl.tsx +42 -16
  56. package/src/components/SelectMenu.tsx +3 -1
  57. package/src/components/SelectionToolbar.tsx +3 -1
  58. package/src/components/Slider.tsx +3 -1
  59. package/src/components/Switch.tsx +3 -1
  60. package/src/components/Tabs.tsx +3 -1
  61. package/src/components/Text.tsx +6 -5
  62. package/src/components/TextArea.tsx +2 -0
  63. package/src/components/Toast.tsx +1 -1
  64. package/src/components/Toolbar.tsx +8 -2
  65. package/src/components/ToolbarDrawer.tsx +29 -0
  66. package/src/components/Tooltip.tsx +1 -1
  67. package/src/components/TreeView.tsx +2 -0
  68. package/src/components/UserPointer.tsx +2 -0
  69. package/src/components/ai-assistant/AIAssistantLayout.tsx +2 -0
  70. package/src/components/blocks/ImageBlockComponent.tsx +156 -0
  71. package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +2 -0
  72. package/src/components/file-explorer/FileExplorerLayout.tsx +2 -0
  73. package/src/components/internal/Checkmark.tsx +24 -0
  74. package/src/components/internal/Menu.tsx +2 -1
  75. package/src/components/internal/MenuViewport.tsx +4 -2
  76. package/src/components/internal/SelectItem.tsx +4 -23
  77. package/src/components/internal/TextInput.tsx +2 -0
  78. package/src/components/sorting/DragRegistration.tsx +8 -4
  79. package/src/components/sorting/SharedDragProvider.tsx +43 -18
  80. package/src/components/sorting/Sortable.tsx +19 -10
  81. package/src/components/sorting/createSharedDrag.tsx +7 -6
  82. package/src/components/sorting/sorting.ts +6 -9
  83. package/src/components/workspace/DrawerWorkspaceLayout.tsx +107 -26
  84. package/src/components/workspace/PanelWorkspaceLayout.tsx +98 -65
  85. package/src/components/workspace/VerticalTabMenu.tsx +36 -0
  86. package/src/components/workspace/WorkspaceLayout.tsx +46 -42
  87. package/src/components/workspace/constants.ts +4 -0
  88. package/src/components/workspace/panelStorage.ts +216 -0
  89. package/src/components/workspace/types.ts +11 -4
  90. package/src/contexts/DesignSystemConfiguration.tsx +12 -5
  91. package/src/contexts/DialogContext.tsx +2 -0
  92. package/src/contexts/FloatingWindowContext.tsx +2 -0
  93. package/src/contexts/GlobalInputBlurContext.tsx +2 -0
  94. package/src/contexts/ImageDataContext.tsx +2 -0
  95. package/src/contexts/LinkComponentContext.tsx +17 -0
  96. package/src/contexts/OpenPortalsContext.tsx +2 -0
  97. package/src/contexts/PortalScopeContext.tsx +2 -0
  98. package/src/hooks/mergeEventHandlers.ts +1 -1
  99. package/src/hooks/useHover.ts +30 -17
  100. package/src/hooks/useIndent.ts +2 -0
  101. package/src/hooks/useLabel.ts +2 -0
  102. package/src/hooks/useObjectURL.ts +5 -3
  103. package/src/hooks/usePlatform.ts +5 -3
  104. package/src/hooks/useTheme.ts +3 -1
  105. package/src/index.css +19 -0
  106. package/src/index.tsx +5 -2
  107. package/src/utils/classNames.ts +4 -3
  108. package/src/utils/combobox.ts +2 -0
  109. package/src/utils/formatByteSize.ts +8 -0
  110. package/src/utils/withSeparatorElements.ts +1 -1
  111. package/src/hooks/usePreservePanelSize.tsx +0 -183
  112. package/src/mediaQuery.ts +0 -13
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { KeyModifiers } from "@noya-app/noya-keymap";
2
4
  import { forwardRefGeneric, memoGeneric } from "@noya-app/react-utils";
3
5
  import React, {
@@ -11,6 +13,7 @@ import React, {
11
13
  useContext,
12
14
  useMemo,
13
15
  } from "react";
16
+ import { useLinkComponent } from "../contexts/LinkComponentContext";
14
17
  import { useHover } from "../hooks/useHover";
15
18
  import { cx, mergeConflictingClassNames } from "../utils/classNames";
16
19
  import withSeparatorElements from "../utils/withSeparatorElements";
@@ -79,6 +82,7 @@ const SectionTitle = ({
79
82
 
80
83
  interface ItemProps<MenuItemType extends string = string> {
81
84
  id: string;
85
+ href?: string;
82
86
  title?: ReactNode;
83
87
  subtitle?: ReactNode;
84
88
  loading?: boolean;
@@ -105,6 +109,7 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
105
109
  >(
106
110
  {
107
111
  id,
112
+ href,
108
113
  title,
109
114
  subtitle,
110
115
  loading,
@@ -125,23 +130,27 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
125
130
  testShowInsideDropIndicator: showInsideDropIndicator = false,
126
131
  ...props
127
132
  }: ItemProps<MenuItemType>,
128
- forwardedRef: ForwardedRef<HTMLDivElement>
133
+ forwardedRef: ForwardedRef<HTMLElement>
129
134
  ) {
130
135
  const { hoverProps } = useHover({
131
136
  onHoverChange,
132
137
  });
133
138
 
134
- const { textPosition, bordered, disabled } = useContext(GridViewContext);
139
+ const { textPosition, bordered, disabled, alwaysShowSubtitles } =
140
+ useContext(GridViewContext);
135
141
 
136
142
  const handleClick = useCallback(
137
143
  (event: React.MouseEvent) => {
138
144
  event.stopPropagation();
139
- event.preventDefault();
145
+
146
+ if (!href) {
147
+ event.preventDefault();
148
+ }
140
149
 
141
150
  onClick?.(event);
142
151
  onSelect?.(event);
143
152
  },
144
- [onClick, onSelect]
153
+ [href, onClick, onSelect]
145
154
  );
146
155
 
147
156
  const handleKeyDown = useCallback(
@@ -159,8 +168,11 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
159
168
  [onSelect, onKeyDown]
160
169
  );
161
170
 
171
+ const LinkComponent = useLinkComponent();
172
+ const Component = typeof href === "string" ? LinkComponent : "div";
173
+
162
174
  let element = (
163
- <div
175
+ <Component
164
176
  {...props}
165
177
  className={cx(
166
178
  "flex flex-col relative rounded p-2 -m-2 text-text",
@@ -173,7 +185,8 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
173
185
  "focus:ring-2 focus:ring-primary focus:ring-inset focus:outline-none"
174
186
  )}
175
187
  id={id}
176
- ref={forwardedRef}
188
+ href={href}
189
+ ref={forwardedRef as ForwardedRef<HTMLAnchorElement & HTMLDivElement>}
177
190
  {...hoverProps}
178
191
  tabIndex={disabled ? undefined : 0}
179
192
  onKeyDown={handleKeyDown}
@@ -194,8 +207,12 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
194
207
  <>
195
208
  <Spacer.Vertical size={8} />
196
209
  <ItemTitle>{title || " "}</ItemTitle>
197
- <Spacer.Vertical size={2} />
198
- <ItemDescription>{subtitle || " "}</ItemDescription>
210
+ {(alwaysShowSubtitles || subtitle) && (
211
+ <>
212
+ <Spacer.Vertical size={2} />
213
+ <ItemDescription>{subtitle || " "}</ItemDescription>
214
+ </>
215
+ )}
199
216
  </>
200
217
  )}
201
218
  {textPosition === "overlay" && hovered && (title || subtitle) && (
@@ -216,7 +233,7 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
216
233
  <ActivityIndicator opacity={0.5} size={13} />
217
234
  </div>
218
235
  )}
219
- </div>
236
+ </Component>
220
237
  );
221
238
 
222
239
  if (menuItems && onSelectMenuItem) {
@@ -255,6 +272,7 @@ type GridViewContextValue = {
255
272
  bordered: boolean;
256
273
  disabled: boolean;
257
274
  gap: number;
275
+ alwaysShowSubtitles: boolean;
258
276
  };
259
277
 
260
278
  const GridViewContext = createContext<GridViewContextValue>({
@@ -263,6 +281,7 @@ const GridViewContext = createContext<GridViewContextValue>({
263
281
  bordered: false,
264
282
  disabled: false,
265
283
  gap: 0,
284
+ alwaysShowSubtitles: false,
266
285
  });
267
286
 
268
287
  interface GridViewRootProps extends Partial<GridViewContextValue> {
@@ -277,6 +296,7 @@ interface GridViewRootProps extends Partial<GridViewContextValue> {
277
296
  role?: string;
278
297
  renderEmptyState?: () => ReactNode;
279
298
  contentClassName?: string;
299
+ alwaysShowSubtitles?: boolean;
280
300
  }
281
301
 
282
302
  const GridViewRoot = forwardRef(function GridViewRoot(
@@ -291,6 +311,7 @@ const GridViewRoot = forwardRef(function GridViewRoot(
291
311
  disabled = false,
292
312
  className,
293
313
  contentClassName,
314
+ alwaysShowSubtitles = false,
294
315
  ...props
295
316
  }: GridViewRootProps,
296
317
  forwardedRef: ForwardedRef<HTMLDivElement>
@@ -306,14 +327,15 @@ const GridViewRoot = forwardRef(function GridViewRoot(
306
327
  );
307
328
 
308
329
  const contextValue = useMemo(
309
- () => ({
330
+ (): GridViewContextValue => ({
310
331
  minColumnWidth,
311
332
  textPosition,
312
333
  bordered,
313
334
  disabled,
314
335
  gap,
336
+ alwaysShowSubtitles,
315
337
  }),
316
- [bordered, disabled, gap, minColumnWidth, textPosition]
338
+ [bordered, disabled, gap, minColumnWidth, textPosition, alwaysShowSubtitles]
317
339
  );
318
340
 
319
341
  const mergedClassName = useMemo(() => {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { cssVars } from "../theme";
3
5
  import { Button, ButtonProps } from "./Button";
@@ -11,7 +11,7 @@ export function renderIcon(
11
11
  if (typeof iconName === "string") {
12
12
  const Icon = Icons[iconName as IconName];
13
13
 
14
- return <Icon />;
14
+ return <Icon className="min-h-[15px]" />;
15
15
  }
16
16
 
17
17
  return iconName;
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { DropdownChevronIcon } from "@noya-app/noya-icons";
2
4
  import { memoGeneric, useMergeRefs, useSize } from "@noya-app/react-utils";
3
5
  import { Property } from "csstype";
@@ -5,7 +5,7 @@ export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
5
5
 
6
6
  const labelStyles = "flex items-center select-none z-label relative";
7
7
  const labelTextStyles =
8
- "font-sans text-label uppercase leading-3 font-bold text-text-muted";
8
+ "font-sans text-label uppercase leading-none font-bold text-text-muted";
9
9
 
10
10
  export const Label = memo(
11
11
  forwardRef<HTMLLabelElement, LabelProps>(function Label(
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as kiwi from "kiwi.js";
2
4
  import * as React from "react";
3
5
 
@@ -13,11 +15,13 @@ export const LabeledElementView = React.memo(function LabeledElementView({
13
15
  const elementIds: string[] = React.Children.toArray(children)
14
16
  .flatMap((child) =>
15
17
  React.isValidElement(child) && child.type === React.Fragment
16
- ? (child.props.children as React.ReactNode[])
18
+ ? ((child as any).props.children as React.ReactNode[])
17
19
  : [child]
18
20
  )
19
21
  .map((child) =>
20
- React.isValidElement(child) && "id" in child.props ? child.props.id : null
22
+ React.isValidElement(child) && "id" in (child as any).props
23
+ ? (child as any).props.id
24
+ : null
21
25
  )
22
26
  .filter((id) => !!id);
23
27
  const serializedIds = elementIds.join(",");
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React, { memo, useMemo } from "react";
2
4
  import { useIndent } from "../hooks/useIndent";
3
5
  import {
@@ -1,9 +1,12 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  forwardRefGeneric,
3
5
  memoGeneric,
4
6
  useFileDropTarget,
5
7
  } from "@noya-app/react-utils";
6
8
  import React, { memo, useImperativeHandle, useRef, useState } from "react";
9
+ import { useLinkComponent } from "../contexts/LinkComponentContext";
7
10
  import { cssVars } from "../theme";
8
11
  import { cx } from "../utils/classNames";
9
12
  import { updateSelection } from "../utils/selection";
@@ -32,6 +35,7 @@ export const List = memoGeneric(
32
35
  items,
33
36
  getId,
34
37
  getName,
38
+ getHref,
35
39
  expandable,
36
40
  getExpanded,
37
41
  getRenamable,
@@ -101,8 +105,9 @@ export const List = memoGeneric(
101
105
  };
102
106
 
103
107
  const ViewComponent = expandable ? TreeView : ListView;
108
+ const LinkComponent = useLinkComponent();
104
109
 
105
- const fileDropTargetRef = useRef<HTMLDivElement>(null);
110
+ const fileDropTargetRef = useRef<HTMLDivElement | null>(null);
106
111
  const { isDropTargetActive, dropTargetProps } = useFileDropTarget(
107
112
  fileDropTargetRef,
108
113
  onFilesDrop
@@ -144,6 +149,7 @@ export const List = memoGeneric(
144
149
  { isDragOverlay }: ListViewItemInfo
145
150
  ) => {
146
151
  const id = getId(item);
152
+ const href = getHref?.(item);
147
153
  const isDropdownOpen = dropdownOpenId === id;
148
154
  const isHovered =
149
155
  (hoveredId === id || isDropdownOpen) && !isDragOverlay;
@@ -197,6 +203,8 @@ export const List = memoGeneric(
197
203
 
198
204
  return (
199
205
  <ViewComponent.Row
206
+ as={typeof href === "string" ? LinkComponent : undefined}
207
+ href={href}
200
208
  id={id}
201
209
  key={id}
202
210
  role="option"
@@ -355,7 +363,7 @@ const rowGapMap: Record<CollectionItemSize, string> = {
355
363
  xxs: "gap-2",
356
364
  xs: "gap-2",
357
365
  small: "gap-2",
358
- medium: "gap-3",
366
+ medium: "gap-2",
359
367
  mediumLarge: "gap-3",
360
368
  large: "gap-3",
361
369
  xl: "gap-4",
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Size } from "@noya-app/noya-geometry";
2
4
  import { range } from "@noya-app/noya-utils";
3
5
  import { forwardRefGeneric, memoGeneric } from "@noya-app/react-utils";
@@ -192,10 +194,14 @@ const RowContainer = forwardRef<
192
194
  $gap: number;
193
195
  $backgroundColor?: CSSProperties["backgroundColor"];
194
196
  $clickable?: boolean;
197
+ href?: string;
198
+ as?: React.ElementType;
195
199
  }
196
200
  >(
197
201
  (
198
202
  {
203
+ as: Component = "div",
204
+ href,
199
205
  className,
200
206
  $marginType,
201
207
  $selected,
@@ -222,8 +228,9 @@ const RowContainer = forwardRef<
222
228
  ref
223
229
  ) => {
224
230
  return (
225
- <div
231
+ <Component
226
232
  ref={ref}
233
+ href={href}
227
234
  role={role}
228
235
  aria-label={ariaLabel}
229
236
  aria-roledescription={ariaRoleDescription}
@@ -363,7 +370,7 @@ const ListViewDragIndicatorElement = forwardRef<
363
370
  backgroundColor:
364
371
  $colorScheme === "secondary"
365
372
  ? cssVars.colors.secondary
366
- : cssVars.colors.selectedListItemBackground,
373
+ : cssVars.colors.dragOutline,
367
374
  border: `2px solid white`,
368
375
  boxShadow: "0 0 2px rgba(0,0,0,0.5)",
369
376
  }),
@@ -410,6 +417,8 @@ export interface ListViewRowProps<MenuItemType extends string = string> {
410
417
  | CSSProperties
411
418
  | ((props: DragIndicatorStyleProps) => CSSProperties);
412
419
  testRelativeDropPosition?: RelativeDropPosition;
420
+ href?: string;
421
+ as?: React.ElementType;
413
422
  }
414
423
 
415
424
  export type DragIndicatorStyleProps = {
@@ -446,6 +455,8 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
446
455
  dragIndicatorStyle,
447
456
  className,
448
457
  testRelativeDropPosition,
458
+ href,
459
+ as,
449
460
  }: ListViewRowProps<MenuItemType>,
450
461
  forwardedRef: ForwardedRef<HTMLElement>
451
462
  ) {
@@ -472,7 +483,7 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
472
483
  // We use preventDefault as a hack to mark this event as handled. We check for
473
484
  // this in the ListView.Root. We can't stopPropagation here or existing ContextMenus
474
485
  // won't close (onPointerDownOutside won't fire).
475
- event.preventDefault();
486
+ // event.preventDefault();
476
487
 
477
488
  // We only want to handle left clicks
478
489
  if (event.button !== 0) return;
@@ -517,6 +528,8 @@ const ListViewRow = forwardRefGeneric(function ListViewRow<
517
528
 
518
529
  const element = (
519
530
  <Component
531
+ as={as}
532
+ href={href}
520
533
  ref={ref as any}
521
534
  $colorScheme={colorScheme}
522
535
  onContextMenu={onContextMenu}
@@ -787,7 +800,7 @@ export type ListViewRootProps = {
787
800
  divider?: boolean;
788
801
  gap?: number;
789
802
  colorScheme?: ListColorScheme;
790
- containerRef?: React.RefObject<HTMLDivElement>;
803
+ containerRef?: React.RefObject<HTMLDivElement | null>;
791
804
  onDragOver?: React.DragEventHandler<HTMLDivElement>;
792
805
  onDragEnter?: React.DragEventHandler<HTMLDivElement>;
793
806
  onDragLeave?: React.DragEventHandler<HTMLDivElement>;
@@ -897,18 +910,18 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
897
910
  const prevChild = i - 1 >= 0 && renderChild(i - 1);
898
911
  const nextChild = i + 1 < data.length && renderChild(i + 1);
899
912
 
900
- const next: ReactElement | undefined = isValidElement(nextChild)
913
+ const next: ReactElement<any> | undefined = isValidElement(nextChild)
901
914
  ? nextChild
902
915
  : undefined;
903
- const prev: ReactElement | undefined = isValidElement(prevChild)
916
+ const prev: ReactElement<any> | undefined = isValidElement(prevChild)
904
917
  ? prevChild
905
918
  : undefined;
906
919
 
907
920
  const hasMarginTop = !prev;
908
921
  const hasMarginBottom =
909
922
  !next ||
910
- current.props.isSectionHeader ||
911
- (next && next.props.isSectionHeader);
923
+ (current as any).props.isSectionHeader ||
924
+ (next && (next as any).props.isSectionHeader);
912
925
 
913
926
  let marginType: ListRowMarginType;
914
927
 
@@ -924,11 +937,11 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
924
937
 
925
938
  let selectedPosition: ListRowPosition = "only";
926
939
 
927
- if (current.props.selected) {
940
+ if ((current as any).props.selected) {
928
941
  const nextSelected =
929
- next && !next.props.isSectionHeader && next.props.selected;
942
+ next && !next.props.isSectionHeader && (next as any).props.selected;
930
943
  const prevSelected =
931
- prev && !prev.props.isSectionHeader && prev.props.selected;
944
+ prev && !prev.props.isSectionHeader && (prev as any).props.selected;
932
945
 
933
946
  if (nextSelected && prevSelected) {
934
947
  selectedPosition = "middle";
@@ -949,7 +962,7 @@ const ListViewRootInner = forwardRefGeneric(function ListViewRootInner<T>(
949
962
  pressEventName,
950
963
  variant,
951
964
  sectionHeaderVariant,
952
- isSectionHeader: current.props.isSectionHeader,
965
+ isSectionHeader: (current as any).props.isSectionHeader,
953
966
  gap,
954
967
  };
955
968
  },
@@ -1100,7 +1113,7 @@ const ChildrenListViewInner = forwardRef(function ChildrenListViewInner(
1100
1113
  { children, ...rest }: ChildrenProps & ListViewRootProps,
1101
1114
  forwardedRef: ForwardedRef<IVirtualizedList>
1102
1115
  ) {
1103
- const items: ReactElement[] = useMemo(
1116
+ const items: ReactElement<any>[] = useMemo(
1104
1117
  () =>
1105
1118
  Children.toArray(children).flatMap((child) =>
1106
1119
  isValidElement(child) ? [child] : []
@@ -1118,7 +1131,7 @@ const ChildrenListViewInner = forwardRef(function ChildrenListViewInner(
1118
1131
  typeof key === "string" ? key : (key ?? index).toString(),
1119
1132
  []
1120
1133
  )}
1121
- renderItem={useCallback((item: ReactElement) => item, [])}
1134
+ renderItem={useCallback((item: ReactElement<any>) => item, [])}
1122
1135
  />
1123
1136
  );
1124
1137
  });
@@ -1,6 +1,9 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  FileIcon,
3
5
  ImageIcon,
6
+ NoyaIcon,
4
7
  SpeakerLoudIcon,
5
8
  VideoIcon,
6
9
  } from "@noya-app/noya-icons";
@@ -23,7 +26,12 @@ const getAssetType = (contentType?: string): AssetType | undefined => {
23
26
  return undefined;
24
27
  };
25
28
 
26
- type MediaThumbnailProps = {
29
+ export type RenderThumbnailIconProps = {
30
+ fileName?: string;
31
+ contentType?: string;
32
+ };
33
+
34
+ export type MediaThumbnailProps = {
27
35
  contentType?: string;
28
36
  url?: string;
29
37
  selected?: boolean;
@@ -39,6 +47,7 @@ type MediaThumbnailProps = {
39
47
  size?: CollectionItemSize;
40
48
 
41
49
  fileName?: string;
50
+ renderThumbnailIcon?: (props: RenderThumbnailIconProps) => React.ReactNode;
42
51
  };
43
52
 
44
53
  export const getThumbnailColors = (
@@ -86,6 +95,7 @@ export const MediaThumbnail = memo(function MediaThumbnail({
86
95
  iconName,
87
96
  size = "medium",
88
97
  fileName,
98
+ renderThumbnailIcon,
89
99
  }: MediaThumbnailProps) {
90
100
  const { icon: iconColor, background: backgroundColor } = getThumbnailColors(
91
101
  colorScheme,
@@ -101,6 +111,12 @@ export const MediaThumbnail = memo(function MediaThumbnail({
101
111
  );
102
112
 
103
113
  const content = useMemo(() => {
114
+ if (renderThumbnailIcon) {
115
+ const rendered = renderThumbnailIcon({ fileName, contentType });
116
+
117
+ if (rendered) return rendered;
118
+ }
119
+
104
120
  switch (contentType) {
105
121
  case "image":
106
122
  if (!url) return <ImageIcon color={iconColor} style={iconStyles} />;
@@ -124,7 +140,12 @@ export const MediaThumbnail = memo(function MediaThumbnail({
124
140
  }
125
141
 
126
142
  if (fileName) {
143
+ if (fileName.endsWith(".noya")) {
144
+ return <NoyaIcon color={iconColor} style={iconStyles} />;
145
+ }
146
+
127
147
  const fileIcon = findFileIcon(fileName);
148
+
128
149
  if (fileIcon) {
129
150
  return (
130
151
  <img
@@ -138,7 +159,15 @@ export const MediaThumbnail = memo(function MediaThumbnail({
138
159
 
139
160
  return <FileIcon color={iconColor} style={iconStyles} />;
140
161
  }
141
- }, [contentType, url, iconColor, iconStyles, iconName, fileName]);
162
+ }, [
163
+ contentType,
164
+ url,
165
+ iconColor,
166
+ iconStyles,
167
+ iconName,
168
+ renderThumbnailIcon,
169
+ fileName,
170
+ ]);
142
171
 
143
172
  return (
144
173
  <div
@@ -1,11 +1,13 @@
1
+ "use client";
2
+
1
3
  import { MultiplayerUser } from "@noya-app/noya-multiplayer-react";
4
+ import { uuid } from "@noya-app/noya-utils";
2
5
  import React, { forwardRef, memo, useMemo } from "react";
3
6
  import { cssVars, INPUT_HEIGHT } from "../theme";
4
7
  import { cx } from "../utils/classNames";
5
8
  import { colorFromString } from "../utils/colorFromString";
6
9
  import { Avatar } from "./Avatar";
7
10
  import { Logo } from "./NoyaLogo";
8
- import { Text } from "./Text";
9
11
 
10
12
  export type MessageProps = {
11
13
  role: "user" | "assistant" | "system" | "data";
@@ -20,7 +22,7 @@ export const Message = memo(
20
22
  { role, children, user, avatarSize = INPUT_HEIGHT }: MessageProps,
21
23
  ref: React.ForwardedRef<HTMLDivElement>
22
24
  ) {
23
- const randomId = crypto.randomUUID();
25
+ const randomId = uuid();
24
26
  const userMessageBackgroundColor = colorFromString(user?.id ?? randomId);
25
27
  const styles = useMemo(
26
28
  () =>
@@ -56,18 +58,15 @@ export const Message = memo(
56
58
  )}
57
59
  <div
58
60
  className={cx(
59
- "flex flex-row gap-2 rounded-lg py-2 px-3 transition-all duration-200 min-w-6 max-w-[70%] items-start",
61
+ "markdown-message",
62
+ "flex flex-row gap-2 rounded-lg py-2 px-3 transition-all duration-200 max-w-[70%] text-sm",
63
+ role === "user" ? "text-white" : "text-text",
60
64
  role === "user" ? "ml-auto" : "bg-input-background"
61
65
  )}
62
66
  style={styles}
63
67
  ref={ref}
64
68
  >
65
- <Text
66
- variant="small"
67
- className={role === "user" ? "text-white" : "text-text"}
68
- >
69
- {children}
70
- </Text>
69
+ {children}
71
70
  </div>
72
71
  </div>
73
72
  );
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { useControlledOrUncontrolled } from "@noya-app/react-utils";
2
- import * as PopoverPrimitive from "@radix-ui/react-popover";
4
+ import { Popover as PopoverPrimitive } from "radix-ui";
3
5
  import React, {
4
6
  ComponentProps,
5
7
  useCallback,
@@ -1,18 +1,22 @@
1
+ "use client";
2
+
1
3
  import { clamp } from "@noya-app/noya-utils";
2
- import * as ProgressPrimitive from "@radix-ui/react-progress";
4
+ import { Progress as ProgressPrimitive } from "radix-ui";
3
5
  import * as React from "react";
4
6
  import { cx } from "../utils/classNames";
5
7
 
6
- type ProgressVariant = "normal" | "warning" | "primary" | "secondary";
8
+ type ColorScheme = "normal" | "warning" | "primary" | "secondary";
7
9
 
8
10
  export function Progress({
9
11
  value,
10
- variant = "normal",
12
+ colorScheme = "normal",
11
13
  className,
14
+ style,
12
15
  }: {
13
16
  value: number;
14
- variant?: ProgressVariant;
17
+ colorScheme?: ColorScheme;
15
18
  className?: string;
19
+ style?: React.CSSProperties;
16
20
  }) {
17
21
  const clampedValue = clamp(value, 0, 100);
18
22
  const transformStyles = React.useMemo(
@@ -24,12 +28,21 @@ export function Progress({
24
28
 
25
29
  return (
26
30
  <ProgressPrimitive.Root
27
- className={cx(`relative hidden bg-input-background h-[5px] `, className)}
28
- style={{ transform: "translateZ(0)" }}
31
+ className={cx(
32
+ `relative overflow-hidden bg-input-background h-[5px]`,
33
+ className
34
+ )}
35
+ style={{ transform: "translateZ(0)", ...style }}
29
36
  value={clampedValue}
30
37
  >
31
38
  <ProgressPrimitive.Indicator
32
- className={`${variant === "primary" ? "bg-primary" : variant === "secondary" ? "bg-secondary" : variant === "warning" ? "bg-warning" : "bg-text"} transition-[transform_660ms_cubic-bezier(0.65,0,0.35,1) w-full h-full`}
39
+ className={cx(
40
+ "transition-[transform_660ms_cubic-bezier(0.65,0,0.35,1) w-full h-full",
41
+ colorScheme === "primary" && "bg-primary",
42
+ colorScheme === "secondary" && "bg-secondary",
43
+ colorScheme === "warning" && "bg-warning",
44
+ colorScheme === "normal" && "bg-text"
45
+ )}
33
46
  style={transformStyles}
34
47
  />
35
48
  </ProgressPrimitive.Root>
@@ -1,4 +1,6 @@
1
- import * as RadixScrollArea from "@radix-ui/react-scroll-area";
1
+ "use client";
2
+
3
+ import { ScrollArea as RadixScrollArea } from "radix-ui";
2
4
  import * as React from "react";
3
5
 
4
6
  interface Props {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { getCurrentPlatform, handleKeyboardEvent } from "@noya-app/noya-keymap";
2
4
  import React, {
3
5
  forwardRef,
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import {
2
4
  Button,
3
5
  IconButton,