@flamingo-stack/openframe-frontend-core 0.0.484 → 0.0.485

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 (99) hide show
  1. package/dist/{chunk-DVYTY2GK.js → chunk-2BMYCH3D.js} +2 -2
  2. package/dist/{chunk-F3UM6ZPO.js → chunk-5WZGZ6B2.js} +4 -4
  3. package/dist/{chunk-KN7MUVNB.js → chunk-6QJ2QRQE.js} +4 -4
  4. package/dist/{chunk-IB4YMANE.cjs → chunk-BVHMDHQY.cjs} +31 -31
  5. package/dist/{chunk-IB4YMANE.cjs.map → chunk-BVHMDHQY.cjs.map} +1 -1
  6. package/dist/{chunk-SGUKPH7Z.cjs → chunk-C4QKMOQK.cjs} +7 -7
  7. package/dist/{chunk-SGUKPH7Z.cjs.map → chunk-C4QKMOQK.cjs.map} +1 -1
  8. package/dist/{chunk-ZQXNTV66.cjs → chunk-F3BH5B7M.cjs} +11 -11
  9. package/dist/{chunk-ZQXNTV66.cjs.map → chunk-F3BH5B7M.cjs.map} +1 -1
  10. package/dist/{chunk-V36NQIAO.cjs → chunk-HQITFULS.cjs} +9 -9
  11. package/dist/{chunk-V36NQIAO.cjs.map → chunk-HQITFULS.cjs.map} +1 -1
  12. package/dist/{chunk-3MTN5XHZ.js → chunk-J5KX5K5T.js} +2 -2
  13. package/dist/{chunk-KHDLNPAM.js → chunk-KA4ILVCX.js} +2 -2
  14. package/dist/{chunk-OTJMDYZ5.js → chunk-KRKNTK5Y.js} +2 -2
  15. package/dist/{chunk-HSVQ7W5M.js → chunk-MD5E5BKO.js} +4 -4
  16. package/dist/{chunk-CGEC7YIX.cjs → chunk-NTHTORJ3.cjs} +59 -59
  17. package/dist/{chunk-CGEC7YIX.cjs.map → chunk-NTHTORJ3.cjs.map} +1 -1
  18. package/dist/{chunk-UFPGVLHZ.cjs → chunk-NZYUSFMI.cjs} +72 -19
  19. package/dist/chunk-NZYUSFMI.cjs.map +1 -0
  20. package/dist/{chunk-X3KQ3B35.js → chunk-OW33LYO2.js} +4 -4
  21. package/dist/{chunk-AVN6WGIV.cjs → chunk-PHYEC3EG.cjs} +26 -26
  22. package/dist/{chunk-AVN6WGIV.cjs.map → chunk-PHYEC3EG.cjs.map} +1 -1
  23. package/dist/{chunk-HTHR7KWB.cjs → chunk-RB6HX4KC.cjs} +26 -26
  24. package/dist/{chunk-HTHR7KWB.cjs.map → chunk-RB6HX4KC.cjs.map} +1 -1
  25. package/dist/{chunk-SSMNDK3Z.js → chunk-REURWDBC.js} +1659 -1606
  26. package/dist/chunk-REURWDBC.js.map +1 -0
  27. package/dist/{chunk-P3Q7KMPQ.cjs → chunk-RILRBUDL.cjs} +62 -62
  28. package/dist/{chunk-P3Q7KMPQ.cjs.map → chunk-RILRBUDL.cjs.map} +1 -1
  29. package/dist/{chunk-3VZJHXPE.cjs → chunk-RR6XPEEG.cjs} +15 -15
  30. package/dist/{chunk-3VZJHXPE.cjs.map → chunk-RR6XPEEG.cjs.map} +1 -1
  31. package/dist/{chunk-YSA7KE2P.cjs → chunk-XJ6VL3AP.cjs} +37 -37
  32. package/dist/{chunk-YSA7KE2P.cjs.map → chunk-XJ6VL3AP.cjs.map} +1 -1
  33. package/dist/{chunk-G2L46BDG.js → chunk-XOIKQW2H.js} +2 -2
  34. package/dist/{chunk-WZ3YAIQQ.js → chunk-Z3V42W7P.js} +2 -2
  35. package/dist/{chunk-4PVTF36S.js → chunk-ZTRRB2Y2.js} +2 -2
  36. package/dist/{chunk-SAVJIRUK.cjs → chunk-ZVFH4LTD.cjs} +5 -5
  37. package/dist/{chunk-SAVJIRUK.cjs.map → chunk-ZVFH4LTD.cjs.map} +1 -1
  38. package/dist/components/case-studies/index.cjs +8 -8
  39. package/dist/components/case-studies/index.js +2 -2
  40. package/dist/components/chat/index.cjs +2 -2
  41. package/dist/components/chat/index.js +1 -1
  42. package/dist/components/contact/index.cjs +3 -3
  43. package/dist/components/contact/index.js +2 -2
  44. package/dist/components/docs/index.cjs +5 -5
  45. package/dist/components/docs/index.js +4 -4
  46. package/dist/components/embeds/index.cjs +3 -3
  47. package/dist/components/embeds/index.js +2 -2
  48. package/dist/components/faq/index.cjs +3 -3
  49. package/dist/components/faq/index.js +2 -2
  50. package/dist/components/features/index.cjs +2 -2
  51. package/dist/components/features/index.js +1 -1
  52. package/dist/components/help-center-pages/index.cjs +20 -20
  53. package/dist/components/help-center-pages/index.js +11 -11
  54. package/dist/components/index.cjs +132 -130
  55. package/dist/components/index.cjs.map +1 -1
  56. package/dist/components/index.js +11 -9
  57. package/dist/components/index.js.map +1 -1
  58. package/dist/components/navigation/index.cjs +2 -2
  59. package/dist/components/navigation/index.js +1 -1
  60. package/dist/components/onboarding-guides/index.cjs +5 -5
  61. package/dist/components/onboarding-guides/index.js +4 -4
  62. package/dist/components/related-content/index.cjs +3 -3
  63. package/dist/components/related-content/index.js +2 -2
  64. package/dist/components/tickets/index.cjs +5 -5
  65. package/dist/components/tickets/index.js +4 -4
  66. package/dist/components/ui/data-table/data-table-skeleton.d.ts +7 -3
  67. package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
  68. package/dist/components/ui/index.cjs +4 -2
  69. package/dist/components/ui/index.cjs.map +1 -1
  70. package/dist/components/ui/index.js +3 -1
  71. package/dist/components/ui/scroll-fade.d.ts +47 -7
  72. package/dist/components/ui/scroll-fade.d.ts.map +1 -1
  73. package/dist/components/ui/table/table-skeleton.d.ts +2 -2
  74. package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
  75. package/dist/index.cjs +4 -2
  76. package/dist/index.cjs.map +1 -1
  77. package/dist/index.js +3 -1
  78. package/package.json +1 -1
  79. package/src/components/ui/data-table/.data-table-skeleton.md +2 -2
  80. package/src/components/ui/data-table/data-table-header.tsx +3 -1
  81. package/src/components/ui/data-table/data-table-row.tsx +1 -1
  82. package/src/components/ui/data-table/data-table-skeleton.tsx +7 -3
  83. package/src/components/ui/scroll-fade.tsx +115 -16
  84. package/src/components/ui/table/.table-skeleton.md +2 -2
  85. package/src/components/ui/table/table-skeleton.tsx +4 -2
  86. package/src/stories/ScrollShadow.stories.tsx +132 -0
  87. package/dist/chunk-SSMNDK3Z.js.map +0 -1
  88. package/dist/chunk-UFPGVLHZ.cjs.map +0 -1
  89. /package/dist/{chunk-DVYTY2GK.js.map → chunk-2BMYCH3D.js.map} +0 -0
  90. /package/dist/{chunk-F3UM6ZPO.js.map → chunk-5WZGZ6B2.js.map} +0 -0
  91. /package/dist/{chunk-KN7MUVNB.js.map → chunk-6QJ2QRQE.js.map} +0 -0
  92. /package/dist/{chunk-3MTN5XHZ.js.map → chunk-J5KX5K5T.js.map} +0 -0
  93. /package/dist/{chunk-KHDLNPAM.js.map → chunk-KA4ILVCX.js.map} +0 -0
  94. /package/dist/{chunk-OTJMDYZ5.js.map → chunk-KRKNTK5Y.js.map} +0 -0
  95. /package/dist/{chunk-HSVQ7W5M.js.map → chunk-MD5E5BKO.js.map} +0 -0
  96. /package/dist/{chunk-X3KQ3B35.js.map → chunk-OW33LYO2.js.map} +0 -0
  97. /package/dist/{chunk-G2L46BDG.js.map → chunk-XOIKQW2H.js.map} +0 -0
  98. /package/dist/{chunk-WZ3YAIQQ.js.map → chunk-Z3V42W7P.js.map} +0 -0
  99. /package/dist/{chunk-4PVTF36S.js.map → chunk-ZTRRB2Y2.js.map} +0 -0
@@ -3,27 +3,39 @@
3
3
  import * as React from 'react'
4
4
  import { cn } from '../../utils/cn'
5
5
 
6
+ export type ScrollFadeAxis = 'vertical' | 'horizontal' | 'both'
7
+
6
8
  /**
7
- * Tracks whether a scrollable element is scrolled away from its top/bottom
8
- * edge, so edge fades ("scroll shadows") can be shown only where content
9
- * continues. Attach `scrollRef` to the scrollable element and `update` to its
9
+ * Tracks whether a scrollable element is scrolled away from its edges, so
10
+ * edge fades ("scroll shadows") can be shown only where content continues.
11
+ * Attach `scrollRef` to the scrollable element and `update` to its
10
12
  * `onScroll`; content/size changes are re-measured via ResizeObserver.
11
13
  *
12
- * Canonical fade pattern shared by chat lists and FilterModal.
14
+ * `axis` picks which edges are tracked: 'vertical' (top/bottom — default,
15
+ * matches the original hook), 'horizontal' (left/right), or 'both'.
16
+ *
17
+ * Canonical fade pattern shared by chat lists, FilterModal, and the
18
+ * `ScrollShadow` wrapper below.
13
19
  */
14
- export function useScrollFade<T extends HTMLElement = HTMLDivElement>() {
20
+ export function useScrollFade<T extends HTMLElement = HTMLDivElement>(axis: ScrollFadeAxis = 'vertical') {
15
21
  const scrollRef = React.useRef<T | null>(null)
16
- const [fade, setFade] = React.useState({ top: false, bottom: false })
22
+ const [fade, setFade] = React.useState({ top: false, bottom: false, left: false, right: false })
17
23
 
18
24
  const update = React.useCallback(() => {
19
25
  const el = scrollRef.current
20
26
  if (!el) return
21
27
  setFade((prev) => {
22
- const top = el.scrollTop > 0
23
- const bottom = el.scrollHeight - el.scrollTop - el.clientHeight > 1
24
- return prev.top === top && prev.bottom === bottom ? prev : { top, bottom }
28
+ const vertical = axis !== 'horizontal'
29
+ const horizontal = axis !== 'vertical'
30
+ const top = vertical && el.scrollTop > 0
31
+ const bottom = vertical && el.scrollHeight - el.scrollTop - el.clientHeight > 1
32
+ const left = horizontal && el.scrollLeft > 0
33
+ const right = horizontal && el.scrollWidth - el.scrollLeft - el.clientWidth > 1
34
+ return prev.top === top && prev.bottom === bottom && prev.left === left && prev.right === right
35
+ ? prev
36
+ : { top, bottom, left, right }
25
37
  })
26
- }, [])
38
+ }, [axis])
27
39
 
28
40
  React.useLayoutEffect(() => {
29
41
  update()
@@ -47,25 +59,44 @@ export function useScrollFade<T extends HTMLElement = HTMLDivElement>() {
47
59
  }
48
60
  }, [update])
49
61
 
50
- return { scrollRef, fadeTop: fade.top, fadeBottom: fade.bottom, update }
62
+ return {
63
+ scrollRef,
64
+ fadeTop: fade.top,
65
+ fadeBottom: fade.bottom,
66
+ fadeLeft: fade.left,
67
+ fadeRight: fade.right,
68
+ update,
69
+ }
51
70
  }
52
71
 
72
+ export type ScrollFadeEdge = 'top' | 'bottom' | 'left' | 'right'
73
+
53
74
  export interface ScrollFadeOverlayProps {
54
75
  /** Which edge of the scroll container the fade sits on. */
55
- edge: 'top' | 'bottom'
76
+ edge: ScrollFadeEdge
56
77
  /** Whether the fade is currently shown (content continues past this edge). */
57
78
  visible: boolean
58
79
  /**
59
80
  * CSS color the content fades into — should match the surface behind the
60
- * list. Defaults to the page background token.
81
+ * list. Defaults to the page background token, which is theme-aware (the
82
+ * light theme redefines the token, so the fade flips with it).
61
83
  */
62
84
  color?: string
63
85
  className?: string
64
86
  }
65
87
 
88
+ const EDGE_GRADIENT_ANGLE: Record<ScrollFadeEdge, string> = {
89
+ top: '0deg',
90
+ bottom: '180deg',
91
+ left: '270deg',
92
+ right: '90deg',
93
+ }
94
+
66
95
  /**
67
96
  * Edge overlay that fades scrollable content into the surface behind it.
68
97
  * Render inside a `relative` wrapper around the scrollable element.
98
+ * Vertical fades are 64px tall (legacy chat/FilterModal size); horizontal
99
+ * fades are 40px wide per the ODS "scroll gradient" spec.
69
100
  */
70
101
  export function ScrollFadeOverlay({
71
102
  edge,
@@ -73,18 +104,86 @@ export function ScrollFadeOverlay({
73
104
  color = 'var(--color-bg)',
74
105
  className,
75
106
  }: ScrollFadeOverlayProps) {
107
+ const isVertical = edge === 'top' || edge === 'bottom'
76
108
  return (
77
109
  <div
78
110
  aria-hidden
79
111
  className={cn(
80
- 'pointer-events-none absolute inset-x-0 h-16 transition-opacity duration-150',
81
- edge === 'top' ? 'top-0' : 'bottom-0',
112
+ 'pointer-events-none absolute transition-opacity duration-150',
113
+ isVertical ? 'inset-x-0 h-16' : 'inset-y-0 w-10',
114
+ edge === 'top' && 'top-0',
115
+ edge === 'bottom' && 'bottom-0',
116
+ edge === 'left' && 'left-0',
117
+ edge === 'right' && 'right-0',
82
118
  visible ? 'opacity-100' : 'opacity-0',
83
119
  className,
84
120
  )}
85
121
  style={{
86
- background: `linear-gradient(${edge === 'top' ? '0deg' : '180deg'}, transparent 0%, ${color} 100%)`,
122
+ background: `linear-gradient(${EDGE_GRADIENT_ANGLE[edge]}, transparent 0%, ${color} 100%)`,
87
123
  }}
88
124
  />
89
125
  )
90
126
  }
127
+
128
+ export interface ScrollShadowProps extends React.HTMLAttributes<HTMLDivElement> {
129
+ /** Which scroll direction(s) to track and fade. */
130
+ axis?: ScrollFadeAxis
131
+ /**
132
+ * CSS color of the fade — the surface the content visually sits on.
133
+ * Defaults to the page background token; pass e.g. `var(--color-bg-card)`
134
+ * when the scrollable sits on a card. Tokens are theme-aware, so the same
135
+ * component works in light and dark themes (dark by default).
136
+ */
137
+ color?: string
138
+ /** Classes for the inner scrollable element (heights, paddings, etc.). */
139
+ scrollClassName?: string
140
+ /** Extra classes for every fade overlay (e.g. custom size). */
141
+ overlayClassName?: string
142
+ children: React.ReactNode
143
+ }
144
+
145
+ /**
146
+ * Drop-in wrapper that makes its child scrollable and fades the edges where
147
+ * more content exists — right/left for horizontal overflow (wide tables),
148
+ * top/bottom for vertical (long lists). Fades appear and hide automatically
149
+ * as the user scrolls or the content resizes.
150
+ *
151
+ * ```tsx
152
+ * <ScrollShadow axis="horizontal">
153
+ * <WideTable />
154
+ * </ScrollShadow>
155
+ * ```
156
+ */
157
+ export function ScrollShadow({
158
+ axis = 'vertical',
159
+ color = 'var(--color-bg)',
160
+ className,
161
+ scrollClassName,
162
+ overlayClassName,
163
+ children,
164
+ ...rest
165
+ }: ScrollShadowProps) {
166
+ const { scrollRef, fadeTop, fadeBottom, fadeLeft, fadeRight, update } = useScrollFade<HTMLDivElement>(axis)
167
+ const vertical = axis !== 'horizontal'
168
+ const horizontal = axis !== 'vertical'
169
+
170
+ return (
171
+ <div className={cn('relative', className)} {...rest}>
172
+ <div
173
+ ref={scrollRef}
174
+ onScroll={update}
175
+ className={cn(horizontal && 'overflow-x-auto', vertical && 'overflow-y-auto', scrollClassName)}
176
+ >
177
+ {children}
178
+ </div>
179
+ {vertical && <ScrollFadeOverlay edge="top" visible={fadeTop} color={color} className={overlayClassName} />}
180
+ {vertical && (
181
+ <ScrollFadeOverlay edge="bottom" visible={fadeBottom} color={color} className={overlayClassName} />
182
+ )}
183
+ {horizontal && <ScrollFadeOverlay edge="left" visible={fadeLeft} color={color} className={overlayClassName} />}
184
+ {horizontal && (
185
+ <ScrollFadeOverlay edge="right" visible={fadeRight} color={color} className={overlayClassName} />
186
+ )}
187
+ </div>
188
+ )
189
+ }
@@ -18,8 +18,8 @@ Main export. Renders `n` skeleton card rows with responsive desktop (`md:flex`)
18
18
  | `rowClassName` | `string` | — | Extra classes on the inner flex row |
19
19
 
20
20
  ### Constants *(deprecated)*
21
- - `ROW_HEIGHT_DESKTOP` — `'h-[68px] md:h-[80px]'`
22
- - `ROW_HEIGHT_MOBILE` — `'h-[68px]'`
21
+ - `ROW_HEIGHT_DESKTOP` — `'h-[66px] md:h-[78px]'` (inner; +2px border = 68/80 total)
22
+ - `ROW_HEIGHT_MOBILE` — `'h-[66px]'` (inner)
23
23
 
24
24
  **Primary column detection:** The component automatically identifies the widest (`flex-1` or `flex-[n]`) column as "primary" and renders a second skeleton line on every other row to simulate multi-line content.
25
25
 
@@ -4,8 +4,10 @@ import React from 'react'
4
4
  import { cn } from '../../../utils/cn'
5
5
  import type { TableCardSkeletonProps } from './types'
6
6
 
7
- const ROW_HEIGHT_DESKTOP = 'h-[68px] md:h-[80px]'
8
- const ROW_HEIGHT_MOBILE = 'h-[68px]'
7
+ // INNER row heights: the bordered row card adds 1px top + bottom, so the
8
+ // outer block totals the designed 68px / 80px.
9
+ const ROW_HEIGHT_DESKTOP = 'h-[66px] md:h-[78px]'
10
+ const ROW_HEIGHT_MOBILE = 'h-[66px]'
9
11
 
10
12
  /** @deprecated Use `DataTableSkeleton` from `data-table` instead. */
11
13
  export function TableCardSkeleton({
@@ -0,0 +1,132 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { ScrollShadow } from '../components/ui/scroll-fade';
3
+
4
+ const meta = {
5
+ title: 'UI/ScrollShadow',
6
+ component: ScrollShadow,
7
+ argTypes: {
8
+ axis: {
9
+ control: 'select',
10
+ options: ['vertical', 'horizontal', 'both'],
11
+ },
12
+ color: { control: 'text' },
13
+ },
14
+ parameters: {
15
+ docs: {
16
+ description: {
17
+ component:
18
+ 'Wrapper that makes its child scrollable and fades the edges where more content exists. ' +
19
+ 'The fade color defaults to the page background token and is theme-aware; pass a card/light ' +
20
+ 'surface color when the scrollable sits on another surface.',
21
+ },
22
+ },
23
+ },
24
+ } satisfies Meta<typeof ScrollShadow>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ const COLUMNS = ['HOSTNAME', 'PID', 'NAME', 'PATH', 'STATE', 'UID', 'RESIDENT', 'THREADS', 'START TIME'];
30
+
31
+ const WideTable = () => (
32
+ <div className="w-max min-w-full">
33
+ <div className="flex px-[var(--spacing-system-mf)] gap-[var(--spacing-system-mf)]">
34
+ {COLUMNS.map(col => (
35
+ <div key={col} className="flex h-12 w-[160px] shrink-0 items-center">
36
+ <span className="text-h5 text-ods-text-secondary uppercase">{col}</span>
37
+ </div>
38
+ ))}
39
+ </div>
40
+ {[1, 2, 3].map(row => (
41
+ <div key={row} className="rounded-md bg-ods-card border border-ods-border overflow-hidden mb-[var(--spacing-system-xsf)]">
42
+ <div className="flex h-[78px] items-center px-[var(--spacing-system-mf)] gap-[var(--spacing-system-mf)]">
43
+ {COLUMNS.map(col => (
44
+ <div key={col} className="w-[160px] shrink-0">
45
+ <span className="text-h4 text-ods-text-primary truncate">
46
+ {col.toLowerCase()}-{row}
47
+ </span>
48
+ </div>
49
+ ))}
50
+ </div>
51
+ </div>
52
+ ))}
53
+ </div>
54
+ );
55
+
56
+ /** Wide table: right fade on load, left fade appears once scrolled. */
57
+ export const HorizontalTable: Story = {
58
+ args: {
59
+ axis: 'horizontal',
60
+ children: <WideTable />,
61
+ },
62
+ render: args => (
63
+ <div className="max-w-[720px] bg-ods-bg p-[var(--spacing-system-mf)]">
64
+ <ScrollShadow {...args} />
65
+ </div>
66
+ ),
67
+ };
68
+
69
+ const LongList = () => (
70
+ <div className="flex flex-col gap-[var(--spacing-system-xsf)] p-[var(--spacing-system-mf)]">
71
+ {Array.from({ length: 20 }).map((_, i) => (
72
+ <div key={i} className="rounded-md bg-ods-card border border-ods-border p-[var(--spacing-system-mf)]">
73
+ <span className="text-h4 text-ods-text-primary">Message {i + 1}</span>
74
+ </div>
75
+ ))}
76
+ </div>
77
+ );
78
+
79
+ /** Long list: bottom fade on load, top fade appears once scrolled down. */
80
+ export const VerticalList: Story = {
81
+ args: {
82
+ axis: 'vertical',
83
+ children: <LongList />,
84
+ scrollClassName: 'h-[320px]',
85
+ },
86
+ render: args => (
87
+ <div className="max-w-[480px] bg-ods-bg">
88
+ <ScrollShadow {...args} />
89
+ </div>
90
+ ),
91
+ };
92
+
93
+ /**
94
+ * Light-surface usage (e.g. ChatMessageList themes): pass the surface color
95
+ * the content sits on — with theme-aware tokens the same prop value works in
96
+ * both themes; a literal color is shown here to make the story self-contained.
97
+ */
98
+ export const LightSurface: Story = {
99
+ args: {
100
+ axis: 'vertical',
101
+ color: '#ffffff',
102
+ children: (
103
+ <div className="flex flex-col gap-2 p-4">
104
+ {Array.from({ length: 20 }).map((_, i) => (
105
+ <div key={i} className="rounded-md border border-gray-200 bg-gray-50 p-4">
106
+ <span className="text-gray-900">Light message {i + 1}</span>
107
+ </div>
108
+ ))}
109
+ </div>
110
+ ),
111
+ scrollClassName: 'h-[320px]',
112
+ },
113
+ render: args => (
114
+ <div className="max-w-[480px] bg-white rounded-md">
115
+ <ScrollShadow {...args} />
116
+ </div>
117
+ ),
118
+ };
119
+
120
+ /** Both axes at once. */
121
+ export const BothAxes: Story = {
122
+ args: {
123
+ axis: 'both',
124
+ children: <WideTable />,
125
+ scrollClassName: 'h-[200px]',
126
+ },
127
+ render: args => (
128
+ <div className="max-w-[720px] bg-ods-bg p-[var(--spacing-system-mf)]">
129
+ <ScrollShadow {...args} />
130
+ </div>
131
+ ),
132
+ };