@vertesia/tools-admin-ui 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +120 -0
  3. package/lib/AdminApp.d.ts +13 -0
  4. package/lib/AdminApp.d.ts.map +1 -0
  5. package/lib/AdminContext.d.ts +10 -0
  6. package/lib/AdminContext.d.ts.map +1 -0
  7. package/lib/components/CollectionCard.d.ts +5 -0
  8. package/lib/components/CollectionCard.d.ts.map +1 -0
  9. package/lib/components/DetailPage.d.ts +13 -0
  10. package/lib/components/DetailPage.d.ts.map +1 -0
  11. package/lib/components/EndpointPanel.d.ts +5 -0
  12. package/lib/components/EndpointPanel.d.ts.map +1 -0
  13. package/lib/components/HeroSection.d.ts +9 -0
  14. package/lib/components/HeroSection.d.ts.map +1 -0
  15. package/lib/components/ResourceCard.d.ts +5 -0
  16. package/lib/components/ResourceCard.d.ts.map +1 -0
  17. package/lib/components/ResourceSection.d.ts +10 -0
  18. package/lib/components/ResourceSection.d.ts.map +1 -0
  19. package/lib/components/SearchBar.d.ts +10 -0
  20. package/lib/components/SearchBar.d.ts.map +1 -0
  21. package/lib/components/SummaryBadge.d.ts +5 -0
  22. package/lib/components/SummaryBadge.d.ts.map +1 -0
  23. package/lib/components/index.d.ts +9 -0
  24. package/lib/components/index.d.ts.map +1 -0
  25. package/lib/hooks.d.ts +26 -0
  26. package/lib/hooks.d.ts.map +1 -0
  27. package/lib/index.d.ts +7 -0
  28. package/lib/index.d.ts.map +1 -0
  29. package/lib/pages/HomePage.d.ts +2 -0
  30. package/lib/pages/HomePage.d.ts.map +1 -0
  31. package/lib/pages/InteractionCollection.d.ts +2 -0
  32. package/lib/pages/InteractionCollection.d.ts.map +1 -0
  33. package/lib/pages/InteractionDetail.d.ts +2 -0
  34. package/lib/pages/InteractionDetail.d.ts.map +1 -0
  35. package/lib/pages/SkillCollection.d.ts +2 -0
  36. package/lib/pages/SkillCollection.d.ts.map +1 -0
  37. package/lib/pages/SkillDetail.d.ts +2 -0
  38. package/lib/pages/SkillDetail.d.ts.map +1 -0
  39. package/lib/pages/TemplateCollection.d.ts +2 -0
  40. package/lib/pages/TemplateCollection.d.ts.map +1 -0
  41. package/lib/pages/TemplateDetail.d.ts +2 -0
  42. package/lib/pages/TemplateDetail.d.ts.map +1 -0
  43. package/lib/pages/ToolCollection.d.ts +2 -0
  44. package/lib/pages/ToolCollection.d.ts.map +1 -0
  45. package/lib/pages/TypeCollection.d.ts +2 -0
  46. package/lib/pages/TypeCollection.d.ts.map +1 -0
  47. package/lib/pages/TypeDetail.d.ts +2 -0
  48. package/lib/pages/TypeDetail.d.ts.map +1 -0
  49. package/lib/tools-admin-ui.js +935 -0
  50. package/lib/tools-admin-ui.js.map +1 -0
  51. package/lib/types.d.ts +89 -0
  52. package/lib/types.d.ts.map +1 -0
  53. package/package.json +50 -0
  54. package/src/AdminApp.tsx +87 -0
  55. package/src/AdminContext.ts +17 -0
  56. package/src/admin.css +650 -0
  57. package/src/components/CollectionCard.tsx +23 -0
  58. package/src/components/DetailPage.tsx +40 -0
  59. package/src/components/EndpointPanel.tsx +24 -0
  60. package/src/components/HeroSection.tsx +88 -0
  61. package/src/components/ResourceCard.tsx +25 -0
  62. package/src/components/ResourceSection.tsx +31 -0
  63. package/src/components/SearchBar.tsx +35 -0
  64. package/src/components/SummaryBadge.tsx +9 -0
  65. package/src/components/index.ts +8 -0
  66. package/src/dev/env.ts +14 -0
  67. package/src/dev/main.tsx +37 -0
  68. package/src/hooks.ts +36 -0
  69. package/src/index.ts +6 -0
  70. package/src/pages/HomePage.tsx +99 -0
  71. package/src/pages/InteractionCollection.tsx +59 -0
  72. package/src/pages/InteractionDetail.tsx +92 -0
  73. package/src/pages/SkillCollection.tsx +111 -0
  74. package/src/pages/SkillDetail.tsx +112 -0
  75. package/src/pages/TemplateCollection.tsx +54 -0
  76. package/src/pages/TemplateDetail.tsx +68 -0
  77. package/src/pages/ToolCollection.tsx +55 -0
  78. package/src/pages/TypeCollection.tsx +60 -0
  79. package/src/pages/TypeDetail.tsx +63 -0
  80. package/src/types.ts +304 -0
  81. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,111 @@
1
+ import { useMemo } from 'react';
2
+ import { useFetch } from '@vertesia/ui/core';
3
+ import { useParams, NavLink } from '@vertesia/ui/router';
4
+ import { useAdminContext } from '../AdminContext.js';
5
+ import { DetailPage } from '../components/DetailPage.js';
6
+
7
+ interface SkillToolDef {
8
+ name: string;
9
+ description?: string;
10
+ related_tools?: string[];
11
+ }
12
+
13
+ interface SkillCollectionResponse {
14
+ title: string;
15
+ description: string;
16
+ tools: SkillToolDef[];
17
+ }
18
+
19
+ interface WidgetInfo {
20
+ skill: string;
21
+ collection: string;
22
+ url: string;
23
+ }
24
+
25
+ interface WidgetsResponse {
26
+ widgets: Record<string, WidgetInfo>;
27
+ }
28
+
29
+ /** Strip the learn_ prefix added by the SDK when exposing skills as tools. */
30
+ function skillDisplayName(name: string): string {
31
+ return name.startsWith('learn_') ? name.slice(6) : name;
32
+ }
33
+
34
+ export function SkillCollection() {
35
+ const collection = useParams('collection');
36
+ const { baseUrl } = useAdminContext();
37
+
38
+ const { data, isLoading, error } = useFetch<SkillCollectionResponse>(
39
+ () => fetch(`${baseUrl}/skills/${collection}`).then(r => {
40
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
41
+ return r.json();
42
+ }),
43
+ [baseUrl, collection]
44
+ );
45
+
46
+ const { data: widgetsData } = useFetch<WidgetsResponse>(
47
+ () => fetch(`${baseUrl}/package?scope=widgets`).then(r => r.ok ? r.json() : { widgets: {} }),
48
+ [baseUrl]
49
+ );
50
+
51
+ const collectionWidgets = useMemo(() => {
52
+ if (!widgetsData?.widgets) return [];
53
+ return Object.entries(widgetsData.widgets)
54
+ .filter(([_, w]) => w.collection === collection)
55
+ .map(([name, w]) => ({ name, ...w }));
56
+ }, [widgetsData, collection]);
57
+
58
+ if (isLoading) return <div className="vta-loading">Loading collection...</div>;
59
+ if (error || !data) return <div className="vta-error">Failed to load skill collection &ldquo;{collection}&rdquo;.</div>;
60
+
61
+ return (
62
+ <DetailPage
63
+ type="skill"
64
+ title={data.title || collection}
65
+ description={data.description || `${data.tools.length} skill${data.tools.length !== 1 ? 's' : ''} in this collection.`}
66
+ >
67
+ {/* Widgets provided by this collection */}
68
+ {collectionWidgets.length > 0 && (
69
+ <div className="vta-detail-section">
70
+ <h2>Widgets</h2>
71
+ <div className="vta-detail-flags">
72
+ {collectionWidgets.map(w => (
73
+ <span key={w.name} className="vta-detail-flag">
74
+ {w.name}
75
+ <span className="vta-card-url" style={{ marginLeft: '0.5rem' }}>
76
+ (skill: {w.skill})
77
+ </span>
78
+ </span>
79
+ ))}
80
+ </div>
81
+ </div>
82
+ )}
83
+
84
+ <div className="vta-card-grid">
85
+ {data.tools.map(skill => {
86
+ const displayName = skillDisplayName(skill.name);
87
+ return (
88
+ <NavLink
89
+ key={skill.name}
90
+ href={`/skills/${collection}/${displayName}`}
91
+ className="vta-card-link"
92
+ >
93
+ <div className="vta-card vta-card--link">
94
+ <span className="vta-card-type vta-card-type--skill">skill</span>
95
+ <div className="vta-card-title">{displayName}</div>
96
+ <div className="vta-card-desc">{skill.description || 'No description'}</div>
97
+ {skill.related_tools && skill.related_tools.length > 0 && (
98
+ <div className="vta-card-tags">
99
+ {skill.related_tools.map(t => (
100
+ <span key={t} className="vta-tag">{t}</span>
101
+ ))}
102
+ </div>
103
+ )}
104
+ </div>
105
+ </NavLink>
106
+ );
107
+ })}
108
+ </div>
109
+ </DetailPage>
110
+ );
111
+ }
@@ -0,0 +1,112 @@
1
+ import { useFetch } from '@vertesia/ui/core';
2
+ import { useParams } from '@vertesia/ui/router';
3
+ import { useAdminContext } from '../AdminContext.js';
4
+ import { DetailPage } from '../components/DetailPage.js';
5
+
6
+ interface SkillDefinitionResponse {
7
+ name: string;
8
+ title?: string;
9
+ description: string;
10
+ instructions: string;
11
+ content_type: 'md' | 'jst';
12
+ input_schema?: Record<string, unknown>;
13
+ related_tools?: string[];
14
+ execution?: {
15
+ language: string;
16
+ packages?: string[];
17
+ };
18
+ scripts?: string[];
19
+ widgets?: string[];
20
+ }
21
+
22
+ export function SkillDetail() {
23
+ const params = useParams();
24
+ const collection = params.collection;
25
+ const name = params.name;
26
+ const { baseUrl } = useAdminContext();
27
+
28
+ const { data: skill, isLoading, error } = useFetch<SkillDefinitionResponse>(
29
+ () => fetch(`${baseUrl}/skills/${collection}/${name}`).then(r => {
30
+ if (!r.ok) throw new Error(`Failed to load skill: ${r.statusText}`);
31
+ return r.json();
32
+ }),
33
+ [baseUrl, collection, name]
34
+ );
35
+
36
+ if (isLoading) return <div className="vta-loading">Loading skill...</div>;
37
+ if (error || !skill) return <div className="vta-error">Failed to load skill &ldquo;{name}&rdquo;.</div>;
38
+
39
+ return (
40
+ <DetailPage
41
+ type="skill"
42
+ title={skill.title || skill.name}
43
+ description={skill.description}
44
+ backHref={`/skills/${collection}`}
45
+ >
46
+ {/* Widgets */}
47
+ {skill.widgets && skill.widgets.length > 0 && (
48
+ <div className="vta-detail-section">
49
+ <h2>Widgets</h2>
50
+ <div className="vta-detail-flags">
51
+ {skill.widgets.map(w => (
52
+ <span key={w} className="vta-detail-flag">{w}</span>
53
+ ))}
54
+ </div>
55
+ </div>
56
+ )}
57
+
58
+ {/* Scripts */}
59
+ {skill.scripts && skill.scripts.length > 0 && (
60
+ <div className="vta-detail-section">
61
+ <h2>Scripts</h2>
62
+ <div className="vta-detail-flags">
63
+ {skill.scripts.map(s => (
64
+ <span key={s} className="vta-detail-flag">{s}</span>
65
+ ))}
66
+ </div>
67
+ </div>
68
+ )}
69
+
70
+ {/* Related tools */}
71
+ {skill.related_tools && skill.related_tools.length > 0 && (
72
+ <div className="vta-detail-section">
73
+ <h2>Related Tools</h2>
74
+ <div className="vta-detail-flags">
75
+ {skill.related_tools.map(t => (
76
+ <span key={t} className="vta-detail-flag">{t}</span>
77
+ ))}
78
+ </div>
79
+ </div>
80
+ )}
81
+
82
+ {/* Execution environment */}
83
+ {skill.execution && (
84
+ <div className="vta-detail-section">
85
+ <h2>Execution</h2>
86
+ <div className="vta-detail-flags">
87
+ <span className="vta-detail-flag">{skill.execution.language}</span>
88
+ {skill.execution.packages?.map(p => (
89
+ <span key={p} className="vta-detail-flag">{p}</span>
90
+ ))}
91
+ </div>
92
+ </div>
93
+ )}
94
+
95
+ {/* Instructions */}
96
+ <div className="vta-detail-section">
97
+ <h2>Instructions {skill.content_type === 'jst' && <span className="vta-tag">JST template</span>}</h2>
98
+ <pre className="vta-detail-code">{skill.instructions}</pre>
99
+ </div>
100
+
101
+ {/* Input schema */}
102
+ {skill.input_schema && (
103
+ <div className="vta-detail-section">
104
+ <h2>Input Schema</h2>
105
+ <pre className="vta-detail-code">
106
+ {JSON.stringify(skill.input_schema, null, 2)}
107
+ </pre>
108
+ </div>
109
+ )}
110
+ </DetailPage>
111
+ );
112
+ }
@@ -0,0 +1,54 @@
1
+ import { useFetch } from '@vertesia/ui/core';
2
+ import { useParams, NavLink } from '@vertesia/ui/router';
3
+ import type { RenderingTemplateDefinitionRef } from '@vertesia/common';
4
+ import { useAdminContext } from '../AdminContext.js';
5
+ import { DetailPage } from '../components/DetailPage.js';
6
+
7
+ export function TemplateCollection() {
8
+ const collection = useParams('collection');
9
+ const { baseUrl } = useAdminContext();
10
+
11
+ const { data: templates, isLoading, error } = useFetch<RenderingTemplateDefinitionRef[]>(
12
+ () => fetch(`${baseUrl}/templates/${collection}`).then(r => {
13
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
14
+ return r.json();
15
+ }),
16
+ [baseUrl, collection]
17
+ );
18
+
19
+ if (isLoading) return <div className="vta-loading">Loading collection...</div>;
20
+ if (error || !templates) return <div className="vta-error">Failed to load template collection &ldquo;{collection}&rdquo;.</div>;
21
+
22
+ return (
23
+ <DetailPage
24
+ type="template"
25
+ title={collection}
26
+ description={`${templates.length} template${templates.length !== 1 ? 's' : ''} in this collection.`}
27
+ >
28
+ <div className="vta-card-grid">
29
+ {templates.map(tmpl => (
30
+ <NavLink
31
+ key={tmpl.name}
32
+ href={`/templates/${collection}/${tmpl.name}`}
33
+ className="vta-card-link"
34
+ >
35
+ <div className="vta-card vta-card--link">
36
+ <div className="vta-card-type vta-card-type--template">
37
+ {tmpl.type || 'template'}
38
+ </div>
39
+ <div className="vta-card-title">{tmpl.title || tmpl.name}</div>
40
+ <div className="vta-card-desc">{tmpl.description || 'No description'}</div>
41
+ {tmpl.tags && tmpl.tags.length > 0 && (
42
+ <div className="vta-card-tags">
43
+ {tmpl.tags.map(tag => (
44
+ <span key={tag} className="vta-tag">{tag}</span>
45
+ ))}
46
+ </div>
47
+ )}
48
+ </div>
49
+ </NavLink>
50
+ ))}
51
+ </div>
52
+ </DetailPage>
53
+ );
54
+ }
@@ -0,0 +1,68 @@
1
+ import { useFetch } from '@vertesia/ui/core';
2
+ import { useParams } from '@vertesia/ui/router';
3
+ import { useAdminContext } from '../AdminContext.js';
4
+ import { DetailPage } from '../components/DetailPage.js';
5
+
6
+ interface TemplateDefinitionResponse {
7
+ name: string;
8
+ title?: string;
9
+ description: string;
10
+ type: 'presentation' | 'document';
11
+ tags?: string[];
12
+ assets: string[];
13
+ instructions: string;
14
+ }
15
+
16
+ export function TemplateDetail() {
17
+ const params = useParams();
18
+ const collection = params.collection;
19
+ const name = params.name;
20
+ const { baseUrl } = useAdminContext();
21
+
22
+ const { data: template, isLoading, error } = useFetch<TemplateDefinitionResponse>(
23
+ () => fetch(`${baseUrl}/templates/${collection}/${name}`).then(r => {
24
+ if (!r.ok) throw new Error(`Failed to load template: ${r.statusText}`);
25
+ return r.json();
26
+ }),
27
+ [baseUrl, collection, name]
28
+ );
29
+
30
+ if (isLoading) return <div className="vta-loading">Loading template...</div>;
31
+ if (error || !template) return <div className="vta-error">Failed to load template &ldquo;{name}&rdquo;.</div>;
32
+
33
+ return (
34
+ <DetailPage
35
+ type="template"
36
+ title={template.title || template.name}
37
+ description={template.description}
38
+ tags={template.tags}
39
+ backHref={`/templates/${collection}`}
40
+ >
41
+ {/* Type badge & Assets */}
42
+ <div className="vta-detail-section">
43
+ <div className="vta-detail-flags">
44
+ <span className="vta-detail-flag">{template.type}</span>
45
+ </div>
46
+ </div>
47
+
48
+ {template.assets && template.assets.length > 0 && (
49
+ <div className="vta-detail-section">
50
+ <h2>Assets</h2>
51
+ <div className="vta-detail-flags">
52
+ {template.assets.map(asset => (
53
+ <span key={asset} className="vta-detail-flag">
54
+ {asset.split('/').pop()}
55
+ </span>
56
+ ))}
57
+ </div>
58
+ </div>
59
+ )}
60
+
61
+ {/* Instructions */}
62
+ <div className="vta-detail-section">
63
+ <h2>Instructions</h2>
64
+ <pre className="vta-detail-code">{template.instructions}</pre>
65
+ </div>
66
+ </DetailPage>
67
+ );
68
+ }
@@ -0,0 +1,55 @@
1
+ import { useFetch } from '@vertesia/ui/core';
2
+ import { useParams } from '@vertesia/ui/router';
3
+ import { useAdminContext } from '../AdminContext.js';
4
+ import { DetailPage } from '../components/DetailPage.js';
5
+
6
+ interface ToolDef {
7
+ name: string;
8
+ description?: string;
9
+ input_schema?: Record<string, unknown>;
10
+ }
11
+
12
+ interface ToolCollectionResponse {
13
+ title: string;
14
+ description: string;
15
+ tools: ToolDef[];
16
+ }
17
+
18
+ export function ToolCollection() {
19
+ const collection = useParams('collection');
20
+ const { baseUrl } = useAdminContext();
21
+
22
+ const { data, isLoading, error } = useFetch<ToolCollectionResponse>(
23
+ () => fetch(`${baseUrl}/tools/${collection}`).then(r => {
24
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
25
+ return r.json();
26
+ }),
27
+ [baseUrl, collection]
28
+ );
29
+
30
+ if (isLoading) return <div className="vta-loading">Loading collection...</div>;
31
+ if (error || !data) return <div className="vta-error">Failed to load tool collection &ldquo;{collection}&rdquo;.</div>;
32
+
33
+ return (
34
+ <DetailPage
35
+ type="tool"
36
+ title={data.title || collection}
37
+ description={data.description || `${data.tools.length} tool${data.tools.length !== 1 ? 's' : ''} in this collection.`}
38
+ >
39
+ {data.tools.map(tool => (
40
+ <div key={tool.name} className="vta-detail-card">
41
+ <div className="vta-detail-card-header">
42
+ <span className="vta-card-type vta-card-type--tool">tool</span>
43
+ <div className="vta-card-title">{tool.name}</div>
44
+ </div>
45
+ <div className="vta-card-desc">{tool.description || 'No description'}</div>
46
+ {tool.input_schema && (
47
+ <pre className="vta-detail-code">
48
+ {JSON.stringify(tool.input_schema, null, 2)}
49
+ </pre>
50
+ )}
51
+ </div>
52
+ ))}
53
+ </DetailPage>
54
+ );
55
+ }
@@ -0,0 +1,60 @@
1
+ import { useFetch } from '@vertesia/ui/core';
2
+ import { useParams, NavLink } from '@vertesia/ui/router';
3
+ import type { InCodeTypeDefinition } from '@vertesia/common';
4
+ import { useAdminContext } from '../AdminContext.js';
5
+ import { DetailPage } from '../components/DetailPage.js';
6
+
7
+ export function TypeCollection() {
8
+ const collection = useParams('collection');
9
+ const { baseUrl } = useAdminContext();
10
+
11
+ const { data: types, isLoading, error } = useFetch<InCodeTypeDefinition[]>(
12
+ () => fetch(`${baseUrl}/types/${collection}`).then(r => {
13
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
14
+ return r.json();
15
+ }),
16
+ [baseUrl, collection]
17
+ );
18
+
19
+ if (isLoading) return <div className="vta-loading">Loading collection...</div>;
20
+ if (error || !types) return <div className="vta-error">Failed to load type collection &ldquo;{collection}&rdquo;.</div>;
21
+
22
+ return (
23
+ <DetailPage
24
+ type="type"
25
+ title={collection}
26
+ description={`${types.length} content type${types.length !== 1 ? 's' : ''} in this collection.`}
27
+ >
28
+ <div className="vta-card-grid">
29
+ {types.map(t => {
30
+ const typeName = t.id?.split(':')[1] || t.name;
31
+ return (
32
+ <NavLink
33
+ key={t.name}
34
+ href={`/types/${collection}/${typeName}`}
35
+ className="vta-card-link"
36
+ >
37
+ <div className="vta-card vta-card--link">
38
+ <span className="vta-card-type vta-card-type--type">type</span>
39
+ <div className="vta-card-title">{t.name}</div>
40
+ <div className="vta-card-desc">{t.description || 'No description'}</div>
41
+ {t.tags && t.tags.length > 0 && (
42
+ <div className="vta-card-tags">
43
+ {t.tags.map(tag => (
44
+ <span key={tag} className="vta-tag">{tag}</span>
45
+ ))}
46
+ </div>
47
+ )}
48
+ <div className="vta-card-url">
49
+ {t.is_chunkable && 'chunkable'}
50
+ {t.is_chunkable && t.strict_mode && ' · '}
51
+ {t.strict_mode && 'strict'}
52
+ </div>
53
+ </div>
54
+ </NavLink>
55
+ );
56
+ })}
57
+ </div>
58
+ </DetailPage>
59
+ );
60
+ }
@@ -0,0 +1,63 @@
1
+ import { useFetch } from '@vertesia/ui/core';
2
+ import { useParams } from '@vertesia/ui/router';
3
+ import type { InCodeTypeDefinition } from '@vertesia/common';
4
+ import { useAdminContext } from '../AdminContext.js';
5
+ import { DetailPage } from '../components/DetailPage.js';
6
+
7
+ export function TypeDetail() {
8
+ const params = useParams();
9
+ const collection = params.collection;
10
+ const name = params.name;
11
+ const { baseUrl } = useAdminContext();
12
+
13
+ const { data: typeDef, isLoading, error } = useFetch<InCodeTypeDefinition>(
14
+ () => fetch(`${baseUrl}/types/${collection}/${name}`).then(r => {
15
+ if (!r.ok) throw new Error(`Failed to load type: ${r.statusText}`);
16
+ return r.json();
17
+ }),
18
+ [baseUrl, collection, name]
19
+ );
20
+
21
+ if (isLoading) return <div className="vta-loading">Loading type...</div>;
22
+ if (error || !typeDef) return <div className="vta-error">Failed to load type &ldquo;{name}&rdquo;.</div>;
23
+
24
+ return (
25
+ <DetailPage
26
+ type="type"
27
+ title={typeDef.name}
28
+ description={typeDef.description}
29
+ tags={typeDef.tags}
30
+ backHref={`/types/${collection}`}
31
+ >
32
+ {/* Flags */}
33
+ {(typeDef.is_chunkable || typeDef.strict_mode) && (
34
+ <div className="vta-detail-section">
35
+ <div className="vta-detail-flags">
36
+ {typeDef.is_chunkable && <span className="vta-detail-flag">Chunkable</span>}
37
+ {typeDef.strict_mode && <span className="vta-detail-flag">Strict Mode</span>}
38
+ </div>
39
+ </div>
40
+ )}
41
+
42
+ {/* Object Schema */}
43
+ {typeDef.object_schema && (
44
+ <div className="vta-detail-section">
45
+ <h2>Object Schema</h2>
46
+ <pre className="vta-detail-code">
47
+ {JSON.stringify(typeDef.object_schema, null, 2)}
48
+ </pre>
49
+ </div>
50
+ )}
51
+
52
+ {/* Table Layout */}
53
+ {typeDef.table_layout && typeDef.table_layout.length > 0 && (
54
+ <div className="vta-detail-section">
55
+ <h2>Table Layout</h2>
56
+ <pre className="vta-detail-code">
57
+ {JSON.stringify(typeDef.table_layout, null, 2)}
58
+ </pre>
59
+ </div>
60
+ )}
61
+ </DetailPage>
62
+ );
63
+ }