出品者:zinc

Keila is a self-hostable newsletter platform for creating sign-up forms and sending campaigns via SMTP, AWS SES, Sendgrid, Mailgun, or Postmark. An open-source alternative to Mailchimp for teams of any size.
This block provides a rich block-based campaign editor built on EditorJS, designed for composing newsletter campaigns with structured content blocks (text, image, button, layout columns, social icons). The typical buyer is a developer embedding a newsletter editor UI into a Node.js/TypeScript web application or integrating Keila's frontend editor widgets into their own Phoenix-like or custom backend project.
assets/js/campaign-editors/block/index.js - Main BlockEditor class; mounts EditorJS with all configured toolsassets/js/campaign-editors/block/blocks/button/index.js - Custom EditorJS block: clickable button with label, URL, and centering toggleassets/js/campaign-editors/block/blocks/image/index.js - Custom EditorJS block: image with caption, alt, title, width, link, and alignment tunesassets/js/campaign-editors/block/blocks/layout/index.js - Custom EditorJS block: multi-column grid layout hosting nested EditorJS instancesassets/js/campaign-editors/block/blocks/social-icons/index.js - Custom EditorJS block: row of social media icons with configurable URLs and colorsassets/js/campaign-editors/block/blocks/separator.js - Custom EditorJS block: horizontal rule separatorassets/js/campaign-editors/block/tools/alignment.js - Inline toolbar tool for text alignmentassets/js/campaign-editors/block/tools/text-color.js - Inline toolbar tool for text colorassets/js/campaign-editors/block/tunes/alignment.js - Block tune for per-block alignment controlassets/js/campaign-editors/markdown/index.js - ProseMirror-based Markdown editor variantassets/css/ - SCSS stylesheets including editor-specific stylesassets/js/hooks/ - Phoenix LiveView hooks for wiring editors to the DOMpriv/ - Elixir/Phoenix static assets and templateslib/ - Elixir application source (campaigns, contacts, mailers, etc.)config/ - Elixir app configurationnpm install @editorjs/editorjs @editorjs/header @editorjs/nested-list @editorjs/quote
No native iOS/Android build steps required. These are pure JavaScript packages. If you use the Markdown editor variant (campaign-editors/markdown/), you will also need:
隔離されたサンドボックスを起動しサーバー側で実行 — ローカル設定不要。
このバージョンの Tetrees AI Review
This Elixir, JavaScript cli / script completed archive review with strong static results. Structure, dependency manifests, documentation, functional source, and common risk patterns were checked by the Tetrees verification pipeline; runtime phases are stated separately.
Deterministic AVCP artifact review
パイプライン avcp-2026-08-04.1 · SHA-256 6459bb7b7d0bce94…
This version-scoped review deterministically inspects the submitted archive for structure, dependencies, documentation, functional source, and common malicious or high-risk signals. Build and test phases are reported as passed only after an isolated sandbox audition. It is not a guarantee of perfect security.
レビュー日 2026年8月4日
この製品をお使いのAI IDE・Webビルダー・クラウドIDEに直接取り込みます。
Tetreesを対応AI IDEに接続し、所有製品の一覧取得と検証済みZIPの取得を、販売者のアップロード権限を公開せずに行えます。
まだレビューがありません。
Sign in to join the discussion
Loading discussion…
npm install prosemirror-state prosemirror-view prosemirror-model prosemirror-inputrules prosemirror-keymap markdown-it
Copy the source/assets/js/campaign-editors/block/ directory into your project, e.g. src/editors/block/.
Ensure your bundler (Webpack, Vite, esbuild) resolves the internal relative imports correctly. No path alias is required since all imports are relative.
Add a container element to your HTML that the editor will mount into, and a sibling element with the id block-container-assets containing the icon/label elements the blocks read via document.querySelector:
<div id="block-container-assets" style="display:none">
<span class="editor-placeholder">Start writing...</span>
<span class="editor-button-title">Button</span>
<span class="icon-button-alt"><!-- SVG --></span>
<span class="editor-button-make-full-width-label">Full width</span>
<span class="editor-button-make-centered-label">Centered</span>
<span class="editor-layout-placeholder">Add content...</span>
<!-- additional icon/label spans required by Image and SocialIcons blocks -->
</div>
<div id="editor-mount"></div>
<textarea id="editor-source" style="display:none">{"blocks":[]}</textarea>
BlockEditor:import BlockEditor from "./editors/block/index.js"
const source = document.getElementById("editor-source") as HTMLTextAreaElement
const editor = new BlockEditor("editor-mount", source)
#block-container-assets.class BlockEditor {
constructor(place: string, source: HTMLTextAreaElement): BlockEditor
}
place is the id of the DOM element to mount into. source is a <textarea> whose .value is a JSON string of EditorJS block data ({"blocks": [...], "version": "..."}). Instantiate once per editor mount point. The editor reads initial data from source.value on construction.
class Button {
constructor({ data, block }: { data: { label: string | null, url: string | null, centered: boolean }, block: any }): Button
static get toolbox(): { title: string, icon: string }
renderSettings(): Array<{ icon: string, label: string, onActivate: () => void, closeOnActivate: boolean }>
save(_blockContent: HTMLElement): { label: string, url: string, centered: boolean }
toggleCentered(): void
}
Register as an EditorJS tool under key "button". Use when you need a styled CTA link block inside the editor. renderSettings() exposes a toggle for full-width vs. centered rendering.
class Layout {
constructor({ data, config, api, block }: {
data: { blocks: any[], columns: number, ratio: string },
config: { tools: Record<string, any> },
api: any,
block: any
}): Layout
render(): HTMLElement
save(_blockContent: HTMLElement): Promise<{ blocks: any[], columns: number, ratio: string }>
}
Renders a CSS grid with columns columns and a ratio string like "1-1" or "1-2". Each column hosts a nested EditorJS instance initialized from config.tools. Use when multi-column newsletter layouts are required.
class Image {
constructor({ data, api, block }: {
data: { caption: string | null, alt: string | null, title: string | null, width: string | null, image: { id: string | null, src: string | null }, link: { url: string | null }, tunes?: { alignment?: string } },
api: any,
block: any
}): Image
render(): HTMLElement
save(_blockContent: HTMLElement): object
}
Supports left/center/right alignment via tunes, a link URL, caption, and an image picker integrated with the asset system via #block-container-assets. Use for inline images within newsletter blocks.
Mount the block editor onto a form textarea to capture campaign content:
import BlockEditor from "./editors/block/index.js"
document.addEventListener("DOMContentLoaded", () => {
const sourceEl = document.getElementById("campaign-body") as HTMLTextAreaElement
if (!sourceEl.value) {
sourceEl.value = JSON.stringify({ time: Date.now(), blocks: [], version: "2.28.0" })
}
new BlockEditor("campaign-editor", sourceEl)
})
Load saved campaign data from your backend and restore it into the editor:
import BlockEditor from "./editors/block/index.js"
async function mountEditor(campaignId: string) {
const res = await fetch(`/api/campaigns/${campaignId}/body`)
const json = await res.json()
const sourceEl = document.getElementById("editor-source") as HTMLTextAreaElement
sourceEl.value = JSON.stringify(json)
new BlockEditor("editor-mount", sourceEl)
}
mountEditor("abc123")
Use the Button block directly in your own EditorJS instance without the full BlockEditor wrapper:
import EditorJS from "@editorjs/editorjs"
import Button from "./editors/block/blocks/button/index.js"
const editor = new EditorJS({
holder: "my-editor",
tools: {
button: {
class: Button
}
},
data: {
blocks: [
{
type: "button",
data: { label: "Sign up now", url: "https://example.com", centered: true }
}
]
}
})
editor.save().then(output => {
console.log(output)
})
assets/js/campaign-editors/block/index.js - Orchestrates all tools and tunes into a single EditorJS configuration; the only entry point buyers should import.assets/js/campaign-editors/block/blocks/button/index.js - Self-contained button block; reads DOM for labels, handles centering toggle via renderSettings.assets/js/campaign-editors/block/blocks/image/index.js - Image block with alignment tunes, ResizeObserver for responsive display, and caption/link fields.assets/js/campaign-editors/block/blocks/layout/index.js - Creates nested EditorJS instances per column; forwards config.tools so nested editors share the same tool set.assets/js/campaign-editors/block/blocks/social-icons/index.js - Manages a list of known social platforms with brand colors; renders icon rows with per-icon URL/color overrides.assets/js/campaign-editors/block/blocks/separator.js - Minimal block rendering an <hr> with no configurable data.assets/js/campaign-editors/block/tools/alignment.js - Inline toolbar tool that wraps selected content in an alignment span.assets/js/campaign-editors/block/tools/text-color.js - Inline toolbar tool applying a color style to selected text.assets/js/campaign-editors/block/tunes/alignment.js - Block-level tune adding alignment CSS classes to entire blocks.assets/js/campaign-editors/markdown/ - Alternative ProseMirror-based editor for markdown campaigns; independent of the block editor.assets/css/ - SCSS styles including _markdown_wysiwyg.scss and app.scss for editor chrome.lib/ - Elixir/Phoenix backend: campaign delivery, contacts, forms, API. Not used on the JS side.config/ - Elixir runtime and environment config. Irrelevant to the JS editor.priv/ - Phoenix static assets, migrations, gettext translations.document.querySelector("#block-container-assets .editor-button-title") returns null: All blocks call document.querySelector at render/toolbox time against a hidden DOM node. Mount this node before instantiating BlockEditor or you will get null-reference errors.Layout does not propagate changes: Layout calls this.block.dispatchChange() in the nested editor's onChange. If you override onChange at the top level, ensure you do not replace the inner handler; use the top-level onChange config key only.save() on Layout is async: Layout.save() calls editor.save() on each nested instance, returning a Promise. Callers of the top-level editor.save() must await to get fully resolved column data.Layout uses grid-cols-2, grid-cols-3, col-span-1, etc. built at runtime from the ratio string. Add them to your Tailwind safelist or rely on the _requiredClasses comment pattern already present in the source.@editorjs/nested-list vs @editorjs/list version mismatch: The source imports @editorjs/nested-list specifically, not the plain list package. Installing @editorjs/list instead will cause a missing-tool error at runtime.esModuleInterop), ensure @editorjs/editorjs is not transpiled as CommonJS or you will see EditorJS is not a constructor.I have purchased the Keila Block Campaign Editor source code located in the `source/` directory.
I also have a `USAGE.md` integration guide in the same directory.
The upstream project is `keila` (https://github.com/pentacent/keila).
The main entry point is `source/assets/js/campaign-editors/block/index.js`, which exports a
default class `BlockEditor`.
Please integrate this editor into my existing project step by step:
1. Read `USAGE.md` fully before making any changes.
2. Install all required npm dependencies listed in the `## Required dependencies` section.
3. Copy the block editor source files into `src/editors/block/` in my project.
4. Add the required `#block-container-assets` DOM node to my HTML template using the structure
shown in `USAGE.md` under `## Project setup`.
5. Create a TypeScript module `src/mountEditor.ts` that imports `BlockEditor` from
`src/editors/block/index.js` and mounts it to a `<div id="editor-mount">` in my page,
reading initial data from a `<textarea id="editor-source">`.
6. Confirm that the `Layout` block receives the correct `config.tools` object so nested
editors share the same tool configuration as the top-level editor.
7. Point out any Tailwind safelist entries I need to add based on the dynamic class names
used in `Layout`.
Show me each file you create or modify.
Keila is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). Any project that incorporates this source and is accessible over a network must make its full source code available under the same license. See source/LICENSE.md for the full text.
Upstream repository: https://github.com/pentacent/keila
This product’s unique contribution is clean room implementation of valuable code block into AVCP compatible standard.
完全なインストールガイドと統合プロンプトは購入後に解放されます。
PHP, Laravel & Business Scripts
無料