@mastra/playground-ui 41.1.0-alpha.2 → 42.0.0-alpha.4

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 (48) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/{Txt-p33XTnu8.js → Txt-Df42XG5p.js} +3 -3
  3. package/dist/Txt-Df42XG5p.js.map +1 -0
  4. package/dist/{Txt-BT8_EkuZ.cjs → Txt-I5lK8zy7.cjs} +3 -3
  5. package/dist/Txt-I5lK8zy7.cjs.map +1 -0
  6. package/dist/components/Avatar.cjs.js +1 -1
  7. package/dist/components/Avatar.es.js +1 -1
  8. package/dist/components/ClampedText.cjs.js +51 -0
  9. package/dist/components/ClampedText.cjs.js.map +1 -0
  10. package/dist/components/ClampedText.d.ts +2 -0
  11. package/dist/components/ClampedText.es.js +47 -0
  12. package/dist/components/ClampedText.es.js.map +1 -0
  13. package/dist/components/Entity.cjs.js +1 -1
  14. package/dist/components/Entity.es.js +1 -1
  15. package/dist/components/EntityHeader.cjs.js +1 -1
  16. package/dist/components/EntityHeader.es.js +1 -1
  17. package/dist/components/Entry.cjs.js +1 -1
  18. package/dist/components/Entry.es.js +1 -1
  19. package/dist/components/EnvironmentVariablesEditor.cjs.js +1 -1
  20. package/dist/components/EnvironmentVariablesEditor.es.js +1 -1
  21. package/dist/components/Header.cjs.js +1 -1
  22. package/dist/components/Header.es.js +1 -1
  23. package/dist/components/Table.cjs.js +1 -1
  24. package/dist/components/Table.es.js +1 -1
  25. package/dist/components/ThreadList.cjs.js +1 -1
  26. package/dist/components/ThreadList.es.js +1 -1
  27. package/dist/components/Truncate.cjs.js +1 -1
  28. package/dist/components/Truncate.es.js +1 -1
  29. package/dist/components/Txt.cjs.js +1 -1
  30. package/dist/components/Txt.es.js +1 -1
  31. package/dist/components/ai/plan.cjs.js +1 -1
  32. package/dist/components/ai/plan.es.js +1 -1
  33. package/dist/domains/memory/components/memory-studio-panel.cjs.js +1 -1
  34. package/dist/domains/memory/components/memory-studio-panel.es.js +1 -1
  35. package/dist/hooks/use-is-clamped.cjs.js +36 -0
  36. package/dist/hooks/use-is-clamped.cjs.js.map +1 -0
  37. package/dist/hooks/use-is-clamped.d.ts +2 -0
  38. package/dist/hooks/use-is-clamped.es.js +32 -0
  39. package/dist/hooks/use-is-clamped.es.js.map +1 -0
  40. package/dist/src/ds/components/ClampedText/clamped-text.d.ts +20 -0
  41. package/dist/src/ds/components/ClampedText/clamped-text.stories.d.ts +8 -0
  42. package/dist/src/ds/components/ClampedText/index.d.ts +1 -0
  43. package/dist/src/ds/components/Txt/Txt.d.ts +3 -2
  44. package/dist/src/hooks/use-is-clamped.d.ts +11 -0
  45. package/dist/style.css +32 -0
  46. package/package.json +6 -6
  47. package/dist/Txt-BT8_EkuZ.cjs.map +0 -1
  48. package/dist/Txt-p33XTnu8.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @mastra/playground-ui
2
2
 
3
+ ## 42.0.0-alpha.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`4cfdd64`](https://github.com/mastra-ai/mastra/commit/4cfdd645794feaea0c4ea711e70ecdfbef0c5b8e)]:
8
+ - @mastra/core@1.52.0-alpha.4
9
+ - @mastra/client-js@1.33.0-alpha.4
10
+ - @mastra/react@1.2.6-alpha.4
11
+
12
+ ## 42.0.0-alpha.3
13
+
14
+ ### Minor Changes
15
+
16
+ - Added ClampedText, a design-system component that clamps text to a number of lines and shows a "Read more" toggle only when the clamp actually cuts content. Detection is based on the rendered layout (element measurement, re-checked on resize and after fonts load), not on character count. ([#19565](https://github.com/mastra-ai/mastra/pull/19565))
17
+
18
+ ### Patch Changes
19
+
20
+ - Fixed the ClampedText read-more button to announce its expanded state to screen readers, and fixed clamp measurement so font-load re-measure and effect cleanup still run in browsers without ResizeObserver. ([#19582](https://github.com/mastra-ai/mastra/pull/19582))
21
+
22
+ - Updated dependencies [[`1426af2`](https://github.com/mastra-ai/mastra/commit/1426af24975879c000d13ac75673f630fcc970c1), [`4696604`](https://github.com/mastra-ai/mastra/commit/4696604a8db7517459e7075ed4a6924114cdbdfb), [`975295d`](https://github.com/mastra-ai/mastra/commit/975295d418552f0d46a59edfef4c3ee555f9930a), [`85e4fb5`](https://github.com/mastra-ai/mastra/commit/85e4fb50087a81c74df3a762f53b56373db0b912), [`22c7ca8`](https://github.com/mastra-ai/mastra/commit/22c7ca85b893ebc9bf0113309e13d238a896bdaf), [`85e4fb5`](https://github.com/mastra-ai/mastra/commit/85e4fb50087a81c74df3a762f53b56373db0b912), [`ef03c0c`](https://github.com/mastra-ai/mastra/commit/ef03c0cfc62367a458e4cc56462e2148b35681c5), [`4fb4d88`](https://github.com/mastra-ai/mastra/commit/4fb4d881bc107acee13890ad4d78661016c510ed), [`4eba27a`](https://github.com/mastra-ai/mastra/commit/4eba27adcf60f991df0e62f94b3e75b4e67f3b4b), [`c701be3`](https://github.com/mastra-ai/mastra/commit/c701be32d7d9aa94a66da8c6cc38dcac6856f464), [`50e0ec5`](https://github.com/mastra-ai/mastra/commit/50e0ec5a1b2f3dec9eb1333487be733ac499173c)]:
23
+ - @mastra/core@1.52.0-alpha.3
24
+ - @mastra/client-js@1.33.0-alpha.3
25
+ - @mastra/react@1.2.6-alpha.3
26
+
3
27
  ## 41.1.0-alpha.2
4
28
 
5
29
  ### Patch Changes
@@ -18,9 +18,9 @@ const variants = {
18
18
  const fonts = {
19
19
  mono: "font-mono"
20
20
  };
21
- const Txt = ({ as: Root = "p", className, variant = "ui-md", font, ...props }) => {
22
- return /* @__PURE__ */ jsx(Root, { className: cn(variants[variant], font && fonts[font], className), ...props });
21
+ const Txt = ({ as: Root = "p", className, variant = "ui-md", font, ref, ...props }) => {
22
+ return /* @__PURE__ */ jsx(Root, { ref, className: cn(variants[variant], font && fonts[font], className), ...props });
23
23
  };
24
24
 
25
25
  export { Txt as T };
26
- //# sourceMappingURL=Txt-p33XTnu8.js.map
26
+ //# sourceMappingURL=Txt-Df42XG5p.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Txt-Df42XG5p.js","sources":["../src/ds/components/Txt/Txt.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode, Ref } from 'react';\n\nimport type { FontSizes } from '../../tokens';\nimport { cn } from '@/lib/utils';\n\nexport interface TxtProps extends HTMLAttributes<HTMLDivElement | HTMLLabelElement> {\n as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div';\n ref?: Ref<HTMLElement>;\n variant?: keyof typeof FontSizes;\n font?: 'mono';\n htmlFor?: string;\n className?: string;\n title?: string;\n children?: ReactNode;\n}\n\nconst variants = {\n // UI text sizes\n 'ui-xs': 'text-ui-xs leading-ui-xs',\n 'ui-sm': 'text-ui-sm leading-ui-sm',\n 'ui-smd': 'text-ui-smd leading-ui-smd',\n 'ui-md': 'text-ui-md leading-ui-md',\n 'ui-lg': 'text-ui-lg leading-ui-lg',\n // Header sizes\n 'header-xs': 'text-header-xs leading-header-xs',\n 'header-sm': 'text-header-sm leading-header-sm',\n 'header-md': 'text-header-md leading-header-md',\n 'header-lg': 'text-header-lg leading-header-lg',\n 'header-xl': 'text-header-xl leading-header-xl',\n};\n\nconst fonts = {\n mono: 'font-mono',\n};\n\nexport const Txt = ({ as: Root = 'p', className, variant = 'ui-md', font, ref, ...props }: TxtProps) => {\n // Cast needed: `Root` is polymorphic, so TS narrows the expected ref to a single element type.\n return <Root ref={ref as never} className={cn(variants[variant], font && fonts[font], className)} {...props} />;\n};\n"],"names":[],"mappings":";;;AAgBA,MAAM,QAAA,GAAW;AAAA;AAAA,EAEf,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA,EACT,QAAA,EAAU,4BAAA;AAAA,EACV,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA;AAAA,EAET,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa;AACf,CAAA;AAEA,MAAM,KAAA,GAAQ;AAAA,EACZ,IAAA,EAAM;AACR,CAAA;AAEO,MAAM,GAAA,GAAM,CAAC,EAAE,EAAA,EAAI,IAAA,GAAO,GAAA,EAAK,SAAA,EAAW,OAAA,GAAU,OAAA,EAAS,IAAA,EAAM,GAAA,EAAK,GAAG,OAAM,KAAgB;AAEtG,EAAA,uBAAO,GAAA,CAAC,IAAA,EAAA,EAAK,GAAA,EAAmB,SAAA,EAAW,GAAG,QAAA,CAAS,OAAO,CAAA,EAAG,IAAA,IAAQ,MAAM,IAAI,CAAA,EAAG,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC/G;;;;"}
@@ -20,9 +20,9 @@ const variants = {
20
20
  const fonts = {
21
21
  mono: "font-mono"
22
22
  };
23
- const Txt = ({ as: Root = "p", className, variant = "ui-md", font, ...props }) => {
24
- return /* @__PURE__ */ jsxRuntime.jsx(Root, { className: utils_cn.cn(variants[variant], font && fonts[font], className), ...props });
23
+ const Txt = ({ as: Root = "p", className, variant = "ui-md", font, ref, ...props }) => {
24
+ return /* @__PURE__ */ jsxRuntime.jsx(Root, { ref, className: utils_cn.cn(variants[variant], font && fonts[font], className), ...props });
25
25
  };
26
26
 
27
27
  exports.Txt = Txt;
28
- //# sourceMappingURL=Txt-BT8_EkuZ.cjs.map
28
+ //# sourceMappingURL=Txt-I5lK8zy7.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Txt-I5lK8zy7.cjs","sources":["../src/ds/components/Txt/Txt.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode, Ref } from 'react';\n\nimport type { FontSizes } from '../../tokens';\nimport { cn } from '@/lib/utils';\n\nexport interface TxtProps extends HTMLAttributes<HTMLDivElement | HTMLLabelElement> {\n as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div';\n ref?: Ref<HTMLElement>;\n variant?: keyof typeof FontSizes;\n font?: 'mono';\n htmlFor?: string;\n className?: string;\n title?: string;\n children?: ReactNode;\n}\n\nconst variants = {\n // UI text sizes\n 'ui-xs': 'text-ui-xs leading-ui-xs',\n 'ui-sm': 'text-ui-sm leading-ui-sm',\n 'ui-smd': 'text-ui-smd leading-ui-smd',\n 'ui-md': 'text-ui-md leading-ui-md',\n 'ui-lg': 'text-ui-lg leading-ui-lg',\n // Header sizes\n 'header-xs': 'text-header-xs leading-header-xs',\n 'header-sm': 'text-header-sm leading-header-sm',\n 'header-md': 'text-header-md leading-header-md',\n 'header-lg': 'text-header-lg leading-header-lg',\n 'header-xl': 'text-header-xl leading-header-xl',\n};\n\nconst fonts = {\n mono: 'font-mono',\n};\n\nexport const Txt = ({ as: Root = 'p', className, variant = 'ui-md', font, ref, ...props }: TxtProps) => {\n // Cast needed: `Root` is polymorphic, so TS narrows the expected ref to a single element type.\n return <Root ref={ref as never} className={cn(variants[variant], font && fonts[font], className)} {...props} />;\n};\n"],"names":["jsx","cn"],"mappings":";;;;;AAgBA,MAAM,QAAA,GAAW;AAAA;AAAA,EAEf,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA,EACT,QAAA,EAAU,4BAAA;AAAA,EACV,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA;AAAA,EAET,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa;AACf,CAAA;AAEA,MAAM,KAAA,GAAQ;AAAA,EACZ,IAAA,EAAM;AACR,CAAA;AAEO,MAAM,GAAA,GAAM,CAAC,EAAE,EAAA,EAAI,IAAA,GAAO,GAAA,EAAK,SAAA,EAAW,OAAA,GAAU,OAAA,EAAS,IAAA,EAAM,GAAA,EAAK,GAAG,OAAM,KAAgB;AAEtG,EAAA,uBAAOA,cAAA,CAAC,IAAA,EAAA,EAAK,GAAA,EAAmB,SAAA,EAAWC,YAAG,QAAA,CAAS,OAAO,CAAA,EAAG,IAAA,IAAQ,MAAM,IAAI,CAAA,EAAG,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC/G;;;;"}
@@ -4,7 +4,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
6
  const React = require('react');
7
- const Txt = require('../Txt-BT8_EkuZ.cjs');
7
+ const Txt = require('../Txt-I5lK8zy7.cjs');
8
8
  const primitives_transitions = require('../primitives/transitions.cjs.js');
9
9
  const utils_cn = require('../cn-vA5sWo9W.cjs');
10
10
 
@@ -1,6 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { useState } from 'react';
3
- import { T as Txt } from '../Txt-p33XTnu8.js';
3
+ import { T as Txt } from '../Txt-Df42XG5p.js';
4
4
  import { transitions } from '../primitives/transitions.es.js';
5
5
  import { c as cn } from '../cn-BakJzhuv.js';
6
6
 
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const jsxRuntime = require('react/jsx-runtime');
6
+ const React = require('react');
7
+ const Button = require('../Button-B5RnKrGb.cjs');
8
+ const Txt = require('../Txt-I5lK8zy7.cjs');
9
+ const icons_ChevronIcon = require('../icons/ChevronIcon.cjs.js');
10
+ const hooks_useIsClamped = require('../hooks/use-is-clamped.cjs.js');
11
+ const utils_cn = require('../cn-vA5sWo9W.cjs');
12
+
13
+ const clampClasses = {
14
+ 1: "line-clamp-1",
15
+ 2: "line-clamp-2",
16
+ 3: "line-clamp-3",
17
+ 4: "line-clamp-4",
18
+ 5: "line-clamp-5",
19
+ 6: "line-clamp-6"
20
+ };
21
+ function ClampedText({
22
+ children,
23
+ lines = 2,
24
+ readMoreLabel = "Read more",
25
+ showLessLabel = "Show less",
26
+ className,
27
+ ...txtProps
28
+ }) {
29
+ const [isExpanded, setIsExpanded] = React.useState(false);
30
+ const { ref, isClamped } = hooks_useIsClamped.useIsClamped({ enabled: !isExpanded });
31
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
32
+ /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { ...txtProps, className: utils_cn.cn(!isExpanded && clampClasses[lines], className), ref, children }),
33
+ (isClamped || isExpanded) && /* @__PURE__ */ jsxRuntime.jsxs(
34
+ Button.Button,
35
+ {
36
+ variant: "ghost",
37
+ size: "xs",
38
+ className: "mt-1 -ml-[.8em] self-start justify-self-start",
39
+ "aria-expanded": isExpanded,
40
+ onClick: () => setIsExpanded((v) => !v),
41
+ children: [
42
+ isExpanded ? showLessLabel : readMoreLabel,
43
+ /* @__PURE__ */ jsxRuntime.jsx(icons_ChevronIcon.ChevronIcon, { className: utils_cn.cn("transition-transform", isExpanded && "rotate-180") })
44
+ ]
45
+ }
46
+ )
47
+ ] });
48
+ }
49
+
50
+ exports.ClampedText = ClampedText;
51
+ //# sourceMappingURL=ClampedText.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClampedText.cjs.js","sources":["../../src/ds/components/ClampedText/clamped-text.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Button } from '@/ds/components/Button';\nimport type { TxtProps } from '@/ds/components/Txt';\nimport { Txt } from '@/ds/components/Txt';\nimport { ChevronIcon } from '@/ds/icons/ChevronIcon';\nimport { useIsClamped } from '@/hooks/use-is-clamped';\nimport { cn } from '@/lib/utils';\n\n// Tailwind needs static class names — map instead of interpolating.\nconst clampClasses = {\n 1: 'line-clamp-1',\n 2: 'line-clamp-2',\n 3: 'line-clamp-3',\n 4: 'line-clamp-4',\n 5: 'line-clamp-5',\n 6: 'line-clamp-6',\n} as const;\n\nexport type ClampedTextLines = keyof typeof clampClasses;\n\nexport interface ClampedTextProps extends Omit<TxtProps, 'children' | 'ref'> {\n children: string;\n /** Number of lines to clamp to (default: 2) */\n lines?: ClampedTextLines;\n readMoreLabel?: string;\n showLessLabel?: string;\n}\n\n/** Text clamped to a number of lines, with a \"read more\" toggle shown only when the clamp actually cuts content. */\nexport function ClampedText({\n children,\n lines = 2,\n readMoreLabel = 'Read more',\n showLessLabel = 'Show less',\n className,\n ...txtProps\n}: ClampedTextProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const { ref, isClamped } = useIsClamped({ enabled: !isExpanded });\n\n return (\n <>\n <Txt {...txtProps} className={cn(!isExpanded && clampClasses[lines], className)} ref={ref}>\n {children}\n </Txt>\n {(isClamped || isExpanded) && (\n <Button\n variant=\"ghost\"\n size=\"xs\"\n className=\"mt-1 -ml-[.8em] self-start justify-self-start\"\n aria-expanded={isExpanded}\n onClick={() => setIsExpanded(v => !v)}\n >\n {isExpanded ? showLessLabel : readMoreLabel}\n <ChevronIcon className={cn('transition-transform', isExpanded && 'rotate-180')} />\n </Button>\n )}\n </>\n );\n}\n"],"names":["useState","useIsClamped","jsxs","Fragment","jsx","Txt","cn","Button","ChevronIcon"],"mappings":";;;;;;;;;;;;AASA,MAAM,YAAA,GAAe;AAAA,EACnB,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG;AACL,CAAA;AAaO,SAAS,WAAA,CAAY;AAAA,EAC1B,QAAA;AAAA,EACA,KAAA,GAAQ,CAAA;AAAA,EACR,aAAA,GAAgB,WAAA;AAAA,EAChB,aAAA,GAAgB,WAAA;AAAA,EAChB,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAqB;AACnB,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,EAAE,KAAK,SAAA,EAAU,GAAIC,gCAAa,EAAE,OAAA,EAAS,CAAC,UAAA,EAAY,CAAA;AAEhE,EAAA,uBACEC,eAAA,CAAAC,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAACC,OAAA,EAAA,EAAK,GAAG,QAAA,EAAU,SAAA,EAAWC,WAAA,CAAG,CAAC,UAAA,IAAc,YAAA,CAAa,KAAK,CAAA,EAAG,SAAS,CAAA,EAAG,KAC9E,QAAA,EACH,CAAA;AAAA,IAAA,CACE,aAAa,UAAA,qBACbJ,eAAA;AAAA,MAACK,aAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,OAAA;AAAA,QACR,IAAA,EAAK,IAAA;AAAA,QACL,SAAA,EAAU,+CAAA;AAAA,QACV,eAAA,EAAe,UAAA;AAAA,QACf,OAAA,EAAS,MAAM,aAAA,CAAc,CAAA,CAAA,KAAK,CAAC,CAAC,CAAA;AAAA,QAEnC,QAAA,EAAA;AAAA,UAAA,UAAA,GAAa,aAAA,GAAgB,aAAA;AAAA,yCAC7BC,6BAAA,EAAA,EAAY,SAAA,EAAWF,YAAG,sBAAA,EAAwB,UAAA,IAAc,YAAY,CAAA,EAAG;AAAA;AAAA;AAAA;AAClF,GAAA,EAEJ,CAAA;AAEJ;;;;"}
@@ -0,0 +1,2 @@
1
+ export * from '../src/ds/components/ClampedText/index'
2
+ export {}
@@ -0,0 +1,47 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { B as Button } from '../Button-Dh5CZ6Ir.js';
4
+ import { T as Txt } from '../Txt-Df42XG5p.js';
5
+ import { ChevronIcon } from '../icons/ChevronIcon.es.js';
6
+ import { useIsClamped } from '../hooks/use-is-clamped.es.js';
7
+ import { c as cn } from '../cn-BakJzhuv.js';
8
+
9
+ const clampClasses = {
10
+ 1: "line-clamp-1",
11
+ 2: "line-clamp-2",
12
+ 3: "line-clamp-3",
13
+ 4: "line-clamp-4",
14
+ 5: "line-clamp-5",
15
+ 6: "line-clamp-6"
16
+ };
17
+ function ClampedText({
18
+ children,
19
+ lines = 2,
20
+ readMoreLabel = "Read more",
21
+ showLessLabel = "Show less",
22
+ className,
23
+ ...txtProps
24
+ }) {
25
+ const [isExpanded, setIsExpanded] = useState(false);
26
+ const { ref, isClamped } = useIsClamped({ enabled: !isExpanded });
27
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
28
+ /* @__PURE__ */ jsx(Txt, { ...txtProps, className: cn(!isExpanded && clampClasses[lines], className), ref, children }),
29
+ (isClamped || isExpanded) && /* @__PURE__ */ jsxs(
30
+ Button,
31
+ {
32
+ variant: "ghost",
33
+ size: "xs",
34
+ className: "mt-1 -ml-[.8em] self-start justify-self-start",
35
+ "aria-expanded": isExpanded,
36
+ onClick: () => setIsExpanded((v) => !v),
37
+ children: [
38
+ isExpanded ? showLessLabel : readMoreLabel,
39
+ /* @__PURE__ */ jsx(ChevronIcon, { className: cn("transition-transform", isExpanded && "rotate-180") })
40
+ ]
41
+ }
42
+ )
43
+ ] });
44
+ }
45
+
46
+ export { ClampedText };
47
+ //# sourceMappingURL=ClampedText.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClampedText.es.js","sources":["../../src/ds/components/ClampedText/clamped-text.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Button } from '@/ds/components/Button';\nimport type { TxtProps } from '@/ds/components/Txt';\nimport { Txt } from '@/ds/components/Txt';\nimport { ChevronIcon } from '@/ds/icons/ChevronIcon';\nimport { useIsClamped } from '@/hooks/use-is-clamped';\nimport { cn } from '@/lib/utils';\n\n// Tailwind needs static class names — map instead of interpolating.\nconst clampClasses = {\n 1: 'line-clamp-1',\n 2: 'line-clamp-2',\n 3: 'line-clamp-3',\n 4: 'line-clamp-4',\n 5: 'line-clamp-5',\n 6: 'line-clamp-6',\n} as const;\n\nexport type ClampedTextLines = keyof typeof clampClasses;\n\nexport interface ClampedTextProps extends Omit<TxtProps, 'children' | 'ref'> {\n children: string;\n /** Number of lines to clamp to (default: 2) */\n lines?: ClampedTextLines;\n readMoreLabel?: string;\n showLessLabel?: string;\n}\n\n/** Text clamped to a number of lines, with a \"read more\" toggle shown only when the clamp actually cuts content. */\nexport function ClampedText({\n children,\n lines = 2,\n readMoreLabel = 'Read more',\n showLessLabel = 'Show less',\n className,\n ...txtProps\n}: ClampedTextProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const { ref, isClamped } = useIsClamped({ enabled: !isExpanded });\n\n return (\n <>\n <Txt {...txtProps} className={cn(!isExpanded && clampClasses[lines], className)} ref={ref}>\n {children}\n </Txt>\n {(isClamped || isExpanded) && (\n <Button\n variant=\"ghost\"\n size=\"xs\"\n className=\"mt-1 -ml-[.8em] self-start justify-self-start\"\n aria-expanded={isExpanded}\n onClick={() => setIsExpanded(v => !v)}\n >\n {isExpanded ? showLessLabel : readMoreLabel}\n <ChevronIcon className={cn('transition-transform', isExpanded && 'rotate-180')} />\n </Button>\n )}\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AASA,MAAM,YAAA,GAAe;AAAA,EACnB,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG;AACL,CAAA;AAaO,SAAS,WAAA,CAAY;AAAA,EAC1B,QAAA;AAAA,EACA,KAAA,GAAQ,CAAA;AAAA,EACR,aAAA,GAAgB,WAAA;AAAA,EAChB,aAAA,GAAgB,WAAA;AAAA,EAChB,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAqB;AACnB,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAS,KAAK,CAAA;AAClD,EAAA,MAAM,EAAE,KAAK,SAAA,EAAU,GAAI,aAAa,EAAE,OAAA,EAAS,CAAC,UAAA,EAAY,CAAA;AAEhE,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,GAAA,EAAA,EAAK,GAAG,QAAA,EAAU,SAAA,EAAW,EAAA,CAAG,CAAC,UAAA,IAAc,YAAA,CAAa,KAAK,CAAA,EAAG,SAAS,CAAA,EAAG,KAC9E,QAAA,EACH,CAAA;AAAA,IAAA,CACE,aAAa,UAAA,qBACb,IAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,OAAA;AAAA,QACR,IAAA,EAAK,IAAA;AAAA,QACL,SAAA,EAAU,+CAAA;AAAA,QACV,eAAA,EAAe,UAAA;AAAA,QACf,OAAA,EAAS,MAAM,aAAA,CAAc,CAAA,CAAA,KAAK,CAAC,CAAC,CAAA;AAAA,QAEnC,QAAA,EAAA;AAAA,UAAA,UAAA,GAAa,aAAA,GAAgB,aAAA;AAAA,8BAC7B,WAAA,EAAA,EAAY,SAAA,EAAW,GAAG,sBAAA,EAAwB,UAAA,IAAc,YAAY,CAAA,EAAG;AAAA;AAAA;AAAA;AAClF,GAAA,EAEJ,CAAA;AAEJ;;;;"}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
- const Txt = require('../Txt-BT8_EkuZ.cjs');
6
+ const Txt = require('../Txt-I5lK8zy7.cjs');
7
7
  const icons_Icon = require('../icons/Icon.cjs.js');
8
8
  require('lucide-react');
9
9
  const utils_cn = require('../cn-vA5sWo9W.cjs');
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { T as Txt } from '../Txt-p33XTnu8.js';
2
+ import { T as Txt } from '../Txt-Df42XG5p.js';
3
3
  import { Icon } from '../icons/Icon.es.js';
4
4
  import 'lucide-react';
5
5
  import { c as cn } from '../cn-BakJzhuv.js';
@@ -4,7 +4,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
6
  const skeleton = require('../skeleton-C0zXt2_f.cjs');
7
- const Txt = require('../Txt-BT8_EkuZ.cjs');
7
+ const Txt = require('../Txt-I5lK8zy7.cjs');
8
8
  const icons_Icon = require('../icons/Icon.cjs.js');
9
9
  require('lucide-react');
10
10
 
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { S as Skeleton } from '../skeleton-C5-7V8Ni.js';
3
- import { T as Txt } from '../Txt-p33XTnu8.js';
3
+ import { T as Txt } from '../Txt-Df42XG5p.js';
4
4
  import { Icon } from '../icons/Icon.es.js';
5
5
  import 'lucide-react';
6
6
 
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
- const Txt = require('../Txt-BT8_EkuZ.cjs');
6
+ const Txt = require('../Txt-I5lK8zy7.cjs');
7
7
 
8
8
  const Entry = ({ label, children }) => {
9
9
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { T as Txt } from '../Txt-p33XTnu8.js';
2
+ import { T as Txt } from '../Txt-Df42XG5p.js';
3
3
 
4
4
  const Entry = ({ label, children }) => {
5
5
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
@@ -9,7 +9,7 @@ const React = require('react');
9
9
  const Button = require('../Button-B5RnKrGb.cjs');
10
10
  const Notice = require('../Notice-WhHdy89l.cjs');
11
11
  const envFile = require('../env-file-BU2hStkm.cjs');
12
- const Txt = require('../Txt-BT8_EkuZ.cjs');
12
+ const Txt = require('../Txt-I5lK8zy7.cjs');
13
13
  const dataList = require('../data-list-BwGRWaNd.cjs');
14
14
  const hooks_useCopyToClipboard = require('../hooks/use-copy-to-clipboard.cjs.js');
15
15
  const fieldBlock = require('../field-block-Dx-fDIP9.cjs');
@@ -5,7 +5,7 @@ import { createContext, use, useState, useMemo } from 'react';
5
5
  import { B as Button } from '../Button-Dh5CZ6Ir.js';
6
6
  import { N as Notice } from '../Notice-jmHNt3gS.js';
7
7
  import { D as DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE } from '../env-file-BITYowCx.js';
8
- import { T as Txt } from '../Txt-p33XTnu8.js';
8
+ import { T as Txt } from '../Txt-Df42XG5p.js';
9
9
  import { D as DataList } from '../data-list-By3xmGvT.js';
10
10
  import { useCopyToClipboard } from '../hooks/use-copy-to-clipboard.es.js';
11
11
  import { F as FieldBlock } from '../field-block-CC_EYPWe.js';
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
- const Txt = require('../Txt-BT8_EkuZ.cjs');
6
+ const Txt = require('../Txt-I5lK8zy7.cjs');
7
7
  const utils_cn = require('../cn-vA5sWo9W.cjs');
8
8
 
9
9
  const Header = ({ children, border = true, className }) => {
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { T as Txt } from '../Txt-p33XTnu8.js';
2
+ import { T as Txt } from '../Txt-Df42XG5p.js';
3
3
  import { c as cn } from '../cn-BakJzhuv.js';
4
4
 
5
5
  const Header = ({ children, border = true, className }) => {
@@ -6,7 +6,7 @@ const jsxRuntime = require('react/jsx-runtime');
6
6
  const React = require('react');
7
7
  const utils_cn = require('../cn-vA5sWo9W.cjs');
8
8
  const icons_Icon = require('../icons/Icon.cjs.js');
9
- const Txt = require('../Txt-BT8_EkuZ.cjs');
9
+ const Txt = require('../Txt-I5lK8zy7.cjs');
10
10
 
11
11
  const rowSize = {
12
12
  default: "[&>tbody>tr]:h-table-row",
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { forwardRef, useRef, useEffect, useState, useCallback } from 'react';
3
3
  import { c as cn } from '../cn-BakJzhuv.js';
4
4
  import { Icon } from '../icons/Icon.es.js';
5
- import { T as Txt } from '../Txt-p33XTnu8.js';
5
+ import { T as Txt } from '../Txt-Df42XG5p.js';
6
6
 
7
7
  const rowSize = {
8
8
  default: "[&>tbody>tr]:h-table-row",
@@ -6,7 +6,7 @@ const jsxRuntime = require('react/jsx-runtime');
6
6
  const lucideReact = require('lucide-react');
7
7
  const utils_cn = require('../cn-vA5sWo9W.cjs');
8
8
  const Button = require('../Button-B5RnKrGb.cjs');
9
- const Txt = require('../Txt-BT8_EkuZ.cjs');
9
+ const Txt = require('../Txt-I5lK8zy7.cjs');
10
10
 
11
11
  const ThreadList = ({ children, "aria-label": ariaLabel = "Threads", embedded = false }) => {
12
12
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils_cn.cn("size-full", !embedded && "pb-2 pl-2"), children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { X } from 'lucide-react';
3
3
  import { c as cn } from '../cn-BakJzhuv.js';
4
4
  import { B as Button } from '../Button-Dh5CZ6Ir.js';
5
- import { T as Txt } from '../Txt-p33XTnu8.js';
5
+ import { T as Txt } from '../Txt-Df42XG5p.js';
6
6
 
7
7
  const ThreadList = ({ children, "aria-label": ariaLabel = "Threads", embedded = false }) => {
8
8
  return /* @__PURE__ */ jsx("div", { className: cn("size-full", !embedded && "pb-2 pl-2"), children: /* @__PURE__ */ jsx(
@@ -5,7 +5,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
6
  const copyButton = require('../copy-button-DNR760m7.cjs');
7
7
  const tooltip = require('../tooltip-VGyJcEoE.cjs');
8
- const Txt = require('../Txt-BT8_EkuZ.cjs');
8
+ const Txt = require('../Txt-I5lK8zy7.cjs');
9
9
  const utils_cn = require('../cn-vA5sWo9W.cjs');
10
10
 
11
11
  function Truncate({
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { C as CopyButton } from '../copy-button-BuIpvUI7.js';
3
3
  import { T as Tooltip, c as TooltipTrigger, a as TooltipContent } from '../tooltip-Ch06EF_U.js';
4
- import { T as Txt } from '../Txt-p33XTnu8.js';
4
+ import { T as Txt } from '../Txt-Df42XG5p.js';
5
5
  import { c as cn } from '../cn-BakJzhuv.js';
6
6
 
7
7
  function Truncate({
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
- const Txt = require('../Txt-BT8_EkuZ.cjs');
5
+ const Txt = require('../Txt-I5lK8zy7.cjs');
6
6
 
7
7
 
8
8
 
@@ -1,2 +1,2 @@
1
- export { T as Txt } from '../Txt-p33XTnu8.js';
1
+ export { T as Txt } from '../Txt-Df42XG5p.js';
2
2
  //# sourceMappingURL=Txt.es.js.map
@@ -8,7 +8,7 @@ const React = require('react');
8
8
  const Badge = require('../../Badge-BlJpI3fY.cjs');
9
9
  const Button = require('../../Button-B5RnKrGb.cjs');
10
10
  const markdownRenderer = require('../../markdown-renderer-DRnUrMmF.cjs');
11
- const Txt = require('../../Txt-BT8_EkuZ.cjs');
11
+ const Txt = require('../../Txt-I5lK8zy7.cjs');
12
12
  const icons_Icon = require('../../icons/Icon.cjs.js');
13
13
  const hooks_useCopyToClipboard = require('../../hooks/use-copy-to-clipboard.cjs.js');
14
14
  const utils_cn = require('../../cn-vA5sWo9W.cjs');
@@ -4,7 +4,7 @@ import { useState, createContext, useContext } from 'react';
4
4
  import { B as Badge } from '../../Badge-woV6OD8k.js';
5
5
  import { B as Button } from '../../Button-Dh5CZ6Ir.js';
6
6
  import { M as MarkdownRenderer } from '../../markdown-renderer-ZzJcwfS3.js';
7
- import { T as Txt } from '../../Txt-p33XTnu8.js';
7
+ import { T as Txt } from '../../Txt-Df42XG5p.js';
8
8
  import { Icon } from '../../icons/Icon.es.js';
9
9
  import { useCopyToClipboard } from '../../hooks/use-copy-to-clipboard.es.js';
10
10
  import { c as cn } from '../../cn-BakJzhuv.js';
@@ -7,7 +7,7 @@ const lucideReact = require('lucide-react');
7
7
  const React = require('react');
8
8
  const Button = require('../../../Button-B5RnKrGb.cjs');
9
9
  const skeleton = require('../../../skeleton-C0zXt2_f.cjs');
10
- const Txt = require('../../../Txt-BT8_EkuZ.cjs');
10
+ const Txt = require('../../../Txt-I5lK8zy7.cjs');
11
11
  const icons_MemoryIcon = require('../../../icons/MemoryIcon.cjs.js');
12
12
  const domains_memory_lib_extractMarkers = require('../lib/extract-markers.cjs.js');
13
13
  const domains_memory_lib_replaySelection = require('../lib/replay-selection.cjs.js');
@@ -3,7 +3,7 @@ import { ArrowLeftIcon } from 'lucide-react';
3
3
  import { useState, useMemo, useEffect } from 'react';
4
4
  import { B as Button } from '../../../Button-Dh5CZ6Ir.js';
5
5
  import { S as Skeleton } from '../../../skeleton-C5-7V8Ni.js';
6
- import { T as Txt } from '../../../Txt-p33XTnu8.js';
6
+ import { T as Txt } from '../../../Txt-Df42XG5p.js';
7
7
  import { MemoryIcon } from '../../../icons/MemoryIcon.es.js';
8
8
  import { extractOmMarkers } from '../lib/extract-markers.es.js';
9
9
  import { getObservationTimestampMs, findRecordIdAtOrBefore } from '../lib/replay-selection.es.js';
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const React = require('react');
6
+ const hooks_useIsomorphicLayoutEffect = require('./use-isomorphic-layout-effect.cjs.js');
7
+
8
+ function useIsClamped({
9
+ enabled = true
10
+ } = {}) {
11
+ const [element, setElement] = React.useState(null);
12
+ const [isClamped, setIsClamped] = React.useState(false);
13
+ hooks_useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
14
+ if (!element || !enabled) return;
15
+ let stopped = false;
16
+ const measure = () => {
17
+ if (!stopped) setIsClamped(element.scrollHeight > element.clientHeight);
18
+ };
19
+ measure();
20
+ let observer;
21
+ if (typeof ResizeObserver !== "undefined") {
22
+ observer = new ResizeObserver(measure);
23
+ observer.observe(element);
24
+ }
25
+ document.fonts?.ready.then(measure).catch(() => {
26
+ });
27
+ return () => {
28
+ stopped = true;
29
+ observer?.disconnect();
30
+ };
31
+ }, [element, enabled]);
32
+ return { ref: setElement, isClamped };
33
+ }
34
+
35
+ exports.useIsClamped = useIsClamped;
36
+ //# sourceMappingURL=use-is-clamped.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-is-clamped.cjs.js","sources":["../../src/hooks/use-is-clamped.ts"],"sourcesContent":["import { useState } from 'react';\nimport type { RefCallback } from 'react';\n\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\nexport interface UseIsClampedOptions {\n /** Set false while the clamp is lifted (e.g. text expanded) to freeze the last measurement. */\n enabled?: boolean;\n}\n\nexport interface UseIsClampedResult<TElement extends HTMLElement> {\n ref: RefCallback<TElement>;\n isClamped: boolean;\n}\n\n/** Measures whether a clamped element has content cut off (`scrollHeight > clientHeight`), re-measuring on resize and font load. */\nexport function useIsClamped<TElement extends HTMLElement = HTMLElement>({\n enabled = true,\n}: UseIsClampedOptions = {}): UseIsClampedResult<TElement> {\n const [element, setElement] = useState<TElement | null>(null);\n const [isClamped, setIsClamped] = useState(false);\n\n useIsomorphicLayoutEffect(() => {\n if (!element || !enabled) return;\n\n let stopped = false;\n const measure = () => {\n if (!stopped) setIsClamped(element.scrollHeight > element.clientHeight);\n };\n\n measure();\n\n let observer: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(measure);\n observer.observe(element);\n }\n // Font swaps change line wrapping without resizing the observed box.\n document.fonts?.ready.then(measure).catch(() => {});\n\n return () => {\n stopped = true;\n observer?.disconnect();\n };\n }, [element, enabled]);\n\n return { ref: setElement, isClamped };\n}\n"],"names":["useState","useIsomorphicLayoutEffect"],"mappings":";;;;;;;AAgBO,SAAS,YAAA,CAAyD;AAAA,EACvE,OAAA,GAAU;AACZ,CAAA,GAAyB,EAAC,EAAiC;AACzD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAA0B,IAAI,CAAA;AAC5D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,eAAS,KAAK,CAAA;AAEhD,EAAAC,yDAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,EAAS;AAE1B,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,IAAI,CAAC,OAAA,EAAS,YAAA,CAAa,OAAA,CAAQ,YAAA,GAAe,QAAQ,YAAY,CAAA;AAAA,IACxE,CAAA;AAEA,IAAA,OAAA,EAAQ;AAER,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AACzC,MAAA,QAAA,GAAW,IAAI,eAAe,OAAO,CAAA;AACrC,MAAA,QAAA,CAAS,QAAQ,OAAO,CAAA;AAAA,IAC1B;AAEA,IAAA,QAAA,CAAS,OAAO,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA,CAAE,MAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAElD,IAAA,OAAO,MAAM;AACX,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,QAAA,EAAU,UAAA,EAAW;AAAA,IACvB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,OAAO,CAAC,CAAA;AAErB,EAAA,OAAO,EAAE,GAAA,EAAK,UAAA,EAAY,SAAA,EAAU;AACtC;;;;"}
@@ -0,0 +1,2 @@
1
+ export * from '../src/hooks/use-is-clamped'
2
+ export {}
@@ -0,0 +1,32 @@
1
+ import { useState } from 'react';
2
+ import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.es.js';
3
+
4
+ function useIsClamped({
5
+ enabled = true
6
+ } = {}) {
7
+ const [element, setElement] = useState(null);
8
+ const [isClamped, setIsClamped] = useState(false);
9
+ useIsomorphicLayoutEffect(() => {
10
+ if (!element || !enabled) return;
11
+ let stopped = false;
12
+ const measure = () => {
13
+ if (!stopped) setIsClamped(element.scrollHeight > element.clientHeight);
14
+ };
15
+ measure();
16
+ let observer;
17
+ if (typeof ResizeObserver !== "undefined") {
18
+ observer = new ResizeObserver(measure);
19
+ observer.observe(element);
20
+ }
21
+ document.fonts?.ready.then(measure).catch(() => {
22
+ });
23
+ return () => {
24
+ stopped = true;
25
+ observer?.disconnect();
26
+ };
27
+ }, [element, enabled]);
28
+ return { ref: setElement, isClamped };
29
+ }
30
+
31
+ export { useIsClamped };
32
+ //# sourceMappingURL=use-is-clamped.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-is-clamped.es.js","sources":["../../src/hooks/use-is-clamped.ts"],"sourcesContent":["import { useState } from 'react';\nimport type { RefCallback } from 'react';\n\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\nexport interface UseIsClampedOptions {\n /** Set false while the clamp is lifted (e.g. text expanded) to freeze the last measurement. */\n enabled?: boolean;\n}\n\nexport interface UseIsClampedResult<TElement extends HTMLElement> {\n ref: RefCallback<TElement>;\n isClamped: boolean;\n}\n\n/** Measures whether a clamped element has content cut off (`scrollHeight > clientHeight`), re-measuring on resize and font load. */\nexport function useIsClamped<TElement extends HTMLElement = HTMLElement>({\n enabled = true,\n}: UseIsClampedOptions = {}): UseIsClampedResult<TElement> {\n const [element, setElement] = useState<TElement | null>(null);\n const [isClamped, setIsClamped] = useState(false);\n\n useIsomorphicLayoutEffect(() => {\n if (!element || !enabled) return;\n\n let stopped = false;\n const measure = () => {\n if (!stopped) setIsClamped(element.scrollHeight > element.clientHeight);\n };\n\n measure();\n\n let observer: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(measure);\n observer.observe(element);\n }\n // Font swaps change line wrapping without resizing the observed box.\n document.fonts?.ready.then(measure).catch(() => {});\n\n return () => {\n stopped = true;\n observer?.disconnect();\n };\n }, [element, enabled]);\n\n return { ref: setElement, isClamped };\n}\n"],"names":[],"mappings":";;;AAgBO,SAAS,YAAA,CAAyD;AAAA,EACvE,OAAA,GAAU;AACZ,CAAA,GAAyB,EAAC,EAAiC;AACzD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA0B,IAAI,CAAA;AAC5D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAEhD,EAAA,yBAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,EAAS;AAE1B,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,IAAI,CAAC,OAAA,EAAS,YAAA,CAAa,OAAA,CAAQ,YAAA,GAAe,QAAQ,YAAY,CAAA;AAAA,IACxE,CAAA;AAEA,IAAA,OAAA,EAAQ;AAER,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AACzC,MAAA,QAAA,GAAW,IAAI,eAAe,OAAO,CAAA;AACrC,MAAA,QAAA,CAAS,QAAQ,OAAO,CAAA;AAAA,IAC1B;AAEA,IAAA,QAAA,CAAS,OAAO,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA,CAAE,MAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAElD,IAAA,OAAO,MAAM;AACX,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,QAAA,EAAU,UAAA,EAAW;AAAA,IACvB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,OAAO,CAAC,CAAA;AAErB,EAAA,OAAO,EAAE,GAAA,EAAK,UAAA,EAAY,SAAA,EAAU;AACtC;;;;"}
@@ -0,0 +1,20 @@
1
+ import { TxtProps } from '../Txt';
2
+ declare const clampClasses: {
3
+ readonly 1: "line-clamp-1";
4
+ readonly 2: "line-clamp-2";
5
+ readonly 3: "line-clamp-3";
6
+ readonly 4: "line-clamp-4";
7
+ readonly 5: "line-clamp-5";
8
+ readonly 6: "line-clamp-6";
9
+ };
10
+ export type ClampedTextLines = keyof typeof clampClasses;
11
+ export interface ClampedTextProps extends Omit<TxtProps, 'children' | 'ref'> {
12
+ children: string;
13
+ /** Number of lines to clamp to (default: 2) */
14
+ lines?: ClampedTextLines;
15
+ readMoreLabel?: string;
16
+ showLessLabel?: string;
17
+ }
18
+ /** Text clamped to a number of lines, with a "read more" toggle shown only when the clamp actually cuts content. */
19
+ export declare function ClampedText({ children, lines, readMoreLabel, showLessLabel, className, ...txtProps }: ClampedTextProps): import("react").JSX.Element;
20
+ export {};
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ClampedText } from './clamped-text';
3
+ declare const meta: Meta<typeof ClampedText>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ClampedText>;
6
+ export declare const Default: Story;
7
+ export declare const ShortTextNoToggle: Story;
8
+ export declare const ThreeLines: Story;
@@ -0,0 +1 @@
1
+ export * from './clamped-text';
@@ -1,7 +1,8 @@
1
- import { HTMLAttributes, ReactNode } from 'react';
1
+ import { HTMLAttributes, ReactNode, Ref } from 'react';
2
2
  import { FontSizes } from '../../tokens';
3
3
  export interface TxtProps extends HTMLAttributes<HTMLDivElement | HTMLLabelElement> {
4
4
  as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div';
5
+ ref?: Ref<HTMLElement>;
5
6
  variant?: keyof typeof FontSizes;
6
7
  font?: 'mono';
7
8
  htmlFor?: string;
@@ -9,4 +10,4 @@ export interface TxtProps extends HTMLAttributes<HTMLDivElement | HTMLLabelEleme
9
10
  title?: string;
10
11
  children?: ReactNode;
11
12
  }
12
- export declare const Txt: ({ as: Root, className, variant, font, ...props }: TxtProps) => import("react").JSX.Element;
13
+ export declare const Txt: ({ as: Root, className, variant, font, ref, ...props }: TxtProps) => import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { RefCallback } from 'react';
2
+ export interface UseIsClampedOptions {
3
+ /** Set false while the clamp is lifted (e.g. text expanded) to freeze the last measurement. */
4
+ enabled?: boolean;
5
+ }
6
+ export interface UseIsClampedResult<TElement extends HTMLElement> {
7
+ ref: RefCallback<TElement>;
8
+ isClamped: boolean;
9
+ }
10
+ /** Measures whether a clamped element has content cut off (`scrollHeight > clientHeight`), re-measuring on resize and font load. */
11
+ export declare function useIsClamped<TElement extends HTMLElement = HTMLElement>({ enabled, }?: UseIsClampedOptions): UseIsClampedResult<TElement>;
package/dist/style.css CHANGED
@@ -1162,6 +1162,10 @@
1162
1162
  margin-left: calc(var(--spacing-5) * -1);
1163
1163
  }
1164
1164
 
1165
+ .-ml-\[\.8em\] {
1166
+ margin-left: -.8em;
1167
+ }
1168
+
1165
1169
  .-ml-px {
1166
1170
  margin-left: -1px;
1167
1171
  margin-left: calc(var(--spacing-px) * -1);
@@ -1203,6 +1207,13 @@
1203
1207
  box-sizing: border-box;
1204
1208
  }
1205
1209
 
1210
+ .line-clamp-1 {
1211
+ -webkit-line-clamp: 1;
1212
+ -webkit-box-orient: vertical;
1213
+ display: -webkit-box;
1214
+ overflow: hidden;
1215
+ }
1216
+
1206
1217
  .line-clamp-2 {
1207
1218
  -webkit-line-clamp: 2;
1208
1219
  -webkit-box-orient: vertical;
@@ -1217,6 +1228,27 @@
1217
1228
  overflow: hidden;
1218
1229
  }
1219
1230
 
1231
+ .line-clamp-4 {
1232
+ -webkit-line-clamp: 4;
1233
+ -webkit-box-orient: vertical;
1234
+ display: -webkit-box;
1235
+ overflow: hidden;
1236
+ }
1237
+
1238
+ .line-clamp-5 {
1239
+ -webkit-line-clamp: 5;
1240
+ -webkit-box-orient: vertical;
1241
+ display: -webkit-box;
1242
+ overflow: hidden;
1243
+ }
1244
+
1245
+ .line-clamp-6 {
1246
+ -webkit-line-clamp: 6;
1247
+ -webkit-box-orient: vertical;
1248
+ display: -webkit-box;
1249
+ overflow: hidden;
1250
+ }
1251
+
1220
1252
  .block {
1221
1253
  display: block;
1222
1254
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mastra/playground-ui",
3
3
  "type": "module",
4
- "version": "41.1.0-alpha.2",
4
+ "version": "42.0.0-alpha.4",
5
5
  "description": "Mastra Playground components",
6
6
  "sideEffects": [
7
7
  "**/*.css"
@@ -150,8 +150,8 @@
150
150
  "react": ">=19.0.0",
151
151
  "react-dom": ">=19.0.0",
152
152
  "tailwindcss": "^4.0.0",
153
- "@mastra/client-js": "^1.32.1-alpha.2",
154
- "@mastra/react": "1.2.6-alpha.2"
153
+ "@mastra/client-js": "^1.33.0-alpha.4",
154
+ "@mastra/react": "1.2.6-alpha.4"
155
155
  },
156
156
  "devDependencies": {
157
157
  "@storybook/addon-a11y": "^10.4.1",
@@ -190,10 +190,10 @@
190
190
  "vite-plugin-lib-inject-css": "^2.2.2",
191
191
  "vitest": "4.1.10",
192
192
  "@internal/lint": "0.0.114",
193
- "@mastra/core": "1.52.0-alpha.2",
194
- "@mastra/client-js": "^1.32.1-alpha.2",
193
+ "@mastra/client-js": "^1.33.0-alpha.4",
195
194
  "@mastra/memory": "1.23.1-alpha.0",
196
- "@mastra/react": "1.2.6-alpha.2"
195
+ "@mastra/core": "1.52.0-alpha.4",
196
+ "@mastra/react": "1.2.6-alpha.4"
197
197
  },
198
198
  "homepage": "https://mastra.ai",
199
199
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"Txt-BT8_EkuZ.cjs","sources":["../src/ds/components/Txt/Txt.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react';\n\nimport type { FontSizes } from '../../tokens';\nimport { cn } from '@/lib/utils';\n\nexport interface TxtProps extends HTMLAttributes<HTMLDivElement | HTMLLabelElement> {\n as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div';\n variant?: keyof typeof FontSizes;\n font?: 'mono';\n htmlFor?: string;\n className?: string;\n title?: string;\n children?: ReactNode;\n}\n\nconst variants = {\n // UI text sizes\n 'ui-xs': 'text-ui-xs leading-ui-xs',\n 'ui-sm': 'text-ui-sm leading-ui-sm',\n 'ui-smd': 'text-ui-smd leading-ui-smd',\n 'ui-md': 'text-ui-md leading-ui-md',\n 'ui-lg': 'text-ui-lg leading-ui-lg',\n // Header sizes\n 'header-xs': 'text-header-xs leading-header-xs',\n 'header-sm': 'text-header-sm leading-header-sm',\n 'header-md': 'text-header-md leading-header-md',\n 'header-lg': 'text-header-lg leading-header-lg',\n 'header-xl': 'text-header-xl leading-header-xl',\n};\n\nconst fonts = {\n mono: 'font-mono',\n};\n\nexport const Txt = ({ as: Root = 'p', className, variant = 'ui-md', font, ...props }: TxtProps) => {\n return <Root className={cn(variants[variant], font && fonts[font], className)} {...props} />;\n};\n"],"names":["jsx","cn"],"mappings":";;;;;AAeA,MAAM,QAAA,GAAW;AAAA;AAAA,EAEf,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA,EACT,QAAA,EAAU,4BAAA;AAAA,EACV,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA;AAAA,EAET,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa;AACf,CAAA;AAEA,MAAM,KAAA,GAAQ;AAAA,EACZ,IAAA,EAAM;AACR,CAAA;AAEO,MAAM,GAAA,GAAM,CAAC,EAAE,EAAA,EAAI,IAAA,GAAO,GAAA,EAAK,SAAA,EAAW,OAAA,GAAU,OAAA,EAAS,IAAA,EAAM,GAAG,KAAA,EAAM,KAAgB;AACjG,EAAA,uBAAOA,cAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAWC,WAAA,CAAG,SAAS,OAAO,CAAA,EAAG,IAAA,IAAQ,KAAA,CAAM,IAAI,CAAA,EAAG,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC5F;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Txt-p33XTnu8.js","sources":["../src/ds/components/Txt/Txt.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react';\n\nimport type { FontSizes } from '../../tokens';\nimport { cn } from '@/lib/utils';\n\nexport interface TxtProps extends HTMLAttributes<HTMLDivElement | HTMLLabelElement> {\n as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div';\n variant?: keyof typeof FontSizes;\n font?: 'mono';\n htmlFor?: string;\n className?: string;\n title?: string;\n children?: ReactNode;\n}\n\nconst variants = {\n // UI text sizes\n 'ui-xs': 'text-ui-xs leading-ui-xs',\n 'ui-sm': 'text-ui-sm leading-ui-sm',\n 'ui-smd': 'text-ui-smd leading-ui-smd',\n 'ui-md': 'text-ui-md leading-ui-md',\n 'ui-lg': 'text-ui-lg leading-ui-lg',\n // Header sizes\n 'header-xs': 'text-header-xs leading-header-xs',\n 'header-sm': 'text-header-sm leading-header-sm',\n 'header-md': 'text-header-md leading-header-md',\n 'header-lg': 'text-header-lg leading-header-lg',\n 'header-xl': 'text-header-xl leading-header-xl',\n};\n\nconst fonts = {\n mono: 'font-mono',\n};\n\nexport const Txt = ({ as: Root = 'p', className, variant = 'ui-md', font, ...props }: TxtProps) => {\n return <Root className={cn(variants[variant], font && fonts[font], className)} {...props} />;\n};\n"],"names":[],"mappings":";;;AAeA,MAAM,QAAA,GAAW;AAAA;AAAA,EAEf,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA,EACT,QAAA,EAAU,4BAAA;AAAA,EACV,OAAA,EAAS,0BAAA;AAAA,EACT,OAAA,EAAS,0BAAA;AAAA;AAAA,EAET,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa;AACf,CAAA;AAEA,MAAM,KAAA,GAAQ;AAAA,EACZ,IAAA,EAAM;AACR,CAAA;AAEO,MAAM,GAAA,GAAM,CAAC,EAAE,EAAA,EAAI,IAAA,GAAO,GAAA,EAAK,SAAA,EAAW,OAAA,GAAU,OAAA,EAAS,IAAA,EAAM,GAAG,KAAA,EAAM,KAAgB;AACjG,EAAA,uBAAO,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAW,EAAA,CAAG,SAAS,OAAO,CAAA,EAAG,IAAA,IAAQ,KAAA,CAAM,IAAI,CAAA,EAAG,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC5F;;;;"}