Skip to content

Commit 7d690ae

Browse files
authored
Merge pull request #1080 from objectstack-ai/copilot/fix-code-tab-export-issue
2 parents 68a673c + 07f22bd commit 7d690ae

File tree

2 files changed

+99
-9
lines changed

2 files changed

+99
-9
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Fixed
11+
- **Studio: Code tab now shows CodeExporter** — The Code tab in Studio metadata detail pages
12+
now correctly renders the `CodeExporter` component (TypeScript/JSON export with copy-to-clipboard)
13+
instead of always showing the JSON Inspector preview. The default plugin now registers two separate
14+
viewers: `json-inspector` for preview mode and `code-exporter` for code mode.
1115
- **CI Test Failures** — Resolved test failures across multiple packages:
1216
- `@objectstack/service-ai`: Fixed SDK fallback test by mocking `@ai-sdk/openai` dynamic import
1317
(SDK now available as transitive workspace dependency)

apps/studio/src/plugins/built-in/default-plugin.tsx

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,130 @@
33
/**
44
* Built-in Plugin: Default Metadata Inspector
55
*
6-
* Provides a JSON tree viewer as the fallback for any metadata type
7-
* that doesn't have a specialized plugin. This is the "catch-all" viewer.
6+
* Provides two fallback viewers for any metadata type:
7+
* - **json-inspector** (preview mode): JSON tree viewer via MetadataInspector
8+
* - **code-exporter** (code mode): Exportable TypeScript/JSON via CodeExporter
89
*
910
* Priority is set to -1 so any type-specific plugin will take precedence.
1011
*/
1112

13+
import { useState, useEffect } from 'react';
1214
import { defineStudioPlugin } from '@objectstack/spec/studio';
15+
import { useClient } from '@objectstack/client-react';
1316
import { MetadataInspector } from '@/components/MetadataInspector';
17+
import { CodeExporter } from '@/components/CodeExporter';
18+
import type { CodeExporterProps } from '@/components/CodeExporter';
1419
import type { StudioPlugin, MetadataViewerProps } from '../types';
1520

16-
// ─── Viewer Component (adapts MetadataInspector to plugin interface)
21+
// ─── Helpers ────────────────────────────────────────────────────────
1722

18-
function DefaultViewerComponent({ metadataType, metadataName, packageId }: MetadataViewerProps) {
23+
/** Map Studio metadataType (often plural) to CodeExporter's ExportType (singular). */
24+
const METADATA_TO_EXPORT_TYPE: Record<string, CodeExporterProps['type']> = {
25+
object: 'object',
26+
objects: 'object',
27+
view: 'view',
28+
views: 'view',
29+
flow: 'flow',
30+
flows: 'flow',
31+
agent: 'agent',
32+
agents: 'agent',
33+
app: 'app',
34+
apps: 'app',
35+
};
36+
37+
// ─── Preview Viewer (JSON Inspector) ─────────────────────────────────
38+
39+
function PreviewViewerComponent({ metadataType, metadataName, packageId }: MetadataViewerProps) {
1940
return <MetadataInspector metaType={metadataType} metaName={metadataName} packageId={packageId} />;
2041
}
2142

43+
// ─── Code Viewer (Code Exporter) ─────────────────────────────────────
44+
45+
function CodeViewerComponent({ metadataType, metadataName, data, packageId }: MetadataViewerProps) {
46+
const client = useClient();
47+
const [definition, setDefinition] = useState<Record<string, unknown> | null>(data ?? null);
48+
const [loading, setLoading] = useState(!data);
49+
50+
useEffect(() => {
51+
// If data was passed directly, use it
52+
if (data) {
53+
setDefinition(data as Record<string, unknown>);
54+
setLoading(false);
55+
return;
56+
}
57+
58+
let mounted = true;
59+
setLoading(true);
60+
61+
async function load() {
62+
try {
63+
const result: any = await client.meta.getItem(metadataType, metadataName, packageId ? { packageId } : undefined);
64+
if (mounted) {
65+
setDefinition(result?.item || result);
66+
}
67+
} catch (err) {
68+
console.error(`[CodeViewer] Failed to load ${metadataType}/${metadataName}:`, err);
69+
} finally {
70+
if (mounted) setLoading(false);
71+
}
72+
}
73+
load();
74+
return () => { mounted = false; };
75+
}, [client, metadataType, metadataName, data, packageId]);
76+
77+
if (loading) {
78+
return (
79+
<div className="p-4 text-sm text-muted-foreground">Loading definition…</div>
80+
);
81+
}
82+
83+
if (!definition) {
84+
return (
85+
<div className="p-4 text-sm text-muted-foreground">
86+
Definition not found: <code className="font-mono">{metadataName}</code>
87+
</div>
88+
);
89+
}
90+
91+
const exportType = METADATA_TO_EXPORT_TYPE[metadataType] ?? 'object';
92+
93+
return (
94+
<div className="p-4">
95+
<CodeExporter type={exportType} definition={definition} name={metadataName} />
96+
</div>
97+
);
98+
}
99+
22100
// ─── Plugin Definition ───────────────────────────────────────────────
23101

24102
export const defaultInspectorPlugin: StudioPlugin = {
25103
manifest: defineStudioPlugin({
26104
id: 'objectstack.default-inspector',
27105
name: 'Default Metadata Inspector',
28106
version: '1.0.0',
29-
description: 'JSON tree viewer for any metadata type. Fallback when no specialized viewer is available.',
107+
description: 'JSON tree viewer and code exporter for any metadata type. Fallback when no specialized viewer is available.',
30108
contributes: {
31109
metadataViewers: [
32110
{
33111
id: 'json-inspector',
34-
metadataTypes: ['*'], // Wildcard: matches all types
112+
metadataTypes: ['*'],
35113
label: 'JSON Inspector',
36-
priority: -1, // Lowest priority — any plugin overrides this
37-
modes: ['preview', 'code'],
114+
priority: -1,
115+
modes: ['preview'],
116+
},
117+
{
118+
id: 'code-exporter',
119+
metadataTypes: ['*'],
120+
label: 'Code Export',
121+
priority: -1,
122+
modes: ['code'],
38123
},
39124
],
40125
},
41126
}),
42127

43128
activate(api) {
44-
api.registerViewer('json-inspector', DefaultViewerComponent);
129+
api.registerViewer('json-inspector', PreviewViewerComponent);
130+
api.registerViewer('code-exporter', CodeViewerComponent);
45131
},
46132
};

0 commit comments

Comments
 (0)