macron-cms 3.0.0 → 5.0.0
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.
- checksums.yaml +4 -4
- data/README.md +338 -25
- data/app/assets/stylesheets/macron/cms/application.css +1 -1
- data/app/controllers/concerns/macron/cms/api_serialization.rb +79 -0
- data/app/controllers/concerns/macron/cms/slug_redirectable.rb +27 -0
- data/app/controllers/macron/cms/api/v1/article_contents_controller.rb +221 -0
- data/app/controllers/macron/cms/api/v1/articles_controller.rb +140 -66
- data/app/controllers/macron/cms/api/v1/content_types_controller.rb +3 -1
- data/app/controllers/macron/cms/article_contents_controller.rb +44 -10
- data/app/controllers/macron/cms/articles_controller.rb +4 -3
- data/app/controllers/macron/cms/content_types_controller.rb +2 -0
- data/app/controllers/macron/cms/dashboard_controller.rb +1 -1
- data/app/helpers/macron/cms/content_helper.rb +1 -1
- data/app/helpers/macron/cms/fields_helper.rb +14 -0
- data/app/models/macron/cms/article.rb +45 -15
- data/app/models/macron/cms/article_content.rb +24 -0
- data/app/models/macron/cms/content.rb +27 -0
- data/app/services/macron/cms/inline_asset_attacher.rb +70 -0
- data/app/services/macron/cms/remote_asset_attacher.rb +16 -6
- data/app/views/macron/cms/articles/_content_blocks_section.html.erb +494 -199
- data/app/views/macron/cms/articles/_form.html.erb +10 -2
- data/app/views/macron/cms/articles/_table.html.erb +3 -3
- data/app/views/macron/cms/articles/show.html.erb +3 -1
- data/app/views/macron/cms/content_types/index.html.erb +30 -0
- data/app/views/macron/cms/content_types/show.html.erb +34 -0
- data/config/routes.rb +12 -1
- data/db/migrate/20261029000001_add_convertible_to_to_macron_cms_contents.rb +5 -0
- data/db/migrate/20261029120001_add_featured_image_to_macron_cms_contents.rb +5 -0
- data/db/migrate/20261029120002_add_featured_image_blob_to_macron_cms_articles.rb +8 -0
- data/db/migrate/20261029140000_create_friendly_id_slugs.rb +26 -0
- data/db/migrate/20261029150000_remove_name_from_macron_cms_articles.rb +6 -0
- data/db/migrate/20261029160000_add_published_at_to_macron_cms_articles.rb +14 -0
- data/db/seeds.rb +3 -2
- data/lib/macron/cms/version.rb +1 -1
- data/lib/tasks/macron/cms_tasks.rake +6 -0
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d33f2ffc21a60f2fed407e0b1aac793f203a304203a6d58c0a5c87680e7d33e3
|
|
4
|
+
data.tar.gz: 27faeca2f7e1f4dc031e9786ec1739f19b59e2d088a0a7f7f5cdcc1f43bdb9b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16aabb8fa8b2be75dc01a49f0c5e2267d85266a2c80566d6730095e8300df6cdb544962a1184a07fae8632145893f9a6307395762a1e16396b6fabcb402fef4b
|
|
7
|
+
data.tar.gz: 8ce4de32d67626c86c5025bc78a07f1598a8cfc26d5ce3e980cf00581b8484b6976f0e9dfea91b8ea97f3df17c7542779c319151f186664256a9accdcc5c804d
|
data/README.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# MacronCMS
|
|
2
2
|
|
|
3
|
-
[](#version)
|
|
4
4
|
[](https://rubyonrails.org)
|
|
5
|
+
[](https://react.dev)
|
|
6
|
+
[](https://threejs.org)
|
|
7
|
+
[](https://github.com/macron1-automations/macron-cms-mcp)
|
|
5
8
|
[](MIT-LICENSE)
|
|
6
9
|
|
|
7
10
|
A simple, flexible CMS engine for Ruby on Rails, designed for building highly interactive content with dynamic content blocks, declarative schemas, and ViewComponent widgets.
|
|
@@ -18,13 +21,16 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
18
21
|
|
|
19
22
|
### 🛠️ Dedicated Admin Dashboard (`/cms`)
|
|
20
23
|
- **Modern Zinc Aesthetic**: Clean, responsive monochrome layout with sticky sidebar navigation, top breadcrumbs, metric cards, quick-action buttons, and flash notification banners.
|
|
21
|
-
- **Article CRUD &
|
|
24
|
+
- **Article CRUD & Canonical Slugs**: Manage articles (`Macron::Cms::Article`) out-of-the-box with dedicated create, edit, show, and delete interfaces. Automatic slug generation via `friendly_id` produces clean SEO URLs.
|
|
25
|
+
- **Publication Scheduling & Custom Chronology**: Configure exact `published_at` timestamps with an integrated datetime picker. Articles default to descending publication order (`published_at: :desc, created_at: :desc`), enabling scheduled publishing and custom sorting.
|
|
26
|
+
- **Slug History & Canonical 301 Redirects**: Automatic FriendlyId slug history tracking. Host controllers use the `Macron::Cms::SlugRedirectable` concern to automatically issue permanent 301 redirects when an article is accessed via a previous slug, maintaining SEO equity and preventing 404s.
|
|
22
27
|
- **Deletion Safeguards**: Browser confirmation dialogs on delete actions with article titles to prevent accidental deletion.
|
|
23
28
|
- **Host App Integration**: Quick links between the CMS admin console and public-facing host views.
|
|
24
29
|
|
|
25
30
|
### 🧱 Dynamic Article Content Blocks & In-Place Inserters
|
|
26
31
|
- **Multiple Blocks Per Article**: Associate an arbitrary series of content blocks with an article (`Macron::Cms::ArticleContent`). Add multiple blocks of the same content type (e.g. headings, markdown blocks, images) with distinct values.
|
|
27
32
|
- **In-Place Block Inserters**: Add new blocks directly above, between, or below existing blocks on the Article management screen with one click.
|
|
33
|
+
- **Declarative In-Place Block Conversion**: Convert blocks between compatible content types directly in-place (e.g. `image` <-> `three_js_image`) via the `convertible_to` schema definition. Conversions update records in place without duplicating Active Storage file attachments, and preserve existing and dormant JSONB `payload` attributes non-destructively.
|
|
28
34
|
- **Inline Turbo Stream Updates**: Add, edit, and reorder content blocks with instantaneous Turbo Stream updates—no full-page reloads.
|
|
29
35
|
- **Dynamic Field Generation**: Field controls are generated dynamically based on the content type schema and validated before saving into the block's `payload` JSONB column.
|
|
30
36
|
- **8 Extensible Field Kinds**:
|
|
@@ -38,6 +44,11 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
38
44
|
- `latex`: CodeMirror LaTeX editor with `stex` syntax mode and KaTeX mathematical equation preview.
|
|
39
45
|
- **File Attachment Purging**: Remove or replace existing Active Storage attachments directly from the block editor with a dedicated purge checkbox, keeping payloads clean without recreating blocks.
|
|
40
46
|
|
|
47
|
+
### 🖼️ Automatic Featured Image Bubbling
|
|
48
|
+
- **Declarative Tagging**: Tag image-bearing content types with `featured_image: true` in their YAML schemas (`config/content_types/*.yml`).
|
|
49
|
+
- **Write-Time Image Bubbling**: Active Storage image attachments on eligible blocks automatically bubble up to `article.featured_image_blob` upon saving or deleting blocks, pointing to the first attached image in block sort order.
|
|
50
|
+
- **Zero-Join Editorial Streams**: Eager-load and render article feeds or homepage streams (`@articles = Macron::Cms::Article.includes(:featured_image_blob)`) and access `article.featured_image` without expensive multi-table joins across heterogeneous content blocks.
|
|
51
|
+
|
|
41
52
|
### 🎨 Article Background & Typography Customization
|
|
42
53
|
- **Custom Background Colors & Images**: Configure an optional background color or attach a background image directly to any article via Active Storage, with live image previews and one-click purge.
|
|
43
54
|
- **CSS Background Display Controls**: Fine-tune background display settings per article:
|
|
@@ -52,12 +63,15 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
52
63
|
- **CodeMirror Markdown & LaTeX Editors**: Integrated CodeMirror editors in the CMS admin for writing Markdown content and raw LaTeX expressions with syntax highlighting (`stex` mode).
|
|
53
64
|
- **Fast KaTeX Math Rendering**: Embedded KaTeX engine renders complex LaTeX equations, mathematical notation, and symbols instantaneously in both admin previews and front-end layouts.
|
|
54
65
|
- **Formatted Markdown Previews**: Formatted GitHub Flavored Markdown (GFM) previews rendered directly on the article management page.
|
|
55
|
-
- **Sandboxed Embeds**: Render third-party media and raw HTML snippets safely inside isolated `<iframe>` elements using `srcdoc` to prevent Active Storage download blocks and script collisions.
|
|
66
|
+
- **Sandboxed Embeds with Configurable Heights**: Render third-party media and raw HTML snippets safely inside isolated `<iframe>` elements using `srcdoc` to prevent Active Storage download blocks and script collisions. Configure display heights (`sm`: 240px, `md`: 320px, `lg`: 480px, `xl`: 640px, `2xl`: 800px) alongside responsive widths (`25%` to `100%`).
|
|
67
|
+
- **Native Fullscreen Maximize**: Embed containers feature a floating toggle button powered by the HTML5 Fullscreen API (`widgets/embed.js`), allowing users to expand embedded presentations, charts, or maps to full screen.
|
|
56
68
|
- **Responsive Media Controls**: Configure image and embed widgets with responsive width options (`25%`, `33%`, `50%`, `66%`, `75%`, `100%`) and alignment (`left`, `center`, `right`).
|
|
57
69
|
|
|
58
|
-
### ↕️ Drag-and-Drop &
|
|
70
|
+
### ↕️ Drag-and-Drop, Compact View & Batch Reordering
|
|
59
71
|
- **HTML5 Drag-and-Drop**: Easily re-arrange article content blocks on the Article show page with immediate position updates and asynchronous persistence.
|
|
60
72
|
- **Accessible Move Up / Down Buttons**: Keyboard- and mobile-friendly reordering buttons on each block card as a built-in fallback.
|
|
73
|
+
- **High-Density Compact View**: Segmented toggle (`?view=compact`) for rapid content reordering across long articles. Compact mode hides in-between inserters, displays the primary field defined in the schema (`primary_field`), and clamps text output to two lines for streamlined scanning.
|
|
74
|
+
- **Shift+Click Multi-Selection & Multi-Drag**: Select multiple blocks in compact view using checkboxes or Shift+Click contiguous range selection with an active selection count badge. Dragging any selected card moves the entire group contiguously to the drop target.
|
|
61
75
|
|
|
62
76
|
### 🧩 Declarative Content Types & Generators
|
|
63
77
|
- **YAML-Defined Schemas**: Define structured content types with typed fields (`name:kind`) stored directly in source control (`config/content_types/*.yml`).
|
|
@@ -69,16 +83,27 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
69
83
|
- **Seamless Rendering Helpers**: Render all article content blocks ordered by `sort_order` with `<%= cms_render_article_contents(@article) %>`, or render individual blocks via `<%= cms_render_widget(article_content) %>`.
|
|
70
84
|
- **Informative Error Handling**: Raises `Macron::Cms::MissingWidgetError` if a content type does not have a corresponding `Widgets::<Name>Component` in the host application.
|
|
71
85
|
|
|
86
|
+
### ⚡ High-Performance Preloading & O(1) Queries
|
|
87
|
+
- **Zero N+1 Query Overhead**: Built-in `ArticleContent.for_display` scope combines Active Storage's `with_attached_files` with `.includes(:content)` to preload associations in constant-time batches.
|
|
88
|
+
- **Bounded Query Execution**: Admin show views, high-frequency Turbo Stream inserts/reorders, and public host rendering (`cms_render_article_contents`) run in bounded O(1) database queries regardless of article length or attached media count.
|
|
89
|
+
|
|
72
90
|
### 🔒 Devise Authentication & Authorization
|
|
73
91
|
- **Configurable Access Guard**: Protect the `/cms` engine namespace behind host application authentication.
|
|
74
92
|
- **Role Customization**: Configure custom user models (`user_class`) and admin flags (`admin_flag`) via initializers.
|
|
75
93
|
|
|
76
|
-
### 🤖 REST API &
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
79
|
-
- **
|
|
94
|
+
### 🤖 Granular REST API & Headless Operations (`/cms/api/v1`)
|
|
95
|
+
- **Granular Block-Level Operations**: Full REST control over article blocks under `/cms/api/v1/articles/:article_id/content_blocks`—create with relative positioning (`insert_at`), update payloads, move blocks (`PATCH :id/move`), convert types in place (`PATCH :id/convert`), delete blocks, and batch reorder (`PATCH reorder`).
|
|
96
|
+
- **Inline & Remote Asset Ingestion**: Attach files directly using base64 data payloads (`file_upload`) or provide remote URLs (`image_url` / `upload_url`) with automatic MIME detection and background Active Storage attachment.
|
|
97
|
+
- **Atomic Nested Import & Upsert**: Create or update articles and complete sequences of typed content blocks in a single atomic transaction with `upsert: true`.
|
|
98
|
+
- **Article Lifecycle Control**: Programmatically manage article metadata, publishing timestamps (`published_at`), and deletion via `PATCH` and `DELETE /cms/api/v1/articles/:id`.
|
|
99
|
+
- **Historical Slug Resolution**: Requests referencing previous slugs resolve with `200 OK` and return the current canonical slug in the response, preventing integration breaks.
|
|
80
100
|
- **Content Type Schema Introspection**: Query `GET /cms/api/v1/content_types` dynamically so AI agents know exact fields and widget types available.
|
|
81
101
|
- **Bearer Token Auth**: Secure endpoints with `Authorization: Bearer <token>` or `X-API-Key`.
|
|
102
|
+
- **Full API Documentation**: Complete endpoint specifications, payload schemas, and response models in [`docs/api.md`](docs/api.md).
|
|
103
|
+
|
|
104
|
+
### 🧠 Model Context Protocol (MCP) Server
|
|
105
|
+
- **Turnkey AI Pair-Programming**: Connect Claude Desktop, Cursor, Antigravity, or autonomous agents to MacronCMS via the official [macron-cms-mcp](https://github.com/macron1-automations/macron-cms-mcp) server.
|
|
106
|
+
- **Native AI Tool Calling**: Enables LLMs to dynamically inspect schemas, draft articles, insert/edit blocks, reorder sections, and upload media assets natively using structured tool definitions.
|
|
82
107
|
|
|
83
108
|
### 🎨 Self-Contained Tailwind CSS v4
|
|
84
109
|
- **Zero Host Setup Required**: MacronCMS packages its own standalone, precompiled Tailwind CSS v4 bundle (`app/assets/stylesheets/macron/cms/application.css`). Host applications do not need Tailwind installed to display the CMS admin interface.
|
|
@@ -165,7 +190,7 @@ This generates:
|
|
|
165
190
|
|
|
166
191
|
---
|
|
167
192
|
|
|
168
|
-
### 2. Supported Field Kinds
|
|
193
|
+
### 2. Supported Field Kinds & Schema Options
|
|
169
194
|
|
|
170
195
|
| Kind | Description | Config Options | Accessor in Widget |
|
|
171
196
|
|---|---|---|---|
|
|
@@ -178,6 +203,18 @@ This generates:
|
|
|
178
203
|
| `json` | Formatted JSON editor | — | `field_name` |
|
|
179
204
|
| `latex` | Mathematical formula editor with CodeMirror & KaTeX | — | `field_name` |
|
|
180
205
|
|
|
206
|
+
#### Schema Configuration Options
|
|
207
|
+
|
|
208
|
+
In addition to fields, content type schemas support top-level configuration options:
|
|
209
|
+
|
|
210
|
+
| Option | Type | Description |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `name` | String | Human-readable name of the content type |
|
|
213
|
+
| `description` | String | Detailed description displayed in the CMS admin dashboard |
|
|
214
|
+
| `featured_image` | Boolean | Set to `true` to declare this block as an image source for article featured image bubbling |
|
|
215
|
+
| `convertible_to` | Array of strings | Target content types eligible for declarative in-place conversion |
|
|
216
|
+
| `primary_field` | String | Designation of the field displayed as the primary preview in compact block view |
|
|
217
|
+
|
|
181
218
|
---
|
|
182
219
|
|
|
183
220
|
### 3. Schema Examples
|
|
@@ -215,6 +252,9 @@ fields:
|
|
|
215
252
|
```yaml
|
|
216
253
|
name: Image
|
|
217
254
|
description: Image with configurable responsive width, alignment, and optional caption
|
|
255
|
+
featured_image: true
|
|
256
|
+
convertible_to:
|
|
257
|
+
- three_js_image
|
|
218
258
|
fields:
|
|
219
259
|
- name: file
|
|
220
260
|
kind: file
|
|
@@ -253,6 +293,19 @@ fields:
|
|
|
253
293
|
- 66%
|
|
254
294
|
- 75%
|
|
255
295
|
- 100%
|
|
296
|
+
- name: height
|
|
297
|
+
kind: dropdown
|
|
298
|
+
options:
|
|
299
|
+
- name: Small (240px)
|
|
300
|
+
value: sm
|
|
301
|
+
- name: Medium (320px)
|
|
302
|
+
value: md
|
|
303
|
+
- name: Large (480px)
|
|
304
|
+
value: lg
|
|
305
|
+
- name: Extra Large (640px)
|
|
306
|
+
value: xl
|
|
307
|
+
- name: Very Large (800px)
|
|
308
|
+
value: 2xl
|
|
256
309
|
- name: align
|
|
257
310
|
kind: dropdown
|
|
258
311
|
options:
|
|
@@ -346,6 +399,72 @@ fields:
|
|
|
346
399
|
value: strong
|
|
347
400
|
```
|
|
348
401
|
|
|
402
|
+
#### Interactive 3D Image Effects (`config/content_types/three_js_image.yml`)
|
|
403
|
+
```yaml
|
|
404
|
+
featured_image: true
|
|
405
|
+
convertible_to:
|
|
406
|
+
- image
|
|
407
|
+
fields:
|
|
408
|
+
- name: upload
|
|
409
|
+
kind: file
|
|
410
|
+
- name: size
|
|
411
|
+
kind: dropdown
|
|
412
|
+
options:
|
|
413
|
+
- name: Small (33%)
|
|
414
|
+
value: sm
|
|
415
|
+
- name: Medium (50%)
|
|
416
|
+
value: md
|
|
417
|
+
- name: Large (75%)
|
|
418
|
+
value: lg
|
|
419
|
+
- name: Full Width (100%)
|
|
420
|
+
value: full
|
|
421
|
+
- name: effect
|
|
422
|
+
kind: dropdown
|
|
423
|
+
options:
|
|
424
|
+
- name: Water Ripple
|
|
425
|
+
value: ripple
|
|
426
|
+
- name: Underwater Wave
|
|
427
|
+
value: wave
|
|
428
|
+
- name: RGB Glitch
|
|
429
|
+
value: glitch
|
|
430
|
+
- name: animation_speed
|
|
431
|
+
kind: dropdown
|
|
432
|
+
options:
|
|
433
|
+
- name: Slow
|
|
434
|
+
value: slow
|
|
435
|
+
- name: Normal
|
|
436
|
+
value: normal
|
|
437
|
+
- name: Fast
|
|
438
|
+
value: fast
|
|
439
|
+
- name: intensity
|
|
440
|
+
kind: dropdown
|
|
441
|
+
options:
|
|
442
|
+
- name: Subtle
|
|
443
|
+
value: subtle
|
|
444
|
+
- name: Medium
|
|
445
|
+
value: medium
|
|
446
|
+
- name: Strong
|
|
447
|
+
value: strong
|
|
448
|
+
- name: canvas_height
|
|
449
|
+
kind: dropdown
|
|
450
|
+
options:
|
|
451
|
+
- name: Small (240px)
|
|
452
|
+
value: sm
|
|
453
|
+
- name: Medium (320px)
|
|
454
|
+
value: md
|
|
455
|
+
- name: Large (480px)
|
|
456
|
+
value: lg
|
|
457
|
+
- name: Extra Large (640px)
|
|
458
|
+
value: xl
|
|
459
|
+
- name: fit_mode
|
|
460
|
+
kind: dropdown
|
|
461
|
+
options:
|
|
462
|
+
- name: Cover (Fill & Crop)
|
|
463
|
+
value: cover
|
|
464
|
+
- name: Contain (Fit Inside)
|
|
465
|
+
value: contain
|
|
466
|
+
```
|
|
467
|
+
|
|
349
468
|
---
|
|
350
469
|
|
|
351
470
|
### 4. Importing Content Types
|
|
@@ -368,6 +487,54 @@ The import task is idempotent and creates or updates existing records. Once impo
|
|
|
368
487
|
|
|
369
488
|
### 5. Front-End Integration & ViewComponent Widgets
|
|
370
489
|
|
|
490
|
+
#### Host Application Controller (`app/controllers/articles_controller.rb`)
|
|
491
|
+
|
|
492
|
+
Include `Macron::Cms::SlugRedirectable` in your host controller to automatically issue canonical 301 permanent redirects if a visitor or search engine arrives via an outdated historical slug:
|
|
493
|
+
|
|
494
|
+
```ruby
|
|
495
|
+
class ArticlesController < ApplicationController
|
|
496
|
+
include Macron::Cms::SlugRedirectable
|
|
497
|
+
|
|
498
|
+
def show
|
|
499
|
+
@article = Macron::Cms::Article.friendly.find(params[:id])
|
|
500
|
+
redirect_to_canonical_slug_if_needed(@article)
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### Editorial Stream & Featured Images (`app/views/pages/index.html.erb`)
|
|
506
|
+
|
|
507
|
+
When listing articles on a homepage or editorial index, eager-load `:featured_image_blob` to display automatically bubbled featured images with zero N+1 database queries:
|
|
508
|
+
|
|
509
|
+
```ruby
|
|
510
|
+
# app/controllers/pages_controller.rb
|
|
511
|
+
class PagesController < ApplicationController
|
|
512
|
+
def index
|
|
513
|
+
# Default scope orders chronologically by published_at DESC, created_at DESC
|
|
514
|
+
@articles = Macron::Cms::Article.includes(:featured_image_blob)
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
```erb
|
|
520
|
+
<%# app/views/pages/index.html.erb %>
|
|
521
|
+
<div class="space-y-6">
|
|
522
|
+
<% @articles.each do |article| %>
|
|
523
|
+
<div class="flex items-start gap-4">
|
|
524
|
+
<% if article.featured_image.present? %>
|
|
525
|
+
<%= link_to article_path(article), class: "shrink-0" do %>
|
|
526
|
+
<%= image_tag article.featured_image, class: "w-24 h-24 object-cover rounded-lg border border-zinc-200" %>
|
|
527
|
+
<% end %>
|
|
528
|
+
<% end %>
|
|
529
|
+
<div>
|
|
530
|
+
<h2 class="text-xl font-bold"><%= link_to article.title, article_path(article) %></h2>
|
|
531
|
+
<p class="text-sm text-zinc-600 line-clamp-2"><%= article.seo_description %></p>
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
<% end %>
|
|
535
|
+
</div>
|
|
536
|
+
```
|
|
537
|
+
|
|
371
538
|
#### Host Application View (`app/views/articles/show.html.erb`)
|
|
372
539
|
|
|
373
540
|
Use `cms_seo_meta` for SEO metadata and `cms_article_background_style` to apply custom background colors, images, and text colors directly to the layout:
|
|
@@ -411,6 +578,8 @@ In your application layout (`app/views/layouts/application.html.erb`), apply the
|
|
|
411
578
|
<%= javascript_include_tag "vendor/three_text_bundle", "data-turbo-track": "reload", defer: true %>
|
|
412
579
|
<%= javascript_include_tag "vendor/three_fonts_bundle", "data-turbo-track": "reload", defer: true %>
|
|
413
580
|
<%= javascript_include_tag "widgets/three_js_quote", "data-turbo-track": "reload", defer: true %>
|
|
581
|
+
<%= javascript_include_tag "widgets/three_js_image", "data-turbo-track": "reload", defer: true %>
|
|
582
|
+
<%= javascript_include_tag "widgets/embed", "data-turbo-track": "reload", defer: true %>
|
|
414
583
|
</head>
|
|
415
584
|
|
|
416
585
|
<body class="min-h-screen text-zinc-900 <%= 'bg-zinc-50' unless content_for?(:body_style) %>"
|
|
@@ -522,8 +691,17 @@ end
|
|
|
522
691
|
# app/components/widgets/embed_component.rb
|
|
523
692
|
module Widgets
|
|
524
693
|
class EmbedComponent < Macron::Cms::WidgetComponent
|
|
694
|
+
HEIGHT_CLASSES = {
|
|
695
|
+
"sm" => "h-60 sm:h-72",
|
|
696
|
+
"md" => "h-72 sm:h-80",
|
|
697
|
+
"lg" => "h-96 sm:h-[480px]",
|
|
698
|
+
"xl" => "h-[540px] sm:h-[640px]",
|
|
699
|
+
"2xl" => "h-[640px] sm:h-[800px]"
|
|
700
|
+
}.freeze
|
|
701
|
+
DEFAULT_HEIGHT = "lg"
|
|
702
|
+
|
|
525
703
|
def embed_html
|
|
526
|
-
field_value(:html).
|
|
704
|
+
field_value(:html).presence || (upload.present? ? upload.download.force_encoding("UTF-8") : nil)
|
|
527
705
|
end
|
|
528
706
|
|
|
529
707
|
def width_class
|
|
@@ -544,18 +722,42 @@ module Widgets
|
|
|
544
722
|
else "mx-auto"
|
|
545
723
|
end
|
|
546
724
|
end
|
|
725
|
+
|
|
726
|
+
def height_class
|
|
727
|
+
h = field_value(:height).to_s.strip.downcase
|
|
728
|
+
HEIGHT_CLASSES[h] || HEIGHT_CLASSES[DEFAULT_HEIGHT]
|
|
729
|
+
end
|
|
547
730
|
end
|
|
548
731
|
end
|
|
549
732
|
```
|
|
550
733
|
|
|
551
734
|
```erb
|
|
552
735
|
<%# app/components/widgets/embed_component.html.erb %>
|
|
553
|
-
<div class="<%= width_class %> <%= align_class %>
|
|
736
|
+
<div class="relative group my-6 <%= width_class %> <%= align_class %> overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-xs [&:fullscreen]:m-0 [&:fullscreen]:w-screen [&:fullscreen]:h-screen [&:fullscreen]:rounded-none" data-embed-card>
|
|
737
|
+
<!-- Fullscreen Toggle Button -->
|
|
738
|
+
<button
|
|
739
|
+
type="button"
|
|
740
|
+
class="absolute top-3 right-3 z-10 flex h-8 w-8 items-center justify-center rounded-lg bg-zinc-900/60 text-white backdrop-blur-xs transition hover:bg-zinc-900/90 focus:outline-none opacity-70 group-hover:opacity-100 cursor-pointer"
|
|
741
|
+
data-embed-fullscreen-btn
|
|
742
|
+
aria-label="Maximize"
|
|
743
|
+
title="Maximize"
|
|
744
|
+
>
|
|
745
|
+
<svg class="h-4 w-4" data-icon-enter-fullscreen fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
746
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
|
|
747
|
+
</svg>
|
|
748
|
+
<svg class="h-4 w-4 hidden" data-icon-exit-fullscreen fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
749
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M9 9V4.5M9 9H4.5M9 9 3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25" />
|
|
750
|
+
</svg>
|
|
751
|
+
</button>
|
|
752
|
+
|
|
554
753
|
<iframe
|
|
555
754
|
srcdoc="<%= embed_html %>"
|
|
755
|
+
class="w-full <%= height_class %> border-0 [&.is-fullscreen-iframe]:h-full"
|
|
556
756
|
sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
|
|
557
|
-
|
|
558
|
-
|
|
757
|
+
allowfullscreen
|
|
758
|
+
allow="fullscreen"
|
|
759
|
+
loading="lazy"
|
|
760
|
+
title="Embedded content">
|
|
559
761
|
</iframe>
|
|
560
762
|
</div>
|
|
561
763
|
```
|
|
@@ -704,6 +906,9 @@ end
|
|
|
704
906
|
|
|
705
907
|
MacronCMS provides a JSON REST API under `/cms/api/v1/` designed for programmatic ingestion, automated scripts, and autonomous AI agents migrating content from external platforms (such as Substack, WordPress, or Medium).
|
|
706
908
|
|
|
909
|
+
> [!TIP]
|
|
910
|
+
> For the complete endpoint reference, request/response payload schemas, parameter definitions, and status codes, see the dedicated [**REST API Documentation (`docs/api.md`)**](docs/api.md). If you are building an automated ingestion pipeline or AI migration agent, refer to the [**AI Article Importer Guide (`docs/ai_importer_guide.md`)**](docs/ai_importer_guide.md).
|
|
911
|
+
|
|
707
912
|
### Authentication
|
|
708
913
|
|
|
709
914
|
API endpoints are secured using a Bearer token or API key header:
|
|
@@ -724,16 +929,24 @@ Configure your key in an initializer (`config.api_key`) or via the `MACRON_CMS_A
|
|
|
724
929
|
|
|
725
930
|
| Method | Endpoint | Description |
|
|
726
931
|
|---|---|---|
|
|
727
|
-
| `
|
|
728
|
-
| `GET` | `/cms/api/v1/articles
|
|
729
|
-
| `
|
|
730
|
-
| `GET` | `/cms/api/v1/
|
|
932
|
+
| `GET` | [`/cms/api/v1/content_types`](docs/api.md#1-list-content-types-get-cmsapiv1content_types) | Introspect available content types and schema field requirements |
|
|
933
|
+
| `GET` | [`/cms/api/v1/articles`](docs/api.md#2-list-articles-get-cmsapiv1articles) | List articles with metadata, slugs, and publication dates |
|
|
934
|
+
| `POST` | [`/cms/api/v1/articles`](docs/api.md#3-create-or-upsert-article-post-cmsapiv1articles) | Create or upsert an article with nested content blocks |
|
|
935
|
+
| `GET` | [`/cms/api/v1/articles/:id`](docs/api.md#4-retrieve-article-get-cmsapiv1articlesid) | Retrieve article metadata and ordered content blocks (JSON) |
|
|
936
|
+
| `PATCH` | [`/cms/api/v1/articles/:id`](docs/api.md#5-update-article-patch-cmsapiv1articlesid) | Update article metadata and synchronize content blocks |
|
|
937
|
+
| `DELETE` | [`/cms/api/v1/articles/:id`](docs/api.md#6-delete-article-delete-cmsapiv1articlesid) | Delete an article and its associated content blocks |
|
|
938
|
+
| `POST` | [`/cms/api/v1/articles/:article_id/content_blocks`](docs/api.md#7-content-block-operations) | Insert a content block (supports `insert_at` and `file_upload`) |
|
|
939
|
+
| `PATCH` | [`/cms/api/v1/articles/:article_id/content_blocks/:id`](docs/api.md#7-content-block-operations) | Update payload attributes, replace files, or purge attachments |
|
|
940
|
+
| `DELETE` | [`/cms/api/v1/articles/:article_id/content_blocks/:id`](docs/api.md#7-content-block-operations) | Delete a content block and re-normalize sequence order |
|
|
941
|
+
| `PATCH` | [`/cms/api/v1/articles/:article_id/content_blocks/:id/move`](docs/api.md#7-content-block-operations) | Move block position (`up`, `down`, or target position) |
|
|
942
|
+
| `PATCH` | [`/cms/api/v1/articles/:article_id/content_blocks/:id/convert`](docs/api.md#7-content-block-operations) | Convert block in-place to compatible schema type |
|
|
943
|
+
| `PATCH` | [`/cms/api/v1/articles/:article_id/content_blocks/reorder`](docs/api.md#7-content-block-operations) | Batch reorder blocks via array of ordered IDs |
|
|
731
944
|
|
|
732
945
|
---
|
|
733
946
|
|
|
734
947
|
### 1. Ingesting Articles with Nested Content Blocks
|
|
735
948
|
|
|
736
|
-
You can import an entire article—including its metadata, SEO tags, colors, and an ordered list of typed content blocks—in a single atomic transaction:
|
|
949
|
+
You can import an entire article—including its metadata, SEO tags, colors, publication timestamp (`published_at`), and an ordered list of typed content blocks—in a single atomic transaction:
|
|
737
950
|
|
|
738
951
|
```bash
|
|
739
952
|
curl -X POST http://localhost:3000/cms/api/v1/articles \
|
|
@@ -743,7 +956,8 @@ curl -X POST http://localhost:3000/cms/api/v1/articles \
|
|
|
743
956
|
"upsert": true,
|
|
744
957
|
"article": {
|
|
745
958
|
"title": "The Myth of Your Limitations",
|
|
746
|
-
"
|
|
959
|
+
"slug": "the-myth-of-your-limitations",
|
|
960
|
+
"published_at": "2026-10-29T19:30:00Z",
|
|
747
961
|
"meta_title": "The Myth of Your Limitations | Mindset Guide",
|
|
748
962
|
"meta_description": "How a simple mindset shift can prove you wrong.",
|
|
749
963
|
"background_color": "#ffffff",
|
|
@@ -776,7 +990,7 @@ curl -X POST http://localhost:3000/cms/api/v1/articles \
|
|
|
776
990
|
When an `image_url` (or `upload_url`) is provided in a content block, MacronCMS automatically downloads the asset from the remote source (e.g. Substack CDN), verifies content size and type, and attaches it as an Active Storage blob on the content block. If the remote server is unreachable, it gracefully falls back to storing the direct URL in the block payload.
|
|
777
991
|
|
|
778
992
|
#### Idempotent Upserting
|
|
779
|
-
When `"upsert": true` is specified, the API searches for an existing article matching `
|
|
993
|
+
When `"upsert": true` is specified, the API searches for an existing article matching `slug` or `title`. If found, it updates the article attributes and atomically replaces its content blocks with the incoming sequence, making bulk migrations safe to re-run.
|
|
780
994
|
|
|
781
995
|
---
|
|
782
996
|
|
|
@@ -794,20 +1008,119 @@ AI agents can query `/cms/api/v1/content_types` before generating payloads to dy
|
|
|
794
1008
|
{ "name": "title", "kind": "string" },
|
|
795
1009
|
{ "name": "level", "kind": "dropdown", "options": ["h1", "h2", "h3", "h4", "h5", "h6"] },
|
|
796
1010
|
{ "name": "color", "kind": "color" }
|
|
797
|
-
]
|
|
1011
|
+
],
|
|
1012
|
+
"convertible_to": [],
|
|
1013
|
+
"featured_image": false
|
|
798
1014
|
},
|
|
799
1015
|
{
|
|
800
1016
|
"id": 2,
|
|
801
|
-
"name": "
|
|
1017
|
+
"name": "image",
|
|
802
1018
|
"fields": [
|
|
803
|
-
{ "name": "
|
|
804
|
-
|
|
1019
|
+
{ "name": "upload", "kind": "file" },
|
|
1020
|
+
{ "name": "size", "kind": "dropdown", "options": ["sm", "md", "lg", "full"] }
|
|
1021
|
+
],
|
|
1022
|
+
"convertible_to": [
|
|
1023
|
+
"three_js_image"
|
|
1024
|
+
],
|
|
1025
|
+
"featured_image": true
|
|
805
1026
|
}
|
|
806
1027
|
]
|
|
807
1028
|
}
|
|
808
1029
|
```
|
|
809
1030
|
|
|
810
|
-
|
|
1031
|
+
---
|
|
1032
|
+
|
|
1033
|
+
### 3. Granular Content Block Operations
|
|
1034
|
+
|
|
1035
|
+
MacronCMS supports fine-grained REST manipulation of article blocks, allowing agents and scripts to make targeted edits without replacing the entire article:
|
|
1036
|
+
|
|
1037
|
+
#### Add Block with Inline Base64 Asset
|
|
1038
|
+
```bash
|
|
1039
|
+
curl -X POST http://localhost:3000/cms/api/v1/articles/the-myth-of-your-limitations/content_blocks \
|
|
1040
|
+
-H "Content-Type: application/json" \
|
|
1041
|
+
-H "Authorization: Bearer test-secret-key" \
|
|
1042
|
+
-d '{
|
|
1043
|
+
"content_type": "image",
|
|
1044
|
+
"insert_at": 2,
|
|
1045
|
+
"payload": { "size": "md" },
|
|
1046
|
+
"file_upload": {
|
|
1047
|
+
"filename": "diagram.png",
|
|
1048
|
+
"content_type": "image/png",
|
|
1049
|
+
"data_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
|
1050
|
+
}
|
|
1051
|
+
}'
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
#### In-Place Block Conversion
|
|
1055
|
+
```bash
|
|
1056
|
+
curl -X PATCH http://localhost:3000/cms/api/v1/articles/the-myth-of-your-limitations/content_blocks/42/convert \
|
|
1057
|
+
-H "Content-Type: application/json" \
|
|
1058
|
+
-H "Authorization: Bearer test-secret-key" \
|
|
1059
|
+
-d '{ "target_content_type": "three_js_image" }'
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
#### Reordering and Relative Moves
|
|
1063
|
+
```bash
|
|
1064
|
+
# Shift block up or down relative to its current position
|
|
1065
|
+
curl -X PATCH http://localhost:3000/cms/api/v1/articles/the-myth-of-your-limitations/content_blocks/42/move \
|
|
1066
|
+
-H "Content-Type: application/json" \
|
|
1067
|
+
-H "Authorization: Bearer test-secret-key" \
|
|
1068
|
+
-d '{ "direction": "up" }'
|
|
1069
|
+
|
|
1070
|
+
# Or batch reorder by an explicit list of block IDs
|
|
1071
|
+
curl -X PATCH http://localhost:3000/cms/api/v1/articles/the-myth-of-your-limitations/content_blocks/reorder \
|
|
1072
|
+
-H "Content-Type: application/json" \
|
|
1073
|
+
-H "Authorization: Bearer test-secret-key" \
|
|
1074
|
+
-d '{ "ordered_ids": [45, 42, 43, 44] }'
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
For complete endpoint specifications, payload schemas, and response formats, see [`docs/api.md`](docs/api.md). For Substack HTML mapping rules and a ready-to-use prompt template for AI agents, see [`docs/ai_importer_guide.md`](docs/ai_importer_guide.md).
|
|
1078
|
+
|
|
1079
|
+
---
|
|
1080
|
+
|
|
1081
|
+
## 🤖 Model Context Protocol (MCP) Server
|
|
1082
|
+
|
|
1083
|
+
MacronCMS includes an official **Model Context Protocol (MCP) Server**: [**`macron-cms-mcp`**](https://github.com/macron1-automations/macron-cms-mcp).
|
|
1084
|
+
|
|
1085
|
+
The MCP server connects AI coding assistants and autonomous agents—including Claude Desktop, Cursor, and Antigravity—directly to MacronCMS, exposing rich tool-calling capabilities to manage and pair-program content directly from within your agent workflow.
|
|
1086
|
+
|
|
1087
|
+
### Available MCP Tools
|
|
1088
|
+
|
|
1089
|
+
| Tool | Description |
|
|
1090
|
+
|---|---|
|
|
1091
|
+
| `list_content_types` | Introspect all registered YAML content types, field specifications, and type converters |
|
|
1092
|
+
| `list_articles` | Retrieve articles with metadata, canonical slugs, and publication timestamps |
|
|
1093
|
+
| `get_article` | Fetch article details and its ordered content blocks |
|
|
1094
|
+
| `create_article` | Scaffold a new article with optional nested blocks and publication scheduling |
|
|
1095
|
+
| `update_article_metadata` | Update article title, slug, publication date, text/background styling, or SEO tags |
|
|
1096
|
+
| `delete_article` | Delete an article and its content blocks |
|
|
1097
|
+
| `add_content_block` | Insert a new content block at a specific position with payload or Base64 attachments |
|
|
1098
|
+
| `update_content_block` | Modify block payload attributes, replace attachments, or purge files |
|
|
1099
|
+
| `delete_content_block` | Remove a block and automatically re-normalize sequence ordering |
|
|
1100
|
+
| `move_content_block` | Shift block position up, down, or to an explicit target index |
|
|
1101
|
+
| `reorder_content_blocks` | Batch reorder all blocks on an article |
|
|
1102
|
+
| `convert_content_block` | In-place convert a block between compatible content types (e.g. `image` ↔ `three_js_image`) |
|
|
1103
|
+
|
|
1104
|
+
### AI Agent / Client Configuration
|
|
1105
|
+
|
|
1106
|
+
Add the server to your MCP client configuration (such as `claude_desktop_config.json` or your Antigravity / Cursor MCP configuration):
|
|
1107
|
+
|
|
1108
|
+
```json
|
|
1109
|
+
{
|
|
1110
|
+
"mcpServers": {
|
|
1111
|
+
"macron-cms": {
|
|
1112
|
+
"command": "npx",
|
|
1113
|
+
"args": ["-y", "@macron1/cms-mcp"],
|
|
1114
|
+
"env": {
|
|
1115
|
+
"MACRON_CMS_BASE_URL": "http://localhost:3000/cms/api/v1",
|
|
1116
|
+
"MACRON_CMS_API_KEY": "<your-api-key>"
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
For full installation and tool usage documentation, visit the [**macron-cms-mcp repository on GitHub**](https://github.com/macron1-automations/macron-cms-mcp).
|
|
811
1124
|
|
|
812
1125
|
---
|
|
813
1126
|
|