@blocklet/launcher-workflow 2.4.3 → 2.4.5

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 (39) hide show
  1. package/es/components/in-progress-session.js +1 -1
  2. package/es/components/launch-serverless/allocate.js +181 -0
  3. package/es/components/launch-serverless/install.js +203 -0
  4. package/es/components/launch-serverless/shared/base-serverless-layout.js +53 -0
  5. package/es/components/launch-serverless/shared/common-components.js +605 -0
  6. package/es/components/launch-serverless/shared/loading-display-layout.js +122 -0
  7. package/es/components/launch-serverless/shared/retry-error-message.js +45 -0
  8. package/es/components/launch-serverless/start-app.js +356 -0
  9. package/es/contexts/request.js +2 -2
  10. package/es/hooks/use-serial-polling.js +43 -0
  11. package/es/install.js +28 -0
  12. package/es/launch.js +1 -1
  13. package/es/locales/en.js +71 -14
  14. package/es/locales/zh.js +68 -12
  15. package/es/paid.js +1 -1
  16. package/es/prepare.js +1 -1
  17. package/es/start-app.js +28 -0
  18. package/es/util.js +181 -2
  19. package/lib/components/in-progress-session.js +3 -3
  20. package/lib/components/launch-serverless/allocate.js +198 -0
  21. package/lib/components/launch-serverless/install.js +223 -0
  22. package/lib/components/launch-serverless/shared/base-serverless-layout.js +59 -0
  23. package/lib/components/launch-serverless/shared/common-components.js +635 -0
  24. package/lib/components/launch-serverless/shared/loading-display-layout.js +131 -0
  25. package/lib/components/launch-serverless/shared/retry-error-message.js +52 -0
  26. package/lib/components/launch-serverless/start-app.js +369 -0
  27. package/lib/contexts/request.js +2 -2
  28. package/lib/hooks/use-serial-polling.js +49 -0
  29. package/lib/install.js +35 -0
  30. package/lib/launch.js +2 -2
  31. package/lib/locales/en.js +71 -14
  32. package/lib/locales/zh.js +68 -12
  33. package/lib/paid.js +2 -2
  34. package/lib/prepare.js +2 -2
  35. package/lib/start-app.js +35 -0
  36. package/lib/util.js +214 -11
  37. package/package.json +15 -12
  38. package/es/components/launch-serverless.js +0 -115
  39. package/lib/components/launch-serverless.js +0 -89
@@ -0,0 +1,605 @@
1
+ import { getBaseURL, getSubscriptionLink } from '@blocklet/launcher-util/es/util';
2
+ import ContentCopyIcon from '@mui/icons-material/ContentCopy';
3
+ import DashboardIcon from '@mui/icons-material/Dashboard';
4
+ import InfoIcon from '@mui/icons-material/Info';
5
+ import InventoryIcon from '@mui/icons-material/Inventory2';
6
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
7
+ import TaskAltIcon from '@mui/icons-material/TaskAlt';
8
+ import { Alert, AlertTitle, alpha, Box, LinearProgress, Typography, useTheme } from '@mui/material';
9
+ import Chip from '@mui/material/Chip';
10
+ import IconButton from '@mui/material/IconButton';
11
+ import PropTypes from 'prop-types';
12
+ import React, { useEffect, useRef, useState } from 'react';
13
+ import { joinURL } from 'ufo';
14
+ import Img from '@arcblock/ux/lib/Img';
15
+ import { useLocaleContext } from '../../../contexts/locale';
16
+ import { checkUrlAccessible } from '../../../util';
17
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
+ export function proxyUrl(url) {
19
+ if (url.startsWith('/')) {
20
+ return joinURL(window.location.origin, url);
21
+ }
22
+ return `${window.location.origin}/.well-known/service/proxy?url=${url}`;
23
+ }
24
+ const COMPONENT_BASE_TIME = 2; // 每个组件基础处理时间 4s
25
+ const SIZE_TIME_RATIO = 0.2; // 每 1MB 增加 1s
26
+ // 计算预估安装时间
27
+ export const calculateEstimatedTime = (components = []) => {
28
+ if (!Array.isArray(components) || components.length === 0) {
29
+ return 0;
30
+ }
31
+
32
+ // 计算所有组件的总大小(MB)
33
+ const totalSizeMB = components.reduce((acc, comp) => {
34
+ const sizeInMB = (comp?.meta?.dist?.size || 0) / (1024 * 1024);
35
+ return acc + sizeInMB;
36
+ }, 0);
37
+
38
+ // 计算总时间
39
+ const totalTime = components.length * COMPONENT_BASE_TIME + totalSizeMB * SIZE_TIME_RATIO;
40
+ return Math.ceil(totalTime);
41
+ };
42
+ export function LoadingContainer({
43
+ children
44
+ }) {
45
+ return /*#__PURE__*/_jsx(Box, {
46
+ sx: {
47
+ padding: '32px 16px 16px 16px',
48
+ maxWidth: 'sm',
49
+ width: '100%',
50
+ margin: '0 auto',
51
+ borderRadius: '16px',
52
+ position: 'relative',
53
+ overflow: 'hidden'
54
+ },
55
+ children: children
56
+ });
57
+ }
58
+ LoadingContainer.propTypes = {
59
+ children: PropTypes.node.isRequired
60
+ };
61
+ export function StyledProgress({
62
+ value
63
+ }) {
64
+ return /*#__PURE__*/_jsx(LinearProgress, {
65
+ variant: "determinate",
66
+ value: value,
67
+ sx: {
68
+ height: 8,
69
+ borderRadius: 4,
70
+ background: '#f5f5f5',
71
+ '& .MuiLinearProgress-bar': {
72
+ borderRadius: 4,
73
+ background: 'linear-gradient(90deg, #1976d2, #42a5f5)'
74
+ }
75
+ }
76
+ });
77
+ }
78
+ StyledProgress.propTypes = {
79
+ value: PropTypes.number.isRequired
80
+ };
81
+ export function TimeText({
82
+ children
83
+ }) {
84
+ return /*#__PURE__*/_jsx(Typography, {
85
+ sx: {
86
+ fontSize: 14,
87
+ color: '#888',
88
+ marginLeft: 8,
89
+ minWidth: 90,
90
+ textAlign: 'right',
91
+ display: 'inline-block'
92
+ },
93
+ children: children
94
+ });
95
+ }
96
+ TimeText.propTypes = {
97
+ children: PropTypes.node.isRequired
98
+ };
99
+ export function ActionCardBox({
100
+ children,
101
+ tipFlag = null
102
+ }) {
103
+ const childrenRef = useRef(children);
104
+ childrenRef.current = children;
105
+ const [isTransitioning, setIsTransitioning] = useState(false);
106
+ const [currentContent, setCurrentContent] = useState(children);
107
+ useEffect(() => {
108
+ if (tipFlag !== null) {
109
+ // 开始动画:透明度 1 -> 0
110
+ setIsTransitioning(true);
111
+
112
+ // 400ms 后替换内容并开始显示:透明度 0 -> 1
113
+ const timer = setTimeout(() => {
114
+ setCurrentContent(childrenRef.current);
115
+ setIsTransitioning(false);
116
+ }, 400);
117
+ return () => clearTimeout(timer);
118
+ }
119
+ return undefined;
120
+ }, [tipFlag]);
121
+ return /*#__PURE__*/_jsx(Typography, {
122
+ variant: "body1",
123
+ sx: {
124
+ textAlign: 'center',
125
+ opacity: isTransitioning ? 0 : 0.5,
126
+ transition: 'opacity 0.4s ease-in-out'
127
+ },
128
+ children: currentContent
129
+ });
130
+ }
131
+ ActionCardBox.propTypes = {
132
+ children: PropTypes.node.isRequired,
133
+ tipFlag: PropTypes.any // 可以是任何类型,用于触发动画
134
+ };
135
+
136
+ // 公共 Hook:格式化时间
137
+ export const useFormatTime = () => {
138
+ const formatTime = seconds => {
139
+ const mins = Math.floor(seconds / 60);
140
+ const secs = seconds % 60;
141
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
142
+ };
143
+ return formatTime;
144
+ };
145
+
146
+ // 公共 Hook:时间递增
147
+ export const useElapsedTime = (elapsedSeconds = 0) => {
148
+ const [time, setTime] = useState(elapsedSeconds);
149
+ const timerRef = useRef();
150
+ useEffect(() => {
151
+ timerRef.current = setInterval(() => {
152
+ setTime(t => t + 1);
153
+ }, 1000);
154
+ return () => clearInterval(timerRef.current);
155
+ }, []);
156
+ return time;
157
+ };
158
+
159
+ // 公共 Hook:动态进度
160
+ export function useDisplayProgress(start, duration = 5) {
161
+ const [progress, setProgress] = useState(start);
162
+ useEffect(() => {
163
+ setProgress(prev => Math.max(prev, start));
164
+ }, [start]);
165
+ useEffect(() => {
166
+ const timerDuration = Math.round((duration || 1) * 1000 / 99);
167
+ const interval = setInterval(() => {
168
+ setProgress(prev => Math.min(prev + 1, 99));
169
+ }, timerDuration);
170
+ return () => clearInterval(interval);
171
+ }, [duration]);
172
+ return progress;
173
+ }
174
+
175
+ // 公共组件:产品介绍轮播
176
+ export function ProductIntroCarousel({
177
+ tips
178
+ }) {
179
+ const [slideIndex, setSlideIndex] = useState(Math.floor(Math.random() * tips.length));
180
+ const [slideIn, setSlideIn] = useState(true);
181
+ const usedIndicesRef = useRef(new Set([0]));
182
+ useEffect(() => {
183
+ // 获取下一个随机索引(排除已使用的)
184
+ const getNextRandomIndex = () => {
185
+ const availableIndices = tips.map((_, index) => index).filter(index => !usedIndicesRef.current.has(index));
186
+
187
+ // 如果所有索引都用过了,重置并重新开始
188
+ if (availableIndices.length === 0) {
189
+ usedIndicesRef.current = new Set();
190
+ return Math.floor(Math.random() * tips.length);
191
+ }
192
+
193
+ // 从可用索引中随机选择一个
194
+ const randomIndex = Math.floor(Math.random() * availableIndices.length);
195
+ return availableIndices[randomIndex];
196
+ };
197
+ const timer = setInterval(() => {
198
+ setSlideIn(false);
199
+ setTimeout(() => {
200
+ const nextIndex = getNextRandomIndex();
201
+ setSlideIndex(nextIndex);
202
+ usedIndicesRef.current = new Set([...usedIndicesRef.current, nextIndex]);
203
+ setSlideIn(true);
204
+ }, 400);
205
+ }, 6000);
206
+ return () => clearInterval(timer);
207
+ }, [tips]);
208
+ if (!tips[slideIndex]) {
209
+ return null;
210
+ }
211
+ return /*#__PURE__*/_jsxs(Alert, {
212
+ severity: "info",
213
+ sx: {
214
+ mt: 8,
215
+ p: 1.5,
216
+ opacity: slideIn ? 0.8 : 0,
217
+ transform: slideIn ? 'translateY(0)' : 'translateY(6px)',
218
+ transition: 'all 0.4s ease-in-out'
219
+ },
220
+ children: [/*#__PURE__*/_jsx(AlertTitle, {
221
+ sx: {
222
+ textAlign: 'left'
223
+ },
224
+ children: tips[slideIndex].title
225
+ }), /*#__PURE__*/_jsx(Typography, {
226
+ variant: "body2",
227
+ color: "text.secondary",
228
+ sx: {
229
+ textAlign: 'left'
230
+ },
231
+ children: tips[slideIndex].desc
232
+ })]
233
+ });
234
+ }
235
+ ProductIntroCarousel.propTypes = {
236
+ tips: PropTypes.arrayOf(PropTypes.shape({
237
+ title: PropTypes.string.isRequired,
238
+ desc: PropTypes.string.isRequired
239
+ })).isRequired
240
+ };
241
+
242
+ // 公共组件:错误处理
243
+ export function ErrorDisplay({
244
+ error = null,
245
+ onRetry = null
246
+ }) {
247
+ const {
248
+ t
249
+ } = useLocaleContext();
250
+ if (!error) return null;
251
+ return /*#__PURE__*/_jsxs(Box, {
252
+ sx: {
253
+ mt: 3,
254
+ p: 2,
255
+ bgcolor: 'error.light',
256
+ borderRadius: 1
257
+ },
258
+ children: [/*#__PURE__*/_jsx(Typography, {
259
+ color: "error",
260
+ variant: "body2",
261
+ children: error
262
+ }), onRetry && /*#__PURE__*/_jsx(Box, {
263
+ sx: {
264
+ mt: 1
265
+ },
266
+ children: /*#__PURE__*/_jsx("button", {
267
+ type: "button",
268
+ onClick: onRetry,
269
+ style: {
270
+ background: 'none',
271
+ border: '1px solid #f44336',
272
+ color: '#f44336',
273
+ padding: '4px 8px',
274
+ borderRadius: '4px',
275
+ cursor: 'pointer'
276
+ },
277
+ children: t('loading.retry')
278
+ })
279
+ })]
280
+ });
281
+ }
282
+ ErrorDisplay.propTypes = {
283
+ error: PropTypes.string,
284
+ onRetry: PropTypes.func
285
+ };
286
+ export function AppSuccessDisplay({
287
+ accessibleUrl,
288
+ sessionId,
289
+ blockletInfo,
290
+ urls,
291
+ launchSession
292
+ }) {
293
+ const theme = useTheme();
294
+ const {
295
+ t
296
+ } = useLocaleContext();
297
+
298
+ // 获取应用基本信息
299
+ const appName = blockletInfo?.appName || t('startApp.unknownApp');
300
+ const appVersion = blockletInfo?.appVersion || '';
301
+ const appLogo = blockletInfo?.appLogo || '';
302
+ useEffect(() => {
303
+ const stepActive = document.querySelector('.step-active');
304
+ if (stepActive) {
305
+ stepActive.classList.add('step-checked');
306
+ }
307
+ }, []);
308
+ const [accessibleUrls, setAccessibleUrls] = useState([accessibleUrl]);
309
+ useEffect(() => {
310
+ const checkUrls = urls.filter(url => !accessibleUrls.includes(url));
311
+ checkUrls.forEach(url => {
312
+ checkUrlAccessible(url, 10 * 60 * 1000).then(res => {
313
+ if (res) {
314
+ setAccessibleUrls(prev => {
315
+ const accessible = [...prev, url];
316
+ return urls.filter(item => accessible.includes(item));
317
+ });
318
+ }
319
+ });
320
+ });
321
+ }, [accessibleUrls, urls]);
322
+ const appUrl = accessibleUrls[0] || urls[0];
323
+
324
+ // 功能卡片数据 - 2x2 网格布局
325
+ const featureCards = [{
326
+ id: 'visit-app',
327
+ icon: /*#__PURE__*/_jsx(OpenInNewIcon, {
328
+ sx: {
329
+ fontSize: 24,
330
+ color: 'primary.main'
331
+ }
332
+ }),
333
+ title: t('startApp.visitApp'),
334
+ description: t('startApp.visitAppDesc'),
335
+ onClick: () => window.open(joinURL(getBaseURL(), `/api/launches/${sessionId}/redirect/app?appUrl=${appUrl}`), '_blank')
336
+ }, {
337
+ id: 'dashboard',
338
+ icon: /*#__PURE__*/_jsx(DashboardIcon, {
339
+ sx: {
340
+ fontSize: 24,
341
+ color: 'primary.main'
342
+ }
343
+ }),
344
+ title: t('startApp.dashboard'),
345
+ description: t('startApp.dashboardDesc'),
346
+ onClick: () => window.open(joinURL(getBaseURL(), `/api/launches/${sessionId}/redirect/dashboard?appUrl=${appUrl}`), '_blank')
347
+ }, {
348
+ id: 'subscription',
349
+ icon: /*#__PURE__*/_jsx(InfoIcon, {
350
+ sx: {
351
+ fontSize: 24,
352
+ color: 'primary.main'
353
+ }
354
+ }),
355
+ title: t('startApp.subscription'),
356
+ description: t('startApp.subscriptionDesc'),
357
+ onClick: () => window.open(getSubscriptionLink(launchSession.subscriptionId), '_blank'),
358
+ show: !!launchSession.subscriptionId
359
+ }, {
360
+ id: 'my-orders',
361
+ icon: /*#__PURE__*/_jsx(InventoryIcon, {
362
+ sx: {
363
+ fontSize: 24,
364
+ color: 'primary.main'
365
+ }
366
+ }),
367
+ title: t('startApp.myOrders'),
368
+ description: t('startApp.myOrdersDesc'),
369
+ onClick: () => window.open(joinURL(getBaseURL(), '/u/orders'), '_blank')
370
+ }];
371
+ return /*#__PURE__*/_jsxs(Box, {
372
+ sx: {
373
+ width: '100%',
374
+ maxWidth: 800,
375
+ margin: '0 auto',
376
+ p: {
377
+ xs: 2,
378
+ sm: 3
379
+ }
380
+ },
381
+ children: [/*#__PURE__*/_jsxs(Box, {
382
+ textAlign: "center",
383
+ mb: 4,
384
+ children: [/*#__PURE__*/_jsx(Box, {
385
+ sx: {
386
+ width: 80,
387
+ height: 80,
388
+ borderRadius: '50%',
389
+ backgroundColor: alpha(theme.palette.success.main, 0.2),
390
+ display: 'flex',
391
+ alignItems: 'center',
392
+ justifyContent: 'center',
393
+ margin: '0 auto 16px'
394
+ },
395
+ children: /*#__PURE__*/_jsx(TaskAltIcon, {
396
+ sx: {
397
+ fontSize: 40,
398
+ color: 'success.main'
399
+ }
400
+ })
401
+ }), /*#__PURE__*/_jsxs(Typography, {
402
+ variant: "h3",
403
+ component: "h1",
404
+ fontWeight: "bold",
405
+ gutterBottom: true,
406
+ children: ["\uD83C\uDF89 ", t('startApp.started')]
407
+ }), /*#__PURE__*/_jsx(Typography, {
408
+ variant: "body1",
409
+ color: "text.secondary",
410
+ children: t('startApp.startedDescription')
411
+ })]
412
+ }), /*#__PURE__*/_jsxs(Box, {
413
+ sx: {
414
+ p: 3,
415
+ mb: 3,
416
+ borderRadius: 2,
417
+ border: '1px solid',
418
+ borderColor: 'divider',
419
+ backgroundColor: 'background.paper'
420
+ },
421
+ children: [/*#__PURE__*/_jsxs(Box, {
422
+ display: "flex",
423
+ alignItems: "center",
424
+ gap: 2,
425
+ mb: 2,
426
+ flexWrap: "wrap",
427
+ children: [/*#__PURE__*/_jsxs(Box, {
428
+ flex: 1,
429
+ display: "flex",
430
+ alignItems: "center",
431
+ gap: 2,
432
+ children: [appLogo ? /*#__PURE__*/_jsx(Img, {
433
+ src: joinURL(appUrl, appLogo),
434
+ alt: appName,
435
+ width: 48,
436
+ height: 48,
437
+ style: {
438
+ borderRadius: 10,
439
+ overflow: 'hidden'
440
+ }
441
+ }, appUrl) : null, /*#__PURE__*/_jsxs(Box, {
442
+ flex: 1,
443
+ textAlign: "left",
444
+ children: [/*#__PURE__*/_jsx(Typography, {
445
+ variant: "h6",
446
+ color: "text.hint",
447
+ mb: 1,
448
+ children: t('startApp.projectName')
449
+ }), /*#__PURE__*/_jsx(Typography, {
450
+ variant: "body1",
451
+ fontWeight: "medium",
452
+ children: appName
453
+ })]
454
+ })]
455
+ }), /*#__PURE__*/_jsxs(Box, {
456
+ flex: 1,
457
+ display: "flex",
458
+ alignItems: "center",
459
+ gap: 2,
460
+ minWidth: {
461
+ xs: '100%',
462
+ sm: 'auto'
463
+ },
464
+ children: [appVersion ? /*#__PURE__*/_jsxs(Box, {
465
+ flex: 1,
466
+ textAlign: "left",
467
+ children: [/*#__PURE__*/_jsx(Typography, {
468
+ variant: "h6",
469
+ color: "text.hint",
470
+ mb: 1,
471
+ children: t('startApp.version')
472
+ }), /*#__PURE__*/_jsx(Chip, {
473
+ label: `v${appVersion}`,
474
+ size: "small",
475
+ color: "primary",
476
+ variant: "outlined"
477
+ })]
478
+ }) : null, /*#__PURE__*/_jsxs(Box, {
479
+ flex: 1,
480
+ textAlign: "left",
481
+ children: [/*#__PURE__*/_jsx(Typography, {
482
+ variant: "h6",
483
+ color: "text.hint",
484
+ mb: 1,
485
+ children: t('startApp.status')
486
+ }), /*#__PURE__*/_jsx(Chip, {
487
+ label: t('startApp.running'),
488
+ size: "small",
489
+ color: "success"
490
+ })]
491
+ })]
492
+ })]
493
+ }), /*#__PURE__*/_jsxs(Box, {
494
+ children: [/*#__PURE__*/_jsx(Typography, {
495
+ variant: "h6",
496
+ color: "text.hint",
497
+ textAlign: "left",
498
+ children: t('startApp.visitUrl')
499
+ }), urls.map(url => /*#__PURE__*/_jsxs(Box, {
500
+ display: "flex",
501
+ alignItems: "center",
502
+ mt: 1,
503
+ gap: 1,
504
+ sx: {
505
+ backgroundColor: 'divider',
506
+ borderRadius: 1,
507
+ py: 0.5,
508
+ px: 1.5,
509
+ flex: 1
510
+ },
511
+ children: [/*#__PURE__*/_jsx(Box, {
512
+ sx: {
513
+ width: 8,
514
+ height: 8,
515
+ borderRadius: '50%',
516
+ backgroundColor: accessibleUrls.includes(url) ? 'success.main' : 'error.main'
517
+ }
518
+ }), /*#__PURE__*/_jsx(Typography, {
519
+ variant: "body2",
520
+ textAlign: "left",
521
+ sx: {
522
+ flex: 1,
523
+ fontSize: '0.875rem',
524
+ wordBreak: 'break-all'
525
+ },
526
+ children: url
527
+ }), /*#__PURE__*/_jsx(IconButton, {
528
+ size: "small",
529
+ onClick: () => navigator.clipboard.writeText(url),
530
+ children: /*#__PURE__*/_jsx(ContentCopyIcon, {
531
+ sx: {
532
+ fontSize: 16
533
+ }
534
+ })
535
+ })]
536
+ }, url))]
537
+ })]
538
+ }), /*#__PURE__*/_jsx(Box, {
539
+ display: "grid",
540
+ gridTemplateColumns: {
541
+ xs: '1fr',
542
+ sm: '1fr 1fr'
543
+ },
544
+ gap: 2,
545
+ children: featureCards.filter(card => card.show !== false).map(card => /*#__PURE__*/_jsxs(Box, {
546
+ sx: {
547
+ p: 3,
548
+ display: 'flex',
549
+ alignItems: 'center',
550
+ justifyContent: 'flex-start',
551
+ cursor: 'pointer',
552
+ gap: 1,
553
+ borderRadius: 2,
554
+ border: '1px solid',
555
+ borderColor: 'divider',
556
+ backgroundColor: 'background.paper',
557
+ transition: 'all 0.2s ease',
558
+ '&:hover': {
559
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
560
+ }
561
+ },
562
+ onClick: card.onClick,
563
+ children: [/*#__PURE__*/_jsx(Box, {
564
+ sx: {
565
+ width: 48,
566
+ height: 48,
567
+ borderRadius: 2,
568
+ backgroundColor: 'rgba(0, 0, 0, 0.04)',
569
+ display: 'flex',
570
+ alignItems: 'center',
571
+ justifyContent: 'center'
572
+ },
573
+ children: card.icon
574
+ }), /*#__PURE__*/_jsxs(Box, {
575
+ children: [/*#__PURE__*/_jsx(Typography, {
576
+ variant: "subtitle2",
577
+ fontWeight: "bold",
578
+ textAlign: "left",
579
+ gutterBottom: true,
580
+ children: card.title
581
+ }), /*#__PURE__*/_jsx(Typography, {
582
+ variant: "caption",
583
+ color: "text.secondary",
584
+ textAlign: "center",
585
+ sx: {
586
+ display: '-webkit-box',
587
+ WebkitLineClamp: 2,
588
+ WebkitBoxOrient: 'vertical',
589
+ overflow: 'hidden',
590
+ fontSize: '0.75rem'
591
+ },
592
+ children: card.description
593
+ })]
594
+ })]
595
+ }, card.id))
596
+ }, appUrl)]
597
+ });
598
+ }
599
+ AppSuccessDisplay.propTypes = {
600
+ launchSession: PropTypes.object.isRequired,
601
+ urls: PropTypes.arrayOf(PropTypes.string).isRequired,
602
+ accessibleUrl: PropTypes.string.isRequired,
603
+ sessionId: PropTypes.string.isRequired,
604
+ blockletInfo: PropTypes.object.isRequired
605
+ };