@olonjs/cli 3.0.83 → 3.0.84
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.
|
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
|
|
|
596
596
|
"@tiptap/extension-link": "^2.11.5",
|
|
597
597
|
"@tiptap/react": "^2.11.5",
|
|
598
598
|
"@tiptap/starter-kit": "^2.11.5",
|
|
599
|
-
"@olonjs/core": "^1.0.
|
|
599
|
+
"@olonjs/core": "^1.0.72",
|
|
600
600
|
"clsx": "^2.1.1",
|
|
601
601
|
"lucide-react": "^0.474.0",
|
|
602
602
|
"react": "^19.0.0",
|
|
@@ -1780,102 +1780,6 @@ function App() {
|
|
|
1780
1780
|
export default App;
|
|
1781
1781
|
|
|
1782
1782
|
|
|
1783
|
-
END_OF_FILE_CONTENT
|
|
1784
|
-
# SKIP: src/App.tsx:Zone.Identifier is binary and cannot be embedded as text.
|
|
1785
|
-
echo "Creating src/App_.tsx..."
|
|
1786
|
-
cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
|
|
1787
|
-
import { useState, useEffect } from 'react';
|
|
1788
|
-
import { JsonPagesEngine } from '@jsonpages/core';
|
|
1789
|
-
import type { LibraryImageEntry } from '@jsonpages/core';
|
|
1790
|
-
import { ComponentRegistry } from '@/lib/ComponentRegistry';
|
|
1791
|
-
import { SECTION_SCHEMAS } from '@/lib/schemas';
|
|
1792
|
-
import { addSectionConfig } from '@/lib/addSectionConfig';
|
|
1793
|
-
import { getHydratedData } from '@/lib/draftStorage';
|
|
1794
|
-
import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
|
|
1795
|
-
import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
|
|
1796
|
-
|
|
1797
|
-
import siteData from '@/data/config/site.json';
|
|
1798
|
-
import themeData from '@/data/config/theme.json';
|
|
1799
|
-
import menuData from '@/data/config/menu.json';
|
|
1800
|
-
import { getFilePages } from '@/lib/getFilePages';
|
|
1801
|
-
|
|
1802
|
-
import fontsCss from './fonts.css?inline';
|
|
1803
|
-
import tenantCss from './index.css?inline';
|
|
1804
|
-
|
|
1805
|
-
const themeConfig = themeData as unknown as ThemeConfig;
|
|
1806
|
-
const menuConfig = menuData as unknown as MenuConfig;
|
|
1807
|
-
const TENANT_ID = 'alpha';
|
|
1808
|
-
|
|
1809
|
-
const filePages = getFilePages();
|
|
1810
|
-
const fileSiteConfig = siteData as unknown as SiteConfig;
|
|
1811
|
-
const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
|
|
1812
|
-
|
|
1813
|
-
function getInitialData() {
|
|
1814
|
-
return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
function App() {
|
|
1818
|
-
const [{ pages, siteConfig }] = useState(getInitialData);
|
|
1819
|
-
const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
|
|
1820
|
-
|
|
1821
|
-
useEffect(() => {
|
|
1822
|
-
fetch('/api/list-assets')
|
|
1823
|
-
.then((r) => (r.ok ? r.json() : []))
|
|
1824
|
-
.then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
|
|
1825
|
-
.catch(() => setAssetsManifest([]));
|
|
1826
|
-
}, []);
|
|
1827
|
-
|
|
1828
|
-
const config: JsonPagesConfig = {
|
|
1829
|
-
tenantId: TENANT_ID,
|
|
1830
|
-
registry: ComponentRegistry as JsonPagesConfig['registry'],
|
|
1831
|
-
schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
|
|
1832
|
-
pages,
|
|
1833
|
-
siteConfig,
|
|
1834
|
-
themeConfig,
|
|
1835
|
-
menuConfig,
|
|
1836
|
-
themeCss: { tenant: fontsCss + '\n' + tenantCss },
|
|
1837
|
-
addSection: addSectionConfig,
|
|
1838
|
-
persistence: {
|
|
1839
|
-
async saveToFile(state: ProjectState, slug: string): Promise<void> {
|
|
1840
|
-
const res = await fetch('/api/save-to-file', {
|
|
1841
|
-
method: 'POST',
|
|
1842
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1843
|
-
body: JSON.stringify({ projectState: state, slug }),
|
|
1844
|
-
});
|
|
1845
|
-
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
|
1846
|
-
if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
|
|
1847
|
-
},
|
|
1848
|
-
},
|
|
1849
|
-
assets: {
|
|
1850
|
-
assetsBaseUrl: '/assets',
|
|
1851
|
-
assetsManifest,
|
|
1852
|
-
async onAssetUpload(file: File): Promise<string> {
|
|
1853
|
-
if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
|
|
1854
|
-
if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
|
|
1855
|
-
const base64 = await new Promise<string>((resolve, reject) => {
|
|
1856
|
-
const reader = new FileReader();
|
|
1857
|
-
reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
|
|
1858
|
-
reader.onerror = () => reject(reader.error);
|
|
1859
|
-
reader.readAsDataURL(file);
|
|
1860
|
-
});
|
|
1861
|
-
const res = await fetch('/api/upload-asset', {
|
|
1862
|
-
method: 'POST',
|
|
1863
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1864
|
-
body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
|
|
1865
|
-
});
|
|
1866
|
-
const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
|
|
1867
|
-
if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
|
|
1868
|
-
if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
|
|
1869
|
-
return body.url;
|
|
1870
|
-
},
|
|
1871
|
-
},
|
|
1872
|
-
};
|
|
1873
|
-
|
|
1874
|
-
return <JsonPagesEngine config={config} />;
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
export default App;
|
|
1878
|
-
|
|
1879
1783
|
END_OF_FILE_CONTENT
|
|
1880
1784
|
mkdir -p "src/components"
|
|
1881
1785
|
echo "Creating src/components/NotFound.tsx..."
|
|
@@ -9394,24 +9298,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
9394
9298
|
|
|
9395
9299
|
|
|
9396
9300
|
|
|
9397
|
-
END_OF_FILE_CONTENT
|
|
9398
|
-
echo "Creating src/main_.tsx..."
|
|
9399
|
-
cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
|
|
9400
|
-
import '@/types'; // TBP: load type augmentation from capsule-driven types
|
|
9401
|
-
import React from 'react';
|
|
9402
|
-
import ReactDOM from 'react-dom/client';
|
|
9403
|
-
import App from './App';
|
|
9404
|
-
// ... resto del file
|
|
9405
|
-
|
|
9406
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
9407
|
-
<React.StrictMode>
|
|
9408
|
-
<App />
|
|
9409
|
-
</React.StrictMode>
|
|
9410
|
-
);
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
9301
|
END_OF_FILE_CONTENT
|
|
9416
9302
|
# SKIP: src/registry-types.ts is binary and cannot be embedded as text.
|
|
9417
9303
|
mkdir -p "src/server"
|
|
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
|
|
|
596
596
|
"@tiptap/extension-link": "^2.11.5",
|
|
597
597
|
"@tiptap/react": "^2.11.5",
|
|
598
598
|
"@tiptap/starter-kit": "^2.11.5",
|
|
599
|
-
"@olonjs/core": "^1.0.
|
|
599
|
+
"@olonjs/core": "^1.0.72",
|
|
600
600
|
"clsx": "^2.1.1",
|
|
601
601
|
"lucide-react": "^0.474.0",
|
|
602
602
|
"react": "^19.0.0",
|
|
@@ -1144,264 +1144,6 @@ function App() {
|
|
|
1144
1144
|
|
|
1145
1145
|
export default App;
|
|
1146
1146
|
|
|
1147
|
-
END_OF_FILE_CONTENT
|
|
1148
|
-
echo "Creating src/App_.tsx..."
|
|
1149
|
-
cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
|
|
1150
|
-
import { useState, useEffect } from 'react';
|
|
1151
|
-
import { JsonPagesEngine } from '@jsonpages/core';
|
|
1152
|
-
import type { LibraryImageEntry } from '@jsonpages/core';
|
|
1153
|
-
import { ComponentRegistry } from '@/lib/ComponentRegistry';
|
|
1154
|
-
import { SECTION_SCHEMAS } from '@/lib/schemas';
|
|
1155
|
-
import { addSectionConfig } from '@/lib/addSectionConfig';
|
|
1156
|
-
import { getHydratedData } from '@/lib/draftStorage';
|
|
1157
|
-
import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
|
|
1158
|
-
import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
|
|
1159
|
-
|
|
1160
|
-
import siteData from '@/data/config/site.json';
|
|
1161
|
-
import themeData from '@/data/config/theme.json';
|
|
1162
|
-
import menuData from '@/data/config/menu.json';
|
|
1163
|
-
import { getFilePages } from '@/lib/getFilePages';
|
|
1164
|
-
|
|
1165
|
-
import fontsCss from './fonts.css?inline';
|
|
1166
|
-
import tenantCss from './index.css?inline';
|
|
1167
|
-
|
|
1168
|
-
const themeConfig = themeData as unknown as ThemeConfig;
|
|
1169
|
-
const menuConfig = menuData as unknown as MenuConfig;
|
|
1170
|
-
const TENANT_ID = 'alpha';
|
|
1171
|
-
|
|
1172
|
-
const filePages = getFilePages();
|
|
1173
|
-
const fileSiteConfig = siteData as unknown as SiteConfig;
|
|
1174
|
-
const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
|
|
1175
|
-
|
|
1176
|
-
function getInitialData() {
|
|
1177
|
-
return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
function App() {
|
|
1181
|
-
const [{ pages, siteConfig }] = useState(getInitialData);
|
|
1182
|
-
const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
|
|
1183
|
-
|
|
1184
|
-
useEffect(() => {
|
|
1185
|
-
fetch('/api/list-assets')
|
|
1186
|
-
.then((r) => (r.ok ? r.json() : []))
|
|
1187
|
-
.then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
|
|
1188
|
-
.catch(() => setAssetsManifest([]));
|
|
1189
|
-
}, []);
|
|
1190
|
-
|
|
1191
|
-
const config: JsonPagesConfig = {
|
|
1192
|
-
tenantId: TENANT_ID,
|
|
1193
|
-
registry: ComponentRegistry as JsonPagesConfig['registry'],
|
|
1194
|
-
schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
|
|
1195
|
-
pages,
|
|
1196
|
-
siteConfig,
|
|
1197
|
-
themeConfig,
|
|
1198
|
-
menuConfig,
|
|
1199
|
-
themeCss: { tenant: fontsCss + '\n' + tenantCss },
|
|
1200
|
-
addSection: addSectionConfig,
|
|
1201
|
-
persistence: {
|
|
1202
|
-
async saveToFile(state: ProjectState, slug: string): Promise<void> {
|
|
1203
|
-
const res = await fetch('/api/save-to-file', {
|
|
1204
|
-
method: 'POST',
|
|
1205
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1206
|
-
body: JSON.stringify({ projectState: state, slug }),
|
|
1207
|
-
});
|
|
1208
|
-
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
|
1209
|
-
if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
|
|
1210
|
-
},
|
|
1211
|
-
},
|
|
1212
|
-
assets: {
|
|
1213
|
-
assetsBaseUrl: '/assets',
|
|
1214
|
-
assetsManifest,
|
|
1215
|
-
async onAssetUpload(file: File): Promise<string> {
|
|
1216
|
-
if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
|
|
1217
|
-
if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
|
|
1218
|
-
const base64 = await new Promise<string>((resolve, reject) => {
|
|
1219
|
-
const reader = new FileReader();
|
|
1220
|
-
reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
|
|
1221
|
-
reader.onerror = () => reject(reader.error);
|
|
1222
|
-
reader.readAsDataURL(file);
|
|
1223
|
-
});
|
|
1224
|
-
const res = await fetch('/api/upload-asset', {
|
|
1225
|
-
method: 'POST',
|
|
1226
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1227
|
-
body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
|
|
1228
|
-
});
|
|
1229
|
-
const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
|
|
1230
|
-
if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
|
|
1231
|
-
if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
|
|
1232
|
-
return body.url;
|
|
1233
|
-
},
|
|
1234
|
-
},
|
|
1235
|
-
};
|
|
1236
|
-
|
|
1237
|
-
return <JsonPagesEngine config={config} />;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
export default App;
|
|
1241
|
-
|
|
1242
|
-
END_OF_FILE_CONTENT
|
|
1243
|
-
echo "Creating src/_App.tsx..."
|
|
1244
|
-
cat << 'END_OF_FILE_CONTENT' > "src/_App.tsx"
|
|
1245
|
-
/**
|
|
1246
|
-
* Thin Entry Point (Tenant).
|
|
1247
|
-
* Data from getHydratedData (file-backed or draft); assets from public/assets/images.
|
|
1248
|
-
* Supports Hybrid Persistence: Local Filesystem (Dev) or Cloud Bridge (Prod).
|
|
1249
|
-
*/
|
|
1250
|
-
import { useState, useEffect } from 'react';
|
|
1251
|
-
import { JsonPagesEngine } from '@jsonpages/core';
|
|
1252
|
-
import type { LibraryImageEntry } from '@jsonpages/core';
|
|
1253
|
-
import { ComponentRegistry } from '@/lib/ComponentRegistry';
|
|
1254
|
-
import { SECTION_SCHEMAS } from '@/lib/schemas';
|
|
1255
|
-
import { addSectionConfig } from '@/lib/addSectionConfig';
|
|
1256
|
-
import { getHydratedData } from '@/lib/draftStorage';
|
|
1257
|
-
import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
|
|
1258
|
-
import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
|
|
1259
|
-
|
|
1260
|
-
import siteData from '@/data/config/site.json';
|
|
1261
|
-
import themeData from '@/data/config/theme.json';
|
|
1262
|
-
import menuData from '@/data/config/menu.json';
|
|
1263
|
-
import { getFilePages } from '@/lib/getFilePages';
|
|
1264
|
-
|
|
1265
|
-
import fontsCss from './fonts.css?inline';
|
|
1266
|
-
import tenantCss from './index.css?inline';
|
|
1267
|
-
|
|
1268
|
-
// Cloud Configuration (Injected by Vercel/Netlify Env Vars)
|
|
1269
|
-
const CLOUD_API_URL = import.meta.env.VITE_JSONPAGES_CLOUD_URL;
|
|
1270
|
-
const CLOUD_API_KEY = import.meta.env.VITE_JSONPAGES_API_KEY;
|
|
1271
|
-
|
|
1272
|
-
const themeConfig = themeData as unknown as ThemeConfig;
|
|
1273
|
-
const menuConfig = menuData as unknown as MenuConfig;
|
|
1274
|
-
const TENANT_ID = 'alpha';
|
|
1275
|
-
|
|
1276
|
-
const filePages = getFilePages();
|
|
1277
|
-
const fileSiteConfig = siteData as unknown as SiteConfig;
|
|
1278
|
-
const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
|
|
1279
|
-
|
|
1280
|
-
function getInitialData() {
|
|
1281
|
-
return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
function App() {
|
|
1285
|
-
const [{ pages, siteConfig }] = useState(getInitialData);
|
|
1286
|
-
const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
|
|
1287
|
-
|
|
1288
|
-
useEffect(() => {
|
|
1289
|
-
// In Cloud mode, listing assets might be different or disabled for MVP
|
|
1290
|
-
// For now, we keep the local fetch which will fail gracefully on Vercel (404)
|
|
1291
|
-
fetch('/api/list-assets')
|
|
1292
|
-
.then((r) => (r.ok ? r.json() : []))
|
|
1293
|
-
.then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
|
|
1294
|
-
.catch(() => setAssetsManifest([]));
|
|
1295
|
-
}, []);
|
|
1296
|
-
|
|
1297
|
-
console.log("🔍 DEBUG ENV:", {
|
|
1298
|
-
URL: import.meta.env.VITE_JSONPAGES_CLOUD_URL,
|
|
1299
|
-
KEY: import.meta.env.VITE_JSONPAGES_API_KEY ? "PRESENT" : "MISSING"
|
|
1300
|
-
});
|
|
1301
|
-
const config: JsonPagesConfig = {
|
|
1302
|
-
tenantId: TENANT_ID,
|
|
1303
|
-
registry: ComponentRegistry as JsonPagesConfig['registry'],
|
|
1304
|
-
schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
|
|
1305
|
-
pages,
|
|
1306
|
-
siteConfig,
|
|
1307
|
-
themeConfig,
|
|
1308
|
-
menuConfig,
|
|
1309
|
-
themeCss: { tenant: fontsCss + '\n' + tenantCss },
|
|
1310
|
-
addSection: addSectionConfig,
|
|
1311
|
-
persistence: {
|
|
1312
|
-
async saveToFile(state: ProjectState, slug: string): Promise<void> {
|
|
1313
|
-
|
|
1314
|
-
// ☁️ SCENARIO A: CLOUD BRIDGE (Production)
|
|
1315
|
-
if (CLOUD_API_URL && CLOUD_API_KEY) {
|
|
1316
|
-
console.log(`☁️ Saving ${slug} via Cloud Bridge...`);
|
|
1317
|
-
|
|
1318
|
-
const res = await fetch(`${CLOUD_API_URL}/save`, {
|
|
1319
|
-
method: 'POST',
|
|
1320
|
-
headers: {
|
|
1321
|
-
'Content-Type': 'application/json',
|
|
1322
|
-
'Authorization': `Bearer ${CLOUD_API_KEY}`
|
|
1323
|
-
},
|
|
1324
|
-
body: JSON.stringify({
|
|
1325
|
-
// Mapping logical slug to physical path in repo
|
|
1326
|
-
path: `src/data/pages/${slug}.json`,
|
|
1327
|
-
// We save the page config specifically
|
|
1328
|
-
content: state.page,
|
|
1329
|
-
message: `Content update for ${slug} via Visual Editor`
|
|
1330
|
-
}),
|
|
1331
|
-
});
|
|
1332
|
-
|
|
1333
|
-
if (!res.ok) {
|
|
1334
|
-
const err = await res.json().catch(() => ({}));
|
|
1335
|
-
throw new Error(err.error || `Cloud save failed: ${res.status}`);
|
|
1336
|
-
}
|
|
1337
|
-
return;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
// 💻 SCENARIO B: LOCAL FILESYSTEM (Development)
|
|
1341
|
-
console.log(`💻 Saving ${slug} to Local Filesystem...`);
|
|
1342
|
-
const res = await fetch('/api/save-to-file', {
|
|
1343
|
-
method: 'POST',
|
|
1344
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1345
|
-
body: JSON.stringify({ projectState: state, slug }),
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
|
1349
|
-
if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
|
|
1350
|
-
},
|
|
1351
|
-
},
|
|
1352
|
-
assets: {
|
|
1353
|
-
assetsBaseUrl: '/assets',
|
|
1354
|
-
assetsManifest,
|
|
1355
|
-
async onAssetUpload(file: File): Promise<string> {
|
|
1356
|
-
// Note: Asset upload in Cloud Mode requires the R2 Bridge (Next Step in Roadmap)
|
|
1357
|
-
// For now, this works in Local Mode.
|
|
1358
|
-
if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
|
|
1359
|
-
if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
|
|
1360
|
-
|
|
1361
|
-
const base64 = await new Promise<string>((resolve, reject) => {
|
|
1362
|
-
const reader = new FileReader();
|
|
1363
|
-
reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
|
|
1364
|
-
reader.onerror = () => reject(reader.error);
|
|
1365
|
-
reader.readAsDataURL(file);
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
const res = await fetch('/api/upload-asset', {
|
|
1369
|
-
method: 'POST',
|
|
1370
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1371
|
-
body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
|
|
1375
|
-
if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
|
|
1376
|
-
if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
|
|
1377
|
-
return body.url;
|
|
1378
|
-
},
|
|
1379
|
-
},
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
return <JsonPagesEngine config={config} />;
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
export default App;
|
|
1386
|
-
|
|
1387
|
-
END_OF_FILE_CONTENT
|
|
1388
|
-
echo "Creating src/_main.tsx..."
|
|
1389
|
-
cat << 'END_OF_FILE_CONTENT' > "src/_main.tsx"
|
|
1390
|
-
import '@/types'; // TBP: load type augmentation from capsule-driven types
|
|
1391
|
-
import React from 'react';
|
|
1392
|
-
import ReactDOM from 'react-dom/client';
|
|
1393
|
-
import App from './App';
|
|
1394
|
-
// ... resto del file
|
|
1395
|
-
|
|
1396
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
1397
|
-
<React.StrictMode>
|
|
1398
|
-
<App />
|
|
1399
|
-
</React.StrictMode>
|
|
1400
|
-
);
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
1147
|
END_OF_FILE_CONTENT
|
|
1406
1148
|
mkdir -p "src/components"
|
|
1407
1149
|
echo "Creating src/components/NotFound.tsx..."
|
|
@@ -10933,24 +10675,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
10933
10675
|
|
|
10934
10676
|
|
|
10935
10677
|
|
|
10936
|
-
END_OF_FILE_CONTENT
|
|
10937
|
-
echo "Creating src/main_.tsx..."
|
|
10938
|
-
cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
|
|
10939
|
-
import '@/types'; // TBP: load type augmentation from capsule-driven types
|
|
10940
|
-
import React from 'react';
|
|
10941
|
-
import ReactDOM from 'react-dom/client';
|
|
10942
|
-
import App from './App';
|
|
10943
|
-
// ... resto del file
|
|
10944
|
-
|
|
10945
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
10946
|
-
<React.StrictMode>
|
|
10947
|
-
<App />
|
|
10948
|
-
</React.StrictMode>
|
|
10949
|
-
);
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
10678
|
END_OF_FILE_CONTENT
|
|
10955
10679
|
# SKIP: src/registry-types.ts is binary and cannot be embedded as text.
|
|
10956
10680
|
mkdir -p "src/server"
|
|
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
|
|
|
596
596
|
"@tiptap/extension-link": "^2.11.5",
|
|
597
597
|
"@tiptap/react": "^2.11.5",
|
|
598
598
|
"@tiptap/starter-kit": "^2.11.5",
|
|
599
|
-
"@olonjs/core": "^1.0.
|
|
599
|
+
"@olonjs/core": "^1.0.72",
|
|
600
600
|
"clsx": "^2.1.1",
|
|
601
601
|
"lucide-react": "^0.474.0",
|
|
602
602
|
"react": "^19.0.0",
|
|
@@ -1780,102 +1780,6 @@ function App() {
|
|
|
1780
1780
|
export default App;
|
|
1781
1781
|
|
|
1782
1782
|
|
|
1783
|
-
END_OF_FILE_CONTENT
|
|
1784
|
-
# SKIP: src/App.tsx:Zone.Identifier is binary and cannot be embedded as text.
|
|
1785
|
-
echo "Creating src/App_.tsx..."
|
|
1786
|
-
cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
|
|
1787
|
-
import { useState, useEffect } from 'react';
|
|
1788
|
-
import { JsonPagesEngine } from '@jsonpages/core';
|
|
1789
|
-
import type { LibraryImageEntry } from '@jsonpages/core';
|
|
1790
|
-
import { ComponentRegistry } from '@/lib/ComponentRegistry';
|
|
1791
|
-
import { SECTION_SCHEMAS } from '@/lib/schemas';
|
|
1792
|
-
import { addSectionConfig } from '@/lib/addSectionConfig';
|
|
1793
|
-
import { getHydratedData } from '@/lib/draftStorage';
|
|
1794
|
-
import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
|
|
1795
|
-
import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
|
|
1796
|
-
|
|
1797
|
-
import siteData from '@/data/config/site.json';
|
|
1798
|
-
import themeData from '@/data/config/theme.json';
|
|
1799
|
-
import menuData from '@/data/config/menu.json';
|
|
1800
|
-
import { getFilePages } from '@/lib/getFilePages';
|
|
1801
|
-
|
|
1802
|
-
import fontsCss from './fonts.css?inline';
|
|
1803
|
-
import tenantCss from './index.css?inline';
|
|
1804
|
-
|
|
1805
|
-
const themeConfig = themeData as unknown as ThemeConfig;
|
|
1806
|
-
const menuConfig = menuData as unknown as MenuConfig;
|
|
1807
|
-
const TENANT_ID = 'alpha';
|
|
1808
|
-
|
|
1809
|
-
const filePages = getFilePages();
|
|
1810
|
-
const fileSiteConfig = siteData as unknown as SiteConfig;
|
|
1811
|
-
const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
|
|
1812
|
-
|
|
1813
|
-
function getInitialData() {
|
|
1814
|
-
return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
function App() {
|
|
1818
|
-
const [{ pages, siteConfig }] = useState(getInitialData);
|
|
1819
|
-
const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
|
|
1820
|
-
|
|
1821
|
-
useEffect(() => {
|
|
1822
|
-
fetch('/api/list-assets')
|
|
1823
|
-
.then((r) => (r.ok ? r.json() : []))
|
|
1824
|
-
.then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
|
|
1825
|
-
.catch(() => setAssetsManifest([]));
|
|
1826
|
-
}, []);
|
|
1827
|
-
|
|
1828
|
-
const config: JsonPagesConfig = {
|
|
1829
|
-
tenantId: TENANT_ID,
|
|
1830
|
-
registry: ComponentRegistry as JsonPagesConfig['registry'],
|
|
1831
|
-
schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
|
|
1832
|
-
pages,
|
|
1833
|
-
siteConfig,
|
|
1834
|
-
themeConfig,
|
|
1835
|
-
menuConfig,
|
|
1836
|
-
themeCss: { tenant: fontsCss + '\n' + tenantCss },
|
|
1837
|
-
addSection: addSectionConfig,
|
|
1838
|
-
persistence: {
|
|
1839
|
-
async saveToFile(state: ProjectState, slug: string): Promise<void> {
|
|
1840
|
-
const res = await fetch('/api/save-to-file', {
|
|
1841
|
-
method: 'POST',
|
|
1842
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1843
|
-
body: JSON.stringify({ projectState: state, slug }),
|
|
1844
|
-
});
|
|
1845
|
-
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
|
1846
|
-
if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
|
|
1847
|
-
},
|
|
1848
|
-
},
|
|
1849
|
-
assets: {
|
|
1850
|
-
assetsBaseUrl: '/assets',
|
|
1851
|
-
assetsManifest,
|
|
1852
|
-
async onAssetUpload(file: File): Promise<string> {
|
|
1853
|
-
if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
|
|
1854
|
-
if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
|
|
1855
|
-
const base64 = await new Promise<string>((resolve, reject) => {
|
|
1856
|
-
const reader = new FileReader();
|
|
1857
|
-
reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
|
|
1858
|
-
reader.onerror = () => reject(reader.error);
|
|
1859
|
-
reader.readAsDataURL(file);
|
|
1860
|
-
});
|
|
1861
|
-
const res = await fetch('/api/upload-asset', {
|
|
1862
|
-
method: 'POST',
|
|
1863
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1864
|
-
body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
|
|
1865
|
-
});
|
|
1866
|
-
const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
|
|
1867
|
-
if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
|
|
1868
|
-
if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
|
|
1869
|
-
return body.url;
|
|
1870
|
-
},
|
|
1871
|
-
},
|
|
1872
|
-
};
|
|
1873
|
-
|
|
1874
|
-
return <JsonPagesEngine config={config} />;
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
export default App;
|
|
1878
|
-
|
|
1879
1783
|
END_OF_FILE_CONTENT
|
|
1880
1784
|
mkdir -p "src/components"
|
|
1881
1785
|
echo "Creating src/components/NotFound.tsx..."
|
|
@@ -9394,24 +9298,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
9394
9298
|
|
|
9395
9299
|
|
|
9396
9300
|
|
|
9397
|
-
END_OF_FILE_CONTENT
|
|
9398
|
-
echo "Creating src/main_.tsx..."
|
|
9399
|
-
cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
|
|
9400
|
-
import '@/types'; // TBP: load type augmentation from capsule-driven types
|
|
9401
|
-
import React from 'react';
|
|
9402
|
-
import ReactDOM from 'react-dom/client';
|
|
9403
|
-
import App from './App';
|
|
9404
|
-
// ... resto del file
|
|
9405
|
-
|
|
9406
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
9407
|
-
<React.StrictMode>
|
|
9408
|
-
<App />
|
|
9409
|
-
</React.StrictMode>
|
|
9410
|
-
);
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
9301
|
END_OF_FILE_CONTENT
|
|
9416
9302
|
# SKIP: src/registry-types.ts is binary and cannot be embedded as text.
|
|
9417
9303
|
mkdir -p "src/server"
|