@flamingo-stack/openframe-frontend-core 0.0.295 → 0.0.296-snapshot.20260621021605
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/README.md +9 -0
- package/dist/{chunk-7RIYT7ZH.js → chunk-2QG57XOJ.js} +1067 -205
- package/dist/chunk-2QG57XOJ.js.map +1 -0
- package/dist/{chunk-7KXD7CWD.js → chunk-3JIQVE7T.js} +9 -15
- package/dist/{chunk-7KXD7CWD.js.map → chunk-3JIQVE7T.js.map} +1 -1
- package/dist/{chunk-FT4FCV7L.cjs → chunk-4PSQS3SW.cjs} +7 -9
- package/dist/chunk-4PSQS3SW.cjs.map +1 -0
- package/dist/{chunk-OOKKGOPQ.js → chunk-4TLE6VLU.js} +30 -24
- package/dist/chunk-4TLE6VLU.js.map +1 -0
- package/dist/{chunk-6IBA2MQV.cjs → chunk-53FUMSZ5.cjs} +40 -46
- package/dist/chunk-53FUMSZ5.cjs.map +1 -0
- package/dist/{chunk-D3LEFMOA.cjs → chunk-54KNMC2R.cjs} +3 -3
- package/dist/{chunk-D3LEFMOA.cjs.map → chunk-54KNMC2R.cjs.map} +1 -1
- package/dist/{chunk-EYEW6PTA.cjs → chunk-6C526VNN.cjs} +358 -118
- package/dist/chunk-6C526VNN.cjs.map +1 -0
- package/dist/{chunk-5O6N3BKR.cjs → chunk-7OVGB2DQ.cjs} +19 -25
- package/dist/chunk-7OVGB2DQ.cjs.map +1 -0
- package/dist/{chunk-6GCI7JOE.js → chunk-AD6C23QY.js} +8 -7
- package/dist/{chunk-6GCI7JOE.js.map → chunk-AD6C23QY.js.map} +1 -1
- package/dist/chunk-F5OB2YAL.cjs +144 -0
- package/dist/chunk-F5OB2YAL.cjs.map +1 -0
- package/dist/chunk-FBWXMMRB.cjs +2 -0
- package/dist/chunk-FBWXMMRB.cjs.map +1 -0
- package/dist/{chunk-YIGPRLQY.cjs → chunk-FCDQNTDG.cjs} +21 -20
- package/dist/chunk-FCDQNTDG.cjs.map +1 -0
- package/dist/{chunk-XXI7BNB6.cjs → chunk-FQOTC3UU.cjs} +321 -18
- package/dist/chunk-FQOTC3UU.cjs.map +1 -0
- package/dist/{chunk-INDQMNP6.cjs → chunk-GUTS7HGA.cjs} +11658 -2146
- package/dist/chunk-GUTS7HGA.cjs.map +1 -0
- package/dist/chunk-GZ4C3XW6.js +2 -0
- package/dist/chunk-GZ4C3XW6.js.map +1 -0
- package/dist/{chunk-HOVJGXF7.js → chunk-IL47XWV5.js} +8 -14
- package/dist/{chunk-HOVJGXF7.js.map → chunk-IL47XWV5.js.map} +1 -1
- package/dist/{chunk-LCNMR277.js → chunk-IZ7JSBFP.js} +1 -1
- package/dist/chunk-IZ7JSBFP.js.map +1 -0
- package/dist/{chunk-5IJ46KAV.js → chunk-JALO4TAZ.js} +360 -57
- package/dist/chunk-JALO4TAZ.js.map +1 -0
- package/dist/{chunk-AQOWFSMB.cjs → chunk-L6PSSIUQ.cjs} +1 -1
- package/dist/chunk-L6PSSIUQ.cjs.map +1 -0
- package/dist/{chunk-J3RDKZ32.js → chunk-L7ULJKG7.js} +6 -10
- package/dist/{chunk-J3RDKZ32.js.map → chunk-L7ULJKG7.js.map} +1 -1
- package/dist/{chunk-6BZEAPNT.js → chunk-PC746XCO.js} +15120 -5608
- package/dist/chunk-PC746XCO.js.map +1 -0
- package/dist/{chunk-3ZXUQQL4.js → chunk-PI4WSYQV.js} +2 -2
- package/dist/{chunk-E4XABBSU.js → chunk-PWQUAVA3.js} +338 -98
- package/dist/chunk-PWQUAVA3.js.map +1 -0
- package/dist/chunk-SA2WPJVO.js +144 -0
- package/dist/chunk-SA2WPJVO.js.map +1 -0
- package/dist/{chunk-ETACGX2A.cjs → chunk-UNVE2SDJ.cjs} +37 -31
- package/dist/chunk-UNVE2SDJ.cjs.map +1 -0
- package/dist/{chunk-5E2HOSSH.cjs → chunk-WMSTJAZT.cjs} +913 -51
- package/dist/chunk-WMSTJAZT.cjs.map +1 -0
- package/dist/{chunk-EJXHZX2E.js → chunk-X4DOXQRT.js} +4 -6
- package/dist/{chunk-EJXHZX2E.js.map → chunk-X4DOXQRT.js.map} +1 -1
- package/dist/{chunk-A2YL7QRX.cjs → chunk-YBYI62OE.cjs} +33 -37
- package/dist/chunk-YBYI62OE.cjs.map +1 -0
- package/dist/components/case-studies/index.cjs +126 -0
- package/dist/components/case-studies/index.cjs.map +1 -0
- package/dist/components/case-studies/index.d.ts +2 -0
- package/dist/components/case-studies/index.d.ts.map +1 -0
- package/dist/components/case-studies/index.js +126 -0
- package/dist/components/case-studies/index.js.map +1 -0
- package/dist/components/case-studies/share-experience-section.d.ts +48 -0
- package/dist/components/case-studies/share-experience-section.d.ts.map +1 -0
- package/dist/components/chat/chat-container.d.ts.map +1 -1
- package/dist/components/chat/error-message-display.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +8 -18
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +75 -85
- package/dist/components/chat/types/component.types.d.ts +2 -0
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +8 -15
- package/dist/components/contact/index.cjs.map +1 -1
- package/dist/components/contact/index.js +7 -14
- package/dist/components/docs/doc-viewer.d.ts +39 -2
- package/dist/components/docs/doc-viewer.d.ts.map +1 -1
- package/dist/components/docs/docs-hub-page.d.ts +46 -0
- package/dist/components/docs/docs-hub-page.d.ts.map +1 -0
- package/dist/components/docs/index.cjs +17 -9
- package/dist/components/docs/index.cjs.map +1 -1
- package/dist/components/docs/index.d.ts +4 -0
- package/dist/components/docs/index.d.ts.map +1 -1
- package/dist/components/docs/index.js +16 -8
- package/dist/components/docs/skeletons.d.ts +32 -0
- package/dist/components/docs/skeletons.d.ts.map +1 -0
- package/dist/components/docs/use-docs-resolve-link.d.ts +20 -0
- package/dist/components/docs/use-docs-resolve-link.d.ts.map +1 -0
- package/dist/components/docs/use-document-tree.d.ts.map +1 -1
- package/dist/components/embeds/embed-container.d.ts +37 -0
- package/dist/components/embeds/embed-container.d.ts.map +1 -0
- package/dist/components/embeds/embed-iframe.d.ts.map +1 -1
- package/dist/components/embeds/file-download-card.d.ts +18 -0
- package/dist/components/embeds/file-download-card.d.ts.map +1 -0
- package/dist/components/embeds/index.cjs +38 -15
- package/dist/components/embeds/index.cjs.map +1 -1
- package/dist/components/embeds/index.d.ts +8 -0
- package/dist/components/embeds/index.d.ts.map +1 -1
- package/dist/components/embeds/index.js +40 -17
- package/dist/components/embeds/linkedin-embed-client.d.ts +8 -0
- package/dist/components/embeds/linkedin-embed-client.d.ts.map +1 -0
- package/dist/components/embeds/markdown-image.d.ts +5 -0
- package/dist/components/embeds/markdown-image.d.ts.map +1 -0
- package/dist/components/embeds/reddit-embed-client.d.ts +7 -0
- package/dist/components/embeds/reddit-embed-client.d.ts.map +1 -0
- package/dist/components/embeds/rich-markdown-runtime.d.ts +46 -0
- package/dist/components/embeds/rich-markdown-runtime.d.ts.map +1 -0
- package/dist/components/embeds/twitter-embed-client.d.ts +8 -0
- package/dist/components/embeds/twitter-embed-client.d.ts.map +1 -0
- package/dist/components/faq/index.cjs +9 -16
- package/dist/components/faq/index.cjs.map +1 -1
- package/dist/components/faq/index.js +8 -15
- package/dist/components/features/index.cjs +8 -16
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +24 -32
- package/dist/components/features/notifications/notification-drawer.d.ts.map +1 -1
- package/dist/components/features/notifications/notifications-context.d.ts +5 -1
- package/dist/components/features/notifications/notifications-context.d.ts.map +1 -1
- package/dist/components/index.cjs +257 -452
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +781 -976
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-header.d.ts +78 -0
- package/dist/components/layout/page-header.d.ts.map +1 -0
- package/dist/components/layout/page-layout.d.ts +10 -1
- package/dist/components/layout/page-layout.d.ts.map +1 -1
- package/dist/components/layout/page-with-header.d.ts +67 -0
- package/dist/components/layout/page-with-header.d.ts.map +1 -0
- package/dist/components/layout/title-block.d.ts +17 -1
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +7 -15
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.js +9 -17
- package/dist/components/onboarding-guides/index.cjs +35 -36
- package/dist/components/onboarding-guides/index.cjs.map +1 -1
- package/dist/components/onboarding-guides/index.js +13 -14
- package/dist/components/onboarding-guides/index.js.map +1 -1
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts +1 -1
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +9 -16
- package/dist/components/related-content/index.cjs.map +1 -1
- package/dist/components/related-content/index.js +8 -15
- package/dist/components/shared/dev-section/dev-section-page.d.ts +9 -0
- package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -1
- package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
- package/dist/components/shared/dev-section/index.d.ts +1 -1
- package/dist/components/shared/dev-section/index.d.ts.map +1 -1
- package/dist/components/shared/doc-search/use-doc-search.d.ts.map +1 -1
- package/dist/components/shared/legal-document/legal-document-page.d.ts.map +1 -1
- package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +100 -112
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.js +20 -32
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/ui/button/split-button.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -52
- package/dist/components/ui/file-manager/index.cjs.map +1 -1
- package/dist/components/ui/file-manager/index.js +4 -6
- package/dist/components/ui/file-manager/index.js.map +1 -1
- package/dist/components/ui/index.cjs +13 -19
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +133 -139
- package/dist/components/ui/release-changelog-section.d.ts +6 -2
- package/dist/components/ui/release-changelog-section.d.ts.map +1 -1
- package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
- package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
- package/dist/components/ui/simple-markdown-renderer.d.ts +2 -8
- package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
- package/dist/contexts/chat-runtime-context.d.ts +14 -0
- package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
- package/dist/contexts/index.cjs +3 -3
- package/dist/contexts/index.js +5 -5
- package/dist/embed-shims/index.cjs +3 -3
- package/dist/embed-shims/index.cjs.map +1 -1
- package/dist/embed-shims/index.js +4 -4
- package/dist/hooks/index.cjs +4 -9
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.js +6 -11
- package/dist/index.cjs +14 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +362 -368
- package/dist/types/doc-source.d.ts +31 -1
- package/dist/types/doc-source.d.ts.map +1 -1
- package/dist/utils/index.cjs +4 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/page-header-constants.d.ts +15 -0
- package/dist/utils/page-header-constants.d.ts.map +1 -0
- package/dist/utils/social-embed-cache.d.ts +29 -0
- package/dist/utils/social-embed-cache.d.ts.map +1 -0
- package/package.json +7 -1
- package/src/components/case-studies/index.ts +4 -0
- package/src/components/case-studies/share-experience-section.tsx +185 -0
- package/src/components/chat/chat-container.tsx +5 -7
- package/src/components/chat/embeddable-chat.tsx +1 -1
- package/src/components/chat/error-message-display.tsx +49 -31
- package/src/components/chat/types/component.types.ts +2 -0
- package/src/components/docs/doc-viewer.tsx +111 -19
- package/src/components/docs/docs-hub-page.tsx +149 -0
- package/src/components/docs/index.ts +17 -0
- package/src/components/docs/skeletons.tsx +138 -0
- package/src/components/docs/use-docs-resolve-link.ts +52 -0
- package/src/components/docs/use-document-tree.ts +21 -0
- package/src/components/embeds/embed-container.tsx +80 -0
- package/src/components/embeds/embed-iframe.tsx +7 -9
- package/src/components/embeds/file-download-card.tsx +54 -0
- package/src/components/embeds/index.ts +30 -0
- package/src/components/embeds/linkedin-embed-client.tsx +100 -0
- package/src/components/embeds/markdown-image.tsx +88 -0
- package/src/components/embeds/og-link-preview.tsx +13 -13
- package/src/components/embeds/reddit-embed-client.tsx +550 -0
- package/src/components/embeds/rich-markdown-runtime.tsx +79 -0
- package/src/components/embeds/twitter-embed-client.tsx +308 -0
- package/src/components/features/notifications/notification-drawer.tsx +18 -7
- package/src/components/features/notifications/notifications-context.tsx +7 -0
- package/src/components/layout/page-header.tsx +182 -0
- package/src/components/layout/page-layout.tsx +14 -1
- package/src/components/layout/page-with-header.tsx +110 -0
- package/src/components/layout/title-block.tsx +40 -62
- package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +3 -3
- package/src/components/shared/dev-section/dev-section-page.tsx +9 -1
- package/src/components/shared/dev-section/dev-section-view.tsx +14 -9
- package/src/components/shared/dev-section/index.ts +1 -1
- package/src/components/shared/doc-search/use-doc-search.ts +7 -3
- package/src/components/shared/legal-document/legal-document-page.tsx +2 -2
- package/src/components/shared/product-release/release-detail-page.tsx +6 -4
- package/src/components/ui/button/split-button.tsx +5 -2
- package/src/components/ui/index.ts +2 -0
- package/src/components/ui/release-changelog-section.tsx +7 -2
- package/src/components/ui/rich-markdown-renderer.tsx +1203 -0
- package/src/components/ui/simple-markdown-renderer.tsx +7 -11
- package/src/contexts/chat-runtime-context.tsx +14 -0
- package/src/stories/NotificationDrawer.stories.tsx +2 -0
- package/src/types/doc-source.ts +33 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/page-header-constants.ts +15 -0
- package/src/utils/social-embed-cache.ts +391 -0
- package/dist/chunk-26PKDALD.js +0 -2379
- package/dist/chunk-26PKDALD.js.map +0 -1
- package/dist/chunk-3MCHAFHB.js +0 -89
- package/dist/chunk-3MCHAFHB.js.map +0 -1
- package/dist/chunk-3XIB4VKS.cjs +0 -619
- package/dist/chunk-3XIB4VKS.cjs.map +0 -1
- package/dist/chunk-4W7NYJ3B.cjs +0 -3009
- package/dist/chunk-4W7NYJ3B.cjs.map +0 -1
- package/dist/chunk-5E2HOSSH.cjs.map +0 -1
- package/dist/chunk-5IJ46KAV.js.map +0 -1
- package/dist/chunk-5O6N3BKR.cjs.map +0 -1
- package/dist/chunk-6BZEAPNT.js.map +0 -1
- package/dist/chunk-6IBA2MQV.cjs.map +0 -1
- package/dist/chunk-6JINAOI7.cjs +0 -311
- package/dist/chunk-6JINAOI7.cjs.map +0 -1
- package/dist/chunk-7RIYT7ZH.js.map +0 -1
- package/dist/chunk-A2YL7QRX.cjs.map +0 -1
- package/dist/chunk-AQOWFSMB.cjs.map +0 -1
- package/dist/chunk-E4XABBSU.js.map +0 -1
- package/dist/chunk-ETACGX2A.cjs.map +0 -1
- package/dist/chunk-EYEW6PTA.cjs.map +0 -1
- package/dist/chunk-FQJK446R.js +0 -1606
- package/dist/chunk-FQJK446R.js.map +0 -1
- package/dist/chunk-FT4FCV7L.cjs.map +0 -1
- package/dist/chunk-INDQMNP6.cjs.map +0 -1
- package/dist/chunk-J54Z3OCR.cjs +0 -1606
- package/dist/chunk-J54Z3OCR.cjs.map +0 -1
- package/dist/chunk-KXCRGTRN.cjs +0 -2379
- package/dist/chunk-KXCRGTRN.cjs.map +0 -1
- package/dist/chunk-LCNMR277.js.map +0 -1
- package/dist/chunk-LFGGF7OT.cjs +0 -449
- package/dist/chunk-LFGGF7OT.cjs.map +0 -1
- package/dist/chunk-M2OCXTNT.js +0 -311
- package/dist/chunk-M2OCXTNT.js.map +0 -1
- package/dist/chunk-NSPOYUBH.js +0 -3009
- package/dist/chunk-NSPOYUBH.js.map +0 -1
- package/dist/chunk-OOKKGOPQ.js.map +0 -1
- package/dist/chunk-OQ6X7ZOC.js +0 -449
- package/dist/chunk-OQ6X7ZOC.js.map +0 -1
- package/dist/chunk-POKKCWKF.js +0 -354
- package/dist/chunk-POKKCWKF.js.map +0 -1
- package/dist/chunk-TFSYSWPS.cjs +0 -89
- package/dist/chunk-TFSYSWPS.cjs.map +0 -1
- package/dist/chunk-XXI7BNB6.cjs.map +0 -1
- package/dist/chunk-YD43AKI5.js +0 -619
- package/dist/chunk-YD43AKI5.js.map +0 -1
- package/dist/chunk-YETA25JW.cjs +0 -354
- package/dist/chunk-YETA25JW.cjs.map +0 -1
- package/dist/chunk-YIGPRLQY.cjs.map +0 -1
- /package/dist/{chunk-3ZXUQQL4.js.map → chunk-PI4WSYQV.js.map} +0 -0
package/dist/chunk-NSPOYUBH.js
DELETED
|
@@ -1,3009 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import {
|
|
3
|
-
COMPACT_CARD_IMAGE_SLOT,
|
|
4
|
-
COMPACT_CARD_META_ROW_BOX,
|
|
5
|
-
COMPACT_CARD_OUTER,
|
|
6
|
-
COMPACT_CARD_ROW_FILLER,
|
|
7
|
-
COMPACT_CARD_SKELETON_IMAGE_SLOT,
|
|
8
|
-
COMPACT_CARD_SKELETON_OUTER,
|
|
9
|
-
COMPACT_CARD_SUMMARY,
|
|
10
|
-
COMPACT_CARD_TEXT_COL,
|
|
11
|
-
COMPACT_CARD_TITLE,
|
|
12
|
-
COMPACT_CARD_TITLE_ROW,
|
|
13
|
-
SECTION_HEADING_CLASS,
|
|
14
|
-
decideNewTab,
|
|
15
|
-
fetchPriorityProp,
|
|
16
|
-
formatDate,
|
|
17
|
-
formatDurationMMSS,
|
|
18
|
-
getFirstLastInitials,
|
|
19
|
-
nameInitials
|
|
20
|
-
} from "./chunk-FQJK446R.js";
|
|
21
|
-
import {
|
|
22
|
-
useNearViewport,
|
|
23
|
-
useOgPlaceholder
|
|
24
|
-
} from "./chunk-3MCHAFHB.js";
|
|
25
|
-
import {
|
|
26
|
-
useChatRuntime
|
|
27
|
-
} from "./chunk-LCNMR277.js";
|
|
28
|
-
import {
|
|
29
|
-
next_image_default
|
|
30
|
-
} from "./chunk-LXC6P2EO.js";
|
|
31
|
-
import {
|
|
32
|
-
Button,
|
|
33
|
-
DropdownMenu,
|
|
34
|
-
DropdownMenuContent,
|
|
35
|
-
DropdownMenuTrigger,
|
|
36
|
-
SplitButton,
|
|
37
|
-
init_button2 as init_button
|
|
38
|
-
} from "./chunk-5IJ46KAV.js";
|
|
39
|
-
import {
|
|
40
|
-
init_next_link,
|
|
41
|
-
next_link_default
|
|
42
|
-
} from "./chunk-OHPI2HRK.js";
|
|
43
|
-
import {
|
|
44
|
-
cn,
|
|
45
|
-
init_cn
|
|
46
|
-
} from "./chunk-XTCBRQN2.js";
|
|
47
|
-
import {
|
|
48
|
-
AlertCircleIcon,
|
|
49
|
-
Chevron02DownIcon,
|
|
50
|
-
Chevron02LeftIcon,
|
|
51
|
-
Chevron02RightIcon,
|
|
52
|
-
Ellipsis01Icon,
|
|
53
|
-
PlayIcon
|
|
54
|
-
} from "./chunk-J7AV6H63.js";
|
|
55
|
-
|
|
56
|
-
// src/components/ui/card.tsx
|
|
57
|
-
init_cn();
|
|
58
|
-
import * as React from "react";
|
|
59
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
60
|
-
var Card = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
61
|
-
"div",
|
|
62
|
-
{
|
|
63
|
-
ref,
|
|
64
|
-
className: cn(
|
|
65
|
-
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
66
|
-
className
|
|
67
|
-
),
|
|
68
|
-
...props
|
|
69
|
-
}
|
|
70
|
-
));
|
|
71
|
-
Card.displayName = "Card";
|
|
72
|
-
var CardHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
73
|
-
"div",
|
|
74
|
-
{
|
|
75
|
-
ref,
|
|
76
|
-
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
77
|
-
...props
|
|
78
|
-
}
|
|
79
|
-
));
|
|
80
|
-
CardHeader.displayName = "CardHeader";
|
|
81
|
-
var CardTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
82
|
-
"div",
|
|
83
|
-
{
|
|
84
|
-
ref,
|
|
85
|
-
className: cn(
|
|
86
|
-
"text-2xl font-semibold leading-none tracking-tight",
|
|
87
|
-
className
|
|
88
|
-
),
|
|
89
|
-
...props
|
|
90
|
-
}
|
|
91
|
-
));
|
|
92
|
-
CardTitle.displayName = "CardTitle";
|
|
93
|
-
var CardDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
94
|
-
"div",
|
|
95
|
-
{
|
|
96
|
-
ref,
|
|
97
|
-
className: cn("text-sm text-muted-foreground", className),
|
|
98
|
-
...props
|
|
99
|
-
}
|
|
100
|
-
));
|
|
101
|
-
CardDescription.displayName = "CardDescription";
|
|
102
|
-
var CardContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
103
|
-
CardContent.displayName = "CardContent";
|
|
104
|
-
var CardFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
105
|
-
"div",
|
|
106
|
-
{
|
|
107
|
-
ref,
|
|
108
|
-
className: cn("flex items-center p-6 pt-0", className),
|
|
109
|
-
...props
|
|
110
|
-
}
|
|
111
|
-
));
|
|
112
|
-
CardFooter.displayName = "CardFooter";
|
|
113
|
-
function CardHorizontal({ icon, title, description, className = "", borderLeft = true }) {
|
|
114
|
-
return /* @__PURE__ */ jsxs(
|
|
115
|
-
"div",
|
|
116
|
-
{
|
|
117
|
-
className: cn(
|
|
118
|
-
"w-full flex flex-row items-center gap-3 md:gap-4 bg-ods-card p-4 md:p-6 min-h-[80px]",
|
|
119
|
-
borderLeft ? "border-l border-ods-border" : "",
|
|
120
|
-
className
|
|
121
|
-
),
|
|
122
|
-
children: [
|
|
123
|
-
/* @__PURE__ */ jsx("div", { className: "w-5 h-5 flex-shrink-0", children: icon }),
|
|
124
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
|
|
125
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-sm md:text-sm text-ods-text-primary leading-tight mb-0.5 text-left", children: title }),
|
|
126
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs md:text-sm text-ods-text-secondary leading-tight text-left", children: description })
|
|
127
|
-
] })
|
|
128
|
-
]
|
|
129
|
-
}
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/components/ui/tabs.tsx
|
|
134
|
-
init_cn();
|
|
135
|
-
import * as React2 from "react";
|
|
136
|
-
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
137
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
138
|
-
var Tabs = TabsPrimitive.Root;
|
|
139
|
-
var TabsList = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
140
|
-
TabsPrimitive.List,
|
|
141
|
-
{
|
|
142
|
-
ref,
|
|
143
|
-
className: cn(
|
|
144
|
-
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
145
|
-
className
|
|
146
|
-
),
|
|
147
|
-
...props
|
|
148
|
-
}
|
|
149
|
-
));
|
|
150
|
-
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
151
|
-
var TabsTrigger = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
152
|
-
TabsPrimitive.Trigger,
|
|
153
|
-
{
|
|
154
|
-
ref,
|
|
155
|
-
className: cn(
|
|
156
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
|
157
|
-
className
|
|
158
|
-
),
|
|
159
|
-
...props
|
|
160
|
-
}
|
|
161
|
-
));
|
|
162
|
-
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
163
|
-
var TabsContent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
164
|
-
TabsPrimitive.Content,
|
|
165
|
-
{
|
|
166
|
-
ref,
|
|
167
|
-
className: cn(
|
|
168
|
-
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
169
|
-
className
|
|
170
|
-
),
|
|
171
|
-
...props
|
|
172
|
-
}
|
|
173
|
-
));
|
|
174
|
-
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
175
|
-
|
|
176
|
-
// src/components/ui/status-badge.tsx
|
|
177
|
-
init_cn();
|
|
178
|
-
import { cva } from "class-variance-authority";
|
|
179
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
180
|
-
var statusBadgeVariants = cva(
|
|
181
|
-
"inline-flex items-center justify-center rounded font-mono font-medium uppercase tracking-wide",
|
|
182
|
-
{
|
|
183
|
-
variants: {
|
|
184
|
-
variant: {
|
|
185
|
-
card: "px-3 py-1.5 text-sm",
|
|
186
|
-
button: "px-2 py-0.5 text-[10px] leading-none"
|
|
187
|
-
},
|
|
188
|
-
colorScheme: {
|
|
189
|
-
cyan: "bg-[var(--ods-flamingo-cyan-base)] text-ods-text-on-accent",
|
|
190
|
-
pink: "bg-[var(--ods-flamingo-pink-base)] text-ods-text-on-accent",
|
|
191
|
-
yellow: "bg-[var(--ods-flamingo-yellow-base)] text-ods-text-on-accent border border-[var(--ods-system-greys-black)]",
|
|
192
|
-
green: "bg-[var(--ods-flamingo-green-base)] text-ods-text-on-accent",
|
|
193
|
-
purple: "bg-[var(--ods-flamingo-purple-base)] text-ods-text-on-accent",
|
|
194
|
-
success: "bg-[var(--ods-attention-green-success-secondary)] text-[var(--ods-attention-green-success)]",
|
|
195
|
-
error: "bg-[var(--ods-attention-red-error-secondary)] text-[var(--ods-attention-red-error)]",
|
|
196
|
-
warning: "bg-[var(--ods-attention-yellow-warning-secondary)] text-[var(--ods-attention-yellow-warning)]",
|
|
197
|
-
default: "bg-ods-bg-secondary text-ods-text-primary",
|
|
198
|
-
// Border-only variants (no background) - for task type badges
|
|
199
|
-
accentBorder: "bg-transparent border-2 text-ods-accent border-ods-accent",
|
|
200
|
-
errorBorder: "bg-transparent border-2 text-[var(--ods-attention-red-error)] border-[var(--ods-attention-red-error)]",
|
|
201
|
-
whiteBorder: "bg-transparent border-2 text-ods-text-primary border-ods-text-primary"
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
defaultVariants: {
|
|
205
|
-
variant: "card",
|
|
206
|
-
colorScheme: "default"
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
);
|
|
210
|
-
function StatusBadge({
|
|
211
|
-
text,
|
|
212
|
-
variant,
|
|
213
|
-
colorScheme,
|
|
214
|
-
className,
|
|
215
|
-
singleLine,
|
|
216
|
-
...props
|
|
217
|
-
}) {
|
|
218
|
-
const renderText = () => {
|
|
219
|
-
if (singleLine) return text;
|
|
220
|
-
if (variant === "button" && text.includes(" ")) {
|
|
221
|
-
const words = text.split(" ");
|
|
222
|
-
return /* @__PURE__ */ jsx3("span", { className: "flex flex-col items-center justify-center text-center gap-0", children: words.map((word, index) => /* @__PURE__ */ jsx3("span", { className: "block", children: word }, index)) });
|
|
223
|
-
}
|
|
224
|
-
return text;
|
|
225
|
-
};
|
|
226
|
-
return /* @__PURE__ */ jsx3(
|
|
227
|
-
"span",
|
|
228
|
-
{
|
|
229
|
-
className: cn(statusBadgeVariants({ variant, colorScheme }), className),
|
|
230
|
-
...props,
|
|
231
|
-
children: renderText()
|
|
232
|
-
}
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// src/components/ui/simple-markdown-renderer.tsx
|
|
237
|
-
import { useEffect, useState, useRef, useMemo, useCallback, memo } from "react";
|
|
238
|
-
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
|
|
239
|
-
import remarkGfm from "remark-gfm";
|
|
240
|
-
import remarkBreaks from "remark-breaks";
|
|
241
|
-
import rehypeHighlight from "rehype-highlight";
|
|
242
|
-
import rehypeRaw from "rehype-raw";
|
|
243
|
-
import { visit } from "unist-util-visit";
|
|
244
|
-
init_cn();
|
|
245
|
-
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
246
|
-
var EVENT_HANDLER_ATTR_RE = /^on[a-z]+$/i;
|
|
247
|
-
var JAVASCRIPT_URL_RE = /^[\s\x00-\x1f]*javascript:/i;
|
|
248
|
-
var DATA_URL_RE = /^[\s\x00-\x1f]*data:/i;
|
|
249
|
-
var URL_ATTRS = /* @__PURE__ */ new Set([
|
|
250
|
-
"href",
|
|
251
|
-
"src",
|
|
252
|
-
// `srcset` accepts `javascript:` on legacy browsers — same guard
|
|
253
|
-
// as the canonical hast-util-sanitize default schema's attr set.
|
|
254
|
-
// Multi-candidate scanning lives in `srcsetHasUnsafeCandidate` below;
|
|
255
|
-
// a single-URL check would miss a malicious second candidate like
|
|
256
|
-
// `"https://safe.png 1x, javascript:alert(1) 2x"`.
|
|
257
|
-
"srcset",
|
|
258
|
-
"formaction",
|
|
259
|
-
"xlink:href",
|
|
260
|
-
"poster",
|
|
261
|
-
"data",
|
|
262
|
-
"action",
|
|
263
|
-
"background"
|
|
264
|
-
]);
|
|
265
|
-
function srcsetHasUnsafeCandidate(srcset) {
|
|
266
|
-
for (const candidate of srcset.split(",")) {
|
|
267
|
-
const url = candidate.trim().split(/\s+/)[0] ?? "";
|
|
268
|
-
if (JAVASCRIPT_URL_RE.test(url) || DATA_URL_RE.test(url)) return true;
|
|
269
|
-
}
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
var STRIP_ELEMENTS = /* @__PURE__ */ new Set([
|
|
273
|
-
"script",
|
|
274
|
-
"style",
|
|
275
|
-
"noscript",
|
|
276
|
-
"noembed",
|
|
277
|
-
"object",
|
|
278
|
-
"embed",
|
|
279
|
-
"applet",
|
|
280
|
-
"base",
|
|
281
|
-
"meta"
|
|
282
|
-
]);
|
|
283
|
-
function rehypeStripUnsafe() {
|
|
284
|
-
return (tree) => {
|
|
285
|
-
visit(tree, "element", (node, index, parent) => {
|
|
286
|
-
const tag = String(node.tagName ?? "").toLowerCase();
|
|
287
|
-
if (STRIP_ELEMENTS.has(tag)) {
|
|
288
|
-
if (parent && typeof index === "number") {
|
|
289
|
-
parent.children.splice(index, 1);
|
|
290
|
-
return index;
|
|
291
|
-
}
|
|
292
|
-
node.children = [];
|
|
293
|
-
node.tagName = "span";
|
|
294
|
-
node.properties = {};
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (!node.properties || typeof node.properties !== "object") return;
|
|
298
|
-
for (const key of Object.keys(node.properties)) {
|
|
299
|
-
if (EVENT_HANDLER_ATTR_RE.test(key)) {
|
|
300
|
-
delete node.properties[key];
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
if (URL_ATTRS.has(key.toLowerCase())) {
|
|
304
|
-
const raw = node.properties[key];
|
|
305
|
-
const v = Array.isArray(raw) ? raw[0] : raw;
|
|
306
|
-
if (typeof v === "string") {
|
|
307
|
-
const unsafe = key.toLowerCase() === "srcset" ? srcsetHasUnsafeCandidate(v) : JAVASCRIPT_URL_RE.test(v) || DATA_URL_RE.test(v);
|
|
308
|
-
if (unsafe) {
|
|
309
|
-
delete node.properties[key];
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (tag === "iframe" && key.toLowerCase() === "srcdoc") {
|
|
315
|
-
delete node.properties[key];
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
function cardAwareUrlTransform(url, key) {
|
|
322
|
-
if (key === "href" && typeof url === "string" && (url.startsWith("card://") || url.startsWith("mention://")))
|
|
323
|
-
return url;
|
|
324
|
-
return defaultUrlTransform(url);
|
|
325
|
-
}
|
|
326
|
-
var SAFE_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
327
|
-
// Block + inline text
|
|
328
|
-
"a",
|
|
329
|
-
"abbr",
|
|
330
|
-
"address",
|
|
331
|
-
"article",
|
|
332
|
-
"aside",
|
|
333
|
-
"b",
|
|
334
|
-
"bdi",
|
|
335
|
-
"bdo",
|
|
336
|
-
"blockquote",
|
|
337
|
-
"br",
|
|
338
|
-
"caption",
|
|
339
|
-
"cite",
|
|
340
|
-
"code",
|
|
341
|
-
"col",
|
|
342
|
-
"colgroup",
|
|
343
|
-
"data",
|
|
344
|
-
"dd",
|
|
345
|
-
"del",
|
|
346
|
-
"details",
|
|
347
|
-
"dfn",
|
|
348
|
-
"div",
|
|
349
|
-
"dl",
|
|
350
|
-
"dt",
|
|
351
|
-
"em",
|
|
352
|
-
"figcaption",
|
|
353
|
-
"figure",
|
|
354
|
-
"footer",
|
|
355
|
-
"h1",
|
|
356
|
-
"h2",
|
|
357
|
-
"h3",
|
|
358
|
-
"h4",
|
|
359
|
-
"h5",
|
|
360
|
-
"h6",
|
|
361
|
-
"header",
|
|
362
|
-
"hgroup",
|
|
363
|
-
"hr",
|
|
364
|
-
"i",
|
|
365
|
-
"ins",
|
|
366
|
-
"kbd",
|
|
367
|
-
"li",
|
|
368
|
-
"main",
|
|
369
|
-
"mark",
|
|
370
|
-
"nav",
|
|
371
|
-
"ol",
|
|
372
|
-
"p",
|
|
373
|
-
"pre",
|
|
374
|
-
"q",
|
|
375
|
-
"rp",
|
|
376
|
-
"rt",
|
|
377
|
-
"ruby",
|
|
378
|
-
"s",
|
|
379
|
-
"samp",
|
|
380
|
-
"section",
|
|
381
|
-
"small",
|
|
382
|
-
"span",
|
|
383
|
-
"strong",
|
|
384
|
-
"sub",
|
|
385
|
-
"summary",
|
|
386
|
-
"sup",
|
|
387
|
-
"table",
|
|
388
|
-
"tbody",
|
|
389
|
-
"td",
|
|
390
|
-
"tfoot",
|
|
391
|
-
"th",
|
|
392
|
-
"thead",
|
|
393
|
-
"time",
|
|
394
|
-
"tr",
|
|
395
|
-
"u",
|
|
396
|
-
"ul",
|
|
397
|
-
"var",
|
|
398
|
-
"wbr",
|
|
399
|
-
// Media
|
|
400
|
-
"img",
|
|
401
|
-
"picture",
|
|
402
|
-
"source",
|
|
403
|
-
"audio",
|
|
404
|
-
"video",
|
|
405
|
-
"iframe",
|
|
406
|
-
"track",
|
|
407
|
-
// Forms (rehype-raw allows them; mostly harmless for chat output)
|
|
408
|
-
"button",
|
|
409
|
-
"input",
|
|
410
|
-
"label",
|
|
411
|
-
"select",
|
|
412
|
-
"option",
|
|
413
|
-
"optgroup",
|
|
414
|
-
"textarea",
|
|
415
|
-
"form",
|
|
416
|
-
"fieldset",
|
|
417
|
-
"legend"
|
|
418
|
-
]);
|
|
419
|
-
var TAG_LIKE_REGEX = /<(\/?)([a-zA-Z][a-zA-Z0-9-]{0,63})((?:\s[^>]{0,4096}?)?)(\/?)>/g;
|
|
420
|
-
function escapeUnknownHtmlTags(text) {
|
|
421
|
-
if (!text || text.indexOf("<") === -1) return text;
|
|
422
|
-
const parts = [];
|
|
423
|
-
let cursor = 0;
|
|
424
|
-
const PROTECTED_SPAN_RE = /```[\s\S]*?```|`[^`\n]+`/g;
|
|
425
|
-
let span;
|
|
426
|
-
while ((span = PROTECTED_SPAN_RE.exec(text)) !== null) {
|
|
427
|
-
if (span.index > cursor) {
|
|
428
|
-
parts.push(escapeOutsideFences(text.slice(cursor, span.index)));
|
|
429
|
-
}
|
|
430
|
-
parts.push(span[0]);
|
|
431
|
-
cursor = span.index + span[0].length;
|
|
432
|
-
}
|
|
433
|
-
if (cursor < text.length) {
|
|
434
|
-
parts.push(escapeOutsideFences(text.slice(cursor)));
|
|
435
|
-
}
|
|
436
|
-
return parts.join("");
|
|
437
|
-
}
|
|
438
|
-
function escapeOutsideFences(segment) {
|
|
439
|
-
return segment.replace(TAG_LIKE_REGEX, (match, slash, tag, rest, selfClose) => {
|
|
440
|
-
const lower = tag.toLowerCase();
|
|
441
|
-
if (SAFE_HTML_TAGS.has(lower)) return match;
|
|
442
|
-
return `<${slash}${tag}${rest}${selfClose}>`;
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
var mermaidStyles = `
|
|
446
|
-
.mermaid-svg-container svg {
|
|
447
|
-
max-width: 100% !important;
|
|
448
|
-
height: auto !important;
|
|
449
|
-
min-height: 200px;
|
|
450
|
-
font-family: 'DM Sans', sans-serif !important;
|
|
451
|
-
font-size: 14px !important;
|
|
452
|
-
}
|
|
453
|
-
@media (min-width: 1520px) {
|
|
454
|
-
.mermaid-svg-container svg {
|
|
455
|
-
max-width: 900px !important;
|
|
456
|
-
max-height: 700px !important;
|
|
457
|
-
min-height: 300px;
|
|
458
|
-
font-size: 16px !important;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
@media (min-width: 768px) and (max-width: 1519px) {
|
|
462
|
-
.mermaid-svg-container svg {
|
|
463
|
-
max-width: 700px !important;
|
|
464
|
-
max-height: 600px !important;
|
|
465
|
-
min-height: 250px;
|
|
466
|
-
font-size: 15px !important;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
@media (max-width: 767px) {
|
|
470
|
-
.mermaid-svg-container svg {
|
|
471
|
-
max-width: 90vw !important;
|
|
472
|
-
max-height: 400px !important;
|
|
473
|
-
min-height: 200px;
|
|
474
|
-
font-size: 13px !important;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
.mermaid-svg-container svg[width] { width: 100% !important; }
|
|
478
|
-
.mermaid-svg-container .node rect,
|
|
479
|
-
.mermaid-svg-container .node circle,
|
|
480
|
-
.mermaid-svg-container .node ellipse,
|
|
481
|
-
.mermaid-svg-container .node polygon { stroke-width: 2px !important; }
|
|
482
|
-
.mermaid-svg-container .edgePath path { stroke-width: 2px !important; }
|
|
483
|
-
@media (min-width: 768px) {
|
|
484
|
-
.mermaid-svg-container .node text,
|
|
485
|
-
.mermaid-svg-container .edgeLabel text { font-size: 14px !important; }
|
|
486
|
-
}
|
|
487
|
-
@media (min-width: 1520px) {
|
|
488
|
-
.mermaid-svg-container .node text,
|
|
489
|
-
.mermaid-svg-container .edgeLabel text { font-size: 16px !important; }
|
|
490
|
-
}
|
|
491
|
-
`;
|
|
492
|
-
var MermaidDiagram = ({ chart }) => {
|
|
493
|
-
const [svg, setSvg] = useState("");
|
|
494
|
-
const [error, setError] = useState("");
|
|
495
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
496
|
-
const [mounted, setMounted] = useState(false);
|
|
497
|
-
useEffect(() => {
|
|
498
|
-
setMounted(true);
|
|
499
|
-
}, []);
|
|
500
|
-
useEffect(() => {
|
|
501
|
-
const renderMermaid = async () => {
|
|
502
|
-
try {
|
|
503
|
-
setIsLoading(true);
|
|
504
|
-
const { default: mermaid } = await import("mermaid");
|
|
505
|
-
mermaid.initialize({
|
|
506
|
-
startOnLoad: false,
|
|
507
|
-
theme: "dark",
|
|
508
|
-
themeVariables: {
|
|
509
|
-
primaryColor: "#FFC008",
|
|
510
|
-
primaryTextColor: "#FAFAFA",
|
|
511
|
-
primaryBorderColor: "#3A3A3A",
|
|
512
|
-
lineColor: "#888888",
|
|
513
|
-
secondaryColor: "#212121",
|
|
514
|
-
tertiaryColor: "#2A2A2A",
|
|
515
|
-
background: "transparent",
|
|
516
|
-
mainBkg: "transparent",
|
|
517
|
-
secondBkg: "transparent",
|
|
518
|
-
tertiaryBkg: "transparent",
|
|
519
|
-
cScale0: "#FFC008",
|
|
520
|
-
cScale1: "#4ECDC4",
|
|
521
|
-
cScale2: "#45B7D1",
|
|
522
|
-
cScale3: "#96CEB4",
|
|
523
|
-
cScale4: "#FFEAA7",
|
|
524
|
-
cScale5: "#DDA0DD",
|
|
525
|
-
cScale6: "#98D8C8",
|
|
526
|
-
cScale7: "#F7DC6F",
|
|
527
|
-
cScale8: "#BB8FCE",
|
|
528
|
-
cScale9: "#85C1E9",
|
|
529
|
-
taskTextColor: "#FAFAFA",
|
|
530
|
-
taskTextOutsideColor: "#FAFAFA",
|
|
531
|
-
activeTaskTextColor: "#1A1A1A",
|
|
532
|
-
nodeTextColor: "#FAFAFA"
|
|
533
|
-
},
|
|
534
|
-
flowchart: { useMaxWidth: true, htmlLabels: true, rankSpacing: 50, nodeSpacing: 30, curve: "basis" },
|
|
535
|
-
sequence: { useMaxWidth: true, width: 150 },
|
|
536
|
-
pie: { useMaxWidth: true, useWidth: void 0 },
|
|
537
|
-
fontFamily: "DM Sans, sans-serif",
|
|
538
|
-
fontSize: 14,
|
|
539
|
-
securityLevel: "loose"
|
|
540
|
-
});
|
|
541
|
-
const { svg: renderedSvg } = await mermaid.render(`mermaid-${Date.now()}`, chart);
|
|
542
|
-
setSvg(renderedSvg);
|
|
543
|
-
setIsLoading(false);
|
|
544
|
-
} catch (err) {
|
|
545
|
-
console.error("Mermaid rendering error:", err);
|
|
546
|
-
setError(`Failed to render diagram: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
547
|
-
setIsLoading(false);
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
if (mounted) {
|
|
551
|
-
renderMermaid();
|
|
552
|
-
}
|
|
553
|
-
}, [chart, mounted]);
|
|
554
|
-
if (error) {
|
|
555
|
-
return /* @__PURE__ */ jsxs2("div", { className: "error-state bg-ods-card border border-ods-border rounded-lg p-6 my-6", children: [
|
|
556
|
-
/* @__PURE__ */ jsx4("div", { className: "error-icon flex justify-center mb-4", children: /* @__PURE__ */ jsx4(AlertCircleIcon, { className: "w-12 h-12 text-ods-error" }) }),
|
|
557
|
-
/* @__PURE__ */ jsx4("div", { className: "error-title text-center font-sans font-semibold text-lg text-ods-error mb-2", children: "Diagram Error" }),
|
|
558
|
-
/* @__PURE__ */ jsx4("div", { className: "error-description text-center font-sans text-sm text-ods-text-secondary mb-4 break-words overflow-hidden max-w-full", children: /* @__PURE__ */ jsx4("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx4("pre", { className: "whitespace-pre-wrap break-words text-xs", children: error }) }) })
|
|
559
|
-
] });
|
|
560
|
-
}
|
|
561
|
-
if (isLoading || !svg) {
|
|
562
|
-
return /* @__PURE__ */ jsx4("div", { className: "skeleton-code bg-ods-card border border-ods-border rounded-lg p-6 min-h-[120px] flex items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-ods-text-tertiary font-sans", children: isLoading ? "Loading diagram renderer..." : "Rendering diagram..." }) });
|
|
563
|
-
}
|
|
564
|
-
return /* @__PURE__ */ jsx4("div", { className: "mermaid-container rounded-lg p-4 md:p-6 lg:p-8 my-6 overflow-x-auto bg-ods-card border border-ods-border", children: /* @__PURE__ */ jsx4("div", { className: "flex justify-center items-center w-full min-h-[200px] md:min-h-[250px] lg:min-h-[300px]", children: /* @__PURE__ */ jsx4(
|
|
565
|
-
"div",
|
|
566
|
-
{
|
|
567
|
-
className: "mermaid-svg-container w-full flex justify-center max-w-full",
|
|
568
|
-
style: { fontSize: "14px" },
|
|
569
|
-
dangerouslySetInnerHTML: {
|
|
570
|
-
__html: svg.replace(
|
|
571
|
-
/<svg[^>]*>/,
|
|
572
|
-
(match) => match.replace(/width="[^"]*"/, 'width="100%"').replace(/height="[^"]*"/, 'height="auto"')
|
|
573
|
-
)
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
) }) });
|
|
577
|
-
};
|
|
578
|
-
function extractText(node) {
|
|
579
|
-
if (typeof node === "string") return node;
|
|
580
|
-
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
581
|
-
if (node?.props?.children) return extractText(node.props.children);
|
|
582
|
-
return "";
|
|
583
|
-
}
|
|
584
|
-
var TEXT_SIZE_PRESETS = {
|
|
585
|
-
default: {
|
|
586
|
-
h1: "text-heading-1",
|
|
587
|
-
h2: "text-heading-2",
|
|
588
|
-
h3: "text-2xl md:text-3xl",
|
|
589
|
-
h4: "text-xl",
|
|
590
|
-
h5: "text-lg md:text-xl",
|
|
591
|
-
h6: "text-base md:text-lg",
|
|
592
|
-
p: "md:text-h4 lg:text-h4",
|
|
593
|
-
li: "text-base md:text-lg",
|
|
594
|
-
blockquote: "text-lg",
|
|
595
|
-
code: "text-[14px]",
|
|
596
|
-
th: "text-xs md:text-sm",
|
|
597
|
-
td: "text-xs md:text-sm"
|
|
598
|
-
},
|
|
599
|
-
compact: {
|
|
600
|
-
h1: "text-heading-2",
|
|
601
|
-
h2: "text-heading-3",
|
|
602
|
-
h3: "text-xl md:text-2xl",
|
|
603
|
-
h4: "text-lg md:text-xl",
|
|
604
|
-
h5: "text-base md:text-lg",
|
|
605
|
-
h6: "text-sm md:text-base",
|
|
606
|
-
p: "text-base md:text-lg",
|
|
607
|
-
li: "text-base md:text-lg",
|
|
608
|
-
blockquote: "text-base md:text-lg",
|
|
609
|
-
code: "text-[13px]",
|
|
610
|
-
th: "text-xs md:text-sm",
|
|
611
|
-
td: "text-xs md:text-sm"
|
|
612
|
-
},
|
|
613
|
-
large: {
|
|
614
|
-
h1: "text-heading-1",
|
|
615
|
-
h2: "text-heading-1",
|
|
616
|
-
h3: "text-heading-2",
|
|
617
|
-
h4: "text-2xl md:text-3xl",
|
|
618
|
-
h5: "text-xl md:text-2xl",
|
|
619
|
-
h6: "text-lg md:text-xl",
|
|
620
|
-
p: "text-h3",
|
|
621
|
-
li: "text-lg md:text-xl",
|
|
622
|
-
blockquote: "text-xl md:text-2xl",
|
|
623
|
-
code: "text-[16px]",
|
|
624
|
-
th: "text-sm md:text-base",
|
|
625
|
-
td: "text-sm md:text-base"
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
function resolveTextSizeConfig(config) {
|
|
629
|
-
const defaultSizes = TEXT_SIZE_PRESETS.default;
|
|
630
|
-
if (!config) return defaultSizes;
|
|
631
|
-
if (typeof config === "string") return TEXT_SIZE_PRESETS[config];
|
|
632
|
-
if ("preset" in config) return { ...TEXT_SIZE_PRESETS[config.preset], ...config.overrides };
|
|
633
|
-
return { ...defaultSizes, ...config };
|
|
634
|
-
}
|
|
635
|
-
var SimpleMarkdownRendererImpl = ({
|
|
636
|
-
content,
|
|
637
|
-
className = "",
|
|
638
|
-
sectionIds,
|
|
639
|
-
demoteMarkdownH1ToH2 = false,
|
|
640
|
-
brokenLinks = [],
|
|
641
|
-
onInternalLinkClick,
|
|
642
|
-
currentPath: propCurrentPath,
|
|
643
|
-
onResolveLink,
|
|
644
|
-
preprocessContent,
|
|
645
|
-
componentOverrides,
|
|
646
|
-
textSize,
|
|
647
|
-
additionalRemarkPlugins
|
|
648
|
-
}) => {
|
|
649
|
-
const idCountsRef = useRef({});
|
|
650
|
-
const textSizes = useMemo(() => resolveTextSizeConfig(textSize), [textSize]);
|
|
651
|
-
const sectionIdMap = useMemo(() => {
|
|
652
|
-
const map = /* @__PURE__ */ new Map();
|
|
653
|
-
if (sectionIds) {
|
|
654
|
-
sectionIds.forEach((section) => {
|
|
655
|
-
const cleanTitle = section.title.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim().toLowerCase();
|
|
656
|
-
map.set(section.title.toLowerCase(), section.id);
|
|
657
|
-
map.set(cleanTitle, section.id);
|
|
658
|
-
map.set(section.title, section.id);
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
return map;
|
|
662
|
-
}, [sectionIds]);
|
|
663
|
-
const generateHeadingId = useCallback((text, level) => {
|
|
664
|
-
if (sectionIds && (level === 1 || level === 2)) {
|
|
665
|
-
const variations = [
|
|
666
|
-
text,
|
|
667
|
-
text.toLowerCase(),
|
|
668
|
-
text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim(),
|
|
669
|
-
text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim().toLowerCase()
|
|
670
|
-
];
|
|
671
|
-
for (const v of variations) {
|
|
672
|
-
const id = sectionIdMap.get(v);
|
|
673
|
-
if (id) return id;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
const baseId = text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, "").trim().toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/^-+|-+$/g, "");
|
|
677
|
-
const cleanId = baseId || `section-${Object.keys(idCountsRef.current).length + 1}`;
|
|
678
|
-
if (idCountsRef.current[cleanId]) {
|
|
679
|
-
idCountsRef.current[cleanId]++;
|
|
680
|
-
return `${cleanId}-${idCountsRef.current[cleanId]}`;
|
|
681
|
-
}
|
|
682
|
-
idCountsRef.current[cleanId] = 1;
|
|
683
|
-
return cleanId;
|
|
684
|
-
}, [sectionIds, sectionIdMap]);
|
|
685
|
-
const processedContent = useMemo(
|
|
686
|
-
() => escapeUnknownHtmlTags(preprocessContent ? preprocessContent(content) : content),
|
|
687
|
-
[preprocessContent, content]
|
|
688
|
-
);
|
|
689
|
-
const makeHeading = useCallback(
|
|
690
|
-
(Tag, level, headingClassName) => ({ children }) => {
|
|
691
|
-
const text = extractText(children);
|
|
692
|
-
const effectiveLevel = Tag === "h1" && demoteMarkdownH1ToH2 ? 2 : level;
|
|
693
|
-
const id = generateHeadingId(text, effectiveLevel);
|
|
694
|
-
const EffectiveTag = Tag === "h1" && demoteMarkdownH1ToH2 ? "h2" : Tag;
|
|
695
|
-
return /* @__PURE__ */ jsx4(EffectiveTag, { id, className: headingClassName, children });
|
|
696
|
-
},
|
|
697
|
-
[generateHeadingId, demoteMarkdownH1ToH2]
|
|
698
|
-
);
|
|
699
|
-
const components = useMemo(() => ({
|
|
700
|
-
// --- code ---
|
|
701
|
-
code: ({ node, inline, className: codeClassName, children, ...props }) => {
|
|
702
|
-
const match = /language-(\w+)/.exec(codeClassName || "");
|
|
703
|
-
const language = match ? match[1] : "";
|
|
704
|
-
if (!inline && language === "mermaid") {
|
|
705
|
-
return /* @__PURE__ */ jsx4(MermaidDiagram, { chart: String(children).replace(/\n$/, "") });
|
|
706
|
-
}
|
|
707
|
-
if (!inline && match) {
|
|
708
|
-
return /* @__PURE__ */ jsxs2("div", { className: "code-block-container border rounded-lg my-6 overflow-hidden bg-ods-card border-ods-border", children: [
|
|
709
|
-
/* @__PURE__ */ jsx4("div", { className: "code-header border-b px-4 py-2 bg-ods-card border-ods-border", children: /* @__PURE__ */ jsx4("span", { className: "font-sans text-xs uppercase tracking-wide text-ods-text-tertiary", children: language || "code" }) }),
|
|
710
|
-
/* @__PURE__ */ jsx4("div", { className: "p-4", children: /* @__PURE__ */ jsx4("pre", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx4(
|
|
711
|
-
"code",
|
|
712
|
-
{
|
|
713
|
-
className: cn(`language-${language} hljs`, textSizes.code),
|
|
714
|
-
style: {
|
|
715
|
-
fontFamily: "JetBrains Mono', 'SF Mono', Consolas, monospace",
|
|
716
|
-
background: "transparent",
|
|
717
|
-
color: "var(--ods-text-primary)"
|
|
718
|
-
},
|
|
719
|
-
...props,
|
|
720
|
-
children
|
|
721
|
-
}
|
|
722
|
-
) }) })
|
|
723
|
-
] });
|
|
724
|
-
}
|
|
725
|
-
return /* @__PURE__ */ jsx4("code", { className: "font-mono text-[0.9em] px-1.5 py-0.5 rounded border bg-ods-card text-ods-text-primary border-ods-border", ...props, children });
|
|
726
|
-
},
|
|
727
|
-
// --- div (pass-through, overridable for embeds) ---
|
|
728
|
-
div: ({ node, className: divClassName, children, ...props }) => /* @__PURE__ */ jsx4("div", { className: divClassName, ...props, children }),
|
|
729
|
-
// --- blockquote ---
|
|
730
|
-
blockquote: ({ children }) => /* @__PURE__ */ jsx4("blockquote", { className: "border-l-4 border-ods-accent ml-0 pl-6 my-8 py-4 rounded-r-lg bg-ods-bg-secondary", children: /* @__PURE__ */ jsx4("div", { className: cn("font-sans leading-relaxed text-ods-text-secondary", textSizes.blockquote), children }) }),
|
|
731
|
-
// --- headings ---
|
|
732
|
-
h1: makeHeading("h1", 1, cn("font-sans font-bold mt-8 mb-4 first:mt-0 text-ods-text-primary", textSizes.h1)),
|
|
733
|
-
h2: makeHeading("h2", 2, cn("font-sans font-semibold mt-8 mb-4 pb-2 border-b text-ods-text-primary border-ods-border", textSizes.h2)),
|
|
734
|
-
h3: makeHeading("h3", 3, cn("font-sans font-semibold mt-6 mb-3 text-ods-text-primary", textSizes.h3)),
|
|
735
|
-
h4: makeHeading("h4", 4, cn("font-sans font-semibold mt-4 mb-2 text-ods-text-primary", textSizes.h4)),
|
|
736
|
-
h5: makeHeading("h5", 5, cn("font-sans font-semibold mt-3 mb-2 text-ods-text-primary", textSizes.h5)),
|
|
737
|
-
h6: makeHeading("h6", 6, cn("font-sans font-semibold mt-3 mb-1 text-ods-text-primary", textSizes.h6)),
|
|
738
|
-
// --- paragraph ---
|
|
739
|
-
p: ({ children }) => /* @__PURE__ */ jsx4("p", { className: cn("leading-relaxed mb-4 first:mt-0 last:mb-0 text-ods-text-primary", textSizes.p), children }),
|
|
740
|
-
// --- links ---
|
|
741
|
-
a: ({ href, children, className: linkClassName }) => {
|
|
742
|
-
const isBroken = brokenLinks.includes(href);
|
|
743
|
-
const isInternalDocLink = propCurrentPath !== void 0 && propCurrentPath !== null && href && !href.startsWith("http") && !href.startsWith("#");
|
|
744
|
-
if (isBroken) {
|
|
745
|
-
return /* @__PURE__ */ jsxs2("span", { className: "text-ods-accent cursor-not-allowed", children: [
|
|
746
|
-
children,
|
|
747
|
-
/* @__PURE__ */ jsx4("sup", { className: "ml-1 text-xs font-bold text-ods-attention-red-error", children: "[BROKEN]" })
|
|
748
|
-
] });
|
|
749
|
-
}
|
|
750
|
-
if (isInternalDocLink && onInternalLinkClick) {
|
|
751
|
-
const currentPath = propCurrentPath ?? "";
|
|
752
|
-
return /* @__PURE__ */ jsx4(
|
|
753
|
-
"span",
|
|
754
|
-
{
|
|
755
|
-
className: "text-ods-accent no-underline relative transition-colors duration-200 hover:after:w-full after:content-[''] after:absolute after:w-0 after:h-0.5 after:-bottom-0.5 after:left-0 after:bg-ods-accent after:transition-all after:duration-300 cursor-pointer",
|
|
756
|
-
onClick: async (e) => {
|
|
757
|
-
e.preventDefault();
|
|
758
|
-
e.stopPropagation();
|
|
759
|
-
if (onResolveLink) {
|
|
760
|
-
try {
|
|
761
|
-
const result = await onResolveLink(href, currentPath);
|
|
762
|
-
if (result.type === "folder-no-readme" && result.action === "expand_folder") {
|
|
763
|
-
onInternalLinkClick(result.resolvedPath, { expandFolder: true, fromInternalLink: true });
|
|
764
|
-
} else if (result.type === "not-found") {
|
|
765
|
-
return;
|
|
766
|
-
} else if (result.success && result.resolvedPath) {
|
|
767
|
-
onInternalLinkClick(result.resolvedPath, { fromInternalLink: true });
|
|
768
|
-
}
|
|
769
|
-
} catch (error) {
|
|
770
|
-
console.error("Error resolving link:", error);
|
|
771
|
-
}
|
|
772
|
-
} else {
|
|
773
|
-
onInternalLinkClick(href, { fromInternalLink: true });
|
|
774
|
-
}
|
|
775
|
-
},
|
|
776
|
-
role: "link",
|
|
777
|
-
tabIndex: 0,
|
|
778
|
-
onKeyDown: (e) => {
|
|
779
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
780
|
-
e.currentTarget.click();
|
|
781
|
-
}
|
|
782
|
-
},
|
|
783
|
-
children
|
|
784
|
-
}
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
return /* @__PURE__ */ jsx4(
|
|
788
|
-
"a",
|
|
789
|
-
{
|
|
790
|
-
href,
|
|
791
|
-
className: `text-ods-accent no-underline relative transition-colors duration-200 hover:after:w-full after:content-[''] after:absolute after:w-0 after:h-0.5 after:-bottom-0.5 after:left-0 after:bg-ods-accent after:transition-all after:duration-300 ${linkClassName || ""}`,
|
|
792
|
-
target: href?.startsWith("http") ? "_blank" : void 0,
|
|
793
|
-
rel: href?.startsWith("http") ? "noopener noreferrer" : void 0,
|
|
794
|
-
children
|
|
795
|
-
}
|
|
796
|
-
);
|
|
797
|
-
},
|
|
798
|
-
// --- images ---
|
|
799
|
-
// Inline content image renderer. Used by blog posts, docs, AND chat
|
|
800
|
-
// messages (where users may attach screenshots / photos via the +
|
|
801
|
-
// attachment button).
|
|
802
|
-
//
|
|
803
|
-
// Sizing rules (2025-2026 best practice — Claude.ai, ChatGPT,
|
|
804
|
-
// iMessage, Slack, Discord inline image patterns):
|
|
805
|
-
//
|
|
806
|
-
// - CAP max-width at 400px so inline images don't blow out the
|
|
807
|
-
// message column on wide panels. Click-to-expand opens a
|
|
808
|
-
// full-resolution modal for users who need detail.
|
|
809
|
-
// - CAP max-height at 400px so portrait-orientation images
|
|
810
|
-
// don't dominate vertical space (a 1000x3000 phone screenshot
|
|
811
|
-
// would otherwise push the next message off-screen).
|
|
812
|
-
// - Small images render at NATURAL pixel size — a 64x64
|
|
813
|
-
// thumbnail stays 64x64, not stretched to fill the column.
|
|
814
|
-
// - `object-contain` preserves aspect ratio when both dimensions
|
|
815
|
-
// are constrained (long landscape, tall portrait).
|
|
816
|
-
//
|
|
817
|
-
// Implementation: Next.js `<Image>` — the project's canonical
|
|
818
|
-
// image primitive. Gives us:
|
|
819
|
-
// - WebP/AVIF format conversion for modern browsers (smaller
|
|
820
|
-
// bytes for the same visual quality).
|
|
821
|
-
// - Responsive `srcset` via the `sizes` prop (browser picks the
|
|
822
|
-
// right variant for the viewport).
|
|
823
|
-
// - Automatic lazy-loading (`loading="lazy"` by default, mid-
|
|
824
|
-
// page images skipped until they near the viewport).
|
|
825
|
-
// - Automatic `decoding="async"` so image decode doesn't block
|
|
826
|
-
// paint.
|
|
827
|
-
//
|
|
828
|
-
// `width={400} height={400}` props are REQUIRED by Next.js
|
|
829
|
-
// `<Image>` (non-`fill` mode throws without them) but they're
|
|
830
|
-
// effectively a CEILING here, not the display size — the CSS
|
|
831
|
-
// overrides (`w-auto h-auto max-w-full max-h-[400px]`) drive
|
|
832
|
-
// the actual rendered size. The inline `style={{ width: 'auto',
|
|
833
|
-
// height: 'auto' }}` is belt-and-suspenders: Next.js Image sets
|
|
834
|
-
// matching HTML `width`/`height` attributes on the rendered
|
|
835
|
-
// `<img>` and inline style wins over both HTML attributes AND
|
|
836
|
-
// utility classes regardless of CSS-specificity surprises.
|
|
837
|
-
//
|
|
838
|
-
// Layout reservation trade-off: until image bytes arrive, the
|
|
839
|
-
// browser may reserve a placeholder box up to 400x400 (the props'
|
|
840
|
-
// intrinsic-ratio hint). Once loaded, the box collapses to the
|
|
841
|
-
// natural size if smaller. This is the standard Next.js Image
|
|
842
|
-
// behavior across the codebase — accepted for the optimizer +
|
|
843
|
-
// responsive-srcset benefits. Chat attachments hosted in side
|
|
844
|
-
// panels see this only on first render of a fresh attachment
|
|
845
|
-
// (cached re-renders pop in without a perceptible shift).
|
|
846
|
-
img: ({ src, alt }) => {
|
|
847
|
-
if (!src || typeof src !== "string" || src.trim() === "") return null;
|
|
848
|
-
return /* @__PURE__ */ jsx4(
|
|
849
|
-
next_image_default,
|
|
850
|
-
{
|
|
851
|
-
src,
|
|
852
|
-
alt: alt ?? "No image available",
|
|
853
|
-
width: 400,
|
|
854
|
-
height: 400,
|
|
855
|
-
sizes: "(max-width: 400px) 100vw, 400px",
|
|
856
|
-
className: "max-w-full max-h-[400px] w-auto h-auto rounded-lg object-contain",
|
|
857
|
-
style: { width: "auto", height: "auto" }
|
|
858
|
-
}
|
|
859
|
-
);
|
|
860
|
-
},
|
|
861
|
-
// --- lists ---
|
|
862
|
-
ul: ({ children }) => /* @__PURE__ */ jsx4("ul", { className: "list-disc list-outside my-4 ml-8 space-y-2 text-ods-text-primary", children }),
|
|
863
|
-
ol: ({ children }) => /* @__PURE__ */ jsx4("ol", { className: "list-decimal list-outside my-4 ml-8 space-y-2 text-ods-text-primary", children }),
|
|
864
|
-
li: ({ children }) => /* @__PURE__ */ jsx4("li", { className: cn("leading-relaxed pl-2", textSizes.li), children }),
|
|
865
|
-
// --- tables ---
|
|
866
|
-
table: ({ children }) => /* @__PURE__ */ jsx4("div", { className: "table-container my-6 overflow-x-auto", children: /* @__PURE__ */ jsx4("div", { className: "min-w-full border rounded-lg border-ods-border bg-ods-card", children: /* @__PURE__ */ jsx4("table", { className: "w-full table-fixed md:table-auto", children }) }) }),
|
|
867
|
-
thead: ({ children }) => /* @__PURE__ */ jsx4("thead", { className: "bg-ods-bg-secondary", children }),
|
|
868
|
-
th: ({ children }) => /* @__PURE__ */ jsx4("th", { className: cn("px-2 md:px-4 py-3 text-left font-semibold text-ods-accent border-r last:border-r-0 break-words border-ods-border", textSizes.th), children }),
|
|
869
|
-
td: ({ children }) => /* @__PURE__ */ jsx4("td", { className: cn("px-2 md:px-4 py-3 border-r last:border-r-0 border-b break-words whitespace-normal text-ods-text-primary border-ods-border", textSizes.td), children }),
|
|
870
|
-
// --- horizontal rule ---
|
|
871
|
-
hr: () => /* @__PURE__ */ jsx4("hr", { className: "border-0 border-t my-8 border-ods-border" }),
|
|
872
|
-
// --- merge overrides ---
|
|
873
|
-
...componentOverrides
|
|
874
|
-
}), [makeHeading, brokenLinks, propCurrentPath, onInternalLinkClick, onResolveLink, componentOverrides, textSizes]);
|
|
875
|
-
return /* @__PURE__ */ jsxs2("div", { className: `simple-markdown-renderer ${className}`, children: [
|
|
876
|
-
/* @__PURE__ */ jsx4("style", { dangerouslySetInnerHTML: { __html: mermaidStyles } }),
|
|
877
|
-
/* @__PURE__ */ jsx4("div", { className: "content-wrapper max-w-none break-words", children: /* @__PURE__ */ jsx4("article", { className: "prose prose-lg max-w-none", children: /* @__PURE__ */ jsx4(
|
|
878
|
-
ReactMarkdown,
|
|
879
|
-
{
|
|
880
|
-
remarkPlugins: [remarkGfm, remarkBreaks, ...additionalRemarkPlugins ?? []],
|
|
881
|
-
rehypePlugins: [
|
|
882
|
-
// ORDER MATTERS: rehype-raw parses the raw HTML embedded
|
|
883
|
-
// in the source markdown into HAST nodes; rehypeStripUnsafe
|
|
884
|
-
// then walks the HAST tree and drops XSS vectors (on*
|
|
885
|
-
// event handlers, javascript: URLs, script/style/iframe-srcdoc,
|
|
886
|
-
// data: URIs). Reversing the order would have nothing to
|
|
887
|
-
// sanitize (raw HTML would still be strings).
|
|
888
|
-
rehypeRaw,
|
|
889
|
-
rehypeStripUnsafe,
|
|
890
|
-
[rehypeHighlight, { detect: true, ignoreMissing: true }]
|
|
891
|
-
],
|
|
892
|
-
urlTransform: cardAwareUrlTransform,
|
|
893
|
-
components,
|
|
894
|
-
children: processedContent
|
|
895
|
-
}
|
|
896
|
-
) }) })
|
|
897
|
-
] });
|
|
898
|
-
};
|
|
899
|
-
var SimpleMarkdownRenderer = memo(SimpleMarkdownRendererImpl);
|
|
900
|
-
|
|
901
|
-
// src/components/ui/square-avatar.tsx
|
|
902
|
-
import * as React4 from "react";
|
|
903
|
-
init_cn();
|
|
904
|
-
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
905
|
-
var SquareAvatar = React4.memo(React4.forwardRef(
|
|
906
|
-
({ className, src, alt, size = "md", fallback, variant = "square", initialsClassName, ...props }, ref) => {
|
|
907
|
-
const sizeClasses = {
|
|
908
|
-
sm: "h-8 w-8",
|
|
909
|
-
md: "h-10 w-10",
|
|
910
|
-
lg: "h-12 w-12",
|
|
911
|
-
xl: "h-16 w-16"
|
|
912
|
-
};
|
|
913
|
-
const sizePx = {
|
|
914
|
-
sm: 32,
|
|
915
|
-
md: 40,
|
|
916
|
-
lg: 48,
|
|
917
|
-
xl: 64
|
|
918
|
-
};
|
|
919
|
-
const variantClasses = {
|
|
920
|
-
square: "rounded-md",
|
|
921
|
-
round: "rounded-full"
|
|
922
|
-
};
|
|
923
|
-
return /* @__PURE__ */ jsxs3(
|
|
924
|
-
"div",
|
|
925
|
-
{
|
|
926
|
-
className: cn(
|
|
927
|
-
"relative flex items-center justify-center shrink-0 overflow-hidden border border-ods-border bg-ods-bg",
|
|
928
|
-
sizeClasses[size],
|
|
929
|
-
variantClasses[variant],
|
|
930
|
-
className
|
|
931
|
-
),
|
|
932
|
-
ref,
|
|
933
|
-
...props,
|
|
934
|
-
children: [
|
|
935
|
-
/* @__PURE__ */ jsx5("div", { className: cn(
|
|
936
|
-
// Initials default to `--color-text-primary` (the old
|
|
937
|
-
// `text-ods-text-primary` value) so they stay readable on the default
|
|
938
|
-
// `bg-ods-bg` AND on the brand accent fills (`bg-ods-flamingo-pink`
|
|
939
|
-
// for the current user, `bg-ods-flamingo-cyan` for Mingo). The color
|
|
940
|
-
// resolves through `--ods-avatar-initials` with that fallback, so a
|
|
941
|
-
// host themed with a custom avatar fill can override the var with a
|
|
942
|
-
// contrast-correct value (e.g. `getReadableTextColor(accent)`) WITHOUT
|
|
943
|
-
// regressing any avatar that leaves the var unset. A caller passing
|
|
944
|
-
// its own `initialsClassName` text color still wins (tailwind-merge
|
|
945
|
-
// keeps the later class).
|
|
946
|
-
"flex items-center justify-center text-xs font-medium text-[color:var(--ods-avatar-initials,var(--color-text-primary))]",
|
|
947
|
-
initialsClassName,
|
|
948
|
-
src && "hidden"
|
|
949
|
-
), children: getFirstLastInitials(fallback || alt) || "?" }),
|
|
950
|
-
src && /* @__PURE__ */ jsx5(
|
|
951
|
-
next_image_default,
|
|
952
|
-
{
|
|
953
|
-
className: "absolute inset-0 h-full w-full object-cover",
|
|
954
|
-
src,
|
|
955
|
-
alt: alt || "",
|
|
956
|
-
width: sizePx[size],
|
|
957
|
-
height: sizePx[size],
|
|
958
|
-
onError: (e) => {
|
|
959
|
-
e.currentTarget.style.display = "none";
|
|
960
|
-
const el = e.currentTarget.previousElementSibling;
|
|
961
|
-
if (el) el.classList.remove("hidden");
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
)
|
|
965
|
-
]
|
|
966
|
-
}
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
));
|
|
970
|
-
SquareAvatar.displayName = "SquareAvatar";
|
|
971
|
-
|
|
972
|
-
// src/components/ui/actions-menu.tsx
|
|
973
|
-
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
974
|
-
|
|
975
|
-
// node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
976
|
-
import * as React6 from "react";
|
|
977
|
-
|
|
978
|
-
// node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
|
|
979
|
-
import * as React5 from "react";
|
|
980
|
-
var useLayoutEffect2 = globalThis?.document ? React5.useLayoutEffect : () => {
|
|
981
|
-
};
|
|
982
|
-
|
|
983
|
-
// node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
984
|
-
import * as React22 from "react";
|
|
985
|
-
var useInsertionEffect = React6[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
|
|
986
|
-
function useControllableState({
|
|
987
|
-
prop,
|
|
988
|
-
defaultProp,
|
|
989
|
-
onChange = () => {
|
|
990
|
-
},
|
|
991
|
-
caller
|
|
992
|
-
}) {
|
|
993
|
-
const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
|
|
994
|
-
defaultProp,
|
|
995
|
-
onChange
|
|
996
|
-
});
|
|
997
|
-
const isControlled = prop !== void 0;
|
|
998
|
-
const value = isControlled ? prop : uncontrolledProp;
|
|
999
|
-
if (true) {
|
|
1000
|
-
const isControlledRef = React6.useRef(prop !== void 0);
|
|
1001
|
-
React6.useEffect(() => {
|
|
1002
|
-
const wasControlled = isControlledRef.current;
|
|
1003
|
-
if (wasControlled !== isControlled) {
|
|
1004
|
-
const from = wasControlled ? "controlled" : "uncontrolled";
|
|
1005
|
-
const to = isControlled ? "controlled" : "uncontrolled";
|
|
1006
|
-
console.warn(
|
|
1007
|
-
`${caller} is changing from ${from} to ${to}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
|
-
isControlledRef.current = isControlled;
|
|
1011
|
-
}, [isControlled, caller]);
|
|
1012
|
-
}
|
|
1013
|
-
const setValue = React6.useCallback(
|
|
1014
|
-
(nextValue) => {
|
|
1015
|
-
if (isControlled) {
|
|
1016
|
-
const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
|
|
1017
|
-
if (value2 !== prop) {
|
|
1018
|
-
onChangeRef.current?.(value2);
|
|
1019
|
-
}
|
|
1020
|
-
} else {
|
|
1021
|
-
setUncontrolledProp(nextValue);
|
|
1022
|
-
}
|
|
1023
|
-
},
|
|
1024
|
-
[isControlled, prop, setUncontrolledProp, onChangeRef]
|
|
1025
|
-
);
|
|
1026
|
-
return [value, setValue];
|
|
1027
|
-
}
|
|
1028
|
-
function useUncontrolledState({
|
|
1029
|
-
defaultProp,
|
|
1030
|
-
onChange
|
|
1031
|
-
}) {
|
|
1032
|
-
const [value, setValue] = React6.useState(defaultProp);
|
|
1033
|
-
const prevValueRef = React6.useRef(value);
|
|
1034
|
-
const onChangeRef = React6.useRef(onChange);
|
|
1035
|
-
useInsertionEffect(() => {
|
|
1036
|
-
onChangeRef.current = onChange;
|
|
1037
|
-
}, [onChange]);
|
|
1038
|
-
React6.useEffect(() => {
|
|
1039
|
-
if (prevValueRef.current !== value) {
|
|
1040
|
-
onChangeRef.current?.(value);
|
|
1041
|
-
prevValueRef.current = value;
|
|
1042
|
-
}
|
|
1043
|
-
}, [value, prevValueRef]);
|
|
1044
|
-
return [value, setValue, onChangeRef];
|
|
1045
|
-
}
|
|
1046
|
-
function isFunction(value) {
|
|
1047
|
-
return typeof value === "function";
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
// src/components/ui/actions-menu.tsx
|
|
1051
|
-
init_next_link();
|
|
1052
|
-
import { Check } from "lucide-react";
|
|
1053
|
-
import React7, { useCallback as useCallback3 } from "react";
|
|
1054
|
-
init_cn();
|
|
1055
|
-
init_button();
|
|
1056
|
-
import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1057
|
-
var ROW_CLASSES = "flex flex-1 min-w-0 items-center gap-[var(--spacing-system-xsf)] p-[var(--spacing-system-s)] cursor-pointer transition-colors bg-ods-bg outline-none";
|
|
1058
|
-
var WRAPPER_CLASSES = "relative flex items-stretch border-b border-ods-border last:border-b-0";
|
|
1059
|
-
var SECONDARY_ACTION_CLASSES = "flex p-[var(--spacing-system-s)] shrink-0 items-center justify-center self-stretch border-l border-ods-border transition-colors hover:bg-ods-bg-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus [&_svg]:w-4 [&_svg]:h-4 md:[&_svg]:w-6 md:[&_svg]:h-6";
|
|
1060
|
-
var SecondaryAction = ({ action }) => {
|
|
1061
|
-
const handleClick = useCallback3(
|
|
1062
|
-
(e) => {
|
|
1063
|
-
e.stopPropagation();
|
|
1064
|
-
if (action.disabled) {
|
|
1065
|
-
e.preventDefault();
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
action.onClick?.();
|
|
1069
|
-
},
|
|
1070
|
-
[action]
|
|
1071
|
-
);
|
|
1072
|
-
const classes = cn(
|
|
1073
|
-
SECONDARY_ACTION_CLASSES,
|
|
1074
|
-
action.disabled && "cursor-not-allowed opacity-60 pointer-events-none"
|
|
1075
|
-
);
|
|
1076
|
-
if (action.href) {
|
|
1077
|
-
return /* @__PURE__ */ jsx6(
|
|
1078
|
-
next_link_default,
|
|
1079
|
-
{
|
|
1080
|
-
href: action.href,
|
|
1081
|
-
prefetch: false,
|
|
1082
|
-
target: action.openInNewTab ? "_blank" : void 0,
|
|
1083
|
-
rel: action.openInNewTab ? "noopener noreferrer" : void 0,
|
|
1084
|
-
"aria-label": action["aria-label"],
|
|
1085
|
-
"aria-disabled": action.disabled || void 0,
|
|
1086
|
-
tabIndex: action.disabled ? -1 : void 0,
|
|
1087
|
-
className: classes,
|
|
1088
|
-
onClick: handleClick,
|
|
1089
|
-
children: action.icon
|
|
1090
|
-
}
|
|
1091
|
-
);
|
|
1092
|
-
}
|
|
1093
|
-
return /* @__PURE__ */ jsx6(
|
|
1094
|
-
"button",
|
|
1095
|
-
{
|
|
1096
|
-
type: "button",
|
|
1097
|
-
"aria-label": action["aria-label"],
|
|
1098
|
-
disabled: action.disabled,
|
|
1099
|
-
className: classes,
|
|
1100
|
-
onClick: handleClick,
|
|
1101
|
-
children: action.icon
|
|
1102
|
-
}
|
|
1103
|
-
);
|
|
1104
|
-
};
|
|
1105
|
-
var MenuItem = ({ item, onItemClick }) => {
|
|
1106
|
-
const activate = useCallback3(() => {
|
|
1107
|
-
if (item.disabled) return;
|
|
1108
|
-
if (item.type === "checkbox") {
|
|
1109
|
-
item.onClick?.();
|
|
1110
|
-
onItemClick?.(item);
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
if (item.type === "submenu") return;
|
|
1114
|
-
item.onClick?.();
|
|
1115
|
-
onItemClick?.(item);
|
|
1116
|
-
}, [item, onItemClick]);
|
|
1117
|
-
const handleClick = useCallback3(
|
|
1118
|
-
(e) => {
|
|
1119
|
-
e.stopPropagation();
|
|
1120
|
-
e.preventDefault();
|
|
1121
|
-
activate();
|
|
1122
|
-
},
|
|
1123
|
-
[activate]
|
|
1124
|
-
);
|
|
1125
|
-
const handleKeyDown = useCallback3(
|
|
1126
|
-
(e) => {
|
|
1127
|
-
if (e.key !== "Enter" && e.key !== " ") return;
|
|
1128
|
-
e.preventDefault();
|
|
1129
|
-
e.stopPropagation();
|
|
1130
|
-
activate();
|
|
1131
|
-
},
|
|
1132
|
-
[activate]
|
|
1133
|
-
);
|
|
1134
|
-
const handleLinkClick = useCallback3(
|
|
1135
|
-
(e) => {
|
|
1136
|
-
if (item.disabled) {
|
|
1137
|
-
e.preventDefault();
|
|
1138
|
-
e.stopPropagation();
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
item.onClick?.();
|
|
1142
|
-
onItemClick?.(item);
|
|
1143
|
-
},
|
|
1144
|
-
[item, onItemClick]
|
|
1145
|
-
);
|
|
1146
|
-
if (item.type === "separator") {
|
|
1147
|
-
return /* @__PURE__ */ jsx6("div", { className: "bg-ods-system-greys-soft-grey h-1 w-full" });
|
|
1148
|
-
}
|
|
1149
|
-
const itemClasses = cn(
|
|
1150
|
-
ROW_CLASSES,
|
|
1151
|
-
item.disabled ? "text-ods-text-secondary cursor-not-allowed pointer-events-none opacity-60" : "text-ods-text-primary hover:bg-ods-bg-hover"
|
|
1152
|
-
);
|
|
1153
|
-
const subTriggerClasses = cn(
|
|
1154
|
-
itemClasses,
|
|
1155
|
-
"data-[state=open]:bg-ods-bg-active focus:bg-ods-bg-hover"
|
|
1156
|
-
);
|
|
1157
|
-
const renderAsLink = !!item.href && item.type !== "submenu" && item.type !== "checkbox";
|
|
1158
|
-
const rowContent = /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1159
|
-
item.icon && /* @__PURE__ */ jsx6(
|
|
1160
|
-
"div",
|
|
1161
|
-
{
|
|
1162
|
-
className: cn(
|
|
1163
|
-
"w-4 h-4 md:w-6 md:h-6 flex-shrink-0 flex items-center justify-center",
|
|
1164
|
-
item.danger && "text-ods-error",
|
|
1165
|
-
item.disabled && "opacity-50"
|
|
1166
|
-
),
|
|
1167
|
-
children: item.icon
|
|
1168
|
-
}
|
|
1169
|
-
),
|
|
1170
|
-
/* @__PURE__ */ jsx6(
|
|
1171
|
-
"span",
|
|
1172
|
-
{
|
|
1173
|
-
className: cn(
|
|
1174
|
-
"flex-1 text-h4 font-medium leading-6",
|
|
1175
|
-
item.disabled ? "text-ods-text-secondary" : item.danger ? "text-ods-error" : "text-ods-text-primary"
|
|
1176
|
-
),
|
|
1177
|
-
children: item.label
|
|
1178
|
-
}
|
|
1179
|
-
),
|
|
1180
|
-
item.type === "checkbox" && /* @__PURE__ */ jsx6(
|
|
1181
|
-
"div",
|
|
1182
|
-
{
|
|
1183
|
-
className: cn(
|
|
1184
|
-
"w-4 h-4 md:w-6 md:h-6 flex items-center justify-center rounded-md transition-colors",
|
|
1185
|
-
item.checked ? "bg-ods-accent" : "border-2 border-ods-border bg-transparent"
|
|
1186
|
-
),
|
|
1187
|
-
children: item.checked && /* @__PURE__ */ jsx6(Check, { className: "w-3 h-3 md:w-4 md:h-4 text-ods-text-on-accent", strokeWidth: 3 })
|
|
1188
|
-
}
|
|
1189
|
-
),
|
|
1190
|
-
item.type === "submenu" && /* @__PURE__ */ jsx6(Chevron02RightIcon, { className: "w-4 h-4 md:w-6 md:h-6 text-ods-text-secondary" })
|
|
1191
|
-
] });
|
|
1192
|
-
if (renderAsLink && item.href) {
|
|
1193
|
-
return /* @__PURE__ */ jsxs4("div", { className: WRAPPER_CLASSES, children: [
|
|
1194
|
-
/* @__PURE__ */ jsx6(
|
|
1195
|
-
next_link_default,
|
|
1196
|
-
{
|
|
1197
|
-
href: item.href,
|
|
1198
|
-
prefetch: false,
|
|
1199
|
-
className: itemClasses,
|
|
1200
|
-
onClick: handleLinkClick,
|
|
1201
|
-
"aria-disabled": item.disabled,
|
|
1202
|
-
tabIndex: item.disabled ? -1 : void 0,
|
|
1203
|
-
children: rowContent
|
|
1204
|
-
}
|
|
1205
|
-
),
|
|
1206
|
-
item.iconAction && /* @__PURE__ */ jsx6(SecondaryAction, { action: item.iconAction })
|
|
1207
|
-
] });
|
|
1208
|
-
}
|
|
1209
|
-
if (item.type === "submenu" && item.submenu) {
|
|
1210
|
-
return /* @__PURE__ */ jsxs4("div", { className: WRAPPER_CLASSES, children: [
|
|
1211
|
-
/* @__PURE__ */ jsxs4(DropdownMenuPrimitive.Sub, { children: [
|
|
1212
|
-
/* @__PURE__ */ jsx6(
|
|
1213
|
-
DropdownMenuPrimitive.SubTrigger,
|
|
1214
|
-
{
|
|
1215
|
-
disabled: item.disabled,
|
|
1216
|
-
className: subTriggerClasses,
|
|
1217
|
-
children: rowContent
|
|
1218
|
-
}
|
|
1219
|
-
),
|
|
1220
|
-
/* @__PURE__ */ jsx6(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx6(
|
|
1221
|
-
DropdownMenuPrimitive.SubContent,
|
|
1222
|
-
{
|
|
1223
|
-
sideOffset: 4,
|
|
1224
|
-
className: "z-[1500] min-w-[256px] max-h-[var(--radix-popper-available-height)] bg-ods-bg border border-ods-border rounded-md shadow-xl overflow-y-auto p-0",
|
|
1225
|
-
children: item.submenu.map((subItem, index) => /* @__PURE__ */ jsx6(
|
|
1226
|
-
MenuItem,
|
|
1227
|
-
{
|
|
1228
|
-
item: subItem,
|
|
1229
|
-
onItemClick
|
|
1230
|
-
},
|
|
1231
|
-
subItem.id || index
|
|
1232
|
-
))
|
|
1233
|
-
}
|
|
1234
|
-
) })
|
|
1235
|
-
] }),
|
|
1236
|
-
item.iconAction && /* @__PURE__ */ jsx6(SecondaryAction, { action: item.iconAction })
|
|
1237
|
-
] });
|
|
1238
|
-
}
|
|
1239
|
-
return /* @__PURE__ */ jsxs4("div", { className: WRAPPER_CLASSES, children: [
|
|
1240
|
-
/* @__PURE__ */ jsx6(
|
|
1241
|
-
"div",
|
|
1242
|
-
{
|
|
1243
|
-
role: "menuitem",
|
|
1244
|
-
tabIndex: item.disabled ? -1 : 0,
|
|
1245
|
-
"aria-disabled": item.disabled,
|
|
1246
|
-
className: itemClasses,
|
|
1247
|
-
onClick: handleClick,
|
|
1248
|
-
onKeyDown: handleKeyDown,
|
|
1249
|
-
children: rowContent
|
|
1250
|
-
}
|
|
1251
|
-
),
|
|
1252
|
-
item.iconAction && /* @__PURE__ */ jsx6(SecondaryAction, { action: item.iconAction })
|
|
1253
|
-
] });
|
|
1254
|
-
};
|
|
1255
|
-
var GroupSeparator = () => /* @__PURE__ */ jsx6("div", { className: "bg-ods-bg-surface h-[3px] w-full" });
|
|
1256
|
-
var ActionsMenu = ({
|
|
1257
|
-
groups,
|
|
1258
|
-
className = "",
|
|
1259
|
-
onItemClick
|
|
1260
|
-
}) => {
|
|
1261
|
-
return /* @__PURE__ */ jsx6(
|
|
1262
|
-
"div",
|
|
1263
|
-
{
|
|
1264
|
-
className: `relative min-w-[256px] max-h-[var(--radix-popper-available-height)] bg-ods-bg border border-ods-border rounded-md shadow-lg overflow-y-auto ${className}`,
|
|
1265
|
-
children: groups.map((group, groupIndex) => {
|
|
1266
|
-
const groupKey = group.id || group.items.map((i) => i.id).join("|");
|
|
1267
|
-
return /* @__PURE__ */ jsxs4(React7.Fragment, { children: [
|
|
1268
|
-
group.items.map((item, itemIndex) => /* @__PURE__ */ jsx6(
|
|
1269
|
-
MenuItem,
|
|
1270
|
-
{
|
|
1271
|
-
item,
|
|
1272
|
-
onItemClick
|
|
1273
|
-
},
|
|
1274
|
-
item.id || `${groupKey}-${itemIndex}`
|
|
1275
|
-
)),
|
|
1276
|
-
group.separator && groupIndex < groups.length - 1 && /* @__PURE__ */ jsx6(GroupSeparator, {})
|
|
1277
|
-
] }, groupKey);
|
|
1278
|
-
})
|
|
1279
|
-
}
|
|
1280
|
-
);
|
|
1281
|
-
};
|
|
1282
|
-
var ActionsMenuDropdown = ({
|
|
1283
|
-
groups,
|
|
1284
|
-
onItemClick,
|
|
1285
|
-
className,
|
|
1286
|
-
trigger,
|
|
1287
|
-
customTrigger,
|
|
1288
|
-
triggerAriaLabel = "More actions",
|
|
1289
|
-
triggerClassName,
|
|
1290
|
-
contentClassName,
|
|
1291
|
-
align = "end",
|
|
1292
|
-
side = "bottom",
|
|
1293
|
-
sideOffset = 6,
|
|
1294
|
-
open: openProp,
|
|
1295
|
-
onOpenChange,
|
|
1296
|
-
onCloseAutoFocus
|
|
1297
|
-
}) => {
|
|
1298
|
-
const [open = false, setOpen] = useControllableState({
|
|
1299
|
-
prop: openProp,
|
|
1300
|
-
defaultProp: false,
|
|
1301
|
-
onChange: onOpenChange
|
|
1302
|
-
});
|
|
1303
|
-
const handleItemClick = useCallback3(
|
|
1304
|
-
(item) => {
|
|
1305
|
-
onItemClick?.(item);
|
|
1306
|
-
if (item.type !== "checkbox" && item.type !== "submenu" && item.closeOnSelect !== false) {
|
|
1307
|
-
setOpen(false);
|
|
1308
|
-
}
|
|
1309
|
-
},
|
|
1310
|
-
[onItemClick, setOpen]
|
|
1311
|
-
);
|
|
1312
|
-
return /* @__PURE__ */ jsxs4(DropdownMenu, { open, onOpenChange: setOpen, modal: false, children: [
|
|
1313
|
-
/* @__PURE__ */ jsx6(DropdownMenuTrigger, { asChild: true, children: customTrigger ?? /* @__PURE__ */ jsx6(
|
|
1314
|
-
Button,
|
|
1315
|
-
{
|
|
1316
|
-
variant: "outline",
|
|
1317
|
-
size: "icon",
|
|
1318
|
-
"aria-label": triggerAriaLabel,
|
|
1319
|
-
className: triggerClassName || "bg-ods-card border-ods-border hover:bg-ods-bg-hover flex items-center justify-center focus-visible:ring-0",
|
|
1320
|
-
leftIcon: trigger ?? /* @__PURE__ */ jsx6(Ellipsis01Icon, { size: 24, className: "text-ods-text-primary" })
|
|
1321
|
-
}
|
|
1322
|
-
) }),
|
|
1323
|
-
/* @__PURE__ */ jsx6(
|
|
1324
|
-
DropdownMenuContent,
|
|
1325
|
-
{
|
|
1326
|
-
align,
|
|
1327
|
-
side,
|
|
1328
|
-
sideOffset,
|
|
1329
|
-
onCloseAutoFocus,
|
|
1330
|
-
className: cn(
|
|
1331
|
-
"p-0 border-0 bg-transparent shadow-none overflow-visible",
|
|
1332
|
-
contentClassName
|
|
1333
|
-
),
|
|
1334
|
-
children: /* @__PURE__ */ jsx6(
|
|
1335
|
-
ActionsMenu,
|
|
1336
|
-
{
|
|
1337
|
-
groups,
|
|
1338
|
-
onItemClick: handleItemClick,
|
|
1339
|
-
className
|
|
1340
|
-
}
|
|
1341
|
-
)
|
|
1342
|
-
}
|
|
1343
|
-
)
|
|
1344
|
-
] });
|
|
1345
|
-
};
|
|
1346
|
-
|
|
1347
|
-
// src/components/chat/utils/nav-anchor-props.ts
|
|
1348
|
-
function isSameOriginAbsoluteHref(href) {
|
|
1349
|
-
if (typeof window === "undefined") return false;
|
|
1350
|
-
if (!/^https?:\/\//i.test(href)) return false;
|
|
1351
|
-
try {
|
|
1352
|
-
return new URL(href).origin === window.location.origin;
|
|
1353
|
-
} catch {
|
|
1354
|
-
return false;
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
function computeIsNewTab(runtime, href, targetPlatform) {
|
|
1358
|
-
if (!href) return false;
|
|
1359
|
-
if (runtime.navigation.mode === "embed") return !isSameOriginAbsoluteHref(href);
|
|
1360
|
-
return runtime.navigation.decideNewTab?.({ href, targetPlatform }) ?? decideNewTab({
|
|
1361
|
-
href,
|
|
1362
|
-
targetPlatform,
|
|
1363
|
-
currentSource: runtime.source ?? ""
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
function newTabAnchorAttrs(isNewTab) {
|
|
1367
|
-
return isNewTab ? { target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1368
|
-
}
|
|
1369
|
-
function buildAnchorProps(href, isNewTab) {
|
|
1370
|
-
return href ? { href, ...newTabAnchorAttrs(isNewTab) } : void 0;
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
// src/components/chat/entity-cards/entity-author-card.tsx
|
|
1374
|
-
init_next_link();
|
|
1375
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1376
|
-
var EMPTY_AUTHOR_PLACEHOLDER = {
|
|
1377
|
-
full_name: "\u2014",
|
|
1378
|
-
avatar_url: null,
|
|
1379
|
-
job_title: "Unknown"
|
|
1380
|
-
};
|
|
1381
|
-
function EntityMetadataValueCell({
|
|
1382
|
-
value,
|
|
1383
|
-
label,
|
|
1384
|
-
className,
|
|
1385
|
-
uppercase = true
|
|
1386
|
-
}) {
|
|
1387
|
-
return /* @__PURE__ */ jsx7("div", { className: `bg-ods-card p-4 flex flex-col gap-3 ${className ?? ""}`, children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-0", children: [
|
|
1388
|
-
/* @__PURE__ */ jsx7("p", { className: "text-h4 text-ods-text-primary", children: uppercase ? value.toLocaleUpperCase() : value }),
|
|
1389
|
-
/* @__PURE__ */ jsx7("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: label })
|
|
1390
|
-
] }) });
|
|
1391
|
-
}
|
|
1392
|
-
function EntityMetadataAuthorCell({
|
|
1393
|
-
author,
|
|
1394
|
-
roleLabel = "Author",
|
|
1395
|
-
authorHref,
|
|
1396
|
-
className
|
|
1397
|
-
}) {
|
|
1398
|
-
const trimmedName = typeof author.full_name === "string" ? author.full_name.trim() : "";
|
|
1399
|
-
const fullName = trimmedName || "Unknown Author";
|
|
1400
|
-
return /* @__PURE__ */ jsxs5("div", { className: `bg-ods-card p-4 flex items-center gap-3 ${className ?? ""}`, children: [
|
|
1401
|
-
/* @__PURE__ */ jsx7(
|
|
1402
|
-
SquareAvatar,
|
|
1403
|
-
{
|
|
1404
|
-
src: author.avatar_url || "",
|
|
1405
|
-
alt: fullName,
|
|
1406
|
-
fallback: nameInitials(fullName, ""),
|
|
1407
|
-
size: "md",
|
|
1408
|
-
variant: "round"
|
|
1409
|
-
}
|
|
1410
|
-
),
|
|
1411
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-0 flex-1 min-w-0", children: [
|
|
1412
|
-
/* @__PURE__ */ jsx7("p", { className: "text-h3 tracking-[-0.36px] text-ods-text-primary truncate", title: fullName, children: authorHref && trimmedName ? /* @__PURE__ */ jsx7(next_link_default, { href: authorHref, className: "hover:text-ods-accent transition-colors", children: fullName }) : fullName }),
|
|
1413
|
-
/* @__PURE__ */ jsx7("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: author.job_title || roleLabel })
|
|
1414
|
-
] })
|
|
1415
|
-
] });
|
|
1416
|
-
}
|
|
1417
|
-
function EntityAuthorCard({
|
|
1418
|
-
author,
|
|
1419
|
-
roleLabel = "Author",
|
|
1420
|
-
authorHref,
|
|
1421
|
-
publishedAt,
|
|
1422
|
-
publishedLabel = "Published",
|
|
1423
|
-
extraCells,
|
|
1424
|
-
renderEmptyAuthor = false,
|
|
1425
|
-
className
|
|
1426
|
-
}) {
|
|
1427
|
-
const hasAuthor = !!author?.full_name;
|
|
1428
|
-
if (!hasAuthor && !renderEmptyAuthor) return null;
|
|
1429
|
-
const effectiveAuthor = hasAuthor ? author : EMPTY_AUTHOR_PLACEHOLDER;
|
|
1430
|
-
const formatted = publishedAt ? formatDate(publishedAt) : "";
|
|
1431
|
-
const dateLabel = formatted === "Invalid Date" ? "" : formatted;
|
|
1432
|
-
const showDateCell = !!dateLabel;
|
|
1433
|
-
const extras = extraCells ?? [];
|
|
1434
|
-
const totalCells = extras.length + (showDateCell ? 1 : 0) + 1;
|
|
1435
|
-
const gridColsClass = totalCells >= 4 ? "md:grid-cols-4" : totalCells === 3 ? "md:grid-cols-3" : totalCells === 2 ? "md:grid-cols-2" : "md:grid-cols-1";
|
|
1436
|
-
const dividerClass = "border-b md:border-b-0 md:border-r border-ods-border";
|
|
1437
|
-
return /* @__PURE__ */ jsxs5(
|
|
1438
|
-
"div",
|
|
1439
|
-
{
|
|
1440
|
-
className: `grid grid-cols-1 ${gridColsClass} border border-ods-border rounded-md overflow-hidden w-full ${className ?? ""}`,
|
|
1441
|
-
children: [
|
|
1442
|
-
extras.map((cell, i) => /* @__PURE__ */ jsx7(
|
|
1443
|
-
EntityMetadataValueCell,
|
|
1444
|
-
{
|
|
1445
|
-
value: cell.value,
|
|
1446
|
-
label: cell.label,
|
|
1447
|
-
uppercase: cell.uppercase ?? true,
|
|
1448
|
-
className: dividerClass
|
|
1449
|
-
},
|
|
1450
|
-
`${cell.label}-${i}`
|
|
1451
|
-
)),
|
|
1452
|
-
showDateCell && /* @__PURE__ */ jsx7(
|
|
1453
|
-
EntityMetadataValueCell,
|
|
1454
|
-
{
|
|
1455
|
-
value: dateLabel,
|
|
1456
|
-
label: publishedLabel,
|
|
1457
|
-
uppercase: false,
|
|
1458
|
-
className: dividerClass
|
|
1459
|
-
}
|
|
1460
|
-
),
|
|
1461
|
-
/* @__PURE__ */ jsx7(EntityMetadataAuthorCell, { author: effectiveAuthor, roleLabel, authorHref })
|
|
1462
|
-
]
|
|
1463
|
-
}
|
|
1464
|
-
);
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
// src/components/chat/entity-cards/blog-image-placeholder.tsx
|
|
1468
|
-
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1469
|
-
function BlogImagePlaceholder({
|
|
1470
|
-
imageUrl,
|
|
1471
|
-
title,
|
|
1472
|
-
className = ""
|
|
1473
|
-
}) {
|
|
1474
|
-
if (!imageUrl) return null;
|
|
1475
|
-
return /* @__PURE__ */ jsx8("span", { className: `relative block w-full h-full overflow-hidden bg-ods-bg ${className}`, children: /* @__PURE__ */ jsx8(
|
|
1476
|
-
"img",
|
|
1477
|
-
{
|
|
1478
|
-
src: imageUrl,
|
|
1479
|
-
alt: `Cover image for ${title}`,
|
|
1480
|
-
className: "block w-full h-full object-contain",
|
|
1481
|
-
loading: "lazy",
|
|
1482
|
-
onError: (e) => {
|
|
1483
|
-
e.currentTarget.style.display = "none";
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
) });
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// src/components/features/video.tsx
|
|
1490
|
-
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
|
|
1491
|
-
import MuxPlayer from "@mux/mux-player-react";
|
|
1492
|
-
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1493
|
-
if (typeof window !== "undefined") {
|
|
1494
|
-
const w = window;
|
|
1495
|
-
if (!w.chrome?.cast) {
|
|
1496
|
-
w.chrome = { ...w.chrome ?? {}, cast: { isAvailable: false } };
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
if (typeof window !== "undefined" && typeof console !== "undefined") {
|
|
1500
|
-
const w = window;
|
|
1501
|
-
if (!w.__MEDIA_CHROME_WARN_PATCHED__) {
|
|
1502
|
-
w.__MEDIA_CHROME_WARN_PATCHED__ = true;
|
|
1503
|
-
const MEDIA_CHROME_NO_STYLESHEET_PREFIX = "Media Chrome: No style sheet found on style tag of";
|
|
1504
|
-
const originalWarn = console.warn.bind(console);
|
|
1505
|
-
console.warn = (...args) => {
|
|
1506
|
-
if (typeof args[0] === "string" && args[0].startsWith(MEDIA_CHROME_NO_STYLESHEET_PREFIX)) {
|
|
1507
|
-
return;
|
|
1508
|
-
}
|
|
1509
|
-
originalWarn(...args);
|
|
1510
|
-
};
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
var YT_HOSTS = /* @__PURE__ */ new Set([
|
|
1514
|
-
"youtube.com",
|
|
1515
|
-
"www.youtube.com",
|
|
1516
|
-
"m.youtube.com",
|
|
1517
|
-
"youtu.be",
|
|
1518
|
-
"youtube-nocookie.com",
|
|
1519
|
-
"www.youtube-nocookie.com"
|
|
1520
|
-
]);
|
|
1521
|
-
function isYouTubeUrl(url) {
|
|
1522
|
-
try {
|
|
1523
|
-
return YT_HOSTS.has(new URL(url, "http://placeholder.local").hostname.toLowerCase());
|
|
1524
|
-
} catch {
|
|
1525
|
-
return false;
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
var YT_PATH_RE = /^\/(?:embed|v|shorts)\/([^/]+)\/?$/;
|
|
1529
|
-
var BARE_YT_ID_RE = /^[A-Za-z0-9_-]{11}$/;
|
|
1530
|
-
function extractYouTubeId(url) {
|
|
1531
|
-
if (!url) return null;
|
|
1532
|
-
if (BARE_YT_ID_RE.test(url)) return url;
|
|
1533
|
-
let u;
|
|
1534
|
-
try {
|
|
1535
|
-
u = new URL(url, "http://placeholder.local");
|
|
1536
|
-
} catch {
|
|
1537
|
-
return null;
|
|
1538
|
-
}
|
|
1539
|
-
if (!YT_HOSTS.has(u.hostname.toLowerCase())) return null;
|
|
1540
|
-
if (u.hostname.toLowerCase().endsWith("youtu.be")) {
|
|
1541
|
-
return u.pathname.split("/").filter(Boolean)[0] ?? null;
|
|
1542
|
-
}
|
|
1543
|
-
const v = u.searchParams.get("v");
|
|
1544
|
-
if (v) return v;
|
|
1545
|
-
const m = u.pathname.match(YT_PATH_RE);
|
|
1546
|
-
return m ? m[1] : null;
|
|
1547
|
-
}
|
|
1548
|
-
function Video(props) {
|
|
1549
|
-
const url = props.url;
|
|
1550
|
-
if (!url) return null;
|
|
1551
|
-
const effectiveKind = resolveKind(props, url);
|
|
1552
|
-
const layout = props.layout ?? "native";
|
|
1553
|
-
const inner = effectiveKind === "youtube" ? /* @__PURE__ */ jsx9(
|
|
1554
|
-
YouTubeFacade,
|
|
1555
|
-
{
|
|
1556
|
-
url,
|
|
1557
|
-
title: props.title,
|
|
1558
|
-
priority: props.priority,
|
|
1559
|
-
className: props.className,
|
|
1560
|
-
minimalControls: props.minimalControls
|
|
1561
|
-
}
|
|
1562
|
-
) : /* @__PURE__ */ jsx9(
|
|
1563
|
-
FilePlayer,
|
|
1564
|
-
{
|
|
1565
|
-
url,
|
|
1566
|
-
poster: props.poster,
|
|
1567
|
-
muted: props.muted,
|
|
1568
|
-
srtContent: "srtContent" in props ? props.srtContent : null,
|
|
1569
|
-
captionsUrl: "captionsUrl" in props ? props.captionsUrl : null,
|
|
1570
|
-
className: props.className
|
|
1571
|
-
}
|
|
1572
|
-
);
|
|
1573
|
-
return wrapWithLayout(inner, layout);
|
|
1574
|
-
}
|
|
1575
|
-
function resolveKind(props, url) {
|
|
1576
|
-
if ("kind" in props) {
|
|
1577
|
-
if (props.kind === "youtube") return "youtube";
|
|
1578
|
-
if (props.kind === "file") return "file";
|
|
1579
|
-
}
|
|
1580
|
-
if (BARE_YT_ID_RE.test(url)) return "youtube";
|
|
1581
|
-
return isYouTubeUrl(url) ? "youtube" : "file";
|
|
1582
|
-
}
|
|
1583
|
-
function wrapWithLayout(inner, layout) {
|
|
1584
|
-
switch (layout) {
|
|
1585
|
-
case "centered":
|
|
1586
|
-
return /* @__PURE__ */ jsx9("div", { className: "flex justify-center w-full", children: /* @__PURE__ */ jsx9("div", { className: "w-full max-w-3xl aspect-video rounded-lg overflow-hidden border border-ods-border", children: inner }) });
|
|
1587
|
-
case "fill":
|
|
1588
|
-
return /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 w-full h-full", children: inner });
|
|
1589
|
-
case "native":
|
|
1590
|
-
default:
|
|
1591
|
-
return inner;
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
function FilePlayer({
|
|
1595
|
-
url,
|
|
1596
|
-
poster,
|
|
1597
|
-
muted,
|
|
1598
|
-
srtContent,
|
|
1599
|
-
captionsUrl,
|
|
1600
|
-
className
|
|
1601
|
-
}) {
|
|
1602
|
-
if (process.env.NODE_ENV !== "production" && srtContent && !captionsUrl) {
|
|
1603
|
-
console.warn(
|
|
1604
|
-
"[Video] srtContent supplied without captionsUrl \u2014 captions will not render. Pass captionsUrl (the VTT URL) instead; raw SRT text overlays are no longer supported."
|
|
1605
|
-
);
|
|
1606
|
-
}
|
|
1607
|
-
return /* @__PURE__ */ jsx9(
|
|
1608
|
-
MuxPlayer,
|
|
1609
|
-
{
|
|
1610
|
-
src: url,
|
|
1611
|
-
poster: poster || void 0,
|
|
1612
|
-
streamType: "on-demand",
|
|
1613
|
-
playsInline: true,
|
|
1614
|
-
muted,
|
|
1615
|
-
preferCmcd: "header",
|
|
1616
|
-
accentColor: "var(--ods-accent, var(--color-accent-primary))",
|
|
1617
|
-
className,
|
|
1618
|
-
style: { width: "100%", height: "100%" },
|
|
1619
|
-
children: captionsUrl ? /* @__PURE__ */ jsx9(
|
|
1620
|
-
"track",
|
|
1621
|
-
{
|
|
1622
|
-
kind: "captions",
|
|
1623
|
-
src: captionsUrl,
|
|
1624
|
-
srcLang: "en",
|
|
1625
|
-
label: "English",
|
|
1626
|
-
default: true
|
|
1627
|
-
}
|
|
1628
|
-
) : null
|
|
1629
|
-
}
|
|
1630
|
-
);
|
|
1631
|
-
}
|
|
1632
|
-
function YouTubeFacade({
|
|
1633
|
-
url,
|
|
1634
|
-
title = "YouTube Video",
|
|
1635
|
-
priority,
|
|
1636
|
-
className,
|
|
1637
|
-
minimalControls
|
|
1638
|
-
}) {
|
|
1639
|
-
const videoId = extractYouTubeId(url);
|
|
1640
|
-
if (!videoId) return null;
|
|
1641
|
-
return /* @__PURE__ */ jsx9(YouTubeFacadeInner, { videoId, title, priority, className, minimalControls });
|
|
1642
|
-
}
|
|
1643
|
-
var YT_NOCOOKIE_ORIGIN = "https://www.youtube-nocookie.com";
|
|
1644
|
-
var YT_STATE_ENDED = 0;
|
|
1645
|
-
var YT_STATE_PLAYING = 1;
|
|
1646
|
-
var YT_PLAYING_BLUR_DELAY_MS = 1e3;
|
|
1647
|
-
function YouTubeFacadeInner({
|
|
1648
|
-
videoId,
|
|
1649
|
-
title,
|
|
1650
|
-
priority,
|
|
1651
|
-
className,
|
|
1652
|
-
minimalControls
|
|
1653
|
-
}) {
|
|
1654
|
-
const [activated, setActivated] = useState3(false);
|
|
1655
|
-
const iframeRef = useRef4(null);
|
|
1656
|
-
const { embedUrl, posterJpg, posterWebp } = useMemo3(() => {
|
|
1657
|
-
const params = new URLSearchParams({
|
|
1658
|
-
autoplay: "1",
|
|
1659
|
-
rel: "0",
|
|
1660
|
-
modestbranding: "1",
|
|
1661
|
-
playsinline: "1",
|
|
1662
|
-
enablejsapi: "1"
|
|
1663
|
-
});
|
|
1664
|
-
if (typeof window !== "undefined") {
|
|
1665
|
-
params.set("origin", window.location.origin);
|
|
1666
|
-
}
|
|
1667
|
-
if (minimalControls) {
|
|
1668
|
-
params.set("controls", "0");
|
|
1669
|
-
params.set("fs", "0");
|
|
1670
|
-
params.set("iv_load_policy", "3");
|
|
1671
|
-
params.set("cc_load_policy", "0");
|
|
1672
|
-
params.set("disablekb", "1");
|
|
1673
|
-
}
|
|
1674
|
-
return {
|
|
1675
|
-
embedUrl: `${YT_NOCOOKIE_ORIGIN}/embed/${videoId}?${params.toString()}`,
|
|
1676
|
-
posterJpg: `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`,
|
|
1677
|
-
posterWebp: `https://i.ytimg.com/vi_webp/${videoId}/mqdefault.webp`
|
|
1678
|
-
};
|
|
1679
|
-
}, [videoId, minimalControls]);
|
|
1680
|
-
useEffect4(() => {
|
|
1681
|
-
if (!activated) return;
|
|
1682
|
-
const iframe = iframeRef.current;
|
|
1683
|
-
if (!iframe) return;
|
|
1684
|
-
function subscribe() {
|
|
1685
|
-
iframe?.contentWindow?.postMessage(
|
|
1686
|
-
'{"event":"listening"}',
|
|
1687
|
-
YT_NOCOOKIE_ORIGIN
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
iframe.addEventListener("load", subscribe);
|
|
1691
|
-
subscribe();
|
|
1692
|
-
let blurTimer = null;
|
|
1693
|
-
function handleMessage(event) {
|
|
1694
|
-
if (event.origin !== YT_NOCOOKIE_ORIGIN) return;
|
|
1695
|
-
if (typeof event.data !== "string") return;
|
|
1696
|
-
let payload = null;
|
|
1697
|
-
try {
|
|
1698
|
-
payload = JSON.parse(event.data);
|
|
1699
|
-
} catch {
|
|
1700
|
-
return;
|
|
1701
|
-
}
|
|
1702
|
-
if (!payload || payload.event !== "infoDelivery") return;
|
|
1703
|
-
const state = payload.info?.playerState;
|
|
1704
|
-
if (typeof state !== "number") return;
|
|
1705
|
-
if (state === YT_STATE_PLAYING) {
|
|
1706
|
-
if (blurTimer !== null) return;
|
|
1707
|
-
blurTimer = setTimeout(() => {
|
|
1708
|
-
blurTimer = null;
|
|
1709
|
-
iframeRef.current?.blur();
|
|
1710
|
-
}, YT_PLAYING_BLUR_DELAY_MS);
|
|
1711
|
-
return;
|
|
1712
|
-
}
|
|
1713
|
-
if (state === YT_STATE_ENDED) {
|
|
1714
|
-
setActivated(false);
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
window.addEventListener("message", handleMessage);
|
|
1718
|
-
return () => {
|
|
1719
|
-
iframe.removeEventListener("load", subscribe);
|
|
1720
|
-
window.removeEventListener("message", handleMessage);
|
|
1721
|
-
if (blurTimer !== null) clearTimeout(blurTimer);
|
|
1722
|
-
};
|
|
1723
|
-
}, [activated]);
|
|
1724
|
-
const wrapperClass = `relative w-full ${className ?? ""}`;
|
|
1725
|
-
const wrapperStyle = { paddingBottom: "56.25%" };
|
|
1726
|
-
if (activated) {
|
|
1727
|
-
return /* @__PURE__ */ jsx9("div", { className: wrapperClass, style: wrapperStyle, children: /* @__PURE__ */ jsx9(
|
|
1728
|
-
"iframe",
|
|
1729
|
-
{
|
|
1730
|
-
ref: iframeRef,
|
|
1731
|
-
src: embedUrl,
|
|
1732
|
-
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
|
|
1733
|
-
allowFullScreen: true,
|
|
1734
|
-
title,
|
|
1735
|
-
className: "absolute inset-0 w-full h-full border-0 rounded-lg"
|
|
1736
|
-
}
|
|
1737
|
-
) });
|
|
1738
|
-
}
|
|
1739
|
-
return /* @__PURE__ */ jsx9("div", { className: wrapperClass, style: wrapperStyle, children: /* @__PURE__ */ jsxs6(
|
|
1740
|
-
"button",
|
|
1741
|
-
{
|
|
1742
|
-
type: "button",
|
|
1743
|
-
"aria-label": `Play: ${title}`,
|
|
1744
|
-
onClick: () => setActivated(true),
|
|
1745
|
-
className: "group absolute inset-0 p-0 m-0 border border-ods-border rounded-lg overflow-hidden bg-ods-card cursor-pointer",
|
|
1746
|
-
children: [
|
|
1747
|
-
/* @__PURE__ */ jsxs6("picture", { children: [
|
|
1748
|
-
/* @__PURE__ */ jsx9("source", { type: "image/webp", srcSet: posterWebp }),
|
|
1749
|
-
/* @__PURE__ */ jsx9(
|
|
1750
|
-
"img",
|
|
1751
|
-
{
|
|
1752
|
-
src: posterJpg,
|
|
1753
|
-
alt: title,
|
|
1754
|
-
loading: "lazy",
|
|
1755
|
-
...fetchPriorityProp(priority),
|
|
1756
|
-
decoding: priority ? "sync" : "async",
|
|
1757
|
-
className: "absolute inset-0 w-full h-full object-cover"
|
|
1758
|
-
}
|
|
1759
|
-
)
|
|
1760
|
-
] }),
|
|
1761
|
-
/* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center bg-ods-bg-inverse bg-opacity-20 transition-opacity duration-200 group-hover:bg-opacity-30", children: /* @__PURE__ */ jsx9("span", { className: "flex items-center justify-center w-16 h-16 rounded-full bg-ods-accent text-ods-text-on-accent shadow-lg transition-transform duration-200 group-hover:scale-110", children: /* @__PURE__ */ jsx9(PlayIcon, { size: 24, color: "currentColor", className: "ml-1" }) }) })
|
|
1762
|
-
]
|
|
1763
|
-
}
|
|
1764
|
-
) });
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
// src/components/features/video-ratio-tabs.tsx
|
|
1768
|
-
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1769
|
-
var TAB_TRIGGER_CLASS = "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 py-2 text-sm text-ods-text-secondary data-[state=active]:text-ods-text-primary";
|
|
1770
|
-
var RATIO_GRID_CLASS = {
|
|
1771
|
-
portrait: "grid grid-cols-2 md:grid-cols-3 gap-4",
|
|
1772
|
-
square: "grid grid-cols-2 md:grid-cols-3 gap-4",
|
|
1773
|
-
landscape: "grid grid-cols-1 md:grid-cols-2 gap-4"
|
|
1774
|
-
};
|
|
1775
|
-
var RATIO_DISPLAY_GRID_CLASS = {
|
|
1776
|
-
portrait: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4",
|
|
1777
|
-
square: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4",
|
|
1778
|
-
landscape: "grid grid-cols-1 md:grid-cols-2 gap-6"
|
|
1779
|
-
};
|
|
1780
|
-
var RATIO_TAB_CONFIG = [
|
|
1781
|
-
{ key: "portrait", label: "Portrait 9:16" },
|
|
1782
|
-
{ key: "square", label: "Square 1:1" },
|
|
1783
|
-
{ key: "landscape", label: "Landscape 16:9" }
|
|
1784
|
-
];
|
|
1785
|
-
function RatioTabs({
|
|
1786
|
-
groups,
|
|
1787
|
-
defaultTab,
|
|
1788
|
-
className = ""
|
|
1789
|
-
}) {
|
|
1790
|
-
const activeTabs = RATIO_TAB_CONFIG.filter((t) => groups[t.key].count > 0);
|
|
1791
|
-
if (activeTabs.length <= 1) {
|
|
1792
|
-
const active = activeTabs[0];
|
|
1793
|
-
return active ? /* @__PURE__ */ jsx10(Fragment2, { children: groups[active.key].render() }) : null;
|
|
1794
|
-
}
|
|
1795
|
-
const firstTab = defaultTab && groups[defaultTab].count > 0 ? defaultTab : activeTabs[0].key;
|
|
1796
|
-
return /* @__PURE__ */ jsxs7(Tabs, { defaultValue: firstTab, className: `w-full ${className}`, children: [
|
|
1797
|
-
/* @__PURE__ */ jsx10(TabsList, { className: "inline-flex justify-start rounded-none bg-transparent h-auto p-0 gap-0 mb-2", children: activeTabs.map((t) => /* @__PURE__ */ jsxs7(TabsTrigger, { value: t.key, className: TAB_TRIGGER_CLASS, children: [
|
|
1798
|
-
t.label,
|
|
1799
|
-
" (",
|
|
1800
|
-
groups[t.key].count,
|
|
1801
|
-
")"
|
|
1802
|
-
] }, t.key)) }),
|
|
1803
|
-
activeTabs.map((t) => /* @__PURE__ */ jsx10(
|
|
1804
|
-
TabsContent,
|
|
1805
|
-
{
|
|
1806
|
-
value: t.key,
|
|
1807
|
-
forceMount: true,
|
|
1808
|
-
className: "data-[state=inactive]:hidden",
|
|
1809
|
-
children: groups[t.key].render()
|
|
1810
|
-
},
|
|
1811
|
-
t.key
|
|
1812
|
-
))
|
|
1813
|
-
] });
|
|
1814
|
-
}
|
|
1815
|
-
function detectAspectRatio(ratioString, width, height) {
|
|
1816
|
-
if (ratioString === "16:9") return "16:9";
|
|
1817
|
-
if (ratioString === "1:1") return "1:1";
|
|
1818
|
-
if (ratioString === "9:16") return "9:16";
|
|
1819
|
-
if (width && height) {
|
|
1820
|
-
if (Math.abs(width - height) < Math.min(width, height) * 0.1) return "1:1";
|
|
1821
|
-
if (width > height) return "16:9";
|
|
1822
|
-
}
|
|
1823
|
-
return "9:16";
|
|
1824
|
-
}
|
|
1825
|
-
function ratioToCategory(ratio) {
|
|
1826
|
-
if (ratio === "16:9") return "landscape";
|
|
1827
|
-
if (ratio === "1:1") return "square";
|
|
1828
|
-
return "portrait";
|
|
1829
|
-
}
|
|
1830
|
-
function groupByAspectRatio(items, getAspectRatio) {
|
|
1831
|
-
const portrait = [];
|
|
1832
|
-
const square = [];
|
|
1833
|
-
const landscape = [];
|
|
1834
|
-
for (const item of items) {
|
|
1835
|
-
const cat = ratioToCategory(getAspectRatio(item));
|
|
1836
|
-
if (cat === "landscape") landscape.push(item);
|
|
1837
|
-
else if (cat === "square") square.push(item);
|
|
1838
|
-
else portrait.push(item);
|
|
1839
|
-
}
|
|
1840
|
-
const filled = [portrait, square, landscape].filter((a) => a.length > 0).length;
|
|
1841
|
-
return { portrait, square, landscape, hasMultiple: filled > 1 };
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
// src/components/features/video-bites-display.tsx
|
|
1845
|
-
import { useMemo as useMemo4 } from "react";
|
|
1846
|
-
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1847
|
-
var RATIO_TO_CSS_ASPECT = {
|
|
1848
|
-
portrait: "9 / 16",
|
|
1849
|
-
square: "1 / 1",
|
|
1850
|
-
landscape: "16 / 9"
|
|
1851
|
-
};
|
|
1852
|
-
function LazyBite({ ratio, children }) {
|
|
1853
|
-
const { ref, isNear } = useNearViewport("500px");
|
|
1854
|
-
return /* @__PURE__ */ jsx11("div", { ref, style: { aspectRatio: RATIO_TO_CSS_ASPECT[ratio] }, children: isNear ? children : /* @__PURE__ */ jsx11("div", { className: "w-full h-full bg-ods-card rounded-md" }) });
|
|
1855
|
-
}
|
|
1856
|
-
function VideoBitesDisplay({
|
|
1857
|
-
bites,
|
|
1858
|
-
title = "Video Highlights",
|
|
1859
|
-
filterPublished = true,
|
|
1860
|
-
showTitle = true
|
|
1861
|
-
}) {
|
|
1862
|
-
const grouped = useMemo4(() => {
|
|
1863
|
-
const filtered = filterPublished ? bites.filter((b) => b.published) : bites;
|
|
1864
|
-
const sorted = [...filtered].sort((a, b) => {
|
|
1865
|
-
if (!a.created_at && !b.created_at) return 0;
|
|
1866
|
-
if (!a.created_at) return 1;
|
|
1867
|
-
if (!b.created_at) return -1;
|
|
1868
|
-
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
|
1869
|
-
});
|
|
1870
|
-
return groupByAspectRatio(
|
|
1871
|
-
sorted,
|
|
1872
|
-
(b) => detectAspectRatio(b.aspect_ratio)
|
|
1873
|
-
);
|
|
1874
|
-
}, [bites, filterPublished]);
|
|
1875
|
-
const totalCount = grouped.portrait.length + grouped.square.length + grouped.landscape.length;
|
|
1876
|
-
if (totalCount === 0) return null;
|
|
1877
|
-
return /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-6 w-full min-w-0", children: [
|
|
1878
|
-
showTitle && /* @__PURE__ */ jsx11("h2", { className: `${SECTION_HEADING_CLASS} break-words`, children: title }),
|
|
1879
|
-
grouped.hasMultiple ? /* @__PURE__ */ jsx11(
|
|
1880
|
-
RatioTabs,
|
|
1881
|
-
{
|
|
1882
|
-
groups: {
|
|
1883
|
-
portrait: {
|
|
1884
|
-
count: grouped.portrait.length,
|
|
1885
|
-
render: () => /* @__PURE__ */ jsx11(BiteGrid, { bites: grouped.portrait, ratio: "portrait" })
|
|
1886
|
-
},
|
|
1887
|
-
square: {
|
|
1888
|
-
count: grouped.square.length,
|
|
1889
|
-
render: () => /* @__PURE__ */ jsx11(BiteGrid, { bites: grouped.square, ratio: "square" })
|
|
1890
|
-
},
|
|
1891
|
-
landscape: {
|
|
1892
|
-
count: grouped.landscape.length,
|
|
1893
|
-
render: () => /* @__PURE__ */ jsx11(BiteGrid, { bites: grouped.landscape, ratio: "landscape" })
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
) : /* @__PURE__ */ jsx11(
|
|
1898
|
-
BiteGrid,
|
|
1899
|
-
{
|
|
1900
|
-
bites: grouped.portrait.length > 0 ? grouped.portrait : grouped.square.length > 0 ? grouped.square : grouped.landscape,
|
|
1901
|
-
ratio: grouped.portrait.length > 0 ? "portrait" : grouped.square.length > 0 ? "square" : "landscape"
|
|
1902
|
-
}
|
|
1903
|
-
)
|
|
1904
|
-
] });
|
|
1905
|
-
}
|
|
1906
|
-
function BiteGrid({ bites, ratio }) {
|
|
1907
|
-
return /* @__PURE__ */ jsx11("div", { className: RATIO_DISPLAY_GRID_CLASS[ratio], children: bites.map((bite, index) => /* @__PURE__ */ jsx11(LazyBite, { ratio, children: /* @__PURE__ */ jsx11(VideoBiteCard, { url: bite.url, title: bite.title, thumbnailUrl: bite.thumbnail_url }) }, bite.url || index)) });
|
|
1908
|
-
}
|
|
1909
|
-
function VideoBiteCard({ url, title, thumbnailUrl }) {
|
|
1910
|
-
return /* @__PURE__ */ jsxs8(Card, { className: "overflow-hidden border border-ods-border bg-ods-card hover:border-ods-accent transition-colors flex flex-col h-full", children: [
|
|
1911
|
-
/* @__PURE__ */ jsx11("div", { className: "relative flex-1 min-h-0", children: /* @__PURE__ */ jsx11(Video, { url, poster: thumbnailUrl || void 0, layout: "fill" }) }),
|
|
1912
|
-
title && /* @__PURE__ */ jsx11("div", { className: "p-4", children: /* @__PURE__ */ jsx11("p", { className: "text-h4 text-ods-text-primary line-clamp-2", title, children: title }) })
|
|
1913
|
-
] });
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
// src/components/features/entity-video-section.tsx
|
|
1917
|
-
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1918
|
-
function EntityVideoSection({
|
|
1919
|
-
mainVideoUrl,
|
|
1920
|
-
youtubeUrl,
|
|
1921
|
-
highlightVideoUrl,
|
|
1922
|
-
highlightVideoThumbnail,
|
|
1923
|
-
mainVideoPoster,
|
|
1924
|
-
title = "Video",
|
|
1925
|
-
videoSummary,
|
|
1926
|
-
videoBites,
|
|
1927
|
-
bitesTitle = "Video Highlights",
|
|
1928
|
-
filterPublishedBites = true,
|
|
1929
|
-
MarkdownRenderer,
|
|
1930
|
-
srtContent,
|
|
1931
|
-
captionsUrl,
|
|
1932
|
-
priority = false
|
|
1933
|
-
}) {
|
|
1934
|
-
const hasFullVideo = !!(youtubeUrl || mainVideoUrl);
|
|
1935
|
-
const hasHighlight = !!highlightVideoUrl;
|
|
1936
|
-
const hasVideo = hasFullVideo || hasHighlight;
|
|
1937
|
-
if (!hasVideo && !videoSummary && (!videoBites || videoBites.length === 0)) {
|
|
1938
|
-
return null;
|
|
1939
|
-
}
|
|
1940
|
-
const fullVideoUrl = youtubeUrl || mainVideoUrl || null;
|
|
1941
|
-
const fullVideoKind = youtubeUrl ? "youtube" : "auto";
|
|
1942
|
-
return /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
1943
|
-
hasVideo && (hasFullVideo && hasHighlight ? /* @__PURE__ */ jsxs9(Tabs, { defaultValue: "full-video", className: "w-full", children: [
|
|
1944
|
-
/* @__PURE__ */ jsxs9(TabsList, { className: "inline-flex justify-start rounded-none bg-transparent h-auto p-0 gap-0", children: [
|
|
1945
|
-
/* @__PURE__ */ jsx12(
|
|
1946
|
-
TabsTrigger,
|
|
1947
|
-
{
|
|
1948
|
-
value: "full-video",
|
|
1949
|
-
className: "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 md:px-6 py-3 text-ods-text-secondary data-[state=active]:text-ods-text-primary",
|
|
1950
|
-
children: "Full Video"
|
|
1951
|
-
}
|
|
1952
|
-
),
|
|
1953
|
-
/* @__PURE__ */ jsx12(
|
|
1954
|
-
TabsTrigger,
|
|
1955
|
-
{
|
|
1956
|
-
value: "highlights",
|
|
1957
|
-
className: "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 md:px-6 py-3 text-ods-text-secondary data-[state=active]:text-ods-text-primary",
|
|
1958
|
-
children: "Highlights"
|
|
1959
|
-
}
|
|
1960
|
-
)
|
|
1961
|
-
] }),
|
|
1962
|
-
/* @__PURE__ */ jsx12(TabsContent, { value: "full-video", className: "mt-4", children: /* @__PURE__ */ jsx12(
|
|
1963
|
-
Video,
|
|
1964
|
-
{
|
|
1965
|
-
kind: fullVideoKind,
|
|
1966
|
-
url: fullVideoUrl,
|
|
1967
|
-
poster: mainVideoPoster,
|
|
1968
|
-
title,
|
|
1969
|
-
srtContent,
|
|
1970
|
-
captionsUrl,
|
|
1971
|
-
layout: "centered",
|
|
1972
|
-
priority
|
|
1973
|
-
}
|
|
1974
|
-
) }),
|
|
1975
|
-
/* @__PURE__ */ jsx12(TabsContent, { value: "highlights", className: "mt-4", children: /* @__PURE__ */ jsx12(
|
|
1976
|
-
Video,
|
|
1977
|
-
{
|
|
1978
|
-
url: highlightVideoUrl,
|
|
1979
|
-
poster: highlightVideoThumbnail,
|
|
1980
|
-
layout: "centered"
|
|
1981
|
-
}
|
|
1982
|
-
) })
|
|
1983
|
-
] }) : hasFullVideo ? /* @__PURE__ */ jsx12(
|
|
1984
|
-
Video,
|
|
1985
|
-
{
|
|
1986
|
-
kind: fullVideoKind,
|
|
1987
|
-
url: fullVideoUrl,
|
|
1988
|
-
poster: mainVideoPoster,
|
|
1989
|
-
title,
|
|
1990
|
-
srtContent,
|
|
1991
|
-
captionsUrl,
|
|
1992
|
-
layout: "centered",
|
|
1993
|
-
priority
|
|
1994
|
-
}
|
|
1995
|
-
) : /* @__PURE__ */ jsx12(
|
|
1996
|
-
Video,
|
|
1997
|
-
{
|
|
1998
|
-
url: highlightVideoUrl,
|
|
1999
|
-
poster: highlightVideoThumbnail,
|
|
2000
|
-
layout: "centered",
|
|
2001
|
-
priority
|
|
2002
|
-
}
|
|
2003
|
-
)),
|
|
2004
|
-
videoSummary && MarkdownRenderer && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-6 w-full min-w-0", children: [
|
|
2005
|
-
/* @__PURE__ */ jsx12("h2", { className: `${SECTION_HEADING_CLASS} break-words`, children: "Summary" }),
|
|
2006
|
-
/* @__PURE__ */ jsx12("div", { className: "text-h4 text-ods-text-primary break-words overflow-hidden", children: /* @__PURE__ */ jsx12(MarkdownRenderer, { content: videoSummary }) })
|
|
2007
|
-
] }),
|
|
2008
|
-
videoBites && videoBites.length > 0 && /* @__PURE__ */ jsx12(
|
|
2009
|
-
VideoBitesDisplay,
|
|
2010
|
-
{
|
|
2011
|
-
bites: videoBites,
|
|
2012
|
-
title: bitesTitle,
|
|
2013
|
-
filterPublished: filterPublishedBites
|
|
2014
|
-
}
|
|
2015
|
-
)
|
|
2016
|
-
] });
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
// src/components/chat/entity-cards/onboarding-guide-card.tsx
|
|
2020
|
-
init_next_link();
|
|
2021
|
-
init_cn();
|
|
2022
|
-
import { Clock, ExternalLink, GraduationCap, Play } from "lucide-react";
|
|
2023
|
-
|
|
2024
|
-
// src/components/chat/entity-cards/use-entity-card-link.ts
|
|
2025
|
-
import { useMemo as useMemo5 } from "react";
|
|
2026
|
-
function useEntityCardLink({
|
|
2027
|
-
href,
|
|
2028
|
-
targetPlatform,
|
|
2029
|
-
target,
|
|
2030
|
-
rel
|
|
2031
|
-
}) {
|
|
2032
|
-
const runtime = useChatRuntime();
|
|
2033
|
-
return useMemo5(() => {
|
|
2034
|
-
if (target !== void 0 || rel !== void 0) {
|
|
2035
|
-
const safeRel = rel ?? (target === "_blank" ? "noopener noreferrer" : void 0);
|
|
2036
|
-
return { target, rel: safeRel };
|
|
2037
|
-
}
|
|
2038
|
-
const newTab = runtime ? computeIsNewTab(runtime, href, targetPlatform ?? null) : false;
|
|
2039
|
-
return newTab ? { target: "_blank", rel: "noopener noreferrer" } : { target: void 0, rel: void 0 };
|
|
2040
|
-
}, [target, rel, href, targetPlatform, runtime]);
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
// src/components/chat/entity-cards/use-entity-card-placeholder.ts
|
|
2044
|
-
var NO_OP_BUILDER = () => "";
|
|
2045
|
-
function useEntityCardPlaceholder({
|
|
2046
|
-
title,
|
|
2047
|
-
placeholderUrl,
|
|
2048
|
-
siteName = "",
|
|
2049
|
-
aspect = "wide"
|
|
2050
|
-
}) {
|
|
2051
|
-
const runtime = useChatRuntime();
|
|
2052
|
-
const builder = runtime?.resolvePlaceholderUrl ?? NO_OP_BUILDER;
|
|
2053
|
-
const enabled = placeholderUrl === void 0 && !!runtime?.resolvePlaceholderUrl;
|
|
2054
|
-
const derived = useOgPlaceholder(builder, title, siteName, enabled, aspect);
|
|
2055
|
-
return placeholderUrl !== void 0 ? placeholderUrl : derived;
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
// src/components/chat/entity-cards/onboarding-guide-card.tsx
|
|
2059
|
-
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2060
|
-
function stripMarkdownPreview(text) {
|
|
2061
|
-
return text.replace(/```[\s\S]*?```/g, " ").replace(/^#{1,6}\s+/gm, "").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_~`>]/g, "").replace(/^\s*[-+]\s+/gm, "").replace(/-{3,}/g, " ").replace(/\s+/g, " ").trim().replace(/^([\s\S]{240})[\s\S]+$/, "$1\u2026");
|
|
2062
|
-
}
|
|
2063
|
-
var HORIZONTAL_SIZE_TOKENS = {
|
|
2064
|
-
default: {
|
|
2065
|
-
padding: "p-4",
|
|
2066
|
-
step: "w-8 h-8 text-sm",
|
|
2067
|
-
title: "text-h5",
|
|
2068
|
-
summaryClamp: "line-clamp-2"
|
|
2069
|
-
}
|
|
2070
|
-
};
|
|
2071
|
-
function OnboardingGuideCardSkeleton({ size = "default" }) {
|
|
2072
|
-
if (size === "catalog") {
|
|
2073
|
-
return /* @__PURE__ */ jsxs10("div", { className: "bg-ods-system-greys-black border border-ods-border rounded-lg overflow-hidden flex flex-col p-6 gap-4 animate-pulse", children: [
|
|
2074
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col md:flex-row gap-4 md:gap-6", children: [
|
|
2075
|
-
/* @__PURE__ */ jsx13("div", { className: "w-full md:w-[256px] aspect-[1200/630] bg-ods-border rounded-lg flex-shrink-0" }),
|
|
2076
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0 flex flex-col", children: [
|
|
2077
|
-
/* @__PURE__ */ jsxs10("div", { className: "min-h-[60px] md:min-h-[72px] flex flex-col gap-1.5 justify-start mb-3", children: [
|
|
2078
|
-
/* @__PURE__ */ jsx13("div", { className: "h-[25px] md:h-[30px] w-3/4 bg-ods-border rounded" }),
|
|
2079
|
-
/* @__PURE__ */ jsx13("div", { className: "h-[25px] md:h-[30px] w-1/2 bg-ods-border rounded" })
|
|
2080
|
-
] }),
|
|
2081
|
-
/* @__PURE__ */ jsxs10("div", { className: "min-h-[46px] md:min-h-[52px] flex flex-col gap-2 justify-start", children: [
|
|
2082
|
-
/* @__PURE__ */ jsx13("div", { className: "h-3 w-full bg-ods-border/70 rounded" }),
|
|
2083
|
-
/* @__PURE__ */ jsx13("div", { className: "h-3 w-5/6 bg-ods-border/70 rounded" })
|
|
2084
|
-
] })
|
|
2085
|
-
] })
|
|
2086
|
-
] }),
|
|
2087
|
-
/* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-1 md:grid-cols-3 border border-ods-border rounded-md overflow-hidden w-full", children: [
|
|
2088
|
-
[0, 1].map((i) => /* @__PURE__ */ jsx13(
|
|
2089
|
-
"div",
|
|
2090
|
-
{
|
|
2091
|
-
className: "bg-ods-card p-4 flex flex-col gap-3 border-b md:border-b-0 md:border-r border-ods-border",
|
|
2092
|
-
children: /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
|
|
2093
|
-
/* @__PURE__ */ jsx13("div", { className: "h-6 w-32 bg-ods-bg rounded" }),
|
|
2094
|
-
/* @__PURE__ */ jsx13("div", { className: "h-3 w-20 bg-ods-bg/60 rounded" })
|
|
2095
|
-
] })
|
|
2096
|
-
},
|
|
2097
|
-
`cell-${i}`
|
|
2098
|
-
)),
|
|
2099
|
-
/* @__PURE__ */ jsxs10("div", { className: "bg-ods-card p-4 flex items-center gap-3", children: [
|
|
2100
|
-
/* @__PURE__ */ jsx13("div", { className: "h-10 w-10 rounded-full bg-ods-bg shrink-0" }),
|
|
2101
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
|
|
2102
|
-
/* @__PURE__ */ jsx13("div", { className: "h-4 w-3/4 bg-ods-bg rounded" }),
|
|
2103
|
-
/* @__PURE__ */ jsx13("div", { className: "h-3 w-1/2 bg-ods-bg/60 rounded" })
|
|
2104
|
-
] })
|
|
2105
|
-
] })
|
|
2106
|
-
] })
|
|
2107
|
-
] });
|
|
2108
|
-
}
|
|
2109
|
-
if (size === "sm") {
|
|
2110
|
-
return /* @__PURE__ */ jsxs10("span", { className: COMPACT_CARD_SKELETON_OUTER, children: [
|
|
2111
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_SKELETON_IMAGE_SLOT }),
|
|
2112
|
-
/* @__PURE__ */ jsxs10("span", { className: COMPACT_CARD_TEXT_COL, children: [
|
|
2113
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_TITLE_ROW, children: /* @__PURE__ */ jsx13("span", { className: "h-3.5 w-3/5 rounded bg-ods-bg" }) }),
|
|
2114
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx13("span", { className: "h-3 w-2/5 rounded bg-ods-bg/70" }) }),
|
|
2115
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx13("span", { className: "h-3 w-11/12 rounded bg-ods-bg/40" }) })
|
|
2116
|
-
] }),
|
|
2117
|
-
/* @__PURE__ */ jsx13("span", { className: "flex shrink-0 items-center self-start h-5", children: /* @__PURE__ */ jsx13("span", { className: "h-3.5 w-3.5 rounded bg-ods-bg" }) })
|
|
2118
|
-
] });
|
|
2119
|
-
}
|
|
2120
|
-
const t = HORIZONTAL_SIZE_TOKENS.default;
|
|
2121
|
-
return /* @__PURE__ */ jsxs10(
|
|
2122
|
-
"span",
|
|
2123
|
-
{
|
|
2124
|
-
className: `flex items-start gap-3 rounded-md border border-ods-border bg-ods-card ${t.padding} animate-pulse`,
|
|
2125
|
-
children: [
|
|
2126
|
-
/* @__PURE__ */ jsx13("span", { className: `shrink-0 inline-flex items-center justify-center rounded-full bg-ods-bg ${t.step}` }),
|
|
2127
|
-
/* @__PURE__ */ jsxs10("span", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
|
|
2128
|
-
/* @__PURE__ */ jsx13("span", { className: "block h-4 w-2/3 rounded bg-ods-bg" }),
|
|
2129
|
-
/* @__PURE__ */ jsx13("span", { className: "block h-3 w-1/3 rounded bg-ods-bg/70" }),
|
|
2130
|
-
/* @__PURE__ */ jsx13("span", { className: "block h-3 w-full rounded bg-ods-bg/40" })
|
|
2131
|
-
] })
|
|
2132
|
-
]
|
|
2133
|
-
}
|
|
2134
|
-
);
|
|
2135
|
-
}
|
|
2136
|
-
function OnboardingGuideCard({
|
|
2137
|
-
guide,
|
|
2138
|
-
href,
|
|
2139
|
-
target: targetProp,
|
|
2140
|
-
rel: relProp,
|
|
2141
|
-
targetPlatform,
|
|
2142
|
-
placeholderUrl: placeholderUrlProp,
|
|
2143
|
-
size = "default",
|
|
2144
|
-
className
|
|
2145
|
-
}) {
|
|
2146
|
-
const { target, rel } = useEntityCardLink({
|
|
2147
|
-
href,
|
|
2148
|
-
targetPlatform,
|
|
2149
|
-
target: targetProp,
|
|
2150
|
-
rel: relProp
|
|
2151
|
-
});
|
|
2152
|
-
const placeholderUrl = useEntityCardPlaceholder({
|
|
2153
|
-
title: guide.title,
|
|
2154
|
-
placeholderUrl: placeholderUrlProp,
|
|
2155
|
-
aspect: size === "sm" ? "square" : "wide"
|
|
2156
|
-
});
|
|
2157
|
-
if (size === "catalog") {
|
|
2158
|
-
const coverImage = guide.featured_image || guide.main_video_thumbnail || guide.og_image_url || null;
|
|
2159
|
-
const hasVideoCover = !!(guide.main_video_thumbnail || guide.highlight_video_thumbnail);
|
|
2160
|
-
const stepLabel = typeof guide.step_order === "number" ? String(guide.step_order).padStart(2, "0") : "\u2014";
|
|
2161
|
-
const durationLabel = typeof guide.highlight_video_duration_ms === "number" && guide.highlight_video_duration_ms > 0 ? formatDurationMMSS(Math.floor(guide.highlight_video_duration_ms / 1e3)) : "";
|
|
2162
|
-
return /* @__PURE__ */ jsx13(
|
|
2163
|
-
next_link_default,
|
|
2164
|
-
{
|
|
2165
|
-
href,
|
|
2166
|
-
target,
|
|
2167
|
-
rel,
|
|
2168
|
-
prefetch: false,
|
|
2169
|
-
className: cn(
|
|
2170
|
-
"group block no-underline bg-ods-system-greys-black",
|
|
2171
|
-
"border border-ods-border rounded-lg overflow-hidden",
|
|
2172
|
-
"transition-all duration-300 ease-out",
|
|
2173
|
-
"transform hover:translate-y-[-2px]",
|
|
2174
|
-
"hover:border-ods-accent hover:shadow-lg hover:shadow-ods-accent/[0.08]",
|
|
2175
|
-
className
|
|
2176
|
-
),
|
|
2177
|
-
"aria-label": `Open ${guide.title}`,
|
|
2178
|
-
children: /* @__PURE__ */ jsxs10("div", { className: "flex flex-col p-6 gap-4", children: [
|
|
2179
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col md:flex-row gap-4 md:gap-6", children: [
|
|
2180
|
-
/* @__PURE__ */ jsx13("div", { className: "w-full md:w-[256px] flex-shrink-0", children: /* @__PURE__ */ jsxs10("div", { className: "relative rounded-lg overflow-hidden w-full aspect-[1200/630] bg-ods-bg", children: [
|
|
2181
|
-
coverImage ? /* @__PURE__ */ jsx13(
|
|
2182
|
-
next_image_default,
|
|
2183
|
-
{
|
|
2184
|
-
src: coverImage,
|
|
2185
|
-
alt: guide.title,
|
|
2186
|
-
fill: true,
|
|
2187
|
-
sizes: "(max-width: 768px) 100vw, 256px",
|
|
2188
|
-
className: "object-cover",
|
|
2189
|
-
unoptimized: true
|
|
2190
|
-
}
|
|
2191
|
-
) : /* @__PURE__ */ jsx13(
|
|
2192
|
-
BlogImagePlaceholder,
|
|
2193
|
-
{
|
|
2194
|
-
title: guide.title,
|
|
2195
|
-
imageUrl: placeholderUrl ?? null,
|
|
2196
|
-
className: "absolute inset-0"
|
|
2197
|
-
}
|
|
2198
|
-
),
|
|
2199
|
-
hasVideoCover && coverImage && /* @__PURE__ */ jsx13("span", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: /* @__PURE__ */ jsx13(Play, { className: "w-10 h-10 text-white", fill: "white" }) }),
|
|
2200
|
-
durationLabel && /* @__PURE__ */ jsxs10("span", { className: "absolute bottom-2 right-2 inline-flex items-center gap-1 px-2 py-1 rounded bg-black/60 text-white text-xs font-medium font-mono", children: [
|
|
2201
|
-
/* @__PURE__ */ jsx13(Clock, { className: "w-3 h-3" }),
|
|
2202
|
-
durationLabel
|
|
2203
|
-
] })
|
|
2204
|
-
] }) }),
|
|
2205
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0 flex flex-col", children: [
|
|
2206
|
-
/* @__PURE__ */ jsx13("div", { className: "min-h-[60px] md:min-h-[72px] flex items-start mb-3", children: /* @__PURE__ */ jsx13("h3", { className: "font-['Azeret_Mono'] font-semibold text-xl md:text-2xl text-ods-text-primary leading-tight line-clamp-2", children: guide.title }) }),
|
|
2207
|
-
/* @__PURE__ */ jsx13("div", { className: "min-h-[46px] md:min-h-[52px]", children: /* @__PURE__ */ jsx13("p", { className: "font-['DM_Sans'] text-sm md:text-base text-ods-text-secondary leading-relaxed line-clamp-2", children: stripMarkdownPreview(guide.video_summary || guide.content || "") }) })
|
|
2208
|
-
] })
|
|
2209
|
-
] }),
|
|
2210
|
-
/* @__PURE__ */ jsx13(
|
|
2211
|
-
EntityAuthorCard,
|
|
2212
|
-
{
|
|
2213
|
-
author: guide.author,
|
|
2214
|
-
publishedAt: guide.published_at,
|
|
2215
|
-
renderEmptyAuthor: true,
|
|
2216
|
-
extraCells: [
|
|
2217
|
-
{
|
|
2218
|
-
value: `${guide.section} \xB7 Step ${stepLabel}`,
|
|
2219
|
-
label: "Section",
|
|
2220
|
-
uppercase: false
|
|
2221
|
-
}
|
|
2222
|
-
]
|
|
2223
|
-
}
|
|
2224
|
-
)
|
|
2225
|
-
] })
|
|
2226
|
-
}
|
|
2227
|
-
);
|
|
2228
|
-
}
|
|
2229
|
-
if (size === "sm") {
|
|
2230
|
-
const coverImage = guide.featured_image || guide.main_video_thumbnail || guide.og_image_url || null;
|
|
2231
|
-
const compactCover = coverImage || placeholderUrl || null;
|
|
2232
|
-
const hasVideoCover = !guide.featured_image && !!guide.main_video_thumbnail;
|
|
2233
|
-
const summary2 = stripMarkdownPreview(guide.video_summary || guide.content || "");
|
|
2234
|
-
const author = guide.author?.full_name?.trim() || "";
|
|
2235
|
-
const subtitleParts = [
|
|
2236
|
-
`Step ${guide.step_order}`,
|
|
2237
|
-
guide.section,
|
|
2238
|
-
author
|
|
2239
|
-
].filter((s) => typeof s === "string" && s.length > 0);
|
|
2240
|
-
return /* @__PURE__ */ jsxs10(next_link_default, { href, target, rel, prefetch: false, className: cn(COMPACT_CARD_OUTER, className), children: [
|
|
2241
|
-
/* @__PURE__ */ jsxs10("span", { className: COMPACT_CARD_IMAGE_SLOT, children: [
|
|
2242
|
-
compactCover ? /* @__PURE__ */ jsx13(
|
|
2243
|
-
next_image_default,
|
|
2244
|
-
{
|
|
2245
|
-
src: compactCover,
|
|
2246
|
-
alt: guide.title,
|
|
2247
|
-
fill: true,
|
|
2248
|
-
sizes: "56px",
|
|
2249
|
-
className: "object-contain",
|
|
2250
|
-
unoptimized: true
|
|
2251
|
-
}
|
|
2252
|
-
) : /* @__PURE__ */ jsx13("span", { className: "flex h-full w-full items-center justify-center text-ods-accent", children: /* @__PURE__ */ jsx13(GraduationCap, { className: "w-4 h-4" }) }),
|
|
2253
|
-
hasVideoCover && compactCover && /* @__PURE__ */ jsx13("span", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: /* @__PURE__ */ jsx13(Play, { className: "h-4 w-4 text-white", fill: "white" }) })
|
|
2254
|
-
] }),
|
|
2255
|
-
/* @__PURE__ */ jsxs10("span", { className: COMPACT_CARD_TEXT_COL, children: [
|
|
2256
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_TITLE_ROW, children: /* @__PURE__ */ jsx13("span", { className: cn(COMPACT_CARD_TITLE, "font-['Azeret_Mono']"), children: guide.title }) }),
|
|
2257
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx13("span", { className: "truncate text-[11px] leading-4 text-[var(--color-accent-primary)]", children: subtitleParts.join(" \xB7 ") }) }),
|
|
2258
|
-
/* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_META_ROW_BOX, children: /* @__PURE__ */ jsx13("span", { className: COMPACT_CARD_SUMMARY, children: summary2 || COMPACT_CARD_ROW_FILLER }) })
|
|
2259
|
-
] }),
|
|
2260
|
-
/* @__PURE__ */ jsx13("span", { className: "flex shrink-0 items-center self-start h-5 text-ods-text-secondary", children: /* @__PURE__ */ jsx13(ExternalLink, { className: "w-3.5 h-3.5" }) })
|
|
2261
|
-
] });
|
|
2262
|
-
}
|
|
2263
|
-
const t = HORIZONTAL_SIZE_TOKENS.default;
|
|
2264
|
-
const summary = stripMarkdownPreview(guide.video_summary || guide.content || "");
|
|
2265
|
-
return /* @__PURE__ */ jsxs10(
|
|
2266
|
-
next_link_default,
|
|
2267
|
-
{
|
|
2268
|
-
href,
|
|
2269
|
-
target,
|
|
2270
|
-
rel,
|
|
2271
|
-
prefetch: false,
|
|
2272
|
-
className: cn(
|
|
2273
|
-
`flex items-start gap-3 rounded-md border border-ods-border bg-ods-card hover:border-ods-accent transition-colors ${t.padding}`,
|
|
2274
|
-
className
|
|
2275
|
-
),
|
|
2276
|
-
children: [
|
|
2277
|
-
/* @__PURE__ */ jsx13(
|
|
2278
|
-
"span",
|
|
2279
|
-
{
|
|
2280
|
-
className: `shrink-0 inline-flex items-center justify-center rounded-full bg-ods-accent/10 text-ods-accent font-semibold ${t.step}`,
|
|
2281
|
-
"aria-hidden": "true",
|
|
2282
|
-
children: guide.step_order
|
|
2283
|
-
}
|
|
2284
|
-
),
|
|
2285
|
-
/* @__PURE__ */ jsxs10("span", { className: "flex flex-col gap-0.5 flex-1 min-w-0", children: [
|
|
2286
|
-
/* @__PURE__ */ jsx13("span", { className: `block ${t.title} text-ods-text-primary truncate`, children: guide.title }),
|
|
2287
|
-
/* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1 font-['DM_Sans'] text-[12px] leading-[16px] text-ods-text-secondary", children: [
|
|
2288
|
-
/* @__PURE__ */ jsx13(GraduationCap, { className: "h-3 w-3 shrink-0" }),
|
|
2289
|
-
/* @__PURE__ */ jsx13("span", { className: "truncate", children: guide.section })
|
|
2290
|
-
] }),
|
|
2291
|
-
summary && /* @__PURE__ */ jsx13("span", { className: `font-['DM_Sans'] text-[14px] leading-[20px] text-ods-text-secondary ${t.summaryClamp}`, children: summary })
|
|
2292
|
-
] })
|
|
2293
|
-
]
|
|
2294
|
-
}
|
|
2295
|
-
);
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
// src/components/ui/page-actions.tsx
|
|
2299
|
-
init_cn();
|
|
2300
|
-
import React10 from "react";
|
|
2301
|
-
init_button();
|
|
2302
|
-
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2303
|
-
function actionKey(action, idx) {
|
|
2304
|
-
return `${action.label ?? action.ariaLabel ?? "action"}-${idx}`;
|
|
2305
|
-
}
|
|
2306
|
-
function actionToMenuItems(action, idx) {
|
|
2307
|
-
if (action.submenu && action.submenu.length > 0) {
|
|
2308
|
-
if (!action.label) return action.submenu;
|
|
2309
|
-
return action.submenu.map((item) => ({
|
|
2310
|
-
...item,
|
|
2311
|
-
label: `${action.label} (${item.label})`
|
|
2312
|
-
}));
|
|
2313
|
-
}
|
|
2314
|
-
if (!action.label) return [];
|
|
2315
|
-
return [{
|
|
2316
|
-
id: `action-${idx}`,
|
|
2317
|
-
label: action.label,
|
|
2318
|
-
icon: action.icon,
|
|
2319
|
-
onClick: action.onClick,
|
|
2320
|
-
disabled: action.disabled,
|
|
2321
|
-
href: action.href,
|
|
2322
|
-
iconAction: action.iconAction ? {
|
|
2323
|
-
icon: action.iconAction.icon,
|
|
2324
|
-
"aria-label": action.iconAction["aria-label"],
|
|
2325
|
-
onClick: action.iconAction.onClick,
|
|
2326
|
-
href: action.iconAction.href,
|
|
2327
|
-
openInNewTab: action.iconAction.openInNewTab,
|
|
2328
|
-
disabled: action.iconAction.disabled
|
|
2329
|
-
} : void 0
|
|
2330
|
-
}];
|
|
2331
|
-
}
|
|
2332
|
-
function renderActionButton(action, opts = {}) {
|
|
2333
|
-
if (action.iconAction) {
|
|
2334
|
-
return /* @__PURE__ */ jsx14(
|
|
2335
|
-
SplitButton,
|
|
2336
|
-
{
|
|
2337
|
-
variant: action.variant ?? void 0,
|
|
2338
|
-
href: action.href,
|
|
2339
|
-
prefetch: action.prefetch,
|
|
2340
|
-
openInNewTab: action.openInNewTab,
|
|
2341
|
-
onClick: action.onClick,
|
|
2342
|
-
disabled: action.disabled,
|
|
2343
|
-
mainDisabled: action.mainDisabled,
|
|
2344
|
-
leftIcon: action.icon,
|
|
2345
|
-
fullWidth: opts.fullWidth,
|
|
2346
|
-
iconAction: action.iconAction,
|
|
2347
|
-
children: action.label
|
|
2348
|
-
}
|
|
2349
|
-
);
|
|
2350
|
-
}
|
|
2351
|
-
if (action.submenu && action.submenu.length > 0) {
|
|
2352
|
-
return /* @__PURE__ */ jsx14(
|
|
2353
|
-
ActionsMenuDropdown,
|
|
2354
|
-
{
|
|
2355
|
-
groups: [{ items: action.submenu }],
|
|
2356
|
-
customTrigger: /* @__PURE__ */ jsx14(
|
|
2357
|
-
Button,
|
|
2358
|
-
{
|
|
2359
|
-
variant: "outline",
|
|
2360
|
-
disabled: action.disabled,
|
|
2361
|
-
loading: action.loading,
|
|
2362
|
-
leftIcon: action.icon,
|
|
2363
|
-
splitIcon: /* @__PURE__ */ jsx14(Chevron02DownIcon, { className: "h-4 w-4" }),
|
|
2364
|
-
className: opts.fullWidth ? "flex-1" : void 0,
|
|
2365
|
-
children: action.label
|
|
2366
|
-
}
|
|
2367
|
-
)
|
|
2368
|
-
}
|
|
2369
|
-
);
|
|
2370
|
-
}
|
|
2371
|
-
const isIconOnly = opts.iconOnly || !action.label || action.iconOnlyOnDesktop;
|
|
2372
|
-
if (isIconOnly) {
|
|
2373
|
-
const iconNode = action.iconOnlyOnDesktop ? /* @__PURE__ */ jsx14("span", { className: "inline-flex [&_svg]:!text-ods-text-primary", children: action.icon }) : action.icon;
|
|
2374
|
-
return /* @__PURE__ */ jsx14(
|
|
2375
|
-
Button,
|
|
2376
|
-
{
|
|
2377
|
-
variant: action.variant,
|
|
2378
|
-
size: "icon",
|
|
2379
|
-
href: action.href,
|
|
2380
|
-
prefetch: action.prefetch,
|
|
2381
|
-
openInNewTab: action.openInNewTab,
|
|
2382
|
-
onClick: action.onClick,
|
|
2383
|
-
disabled: action.disabled,
|
|
2384
|
-
loading: action.loading,
|
|
2385
|
-
leftIcon: iconNode,
|
|
2386
|
-
"aria-label": action.label ?? action.ariaLabel
|
|
2387
|
-
}
|
|
2388
|
-
);
|
|
2389
|
-
}
|
|
2390
|
-
return /* @__PURE__ */ jsx14(
|
|
2391
|
-
Button,
|
|
2392
|
-
{
|
|
2393
|
-
variant: action.variant,
|
|
2394
|
-
href: action.href,
|
|
2395
|
-
prefetch: action.prefetch,
|
|
2396
|
-
openInNewTab: action.openInNewTab,
|
|
2397
|
-
onClick: action.onClick,
|
|
2398
|
-
disabled: action.disabled,
|
|
2399
|
-
loading: action.loading,
|
|
2400
|
-
leftIcon: action.icon,
|
|
2401
|
-
className: opts.fullWidth ? "flex-1" : void 0,
|
|
2402
|
-
children: action.label
|
|
2403
|
-
}
|
|
2404
|
-
);
|
|
2405
|
-
}
|
|
2406
|
-
var ACTIONS_GAP = "gap-[var(--spacing-system-xs)]";
|
|
2407
|
-
function PageActions({
|
|
2408
|
-
variant = "icon-buttons",
|
|
2409
|
-
actions,
|
|
2410
|
-
menuActions,
|
|
2411
|
-
selector,
|
|
2412
|
-
className
|
|
2413
|
-
}) {
|
|
2414
|
-
if (variant === "icon-buttons") {
|
|
2415
|
-
return /* @__PURE__ */ jsx14(IconButtonsVariant, { actions, menuActions, selector, className });
|
|
2416
|
-
}
|
|
2417
|
-
if (variant === "menu-primary") {
|
|
2418
|
-
return /* @__PURE__ */ jsx14(MenuPrimaryVariant, { actions, menuActions: menuActions || [], selector, className });
|
|
2419
|
-
}
|
|
2420
|
-
return /* @__PURE__ */ jsx14(PrimaryButtonsVariant, { actions, className });
|
|
2421
|
-
}
|
|
2422
|
-
function IconButtonsVariant({
|
|
2423
|
-
actions,
|
|
2424
|
-
menuActions,
|
|
2425
|
-
selector,
|
|
2426
|
-
className
|
|
2427
|
-
}) {
|
|
2428
|
-
const desktopActions = actions.filter((a) => !a.showOnlyMobile);
|
|
2429
|
-
const hasMenuActions = !!menuActions && menuActions.some((g) => g.items.length > 0);
|
|
2430
|
-
const isSingleAction = actions.length === 1 && !actions[0].submenu?.length;
|
|
2431
|
-
const singleAction = isSingleAction ? actions[0] : null;
|
|
2432
|
-
const useSingleActionMobile = isSingleAction && !hasMenuActions;
|
|
2433
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
2434
|
-
/* @__PURE__ */ jsxs11("div", { className: cn("hidden md:flex items-center", ACTIONS_GAP, className), children: [
|
|
2435
|
-
selector,
|
|
2436
|
-
desktopActions.map((action, idx) => /* @__PURE__ */ jsx14(React10.Fragment, { children: renderActionButton(action) }, actionKey(action, idx))),
|
|
2437
|
-
hasMenuActions && /* @__PURE__ */ jsx14(ActionsMenuDropdown, { groups: menuActions })
|
|
2438
|
-
] }),
|
|
2439
|
-
/* @__PURE__ */ jsx14("div", { className: cn("flex md:hidden", className), children: useSingleActionMobile && singleAction ? renderActionButton(singleAction, { iconOnly: true }) : /* @__PURE__ */ jsx14(
|
|
2440
|
-
ActionsMenuDropdown,
|
|
2441
|
-
{
|
|
2442
|
-
groups: [
|
|
2443
|
-
{ items: actions.flatMap(actionToMenuItems) },
|
|
2444
|
-
...menuActions ?? []
|
|
2445
|
-
]
|
|
2446
|
-
}
|
|
2447
|
-
) })
|
|
2448
|
-
] });
|
|
2449
|
-
}
|
|
2450
|
-
function PrimaryButtonsVariant({
|
|
2451
|
-
actions,
|
|
2452
|
-
className
|
|
2453
|
-
}) {
|
|
2454
|
-
const sortedActions = [...actions].sort((a, b) => {
|
|
2455
|
-
if (a.variant === "accent" && b.variant !== "accent") return 1;
|
|
2456
|
-
if (a.variant !== "accent" && b.variant === "accent") return -1;
|
|
2457
|
-
return 0;
|
|
2458
|
-
});
|
|
2459
|
-
const desktopActions = sortedActions.filter((a) => !a.showOnlyMobile);
|
|
2460
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
2461
|
-
/* @__PURE__ */ jsx14("div", { className: cn("hidden md:flex items-center", ACTIONS_GAP, className), children: desktopActions.map((action, idx) => /* @__PURE__ */ jsx14(React10.Fragment, { children: renderActionButton(action) }, `desktop-${actionKey(action, idx)}`)) }),
|
|
2462
|
-
/* @__PURE__ */ jsx14(MobileBottomActions, { actions: sortedActions })
|
|
2463
|
-
] });
|
|
2464
|
-
}
|
|
2465
|
-
function MenuPrimaryVariant({
|
|
2466
|
-
actions,
|
|
2467
|
-
menuActions,
|
|
2468
|
-
selector,
|
|
2469
|
-
className
|
|
2470
|
-
}) {
|
|
2471
|
-
const desktopActions = actions.filter((a) => !a.showOnlyMobile);
|
|
2472
|
-
const hasMenuActions = menuActions.some((g) => g.items.length > 0);
|
|
2473
|
-
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
2474
|
-
/* @__PURE__ */ jsxs11("div", { className: cn("hidden md:flex items-center", ACTIONS_GAP, className), children: [
|
|
2475
|
-
selector,
|
|
2476
|
-
hasMenuActions && /* @__PURE__ */ jsx14(ActionsMenuDropdown, { groups: menuActions }),
|
|
2477
|
-
desktopActions.map((action, idx) => /* @__PURE__ */ jsx14(React10.Fragment, { children: renderActionButton({ ...action, variant: action.variant || "accent" }) }, `desktop-${actionKey(action, idx)}`))
|
|
2478
|
-
] }),
|
|
2479
|
-
/* @__PURE__ */ jsx14("div", { className: cn("flex md:hidden", className), children: /* @__PURE__ */ jsx14(
|
|
2480
|
-
ActionsMenuDropdown,
|
|
2481
|
-
{
|
|
2482
|
-
groups: [
|
|
2483
|
-
{ items: actions.flatMap(actionToMenuItems) },
|
|
2484
|
-
...menuActions
|
|
2485
|
-
]
|
|
2486
|
-
}
|
|
2487
|
-
) })
|
|
2488
|
-
] });
|
|
2489
|
-
}
|
|
2490
|
-
function MobileBottomActions({ actions }) {
|
|
2491
|
-
return /* @__PURE__ */ jsx14("div", { className: cn(
|
|
2492
|
-
"fixed md:hidden bottom-0 left-0 right-0 z-50",
|
|
2493
|
-
"bg-ods-card border-t border-ods-border",
|
|
2494
|
-
"flex items-start pt-6 pb-6 px-6",
|
|
2495
|
-
ACTIONS_GAP
|
|
2496
|
-
), children: actions.map((action, idx) => /* @__PURE__ */ jsx14(React10.Fragment, { children: renderActionButton(action, { fullWidth: !!action.label }) }, `mobile-${actionKey(action, idx)}`)) });
|
|
2497
|
-
}
|
|
2498
|
-
function usePageActionsBottomPadding(variant) {
|
|
2499
|
-
return variant === "primary-buttons" || variant === "menu-primary" ? "pb-40 md:pb-0" : "";
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
// src/components/ui/entity-image.tsx
|
|
2503
|
-
init_cn();
|
|
2504
|
-
import React11 from "react";
|
|
2505
|
-
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2506
|
-
function EntityImage({ src, alt, fallbackText, className }) {
|
|
2507
|
-
const [imageFailed, setImageFailed] = React11.useState(false);
|
|
2508
|
-
React11.useEffect(() => {
|
|
2509
|
-
setImageFailed(false);
|
|
2510
|
-
}, [src]);
|
|
2511
|
-
const showFallback = imageFailed || !src;
|
|
2512
|
-
const initials = getFirstLastInitials(fallbackText ?? alt);
|
|
2513
|
-
if (showFallback) {
|
|
2514
|
-
return /* @__PURE__ */ jsx15(
|
|
2515
|
-
"div",
|
|
2516
|
-
{
|
|
2517
|
-
"aria-label": alt,
|
|
2518
|
-
className: cn(
|
|
2519
|
-
"size-[52px] md:size-[60px] shrink-0 rounded-md border border-ods-border bg-ods-bg flex items-center justify-center text-ods-text-secondary text-h4 select-none",
|
|
2520
|
-
className
|
|
2521
|
-
),
|
|
2522
|
-
children: initials || "?"
|
|
2523
|
-
}
|
|
2524
|
-
);
|
|
2525
|
-
}
|
|
2526
|
-
return /* @__PURE__ */ jsx15(
|
|
2527
|
-
"img",
|
|
2528
|
-
{
|
|
2529
|
-
src: src ?? void 0,
|
|
2530
|
-
alt: alt ?? "",
|
|
2531
|
-
onError: () => setImageFailed(true),
|
|
2532
|
-
className: cn(
|
|
2533
|
-
"size-[52px] md:size-[60px] shrink-0 rounded-md border border-ods-border object-contain",
|
|
2534
|
-
className
|
|
2535
|
-
)
|
|
2536
|
-
}
|
|
2537
|
-
);
|
|
2538
|
-
}
|
|
2539
|
-
|
|
2540
|
-
// src/components/layout/title-block.tsx
|
|
2541
|
-
init_cn();
|
|
2542
|
-
|
|
2543
|
-
// src/components/layout/back-button.tsx
|
|
2544
|
-
init_cn();
|
|
2545
|
-
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2546
|
-
function BackButton({ label = "Back", className, type = "button", ...props }) {
|
|
2547
|
-
return /* @__PURE__ */ jsxs12(
|
|
2548
|
-
"button",
|
|
2549
|
-
{
|
|
2550
|
-
type,
|
|
2551
|
-
className: cn(
|
|
2552
|
-
"group inline-flex items-center justify-center self-start rounded-md",
|
|
2553
|
-
"gap-[var(--spacing-system-xsf)] py-[var(--spacing-system-sf)]",
|
|
2554
|
-
"text-ods-text-secondary hover:text-ods-text-primary",
|
|
2555
|
-
"transition-colors duration-200",
|
|
2556
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus",
|
|
2557
|
-
className
|
|
2558
|
-
),
|
|
2559
|
-
...props,
|
|
2560
|
-
children: [
|
|
2561
|
-
/* @__PURE__ */ jsx16(Chevron02LeftIcon, { className: "size-6 shrink-0" }),
|
|
2562
|
-
/* @__PURE__ */ jsx16("span", { className: "text-h4", children: label })
|
|
2563
|
-
]
|
|
2564
|
-
}
|
|
2565
|
-
);
|
|
2566
|
-
}
|
|
2567
|
-
|
|
2568
|
-
// src/components/layout/title-block.tsx
|
|
2569
|
-
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2570
|
-
function TitleBlock({
|
|
2571
|
-
title,
|
|
2572
|
-
subtitle,
|
|
2573
|
-
image,
|
|
2574
|
-
backButton,
|
|
2575
|
-
actions,
|
|
2576
|
-
actionsVariant = "icon-buttons",
|
|
2577
|
-
menuActions,
|
|
2578
|
-
selector,
|
|
2579
|
-
variant = "plain",
|
|
2580
|
-
className
|
|
2581
|
-
}) {
|
|
2582
|
-
const hasActions = actions && actions.length > 0;
|
|
2583
|
-
const hasMenuActions = !!menuActions && menuActions.some((g) => g.items.length > 0);
|
|
2584
|
-
return /* @__PURE__ */ jsxs13(
|
|
2585
|
-
"div",
|
|
2586
|
-
{
|
|
2587
|
-
className: cn(
|
|
2588
|
-
"flex items-end justify-between gap-[var(--spacing-system-m)]",
|
|
2589
|
-
"md:flex-col md:items-start md:justify-start lg:flex-row lg:items-end lg:justify-between",
|
|
2590
|
-
"pt-[var(--spacing-system-l)]",
|
|
2591
|
-
variant === "card" ? cn(
|
|
2592
|
-
"bg-ods-card border-b border-ods-border",
|
|
2593
|
-
"px-[var(--spacing-system-l)] pb-[var(--spacing-system-l)]",
|
|
2594
|
-
"md:bg-transparent md:border-b-0",
|
|
2595
|
-
"md:px-0 md:pb-0",
|
|
2596
|
-
"md:mb-[var(--spacing-system-l)]"
|
|
2597
|
-
) : "mb-[var(--spacing-system-l)]",
|
|
2598
|
-
className
|
|
2599
|
-
),
|
|
2600
|
-
children: [
|
|
2601
|
-
/* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-[var(--spacing-system-xs)] flex-1 min-w-0", children: [
|
|
2602
|
-
backButton && /* @__PURE__ */ jsx17(
|
|
2603
|
-
BackButton,
|
|
2604
|
-
{
|
|
2605
|
-
onClick: backButton.onClick,
|
|
2606
|
-
label: backButton.label,
|
|
2607
|
-
className: "hidden md:inline-flex"
|
|
2608
|
-
}
|
|
2609
|
-
),
|
|
2610
|
-
image || subtitle ? /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-[var(--spacing-system-m)] min-w-0 w-full", children: [
|
|
2611
|
-
image && /* @__PURE__ */ jsx17(
|
|
2612
|
-
EntityImage,
|
|
2613
|
-
{
|
|
2614
|
-
src: image.src,
|
|
2615
|
-
alt: image.alt,
|
|
2616
|
-
fallbackText: image.alt || title
|
|
2617
|
-
}
|
|
2618
|
-
),
|
|
2619
|
-
/* @__PURE__ */ jsxs13("div", { className: "flex flex-col justify-center min-w-0 flex-1", children: [
|
|
2620
|
-
title && /* @__PURE__ */ jsx17("h1", { className: "text-h2 text-ods-text-primary truncate", title, children: title }),
|
|
2621
|
-
subtitle && /* @__PURE__ */ jsx17("p", { className: "text-h6 text-ods-text-secondary truncate", title: subtitle, children: subtitle })
|
|
2622
|
-
] })
|
|
2623
|
-
] }) : title && /* @__PURE__ */ jsx17("h1", { className: "text-h2 text-ods-text-primary", children: title })
|
|
2624
|
-
] }),
|
|
2625
|
-
(hasActions || hasMenuActions || selector) && /* @__PURE__ */ jsx17("div", { className: "flex gap-2 items-center shrink-0", children: /* @__PURE__ */ jsx17(
|
|
2626
|
-
PageActions,
|
|
2627
|
-
{
|
|
2628
|
-
variant: actionsVariant,
|
|
2629
|
-
actions: actions ?? [],
|
|
2630
|
-
menuActions,
|
|
2631
|
-
selector
|
|
2632
|
-
}
|
|
2633
|
-
) })
|
|
2634
|
-
]
|
|
2635
|
-
}
|
|
2636
|
-
);
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
// src/components/layout/page-layout.tsx
|
|
2640
|
-
init_cn();
|
|
2641
|
-
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2642
|
-
function PageLayout({
|
|
2643
|
-
children,
|
|
2644
|
-
title,
|
|
2645
|
-
subtitle,
|
|
2646
|
-
image,
|
|
2647
|
-
backButton,
|
|
2648
|
-
actions,
|
|
2649
|
-
actionsVariant = "icon-buttons",
|
|
2650
|
-
menuActions,
|
|
2651
|
-
selector,
|
|
2652
|
-
headerVariant,
|
|
2653
|
-
className,
|
|
2654
|
-
contentClassName,
|
|
2655
|
-
showHeader = true
|
|
2656
|
-
}) {
|
|
2657
|
-
const hasActions = actions && actions.length > 0;
|
|
2658
|
-
const needsBottomPadding = hasActions && actionsVariant === "primary-buttons";
|
|
2659
|
-
const hasHeader = showHeader && (title || subtitle || image || backButton || hasActions || selector);
|
|
2660
|
-
return /* @__PURE__ */ jsxs14("div", { className: cn("flex flex-col w-full", className), children: [
|
|
2661
|
-
hasHeader && /* @__PURE__ */ jsx18(
|
|
2662
|
-
TitleBlock,
|
|
2663
|
-
{
|
|
2664
|
-
title,
|
|
2665
|
-
subtitle,
|
|
2666
|
-
image,
|
|
2667
|
-
backButton,
|
|
2668
|
-
actions,
|
|
2669
|
-
actionsVariant,
|
|
2670
|
-
menuActions,
|
|
2671
|
-
selector,
|
|
2672
|
-
variant: headerVariant
|
|
2673
|
-
}
|
|
2674
|
-
),
|
|
2675
|
-
/* @__PURE__ */ jsx18("div", { className: cn("flex flex-col flex-1 gap-[var(--spacing-system-l)]", needsBottomPadding && "pb-28 md:pb-0", contentClassName), children })
|
|
2676
|
-
] });
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
// src/components/layout/article-detail-layout.tsx
|
|
2680
|
-
init_cn();
|
|
2681
|
-
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2682
|
-
function PageShell({ children, schemas, contentClassName }) {
|
|
2683
|
-
return /* @__PURE__ */ jsxs15("main", { className: "bg-ods-bg min-h-screen", children: [
|
|
2684
|
-
schemas,
|
|
2685
|
-
/* @__PURE__ */ jsx19("div", { className: cn("page-shell-content max-w-[1920px] mx-auto", contentClassName), children })
|
|
2686
|
-
] });
|
|
2687
|
-
}
|
|
2688
|
-
function ArticleDetailLayout({ children, schemas, contentClassName }) {
|
|
2689
|
-
return /* @__PURE__ */ jsxs15("main", { className: "bg-ods-bg min-h-screen", children: [
|
|
2690
|
-
schemas,
|
|
2691
|
-
/* @__PURE__ */ jsx19("div", { className: cn("max-w-[1280px] mx-auto px-6 md:px-20 py-6 md:py-10", contentClassName), children })
|
|
2692
|
-
] });
|
|
2693
|
-
}
|
|
2694
|
-
|
|
2695
|
-
// src/components/ui/error-state.tsx
|
|
2696
|
-
init_button();
|
|
2697
|
-
init_cn();
|
|
2698
|
-
import { AlertTriangle, RefreshCw, Home } from "lucide-react";
|
|
2699
|
-
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2700
|
-
function ErrorState({
|
|
2701
|
-
title = "Error",
|
|
2702
|
-
message,
|
|
2703
|
-
variant = "error",
|
|
2704
|
-
showIcon = true,
|
|
2705
|
-
showRetry = false,
|
|
2706
|
-
showHome = false,
|
|
2707
|
-
onRetry,
|
|
2708
|
-
onHome,
|
|
2709
|
-
className,
|
|
2710
|
-
containerClassName
|
|
2711
|
-
}) {
|
|
2712
|
-
const getVariantStyles = () => {
|
|
2713
|
-
switch (variant) {
|
|
2714
|
-
case "error":
|
|
2715
|
-
return {
|
|
2716
|
-
bg: "bg-ods-attention-red-error/20",
|
|
2717
|
-
border: "border-ods-attention-red-error",
|
|
2718
|
-
text: "text-ods-attention-red-error",
|
|
2719
|
-
icon: "text-ods-attention-red-error"
|
|
2720
|
-
};
|
|
2721
|
-
case "warning":
|
|
2722
|
-
return {
|
|
2723
|
-
bg: "bg-ods-attention-yellow-warning/20",
|
|
2724
|
-
border: "border-ods-attention-yellow-warning",
|
|
2725
|
-
text: "text-ods-attention-yellow-warning",
|
|
2726
|
-
icon: "text-ods-attention-yellow-warning"
|
|
2727
|
-
};
|
|
2728
|
-
case "info":
|
|
2729
|
-
return {
|
|
2730
|
-
bg: "bg-ods-bg-surface",
|
|
2731
|
-
border: "border-ods-border",
|
|
2732
|
-
text: "text-ods-text-secondary",
|
|
2733
|
-
icon: "text-ods-text-secondary"
|
|
2734
|
-
};
|
|
2735
|
-
}
|
|
2736
|
-
};
|
|
2737
|
-
const styles = getVariantStyles();
|
|
2738
|
-
return /* @__PURE__ */ jsx20("div", { className: cn("p-6", containerClassName), children: /* @__PURE__ */ jsx20("div", { className: cn(
|
|
2739
|
-
"rounded-lg p-4 border",
|
|
2740
|
-
styles.bg,
|
|
2741
|
-
styles.border,
|
|
2742
|
-
className
|
|
2743
|
-
), children: /* @__PURE__ */ jsxs16("div", { className: "flex items-start gap-3", children: [
|
|
2744
|
-
showIcon && /* @__PURE__ */ jsx20(AlertTriangle, { className: cn("h-5 w-5 mt-0.5 flex-shrink-0", styles.icon) }),
|
|
2745
|
-
/* @__PURE__ */ jsxs16("div", { className: "flex-1", children: [
|
|
2746
|
-
/* @__PURE__ */ jsx20("h3", { className: cn("font-semibold mb-1", styles.text), children: title }),
|
|
2747
|
-
/* @__PURE__ */ jsx20("p", { className: cn("text-sm", styles.text), children: message }),
|
|
2748
|
-
(showRetry || showHome) && /* @__PURE__ */ jsxs16("div", { className: "flex gap-2 mt-3", children: [
|
|
2749
|
-
showRetry && onRetry && /* @__PURE__ */ jsx20(
|
|
2750
|
-
Button,
|
|
2751
|
-
{
|
|
2752
|
-
onClick: onRetry,
|
|
2753
|
-
variant: "outline",
|
|
2754
|
-
size: "small-legacy",
|
|
2755
|
-
className: "h-8",
|
|
2756
|
-
leftIcon: /* @__PURE__ */ jsx20(RefreshCw, { className: "h-4 w-4" }),
|
|
2757
|
-
children: "Try Again"
|
|
2758
|
-
}
|
|
2759
|
-
),
|
|
2760
|
-
showHome && onHome && /* @__PURE__ */ jsx20(
|
|
2761
|
-
Button,
|
|
2762
|
-
{
|
|
2763
|
-
onClick: onHome,
|
|
2764
|
-
variant: "outline",
|
|
2765
|
-
size: "small-legacy",
|
|
2766
|
-
className: "h-8",
|
|
2767
|
-
leftIcon: /* @__PURE__ */ jsx20(Home, { className: "h-4 w-4" }),
|
|
2768
|
-
children: "Go Home"
|
|
2769
|
-
}
|
|
2770
|
-
)
|
|
2771
|
-
] })
|
|
2772
|
-
] })
|
|
2773
|
-
] }) }) });
|
|
2774
|
-
}
|
|
2775
|
-
function PageError({ message, onRetry, onHome }) {
|
|
2776
|
-
return /* @__PURE__ */ jsx20(
|
|
2777
|
-
ErrorState,
|
|
2778
|
-
{
|
|
2779
|
-
title: "Page Error",
|
|
2780
|
-
message,
|
|
2781
|
-
variant: "error",
|
|
2782
|
-
showRetry: !!onRetry,
|
|
2783
|
-
showHome: !!onHome,
|
|
2784
|
-
onRetry,
|
|
2785
|
-
onHome
|
|
2786
|
-
}
|
|
2787
|
-
);
|
|
2788
|
-
}
|
|
2789
|
-
function LoadError({ message, onRetry }) {
|
|
2790
|
-
return /* @__PURE__ */ jsx20(
|
|
2791
|
-
ErrorState,
|
|
2792
|
-
{
|
|
2793
|
-
title: "Loading Error",
|
|
2794
|
-
message,
|
|
2795
|
-
variant: "error",
|
|
2796
|
-
showRetry: !!onRetry,
|
|
2797
|
-
onRetry
|
|
2798
|
-
}
|
|
2799
|
-
);
|
|
2800
|
-
}
|
|
2801
|
-
function NotFoundError({ message = "The requested item was not found", onHome }) {
|
|
2802
|
-
return /* @__PURE__ */ jsx20(
|
|
2803
|
-
ErrorState,
|
|
2804
|
-
{
|
|
2805
|
-
title: "Not Found",
|
|
2806
|
-
message,
|
|
2807
|
-
variant: "warning",
|
|
2808
|
-
showHome: !!onHome,
|
|
2809
|
-
onHome
|
|
2810
|
-
}
|
|
2811
|
-
);
|
|
2812
|
-
}
|
|
2813
|
-
|
|
2814
|
-
// src/components/features/entity-tag-badges.tsx
|
|
2815
|
-
import Link from "next/link";
|
|
2816
|
-
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2817
|
-
var TAG_BADGE_CLASS = "bg-ods-card border border-ods-border";
|
|
2818
|
-
var BADGE_CLASS = TAG_BADGE_CLASS;
|
|
2819
|
-
function EntityTagBadges({ tags, basePath, max, className }) {
|
|
2820
|
-
const items = (tags || []).filter((t) => !!t && !!t.name);
|
|
2821
|
-
if (items.length === 0) return null;
|
|
2822
|
-
const visibleLimit = max == null ? items.length : Math.max(0, max);
|
|
2823
|
-
const shown = items.slice(0, visibleLimit);
|
|
2824
|
-
const overflow = items.length - shown.length;
|
|
2825
|
-
return /* @__PURE__ */ jsxs17("div", { className: `flex flex-wrap items-center gap-2 w-full ${className || ""}`, children: [
|
|
2826
|
-
shown.map((tag) => {
|
|
2827
|
-
const key = tag.tag_id ?? tag.id ?? tag.slug ?? tag.name;
|
|
2828
|
-
const label = (tag.name || "").toUpperCase();
|
|
2829
|
-
if (basePath && tag.slug) {
|
|
2830
|
-
return /* @__PURE__ */ jsx21(Link, { href: `${basePath}?tags=${tag.slug}`, className: "inline-flex", children: /* @__PURE__ */ jsx21(
|
|
2831
|
-
StatusBadge,
|
|
2832
|
-
{
|
|
2833
|
-
text: label,
|
|
2834
|
-
variant: "card",
|
|
2835
|
-
className: `${BADGE_CLASS} cursor-pointer transition-colors hover:border-ods-accent`
|
|
2836
|
-
}
|
|
2837
|
-
) }, key);
|
|
2838
|
-
}
|
|
2839
|
-
return /* @__PURE__ */ jsx21(StatusBadge, { text: label, variant: "card", className: BADGE_CLASS }, key);
|
|
2840
|
-
}),
|
|
2841
|
-
overflow > 0 && /* @__PURE__ */ jsx21(StatusBadge, { text: `+${overflow}`, variant: "card", className: `${BADGE_CLASS} text-ods-text-secondary` })
|
|
2842
|
-
] });
|
|
2843
|
-
}
|
|
2844
|
-
|
|
2845
|
-
// src/components/features/mux-origins.ts
|
|
2846
|
-
var MUX_STREAM_ORIGIN = "https://stream.mux.com";
|
|
2847
|
-
var MUX_IMAGE_ORIGIN = "https://image.mux.com";
|
|
2848
|
-
|
|
2849
|
-
// src/components/features/use-video-warmup.ts
|
|
2850
|
-
import { useEffect as useEffect5 } from "react";
|
|
2851
|
-
import ReactDOM from "react-dom";
|
|
2852
|
-
function useVideoOriginPreconnect({
|
|
2853
|
-
supabaseStorageOrigin
|
|
2854
|
-
} = {}) {
|
|
2855
|
-
const runtime = useChatRuntime();
|
|
2856
|
-
const resolvedOrigin = supabaseStorageOrigin ?? runtime?.endpoints.supabaseStorageOrigin;
|
|
2857
|
-
try {
|
|
2858
|
-
ReactDOM.preconnect(MUX_STREAM_ORIGIN, { crossOrigin: "anonymous" });
|
|
2859
|
-
ReactDOM.preconnect(MUX_IMAGE_ORIGIN, { crossOrigin: "anonymous" });
|
|
2860
|
-
if (resolvedOrigin) {
|
|
2861
|
-
ReactDOM.preconnect(resolvedOrigin, { crossOrigin: "anonymous" });
|
|
2862
|
-
}
|
|
2863
|
-
} catch (err) {
|
|
2864
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2865
|
-
console.warn("[useVideoOriginPreconnect] preconnect failed:", err);
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
}
|
|
2869
|
-
function useVideoWarmup({
|
|
2870
|
-
videoUrl,
|
|
2871
|
-
supabaseStorageOrigin,
|
|
2872
|
-
nearMargin = "1000px"
|
|
2873
|
-
} = {}) {
|
|
2874
|
-
const runtime = useChatRuntime();
|
|
2875
|
-
const resolvedOrigin = supabaseStorageOrigin ?? runtime?.endpoints.supabaseStorageOrigin;
|
|
2876
|
-
useVideoOriginPreconnect({ supabaseStorageOrigin: resolvedOrigin });
|
|
2877
|
-
const { ref, isNear } = useNearViewport(nearMargin);
|
|
2878
|
-
useEffect5(() => {
|
|
2879
|
-
if (!isNear || !videoUrl || !resolvedOrigin) return;
|
|
2880
|
-
const conn = navigator.connection;
|
|
2881
|
-
if (conn?.saveData === true) return;
|
|
2882
|
-
let videoOrigin;
|
|
2883
|
-
try {
|
|
2884
|
-
videoOrigin = new URL(videoUrl, "http://placeholder.local").origin;
|
|
2885
|
-
} catch {
|
|
2886
|
-
return;
|
|
2887
|
-
}
|
|
2888
|
-
if (videoOrigin !== resolvedOrigin) return;
|
|
2889
|
-
const link = document.createElement("link");
|
|
2890
|
-
link.rel = "preload";
|
|
2891
|
-
link.as = "video";
|
|
2892
|
-
link.href = videoUrl;
|
|
2893
|
-
link.crossOrigin = "anonymous";
|
|
2894
|
-
if ("fetchPriority" in link) {
|
|
2895
|
-
;
|
|
2896
|
-
link.fetchPriority = "low";
|
|
2897
|
-
}
|
|
2898
|
-
document.head.appendChild(link);
|
|
2899
|
-
return () => {
|
|
2900
|
-
link.remove();
|
|
2901
|
-
};
|
|
2902
|
-
}, [isNear, videoUrl, resolvedOrigin]);
|
|
2903
|
-
return { ref, isNear };
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
// src/components/features/captions-url.ts
|
|
2907
|
-
function getCaptionsUrl(entityType, entityId, srtContent) {
|
|
2908
|
-
if (!srtContent) return void 0;
|
|
2909
|
-
const hash = `${srtContent.length}-${srtContent.slice(0, 8).replace(/\s/g, "")}`;
|
|
2910
|
-
return `/api/captions/${entityType}/${entityId}?v=${hash}`;
|
|
2911
|
-
}
|
|
2912
|
-
|
|
2913
|
-
// src/components/ui/filter-pill-row.tsx
|
|
2914
|
-
init_button();
|
|
2915
|
-
import { Filter } from "lucide-react";
|
|
2916
|
-
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2917
|
-
function FilterPillRow({
|
|
2918
|
-
label,
|
|
2919
|
-
selectedValue,
|
|
2920
|
-
onValueChange,
|
|
2921
|
-
options,
|
|
2922
|
-
countLabel,
|
|
2923
|
-
children
|
|
2924
|
-
}) {
|
|
2925
|
-
return /* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-center gap-3 p-4 bg-ods-card border border-ods-border rounded-lg", children: [
|
|
2926
|
-
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
|
|
2927
|
-
/* @__PURE__ */ jsx22(Filter, { className: "h-4 w-4 text-ods-accent" }),
|
|
2928
|
-
/* @__PURE__ */ jsx22("span", { className: "text-h5 text-ods-text-secondary", children: label })
|
|
2929
|
-
] }),
|
|
2930
|
-
children || options.map((opt) => /* @__PURE__ */ jsx22(
|
|
2931
|
-
Button,
|
|
2932
|
-
{
|
|
2933
|
-
type: "button",
|
|
2934
|
-
variant: selectedValue === opt.value ? "accent" : "outline",
|
|
2935
|
-
size: "small-legacy",
|
|
2936
|
-
onClick: () => onValueChange(opt.value),
|
|
2937
|
-
className: "text-h3",
|
|
2938
|
-
children: opt.label
|
|
2939
|
-
},
|
|
2940
|
-
opt.value
|
|
2941
|
-
)),
|
|
2942
|
-
countLabel && /* @__PURE__ */ jsx22("div", { className: "ml-auto text-[12px] font-['DM_Sans'] text-ods-text-secondary shrink-0", children: countLabel })
|
|
2943
|
-
] });
|
|
2944
|
-
}
|
|
2945
|
-
|
|
2946
|
-
export {
|
|
2947
|
-
Card,
|
|
2948
|
-
CardHeader,
|
|
2949
|
-
CardTitle,
|
|
2950
|
-
CardDescription,
|
|
2951
|
-
CardContent,
|
|
2952
|
-
CardFooter,
|
|
2953
|
-
CardHorizontal,
|
|
2954
|
-
Tabs,
|
|
2955
|
-
TabsList,
|
|
2956
|
-
TabsTrigger,
|
|
2957
|
-
TabsContent,
|
|
2958
|
-
statusBadgeVariants,
|
|
2959
|
-
StatusBadge,
|
|
2960
|
-
SimpleMarkdownRenderer,
|
|
2961
|
-
SquareAvatar,
|
|
2962
|
-
useControllableState,
|
|
2963
|
-
ActionsMenu,
|
|
2964
|
-
ActionsMenuDropdown,
|
|
2965
|
-
computeIsNewTab,
|
|
2966
|
-
newTabAnchorAttrs,
|
|
2967
|
-
buildAnchorProps,
|
|
2968
|
-
EMPTY_AUTHOR_PLACEHOLDER,
|
|
2969
|
-
EntityMetadataValueCell,
|
|
2970
|
-
EntityMetadataAuthorCell,
|
|
2971
|
-
EntityAuthorCard,
|
|
2972
|
-
BlogImagePlaceholder,
|
|
2973
|
-
extractYouTubeId,
|
|
2974
|
-
Video,
|
|
2975
|
-
RATIO_GRID_CLASS,
|
|
2976
|
-
RATIO_DISPLAY_GRID_CLASS,
|
|
2977
|
-
RatioTabs,
|
|
2978
|
-
detectAspectRatio,
|
|
2979
|
-
ratioToCategory,
|
|
2980
|
-
groupByAspectRatio,
|
|
2981
|
-
VideoBitesDisplay,
|
|
2982
|
-
VideoBiteCard,
|
|
2983
|
-
EntityVideoSection,
|
|
2984
|
-
useEntityCardLink,
|
|
2985
|
-
useEntityCardPlaceholder,
|
|
2986
|
-
OnboardingGuideCardSkeleton,
|
|
2987
|
-
OnboardingGuideCard,
|
|
2988
|
-
PageActions,
|
|
2989
|
-
usePageActionsBottomPadding,
|
|
2990
|
-
BackButton,
|
|
2991
|
-
EntityImage,
|
|
2992
|
-
TitleBlock,
|
|
2993
|
-
PageLayout,
|
|
2994
|
-
PageShell,
|
|
2995
|
-
ArticleDetailLayout,
|
|
2996
|
-
ErrorState,
|
|
2997
|
-
PageError,
|
|
2998
|
-
LoadError,
|
|
2999
|
-
NotFoundError,
|
|
3000
|
-
TAG_BADGE_CLASS,
|
|
3001
|
-
EntityTagBadges,
|
|
3002
|
-
MUX_STREAM_ORIGIN,
|
|
3003
|
-
MUX_IMAGE_ORIGIN,
|
|
3004
|
-
useVideoOriginPreconnect,
|
|
3005
|
-
useVideoWarmup,
|
|
3006
|
-
getCaptionsUrl,
|
|
3007
|
-
FilterPillRow
|
|
3008
|
-
};
|
|
3009
|
-
//# sourceMappingURL=chunk-NSPOYUBH.js.map
|