{"version":3,"sources":["../src/platformLink.tsx"],"sourcesContent":["import type { AnchorHTMLAttributes, ReactNode } from 'react';\nimport { use } from 'react';\n\nimport { isBrowserGestureClick, useComposedAnchorClick } from './anchorClick';\nimport { navigate } from './routing';\nimport { TinkerableContext } from './TinkerableContext';\nimport { platformHref } from './urlUtils';\n\n/**\n * Build a PLATFORM-space href (`/present/…`, `/edit/github/…`, `/home`) in the host's URL\n * space, reading `outerHref` from {@link TinkerableContext} the way `useTinkerableLink` does.\n * The returned closure is fresh each render (its output is pure, so identity churn is\n * harmless); an empty context (no host, `vite dev`) yields the path unchanged.\n *\n * Prefer {@link PlatformLink} over calling this directly. An href alone does not reach a\n * platform route from inside the app frame (see that component), so a consumer that renders\n * its own anchor from this string must ask the host itself — otherwise it ships a link that\n * copies and opens-in-new-tab correctly and does nothing at all on a plain click. It is kept\n * exported because the wire and the module surface are additive-only\n * (`SDK_PACKAGING_SPEC` §9): an app pinned to an older SDK may already import it.\n */\nexport const usePlatformHref = (): ((path: string) => string) => {\n  const { outerHref } = use(TinkerableContext);\n  return (path: string) => platformHref(outerHref, path);\n};\n\n/**\n * Targets that reuse an existing browsing context. All three are unreachable from inside the\n * sandboxed app frame by the anchor alone — `_top`/`_parent` are refused outright, `_self`\n * merely moves the frame — so all three are asked of the host instead. Anything else opens a\n * new context, which the sandbox allows.\n */\nconst SAME_CONTEXT_TARGETS = new Set(['_top', '_self', '_parent']);\n\nexport interface PlatformLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n  /** A root-relative platform path, e.g. `/present/github/acme/todo`. */\n  path: string;\n  children?: ReactNode;\n}\n\n/**\n * The ONE way to render an anchor to a PLATFORM route.\n *\n * It builds the href with {@link platformHref} (resolving against the host's outer origin)\n * and, on a plain left-click, asks the HOST to perform the navigation.\n *\n * **Why the anchor alone does not work (R3-568).** Until R3-568 this component relied solely\n * on `target=\"_top\"`. The app frame's sandbox omits `allow-top-navigation-by-user-activation`\n * deliberately — an app that can move the top-level window on its own schedule is a phishing\n * primitive — so the browser refuses outright and logs *\"Unsafe attempt to initiate\n * navigation…\"*. Every platform link in every app was inert on the host: measured on\n * production, no Open, no Fork, no Run, and no way to sign in.\n *\n * The host is therefore the only thing that can perform this navigation, and it is asked the\n * same way in-app routing asks — {@link navigate}. The host decides: it accepts a target\n * outside the app's own path prefix only when the target is same-origin, is a recognised\n * platform route, and the HOST's own `navigator.userActivation` says a person just acted.\n * Nothing the app asserts substitutes for that gesture, and a refusal is the host's to report.\n *\n * **The `href` and `target` stay.** They are what make copy-link, middle-click and\n * open-in-new-tab produce something that resolves for another reader — gestures the sandbox\n * does allow (`allow-popups`), which the handler below deliberately declines to intercept.\n * The href is also the correct behaviour with no host at all (`vite dev`), where there is\n * nobody to ask.\n *\n * External URLs (`https://…`) are not platform routes. On a host they should be opened\n * through {@link openExternal} — the app asks the host, which validates the URL, confirms\n * the destination, and opens the tab (a plain `<a target=\"_blank\">` from inside the\n * sandboxed frame opens a tab with no origin of its own, so anything that signs in or\n * posts fails). The plain `<a target=\"_blank\">` anchor is kept only as the no-host\n * fallback (`vite dev`), where there is nobody to ask.\n */\nexport function PlatformLink({ path, children, onClick, target = '_top', ...rest }: PlatformLinkProps) {\n  const { outerHref } = use(TinkerableContext);\n  const href = platformHref(outerHref, path);\n\n  const clickHandler = useComposedAnchorClick(\n    onClick,\n    (event) => {\n      // Open-in-new-tab gestures are the browser's — the sandbox allows those.\n      if (isBrowserGestureClick(event)) return;\n      // Intercept every target that stays in an EXISTING browsing context, not just the\n      // default. `_top` and `_parent` both address the host document from inside the app\n      // frame and are refused by the same missing sandbox flag; `_self` would navigate the\n      // app frame itself to a host URL, framing the host inside its own sandbox — the\n      // regression `components/Link.tsx` documents. Only a NEW context (`_blank`, a named\n      // window) is genuinely the browser's, because that is what `allow-popups` permits.\n      if (!SAME_CONTEXT_TARGETS.has(target)) return;\n      // No host (`vite dev`): there is nobody to ask, and the anchor's own href is right.\n      if (!outerHref) return;\n      event.preventDefault();\n      navigate(href);\n    },\n    [href, outerHref, target],\n  );\n\n  return (\n    <a {...rest} href={href} target={target} onClick={clickHandler}>\n      {children}\n    </a>\n  );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiGI;AAhGJ,mBAAoB;AAEpB,yBAA8D;AAC9D,qBAAyB;AACzB,+BAAkC;AAClC,sBAA6B;AAetB,MAAM,kBAAkB,MAAkC;AAC/D,QAAM,EAAE,UAAU,QAAI,kBAAI,0CAAiB;AAC3C,SAAO,CAAC,aAAiB,8BAAa,WAAW,IAAI;AACvD;AAQA,MAAM,uBAAuB,oBAAI,IAAI,CAAC,QAAQ,SAAS,SAAS,CAAC;AAwC1D,SAAS,aAAa,EAAE,MAAM,UAAU,SAAS,SAAS,QAAQ,GAAG,KAAK,GAAsB;AACrG,QAAM,EAAE,UAAU,QAAI,kBAAI,0CAAiB;AAC3C,QAAM,WAAO,8BAAa,WAAW,IAAI;AAEzC,QAAM,mBAAe;AAAA,IACnB;AAAA,IACA,CAAC,UAAU;AAET,cAAI,0CAAsB,KAAK,EAAG;AAOlC,UAAI,CAAC,qBAAqB,IAAI,MAAM,EAAG;AAEvC,UAAI,CAAC,UAAW;AAChB,YAAM,eAAe;AACrB,mCAAS,IAAI;AAAA,IACf;AAAA,IACA,CAAC,MAAM,WAAW,MAAM;AAAA,EAC1B;AAEA,SACE,4CAAC,OAAG,GAAG,MAAM,MAAY,QAAgB,SAAS,cAC/C,UACH;AAEJ;","names":[]}