@dyrected/core 1.0.5 → 1.0.7
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.
- package/LICENSE.md +1 -1
- package/dist/index.cjs +73 -169
- package/dist/index.js +73 -169
- package/package.json +1 -1
package/LICENSE.md
CHANGED
|
@@ -2,7 +2,7 @@ Business Source License 1.1
|
|
|
2
2
|
|
|
3
3
|
Parameters
|
|
4
4
|
|
|
5
|
-
Licensor:
|
|
5
|
+
Licensor: Dyrected
|
|
6
6
|
Licensed Work: Dyrected
|
|
7
7
|
Additional Use Grant: Commercial use is permitted as long as it is not used to provide a hosted or managed service that competes with Dyrected.
|
|
8
8
|
Change Date: 2030-05-11
|
package/dist/index.cjs
CHANGED
|
@@ -31,206 +31,109 @@ module.exports = __toCommonJS(index_exports);
|
|
|
31
31
|
// src/utils/setup-prompt.ts
|
|
32
32
|
function generateAIPrompt(activeTab, config) {
|
|
33
33
|
const frameworkLabel = activeTab === "next" ? "Next.js" : activeTab === "nuxt" ? "Nuxt" : activeTab.charAt(0).toUpperCase() + activeTab.slice(1);
|
|
34
|
-
const backendPkg = activeTab === "nuxt" ? "@dyrected/nuxt" : "@dyrected/next";
|
|
35
34
|
const isSelfHosted = config.isSelfHosted ?? (config.baseUrl?.includes("localhost") || !config.apiKey);
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const credentials = `
|
|
35
|
+
const envPrefix = activeTab === "next" ? "NEXT_PUBLIC_" : activeTab === "nuxt" ? "NUXT_PUBLIC_" : "";
|
|
36
|
+
const mission = `You are a Senior Content Architect. Your mission is to create a robust CMS integration plan for a ${frameworkLabel} website using Dyrected CMS.
|
|
37
|
+
|
|
40
38
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
41
|
-
|
|
39
|
+
1. ENVIRONMENT
|
|
42
40
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
API
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
- Framework: ${frameworkLabel}
|
|
42
|
+
- Host Type: ${isSelfHosted ? "Self-Hosted (Local/Private Server)" : "Managed (Dyrected Cloud)"}
|
|
43
|
+
- API Base : ${config.baseUrl || "http://localhost:3000"}
|
|
44
|
+
${isSelfHosted ? "" : `- Site ID : ${config.siteId}
|
|
45
|
+
- API Key : ${config.apiKey}`}
|
|
46
|
+
|
|
49
47
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
50
|
-
|
|
48
|
+
2. ARCHITECTURE & CONSTRAINTS
|
|
51
49
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
52
|
-
- \`client.collection(slug)\`
|
|
53
|
-
-
|
|
54
|
-
- MARKETING INDEPENDENCE:
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
- API ACCESS: Use \`client.collection(slug)\` as the primary entry point. Do NOT use \`client.collections\`.
|
|
51
|
+
- ZERO-STATE ROBUSTNESS: Always use \`initialData\` in all data fetches to ensure the site renders correctly on first load.
|
|
52
|
+
- MARKETING INDEPENDENCE: Use a dynamic \`pages\` collection with a catch-all route for marketing-managed pages.
|
|
53
|
+
- BLOCKS-BASED DESIGN: Use \`blocks\` for flexible page builders. Iterate the array and switch on \`blockType\` in your frontend.
|
|
54
|
+
- DATA PRESERVATION: Do NOT modify or overwrite existing pages without first extracting and preserving their data.
|
|
55
|
+
- NO DEPRECATIONS: Use the framework-specific \`DyrectedAdmin\` components (Next/Nuxt) which handle routing and CSS automatically.
|
|
56
|
+
|
|
57
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
58
|
+
3. REQUIRED DELIVERABLES
|
|
59
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
60
|
+
Your final response MUST include:
|
|
61
|
+
1. A complete content model plan (\`dyrected.config.ts\`).
|
|
62
|
+
2. The Admin UI mounting route (e.g. \`/admin\`) using the specialized \`DyrectedAdmin\` component.
|
|
63
|
+
3. A catch-all frontend page route for CMS-managed dynamic pages.
|
|
64
|
+
4. A block type list covering existing site sections (Hero, Content, CTA, etc.).
|
|
65
|
+
5. A migration/fallback strategy for current static pages.
|
|
66
|
+
6. A safe schema sync step using \`npx @dyrected/cli sync:schema\`.
|
|
67
|
+
|
|
63
68
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
64
|
-
PHASE 0 \u2014
|
|
69
|
+
4. PHASE 0 \u2014 DISCOVERY & PRESERVATION
|
|
65
70
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- "
|
|
70
|
-
|
|
71
|
+
Before writing any code, you MUST:
|
|
72
|
+
1. BACKUP: Propose a plan to save current site content into a \`migration/\` folder.
|
|
73
|
+
2. ASK QUESTIONS: If the site content types are unknown, ask the user:
|
|
74
|
+
- "What are your core content types (Services, Team, Projects)?"
|
|
75
|
+
- "Which existing pages must remain static vs. becoming dynamic?"
|
|
76
|
+
- "What layouts should marketing be able to manage with blocks?"
|
|
77
|
+
- "What existing hardcoded sections must be preserved?"
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
80
|
+
5. TECHNICAL REFERENCE (Field Types & Syntax)
|
|
73
81
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
74
82
|
Use \`defineCollection\`, \`defineGlobal\`, and \`defineConfig\` from '@dyrected/core'.
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
FIELD TYPES:
|
|
85
|
+
- Primitive : text | textarea | richText | number | boolean | date | email | url | json
|
|
86
|
+
- Choice : select | multiSelect (requires \`options: [{ label, value }]\`)
|
|
87
|
+
- Structural : array | object (requires \`fields: [...]\`)
|
|
88
|
+
- Relation : relationship (requires \`collection: '<slug>'\`)
|
|
89
|
+
- Media : relationship to an upload collection (e.g. 'media')
|
|
90
|
+
- Blocks : blocks (requires \`blocks: [{ slug, labels, fields }]\`)
|
|
83
91
|
|
|
84
92
|
COLLECTION OPTIONS:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
\`admin.useAsTitle\` \u2014 field to use as the display title in the admin list view
|
|
89
|
-
\`admin.hidden\` \u2014 hide from sidebar (useful for internal/system collections)
|
|
90
|
-
|
|
91
|
-
FIELD OPTIONS:
|
|
92
|
-
\`required\` \u2014 validation
|
|
93
|
-
\`unique\` \u2014 database-level uniqueness
|
|
94
|
-
\`defaultValue\` \u2014 fallback value
|
|
95
|
-
\`admin.condition\` \u2014 "expression" \u2014 Jexl string expression to show/hide field (e.g. "status == \\"published\\"")
|
|
96
|
-
\`admin.layout\` \u2014 "radio" | "dropdown" \u2014 Visual layout for select/multiSelect
|
|
97
|
-
\`admin.direction\` \u2014 "vertical" | "horizontal" \u2014 Layout direction for radio groups
|
|
98
|
-
\`admin.readOnly\` \u2014 display-only in the form
|
|
99
|
-
\`admin.hidden\` \u2014 completely hidden from editor UI
|
|
100
|
-
\`access.read\` \u2014 ({ user }) => boolean \u2014 field-level read access
|
|
101
|
-
\`access.update\` \u2014 ({ user }) => boolean \u2014 field-level write access
|
|
102
|
-
\`hooks.beforeChange\` \u2014 [async (value) => newValue] \u2014 transform value before save
|
|
103
|
-
\`hooks.afterRead\` \u2014 [async (value) => newValue] \u2014 transform value after read
|
|
93
|
+
- \`upload: true\`: Use for media libraries.
|
|
94
|
+
- \`auth: true\`: Adds auth endpoints (login/me) and an auto-managed password field.
|
|
95
|
+
- \`admin.useAsTitle\`: Field to display in admin lists.
|
|
104
96
|
|
|
105
|
-
BLOCKS
|
|
106
|
-
|
|
107
|
-
Each block has a \`blockType\` discriminator and its own set of fields.
|
|
108
|
-
The admin UI renders a drag-and-drop block editor automatically.
|
|
109
|
-
On the frontend, iterate the array and switch on \`block.blockType\`.
|
|
97
|
+
BLOCKS SYNTAX:
|
|
98
|
+
Blocks are stored as \`[{ blockType: 'slug', ...fields }]\`. The Admin UI renders a drag-and-drop editor for these automatically.
|
|
110
99
|
|
|
111
|
-
|
|
112
|
-
\`\`\`
|
|
113
|
-
import { defineCollection,
|
|
100
|
+
SCHEMA EXAMPLE:
|
|
101
|
+
\`\`\`ts
|
|
102
|
+
import { defineCollection, defineConfig } from '@dyrected/core'
|
|
114
103
|
|
|
115
|
-
// \u2500\u2500 Media \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
116
104
|
const media = defineCollection({
|
|
117
105
|
slug: 'media',
|
|
118
|
-
labels: { singular: 'Media Item', plural: 'Media' },
|
|
119
106
|
upload: true,
|
|
120
|
-
fields: [
|
|
121
|
-
{ name: 'alt', type: 'text', label: 'Alt Text' },
|
|
122
|
-
{ name: 'caption', type: 'textarea', label: 'Caption' },
|
|
123
|
-
],
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
// \u2500\u2500 Authentication collection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
127
|
-
const customers = defineCollection({
|
|
128
|
-
slug: 'customers',
|
|
129
|
-
labels: { singular: 'Customer', plural: 'Customers' },
|
|
130
|
-
auth: true, // adds /customers/login, /customers/me, etc.
|
|
131
|
-
admin: { group: 'Membership' },
|
|
132
|
-
fields: [
|
|
133
|
-
{ name: 'name', type: 'text', required: true },
|
|
134
|
-
{ name: 'email', type: 'email', required: true, unique: true },
|
|
135
|
-
// 'password' is auto-added when auth: true
|
|
136
|
-
{ name: 'avatar', type: 'relationship', relationTo: 'media' },
|
|
137
|
-
{ name: 'role', type: 'select', admin: { layout: 'radio' }, options: [
|
|
138
|
-
{ label: 'Member', value: 'member' },
|
|
139
|
-
{ label: 'VIP', value: 'vip' },
|
|
140
|
-
]},
|
|
141
|
-
],
|
|
107
|
+
fields: [{ name: 'alt', type: 'text' }]
|
|
142
108
|
})
|
|
143
109
|
|
|
144
|
-
// \u2500\u2500 Pages with blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
145
110
|
const pages = defineCollection({
|
|
146
111
|
slug: 'pages',
|
|
147
|
-
labels: { singular: 'Page', plural: 'Pages' },
|
|
148
|
-
admin: { useAsTitle: 'title', group: 'Content' },
|
|
149
112
|
fields: [
|
|
150
113
|
{ name: 'title', type: 'text', required: true },
|
|
151
|
-
{ name: 'slug',
|
|
152
|
-
{ name: '
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
]},
|
|
157
|
-
{
|
|
158
|
-
name: 'layout',
|
|
159
|
-
type: 'blocks',
|
|
160
|
-
label: 'Page Layout',
|
|
161
|
-
blocks: [
|
|
162
|
-
{
|
|
163
|
-
slug: 'hero',
|
|
164
|
-
labels: { singular: 'Hero', plural: 'Heroes' },
|
|
165
|
-
fields: [
|
|
166
|
-
{ name: 'heading', type: 'text', required: true },
|
|
167
|
-
{ name: 'subheading', type: 'textarea' },
|
|
168
|
-
{ name: 'image', type: 'relationship', relationTo: 'media' },
|
|
169
|
-
{ name: 'ctaLabel', type: 'text' },
|
|
170
|
-
{ name: 'ctaLink', type: 'url' },
|
|
171
|
-
],
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
slug: 'richContent',
|
|
175
|
-
labels: { singular: 'Rich Content', plural: 'Rich Content Blocks' },
|
|
176
|
-
fields: [
|
|
177
|
-
{ name: 'content', type: 'richText', required: true },
|
|
178
|
-
],
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
],
|
|
114
|
+
{ name: 'slug', type: 'text', required: true, unique: true },
|
|
115
|
+
{ name: 'layout', type: 'blocks', blocks: [
|
|
116
|
+
{ slug: 'hero', fields: [{ name: 'title', type: 'text' }] }
|
|
117
|
+
]}
|
|
118
|
+
]
|
|
183
119
|
})
|
|
184
120
|
|
|
185
|
-
export default defineConfig({
|
|
186
|
-
collections: [media, customers, pages],
|
|
187
|
-
admin: {
|
|
188
|
-
branding: {
|
|
189
|
-
primaryColor: '#4f46e5',
|
|
190
|
-
logo: '/logo.png',
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
})
|
|
121
|
+
export default defineConfig({ collections: [media, pages] })
|
|
194
122
|
\`\`\`
|
|
195
123
|
|
|
196
|
-
${isSelfHosted ? "" : `
|
|
197
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
198
|
-
STEP 2 \u2014 CHOOSE YOUR MODE
|
|
199
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
200
|
-
The developer can choose between two modes:
|
|
201
|
-
|
|
202
|
-
1. CLOUD MODE (Managed)
|
|
203
|
-
- Use the SITE CREDENTIALS above.
|
|
204
|
-
- Point baseUrl to ${config.baseUrl}.
|
|
205
|
-
- Content is stored in Dyrected Cloud.
|
|
206
|
-
|
|
207
|
-
2. SELF-HOSTED MODE (Core)
|
|
208
|
-
- Do NOT use apiKey/siteId (unless for proxying).
|
|
209
|
-
- Use a database adapter like \`SqliteAdapter\` from '@dyrected/db-sqlite'.
|
|
210
|
-
- Content is stored locally in the developer's project.
|
|
211
|
-
`}
|
|
212
|
-
|
|
213
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
214
|
-
STEP ${isSelfHosted ? "2" : "3"} \u2014 MOUNTING THE ADMIN UI
|
|
215
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
216
|
-
The Admin UI can be mounted on any path (e.g. /cms-admin).
|
|
217
|
-
Pass the \`basename\` prop to the \`<AdminUI />\` component to match your route.
|
|
218
|
-
|
|
219
124
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
220
|
-
|
|
125
|
+
6. IMPLEMENTATION DETAILS (${frameworkLabel})
|
|
221
126
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
222
127
|
`;
|
|
223
128
|
const frameworks = {
|
|
224
|
-
next: `
|
|
225
|
-
|
|
226
|
-
1. **SDK Setup** (\`lib/dyrected.ts\`):
|
|
129
|
+
next: `1. **SDK Setup** (\`lib/dyrected.ts\`):
|
|
227
130
|
\`\`\`ts
|
|
228
131
|
import { createClient } from '@dyrected/sdk'
|
|
229
132
|
|
|
230
133
|
export const dyrected = createClient({
|
|
231
|
-
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
232
|
-
apiKey: '${config.apiKey}',
|
|
233
|
-
siteId: '${config.siteId}',`}
|
|
134
|
+
baseUrl: process.env.${envPrefix}DYRECTED_URL || '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
135
|
+
apiKey: process.env.${envPrefix}DYRECTED_API_KEY || '${config.apiKey}',
|
|
136
|
+
siteId: process.env.${envPrefix}SITE_ID || '${config.siteId}',`}
|
|
234
137
|
})
|
|
235
138
|
\`\`\`
|
|
236
139
|
|
|
@@ -239,23 +142,24 @@ export const dyrected = createClient({
|
|
|
239
142
|
import { DyrectedAdmin } from '@dyrected/next/admin'
|
|
240
143
|
|
|
241
144
|
export default function AdminPage() {
|
|
145
|
+
// DyrectedAdmin handles router, CSS, and "use client" for you
|
|
242
146
|
return <DyrectedAdmin basename="/admin" />
|
|
243
147
|
}
|
|
244
148
|
\`\`\`
|
|
245
149
|
`,
|
|
246
|
-
nuxt: `
|
|
150
|
+
nuxt: `1. **Nuxt Config** (\`nuxt.config.ts\`):
|
|
247
151
|
\`\`\`ts
|
|
248
152
|
export default defineNuxtConfig({
|
|
249
153
|
modules: ['@dyrected/nuxt'],
|
|
250
154
|
dyrected: {
|
|
251
|
-
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
252
|
-
apiKey: '${config.apiKey}',
|
|
253
|
-
siteId: '${config.siteId}',`}
|
|
155
|
+
baseUrl: process.env.${envPrefix}DYRECTED_URL || '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
156
|
+
apiKey: process.env.${envPrefix}DYRECTED_API_KEY || '${config.apiKey}',
|
|
157
|
+
siteId: process.env.${envPrefix}SITE_ID || '${config.siteId}',`}
|
|
254
158
|
},
|
|
255
159
|
})
|
|
256
160
|
\`\`\`
|
|
257
161
|
|
|
258
|
-
|
|
162
|
+
2. **Admin Dashboard** (\`pages/admin.vue\`):
|
|
259
163
|
\`\`\`vue
|
|
260
164
|
<script setup lang="ts">
|
|
261
165
|
// DyrectedAdmin is auto-imported by the module
|
|
@@ -336,7 +240,7 @@ onMounted(() => {
|
|
|
336
240
|
\`\`\`
|
|
337
241
|
`
|
|
338
242
|
};
|
|
339
|
-
return
|
|
243
|
+
return mission + (frameworks[activeTab] || frameworks.next) + `
|
|
340
244
|
API Reference: https://docs.dyrected.com`;
|
|
341
245
|
}
|
|
342
246
|
|
package/dist/index.js
CHANGED
|
@@ -5,206 +5,109 @@ import {
|
|
|
5
5
|
// src/utils/setup-prompt.ts
|
|
6
6
|
function generateAIPrompt(activeTab, config) {
|
|
7
7
|
const frameworkLabel = activeTab === "next" ? "Next.js" : activeTab === "nuxt" ? "Nuxt" : activeTab.charAt(0).toUpperCase() + activeTab.slice(1);
|
|
8
|
-
const backendPkg = activeTab === "nuxt" ? "@dyrected/nuxt" : "@dyrected/next";
|
|
9
8
|
const isSelfHosted = config.isSelfHosted ?? (config.baseUrl?.includes("localhost") || !config.apiKey);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const credentials = `
|
|
9
|
+
const envPrefix = activeTab === "next" ? "NEXT_PUBLIC_" : activeTab === "nuxt" ? "NUXT_PUBLIC_" : "";
|
|
10
|
+
const mission = `You are a Senior Content Architect. Your mission is to create a robust CMS integration plan for a ${frameworkLabel} website using Dyrected CMS.
|
|
11
|
+
|
|
14
12
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
15
|
-
|
|
13
|
+
1. ENVIRONMENT
|
|
16
14
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
API
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
- Framework: ${frameworkLabel}
|
|
16
|
+
- Host Type: ${isSelfHosted ? "Self-Hosted (Local/Private Server)" : "Managed (Dyrected Cloud)"}
|
|
17
|
+
- API Base : ${config.baseUrl || "http://localhost:3000"}
|
|
18
|
+
${isSelfHosted ? "" : `- Site ID : ${config.siteId}
|
|
19
|
+
- API Key : ${config.apiKey}`}
|
|
20
|
+
|
|
23
21
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
24
|
-
|
|
22
|
+
2. ARCHITECTURE & CONSTRAINTS
|
|
25
23
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
26
|
-
- \`client.collection(slug)\`
|
|
27
|
-
-
|
|
28
|
-
- MARKETING INDEPENDENCE:
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
- API ACCESS: Use \`client.collection(slug)\` as the primary entry point. Do NOT use \`client.collections\`.
|
|
25
|
+
- ZERO-STATE ROBUSTNESS: Always use \`initialData\` in all data fetches to ensure the site renders correctly on first load.
|
|
26
|
+
- MARKETING INDEPENDENCE: Use a dynamic \`pages\` collection with a catch-all route for marketing-managed pages.
|
|
27
|
+
- BLOCKS-BASED DESIGN: Use \`blocks\` for flexible page builders. Iterate the array and switch on \`blockType\` in your frontend.
|
|
28
|
+
- DATA PRESERVATION: Do NOT modify or overwrite existing pages without first extracting and preserving their data.
|
|
29
|
+
- NO DEPRECATIONS: Use the framework-specific \`DyrectedAdmin\` components (Next/Nuxt) which handle routing and CSS automatically.
|
|
30
|
+
|
|
31
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
32
|
+
3. REQUIRED DELIVERABLES
|
|
33
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
34
|
+
Your final response MUST include:
|
|
35
|
+
1. A complete content model plan (\`dyrected.config.ts\`).
|
|
36
|
+
2. The Admin UI mounting route (e.g. \`/admin\`) using the specialized \`DyrectedAdmin\` component.
|
|
37
|
+
3. A catch-all frontend page route for CMS-managed dynamic pages.
|
|
38
|
+
4. A block type list covering existing site sections (Hero, Content, CTA, etc.).
|
|
39
|
+
5. A migration/fallback strategy for current static pages.
|
|
40
|
+
6. A safe schema sync step using \`npx @dyrected/cli sync:schema\`.
|
|
41
|
+
|
|
37
42
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
38
|
-
PHASE 0 \u2014
|
|
43
|
+
4. PHASE 0 \u2014 DISCOVERY & PRESERVATION
|
|
39
44
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- "
|
|
44
|
-
|
|
45
|
+
Before writing any code, you MUST:
|
|
46
|
+
1. BACKUP: Propose a plan to save current site content into a \`migration/\` folder.
|
|
47
|
+
2. ASK QUESTIONS: If the site content types are unknown, ask the user:
|
|
48
|
+
- "What are your core content types (Services, Team, Projects)?"
|
|
49
|
+
- "Which existing pages must remain static vs. becoming dynamic?"
|
|
50
|
+
- "What layouts should marketing be able to manage with blocks?"
|
|
51
|
+
- "What existing hardcoded sections must be preserved?"
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
54
|
+
5. TECHNICAL REFERENCE (Field Types & Syntax)
|
|
47
55
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
48
56
|
Use \`defineCollection\`, \`defineGlobal\`, and \`defineConfig\` from '@dyrected/core'.
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
FIELD TYPES:
|
|
59
|
+
- Primitive : text | textarea | richText | number | boolean | date | email | url | json
|
|
60
|
+
- Choice : select | multiSelect (requires \`options: [{ label, value }]\`)
|
|
61
|
+
- Structural : array | object (requires \`fields: [...]\`)
|
|
62
|
+
- Relation : relationship (requires \`collection: '<slug>'\`)
|
|
63
|
+
- Media : relationship to an upload collection (e.g. 'media')
|
|
64
|
+
- Blocks : blocks (requires \`blocks: [{ slug, labels, fields }]\`)
|
|
57
65
|
|
|
58
66
|
COLLECTION OPTIONS:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
\`admin.useAsTitle\` \u2014 field to use as the display title in the admin list view
|
|
63
|
-
\`admin.hidden\` \u2014 hide from sidebar (useful for internal/system collections)
|
|
67
|
+
- \`upload: true\`: Use for media libraries.
|
|
68
|
+
- \`auth: true\`: Adds auth endpoints (login/me) and an auto-managed password field.
|
|
69
|
+
- \`admin.useAsTitle\`: Field to display in admin lists.
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
\`unique\` \u2014 database-level uniqueness
|
|
68
|
-
\`defaultValue\` \u2014 fallback value
|
|
69
|
-
\`admin.condition\` \u2014 "expression" \u2014 Jexl string expression to show/hide field (e.g. "status == \\"published\\"")
|
|
70
|
-
\`admin.layout\` \u2014 "radio" | "dropdown" \u2014 Visual layout for select/multiSelect
|
|
71
|
-
\`admin.direction\` \u2014 "vertical" | "horizontal" \u2014 Layout direction for radio groups
|
|
72
|
-
\`admin.readOnly\` \u2014 display-only in the form
|
|
73
|
-
\`admin.hidden\` \u2014 completely hidden from editor UI
|
|
74
|
-
\`access.read\` \u2014 ({ user }) => boolean \u2014 field-level read access
|
|
75
|
-
\`access.update\` \u2014 ({ user }) => boolean \u2014 field-level write access
|
|
76
|
-
\`hooks.beforeChange\` \u2014 [async (value) => newValue] \u2014 transform value before save
|
|
77
|
-
\`hooks.afterRead\` \u2014 [async (value) => newValue] \u2014 transform value after read
|
|
71
|
+
BLOCKS SYNTAX:
|
|
72
|
+
Blocks are stored as \`[{ blockType: 'slug', ...fields }]\`. The Admin UI renders a drag-and-drop editor for these automatically.
|
|
78
73
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
The admin UI renders a drag-and-drop block editor automatically.
|
|
83
|
-
On the frontend, iterate the array and switch on \`block.blockType\`.
|
|
84
|
-
|
|
85
|
-
COMPLETE EXAMPLE:
|
|
86
|
-
\`\`\`typescript
|
|
87
|
-
import { defineCollection, defineGlobal, defineConfig } from '@dyrected/core'
|
|
74
|
+
SCHEMA EXAMPLE:
|
|
75
|
+
\`\`\`ts
|
|
76
|
+
import { defineCollection, defineConfig } from '@dyrected/core'
|
|
88
77
|
|
|
89
|
-
// \u2500\u2500 Media \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
90
78
|
const media = defineCollection({
|
|
91
79
|
slug: 'media',
|
|
92
|
-
labels: { singular: 'Media Item', plural: 'Media' },
|
|
93
80
|
upload: true,
|
|
94
|
-
fields: [
|
|
95
|
-
{ name: 'alt', type: 'text', label: 'Alt Text' },
|
|
96
|
-
{ name: 'caption', type: 'textarea', label: 'Caption' },
|
|
97
|
-
],
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
// \u2500\u2500 Authentication collection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
101
|
-
const customers = defineCollection({
|
|
102
|
-
slug: 'customers',
|
|
103
|
-
labels: { singular: 'Customer', plural: 'Customers' },
|
|
104
|
-
auth: true, // adds /customers/login, /customers/me, etc.
|
|
105
|
-
admin: { group: 'Membership' },
|
|
106
|
-
fields: [
|
|
107
|
-
{ name: 'name', type: 'text', required: true },
|
|
108
|
-
{ name: 'email', type: 'email', required: true, unique: true },
|
|
109
|
-
// 'password' is auto-added when auth: true
|
|
110
|
-
{ name: 'avatar', type: 'relationship', relationTo: 'media' },
|
|
111
|
-
{ name: 'role', type: 'select', admin: { layout: 'radio' }, options: [
|
|
112
|
-
{ label: 'Member', value: 'member' },
|
|
113
|
-
{ label: 'VIP', value: 'vip' },
|
|
114
|
-
]},
|
|
115
|
-
],
|
|
81
|
+
fields: [{ name: 'alt', type: 'text' }]
|
|
116
82
|
})
|
|
117
83
|
|
|
118
|
-
// \u2500\u2500 Pages with blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
119
84
|
const pages = defineCollection({
|
|
120
85
|
slug: 'pages',
|
|
121
|
-
labels: { singular: 'Page', plural: 'Pages' },
|
|
122
|
-
admin: { useAsTitle: 'title', group: 'Content' },
|
|
123
86
|
fields: [
|
|
124
87
|
{ name: 'title', type: 'text', required: true },
|
|
125
|
-
{ name: 'slug',
|
|
126
|
-
{ name: '
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
]},
|
|
131
|
-
{
|
|
132
|
-
name: 'layout',
|
|
133
|
-
type: 'blocks',
|
|
134
|
-
label: 'Page Layout',
|
|
135
|
-
blocks: [
|
|
136
|
-
{
|
|
137
|
-
slug: 'hero',
|
|
138
|
-
labels: { singular: 'Hero', plural: 'Heroes' },
|
|
139
|
-
fields: [
|
|
140
|
-
{ name: 'heading', type: 'text', required: true },
|
|
141
|
-
{ name: 'subheading', type: 'textarea' },
|
|
142
|
-
{ name: 'image', type: 'relationship', relationTo: 'media' },
|
|
143
|
-
{ name: 'ctaLabel', type: 'text' },
|
|
144
|
-
{ name: 'ctaLink', type: 'url' },
|
|
145
|
-
],
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
slug: 'richContent',
|
|
149
|
-
labels: { singular: 'Rich Content', plural: 'Rich Content Blocks' },
|
|
150
|
-
fields: [
|
|
151
|
-
{ name: 'content', type: 'richText', required: true },
|
|
152
|
-
],
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
],
|
|
88
|
+
{ name: 'slug', type: 'text', required: true, unique: true },
|
|
89
|
+
{ name: 'layout', type: 'blocks', blocks: [
|
|
90
|
+
{ slug: 'hero', fields: [{ name: 'title', type: 'text' }] }
|
|
91
|
+
]}
|
|
92
|
+
]
|
|
157
93
|
})
|
|
158
94
|
|
|
159
|
-
export default defineConfig({
|
|
160
|
-
collections: [media, customers, pages],
|
|
161
|
-
admin: {
|
|
162
|
-
branding: {
|
|
163
|
-
primaryColor: '#4f46e5',
|
|
164
|
-
logo: '/logo.png',
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
})
|
|
95
|
+
export default defineConfig({ collections: [media, pages] })
|
|
168
96
|
\`\`\`
|
|
169
97
|
|
|
170
|
-
${isSelfHosted ? "" : `
|
|
171
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
172
|
-
STEP 2 \u2014 CHOOSE YOUR MODE
|
|
173
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
174
|
-
The developer can choose between two modes:
|
|
175
|
-
|
|
176
|
-
1. CLOUD MODE (Managed)
|
|
177
|
-
- Use the SITE CREDENTIALS above.
|
|
178
|
-
- Point baseUrl to ${config.baseUrl}.
|
|
179
|
-
- Content is stored in Dyrected Cloud.
|
|
180
|
-
|
|
181
|
-
2. SELF-HOSTED MODE (Core)
|
|
182
|
-
- Do NOT use apiKey/siteId (unless for proxying).
|
|
183
|
-
- Use a database adapter like \`SqliteAdapter\` from '@dyrected/db-sqlite'.
|
|
184
|
-
- Content is stored locally in the developer's project.
|
|
185
|
-
`}
|
|
186
|
-
|
|
187
98
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
188
|
-
|
|
189
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
190
|
-
The Admin UI can be mounted on any path (e.g. /cms-admin).
|
|
191
|
-
Pass the \`basename\` prop to the \`<AdminUI />\` component to match your route.
|
|
192
|
-
|
|
193
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
194
|
-
STEP ${isSelfHosted ? "3" : "4"} \u2014 FRONTEND IMPLEMENTATION
|
|
99
|
+
6. IMPLEMENTATION DETAILS (${frameworkLabel})
|
|
195
100
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
196
101
|
`;
|
|
197
102
|
const frameworks = {
|
|
198
|
-
next: `
|
|
199
|
-
|
|
200
|
-
1. **SDK Setup** (\`lib/dyrected.ts\`):
|
|
103
|
+
next: `1. **SDK Setup** (\`lib/dyrected.ts\`):
|
|
201
104
|
\`\`\`ts
|
|
202
105
|
import { createClient } from '@dyrected/sdk'
|
|
203
106
|
|
|
204
107
|
export const dyrected = createClient({
|
|
205
|
-
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
206
|
-
apiKey: '${config.apiKey}',
|
|
207
|
-
siteId: '${config.siteId}',`}
|
|
108
|
+
baseUrl: process.env.${envPrefix}DYRECTED_URL || '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
109
|
+
apiKey: process.env.${envPrefix}DYRECTED_API_KEY || '${config.apiKey}',
|
|
110
|
+
siteId: process.env.${envPrefix}SITE_ID || '${config.siteId}',`}
|
|
208
111
|
})
|
|
209
112
|
\`\`\`
|
|
210
113
|
|
|
@@ -213,23 +116,24 @@ export const dyrected = createClient({
|
|
|
213
116
|
import { DyrectedAdmin } from '@dyrected/next/admin'
|
|
214
117
|
|
|
215
118
|
export default function AdminPage() {
|
|
119
|
+
// DyrectedAdmin handles router, CSS, and "use client" for you
|
|
216
120
|
return <DyrectedAdmin basename="/admin" />
|
|
217
121
|
}
|
|
218
122
|
\`\`\`
|
|
219
123
|
`,
|
|
220
|
-
nuxt: `
|
|
124
|
+
nuxt: `1. **Nuxt Config** (\`nuxt.config.ts\`):
|
|
221
125
|
\`\`\`ts
|
|
222
126
|
export default defineNuxtConfig({
|
|
223
127
|
modules: ['@dyrected/nuxt'],
|
|
224
128
|
dyrected: {
|
|
225
|
-
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
226
|
-
apiKey: '${config.apiKey}',
|
|
227
|
-
siteId: '${config.siteId}',`}
|
|
129
|
+
baseUrl: process.env.${envPrefix}DYRECTED_URL || '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
130
|
+
apiKey: process.env.${envPrefix}DYRECTED_API_KEY || '${config.apiKey}',
|
|
131
|
+
siteId: process.env.${envPrefix}SITE_ID || '${config.siteId}',`}
|
|
228
132
|
},
|
|
229
133
|
})
|
|
230
134
|
\`\`\`
|
|
231
135
|
|
|
232
|
-
|
|
136
|
+
2. **Admin Dashboard** (\`pages/admin.vue\`):
|
|
233
137
|
\`\`\`vue
|
|
234
138
|
<script setup lang="ts">
|
|
235
139
|
// DyrectedAdmin is auto-imported by the module
|
|
@@ -310,7 +214,7 @@ onMounted(() => {
|
|
|
310
214
|
\`\`\`
|
|
311
215
|
`
|
|
312
216
|
};
|
|
313
|
-
return
|
|
217
|
+
return mission + (frameworks[activeTab] || frameworks.next) + `
|
|
314
218
|
API Reference: https://docs.dyrected.com`;
|
|
315
219
|
}
|
|
316
220
|
|