Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Streams Ace Integration Extension Laravel Package

anomaly/streams_ace_integration-extension

Streams ACE Integration Extension connects the Streams Platform with ACE, letting you manage and edit stream entries directly from the control panel. It bridges ACE's editing tools and Streams data for a smoother admin workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify compatibility before anything else. This extension is effectively unproven (0 stars, 0 dependents, score 0.03), so the first task is not "install it" but "prove it works on your Streams version". Open the repo and read, in this order:

  1. composer.json — the anomaly/streams-platform constraint tells you which PyroCMS 3.x / Streams line it targets. If it's ^1.0 against a 1.3+ install, expect friction.
  2. addon.php — the Streams manifest (name, slug, namespace, version, provides). The provides key tells you what it actually contributes (asset, field_type, plugin, etc.), which is the single most useful piece of metadata here.
  3. src/StreamsAceIntegrationExtensionServiceProvider.php — the whole package is likely 50–200 lines; you can read it in full. Look for $this->asset->add(...), view overrides, and any FieldType/FormBuilder hooks.
  4. resources/ — js/, scss/, views/, config/. This is where the Ace bundle and the init script live.

Install as a VCS/local path dependency, not from Packagist (it almost certainly isn't published, and even if it is, there's no release hygiene to rely on):

"repositories": [
  { "type": "vcs", "url": "https://github.com/anomalylabs/streams_ace_integration-extension" }
],
"require": {
  "anomaly/streams_ace_integration-extension": "dev-master"
}

Then run the Streams addon lifecycle, in order:

composer update anomaly/streams_ace_integration-extension
php artisan addon:install anomaly/streams_ace_integration-extension
php artisan addon:publish anomaly/streams_ace_integration-extension
php artisan assets:clear

addon:install registers it in addons state; addon:publish copies resources/ into public/addons/{vendor}/{slug}/; assets:clear is mandatory because Streams caches the asset manifest and you will otherwise stare at stale JS.

First use case. The realistic day-to-day win is a code/config textarea in the control panel that gets syntax highlighting, line numbers, and bracket matching instead of a bare <textarea>. Start there: create a textarea field on a stream, edit an entry, and check the DOM for a data-* attribute or a .ace-editor class injected by the extension's init script. If the extension registers globally in the CP, that's it — you get it for free. If it only exposes an asset/mode, you'll wire it into your own field type (see below).

If nothing visible happens, View::make('anomaly.streams_ace_integration_extension::scripts') in a Blade partial is the fastest way to confirm the addon is installed, published, and namespaced correctly before you debug anything else.

Implementation Patterns

Asset registration from your own addon, borrowing the extension's Ace bundle. Extension service providers are the idiomatic place to push CP assets. Gate on the control panel so the frontend doesn't pay for a 500KB editor:

protected function loadAssets()
{
    if (!request()->is('admin/*')) {
        return;
    }

    $this->asset->add(
        'anomaly.streams_ace_integration_extension::js/ace.js'
    )->add(
        'anomaly.streams_ace_integration_extension::js/init.js'
    );
}

The vendor.slug::path notation resolves through the addon's published public directory — this is the pattern to copy even if you end up replacing their assets with your own pinned Ace build.

Attribute-driven initialization. The clean integration is to let the markup declare intent and let JS find it, rather than hardcoding a field slug. Emit data-ace, data-ace-mode="php", data-ace-theme="monokai" from your form/field type, then:

document.querySelectorAll('textarea[data-ace]').forEach(function (el) {
    if (el.dataset.aceInitialized) return;      // guard: Streams re-renders forms
    el.dataset.aceInitialized = '1';

    var editor = ace.edit(el.nextElementSibling); // the div the extension injects
    editor.session.setMode('ace/mode/' + (el.dataset.aceMode || 'text'));
    editor.setTheme('ace/theme/' + (el.dataset.aceTheme || 'chrome'));
    editor.session.setValue(el.value);
    editor.session.on('change', function () { el.value = editor.getValue(); });
});

Field type wrapper. If you want this to be a first-class field type rather than a per-form hack, subclass the textarea field type and override getInputView() to render your Ace-enabled partial. That's the Streams-native extension point and it survives CP upgrades far better than JS selectors against vendor markup.

Sync on submit, not just on change. editor.session.on('change') covers typing, but Streams forms can be submitted programmatically (modal save handlers, AjaxComplete flows). Add a belt-and-braces hook:

$(document).on('submit', 'form', function () {
    $(this).find('textarea[data-ace]').each(function () {
        var ed = ace.edit($(this).next()[0]);
        $(this).val(ed.getValue());
    });
});

Config-driven defaults. Publish the extension's config (php artisan addon:publish then edit under resources/config/) for theme/mode/font-size defaults so per-project customization doesn't live in a forked JS file. If the extension ships no config, add one in your addon and read it via config('anomaly.extension.my_addon::ace.theme').

Compose, don't fork — for now. Keep your init script in a small in-house addon (acme/ace-extension) that depends on this one, and let the vendor package supply only the Ace core. When the upstream inevitably goes stale, you swap the core build without touching your integration logic.

Gotchas and Tips

Ace's worker files are the #1 failure mode. Ace spawns worker-*.js via a computed basePath. Streams pipes assets through its own manifest/concatenation, which breaks relative resolution. Symptom: the editor renders but syntax checking is dead and the console shows 404s. Fix explicitly:

ace.config.set('basePath', '/addons/anomalylabs/streams_ace_integration_extension/js');

Verify the actual published path with ls public/addons/anomalylabs/. If the slug differs from the repo name (common — underscore vs hyphen, -extension suffix), the hardcoded path in your JS will 404 while Asset::add() silently succeeds.

CSP. Ace uses new Function in some modes and its eval-based worker loader trips a strict script-src without unsafe-eval. Either relax CSP for the CP routes or use the CSP-safe Ace build. This bites in production only, because dev usually runs without the CSP header.

Asset cache is the second-most-common "it doesn't work." After any change to published JS/CSS: php artisan assets:clear. Streams will happily serve the old bundle forever otherwise. Add it to your deploy script next to view:clear.

Multiple editors, stale instances. Streams CP pages routinely have several forms and modals mounted at once. Ace instances attached to a removed DOM node keep listeners and throw on editor.getValue() later. Always editor.destroy() in your teardown, and re-init on Streams' ajax/modal events (ajaxComplete, modal open) rather than only on DOMContentLoaded.

Keyboard conflicts. Ace swallows Ctrl+F, Ctrl+H, tab, and escape, which collide with CP hotkeys and the modal close handler. Register commands with editor.commands.addCommand({..., bindKey: {win: 'Ctrl-S'}}) and event.stopPropagation() where needed; otherwise users get trapped in the editor with a modal that won't close.

Don't register on the frontend. If the service provider calls $this->asset->add(...) unconditionally, every public page loads Ace. Cost is real and the benefit is zero. Guard on the CP request as shown above.

The hidden textarea is the source of truth. If you render Ace into a <div> sibling and forget the change→val() sync, entries save as empty strings and look like a database problem. Always confirm the textarea's value in devtools before saving.

Overrides live in your project, not vendor/. Streams supports view overrides via the standard view namespace; put them in resources/views/vendor/... in your app. Editing published files under public/addons/ is guaranteed to be clobbered by the next addon:publish.

Pin and fork. 0 stars / 0 dependents means no community pressure to fix breakage. Before shipping, fork to your org, tag a commit, and point Composer at your fork. If compatibility with your Streams version is broken, the fix is usually a one-line addon.php version bump or an asset path correction — small enough to maintain yourself. Budget for it.

Debugging checklist when nothing appears: (1) php artisan addon:list shows it installed? (2) public/addons/anomalylabs/streams_ace_integration_extension/ populated? (3) network tab shows the Ace JS request with 200, not 404? (4) addon.php provides actually declares an asset? (5) are you on an /admin route?

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr