@flamingo-stack/openframe-frontend-core 0.0.292 → 0.0.293
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6FHO73AP.js → chunk-26PKDALD.js} +7 -79
- package/dist/chunk-26PKDALD.js.map +1 -0
- package/dist/{chunk-B2U6INNO.js → chunk-2U2M2TG2.js} +4 -4
- package/dist/{chunk-OXOTKEYY.cjs → chunk-4W7NYJ3B.cjs} +23 -23
- package/dist/{chunk-OXOTKEYY.cjs.map → chunk-4W7NYJ3B.cjs.map} +1 -1
- package/dist/{chunk-KBKZYJRI.cjs → chunk-5E2HOSSH.cjs} +66 -19
- package/dist/chunk-5E2HOSSH.cjs.map +1 -0
- package/dist/{chunk-PZZGDS5I.cjs → chunk-6G2INVGG.cjs} +24 -22
- package/dist/chunk-6G2INVGG.cjs.map +1 -0
- package/dist/{chunk-CUQH4SHH.js → chunk-6GCI7JOE.js} +2 -2
- package/dist/{chunk-N6ZM5PYZ.js → chunk-7RIYT7ZH.js} +49 -2
- package/dist/chunk-7RIYT7ZH.js.map +1 -0
- package/dist/{chunk-E2YXRSDG.js → chunk-BRNHX6C6.js} +15 -13
- package/dist/chunk-BRNHX6C6.js.map +1 -0
- package/dist/{chunk-5FK7X3EE.js → chunk-DOMJSNXW.js} +8 -7
- package/dist/chunk-DOMJSNXW.js.map +1 -0
- package/dist/{chunk-VK4B6UGU.js → chunk-E4XABBSU.js} +16 -8
- package/dist/{chunk-VK4B6UGU.js.map → chunk-E4XABBSU.js.map} +1 -1
- package/dist/{chunk-DUIWR7RQ.js → chunk-EJXHZX2E.js} +3 -3
- package/dist/{chunk-5PELVUFT.cjs → chunk-EYEW6PTA.cjs} +44 -36
- package/dist/chunk-EYEW6PTA.cjs.map +1 -0
- package/dist/{chunk-HTYUZXQP.js → chunk-FJDPUPXC.js} +5 -5
- package/dist/{chunk-SLP4KXP6.js → chunk-FQJK446R.js} +8 -2
- package/dist/chunk-FQJK446R.js.map +1 -0
- package/dist/{chunk-JC5RN7ZS.cjs → chunk-FT4FCV7L.cjs} +6 -6
- package/dist/{chunk-JC5RN7ZS.cjs.map → chunk-FT4FCV7L.cjs.map} +1 -1
- package/dist/{chunk-ZHNL2IPK.cjs → chunk-J54Z3OCR.cjs} +8 -2
- package/dist/chunk-J54Z3OCR.cjs.map +1 -0
- package/dist/{chunk-2NJ44RTT.cjs → chunk-JSOMFVEV.cjs} +30 -30
- package/dist/{chunk-2NJ44RTT.cjs.map → chunk-JSOMFVEV.cjs.map} +1 -1
- package/dist/{chunk-Z6BK4XHH.cjs → chunk-KXCRGTRN.cjs} +10 -82
- package/dist/chunk-KXCRGTRN.cjs.map +1 -0
- package/dist/{chunk-5KD3S25X.cjs → chunk-LFGGF7OT.cjs} +139 -2
- package/dist/chunk-LFGGF7OT.cjs.map +1 -0
- package/dist/{chunk-N45M3TK3.js → chunk-NSPOYUBH.js} +2 -2
- package/dist/{chunk-TYZEMPPH.js → chunk-OQ6X7ZOC.js} +138 -1
- package/dist/chunk-OQ6X7ZOC.js.map +1 -0
- package/dist/{chunk-MDLWEJAV.cjs → chunk-RJL6PIOK.cjs} +454 -453
- package/dist/chunk-RJL6PIOK.cjs.map +1 -0
- package/dist/{chunk-IXDTNQF4.js → chunk-SOJCR63T.js} +4 -4
- package/dist/{chunk-5R5OODNE.cjs → chunk-TYMUKFP2.cjs} +40 -40
- package/dist/{chunk-5R5OODNE.cjs.map → chunk-TYMUKFP2.cjs.map} +1 -1
- package/dist/{chunk-CDJOKNCS.cjs → chunk-VTY7S2QG.cjs} +25 -19
- package/dist/chunk-VTY7S2QG.cjs.map +1 -0
- package/dist/{chunk-FFP2A77V.cjs → chunk-X3TSMCKX.cjs} +12 -12
- package/dist/{chunk-FFP2A77V.cjs.map → chunk-X3TSMCKX.cjs.map} +1 -1
- package/dist/{chunk-C667P6LZ.js → chunk-YICTMMXP.js} +13 -7
- package/dist/{chunk-C667P6LZ.js.map → chunk-YICTMMXP.js.map} +1 -1
- package/dist/{chunk-2BMVBPC7.cjs → chunk-YIGPRLQY.cjs} +9 -9
- package/dist/{chunk-2BMVBPC7.cjs.map → chunk-YIGPRLQY.cjs.map} +1 -1
- package/dist/components/chat/entity-cards/roadmap-card.d.ts +7 -1
- package/dist/components/chat/entity-cards/roadmap-card.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +7 -7
- package/dist/components/chat/index.js +6 -6
- package/dist/components/contact/index.cjs +8 -8
- package/dist/components/contact/index.js +7 -7
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/docs/use-document-tree.d.ts.map +1 -1
- package/dist/components/embeds/index.cjs +8 -8
- package/dist/components/embeds/index.js +7 -7
- package/dist/components/faq/faq-section.d.ts.map +1 -1
- package/dist/components/faq/index.cjs +8 -8
- package/dist/components/faq/index.js +7 -7
- package/dist/components/features/index.cjs +7 -7
- package/dist/components/features/index.js +6 -6
- package/dist/components/index.cjs +214 -193
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +50 -29
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +7 -7
- package/dist/components/navigation/index.js +6 -6
- package/dist/components/onboarding-guides/index.cjs +24 -24
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +8 -8
- package/dist/components/related-content/index.js +7 -7
- package/dist/components/shared/delivery/delivery-lists.d.ts.map +1 -1
- package/dist/components/shared/delivery/delivery-row.d.ts +8 -1
- package/dist/components/shared/delivery/delivery-row.d.ts.map +1 -1
- package/dist/components/shared/delivery/delivery-table.d.ts.map +1 -1
- package/dist/components/shared/roadmap/roadmap-grid.d.ts.map +1 -1
- package/dist/components/shared/roadmap/roadmap-view.d.ts.map +1 -1
- package/dist/components/tickets/help-center-card.d.ts +7 -1
- package/dist/components/tickets/help-center-card.d.ts.map +1 -1
- package/dist/components/tickets/help-center-list.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +82 -73
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.js +24 -15
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/tickets/ticket-center.d.ts.map +1 -1
- package/dist/components/tickets/ticket-row.d.ts +6 -1
- package/dist/components/tickets/ticket-row.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +7 -7
- package/dist/components/ui/index.js +6 -6
- package/dist/hooks/index.cjs +5 -3
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -2
- package/dist/hooks/use-scroll-to-hash.d.ts +17 -0
- package/dist/hooks/use-scroll-to-hash.d.ts.map +1 -0
- package/dist/index.cjs +19 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +19 -7
- package/dist/utils/dev-sections/dev-section-param-keys.d.ts +10 -0
- package/dist/utils/dev-sections/dev-section-param-keys.d.ts.map +1 -1
- package/dist/utils/index.cjs +71 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +67 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/same-page-hash-nav.d.ts +37 -0
- package/dist/utils/same-page-hash-nav.d.ts.map +1 -0
- package/dist/utils/source-icons.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/entity-cards/roadmap-card.tsx +8 -1
- package/src/components/docs/use-document-tree.ts +45 -3
- package/src/components/faq/faq-section.tsx +22 -9
- package/src/components/shared/delivery/delivery-lists.tsx +9 -0
- package/src/components/shared/delivery/delivery-row.tsx +15 -2
- package/src/components/shared/delivery/delivery-table.tsx +7 -1
- package/src/components/shared/roadmap/roadmap-grid.tsx +7 -0
- package/src/components/shared/roadmap/roadmap-view.tsx +11 -0
- package/src/components/tickets/help-center-card.tsx +9 -17
- package/src/components/tickets/help-center-list.tsx +13 -0
- package/src/components/tickets/ticket-center.tsx +2 -0
- package/src/components/tickets/ticket-row.tsx +7 -1
- package/src/hooks/index.ts +5 -0
- package/src/hooks/use-scroll-to-hash.ts +74 -0
- package/src/utils/.source-icons.md +1 -1
- package/src/utils/dev-sections/dev-section-param-keys.ts +14 -0
- package/src/utils/index.ts +17 -1
- package/src/utils/same-page-hash-nav.ts +115 -0
- package/src/utils/source-icons.ts +7 -1
- package/dist/chunk-5FK7X3EE.js.map +0 -1
- package/dist/chunk-5KD3S25X.cjs.map +0 -1
- package/dist/chunk-5PELVUFT.cjs.map +0 -1
- package/dist/chunk-6FHO73AP.js.map +0 -1
- package/dist/chunk-CDJOKNCS.cjs.map +0 -1
- package/dist/chunk-E2YXRSDG.js.map +0 -1
- package/dist/chunk-KBKZYJRI.cjs.map +0 -1
- package/dist/chunk-MDLWEJAV.cjs.map +0 -1
- package/dist/chunk-N6ZM5PYZ.js.map +0 -1
- package/dist/chunk-PZZGDS5I.cjs.map +0 -1
- package/dist/chunk-SLP4KXP6.js.map +0 -1
- package/dist/chunk-TYZEMPPH.js.map +0 -1
- package/dist/chunk-Z6BK4XHH.cjs.map +0 -1
- package/dist/chunk-ZHNL2IPK.cjs.map +0 -1
- /package/dist/{chunk-B2U6INNO.js.map → chunk-2U2M2TG2.js.map} +0 -0
- /package/dist/{chunk-CUQH4SHH.js.map → chunk-6GCI7JOE.js.map} +0 -0
- /package/dist/{chunk-DUIWR7RQ.js.map → chunk-EJXHZX2E.js.map} +0 -0
- /package/dist/{chunk-HTYUZXQP.js.map → chunk-FJDPUPXC.js.map} +0 -0
- /package/dist/{chunk-N45M3TK3.js.map → chunk-NSPOYUBH.js.map} +0 -0
- /package/dist/{chunk-IXDTNQF4.js.map → chunk-SOJCR63T.js.map} +0 -0
|
@@ -259,6 +259,138 @@ async function validateAndConsumeAccessCode(email, code, endpoints) {
|
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
// src/utils/scroll-into-view.ts
|
|
263
|
+
var activeRaf = 0;
|
|
264
|
+
var teardownActive = null;
|
|
265
|
+
function cancelActiveScroll() {
|
|
266
|
+
if (activeRaf) {
|
|
267
|
+
cancelAnimationFrame(activeRaf);
|
|
268
|
+
activeRaf = 0;
|
|
269
|
+
}
|
|
270
|
+
if (teardownActive) {
|
|
271
|
+
teardownActive();
|
|
272
|
+
teardownActive = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
276
|
+
function getScrollableAncestor(el) {
|
|
277
|
+
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
278
|
+
const overflowY = getComputedStyle(node).overflowY;
|
|
279
|
+
if ((overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight) {
|
|
280
|
+
return node;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
function scrollElementIntoView(target, options = {}) {
|
|
286
|
+
if (typeof window === "undefined" || !target) return;
|
|
287
|
+
const { headerOffset = 0, behavior = "smooth", adjustTargetY, durationMs = 320 } = options;
|
|
288
|
+
const container = getScrollableAncestor(target);
|
|
289
|
+
const readCurrent = () => container ? container.scrollTop : window.scrollY;
|
|
290
|
+
const writeTo = (y) => {
|
|
291
|
+
if (container) container.scrollTop = y;
|
|
292
|
+
else window.scrollTo(0, y);
|
|
293
|
+
};
|
|
294
|
+
const computeTarget = () => {
|
|
295
|
+
const raw = container ? container.scrollTop + (target.getBoundingClientRect().top - container.getBoundingClientRect().top) - headerOffset : target.getBoundingClientRect().top + window.scrollY - headerOffset;
|
|
296
|
+
const adjusted = adjustTargetY ? adjustTargetY(raw) : raw;
|
|
297
|
+
const maxScroll = container ? Math.max(0, container.scrollHeight - container.clientHeight) : Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
|
298
|
+
return Math.min(Math.max(0, adjusted), maxScroll);
|
|
299
|
+
};
|
|
300
|
+
cancelActiveScroll();
|
|
301
|
+
const prefersReduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
302
|
+
if (behavior === "instant" || behavior === "auto" || prefersReduced) {
|
|
303
|
+
writeTo(computeTarget());
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
let startY = null;
|
|
307
|
+
let startTime = 0;
|
|
308
|
+
const onUserGesture = () => cancelActiveScroll();
|
|
309
|
+
window.addEventListener("wheel", onUserGesture, { passive: true });
|
|
310
|
+
window.addEventListener("touchmove", onUserGesture, { passive: true });
|
|
311
|
+
teardownActive = () => {
|
|
312
|
+
window.removeEventListener("wheel", onUserGesture);
|
|
313
|
+
window.removeEventListener("touchmove", onUserGesture);
|
|
314
|
+
};
|
|
315
|
+
const step = (now) => {
|
|
316
|
+
if (startY === null) {
|
|
317
|
+
startY = readCurrent();
|
|
318
|
+
startTime = now;
|
|
319
|
+
}
|
|
320
|
+
const targetY = computeTarget();
|
|
321
|
+
const t = Math.min(1, (now - startTime) / durationMs);
|
|
322
|
+
const y = startY + (targetY - startY) * easeOutCubic(t);
|
|
323
|
+
writeTo(y);
|
|
324
|
+
if (t < 1) {
|
|
325
|
+
activeRaf = requestAnimationFrame(step);
|
|
326
|
+
} else {
|
|
327
|
+
writeTo(computeTarget());
|
|
328
|
+
activeRaf = 0;
|
|
329
|
+
if (teardownActive) {
|
|
330
|
+
teardownActive();
|
|
331
|
+
teardownActive = null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
activeRaf = requestAnimationFrame(step);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/utils/same-page-hash-nav.ts
|
|
339
|
+
var STICKY_HEADER_OFFSET_PX = 96;
|
|
340
|
+
var HUB_HEADER_OFFSET_PX = 80;
|
|
341
|
+
function normalizeHashFragment(hash) {
|
|
342
|
+
if (!hash) return "";
|
|
343
|
+
const second = hash.indexOf("#", 1);
|
|
344
|
+
return second < 0 ? hash : hash.slice(0, second);
|
|
345
|
+
}
|
|
346
|
+
function navigateSamePageHash(target, options = {}) {
|
|
347
|
+
if (typeof window === "undefined") return false;
|
|
348
|
+
const { headerOffset = 0, history: historyMode = "push" } = options;
|
|
349
|
+
const normalizedTarget = target.startsWith("#") ? window.location.pathname + window.location.search + target : target;
|
|
350
|
+
let url;
|
|
351
|
+
try {
|
|
352
|
+
url = new URL(normalizedTarget, window.location.href);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
if (url.origin !== window.location.origin || url.pathname !== window.location.pathname || url.search !== window.location.search) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
const current = window.location.pathname + window.location.search + window.location.hash;
|
|
360
|
+
const normalizedHash = normalizeHashFragment(url.hash);
|
|
361
|
+
if (process.env.NODE_ENV === "development" && normalizedHash !== url.hash) {
|
|
362
|
+
console.warn(
|
|
363
|
+
`[navigateSamePageHash] malformed fragment "${url.hash}" \u2192 normalizing to "${normalizedHash}". Fix the upstream composer.`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
const next = url.pathname + url.search + normalizedHash;
|
|
367
|
+
const id = normalizedHash && normalizedHash !== "#" ? normalizedHash.slice(1) : "";
|
|
368
|
+
if (!id && next !== current) return false;
|
|
369
|
+
if (next !== current) {
|
|
370
|
+
const oldURL = window.location.href;
|
|
371
|
+
if (historyMode === "replace") {
|
|
372
|
+
window.history.replaceState(null, "", next);
|
|
373
|
+
} else {
|
|
374
|
+
window.history.pushState(null, "", next);
|
|
375
|
+
}
|
|
376
|
+
window.dispatchEvent(new HashChangeEvent("hashchange", {
|
|
377
|
+
oldURL,
|
|
378
|
+
newURL: window.location.href
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
const el = id ? document.getElementById(id) : null;
|
|
382
|
+
if (id && !el && process.env.NODE_ENV === "development") {
|
|
383
|
+
console.warn(
|
|
384
|
+
`[navigateSamePageHash] anchor "#${id}" not found \u2014 scrolling to top.`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
scrollElementIntoView(_nullishCoalesce(el, () => ( document.documentElement)), {
|
|
388
|
+
behavior: "smooth",
|
|
389
|
+
headerOffset
|
|
390
|
+
});
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
262
394
|
// src/utils/humanity-signals.ts
|
|
263
395
|
var HONEYPOT_FIELD = "contact_url_confirm";
|
|
264
396
|
var ELAPSED_MS_FIELD = "form_elapsed_ms";
|
|
@@ -308,5 +440,10 @@ var splitCsvEnv = (s) => _nullishCoalesce(_optionalChain([s, 'optionalAccess', _
|
|
|
308
440
|
|
|
309
441
|
|
|
310
442
|
|
|
311
|
-
|
|
312
|
-
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
exports.platformIcons = platformIcons; exports.platformColors = platformColors; exports.platformDisplayNames = platformDisplayNames; exports.platformDescriptions = platformDescriptions; exports.platformSlogans = platformSlogans; exports.platformHexColors = platformHexColors; exports.platformIconNames = platformIconNames; exports.getDefaultColorForPlatform = getDefaultColorForPlatform; exports.getDefaultIconForPlatform = getDefaultIconForPlatform; exports.transformPlatformConfigsToOptions = transformPlatformConfigsToOptions; exports.getPlatformIcon = getPlatformIcon; exports.getPlatformColor = getPlatformColor; exports.getPlatformDisplayName = getPlatformDisplayName; exports.getPlatformDescription = getPlatformDescription; exports.getPlatformSlogan = getPlatformSlogan; exports.getSmallPlatformIcon = getSmallPlatformIcon; exports.getPlatformIconComponent = getPlatformIconComponent; exports.ToolTypeValues = ToolTypeValues; exports.toolLabels = toolLabels; exports.validateAccessCode = validateAccessCode; exports.consumeAccessCode = consumeAccessCode; exports.validateAndConsumeAccessCode = validateAndConsumeAccessCode; exports.scrollElementIntoView = scrollElementIntoView; exports.STICKY_HEADER_OFFSET_PX = STICKY_HEADER_OFFSET_PX; exports.HUB_HEADER_OFFSET_PX = HUB_HEADER_OFFSET_PX; exports.normalizeHashFragment = normalizeHashFragment; exports.navigateSamePageHash = navigateSamePageHash; exports.HONEYPOT_FIELD = HONEYPOT_FIELD; exports.ELAPSED_MS_FIELD = ELAPSED_MS_FIELD; exports.DEFAULT_MIN_FILL_MS = DEFAULT_MIN_FILL_MS; exports.extractHumanitySignals = extractHumanitySignals; exports.evaluateHumanitySignals = evaluateHumanitySignals; exports.splitCsvEnv = splitCsvEnv;
|
|
449
|
+
//# sourceMappingURL=chunk-LFGGF7OT.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-LFGGF7OT.cjs","../src/utils/platform-config.tsx","../src/types/tool.types.ts","../src/utils/access-code-client.ts","../src/utils/scroll-into-view.ts","../src/utils/same-page-hash-nav.ts","../src/utils/humanity-signals.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-LFGGF7OT.cjs","sourcesContent":[null,"import React from 'react';\nimport { OpenmspLogo, FlamingoLogo, OpenFrameLogo, MiamiCyberGangLogoFaceOnly } from '../components/icons';\nimport { Globe } from 'lucide-react';\nimport type { SelectableOption } from '../components/features';\nimport type { PlatformConfig } from '../types/platform';\n\n// Platform icons mapping with consistent colors matching app theme\nexport const platformIcons = {\n openframe: <OpenFrameLogo className=\"h-5 w-5\" lowerPathColor=\"#FFC008\" upperPathColor=\"#ffffff\" />,\n openmsp: <OpenmspLogo className=\"h-5 w-5\" />,\n flamingo: <FlamingoLogo className=\"h-5 w-5\" fill=\"#EC4899\" />,\n 'flamingo-teaser': <FlamingoLogo className=\"h-5 w-5\" fill=\"#EC4899\" />,\n 'marketing-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#F357BB\" />,\n 'product-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#5EA62E\" />,\n 'revenue-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#FFC008\" />,\n 'people-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#5EFAF0\" />,\n 'company-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#f36666\" />,\n tmcg: <MiamiCyberGangLogoFaceOnly className=\"h-5 w-5\" />,\n universal: <Globe className=\"h-5 w-5 text-[#10B981]\" />\n};\n\n// Platform colors mapping\nexport const platformColors = {\n openmsp: 'bg-[#3B82F6]',\n openframe: 'bg-[#8B5CF6]',\n flamingo: 'bg-[#EC4899]',\n 'flamingo-teaser': 'bg-[#F59E0B]',\n 'marketing-hub': 'bg-[#F357BB]',\n 'product-hub': 'bg-[#5EA62E]',\n 'revenue-hub': 'bg-[#FFC008]',\n 'people-hub': 'bg-[#5EFAF0]',\n 'company-hub': 'bg-[#f36666]',\n tmcg: 'bg-[#FF6B6B]',\n universal: 'bg-[#10B981]'\n};\n\n// Platform display names for consistent naming across the app\nexport const platformDisplayNames = {\n openmsp: 'OpenMSP',\n openframe: 'OpenFrame',\n flamingo: 'Flamingo',\n 'flamingo-teaser': 'Flamingo Teaser',\n 'marketing-hub': 'Flamingo Marketing Hub',\n 'product-hub': 'Flamingo Product Hub',\n 'revenue-hub': 'Flamingo Revenue Hub',\n 'people-hub': 'Flamingo People Hub',\n 'company-hub': 'Flamingo Company Hub',\n tmcg: 'TMCG',\n universal: 'Universal'\n};\n\n// Platform descriptions for consistent messaging across the app\nexport const platformDescriptions = {\n openmsp: 'Comprehensive directory and comparison platform for managed service providers (MSPs) and technology vendors. Reduce vendor costs and discover open-source alternatives.',\n openframe: 'AI-driven open-source security operations center (SOC) and endpoint detection platform for MSPs.',\n flamingo: 'AI-driven open-source OS for MSPs. Swap bloated vendor tools for open ones. Automate the boring crap. Take your margin back.',\n 'flamingo-teaser': 'Preview of Flamingo - the AI-driven open-source OS for MSPs.',\n tmcg: 'The Miami Cyber Gang - A cybersecurity community focused on education and collaboration.',\n universal: 'Cross-platform universal content.'\n};\n\n// Platform slogans for branding consistency\nexport const platformSlogans = {\n openmsp: 'Find Your Perfect MSP Partner',\n openframe: 'Open-Source Security Operations',\n flamingo: 'Open-Source OS for MSPs',\n 'flamingo-teaser': 'Coming Soon: Open-Source OS for MSPs',\n tmcg: 'Miami Cyber Community',\n universal: 'Universal Platform'\n};\n\n// Platform hex colors for default configuration\nexport const platformHexColors = {\n openmsp: '#FFC008',\n openframe: '#FFC008',\n flamingo: '#FF6B9D',\n universal: '#FFC008',\n 'flamingo-teaser': '#F59E0B',\n 'marketing-hub': '#F357BB',\n 'product-hub': '#5EA62E',\n 'revenue-hub': '#FFC008',\n 'people-hub': '#5EFAF0',\n 'company-hub': '#f36666',\n tmcg: '#FF6B6B'\n};\n\n// Platform icon names for default configuration\nexport const platformIconNames = {\n openmsp: 'openmsp-logo',\n openframe: 'openframe-logo',\n flamingo: 'flamingo-logo',\n universal: 'globe',\n 'flamingo-teaser': 'flamingo-logo',\n 'marketing-hub': 'flamingo-logo',\n 'product-hub': 'flamingo-logo',\n 'revenue-hub': 'flamingo-logo',\n 'people-hub': 'flamingo-logo',\n 'company-hub': 'flamingo-logo',\n tmcg: 'tmcg-logo'\n};\n\n/**\n * Get default color for platform\n */\nexport function getDefaultColorForPlatform(platformName: string): string {\n return platformHexColors[platformName as keyof typeof platformHexColors] || platformHexColors.universal;\n}\n\n/**\n * Get default icon name for platform\n */\nexport function getDefaultIconForPlatform(platformName: string): string {\n return platformIconNames[platformName as keyof typeof platformIconNames] || platformIconNames.universal;\n}\n\nexport function transformPlatformConfigsToOptions(platformConfigs: PlatformConfig[]): SelectableOption[] {\n return platformConfigs.map((platform: PlatformConfig) => ({\n id: platform.id, // Database UUID for matching\n name: platform.name, // Platform name enum\n displayName: platform.display_name, // Human-readable name\n description: platform.description,\n icon: platformIcons[platform.name as keyof typeof platformIcons] || platformIcons.universal,\n color: platformColors[platform.name as keyof typeof platformColors] || platformColors.universal\n }));\n}\n\n/**\n * Get platform icon by name\n */\nexport function getPlatformIcon(platformName: string) {\n return platformIcons[platformName as keyof typeof platformIcons] || platformIcons.universal;\n}\n\n/**\n * Get platform color by name\n */\nexport function getPlatformColor(platformName: string) {\n return platformColors[platformName as keyof typeof platformColors] || platformColors.universal;\n}\n\n/**\n * Get platform display name by name\n */\nexport function getPlatformDisplayName(platformName: string): string {\n return platformDisplayNames[platformName as keyof typeof platformDisplayNames] || platformName;\n}\n\n/**\n * Get platform description by name\n */\nexport function getPlatformDescription(platformName: string): string {\n return platformDescriptions[platformName as keyof typeof platformDescriptions] || platformName;\n}\n\n/**\n * Get platform slogan by name\n */\nexport function getPlatformSlogan(platformName: string): string {\n return platformSlogans[platformName as keyof typeof platformSlogans] || platformName;\n}\n\n/**\n * Get small platform icon for filter buttons with white colors (4x4 size)\n */\nexport function getSmallPlatformIcon(platformName: string): React.ReactNode {\n const className = \"h-4 w-4 flex-shrink-0\";\n\n switch (platformName) {\n case 'openframe':\n return <OpenFrameLogo className={className} lowerPathColor=\"#FFC008\" upperPathColor=\"#ffffff\" />;\n case 'openmsp':\n return <OpenmspLogo className={className} frontBubbleColor=\"#f1f1f1\" innerFrontBubbleColor=\"#000000\" backBubbleColor=\"#FFC008\" />;\n case 'flamingo':\n case 'flamingo-teaser':\n return <FlamingoLogo className={`${className}`} fill=\"#EC4899\" />;\n case 'marketing-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-pink-base)\" />;\n case 'product-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-green-success)\" />;\n case 'revenue-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-yellow-warning)\" />;\n case 'people-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-cyan-base)\" />;\n case 'company-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-red-error)\" />;\n case 'tmcg':\n return <MiamiCyberGangLogoFaceOnly className={className} />;\n case 'universal':\n default:\n return <Globe className={className} />;\n }\n}\n\n/**\n * Get platform icon for admin/selector components (standard 6x6 size)\n */\nexport function getPlatformIconComponent(platformName: string, className: string = \"h-6 w-6\"): React.ReactNode {\n switch (platformName) {\n case 'openframe':\n return <OpenFrameLogo className={className} />;\n case 'openmsp':\n return <OpenmspLogo className={className} color=\"#f1f1f1\" />;\n case 'flamingo':\n case 'flamingo-teaser':\n return <FlamingoLogo className={`${className} text-white`} />;\n case 'marketing-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-pink-base)\" />;\n case 'product-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-green-success)\" />;\n case 'revenue-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-yellow-warning)\" />;\n case 'people-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-cyan-base)\" />;\n case 'company-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-red-error)\" />;\n case 'tmcg':\n return <MiamiCyberGangLogoFaceOnly size={24} className={className} />;\n case 'universal':\n default:\n return <Globe className={className} />;\n }\n}","/**\n * Centralized Tool Types\n *\n * Single source of truth for all tool-related types across the entire platform.\n * Used by ToolBadge, ToolIcon, and any component that needs tool type information.\n */\n\nexport const ToolTypeValues = {\n TACTICAL_RMM: 'TACTICAL_RMM',\n FLEET_MDM: 'FLEET_MDM',\n MESHCENTRAL: 'MESHCENTRAL',\n AUTHENTIK: 'AUTHENTIK',\n OPENFRAME: 'OPENFRAME',\n OPENFRAME_CHAT: 'OPENFRAME_CHAT',\n OPENFRAME_CLIENT: 'OPENFRAME_CLIENT',\n OSQUERY: 'OSQUERY',\n SYSTEM: 'SYSTEM'\n} as const\n\nexport type ToolType = (typeof ToolTypeValues)[keyof typeof ToolTypeValues]\n\n/**\n * Maps tool types to display labels\n */\nexport const toolLabels: Record<ToolType, string> = {\n TACTICAL_RMM: 'Tactical',\n FLEET_MDM: 'Fleet',\n MESHCENTRAL: 'MeshCentral',\n AUTHENTIK: 'Authentik',\n OPENFRAME: 'OpenFrame',\n OPENFRAME_CHAT: 'OpenFrame Chat',\n OPENFRAME_CLIENT: 'OpenFrame Client',\n OSQUERY: 'Osquery',\n SYSTEM: 'System'\n}\n","/**\n * Access Code Client Utilities — pure standalone functions.\n *\n * Endpoint paths are NOT hardcoded — every function takes an\n * `endpoints` argument. The React-side wrapper that binds them from\n * `EndpointsRuntimeContext` lives separately at\n * `hooks/use-access-code-integration.ts` (`useAccessCodeIntegration`).\n *\n * Keep this file **free of React imports** — it lives in the\n * server-safe `utils/index` tsup bundle. Any module-top-level call\n * into `createContext()` (which the runtime context file does) would\n * be pulled into the server bundle and crash SSR with\n * `createContext is not a function`.\n */\n\nimport {\n AccessCodeValidation,\n AccessCodeValidationResponse,\n AccessCodeConsumptionResponse\n} from '../types/access-code-cohorts';\n\n/** Endpoints required by the standalone client utilities. The\n * `useAccessCodeIntegration` hook (in `hooks/`) resolves these from\n * `EndpointsRuntimeContext.accessCode` automatically. */\nexport interface AccessCodeEndpoints {\n validateUrl: string\n consumeUrl: string\n}\n\n/**\n * Validate an access code for a given email\n *\n * @param email - User's email address\n * @param code - Access code to validate\n * @returns Promise with validation result\n *\n * @example\n * const result = await validateAccessCode('user@example.com', 'ABC123XY');\n * if (result.valid) {\n * // Allow user to proceed with registration\n * console.log(`Welcome to ${result.cohort_name}!`);\n * } else {\n * // Show error message\n * console.error(result.message);\n * }\n */\nexport async function validateAccessCode(\n email: string,\n code: string,\n endpoints: AccessCodeEndpoints,\n): Promise<AccessCodeValidationResponse> {\n try {\n const response = await fetch(endpoints.validateUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ email, code } as AccessCodeValidation),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Validation request failed');\n }\n\n return await response.json() as AccessCodeValidationResponse;\n } catch (error) {\n return {\n valid: false,\n message: error instanceof Error ? error.message : 'Validation failed',\n };\n }\n}\n\n/**\n * Consume an access code after successful registration\n *\n * Call this ONLY after the user has successfully completed registration.\n * This marks the code as used and prevents further usage.\n *\n * @param email - User's email address\n * @param code - Access code to consume\n * @returns Promise with consumption result\n *\n * @example\n * // After successful registration\n * const result = await consumeAccessCode('user@example.com', 'ABC123XY');\n * if (result.consumed) {\n * console.log('Access code consumed successfully');\n * } else {\n * console.warn('Failed to consume access code:', result.message);\n * }\n */\nexport async function consumeAccessCode(\n email: string,\n code: string,\n endpoints: AccessCodeEndpoints,\n): Promise<AccessCodeConsumptionResponse> {\n try {\n const response = await fetch(endpoints.consumeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ email, code } as AccessCodeValidation),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Consumption request failed');\n }\n\n return await response.json() as AccessCodeConsumptionResponse;\n } catch (error) {\n return {\n success: false,\n consumed: false,\n message: error instanceof Error ? error.message : 'Consumption failed',\n };\n }\n}\n\n/**\n * Complete access code flow: validate then consume\n *\n * This is a convenience function that validates an access code and,\n * if valid, immediately consumes it. Use this when you want to\n * validate and consume in one step during registration.\n *\n * @param email - User's email address\n * @param code - Access code to validate and consume\n * @returns Promise with validation and consumption results\n *\n * @example\n * const result = await validateAndConsumeAccessCode('user@example.com', 'ABC123XY');\n * if (result.valid && result.consumed) {\n * // Registration successful\n * console.log(`Welcome to ${result.cohort_name}!`);\n * } else {\n * console.error(result.message);\n * }\n */\nexport async function validateAndConsumeAccessCode(\n email: string,\n code: string,\n endpoints: AccessCodeEndpoints,\n): Promise<AccessCodeValidationResponse & { consumed?: boolean }> {\n // First validate\n const validation = await validateAccessCode(email, code, endpoints);\n\n if (!validation.valid) {\n return validation;\n }\n\n // If valid, consume the code\n const consumption = await consumeAccessCode(email, code, endpoints);\n\n return {\n ...validation,\n consumed: consumption.consumed,\n message: consumption.consumed\n ? `Access granted for ${validation.cohort_name}`\n : consumption.message || validation.message,\n };\n}\n\n// `useAccessCodeIntegration` (the React-side wrapper) lives in\n// `hooks/use-access-code-integration.ts`. It binds the endpoints from\n// `EndpointsRuntimeContext` so React callers don't have to plumb URLs.\n","/**\n * `scrollElementIntoView` — canonical \"scroll an element to the top of the\n * viewport, account for sticky chrome, survive layout shifts\" helper.\n *\n * One shared implementation so every caller (the ticket drawer expand, the\n * hub's `useUnifiedNav` / `use-nav-link` hash scroll, doc-tree, delivery\n * `?focus=`, sticky-section-nav, …) inherits the SAME cancellation-proof\n * motion.\n *\n * WHY A SELF-DRIVEN rAF TWEEN INSTEAD OF `window.scrollTo({behavior:'smooth'})`:\n * the native smooth scroll is CANCELLABLE, and in real pages it gets cancelled\n * constantly:\n *\n * - Browser SCROLL ANCHORING: when content is inserted/removed above or\n * around the target (a collapsible drawer expanding, an async image\n * loading, a list re-rendering) the browser issues a synchronous scrollTop\n * correction to keep the anchored element stable. Per CSSOM-View \"perform a\n * scroll\" step 1 (\"abort any ongoing smooth scroll\"), that correction\n * ABORTS an in-flight native smooth scroll — so it lands as an instant jump.\n * Anchoring is suppressed when the scroll offset is 0, which is exactly why\n * a native smooth scroll appears to work the FIRST time (page at top) and\n * jumps on every repeat (page already scrolled). This was a multi-day\n * \"smooth only works once\" bug on the /tickets drawer.\n * - A second programmatic scroll on the same frame, or a `focus()` without\n * `{preventScroll:true}`, cancels it the same way.\n *\n * A tween that re-asserts the position with INSTANT writes every frame is\n * immune: there is no \"ongoing native smooth scroll\" for anchoring/focus to\n * abort, and any correction that lands between our frames is overwritten on the\n * next frame. We also RECOMPUTE the target each frame, so an element whose\n * final position is still settling (drawer still expanding, images loading)\n * is tracked to its resting place instead of animating to a stale pixel.\n *\n * Honors `prefers-reduced-motion` (jumps instantly) and cancels on genuine user\n * scroll intent (wheel / touch) so we never fight the user.\n *\n * WINDOW *OR* A SCROLLABLE ANCESTOR: the helper is not hard-wired to the window\n * scroller. It walks up from the target to the nearest ancestor that is an\n * actual scroll container (`overflow-y: auto | scroll | overlay` AND\n * `scrollHeight > clientHeight`) and drives THAT element; only when none exists\n * does it fall back to `window`. This is what makes it work inside app shells\n * that put page content in a fixed-height `<main class=\"overflow-y-auto\">`\n * (e.g. OpenFrame's `AppLayout`) where the document/window never scrolls — the\n * old window-only version was a silent no-op there. Note `overflow: clip` /\n * `hidden` are deliberately NOT treated as scroll containers, so a list wrapper\n * that uses `overflow-clip` only to round its corners still bubbles the scroll\n * up to the real container (matches the `<HelpCenterCard>` list intent).\n */\n\nexport interface ScrollElementIntoViewOptions {\n /** Pixels to subtract from the target element's `top` so it lands BELOW\n * sticky chrome. Defaults to 0. Pass `96` for the standard hub header. */\n headerOffset?: number\n /** `'smooth'` (default) runs the self-driven tween; `'instant'` / `'auto'`\n * jump in one synchronous write (deep-link land, programmatic focus moves). */\n behavior?: ScrollBehavior\n /** Optional adjustment applied to the computed pixel target each frame. The\n * callback receives the \"raw\" Y (`element.top + scrollY - headerOffset`) and\n * returns the FINAL target. Use when the caller knows about a layout shift\n * (e.g. a sibling drawer collapsing) the geometry can't yet reflect. */\n adjustTargetY?: (rawTargetY: number) => number\n /** Tween duration in ms (smooth only). Default 320. */\n durationMs?: number\n}\n\n/** Module-level handle to the in-flight tween so a new call (or a user\n * gesture) cancels the previous one — only ever one page-scroll animation at\n * a time. */\nlet activeRaf = 0\nlet teardownActive: (() => void) | null = null\n\nfunction cancelActiveScroll(): void {\n if (activeRaf) {\n cancelAnimationFrame(activeRaf)\n activeRaf = 0\n }\n if (teardownActive) {\n teardownActive()\n teardownActive = null\n }\n}\n\nconst easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3)\n\n/** Nearest ancestor that is a *real* scroll container, or `null` when the\n * window/document is the scroller. Only `auto | scroll | overlay` count —\n * `clip` / `hidden` are intentionally excluded (a wrapper using `overflow-clip`\n * purely to round corners must let the scroll bubble to the page). */\nfunction getScrollableAncestor(el: HTMLElement): HTMLElement | null {\n for (let node = el.parentElement; node; node = node.parentElement) {\n const overflowY = getComputedStyle(node).overflowY\n if (\n (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') &&\n node.scrollHeight > node.clientHeight\n ) {\n return node\n }\n }\n return null\n}\n\n/**\n * Scroll the page so `target` lands at the top of the viewport (below sticky\n * chrome via `headerOffset`). SSR-safe; `null`/`undefined` target is a no-op so\n * callers can pass refs without defensive branching.\n */\nexport function scrollElementIntoView(\n target: HTMLElement | null | undefined,\n options: ScrollElementIntoViewOptions = {},\n): void {\n if (typeof window === 'undefined' || !target) return\n const { headerOffset = 0, behavior = 'smooth', adjustTargetY, durationMs = 320 } = options\n\n // Pick the scroller ONCE: a fixed-height `<main overflow-y-auto>` shell scrolls\n // the element, a plain document scrolls the window. The choice can't change\n // mid-tween, so resolve it up front and route every read/write through it.\n const container = getScrollableAncestor(target)\n const readCurrent = (): number => (container ? container.scrollTop : window.scrollY)\n const writeTo = (y: number): void => {\n if (container) container.scrollTop = y\n else window.scrollTo(0, y)\n }\n\n // Target is recomputed every frame: the row's absolute position can move as\n // the page reflows (a sibling drawer collapsing) and the reachable max grows\n // as the just-opened drawer expands. Clamp to the LIVE max each frame.\n const computeTarget = (): number => {\n const raw = container\n ? container.scrollTop +\n (target.getBoundingClientRect().top - container.getBoundingClientRect().top) -\n headerOffset\n : target.getBoundingClientRect().top + window.scrollY - headerOffset\n const adjusted = adjustTargetY ? adjustTargetY(raw) : raw\n const maxScroll = container\n ? Math.max(0, container.scrollHeight - container.clientHeight)\n : Math.max(0, document.documentElement.scrollHeight - window.innerHeight)\n return Math.min(Math.max(0, adjusted), maxScroll)\n }\n\n // Any prior animation loses — one page scroll at a time.\n cancelActiveScroll()\n\n const prefersReduced =\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n // Instant paths: a single synchronous write. No tween, no anchoring race.\n if (behavior === 'instant' || behavior === 'auto' || prefersReduced) {\n writeTo(computeTarget())\n return\n }\n\n // Smooth: self-driven tween with instant per-frame writes (anchoring-proof).\n let startY: number | null = null\n let startTime = 0\n\n // Bail the moment the user takes over with a real scroll gesture — we must\n // never fight them. (Not keydown: the ticket composer auto-focuses on open,\n // and typing there should not abort the scroll.)\n const onUserGesture = () => cancelActiveScroll()\n window.addEventListener('wheel', onUserGesture, { passive: true })\n window.addEventListener('touchmove', onUserGesture, { passive: true })\n teardownActive = () => {\n window.removeEventListener('wheel', onUserGesture)\n window.removeEventListener('touchmove', onUserGesture)\n }\n\n const step = (now: number) => {\n if (startY === null) {\n startY = readCurrent()\n startTime = now\n }\n const targetY = computeTarget()\n const t = Math.min(1, (now - startTime) / durationMs)\n const y = startY + (targetY - startY) * easeOutCubic(t)\n writeTo(y)\n if (t < 1) {\n activeRaf = requestAnimationFrame(step)\n } else {\n // Final exact write in case easing left a sub-pixel gap, then teardown.\n writeTo(computeTarget())\n activeRaf = 0\n if (teardownActive) {\n teardownActive()\n teardownActive = null\n }\n }\n }\n activeRaf = requestAnimationFrame(step)\n}\n","import { scrollElementIntoView } from './scroll-into-view'\n\n/** Pages with a section-nav STRIP on top of the global hub header\n * (dev-center roadmap/delivery/tickets, FAQ category-pill nav).\n * Anchor lands BELOW both layers. */\nexport const STICKY_HEADER_OFFSET_PX = 96\n\n/** Pages with only the global hub header (docs, blog, vendor detail).\n * Anchor lands BELOW the header bar. */\nexport const HUB_HEADER_OFFSET_PX = 80\n\n/**\n * Take only the FIRST hash segment from a fragment that may contain extra\n * `#` characters. `'' → ''`, `'#a' → '#a'`, `'#a#b' → '#a'`.\n *\n * No real DOM id contains `#`, so a multi-fragment hash is always a bug at\n * the composer site; `navigateSamePageHash` + `useScrollToHash` both call\n * this so URL bar and `getElementById` stay in sync.\n */\nexport function normalizeHashFragment(hash: string): string {\n if (!hash) return ''\n const second = hash.indexOf('#', 1)\n return second < 0 ? hash : hash.slice(0, second)\n}\n\nexport interface NavigateSamePageHashOptions {\n /** Pixels to subtract for sticky chrome. */\n headerOffset?: number\n /** `'push'` (default) — new history entry; `'replace'` — overwrite\n * current entry (use for TOC-style in-page navigators). */\n history?: 'push' | 'replace'\n}\n\n/**\n * Same-page hash navigation primitive: pushState + synthetic `hashchange`\n * + anchoring-proof smooth scroll. Replaces `router.push` for hash CTAs\n * (Next.js suppresses smooth-scroll during navigation; `router.push` on\n * an exact-URL match is a no-op). Returns `true` when the helper claimed\n * the nav (same pathname + search); `false` for cross-page targets so\n * callers fall through to `router.push`.\n *\n * `target` accepts an origin-stripped path (`/x#anchor`) or a bare hash\n * (`#anchor`); bare-hash callers don't need to reconstruct `pathname +\n * search` themselves.\n */\nexport function navigateSamePageHash(\n target: string,\n options: NavigateSamePageHashOptions = {},\n): boolean {\n if (typeof window === 'undefined') return false\n const { headerOffset = 0, history: historyMode = 'push' } = options\n const normalizedTarget =\n target.startsWith('#')\n ? window.location.pathname + window.location.search + target\n : target\n // `new URL(absoluteUrl, base)` ignores `base` per RFC 3986; an absolute\n // cross-origin target sharing pathname/search would otherwise pass the\n // check below and trip pushState's same-origin enforcement. Parse with\n // an explicit base so malformed inputs cleanly fall through.\n let url: URL\n try {\n url = new URL(normalizedTarget, window.location.href)\n } catch {\n return false\n }\n if (\n url.origin !== window.location.origin ||\n url.pathname !== window.location.pathname ||\n url.search !== window.location.search\n ) {\n return false\n }\n const current = window.location.pathname + window.location.search + window.location.hash\n // Heal a malformed multi-fragment hash so the URL bar is clean and\n // `getElementById` resolves. Dev-warn fingers the upstream composer.\n const normalizedHash = normalizeHashFragment(url.hash)\n if (process.env.NODE_ENV === 'development' && normalizedHash !== url.hash) {\n // eslint-disable-next-line no-console\n console.warn(\n `[navigateSamePageHash] malformed fragment \"${url.hash}\" → normalizing to \"${normalizedHash}\". Fix the upstream composer.`,\n )\n }\n const next = url.pathname + url.search + normalizedHash\n const id = normalizedHash && normalizedHash !== '#' ? normalizedHash.slice(1) : ''\n // Hash-less targets are only ours on an EXACT URL re-click.\n if (!id && next !== current) return false\n if (next !== current) {\n const oldURL = window.location.href\n if (historyMode === 'replace') {\n window.history.replaceState(null, '', next)\n } else {\n window.history.pushState(null, '', next)\n }\n // Synthetic `hashchange` — `pushState` doesn't fire it (HTML spec),\n // so URL-hash-bound listeners (FAQ auto-expand, etc.) wouldn't react.\n window.dispatchEvent(new HashChangeEvent('hashchange', {\n oldURL,\n newURL: window.location.href,\n }))\n }\n const el = id ? document.getElementById(id) : null\n if (id && !el && process.env.NODE_ENV === 'development') {\n // eslint-disable-next-line no-console\n console.warn(\n `[navigateSamePageHash] anchor \"#${id}\" not found — scrolling to top.`,\n )\n }\n // Missing anchor → tween to page top. `documentElement` is at 0 by\n // definition, so one tween covers both branches.\n scrollElementIntoView(el ?? document.documentElement, {\n behavior: 'smooth',\n headerOffset,\n })\n return true\n}\n","/**\n * Humanity signals — invisible bot-protection primitives shared by the lib's\n * public forms (client) and the hub's per-route `verifyHuman` gate (server).\n *\n * PURE + React-free on purpose: this module is a tsup SERVER entry (no\n * \"use client\" banner) so the hub can import it server-side without pulling a\n * client-reference boundary — same pattern as `schemas/contact-schema` and\n * `components/features/mux-origins`.\n *\n * Two origin-independent signals travel in the POST body: a honeypot (a hidden\n * field real users never fill) and timing (ms from form mount to submit).\n * `evaluateHumanitySignals` is the SINGLE source of truth for the block/allow\n * decision — the hub imports + calls it rather than re-implementing the rules.\n */\n\n/** Hidden honeypot field name. Innocuous + autofill-resistant (deliberately NOT name/email). */\nexport const HONEYPOT_FIELD = 'contact_url_confirm'\n/** Client-measured ms between form mount and submit. */\nexport const ELAPSED_MS_FIELD = 'form_elapsed_ms'\n/** Default minimum fill time (ms). A submit faster than this is treated as a bot. */\nexport const DEFAULT_MIN_FILL_MS = 700\n\n/** Keyed wire object produced by `useHumanitySignals().getSignals()` and spread into the POST body. */\nexport type HumanitySignals = Record<string, string | number>\n\n/** Result of {@link evaluateHumanitySignals}. */\nexport type HumanityVerdict = { ok: true } | { ok: false; reason: 'honeypot' | 'too_fast' }\n\n/** Tolerant reader — never throws; missing/garbage timing → null. */\nexport function extractHumanitySignals(body: unknown): { honeypot: string; elapsedMs: number | null } {\n const b = (body ?? {}) as Record<string, unknown>\n const rawHp = b[HONEYPOT_FIELD]\n // A legit client always sends a STRING here (getSignals → ref.value ?? ''),\n // so ANY present non-string value is a bot filling the decoy with a non-string\n // to dodge the empty-check — coerce to a (non-empty) string so it still trips.\n // null/undefined → '' = the correct \"field absent / unfilled\" allow case.\n const honeypot = rawHp == null ? '' : String(rawHp)\n const rawMs = b[ELAPSED_MS_FIELD]\n const elapsedMs = typeof rawMs === 'number' && Number.isFinite(rawMs) ? rawMs : null\n return { honeypot, elapsedMs }\n}\n\n/**\n * SINGLE decision fn for honeypot + timing (the hub's `verifyHuman` imports + calls this):\n * - honeypot non-empty → bot (real users never fill the off-screen field)\n * - elapsed below `minFillMs` → bot (humans take time; a MISSING timing value never blocks)\n */\nexport function evaluateHumanitySignals(body: unknown, opts: { minFillMs: number }): HumanityVerdict {\n const { honeypot, elapsedMs } = extractHumanitySignals(body)\n if (honeypot.trim() !== '') return { ok: false, reason: 'honeypot' }\n if (elapsedMs !== null && elapsedMs < opts.minFillMs) return { ok: false, reason: 'too_fast' }\n return { ok: true }\n}\n\n/** Parse a comma-separated env string → trimmed, non-empty entries (undefined → []). */\nexport const splitCsvEnv = (s?: string): string[] =>\n s?.split(',').map((t) => t.trim()).filter(Boolean) ?? []\n"]}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
formatDurationMMSS,
|
|
18
18
|
getFirstLastInitials,
|
|
19
19
|
nameInitials
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-FQJK446R.js";
|
|
21
21
|
import {
|
|
22
22
|
useNearViewport,
|
|
23
23
|
useOgPlaceholder
|
|
@@ -3006,4 +3006,4 @@ export {
|
|
|
3006
3006
|
getCaptionsUrl,
|
|
3007
3007
|
FilterPillRow
|
|
3008
3008
|
};
|
|
3009
|
-
//# sourceMappingURL=chunk-
|
|
3009
|
+
//# sourceMappingURL=chunk-NSPOYUBH.js.map
|
|
@@ -259,6 +259,138 @@ async function validateAndConsumeAccessCode(email, code, endpoints) {
|
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
// src/utils/scroll-into-view.ts
|
|
263
|
+
var activeRaf = 0;
|
|
264
|
+
var teardownActive = null;
|
|
265
|
+
function cancelActiveScroll() {
|
|
266
|
+
if (activeRaf) {
|
|
267
|
+
cancelAnimationFrame(activeRaf);
|
|
268
|
+
activeRaf = 0;
|
|
269
|
+
}
|
|
270
|
+
if (teardownActive) {
|
|
271
|
+
teardownActive();
|
|
272
|
+
teardownActive = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
276
|
+
function getScrollableAncestor(el) {
|
|
277
|
+
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
278
|
+
const overflowY = getComputedStyle(node).overflowY;
|
|
279
|
+
if ((overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight) {
|
|
280
|
+
return node;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
function scrollElementIntoView(target, options = {}) {
|
|
286
|
+
if (typeof window === "undefined" || !target) return;
|
|
287
|
+
const { headerOffset = 0, behavior = "smooth", adjustTargetY, durationMs = 320 } = options;
|
|
288
|
+
const container = getScrollableAncestor(target);
|
|
289
|
+
const readCurrent = () => container ? container.scrollTop : window.scrollY;
|
|
290
|
+
const writeTo = (y) => {
|
|
291
|
+
if (container) container.scrollTop = y;
|
|
292
|
+
else window.scrollTo(0, y);
|
|
293
|
+
};
|
|
294
|
+
const computeTarget = () => {
|
|
295
|
+
const raw = container ? container.scrollTop + (target.getBoundingClientRect().top - container.getBoundingClientRect().top) - headerOffset : target.getBoundingClientRect().top + window.scrollY - headerOffset;
|
|
296
|
+
const adjusted = adjustTargetY ? adjustTargetY(raw) : raw;
|
|
297
|
+
const maxScroll = container ? Math.max(0, container.scrollHeight - container.clientHeight) : Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
|
298
|
+
return Math.min(Math.max(0, adjusted), maxScroll);
|
|
299
|
+
};
|
|
300
|
+
cancelActiveScroll();
|
|
301
|
+
const prefersReduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
302
|
+
if (behavior === "instant" || behavior === "auto" || prefersReduced) {
|
|
303
|
+
writeTo(computeTarget());
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
let startY = null;
|
|
307
|
+
let startTime = 0;
|
|
308
|
+
const onUserGesture = () => cancelActiveScroll();
|
|
309
|
+
window.addEventListener("wheel", onUserGesture, { passive: true });
|
|
310
|
+
window.addEventListener("touchmove", onUserGesture, { passive: true });
|
|
311
|
+
teardownActive = () => {
|
|
312
|
+
window.removeEventListener("wheel", onUserGesture);
|
|
313
|
+
window.removeEventListener("touchmove", onUserGesture);
|
|
314
|
+
};
|
|
315
|
+
const step = (now) => {
|
|
316
|
+
if (startY === null) {
|
|
317
|
+
startY = readCurrent();
|
|
318
|
+
startTime = now;
|
|
319
|
+
}
|
|
320
|
+
const targetY = computeTarget();
|
|
321
|
+
const t = Math.min(1, (now - startTime) / durationMs);
|
|
322
|
+
const y = startY + (targetY - startY) * easeOutCubic(t);
|
|
323
|
+
writeTo(y);
|
|
324
|
+
if (t < 1) {
|
|
325
|
+
activeRaf = requestAnimationFrame(step);
|
|
326
|
+
} else {
|
|
327
|
+
writeTo(computeTarget());
|
|
328
|
+
activeRaf = 0;
|
|
329
|
+
if (teardownActive) {
|
|
330
|
+
teardownActive();
|
|
331
|
+
teardownActive = null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
activeRaf = requestAnimationFrame(step);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/utils/same-page-hash-nav.ts
|
|
339
|
+
var STICKY_HEADER_OFFSET_PX = 96;
|
|
340
|
+
var HUB_HEADER_OFFSET_PX = 80;
|
|
341
|
+
function normalizeHashFragment(hash) {
|
|
342
|
+
if (!hash) return "";
|
|
343
|
+
const second = hash.indexOf("#", 1);
|
|
344
|
+
return second < 0 ? hash : hash.slice(0, second);
|
|
345
|
+
}
|
|
346
|
+
function navigateSamePageHash(target, options = {}) {
|
|
347
|
+
if (typeof window === "undefined") return false;
|
|
348
|
+
const { headerOffset = 0, history: historyMode = "push" } = options;
|
|
349
|
+
const normalizedTarget = target.startsWith("#") ? window.location.pathname + window.location.search + target : target;
|
|
350
|
+
let url;
|
|
351
|
+
try {
|
|
352
|
+
url = new URL(normalizedTarget, window.location.href);
|
|
353
|
+
} catch {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
if (url.origin !== window.location.origin || url.pathname !== window.location.pathname || url.search !== window.location.search) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
const current = window.location.pathname + window.location.search + window.location.hash;
|
|
360
|
+
const normalizedHash = normalizeHashFragment(url.hash);
|
|
361
|
+
if (process.env.NODE_ENV === "development" && normalizedHash !== url.hash) {
|
|
362
|
+
console.warn(
|
|
363
|
+
`[navigateSamePageHash] malformed fragment "${url.hash}" \u2192 normalizing to "${normalizedHash}". Fix the upstream composer.`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
const next = url.pathname + url.search + normalizedHash;
|
|
367
|
+
const id = normalizedHash && normalizedHash !== "#" ? normalizedHash.slice(1) : "";
|
|
368
|
+
if (!id && next !== current) return false;
|
|
369
|
+
if (next !== current) {
|
|
370
|
+
const oldURL = window.location.href;
|
|
371
|
+
if (historyMode === "replace") {
|
|
372
|
+
window.history.replaceState(null, "", next);
|
|
373
|
+
} else {
|
|
374
|
+
window.history.pushState(null, "", next);
|
|
375
|
+
}
|
|
376
|
+
window.dispatchEvent(new HashChangeEvent("hashchange", {
|
|
377
|
+
oldURL,
|
|
378
|
+
newURL: window.location.href
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
const el = id ? document.getElementById(id) : null;
|
|
382
|
+
if (id && !el && process.env.NODE_ENV === "development") {
|
|
383
|
+
console.warn(
|
|
384
|
+
`[navigateSamePageHash] anchor "#${id}" not found \u2014 scrolling to top.`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
scrollElementIntoView(el ?? document.documentElement, {
|
|
388
|
+
behavior: "smooth",
|
|
389
|
+
headerOffset
|
|
390
|
+
});
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
262
394
|
// src/utils/humanity-signals.ts
|
|
263
395
|
var HONEYPOT_FIELD = "contact_url_confirm";
|
|
264
396
|
var ELAPSED_MS_FIELD = "form_elapsed_ms";
|
|
@@ -302,6 +434,11 @@ export {
|
|
|
302
434
|
validateAccessCode,
|
|
303
435
|
consumeAccessCode,
|
|
304
436
|
validateAndConsumeAccessCode,
|
|
437
|
+
scrollElementIntoView,
|
|
438
|
+
STICKY_HEADER_OFFSET_PX,
|
|
439
|
+
HUB_HEADER_OFFSET_PX,
|
|
440
|
+
normalizeHashFragment,
|
|
441
|
+
navigateSamePageHash,
|
|
305
442
|
HONEYPOT_FIELD,
|
|
306
443
|
ELAPSED_MS_FIELD,
|
|
307
444
|
DEFAULT_MIN_FILL_MS,
|
|
@@ -309,4 +446,4 @@ export {
|
|
|
309
446
|
evaluateHumanitySignals,
|
|
310
447
|
splitCsvEnv
|
|
311
448
|
};
|
|
312
|
-
//# sourceMappingURL=chunk-
|
|
449
|
+
//# sourceMappingURL=chunk-OQ6X7ZOC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/platform-config.tsx","../src/types/tool.types.ts","../src/utils/access-code-client.ts","../src/utils/scroll-into-view.ts","../src/utils/same-page-hash-nav.ts","../src/utils/humanity-signals.ts"],"sourcesContent":["import React from 'react';\nimport { OpenmspLogo, FlamingoLogo, OpenFrameLogo, MiamiCyberGangLogoFaceOnly } from '../components/icons';\nimport { Globe } from 'lucide-react';\nimport type { SelectableOption } from '../components/features';\nimport type { PlatformConfig } from '../types/platform';\n\n// Platform icons mapping with consistent colors matching app theme\nexport const platformIcons = {\n openframe: <OpenFrameLogo className=\"h-5 w-5\" lowerPathColor=\"#FFC008\" upperPathColor=\"#ffffff\" />,\n openmsp: <OpenmspLogo className=\"h-5 w-5\" />,\n flamingo: <FlamingoLogo className=\"h-5 w-5\" fill=\"#EC4899\" />,\n 'flamingo-teaser': <FlamingoLogo className=\"h-5 w-5\" fill=\"#EC4899\" />,\n 'marketing-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#F357BB\" />,\n 'product-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#5EA62E\" />,\n 'revenue-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#FFC008\" />,\n 'people-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#5EFAF0\" />,\n 'company-hub': <FlamingoLogo className=\"h-5 w-5\" fill=\"#f36666\" />,\n tmcg: <MiamiCyberGangLogoFaceOnly className=\"h-5 w-5\" />,\n universal: <Globe className=\"h-5 w-5 text-[#10B981]\" />\n};\n\n// Platform colors mapping\nexport const platformColors = {\n openmsp: 'bg-[#3B82F6]',\n openframe: 'bg-[#8B5CF6]',\n flamingo: 'bg-[#EC4899]',\n 'flamingo-teaser': 'bg-[#F59E0B]',\n 'marketing-hub': 'bg-[#F357BB]',\n 'product-hub': 'bg-[#5EA62E]',\n 'revenue-hub': 'bg-[#FFC008]',\n 'people-hub': 'bg-[#5EFAF0]',\n 'company-hub': 'bg-[#f36666]',\n tmcg: 'bg-[#FF6B6B]',\n universal: 'bg-[#10B981]'\n};\n\n// Platform display names for consistent naming across the app\nexport const platformDisplayNames = {\n openmsp: 'OpenMSP',\n openframe: 'OpenFrame',\n flamingo: 'Flamingo',\n 'flamingo-teaser': 'Flamingo Teaser',\n 'marketing-hub': 'Flamingo Marketing Hub',\n 'product-hub': 'Flamingo Product Hub',\n 'revenue-hub': 'Flamingo Revenue Hub',\n 'people-hub': 'Flamingo People Hub',\n 'company-hub': 'Flamingo Company Hub',\n tmcg: 'TMCG',\n universal: 'Universal'\n};\n\n// Platform descriptions for consistent messaging across the app\nexport const platformDescriptions = {\n openmsp: 'Comprehensive directory and comparison platform for managed service providers (MSPs) and technology vendors. Reduce vendor costs and discover open-source alternatives.',\n openframe: 'AI-driven open-source security operations center (SOC) and endpoint detection platform for MSPs.',\n flamingo: 'AI-driven open-source OS for MSPs. Swap bloated vendor tools for open ones. Automate the boring crap. Take your margin back.',\n 'flamingo-teaser': 'Preview of Flamingo - the AI-driven open-source OS for MSPs.',\n tmcg: 'The Miami Cyber Gang - A cybersecurity community focused on education and collaboration.',\n universal: 'Cross-platform universal content.'\n};\n\n// Platform slogans for branding consistency\nexport const platformSlogans = {\n openmsp: 'Find Your Perfect MSP Partner',\n openframe: 'Open-Source Security Operations',\n flamingo: 'Open-Source OS for MSPs',\n 'flamingo-teaser': 'Coming Soon: Open-Source OS for MSPs',\n tmcg: 'Miami Cyber Community',\n universal: 'Universal Platform'\n};\n\n// Platform hex colors for default configuration\nexport const platformHexColors = {\n openmsp: '#FFC008',\n openframe: '#FFC008',\n flamingo: '#FF6B9D',\n universal: '#FFC008',\n 'flamingo-teaser': '#F59E0B',\n 'marketing-hub': '#F357BB',\n 'product-hub': '#5EA62E',\n 'revenue-hub': '#FFC008',\n 'people-hub': '#5EFAF0',\n 'company-hub': '#f36666',\n tmcg: '#FF6B6B'\n};\n\n// Platform icon names for default configuration\nexport const platformIconNames = {\n openmsp: 'openmsp-logo',\n openframe: 'openframe-logo',\n flamingo: 'flamingo-logo',\n universal: 'globe',\n 'flamingo-teaser': 'flamingo-logo',\n 'marketing-hub': 'flamingo-logo',\n 'product-hub': 'flamingo-logo',\n 'revenue-hub': 'flamingo-logo',\n 'people-hub': 'flamingo-logo',\n 'company-hub': 'flamingo-logo',\n tmcg: 'tmcg-logo'\n};\n\n/**\n * Get default color for platform\n */\nexport function getDefaultColorForPlatform(platformName: string): string {\n return platformHexColors[platformName as keyof typeof platformHexColors] || platformHexColors.universal;\n}\n\n/**\n * Get default icon name for platform\n */\nexport function getDefaultIconForPlatform(platformName: string): string {\n return platformIconNames[platformName as keyof typeof platformIconNames] || platformIconNames.universal;\n}\n\nexport function transformPlatformConfigsToOptions(platformConfigs: PlatformConfig[]): SelectableOption[] {\n return platformConfigs.map((platform: PlatformConfig) => ({\n id: platform.id, // Database UUID for matching\n name: platform.name, // Platform name enum\n displayName: platform.display_name, // Human-readable name\n description: platform.description,\n icon: platformIcons[platform.name as keyof typeof platformIcons] || platformIcons.universal,\n color: platformColors[platform.name as keyof typeof platformColors] || platformColors.universal\n }));\n}\n\n/**\n * Get platform icon by name\n */\nexport function getPlatformIcon(platformName: string) {\n return platformIcons[platformName as keyof typeof platformIcons] || platformIcons.universal;\n}\n\n/**\n * Get platform color by name\n */\nexport function getPlatformColor(platformName: string) {\n return platformColors[platformName as keyof typeof platformColors] || platformColors.universal;\n}\n\n/**\n * Get platform display name by name\n */\nexport function getPlatformDisplayName(platformName: string): string {\n return platformDisplayNames[platformName as keyof typeof platformDisplayNames] || platformName;\n}\n\n/**\n * Get platform description by name\n */\nexport function getPlatformDescription(platformName: string): string {\n return platformDescriptions[platformName as keyof typeof platformDescriptions] || platformName;\n}\n\n/**\n * Get platform slogan by name\n */\nexport function getPlatformSlogan(platformName: string): string {\n return platformSlogans[platformName as keyof typeof platformSlogans] || platformName;\n}\n\n/**\n * Get small platform icon for filter buttons with white colors (4x4 size)\n */\nexport function getSmallPlatformIcon(platformName: string): React.ReactNode {\n const className = \"h-4 w-4 flex-shrink-0\";\n\n switch (platformName) {\n case 'openframe':\n return <OpenFrameLogo className={className} lowerPathColor=\"#FFC008\" upperPathColor=\"#ffffff\" />;\n case 'openmsp':\n return <OpenmspLogo className={className} frontBubbleColor=\"#f1f1f1\" innerFrontBubbleColor=\"#000000\" backBubbleColor=\"#FFC008\" />;\n case 'flamingo':\n case 'flamingo-teaser':\n return <FlamingoLogo className={`${className}`} fill=\"#EC4899\" />;\n case 'marketing-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-pink-base)\" />;\n case 'product-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-green-success)\" />;\n case 'revenue-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-yellow-warning)\" />;\n case 'people-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-cyan-base)\" />;\n case 'company-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-red-error)\" />;\n case 'tmcg':\n return <MiamiCyberGangLogoFaceOnly className={className} />;\n case 'universal':\n default:\n return <Globe className={className} />;\n }\n}\n\n/**\n * Get platform icon for admin/selector components (standard 6x6 size)\n */\nexport function getPlatformIconComponent(platformName: string, className: string = \"h-6 w-6\"): React.ReactNode {\n switch (platformName) {\n case 'openframe':\n return <OpenFrameLogo className={className} />;\n case 'openmsp':\n return <OpenmspLogo className={className} color=\"#f1f1f1\" />;\n case 'flamingo':\n case 'flamingo-teaser':\n return <FlamingoLogo className={`${className} text-white`} />;\n case 'marketing-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-pink-base)\" />;\n case 'product-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-green-success)\" />;\n case 'revenue-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-yellow-warning)\" />;\n case 'people-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-flamingo-cyan-base)\" />;\n case 'company-hub':\n return <FlamingoLogo className={className} fill=\"var(--ods-attention-red-error)\" />;\n case 'tmcg':\n return <MiamiCyberGangLogoFaceOnly size={24} className={className} />;\n case 'universal':\n default:\n return <Globe className={className} />;\n }\n}","/**\n * Centralized Tool Types\n *\n * Single source of truth for all tool-related types across the entire platform.\n * Used by ToolBadge, ToolIcon, and any component that needs tool type information.\n */\n\nexport const ToolTypeValues = {\n TACTICAL_RMM: 'TACTICAL_RMM',\n FLEET_MDM: 'FLEET_MDM',\n MESHCENTRAL: 'MESHCENTRAL',\n AUTHENTIK: 'AUTHENTIK',\n OPENFRAME: 'OPENFRAME',\n OPENFRAME_CHAT: 'OPENFRAME_CHAT',\n OPENFRAME_CLIENT: 'OPENFRAME_CLIENT',\n OSQUERY: 'OSQUERY',\n SYSTEM: 'SYSTEM'\n} as const\n\nexport type ToolType = (typeof ToolTypeValues)[keyof typeof ToolTypeValues]\n\n/**\n * Maps tool types to display labels\n */\nexport const toolLabels: Record<ToolType, string> = {\n TACTICAL_RMM: 'Tactical',\n FLEET_MDM: 'Fleet',\n MESHCENTRAL: 'MeshCentral',\n AUTHENTIK: 'Authentik',\n OPENFRAME: 'OpenFrame',\n OPENFRAME_CHAT: 'OpenFrame Chat',\n OPENFRAME_CLIENT: 'OpenFrame Client',\n OSQUERY: 'Osquery',\n SYSTEM: 'System'\n}\n","/**\n * Access Code Client Utilities — pure standalone functions.\n *\n * Endpoint paths are NOT hardcoded — every function takes an\n * `endpoints` argument. The React-side wrapper that binds them from\n * `EndpointsRuntimeContext` lives separately at\n * `hooks/use-access-code-integration.ts` (`useAccessCodeIntegration`).\n *\n * Keep this file **free of React imports** — it lives in the\n * server-safe `utils/index` tsup bundle. Any module-top-level call\n * into `createContext()` (which the runtime context file does) would\n * be pulled into the server bundle and crash SSR with\n * `createContext is not a function`.\n */\n\nimport {\n AccessCodeValidation,\n AccessCodeValidationResponse,\n AccessCodeConsumptionResponse\n} from '../types/access-code-cohorts';\n\n/** Endpoints required by the standalone client utilities. The\n * `useAccessCodeIntegration` hook (in `hooks/`) resolves these from\n * `EndpointsRuntimeContext.accessCode` automatically. */\nexport interface AccessCodeEndpoints {\n validateUrl: string\n consumeUrl: string\n}\n\n/**\n * Validate an access code for a given email\n *\n * @param email - User's email address\n * @param code - Access code to validate\n * @returns Promise with validation result\n *\n * @example\n * const result = await validateAccessCode('user@example.com', 'ABC123XY');\n * if (result.valid) {\n * // Allow user to proceed with registration\n * console.log(`Welcome to ${result.cohort_name}!`);\n * } else {\n * // Show error message\n * console.error(result.message);\n * }\n */\nexport async function validateAccessCode(\n email: string,\n code: string,\n endpoints: AccessCodeEndpoints,\n): Promise<AccessCodeValidationResponse> {\n try {\n const response = await fetch(endpoints.validateUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ email, code } as AccessCodeValidation),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Validation request failed');\n }\n\n return await response.json() as AccessCodeValidationResponse;\n } catch (error) {\n return {\n valid: false,\n message: error instanceof Error ? error.message : 'Validation failed',\n };\n }\n}\n\n/**\n * Consume an access code after successful registration\n *\n * Call this ONLY after the user has successfully completed registration.\n * This marks the code as used and prevents further usage.\n *\n * @param email - User's email address\n * @param code - Access code to consume\n * @returns Promise with consumption result\n *\n * @example\n * // After successful registration\n * const result = await consumeAccessCode('user@example.com', 'ABC123XY');\n * if (result.consumed) {\n * console.log('Access code consumed successfully');\n * } else {\n * console.warn('Failed to consume access code:', result.message);\n * }\n */\nexport async function consumeAccessCode(\n email: string,\n code: string,\n endpoints: AccessCodeEndpoints,\n): Promise<AccessCodeConsumptionResponse> {\n try {\n const response = await fetch(endpoints.consumeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ email, code } as AccessCodeValidation),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Consumption request failed');\n }\n\n return await response.json() as AccessCodeConsumptionResponse;\n } catch (error) {\n return {\n success: false,\n consumed: false,\n message: error instanceof Error ? error.message : 'Consumption failed',\n };\n }\n}\n\n/**\n * Complete access code flow: validate then consume\n *\n * This is a convenience function that validates an access code and,\n * if valid, immediately consumes it. Use this when you want to\n * validate and consume in one step during registration.\n *\n * @param email - User's email address\n * @param code - Access code to validate and consume\n * @returns Promise with validation and consumption results\n *\n * @example\n * const result = await validateAndConsumeAccessCode('user@example.com', 'ABC123XY');\n * if (result.valid && result.consumed) {\n * // Registration successful\n * console.log(`Welcome to ${result.cohort_name}!`);\n * } else {\n * console.error(result.message);\n * }\n */\nexport async function validateAndConsumeAccessCode(\n email: string,\n code: string,\n endpoints: AccessCodeEndpoints,\n): Promise<AccessCodeValidationResponse & { consumed?: boolean }> {\n // First validate\n const validation = await validateAccessCode(email, code, endpoints);\n\n if (!validation.valid) {\n return validation;\n }\n\n // If valid, consume the code\n const consumption = await consumeAccessCode(email, code, endpoints);\n\n return {\n ...validation,\n consumed: consumption.consumed,\n message: consumption.consumed\n ? `Access granted for ${validation.cohort_name}`\n : consumption.message || validation.message,\n };\n}\n\n// `useAccessCodeIntegration` (the React-side wrapper) lives in\n// `hooks/use-access-code-integration.ts`. It binds the endpoints from\n// `EndpointsRuntimeContext` so React callers don't have to plumb URLs.\n","/**\n * `scrollElementIntoView` — canonical \"scroll an element to the top of the\n * viewport, account for sticky chrome, survive layout shifts\" helper.\n *\n * One shared implementation so every caller (the ticket drawer expand, the\n * hub's `useUnifiedNav` / `use-nav-link` hash scroll, doc-tree, delivery\n * `?focus=`, sticky-section-nav, …) inherits the SAME cancellation-proof\n * motion.\n *\n * WHY A SELF-DRIVEN rAF TWEEN INSTEAD OF `window.scrollTo({behavior:'smooth'})`:\n * the native smooth scroll is CANCELLABLE, and in real pages it gets cancelled\n * constantly:\n *\n * - Browser SCROLL ANCHORING: when content is inserted/removed above or\n * around the target (a collapsible drawer expanding, an async image\n * loading, a list re-rendering) the browser issues a synchronous scrollTop\n * correction to keep the anchored element stable. Per CSSOM-View \"perform a\n * scroll\" step 1 (\"abort any ongoing smooth scroll\"), that correction\n * ABORTS an in-flight native smooth scroll — so it lands as an instant jump.\n * Anchoring is suppressed when the scroll offset is 0, which is exactly why\n * a native smooth scroll appears to work the FIRST time (page at top) and\n * jumps on every repeat (page already scrolled). This was a multi-day\n * \"smooth only works once\" bug on the /tickets drawer.\n * - A second programmatic scroll on the same frame, or a `focus()` without\n * `{preventScroll:true}`, cancels it the same way.\n *\n * A tween that re-asserts the position with INSTANT writes every frame is\n * immune: there is no \"ongoing native smooth scroll\" for anchoring/focus to\n * abort, and any correction that lands between our frames is overwritten on the\n * next frame. We also RECOMPUTE the target each frame, so an element whose\n * final position is still settling (drawer still expanding, images loading)\n * is tracked to its resting place instead of animating to a stale pixel.\n *\n * Honors `prefers-reduced-motion` (jumps instantly) and cancels on genuine user\n * scroll intent (wheel / touch) so we never fight the user.\n *\n * WINDOW *OR* A SCROLLABLE ANCESTOR: the helper is not hard-wired to the window\n * scroller. It walks up from the target to the nearest ancestor that is an\n * actual scroll container (`overflow-y: auto | scroll | overlay` AND\n * `scrollHeight > clientHeight`) and drives THAT element; only when none exists\n * does it fall back to `window`. This is what makes it work inside app shells\n * that put page content in a fixed-height `<main class=\"overflow-y-auto\">`\n * (e.g. OpenFrame's `AppLayout`) where the document/window never scrolls — the\n * old window-only version was a silent no-op there. Note `overflow: clip` /\n * `hidden` are deliberately NOT treated as scroll containers, so a list wrapper\n * that uses `overflow-clip` only to round its corners still bubbles the scroll\n * up to the real container (matches the `<HelpCenterCard>` list intent).\n */\n\nexport interface ScrollElementIntoViewOptions {\n /** Pixels to subtract from the target element's `top` so it lands BELOW\n * sticky chrome. Defaults to 0. Pass `96` for the standard hub header. */\n headerOffset?: number\n /** `'smooth'` (default) runs the self-driven tween; `'instant'` / `'auto'`\n * jump in one synchronous write (deep-link land, programmatic focus moves). */\n behavior?: ScrollBehavior\n /** Optional adjustment applied to the computed pixel target each frame. The\n * callback receives the \"raw\" Y (`element.top + scrollY - headerOffset`) and\n * returns the FINAL target. Use when the caller knows about a layout shift\n * (e.g. a sibling drawer collapsing) the geometry can't yet reflect. */\n adjustTargetY?: (rawTargetY: number) => number\n /** Tween duration in ms (smooth only). Default 320. */\n durationMs?: number\n}\n\n/** Module-level handle to the in-flight tween so a new call (or a user\n * gesture) cancels the previous one — only ever one page-scroll animation at\n * a time. */\nlet activeRaf = 0\nlet teardownActive: (() => void) | null = null\n\nfunction cancelActiveScroll(): void {\n if (activeRaf) {\n cancelAnimationFrame(activeRaf)\n activeRaf = 0\n }\n if (teardownActive) {\n teardownActive()\n teardownActive = null\n }\n}\n\nconst easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3)\n\n/** Nearest ancestor that is a *real* scroll container, or `null` when the\n * window/document is the scroller. Only `auto | scroll | overlay` count —\n * `clip` / `hidden` are intentionally excluded (a wrapper using `overflow-clip`\n * purely to round corners must let the scroll bubble to the page). */\nfunction getScrollableAncestor(el: HTMLElement): HTMLElement | null {\n for (let node = el.parentElement; node; node = node.parentElement) {\n const overflowY = getComputedStyle(node).overflowY\n if (\n (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') &&\n node.scrollHeight > node.clientHeight\n ) {\n return node\n }\n }\n return null\n}\n\n/**\n * Scroll the page so `target` lands at the top of the viewport (below sticky\n * chrome via `headerOffset`). SSR-safe; `null`/`undefined` target is a no-op so\n * callers can pass refs without defensive branching.\n */\nexport function scrollElementIntoView(\n target: HTMLElement | null | undefined,\n options: ScrollElementIntoViewOptions = {},\n): void {\n if (typeof window === 'undefined' || !target) return\n const { headerOffset = 0, behavior = 'smooth', adjustTargetY, durationMs = 320 } = options\n\n // Pick the scroller ONCE: a fixed-height `<main overflow-y-auto>` shell scrolls\n // the element, a plain document scrolls the window. The choice can't change\n // mid-tween, so resolve it up front and route every read/write through it.\n const container = getScrollableAncestor(target)\n const readCurrent = (): number => (container ? container.scrollTop : window.scrollY)\n const writeTo = (y: number): void => {\n if (container) container.scrollTop = y\n else window.scrollTo(0, y)\n }\n\n // Target is recomputed every frame: the row's absolute position can move as\n // the page reflows (a sibling drawer collapsing) and the reachable max grows\n // as the just-opened drawer expands. Clamp to the LIVE max each frame.\n const computeTarget = (): number => {\n const raw = container\n ? container.scrollTop +\n (target.getBoundingClientRect().top - container.getBoundingClientRect().top) -\n headerOffset\n : target.getBoundingClientRect().top + window.scrollY - headerOffset\n const adjusted = adjustTargetY ? adjustTargetY(raw) : raw\n const maxScroll = container\n ? Math.max(0, container.scrollHeight - container.clientHeight)\n : Math.max(0, document.documentElement.scrollHeight - window.innerHeight)\n return Math.min(Math.max(0, adjusted), maxScroll)\n }\n\n // Any prior animation loses — one page scroll at a time.\n cancelActiveScroll()\n\n const prefersReduced =\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n // Instant paths: a single synchronous write. No tween, no anchoring race.\n if (behavior === 'instant' || behavior === 'auto' || prefersReduced) {\n writeTo(computeTarget())\n return\n }\n\n // Smooth: self-driven tween with instant per-frame writes (anchoring-proof).\n let startY: number | null = null\n let startTime = 0\n\n // Bail the moment the user takes over with a real scroll gesture — we must\n // never fight them. (Not keydown: the ticket composer auto-focuses on open,\n // and typing there should not abort the scroll.)\n const onUserGesture = () => cancelActiveScroll()\n window.addEventListener('wheel', onUserGesture, { passive: true })\n window.addEventListener('touchmove', onUserGesture, { passive: true })\n teardownActive = () => {\n window.removeEventListener('wheel', onUserGesture)\n window.removeEventListener('touchmove', onUserGesture)\n }\n\n const step = (now: number) => {\n if (startY === null) {\n startY = readCurrent()\n startTime = now\n }\n const targetY = computeTarget()\n const t = Math.min(1, (now - startTime) / durationMs)\n const y = startY + (targetY - startY) * easeOutCubic(t)\n writeTo(y)\n if (t < 1) {\n activeRaf = requestAnimationFrame(step)\n } else {\n // Final exact write in case easing left a sub-pixel gap, then teardown.\n writeTo(computeTarget())\n activeRaf = 0\n if (teardownActive) {\n teardownActive()\n teardownActive = null\n }\n }\n }\n activeRaf = requestAnimationFrame(step)\n}\n","import { scrollElementIntoView } from './scroll-into-view'\n\n/** Pages with a section-nav STRIP on top of the global hub header\n * (dev-center roadmap/delivery/tickets, FAQ category-pill nav).\n * Anchor lands BELOW both layers. */\nexport const STICKY_HEADER_OFFSET_PX = 96\n\n/** Pages with only the global hub header (docs, blog, vendor detail).\n * Anchor lands BELOW the header bar. */\nexport const HUB_HEADER_OFFSET_PX = 80\n\n/**\n * Take only the FIRST hash segment from a fragment that may contain extra\n * `#` characters. `'' → ''`, `'#a' → '#a'`, `'#a#b' → '#a'`.\n *\n * No real DOM id contains `#`, so a multi-fragment hash is always a bug at\n * the composer site; `navigateSamePageHash` + `useScrollToHash` both call\n * this so URL bar and `getElementById` stay in sync.\n */\nexport function normalizeHashFragment(hash: string): string {\n if (!hash) return ''\n const second = hash.indexOf('#', 1)\n return second < 0 ? hash : hash.slice(0, second)\n}\n\nexport interface NavigateSamePageHashOptions {\n /** Pixels to subtract for sticky chrome. */\n headerOffset?: number\n /** `'push'` (default) — new history entry; `'replace'` — overwrite\n * current entry (use for TOC-style in-page navigators). */\n history?: 'push' | 'replace'\n}\n\n/**\n * Same-page hash navigation primitive: pushState + synthetic `hashchange`\n * + anchoring-proof smooth scroll. Replaces `router.push` for hash CTAs\n * (Next.js suppresses smooth-scroll during navigation; `router.push` on\n * an exact-URL match is a no-op). Returns `true` when the helper claimed\n * the nav (same pathname + search); `false` for cross-page targets so\n * callers fall through to `router.push`.\n *\n * `target` accepts an origin-stripped path (`/x#anchor`) or a bare hash\n * (`#anchor`); bare-hash callers don't need to reconstruct `pathname +\n * search` themselves.\n */\nexport function navigateSamePageHash(\n target: string,\n options: NavigateSamePageHashOptions = {},\n): boolean {\n if (typeof window === 'undefined') return false\n const { headerOffset = 0, history: historyMode = 'push' } = options\n const normalizedTarget =\n target.startsWith('#')\n ? window.location.pathname + window.location.search + target\n : target\n // `new URL(absoluteUrl, base)` ignores `base` per RFC 3986; an absolute\n // cross-origin target sharing pathname/search would otherwise pass the\n // check below and trip pushState's same-origin enforcement. Parse with\n // an explicit base so malformed inputs cleanly fall through.\n let url: URL\n try {\n url = new URL(normalizedTarget, window.location.href)\n } catch {\n return false\n }\n if (\n url.origin !== window.location.origin ||\n url.pathname !== window.location.pathname ||\n url.search !== window.location.search\n ) {\n return false\n }\n const current = window.location.pathname + window.location.search + window.location.hash\n // Heal a malformed multi-fragment hash so the URL bar is clean and\n // `getElementById` resolves. Dev-warn fingers the upstream composer.\n const normalizedHash = normalizeHashFragment(url.hash)\n if (process.env.NODE_ENV === 'development' && normalizedHash !== url.hash) {\n // eslint-disable-next-line no-console\n console.warn(\n `[navigateSamePageHash] malformed fragment \"${url.hash}\" → normalizing to \"${normalizedHash}\". Fix the upstream composer.`,\n )\n }\n const next = url.pathname + url.search + normalizedHash\n const id = normalizedHash && normalizedHash !== '#' ? normalizedHash.slice(1) : ''\n // Hash-less targets are only ours on an EXACT URL re-click.\n if (!id && next !== current) return false\n if (next !== current) {\n const oldURL = window.location.href\n if (historyMode === 'replace') {\n window.history.replaceState(null, '', next)\n } else {\n window.history.pushState(null, '', next)\n }\n // Synthetic `hashchange` — `pushState` doesn't fire it (HTML spec),\n // so URL-hash-bound listeners (FAQ auto-expand, etc.) wouldn't react.\n window.dispatchEvent(new HashChangeEvent('hashchange', {\n oldURL,\n newURL: window.location.href,\n }))\n }\n const el = id ? document.getElementById(id) : null\n if (id && !el && process.env.NODE_ENV === 'development') {\n // eslint-disable-next-line no-console\n console.warn(\n `[navigateSamePageHash] anchor \"#${id}\" not found — scrolling to top.`,\n )\n }\n // Missing anchor → tween to page top. `documentElement` is at 0 by\n // definition, so one tween covers both branches.\n scrollElementIntoView(el ?? document.documentElement, {\n behavior: 'smooth',\n headerOffset,\n })\n return true\n}\n","/**\n * Humanity signals — invisible bot-protection primitives shared by the lib's\n * public forms (client) and the hub's per-route `verifyHuman` gate (server).\n *\n * PURE + React-free on purpose: this module is a tsup SERVER entry (no\n * \"use client\" banner) so the hub can import it server-side without pulling a\n * client-reference boundary — same pattern as `schemas/contact-schema` and\n * `components/features/mux-origins`.\n *\n * Two origin-independent signals travel in the POST body: a honeypot (a hidden\n * field real users never fill) and timing (ms from form mount to submit).\n * `evaluateHumanitySignals` is the SINGLE source of truth for the block/allow\n * decision — the hub imports + calls it rather than re-implementing the rules.\n */\n\n/** Hidden honeypot field name. Innocuous + autofill-resistant (deliberately NOT name/email). */\nexport const HONEYPOT_FIELD = 'contact_url_confirm'\n/** Client-measured ms between form mount and submit. */\nexport const ELAPSED_MS_FIELD = 'form_elapsed_ms'\n/** Default minimum fill time (ms). A submit faster than this is treated as a bot. */\nexport const DEFAULT_MIN_FILL_MS = 700\n\n/** Keyed wire object produced by `useHumanitySignals().getSignals()` and spread into the POST body. */\nexport type HumanitySignals = Record<string, string | number>\n\n/** Result of {@link evaluateHumanitySignals}. */\nexport type HumanityVerdict = { ok: true } | { ok: false; reason: 'honeypot' | 'too_fast' }\n\n/** Tolerant reader — never throws; missing/garbage timing → null. */\nexport function extractHumanitySignals(body: unknown): { honeypot: string; elapsedMs: number | null } {\n const b = (body ?? {}) as Record<string, unknown>\n const rawHp = b[HONEYPOT_FIELD]\n // A legit client always sends a STRING here (getSignals → ref.value ?? ''),\n // so ANY present non-string value is a bot filling the decoy with a non-string\n // to dodge the empty-check — coerce to a (non-empty) string so it still trips.\n // null/undefined → '' = the correct \"field absent / unfilled\" allow case.\n const honeypot = rawHp == null ? '' : String(rawHp)\n const rawMs = b[ELAPSED_MS_FIELD]\n const elapsedMs = typeof rawMs === 'number' && Number.isFinite(rawMs) ? rawMs : null\n return { honeypot, elapsedMs }\n}\n\n/**\n * SINGLE decision fn for honeypot + timing (the hub's `verifyHuman` imports + calls this):\n * - honeypot non-empty → bot (real users never fill the off-screen field)\n * - elapsed below `minFillMs` → bot (humans take time; a MISSING timing value never blocks)\n */\nexport function evaluateHumanitySignals(body: unknown, opts: { minFillMs: number }): HumanityVerdict {\n const { honeypot, elapsedMs } = extractHumanitySignals(body)\n if (honeypot.trim() !== '') return { ok: false, reason: 'honeypot' }\n if (elapsedMs !== null && elapsedMs < opts.minFillMs) return { ok: false, reason: 'too_fast' }\n return { ok: true }\n}\n\n/** Parse a comma-separated env string → trimmed, non-empty entries (undefined → []). */\nexport const splitCsvEnv = (s?: string): string[] =>\n s?.split(',').map((t) => t.trim()).filter(Boolean) ?? []\n"],"mappings":"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","names":[]}
|