Artifact surfaces
An artifact surface owns the body of a durable side-panel view. Declare a stable app-namespaced artifactKind; Core uses it to resolve the current native module in that app lane when the app opens the artifact.
contributes:
ui:
- id: report-viewer
kind: nativeModule
surface: artifact
slot: artifact.slot.reports-viewer
artifactKind: reports.viewer
module:
entry: dist/ui/app.js
export: ReportViewer
styles: dist/ui/app.cssimport { useAppContext } from '@sota/platform';
type ArtifactProps = {
artifact: { artifactKind: string; context?: Record<string, unknown> };
};
export function ReportViewer({ artifact }: ArtifactProps) {
const { ui } = useAppContext();
const title = typeof artifact.context?.title === 'string'
? artifact.context.title
: 'Report';
return (
<article>
<h1>{title}</h1>
<button type="button" onClick={ui.closeArtifact}>Close</button>
</article>
);
}Open it from any surface in the same app lane with useAppContext().ui.openArtifact('reports.viewer', context). The lane's current app renders the opaque artifact.context; apps own compatibility with payloads created by earlier releases. Artifact kinds must be unique for the resolved workspace; the core.* namespace is reserved.
import { useAppContext } from '@sota/platform';
export function OpenReportButton({ reportId }: { reportId: string }) {
const { ui } = useAppContext();
return (
<button type="button" onClick={() => ui.openArtifact('reports.viewer', {
reportId,
title: 'Quarterly report',
})}>
Open report
</button>
);
}Validate artifact.context before using it; it is structural transport data, not a substitute for loading authoritative records from the app backend.