@flamingo-stack/openframe-frontend-core 0.0.720 → 0.0.722
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-3HZ6XSZC.js → chunk-2TXGR7IK.js} +136 -59
- package/dist/{chunk-WSDT7LYL.js → chunk-3UBWDR65.js} +1 -1
- package/dist/{chunk-CMZ3QTFQ.js → chunk-4I3K6LEH.js} +24 -0
- package/dist/{chunk-4SIZCHVZ.cjs → chunk-4LY6R4TN.cjs} +126 -49
- package/dist/{chunk-2NASBO7O.js → chunk-5D2K7TFT.js} +3 -3
- package/dist/{chunk-HFEYEPOG.cjs → chunk-5K6YKRP5.cjs} +29 -29
- package/dist/{chunk-7HYEQKN6.js → chunk-65C4WHG4.js} +6 -6
- package/dist/{chunk-DOPOHO6H.cjs → chunk-6HRQLM6T.cjs} +20 -20
- package/dist/{chunk-B2N4YX66.js → chunk-6YBUQAOH.js} +1 -1
- package/dist/{chunk-GANIUMRU.js → chunk-7UZ2JZJY.js} +3 -3
- package/dist/{chunk-4HJRHEXD.cjs → chunk-ABSWD4PQ.cjs} +27 -27
- package/dist/{chunk-VYVPII4W.js → chunk-AWWLEYAU.js} +6 -6
- package/dist/{chunk-V6B2FBZK.js → chunk-BCU6UUNQ.js} +8 -8
- package/dist/{chunk-LO3KCLEF.js → chunk-BUDZWXRG.js} +4 -4
- package/dist/{chunk-WBNAEYTQ.cjs → chunk-BYUIFT7Q.cjs} +6 -6
- package/dist/{chunk-KSGMZHVV.cjs → chunk-DFM572VH.cjs} +2 -2
- package/dist/{chunk-YDPKUAP4.js → chunk-DJ4IFQNQ.js} +2 -0
- package/dist/{chunk-DYDDAA6O.cjs → chunk-DWSBTGKT.cjs} +816 -798
- package/dist/{chunk-PSMJYZMV.js → chunk-DX3W7DPC.js} +2 -2
- package/dist/{chunk-PXAKSPLZ.js → chunk-E7NIZXDT.js} +20 -8
- package/dist/{chunk-6W46LVK4.js → chunk-ETQVXIYW.js} +6 -6
- package/dist/{chunk-742BGXNX.js → chunk-EWED6ISQ.js} +1 -1
- package/dist/{chunk-Q5327DXK.js → chunk-F33RMJOL.js} +11 -11
- package/dist/{chunk-RZQGIWK3.cjs → chunk-FDWYBJYD.cjs} +8 -8
- package/dist/{chunk-QF464PPL.cjs → chunk-GAFK3WNT.cjs} +62 -62
- package/dist/{chunk-6BLN5WDS.cjs → chunk-GYSTZD6X.cjs} +40 -40
- package/dist/{chunk-TORERAXD.js → chunk-HA2ASGJM.js} +6 -6
- package/dist/{chunk-KT3U3ZRP.cjs → chunk-HP4UUBJC.cjs} +16 -16
- package/dist/{chunk-DFQYAYBH.cjs → chunk-HQIOMFJH.cjs} +2 -0
- package/dist/{chunk-AZRI4XBP.js → chunk-I3UUMRO7.js} +1 -1
- package/dist/{chunk-VYZCKY5K.js → chunk-IF72N4PE.js} +7 -7
- package/dist/{chunk-RFUOIQSG.cjs → chunk-IITZ6JSC.cjs} +37 -37
- package/dist/{chunk-7LPR4RZ3.cjs → chunk-JQ55FFIK.cjs} +31 -31
- package/dist/{chunk-GQ7JJUIT.cjs → chunk-K2JUJ37D.cjs} +25 -1
- package/dist/{chunk-URW23QUM.js → chunk-K76ACBR3.js} +2 -2
- package/dist/{chunk-7IOU6BJE.js → chunk-KBPGSPXQ.js} +2387 -2369
- package/dist/{chunk-ILNN6QRW.js → chunk-KS3YLYVL.js} +4 -4
- package/dist/{chunk-2C72EWQR.cjs → chunk-KWBH63GV.cjs} +26 -26
- package/dist/{chunk-IYVFSLWJ.js → chunk-KZQO5EJQ.js} +4 -0
- package/dist/{chunk-IMTY3GRR.cjs → chunk-LV6UJD2K.cjs} +2 -2
- package/dist/{chunk-UWJMDFDO.cjs → chunk-MJ4SXOG2.cjs} +28 -28
- package/dist/{chunk-VF2CJXZ2.cjs → chunk-NFABAOMD.cjs} +77 -79
- package/dist/{chunk-B5LNX4GZ.js → chunk-NMGWNUKB.js} +3 -3
- package/dist/{chunk-RDP5UZXQ.cjs → chunk-P7Y626YJ.cjs} +370 -358
- package/dist/{chunk-P3JMAMHP.js → chunk-REETLEL5.js} +1 -1
- package/dist/{chunk-FFT56TYB.cjs → chunk-RUZIICXT.cjs} +2 -2
- package/dist/{chunk-ISCZ6UNF.cjs → chunk-RVQV6J5K.cjs} +75 -75
- package/dist/{chunk-VBHGJFA3.js → chunk-SZ3CBFSH.js} +13 -13
- package/dist/{chunk-O3MKJU6A.js → chunk-SZF7ENIW.js} +4 -4
- package/dist/{chunk-F5BPGWGT.cjs → chunk-TKFTWM3V.cjs} +5 -1
- package/dist/{chunk-YK3X74SF.js → chunk-UYMTAFHG.js} +6 -8
- package/dist/{chunk-DHNJSQGX.cjs → chunk-VC7COEWF.cjs} +105 -105
- package/dist/{chunk-XW3Q623Q.js → chunk-VSYQJCNR.js} +2 -2
- package/dist/{chunk-IL3ZWNM2.cjs → chunk-VU7RU3LL.cjs} +7 -7
- package/dist/{chunk-XSM46FHD.cjs → chunk-VV5INIQB.cjs} +3 -3
- package/dist/{chunk-HTS46FFY.cjs → chunk-VYHCN5NU.cjs} +16 -16
- package/dist/{chunk-ZPHOQDAE.js → chunk-WY4SLTMZ.js} +3 -3
- package/dist/{chunk-5XDCCQ4A.cjs → chunk-XJYAUIOL.cjs} +13 -13
- package/dist/{chunk-ZHD3F2TH.cjs → chunk-XR7NVJXS.cjs} +13 -13
- package/dist/{chunk-EVMM3JE6.cjs → chunk-ZHC6MDGF.cjs} +43 -43
- package/dist/{chunk-K2BYVPGR.js → chunk-ZMHP4Y6M.js} +5 -5
- package/dist/{chunk-NYSMDM4J.cjs → chunk-ZSOFLPQN.cjs} +7 -7
- package/dist/{cloud-tenants-RKQRSDNE.js → cloud-tenants-4Z36KWSX.js} +14 -14
- package/dist/{cloud-tenants-UQZASXUB.cjs → cloud-tenants-5NIC5VXK.cjs} +15 -15
- package/dist/components/calendar.cjs +4 -4
- package/dist/components/calendar.js +1 -1
- package/dist/components/case-studies/index.cjs +29 -29
- package/dist/components/case-studies/index.js +15 -15
- package/dist/components/chat/index.cjs +10 -10
- package/dist/components/chat/index.js +9 -9
- package/dist/components/contact/index.cjs +16 -16
- package/dist/components/contact/index.js +15 -15
- package/dist/components/docs/index.cjs +19 -19
- package/dist/components/docs/index.js +18 -18
- package/dist/components/embeds/index.cjs +16 -16
- package/dist/components/embeds/index.js +15 -15
- package/dist/components/faq/index.cjs +16 -16
- package/dist/components/faq/index.js +15 -15
- package/dist/components/features/index.cjs +15 -15
- package/dist/components/features/index.js +14 -14
- package/dist/components/features/remote-session/remote-access-request-card.d.ts.map +1 -1
- package/dist/components/help-center-pages/download-apps-page.d.ts +68 -0
- package/dist/components/help-center-pages/download-apps-page.d.ts.map +1 -0
- package/dist/components/help-center-pages/index.cjs +347 -117
- package/dist/components/help-center-pages/index.d.ts +1 -0
- package/dist/components/help-center-pages/index.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.js +260 -30
- package/dist/components/index.cjs +87 -85
- package/dist/components/index.js +30 -28
- package/dist/components/meeting-scheduler/index.cjs +67 -67
- package/dist/components/meeting-scheduler/index.js +16 -16
- package/dist/components/navigation/index.cjs +18 -18
- package/dist/components/navigation/index.js +17 -17
- package/dist/components/onboarding-guides/index.cjs +18 -18
- package/dist/components/onboarding-guides/index.js +17 -17
- package/dist/components/product-demo/index.cjs +8 -8
- package/dist/components/product-demo/index.js +8 -8
- package/dist/components/related-content/index.cjs +7 -7
- package/dist/components/related-content/index.js +6 -6
- package/dist/components/tickets/index.cjs +20 -20
- package/dist/components/tickets/index.js +19 -19
- package/dist/components/ui/button/button.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +11 -11
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +17 -15
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +16 -14
- package/dist/components/ui/mobile-app-qr.d.ts +21 -0
- package/dist/components/ui/mobile-app-qr.d.ts.map +1 -0
- package/dist/components/ui/store-badges.d.ts +7 -4
- package/dist/components/ui/store-badges.d.ts.map +1 -1
- package/dist/{devices-U3Y4IIW4.js → devices-2TK3LAKM.js} +14 -14
- package/dist/{devices-7Q2EUM7B.cjs → devices-7IKT72DK.cjs} +16 -16
- package/dist/hooks/index.cjs +9 -3
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +8 -2
- package/dist/hooks/use-downloads.d.ts +41 -0
- package/dist/hooks/use-downloads.d.ts.map +1 -0
- package/dist/index.cjs +52 -20
- package/dist/index.js +50 -18
- package/dist/{knowledge-NST53EXE.js → knowledge-3GJ4MU3V.js} +14 -14
- package/dist/{knowledge-UYIZ74MA.cjs → knowledge-HYSEV35H.cjs} +16 -16
- package/dist/{logs-JVTRG232.js → logs-TATVM62H.js} +14 -14
- package/dist/{logs-HTH7X7KV.cjs → logs-ZX3NADQC.cjs} +24 -24
- package/dist/{policies-MXK2HB4W.cjs → policies-J4FSDKPI.cjs} +19 -19
- package/dist/{policies-DTXTRK56.js → policies-MRRKLF4Q.js} +14 -14
- package/dist/{remote-session-LAJZKQBN.js → remote-session-WCLR47RN.js} +6 -6
- package/dist/{remote-session-SKKF4PBV.cjs → remote-session-Z2YCUPHU.cjs} +8 -8
- package/dist/{software-update-P6BSM6HX.cjs → software-update-CXUBWYD2.cjs} +19 -19
- package/dist/{software-update-6RHMPM3W.js → software-update-KM46WUVQ.js} +14 -14
- package/dist/{tickets-board-BHLPAVNJ.cjs → tickets-board-R5OQSEEC.cjs} +10 -10
- package/dist/{tickets-board-PYDNELSK.js → tickets-board-U3RF3AO4.js} +5 -5
- package/dist/types/downloads.d.ts +83 -0
- package/dist/types/downloads.d.ts.map +1 -0
- package/dist/types/index.cjs +24 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +16 -1
- package/dist/utils/format.d.ts +11 -0
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/index.cjs +15 -0
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +13 -1
- package/dist/utils/mobile-app.cjs +37 -0
- package/dist/utils/mobile-app.d.ts +23 -0
- package/dist/utils/mobile-app.d.ts.map +1 -0
- package/dist/utils/mobile-app.js +34 -0
- package/package.json +7 -1
- package/src/components/features/remote-session/remote-access-request-card.tsx +6 -6
- package/src/components/help-center-pages/download-apps-page.tsx +388 -0
- package/src/components/help-center-pages/index.ts +1 -0
- package/src/components/ui/button/button.tsx +3 -0
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/mobile-app-qr.tsx +35 -0
- package/src/components/ui/store-badges.tsx +17 -9
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-downloads.ts +138 -0
- package/src/types/downloads.ts +99 -0
- package/src/types/index.ts +1 -0
- package/src/utils/format.ts +15 -0
- package/src/utils/index.ts +3 -1
- package/src/utils/mobile-app.ts +34 -0
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { cn } from './cn';
|
|
2
2
|
export { getPlatformUrl, getPlatformProductionUrl, getAllPlatformBaseDomains, getDeploymentUrl, getRequestOrigin, isLocalUrl, resolveRedirectTarget, } from '../platform-domains';
|
|
3
|
-
export { formatDate, formatNumber, formatPrice, formatBytes } from './format';
|
|
3
|
+
export { formatDate, formatNumber, formatPrice, formatBytes, formatUrlForDisplay } from './format';
|
|
4
4
|
export { TRIAL_DAYS, TRIAL_LABELS } from './trial';
|
|
5
5
|
export { VENDOR_CLASSIFICATIONS, OPENFRAME_CLASSIFICATIONS, isVendorClassification, isOpenFrameClassification, type VendorClassificationValue, type OpenFrameClassification, type OpenFrameClassificationCopy, } from './vendor-classification';
|
|
6
6
|
export { PLAY_ICON_PATH } from '../components/icons-v2-generated/media-playback/play-icon';
|
|
@@ -28,6 +28,7 @@ export * from './shell-utils';
|
|
|
28
28
|
export * from './video-bite-id';
|
|
29
29
|
export * from './os-utils';
|
|
30
30
|
export * from './visitor-os';
|
|
31
|
+
export * from './mobile-app';
|
|
31
32
|
export * from './country-phone-utils';
|
|
32
33
|
export * from './compliance-standards';
|
|
33
34
|
export * from './generic-domain-utils';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAK1B,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.js
CHANGED
|
@@ -648,6 +648,9 @@ function formatWebinarTimeMeta(at, opts) {
|
|
|
648
648
|
const time = formatProgramTimeRange(at, { startAt: opts.startAt, withZoneLabel: opts.withZoneLabel });
|
|
649
649
|
return [time, duration].filter(Boolean).join(" \xB7 ");
|
|
650
650
|
}
|
|
651
|
+
function formatUrlForDisplay(url, options = {}) {
|
|
652
|
+
return url.replace(options.keepWww ? /^https?:\/\//i : /^https?:\/\/(www\.)?/i, "").replace(/[?#].*$/, "").replace(/\/$/, "");
|
|
653
|
+
}
|
|
651
654
|
|
|
652
655
|
// src/utils/trial.ts
|
|
653
656
|
var TRIAL_DAYS = 14;
|
|
@@ -2874,6 +2877,15 @@ function usesCommandKey(os) {
|
|
|
2874
2877
|
function shortcutLabel(os, key) {
|
|
2875
2878
|
return usesCommandKey(os) ? `\u2318${key}` : `Ctrl ${key}`;
|
|
2876
2879
|
}
|
|
2880
|
+
|
|
2881
|
+
// src/utils/mobile-app.ts
|
|
2882
|
+
var DOWNLOAD_PAGE_PATH = "/download";
|
|
2883
|
+
function resolveMobileStoreUrl(links, userAgent, maxTouchPoints) {
|
|
2884
|
+
const os = detectVisitorOs(userAgent, { maxTouchPoints });
|
|
2885
|
+
if (os === "ios") return links.appStoreUrl;
|
|
2886
|
+
if (os === "android") return links.googlePlayUrl;
|
|
2887
|
+
return null;
|
|
2888
|
+
}
|
|
2877
2889
|
var PRIORITY_CODES = ["US", "CA", "GB", "AU"];
|
|
2878
2890
|
var COUNTRY_NAME_ALIASES = {
|
|
2879
2891
|
// United States
|
|
@@ -6452,4 +6464,4 @@ function pathMatchesAny(pathname, patterns) {
|
|
|
6452
6464
|
return (patterns ?? []).some((pattern) => matchesPathPattern(pathname, pattern));
|
|
6453
6465
|
}
|
|
6454
6466
|
|
|
6455
|
-
export { ACTIVE_ANCHOR_ATTRIBUTE, ANTHROPIC_SUPPORTED_IMAGE_MIME, AUTO_CONTINUATION_DIRECTIVE_PREFIX, BADGE_PALETTE, BITE_ID_RE, BRAND_MARK_SVGS, CHAT_ATTACHMENT_MARKDOWN_PATTERN, CHAT_ATTACHMENT_VIEW_TOKEN_QUERY_PARAM, CHAT_ATTACHMENT_VIEW_URL_PREFIX, CHAT_ATTACHMENT_VIEW_URL_PREFIX_REGEX_ESCAPED, COMPLIANCE_STANDARDS, CONTENT_REF_GROUPS, CSV_CONTENT_TYPE, CUSTOM_ITEM_ID, DEFAULT_CONTENT_SUFFIXES, DEFAULT_FOLDER_INDEX_FILE, DEFAULT_MIN_FILL_MS, DEFAULT_OS_PLATFORM, DELIVERY_TASK_TYPE_OPTIONS, DESIGN_DOC_READINESS, DESIGN_DOC_READINESS_DISPLAY, DESIGN_PALETTE, DESKTOP_OSES, DEV_SECTION_PARAM_KEYS, DOC_TABLE_TYPES, ELAPSED_MS_FIELD, EMBED_PROXY_OPTIONAL_HEADERS, FIRST_TOUCH_ATTRIBUTION_KEY, FORM_DRAFT_STATUSES, FORM_RESCUE_ATTEMPT_FIELD, FORM_RESCUE_DEBOUNCE_MS, FORM_RESCUE_EVENTS, FORM_RESCUE_MAX_FIELDS, FORM_RESCUE_MAX_VALUE_CHARS, FORM_RESCUE_RESUME_FIELD, FORM_RESCUE_RESUME_PARAM, FORM_RESCUE_SKIP_REASONS, FORM_RESCUE_SUBMIT_KEYS, FORM_RESCUE_VALUE_FIELDS, FullscreenSwitchController, GENERIC_EMAIL_DOMAINS, HEADING_EMOJI_RE, HEX_PATTERN, HONEYPOT_FIELD, HUBSPOT_DO_NOT_COLLECT_FORM_PROPS, HUB_HEADER_OFFSET_PX, HUMANITY_SIGNAL_KEYS, ICON_REGISTRY, IMAGE_FILE_ACCEPT, LOCATION_HASH_SYNC_EVENT, MAX_MONTH_OFFSET, MEDIA_FILE_ACCEPT, MEDIA_TYPE, NO_CLIENT_CACHE, ODS_SPACING_PROPS, ODS_SPACING_TOKENS, ODS_SPACING_TOKEN_PATTERN, ODS_STEM_CLASSES, ODS_STEM_TOKENS, OPENFRAME_CLASSIFICATIONS, OPENFRAME_DEV_SECTIONS, OS_PLATFORMS, PAGE_PARAM_LIMITS, PLATFORM_BRAND, PLATFORM_SURFACE, PLATFORM_THEME, PLAY_ICON_PATH, PROGRAM_META_RENDERERS, RESCUE_FORMS, ROADMAP_STATUS_OPTIONS, SCHEDULING_SLUG_SHAPE, SCROLL_ANCHOR, SCROLL_ANCHOR_WIRE_KEY, SECTION_HERO_ICON_CLASS, SEO_DESCRIPTION_MAX_LENGTH, SEO_TITLE_MAX_LENGTH, SOCIAL_PLATFORM_ALIASES, SOCIAL_PLATFORM_ALT_HOSTS, SOURCE_ICON_NAMES, SOURCE_LABELS_BY_TABLE, STICKY_HEADER_OFFSET_PX, TAILWIND_STEP_TO_ODS_TOKEN, TICKET_OPEN_PARAM, TICKET_STATUS_OPTIONS, TRIAL_DAYS, TRIAL_LABELS, VENDOR_CLASSIFICATIONS, VIEWER_TIMEZONE, WALKTHROUGH_OPEN_QUERY_PARAM, WALKTHROUGH_VIDEO_DISMISS_KEY, analyzeImageColor, announcementDismissCookieName, applyProxyAuth, brandMarkDataUri, buildAutoContinuationDirective, buildChatAttachmentViewUrl, buildDefaultHref, buildDiscussAddendum, buildDocumentTree, buildGroupedSource, buildListUrl, buildOgPlaceholderUrl, buildSuggestionUrl, buildTicketOpenHref, canonicalContentRefType, captureFirstTouchAttribution, captureFormRescueEvent, clamp, clampPageToTotal, classifySocialHost, cleanEmailDomain, clearAnnouncementDismissals, clearDismissCookie, clearEmbedProxyAuth, clearFirstTouchAttribution, clearLegacyAnnouncementCache, clearWalkthroughDismissal, clickupTaskUrl, cn, complianceStandardForName, consumeAccessCode, copyrightLine, createFenceTracker, createHeadingIdDeduper, createSearchParams, createSseSubscription, createUTCTimestamp, csvEscape, deepClone, defineParamSchema, defineRescueForm, delay, deriveActiveColor, deriveHoverColor, designDocReadiness, desktopOsOf, detectDesktopOs, detectVisitorOs, devSectionAnchorId, dismissAnnouncement, dismissWalkthrough, embedAuthedFetch, ensureBiteIds, errorMessage, escapeMarkdownInline, escapeRegExp, evaluateHumanitySignals, eventAudience, extractCardItems, extractDomainFromEmail, extractDominantColor, extractEntityIdFilter, extractHumanitySignals, extractImageEdgeColorAsync, extractItemId, extractItems, extractSections, faqItemAnchor, faqSectionSlug, fetchPriorityProp, fileContentKey, fileMediaType, filledRescueFields, findAnchorElementByNormalizedId, findDocNodeByPath, findHoneypotCopySource, firstParamValue, flattenAssistantContent, formatAbbreviatedNumber, formatAbsoluteDate, formatBioText, formatBytes, formatBytesShort, formatChatAttachmentMarkdownForBubble, formatCitationIndices, formatClassification, formatCompactMetric, formatCompletionLabel, formatCompletionLabelWithBlockers, formatCurrency, formatDate, formatDateRange, formatDateShort, formatDateSlashUTC, formatDateTime, formatDateTimeAt, formatDateTimeYmd, formatDateUTC, formatDateWithTimezone, formatDocName, formatDuration, formatDurationCompact, formatDurationFromMs, formatDurationFromRange, formatDurationMMSS, formatEntryMonthUTC, formatFileSize, formatLargeNumber, formatLegalDate, formatNumber, formatPercent, formatPhoneE164, formatPrice, formatPricingModel, formatProgramDate, formatProgramDateRange, formatProgramTimeRange, formatRelativeTime, formatReleaseDate, formatSingularLookupInvocation, formatTicketFullTimestamp, formatTicketRelativeTime, formatTimeWithTimezone, formatUnderscoreText, formatWebinarTimeMeta, formatWholeDollars, fromKeys, generateBiteId, generateImageSizes, generateRandomString, getAllPlatformBaseDomains, getBestContrastColor, getConfidenceBgClass, getConfidenceBorderClass, getConfidenceColorClass, getConfidenceLabel, getConfidenceLevel, getConfidenceTextClass, getContentRefLabel, getContentRefLabelOrTitleCase, getContrastRatio, getCountryByCode, getCountryByName, getCountryPhoneData, getCurrentPlatform, getDefaultColorForPlatform, getDefaultIconForPlatform, getDeploymentUrl, getDetailedTimeDifference, getDocAncestorNodeIds, getDocSourceDefaultPath, getDynamicIcon, getEmbedProxyAuth, getFirstTouchAttribution, getFlagFromCountryName, getIconComponent, getInitialExpandedNodeIds, getOSIcon, getOSLabel, getOSPlatformId, getOSTypeDefinition, getPersistedProxyEmail, getPlatformAccentColor, getPlatformBackgroundVarName, getPlatformBrand, getPlatformBrandClasses, getPlatformColor, getPlatformConfig, getPlatformDescription, getPlatformDisplayName, getPlatformIcon, getPlatformIconComponent, getPlatformIconComponent as getPlatformLogo, getPlatformProductionUrl, getPlatformSecondaryColor, getPlatformShortName, getPlatformSlogan, getPlatformTextVarName, getPlatformUrl, getProxiedImageUrl, getReadableTextColor, getRequestOrigin, getRescueForm, getSemanticColor, getShellIcon, getShellLabel, getSlackCommunityJoinUrl, getSmallPlatformIcon, getSourceIconName, getSourceLabel, getStatusColorScheme, getTaskTypeLabel, getToolLabel, getToolTypeAliases, getTrendColors, groupSourcesByTable, groupsByTable, hasGenericEmailDomain, hexToRgb2 as hexToRgb, hostMatches, isAnnouncementDismissed, isBadgePaletteKey, isBlankLine, isCrossOriginUrl, isDismissedCookieValue, isExcludedRescueField, isFigmaSlidesUrl, isFormAttemptId, isGenericDomain, isGenericWebsiteDomain, isHumanitySignalKey, isImageMedia, isListedSchedulingName, isLocalUrl, isOSPlatform, isOpenFrameClassification, isOrderedRange, isPublishableMediaType, isRescueEmail, isResumeToken, isScrollSyncedHash, isToday, isValidEmailDomain, isValidPathPattern, isValidSchedulingSlug, isValidToolType, isVendorClassification, isVideoMedia, isWalkthroughDismissed, isWithinMinutes, listRescueForms, makeComposeContentUrl, markdownToPlainText, matchBiteElement, matchesPathPattern, mediaNameKey, nameInitials, navigateSamePageHash, normalizeAnchorId, normalizeDocPath, normalizeDomain, normalizeHashFragment, normalizeIconKey, normalizeOSType, normalizeSchedulingSlug, normalizeSocialPlatform, normalizeToolType, normalizeToolTypeWithFallback, openInNewTab, orderContentRefTypes, pageCount, parseAttributionFromUrl, parseFaqHash, parseFormDraftSaveRequest, parseSchedulingLinkName, parseSchemaParams, parseScrollAnchor, parseWireCommandOverride, partitionMediaByType, pathMatchesAny, pathToNodeId, personFirstName, personInitials, pick, pickBadgePaletteColor, pickReadableTextColor, pickSocialLink, platformAccentToken, platformAccentVarName, platformColors, platformDescriptions, platformDisplayNames, platformHexColors, platformIconNames, platformIcons, platformSecondaryToken, platformSecondaryVarName, platformShortNames, platformSlogans, positiveInt, programDateInstant, programMetaFormatters, programMetaLine, programStr, readDismissCookie, readLeadingDecisionFrame, recordCountLabel, releaseTypeToBadgeColor, replaceLocationHash, rescueCompletionPct, resolveContentFetchPath, resolveRedirectTarget, resolveReleaseCover, rgbToHex, sanitizeLandingUrl, sanitizeRescueFieldName, sanitizeRescueValues, sanitizeTitleForChat, scanHeadings, schedulingAudienceKey, scrollElementIntoView, serializeJsonLd, serializeSchemaParams, setEmbedAuthAdapter, setEmbedProxyAuth, shortcutLabel, shouldIncludeInUrl, shouldProxyImage, singleInitial, skipsImageOptimizer, slugifyHeadingBase, slugifyHeadingText, socialPlatformHosts, sortTreeChildren, splitCsvEnv, stripChatAttachmentMarkdown, stripFolderIndexFromPath, stripHeadingEmojis, stripHtml, stripInlineMarkdown, switchPlatformTheme, titleCaseFromSlug, toClaudeEmbedUrl, toClaudeMirrorPath, toCsv, toDocSlug, toFigmaEmbedUrl, toFigmaOriginalUrl, toGoogleSheetsEmbedUrl, toGoogleSheetsOriginalUrl, toToolLabel, transformPlatformConfigsToOptions, truncateString, urlPathLooksLikeSvg, usePlatformColors, usesCommandKey, validateAccessCode, validateAndConsumeAccessCode, validateEmailDomain, validateEmailDomainList, validatePhoneNumber, walkthroughDismissCookieName, webinarTiming, withFirstTouchAttribution, withQuery, withWalkthroughOpenParam, writeDismissCookie };
|
|
6467
|
+
export { ACTIVE_ANCHOR_ATTRIBUTE, ANTHROPIC_SUPPORTED_IMAGE_MIME, AUTO_CONTINUATION_DIRECTIVE_PREFIX, BADGE_PALETTE, BITE_ID_RE, BRAND_MARK_SVGS, CHAT_ATTACHMENT_MARKDOWN_PATTERN, CHAT_ATTACHMENT_VIEW_TOKEN_QUERY_PARAM, CHAT_ATTACHMENT_VIEW_URL_PREFIX, CHAT_ATTACHMENT_VIEW_URL_PREFIX_REGEX_ESCAPED, COMPLIANCE_STANDARDS, CONTENT_REF_GROUPS, CSV_CONTENT_TYPE, CUSTOM_ITEM_ID, DEFAULT_CONTENT_SUFFIXES, DEFAULT_FOLDER_INDEX_FILE, DEFAULT_MIN_FILL_MS, DEFAULT_OS_PLATFORM, DELIVERY_TASK_TYPE_OPTIONS, DESIGN_DOC_READINESS, DESIGN_DOC_READINESS_DISPLAY, DESIGN_PALETTE, DESKTOP_OSES, DEV_SECTION_PARAM_KEYS, DOC_TABLE_TYPES, DOWNLOAD_PAGE_PATH, ELAPSED_MS_FIELD, EMBED_PROXY_OPTIONAL_HEADERS, FIRST_TOUCH_ATTRIBUTION_KEY, FORM_DRAFT_STATUSES, FORM_RESCUE_ATTEMPT_FIELD, FORM_RESCUE_DEBOUNCE_MS, FORM_RESCUE_EVENTS, FORM_RESCUE_MAX_FIELDS, FORM_RESCUE_MAX_VALUE_CHARS, FORM_RESCUE_RESUME_FIELD, FORM_RESCUE_RESUME_PARAM, FORM_RESCUE_SKIP_REASONS, FORM_RESCUE_SUBMIT_KEYS, FORM_RESCUE_VALUE_FIELDS, FullscreenSwitchController, GENERIC_EMAIL_DOMAINS, HEADING_EMOJI_RE, HEX_PATTERN, HONEYPOT_FIELD, HUBSPOT_DO_NOT_COLLECT_FORM_PROPS, HUB_HEADER_OFFSET_PX, HUMANITY_SIGNAL_KEYS, ICON_REGISTRY, IMAGE_FILE_ACCEPT, LOCATION_HASH_SYNC_EVENT, MAX_MONTH_OFFSET, MEDIA_FILE_ACCEPT, MEDIA_TYPE, NO_CLIENT_CACHE, ODS_SPACING_PROPS, ODS_SPACING_TOKENS, ODS_SPACING_TOKEN_PATTERN, ODS_STEM_CLASSES, ODS_STEM_TOKENS, OPENFRAME_CLASSIFICATIONS, OPENFRAME_DEV_SECTIONS, OS_PLATFORMS, PAGE_PARAM_LIMITS, PLATFORM_BRAND, PLATFORM_SURFACE, PLATFORM_THEME, PLAY_ICON_PATH, PROGRAM_META_RENDERERS, RESCUE_FORMS, ROADMAP_STATUS_OPTIONS, SCHEDULING_SLUG_SHAPE, SCROLL_ANCHOR, SCROLL_ANCHOR_WIRE_KEY, SECTION_HERO_ICON_CLASS, SEO_DESCRIPTION_MAX_LENGTH, SEO_TITLE_MAX_LENGTH, SOCIAL_PLATFORM_ALIASES, SOCIAL_PLATFORM_ALT_HOSTS, SOURCE_ICON_NAMES, SOURCE_LABELS_BY_TABLE, STICKY_HEADER_OFFSET_PX, TAILWIND_STEP_TO_ODS_TOKEN, TICKET_OPEN_PARAM, TICKET_STATUS_OPTIONS, TRIAL_DAYS, TRIAL_LABELS, VENDOR_CLASSIFICATIONS, VIEWER_TIMEZONE, WALKTHROUGH_OPEN_QUERY_PARAM, WALKTHROUGH_VIDEO_DISMISS_KEY, analyzeImageColor, announcementDismissCookieName, applyProxyAuth, brandMarkDataUri, buildAutoContinuationDirective, buildChatAttachmentViewUrl, buildDefaultHref, buildDiscussAddendum, buildDocumentTree, buildGroupedSource, buildListUrl, buildOgPlaceholderUrl, buildSuggestionUrl, buildTicketOpenHref, canonicalContentRefType, captureFirstTouchAttribution, captureFormRescueEvent, clamp, clampPageToTotal, classifySocialHost, cleanEmailDomain, clearAnnouncementDismissals, clearDismissCookie, clearEmbedProxyAuth, clearFirstTouchAttribution, clearLegacyAnnouncementCache, clearWalkthroughDismissal, clickupTaskUrl, cn, complianceStandardForName, consumeAccessCode, copyrightLine, createFenceTracker, createHeadingIdDeduper, createSearchParams, createSseSubscription, createUTCTimestamp, csvEscape, deepClone, defineParamSchema, defineRescueForm, delay, deriveActiveColor, deriveHoverColor, designDocReadiness, desktopOsOf, detectDesktopOs, detectVisitorOs, devSectionAnchorId, dismissAnnouncement, dismissWalkthrough, embedAuthedFetch, ensureBiteIds, errorMessage, escapeMarkdownInline, escapeRegExp, evaluateHumanitySignals, eventAudience, extractCardItems, extractDomainFromEmail, extractDominantColor, extractEntityIdFilter, extractHumanitySignals, extractImageEdgeColorAsync, extractItemId, extractItems, extractSections, faqItemAnchor, faqSectionSlug, fetchPriorityProp, fileContentKey, fileMediaType, filledRescueFields, findAnchorElementByNormalizedId, findDocNodeByPath, findHoneypotCopySource, firstParamValue, flattenAssistantContent, formatAbbreviatedNumber, formatAbsoluteDate, formatBioText, formatBytes, formatBytesShort, formatChatAttachmentMarkdownForBubble, formatCitationIndices, formatClassification, formatCompactMetric, formatCompletionLabel, formatCompletionLabelWithBlockers, formatCurrency, formatDate, formatDateRange, formatDateShort, formatDateSlashUTC, formatDateTime, formatDateTimeAt, formatDateTimeYmd, formatDateUTC, formatDateWithTimezone, formatDocName, formatDuration, formatDurationCompact, formatDurationFromMs, formatDurationFromRange, formatDurationMMSS, formatEntryMonthUTC, formatFileSize, formatLargeNumber, formatLegalDate, formatNumber, formatPercent, formatPhoneE164, formatPrice, formatPricingModel, formatProgramDate, formatProgramDateRange, formatProgramTimeRange, formatRelativeTime, formatReleaseDate, formatSingularLookupInvocation, formatTicketFullTimestamp, formatTicketRelativeTime, formatTimeWithTimezone, formatUnderscoreText, formatUrlForDisplay, formatWebinarTimeMeta, formatWholeDollars, fromKeys, generateBiteId, generateImageSizes, generateRandomString, getAllPlatformBaseDomains, getBestContrastColor, getConfidenceBgClass, getConfidenceBorderClass, getConfidenceColorClass, getConfidenceLabel, getConfidenceLevel, getConfidenceTextClass, getContentRefLabel, getContentRefLabelOrTitleCase, getContrastRatio, getCountryByCode, getCountryByName, getCountryPhoneData, getCurrentPlatform, getDefaultColorForPlatform, getDefaultIconForPlatform, getDeploymentUrl, getDetailedTimeDifference, getDocAncestorNodeIds, getDocSourceDefaultPath, getDynamicIcon, getEmbedProxyAuth, getFirstTouchAttribution, getFlagFromCountryName, getIconComponent, getInitialExpandedNodeIds, getOSIcon, getOSLabel, getOSPlatformId, getOSTypeDefinition, getPersistedProxyEmail, getPlatformAccentColor, getPlatformBackgroundVarName, getPlatformBrand, getPlatformBrandClasses, getPlatformColor, getPlatformConfig, getPlatformDescription, getPlatformDisplayName, getPlatformIcon, getPlatformIconComponent, getPlatformIconComponent as getPlatformLogo, getPlatformProductionUrl, getPlatformSecondaryColor, getPlatformShortName, getPlatformSlogan, getPlatformTextVarName, getPlatformUrl, getProxiedImageUrl, getReadableTextColor, getRequestOrigin, getRescueForm, getSemanticColor, getShellIcon, getShellLabel, getSlackCommunityJoinUrl, getSmallPlatformIcon, getSourceIconName, getSourceLabel, getStatusColorScheme, getTaskTypeLabel, getToolLabel, getToolTypeAliases, getTrendColors, groupSourcesByTable, groupsByTable, hasGenericEmailDomain, hexToRgb2 as hexToRgb, hostMatches, isAnnouncementDismissed, isBadgePaletteKey, isBlankLine, isCrossOriginUrl, isDismissedCookieValue, isExcludedRescueField, isFigmaSlidesUrl, isFormAttemptId, isGenericDomain, isGenericWebsiteDomain, isHumanitySignalKey, isImageMedia, isListedSchedulingName, isLocalUrl, isOSPlatform, isOpenFrameClassification, isOrderedRange, isPublishableMediaType, isRescueEmail, isResumeToken, isScrollSyncedHash, isToday, isValidEmailDomain, isValidPathPattern, isValidSchedulingSlug, isValidToolType, isVendorClassification, isVideoMedia, isWalkthroughDismissed, isWithinMinutes, listRescueForms, makeComposeContentUrl, markdownToPlainText, matchBiteElement, matchesPathPattern, mediaNameKey, nameInitials, navigateSamePageHash, normalizeAnchorId, normalizeDocPath, normalizeDomain, normalizeHashFragment, normalizeIconKey, normalizeOSType, normalizeSchedulingSlug, normalizeSocialPlatform, normalizeToolType, normalizeToolTypeWithFallback, openInNewTab, orderContentRefTypes, pageCount, parseAttributionFromUrl, parseFaqHash, parseFormDraftSaveRequest, parseSchedulingLinkName, parseSchemaParams, parseScrollAnchor, parseWireCommandOverride, partitionMediaByType, pathMatchesAny, pathToNodeId, personFirstName, personInitials, pick, pickBadgePaletteColor, pickReadableTextColor, pickSocialLink, platformAccentToken, platformAccentVarName, platformColors, platformDescriptions, platformDisplayNames, platformHexColors, platformIconNames, platformIcons, platformSecondaryToken, platformSecondaryVarName, platformShortNames, platformSlogans, positiveInt, programDateInstant, programMetaFormatters, programMetaLine, programStr, readDismissCookie, readLeadingDecisionFrame, recordCountLabel, releaseTypeToBadgeColor, replaceLocationHash, rescueCompletionPct, resolveContentFetchPath, resolveMobileStoreUrl, resolveRedirectTarget, resolveReleaseCover, rgbToHex, sanitizeLandingUrl, sanitizeRescueFieldName, sanitizeRescueValues, sanitizeTitleForChat, scanHeadings, schedulingAudienceKey, scrollElementIntoView, serializeJsonLd, serializeSchemaParams, setEmbedAuthAdapter, setEmbedProxyAuth, shortcutLabel, shouldIncludeInUrl, shouldProxyImage, singleInitial, skipsImageOptimizer, slugifyHeadingBase, slugifyHeadingText, socialPlatformHosts, sortTreeChildren, splitCsvEnv, stripChatAttachmentMarkdown, stripFolderIndexFromPath, stripHeadingEmojis, stripHtml, stripInlineMarkdown, switchPlatformTheme, titleCaseFromSlug, toClaudeEmbedUrl, toClaudeMirrorPath, toCsv, toDocSlug, toFigmaEmbedUrl, toFigmaOriginalUrl, toGoogleSheetsEmbedUrl, toGoogleSheetsOriginalUrl, toToolLabel, transformPlatformConfigsToOptions, truncateString, urlPathLooksLikeSvg, usePlatformColors, usesCommandKey, validateAccessCode, validateAndConsumeAccessCode, validateEmailDomain, validateEmailDomainList, validatePhoneNumber, walkthroughDismissCookieName, webinarTiming, withFirstTouchAttribution, withQuery, withWalkthroughOpenParam, writeDismissCookie };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils/visitor-os.ts
|
|
4
|
+
var HINT_PLATFORMS = {
|
|
5
|
+
macos: "mac",
|
|
6
|
+
windows: "windows",
|
|
7
|
+
linux: "linux",
|
|
8
|
+
"chrome os": "linux",
|
|
9
|
+
"chromium os": "linux",
|
|
10
|
+
android: "android",
|
|
11
|
+
ios: "ios"
|
|
12
|
+
};
|
|
13
|
+
function detectVisitorOs(userAgent, hints = {}) {
|
|
14
|
+
const touchMac = (os) => os === "mac" && (hints.maxTouchPoints ?? 0) > 1 ? "ios" : os;
|
|
15
|
+
const hinted = HINT_PLATFORMS[(hints.platform ?? "").replace(/"/g, "").trim().toLowerCase()];
|
|
16
|
+
if (hinted) return touchMac(hinted);
|
|
17
|
+
const ua = userAgent ?? "";
|
|
18
|
+
if (!ua) return null;
|
|
19
|
+
if (/iPhone|iPad|iPod/i.test(ua)) return "ios";
|
|
20
|
+
if (/Android/i.test(ua)) return "android";
|
|
21
|
+
if (/Windows NT|Win64|Win32/i.test(ua)) return "windows";
|
|
22
|
+
if (/Macintosh|Mac OS X/i.test(ua)) return touchMac("mac");
|
|
23
|
+
if (/Linux|X11|CrOS/i.test(ua)) return "linux";
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/utils/mobile-app.ts
|
|
28
|
+
var DOWNLOAD_PAGE_PATH = "/download";
|
|
29
|
+
function resolveMobileStoreUrl(links, userAgent, maxTouchPoints) {
|
|
30
|
+
const os = detectVisitorOs(userAgent, { maxTouchPoints });
|
|
31
|
+
if (os === "ios") return links.appStoreUrl;
|
|
32
|
+
if (os === "android") return links.googlePlayUrl;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
exports.DOWNLOAD_PAGE_PATH = DOWNLOAD_PAGE_PATH;
|
|
37
|
+
exports.resolveMobileStoreUrl = resolveMobileStoreUrl;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The download page's address, and what a server needs to send a phone on to
|
|
3
|
+
* its store. No link lives here: the store listings, the installer links and
|
|
4
|
+
* the install link the QR code encodes are all the server's (`DownloadsPublic`,
|
|
5
|
+
* read through `useDownloads`).
|
|
6
|
+
*
|
|
7
|
+
* Pure and server-safe; also published as the granular subpath
|
|
8
|
+
* `./utils/mobile-app`, for an Edge proxy and other server-only consumers.
|
|
9
|
+
*/
|
|
10
|
+
import type { MobileAppLinks } from '../types/downloads';
|
|
11
|
+
/** The website's download page: every installer and the mobile app. */
|
|
12
|
+
export declare const DOWNLOAD_PAGE_PATH = "/download";
|
|
13
|
+
/**
|
|
14
|
+
* The store for a phone or tablet, or `null` for every other system and for a
|
|
15
|
+
* store the server names no listing for (`links` is the server's answer).
|
|
16
|
+
*
|
|
17
|
+
* The server passes the request's User-Agent (the install link's redirect).
|
|
18
|
+
* A browser may also pass `navigator.maxTouchPoints`, the only signal that tells
|
|
19
|
+
* an iPad asking for the desktop site (it calls itself a Macintosh) from a real
|
|
20
|
+
* Mac; the server cannot, so that iPad lands on the download page and its badges.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveMobileStoreUrl(links: MobileAppLinks, userAgent: string | null | undefined, maxTouchPoints?: number): string | null;
|
|
23
|
+
//# sourceMappingURL=mobile-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile-app.d.ts","sourceRoot":"","sources":["../../src/utils/mobile-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,GAAG,IAAI,CAKf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/utils/visitor-os.ts
|
|
2
|
+
var HINT_PLATFORMS = {
|
|
3
|
+
macos: "mac",
|
|
4
|
+
windows: "windows",
|
|
5
|
+
linux: "linux",
|
|
6
|
+
"chrome os": "linux",
|
|
7
|
+
"chromium os": "linux",
|
|
8
|
+
android: "android",
|
|
9
|
+
ios: "ios"
|
|
10
|
+
};
|
|
11
|
+
function detectVisitorOs(userAgent, hints = {}) {
|
|
12
|
+
const touchMac = (os) => os === "mac" && (hints.maxTouchPoints ?? 0) > 1 ? "ios" : os;
|
|
13
|
+
const hinted = HINT_PLATFORMS[(hints.platform ?? "").replace(/"/g, "").trim().toLowerCase()];
|
|
14
|
+
if (hinted) return touchMac(hinted);
|
|
15
|
+
const ua = userAgent ?? "";
|
|
16
|
+
if (!ua) return null;
|
|
17
|
+
if (/iPhone|iPad|iPod/i.test(ua)) return "ios";
|
|
18
|
+
if (/Android/i.test(ua)) return "android";
|
|
19
|
+
if (/Windows NT|Win64|Win32/i.test(ua)) return "windows";
|
|
20
|
+
if (/Macintosh|Mac OS X/i.test(ua)) return touchMac("mac");
|
|
21
|
+
if (/Linux|X11|CrOS/i.test(ua)) return "linux";
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/utils/mobile-app.ts
|
|
26
|
+
var DOWNLOAD_PAGE_PATH = "/download";
|
|
27
|
+
function resolveMobileStoreUrl(links, userAgent, maxTouchPoints) {
|
|
28
|
+
const os = detectVisitorOs(userAgent, { maxTouchPoints });
|
|
29
|
+
if (os === "ios") return links.appStoreUrl;
|
|
30
|
+
if (os === "android") return links.googlePlayUrl;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { DOWNLOAD_PAGE_PATH, resolveMobileStoreUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flamingo-stack/openframe-frontend-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.722",
|
|
4
4
|
"description": "Shared design system and components for all Flamingo platforms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -255,6 +255,12 @@
|
|
|
255
255
|
"require": "./dist/utils/form-rescue.cjs",
|
|
256
256
|
"default": "./dist/utils/form-rescue.js"
|
|
257
257
|
},
|
|
258
|
+
"./utils/mobile-app": {
|
|
259
|
+
"types": "./dist/utils/mobile-app.d.ts",
|
|
260
|
+
"import": "./dist/utils/mobile-app.js",
|
|
261
|
+
"require": "./dist/utils/mobile-app.cjs",
|
|
262
|
+
"default": "./dist/utils/mobile-app.js"
|
|
263
|
+
},
|
|
258
264
|
"./utils/platform-identity": {
|
|
259
265
|
"types": "./dist/utils/platform-identity.d.ts",
|
|
260
266
|
"import": "./dist/utils/platform-identity.js",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { formatUrlForDisplay } from '../../../utils/format';
|
|
3
4
|
import { ExternalLinkIcon, InfoCircleIcon } from '../../icons-v2-generated';
|
|
4
5
|
import { Button } from '../../ui/button/button';
|
|
5
6
|
import { RemoteSessionOrgLogo } from './remote-session-parts';
|
|
@@ -21,11 +22,6 @@ export interface RemoteAccessRequestCardProps {
|
|
|
21
22
|
onOpenSite?: (url: string) => void;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
/** `https://www.techflow.com/` -> `www.techflow.com`. */
|
|
25
|
-
function displaySiteUrl(url: string): string {
|
|
26
|
-
return url.replace(/^https?:\/\//, '').replace(/\/$/, '');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
25
|
/** The end user's consent dialog for a remote access request: who is asking, what they get, Decline / Allow Access. */
|
|
30
26
|
export function RemoteAccessRequestCard({
|
|
31
27
|
party,
|
|
@@ -47,7 +43,11 @@ export function RemoteAccessRequestCard({
|
|
|
47
43
|
<RemoteSessionOrgLogo name={organizationName} logoUrl={party.organizationLogoUrl} sizePx={64} />
|
|
48
44
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
49
45
|
<p className="w-full truncate text-ods-text-primary text-h3">{organizationName}</p>
|
|
50
|
-
{siteUrl &&
|
|
46
|
+
{siteUrl && (
|
|
47
|
+
<p className="w-full truncate text-ods-text-secondary text-h4">
|
|
48
|
+
{formatUrlForDisplay(siteUrl, { keepWww: true })}
|
|
49
|
+
</p>
|
|
50
|
+
)}
|
|
51
51
|
</div>
|
|
52
52
|
{openableSite && onOpenSite && (
|
|
53
53
|
<Button variant="outline" size="icon" aria-label="Open website" onClick={() => onOpenSite(openableSite)}>
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<DownloadAppsPage>`: every way to install OpenFrame, on one page.
|
|
5
|
+
*
|
|
6
|
+
* - The desktop app: every system at once, one card each (a download page
|
|
7
|
+
* lists its platforms; it does not hide them behind tabs, so nothing moves
|
|
8
|
+
* when a visitor looks for another system). In a card the installer is THE
|
|
9
|
+
* action (the other architecture is a quieter button) and each package
|
|
10
|
+
* manager is one line under it, with its copy button.
|
|
11
|
+
* - The mobile app: the store badges, and the install QR code beside them. The
|
|
12
|
+
* code and the address it encodes are the server's (`mobile.install`): the
|
|
13
|
+
* website's install link, which the server answers with a redirect to the
|
|
14
|
+
* phone's store. This page redirects nobody, and with no install link from
|
|
15
|
+
* the server it draws no code.
|
|
16
|
+
* - `web`: the way in for someone who installs nothing (the website's trial
|
|
17
|
+
* signup). The host names the words and the button; the page draws the card,
|
|
18
|
+
* in the frame of every other card on it.
|
|
19
|
+
*
|
|
20
|
+
* DATA: all of it is the server's answer (`DownloadsPublic`), read by
|
|
21
|
+
* `useDownloads` against `endpoint` (default `DOWNLOADS_API_PATH`; embedders
|
|
22
|
+
* pass their `/content` proxy path), the one request every download button on
|
|
23
|
+
* the page shares. `initialData` (hub SSR) is the answer, with no fetch. The
|
|
24
|
+
* lib holds no link of its own and no fallback: the page ADAPTS, so a system
|
|
25
|
+
* with no installer and no command has no card, a store with no listing has no
|
|
26
|
+
* badge, and a section with nothing to offer is not drawn.
|
|
27
|
+
*
|
|
28
|
+
* A HOST DECIDES `showDesktop`: the website shows the desktop app only to a
|
|
29
|
+
* visitor whose main action is the download; the desktop shell hides it (it is
|
|
30
|
+
* the app already running).
|
|
31
|
+
*
|
|
32
|
+
* The chrome is the canonical `PageShell` + frozen `PageLayout`.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
36
|
+
import { useRouter } from '../../embed-shims/next-navigation';
|
|
37
|
+
import { useCopyToClipboard } from '../../hooks/use-copy-to-clipboard';
|
|
38
|
+
import { useDownloads } from '../../hooks/use-downloads';
|
|
39
|
+
import {
|
|
40
|
+
DESKTOP_OS_LABELS,
|
|
41
|
+
DOWNLOAD_ARCHITECTURE_LABELS,
|
|
42
|
+
DOWNLOADS_API_PATH,
|
|
43
|
+
DOWNLOADS_MOBILE_TAGLINE,
|
|
44
|
+
DOWNLOADS_MOBILE_TITLE,
|
|
45
|
+
DOWNLOADS_TAGLINE,
|
|
46
|
+
DOWNLOADS_TITLE,
|
|
47
|
+
type AppDownload,
|
|
48
|
+
type DownloadActionEvent,
|
|
49
|
+
type DownloadsPublic,
|
|
50
|
+
type MobileAppLinks,
|
|
51
|
+
} from '../../types/downloads';
|
|
52
|
+
import { cn } from '../../utils/cn';
|
|
53
|
+
import { formatUrlForDisplay } from '../../utils/format';
|
|
54
|
+
import { DESKTOP_OSES, type DesktopOs } from '../../utils/visitor-os';
|
|
55
|
+
import { CommandBox } from '../features/command-box';
|
|
56
|
+
import { AppleLogoIcon } from '../icons-v2-generated/brand-logos/apple-logo-icon';
|
|
57
|
+
import { WindowsLogoGreyIcon } from '../icons-v2-generated/brand-logos/windows-logo-grey-icon';
|
|
58
|
+
import { Download01Icon } from '../icons-v2-generated/interface/download-01-icon';
|
|
59
|
+
import { PageShell } from '../layout/article-detail-layout';
|
|
60
|
+
import { PageLayout } from '../layout/page-layout';
|
|
61
|
+
import { UnifiedSkeleton } from '../loading/unified-skeleton';
|
|
62
|
+
import { Button } from '../ui/button/button';
|
|
63
|
+
import { LoadError } from '../ui/error-state';
|
|
64
|
+
import { MobileAppQr } from '../ui/mobile-app-qr';
|
|
65
|
+
import { StoreBadgeLinks } from '../ui/store-badges';
|
|
66
|
+
|
|
67
|
+
export interface DownloadAppsPageProps {
|
|
68
|
+
/** GET endpoint for the public projection. Default `DOWNLOADS_API_PATH`. */
|
|
69
|
+
endpoint?: string;
|
|
70
|
+
/** Optional SSR hydrate (hub server-read): skips the initial client fetch. */
|
|
71
|
+
initialData?: DownloadsPublic;
|
|
72
|
+
/** Render the standalone `<PageShell>`. Default true. Pass false when the host
|
|
73
|
+
* layout already provides the page container (e.g. openframe-frontend). */
|
|
74
|
+
shell?: boolean;
|
|
75
|
+
/** Back-button config. Default: none. */
|
|
76
|
+
backButton?: { label?: string; href?: string } | false;
|
|
77
|
+
/** Page title. Default by what is offered (`DOWNLOADS_TITLE` / `DOWNLOADS_MOBILE_TITLE`). */
|
|
78
|
+
title?: string;
|
|
79
|
+
/** Page subtitle. Default by what is offered. */
|
|
80
|
+
subtitle?: string;
|
|
81
|
+
/** Offer the desktop app. Default true. False: the mobile app only. */
|
|
82
|
+
showDesktop?: boolean;
|
|
83
|
+
/** Open the store listings in a new tab. Default true. */
|
|
84
|
+
openStoresInNewTab?: boolean;
|
|
85
|
+
/** Called when a visitor downloads an installer or copies an install command (the host's analytics). */
|
|
86
|
+
onDownloadAction?: (event: DownloadActionEvent) => void;
|
|
87
|
+
/** The closing card, under the mobile app: using OpenFrame without installing anything. Default: none. */
|
|
88
|
+
web?: DownloadAppsWebOption;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The "no install" card: the host's words and its own button (the page holds no signup link). */
|
|
92
|
+
export interface DownloadAppsWebOption {
|
|
93
|
+
title: string;
|
|
94
|
+
description: string;
|
|
95
|
+
/** The host's button (the website's trial signup). */
|
|
96
|
+
action: ReactNode;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const SECTIONS_CLASS = 'flex flex-col gap-[var(--spacing-system-xlf)]';
|
|
100
|
+
const SECTION_CLASS = 'flex flex-col gap-[var(--spacing-system-lf)]';
|
|
101
|
+
const CARDS_GRID_CLASS = 'grid grid-cols-1 gap-[var(--spacing-system-lf)] content-md:grid-cols-2';
|
|
102
|
+
/** One card takes the row; two or more share it. */
|
|
103
|
+
const cardsGridClass = (cards: number) => (cards > 1 ? CARDS_GRID_CLASS : 'grid grid-cols-1');
|
|
104
|
+
const CARD_CLASS =
|
|
105
|
+
'flex flex-col gap-[var(--spacing-system-lf)] rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-lf)]';
|
|
106
|
+
const CARD_HEADER_CLASS = 'flex items-center gap-[var(--spacing-system-sf)]';
|
|
107
|
+
const BUTTONS_CLASS = 'flex flex-wrap gap-[var(--spacing-system-sf)]';
|
|
108
|
+
const COMMANDS_CLASS = 'flex flex-col gap-[var(--spacing-system-mf)]';
|
|
109
|
+
|
|
110
|
+
const OS_ICONS: Partial<Record<DesktopOs, ComponentType<{ className?: string }>>> = {
|
|
111
|
+
mac: AppleLogoIcon,
|
|
112
|
+
windows: WindowsLogoGreyIcon,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** A card's heading row: a small logo, the name, and one quiet line of detail at the far end. */
|
|
116
|
+
function CardHeader({
|
|
117
|
+
icon: Icon,
|
|
118
|
+
title,
|
|
119
|
+
detail,
|
|
120
|
+
}: {
|
|
121
|
+
icon?: ComponentType<{ className?: string }>;
|
|
122
|
+
title: string;
|
|
123
|
+
detail?: string;
|
|
124
|
+
}) {
|
|
125
|
+
return (
|
|
126
|
+
<div className={CARD_HEADER_CLASS}>
|
|
127
|
+
{Icon && <Icon className="h-6 w-6 shrink-0 text-ods-text-secondary" />}
|
|
128
|
+
<h3 className="m-0 text-ods-text-primary text-h3">{title}</h3>
|
|
129
|
+
{detail && <p className="m-0 ml-auto text-right text-ods-text-secondary text-h6">{detail}</p>}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface SystemRows {
|
|
135
|
+
os: DesktopOs;
|
|
136
|
+
installers: AppDownload[];
|
|
137
|
+
commands: AppDownload[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The rows per system, in the order they arrived. The page adapts to what the
|
|
142
|
+
* deployment names: a system is offered when it has an installer OR a package
|
|
143
|
+
* manager command, and a system with neither has no card at all.
|
|
144
|
+
*/
|
|
145
|
+
function rowsBySystem(downloads: readonly AppDownload[]): SystemRows[] {
|
|
146
|
+
return DESKTOP_OSES.map(os => ({
|
|
147
|
+
os,
|
|
148
|
+
installers: downloads.filter(d => d.os === os && d.kind === 'binary' && d.url),
|
|
149
|
+
commands: downloads.filter(d => d.os === os && d.kind === 'command' && d.command),
|
|
150
|
+
})).filter(system => system.installers.length > 0 || system.commands.length > 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function installerLabel(download: AppDownload, siblings: number): string {
|
|
154
|
+
if (download.label) return download.label;
|
|
155
|
+
const label = `Download for ${DESKTOP_OS_LABELS[download.os]}`;
|
|
156
|
+
const architecture = download.architecture ? DOWNLOAD_ARCHITECTURE_LABELS[download.architecture] : null;
|
|
157
|
+
// One installer for a system needs no qualifier; two are told apart by their architecture.
|
|
158
|
+
return siblings > 1 && architecture ? `${label} (${architecture})` : label;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** One system's card: its logo and name, its installers side by side, then each package manager as one line. */
|
|
162
|
+
function SystemCard({
|
|
163
|
+
system,
|
|
164
|
+
onDownloadAction,
|
|
165
|
+
}: {
|
|
166
|
+
system: SystemRows;
|
|
167
|
+
onDownloadAction?: DownloadAppsPageProps['onDownloadAction'];
|
|
168
|
+
}) {
|
|
169
|
+
const { copy } = useCopyToClipboard({
|
|
170
|
+
successTitle: 'Copied',
|
|
171
|
+
successDescription: 'Paste it into your terminal and press Return.',
|
|
172
|
+
});
|
|
173
|
+
const requirements = [...new Set(system.installers.map(d => d.minOsLabel).filter((l): l is string => !!l))];
|
|
174
|
+
const report = (action: DownloadActionEvent['action'], row: AppDownload) =>
|
|
175
|
+
onDownloadAction?.({ action, id: row.id, os: row.os, architecture: row.architecture });
|
|
176
|
+
return (
|
|
177
|
+
<div className={CARD_CLASS}>
|
|
178
|
+
<CardHeader
|
|
179
|
+
icon={OS_ICONS[system.os]}
|
|
180
|
+
title={DESKTOP_OS_LABELS[system.os]}
|
|
181
|
+
detail={requirements.length > 0 ? `Requires ${requirements.join(' or ')}` : undefined}
|
|
182
|
+
/>
|
|
183
|
+
{system.installers.length > 0 && (
|
|
184
|
+
<div className={BUTTONS_CLASS}>
|
|
185
|
+
{system.installers.map((download, index) => (
|
|
186
|
+
<Button
|
|
187
|
+
key={download.id}
|
|
188
|
+
variant={index === 0 ? 'accent' : 'outline'}
|
|
189
|
+
href={download.url ?? undefined}
|
|
190
|
+
download
|
|
191
|
+
leftIcon={<Download01Icon className="h-5 w-5" />}
|
|
192
|
+
onClick={() => report('installer', download)}
|
|
193
|
+
>
|
|
194
|
+
{installerLabel(download, system.installers.length)}
|
|
195
|
+
</Button>
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
)}
|
|
199
|
+
{system.commands.length > 0 && (
|
|
200
|
+
<div className={COMMANDS_CLASS}>
|
|
201
|
+
{system.commands.map(row =>
|
|
202
|
+
row.command ? (
|
|
203
|
+
<CommandBox
|
|
204
|
+
key={row.id}
|
|
205
|
+
title={row.label ?? undefined}
|
|
206
|
+
command={row.command}
|
|
207
|
+
maxLines={1}
|
|
208
|
+
commandClassName="text-ods-accent"
|
|
209
|
+
copyAriaLabel={`Copy the ${row.label ?? 'install'} command`}
|
|
210
|
+
onCopy={() => {
|
|
211
|
+
void copy(row.command as string);
|
|
212
|
+
report('command', row);
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
215
|
+
) : null,
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** The closing card: one line of what the web offers, and the host's button at the far end. */
|
|
224
|
+
function WebCard({ web }: { web: DownloadAppsWebOption }) {
|
|
225
|
+
return (
|
|
226
|
+
<section
|
|
227
|
+
aria-labelledby="download-web"
|
|
228
|
+
className={cn(CARD_CLASS, 'content-md:flex-row content-md:items-center content-md:justify-between')}
|
|
229
|
+
>
|
|
230
|
+
<div className="flex flex-col gap-[var(--spacing-system-xsf)]">
|
|
231
|
+
<h2 id="download-web" className="m-0 text-ods-text-primary text-h3">
|
|
232
|
+
{web.title}
|
|
233
|
+
</h2>
|
|
234
|
+
<p className="m-0 text-ods-text-secondary text-h4">{web.description}</p>
|
|
235
|
+
</div>
|
|
236
|
+
<div className="shrink-0">{web.action}</div>
|
|
237
|
+
</section>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** A section's cards while the server's answer loads: the same grid and card frame, a button's box in each. */
|
|
242
|
+
function CardsSkeleton({ label }: { label: string }) {
|
|
243
|
+
return (
|
|
244
|
+
<div className={CARDS_GRID_CLASS} aria-busy="true" aria-label={label}>
|
|
245
|
+
{[0, 1].map(card => (
|
|
246
|
+
<div key={card} className={CARD_CLASS}>
|
|
247
|
+
<UnifiedSkeleton className="h-6 w-32 rounded" />
|
|
248
|
+
<UnifiedSkeleton className="h-12 w-56 rounded-md" />
|
|
249
|
+
<UnifiedSkeleton className="h-14 w-full rounded-md" />
|
|
250
|
+
</div>
|
|
251
|
+
))}
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function Section({ id, title, children }: { id: string; title: string; children: ReactNode }) {
|
|
257
|
+
return (
|
|
258
|
+
<section aria-labelledby={id} className={SECTION_CLASS}>
|
|
259
|
+
<h2 id={id} className="m-0 text-ods-text-primary text-h2">
|
|
260
|
+
{title}
|
|
261
|
+
</h2>
|
|
262
|
+
{children}
|
|
263
|
+
</section>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Which devices the store card names: only the stores the server has a listing for. */
|
|
268
|
+
function storeCardTitle(links: MobileAppLinks): string {
|
|
269
|
+
if (links.appStoreUrl && links.googlePlayUrl) return 'iPhone, iPad and Android';
|
|
270
|
+
return links.appStoreUrl ? 'iPhone and iPad' : 'Android';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function DownloadAppsPage({
|
|
274
|
+
endpoint = DOWNLOADS_API_PATH,
|
|
275
|
+
initialData,
|
|
276
|
+
shell = true,
|
|
277
|
+
backButton = false,
|
|
278
|
+
title,
|
|
279
|
+
subtitle,
|
|
280
|
+
showDesktop = true,
|
|
281
|
+
openStoresInNewTab = true,
|
|
282
|
+
onDownloadAction,
|
|
283
|
+
web,
|
|
284
|
+
}: DownloadAppsPageProps) {
|
|
285
|
+
const { data, isLoading, error, reload } = useDownloads({ endpoint, initialData });
|
|
286
|
+
// The page adapts to the server's answer: a section exists only for what it names.
|
|
287
|
+
const systems = showDesktop && data ? rowsBySystem(data.desktop) : [];
|
|
288
|
+
const mobile = data?.mobile ?? null;
|
|
289
|
+
const hasMobile = !!mobile && (!!mobile.appStoreUrl || !!mobile.googlePlayUrl);
|
|
290
|
+
// Until the answer lands, a host that offers the desktop app is titled for it.
|
|
291
|
+
const offersDesktop = showDesktop && (!data || systems.length > 0);
|
|
292
|
+
|
|
293
|
+
let body: ReactNode;
|
|
294
|
+
if (error && !data) {
|
|
295
|
+
body = <LoadError message="Could not load the downloads" onRetry={reload} />;
|
|
296
|
+
} else if (!data) {
|
|
297
|
+
body = isLoading ? (
|
|
298
|
+
<>
|
|
299
|
+
{showDesktop && (
|
|
300
|
+
<Section id="download-desktop" title="Desktop app">
|
|
301
|
+
<CardsSkeleton label="Loading the installers" />
|
|
302
|
+
</Section>
|
|
303
|
+
)}
|
|
304
|
+
<Section id="download-mobile" title="Mobile app">
|
|
305
|
+
<CardsSkeleton label="Loading the mobile app" />
|
|
306
|
+
</Section>
|
|
307
|
+
</>
|
|
308
|
+
) : null;
|
|
309
|
+
} else {
|
|
310
|
+
body = (
|
|
311
|
+
<>
|
|
312
|
+
{systems.length > 0 && (
|
|
313
|
+
<Section id="download-desktop" title="Desktop app">
|
|
314
|
+
<div className={cardsGridClass(systems.length)}>
|
|
315
|
+
{systems.map(system => (
|
|
316
|
+
<SystemCard key={system.os} system={system} onDownloadAction={onDownloadAction} />
|
|
317
|
+
))}
|
|
318
|
+
</div>
|
|
319
|
+
</Section>
|
|
320
|
+
)}
|
|
321
|
+
|
|
322
|
+
{mobile && hasMobile && (
|
|
323
|
+
<Section id="download-mobile" title="Mobile app">
|
|
324
|
+
<div className={cardsGridClass(mobile.install ? 2 : 1)}>
|
|
325
|
+
<div className={CARD_CLASS}>
|
|
326
|
+
<CardHeader title={storeCardTitle(mobile)} />
|
|
327
|
+
<p className="m-0 text-ods-text-secondary text-h4">Get alerts and respond to tickets on the go.</p>
|
|
328
|
+
<StoreBadgeLinks
|
|
329
|
+
appStoreUrl={mobile.appStoreUrl}
|
|
330
|
+
googlePlayUrl={mobile.googlePlayUrl}
|
|
331
|
+
openInNewTab={openStoresInNewTab}
|
|
332
|
+
/>
|
|
333
|
+
</div>
|
|
334
|
+
{mobile.install && (
|
|
335
|
+
<div
|
|
336
|
+
className={cn(CARD_CLASS, 'content-md:flex-row content-md:items-center content-md:justify-between')}
|
|
337
|
+
>
|
|
338
|
+
<div className="flex flex-col gap-[var(--spacing-system-sf)]">
|
|
339
|
+
<CardHeader title="Scan with your phone" />
|
|
340
|
+
<p className="m-0 text-ods-text-secondary text-h4">
|
|
341
|
+
Point your camera at the code, or open {formatUrlForDisplay(mobile.install.url)} on your phone.
|
|
342
|
+
</p>
|
|
343
|
+
</div>
|
|
344
|
+
<MobileAppQr install={mobile.install} className="h-[144px] w-[144px] shrink-0" />
|
|
345
|
+
</div>
|
|
346
|
+
)}
|
|
347
|
+
</div>
|
|
348
|
+
</Section>
|
|
349
|
+
)}
|
|
350
|
+
</>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return (
|
|
355
|
+
<DownloadAppsChrome
|
|
356
|
+
shell={shell}
|
|
357
|
+
backButton={backButton}
|
|
358
|
+
title={title ?? (offersDesktop ? DOWNLOADS_TITLE : DOWNLOADS_MOBILE_TITLE)}
|
|
359
|
+
subtitle={subtitle ?? (offersDesktop ? DOWNLOADS_TAGLINE : DOWNLOADS_MOBILE_TAGLINE)}
|
|
360
|
+
>
|
|
361
|
+
<div className={SECTIONS_CLASS}>
|
|
362
|
+
{body}
|
|
363
|
+
{web && <WebCard web={web} />}
|
|
364
|
+
</div>
|
|
365
|
+
</DownloadAppsChrome>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** The page's chrome: the standalone `PageShell` or the host's padded box, and the frozen `PageLayout` header. */
|
|
370
|
+
function DownloadAppsChrome({
|
|
371
|
+
shell,
|
|
372
|
+
title,
|
|
373
|
+
subtitle,
|
|
374
|
+
backButton,
|
|
375
|
+
children,
|
|
376
|
+
}: Required<Pick<DownloadAppsPageProps, 'shell' | 'title' | 'subtitle' | 'backButton'>> & { children: ReactNode }) {
|
|
377
|
+
const router = useRouter();
|
|
378
|
+
const backCfg =
|
|
379
|
+
backButton === false
|
|
380
|
+
? undefined
|
|
381
|
+
: { label: backButton.label ?? 'Back to home', onClick: () => router.push(backButton.href ?? '/') };
|
|
382
|
+
const inner = (
|
|
383
|
+
<PageLayout title={title} subtitle={subtitle} backButton={backCfg} titleSize="h1" titleWrap>
|
|
384
|
+
{children}
|
|
385
|
+
</PageLayout>
|
|
386
|
+
);
|
|
387
|
+
return shell ? <PageShell>{inner}</PageShell> : <div className="page-shell-content">{inner}</div>;
|
|
388
|
+
}
|