Enable roadmap structures to open, preview, and embed directly from chat and dashboard views. - Register roadmap views as native structures with chat-reference support. - Add composer previews, in-place dashboard embedding, and drag-safe interactions. - Document the native-structure plugin contract and add coverage across supported surfaces. Files changed: .changeset/fn-8859-roadmap-native-structure-embeds.md | 7 + docs/PLUGIN_AUTHORING.md | 1 + docs/dashboard-guide.md | 6 +- packages/dashboard/app/components/MessageComposer.tsx | 16 +- packages/dashboard/app/components/__tests__/MessageComposer.test.tsx | 102 ++++++++++- packages/dashboard/app/components/__tests__/overflowViewRegistry.test.tsx | 68 +++++++- packages/dashboard/app/components/dashboard/MainContent.tsx | 22 ++- packages/dashboard/app/components/dashboard/__tests__/MainContent.native-structure-roadmap.test.tsx | 188 +++++++++++++++++++++ packages/dashboard/app/components/nativeStructureChatRef.ts | 4 +- packages/dashboard/app/components/overflowViewRegistry.tsx | 34 ++-- packages/dashboard/app/plugins/types.ts | 10 +- packages/dashboard/app/utils/__tests__/nativeStructureDrag.test.ts | 46 +++-- packages/dashboard/app/utils/nativeStructureDrag.ts | 18 +- plugins/fusion-plugin-roadmap/src/dashboard-view.tsx | 7 +- plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx | 22 ++- plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx | 51 ++++++ 16 files changed, 560 insertions(+), 42 deletions(-) Fusion-Task-Id: FN-8859 Fusion-Task-Lineage: 99a7292b-2a06-4325-8131-ca74431214e3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
59 lines
2.4 KiB
TypeScript
59 lines
2.4 KiB
TypeScript
import type { NativeStructureRef } from "@fusion/core";
|
|
|
|
const NATIVE_STRUCTURE_KINDS = [
|
|
"mission",
|
|
"milestone",
|
|
"roadmap-item",
|
|
"research-finding",
|
|
"eval-result",
|
|
"goal",
|
|
] as const;
|
|
|
|
type NativeStructureChatKind = (typeof NATIVE_STRUCTURE_KINDS)[number];
|
|
|
|
const nativeStructureKindsPattern = NATIVE_STRUCTURE_KINDS.join("|");
|
|
const canonicalRefPattern = new RegExp(`^fusion://(${nativeStructureKindsPattern})/([^/?#\\s]+)$`);
|
|
const trailingProsePunctuationPattern = /[.,!?;:]+$/;
|
|
|
|
/**
|
|
* FNXC:NativeStructureEmbed 2026-07-19-19:30:
|
|
* Chat references use the explicit `fusion://<kind>/<id>` form instead of free-text matching so
|
|
* ordinary prose cannot accidentally fetch or expose a native structure. This matcher finds only
|
|
* complete bare tokens in surrounding text; every candidate is still validated by the parser.
|
|
*/
|
|
export const nativeStructureChatRefMatcher = new RegExp(
|
|
`(?<![A-Za-z0-9_-])(fusion://(?:${nativeStructureKindsPattern})/[A-Za-z0-9][A-Za-z0-9._:-]*?)([.,!?;:]*)?(?=$|[\\s<>()\\[\\]{}])`,
|
|
"g",
|
|
);
|
|
|
|
/** Returns the canonical token and only terminal prose punctuation consumed by the bare-token matcher. */
|
|
export function splitNativeStructureChatRefMatch(match: RegExpMatchArray): { token: string; trailingPunctuation: string } {
|
|
return { token: match[1] ?? match[0].replace(trailingProsePunctuationPattern, ""), trailingPunctuation: match[2] ?? "" };
|
|
}
|
|
|
|
/**
|
|
* FNXC:NativeStructureEmbed 2026-07-19-19:30:
|
|
* One strict parser is shared by assistant Markdown links, assistant text nodes, and raw user
|
|
* messages. Encoded path separators and extra segments are rejected so a display token cannot
|
|
* resolve a structure other than the one it visibly names.
|
|
*/
|
|
export function parseNativeStructureChatRef(hrefOrToken: string): NativeStructureRef | null {
|
|
const match = canonicalRefPattern.exec(hrefOrToken);
|
|
if (!match) return null;
|
|
|
|
const [, kind, id] = match;
|
|
if (!kind || !id || /%2f|%5c/i.test(id)) return null;
|
|
|
|
let decodedId: string;
|
|
try {
|
|
decodedId = decodeURIComponent(id);
|
|
} catch {
|
|
return null;
|
|
}
|
|
if (!decodedId.trim() || decodedId !== id || /[\\/]/.test(decodedId)) return null;
|
|
|
|
// FNXC:NativeStructureEmbed 2026-08-09-05:13: FN-8859 keeps roadmap-item on the shared contract;
|
|
// its roadmap adapter now resolves previews and mail can attach the same first-class reference.
|
|
return { kind: kind as NativeStructureChatKind, id } as NativeStructureRef;
|
|
}
|