@brightsideoy/kama 1.0.0-alpha.20 → 1.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/content/definitions/blocks/image-gallery.yaml +34 -0
  2. package/content/definitions/blocks/youtube.yaml +8 -0
  3. package/content/definitions/field-sets/access.yaml +10 -0
  4. package/content/definitions/field-sets/page-base.yaml +16 -1
  5. package/content/definitions/taxonomies/media-tag.yaml +15 -0
  6. package/definitions/editor/default.ts +15 -1
  7. package/dist/assets/AssetEditor-B4W7tV1a.js +1 -0
  8. package/dist/assets/AssetEditor-D9sz6bfN.css +1 -0
  9. package/dist/assets/CmsFieldTaxonomy-CVlKL-l2.css +1 -0
  10. package/dist/assets/CmsFieldTaxonomy-C_AwHDwY.js +1 -0
  11. package/dist/assets/{CmsHelp-DyPJLGGi.js → CmsHelp-BQkr9KyU.js} +1 -1
  12. package/dist/assets/{CmsPublisher-DNLwjirk.js → CmsPublisher-XSCPrp5L.js} +1 -1
  13. package/dist/assets/NodeEditor-4TsNvk2f.css +1 -0
  14. package/dist/assets/NodeEditor-DA5GRtRP.js +161 -0
  15. package/dist/assets/{TrashWorkspace-DgIg1v6t.js → TrashWorkspace-CATe46WF.js} +1 -1
  16. package/dist/assets/index-CY0VYSyt.css +1 -0
  17. package/dist/assets/index-DdG9Pozx.js +14 -0
  18. package/dist/assets/{plus-FQRsZBoJ.js → plus-CX_GHzXs.js} +1 -1
  19. package/dist/assets/useNodes-CqFlc72m.js +1 -0
  20. package/dist/{dist-D_Q1y4Kh.js → dist-3Xc2TTZw.js} +1117 -456
  21. package/dist/index.html +4 -4
  22. package/dist/index.js +1117 -456
  23. package/dist/schema-compiler.js +1 -1
  24. package/package.json +5 -5
  25. package/schema.sql +10 -0
  26. package/templates/astro-starter/src/components/global/NavigationMenu.astro +11 -3
  27. package/dist/assets/AssetEditor-D5j7pwcK.css +0 -1
  28. package/dist/assets/AssetEditor-Dt1zCR7_.js +0 -1
  29. package/dist/assets/NodeEditor-CiXQKXK1.js +0 -147
  30. package/dist/assets/NodeEditor-pZI3gu4a.css +0 -1
  31. package/dist/assets/index-DQ6_RmCI.js +0 -14
  32. package/dist/assets/index-DcKiaRjI.css +0 -1
  33. package/dist/assets/useNodes-D2-VxsxR.js +0 -1
  34. package/templates/astro-starter/old_src/components/blocks/BlockRenderer.astro +0 -143
  35. package/templates/astro-starter/old_src/components/blocks/ColumnLayout.astro +0 -67
  36. package/templates/astro-starter/old_src/components/blocks/HeaderImage.astro +0 -126
  37. package/templates/astro-starter/old_src/components/blocks/Hero.astro +0 -218
  38. package/templates/astro-starter/old_src/components/blocks/HtmlBlock.astro +0 -25
  39. package/templates/astro-starter/old_src/components/blocks/ImageBlock.astro +0 -91
  40. package/templates/astro-starter/old_src/components/blocks/NodeList.astro +0 -274
  41. package/templates/astro-starter/old_src/components/blocks/TextBlock.astro +0 -15
  42. package/templates/astro-starter/old_src/components/global/Breadcrumb.astro +0 -108
  43. package/templates/astro-starter/old_src/components/global/Footer.astro +0 -50
  44. package/templates/astro-starter/old_src/components/global/Header.astro +0 -171
  45. package/templates/astro-starter/old_src/components/global/MetaHead.astro +0 -266
  46. package/templates/astro-starter/old_src/components/global/NavigationMenu.astro +0 -413
  47. package/templates/astro-starter/old_src/components/ui/KamaImage.astro +0 -180
  48. package/templates/astro-starter/old_src/layouts/SkeletonLayout.astro +0 -56
  49. package/templates/astro-starter/old_src/layouts/StandardLayout.astro +0 -63
  50. package/templates/astro-starter/old_src/lib/cms.ts +0 -219
  51. package/templates/astro-starter/old_src/lib/media/kama-image-service.ts +0 -67
  52. package/templates/astro-starter/old_src/lib/media/media-proxy.ts +0 -148
  53. package/templates/astro-starter/old_src/lib/media/media-url.ts +0 -112
  54. package/templates/astro-starter/old_src/middleware.ts +0 -536
  55. package/templates/astro-starter/old_src/pages/404.astro +0 -46
  56. package/templates/astro-starter/old_src/pages/[...slug].astro +0 -311
  57. package/templates/astro-starter/old_src/pages/api/load-nodes.ts +0 -68
  58. package/templates/astro-starter/old_src/pages/api/partials/node-cards.astro +0 -101
  59. package/templates/astro-starter/old_src/pages/llms-full.txt.ts +0 -171
  60. package/templates/astro-starter/old_src/pages/llms.txt.ts +0 -113
  61. package/templates/astro-starter/old_src/pages/media/[...path].ts +0 -14
  62. package/templates/astro-starter/old_src/pages/partials/footer.astro +0 -69
  63. package/templates/astro-starter/old_src/pages/partials/nav.astro +0 -141
  64. package/templates/astro-starter/old_src/styles/standard-theme.css +0 -157
  65. package/templates/astro-starter/old_src/utils/tiptap.ts +0 -113
@@ -1,218 +0,0 @@
1
- ---
2
- // src/components/blocks/Hero.astro
3
- ---
4
-
5
- <section class="hero-container">
6
- <div class="hero-content">
7
- <!-- Sub-badge -->
8
- <div class="hero-badge">
9
- <span class="badge-dot"></span>
10
- Freeware for Education & NGOs
11
- </div>
12
-
13
- <!-- H1 Title with Slogan -->
14
- <h1 class="hero-title">KAMA – Headless CMS on Edge</h1>
15
-
16
- <!-- Subtitle -->
17
- <p class="hero-subtitle">
18
- Deploy sub-millisecond corporate web experiences with zero infrastructure
19
- overhead. Built natively on Astro, Hono, and Cloudflare Workers (D1/R2).
20
- </p>
21
-
22
- <!-- Call to Action Buttons -->
23
- <div class="hero-actions">
24
- <a href="/docs/quick-start" class="btn btn-primary">
25
- Get Started
26
- <svg
27
- xmlns="http://www.w3.org/2000/svg"
28
- width="16"
29
- height="16"
30
- viewBox="0 0 24 24"
31
- fill="none"
32
- stroke="currentColor"
33
- stroke-width="2"
34
- stroke-linecap="round"
35
- stroke-linejoin="round"
36
- class="btn-icon"
37
- ><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg
38
- >
39
- </a>
40
- <a
41
- href="https://github.com/brightside-ou/kama"
42
- target="_blank"
43
- rel="noopener noreferrer"
44
- class="btn btn-secondary"
45
- >
46
- View on GitHub
47
- </a>
48
- </div>
49
-
50
- <!-- Tech Stack Highlights -->
51
- <div class="hero-stack">
52
- <span class="stack-label">Powered by edge infrastructure:</span>
53
- <div class="stack-tags">
54
- <span class="tag">Cloudflare Workers</span>
55
- <span class="tag">D1 SQL</span>
56
- <span class="tag">R2 Storage</span>
57
- <span class="tag">Astro</span>
58
- <span class="tag">Hono</span>
59
- </div>
60
- </div>
61
- </div>
62
- </section>
63
-
64
- <style>
65
- .hero-container {
66
- padding: 64px 0 48px;
67
- background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
68
- border-bottom: 1px solid #e2e8f0;
69
- }
70
-
71
- .hero-content {
72
- max-width: 860px;
73
- margin: 0 auto;
74
- text-align: center;
75
- }
76
-
77
- .hero-badge {
78
- display: inline-flex;
79
- align-items: center;
80
- gap: 8px;
81
- padding: 6px 14px;
82
- background-color: #f0f9ff;
83
- border: 1px solid #bae6fd;
84
- color: #0369a1;
85
- font-size: 0.875rem;
86
- font-weight: 600;
87
- margin-bottom: 24px;
88
- }
89
-
90
- .badge-dot {
91
- width: 8px;
92
- height: 8px;
93
- background-color: #0284c7;
94
- border-radius: 50% !important;
95
- }
96
-
97
- .hero-title {
98
- font-size: 3rem;
99
- font-weight: 800;
100
- line-height: 1.15;
101
- color: #0f172a;
102
- letter-spacing: -0.03em;
103
- margin-top: 0;
104
- margin-bottom: 20px;
105
- }
106
-
107
- .hero-subtitle {
108
- font-size: 1.25rem;
109
- line-height: 1.6;
110
- color: #475569;
111
- max-width: 720px;
112
- margin: 0 auto 36px;
113
- }
114
-
115
- .hero-actions {
116
- display: flex;
117
- align-items: center;
118
- justify-content: center;
119
- gap: 16px;
120
- margin-bottom: 48px;
121
- }
122
-
123
- .btn {
124
- display: inline-flex;
125
- align-items: center;
126
- justify-content: center;
127
- gap: 8px;
128
- padding: 12px 28px;
129
- font-size: 1rem;
130
- font-weight: 600;
131
- text-decoration: none;
132
- transition: all 0.15s ease-in-out;
133
- }
134
-
135
- .btn-primary {
136
- background-color: #0284c7;
137
- color: #ffffff;
138
- }
139
-
140
- .btn-primary:hover {
141
- background-color: #0369a1;
142
- color: #ffffff;
143
- }
144
-
145
- .btn-secondary {
146
- background-color: #ffffff;
147
- color: #334155;
148
- border: 1px solid #cbd5e1;
149
- }
150
-
151
- .btn-secondary:hover {
152
- background-color: #f8fafc;
153
- color: #0f172a;
154
- border-color: #94a3b8;
155
- }
156
-
157
- .btn-icon {
158
- transition: transform 0.15s ease;
159
- }
160
-
161
- .btn-primary:hover .btn-icon {
162
- transform: translateX(4px);
163
- }
164
-
165
- .hero-stack {
166
- display: flex;
167
- flex-direction: column;
168
- align-items: center;
169
- gap: 12px;
170
- }
171
-
172
- .stack-label {
173
- font-size: 0.8125rem;
174
- text-transform: uppercase;
175
- letter-spacing: 0.05em;
176
- color: #64748b;
177
- font-weight: 600;
178
- }
179
-
180
- .stack-tags {
181
- display: flex;
182
- flex-wrap: wrap;
183
- justify-content: center;
184
- gap: 8px;
185
- }
186
-
187
- .tag {
188
- padding: 4px 10px;
189
- background-color: #f1f5f9;
190
- color: #334155;
191
- font-size: 0.8125rem;
192
- font-weight: 500;
193
- border: 1px solid #e2e8f0;
194
- }
195
-
196
- @media (max-width: 768px) {
197
- .hero-container {
198
- padding: 40px 0 32px;
199
- }
200
-
201
- .hero-title {
202
- font-size: 2.125rem;
203
- }
204
-
205
- .hero-subtitle {
206
- font-size: 1.05rem;
207
- }
208
-
209
- .hero-actions {
210
- flex-direction: column;
211
- width: 100%;
212
- }
213
-
214
- .btn {
215
- width: 100%;
216
- }
217
- }
218
- </style>
@@ -1,25 +0,0 @@
1
- ---
2
- // src/components/blocks/HtmlBlock.astro
3
-
4
- interface Props {
5
- data: {
6
- body?: string;
7
- html?: string;
8
- content?: string;
9
- text?: string;
10
- };
11
- }
12
-
13
- const { data } = Astro.props;
14
-
15
- // Fallback chain matching your schema's HTML field key ('body') and legacy aliases
16
- const rawHtml = data?.body || data?.html || data?.content || data?.text || "";
17
- ---
18
-
19
- {
20
- rawHtml && (
21
- <div class="cms-block cms-block-html">
22
- <Fragment set:html={rawHtml} />
23
- </div>
24
- )
25
- }
@@ -1,91 +0,0 @@
1
- ---
2
- // src/components/blocks/ImageBlock.astro
3
-
4
- import KamaImage from "@/components/ui/KamaImage.astro";
5
-
6
- interface Props {
7
- data: {
8
- image?: any;
9
- width?: number | string;
10
- height?: number | string;
11
- };
12
- region?: string;
13
- isFirst?: boolean;
14
- }
15
-
16
- const { data, region = "main", isFirst = false } = Astro.props;
17
-
18
- const img = data?.image;
19
- const isLcp = region === "header" || (region === "main" && isFirst);
20
-
21
- // 🎯 Detect column layout based on CMS region names
22
- let columnCount = 1;
23
- if (region.includes("_")) {
24
- columnCount = 2;
25
- if (region.includes("_middle") || region.includes("_col3")) columnCount = 3;
26
- if (region.includes("_col4")) columnCount = 4;
27
- }
28
-
29
- // 🎯 Column Config Lookup (Target width, aligned CDN breakpoint widths, sizes)
30
- const COLUMN_CONFIGS: Record<
31
- number,
32
- { targetWidth: number; widths: number[]; sizes: string }
33
- > = {
34
- 1: {
35
- targetWidth: 1352,
36
- widths: [768, 1352, 1920, 2560],
37
- sizes: "(max-width: 1399px) calc(100vw - 48px), 1352px",
38
- },
39
- 2: {
40
- targetWidth: 660,
41
- widths: [640, 768, 1024, 1352],
42
- sizes:
43
- "(max-width: 767px) calc(100vw - 48px), (max-width: 1399px) calc((100vw - 80px) / 2), 660px",
44
- },
45
- 3: {
46
- targetWidth: 429,
47
- widths: [320, 640, 768, 1024],
48
- sizes:
49
- "(max-width: 767px) calc(100vw - 48px), (max-width: 1399px) calc((100vw - 112px) / 3), 429px",
50
- },
51
- 4: {
52
- targetWidth: 314,
53
- widths: [320, 640, 768],
54
- sizes:
55
- "(max-width: 767px) calc(100vw - 48px), (max-width: 1399px) calc((100vw - 144px) / 4), 314px",
56
- },
57
- };
58
-
59
- const layout = COLUMN_CONFIGS[columnCount] || COLUMN_CONFIGS[1];
60
- ---
61
-
62
- {
63
- img && (
64
- <div class="cms-block cms-block-image">
65
- <KamaImage
66
- image={img}
67
- sizes={layout.sizes}
68
- desktopWidths={layout.widths}
69
- idealWidth={layout.targetWidth}
70
- class="free-crop-image"
71
- loading={isLcp ? "eager" : "lazy"}
72
- fetchpriority={isLcp ? "high" : "auto"}
73
- />
74
- </div>
75
- )
76
- }
77
-
78
- <style>
79
- .cms-block-image {
80
- width: 100%;
81
- margin: 0 auto 1.5rem auto;
82
- }
83
-
84
- :global(.free-crop-image) {
85
- width: 100%;
86
- height: auto;
87
- object-fit: contain;
88
- display: block;
89
- border-radius: var(--cms-radius-md, 0.5rem);
90
- }
91
- </style>
@@ -1,274 +0,0 @@
1
- ---
2
- // src/components/blocks/NodeList.astro
3
- import KamaImage from "@/components/ui/KamaImage.astro";
4
-
5
- interface Props {
6
- data: Record<string, any>;
7
- }
8
-
9
- const { data } = Astro.props;
10
- const items = data.initialItems || [];
11
- const listTitle = data.listTitle || "";
12
- ---
13
-
14
- <div class="cms-block node-list-wrapper">
15
- {listTitle && <h2 class="node-list-title">{listTitle}</h2>}
16
-
17
- {
18
- items.length === 0 ? (
19
- <p class="empty-state">No items found.</p>
20
- ) : (
21
- <div class="node-grid" id={`grid-${data.blockId}`}>
22
- {items.map((item: any) => (
23
- <article class="node-card">
24
- <a href={`/${item.slug}`} class="node-card-link">
25
- {/* 🎯 ARCHITECTURAL FIX: Use KamaImage for initial load optimization */}
26
- {item.image ? (
27
- <KamaImage
28
- image={item.image}
29
- sizes="(max-width: 639px) 100vw, (max-width: 1023px) 50vw, 33vw"
30
- desktopWidths={[320, 640, 768, 1024]}
31
- idealWidth={429}
32
- loading="lazy"
33
- />
34
- ) : (
35
- <div class="image-placeholder" />
36
- )}
37
- <div class="node-card-content">
38
- <h3 class="node-card-title">{item.title}</h3>
39
- {item.createdAt && (
40
- <time datetime={item.createdAt} class="node-card-date">
41
- {new Date(item.createdAt).toLocaleDateString("en-GB", {
42
- day: "numeric",
43
- month: "short",
44
- year: "numeric",
45
- })}
46
- </time>
47
- )}
48
- {item.excerpt && (
49
- <p class="node-card-excerpt">{item.excerpt}</p>
50
- )}
51
- </div>
52
- </a>
53
- </article>
54
- ))}
55
- </div>
56
- )
57
- }
58
-
59
- {
60
- data.loadMoreUrl && (
61
- <div class="load-more-container">
62
- <button
63
- class="load-more-btn"
64
- data-url={data.loadMoreUrl}
65
- data-target={`grid-${data.blockId}`}
66
- >
67
- Load More
68
- </button>
69
- </div>
70
- )
71
- }
72
- </div>
73
-
74
- <style is:global>
75
- .node-list-wrapper {
76
- margin: 2rem 0;
77
- }
78
-
79
- .node-list-title {
80
- font-size: 1.5rem;
81
- font-weight: 700;
82
- margin-bottom: 1.5rem;
83
- color: #1e293b;
84
- }
85
-
86
- .node-list-wrapper .empty-state {
87
- color: #64748b;
88
- font-style: italic;
89
- }
90
-
91
- .node-grid {
92
- display: grid;
93
- grid-template-columns: repeat(1, 1fr);
94
- gap: 1.5rem;
95
- }
96
-
97
- @media (min-width: 640px) {
98
- .node-grid {
99
- grid-template-columns: repeat(2, 1fr);
100
- }
101
- }
102
-
103
- @media (min-width: 1024px) {
104
- .node-grid {
105
- grid-template-columns: repeat(3, 1fr);
106
- }
107
- }
108
-
109
- .node-card {
110
- border: 1px solid #e2e8f0;
111
- border-radius: 8px;
112
- overflow: hidden;
113
- background: #fff;
114
- transition:
115
- transform 0.2s ease,
116
- box-shadow 0.2s ease;
117
- }
118
-
119
- .node-card:hover {
120
- transform: translateY(-2px);
121
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
122
- }
123
-
124
- .node-card-link {
125
- text-decoration: none;
126
- color: inherit;
127
- display: flex;
128
- flex-direction: column;
129
- height: 100%;
130
- }
131
-
132
- /* 🎯 Because of is:global, this styling will automatically apply to <KamaImage> outputs! */
133
- .node-card img,
134
- .node-card .image-placeholder {
135
- width: 100%;
136
- height: auto; /* 🎯 FIX: Allows tall images to display their natural height */
137
- object-fit: cover;
138
- background: #f1f5f9;
139
- }
140
-
141
- .node-card .image-placeholder {
142
- aspect-ratio: 16 / 9; /* Keep the placeholder a rectangle if there is no image at all */
143
- }
144
- .node-card-content {
145
- padding: 1.25rem;
146
- display: flex;
147
- flex-direction: column;
148
- flex-grow: 1;
149
- }
150
-
151
- .node-card-title {
152
- font-size: 1.125rem;
153
- font-weight: 600;
154
- margin: 0 0 0.5rem 0;
155
- color: #0f172a;
156
- line-height: 1.4;
157
- }
158
-
159
- .node-card-date {
160
- font-size: 0.875rem;
161
- color: #64748b;
162
- margin-bottom: 0.75rem;
163
- }
164
-
165
- .node-card-excerpt {
166
- font-size: 0.9375rem;
167
- color: #475569;
168
- margin: 0;
169
- line-height: 1.5;
170
- display: -webkit-box;
171
- -webkit-line-clamp: 3;
172
- -webkit-box-orient: vertical;
173
- overflow: hidden;
174
- }
175
-
176
- .load-more-container {
177
- margin-top: 2rem;
178
- text-align: center;
179
- }
180
-
181
- .load-more-btn {
182
- padding: 0.75rem 1.5rem;
183
- background: #0f172a;
184
- color: #fff;
185
- border: none;
186
- border-radius: 6px;
187
- font-weight: 600;
188
- cursor: pointer;
189
- transition: background 0.2s;
190
- }
191
-
192
- .load-more-btn:hover {
193
- background: #334155;
194
- }
195
- </style>
196
-
197
- <script>
198
- function initLoadMoreButtons() {
199
- const buttons = document.querySelectorAll(".load-more-btn:not(.is-bound)");
200
-
201
- buttons.forEach(btn => {
202
- btn.classList.add("is-bound");
203
-
204
- btn.addEventListener("click", async e => {
205
- const targetBtn = e.currentTarget as HTMLButtonElement;
206
- const urlString = targetBtn.getAttribute("data-url");
207
- const targetId = targetBtn.getAttribute("data-target");
208
-
209
- if (!urlString || !targetId) return;
210
-
211
- const grid = document.getElementById(targetId);
212
- if (!grid) return;
213
-
214
- const urlObj = new URL(urlString, window.location.origin);
215
- // 🎯 ARCHITECTURAL FIX: Route to the new HTML Partial Endpoint
216
- const proxyUrl = `/api/partials/node-cards${urlObj.search}`;
217
-
218
- targetBtn.textContent = "Loading...";
219
- targetBtn.disabled = true;
220
-
221
- try {
222
- const res = await fetch(proxyUrl);
223
-
224
- if (!res.ok) {
225
- throw new Error(`HTTP error! status: ${res.status}`);
226
- }
227
-
228
- // 1. Read metadata from headers sent by our partial endpoint
229
- const rawCountStr = res.headers.get("X-Consumed-Rows");
230
- const limitStr = res.headers.get("X-Limit");
231
- const consumedRows = parseInt(rawCountStr || "0", 10);
232
- const limit = parseInt(limitStr || "12", 10);
233
-
234
- // 2. Extract and render the raw HTML
235
- const htmlSnippet = await res.text();
236
-
237
- if (htmlSnippet.trim().length > 0) {
238
- grid.insertAdjacentHTML("beforeend", htmlSnippet);
239
- }
240
-
241
- // 3. Handle Pagination
242
- if (consumedRows >= limit && limit > 0) {
243
- const currentOffset = parseInt(
244
- urlObj.searchParams.get("offset") || "0",
245
- 10,
246
- );
247
- urlObj.searchParams.set(
248
- "offset",
249
- (currentOffset + limit).toString(),
250
- );
251
-
252
- targetBtn.setAttribute("data-url", urlObj.pathname + urlObj.search);
253
- targetBtn.textContent = "Load More";
254
- targetBtn.disabled = false;
255
- } else {
256
- if (targetBtn.parentElement)
257
- targetBtn.parentElement.style.display = "none";
258
- }
259
- } catch (err) {
260
- console.error("Failed to load more items:", err);
261
- targetBtn.textContent = "Error. Try again";
262
- targetBtn.disabled = false;
263
- }
264
- });
265
- });
266
- }
267
-
268
- if (document.readyState === "loading") {
269
- document.addEventListener("DOMContentLoaded", initLoadMoreButtons);
270
- } else {
271
- initLoadMoreButtons();
272
- }
273
- document.addEventListener("astro:page-load", initLoadMoreButtons);
274
- </script>
@@ -1,15 +0,0 @@
1
- ---
2
- // src/components/blocks/TextBlock.astro
3
-
4
- interface Props {
5
- data: {
6
- text?: string;
7
- content?: string;
8
- };
9
- }
10
- const { data } = Astro.props;
11
- ---
12
-
13
- <div class="block-text">
14
- <Fragment set:html={data.text || data.content || ""} />
15
- </div>