@dyrected/core 1.0.4 → 1.0.6
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 +86 -171
- package/dist/index.js +86 -171
- 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,229 +31,144 @@ 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
|
-
- EMBED ADMIN: You MUST create a route (e.g. \`/admin\`) and mount the Admin UI components described below.
|
|
60
|
-
- ALL-IN-ONE: Your goal is to give the user a project that is READY TO SHIP. Do the setup, do the embedding, and do the sync in one go.
|
|
61
|
-
`;
|
|
62
|
-
const strategy = `
|
|
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
|
+
|
|
63
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
|
|
64
|
-
|
|
58
|
+
3. REQUIRED DELIVERABLES
|
|
65
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
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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\`.
|
|
71
67
|
|
|
72
|
-
|
|
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
|
|
69
|
+
4. PHASE 0 \u2014 DISCOVERY & PRESERVATION
|
|
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
|
|
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?"
|
|
78
|
+
|
|
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
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
|
|
198
|
-
|
|
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
|
-
\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
|
-
STEP ${isSelfHosted ? "3" : "4"} \u2014 FRONTEND IMPLEMENTATION
|
|
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
|
-
SDK CLIENT 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
|
-
|
|
236
|
-
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
2. **Admin Dashboard** (\`app/admin/[[...slug]]/page.tsx\`):
|
|
141
|
+
\`\`\`tsx
|
|
142
|
+
import { DyrectedAdmin } from '@dyrected/next/admin'
|
|
143
|
+
|
|
144
|
+
export default function AdminPage() {
|
|
145
|
+
// DyrectedAdmin handles router, CSS, and "use client" for you
|
|
146
|
+
return <DyrectedAdmin basename="/admin" />
|
|
147
|
+
}
|
|
148
|
+
\`\`\`
|
|
149
|
+
`,
|
|
150
|
+
nuxt: `1. **Nuxt Config** (\`nuxt.config.ts\`):
|
|
237
151
|
\`\`\`ts
|
|
238
152
|
export default defineNuxtConfig({
|
|
239
153
|
modules: ['@dyrected/nuxt'],
|
|
240
154
|
dyrected: {
|
|
241
|
-
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
242
|
-
apiKey: '${config.apiKey}',
|
|
243
|
-
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}',`}
|
|
244
158
|
},
|
|
245
159
|
})
|
|
246
160
|
\`\`\`
|
|
247
161
|
|
|
248
|
-
|
|
162
|
+
2. **Admin Dashboard** (\`pages/admin.vue\`):
|
|
249
163
|
\`\`\`vue
|
|
250
164
|
<script setup lang="ts">
|
|
165
|
+
// DyrectedAdmin is auto-imported by the module
|
|
251
166
|
definePageMeta({ layout: false })
|
|
252
167
|
</script>
|
|
253
168
|
|
|
254
169
|
<template>
|
|
255
170
|
<ClientOnly>
|
|
256
|
-
<DyrectedAdmin basename="/
|
|
171
|
+
<DyrectedAdmin basename="/admin" />
|
|
257
172
|
</ClientOnly>
|
|
258
173
|
</template>
|
|
259
174
|
\`\`\`
|
|
@@ -325,7 +240,7 @@ onMounted(() => {
|
|
|
325
240
|
\`\`\`
|
|
326
241
|
`
|
|
327
242
|
};
|
|
328
|
-
return
|
|
243
|
+
return mission + (frameworks[activeTab] || frameworks.next) + `
|
|
329
244
|
API Reference: https://docs.dyrected.com`;
|
|
330
245
|
}
|
|
331
246
|
|
package/dist/index.js
CHANGED
|
@@ -5,229 +5,144 @@ 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
|
-
- EMBED ADMIN: You MUST create a route (e.g. \`/admin\`) and mount the Admin UI components described below.
|
|
34
|
-
- ALL-IN-ONE: Your goal is to give the user a project that is READY TO SHIP. Do the setup, do the embedding, and do the sync in one go.
|
|
35
|
-
`;
|
|
36
|
-
const strategy = `
|
|
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
|
+
|
|
37
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
|
|
38
|
-
|
|
32
|
+
3. REQUIRED DELIVERABLES
|
|
39
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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\`.
|
|
45
41
|
|
|
46
|
-
|
|
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
|
|
43
|
+
4. PHASE 0 \u2014 DISCOVERY & PRESERVATION
|
|
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
|
|
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?"
|
|
52
|
+
|
|
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
|
-
],
|
|
81
|
+
fields: [{ name: 'alt', type: 'text' }]
|
|
98
82
|
})
|
|
99
83
|
|
|
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
|
-
],
|
|
116
|
-
})
|
|
117
|
-
|
|
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
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
|
|
172
|
-
|
|
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
|
-
\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
|
-
STEP ${isSelfHosted ? "2" : "3"} \u2014 MOUNTING THE ADMIN UI
|
|
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
|
-
SDK CLIENT 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
|
-
|
|
210
|
-
|
|
112
|
+
\`\`\`
|
|
113
|
+
|
|
114
|
+
2. **Admin Dashboard** (\`app/admin/[[...slug]]/page.tsx\`):
|
|
115
|
+
\`\`\`tsx
|
|
116
|
+
import { DyrectedAdmin } from '@dyrected/next/admin'
|
|
117
|
+
|
|
118
|
+
export default function AdminPage() {
|
|
119
|
+
// DyrectedAdmin handles router, CSS, and "use client" for you
|
|
120
|
+
return <DyrectedAdmin basename="/admin" />
|
|
121
|
+
}
|
|
122
|
+
\`\`\`
|
|
123
|
+
`,
|
|
124
|
+
nuxt: `1. **Nuxt Config** (\`nuxt.config.ts\`):
|
|
211
125
|
\`\`\`ts
|
|
212
126
|
export default defineNuxtConfig({
|
|
213
127
|
modules: ['@dyrected/nuxt'],
|
|
214
128
|
dyrected: {
|
|
215
|
-
baseUrl: '${config.baseUrl || "http://localhost:3000"}',${isSelfHosted ? "" : `
|
|
216
|
-
apiKey: '${config.apiKey}',
|
|
217
|
-
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}',`}
|
|
218
132
|
},
|
|
219
133
|
})
|
|
220
134
|
\`\`\`
|
|
221
135
|
|
|
222
|
-
|
|
136
|
+
2. **Admin Dashboard** (\`pages/admin.vue\`):
|
|
223
137
|
\`\`\`vue
|
|
224
138
|
<script setup lang="ts">
|
|
139
|
+
// DyrectedAdmin is auto-imported by the module
|
|
225
140
|
definePageMeta({ layout: false })
|
|
226
141
|
</script>
|
|
227
142
|
|
|
228
143
|
<template>
|
|
229
144
|
<ClientOnly>
|
|
230
|
-
<DyrectedAdmin basename="/
|
|
145
|
+
<DyrectedAdmin basename="/admin" />
|
|
231
146
|
</ClientOnly>
|
|
232
147
|
</template>
|
|
233
148
|
\`\`\`
|
|
@@ -299,7 +214,7 @@ onMounted(() => {
|
|
|
299
214
|
\`\`\`
|
|
300
215
|
`
|
|
301
216
|
};
|
|
302
|
-
return
|
|
217
|
+
return mission + (frameworks[activeTab] || frameworks.next) + `
|
|
303
218
|
API Reference: https://docs.dyrected.com`;
|
|
304
219
|
}
|
|
305
220
|
|