@flamingo-stack/openframe-frontend-core 0.0.216 → 0.0.217
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.
- package/dist/{chunk-SMCG2CCC.cjs → chunk-6DCKL73F.cjs} +24 -24
- package/dist/{chunk-SMCG2CCC.cjs.map → chunk-6DCKL73F.cjs.map} +1 -1
- package/dist/{chunk-QTKU6ULP.js → chunk-BVFRD34B.js} +2 -2
- package/dist/{chunk-CDLYRFDE.js → chunk-ENBGG2K2.js} +3767 -3610
- package/dist/chunk-ENBGG2K2.js.map +1 -0
- package/dist/{chunk-K4DFAVSO.cjs → chunk-G2HHSZ3S.cjs} +9 -9
- package/dist/{chunk-K4DFAVSO.cjs.map → chunk-G2HHSZ3S.cjs.map} +1 -1
- package/dist/{chunk-2V4SACHE.js → chunk-L6IBKPVM.js} +2 -2
- package/dist/{chunk-572WQWIX.cjs → chunk-MVQ3OODK.cjs} +9 -9
- package/dist/{chunk-572WQWIX.cjs.map → chunk-MVQ3OODK.cjs.map} +1 -1
- package/dist/{chunk-GVNQAGXB.js → chunk-N5IKPYRL.js} +3 -81
- package/dist/chunk-N5IKPYRL.js.map +1 -0
- package/dist/{chunk-VC3ND5RB.js → chunk-SWZUZYWR.js} +2 -2
- package/dist/{chunk-IH76P5R6.cjs → chunk-TYIBMDUZ.cjs} +8 -86
- package/dist/chunk-TYIBMDUZ.cjs.map +1 -0
- package/dist/{chunk-ZGTDUPTW.cjs → chunk-YWDC5BXM.cjs} +382 -225
- package/dist/chunk-YWDC5BXM.cjs.map +1 -0
- package/dist/components/chat/chat-attachment-bar.d.ts +13 -2
- package/dist/components/chat/chat-attachment-bar.d.ts.map +1 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-message-row.d.ts +25 -0
- package/dist/components/chat/chat-message-row.d.ts.map +1 -0
- package/dist/components/chat/index.cjs +6 -2
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +5 -1
- package/dist/components/chat/types/component.types.d.ts +8 -1
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/form.d.ts +1 -1
- package/dist/components/form.d.ts.map +1 -1
- package/dist/components/index.cjs +56 -52
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +9 -5
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +18 -18
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/shared/dev-section/dev-card-row.d.ts +5 -45
- package/dist/components/shared/dev-section/dev-card-row.d.ts.map +1 -1
- package/dist/components/shared/legal-document/use-legal-docs.d.ts.map +1 -1
- package/dist/components/tickets/help-center-card.d.ts.map +1 -1
- package/dist/components/tickets/help-center-list.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts +9 -1
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-tickets-list.d.ts +7 -0
- package/dist/components/tickets/hooks/use-tickets-list.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +294 -255
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.d.ts +1 -0
- package/dist/components/tickets/index.d.ts.map +1 -1
- package/dist/components/tickets/index.js +360 -321
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/tickets/ticket-detail-drawer.d.ts.map +1 -1
- package/dist/components/tickets/ticket-reply-composer.d.ts +33 -0
- package/dist/components/tickets/ticket-reply-composer.d.ts.map +1 -0
- package/dist/components/tickets/types.d.ts +13 -0
- package/dist/components/tickets/types.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +6 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +5 -1
- package/dist/components/ui/ticket-attachments-list.d.ts +5 -1
- package/dist/components/ui/ticket-attachments-list.d.ts.map +1 -1
- package/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -1
- package/dist/utils/index.cjs +59 -4
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +59 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/scroll-into-view.d.ts +43 -48
- package/dist/utils/scroll-into-view.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/chat-attachment-bar.tsx +58 -22
- package/src/components/chat/chat-input.tsx +68 -29
- package/src/components/chat/chat-message-row.tsx +124 -0
- package/src/components/chat/index.ts +1 -0
- package/src/components/chat/types/component.types.ts +8 -1
- package/src/components/shared/dev-section/dev-card-row.tsx +5 -183
- package/src/components/shared/legal-document/use-legal-docs.ts +5 -1
- package/src/components/tickets/help-center-card.tsx +26 -29
- package/src/components/tickets/help-center-list.tsx +57 -10
- package/src/components/tickets/hooks/use-ticket-engagements.ts +17 -1
- package/src/components/tickets/hooks/use-tickets-list.ts +13 -0
- package/src/components/tickets/index.ts +4 -0
- package/src/components/tickets/ticket-detail-drawer.tsx +144 -200
- package/src/components/tickets/ticket-reply-composer.tsx +195 -0
- package/src/components/tickets/types.ts +14 -0
- package/src/components/ui/ticket-attachments-list.tsx +26 -8
- package/src/styles/app-globals.css +13 -0
- package/src/utils/scroll-into-view.ts +127 -53
- package/dist/chunk-CDLYRFDE.js.map +0 -1
- package/dist/chunk-GVNQAGXB.js.map +0 -1
- package/dist/chunk-IH76P5R6.cjs.map +0 -1
- package/dist/chunk-ZGTDUPTW.cjs.map +0 -1
- /package/dist/{chunk-QTKU6ULP.js.map → chunk-BVFRD34B.js.map} +0 -0
- /package/dist/{chunk-2V4SACHE.js.map → chunk-L6IBKPVM.js.map} +0 -0
- /package/dist/{chunk-VC3ND5RB.js.map → chunk-SWZUZYWR.js.map} +0 -0
|
@@ -16,10 +16,15 @@ export interface TicketAttachment {
|
|
|
16
16
|
export interface TicketAttachmentsListProps {
|
|
17
17
|
attachments: TicketAttachment[]
|
|
18
18
|
className?: string
|
|
19
|
+
/** `compact` shrinks padding / icon / text / download-button for
|
|
20
|
+
* in-message rendering (the ticket conversation feed). Default keeps the
|
|
21
|
+
* roomier full-row layout for any other surface. */
|
|
22
|
+
size?: 'default' | 'compact'
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
export function TicketAttachmentsList({ attachments, className }: TicketAttachmentsListProps) {
|
|
25
|
+
export function TicketAttachmentsList({ attachments, className, size = 'default' }: TicketAttachmentsListProps) {
|
|
22
26
|
if (attachments.length === 0) return null
|
|
27
|
+
const compact = size === 'compact'
|
|
23
28
|
|
|
24
29
|
return (
|
|
25
30
|
<div className={cn("rounded-[6px] border border-ods-border overflow-hidden", className)}>
|
|
@@ -27,7 +32,8 @@ export function TicketAttachmentsList({ attachments, className }: TicketAttachme
|
|
|
27
32
|
<div
|
|
28
33
|
key={attachment.id}
|
|
29
34
|
className={cn(
|
|
30
|
-
"flex items-center
|
|
35
|
+
"flex items-center bg-ods-card",
|
|
36
|
+
compact ? "gap-2 px-2 py-1.5" : "gap-4 px-4 py-3",
|
|
31
37
|
index < attachments.length - 1 && "border-b border-ods-border",
|
|
32
38
|
)}
|
|
33
39
|
>
|
|
@@ -35,17 +41,29 @@ export function TicketAttachmentsList({ attachments, className }: TicketAttachme
|
|
|
35
41
|
<SquareAvatar
|
|
36
42
|
src={attachment.thumbnailSrc}
|
|
37
43
|
alt={attachment.fileName}
|
|
38
|
-
size="md"
|
|
44
|
+
size={compact ? "sm" : "md"}
|
|
39
45
|
className="shrink-0"
|
|
40
46
|
/>
|
|
41
47
|
) : (
|
|
42
|
-
<div
|
|
43
|
-
|
|
48
|
+
<div
|
|
49
|
+
className={cn(
|
|
50
|
+
"shrink-0 flex items-center justify-center rounded-[6px] bg-ods-card border border-ods-border",
|
|
51
|
+
compact ? "size-8" : "size-10",
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
<FileIcon className={cn("text-ods-text-secondary", compact ? "size-4" : "size-6")} />
|
|
44
55
|
</div>
|
|
45
56
|
)}
|
|
46
57
|
<div className="flex-1 min-w-0 overflow-hidden">
|
|
47
|
-
<p
|
|
48
|
-
|
|
58
|
+
<p
|
|
59
|
+
className={cn("text-ods-text-primary truncate", compact ? "text-h5" : "text-h4")}
|
|
60
|
+
title={attachment.fileName}
|
|
61
|
+
>
|
|
62
|
+
{attachment.fileName}
|
|
63
|
+
</p>
|
|
64
|
+
{attachment.fileSize && (
|
|
65
|
+
<p className="text-h6 text-ods-text-secondary">{attachment.fileSize}</p>
|
|
66
|
+
)}
|
|
49
67
|
</div>
|
|
50
68
|
{attachment.onDownload && (
|
|
51
69
|
<button
|
|
@@ -54,7 +72,7 @@ export function TicketAttachmentsList({ attachments, className }: TicketAttachme
|
|
|
54
72
|
className="shrink-0 text-ods-text-secondary hover:text-ods-text-primary transition-colors"
|
|
55
73
|
aria-label={`Download ${attachment.fileName}`}
|
|
56
74
|
>
|
|
57
|
-
<Download className="size-6" />
|
|
75
|
+
<Download className={compact ? "size-4" : "size-6"} />
|
|
58
76
|
</button>
|
|
59
77
|
)}
|
|
60
78
|
</div>
|
|
@@ -39,11 +39,24 @@ img {
|
|
|
39
39
|
|
|
40
40
|
html {
|
|
41
41
|
background-color: var(--color-bg);
|
|
42
|
+
/* Defense-in-depth for programmatic smooth scrolls (see
|
|
43
|
+
`utils/scroll-into-view.ts`). Browser SCROLL ANCHORING issues a synchronous
|
|
44
|
+
scrollTop correction when content is inserted/removed (e.g. a ticket drawer
|
|
45
|
+
expanding) to keep the anchored element stable — and per CSSOM-View that
|
|
46
|
+
correction ABORTS any in-flight scroll. That cancelled our smooth
|
|
47
|
+
scroll-to-card on every open after the first (anchoring is suppressed only
|
|
48
|
+
at scrollY=0, so the first open looked smooth and every repeat jumped).
|
|
49
|
+
Disabling anchoring on the document scroller removes the correction
|
|
50
|
+
entirely. Put on html/body (the page scroller), NOT a descendant — a child
|
|
51
|
+
can't re-enable it for an ancestor scroll container. The self-driven rAF
|
|
52
|
+
tween in scroll-into-view.ts is the primary fix; this is belt-and-braces. */
|
|
53
|
+
overflow-anchor: none;
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
body {
|
|
45
57
|
background-color: var(--color-bg);
|
|
46
58
|
color: var(--color-text-primary);
|
|
59
|
+
overflow-anchor: none;
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
/* Light mode specific styles (used in alternatives-list.tsx) */
|
|
@@ -1,74 +1,148 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `scrollElementIntoView` — canonical "
|
|
3
|
-
*
|
|
4
|
-
* known layout shifts" helper.
|
|
2
|
+
* `scrollElementIntoView` — canonical "scroll an element to the top of the
|
|
3
|
+
* viewport, account for sticky chrome, survive layout shifts" helper.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* One shared implementation so every caller (the ticket drawer expand, the
|
|
6
|
+
* hub's `useUnifiedNav` / `use-nav-link` hash scroll, doc-tree, delivery
|
|
7
|
+
* `?focus=`, sticky-section-nav, …) inherits the SAME cancellation-proof
|
|
8
|
+
* motion.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - Future ticket-row / docs anchor scrolls
|
|
10
|
+
* WHY A SELF-DRIVEN rAF TWEEN INSTEAD OF `window.scrollTo({behavior:'smooth'})`:
|
|
11
|
+
* the native smooth scroll is CANCELLABLE, and in real pages it gets cancelled
|
|
12
|
+
* constantly:
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* - Browser SCROLL ANCHORING: when content is inserted/removed above or
|
|
15
|
+
* around the target (a collapsible drawer expanding, an async image
|
|
16
|
+
* loading, a list re-rendering) the browser issues a synchronous scrollTop
|
|
17
|
+
* correction to keep the anchored element stable. Per CSSOM-View "perform a
|
|
18
|
+
* scroll" step 1 ("abort any ongoing smooth scroll"), that correction
|
|
19
|
+
* ABORTS an in-flight native smooth scroll — so it lands as an instant jump.
|
|
20
|
+
* Anchoring is suppressed when the scroll offset is 0, which is exactly why
|
|
21
|
+
* a native smooth scroll appears to work the FIRST time (page at top) and
|
|
22
|
+
* jumps on every repeat (page already scrolled). This was a multi-day
|
|
23
|
+
* "smooth only works once" bug on the /tickets drawer.
|
|
24
|
+
* - A second programmatic scroll on the same frame, or a `focus()` without
|
|
25
|
+
* `{preventScroll:true}`, cancels it the same way.
|
|
21
26
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* A tween that re-asserts the position with INSTANT writes every frame is
|
|
28
|
+
* immune: there is no "ongoing native smooth scroll" for anchoring/focus to
|
|
29
|
+
* abort, and any correction that lands between our frames is overwritten on the
|
|
30
|
+
* next frame. We also RECOMPUTE the target each frame, so an element whose
|
|
31
|
+
* final position is still settling (drawer still expanding, images loading)
|
|
32
|
+
* is tracked to its resting place instead of animating to a stale pixel.
|
|
33
|
+
*
|
|
34
|
+
* Honors `prefers-reduced-motion` (jumps instantly) and cancels on genuine user
|
|
35
|
+
* scroll intent (wheel / touch) so we never fight the user.
|
|
30
36
|
*/
|
|
31
37
|
|
|
32
38
|
export interface ScrollElementIntoViewOptions {
|
|
33
|
-
/** Pixels to subtract from the target element's `top` so it lands
|
|
34
|
-
*
|
|
35
|
-
* `scroll-mt-24`) for the standard hub header offset. */
|
|
39
|
+
/** Pixels to subtract from the target element's `top` so it lands BELOW
|
|
40
|
+
* sticky chrome. Defaults to 0. Pass `96` for the standard hub header. */
|
|
36
41
|
headerOffset?: number
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
* link land, programmatic focus moves). */
|
|
42
|
+
/** `'smooth'` (default) runs the self-driven tween; `'instant'` / `'auto'`
|
|
43
|
+
* jump in one synchronous write (deep-link land, programmatic focus moves). */
|
|
40
44
|
behavior?: ScrollBehavior
|
|
41
|
-
/** Optional adjustment applied to the computed pixel target. The
|
|
42
|
-
* callback receives the "raw" Y (`element.top + scrollY -
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* between the call and the animation completing — the browser's
|
|
46
|
-
* smooth-scroll commits to a single pixel value, so providing the
|
|
47
|
-
* post-shift target up front lands the element correctly even as
|
|
48
|
-
* content above it moves. */
|
|
45
|
+
/** Optional adjustment applied to the computed pixel target each frame. The
|
|
46
|
+
* callback receives the "raw" Y (`element.top + scrollY - headerOffset`) and
|
|
47
|
+
* returns the FINAL target. Use when the caller knows about a layout shift
|
|
48
|
+
* (e.g. a sibling drawer collapsing) the geometry can't yet reflect. */
|
|
49
49
|
adjustTargetY?: (rawTargetY: number) => number
|
|
50
|
+
/** Tween duration in ms (smooth only). Default 320. */
|
|
51
|
+
durationMs?: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Module-level handle to the in-flight tween so a new call (or a user
|
|
55
|
+
* gesture) cancels the previous one — only ever one page-scroll animation at
|
|
56
|
+
* a time. */
|
|
57
|
+
let activeRaf = 0
|
|
58
|
+
let teardownActive: (() => void) | null = null
|
|
59
|
+
|
|
60
|
+
function cancelActiveScroll(): void {
|
|
61
|
+
if (activeRaf) {
|
|
62
|
+
cancelAnimationFrame(activeRaf)
|
|
63
|
+
activeRaf = 0
|
|
64
|
+
}
|
|
65
|
+
if (teardownActive) {
|
|
66
|
+
teardownActive()
|
|
67
|
+
teardownActive = null
|
|
68
|
+
}
|
|
50
69
|
}
|
|
51
70
|
|
|
71
|
+
const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3)
|
|
72
|
+
|
|
52
73
|
/**
|
|
53
|
-
* Scroll the page so `target` lands at the top of the viewport
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* Accepts:
|
|
58
|
-
* - `HTMLElement` — direct reference (most common from `useRef`).
|
|
59
|
-
* - `null` / `undefined` — no-op so callers can pass refs without
|
|
60
|
-
* defensive branching.
|
|
61
|
-
*
|
|
62
|
-
* SSR-safe: short-circuits when `window` is undefined.
|
|
74
|
+
* Scroll the page so `target` lands at the top of the viewport (below sticky
|
|
75
|
+
* chrome via `headerOffset`). SSR-safe; `null`/`undefined` target is a no-op so
|
|
76
|
+
* callers can pass refs without defensive branching.
|
|
63
77
|
*/
|
|
64
78
|
export function scrollElementIntoView(
|
|
65
79
|
target: HTMLElement | null | undefined,
|
|
66
80
|
options: ScrollElementIntoViewOptions = {},
|
|
67
81
|
): void {
|
|
68
82
|
if (typeof window === 'undefined' || !target) return
|
|
69
|
-
const { headerOffset = 0, behavior = 'smooth', adjustTargetY } = options
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
const { headerOffset = 0, behavior = 'smooth', adjustTargetY, durationMs = 320 } = options
|
|
84
|
+
|
|
85
|
+
// Target is recomputed every frame: the row's absolute position can move as
|
|
86
|
+
// the page reflows (a sibling drawer collapsing) and the reachable max grows
|
|
87
|
+
// as the just-opened drawer expands. Clamp to the LIVE max each frame.
|
|
88
|
+
const computeTarget = (): number => {
|
|
89
|
+
const raw = target.getBoundingClientRect().top + window.scrollY - headerOffset
|
|
90
|
+
const adjusted = adjustTargetY ? adjustTargetY(raw) : raw
|
|
91
|
+
const maxScroll = Math.max(
|
|
92
|
+
0,
|
|
93
|
+
document.documentElement.scrollHeight - window.innerHeight,
|
|
94
|
+
)
|
|
95
|
+
return Math.min(Math.max(0, adjusted), maxScroll)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Any prior animation loses — one page scroll at a time.
|
|
99
|
+
cancelActiveScroll()
|
|
100
|
+
|
|
101
|
+
const prefersReduced =
|
|
102
|
+
typeof window.matchMedia === 'function' &&
|
|
103
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
104
|
+
|
|
105
|
+
// Instant paths: a single synchronous write. No tween, no anchoring race.
|
|
106
|
+
if (behavior === 'instant' || behavior === 'auto' || prefersReduced) {
|
|
107
|
+
window.scrollTo(0, computeTarget())
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Smooth: self-driven tween with instant per-frame writes (anchoring-proof).
|
|
112
|
+
let startY: number | null = null
|
|
113
|
+
let startTime = 0
|
|
114
|
+
|
|
115
|
+
// Bail the moment the user takes over with a real scroll gesture — we must
|
|
116
|
+
// never fight them. (Not keydown: the ticket composer auto-focuses on open,
|
|
117
|
+
// and typing there should not abort the scroll.)
|
|
118
|
+
const onUserGesture = () => cancelActiveScroll()
|
|
119
|
+
window.addEventListener('wheel', onUserGesture, { passive: true })
|
|
120
|
+
window.addEventListener('touchmove', onUserGesture, { passive: true })
|
|
121
|
+
teardownActive = () => {
|
|
122
|
+
window.removeEventListener('wheel', onUserGesture)
|
|
123
|
+
window.removeEventListener('touchmove', onUserGesture)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const step = (now: number) => {
|
|
127
|
+
if (startY === null) {
|
|
128
|
+
startY = window.scrollY
|
|
129
|
+
startTime = now
|
|
130
|
+
}
|
|
131
|
+
const targetY = computeTarget()
|
|
132
|
+
const t = Math.min(1, (now - startTime) / durationMs)
|
|
133
|
+
const y = startY + (targetY - startY) * easeOutCubic(t)
|
|
134
|
+
window.scrollTo(0, y)
|
|
135
|
+
if (t < 1) {
|
|
136
|
+
activeRaf = requestAnimationFrame(step)
|
|
137
|
+
} else {
|
|
138
|
+
// Final exact write in case easing left a sub-pixel gap, then teardown.
|
|
139
|
+
window.scrollTo(0, computeTarget())
|
|
140
|
+
activeRaf = 0
|
|
141
|
+
if (teardownActive) {
|
|
142
|
+
teardownActive()
|
|
143
|
+
teardownActive = null
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
activeRaf = requestAnimationFrame(step)
|
|
74
148
|
}
|