macron-cms 2.0.0 β 3.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 +376 -14
- data/app/assets/javascripts/macron/cms/codemirror.js +3 -0
- data/app/assets/javascripts/macron/cms/editor.js +34 -6
- data/app/assets/javascripts/macron/cms/katex.min.js +1 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Italic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-Italic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Script-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/katex.min.css +1 -0
- data/app/controllers/macron/cms/api/v1/articles_controller.rb +186 -0
- data/app/controllers/macron/cms/api/v1/base_controller.rb +35 -0
- data/app/controllers/macron/cms/api/v1/content_types_controller.rb +21 -0
- data/app/controllers/macron/cms/article_contents_controller.rb +14 -3
- data/app/helpers/macron/cms/fields_helper.rb +1 -1
- data/app/services/macron/cms/remote_asset_attacher.rb +86 -0
- data/app/views/layouts/macron/cms/application.html.erb +2 -0
- data/app/views/macron/cms/articles/_content_blocks_section.html.erb +5 -1
- data/app/views/macron/cms/fields/_file.html.erb +4 -0
- data/app/views/macron/cms/fields/_latex.html.erb +17 -0
- data/config/routes.rb +7 -0
- data/lib/macron/cms/configuration.rb +2 -1
- data/lib/macron/cms/version.rb +1 -1
- metadata +28 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 774dc3fc574e2fcd3d5e200b370bd6886718539fbab312cfe17632d1b18d153b
|
|
4
|
+
data.tar.gz: 003dbe77a32e518917f3f2c342319a27c701bad690d9a6acfbf009efaef91ce2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75063868fbbbb013c613f307de1c6d790066eb146f9add543dbb3961df14f6ba98502a81a433e1adbd27bbd9304f774dac0dfc592564d92883b66bd7ea1e9b0a
|
|
7
|
+
data.tar.gz: 66ffde9ab34f2cf35255183e7a396f29db811100e858ee56aa6d85a1e565ab13dbe30f66d18e1ea61edfa82312b6bbb0b4d7ed25464674f2c1a0c3c882bbee05
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MacronCMS
|
|
2
2
|
|
|
3
|
-
[](#version)
|
|
4
4
|
[](https://rubyonrails.org)
|
|
5
5
|
[](MIT-LICENSE)
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
27
27
|
- **In-Place Block Inserters**: Add new blocks directly above, between, or below existing blocks on the Article management screen with one click.
|
|
28
28
|
- **Inline Turbo Stream Updates**: Add, edit, and reorder content blocks with instantaneous Turbo Stream updatesβno full-page reloads.
|
|
29
29
|
- **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
|
-
- **
|
|
30
|
+
- **8 Extensible Field Kinds**:
|
|
31
31
|
- `string`: Single-line text input.
|
|
32
32
|
- `text`: Multiline text area.
|
|
33
33
|
- `dropdown`: Select menu configured with custom option lists from the schema YAML.
|
|
@@ -35,6 +35,8 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
35
35
|
- `markdown`: CodeMirror-backed markdown editor with formatted preview on the article show page.
|
|
36
36
|
- `file`: Direct file attachment powered by Active Storage.
|
|
37
37
|
- `json`: Dedicated JSON data editor.
|
|
38
|
+
- `latex`: CodeMirror LaTeX editor with `stex` syntax mode and KaTeX mathematical equation preview.
|
|
39
|
+
- **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.
|
|
38
40
|
|
|
39
41
|
### π¨ Article Background & Typography Customization
|
|
40
42
|
- **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.
|
|
@@ -46,8 +48,9 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
46
48
|
- **Article-Wide Text Color Propagation**: Set a custom text color at the article level that automatically propagates to the host view's `<body>` and header, as well as to child `WidgetComponent` instances (headings, paragraphs, and markdown prose).
|
|
47
49
|
- **Public Style Helper**: Use `cms_article_background_style(@article)` to generate clean inline CSS applied directly to the host application's `<body>` tag.
|
|
48
50
|
|
|
49
|
-
### π CodeMirror Markdown & Rich Media
|
|
50
|
-
- **CodeMirror Markdown
|
|
51
|
+
### π CodeMirror Markdown, LaTeX & Rich Media
|
|
52
|
+
- **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
|
+
- **Fast KaTeX Math Rendering**: Embedded KaTeX engine renders complex LaTeX equations, mathematical notation, and symbols instantaneously in both admin previews and front-end layouts.
|
|
51
54
|
- **Formatted Markdown Previews**: Formatted GitHub Flavored Markdown (GFM) previews rendered directly on the article management page.
|
|
52
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.
|
|
53
56
|
- **Responsive Media Controls**: Configure image and embed widgets with responsive width options (`25%`, `33%`, `50%`, `66%`, `75%`, `100%`) and alignment (`left`, `center`, `right`).
|
|
@@ -70,6 +73,13 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
70
73
|
- **Configurable Access Guard**: Protect the `/cms` engine namespace behind host application authentication.
|
|
71
74
|
- **Role Customization**: Configure custom user models (`user_class`) and admin flags (`admin_flag`) via initializers.
|
|
72
75
|
|
|
76
|
+
### π€ REST API & AI Content Ingestion (`/cms/api/v1`)
|
|
77
|
+
- **Atomic Nested Import**: Create or update articles and complete sequences of typed content blocks in a single atomic transaction.
|
|
78
|
+
- **Automated Remote Asset Ingestion**: Remote image URLs (e.g. Substack, Medium, WordPress) are automatically downloaded, validated, and attached to Active Storage blobs.
|
|
79
|
+
- **Idempotent Upsert Support**: Re-run imports safely using `upsert: true` without creating duplicate articles.
|
|
80
|
+
- **Content Type Schema Introspection**: Query `GET /cms/api/v1/content_types` dynamically so AI agents know exact fields and widget types available.
|
|
81
|
+
- **Bearer Token Auth**: Secure endpoints with `Authorization: Bearer <token>` or `X-API-Key`.
|
|
82
|
+
|
|
73
83
|
### π¨ Self-Contained Tailwind CSS v4
|
|
74
84
|
- **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.
|
|
75
85
|
- **Isolated Styling**: The admin layout operates within its own styling boundary to avoid conflicts with host application design systems.
|
|
@@ -77,15 +87,11 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
77
87
|
### π Built-in SEO Metadata Helpers
|
|
78
88
|
- **`cms_seo_meta(@article)`**: Helper to inject `<title>`, canonical `<link>`, and Open Graph meta tags into the view `<head>` using an article's title, meta title, and meta description overrides.
|
|
79
89
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Drop in React plugins where heavy client-side interactivity is needed, making hydrating complex client componentsβsuch as interactive 3D [Three.js](https://threejs.org/) scenes, interactive canvases, and custom calculatorsβeffortless and isolated.
|
|
86
|
-
|
|
87
|
-
### π€ AI-Friendly API for Content Migration
|
|
88
|
-
An AI-friendly ingest and transformation API for migrating existing content quickly and programmatically across legacy datasets, CMS exports, and unstructured assets.
|
|
90
|
+
### π React & Three.js 3D Hydration
|
|
91
|
+
- **Interactive 3D Client Components**: Effortlessly hydrate rich client-side experiencesβsuch as interactive 3D typography in [Three.js](https://threejs.org/)βwrapped cleanly in ViewComponent widgets.
|
|
92
|
+
- **Rich 3D Customization**: Supports selectable 3D fonts (Helvetiker, Optimer, Gentilis, Droid Serif), customizable font sizes, 3-axis rotation (`x`, `y`, `z`), and variable rotation speeds.
|
|
93
|
+
- **Visual CRT Shader Effects & Backgrounds**: Built-in CRT scanline shader overlays (intensity, density, speed) and dual-mode background support (hex colors or Active Storage background image in stretch/tile modes).
|
|
94
|
+
- **Turbo-Resilient Lifecycle**: Cleanly mounts, re-renders, and disposes React roots and Three.js animation loops across Turbo navigation (`turbo:load`, `turbo:render`, `turbo:before-cache`).
|
|
89
95
|
|
|
90
96
|
---
|
|
91
97
|
|
|
@@ -146,6 +152,8 @@ Scaffold content types using the built-in Rails generator:
|
|
|
146
152
|
$ bin/rails generate macron:cms:content_type heading title:string level:dropdown color:color
|
|
147
153
|
$ bin/rails generate macron:cms:content_type paragraph content:text
|
|
148
154
|
$ bin/rails generate macron:cms:content_type markdown content:markdown
|
|
155
|
+
$ bin/rails generate macron:cms:content_type blockquote content:markdown
|
|
156
|
+
$ bin/rails generate macron:cms:content_type latex content:latex
|
|
149
157
|
$ bin/rails generate macron:cms:content_type image file:file width:dropdown align:dropdown caption:string
|
|
150
158
|
$ bin/rails generate macron:cms:content_type embed html:text width:dropdown align:dropdown
|
|
151
159
|
```
|
|
@@ -168,6 +176,7 @@ This generates:
|
|
|
168
176
|
| `markdown` | Multiline markdown with CodeMirror editor | β | `field_name` |
|
|
169
177
|
| `file` | File upload backed by Active Storage | β | `file` or `upload` (Active Storage attachment) |
|
|
170
178
|
| `json` | Formatted JSON editor | β | `field_name` |
|
|
179
|
+
| `latex` | Mathematical formula editor with CodeMirror & KaTeX | β | `field_name` |
|
|
171
180
|
|
|
172
181
|
---
|
|
173
182
|
|
|
@@ -252,6 +261,91 @@ fields:
|
|
|
252
261
|
- right
|
|
253
262
|
```
|
|
254
263
|
|
|
264
|
+
#### Mathematical Equation (`config/content_types/latex.yml`)
|
|
265
|
+
```yaml
|
|
266
|
+
fields:
|
|
267
|
+
- name: content
|
|
268
|
+
kind: latex
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### Blockquote with Markdown (`config/content_types/blockquote.yml`)
|
|
272
|
+
```yaml
|
|
273
|
+
fields:
|
|
274
|
+
- name: content
|
|
275
|
+
kind: markdown
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Interactive 3D Quote (`config/content_types/three_js_quote.yml`)
|
|
279
|
+
```yaml
|
|
280
|
+
fields:
|
|
281
|
+
- name: text
|
|
282
|
+
kind: string
|
|
283
|
+
- name: color
|
|
284
|
+
kind: color
|
|
285
|
+
- name: rotation_axis
|
|
286
|
+
kind: dropdown
|
|
287
|
+
options:
|
|
288
|
+
- name: X Axis
|
|
289
|
+
value: x
|
|
290
|
+
- name: Y Axis
|
|
291
|
+
value: y
|
|
292
|
+
- name: Z Axis
|
|
293
|
+
value: z
|
|
294
|
+
- name: rotation_speed
|
|
295
|
+
kind: dropdown
|
|
296
|
+
options:
|
|
297
|
+
- name: 0x (Paused)
|
|
298
|
+
value: "0"
|
|
299
|
+
- name: 0.5x
|
|
300
|
+
value: "0.5"
|
|
301
|
+
- name: 1x
|
|
302
|
+
value: "1"
|
|
303
|
+
- name: 2x
|
|
304
|
+
value: "2"
|
|
305
|
+
- name: font
|
|
306
|
+
kind: dropdown
|
|
307
|
+
options:
|
|
308
|
+
- name: Helvetiker (Sans-serif)
|
|
309
|
+
value: helvetiker
|
|
310
|
+
- name: Optimer (Modern Sans)
|
|
311
|
+
value: optimer
|
|
312
|
+
- name: Gentilis (Humanist Sans)
|
|
313
|
+
value: gentilis
|
|
314
|
+
- name: Droid Serif (Serif)
|
|
315
|
+
value: droid_serif
|
|
316
|
+
- name: canvas_height
|
|
317
|
+
kind: dropdown
|
|
318
|
+
options:
|
|
319
|
+
- name: Small (240px)
|
|
320
|
+
value: sm
|
|
321
|
+
- name: Medium (320px)
|
|
322
|
+
value: md
|
|
323
|
+
- name: Large (480px)
|
|
324
|
+
value: lg
|
|
325
|
+
- name: background_color
|
|
326
|
+
kind: color
|
|
327
|
+
- name: upload
|
|
328
|
+
kind: file
|
|
329
|
+
- name: background_mode
|
|
330
|
+
kind: dropdown
|
|
331
|
+
options:
|
|
332
|
+
- name: Stretch
|
|
333
|
+
value: stretch
|
|
334
|
+
- name: Tile
|
|
335
|
+
value: tile
|
|
336
|
+
- name: scanlines_intensity
|
|
337
|
+
kind: dropdown
|
|
338
|
+
options:
|
|
339
|
+
- name: Off
|
|
340
|
+
value: off
|
|
341
|
+
- name: Subtle
|
|
342
|
+
value: subtle
|
|
343
|
+
- name: Medium
|
|
344
|
+
value: medium
|
|
345
|
+
- name: Strong
|
|
346
|
+
value: strong
|
|
347
|
+
```
|
|
348
|
+
|
|
255
349
|
---
|
|
256
350
|
|
|
257
351
|
### 4. Importing Content Types
|
|
@@ -298,9 +392,27 @@ Use `cms_seo_meta` for SEO metadata and `cms_article_background_style` to apply
|
|
|
298
392
|
</article>
|
|
299
393
|
```
|
|
300
394
|
|
|
301
|
-
In your application layout (`app/views/layouts/application.html.erb`), apply the `:body_style` slot:
|
|
395
|
+
In your application layout (`app/views/layouts/application.html.erb`), apply the `:body_style` slot and include the KaTeX and 3D widget assets:
|
|
302
396
|
|
|
303
397
|
```erb
|
|
398
|
+
<head>
|
|
399
|
+
<title><%= content_for(:title) || "Macron CMS" %></title>
|
|
400
|
+
<%= csrf_meta_tags %>
|
|
401
|
+
<%= csp_meta_tag %>
|
|
402
|
+
<%= turbo_include_tags %>
|
|
403
|
+
|
|
404
|
+
<%# KaTeX Stylesheet & Scripts (LaTeX Math Rendering) %>
|
|
405
|
+
<%= stylesheet_link_tag "macron/cms/katex.min", "data-turbo-track": "reload" %>
|
|
406
|
+
<%= javascript_include_tag "macron/cms/katex.min", "data-turbo-track": "reload", defer: true %>
|
|
407
|
+
<%= javascript_include_tag "widgets/latex", "data-turbo-track": "reload", defer: true %>
|
|
408
|
+
|
|
409
|
+
<%# React & Three.js Vendor Bundles (3D Interactive Widgets) %>
|
|
410
|
+
<%= javascript_include_tag "vendor/react_bundle", "data-turbo-track": "reload", defer: true %>
|
|
411
|
+
<%= javascript_include_tag "vendor/three_text_bundle", "data-turbo-track": "reload", defer: true %>
|
|
412
|
+
<%= javascript_include_tag "vendor/three_fonts_bundle", "data-turbo-track": "reload", defer: true %>
|
|
413
|
+
<%= javascript_include_tag "widgets/three_js_quote", "data-turbo-track": "reload", defer: true %>
|
|
414
|
+
</head>
|
|
415
|
+
|
|
304
416
|
<body class="min-h-screen text-zinc-900 <%= 'bg-zinc-50' unless content_for?(:body_style) %>"
|
|
305
417
|
style="<%= yield :body_style if content_for?(:body_style) %>">
|
|
306
418
|
<%= yield %>
|
|
@@ -448,6 +560,255 @@ end
|
|
|
448
560
|
</div>
|
|
449
561
|
```
|
|
450
562
|
|
|
563
|
+
#### Blockquote Widget Component Example
|
|
564
|
+
|
|
565
|
+
Render quote blocks with Markdown and dynamically inherited article text color:
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
# app/components/widgets/blockquote_component.rb
|
|
569
|
+
module Widgets
|
|
570
|
+
class BlockquoteComponent < Macron::Cms::WidgetComponent
|
|
571
|
+
def rendered_html
|
|
572
|
+
return "".html_safe if content.blank?
|
|
573
|
+
|
|
574
|
+
raw_html = Kramdown::Document.new(content.to_s, input: "GFM").to_html
|
|
575
|
+
helpers.sanitize(raw_html)
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def custom_style
|
|
579
|
+
return unless article_text_color.present?
|
|
580
|
+
|
|
581
|
+
"color: #{article_text_color}; border-color: #{article_text_color};"
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def blockquote_classes
|
|
585
|
+
base = "border-l-4 pl-4 py-1.5 my-6 max-w-none text-base leading-relaxed italic space-y-2"
|
|
586
|
+
article_text_color.present? ? base : "#{base} border-zinc-300 text-zinc-800"
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
```erb
|
|
593
|
+
<%# app/components/widgets/blockquote_component.html.erb %>
|
|
594
|
+
<blockquote class="<%= blockquote_classes %>" style="<%= custom_style %>">
|
|
595
|
+
<%= rendered_html %>
|
|
596
|
+
</blockquote>
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
#### LaTeX Mathematical Widget Component Example
|
|
600
|
+
|
|
601
|
+
Render mathematical notation and formulas with KaTeX:
|
|
602
|
+
|
|
603
|
+
```ruby
|
|
604
|
+
# app/components/widgets/latex_component.rb
|
|
605
|
+
module Widgets
|
|
606
|
+
class LatexComponent < Macron::Cms::WidgetComponent
|
|
607
|
+
def formula
|
|
608
|
+
content.to_s
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def custom_style
|
|
612
|
+
"color: #{article_text_color};" if article_text_color.present?
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def latex_classes
|
|
616
|
+
"cms-latex-widget my-6 overflow-x-auto py-2 text-center"
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
```erb
|
|
623
|
+
<%# app/components/widgets/latex_component.html.erb %>
|
|
624
|
+
<% if formula.present? %>
|
|
625
|
+
<%= content_tag(
|
|
626
|
+
:div,
|
|
627
|
+
content_tag(:span, formula, class: "font-mono text-xs opacity-75"),
|
|
628
|
+
class: latex_classes,
|
|
629
|
+
style: custom_style,
|
|
630
|
+
data: { cms_latex: formula, cms_latex_display: "true" }
|
|
631
|
+
) %>
|
|
632
|
+
<% end %>
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### Interactive 3D Three.js Widget Component Example
|
|
636
|
+
|
|
637
|
+
Hydrate a 3D Three.js scene with React in a ViewComponent with Turbo lifecycle awareness:
|
|
638
|
+
|
|
639
|
+
```ruby
|
|
640
|
+
# app/components/widgets/three_js_quote_component.rb
|
|
641
|
+
module Widgets
|
|
642
|
+
class ThreeJsQuoteComponent < Macron::Cms::WidgetComponent
|
|
643
|
+
DEFAULT_COLOR = "#18181b"
|
|
644
|
+
CANVAS_HEIGHT_CLASSES = {
|
|
645
|
+
"sm" => "h-60",
|
|
646
|
+
"md" => "h-64 sm:h-80",
|
|
647
|
+
"lg" => "h-96 sm:h-[480px]",
|
|
648
|
+
"xl" => "h-[540px] sm:h-[640px]"
|
|
649
|
+
}.freeze
|
|
650
|
+
|
|
651
|
+
def render?
|
|
652
|
+
quote_text.present?
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
def quote_text
|
|
656
|
+
field_value("text").to_s.strip.presence
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def effective_color
|
|
660
|
+
field_value("color").presence || article_text_color.presence || DEFAULT_COLOR
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def rotation_axis
|
|
664
|
+
field_value("rotation_axis").presence || "x"
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def rotation_speed
|
|
668
|
+
field_value("rotation_speed").presence || "1"
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def canvas_height_class
|
|
672
|
+
CANVAS_HEIGHT_CLASSES[field_value("canvas_height")] || "h-64 sm:h-80"
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
```erb
|
|
679
|
+
<%# app/components/widgets/three_js_quote_component.html.erb %>
|
|
680
|
+
<figure
|
|
681
|
+
class="my-6 w-full overflow-hidden rounded-xl border border-zinc-200 bg-gradient-to-b from-white to-zinc-50 shadow-xs"
|
|
682
|
+
data-three-js-quote-card
|
|
683
|
+
>
|
|
684
|
+
<blockquote class="sr-only"><%= quote_text %></blockquote>
|
|
685
|
+
<div
|
|
686
|
+
class="relative w-full <%= canvas_height_class %>"
|
|
687
|
+
data-three-js-quote-mount
|
|
688
|
+
data-quote-text="<%= quote_text %>"
|
|
689
|
+
data-quote-color="<%= effective_color %>"
|
|
690
|
+
data-rotation-axis="<%= rotation_axis %>"
|
|
691
|
+
data-rotation-speed="<%= rotation_speed %>"
|
|
692
|
+
data-font="<%= field_value('font').presence || 'helvetiker' %>"
|
|
693
|
+
data-font-size="<%= field_value('font_size').presence || 'md' %>"
|
|
694
|
+
data-scanlines-intensity="<%= field_value('scanlines_intensity').presence || 'off' %>"
|
|
695
|
+
role="img"
|
|
696
|
+
aria-label="<%= quote_text %>"
|
|
697
|
+
></div>
|
|
698
|
+
</figure>
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## REST API & AI Content Ingestion
|
|
704
|
+
|
|
705
|
+
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
|
+
|
|
707
|
+
### Authentication
|
|
708
|
+
|
|
709
|
+
API endpoints are secured using a Bearer token or API key header:
|
|
710
|
+
|
|
711
|
+
```http
|
|
712
|
+
Authorization: Bearer <MACRON_CMS_API_KEY>
|
|
713
|
+
```
|
|
714
|
+
Or:
|
|
715
|
+
```http
|
|
716
|
+
X-API-Key: <MACRON_CMS_API_KEY>
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Configure your key in an initializer (`config.api_key`) or via the `MACRON_CMS_API_KEY` environment variable. In `development` and `test` environments, authentication is optional if no key is configured.
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
### Endpoints
|
|
724
|
+
|
|
725
|
+
| Method | Endpoint | Description |
|
|
726
|
+
|---|---|---|
|
|
727
|
+
| `POST` | `/cms/api/v1/articles` | Create or upsert an article with nested content blocks |
|
|
728
|
+
| `GET` | `/cms/api/v1/articles/:id` | Retrieve article metadata and ordered content blocks (JSON) |
|
|
729
|
+
| `PATCH` | `/cms/api/v1/articles/:id` | Update article attributes and synchronize content blocks |
|
|
730
|
+
| `GET` | `/cms/api/v1/content_types` | Introspect available content types and schema field requirements |
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
### 1. Ingesting Articles with Nested Content Blocks
|
|
735
|
+
|
|
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:
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
curl -X POST http://localhost:3000/cms/api/v1/articles \
|
|
740
|
+
-H "Content-Type: application/json" \
|
|
741
|
+
-H "Authorization: Bearer test-secret-key" \
|
|
742
|
+
-d '{
|
|
743
|
+
"upsert": true,
|
|
744
|
+
"article": {
|
|
745
|
+
"title": "The Myth of Your Limitations",
|
|
746
|
+
"name": "the-myth-of-your-limitations",
|
|
747
|
+
"meta_title": "The Myth of Your Limitations | Mindset Guide",
|
|
748
|
+
"meta_description": "How a simple mindset shift can prove you wrong.",
|
|
749
|
+
"background_color": "#ffffff",
|
|
750
|
+
"text_color": "#18181b"
|
|
751
|
+
},
|
|
752
|
+
"content_blocks": [
|
|
753
|
+
{
|
|
754
|
+
"content_type": "heading",
|
|
755
|
+
"payload": {
|
|
756
|
+
"title": "The Rule of Congruency",
|
|
757
|
+
"level": "h3"
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
"content_type": "markdown",
|
|
762
|
+
"payload": {
|
|
763
|
+
"content": "There is a principle in geometry called the **congruency of triangles**..."
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
"content_type": "image",
|
|
768
|
+
"payload": { "size": "lg" },
|
|
769
|
+
"image_url": "https://substack-post-media.s3.amazonaws.com/public/images/7343afc4-325e-42a1-9145-dd916d0de54d_1024x1024.png"
|
|
770
|
+
}
|
|
771
|
+
]
|
|
772
|
+
}'
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
#### Automated Remote Asset Ingestion
|
|
776
|
+
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
|
+
|
|
778
|
+
#### Idempotent Upserting
|
|
779
|
+
When `"upsert": true` is specified, the API searches for an existing article matching `name`, `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
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
### 2. Schema Introspection (`GET /cms/api/v1/content_types`)
|
|
784
|
+
|
|
785
|
+
AI agents can query `/cms/api/v1/content_types` before generating payloads to dynamically discover which widget types exist and what fields they require:
|
|
786
|
+
|
|
787
|
+
```json
|
|
788
|
+
{
|
|
789
|
+
"content_types": [
|
|
790
|
+
{
|
|
791
|
+
"id": 1,
|
|
792
|
+
"name": "heading",
|
|
793
|
+
"fields": [
|
|
794
|
+
{ "name": "title", "kind": "string" },
|
|
795
|
+
{ "name": "level", "kind": "dropdown", "options": ["h1", "h2", "h3", "h4", "h5", "h6"] },
|
|
796
|
+
{ "name": "color", "kind": "color" }
|
|
797
|
+
]
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
"id": 2,
|
|
801
|
+
"name": "markdown",
|
|
802
|
+
"fields": [
|
|
803
|
+
{ "name": "content", "kind": "markdown" }
|
|
804
|
+
]
|
|
805
|
+
}
|
|
806
|
+
]
|
|
807
|
+
}
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
For complete mapping rules from Substack HTML tags and a ready-to-use prompt template for AI agents, see [`docs/ai_importer_guide.md`](docs/ai_importer_guide.md).
|
|
811
|
+
|
|
451
812
|
---
|
|
452
813
|
|
|
453
814
|
## Configuration
|
|
@@ -459,6 +820,7 @@ Customize CMS options in an initializer:
|
|
|
459
820
|
Macron::Cms.configure do |config|
|
|
460
821
|
config.user_class = "User" # Devise user model (default: "User")
|
|
461
822
|
config.admin_flag = :admin # Method or boolean column for admin check (default: :admin)
|
|
823
|
+
config.api_key = ENV["MACRON_CMS_API_KEY"] # Bearer token for /cms/api/v1 (default: ENV["MACRON_CMS_API_KEY"])
|
|
462
824
|
config.content_types_path = Rails.root.join("config", "content_types") # Custom schema directory
|
|
463
825
|
end
|
|
464
826
|
```
|
|
@@ -4,3 +4,6 @@
|
|
|
4
4
|
!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror"),require("../xml/xml"),require("../meta")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","../xml/xml","../meta"],t):t(CodeMirror)}(function(w){"use strict";w.defineMode("markdown",function(m,d){var g=w.getMode(m,"text/html"),u="null"==g.name;void 0===d.highlightFormatting&&(d.highlightFormatting=!1),void 0===d.maxBlockquoteDepth&&(d.maxBlockquoteDepth=0),void 0===d.taskLists&&(d.taskLists=!1),void 0===d.strikethrough&&(d.strikethrough=!1),void 0===d.emoji&&(d.emoji=!1),void 0===d.fencedCodeBlockHighlighting&&(d.fencedCodeBlockHighlighting=!0),void 0===d.fencedCodeBlockDefaultMode&&(d.fencedCodeBlockDefaultMode="text/plain"),void 0===d.xml&&(d.xml=!0),void 0===d.tokenTypeOverrides&&(d.tokenTypeOverrides={});var t,c={header:"header",code:"comment",quote:"quote",list1:"variable-2",list2:"variable-3",list3:"keyword",hr:"hr",image:"image",imageAltText:"image-alt-text",imageMarker:"image-marker",formatting:"formatting",linkInline:"link",linkEmail:"link",linkText:"link",linkHref:"string",em:"em",strong:"strong",strikethrough:"strikethrough",emoji:"builtin"};for(t in c)c.hasOwnProperty(t)&&d.tokenTypeOverrides[t]&&(c[t]=d.tokenTypeOverrides[t]);var f=/^([*\-_])(?:\s*\1){2,}\s*$/,k=/^(?:[*\-+]|^[0-9]+([.)]))\s+/,F=/^\[(x| )\](?=\s)/i,D=d.allowAtxHeaderWithoutSpace?/^(#+)/:/^(#+)(?: |$)/,p=/^ {0,3}(?:\={1,}|-{2,})\s*$/,i=/^[^#!\[\]*_\\<>` "'(~:]+/,E=/^(~~~+|```+)[ \t]*([\w\/+#-]*)[^\n`]*$/,x=/^\s*\[[^\]]+?\]:.*$/,A=/[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/;function C(t,e,i){return(e.f=e.inline=i)(t,e)}function S(t,e,i){return(e.f=e.block=i)(t,e)}function n(t){var e;return t.linkTitle=!1,t.linkHref=!1,t.linkText=!1,t.em=!1,t.strong=!1,t.strikethrough=!1,t.quote=0,t.indentedCode=!1,t.f==v&&(u?u:"xml"==(e=w.innerMode(g,t.htmlState)).mode.name&&null===e.state.tagStart&&!e.state.context&&e.state.tokenize.isInText)&&(t.f=T,t.block=a,t.htmlState=null),t.trailingSpace=0,t.trailingSpaceNewLine=!1,t.prevLine=t.thisLine,t.thisLine={stream:null},null}function a(t,e){var i=t.column()===e.indentation,n=!(n=e.prevLine.stream)||!/\S/.test(n.string),u=e.indentedCode,a=e.prevLine.hr,r=!1!==e.list,o=(e.listStack[e.listStack.length-1]||0)+3,l=(e.indentedCode=!1,e.indentation);if(null===e.indentationDiff&&(e.indentationDiff=e.indentation,r)){for(e.list=null;l<e.listStack[e.listStack.length-1];)e.listStack.pop(),e.listStack.length?e.indentation=e.listStack[e.listStack.length-1]:e.list=!1;!1!==e.list&&(e.indentationDiff=l-e.listStack[e.listStack.length-1])}var h,s=!(n||a||e.prevLine.header||r&&u||e.prevLine.fencedCodeEnd),a=(!1===e.list||a||n)&&e.indentation<=o&&t.match(f),g=null;return 4<=e.indentationDiff&&(u||e.prevLine.fencedCodeEnd||e.prevLine.header||n)?(t.skipToEnd(),e.indentedCode=!0,c.code):t.eatSpace()?null:i&&e.indentation<=o&&(g=t.match(D))&&g[1].length<=6?(e.quote=0,e.header=g[1].length,e.thisLine.header=!0,d.highlightFormatting&&(e.formatting="header"),e.f=e.inline,L(e)):e.indentation<=o&&t.eat(">")?(e.quote=i?1:e.quote+1,d.highlightFormatting&&(e.formatting="quote"),t.eatSpace(),L(e)):!a&&!e.setext&&i&&e.indentation<=o&&(g=t.match(k))?(u=g[1]?"ol":"ul",e.indentation=l+t.current().length,e.list=!0,e.quote=0,e.listStack.push(e.indentation),e.em=!1,e.strong=!1,e.code=!1,e.strikethrough=!1,d.taskLists&&t.match(F,!1)&&(e.taskList=!0),e.f=e.inline,d.highlightFormatting&&(e.formatting=["list","list-"+u]),L(e)):i&&e.indentation<=o&&(g=t.match(E,!0))?(e.quote=0,e.fencedEndRE=new RegExp(g[1]+"+ *$"),e.localMode=d.fencedCodeBlockHighlighting&&(n=g[2]||d.fencedCodeBlockDefaultMode,w.findModeByName&&(h=w.findModeByName(n))&&(n=h.mime||h.mimes[0]),"null"==(h=w.getMode(m,n)).name?null:h),e.localMode&&(e.localState=w.startState(e.localMode)),e.f=e.block=B,d.highlightFormatting&&(e.formatting="code-block"),e.code=-1,L(e)):e.setext||!(s&&r||e.quote||!1!==e.list||e.code||a||x.test(t.string))&&(g=t.lookAhead(1))&&(g=g.match(p))?(e.setext?(e.header=e.setext,e.setext=0,t.skipToEnd(),d.highlightFormatting&&(e.formatting="header")):(e.header="="==g[0].charAt(0)?1:2,e.setext=e.header),e.thisLine.header=!0,e.f=e.inline,L(e)):a?(t.skipToEnd(),e.hr=!0,e.thisLine.hr=!0,c.hr):"["===t.peek()?C(t,e,b):C(t,e,e.inline)}function v(t,e){var i,n=g.token(t,e.htmlState);return u||("xml"==(i=w.innerMode(g,e.htmlState)).mode.name&&null===i.state.tagStart&&!i.state.context&&i.state.tokenize.isInText||e.md_inside&&-1<t.current().indexOf(">"))&&(e.f=T,e.block=a,e.htmlState=null),n}function B(t,e){var i,n=e.listStack[e.listStack.length-1]||0,u=e.indentation<n;return e.fencedEndRE&&e.indentation<=n+3&&(u||t.match(e.fencedEndRE))?(d.highlightFormatting&&(e.formatting="code-block"),u||(i=L(e)),e.localMode=e.localState=null,e.block=a,e.f=T,e.fencedEndRE=null,e.code=0,e.thisLine.fencedCodeEnd=!0,u?S(t,e,e.block):i):e.localMode?e.localMode.token(t,e.localState):(t.skipToEnd(),c.code)}function L(t){var e,i=[];if(t.formatting){i.push(c.formatting),"string"==typeof t.formatting&&(t.formatting=[t.formatting]);for(var n=0;n<t.formatting.length;n++)i.push(c.formatting+"-"+t.formatting[n]),"header"===t.formatting[n]&&i.push(c.formatting+"-"+t.formatting[n]+"-"+t.header),"quote"===t.formatting[n]&&(!d.maxBlockquoteDepth||d.maxBlockquoteDepth>=t.quote?i.push(c.formatting+"-"+t.formatting[n]+"-"+t.quote):i.push("error"))}return t.taskOpen?i.push("meta"):t.taskClosed?i.push("property"):(t.linkHref?i.push(c.linkHref,"url"):(t.strong&&i.push(c.strong),t.em&&i.push(c.em),t.strikethrough&&i.push(c.strikethrough),t.emoji&&i.push(c.emoji),t.linkText&&i.push(c.linkText),t.code&&i.push(c.code),t.image&&i.push(c.image),t.imageAltText&&i.push(c.imageAltText,"link"),t.imageMarker&&i.push(c.imageMarker)),t.header&&i.push(c.header,c.header+"-"+t.header),t.quote&&(i.push(c.quote),!d.maxBlockquoteDepth||d.maxBlockquoteDepth>=t.quote?i.push(c.quote+"-"+t.quote):i.push(c.quote+"-"+d.maxBlockquoteDepth)),!1!==t.list&&((e=(t.listStack.length-1)%3)?1==e?i.push(c.list2):i.push(c.list3):i.push(c.list1)),t.trailingSpaceNewLine?i.push("trailing-space-new-line"):t.trailingSpace&&i.push("trailing-space-"+(t.trailingSpace%2?"a":"b"))),i.length?i.join(" "):null}function e(t,e){if(t.match(i,!0))return L(e)}function T(t,e){var i=e.text(t,e);if(void 0!==i)return i;if(e.list)return e.list=null,L(e);if(e.taskList)return" "===t.match(F,!0)[1]?e.taskOpen=!0:e.taskClosed=!0,d.highlightFormatting&&(e.formatting="task"),e.taskList=!1,L(e);if(e.taskOpen=!1,e.taskClosed=!1,e.header&&t.match(/^#+$/,!0))return d.highlightFormatting&&(e.formatting="header"),L(e);var n=t.next();if(e.linkTitle){e.linkTitle=!1;var i=((i="("===n?")":n)+"").replace(/([.?*+^\[\]\\(){}|-])/g,"\\$1");if(t.match(new RegExp("^\\s*(?:[^"+i+"\\\\]+|\\\\\\\\|\\\\.)"+i),!0))return c.linkHref}if("`"===n)return i=e.formatting,d.highlightFormatting&&(e.formatting="code"),t.eatWhile("`"),o=t.current().length,0!=e.code||e.quote&&1!=o?o==e.code?(r=L(e),e.code=0,r):(e.formatting=i,L(e)):(e.code=o,L(e));if(e.code)return L(e);if("\\"===n&&(t.next(),d.highlightFormatting))return a=L(e),i=c.formatting+"-escape",a?a+" "+i:i;if("!"===n&&t.match(/\[[^\]]*\] ?(?:\(|\[)/,!1))return e.imageMarker=!0,e.image=!0,d.highlightFormatting&&(e.formatting="image"),L(e);if("["===n&&e.imageMarker&&t.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/,!1))return e.imageMarker=!1,e.imageAltText=!0,d.highlightFormatting&&(e.formatting="image"),L(e);if("]"===n&&e.imageAltText)return d.highlightFormatting&&(e.formatting="image"),a=L(e),e.imageAltText=!1,e.image=!1,e.inline=e.f=q,a;if("["===n&&!e.image)return e.linkText&&t.match(/^.*?\]/)||(e.linkText=!0,d.highlightFormatting&&(e.formatting="link")),L(e);if("]"===n&&e.linkText)return d.highlightFormatting&&(e.formatting="link"),a=L(e),e.linkText=!1,e.inline=e.f=t.match(/\(.*?\)| ?\[.*?\]/,!1)?q:T,a;if("<"===n&&t.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/,!1))return e.f=e.inline=M,d.highlightFormatting&&(e.formatting="link"),(a=L(e))?a+=" ":a="",a+c.linkInline;if("<"===n&&t.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/,!1))return e.f=e.inline=M,d.highlightFormatting&&(e.formatting="link"),(a=L(e))?a+=" ":a="",a+c.linkEmail;if(d.xml&&"<"===n&&t.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i,!1))return-1!=(o=t.string.indexOf(">",t.pos))&&(i=t.string.substring(t.start,o),/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(i)&&(e.md_inside=!0)),t.backUp(1),e.htmlState=w.startState(g),S(t,e,v);if(d.xml&&"<"===n&&t.match(/^\/\w*?>/))return e.md_inside=!1,"tag";if("*"===n||"_"===n){for(var u=1,a=1==t.pos?" ":t.string.charAt(t.pos-2);u<3&&t.eat(n);)u++;var r,o=t.peek()||" ",i=!/\s/.test(o)&&(!A.test(o)||/\s/.test(a)||A.test(a)),l=!/\s/.test(a)&&(!A.test(a)||/\s/.test(o)||A.test(o)),h=null,s=null;if(u%2&&(e.em||!i||"*"!==n&&l&&!A.test(a)?e.em!=n||!l||"*"!==n&&i&&!A.test(o)||(h=!1):h=!0),1<u&&(e.strong||!i||"*"!==n&&l&&!A.test(a)?e.strong!=n||!l||"*"!==n&&i&&!A.test(o)||(s=!1):s=!0),null!=s||null!=h)return d.highlightFormatting&&(e.formatting=null==h?"strong":null==s?"em":"strong em"),!0===h&&(e.em=n),!0===s&&(e.strong=n),r=L(e),!1===h&&(e.em=!1),!1===s&&(e.strong=!1),r}else if(" "===n&&(t.eat("*")||t.eat("_"))){if(" "===t.peek())return L(e);t.backUp(1)}if(d.strikethrough)if("~"===n&&t.eatWhile(n)){if(e.strikethrough)return d.highlightFormatting&&(e.formatting="strikethrough"),r=L(e),e.strikethrough=!1,r;if(t.match(/^[^\s]/,!1))return e.strikethrough=!0,d.highlightFormatting&&(e.formatting="strikethrough"),L(e)}else if(" "===n&&t.match("~~",!0)){if(" "===t.peek())return L(e);t.backUp(2)}return d.emoji&&":"===n&&t.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)?(e.emoji=!0,d.highlightFormatting&&(e.formatting="emoji"),a=L(e),e.emoji=!1,a):(" "===n&&(t.match(/^ +$/,!1)?e.trailingSpace++:e.trailingSpace&&(e.trailingSpaceNewLine=!0)),L(e))}function M(t,e){return">"===t.next()?(e.f=e.inline=T,d.highlightFormatting&&(e.formatting="link"),(e=L(e))?e+=" ":e="",e+c.linkInline):(t.match(/^[^>]+/,!0),c.linkInline)}function q(t,e){if(t.eatSpace())return null;var n,t=t.next();return"("===t||"["===t?(e.f=e.inline=(n="("===t?")":"]",function(t,e){var i;return t.next()===n?(e.f=e.inline=T,d.highlightFormatting&&(e.formatting="link-string"),i=L(e),e.linkHref=!1,i):(t.match(r[n]),e.linkHref=!0,L(e))}),d.highlightFormatting&&(e.formatting="link-string"),e.linkHref=!0,L(e)):"error"}var r={")":/^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,"]":/^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\]]|\\.)*\])*?(?=\])/};function b(t,e){return t.match(/^([^\]\\]|\\.)*\]:/,!1)?(e.f=o,t.next(),d.highlightFormatting&&(e.formatting="link"),e.linkText=!0,L(e)):C(t,e,T)}function o(t,e){var i;return t.match("]:",!0)?(e.f=e.inline=l,d.highlightFormatting&&(e.formatting="link"),i=L(e),e.linkText=!1,i):(t.match(/^([^\]\\]|\\.)+/,!0),c.linkText)}function l(t,e){return t.eatSpace()?null:(t.match(/^[^\s]+/,!0),void 0===t.peek()?e.linkTitle=!0:t.match(/^(?:\s+(?:"(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+'|\((?:[^)\\]|\\.)+\)))?/,!0),e.f=e.inline=T,c.linkHref+" url")}var h={startState:function(){return{f:a,prevLine:{stream:null},thisLine:{stream:null},block:a,htmlState:null,indentation:0,inline:T,text:e,formatting:!1,linkText:!1,linkHref:!1,linkTitle:!1,code:0,em:!1,strong:!1,header:0,setext:0,hr:!1,taskList:!1,list:!1,listStack:[],quote:0,trailingSpace:0,trailingSpaceNewLine:!1,strikethrough:!1,emoji:!1,fencedEndRE:null}},copyState:function(t){return{f:t.f,prevLine:t.prevLine,thisLine:t.thisLine,block:t.block,htmlState:t.htmlState&&w.copyState(g,t.htmlState),indentation:t.indentation,localMode:t.localMode,localState:t.localMode?w.copyState(t.localMode,t.localState):null,inline:t.inline,text:t.text,formatting:!1,linkText:t.linkText,linkTitle:t.linkTitle,linkHref:t.linkHref,code:t.code,em:t.em,strong:t.strong,strikethrough:t.strikethrough,emoji:t.emoji,header:t.header,setext:t.setext,hr:t.hr,taskList:t.taskList,list:t.list,listStack:t.listStack.slice(0),quote:t.quote,indentedCode:t.indentedCode,trailingSpace:t.trailingSpace,trailingSpaceNewLine:t.trailingSpaceNewLine,md_inside:t.md_inside,fencedEndRE:t.fencedEndRE}},token:function(t,e){if(e.formatting=!1,t!=e.thisLine.stream){if(e.header=0,e.hr=!1,t.match(/^\s*$/,!0))return n(e),null;if(e.prevLine=e.thisLine,e.thisLine={stream:t},e.taskList=!1,e.trailingSpace=0,e.trailingSpaceNewLine=!1,!e.localState&&(e.f=e.block,e.f!=v)){var i=t.match(/^\s*/,!0)[0].replace(/\t/g," ").length;if(e.indentation=i,e.indentationDiff=null,0<i)return null}}return e.f(t,e)},innerMode:function(t){return t.block==v?{state:t.htmlState,mode:g}:t.localState?{state:t.localState,mode:t.localMode}:{state:t,mode:h}},indent:function(t,e,i){return t.block==v&&g.indent?g.indent(t.htmlState,e,i):t.localState&&t.localMode.indent?t.localMode.indent(t.localState,e,i):w.Pass},blankLine:n,getType:L,blockCommentStart:"\x3c!--",blockCommentEnd:"--\x3e",closeBrackets:"()[]{}''\"\"``",fold:"markdown"};return h},"xml"),w.defineMIME("text/markdown","markdown"),w.defineMIME("text/x-markdown","markdown")});
|
|
5
5
|
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(rt){"use strict";rt.defineMode("javascript",function(e,l){var t,r,O,P,f=e.indentUnit,N=l.statementIndent,U=l.jsonld,o=l.json||U,W=!1!==l.trackScope,u=l.typescript,B=l.wordCharacters||/[\w$\xa1-\uffff]/,F=(e=n("keyword a"),t=n("keyword b"),r=n("keyword c"),O=n("keyword d"),P=n("operator"),{if:n("if"),while:e,with:e,else:t,do:t,try:t,finally:t,return:O,break:O,continue:O,new:n("new"),delete:r,void:r,throw:r,debugger:n("debugger"),var:n("var"),const:n("var"),let:n("var"),function:n("function"),catch:n("catch"),for:n("for"),switch:n("switch"),case:n("case"),default:n("default"),in:P,typeof:P,instanceof:P,true:e={type:"atom",style:"atom"},false:e,null:e,undefined:e,NaN:e,Infinity:e,this:n("this"),class:n("class"),super:n("atom"),yield:r,export:n("export"),import:n("import"),extends:r,await:r});function n(e){return{type:e,style:"keyword"}}var H,D,G=/[+\-*&%=<>!?|~^@]/,J=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function i(e,t,r){return H=e,D=r,t}function d(e,t){var a,r=e.next();if('"'==r||"'"==r)return t.tokenize=(a=r,function(e,t){var r,n=!1;if(U&&"@"==e.peek()&&e.match(J))return t.tokenize=d,i("jsonld-keyword","meta");for(;null!=(r=e.next())&&(r!=a||n);)n=!n&&"\\"==r;return n||(t.tokenize=d),i("string","string")}),t.tokenize(e,t);if("."==r&&e.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/))return i("number","number");if("."==r&&e.match(".."))return i("spread","meta");if(/[\[\]{}\(\),;\:\.]/.test(r))return i(r);if("="==r&&e.eat(">"))return i("=>","operator");if("0"==r&&e.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/))return i("number","number");if(/\d/.test(r))return e.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/),i("number","number");if("/"==r)return e.eat("*")?(t.tokenize=K)(e,t):e.eat("/")?(e.skipToEnd(),i("comment","comment")):tt(e,t,1)?(function(e){for(var t,r=!1,n=!1;null!=(t=e.next());){if(!r){if("/"==t&&!n)return;"["==t?n=!0:n&&"]"==t&&(n=!1)}r=!r&&"\\"==t}}(e),e.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/),i("regexp","string-2")):(e.eat("="),i("operator","operator",e.current()));if("`"==r)return(t.tokenize=L)(e,t);if("#"==r&&"!"==e.peek())return e.skipToEnd(),i("meta","meta");if("#"==r&&e.eatWhile(B))return i("variable","property");if("<"==r&&e.match("!--")||"-"==r&&e.match("->")&&!/\S/.test(e.string.slice(0,e.start)))return e.skipToEnd(),i("comment","comment");if(G.test(r))return">"==r&&t.lexical&&">"==t.lexical.type||(e.eat("=")?"!"!=r&&"="!=r||e.eat("="):/[<>*+\-|&?]/.test(r)&&(e.eat(r),">"==r&&e.eat(r))),"?"==r&&e.eat(".")?i("."):i("operator","operator",e.current());if(B.test(r)){e.eatWhile(B);r=e.current();if("."!=t.lastType){if(F.propertyIsEnumerable(r))return i((t=F[r]).type,t.style,r);if("async"==r&&e.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/,!1))return i("async","keyword",r)}return i("variable","variable",r)}}function K(e,t){for(var r,n=!1;r=e.next();){if("/"==r&&n){t.tokenize=d;break}n="*"==r}return i("comment","comment")}function L(e,t){for(var r,n=!1;null!=(r=e.next());){if(!n&&("`"==r||"$"==r&&e.eat("{"))){t.tokenize=d;break}n=!n&&"\\"==r}return i("quasi","string-2",e.current())}function Q(e,t){t.fatArrowAt&&(t.fatArrowAt=null);var r=e.string.indexOf("=>",e.start);if(!(r<0)){!u||(n=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,r)))&&(r=n.index);for(var n,a=0,i=!1,o=r-1;0<=o;--o){var c=e.string.charAt(o),s="([{}])".indexOf(c);if(0<=s&&s<3){if(!a){++o;break}if(0==--a){"("==c&&(i=!0);break}}else if(3<=s&&s<6)++a;else if(B.test(c))i=!0;else if(/["'\/`]/.test(c))for(;;--o){if(0==o)return;if(e.string.charAt(o-1)==c&&"\\"!=e.string.charAt(o-2)){o--;break}}else if(i&&!a){++o;break}}i&&!a&&(t.fatArrowAt=o)}}var R={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,this:!0,import:!0,"jsonld-keyword":!0};function X(e,t,r,n,a,i){this.indented=e,this.column=t,this.type=r,this.prev=a,this.info=i,null!=n&&(this.align=n)}function Y(e,t,r,n,a){var i=e.cc;for(c.state=e,c.stream=a,c.marked=null,c.cc=i,c.style=t,e.lexical.hasOwnProperty("align")||(e.lexical.align=!0);;)if((i.length?i.pop():o?x:b)(r,n)){for(;i.length&&i[i.length-1].lex;)i.pop()();return c.marked?c.marked:"variable"==r&&function(e,t){if(W){for(var r=e.localVars;r;r=r.next)if(r.name==t)return 1;for(var n=e.context;n;n=n.prev)for(r=n.vars;r;r=r.next)if(r.name==t)return 1}}(e,n)?"variable-2":t}}var c={state:null,column:null,marked:null,cc:null};function s(){for(var e=arguments.length-1;0<=e;e--)c.cc.push(arguments[e])}function p(){return s.apply(null,arguments),!0}function Z(e,t){for(var r=t;r;r=r.next)if(r.name==e)return 1}function a(e){var t=c.state;if(c.marked="def",W){if(t.context)if("var"==t.lexical.info&&t.context&&t.context.block){var r=function e(t,r){{var n;return r?r.block?(n=e(t,r.prev))?n==r.prev?r:new te(n,r.vars,!0):null:Z(t,r.vars)?r:new te(r.prev,new re(t,r.vars),!1):null}}(e,t.context);if(null!=r)return void(t.context=r)}else if(!Z(e,t.localVars))return void(t.localVars=new re(e,t.localVars));l.globalVars&&!Z(e,t.globalVars)&&(t.globalVars=new re(e,t.globalVars))}}function ee(e){return"public"==e||"private"==e||"protected"==e||"abstract"==e||"readonly"==e}function te(e,t,r){this.prev=e,this.vars=t,this.block=r}function re(e,t){this.name=e,this.next=t}var ne=new re("this",new re("arguments",null));function m(){c.state.context=new te(c.state.context,c.state.localVars,!1),c.state.localVars=ne}function ae(){c.state.context=new te(c.state.context,c.state.localVars,!0),c.state.localVars=null}function k(){c.state.localVars=c.state.context.vars,c.state.context=c.state.context.prev}function v(n,a){function e(){var e=c.state,t=e.indented;if("stat"==e.lexical.type)t=e.lexical.indented;else for(var r=e.lexical;r&&")"==r.type&&r.align;r=r.prev)t=r.indented;e.lexical=new X(t,c.stream.column(),n,null,e.lexical,a)}return e.lex=!0,e}function y(){var e=c.state;e.lexical.prev&&(")"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function w(r){return function e(t){return t==r?p():";"==r||"}"==t||")"==t||"]"==t?s():p(e)}}function b(e,t){return"var"==e?p(v("vardef",t),qe,w(";"),y):"keyword a"==e?p(v("form"),oe,b,y):"keyword b"==e?p(v("form"),b,y):"keyword d"==e?c.stream.match(/^\s*$/,!1)?p():p(v("stat"),g,w(";"),y):"debugger"==e?p(w(";")):"{"==e?p(v("}"),ae,be,y,k):";"==e?p():"if"==e?("else"==c.state.lexical.info&&c.state.cc[c.state.cc.length-1]==y&&c.state.cc.pop()(),p(v("form"),oe,b,y,Oe)):"function"==e?p(q):"for"==e?p(v("form"),ae,Pe,b,k,y):"class"==e||u&&"interface"==t?(c.marked="keyword",p(v("form","class"==e?e:t),Fe,y)):"variable"==e?u&&"declare"==t?(c.marked="keyword",p(b)):u&&("module"==t||"enum"==t||"type"==t)&&c.stream.match(/^\s*\w/,!1)?(c.marked="keyword","enum"==t?p(Ze):"type"==t?p(We,w("operator"),z,w(";")):p(v("form"),T,w("{"),v("}"),be,y,y)):u&&"namespace"==t?(c.marked="keyword",p(v("form"),x,b,y)):u&&"abstract"==t?(c.marked="keyword",p(b)):p(v("stat"),me):"switch"==e?p(v("form"),oe,w("{"),v("}","switch"),ae,be,y,y,k):"case"==e?p(x,w(":")):"default"==e?p(w(":")):"catch"==e?p(v("form"),m,ie,b,y,k):"export"==e?p(v("stat"),Ge,y):"import"==e?p(v("stat"),Ke,y):"async"==e?p(b):"@"==t?p(x,b):s(v("stat"),x,w(";"),y)}function ie(e){if("("==e)return p(S,w(")"))}function x(e,t){return ce(e,t,!1)}function h(e,t){return ce(e,t,!0)}function oe(e){return"("!=e?s():p(v(")"),g,w(")"),y)}function ce(e,t,r){if(c.state.fatArrowAt==c.stream.start){var n=r?fe:le;if("("==e)return p(m,v(")"),V(S,")"),y,w("=>"),n,k);if("variable"==e)return s(m,T,w("=>"),n,k)}var a,n=r?M:j;return R.hasOwnProperty(e)?p(n):"function"==e?p(q,n):"class"==e||u&&"interface"==t?(c.marked="keyword",p(v("form"),Be,y)):"keyword c"==e||"async"==e?p(r?h:x):"("==e?p(v(")"),g,w(")"),y,n):"operator"==e||"spread"==e?p(r?h:x):"["==e?p(v("]"),Ye,y,n):"{"==e?we(ve,"}",null,n):"quasi"==e?s(se,n):"new"==e?p((a=r,function(e){return"."==e?p(a?pe:de):"variable"==e&&u?p(Ie,a?M:j):s(a?h:x)})):p()}function g(e){return e.match(/[;\}\)\],]/)?s():s(x)}function j(e,t){return","==e?p(g):M(e,t,!1)}function M(e,t,r){var n=0==r?j:M,a=0==r?x:h;return"=>"==e?p(m,r?fe:le,k):"operator"==e?/\+\+|--/.test(t)||u&&"!"==t?p(n):u&&"<"==t&&c.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/,!1)?p(v(">"),V(z,">"),y,n):"?"==t?p(x,w(":"),a):p(a):"quasi"==e?s(se,n):";"!=e?"("==e?we(h,")","call",n):"."==e?p(ke,n):"["==e?p(v("]"),g,w("]"),y,n):u&&"as"==t?(c.marked="keyword",p(z,n)):"regexp"==e?(c.state.lastType=c.marked="operator",c.stream.backUp(c.stream.pos-c.stream.start-1),p(a)):void 0:void 0}function se(e,t){return"quasi"!=e?s():"${"!=t.slice(t.length-2)?p(se):p(g,ue)}function ue(e){if("}"==e)return c.marked="string-2",c.state.tokenize=L,p(se)}function le(e){return Q(c.stream,c.state),s("{"==e?b:x)}function fe(e){return Q(c.stream,c.state),s("{"==e?b:h)}function de(e,t){if("target"==t)return c.marked="keyword",p(j)}function pe(e,t){if("target"==t)return c.marked="keyword",p(M)}function me(e){return":"==e?p(y,b):s(j,w(";"),y)}function ke(e){if("variable"==e)return c.marked="property",p()}function ve(e,t){return"async"==e?(c.marked="property",p(ve)):"variable"!=e&&"keyword"!=c.style?"number"==e||"string"==e?(c.marked=U?"property":c.style+" property",p(A)):"jsonld-keyword"==e?p(A):u&&ee(t)?(c.marked="keyword",p(ve)):"["==e?p(x,E,w("]"),A):"spread"==e?p(h,A):"*"==t?(c.marked="keyword",p(ve)):":"==e?s(A):void 0:(c.marked="property","get"==t||"set"==t?p(ye):(u&&c.state.fatArrowAt==c.stream.start&&(e=c.stream.match(/^\s*:\s*/,!1))&&(c.state.fatArrowAt=c.stream.pos+e[0].length),p(A)))}function ye(e){return"variable"!=e?s(A):(c.marked="property",p(q))}function A(e){return":"==e?p(h):"("==e?s(q):void 0}function V(n,a,i){function o(e,t){var r;return(i?-1<i.indexOf(e):","==e)?("call"==(r=c.state.lexical).info&&(r.pos=(r.pos||0)+1),p(function(e,t){return e==a||t==a?s():s(n)},o)):e==a||t==a?p():i&&-1<i.indexOf(";")?s(n):p(w(a))}return function(e,t){return e==a||t==a?p():s(n,o)}}function we(e,t,r){for(var n=3;n<arguments.length;n++)c.cc.push(arguments[n]);return p(v(t,r),V(e,t),y)}function be(e){return"}"==e?p():s(b,be)}function E(e,t){if(u)return":"==e?p(z):"?"==t?p(E):void 0}function xe(e,t){if(u&&(":"==e||"in"==t))return p(z)}function he(e){if(u&&":"==e)return c.stream.match(/^\s*\w+\s+is\b/,!1)?p(x,ge,z):p(z)}function ge(e,t){if("is"==t)return c.marked="keyword",p()}function z(e,t){return"keyof"==t||"typeof"==t||"infer"==t||"readonly"==t?(c.marked="keyword",p("typeof"==t?h:z)):"variable"==e||"void"==t?(c.marked="type",p(I)):"|"==t||"&"==t?p(z):"string"==e||"number"==e||"atom"==e?p(I):"["==e?p(v("]"),V(z,"]",","),y,I):"{"==e?p(v("}"),Me,y,I):"("==e?p(V(ze,")"),je,I):"<"==e?p(V(z,">"),z):"quasi"==e?s(Ve,I):void 0}function je(e){if("=>"==e)return p(z)}function Me(e){return e.match(/[\}\)\]]/)?p():","==e||";"==e?p(Me):s(Ae,Me)}function Ae(e,t){return"variable"==e||"keyword"==c.style?(c.marked="property",p(Ae)):"?"==t||"number"==e||"string"==e?p(Ae):":"==e?p(z):"["==e?p(w("variable"),xe,w("]"),Ae):"("==e?s(C,Ae):e.match(/[;\}\)\],]/)?void 0:p()}function Ve(e,t){return"quasi"!=e?s():"${"!=t.slice(t.length-2)?p(Ve):p(z,Ee)}function Ee(e){if("}"==e)return c.marked="string-2",c.state.tokenize=L,p(Ve)}function ze(e,t){return"variable"==e&&c.stream.match(/^\s*[?:]/,!1)||"?"==t?p(ze):":"==e?p(z):"spread"==e?p(ze):s(z)}function I(e,t){return"<"==t?p(v(">"),V(z,">"),y,I):"|"==t||"."==e||"&"==t?p(z):"["==e?p(z,w("]"),I):"extends"==t||"implements"==t?(c.marked="keyword",p(z)):"?"==t?p(z,w(":"),z):void 0}function Ie(e,t){if("<"==t)return p(v(">"),V(z,">"),y,I)}function Te(){return s(z,$e)}function $e(e,t){if("="==t)return p(z)}function qe(e,t){return"enum"==t?(c.marked="keyword",p(Ze)):s(T,E,$,_e)}function T(e,t){return u&&ee(t)?(c.marked="keyword",p(T)):"variable"==e?(a(t),p()):"spread"==e?p(T):"["==e?we(Se,"]"):"{"==e?we(Ce,"}"):void 0}function Ce(e,t){return"variable"!=e||c.stream.match(/^\s*:/,!1)?("variable"==e&&(c.marked="property"),"spread"==e?p(T):"}"==e?s():"["==e?p(x,w("]"),w(":"),Ce):p(w(":"),T,$)):(a(t),p($))}function Se(){return s(T,$)}function $(e,t){if("="==t)return p(h)}function _e(e){if(","==e)return p(qe)}function Oe(e,t){if("keyword b"==e&&"else"==t)return p(v("form","else"),b,y)}function Pe(e,t){return"await"==t?p(Pe):"("==e?p(v(")"),Ne,y):void 0}function Ne(e){return"var"==e?p(qe,Ue):("variable"==e?p:s)(Ue)}function Ue(e,t){return")"==e?p():";"==e?p(Ue):"in"==t||"of"==t?(c.marked="keyword",p(x,Ue)):s(x,Ue)}function q(e,t){return"*"==t?(c.marked="keyword",p(q)):"variable"==e?(a(t),p(q)):"("==e?p(m,v(")"),V(S,")"),y,he,b,k):u&&"<"==t?p(v(">"),V(Te,">"),y,q):void 0}function C(e,t){return"*"==t?(c.marked="keyword",p(C)):"variable"==e?(a(t),p(C)):"("==e?p(m,v(")"),V(S,")"),y,he,k):u&&"<"==t?p(v(">"),V(Te,">"),y,C):void 0}function We(e,t){return"keyword"==e||"variable"==e?(c.marked="type",p(We)):"<"==t?p(v(">"),V(Te,">"),y):void 0}function S(e,t){return"@"==t&&p(x,S),"spread"==e?p(S):u&&ee(t)?(c.marked="keyword",p(S)):u&&"this"==e?p(E,$):s(T,E,$)}function Be(e,t){return("variable"==e?Fe:He)(e,t)}function Fe(e,t){if("variable"==e)return a(t),p(He)}function He(e,t){return"<"==t?p(v(">"),V(Te,">"),y,He):"extends"==t||"implements"==t||u&&","==e?("implements"==t&&(c.marked="keyword"),p(u?z:x,He)):"{"==e?p(v("}"),_,y):void 0}function _(e,t){return"async"==e||"variable"==e&&("static"==t||"get"==t||"set"==t||u&&ee(t))&&c.stream.match(/^\s+#?[\w$\xa1-\uffff]/,!1)?(c.marked="keyword",p(_)):"variable"==e||"keyword"==c.style?(c.marked="property",p(De,_)):"number"==e||"string"==e?p(De,_):"["==e?p(x,E,w("]"),De,_):"*"==t?(c.marked="keyword",p(_)):u&&"("==e?s(C,_):";"==e||","==e?p(_):"}"==e?p():"@"==t?p(x,_):void 0}function De(e,t){if("!"==t)return p(De);if("?"==t)return p(De);if(":"==e)return p(z,$);if("="==t)return p(h);e=c.state.lexical.prev;return s(e&&"interface"==e.info?C:q)}function Ge(e,t){return"*"==t?(c.marked="keyword",p(Xe,w(";"))):"default"==t?(c.marked="keyword",p(x,w(";"))):"{"==e?p(V(Je,"}"),Xe,w(";")):s(b)}function Je(e,t){return"as"==t?(c.marked="keyword",p(w("variable"))):"variable"==e?s(h,Je):void 0}function Ke(e){return"string"==e?p():"("==e?s(x):"."==e?s(j):s(Le,Qe,Xe)}function Le(e,t){return"{"==e?we(Le,"}"):("variable"==e&&a(t),"*"==t&&(c.marked="keyword"),p(Re))}function Qe(e){if(","==e)return p(Le,Qe)}function Re(e,t){if("as"==t)return c.marked="keyword",p(Le)}function Xe(e,t){if("from"==t)return c.marked="keyword",p(x)}function Ye(e){return"]"==e?p():s(V(h,"]"))}function Ze(){return s(v("form"),T,w("{"),v("}"),V(et,"}"),y,y)}function et(){return s(T,$)}function tt(e,t,r){return t.tokenize==d&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(t.lastType)||"quasi"==t.lastType&&/\{\s*$/.test(e.string.slice(0,e.pos-(r||0)))}return m.lex=ae.lex=!0,y.lex=k.lex=!0,{startState:function(e){e={tokenize:d,lastType:"sof",cc:[],lexical:new X((e||0)-f,0,"block",!1),localVars:l.localVars,context:l.localVars&&new te(null,null,!1),indented:e||0};return l.globalVars&&"object"==typeof l.globalVars&&(e.globalVars=l.globalVars),e},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty("align")||(t.lexical.align=!1),t.indented=e.indentation(),Q(e,t)),t.tokenize!=K&&e.eatSpace())return null;var r=t.tokenize(e,t);return"comment"==H?r:(t.lastType="operator"!=H||"++"!=D&&"--"!=D?H:"incdec",Y(t,r,H,D,e))},indent:function(e,t){if(e.tokenize==K||e.tokenize==L)return rt.Pass;if(e.tokenize!=d)return 0;var r,n=t&&t.charAt(0),a=e.lexical;if(!/^\s*else\b/.test(t))for(var i=e.cc.length-1;0<=i;--i){var o=e.cc[i];if(o==y)a=a.prev;else if(o!=Oe&&o!=k)break}for(;("stat"==a.type||"form"==a.type)&&("}"==n||(r=e.cc[e.cc.length-1])&&(r==j||r==M)&&!/^[,\.=+\-*:?[\(]/.test(t));)a=a.prev;var c,s=(a=N&&")"==a.type&&"stat"==a.prev.type?a.prev:a).type,u=n==s;return"vardef"==s?a.indented+("operator"==e.lastType||","==e.lastType?a.info.length+1:0):"form"==s&&"{"==n?a.indented:"form"==s?a.indented+f:"stat"==s?a.indented+(s=t,"operator"==(c=e).lastType||","==c.lastType||G.test(s.charAt(0))||/[,.]/.test(s.charAt(0))?N||f:0):"switch"!=a.info||u||0==l.doubleIndentSwitch?a.align?a.column+(u?0:1):a.indented+(u?0:f):a.indented+(/^(?:case|default)\b/.test(t)?f:2*f)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:o?null:"/*",blockCommentEnd:o?null:"*/",blockCommentContinue:o?null:" * ",lineComment:o?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:o?"json":"javascript",jsonldMode:U,jsonMode:o,expressionAllowed:tt,skipExpression:function(e){Y(e,"atom","atom","true",new rt.StringStream("",2,null))}}}),rt.registerHelper("wordChars","javascript",/[\w$]/),rt.defineMIME("text/javascript","javascript"),rt.defineMIME("text/ecmascript","javascript"),rt.defineMIME("application/javascript","javascript"),rt.defineMIME("application/x-javascript","javascript"),rt.defineMIME("application/ecmascript","javascript"),rt.defineMIME("application/json",{name:"javascript",json:!0}),rt.defineMIME("application/x-json",{name:"javascript",json:!0}),rt.defineMIME("application/manifest+json",{name:"javascript",json:!0}),rt.defineMIME("application/ld+json",{name:"javascript",jsonld:!0}),rt.defineMIME("text/typescript",{name:"javascript",typescript:!0}),rt.defineMIME("application/typescript",{name:"javascript",typescript:!0})});
|
|
6
6
|
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(o){"use strict";o.overlayMode=function(a,n,t){return{startState:function(){return{base:o.startState(a),overlay:o.startState(n),basePos:0,baseCur:null,overlayPos:0,overlayCur:null,streamSeen:null}},copyState:function(e){return{base:o.copyState(a,e.base),overlay:o.copyState(n,e.overlay),basePos:e.basePos,baseCur:null,overlayPos:e.overlayPos,overlayCur:null}},token:function(e,o){return(e!=o.streamSeen||Math.min(o.basePos,o.overlayPos)<e.start)&&(o.streamSeen=e,o.basePos=o.overlayPos=e.start),e.start==o.basePos&&(o.baseCur=a.token(e,o.base),o.basePos=e.pos),e.start==o.overlayPos&&(e.pos=e.start,o.overlayCur=n.token(e,o.overlay),o.overlayPos=e.pos),e.pos=Math.min(o.basePos,o.overlayPos),null==o.overlayCur?o.baseCur:null!=o.baseCur&&o.overlay.combineTokens||t&&null==o.overlay.combineTokens?o.baseCur+" "+o.overlayCur:o.overlayCur},indent:a.indent&&function(e,o,r){return a.indent(e.base,o,r)},electricChars:a.electricChars,innerMode:function(e){return{state:e.base,mode:a}},blankLine:function(e){var o,r;return a.blankLine&&(o=a.blankLine(e.base)),null==(r=n.blankLine?n.blankLine(e.overlay):r)?o:t&&null!=o?o+" "+r:r}}}});
|
|
7
|
+
|
|
8
|
+
/* CodeMirror sTeX / LaTeX Mode */
|
|
9
|
+
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(CodeMirror){"use strict";CodeMirror.defineMode("stex",function(){return{startState:function(){return{cmdState:[]};},copyState:function(s){return{cmdState:s.cmdState.slice(0)};},token:function(source,state){if(source.eatSpace())return null;if(source.match(/^\\[a-zA-Z@]+/)){var cmd=source.current().slice(1);if(cmd==="begin"||cmd==="end")return"keyword";return"builtin";}if(source.match(/^\\[^a-zA-Z@]/))return"atom";if(source.match(/^\$\$|\$|\\\[|\\\]|\\\(|\\\)/))return"string-2";if(source.match(/^%[^\n]*/))return"comment";if(source.match(/^[0-9]+(\.[0-9]+)?/))return"number";if(source.match(/^[{}[\]()]/))return"bracket";if(source.match(/^[-+*\/=<>!^_,|~&:]/))return"operator";source.next();return null;},lineComment:"%"};});CodeMirror.defineMIME("text/x-stex","stex");CodeMirror.defineMIME("text/x-latex","stex");});
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
return "markdown";
|
|
7
7
|
case "json":
|
|
8
8
|
return { name: "javascript", json: true };
|
|
9
|
+
case "latex":
|
|
10
|
+
return "stex";
|
|
9
11
|
default:
|
|
10
12
|
return "text/plain";
|
|
11
13
|
}
|
|
@@ -78,22 +80,48 @@
|
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
function renderAllLatex(root) {
|
|
84
|
+
if (!window.katex) return;
|
|
85
|
+
var scope = root && root.querySelectorAll ? root : document;
|
|
86
|
+
var elements = scope.querySelectorAll("[data-cms-latex]");
|
|
87
|
+
for (var i = 0; i < elements.length; i++) {
|
|
88
|
+
var el = elements[i];
|
|
89
|
+
var formula = el.getAttribute("data-cms-latex") || el.textContent;
|
|
90
|
+
if (!formula) continue;
|
|
91
|
+
try {
|
|
92
|
+
var isDisplay = el.getAttribute("data-cms-latex-display") !== "false";
|
|
93
|
+
window.katex.render(formula, el, {
|
|
94
|
+
displayMode: isDisplay,
|
|
95
|
+
throwOnError: false
|
|
96
|
+
});
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("KaTeX render error:", err);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
81
103
|
window.MacronCms = window.MacronCms || {};
|
|
82
104
|
window.MacronCms.initEditors = initCmsEditors;
|
|
83
105
|
window.MacronCms.refreshEditors = refreshAllEditors;
|
|
106
|
+
window.MacronCms.renderLatex = renderAllLatex;
|
|
107
|
+
|
|
108
|
+
function initAll(root) {
|
|
109
|
+
initCmsEditors(root);
|
|
110
|
+
renderAllLatex(root);
|
|
111
|
+
}
|
|
84
112
|
|
|
85
113
|
// Listen to Turbo & DOM lifecycle events
|
|
86
|
-
document.addEventListener("DOMContentLoaded", function() {
|
|
87
|
-
document.addEventListener("turbo:load", function() {
|
|
88
|
-
document.addEventListener("turbo:render", function() {
|
|
89
|
-
document.addEventListener("turbo:frame-load", function() {
|
|
114
|
+
document.addEventListener("DOMContentLoaded", function() { initAll(); });
|
|
115
|
+
document.addEventListener("turbo:load", function() { initAll(); });
|
|
116
|
+
document.addEventListener("turbo:render", function() { initAll(); });
|
|
117
|
+
document.addEventListener("turbo:frame-load", function() { initAll(); });
|
|
90
118
|
|
|
91
|
-
// Listen for clicks that might toggle hidden forms and refresh editors
|
|
119
|
+
// Listen for clicks that might toggle hidden forms and refresh editors/render latex
|
|
92
120
|
document.addEventListener("click", function(e) {
|
|
93
121
|
var target = e.target;
|
|
94
122
|
if (target && (target.closest("button") || target.closest("a"))) {
|
|
95
123
|
setTimeout(function() {
|
|
96
|
-
|
|
124
|
+
initAll();
|
|
97
125
|
refreshAllEditors();
|
|
98
126
|
}, 50);
|
|
99
127
|
}
|