@brightsideoy/kama 1.0.0-alpha.17 → 1.0.0-alpha.19
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/bin/cli.js +69 -43
- package/content/definitions/block-kama-forms.yaml +5 -0
- package/content/definitions/blocks/node-list.yaml +8 -0
- package/content/forms/contact.yaml +44 -0
- package/content/forms/subscription.yaml +32 -0
- package/dist/assets/AssetEditor-CY9SjGaJ.js +1 -0
- package/dist/assets/CmsBadge-BNsgxyyB.css +1 -0
- package/dist/assets/CmsBadge-D0Ob9qPU.js +1 -0
- package/dist/assets/CmsButton-BQ85-YXn.css +1 -0
- package/dist/assets/CmsButton-CbOfxvjr.js +1 -0
- package/dist/assets/CmsDataRow--H4iI_u_.js +1 -0
- package/dist/assets/CmsDataRow-CYNxfVaZ.css +1 -0
- package/dist/assets/CmsHelp-DJSdFYVH.js +1 -0
- package/dist/assets/CmsImagePreview-CujwG8r_.css +1 -0
- package/dist/assets/CmsImagePreview-DuHPBzml.js +1 -0
- package/dist/assets/CmsPaneResizer-B62ZqsnE.js +1 -0
- package/dist/assets/CmsPaneResizer-Dkidd4pI.css +1 -0
- package/dist/assets/CmsPublisher-DBcCz-AD.js +3 -0
- package/dist/assets/CmsUserCenter-D3XDzKDC.js +1 -0
- package/dist/assets/EditorSplitPreview-wCnwVnnb.js +1 -0
- package/dist/assets/NodeContextMenu-BYeWzmQ5.css +1 -0
- package/dist/assets/NodeContextMenu-C_HWBGeb.js +1 -0
- package/dist/assets/NodeEditor-D3tAGViP.js +147 -0
- package/dist/assets/NodeEditor-DlfAHtiG.css +1 -0
- package/dist/assets/StatusPill-CbeW5SMT.js +1 -0
- package/dist/assets/StatusPill-Dlm6uvGe.css +1 -0
- package/dist/assets/TrashWorkspace--uFsWt53.js +1 -0
- package/dist/assets/{_plugin-vue_export-helper-CTn6xl4w.js → _plugin-vue_export-helper-CQxWhVtr.js} +1 -1
- package/dist/assets/circle-check-A-QeFqt3.js +1 -0
- package/dist/assets/database-hg5SqKLG.js +1 -0
- package/dist/assets/folder-tree-BWYot72H.js +1 -0
- package/dist/assets/globe-BraoljaN.js +1 -0
- package/dist/assets/{history-XvWNE5zO.js → history-pqIbKK9u.js} +1 -1
- package/dist/assets/index-BIES-eB_.js +14 -0
- package/dist/assets/index-DcKiaRjI.css +1 -0
- package/dist/assets/{layers-D0DLWjff.js → layers-B6RgfpPR.js} +1 -1
- package/dist/assets/loader-circle-BoUkhDjq.js +1 -0
- package/dist/assets/monitor-CrpvInqG.js +1 -0
- package/dist/assets/panel-left-close-CVFakVaA.js +1 -0
- package/dist/assets/plus-mqZr4I1D.js +1 -0
- package/dist/assets/rotate-ccw-CWE4pOjs.js +1 -0
- package/dist/assets/{save-C8e-QRkP.js → save-Ctv8vW9H.js} +1 -1
- package/dist/assets/schema-helpers-1UXlO0Ye.js +1 -0
- package/dist/assets/send-F-mJeL54.js +1 -0
- package/dist/assets/{smartphone-Wy6xQi_i.js → smartphone-EPce6VdP.js} +1 -1
- package/dist/assets/trash-2-CAUF1KtI.js +1 -0
- package/dist/assets/useLocalStorage-C805uplI.js +1 -0
- package/dist/assets/useNodes-W5j_uxdQ.js +1 -0
- package/dist/assets/user-IFlT6mYq.js +1 -0
- package/dist/{dist-BGv2bzzF.js → dist-Bta0OpXl.js} +145 -30
- package/dist/index.html +34 -3
- package/dist/index.js +145 -30
- package/dist/schema-compiler.js +1 -1
- package/documentation/EXTENSIONS.md +204 -0
- package/extension-registry.js +5 -0
- package/package.json +3 -1
- package/templates/astro-starter/old_src/components/blocks/BlockRenderer.astro +143 -0
- package/templates/astro-starter/old_src/components/blocks/ColumnLayout.astro +67 -0
- package/templates/astro-starter/old_src/components/blocks/HeaderImage.astro +126 -0
- package/templates/astro-starter/old_src/components/blocks/Hero.astro +218 -0
- package/templates/astro-starter/old_src/components/blocks/HtmlBlock.astro +25 -0
- package/templates/astro-starter/old_src/components/blocks/ImageBlock.astro +91 -0
- package/templates/astro-starter/old_src/components/blocks/NodeList.astro +274 -0
- package/templates/astro-starter/old_src/components/blocks/TextBlock.astro +15 -0
- package/templates/astro-starter/old_src/components/global/Breadcrumb.astro +108 -0
- package/templates/astro-starter/old_src/components/global/Footer.astro +50 -0
- package/templates/astro-starter/old_src/components/global/Header.astro +171 -0
- package/templates/astro-starter/old_src/components/global/MetaHead.astro +266 -0
- package/templates/astro-starter/old_src/components/global/NavigationMenu.astro +413 -0
- package/templates/astro-starter/old_src/components/ui/KamaImage.astro +180 -0
- package/templates/astro-starter/old_src/layouts/SkeletonLayout.astro +56 -0
- package/templates/astro-starter/old_src/layouts/StandardLayout.astro +63 -0
- package/templates/astro-starter/old_src/lib/cms.ts +219 -0
- package/templates/astro-starter/old_src/lib/media/kama-image-service.ts +67 -0
- package/templates/astro-starter/old_src/lib/media/media-proxy.ts +148 -0
- package/templates/astro-starter/old_src/lib/media/media-url.ts +112 -0
- package/templates/astro-starter/old_src/middleware.ts +536 -0
- package/templates/astro-starter/old_src/pages/404.astro +46 -0
- package/templates/astro-starter/old_src/pages/[...slug].astro +311 -0
- package/templates/astro-starter/old_src/pages/api/load-nodes.ts +68 -0
- package/templates/astro-starter/old_src/pages/api/partials/node-cards.astro +101 -0
- package/templates/astro-starter/old_src/pages/llms-full.txt.ts +171 -0
- package/templates/astro-starter/old_src/pages/llms.txt.ts +113 -0
- package/templates/astro-starter/old_src/pages/media/[...path].ts +14 -0
- package/templates/astro-starter/old_src/pages/partials/footer.astro +69 -0
- package/templates/astro-starter/old_src/pages/partials/nav.astro +141 -0
- package/templates/astro-starter/old_src/styles/standard-theme.css +157 -0
- package/templates/astro-starter/old_src/utils/tiptap.ts +113 -0
- package/templates/astro-starter/src/components/blocks/Form.astro +143 -0
- package/templates/kama-worker/index.js +35 -12
- package/templates/kama-worker/server.js +2 -0
- package/dist/assets/AssetEditor-CCNDGlQ_.js +0 -1
- package/dist/assets/CmsHelp-m9EOY_nE.js +0 -1
- package/dist/assets/CmsPublisher-Cs_ys68V.js +0 -3
- package/dist/assets/CmsUserCenter-D-T6HyWF.js +0 -1
- package/dist/assets/EditorSplitPreview-CnWpXZWQ.js +0 -1
- package/dist/assets/NodeEditor-B1TvB2eW.css +0 -1
- package/dist/assets/NodeEditor-CZGnkwvf.js +0 -147
- package/dist/assets/TrashWorkspace-CERX58Ek.js +0 -1
- package/dist/assets/index-BpOAL4tE.js +0 -14
- package/dist/assets/index-C-EkPNvq.css +0 -1
- package/dist/assets/link-Bs_SUS0O.js +0 -1
- package/dist/assets/rotate-ccw-CvD7yNQj.js +0 -1
package/dist/index.html
CHANGED
|
@@ -6,9 +6,40 @@
|
|
|
6
6
|
<title>KAMA</title>
|
|
7
7
|
<link rel="icon" href="/favicon.png">
|
|
8
8
|
<link rel="apple-touch-icon" sizes="180x180" href="/favicon.png">
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-
|
|
11
|
-
<link rel="
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BIES-eB_.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CQxWhVtr.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/schema-helpers-1UXlO0Ye.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/useNodes-W5j_uxdQ.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/plus-mqZr4I1D.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/send-F-mJeL54.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/CmsPaneResizer-B62ZqsnE.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/user-IFlT6mYq.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/useLocalStorage-C805uplI.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/circle-check-A-QeFqt3.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/loader-circle-BoUkhDjq.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/CmsImagePreview-DuHPBzml.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/assets/database-hg5SqKLG.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="/assets/NodeContextMenu-C_HWBGeb.js">
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/monitor-CrpvInqG.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/rotate-ccw-CWE4pOjs.js">
|
|
25
|
+
<link rel="modulepreload" crossorigin href="/assets/trash-2-CAUF1KtI.js">
|
|
26
|
+
<link rel="modulepreload" crossorigin href="/assets/folder-tree-BWYot72H.js">
|
|
27
|
+
<link rel="modulepreload" crossorigin href="/assets/globe-BraoljaN.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/history-pqIbKK9u.js">
|
|
29
|
+
<link rel="modulepreload" crossorigin href="/assets/layers-B6RgfpPR.js">
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/panel-left-close-CVFakVaA.js">
|
|
31
|
+
<link rel="modulepreload" crossorigin href="/assets/save-Ctv8vW9H.js">
|
|
32
|
+
<link rel="modulepreload" crossorigin href="/assets/smartphone-EPce6VdP.js">
|
|
33
|
+
<link rel="modulepreload" crossorigin href="/assets/CmsButton-CbOfxvjr.js">
|
|
34
|
+
<link rel="modulepreload" crossorigin href="/assets/CmsDataRow--H4iI_u_.js">
|
|
35
|
+
<link rel="modulepreload" crossorigin href="/assets/CmsBadge-D0Ob9qPU.js">
|
|
36
|
+
<link rel="stylesheet" crossorigin href="/assets/CmsPaneResizer-Dkidd4pI.css">
|
|
37
|
+
<link rel="stylesheet" crossorigin href="/assets/CmsImagePreview-CujwG8r_.css">
|
|
38
|
+
<link rel="stylesheet" crossorigin href="/assets/NodeContextMenu-BYeWzmQ5.css">
|
|
39
|
+
<link rel="stylesheet" crossorigin href="/assets/CmsButton-BQ85-YXn.css">
|
|
40
|
+
<link rel="stylesheet" crossorigin href="/assets/CmsDataRow-CYNxfVaZ.css">
|
|
41
|
+
<link rel="stylesheet" crossorigin href="/assets/CmsBadge-BNsgxyyB.css">
|
|
42
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DcKiaRjI.css">
|
|
12
43
|
</head>
|
|
13
44
|
<body>
|
|
14
45
|
<div id="app"></div>
|
package/dist/index.js
CHANGED
|
@@ -21278,6 +21278,13 @@ var Tb = /* @__PURE__ */ o({
|
|
|
21278
21278
|
default: 0,
|
|
21279
21279
|
public: !0,
|
|
21280
21280
|
rules: {}
|
|
21281
|
+
},
|
|
21282
|
+
targetPage: {
|
|
21283
|
+
label: "Target Page for \"Read More\" Links",
|
|
21284
|
+
type: "link",
|
|
21285
|
+
public: !0,
|
|
21286
|
+
allowedLinkTypes: ["tree", "node"],
|
|
21287
|
+
rules: {}
|
|
21281
21288
|
}
|
|
21282
21289
|
},
|
|
21283
21290
|
tabs: { access: {
|
|
@@ -21417,6 +21424,54 @@ var Tb = /* @__PURE__ */ o({
|
|
|
21417
21424
|
}
|
|
21418
21425
|
},
|
|
21419
21426
|
category: "Content"
|
|
21427
|
+
},
|
|
21428
|
+
form: {
|
|
21429
|
+
id: "form",
|
|
21430
|
+
label: "Dynamic Form",
|
|
21431
|
+
description: "Embeds a form definition from content/forms/",
|
|
21432
|
+
category: "Interactive",
|
|
21433
|
+
icon: "ClipboardList",
|
|
21434
|
+
fieldSets: ["access"],
|
|
21435
|
+
fields: {
|
|
21436
|
+
formId: {
|
|
21437
|
+
label: "Select Form",
|
|
21438
|
+
type: "select",
|
|
21439
|
+
required: !0,
|
|
21440
|
+
description: "Choose a YAML form definition",
|
|
21441
|
+
optionsEndpoint: "/api/admin/ext/kama-forms/forms",
|
|
21442
|
+
optionsValueKey: "id",
|
|
21443
|
+
optionsLabelKey: "title",
|
|
21444
|
+
rules: { required: !0 }
|
|
21445
|
+
},
|
|
21446
|
+
isHidden: {
|
|
21447
|
+
type: "boolean",
|
|
21448
|
+
label: "Hidden",
|
|
21449
|
+
default: !1,
|
|
21450
|
+
public: !1,
|
|
21451
|
+
variant: "danger",
|
|
21452
|
+
dbType: "integer",
|
|
21453
|
+
index: !0,
|
|
21454
|
+
position: "sidebar",
|
|
21455
|
+
liveSync: !0,
|
|
21456
|
+
rules: {}
|
|
21457
|
+
},
|
|
21458
|
+
startShowingAt: {
|
|
21459
|
+
type: "datetime",
|
|
21460
|
+
label: "Start Showing At",
|
|
21461
|
+
position: "sidebar",
|
|
21462
|
+
public: !1,
|
|
21463
|
+
index: !0,
|
|
21464
|
+
rules: {}
|
|
21465
|
+
},
|
|
21466
|
+
stopShowingAt: {
|
|
21467
|
+
type: "datetime",
|
|
21468
|
+
label: "Stop Showing At",
|
|
21469
|
+
position: "sidebar",
|
|
21470
|
+
public: !1,
|
|
21471
|
+
index: !0,
|
|
21472
|
+
rules: {}
|
|
21473
|
+
}
|
|
21474
|
+
}
|
|
21420
21475
|
}
|
|
21421
21476
|
},
|
|
21422
21477
|
nodeLayouts: {
|
|
@@ -22235,11 +22290,14 @@ async function jb(e, t) {
|
|
|
22235
22290
|
return /* @__PURE__ */ new Set();
|
|
22236
22291
|
}
|
|
22237
22292
|
}
|
|
22238
|
-
async function Mb(e, t) {
|
|
22293
|
+
async function Mb(e, t, n = []) {
|
|
22239
22294
|
try {
|
|
22240
22295
|
await e.prepare("PRAGMA foreign_keys = ON;").run();
|
|
22241
22296
|
} catch {}
|
|
22242
22297
|
for (let [t, n] of Object.entries(Ob)) await e.prepare(`CREATE TABLE IF NOT EXISTS "${t}" (${n});`).run();
|
|
22298
|
+
if (n && n.length > 0) {
|
|
22299
|
+
for (let t of n) if (t.db && t.db.tables) for (let [n, r] of Object.entries(t.db.tables)) (await jb(e, n)).size === 0 && (await e.prepare(`CREATE TABLE IF NOT EXISTS "${n}" (${r});`).run(), console.log(`[Extension: ${t.id}] Created database table: ${n}`));
|
|
22300
|
+
}
|
|
22243
22301
|
for (let [t] of Object.entries(Ob)) {
|
|
22244
22302
|
let n = await jb(e, t);
|
|
22245
22303
|
if (n.size > 0) {
|
|
@@ -22284,11 +22342,11 @@ async function Mb(e, t) {
|
|
|
22284
22342
|
for (let t of kb) try {
|
|
22285
22343
|
await e.prepare(t).run();
|
|
22286
22344
|
} catch {}
|
|
22287
|
-
let
|
|
22288
|
-
for (let t of Ab(
|
|
22345
|
+
let r = t || Db;
|
|
22346
|
+
for (let t of Ab(r?.rawNodes || {})) try {
|
|
22289
22347
|
await e.prepare(t).run();
|
|
22290
22348
|
} catch {}
|
|
22291
|
-
await Nb(e,
|
|
22349
|
+
await Nb(e, r);
|
|
22292
22350
|
}
|
|
22293
22351
|
async function Nb(e, t) {
|
|
22294
22352
|
if (t) for (let [n, r] of Object.entries(t.rawNodes || {})) {
|
|
@@ -25248,13 +25306,15 @@ function qx() {
|
|
|
25248
25306
|
t = e;
|
|
25249
25307
|
break;
|
|
25250
25308
|
}
|
|
25251
|
-
|
|
25252
|
-
|
|
25309
|
+
let i = Rx.resolve(e, "extensions"), a = Lx.existsSync(i);
|
|
25310
|
+
if (!t && !a) return null;
|
|
25311
|
+
let o = (e, t) => {
|
|
25312
|
+
if (!e) return {};
|
|
25253
25313
|
let n = "";
|
|
25254
|
-
for (let r of
|
|
25255
|
-
let
|
|
25256
|
-
if (Lx.existsSync(
|
|
25257
|
-
n =
|
|
25314
|
+
for (let r of t) {
|
|
25315
|
+
let t = Rx.join(e, r);
|
|
25316
|
+
if (Lx.existsSync(t)) {
|
|
25317
|
+
n = t;
|
|
25258
25318
|
break;
|
|
25259
25319
|
}
|
|
25260
25320
|
}
|
|
@@ -25270,24 +25330,46 @@ function qx() {
|
|
|
25270
25330
|
}
|
|
25271
25331
|
}
|
|
25272
25332
|
return r;
|
|
25273
|
-
}
|
|
25333
|
+
}, s = o(t, ["nodes", "node"]), c = o(t, ["trees", "tree"]), l = o(t, ["channels", "channel"]), u = o(t, ["taxonomies", "taxonomy"]), d = o(t, [
|
|
25334
|
+
"blocks",
|
|
25335
|
+
"elements",
|
|
25336
|
+
"block"
|
|
25337
|
+
]), f = o(t, [
|
|
25338
|
+
"node-layouts",
|
|
25339
|
+
"layouts",
|
|
25340
|
+
"layout"
|
|
25341
|
+
]), p = o(t, ["block-layouts"]), m = o(t, ["field-sets", "fieldsets"]);
|
|
25342
|
+
if (a) {
|
|
25343
|
+
let e = Lx.readdirSync(i);
|
|
25344
|
+
for (let t of e) {
|
|
25345
|
+
let e = Rx.join(i, t);
|
|
25346
|
+
if (Lx.statSync(e).isDirectory()) {
|
|
25347
|
+
let t = Rx.join(e, "content");
|
|
25348
|
+
if (!Lx.existsSync(t)) {
|
|
25349
|
+
let n = Rx.join(e, "content", "definitions");
|
|
25350
|
+
Lx.existsSync(n) && (t = n);
|
|
25351
|
+
}
|
|
25352
|
+
Lx.existsSync(t) && (Object.assign(s, o(t, ["nodes", "node"])), Object.assign(c, o(t, ["trees", "tree"])), Object.assign(l, o(t, ["channels", "channel"])), Object.assign(u, o(t, ["taxonomies", "taxonomy"])), Object.assign(d, o(t, [
|
|
25353
|
+
"blocks",
|
|
25354
|
+
"elements",
|
|
25355
|
+
"block"
|
|
25356
|
+
])), Object.assign(f, o(t, [
|
|
25357
|
+
"node-layouts",
|
|
25358
|
+
"layouts",
|
|
25359
|
+
"layout"
|
|
25360
|
+
])), Object.assign(p, o(t, ["block-layouts"])), Object.assign(m, o(t, ["field-sets", "fieldsets"])));
|
|
25361
|
+
}
|
|
25362
|
+
}
|
|
25363
|
+
}
|
|
25274
25364
|
return {
|
|
25275
|
-
rawNodes:
|
|
25276
|
-
rawTrees:
|
|
25277
|
-
rawChannels:
|
|
25278
|
-
rawTaxonomies:
|
|
25279
|
-
rawBlocks:
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
]),
|
|
25284
|
-
rawNodeLayouts: i([
|
|
25285
|
-
"node-layouts",
|
|
25286
|
-
"layouts",
|
|
25287
|
-
"layout"
|
|
25288
|
-
]),
|
|
25289
|
-
rawBlockLayouts: i(["block-layouts"]),
|
|
25290
|
-
rawFieldSets: i(["field-sets", "fieldsets"])
|
|
25365
|
+
rawNodes: s,
|
|
25366
|
+
rawTrees: c,
|
|
25367
|
+
rawChannels: l,
|
|
25368
|
+
rawTaxonomies: u,
|
|
25369
|
+
rawBlocks: d,
|
|
25370
|
+
rawNodeLayouts: f,
|
|
25371
|
+
rawBlockLayouts: p,
|
|
25372
|
+
rawFieldSets: m
|
|
25291
25373
|
};
|
|
25292
25374
|
} catch (e) {
|
|
25293
25375
|
return console.error("❌ Exception inside readLocalFsBundle:", e), null;
|
|
@@ -25359,7 +25441,7 @@ function Zx(e) {
|
|
|
25359
25441
|
return Qx(t);
|
|
25360
25442
|
}
|
|
25361
25443
|
let t = qx();
|
|
25362
|
-
return t && Object.keys(t.rawNodes || {}).length > 0 ? Qx(t) : Qx(null);
|
|
25444
|
+
return t && (Object.keys(t.rawNodes || {}).length > 0 || Object.keys(t.rawBlocks || {}).length > 0) ? Qx(t) : Qx(null);
|
|
25363
25445
|
}
|
|
25364
25446
|
function Qx(e) {
|
|
25365
25447
|
if (!e) return {
|
|
@@ -27442,18 +27524,51 @@ $.use("*", t()), $.use("/media/*", t({
|
|
|
27442
27524
|
e.set("currentDomain", r);
|
|
27443
27525
|
let i = wb(e);
|
|
27444
27526
|
if (e.set("db", i), !vC) try {
|
|
27445
|
-
await Mb(i), vC = !0;
|
|
27527
|
+
await Mb(i, e.env?.BUNDLED_DEFINITIONS, e.env?.KAMA_EXTENSIONS), vC = !0;
|
|
27446
27528
|
} catch (e) {
|
|
27447
27529
|
console.error("❌ [CMS Server] Failed to initialize system tables on cold start:", e);
|
|
27448
27530
|
}
|
|
27449
27531
|
await t();
|
|
27450
|
-
}), $.route("/media", tC), $.use("/media/*", Pb()), $.
|
|
27532
|
+
}), $.route("/media", tC), $.use("/media/*", Pb()), $.get("/api/cms/ui-extensions", (e) => {
|
|
27533
|
+
let t = (e.env?.KAMA_EXTENSIONS || []).filter((e) => e.ui).map((e) => ({
|
|
27534
|
+
id: e.id,
|
|
27535
|
+
name: e.name || e.id,
|
|
27536
|
+
ui: e.ui
|
|
27537
|
+
}));
|
|
27538
|
+
return e.json(t);
|
|
27539
|
+
}), $.route("/api/cms/nodes", gx), $.route("/api/cms/nodes.json", gx), $.route("/api/cms/formcsv", gx), $.route("/api/cms/formcsv.json", gx), $.route("/api/cms/stats", iS), $.route("/api/cms/stats.json", iS), $.route("/api/cms", Ox), $.route("/api/cms/trees.json", Ox), $.route("/api/cms/channels", Mx), $.route("/api/cms/channels.json", Mx), $.route("/api/cms/taxonomies", Nx), $.route("/api/cms/taxonomies.json", Nx), $.route("/api/cms/schemas", $x), $.route("/api/cms/schemas.json", $x), $.route("/api/cms", tS), $.route("/api/admin/assets", aS), $.use("/api/v1/*", async (e, t) => {
|
|
27451
27540
|
let n = e.env?.FRONTEND_API_SECRET;
|
|
27452
27541
|
if (n) {
|
|
27453
27542
|
let t = e.req.header("Authorization"), r = e.req.header("x-api-key"), i = t?.startsWith("Bearer ") ? t.substring(7).trim() : r?.trim();
|
|
27454
27543
|
if (!i || i !== n) return e.json({ error: "Unauthorized: Invalid or missing API secret." }, 401);
|
|
27455
27544
|
} else console.warn("⚠️ [CMS Security] FRONTEND_API_SECRET is not set in .env. The API is completely exposed to the public.");
|
|
27456
27545
|
return t();
|
|
27546
|
+
}), $.use("/api/v1/ext/*", async (e, t) => {
|
|
27547
|
+
let n = e.env?.KAMA_EXTENSIONS || [], r = e.req.path.match(/^\/api\/v1\/ext\/([^/]+)/);
|
|
27548
|
+
if (r) {
|
|
27549
|
+
let t = r[1], i = n.find((e) => e.id === t);
|
|
27550
|
+
if (i && i.routes?.public) {
|
|
27551
|
+
let t = {
|
|
27552
|
+
...e.env,
|
|
27553
|
+
KAMA_DB: e.get("db")
|
|
27554
|
+
};
|
|
27555
|
+
return i.routes.public.fetch(e.req.raw, t, e.executionCtx);
|
|
27556
|
+
}
|
|
27557
|
+
}
|
|
27558
|
+
return t();
|
|
27559
|
+
}), $.use("/api/admin/ext/*", async (e, t) => {
|
|
27560
|
+
let n = e.env?.KAMA_EXTENSIONS || [], r = e.req.path.match(/^\/api\/admin\/ext\/([^/]+)/);
|
|
27561
|
+
if (r) {
|
|
27562
|
+
let t = r[1], i = n.find((e) => e.id === t);
|
|
27563
|
+
if (i && i.routes?.admin) {
|
|
27564
|
+
let t = {
|
|
27565
|
+
...e.env,
|
|
27566
|
+
KAMA_DB: e.get("db")
|
|
27567
|
+
};
|
|
27568
|
+
return i.routes.admin.fetch(e.req.raw, t, e.executionCtx);
|
|
27569
|
+
}
|
|
27570
|
+
}
|
|
27571
|
+
return t();
|
|
27457
27572
|
}), $.route("/api/v1/node", lS), $.route("/api/v1/nodes", kS), $.route("/api/v1/tree", NS), $.route("/api/v1/channel", HS), $.route("/api/v1/all-paths", oC), $.route("/api/v1/taxonomies", fC), $.doc("/api/openapi.json", {
|
|
27458
27573
|
openapi: "3.0.0",
|
|
27459
27574
|
info: {
|
package/dist/schema-compiler.js
CHANGED
|
@@ -30,7 +30,7 @@ async function f() {
|
|
|
30
30
|
if (n(f)) l = (await import(t(f).href)).compileDefinitions;
|
|
31
31
|
else if (n(p)) l = (await import(t(p).href)).compileDefinitions;
|
|
32
32
|
else try {
|
|
33
|
-
l = (await import("./dist-
|
|
33
|
+
l = (await import("./dist-Bta0OpXl.js")).compileDefinitions;
|
|
34
34
|
} catch (e) {
|
|
35
35
|
console.error("\x1B[31m ✖\x1B[0m \x1B[31mCould not resolve Kama Schema Engine:\x1B[0m", e), process.exit(1);
|
|
36
36
|
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# KAMA CMS — Extension Development Guide
|
|
2
|
+
|
|
3
|
+
KAMA features a **Drop-In Extension Architecture**. Extensions are built using native TypeScript, Vue, and YAML, and are automatically discovered, registered, and compiled into the Cloudflare Worker and Vite SPA at build time.
|
|
4
|
+
|
|
5
|
+
This guarantees zero core bloat, perfect native performance, and a completely isolated development environment.
|
|
6
|
+
|
|
7
|
+
## 1. How It Works (The Auto-Discovery Pipeline)
|
|
8
|
+
|
|
9
|
+
Extensions live in the `extensions/` directory at the root of your workspace.
|
|
10
|
+
|
|
11
|
+
When you run `npm run dev` or `npm run build:schema`, the KAMA CLI automatically:
|
|
12
|
+
|
|
13
|
+
1. Scans the `extensions/` folder.
|
|
14
|
+
2. Generates an `extension-registry.js` file.
|
|
15
|
+
3. Injects your Database tables into the SQLite/D1 initialization sequence.
|
|
16
|
+
4. Mounts your Hono API routes securely behind KAMA's authentication.
|
|
17
|
+
5. Injects your Vue `.vue` components into the SPA routing and sidebars.
|
|
18
|
+
6. Merges your extension's `.yaml` schemas natively into the core bundle.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Directory Structure
|
|
23
|
+
|
|
24
|
+
A standard extension uses the following structure:
|
|
25
|
+
|
|
26
|
+
kama-workspace/
|
|
27
|
+
├─ extensions/
|
|
28
|
+
│ └─ forms/ <-- Your Extension Folder
|
|
29
|
+
│ ├─ index.ts <-- The Manifest (Exports UI, DB, and Routes)
|
|
30
|
+
│ ├─ api/
|
|
31
|
+
│ │ ├─ admin.ts <-- Hono routes for the Admin UI
|
|
32
|
+
│ │ └─ public.ts <-- Hono routes for the Public API
|
|
33
|
+
│ ├─ ui/
|
|
34
|
+
│ │ ├─ SidebarPane.vue <-- Custom Left Drawer Vue Component
|
|
35
|
+
│ │ └─ Dashboard.vue <-- Custom Main Workspace Vue Component
|
|
36
|
+
│ └─ content/ <-- 🎯 Schema folder matching Core
|
|
37
|
+
│ └─ blocks/
|
|
38
|
+
│ └─ form-block.yaml <-- YAML Schemas (Auto-merged into KAMA)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. The Extension Manifest (`index.ts`)
|
|
43
|
+
|
|
44
|
+
Every extension must export a default object serving as the contract between your code and the KAMA core.
|
|
45
|
+
|
|
46
|
+
// extensions/forms/index.ts
|
|
47
|
+
import { adminFormsRouter } from "./api/admin.js";
|
|
48
|
+
import { publicFormsRouter } from "./api/public.js";
|
|
49
|
+
|
|
50
|
+
export default {
|
|
51
|
+
id: "kama-forms",
|
|
52
|
+
name: "Form Builder",
|
|
53
|
+
|
|
54
|
+
// 🎯 1. DATABASE HOOKS
|
|
55
|
+
// KAMA will automatically CREATE TABLE IF NOT EXISTS on boot.
|
|
56
|
+
db: {
|
|
57
|
+
tables: {
|
|
58
|
+
kama_forms_table: ` id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
+
form_id TEXT NOT NULL,
|
|
60
|
+
payload_json TEXT NOT NULL,
|
|
61
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
62
|
+
`,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// 🎯 2. API ROUTING HOOKS
|
|
67
|
+
// Handled natively by Hono. KAMA injects c.env.DB automatically.
|
|
68
|
+
routes: {
|
|
69
|
+
public: publicFormsRouter, // Mounted at: /api/ext/kama-forms/_
|
|
70
|
+
admin: adminFormsRouter, // Mounted at: /api/admin/ext/kama-forms/_
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// 🎯 3. FRONTEND UI HOOKS
|
|
74
|
+
ui: {
|
|
75
|
+
sidebar: [
|
|
76
|
+
{
|
|
77
|
+
id: "forms-nav",
|
|
78
|
+
label: "Forms",
|
|
79
|
+
icon: "Inbox", // Uses KAMA's global icon resolver
|
|
80
|
+
sidebarViewId: "forms-sidebar", // 🎯 Renders in the left drawer panel
|
|
81
|
+
viewId: "forms-dashboard", // 🎯 Renders in the main workspace canvas
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
|
|
85
|
+
// Map your view IDs to your Vue component chunks
|
|
86
|
+
views: [
|
|
87
|
+
{ id: "forms-sidebar", url: "/api/ext/kama-forms/ui-pane.js" },
|
|
88
|
+
{ id: "forms-dashboard", url: "/api/ext/kama-forms/ui.js" }
|
|
89
|
+
],
|
|
90
|
+
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 4. API Routing (`api/admin.ts`)
|
|
97
|
+
|
|
98
|
+
Extension APIs are built using standard Hono syntax.
|
|
99
|
+
|
|
100
|
+
**Important:** Your extension routes do not need to connect to the database manually. KAMA intercepts the request and injects the active database connection directly into the request context.
|
|
101
|
+
|
|
102
|
+
// extensions/forms/api/admin.ts
|
|
103
|
+
import { Hono } from "hono";
|
|
104
|
+
export const adminFormsRouter = new Hono();
|
|
105
|
+
|
|
106
|
+
adminFormsRouter.get("/submissions", async (c) => {
|
|
107
|
+
// Access the core database connection
|
|
108
|
+
const db = (c as any).env?.DB;
|
|
109
|
+
const formId = c.req.query("formId");
|
|
110
|
+
|
|
111
|
+
const stmt = db.prepare("SELECT \* FROM kama_forms_table WHERE form_id = ? ORDER BY createdAt DESC");
|
|
112
|
+
const results = typeof stmt.all === "function" ? await stmt.bind(formId).all() : [];
|
|
113
|
+
|
|
114
|
+
return c.json({ submissions: results?.results || results || [] });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 5. Frontend UI & Cross-Component Communication
|
|
120
|
+
|
|
121
|
+
KAMA enforces strict decoupling. Extensions shouldn't mutate core routing states directly. Instead, KAMA provides generic Event Buses and State Stores.
|
|
122
|
+
|
|
123
|
+
### Triggering Workspace Navigation
|
|
124
|
+
|
|
125
|
+
If a user clicks an item in your left `SidebarPane.vue` and you want to open your main `Dashboard.vue` in the primary workspace, dispatch a standard custom event. Core KAMA listens to this universally:
|
|
126
|
+
|
|
127
|
+
// Inside extensions/forms/ui/SidebarPane.vue
|
|
128
|
+
const openWorkspaceView = () => {
|
|
129
|
+
window.dispatchEvent(new CustomEvent('cms-open-extension-view', {
|
|
130
|
+
detail: { viewId: 'forms-dashboard' }
|
|
131
|
+
}));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
### Passing State Between Extension Components
|
|
135
|
+
|
|
136
|
+
To pass data (like a selected Form ID) from your Sidebar to your Dashboard, use the global `window.Kama` SDK:
|
|
137
|
+
|
|
138
|
+
**Setting State & Emitting Events (Sidebar):**
|
|
139
|
+
|
|
140
|
+
// Set state synchronously
|
|
141
|
+
(window as any).Kama?.state?.set('kama-forms', 'activeFormId', 'contact-form');
|
|
142
|
+
|
|
143
|
+
// Notify other components live
|
|
144
|
+
(window as any).Kama?.events?.emit('kama-forms:select-form', { formId: 'contact-form' });
|
|
145
|
+
|
|
146
|
+
**Listening for State (Dashboard):**
|
|
147
|
+
|
|
148
|
+
// Read initial state on mount
|
|
149
|
+
const initialId = (window as any).Kama?.state?.get('kama-forms', 'activeFormId');
|
|
150
|
+
|
|
151
|
+
// Listen for live changes
|
|
152
|
+
(window as any).Kama?.events?.on('kama-forms:select-form', (payload) => {
|
|
153
|
+
console.log("Form Selected:", payload.formId);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 6. Extension YAML Schemas (`content/`)
|
|
159
|
+
|
|
160
|
+
Extensions can bundle their own content schemas (Nodes, Blocks, Taxonomies). Place them in your extension's `content/` folder (e.g., `extensions/forms/content/blocks/form.yaml`).
|
|
161
|
+
|
|
162
|
+
Run `npm run build:schema` to compile them automatically into KAMA.
|
|
163
|
+
|
|
164
|
+
### 🎯 NEW: Dynamic API Dropdowns (`optionsEndpoint`)
|
|
165
|
+
|
|
166
|
+
KAMA's frontend features Universal Async Field fetching. You can define a `select` or `pills` field that dynamically pulls its options from your extension's API endpoint!
|
|
167
|
+
|
|
168
|
+
**Example YAML Block Definition:**
|
|
169
|
+
|
|
170
|
+
# extensions/forms/content/blocks/form-block.yaml
|
|
171
|
+
|
|
172
|
+
id: form
|
|
173
|
+
label: Dynamic Form
|
|
174
|
+
description: Embeds a form definition into the page.
|
|
175
|
+
category: Interactive
|
|
176
|
+
icon: ClipboardList
|
|
177
|
+
|
|
178
|
+
# You can inherit standard KAMA fieldsets!
|
|
179
|
+
|
|
180
|
+
fieldSets:
|
|
181
|
+
|
|
182
|
+
- access
|
|
183
|
+
|
|
184
|
+
fields:
|
|
185
|
+
|
|
186
|
+
# This dropdown populates automatically via your API!
|
|
187
|
+
|
|
188
|
+
formId:
|
|
189
|
+
label: Select Form
|
|
190
|
+
type: select
|
|
191
|
+
required: true
|
|
192
|
+
optionsEndpoint: /api/admin/ext/kama-forms/forms
|
|
193
|
+
optionsValueKey: id # Maps to the option value
|
|
194
|
+
optionsLabelKey: title # Maps to the visible label
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 7. Caching and Extension Updates
|
|
199
|
+
|
|
200
|
+
Because KAMA caches HTML at the Edge (Cloudflare CDN) and inside Worker KV:
|
|
201
|
+
|
|
202
|
+
If your extension API modifies public-facing content (e.g., approving a review, updating inventory), your extension's Hono router must manually trigger KAMA's cache purger to drop the cached Edge pages.
|
|
203
|
+
|
|
204
|
+
_(Note: KAMA's `EdgeCacheManager` is currently scoped to the core, so for now, extensions should rely on KAMA's internal APIs or direct node updates if immediate Edge Purging is required)._
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightsideoy/kama",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"@tiptap/vue-3": "^3.30.0",
|
|
55
55
|
"drizzle-orm": "^0.45.2",
|
|
56
56
|
"hono": "^4.13.2",
|
|
57
|
+
"js-yaml": "^5.5.0",
|
|
57
58
|
"locale-codes": "^1.3.1",
|
|
58
59
|
"lru-cache": "^11.5.2",
|
|
59
60
|
"lucide-vue-next": "^1.0.0",
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
"@iconify-json/heroicons": "^1.2.3",
|
|
69
70
|
"@iconify-json/lucide": "^1.2.123",
|
|
70
71
|
"@libsql/client": "^0.14.0",
|
|
72
|
+
"@types/js-yaml": "^4.0.9",
|
|
71
73
|
"@types/node": "^26.2.0",
|
|
72
74
|
"@types/sharp": "^0.31.1",
|
|
73
75
|
"@types/yaml": "^1.9.6",
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
// src/components/blocks/BlockRenderer.astro
|
|
3
|
+
|
|
4
|
+
import ImageBlock from "@/components/blocks/ImageBlock.astro";
|
|
5
|
+
import HeaderImage from "@/components/blocks/HeaderImage.astro";
|
|
6
|
+
import ColumnLayout from "@/components/blocks/ColumnLayout.astro";
|
|
7
|
+
import HtmlBlock from "@/components/blocks/HtmlBlock.astro";
|
|
8
|
+
import NodeList from "@/components/blocks/NodeList.astro"; // 🎯 NEW IMPORT
|
|
9
|
+
import { extractHtml } from "@/utils/tiptap";
|
|
10
|
+
|
|
11
|
+
interface Block {
|
|
12
|
+
instanceId: number;
|
|
13
|
+
blockId?: string;
|
|
14
|
+
componentId: string;
|
|
15
|
+
data: Record<string, any>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
blocks?: Block[];
|
|
20
|
+
contentMap?: Record<string, any[]>;
|
|
21
|
+
region?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { blocks = [], contentMap = {}, region = "main" } = Astro.props;
|
|
25
|
+
const safeBlocks = Array.isArray(blocks) ? blocks : [];
|
|
26
|
+
|
|
27
|
+
const visibleBlocks = safeBlocks.filter(b => {
|
|
28
|
+
if (!b || (!b.data && !b.componentId?.includes("column"))) return false;
|
|
29
|
+
|
|
30
|
+
const blockData = b.data || {};
|
|
31
|
+
if (
|
|
32
|
+
blockData.hidden === true ||
|
|
33
|
+
blockData.hidden === 1 ||
|
|
34
|
+
String(blockData.hidden) === "true"
|
|
35
|
+
) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const now = new Date();
|
|
40
|
+
if (blockData.startShowingAt) {
|
|
41
|
+
const start = new Date(blockData.startShowingAt);
|
|
42
|
+
if (!isNaN(start.getTime()) && now < start) return false;
|
|
43
|
+
}
|
|
44
|
+
if (blockData.stopShowingAt) {
|
|
45
|
+
const stop = new Date(blockData.stopShowingAt);
|
|
46
|
+
if (!isNaN(stop.getTime()) && now > stop) return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
});
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
visibleBlocks.map((block, index) => {
|
|
55
|
+
const data = block.data || {};
|
|
56
|
+
const blockId = block.blockId || data.blockId || "";
|
|
57
|
+
const componentId = (block.componentId || "").toLowerCase();
|
|
58
|
+
|
|
59
|
+
// Check for any column layout ending in column or two-column
|
|
60
|
+
if (componentId.includes("column")) {
|
|
61
|
+
return <ColumnLayout blockId={blockId} contentMap={contentMap} />;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
switch (componentId) {
|
|
65
|
+
// 🎯 ADDED NODE LIST CASES
|
|
66
|
+
case "node-list":
|
|
67
|
+
case "nodelist":
|
|
68
|
+
case "article-list":
|
|
69
|
+
case "content-feed": {
|
|
70
|
+
return <NodeList data={data} />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case "header":
|
|
74
|
+
case "headline":
|
|
75
|
+
case "header-block":
|
|
76
|
+
case "header_block": {
|
|
77
|
+
const headline = extractHtml(data.headline || data.title);
|
|
78
|
+
const subhead = extractHtml(data.subhead || data.body);
|
|
79
|
+
return (
|
|
80
|
+
<header class="cms-block cms-block-header">
|
|
81
|
+
{headline && <Fragment set:html={headline} />}
|
|
82
|
+
{subhead && <Fragment set:html={subhead} />}
|
|
83
|
+
</header>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
case "text":
|
|
88
|
+
case "text-block":
|
|
89
|
+
case "text_block": {
|
|
90
|
+
const bodyText = extractHtml(data.body || data.text || data.content);
|
|
91
|
+
return (
|
|
92
|
+
<div class="cms-block cms-block-text">
|
|
93
|
+
<Fragment set:html={bodyText} />
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case "html":
|
|
99
|
+
case "html-block":
|
|
100
|
+
case "html_block":
|
|
101
|
+
case "raw-html": {
|
|
102
|
+
return <HtmlBlock data={data} />;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
case "header-image":
|
|
106
|
+
case "header_image": {
|
|
107
|
+
return (
|
|
108
|
+
<HeaderImage data={data} region={region} isFirst={index === 0} />
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case "image":
|
|
113
|
+
case "image-block":
|
|
114
|
+
case "image_block":
|
|
115
|
+
case "single_image":
|
|
116
|
+
case "single-image": {
|
|
117
|
+
return <ImageBlock data={data} region={region} isFirst={index === 0} />;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
default:
|
|
121
|
+
return import.meta.env.DEV ? (
|
|
122
|
+
<div class="unhandled-block">
|
|
123
|
+
<p>Unhandled component: {block.componentId}</p>
|
|
124
|
+
</div>
|
|
125
|
+
) : null;
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
<style>
|
|
131
|
+
.cms-block {
|
|
132
|
+
margin-bottom: 1.5rem;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.unhandled-block {
|
|
136
|
+
padding: 12px 16px;
|
|
137
|
+
border: 1px dashed #cbd5e1;
|
|
138
|
+
background: #f8fafc;
|
|
139
|
+
color: #64748b;
|
|
140
|
+
font-size: 13px;
|
|
141
|
+
margin-bottom: 1.5rem;
|
|
142
|
+
}
|
|
143
|
+
</style>
|