mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-16 06:39:01 +02:00
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
/**
|
|
* Shared link handling utilities for the editor system.
|
|
*
|
|
* Used by content-editor (ProseMirror click handler), readonly-content
|
|
* (react-markdown link component), and link-hover-card (Open button).
|
|
*/
|
|
|
|
import { isGlobalPath } from "@multica/core/paths";
|
|
|
|
/**
|
|
* Top-level workspace-scoped routes. Used to detect "/{route}/..." paths that
|
|
* were authored without a workspace slug — we prepend the current slug so they
|
|
* resolve correctly under the new /{slug}/{route}/... URL shape.
|
|
*
|
|
* Why a hardcoded allowlist: the heuristic must be conservative. A path like
|
|
* "/acme/issues/abc" already has a slug (first segment "acme" isn't a known
|
|
* route), so leaving it alone is correct. A path like "/foo/bar" where "foo"
|
|
* isn't a known route is ambiguous — we don't rewrite it, treating the author
|
|
* as intentional. Only "/issues/..." style paths get auto-prefixed.
|
|
*/
|
|
const WORKSPACE_ROUTE_SEGMENTS = new Set([
|
|
"issues",
|
|
"projects",
|
|
"autopilots",
|
|
"agents",
|
|
"inbox",
|
|
"my-issues",
|
|
"runtimes",
|
|
"skills",
|
|
"settings",
|
|
]);
|
|
|
|
/**
|
|
* Open a link — internal paths dispatch multica:navigate, external open new tab.
|
|
*
|
|
* If `currentSlug` is provided and `href` is a workspace-scoped path lacking a
|
|
* slug (e.g. "/issues/abc" instead of "/{slug}/issues/abc"), the slug is
|
|
* prepended. This is for legacy markdown content authored before the URL
|
|
* refactor, or future content where users forget the slug when pasting.
|
|
*/
|
|
export function openLink(href: string, currentSlug?: string | null): void {
|
|
if (href.startsWith("/")) {
|
|
let path = href;
|
|
if (currentSlug && !isGlobalPath(path)) {
|
|
const firstSegment = path.split("/")[1];
|
|
if (firstSegment && WORKSPACE_ROUTE_SEGMENTS.has(firstSegment)) {
|
|
// Path looks like /issues/abc (no slug) — prepend current slug.
|
|
path = `/${currentSlug}${path}`;
|
|
}
|
|
// Otherwise the first segment is either already a slug (e.g. "acme" in
|
|
// "/acme/issues") or something unknown (e.g. "/foo"). Leave it alone —
|
|
// the user wrote what they meant.
|
|
}
|
|
window.dispatchEvent(
|
|
new CustomEvent("multica:navigate", { detail: { path } }),
|
|
);
|
|
} else {
|
|
window.open(href, "_blank", "noopener,noreferrer");
|
|
}
|
|
}
|
|
|
|
/** Check if a href is a mention protocol link (should not be opened as a regular link). */
|
|
export function isMentionHref(href: string | null | undefined): href is string {
|
|
return !!href && href.startsWith("mention://");
|
|
}
|