@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-26PKDALD.js
DELETED
|
@@ -1,2379 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import {
|
|
3
|
-
scrollElementIntoView,
|
|
4
|
-
toolLabels
|
|
5
|
-
} from "./chunk-OQ6X7ZOC.js";
|
|
6
|
-
import {
|
|
7
|
-
BashIcon,
|
|
8
|
-
CmdIcon,
|
|
9
|
-
DenoIcon,
|
|
10
|
-
LinuxIcon,
|
|
11
|
-
MacOSIcon,
|
|
12
|
-
NushellIcon,
|
|
13
|
-
ShellIcon,
|
|
14
|
-
WindowsIcon
|
|
15
|
-
} from "./chunk-V4IIBNTA.js";
|
|
16
|
-
import {
|
|
17
|
-
cn,
|
|
18
|
-
init_cn,
|
|
19
|
-
init_platform_domains
|
|
20
|
-
} from "./chunk-XTCBRQN2.js";
|
|
21
|
-
import {
|
|
22
|
-
AppleLogoIcon,
|
|
23
|
-
LinuxLogoIcon,
|
|
24
|
-
PowershellLogoIcon,
|
|
25
|
-
PythonLogoIcon,
|
|
26
|
-
WindowsLogoGreyIcon
|
|
27
|
-
} from "./chunk-J7AV6H63.js";
|
|
28
|
-
|
|
29
|
-
// src/utils/ods-color-utils.ts
|
|
30
|
-
function getPlatformAccentColor(platform) {
|
|
31
|
-
const currentPlatform = platform || getCurrentPlatform();
|
|
32
|
-
const platformColors = {
|
|
33
|
-
"openmsp": "var(--ods-open-yellow-base)",
|
|
34
|
-
// CSS variable instead of hex
|
|
35
|
-
"openframe": "var(--ods-open-yellow-base)",
|
|
36
|
-
// CSS variable instead of hex
|
|
37
|
-
"flamingo": "var(--ods-flamingo-pink-base)"
|
|
38
|
-
// CSS variable instead of hex
|
|
39
|
-
};
|
|
40
|
-
return platformColors[currentPlatform];
|
|
41
|
-
}
|
|
42
|
-
function getCurrentPlatform() {
|
|
43
|
-
if (typeof window === "undefined") {
|
|
44
|
-
return process.env.NEXT_PUBLIC_APP_TYPE || "openmsp";
|
|
45
|
-
}
|
|
46
|
-
const domPlatform = document.documentElement.getAttribute("data-app-type");
|
|
47
|
-
if (domPlatform) {
|
|
48
|
-
return domPlatform;
|
|
49
|
-
}
|
|
50
|
-
return process.env.NEXT_PUBLIC_APP_TYPE || "openmsp";
|
|
51
|
-
}
|
|
52
|
-
function getReadableTextColor(hex) {
|
|
53
|
-
const n = parseInt(hex.replace("#", ""), 16);
|
|
54
|
-
const r = n >> 16 & 255;
|
|
55
|
-
const g = n >> 8 & 255;
|
|
56
|
-
const b = n & 255;
|
|
57
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
58
|
-
return luminance > 0.5 ? "#212121" : "#fafafa";
|
|
59
|
-
}
|
|
60
|
-
var HEX_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
61
|
-
function clamp(n, min, max) {
|
|
62
|
-
return Math.max(min, Math.min(max, n));
|
|
63
|
-
}
|
|
64
|
-
function hexToRgb(hex) {
|
|
65
|
-
if (!HEX_PATTERN.test(hex)) return null;
|
|
66
|
-
const n = Number.parseInt(hex.slice(1), 16);
|
|
67
|
-
return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
|
|
68
|
-
}
|
|
69
|
-
function rgbToHex(r, g, b) {
|
|
70
|
-
const to = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0");
|
|
71
|
-
return `#${to(r)}${to(g)}${to(b)}`;
|
|
72
|
-
}
|
|
73
|
-
var HOVER_DARKEN_STEP = 10;
|
|
74
|
-
var ACTIVE_DARKEN_STEP = 20;
|
|
75
|
-
function darkenByStep(hex, step) {
|
|
76
|
-
const rgb = hexToRgb(hex);
|
|
77
|
-
if (!rgb) return hex;
|
|
78
|
-
return rgbToHex(rgb.r - step, rgb.g - step, rgb.b - step);
|
|
79
|
-
}
|
|
80
|
-
function deriveHoverColor(hex) {
|
|
81
|
-
return darkenByStep(hex, HOVER_DARKEN_STEP);
|
|
82
|
-
}
|
|
83
|
-
function deriveActiveColor(hex) {
|
|
84
|
-
return darkenByStep(hex, ACTIVE_DARKEN_STEP);
|
|
85
|
-
}
|
|
86
|
-
function rgbToHsl(r, g, b) {
|
|
87
|
-
const rn = r / 255;
|
|
88
|
-
const gn = g / 255;
|
|
89
|
-
const bn = b / 255;
|
|
90
|
-
const max = Math.max(rn, gn, bn);
|
|
91
|
-
const min = Math.min(rn, gn, bn);
|
|
92
|
-
const l = (max + min) / 2;
|
|
93
|
-
if (max === min) return { h: 0, s: 0, l: Math.round(l * 100) };
|
|
94
|
-
const d = max - min;
|
|
95
|
-
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
96
|
-
let h;
|
|
97
|
-
if (max === rn) h = (gn - bn) / d + (gn < bn ? 6 : 0);
|
|
98
|
-
else if (max === gn) h = (bn - rn) / d + 2;
|
|
99
|
-
else h = (rn - gn) / d + 4;
|
|
100
|
-
h *= 60;
|
|
101
|
-
return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };
|
|
102
|
-
}
|
|
103
|
-
function hslToRgb(h, s, l) {
|
|
104
|
-
const sn = clamp(s, 0, 100) / 100;
|
|
105
|
-
const ln = clamp(l, 0, 100) / 100;
|
|
106
|
-
const hn = (h % 360 + 360) % 360;
|
|
107
|
-
if (sn === 0) {
|
|
108
|
-
const v = Math.round(ln * 255);
|
|
109
|
-
return { r: v, g: v, b: v };
|
|
110
|
-
}
|
|
111
|
-
const q = ln < 0.5 ? ln * (1 + sn) : ln + sn - ln * sn;
|
|
112
|
-
const p = 2 * ln - q;
|
|
113
|
-
const hue2rgb = (t) => {
|
|
114
|
-
let tn = t;
|
|
115
|
-
if (tn < 0) tn += 1;
|
|
116
|
-
if (tn > 1) tn -= 1;
|
|
117
|
-
if (tn < 1 / 6) return p + (q - p) * 6 * tn;
|
|
118
|
-
if (tn < 1 / 2) return q;
|
|
119
|
-
if (tn < 2 / 3) return p + (q - p) * (2 / 3 - tn) * 6;
|
|
120
|
-
return p;
|
|
121
|
-
};
|
|
122
|
-
const hk = hn / 360;
|
|
123
|
-
return {
|
|
124
|
-
r: Math.round(hue2rgb(hk + 1 / 3) * 255),
|
|
125
|
-
g: Math.round(hue2rgb(hk) * 255),
|
|
126
|
-
b: Math.round(hue2rgb(hk - 1 / 3) * 255)
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// src/components/chat/types/message.types.ts
|
|
131
|
-
var MESSAGE_TYPE = {
|
|
132
|
-
TEXT: "TEXT",
|
|
133
|
-
THINKING: "THINKING",
|
|
134
|
-
EXECUTING_TOOL: "EXECUTING_TOOL",
|
|
135
|
-
EXECUTED_TOOL: "EXECUTED_TOOL",
|
|
136
|
-
APPROVAL_REQUEST: "APPROVAL_REQUEST",
|
|
137
|
-
APPROVAL_RESULT: "APPROVAL_RESULT",
|
|
138
|
-
ERROR: "ERROR",
|
|
139
|
-
MESSAGE_START: "MESSAGE_START",
|
|
140
|
-
MESSAGE_END: "MESSAGE_END",
|
|
141
|
-
MESSAGE_REQUEST: "MESSAGE_REQUEST",
|
|
142
|
-
AI_METADATA: "AI_METADATA",
|
|
143
|
-
TOKEN_USAGE: "TOKEN_USAGE",
|
|
144
|
-
CONTEXT_COMPACTION_START: "CONTEXT_COMPACTION_START",
|
|
145
|
-
CONTEXT_COMPACTION_END: "CONTEXT_COMPACTION_END",
|
|
146
|
-
DIRECT_MESSAGE: "DIRECT_MESSAGE",
|
|
147
|
-
SYSTEM: "SYSTEM",
|
|
148
|
-
DIALOG_CLOSED: "DIALOG_CLOSED"
|
|
149
|
-
};
|
|
150
|
-
var SCROLL_ANCHOR = { TOP: "top", BOTTOM: "bottom" };
|
|
151
|
-
|
|
152
|
-
// src/components/chat/utils/chat-attachment-markdown.ts
|
|
153
|
-
var CHAT_ATTACHMENT_VIEW_URL_PREFIX = "/api/storage/view/chat-attachments/";
|
|
154
|
-
var CHAT_ATTACHMENT_VIEW_TOKEN_QUERY_PARAM = "t";
|
|
155
|
-
var ANTHROPIC_SUPPORTED_IMAGE_MIME = [
|
|
156
|
-
"image/jpeg",
|
|
157
|
-
"image/png",
|
|
158
|
-
"image/webp",
|
|
159
|
-
"image/gif"
|
|
160
|
-
];
|
|
161
|
-
function buildChatAttachmentViewUrl(viewUrlPrefix, storagePath, viewToken) {
|
|
162
|
-
return `${viewUrlPrefix}${storagePath}?${CHAT_ATTACHMENT_VIEW_TOKEN_QUERY_PARAM}=${encodeURIComponent(viewToken)}`;
|
|
163
|
-
}
|
|
164
|
-
function escapeMarkdownInline(text) {
|
|
165
|
-
return text.replace(/[[\]()\\\n\r<>"]/g, (ch) => {
|
|
166
|
-
switch (ch) {
|
|
167
|
-
case "\n":
|
|
168
|
-
return " ";
|
|
169
|
-
case "\r":
|
|
170
|
-
return "";
|
|
171
|
-
default:
|
|
172
|
-
return `\\${ch}`;
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
function formatChatAttachmentMarkdownForBubble(att, viewUrlPrefix) {
|
|
177
|
-
const safeName = escapeMarkdownInline(att.fileName);
|
|
178
|
-
const url = buildChatAttachmentViewUrl(viewUrlPrefix, att.storagePath, att.viewToken);
|
|
179
|
-
const isImage = ANTHROPIC_SUPPORTED_IMAGE_MIME.includes(att.contentType);
|
|
180
|
-
return isImage ? `
|
|
181
|
-
|
|
182
|
-
` : `
|
|
183
|
-
|
|
184
|
-
[Attached: ${safeName}](${url})`;
|
|
185
|
-
}
|
|
186
|
-
var CHAT_ATTACHMENT_VIEW_URL_PREFIX_REGEX_ESCAPED = CHAT_ATTACHMENT_VIEW_URL_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
187
|
-
var CHAT_ATTACHMENT_MARKDOWN_PATTERN = new RegExp(
|
|
188
|
-
`^\\s*!?\\[[^\\]]*\\]\\(${CHAT_ATTACHMENT_VIEW_URL_PREFIX_REGEX_ESCAPED}[^)]+\\)\\s*$`,
|
|
189
|
-
"gm"
|
|
190
|
-
);
|
|
191
|
-
function stripChatAttachmentMarkdown(text) {
|
|
192
|
-
const storagePaths = [];
|
|
193
|
-
const pathExtract = new RegExp(
|
|
194
|
-
`\\(${CHAT_ATTACHMENT_VIEW_URL_PREFIX_REGEX_ESCAPED}([^?)]+)`
|
|
195
|
-
);
|
|
196
|
-
const stripped = text.replace(CHAT_ATTACHMENT_MARKDOWN_PATTERN, (match) => {
|
|
197
|
-
const m = match.match(pathExtract);
|
|
198
|
-
if (m && m[1]) storagePaths.push(m[1]);
|
|
199
|
-
return "";
|
|
200
|
-
});
|
|
201
|
-
return {
|
|
202
|
-
stripped: stripped.replace(/\n{3,}/g, "\n\n").trim(),
|
|
203
|
-
storagePaths
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// src/utils/date-formatters.ts
|
|
208
|
-
var MONTHS_LONG = [
|
|
209
|
-
"January",
|
|
210
|
-
"February",
|
|
211
|
-
"March",
|
|
212
|
-
"April",
|
|
213
|
-
"May",
|
|
214
|
-
"June",
|
|
215
|
-
"July",
|
|
216
|
-
"August",
|
|
217
|
-
"September",
|
|
218
|
-
"October",
|
|
219
|
-
"November",
|
|
220
|
-
"December"
|
|
221
|
-
];
|
|
222
|
-
var MONTHS_SHORT = [
|
|
223
|
-
"Jan",
|
|
224
|
-
"Feb",
|
|
225
|
-
"Mar",
|
|
226
|
-
"Apr",
|
|
227
|
-
"May",
|
|
228
|
-
"Jun",
|
|
229
|
-
"Jul",
|
|
230
|
-
"Aug",
|
|
231
|
-
"Sep",
|
|
232
|
-
"Oct",
|
|
233
|
-
"Nov",
|
|
234
|
-
"Dec"
|
|
235
|
-
];
|
|
236
|
-
function splitYmd(dateString) {
|
|
237
|
-
const head = dateString.split("T")[0];
|
|
238
|
-
const parts = head.split("-");
|
|
239
|
-
if (parts.length !== 3) return null;
|
|
240
|
-
return [parts[0], parts[1], parts[2]];
|
|
241
|
-
}
|
|
242
|
-
function formatReleaseDate(dateString) {
|
|
243
|
-
const ymd = splitYmd(dateString);
|
|
244
|
-
if (!ymd) return dateString;
|
|
245
|
-
const [year, month, day] = ymd;
|
|
246
|
-
return `${MONTHS_LONG[parseInt(month) - 1]} ${parseInt(day)}, ${year}`;
|
|
247
|
-
}
|
|
248
|
-
function formatDateShort(dateString) {
|
|
249
|
-
const ymd = splitYmd(dateString);
|
|
250
|
-
if (!ymd) return dateString;
|
|
251
|
-
const [year, month, day] = ymd;
|
|
252
|
-
return `${MONTHS_SHORT[parseInt(month) - 1]} ${parseInt(day)}, ${year}`;
|
|
253
|
-
}
|
|
254
|
-
function formatDateSlashUTC(dateString) {
|
|
255
|
-
const ymd = splitYmd(dateString);
|
|
256
|
-
if (!ymd) return dateString;
|
|
257
|
-
const [year, month, day] = ymd;
|
|
258
|
-
return `${month}/${day}/${year}`;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/utils/release-cover.ts
|
|
262
|
-
function resolveReleaseCover(r) {
|
|
263
|
-
const cover = r.featured_image || r.highlight_video_thumbnail || r.main_video_thumbnail || r.og_image_url || null;
|
|
264
|
-
const hasVideoCover = !r.featured_image && !!(r.highlight_video_thumbnail || r.main_video_thumbnail);
|
|
265
|
-
return { cover, hasVideoCover };
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/utils/release-badge.ts
|
|
269
|
-
function releaseTypeToBadgeColor(t) {
|
|
270
|
-
switch (t) {
|
|
271
|
-
case "major":
|
|
272
|
-
return "error";
|
|
273
|
-
case "minor":
|
|
274
|
-
return "cyan";
|
|
275
|
-
case "patch":
|
|
276
|
-
return "success";
|
|
277
|
-
case "beta":
|
|
278
|
-
case "alpha":
|
|
279
|
-
return "warning";
|
|
280
|
-
default:
|
|
281
|
-
return void 0;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// src/components/chat/utils/agent-status-message.ts
|
|
286
|
-
function getStatusColorScheme(status) {
|
|
287
|
-
const s = status?.toLowerCase() || "";
|
|
288
|
-
switch (status) {
|
|
289
|
-
case "completed":
|
|
290
|
-
case "success":
|
|
291
|
-
case "active":
|
|
292
|
-
return "success";
|
|
293
|
-
case "failed":
|
|
294
|
-
case "failure":
|
|
295
|
-
case "cancelled":
|
|
296
|
-
case "error":
|
|
297
|
-
return "error";
|
|
298
|
-
case "running":
|
|
299
|
-
case "processing":
|
|
300
|
-
case "closed":
|
|
301
|
-
return "cyan";
|
|
302
|
-
case "pending":
|
|
303
|
-
case "warning":
|
|
304
|
-
return "warning";
|
|
305
|
-
case "draft":
|
|
306
|
-
// review cycles not yet opened to reviewers
|
|
307
|
-
case "info":
|
|
308
|
-
return "default";
|
|
309
|
-
}
|
|
310
|
-
if (s.includes("complete") || s.includes("done")) return "success";
|
|
311
|
-
if (s.includes("review")) return "cyan";
|
|
312
|
-
if (s.includes("working") || s.includes("progress")) return "warning";
|
|
313
|
-
if (s.includes("blocked") || s.includes("failed")) return "error";
|
|
314
|
-
return "default";
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// src/components/chat/utils/clickup-task-type-utils.ts
|
|
318
|
-
var CUSTOM_ITEM_ID = {
|
|
319
|
-
TASK: 1e3,
|
|
320
|
-
MILESTONE: 1001,
|
|
321
|
-
RECURRING: 1002,
|
|
322
|
-
SUBTASK: 1003,
|
|
323
|
-
FORM: 1004,
|
|
324
|
-
PLAN: 1006,
|
|
325
|
-
STRATEGY: 1007,
|
|
326
|
-
BUG: 1008,
|
|
327
|
-
REQUEST: 1009,
|
|
328
|
-
FEATURE: 1010,
|
|
329
|
-
STORY: 1011,
|
|
330
|
-
EPIC: 1012,
|
|
331
|
-
COMPONENT: 1013,
|
|
332
|
-
INITIATIVE: 1014
|
|
333
|
-
};
|
|
334
|
-
function getTaskTypeLabel(customItemId) {
|
|
335
|
-
switch (customItemId) {
|
|
336
|
-
case CUSTOM_ITEM_ID.TASK:
|
|
337
|
-
return "Task";
|
|
338
|
-
case CUSTOM_ITEM_ID.MILESTONE:
|
|
339
|
-
return "Milestone";
|
|
340
|
-
case CUSTOM_ITEM_ID.RECURRING:
|
|
341
|
-
return "Recurring";
|
|
342
|
-
case CUSTOM_ITEM_ID.SUBTASK:
|
|
343
|
-
return "Subtask";
|
|
344
|
-
case CUSTOM_ITEM_ID.FORM:
|
|
345
|
-
return "Form";
|
|
346
|
-
case CUSTOM_ITEM_ID.PLAN:
|
|
347
|
-
return "Plan";
|
|
348
|
-
case CUSTOM_ITEM_ID.STRATEGY:
|
|
349
|
-
return "Strategy";
|
|
350
|
-
case CUSTOM_ITEM_ID.BUG:
|
|
351
|
-
return "Bug";
|
|
352
|
-
case CUSTOM_ITEM_ID.REQUEST:
|
|
353
|
-
return "Request";
|
|
354
|
-
case CUSTOM_ITEM_ID.FEATURE:
|
|
355
|
-
return "Feature";
|
|
356
|
-
case CUSTOM_ITEM_ID.STORY:
|
|
357
|
-
return "Story";
|
|
358
|
-
case CUSTOM_ITEM_ID.EPIC:
|
|
359
|
-
return "Epic";
|
|
360
|
-
case CUSTOM_ITEM_ID.COMPONENT:
|
|
361
|
-
return "Component";
|
|
362
|
-
case CUSTOM_ITEM_ID.INITIATIVE:
|
|
363
|
-
return "Initiative";
|
|
364
|
-
default:
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// src/utils/extract-items.ts
|
|
370
|
-
function extractItems(data) {
|
|
371
|
-
if (!data || typeof data !== "object") return [];
|
|
372
|
-
const obj = data;
|
|
373
|
-
if (Array.isArray(obj.items)) return obj.items;
|
|
374
|
-
if (Array.isArray(obj.posts)) return obj.posts;
|
|
375
|
-
if (Array.isArray(obj.campaigns)) return obj.campaigns;
|
|
376
|
-
if (Array.isArray(obj.completed) || Array.isArray(obj.inProgress)) {
|
|
377
|
-
const completed = Array.isArray(obj.completed) ? obj.completed : [];
|
|
378
|
-
const inProgress = Array.isArray(obj.inProgress) ? obj.inProgress : [];
|
|
379
|
-
return [...completed, ...inProgress];
|
|
380
|
-
}
|
|
381
|
-
if (Array.isArray(obj.data)) return obj.data;
|
|
382
|
-
if (Array.isArray(data)) return data;
|
|
383
|
-
return [];
|
|
384
|
-
}
|
|
385
|
-
function extractItemId(type, item) {
|
|
386
|
-
if (!item || typeof item !== "object") return null;
|
|
387
|
-
const obj = item;
|
|
388
|
-
if (type === "roadmap_item" || type === "delivery_item" || type === "internal_task") {
|
|
389
|
-
const ext = obj.external_id;
|
|
390
|
-
if (typeof ext === "string") return ext;
|
|
391
|
-
}
|
|
392
|
-
const id = obj.id;
|
|
393
|
-
if (typeof id === "string") return id;
|
|
394
|
-
if (typeof id === "number") return String(id);
|
|
395
|
-
return null;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// src/components/chat/utils/scroll-anchor.ts
|
|
399
|
-
var SCROLL_ANCHOR_WIRE_KEY = "scrollAnchor";
|
|
400
|
-
function parseScrollAnchor(raw) {
|
|
401
|
-
return raw === SCROLL_ANCHOR.TOP || raw === SCROLL_ANCHOR.BOTTOM ? raw : null;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// src/components/chat/utils/auto-continuation-directive.ts
|
|
405
|
-
var AUTO_CONTINUATION_DIRECTIVE_PREFIX = "[internal-auto-continuation]";
|
|
406
|
-
function buildAutoContinuationDirective(toolName, opts = {}) {
|
|
407
|
-
const ticketRef = opts.ticketId ? ` (ticket #${opts.ticketId})` : "";
|
|
408
|
-
const ticketHash = opts.ticketId ? ` #${opts.ticketId}` : "";
|
|
409
|
-
const isClose = toolName === "update_ticket" && opts.status?.toUpperCase() === "CLOSED";
|
|
410
|
-
if (toolName === "create_ticket") {
|
|
411
|
-
return `${AUTO_CONTINUATION_DIRECTIVE_PREFIX} The user just approved create_ticket${ticketRef}. Per ticket-protocol \xA71a, ask 2-4 SHORT, issue-specific diagnostic follow-up questions tailored to the symptom they reported in their original message. When they answer, propose an update_ticket with content_addendum carrying a clean Q&A digest. Do NOT call any tool in this turn \u2014 just write the questions as prose.`;
|
|
412
|
-
}
|
|
413
|
-
if (isClose) {
|
|
414
|
-
return `${AUTO_CONTINUATION_DIRECTIVE_PREFIX} The user just approved closing ticket${ticketHash}. Per ticket-protocol \xA71b, ask ONE short question: "How was this resolved? I'll add it to the ticket as a closing note." Phrase kindly even if the user was curt. When they answer, propose an update_ticket with content_addendum="[Resolution] <their words>". Do NOT call any tool in this turn \u2014 just write the prose ask.`;
|
|
415
|
-
}
|
|
416
|
-
return `${AUTO_CONTINUATION_DIRECTIVE_PREFIX} The user just approved update_ticket${ticketHash}. Acknowledge the change in ONE short sentence; if anything else looks like it needs attention, ask. Do NOT call any tool in this turn.`;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// src/components/chat/utils/flatten-assistant-content.ts
|
|
420
|
-
function flattenAssistantContent(raw) {
|
|
421
|
-
if (typeof raw === "string") return raw;
|
|
422
|
-
if (!Array.isArray(raw)) return "";
|
|
423
|
-
const parts = [];
|
|
424
|
-
for (const seg of raw) {
|
|
425
|
-
if (seg && typeof seg === "object" && seg.type === "text") {
|
|
426
|
-
const t = seg.text;
|
|
427
|
-
if (typeof t === "string" && t.length > 0) parts.push(t);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return parts.join("\n\n");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// src/components/chat/utils/slash-dispatch-utils.ts
|
|
434
|
-
var WIRE_TABLE_ID_REGEX = /^[a-z][a-z0-9-]*$/;
|
|
435
|
-
var WIRE_ID_REGEX = /^[A-Za-z0-9._:/-]+$/;
|
|
436
|
-
var WIRE_TABLE_ID_MAX = 60;
|
|
437
|
-
var WIRE_ID_MAX = 200;
|
|
438
|
-
var WIRE_QUERY_MAX = 2e3;
|
|
439
|
-
function parseWireCommandOverride(raw) {
|
|
440
|
-
if (!raw || typeof raw !== "object") return null;
|
|
441
|
-
const out = {};
|
|
442
|
-
const r = raw;
|
|
443
|
-
if (r.entityIdFilter && typeof r.entityIdFilter === "object") {
|
|
444
|
-
const f = r.entityIdFilter;
|
|
445
|
-
if (typeof f.tableId === "string" && f.tableId.length > 0 && f.tableId.length <= WIRE_TABLE_ID_MAX && WIRE_TABLE_ID_REGEX.test(f.tableId) && typeof f.id === "string" && f.id.length > 0 && f.id.length <= WIRE_ID_MAX && WIRE_ID_REGEX.test(f.id)) {
|
|
446
|
-
out.entityIdFilter = { tableId: f.tableId, id: f.id };
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (typeof r.retrievalQueryOverride === "string" && r.retrievalQueryOverride.length > 0 && r.retrievalQueryOverride.length <= WIRE_QUERY_MAX) {
|
|
450
|
-
out.retrievalQueryOverride = r.retrievalQueryOverride;
|
|
451
|
-
}
|
|
452
|
-
if (out.entityIdFilter === void 0 && out.retrievalQueryOverride === void 0) {
|
|
453
|
-
return null;
|
|
454
|
-
}
|
|
455
|
-
return out;
|
|
456
|
-
}
|
|
457
|
-
function sanitizeTitleForChat(value) {
|
|
458
|
-
return (value ?? "").replace(/[\r\n\t]+/g, " ").trim();
|
|
459
|
-
}
|
|
460
|
-
function formatSingularLookupInvocation(cmdId, value) {
|
|
461
|
-
const safe = sanitizeTitleForChat(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
462
|
-
return safe ? `/${cmdId} "${safe}"` : `/${cmdId}`;
|
|
463
|
-
}
|
|
464
|
-
function extractEntityIdFilter(override, expectedTableId) {
|
|
465
|
-
const f = override?.entityIdFilter;
|
|
466
|
-
if (!f) return null;
|
|
467
|
-
const tableId = typeof f.tableId === "string" ? f.tableId : "";
|
|
468
|
-
const id = typeof f.id === "string" ? f.id : "";
|
|
469
|
-
if (!tableId || !id) return null;
|
|
470
|
-
if (expectedTableId && tableId !== expectedTableId) return null;
|
|
471
|
-
return { tableId, id };
|
|
472
|
-
}
|
|
473
|
-
function buildDiscussAddendum(args) {
|
|
474
|
-
const idTag = args.id.length > 24 ? args.id.slice(0, 24) + "\u2026" : args.id;
|
|
475
|
-
return `Ask drill-in for row id="${args.id}" in table "${args.tableId}". Focus the answer on this single record. If retrieval returns 0 rows (privacy filter), say so plainly per Rule 5b.
|
|
476
|
-
|
|
477
|
-
OPEN with the inline card on the FIRST line, immediately followed by the chip and a mono-font id tag \u2014 no prose before the card:
|
|
478
|
-
[card://<type>:${args.id}] [N] \`${args.tableId} \xB7 ${idTag}\`
|
|
479
|
-
Use the EXACT <type> from the row's <document type="..." id="${args.id}"> tag.
|
|
480
|
-
|
|
481
|
-
SURFACE EVERY FIELD PRESENT in the retrieved record \u2014 the user already knows the title from the card; the value of Ask is the BODY (description, status, vote counts, dates, linked URLs, \u2026). Walk each non-empty field. Don't invent fields, don't hallucinate values, silently omit absent fields.
|
|
482
|
-
|
|
483
|
-
FORMAT: card+chip+id-tag opener, then ONE framing sentence, then a compact bulleted "**Field** \u2014 value" list. Don't restate fields already in the list.`;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// src/components/chat/utils/external-app-urls.ts
|
|
487
|
-
var CLICKUP_APP_BASE = "https://app.clickup.com";
|
|
488
|
-
function clickupTaskUrl(externalId) {
|
|
489
|
-
if (!externalId) return null;
|
|
490
|
-
return `${CLICKUP_APP_BASE}/t/${externalId}`;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// src/utils/common.ts
|
|
494
|
-
import { clsx } from "clsx";
|
|
495
|
-
import { twMerge } from "tailwind-merge";
|
|
496
|
-
function cn2(...inputs) {
|
|
497
|
-
return twMerge(clsx(inputs));
|
|
498
|
-
}
|
|
499
|
-
function delay(ms) {
|
|
500
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
501
|
-
}
|
|
502
|
-
function generateRandomString(length = 8) {
|
|
503
|
-
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
504
|
-
let result = "";
|
|
505
|
-
for (let i = 0; i < length; i++) {
|
|
506
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
507
|
-
}
|
|
508
|
-
return result;
|
|
509
|
-
}
|
|
510
|
-
function truncateString(str, maxLength, suffix = "...") {
|
|
511
|
-
if (str.length <= maxLength) return str;
|
|
512
|
-
return str.substring(0, maxLength - suffix.length) + suffix;
|
|
513
|
-
}
|
|
514
|
-
function serializeJsonLd(schema) {
|
|
515
|
-
return JSON.stringify(schema).replace(/</g, "\\u003c");
|
|
516
|
-
}
|
|
517
|
-
function deepClone(obj) {
|
|
518
|
-
return JSON.parse(JSON.stringify(obj));
|
|
519
|
-
}
|
|
520
|
-
function getSlackCommunityJoinUrl() {
|
|
521
|
-
const url = process.env.NEXT_PUBLIC_SLACK_COMMUNITY_JOIN_URL;
|
|
522
|
-
if (!url) {
|
|
523
|
-
console.warn("NEXT_PUBLIC_SLACK_COMMUNITY_JOIN_URL is not defined in environment variables");
|
|
524
|
-
return "https://join.slack.com/t/openmsp/shared_invite/zt-36bl7mx0h-3~U2nFH6nqHqoTPXMaHEHA";
|
|
525
|
-
}
|
|
526
|
-
return url;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// src/utils/os-platforms.ts
|
|
530
|
-
var OS_PLATFORMS = [
|
|
531
|
-
{ id: "windows", name: "Windows", icon: WindowsIcon },
|
|
532
|
-
{ id: "linux", name: "Linux", icon: LinuxIcon },
|
|
533
|
-
{ id: "darwin", name: "MacOS", icon: MacOSIcon }
|
|
534
|
-
];
|
|
535
|
-
var DEFAULT_OS_PLATFORM = "windows";
|
|
536
|
-
|
|
537
|
-
// src/utils/validation-utils.ts
|
|
538
|
-
function isValidEmailDomain(domain) {
|
|
539
|
-
if (!domain || typeof domain !== "string") {
|
|
540
|
-
return false;
|
|
541
|
-
}
|
|
542
|
-
const trimmed = domain.trim();
|
|
543
|
-
if (!trimmed) {
|
|
544
|
-
return false;
|
|
545
|
-
}
|
|
546
|
-
const domainRegex = /^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z0-9-]{1,63})*\.[A-Za-z]{2,}$/;
|
|
547
|
-
return domainRegex.test(trimmed);
|
|
548
|
-
}
|
|
549
|
-
function validateEmailDomain(domain) {
|
|
550
|
-
if (!domain || typeof domain !== "string") {
|
|
551
|
-
return { valid: false, error: "Email domain is required" };
|
|
552
|
-
}
|
|
553
|
-
let trimmed = domain.trim();
|
|
554
|
-
if (trimmed.startsWith("@")) {
|
|
555
|
-
trimmed = trimmed.substring(1);
|
|
556
|
-
}
|
|
557
|
-
if (!trimmed) {
|
|
558
|
-
return { valid: false, error: "Email domain is required" };
|
|
559
|
-
}
|
|
560
|
-
if (trimmed.includes("://")) {
|
|
561
|
-
return { valid: false, error: "Enter email domain only (e.g., openframe.ai), not a URL" };
|
|
562
|
-
}
|
|
563
|
-
if (trimmed.includes("@")) {
|
|
564
|
-
return { valid: false, error: "Enter domain only, not a full email address" };
|
|
565
|
-
}
|
|
566
|
-
if (trimmed.includes(" ")) {
|
|
567
|
-
return { valid: false, error: "Domain cannot contain spaces" };
|
|
568
|
-
}
|
|
569
|
-
if (trimmed.includes("/")) {
|
|
570
|
-
return { valid: false, error: "Enter domain only, without paths" };
|
|
571
|
-
}
|
|
572
|
-
if (!isValidEmailDomain(trimmed)) {
|
|
573
|
-
return { valid: false, error: "Invalid domain format (e.g., openframe.ai)" };
|
|
574
|
-
}
|
|
575
|
-
return { valid: true, cleanedDomain: trimmed.toLowerCase() };
|
|
576
|
-
}
|
|
577
|
-
function validateEmailDomainList(domains) {
|
|
578
|
-
if (!Array.isArray(domains)) {
|
|
579
|
-
return { valid: false, error: "Domains must be an array" };
|
|
580
|
-
}
|
|
581
|
-
const cleanedDomains = [];
|
|
582
|
-
const seenDomains = /* @__PURE__ */ new Set();
|
|
583
|
-
for (const domain of domains) {
|
|
584
|
-
const result = validateEmailDomain(domain);
|
|
585
|
-
if (!result.valid) {
|
|
586
|
-
return { valid: false, error: `Invalid domain "${domain}": ${result.error}` };
|
|
587
|
-
}
|
|
588
|
-
const cleanedDomain = result.cleanedDomain;
|
|
589
|
-
if (seenDomains.has(cleanedDomain)) {
|
|
590
|
-
return { valid: false, error: `Duplicate domain: ${cleanedDomain}` };
|
|
591
|
-
}
|
|
592
|
-
seenDomains.add(cleanedDomain);
|
|
593
|
-
cleanedDomains.push(cleanedDomain);
|
|
594
|
-
}
|
|
595
|
-
return { valid: true, cleanedDomains };
|
|
596
|
-
}
|
|
597
|
-
function cleanEmailDomain(domain) {
|
|
598
|
-
if (!domain || typeof domain !== "string") {
|
|
599
|
-
return "";
|
|
600
|
-
}
|
|
601
|
-
let cleaned = domain.trim().toLowerCase();
|
|
602
|
-
if (cleaned.startsWith("@")) {
|
|
603
|
-
cleaned = cleaned.substring(1);
|
|
604
|
-
}
|
|
605
|
-
cleaned = cleaned.replace(/^https?:\/\//, "");
|
|
606
|
-
cleaned = cleaned.split("/")[0];
|
|
607
|
-
cleaned = cleaned.replace(/^www\./, "");
|
|
608
|
-
return cleaned;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// src/utils/confidence-helpers.ts
|
|
612
|
-
function getConfidenceColorClass(confidence) {
|
|
613
|
-
if (!confidence && confidence !== 0) return "";
|
|
614
|
-
if (confidence >= 80) return "border-green-500 text-green-500";
|
|
615
|
-
if (confidence >= 50) return "border-yellow-500 text-yellow-500";
|
|
616
|
-
return "border-red-500 text-red-500";
|
|
617
|
-
}
|
|
618
|
-
function getConfidenceLevel(confidence) {
|
|
619
|
-
if (!confidence && confidence !== 0) return "none";
|
|
620
|
-
if (confidence >= 80) return "high";
|
|
621
|
-
if (confidence >= 50) return "medium";
|
|
622
|
-
return "low";
|
|
623
|
-
}
|
|
624
|
-
function getConfidenceBorderClass(confidence) {
|
|
625
|
-
if (!confidence && confidence !== 0) return "";
|
|
626
|
-
if (confidence >= 80) return "border-green-500";
|
|
627
|
-
if (confidence >= 50) return "border-yellow-500";
|
|
628
|
-
return "border-red-500";
|
|
629
|
-
}
|
|
630
|
-
function getConfidenceTextClass(confidence) {
|
|
631
|
-
if (!confidence && confidence !== 0) return "";
|
|
632
|
-
if (confidence >= 80) return "text-green-500";
|
|
633
|
-
if (confidence >= 50) return "text-yellow-500";
|
|
634
|
-
return "text-red-500";
|
|
635
|
-
}
|
|
636
|
-
function getConfidenceBgClass(confidence) {
|
|
637
|
-
if (!confidence && confidence !== 0) return "";
|
|
638
|
-
if (confidence >= 80) return "bg-green-500/10";
|
|
639
|
-
if (confidence >= 50) return "bg-yellow-500/10";
|
|
640
|
-
return "bg-red-500/10";
|
|
641
|
-
}
|
|
642
|
-
function getConfidenceLabel(confidence) {
|
|
643
|
-
if (!confidence && confidence !== 0) return "Unknown";
|
|
644
|
-
if (confidence >= 80) return "High";
|
|
645
|
-
if (confidence >= 50) return "Medium";
|
|
646
|
-
return "Low";
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// src/utils/dev-sections/dev-section-param-keys.ts
|
|
650
|
-
var DEV_SECTION_PARAM_KEYS = {
|
|
651
|
-
/** Free-text search box — shared by every dev-center section. */
|
|
652
|
-
search: "search",
|
|
653
|
-
/** Roadmap (and Help Center tickets) status filter. */
|
|
654
|
-
status: "status",
|
|
655
|
-
/** Product-releases stability-tier filter. */
|
|
656
|
-
releaseStatus: "release_status",
|
|
657
|
-
/** Delivery (bug-fix / enhancement) task-type filter. */
|
|
658
|
-
deliveryTaskType: "task_type"
|
|
659
|
-
};
|
|
660
|
-
function devSectionAnchorId(section, id) {
|
|
661
|
-
return `${section}-${id}`;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// src/utils/tool-utils.ts
|
|
665
|
-
var toolAliasMap = {
|
|
666
|
-
// Tactical RMM
|
|
667
|
-
"TACTICAL": "TACTICAL_RMM",
|
|
668
|
-
"TACTICAL_RMM": "TACTICAL_RMM",
|
|
669
|
-
"TACTICAL-RMM": "TACTICAL_RMM",
|
|
670
|
-
"TACTICALRMM": "TACTICAL_RMM",
|
|
671
|
-
"tactical": "TACTICAL_RMM",
|
|
672
|
-
"tactical_rmm": "TACTICAL_RMM",
|
|
673
|
-
"tactical-rmm": "TACTICAL_RMM",
|
|
674
|
-
"tacticalrmm": "TACTICAL_RMM",
|
|
675
|
-
"tacticalrmm-agent": "TACTICAL_RMM",
|
|
676
|
-
// Fleet MDM
|
|
677
|
-
"FLEET": "FLEET_MDM",
|
|
678
|
-
"FLEET_MDM": "FLEET_MDM",
|
|
679
|
-
"FLEET-MDM": "FLEET_MDM",
|
|
680
|
-
"FLEETMDM": "FLEET_MDM",
|
|
681
|
-
"fleet": "FLEET_MDM",
|
|
682
|
-
"fleet_mdm": "FLEET_MDM",
|
|
683
|
-
"fleet-mdm": "FLEET_MDM",
|
|
684
|
-
"fleetmdm": "FLEET_MDM",
|
|
685
|
-
"fleetmdm-agent": "FLEET_MDM",
|
|
686
|
-
// MeshCentral
|
|
687
|
-
"MESHCENTRAL": "MESHCENTRAL",
|
|
688
|
-
"MESH": "MESHCENTRAL",
|
|
689
|
-
"MESH_CENTRAL": "MESHCENTRAL",
|
|
690
|
-
"MESH-CENTRAL": "MESHCENTRAL",
|
|
691
|
-
"mesh": "MESHCENTRAL",
|
|
692
|
-
"meshcentral": "MESHCENTRAL",
|
|
693
|
-
"mesh_central": "MESHCENTRAL",
|
|
694
|
-
"mesh-central": "MESHCENTRAL",
|
|
695
|
-
"meshcentral-agent": "MESHCENTRAL",
|
|
696
|
-
// Authentik
|
|
697
|
-
"AUTHENTIK": "AUTHENTIK",
|
|
698
|
-
"authentik": "AUTHENTIK",
|
|
699
|
-
// OpenFrame
|
|
700
|
-
"OPENFRAME": "OPENFRAME",
|
|
701
|
-
"openframe": "OPENFRAME",
|
|
702
|
-
"OPEN_FRAME": "OPENFRAME",
|
|
703
|
-
"OPEN-FRAME": "OPENFRAME",
|
|
704
|
-
"open_frame": "OPENFRAME",
|
|
705
|
-
"open-frame": "OPENFRAME",
|
|
706
|
-
// OpenFrame Chat
|
|
707
|
-
"OPENFRAME_CHAT": "OPENFRAME_CHAT",
|
|
708
|
-
"OPENFRAME-CHAT": "OPENFRAME_CHAT",
|
|
709
|
-
"OPENFRAMECHAT": "OPENFRAME_CHAT",
|
|
710
|
-
"openframe_chat": "OPENFRAME_CHAT",
|
|
711
|
-
"openframe-chat": "OPENFRAME_CHAT",
|
|
712
|
-
"openframechat": "OPENFRAME_CHAT",
|
|
713
|
-
// OpenFrame Client
|
|
714
|
-
"OPENFRAME_CLIENT": "OPENFRAME_CLIENT",
|
|
715
|
-
"OPENFRAME-CLIENT": "OPENFRAME_CLIENT",
|
|
716
|
-
"OPENFRAMECLIENT": "OPENFRAME_CLIENT",
|
|
717
|
-
"openframe_client": "OPENFRAME_CLIENT",
|
|
718
|
-
"openframe-client": "OPENFRAME_CLIENT",
|
|
719
|
-
"openframeclient": "OPENFRAME_CLIENT",
|
|
720
|
-
// OSQUERY
|
|
721
|
-
"OSQUERY": "OSQUERY",
|
|
722
|
-
// System
|
|
723
|
-
"SYSTEM": "SYSTEM",
|
|
724
|
-
"system": "SYSTEM"
|
|
725
|
-
};
|
|
726
|
-
function normalizeToolType(input) {
|
|
727
|
-
if (!input) return void 0;
|
|
728
|
-
const exact = toolAliasMap[input];
|
|
729
|
-
if (exact) return exact;
|
|
730
|
-
const upper = input.toUpperCase();
|
|
731
|
-
if (toolAliasMap[upper]) return toolAliasMap[upper];
|
|
732
|
-
const lower = input.toLowerCase();
|
|
733
|
-
if (toolAliasMap[lower]) return toolAliasMap[lower];
|
|
734
|
-
return void 0;
|
|
735
|
-
}
|
|
736
|
-
function normalizeToolTypeWithFallback(input) {
|
|
737
|
-
return normalizeToolType(input) ?? "SYSTEM";
|
|
738
|
-
}
|
|
739
|
-
function toToolLabel(input) {
|
|
740
|
-
if (!input) return "";
|
|
741
|
-
const toolType = normalizeToolType(input);
|
|
742
|
-
if (toolType) {
|
|
743
|
-
return toolLabels[toolType];
|
|
744
|
-
}
|
|
745
|
-
return input;
|
|
746
|
-
}
|
|
747
|
-
function isValidToolType(input) {
|
|
748
|
-
return normalizeToolType(input) !== void 0;
|
|
749
|
-
}
|
|
750
|
-
function getToolTypeAliases(toolType) {
|
|
751
|
-
return Object.entries(toolAliasMap).filter(([_, value]) => value === toolType).map(([key]) => key);
|
|
752
|
-
}
|
|
753
|
-
function getToolLabel(toolType) {
|
|
754
|
-
return toolLabels[toolType] || toolType;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
// src/types/shell.types.ts
|
|
758
|
-
var ShellTypeValues = {
|
|
759
|
-
POWERSHELL: "POWERSHELL",
|
|
760
|
-
CMD: "CMD",
|
|
761
|
-
BASH: "BASH",
|
|
762
|
-
PYTHON: "PYTHON",
|
|
763
|
-
NUSHELL: "NUSHELL",
|
|
764
|
-
DENO: "DENO",
|
|
765
|
-
SHELL: "SHELL"
|
|
766
|
-
};
|
|
767
|
-
var SHELL_TYPES = [
|
|
768
|
-
{ id: ShellTypeValues.POWERSHELL, label: "PowerShell", value: "powershell", icon: PowershellLogoIcon },
|
|
769
|
-
{ id: ShellTypeValues.CMD, label: "Batch", value: "cmd", icon: CmdIcon },
|
|
770
|
-
{ id: ShellTypeValues.BASH, label: "Bash", value: "bash", icon: BashIcon },
|
|
771
|
-
{ id: ShellTypeValues.PYTHON, label: "Python", value: "python", icon: PythonLogoIcon },
|
|
772
|
-
{ id: ShellTypeValues.NUSHELL, label: "Nu", value: "nushell", icon: NushellIcon },
|
|
773
|
-
{ id: ShellTypeValues.DENO, label: "Deno", value: "deno", icon: DenoIcon },
|
|
774
|
-
{ id: ShellTypeValues.SHELL, label: "Shell", value: "shell", icon: ShellIcon }
|
|
775
|
-
];
|
|
776
|
-
var shellLabels = {
|
|
777
|
-
[ShellTypeValues.POWERSHELL]: "PowerShell",
|
|
778
|
-
[ShellTypeValues.CMD]: "Batch",
|
|
779
|
-
[ShellTypeValues.BASH]: "Bash",
|
|
780
|
-
[ShellTypeValues.PYTHON]: "Python",
|
|
781
|
-
[ShellTypeValues.NUSHELL]: "Nu",
|
|
782
|
-
[ShellTypeValues.DENO]: "Deno",
|
|
783
|
-
[ShellTypeValues.SHELL]: "Shell"
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
// src/utils/shell-utils.ts
|
|
787
|
-
function getShellLabel(shellType) {
|
|
788
|
-
if (!shellType) return "Unknown";
|
|
789
|
-
const normalized = shellType.toUpperCase();
|
|
790
|
-
return shellLabels[normalized] || shellType;
|
|
791
|
-
}
|
|
792
|
-
function getShellIcon(shellType) {
|
|
793
|
-
if (!shellType) return void 0;
|
|
794
|
-
const normalized = shellType.toUpperCase();
|
|
795
|
-
const shellDef = SHELL_TYPES.find((s) => s.id === normalized);
|
|
796
|
-
return shellDef?.icon;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// src/types/os.types.ts
|
|
800
|
-
var OSTypeValues = {
|
|
801
|
-
WINDOWS: "WINDOWS",
|
|
802
|
-
MACOS: "MACOS",
|
|
803
|
-
LINUX: "LINUX"
|
|
804
|
-
};
|
|
805
|
-
var OS_TYPES = [
|
|
806
|
-
{
|
|
807
|
-
id: OSTypeValues.MACOS,
|
|
808
|
-
label: "macOS",
|
|
809
|
-
value: OSTypeValues.MACOS,
|
|
810
|
-
icon: AppleLogoIcon,
|
|
811
|
-
platformId: "darwin",
|
|
812
|
-
aliases: ["darwin", "macos", "mac os", "osx", "os x", "mac"]
|
|
813
|
-
// Put more specific ones first, 'mac' last to avoid false matches
|
|
814
|
-
},
|
|
815
|
-
{
|
|
816
|
-
id: OSTypeValues.WINDOWS,
|
|
817
|
-
label: "Windows",
|
|
818
|
-
value: OSTypeValues.WINDOWS,
|
|
819
|
-
icon: WindowsLogoGreyIcon,
|
|
820
|
-
platformId: "windows",
|
|
821
|
-
aliases: ["windows", "win32", "win64", "win"]
|
|
822
|
-
// 'win' last since it's shortest
|
|
823
|
-
},
|
|
824
|
-
{
|
|
825
|
-
id: OSTypeValues.LINUX,
|
|
826
|
-
label: "Linux",
|
|
827
|
-
value: OSTypeValues.LINUX,
|
|
828
|
-
icon: LinuxLogoIcon,
|
|
829
|
-
platformId: "linux",
|
|
830
|
-
aliases: ["linux", "ubuntu", "debian", "centos", "redhat", "fedora", "pop", "pop!_os", "arch", "manjaro"]
|
|
831
|
-
}
|
|
832
|
-
];
|
|
833
|
-
var osLabels = {
|
|
834
|
-
[OSTypeValues.WINDOWS]: "Windows",
|
|
835
|
-
[OSTypeValues.MACOS]: "macOS",
|
|
836
|
-
[OSTypeValues.LINUX]: "Linux"
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
// src/utils/os-utils.ts
|
|
840
|
-
function normalizeOSType(osType) {
|
|
841
|
-
if (!osType) return void 0;
|
|
842
|
-
const normalized = osType.toLowerCase().trim();
|
|
843
|
-
for (const osTypeDef of OS_TYPES) {
|
|
844
|
-
if (osTypeDef.aliases.some((alias) => {
|
|
845
|
-
if (normalized === alias) return true;
|
|
846
|
-
const wordBoundaryRegex = new RegExp(`\\b${alias}\\b`, "i");
|
|
847
|
-
return wordBoundaryRegex.test(osType);
|
|
848
|
-
})) {
|
|
849
|
-
return osTypeDef.id;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
for (const osTypeDef of OS_TYPES) {
|
|
853
|
-
if (osTypeDef.aliases.some((alias) => normalized.includes(alias))) {
|
|
854
|
-
return osTypeDef.id;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
return void 0;
|
|
858
|
-
}
|
|
859
|
-
function getOSLabel(osType) {
|
|
860
|
-
if (!osType) return "Unknown";
|
|
861
|
-
const normalized = normalizeOSType(osType);
|
|
862
|
-
return normalized ? osLabels[normalized] : osType;
|
|
863
|
-
}
|
|
864
|
-
function getOSIcon(osType) {
|
|
865
|
-
if (!osType) return void 0;
|
|
866
|
-
const normalized = normalizeOSType(osType);
|
|
867
|
-
if (!normalized) return void 0;
|
|
868
|
-
const osTypeDef = OS_TYPES.find((t) => t.id === normalized);
|
|
869
|
-
return osTypeDef?.icon;
|
|
870
|
-
}
|
|
871
|
-
function getOSTypeDefinition(osType) {
|
|
872
|
-
if (!osType) return void 0;
|
|
873
|
-
const normalized = normalizeOSType(osType);
|
|
874
|
-
if (!normalized) return void 0;
|
|
875
|
-
return OS_TYPES.find((t) => t.id === normalized);
|
|
876
|
-
}
|
|
877
|
-
function getOSPlatformId(osType) {
|
|
878
|
-
const osTypeDef = getOSTypeDefinition(osType);
|
|
879
|
-
return osTypeDef?.platformId;
|
|
880
|
-
}
|
|
881
|
-
function isOSPlatform(deviceOS, targetPlatform) {
|
|
882
|
-
if (!deviceOS || !targetPlatform) return false;
|
|
883
|
-
const platformId = getOSPlatformId(deviceOS);
|
|
884
|
-
return platformId === targetPlatform;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// src/utils/country-phone-utils.ts
|
|
888
|
-
import { getCountries, getCountryCallingCode, isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js";
|
|
889
|
-
var PRIORITY_CODES = ["US", "CA", "GB", "AU"];
|
|
890
|
-
function countryCodeToFlag(code) {
|
|
891
|
-
return code.toUpperCase().split("").map((char) => String.fromCodePoint(127462 + char.charCodeAt(0) - 65)).join("");
|
|
892
|
-
}
|
|
893
|
-
function buildCountryData() {
|
|
894
|
-
const displayNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
895
|
-
const allCodes = getCountries();
|
|
896
|
-
const toData = (code) => ({
|
|
897
|
-
code,
|
|
898
|
-
name: displayNames.of(code) || code,
|
|
899
|
-
dialCode: `+${getCountryCallingCode(code)}`,
|
|
900
|
-
flag: countryCodeToFlag(code)
|
|
901
|
-
});
|
|
902
|
-
const priority = PRIORITY_CODES.map(toData);
|
|
903
|
-
const prioritySet = new Set(PRIORITY_CODES);
|
|
904
|
-
const others = allCodes.filter((c) => !prioritySet.has(c)).map(toData).sort((a, b) => a.name.localeCompare(b.name));
|
|
905
|
-
return { priority, others };
|
|
906
|
-
}
|
|
907
|
-
var _cache = null;
|
|
908
|
-
function getCountryPhoneData() {
|
|
909
|
-
if (!_cache) {
|
|
910
|
-
_cache = buildCountryData();
|
|
911
|
-
}
|
|
912
|
-
return _cache;
|
|
913
|
-
}
|
|
914
|
-
function getCountryByCode(code) {
|
|
915
|
-
const { priority, others } = getCountryPhoneData();
|
|
916
|
-
return priority.find((c) => c.code === code) || others.find((c) => c.code === code);
|
|
917
|
-
}
|
|
918
|
-
function validatePhoneNumber(phoneNumber, countryCode) {
|
|
919
|
-
if (!phoneNumber.trim()) return true;
|
|
920
|
-
return isValidPhoneNumber(phoneNumber, countryCode);
|
|
921
|
-
}
|
|
922
|
-
function formatPhoneE164(phoneNumber, countryCode) {
|
|
923
|
-
try {
|
|
924
|
-
const parsed = parsePhoneNumber(phoneNumber, countryCode);
|
|
925
|
-
if (parsed && parsed.isValid()) {
|
|
926
|
-
return parsed.format("E.164");
|
|
927
|
-
}
|
|
928
|
-
} catch {
|
|
929
|
-
}
|
|
930
|
-
const dialCode = `+${getCountryCallingCode(countryCode)}`;
|
|
931
|
-
const digits = phoneNumber.replace(/\D/g, "");
|
|
932
|
-
return `${dialCode}${digits}`;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// src/utils/generic-domain-utils.ts
|
|
936
|
-
var GENERIC_EMAIL_DOMAINS = [
|
|
937
|
-
// Major providers
|
|
938
|
-
"gmail.com",
|
|
939
|
-
"yahoo.com",
|
|
940
|
-
"hotmail.com",
|
|
941
|
-
"outlook.com",
|
|
942
|
-
"live.com",
|
|
943
|
-
"msn.com",
|
|
944
|
-
// Apple
|
|
945
|
-
"icloud.com",
|
|
946
|
-
"me.com",
|
|
947
|
-
"mac.com",
|
|
948
|
-
// Other providers
|
|
949
|
-
"aol.com",
|
|
950
|
-
"protonmail.com",
|
|
951
|
-
"mail.com",
|
|
952
|
-
"yandex.com",
|
|
953
|
-
"zoho.com",
|
|
954
|
-
"gmx.com",
|
|
955
|
-
"fastmail.com",
|
|
956
|
-
// ISP-based
|
|
957
|
-
"comcast.net",
|
|
958
|
-
"verizon.net",
|
|
959
|
-
"att.net",
|
|
960
|
-
"sbcglobal.net",
|
|
961
|
-
"bellsouth.net",
|
|
962
|
-
"cox.net",
|
|
963
|
-
// International
|
|
964
|
-
"qq.com",
|
|
965
|
-
"163.com",
|
|
966
|
-
"126.com",
|
|
967
|
-
"web.de",
|
|
968
|
-
"t-online.de"
|
|
969
|
-
];
|
|
970
|
-
function extractDomainFromEmail(email) {
|
|
971
|
-
if (!email || !email.includes("@")) return null;
|
|
972
|
-
return email.split("@")[1]?.toLowerCase() || null;
|
|
973
|
-
}
|
|
974
|
-
function normalizeDomain(domain) {
|
|
975
|
-
return domain?.toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0].trim() || "";
|
|
976
|
-
}
|
|
977
|
-
function isGenericDomain(domain) {
|
|
978
|
-
const normalized = normalizeDomain(domain);
|
|
979
|
-
return GENERIC_EMAIL_DOMAINS.includes(normalized);
|
|
980
|
-
}
|
|
981
|
-
function hasGenericEmailDomain(email) {
|
|
982
|
-
const domain = extractDomainFromEmail(email);
|
|
983
|
-
return domain ? isGenericDomain(domain) : false;
|
|
984
|
-
}
|
|
985
|
-
function isGenericWebsiteDomain(website) {
|
|
986
|
-
return isGenericDomain(normalizeDomain(website));
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// src/utils/color-analysis.ts
|
|
990
|
-
var DESIGN_PALETTE = [
|
|
991
|
-
{ name: "yellow", hex: "#FFC008", rgb: [255, 192, 8] },
|
|
992
|
-
{ name: "black", hex: "#161616", rgb: [22, 22, 22] },
|
|
993
|
-
{ name: "gray", hex: "#888888", rgb: [136, 136, 136] }
|
|
994
|
-
];
|
|
995
|
-
function getLuminance(rgb) {
|
|
996
|
-
const [r, g, b] = rgb.map((c) => {
|
|
997
|
-
c = c / 255;
|
|
998
|
-
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
999
|
-
});
|
|
1000
|
-
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
1001
|
-
}
|
|
1002
|
-
function getContrastRatio(color1, color2) {
|
|
1003
|
-
const lum1 = getLuminance(color1);
|
|
1004
|
-
const lum2 = getLuminance(color2);
|
|
1005
|
-
const brightest = Math.max(lum1, lum2);
|
|
1006
|
-
const darkest = Math.min(lum1, lum2);
|
|
1007
|
-
return (brightest + 0.05) / (darkest + 0.05);
|
|
1008
|
-
}
|
|
1009
|
-
function extractDominantColor(canvas) {
|
|
1010
|
-
const ctx = canvas.getContext("2d");
|
|
1011
|
-
if (!ctx) return [128, 128, 128];
|
|
1012
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
1013
|
-
const data = imageData.data;
|
|
1014
|
-
const sampleSize = Math.max(1, Math.floor(data.length / (4 * 1e3)));
|
|
1015
|
-
const colorCounts = {};
|
|
1016
|
-
for (let i = 0; i < data.length; i += 4 * sampleSize) {
|
|
1017
|
-
const r = data[i];
|
|
1018
|
-
const g = data[i + 1];
|
|
1019
|
-
const b = data[i + 2];
|
|
1020
|
-
const alpha = data[i + 3];
|
|
1021
|
-
if (alpha < 128) continue;
|
|
1022
|
-
const bucketR = Math.round(r / 32) * 32;
|
|
1023
|
-
const bucketG = Math.round(g / 32) * 32;
|
|
1024
|
-
const bucketB = Math.round(b / 32) * 32;
|
|
1025
|
-
const key = `${bucketR},${bucketG},${bucketB}`;
|
|
1026
|
-
colorCounts[key] = (colorCounts[key] || 0) + 1;
|
|
1027
|
-
}
|
|
1028
|
-
let maxCount = 0;
|
|
1029
|
-
let dominantColor = [128, 128, 128];
|
|
1030
|
-
for (const [colorKey, count] of Object.entries(colorCounts)) {
|
|
1031
|
-
if (count > maxCount) {
|
|
1032
|
-
maxCount = count;
|
|
1033
|
-
const [r, g, b] = colorKey.split(",").map(Number);
|
|
1034
|
-
dominantColor = [r, g, b];
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
return dominantColor;
|
|
1038
|
-
}
|
|
1039
|
-
function getBestContrastColor(imageColor) {
|
|
1040
|
-
let bestColor = DESIGN_PALETTE[0];
|
|
1041
|
-
let bestContrast = 0;
|
|
1042
|
-
for (const color of DESIGN_PALETTE) {
|
|
1043
|
-
const contrast = getContrastRatio(imageColor, color.rgb);
|
|
1044
|
-
if (contrast > bestContrast) {
|
|
1045
|
-
bestContrast = contrast;
|
|
1046
|
-
bestColor = color;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
return bestContrast >= 3 ? bestColor : DESIGN_PALETTE.find((c) => c.name === "black") || bestColor;
|
|
1050
|
-
}
|
|
1051
|
-
function analyzeImageColor(imageSrc) {
|
|
1052
|
-
return new Promise((resolve, reject) => {
|
|
1053
|
-
const img = new Image();
|
|
1054
|
-
img.crossOrigin = "anonymous";
|
|
1055
|
-
img.onload = () => {
|
|
1056
|
-
try {
|
|
1057
|
-
const canvas = document.createElement("canvas");
|
|
1058
|
-
const ctx = canvas.getContext("2d");
|
|
1059
|
-
if (!ctx) {
|
|
1060
|
-
reject(new Error("Could not get canvas context"));
|
|
1061
|
-
return;
|
|
1062
|
-
}
|
|
1063
|
-
const maxSize = 100;
|
|
1064
|
-
const scale = Math.min(maxSize / img.width, maxSize / img.height);
|
|
1065
|
-
canvas.width = img.width * scale;
|
|
1066
|
-
canvas.height = img.height * scale;
|
|
1067
|
-
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
1068
|
-
const dominantColor = extractDominantColor(canvas);
|
|
1069
|
-
const bestContrastColor = getBestContrastColor(dominantColor);
|
|
1070
|
-
resolve(bestContrastColor);
|
|
1071
|
-
} catch (error) {
|
|
1072
|
-
reject(error);
|
|
1073
|
-
}
|
|
1074
|
-
};
|
|
1075
|
-
img.onerror = () => reject(new Error("Failed to load image"));
|
|
1076
|
-
img.src = imageSrc;
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
async function extractImageEdgeColorAsync(imageUrl) {
|
|
1080
|
-
return new Promise((resolve) => {
|
|
1081
|
-
if (typeof window === "undefined") {
|
|
1082
|
-
resolve("#000000");
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
const img = new Image();
|
|
1086
|
-
img.crossOrigin = "anonymous";
|
|
1087
|
-
img.onload = () => {
|
|
1088
|
-
try {
|
|
1089
|
-
const canvas = document.createElement("canvas");
|
|
1090
|
-
const ctx = canvas.getContext("2d");
|
|
1091
|
-
if (!ctx) {
|
|
1092
|
-
resolve("#000000");
|
|
1093
|
-
return;
|
|
1094
|
-
}
|
|
1095
|
-
const maxSize = 50;
|
|
1096
|
-
const scale = Math.min(maxSize / img.naturalWidth, maxSize / img.naturalHeight);
|
|
1097
|
-
const w = Math.round(img.naturalWidth * scale);
|
|
1098
|
-
const h = Math.round(img.naturalHeight * scale);
|
|
1099
|
-
canvas.width = w;
|
|
1100
|
-
canvas.height = h;
|
|
1101
|
-
ctx.drawImage(img, 0, 0, w, h);
|
|
1102
|
-
const data = ctx.getImageData(0, 0, w, h).data;
|
|
1103
|
-
const edgeW = Math.max(2, Math.round(w * 0.15));
|
|
1104
|
-
const edgeH = Math.max(2, Math.round(h * 0.15));
|
|
1105
|
-
const bucketSize = 32;
|
|
1106
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
1107
|
-
for (let y = 0; y < h; y++) {
|
|
1108
|
-
for (let x = 0; x < w; x++) {
|
|
1109
|
-
if (!(x < edgeW || x >= w - edgeW || y < edgeH || y >= h - edgeH)) continue;
|
|
1110
|
-
const i = (y * w + x) * 4;
|
|
1111
|
-
if (data[i + 3] < 128) continue;
|
|
1112
|
-
const br = Math.floor(data[i] / bucketSize) * bucketSize;
|
|
1113
|
-
const bg = Math.floor(data[i + 1] / bucketSize) * bucketSize;
|
|
1114
|
-
const bb = Math.floor(data[i + 2] / bucketSize) * bucketSize;
|
|
1115
|
-
const key = `${br},${bg},${bb}`;
|
|
1116
|
-
const existing = buckets.get(key);
|
|
1117
|
-
if (existing) {
|
|
1118
|
-
existing.r += data[i];
|
|
1119
|
-
existing.g += data[i + 1];
|
|
1120
|
-
existing.b += data[i + 2];
|
|
1121
|
-
existing.count++;
|
|
1122
|
-
} else {
|
|
1123
|
-
buckets.set(key, { r: data[i], g: data[i + 1], b: data[i + 2], count: 1 });
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
let best = { r: 0, g: 0, b: 0, count: 0 };
|
|
1128
|
-
for (const b2 of buckets.values()) {
|
|
1129
|
-
if (b2.count > best.count) best = b2;
|
|
1130
|
-
}
|
|
1131
|
-
if (best.count === 0) {
|
|
1132
|
-
resolve("#000000");
|
|
1133
|
-
return;
|
|
1134
|
-
}
|
|
1135
|
-
const r = Math.round(best.r / best.count), g = Math.round(best.g / best.count), b = Math.round(best.b / best.count);
|
|
1136
|
-
resolve(`#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`);
|
|
1137
|
-
} catch {
|
|
1138
|
-
resolve("#000000");
|
|
1139
|
-
}
|
|
1140
|
-
};
|
|
1141
|
-
img.onerror = () => resolve("#000000");
|
|
1142
|
-
img.src = imageUrl;
|
|
1143
|
-
});
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
// src/utils/date-utils.ts
|
|
1147
|
-
function formatTicketRelativeTime(iso) {
|
|
1148
|
-
const now = /* @__PURE__ */ new Date();
|
|
1149
|
-
const date = new Date(iso);
|
|
1150
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
1151
|
-
const diffMs = now.getTime() - date.getTime();
|
|
1152
|
-
const diffMin = Math.floor(diffMs / 6e4);
|
|
1153
|
-
if (diffMin < 1) return "Just now";
|
|
1154
|
-
if (diffMin < 60) return `${diffMin} min ago`;
|
|
1155
|
-
const diffHours = Math.floor(diffMin / 60);
|
|
1156
|
-
if (diffHours < 24) return diffHours === 1 ? "1 hour ago" : `${diffHours} hours ago`;
|
|
1157
|
-
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
1158
|
-
const dd = String(date.getDate()).padStart(2, "0");
|
|
1159
|
-
const yyyy = date.getFullYear();
|
|
1160
|
-
return `${mm}/${dd}/${yyyy}`;
|
|
1161
|
-
}
|
|
1162
|
-
function formatTicketFullTimestamp(iso) {
|
|
1163
|
-
const date = new Date(iso);
|
|
1164
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
1165
|
-
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
1166
|
-
const dd = String(date.getDate()).padStart(2, "0");
|
|
1167
|
-
const yyyy = date.getFullYear();
|
|
1168
|
-
let hours = date.getHours();
|
|
1169
|
-
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1170
|
-
const ampm = hours >= 12 ? "PM" : "AM";
|
|
1171
|
-
hours = hours % 12 || 12;
|
|
1172
|
-
return `${mm}/${dd}/${yyyy}, ${hours}:${minutes} ${ampm}`;
|
|
1173
|
-
}
|
|
1174
|
-
function formatRelativeTime(timestamp) {
|
|
1175
|
-
const now = /* @__PURE__ */ new Date();
|
|
1176
|
-
const targetTime = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
1177
|
-
if (isNaN(targetTime.getTime())) {
|
|
1178
|
-
console.warn("\u26A0\uFE0F Invalid timestamp in formatRelativeTime:", timestamp);
|
|
1179
|
-
return "Unknown time";
|
|
1180
|
-
}
|
|
1181
|
-
const diffInMs = now.getTime() - targetTime.getTime();
|
|
1182
|
-
const diffInMinutes = Math.floor(diffInMs / (1e3 * 60));
|
|
1183
|
-
if (diffInMinutes < 0) {
|
|
1184
|
-
return "Just now";
|
|
1185
|
-
}
|
|
1186
|
-
if (diffInMinutes < 1) return "Just now";
|
|
1187
|
-
if (diffInMinutes < 60) return `${diffInMinutes}m ago`;
|
|
1188
|
-
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
1189
|
-
if (diffInHours < 24) return `${diffInHours}h ago`;
|
|
1190
|
-
const diffInDays = Math.floor(diffInHours / 24);
|
|
1191
|
-
if (diffInDays < 7) return `${diffInDays}d ago`;
|
|
1192
|
-
if (diffInDays < 30) {
|
|
1193
|
-
const weeks = Math.floor(diffInDays / 7);
|
|
1194
|
-
return `${weeks}w ago`;
|
|
1195
|
-
}
|
|
1196
|
-
return targetTime.toLocaleDateString("en-US", {
|
|
1197
|
-
month: "short",
|
|
1198
|
-
day: "numeric",
|
|
1199
|
-
year: targetTime.getFullYear() !== now.getFullYear() ? "numeric" : void 0
|
|
1200
|
-
});
|
|
1201
|
-
}
|
|
1202
|
-
function formatAbsoluteDate(timestamp, options = {}) {
|
|
1203
|
-
const targetTime = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
1204
|
-
if (isNaN(targetTime.getTime())) {
|
|
1205
|
-
console.warn("\u26A0\uFE0F Invalid timestamp in formatAbsoluteDate:", timestamp);
|
|
1206
|
-
return "Invalid date";
|
|
1207
|
-
}
|
|
1208
|
-
const defaultOptions = {
|
|
1209
|
-
year: "numeric",
|
|
1210
|
-
month: "short",
|
|
1211
|
-
day: "numeric",
|
|
1212
|
-
...options
|
|
1213
|
-
};
|
|
1214
|
-
return targetTime.toLocaleDateString("en-US", defaultOptions);
|
|
1215
|
-
}
|
|
1216
|
-
function formatDateTime(timestamp, options = {}) {
|
|
1217
|
-
const targetTime = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
1218
|
-
if (isNaN(targetTime.getTime())) {
|
|
1219
|
-
console.warn("\u26A0\uFE0F Invalid timestamp in formatDateTime:", timestamp);
|
|
1220
|
-
return "Invalid date";
|
|
1221
|
-
}
|
|
1222
|
-
const defaultOptions = {
|
|
1223
|
-
year: "numeric",
|
|
1224
|
-
month: "short",
|
|
1225
|
-
day: "numeric",
|
|
1226
|
-
hour: "numeric",
|
|
1227
|
-
minute: "2-digit",
|
|
1228
|
-
...options
|
|
1229
|
-
};
|
|
1230
|
-
return targetTime.toLocaleDateString("en-US", defaultOptions);
|
|
1231
|
-
}
|
|
1232
|
-
function getDetailedTimeDifference(timestamp) {
|
|
1233
|
-
const now = /* @__PURE__ */ new Date();
|
|
1234
|
-
const targetTime = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
1235
|
-
if (isNaN(targetTime.getTime())) {
|
|
1236
|
-
return "Invalid date";
|
|
1237
|
-
}
|
|
1238
|
-
const diffInMs = now.getTime() - targetTime.getTime();
|
|
1239
|
-
const diffInSeconds = Math.floor(diffInMs / 1e3);
|
|
1240
|
-
const diffInMinutes = Math.floor(diffInSeconds / 60);
|
|
1241
|
-
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
1242
|
-
const diffInDays = Math.floor(diffInHours / 24);
|
|
1243
|
-
if (diffInSeconds < 60) return `${diffInSeconds} seconds ago`;
|
|
1244
|
-
if (diffInMinutes < 60) return `${diffInMinutes} minutes ago`;
|
|
1245
|
-
if (diffInHours < 24) return `${diffInHours} hours ago`;
|
|
1246
|
-
if (diffInDays < 30) return `${diffInDays} days ago`;
|
|
1247
|
-
const diffInMonths = Math.floor(diffInDays / 30);
|
|
1248
|
-
if (diffInMonths < 12) return `${diffInMonths} months ago`;
|
|
1249
|
-
const diffInYears = Math.floor(diffInDays / 365);
|
|
1250
|
-
return `${diffInYears} years ago`;
|
|
1251
|
-
}
|
|
1252
|
-
function isToday(timestamp) {
|
|
1253
|
-
const today = /* @__PURE__ */ new Date();
|
|
1254
|
-
const targetTime = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
1255
|
-
return today.toDateString() === targetTime.toDateString();
|
|
1256
|
-
}
|
|
1257
|
-
function isWithinMinutes(timestamp, minutes) {
|
|
1258
|
-
const now = /* @__PURE__ */ new Date();
|
|
1259
|
-
const targetTime = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
1260
|
-
const diffInMs = now.getTime() - targetTime.getTime();
|
|
1261
|
-
const diffInMinutes = diffInMs / (1e3 * 60);
|
|
1262
|
-
return diffInMinutes <= minutes && diffInMinutes >= 0;
|
|
1263
|
-
}
|
|
1264
|
-
function createUTCTimestamp() {
|
|
1265
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// src/utils/sse-decision-frame.ts
|
|
1269
|
-
async function readLeadingDecisionFrame(response) {
|
|
1270
|
-
const reader = response.body?.getReader();
|
|
1271
|
-
if (!reader) throw new Error("readLeadingDecisionFrame: response has no body");
|
|
1272
|
-
const decoder = new TextDecoder();
|
|
1273
|
-
let buffer = "";
|
|
1274
|
-
let frame = null;
|
|
1275
|
-
try {
|
|
1276
|
-
while (true) {
|
|
1277
|
-
const { done, value } = await reader.read();
|
|
1278
|
-
if (done) break;
|
|
1279
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1280
|
-
if (frame === null) {
|
|
1281
|
-
const nullIdx = buffer.indexOf("\0");
|
|
1282
|
-
const recIdx = buffer.indexOf("");
|
|
1283
|
-
if (recIdx !== -1 && (nullIdx === -1 || recIdx < nullIdx)) {
|
|
1284
|
-
throw new Error(
|
|
1285
|
-
"readLeadingDecisionFrame: text-body sentinel arrived before leading frame"
|
|
1286
|
-
);
|
|
1287
|
-
}
|
|
1288
|
-
if (nullIdx !== -1) {
|
|
1289
|
-
const raw = buffer.slice(0, nullIdx);
|
|
1290
|
-
let parsed;
|
|
1291
|
-
try {
|
|
1292
|
-
parsed = JSON.parse(raw);
|
|
1293
|
-
} catch (err) {
|
|
1294
|
-
throw new Error(
|
|
1295
|
-
`readLeadingDecisionFrame: leading JSON parse failed: ${err.message}`
|
|
1296
|
-
);
|
|
1297
|
-
}
|
|
1298
|
-
const obj = parsed;
|
|
1299
|
-
if (obj?.kind !== "decision_resolved") {
|
|
1300
|
-
throw new Error(
|
|
1301
|
-
`readLeadingDecisionFrame: expected decision_resolved, got kind=${String(obj?.kind)}`
|
|
1302
|
-
);
|
|
1303
|
-
}
|
|
1304
|
-
frame = normalizeDecisionFrame(obj);
|
|
1305
|
-
buffer = buffer.slice(nullIdx + 1);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
} finally {
|
|
1310
|
-
try {
|
|
1311
|
-
reader.releaseLock();
|
|
1312
|
-
} catch {
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
if (frame === null) {
|
|
1316
|
-
throw new Error("readLeadingDecisionFrame: stream closed before leading frame arrived");
|
|
1317
|
-
}
|
|
1318
|
-
return frame;
|
|
1319
|
-
}
|
|
1320
|
-
function normalizeDecisionFrame(obj) {
|
|
1321
|
-
const action = obj.action === "rejected" ? "rejected" : "approved";
|
|
1322
|
-
const result = obj.result ?? null;
|
|
1323
|
-
const card = obj.card ?? null;
|
|
1324
|
-
return {
|
|
1325
|
-
kind: "decision_resolved",
|
|
1326
|
-
ok: obj.ok === true,
|
|
1327
|
-
action,
|
|
1328
|
-
...typeof obj.toolName === "string" ? { toolName: obj.toolName } : {},
|
|
1329
|
-
...obj.willAutoContinue === true ? { willAutoContinue: true } : {},
|
|
1330
|
-
...typeof obj.proposalId === "string" ? { proposalId: obj.proposalId } : {},
|
|
1331
|
-
...result ? { result } : {},
|
|
1332
|
-
...card ? { card } : {},
|
|
1333
|
-
...typeof obj.receiptText === "string" ? { receiptText: obj.receiptText } : {}
|
|
1334
|
-
};
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
// src/types/announcement.ts
|
|
1338
|
-
var AVAILABLE_SVG_ICONS = [
|
|
1339
|
-
// OpenFrame Logo Options
|
|
1340
|
-
{ name: "openframe-logo", label: "OpenFrame Logo", component: null },
|
|
1341
|
-
// Platform Logos
|
|
1342
|
-
{ name: "openmsp-logo", label: "OpenMSP Logo", component: null },
|
|
1343
|
-
{ name: "flamingo-logo", label: "Flamingo Logo", component: null },
|
|
1344
|
-
// Lucide Icons
|
|
1345
|
-
{ name: "megaphone", label: "Megaphone", component: null },
|
|
1346
|
-
{ name: "bell", label: "Bell", component: null },
|
|
1347
|
-
{ name: "info", label: "Information", component: null },
|
|
1348
|
-
{ name: "star", label: "Star", component: null },
|
|
1349
|
-
{ name: "rocket", label: "Rocket", component: null },
|
|
1350
|
-
{ name: "package", label: "Package", component: null }
|
|
1351
|
-
];
|
|
1352
|
-
|
|
1353
|
-
// src/types/product-release.ts
|
|
1354
|
-
var releaseTypeOptions = [
|
|
1355
|
-
{ value: "major", label: "Major Release", description: "Breaking changes, new architecture", color: "red" },
|
|
1356
|
-
{ value: "minor", label: "Minor Release", description: "New features, backward compatible", color: "blue" },
|
|
1357
|
-
{ value: "patch", label: "Patch Release", description: "Bug fixes only", color: "green" },
|
|
1358
|
-
{ value: "beta", label: "Beta Release", description: "Pre-release testing version", color: "yellow" },
|
|
1359
|
-
{ value: "alpha", label: "Alpha Release", description: "Early testing version", color: "orange" }
|
|
1360
|
-
];
|
|
1361
|
-
var releaseStatusOptions = [
|
|
1362
|
-
{ value: "alpha", label: "Alpha", description: "Early development, unstable", color: "orange" },
|
|
1363
|
-
{ value: "beta", label: "Beta", description: "Feature complete, testing", color: "yellow" },
|
|
1364
|
-
{ value: "stable", label: "Stable", description: "Production ready", color: "green" },
|
|
1365
|
-
{ value: "deprecated", label: "Deprecated", description: "No longer supported", color: "gray" }
|
|
1366
|
-
];
|
|
1367
|
-
var changelogLabels = {
|
|
1368
|
-
features_added: "Features Added",
|
|
1369
|
-
bugs_fixed: "Bugs Fixed",
|
|
1370
|
-
improvements: "Improvements",
|
|
1371
|
-
breaking_changes: "Breaking Changes"
|
|
1372
|
-
};
|
|
1373
|
-
var SEMVER_REGEX = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
|
|
1374
|
-
|
|
1375
|
-
// src/types/delivery.ts
|
|
1376
|
-
var TASK_TYPE_LABELS = {
|
|
1377
|
-
Request: "ENHANCEMENT",
|
|
1378
|
-
Bug: "BUG-FIX"
|
|
1379
|
-
};
|
|
1380
|
-
var TASK_TYPE_TEXT_COLORS = {
|
|
1381
|
-
Request: "",
|
|
1382
|
-
// Default white/grey
|
|
1383
|
-
Bug: "text-[var(--ods-attention-red-error)]"
|
|
1384
|
-
};
|
|
1385
|
-
|
|
1386
|
-
// src/types/tmcg.ts
|
|
1387
|
-
var TMCG_ROLES = {
|
|
1388
|
-
FOUNDER: "founder",
|
|
1389
|
-
ORGANIZER: "organizer",
|
|
1390
|
-
MEMBER: "member",
|
|
1391
|
-
CONTRIBUTOR: "contributor",
|
|
1392
|
-
SPEAKER: "speaker",
|
|
1393
|
-
MENTOR: "mentor"
|
|
1394
|
-
};
|
|
1395
|
-
var TMCG_ROLE_DISPLAY_NAMES = {
|
|
1396
|
-
[TMCG_ROLES.FOUNDER]: "Founder",
|
|
1397
|
-
[TMCG_ROLES.ORGANIZER]: "Organizer",
|
|
1398
|
-
[TMCG_ROLES.MEMBER]: "Member",
|
|
1399
|
-
[TMCG_ROLES.CONTRIBUTOR]: "Contributor",
|
|
1400
|
-
[TMCG_ROLES.SPEAKER]: "Speaker",
|
|
1401
|
-
[TMCG_ROLES.MENTOR]: "Mentor"
|
|
1402
|
-
};
|
|
1403
|
-
var TMCG_SOCIAL_PLATFORMS = {
|
|
1404
|
-
linkedin: "LinkedIn",
|
|
1405
|
-
twitter: "Twitter",
|
|
1406
|
-
github: "GitHub",
|
|
1407
|
-
website: "Website",
|
|
1408
|
-
youtube: "YouTube",
|
|
1409
|
-
instagram: "Instagram",
|
|
1410
|
-
facebook: "Facebook",
|
|
1411
|
-
discord: "Discord",
|
|
1412
|
-
telegram: "Telegram",
|
|
1413
|
-
slack: "Slack"
|
|
1414
|
-
};
|
|
1415
|
-
|
|
1416
|
-
// src/utils/dev-sections/openframe-dev-sections.ts
|
|
1417
|
-
import { Map as MapIcon, Wrench, Rocket, GraduationCap, LifeBuoy } from "lucide-react";
|
|
1418
|
-
var ROADMAP_STATUS_OPTIONS = [
|
|
1419
|
-
{ value: "all", label: "All" },
|
|
1420
|
-
{ value: "completed", label: "Completed" },
|
|
1421
|
-
{ value: "in_progress", label: "In Progress" }
|
|
1422
|
-
];
|
|
1423
|
-
var DELIVERY_TASK_TYPE_OPTIONS = [
|
|
1424
|
-
{ value: "all", label: "All" },
|
|
1425
|
-
{ value: "Bug", label: "Bug-fix" },
|
|
1426
|
-
{ value: "Request", label: "Enhancement" }
|
|
1427
|
-
];
|
|
1428
|
-
var TICKET_STATUS_OPTIONS = [
|
|
1429
|
-
{ value: "all", label: "All" },
|
|
1430
|
-
{ value: "open", label: "Open" },
|
|
1431
|
-
{ value: "closed", label: "Closed" }
|
|
1432
|
-
];
|
|
1433
|
-
var OPENFRAME_DEV_SECTIONS = {
|
|
1434
|
-
roadmap: {
|
|
1435
|
-
href: "/roadmap",
|
|
1436
|
-
icon: MapIcon,
|
|
1437
|
-
navigator: {
|
|
1438
|
-
title: "Development Roadmap",
|
|
1439
|
-
description: "What we're building next \u2014 vote on upcoming features."
|
|
1440
|
-
},
|
|
1441
|
-
hero: {
|
|
1442
|
-
title: "Development Roadmap",
|
|
1443
|
-
description: "See what's in flight, what's planned, and what's up for community vote. The entire OpenFrame roadmap is public."
|
|
1444
|
-
},
|
|
1445
|
-
search: { placeholder: "Search roadmap items...", paramKey: DEV_SECTION_PARAM_KEYS.search },
|
|
1446
|
-
filter: { label: "Status", paramKey: DEV_SECTION_PARAM_KEYS.status, defaultValue: "all", options: ROADMAP_STATUS_OPTIONS }
|
|
1447
|
-
},
|
|
1448
|
-
delivery: {
|
|
1449
|
-
href: "/bug-fixes-and-enhancements",
|
|
1450
|
-
icon: Wrench,
|
|
1451
|
-
navigator: {
|
|
1452
|
-
title: "Bug-fixes & Enhancements",
|
|
1453
|
-
description: "Recently shipped fixes and improvements."
|
|
1454
|
-
},
|
|
1455
|
-
hero: {
|
|
1456
|
-
title: "Bug-fixes & Enhancements",
|
|
1457
|
-
description: "A running log of fixes and improvements shipping into OpenFrame \u2014 recently completed and actively in progress."
|
|
1458
|
-
},
|
|
1459
|
-
search: { placeholder: "Search tasks...", paramKey: DEV_SECTION_PARAM_KEYS.search },
|
|
1460
|
-
filter: { label: "Type", paramKey: DEV_SECTION_PARAM_KEYS.deliveryTaskType, defaultValue: "all", options: DELIVERY_TASK_TYPE_OPTIONS }
|
|
1461
|
-
},
|
|
1462
|
-
releases: {
|
|
1463
|
-
href: "/releases",
|
|
1464
|
-
icon: Rocket,
|
|
1465
|
-
navigator: {
|
|
1466
|
-
title: "Product Releases",
|
|
1467
|
-
description: "Version history and release notes."
|
|
1468
|
-
},
|
|
1469
|
-
hero: {
|
|
1470
|
-
title: "Product Releases",
|
|
1471
|
-
description: "Version notes, change summaries, and stability tier (alpha / beta / stable) for every OpenFrame release."
|
|
1472
|
-
},
|
|
1473
|
-
search: { placeholder: "Search releases...", paramKey: DEV_SECTION_PARAM_KEYS.search },
|
|
1474
|
-
filter: { label: "Status", paramKey: DEV_SECTION_PARAM_KEYS.releaseStatus, defaultValue: "all", options: releaseStatusOptions }
|
|
1475
|
-
},
|
|
1476
|
-
onboarding: {
|
|
1477
|
-
href: "/onboarding-guides",
|
|
1478
|
-
icon: GraduationCap,
|
|
1479
|
-
navigator: {
|
|
1480
|
-
title: "Onboarding Guides",
|
|
1481
|
-
description: "Step-by-step product walkthroughs."
|
|
1482
|
-
},
|
|
1483
|
-
hero: {
|
|
1484
|
-
title: "Getting Started",
|
|
1485
|
-
description: "Step-by-step walkthroughs for getting up and running with OpenFrame \u2014 from your first device deployment to advanced integrations."
|
|
1486
|
-
},
|
|
1487
|
-
// `search` / `filter` are intentionally null — onboarding-guides
|
|
1488
|
-
// uses a custom RAG-backed search dropdown (`useDocSearch`, not the
|
|
1489
|
-
// local text filter `DevSectionView` ships) and dynamic section
|
|
1490
|
-
// pills (counts vary with content, vs the static status options
|
|
1491
|
-
// every other dev-center surface uses). Both controls are injected
|
|
1492
|
-
// by the host via `DevSectionPage`'s `preControls` slot — same
|
|
1493
|
-
// mechanism tickets uses for its "Open a new ticket" form.
|
|
1494
|
-
search: null,
|
|
1495
|
-
filter: null
|
|
1496
|
-
},
|
|
1497
|
-
tickets: {
|
|
1498
|
-
href: "/tickets",
|
|
1499
|
-
icon: LifeBuoy,
|
|
1500
|
-
navigator: {
|
|
1501
|
-
title: "Help Center",
|
|
1502
|
-
description: "Open and manage your support tickets."
|
|
1503
|
-
},
|
|
1504
|
-
hero: {
|
|
1505
|
-
title: "Help Center",
|
|
1506
|
-
description: "Open new tickets, follow up on existing ones, and track responses from the team \u2014 all in one place."
|
|
1507
|
-
},
|
|
1508
|
-
search: { placeholder: "Search your tickets...", paramKey: DEV_SECTION_PARAM_KEYS.search },
|
|
1509
|
-
filter: {
|
|
1510
|
-
label: "Status",
|
|
1511
|
-
paramKey: DEV_SECTION_PARAM_KEYS.status,
|
|
1512
|
-
defaultValue: "all",
|
|
1513
|
-
options: TICKET_STATUS_OPTIONS
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
};
|
|
1517
|
-
|
|
1518
|
-
// src/utils/list-url.ts
|
|
1519
|
-
var ALIASES = { blog_post_existing: "blog_post" };
|
|
1520
|
-
function canonicalContentRefType(contentRefType) {
|
|
1521
|
-
return ALIASES[contentRefType] ?? contentRefType;
|
|
1522
|
-
}
|
|
1523
|
-
var BUILDERS = {
|
|
1524
|
-
roadmap_item: (ids, b) => `${b}/api/roadmap?task_ids=${ids.join(",")}`,
|
|
1525
|
-
delivery_item: (ids, b) => `${b}/api/delivery?task_ids=${ids.join(",")}`,
|
|
1526
|
-
internal_task: (ids, b) => `${b}/api/internal-tasks?task_ids=${ids.join(",")}`,
|
|
1527
|
-
blog_post: (ids, b) => `${b}/api/blog/posts?ids=${ids.join(",")}&pageSize=${ids.length}`,
|
|
1528
|
-
webinar: (ids, b) => `${b}/api/programs/webinars?ids=${ids.join(",")}&limit=${ids.length}&filter=all`,
|
|
1529
|
-
podcast: (ids, b) => `${b}/api/programs/podcasts?ids=${ids.join(",")}&limit=${ids.length}&filter=all`,
|
|
1530
|
-
event: (ids, b) => `${b}/api/programs/events?ids=${ids.join(",")}&limit=${ids.length}&filter=all`,
|
|
1531
|
-
onboarding_guide: (ids, b) => `${b}/api/onboarding-guides?ids=${ids.join(",")}&limit=${ids.length}`,
|
|
1532
|
-
case_study: (ids, b) => `${b}/api/case-studies?ids=${ids.join(",")}&limit=${ids.length}`,
|
|
1533
|
-
product_release: (ids, b) => `${b}/api/releases?ids=${ids.join(",")}&limit=${ids.length}`,
|
|
1534
|
-
customer_interview: (ids, b) => `${b}/api/customer-interviews?ids=${ids.join(",")}&limit=${ids.length}`,
|
|
1535
|
-
investor_update: (ids, b) => `${b}/api/investor-updates?ids=${ids.join(",")}&limit=${ids.length}`,
|
|
1536
|
-
faq: (ids, b) => `${b}/api/faqs?ids=${ids.join(",")}&limit=${ids.length}`
|
|
1537
|
-
};
|
|
1538
|
-
function buildListUrl(contentRefType, ids, base = "") {
|
|
1539
|
-
if (ids.length === 0) return null;
|
|
1540
|
-
const key = ALIASES[contentRefType] ?? contentRefType;
|
|
1541
|
-
if (key === "marketing_campaign") {
|
|
1542
|
-
return `${base}/api/admin/marketing/campaigns?ids=${ids.join(",")}&pageSize=${ids.length}`;
|
|
1543
|
-
}
|
|
1544
|
-
const fn = Object.prototype.hasOwnProperty.call(BUILDERS, key) ? BUILDERS[key] : void 0;
|
|
1545
|
-
return fn ? fn(ids, base) : null;
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
// src/utils/faq-anchor.ts
|
|
1549
|
-
function faqSectionSlug(section) {
|
|
1550
|
-
return "faq-" + section.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1551
|
-
}
|
|
1552
|
-
function faqItemAnchor(id) {
|
|
1553
|
-
return `faq-item-${id}`;
|
|
1554
|
-
}
|
|
1555
|
-
var FAQ_ITEM_HASH_RE = /^faq-item-(\d+)$/;
|
|
1556
|
-
function parseFaqHash(hash) {
|
|
1557
|
-
if (!hash) return null;
|
|
1558
|
-
const trimmed = hash.replace(/^#/, "");
|
|
1559
|
-
if (!trimmed) return null;
|
|
1560
|
-
const itemMatch = FAQ_ITEM_HASH_RE.exec(trimmed);
|
|
1561
|
-
if (itemMatch) return { kind: "item", rawId: itemMatch[1] };
|
|
1562
|
-
if (trimmed.startsWith("faq-")) return { kind: "section", slug: trimmed };
|
|
1563
|
-
return null;
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
// src/utils/content-ref-groups.ts
|
|
1567
|
-
var CONTENT_REF_GROUPS = {
|
|
1568
|
-
investor_update: { label: "Investor Updates", order: 1, layout: "grid", gridSize: "default" },
|
|
1569
|
-
product_release: { label: "Product Releases", order: 2, layout: "list", gridSize: "lg" },
|
|
1570
|
-
podcast: { label: "Podcasts", order: 3, layout: "list", gridSize: "default" },
|
|
1571
|
-
webinar: { label: "Webinars", order: 4, layout: "list", gridSize: "default" },
|
|
1572
|
-
case_study: { label: "Case Studies", order: 5, layout: "grid", gridSize: "default" },
|
|
1573
|
-
event: { label: "Events", order: 6, layout: "list", gridSize: "default" },
|
|
1574
|
-
blog_post_existing: { label: "Blog Posts", order: 7, layout: "grid", gridSize: "default" },
|
|
1575
|
-
customer_interview: { label: "Customer Interviews", order: 8, layout: "grid", gridSize: "default" },
|
|
1576
|
-
onboarding_guide: { label: "Onboarding Guides", order: 9, layout: "list", gridSize: "default" },
|
|
1577
|
-
what_i_shipped: { label: "What I Shipped", order: 10, layout: "grid", gridSize: "default" }
|
|
1578
|
-
};
|
|
1579
|
-
function getContentRefLabel(type) {
|
|
1580
|
-
return CONTENT_REF_GROUPS[type]?.label ?? null;
|
|
1581
|
-
}
|
|
1582
|
-
function getContentRefLabelOrTitleCase(type) {
|
|
1583
|
-
return CONTENT_REF_GROUPS[type]?.label ?? type.replace(/_/g, " ").replace(/\b\w/g, (ch) => ch.toUpperCase());
|
|
1584
|
-
}
|
|
1585
|
-
function orderContentRefTypes(present) {
|
|
1586
|
-
const presentSet = new Set(present);
|
|
1587
|
-
const registeredInOrder = Object.entries(CONTENT_REF_GROUPS).sort(([, a], [, b]) => a.order - b.order).map(([type]) => type).filter((type) => presentSet.has(type));
|
|
1588
|
-
const unregistered = [...presentSet].filter((type) => !CONTENT_REF_GROUPS[type]);
|
|
1589
|
-
return [...registeredInOrder, ...unregistered];
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
// src/utils/suggestion-url.ts
|
|
1593
|
-
function buildSuggestionUrl(path, opts = {}) {
|
|
1594
|
-
const qs = new URLSearchParams();
|
|
1595
|
-
const { entityType, entityId } = opts;
|
|
1596
|
-
if (entityType && entityId !== void 0 && entityId !== null && entityId !== "") {
|
|
1597
|
-
qs.set("entityType", entityType);
|
|
1598
|
-
qs.set("entityId", String(entityId));
|
|
1599
|
-
}
|
|
1600
|
-
if (opts.count !== void 0) qs.set("count", String(opts.count));
|
|
1601
|
-
for (const [key, value] of Object.entries(opts.extraParams ?? {})) {
|
|
1602
|
-
if (value !== void 0 && value !== "") qs.set(key, value);
|
|
1603
|
-
}
|
|
1604
|
-
const query = qs.toString();
|
|
1605
|
-
return `${opts.apiBaseUrl ?? ""}${path}${query ? `?${query}` : ""}`;
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
// src/utils/doc-tree-nav.ts
|
|
1609
|
-
var DEFAULT_FOLDER_INDEX_FILE = "README.md";
|
|
1610
|
-
var DEFAULT_FOLDER_INDEX = DEFAULT_FOLDER_INDEX_FILE;
|
|
1611
|
-
function stripFolderIndexFromPath(path, folderIndexFile = DEFAULT_FOLDER_INDEX) {
|
|
1612
|
-
const suffix = `/${folderIndexFile}`;
|
|
1613
|
-
if (path.endsWith(suffix)) return path.slice(0, -suffix.length);
|
|
1614
|
-
if (path === folderIndexFile) return "";
|
|
1615
|
-
return path;
|
|
1616
|
-
}
|
|
1617
|
-
function findDocNodeByPath(path, nodes) {
|
|
1618
|
-
for (const node of nodes) {
|
|
1619
|
-
if (node.path === path) return node;
|
|
1620
|
-
if (node.children) {
|
|
1621
|
-
const found = findDocNodeByPath(path, node.children);
|
|
1622
|
-
if (found) return found;
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
return null;
|
|
1626
|
-
}
|
|
1627
|
-
function getDocAncestorNodeIds(nodePath) {
|
|
1628
|
-
const parts = nodePath.split("/");
|
|
1629
|
-
const ids = [];
|
|
1630
|
-
let current = "";
|
|
1631
|
-
for (const part of parts) {
|
|
1632
|
-
current = current ? `${current}-${part}` : part;
|
|
1633
|
-
ids.push(current.toLowerCase());
|
|
1634
|
-
}
|
|
1635
|
-
return ids;
|
|
1636
|
-
}
|
|
1637
|
-
function resolveContentFetchPath(selectedPath, structure, folderIndexFile = DEFAULT_FOLDER_INDEX) {
|
|
1638
|
-
if (selectedPath === "") {
|
|
1639
|
-
return folderIndexFile;
|
|
1640
|
-
}
|
|
1641
|
-
const node = findDocNodeByPath(selectedPath, structure);
|
|
1642
|
-
if (node && node.type === "folder" && !node.hasReadme) {
|
|
1643
|
-
return null;
|
|
1644
|
-
}
|
|
1645
|
-
let pathToFetch = selectedPath;
|
|
1646
|
-
if (node && node.type === "folder" && node.hasReadme) {
|
|
1647
|
-
pathToFetch = `${selectedPath}/${folderIndexFile}`;
|
|
1648
|
-
}
|
|
1649
|
-
return pathToFetch;
|
|
1650
|
-
}
|
|
1651
|
-
function getInitialExpandedNodeIds(cleanInitialPath) {
|
|
1652
|
-
if (!cleanInitialPath) return [];
|
|
1653
|
-
const pathForExpansion = cleanInitialPath.includes(".") ? cleanInitialPath.substring(0, cleanInitialPath.lastIndexOf("/")) : cleanInitialPath;
|
|
1654
|
-
if (!pathForExpansion) return [];
|
|
1655
|
-
return getDocAncestorNodeIds(pathForExpansion);
|
|
1656
|
-
}
|
|
1657
|
-
function getDocSourceDefaultPath(structure, folderIndexFile = DEFAULT_FOLDER_INDEX) {
|
|
1658
|
-
if (!structure.length) return null;
|
|
1659
|
-
const hasRootIndex = structure.some(
|
|
1660
|
-
(node) => node.type === "file" && node.path === folderIndexFile
|
|
1661
|
-
);
|
|
1662
|
-
if (hasRootIndex) return null;
|
|
1663
|
-
const firstFolderWithReadme = structure.find(
|
|
1664
|
-
(node) => node.type === "folder" && node.hasReadme && !!node.path
|
|
1665
|
-
);
|
|
1666
|
-
return firstFolderWithReadme?.path ?? null;
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
// src/utils/doc-path-utils.ts
|
|
1670
|
-
function toDocSlug(name) {
|
|
1671
|
-
return name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
1672
|
-
}
|
|
1673
|
-
function pathToNodeId(path) {
|
|
1674
|
-
return path.replace(/[\/\s]/g, "-").toLowerCase();
|
|
1675
|
-
}
|
|
1676
|
-
function normalizeDocPath(segments, folderIndexFile = DEFAULT_FOLDER_INDEX_FILE) {
|
|
1677
|
-
const lowerPath = (segments?.join("/") || "").toLowerCase();
|
|
1678
|
-
const lowerIndex = folderIndexFile.toLowerCase();
|
|
1679
|
-
const suffix = `/${lowerIndex}`;
|
|
1680
|
-
if (lowerPath.endsWith(suffix)) return lowerPath.slice(0, -suffix.length);
|
|
1681
|
-
if (lowerPath === lowerIndex) return "";
|
|
1682
|
-
return lowerPath;
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
// src/utils/tree-builder.ts
|
|
1686
|
-
function formatDocName(name) {
|
|
1687
|
-
let displayName = name;
|
|
1688
|
-
if (displayName.endsWith(".md")) {
|
|
1689
|
-
displayName = displayName.slice(0, -3);
|
|
1690
|
-
}
|
|
1691
|
-
if (displayName.toLowerCase() === "readme") {
|
|
1692
|
-
return "README";
|
|
1693
|
-
}
|
|
1694
|
-
return displayName.charAt(0).toUpperCase() + displayName.slice(1).replace(/-/g, " ");
|
|
1695
|
-
}
|
|
1696
|
-
function sortTreeChildren(nodes) {
|
|
1697
|
-
nodes.sort((a, b) => {
|
|
1698
|
-
const aIsReadme = a.name.toLowerCase() === "readme" || a.name.toLowerCase() === "readme.md";
|
|
1699
|
-
const bIsReadme = b.name.toLowerCase() === "readme" || b.name.toLowerCase() === "readme.md";
|
|
1700
|
-
if (aIsReadme && !bIsReadme) return -1;
|
|
1701
|
-
if (!aIsReadme && bIsReadme) return 1;
|
|
1702
|
-
if (a.type === "folder" && b.type !== "folder") return -1;
|
|
1703
|
-
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
1704
|
-
const aOrder = a.sortOrder ?? Infinity;
|
|
1705
|
-
const bOrder = b.sortOrder ?? Infinity;
|
|
1706
|
-
if (aOrder !== bOrder) return aOrder - bOrder;
|
|
1707
|
-
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
1708
|
-
});
|
|
1709
|
-
for (const node of nodes) {
|
|
1710
|
-
if (node.type === "folder" && node.children) {
|
|
1711
|
-
node.children = sortTreeChildren(node.children);
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
return nodes;
|
|
1715
|
-
}
|
|
1716
|
-
function buildDocumentTree(docs, mapFn, getParentPath, getPath) {
|
|
1717
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
1718
|
-
const rootNodes = [];
|
|
1719
|
-
for (const doc of docs) {
|
|
1720
|
-
const node = mapFn(doc);
|
|
1721
|
-
nodeMap.set(getPath(doc), node);
|
|
1722
|
-
}
|
|
1723
|
-
for (const doc of docs) {
|
|
1724
|
-
const node = nodeMap.get(getPath(doc));
|
|
1725
|
-
let resolvedParentPath = getParentPath(doc);
|
|
1726
|
-
if (!resolvedParentPath) {
|
|
1727
|
-
const nodePath = getPath(doc);
|
|
1728
|
-
const lastSlash = nodePath.lastIndexOf("/");
|
|
1729
|
-
if (lastSlash > 0) {
|
|
1730
|
-
resolvedParentPath = nodePath.substring(0, lastSlash);
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
if (resolvedParentPath) {
|
|
1734
|
-
const parent = nodeMap.get(resolvedParentPath);
|
|
1735
|
-
if (parent) {
|
|
1736
|
-
if (!parent.children) parent.children = [];
|
|
1737
|
-
parent.children.push(node);
|
|
1738
|
-
} else {
|
|
1739
|
-
rootNodes.push(node);
|
|
1740
|
-
}
|
|
1741
|
-
} else {
|
|
1742
|
-
rootNodes.push(node);
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
return sortTreeChildren(rootNodes);
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
// src/utils/markdown-to-plain.ts
|
|
1749
|
-
function stripInlineMarkdown(s) {
|
|
1750
|
-
return s.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1");
|
|
1751
|
-
}
|
|
1752
|
-
function markdownToPlainText(markdown, options = {}) {
|
|
1753
|
-
const { mode = "article", maxChars } = options;
|
|
1754
|
-
let out = markdown.replace(/```[\s\S]*?```/g, "").replace(/!\[[^\]]*\]\([^)]+\)/g, "");
|
|
1755
|
-
out = stripInlineMarkdown(out).replace(/^#+\s*/gm, "").replace(/^\s*[-*+]\s+/gm, "");
|
|
1756
|
-
if (mode === "preview") {
|
|
1757
|
-
out = out.replace(/\s+/g, " ").trim();
|
|
1758
|
-
} else {
|
|
1759
|
-
out = out.replace(/\n{3,}/g, "\n\n").trim();
|
|
1760
|
-
}
|
|
1761
|
-
if (maxChars != null && out.length > maxChars) {
|
|
1762
|
-
out = out.slice(0, maxChars);
|
|
1763
|
-
}
|
|
1764
|
-
return out;
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
// src/utils/markdown-section-extractor.ts
|
|
1768
|
-
var DEFAULT_OPTIONS = {
|
|
1769
|
-
maxLevel: 2,
|
|
1770
|
-
removeEmojis: true,
|
|
1771
|
-
handleDuplicateIds: true,
|
|
1772
|
-
stripFormattingMarkers: true,
|
|
1773
|
-
skipCodeAndYamlBlocks: true
|
|
1774
|
-
};
|
|
1775
|
-
function extractSections(markdown, options = {}) {
|
|
1776
|
-
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1777
|
-
const sections = [];
|
|
1778
|
-
const lines = markdown.split("\n");
|
|
1779
|
-
const idCounts = {};
|
|
1780
|
-
let inCodeBlock = false;
|
|
1781
|
-
let inYamlBlock = false;
|
|
1782
|
-
for (const line of lines) {
|
|
1783
|
-
if (opts.skipCodeAndYamlBlocks) {
|
|
1784
|
-
if (line.startsWith("```")) {
|
|
1785
|
-
inCodeBlock = !inCodeBlock;
|
|
1786
|
-
continue;
|
|
1787
|
-
}
|
|
1788
|
-
if (line === "---") {
|
|
1789
|
-
inYamlBlock = !inYamlBlock;
|
|
1790
|
-
continue;
|
|
1791
|
-
}
|
|
1792
|
-
if (inCodeBlock || inYamlBlock) continue;
|
|
1793
|
-
}
|
|
1794
|
-
const pattern = new RegExp(`^(#{1,${opts.maxLevel}})\\s+(.+)`);
|
|
1795
|
-
const match = line.match(pattern);
|
|
1796
|
-
if (!match) continue;
|
|
1797
|
-
const level = match[1].length;
|
|
1798
|
-
if (level > opts.maxLevel) continue;
|
|
1799
|
-
let title = match[2].trim();
|
|
1800
|
-
if (opts.stripFormattingMarkers) {
|
|
1801
|
-
title = stripInlineMarkdown(title).trim();
|
|
1802
|
-
}
|
|
1803
|
-
let baseId = title;
|
|
1804
|
-
if (opts.removeEmojis) {
|
|
1805
|
-
baseId = baseId.replace(
|
|
1806
|
-
/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu,
|
|
1807
|
-
""
|
|
1808
|
-
).trim();
|
|
1809
|
-
}
|
|
1810
|
-
baseId = baseId.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/^-+|-+$/g, "");
|
|
1811
|
-
const cleanId = baseId || `section-${sections.length + 1}`;
|
|
1812
|
-
let id = cleanId;
|
|
1813
|
-
if (opts.handleDuplicateIds) {
|
|
1814
|
-
if (idCounts[cleanId]) {
|
|
1815
|
-
idCounts[cleanId]++;
|
|
1816
|
-
id = `${cleanId}-${idCounts[cleanId]}`;
|
|
1817
|
-
} else {
|
|
1818
|
-
idCounts[cleanId] = 1;
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
sections.push({ id, title, level });
|
|
1822
|
-
}
|
|
1823
|
-
return sections;
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
// src/utils/embed-url-converters.ts
|
|
1827
|
-
function toGoogleSheetsEmbedUrl(url) {
|
|
1828
|
-
if (url.includes("/htmlembed")) return url;
|
|
1829
|
-
const match = url.match(/\/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);
|
|
1830
|
-
if (!match) return url;
|
|
1831
|
-
const gidMatch = url.match(/[#?&]gid=(\d+)/);
|
|
1832
|
-
const gid = gidMatch ? gidMatch[1] : "0";
|
|
1833
|
-
return `https://docs.google.com/spreadsheets/d/${match[1]}/htmlembed?widget=true&chrome=false&headers=false&gid=${gid}`;
|
|
1834
|
-
}
|
|
1835
|
-
function toGoogleSheetsOriginalUrl(url) {
|
|
1836
|
-
const match = url.match(/\/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);
|
|
1837
|
-
if (!match) return url;
|
|
1838
|
-
const gidMatch = url.match(/[#?&]gid=(\d+)/);
|
|
1839
|
-
const gid = gidMatch ? gidMatch[1] : "0";
|
|
1840
|
-
return `https://docs.google.com/spreadsheets/d/${match[1]}/edit#gid=${gid}`;
|
|
1841
|
-
}
|
|
1842
|
-
function toFigmaEmbedUrl(url, opts) {
|
|
1843
|
-
if (url.includes("embed.figma.com")) return url;
|
|
1844
|
-
if (url.includes("figma.com/embed")) return url;
|
|
1845
|
-
const match = url.match(
|
|
1846
|
-
/figma\.com\/(design|file|proto|board|slides|deck)\/([a-zA-Z0-9]+)(?:\/([^?]*))?(\?.*)?$/
|
|
1847
|
-
);
|
|
1848
|
-
if (match) {
|
|
1849
|
-
const [, urlType, fileKey, titleSlug, queryString] = match;
|
|
1850
|
-
const isSlides = urlType === "slides" || urlType === "deck";
|
|
1851
|
-
const embedType = urlType === "proto" ? "proto" : isSlides ? opts?.slidesView === "browse" ? "slides" : "deck" : "design";
|
|
1852
|
-
const pathSuffix = titleSlug ? `/${titleSlug}` : "";
|
|
1853
|
-
const params = new URLSearchParams(queryString?.replace(/^\?/, "") || "");
|
|
1854
|
-
if (!params.has("embed-host")) {
|
|
1855
|
-
params.set("embed-host", "flamingo");
|
|
1856
|
-
}
|
|
1857
|
-
const clientId = process.env.NEXT_PUBLIC_FIGMA_CLIENT_ID;
|
|
1858
|
-
if (clientId && !params.has("client-id")) {
|
|
1859
|
-
params.set("client-id", clientId);
|
|
1860
|
-
}
|
|
1861
|
-
return `https://embed.figma.com/${embedType}/${fileKey}${pathSuffix}?${params.toString()}`;
|
|
1862
|
-
}
|
|
1863
|
-
return `https://www.figma.com/embed?embed-host=flamingo&url=${encodeURIComponent(url)}`;
|
|
1864
|
-
}
|
|
1865
|
-
function isFigmaSlidesUrl(url) {
|
|
1866
|
-
if (!url) return false;
|
|
1867
|
-
return /(?:www\.|embed\.)?figma\.com\/(?:slides|deck)\/[a-zA-Z0-9]+/.test(url);
|
|
1868
|
-
}
|
|
1869
|
-
function toFigmaOriginalUrl(url) {
|
|
1870
|
-
if (url.includes("embed.figma.com")) {
|
|
1871
|
-
return url.replace("embed.figma.com", "www.figma.com").replace(/\?.*$/, "");
|
|
1872
|
-
}
|
|
1873
|
-
return url.replace(/\?.*$/, "");
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
// src/utils/index.ts
|
|
1877
|
-
init_cn();
|
|
1878
|
-
init_platform_domains();
|
|
1879
|
-
init_cn();
|
|
1880
|
-
|
|
1881
|
-
// src/components/navigation/sticky-section-nav.tsx
|
|
1882
|
-
import { useEffect, useState, useCallback, useRef } from "react";
|
|
1883
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1884
|
-
function StickySectionNav({
|
|
1885
|
-
sections,
|
|
1886
|
-
activeSection,
|
|
1887
|
-
onSectionClick,
|
|
1888
|
-
className,
|
|
1889
|
-
ribbonPosition = "left",
|
|
1890
|
-
ribbonColor = "#FFC008"
|
|
1891
|
-
}) {
|
|
1892
|
-
const navHeight = sections.length * 40;
|
|
1893
|
-
return /* @__PURE__ */ jsxs("nav", { className: cn("bg-ods-bg relative", className), children: [
|
|
1894
|
-
/* @__PURE__ */ jsx(
|
|
1895
|
-
"div",
|
|
1896
|
-
{
|
|
1897
|
-
className: "absolute bg-ods-border",
|
|
1898
|
-
style: {
|
|
1899
|
-
width: "1px",
|
|
1900
|
-
height: `${navHeight}px`,
|
|
1901
|
-
[ribbonPosition === "left" ? "left" : "right"]: "-2px",
|
|
1902
|
-
top: "0px"
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
),
|
|
1906
|
-
sections.map((section) => /* @__PURE__ */ jsxs(
|
|
1907
|
-
"div",
|
|
1908
|
-
{
|
|
1909
|
-
className: "relative w-full h-10 transition-all duration-200 flex items-stretch",
|
|
1910
|
-
children: [
|
|
1911
|
-
activeSection === section.id && /* @__PURE__ */ jsx(
|
|
1912
|
-
"div",
|
|
1913
|
-
{
|
|
1914
|
-
className: "absolute z-10 transition-all duration-200",
|
|
1915
|
-
style: {
|
|
1916
|
-
backgroundColor: ribbonColor,
|
|
1917
|
-
width: "4px",
|
|
1918
|
-
height: "24px",
|
|
1919
|
-
[ribbonPosition === "left" ? "left" : "right"]: "-2px",
|
|
1920
|
-
top: "8px",
|
|
1921
|
-
borderRadius: "2px"
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
),
|
|
1925
|
-
/* @__PURE__ */ jsx(
|
|
1926
|
-
"button",
|
|
1927
|
-
{
|
|
1928
|
-
onClick: () => onSectionClick(section.id),
|
|
1929
|
-
className: "flex-1 flex items-center gap-2 px-3 py-2 cursor-pointer relative",
|
|
1930
|
-
children: /* @__PURE__ */ jsx("span", { className: cn(
|
|
1931
|
-
"text-left font-['DM_Sans'] text-[14px] font-medium tracking-[-0.02em] leading-[1.43em] transition-all duration-200",
|
|
1932
|
-
activeSection === section.id ? "text-ods-text-primary" : "text-ods-text-secondary hover:text-ods-text-primary"
|
|
1933
|
-
), children: section.label })
|
|
1934
|
-
}
|
|
1935
|
-
)
|
|
1936
|
-
]
|
|
1937
|
-
},
|
|
1938
|
-
section.id
|
|
1939
|
-
))
|
|
1940
|
-
] });
|
|
1941
|
-
}
|
|
1942
|
-
function useSectionNavigation(sections, options) {
|
|
1943
|
-
const [activeSection, setActiveSection] = useState(sections[0]?.id || "");
|
|
1944
|
-
const isScrollingFromClick = useRef(false);
|
|
1945
|
-
const { offset = 100 } = options || {};
|
|
1946
|
-
const handleSectionClick = useCallback((sectionId) => {
|
|
1947
|
-
const targetElement = document.getElementById(sectionId);
|
|
1948
|
-
if (!targetElement) return;
|
|
1949
|
-
isScrollingFromClick.current = true;
|
|
1950
|
-
setActiveSection(sectionId);
|
|
1951
|
-
scrollElementIntoView(targetElement, { headerOffset: offset });
|
|
1952
|
-
setTimeout(() => {
|
|
1953
|
-
isScrollingFromClick.current = false;
|
|
1954
|
-
}, 500);
|
|
1955
|
-
}, [offset]);
|
|
1956
|
-
useEffect(() => {
|
|
1957
|
-
sections.forEach((section) => {
|
|
1958
|
-
if (section.ref.current && !section.ref.current.id) {
|
|
1959
|
-
section.ref.current.id = section.id;
|
|
1960
|
-
}
|
|
1961
|
-
});
|
|
1962
|
-
}, [sections]);
|
|
1963
|
-
useEffect(() => {
|
|
1964
|
-
const handleScroll = () => {
|
|
1965
|
-
if (isScrollingFromClick.current) return;
|
|
1966
|
-
const scrollPosition = window.scrollY + offset + 50;
|
|
1967
|
-
let currentSection = sections[0]?.id || "";
|
|
1968
|
-
for (let i = sections.length - 1; i >= 0; i--) {
|
|
1969
|
-
const element = document.getElementById(sections[i].id);
|
|
1970
|
-
if (element && scrollPosition >= element.offsetTop) {
|
|
1971
|
-
currentSection = sections[i].id;
|
|
1972
|
-
break;
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
setActiveSection(currentSection);
|
|
1976
|
-
};
|
|
1977
|
-
let scrollTimer;
|
|
1978
|
-
const throttledScroll = () => {
|
|
1979
|
-
clearTimeout(scrollTimer);
|
|
1980
|
-
scrollTimer = setTimeout(handleScroll, 100);
|
|
1981
|
-
};
|
|
1982
|
-
window.addEventListener("scroll", throttledScroll);
|
|
1983
|
-
handleScroll();
|
|
1984
|
-
return () => {
|
|
1985
|
-
window.removeEventListener("scroll", throttledScroll);
|
|
1986
|
-
clearTimeout(scrollTimer);
|
|
1987
|
-
};
|
|
1988
|
-
}, [sections, offset]);
|
|
1989
|
-
return {
|
|
1990
|
-
activeSection,
|
|
1991
|
-
handleSectionClick
|
|
1992
|
-
};
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
// src/components/navigation/multi-level-navigation.tsx
|
|
1996
|
-
init_cn();
|
|
1997
|
-
import { ChevronDown, ChevronUp, FileText, Folder, FolderOpen } from "lucide-react";
|
|
1998
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1999
|
-
var DEFAULT_FOLDER_INDEX_FILE2 = DEFAULT_FOLDER_INDEX_FILE;
|
|
2000
|
-
function isNodeVisuallySelected(node, selectedPath, folderIndexFile = DEFAULT_FOLDER_INDEX_FILE2) {
|
|
2001
|
-
const FOLDER_INDEX_FILE = folderIndexFile.toLowerCase();
|
|
2002
|
-
if (selectedPath === node.path) {
|
|
2003
|
-
return !(node.type === "folder" && node.hasReadme);
|
|
2004
|
-
}
|
|
2005
|
-
if (node.type !== "file") return false;
|
|
2006
|
-
const pathLower = node.path.toLowerCase();
|
|
2007
|
-
if (selectedPath !== "" && pathLower === `${selectedPath.toLowerCase()}/${FOLDER_INDEX_FILE}`) {
|
|
2008
|
-
return true;
|
|
2009
|
-
}
|
|
2010
|
-
if (selectedPath === "" && pathLower === FOLDER_INDEX_FILE) {
|
|
2011
|
-
return true;
|
|
2012
|
-
}
|
|
2013
|
-
return false;
|
|
2014
|
-
}
|
|
2015
|
-
function MultiLevelNavigation({
|
|
2016
|
-
nodes,
|
|
2017
|
-
selectedPath,
|
|
2018
|
-
expandedNodes,
|
|
2019
|
-
onNodeClick,
|
|
2020
|
-
onToggleExpand,
|
|
2021
|
-
isLoading,
|
|
2022
|
-
className,
|
|
2023
|
-
folderIndexFile = DEFAULT_FOLDER_INDEX_FILE2
|
|
2024
|
-
}) {
|
|
2025
|
-
if (isLoading) {
|
|
2026
|
-
return /* @__PURE__ */ jsx2("div", { className: cn("space-y-2", className), children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx2("div", { className: "p-3 bg-ods-skeleton rounded-lg animate-pulse h-12" }, i)) });
|
|
2027
|
-
}
|
|
2028
|
-
return /* @__PURE__ */ jsx2("div", { className: cn("space-y-2", className), role: "list", "aria-label": "Navigation list", children: nodes.map((node) => /* @__PURE__ */ jsx2(
|
|
2029
|
-
NavigationItem,
|
|
2030
|
-
{
|
|
2031
|
-
node,
|
|
2032
|
-
selectedPath,
|
|
2033
|
-
expandedNodes,
|
|
2034
|
-
onNodeClick,
|
|
2035
|
-
onToggleExpand,
|
|
2036
|
-
level: 0,
|
|
2037
|
-
folderIndexFile
|
|
2038
|
-
},
|
|
2039
|
-
node.id
|
|
2040
|
-
)) });
|
|
2041
|
-
}
|
|
2042
|
-
function NavigationItem({
|
|
2043
|
-
node,
|
|
2044
|
-
selectedPath,
|
|
2045
|
-
expandedNodes,
|
|
2046
|
-
onNodeClick,
|
|
2047
|
-
onToggleExpand,
|
|
2048
|
-
level,
|
|
2049
|
-
folderIndexFile
|
|
2050
|
-
}) {
|
|
2051
|
-
const isExpanded = expandedNodes.has(node.id);
|
|
2052
|
-
const isSelected = isNodeVisuallySelected(node, selectedPath, folderIndexFile);
|
|
2053
|
-
const hasChildren = node.children && node.children.length > 0;
|
|
2054
|
-
return /* @__PURE__ */ jsxs2("div", { className: "space-y-1", role: "listitem", children: [
|
|
2055
|
-
/* @__PURE__ */ jsx2("div", { className: cn("px-2", level > 0 && "ml-4"), children: /* @__PURE__ */ jsxs2(
|
|
2056
|
-
"div",
|
|
2057
|
-
{
|
|
2058
|
-
className: cn(
|
|
2059
|
-
"w-full rounded-lg transition-all duration-150 border relative",
|
|
2060
|
-
isSelected ? "bg-ods-border border-ods-border" : "bg-ods-card border-ods-border"
|
|
2061
|
-
),
|
|
2062
|
-
children: [
|
|
2063
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center relative", children: [
|
|
2064
|
-
/* @__PURE__ */ jsxs2(
|
|
2065
|
-
"button",
|
|
2066
|
-
{
|
|
2067
|
-
className: cn(
|
|
2068
|
-
"w-full flex items-center h-12 px-2 rounded-lg text-[16px] font-medium font-['DM_Sans'] transition-all duration-150 leading-[1.33em] text-ods-text-primary",
|
|
2069
|
-
!isSelected && "hover:bg-ods-bg-secondary",
|
|
2070
|
-
hasChildren && "pr-12"
|
|
2071
|
-
),
|
|
2072
|
-
onClick: () => onNodeClick(node),
|
|
2073
|
-
"aria-label": `${isSelected ? "Selected" : "Select"} ${node.name}`,
|
|
2074
|
-
children: [
|
|
2075
|
-
/* @__PURE__ */ jsx2("span", { className: "flex-shrink-0 mr-2", children: node.type === "folder" ? isExpanded ? /* @__PURE__ */ jsx2(FolderOpen, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(Folder, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(FileText, { className: "h-4 w-4" }) }),
|
|
2076
|
-
/* @__PURE__ */ jsx2("span", { className: "text-left truncate flex-1 min-w-0", children: node.name.endsWith(".md") ? node.name.replace(".md", "") : node.name }),
|
|
2077
|
-
node.type === "folder" && node.hasReadme && /* @__PURE__ */ jsx2("span", { className: "text-[10px] bg-ods-bg-secondary text-ods-text-tertiary px-1.5 py-0.5 rounded mr-2", children: "README" })
|
|
2078
|
-
]
|
|
2079
|
-
}
|
|
2080
|
-
),
|
|
2081
|
-
hasChildren && /* @__PURE__ */ jsx2(
|
|
2082
|
-
"button",
|
|
2083
|
-
{
|
|
2084
|
-
className: "absolute right-0 top-0 flex items-center justify-center w-12 h-12 text-ods-text-secondary hover:text-ods-text-primary transition-colors duration-150",
|
|
2085
|
-
onClick: (e) => {
|
|
2086
|
-
e.stopPropagation();
|
|
2087
|
-
if (onToggleExpand) {
|
|
2088
|
-
onToggleExpand(node.id);
|
|
2089
|
-
} else {
|
|
2090
|
-
onNodeClick(node);
|
|
2091
|
-
}
|
|
2092
|
-
},
|
|
2093
|
-
"aria-label": `${isExpanded ? "Collapse" : "Expand"} ${node.name}`,
|
|
2094
|
-
children: isExpanded ? /* @__PURE__ */ jsx2(ChevronUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(ChevronDown, { className: "h-4 w-4" })
|
|
2095
|
-
}
|
|
2096
|
-
)
|
|
2097
|
-
] }),
|
|
2098
|
-
isSelected && /* @__PURE__ */ jsx2("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 w-1 h-[calc(100%-8px)] bg-ods-accent rounded-l" })
|
|
2099
|
-
]
|
|
2100
|
-
}
|
|
2101
|
-
) }),
|
|
2102
|
-
hasChildren && isExpanded && /* @__PURE__ */ jsx2("div", { className: "space-y-1", children: node.children.map((child) => /* @__PURE__ */ jsx2(
|
|
2103
|
-
NavigationItem,
|
|
2104
|
-
{
|
|
2105
|
-
node: child,
|
|
2106
|
-
selectedPath,
|
|
2107
|
-
expandedNodes,
|
|
2108
|
-
onNodeClick,
|
|
2109
|
-
onToggleExpand,
|
|
2110
|
-
level: level + 1,
|
|
2111
|
-
folderIndexFile
|
|
2112
|
-
},
|
|
2113
|
-
child.id
|
|
2114
|
-
)) })
|
|
2115
|
-
] });
|
|
2116
|
-
}
|
|
2117
|
-
function MobileNavigationDropdown({
|
|
2118
|
-
nodes,
|
|
2119
|
-
selectedPath,
|
|
2120
|
-
expandedNodes,
|
|
2121
|
-
onNodeClick,
|
|
2122
|
-
onToggleExpand,
|
|
2123
|
-
isLoading,
|
|
2124
|
-
className,
|
|
2125
|
-
folderIndexFile = DEFAULT_FOLDER_INDEX_FILE2
|
|
2126
|
-
}) {
|
|
2127
|
-
if (isLoading) {
|
|
2128
|
-
return /* @__PURE__ */ jsx2("div", { className: cn("space-y-2", className), children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx2("div", { className: "p-3 bg-ods-skeleton rounded-lg animate-pulse h-10" }, i)) });
|
|
2129
|
-
}
|
|
2130
|
-
return /* @__PURE__ */ jsx2("div", { className: cn("space-y-2", className), children: nodes.map((node) => /* @__PURE__ */ jsx2(
|
|
2131
|
-
MobileNavigationItem,
|
|
2132
|
-
{
|
|
2133
|
-
node,
|
|
2134
|
-
selectedPath,
|
|
2135
|
-
expandedNodes,
|
|
2136
|
-
onNodeClick,
|
|
2137
|
-
onToggleExpand,
|
|
2138
|
-
level: 0,
|
|
2139
|
-
folderIndexFile
|
|
2140
|
-
},
|
|
2141
|
-
node.id
|
|
2142
|
-
)) });
|
|
2143
|
-
}
|
|
2144
|
-
function MobileNavigationItem({
|
|
2145
|
-
node,
|
|
2146
|
-
selectedPath,
|
|
2147
|
-
expandedNodes,
|
|
2148
|
-
onNodeClick,
|
|
2149
|
-
onToggleExpand,
|
|
2150
|
-
level,
|
|
2151
|
-
folderIndexFile
|
|
2152
|
-
}) {
|
|
2153
|
-
const isExpanded = expandedNodes.has(node.id);
|
|
2154
|
-
const isSelected = isNodeVisuallySelected(node, selectedPath, folderIndexFile);
|
|
2155
|
-
const hasChildren = node.children && node.children.length > 0;
|
|
2156
|
-
return /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
|
|
2157
|
-
/* @__PURE__ */ jsx2("div", { className: cn("px-2", level > 0 && "ml-3"), children: /* @__PURE__ */ jsxs2(
|
|
2158
|
-
"div",
|
|
2159
|
-
{
|
|
2160
|
-
className: cn(
|
|
2161
|
-
"w-full rounded-lg transition-all duration-150 border relative",
|
|
2162
|
-
isSelected ? "bg-ods-border border-ods-border" : "bg-ods-card border-ods-border"
|
|
2163
|
-
),
|
|
2164
|
-
children: [
|
|
2165
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center relative", children: [
|
|
2166
|
-
/* @__PURE__ */ jsxs2(
|
|
2167
|
-
"button",
|
|
2168
|
-
{
|
|
2169
|
-
className: cn(
|
|
2170
|
-
"w-full flex items-center h-11 px-2 rounded-lg text-[15px] font-medium font-['DM_Sans'] transition-all duration-150 leading-[1.33em] text-ods-text-primary",
|
|
2171
|
-
!isSelected && "hover:bg-ods-bg-secondary",
|
|
2172
|
-
hasChildren && "pr-11"
|
|
2173
|
-
),
|
|
2174
|
-
onClick: () => onNodeClick(node),
|
|
2175
|
-
children: [
|
|
2176
|
-
/* @__PURE__ */ jsx2("span", { className: "flex-shrink-0 mr-2", children: node.type === "folder" ? isExpanded ? /* @__PURE__ */ jsx2(FolderOpen, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(Folder, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(FileText, { className: "h-4 w-4" }) }),
|
|
2177
|
-
/* @__PURE__ */ jsx2("span", { className: "text-left truncate flex-1 min-w-0 text-sm", children: node.name.endsWith(".md") ? node.name.replace(".md", "") : node.name }),
|
|
2178
|
-
node.type === "folder" && node.hasReadme && /* @__PURE__ */ jsx2("span", { className: "text-[10px] bg-ods-bg-secondary text-ods-text-tertiary px-1.5 py-0.5 rounded mr-2", children: "README" })
|
|
2179
|
-
]
|
|
2180
|
-
}
|
|
2181
|
-
),
|
|
2182
|
-
hasChildren && /* @__PURE__ */ jsx2(
|
|
2183
|
-
"button",
|
|
2184
|
-
{
|
|
2185
|
-
className: "absolute right-0 top-0 flex items-center justify-center w-11 h-11 text-ods-text-secondary hover:text-ods-text-primary transition-colors duration-150",
|
|
2186
|
-
onClick: (e) => {
|
|
2187
|
-
e.stopPropagation();
|
|
2188
|
-
if (onToggleExpand) {
|
|
2189
|
-
onToggleExpand(node.id);
|
|
2190
|
-
} else {
|
|
2191
|
-
onNodeClick(node);
|
|
2192
|
-
}
|
|
2193
|
-
},
|
|
2194
|
-
"aria-label": `${isExpanded ? "Collapse" : "Expand"} ${node.name}`,
|
|
2195
|
-
children: isExpanded ? /* @__PURE__ */ jsx2(ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3.5 w-3.5" })
|
|
2196
|
-
}
|
|
2197
|
-
)
|
|
2198
|
-
] }),
|
|
2199
|
-
isSelected && /* @__PURE__ */ jsx2("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 w-1 h-[calc(100%-8px)] bg-ods-accent rounded-l" })
|
|
2200
|
-
]
|
|
2201
|
-
}
|
|
2202
|
-
) }),
|
|
2203
|
-
hasChildren && isExpanded && /* @__PURE__ */ jsx2("div", { className: "space-y-1", children: node.children.map((child) => /* @__PURE__ */ jsx2(
|
|
2204
|
-
MobileNavigationItem,
|
|
2205
|
-
{
|
|
2206
|
-
node: child,
|
|
2207
|
-
selectedPath,
|
|
2208
|
-
expandedNodes,
|
|
2209
|
-
onNodeClick,
|
|
2210
|
-
onToggleExpand,
|
|
2211
|
-
level: level + 1,
|
|
2212
|
-
folderIndexFile
|
|
2213
|
-
},
|
|
2214
|
-
child.id
|
|
2215
|
-
)) })
|
|
2216
|
-
] });
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
export {
|
|
2220
|
-
getPlatformAccentColor,
|
|
2221
|
-
getCurrentPlatform,
|
|
2222
|
-
getReadableTextColor,
|
|
2223
|
-
HEX_PATTERN,
|
|
2224
|
-
hexToRgb,
|
|
2225
|
-
rgbToHex,
|
|
2226
|
-
deriveHoverColor,
|
|
2227
|
-
deriveActiveColor,
|
|
2228
|
-
rgbToHsl,
|
|
2229
|
-
hslToRgb,
|
|
2230
|
-
MESSAGE_TYPE,
|
|
2231
|
-
SCROLL_ANCHOR,
|
|
2232
|
-
CHAT_ATTACHMENT_VIEW_URL_PREFIX,
|
|
2233
|
-
CHAT_ATTACHMENT_VIEW_TOKEN_QUERY_PARAM,
|
|
2234
|
-
ANTHROPIC_SUPPORTED_IMAGE_MIME,
|
|
2235
|
-
buildChatAttachmentViewUrl,
|
|
2236
|
-
escapeMarkdownInline,
|
|
2237
|
-
formatChatAttachmentMarkdownForBubble,
|
|
2238
|
-
CHAT_ATTACHMENT_VIEW_URL_PREFIX_REGEX_ESCAPED,
|
|
2239
|
-
CHAT_ATTACHMENT_MARKDOWN_PATTERN,
|
|
2240
|
-
stripChatAttachmentMarkdown,
|
|
2241
|
-
formatReleaseDate,
|
|
2242
|
-
formatDateShort,
|
|
2243
|
-
formatDateSlashUTC,
|
|
2244
|
-
resolveReleaseCover,
|
|
2245
|
-
releaseTypeToBadgeColor,
|
|
2246
|
-
getStatusColorScheme,
|
|
2247
|
-
CUSTOM_ITEM_ID,
|
|
2248
|
-
getTaskTypeLabel,
|
|
2249
|
-
extractItems,
|
|
2250
|
-
extractItemId,
|
|
2251
|
-
SCROLL_ANCHOR_WIRE_KEY,
|
|
2252
|
-
parseScrollAnchor,
|
|
2253
|
-
AUTO_CONTINUATION_DIRECTIVE_PREFIX,
|
|
2254
|
-
buildAutoContinuationDirective,
|
|
2255
|
-
flattenAssistantContent,
|
|
2256
|
-
parseWireCommandOverride,
|
|
2257
|
-
sanitizeTitleForChat,
|
|
2258
|
-
formatSingularLookupInvocation,
|
|
2259
|
-
extractEntityIdFilter,
|
|
2260
|
-
buildDiscussAddendum,
|
|
2261
|
-
clickupTaskUrl,
|
|
2262
|
-
cn2 as cn,
|
|
2263
|
-
delay,
|
|
2264
|
-
generateRandomString,
|
|
2265
|
-
truncateString,
|
|
2266
|
-
serializeJsonLd,
|
|
2267
|
-
deepClone,
|
|
2268
|
-
getSlackCommunityJoinUrl,
|
|
2269
|
-
OS_PLATFORMS,
|
|
2270
|
-
DEFAULT_OS_PLATFORM,
|
|
2271
|
-
isValidEmailDomain,
|
|
2272
|
-
validateEmailDomain,
|
|
2273
|
-
validateEmailDomainList,
|
|
2274
|
-
cleanEmailDomain,
|
|
2275
|
-
getConfidenceColorClass,
|
|
2276
|
-
getConfidenceLevel,
|
|
2277
|
-
getConfidenceBorderClass,
|
|
2278
|
-
getConfidenceTextClass,
|
|
2279
|
-
getConfidenceBgClass,
|
|
2280
|
-
getConfidenceLabel,
|
|
2281
|
-
DEV_SECTION_PARAM_KEYS,
|
|
2282
|
-
devSectionAnchorId,
|
|
2283
|
-
normalizeToolType,
|
|
2284
|
-
normalizeToolTypeWithFallback,
|
|
2285
|
-
toToolLabel,
|
|
2286
|
-
isValidToolType,
|
|
2287
|
-
getToolTypeAliases,
|
|
2288
|
-
getToolLabel,
|
|
2289
|
-
ShellTypeValues,
|
|
2290
|
-
SHELL_TYPES,
|
|
2291
|
-
shellLabels,
|
|
2292
|
-
getShellLabel,
|
|
2293
|
-
getShellIcon,
|
|
2294
|
-
OSTypeValues,
|
|
2295
|
-
OS_TYPES,
|
|
2296
|
-
osLabels,
|
|
2297
|
-
normalizeOSType,
|
|
2298
|
-
getOSLabel,
|
|
2299
|
-
getOSIcon,
|
|
2300
|
-
getOSTypeDefinition,
|
|
2301
|
-
getOSPlatformId,
|
|
2302
|
-
isOSPlatform,
|
|
2303
|
-
getCountryPhoneData,
|
|
2304
|
-
getCountryByCode,
|
|
2305
|
-
validatePhoneNumber,
|
|
2306
|
-
formatPhoneE164,
|
|
2307
|
-
GENERIC_EMAIL_DOMAINS,
|
|
2308
|
-
extractDomainFromEmail,
|
|
2309
|
-
normalizeDomain,
|
|
2310
|
-
isGenericDomain,
|
|
2311
|
-
hasGenericEmailDomain,
|
|
2312
|
-
isGenericWebsiteDomain,
|
|
2313
|
-
DESIGN_PALETTE,
|
|
2314
|
-
getContrastRatio,
|
|
2315
|
-
extractDominantColor,
|
|
2316
|
-
getBestContrastColor,
|
|
2317
|
-
analyzeImageColor,
|
|
2318
|
-
extractImageEdgeColorAsync,
|
|
2319
|
-
formatTicketRelativeTime,
|
|
2320
|
-
formatTicketFullTimestamp,
|
|
2321
|
-
formatRelativeTime,
|
|
2322
|
-
formatAbsoluteDate,
|
|
2323
|
-
formatDateTime,
|
|
2324
|
-
getDetailedTimeDifference,
|
|
2325
|
-
isToday,
|
|
2326
|
-
isWithinMinutes,
|
|
2327
|
-
createUTCTimestamp,
|
|
2328
|
-
readLeadingDecisionFrame,
|
|
2329
|
-
AVAILABLE_SVG_ICONS,
|
|
2330
|
-
releaseTypeOptions,
|
|
2331
|
-
releaseStatusOptions,
|
|
2332
|
-
changelogLabels,
|
|
2333
|
-
SEMVER_REGEX,
|
|
2334
|
-
TASK_TYPE_LABELS,
|
|
2335
|
-
TASK_TYPE_TEXT_COLORS,
|
|
2336
|
-
TMCG_ROLES,
|
|
2337
|
-
TMCG_ROLE_DISPLAY_NAMES,
|
|
2338
|
-
TMCG_SOCIAL_PLATFORMS,
|
|
2339
|
-
ROADMAP_STATUS_OPTIONS,
|
|
2340
|
-
DELIVERY_TASK_TYPE_OPTIONS,
|
|
2341
|
-
TICKET_STATUS_OPTIONS,
|
|
2342
|
-
OPENFRAME_DEV_SECTIONS,
|
|
2343
|
-
canonicalContentRefType,
|
|
2344
|
-
buildListUrl,
|
|
2345
|
-
faqSectionSlug,
|
|
2346
|
-
faqItemAnchor,
|
|
2347
|
-
parseFaqHash,
|
|
2348
|
-
CONTENT_REF_GROUPS,
|
|
2349
|
-
getContentRefLabel,
|
|
2350
|
-
getContentRefLabelOrTitleCase,
|
|
2351
|
-
orderContentRefTypes,
|
|
2352
|
-
buildSuggestionUrl,
|
|
2353
|
-
DEFAULT_FOLDER_INDEX_FILE,
|
|
2354
|
-
stripFolderIndexFromPath,
|
|
2355
|
-
findDocNodeByPath,
|
|
2356
|
-
getDocAncestorNodeIds,
|
|
2357
|
-
resolveContentFetchPath,
|
|
2358
|
-
getInitialExpandedNodeIds,
|
|
2359
|
-
getDocSourceDefaultPath,
|
|
2360
|
-
toDocSlug,
|
|
2361
|
-
pathToNodeId,
|
|
2362
|
-
normalizeDocPath,
|
|
2363
|
-
formatDocName,
|
|
2364
|
-
sortTreeChildren,
|
|
2365
|
-
buildDocumentTree,
|
|
2366
|
-
stripInlineMarkdown,
|
|
2367
|
-
markdownToPlainText,
|
|
2368
|
-
extractSections,
|
|
2369
|
-
toGoogleSheetsEmbedUrl,
|
|
2370
|
-
toGoogleSheetsOriginalUrl,
|
|
2371
|
-
toFigmaEmbedUrl,
|
|
2372
|
-
isFigmaSlidesUrl,
|
|
2373
|
-
toFigmaOriginalUrl,
|
|
2374
|
-
StickySectionNav,
|
|
2375
|
-
useSectionNavigation,
|
|
2376
|
-
MultiLevelNavigation,
|
|
2377
|
-
MobileNavigationDropdown
|
|
2378
|
-
};
|
|
2379
|
-
//# sourceMappingURL=chunk-26PKDALD.js.map
|