Skip to content

Convert to JS Widget#18

Open
jelaniwoods wants to merge 5 commits into
mainfrom
751-jw
Open

Convert to JS Widget#18
jelaniwoods wants to merge 5 commits into
mainfrom
751-jw

Conversation

@jelaniwoods
Copy link
Copy Markdown

@jelaniwoods jelaniwoods commented May 21, 2026

Resolves https://github.com/firstdraft/appdev/issues/751

Problem

DevToolbar clutters up the source code a lot, which makes teaching them to use View Source harder.

Maybe we can make it into a JS widget that loads itself dynamically so that the bulk of the code isn't in the Source?

Solution

This PR updates the dev_toolbar to now be injected into the DOM via JavaScript. Viewing the page source in the Rails will no longer show the style tag and HTML for this gem.

image image

Review notes

Take any AD1 Rails app and:

  • Update the gem version
    gem "dev_toolbar", github: "firstdraft/dev_toolbar", branch: "751-jw"
  • Install the JS and CSS
    # config/importmap.rb
    pin "dev_toolbar", to: "dev_toolbar/index.js"
    pin "dev_toolbar/toolbar", to: "dev_toolbar/toolbar.js"
    
    # app/javascripts/application.js
    import "dev_toolbar"
    
    # app/assets/stylesheets/application.css
    @import url("dev_toolbar.css");
  • visit any page to interact with the widget
    dev_toolbar-js-widget

}

#dev-toolbar-toggle {
all: unset;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the toolbar icon from a link to a button, since clicking on it doesn't navigate to another (part of the) page. all: unset resets the default styling of the button.

def show
@erd_path = Rails.root.join("erd.png")
render :show
render "dev_toolbar/erd/show", formats: [:html]
Copy link
Copy Markdown
Author

@jelaniwoods jelaniwoods May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Rails app was not finding this view template until I specified it with this default. I don't this this should be necessary, but I wasn't able to figure it out. I'd love to learn why the previous way wasn't working.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also looked into this for a bit, but wasn't able to figure it out. Claude 4.7 sent me down some paths, which were all wrong in the end. This is a weird one, but I'm going to leave it alone now.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7cd1f924bf

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/assets/javascripts/dev_toolbar/index.js Outdated
Comment thread lib/dev_toolbar/middleware.rb
links.classList.toggle('hidden');
});
</script>
<meta name="dev_toolbar_config" content='#{toolbar_links_content}'>
Copy link
Copy Markdown
Author

@jelaniwoods jelaniwoods May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configured toolbar links needed to be passed from the initializer to the JavaScript file somehow so I decided to store them in a meta tag as JSON. Since it's in the <head> it won't clutter up the <body>, which is where students will mostly be looking.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is very small and discrete in the source.

@jelaniwoods
Copy link
Copy Markdown
Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29ae63b3c7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/assets/javascripts/dev_toolbar/index.js Outdated
Comment thread lib/dev_toolbar/middleware.rb
Comment thread lib/dev_toolbar/middleware.rb
@jelaniwoods
Copy link
Copy Markdown
Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9eb1aae206

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

</div>
</div>
`
document.body.insertAdjacentHTML('beforeend', toolbar_html)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make toolbar render idempotent on repeated Turbo loads

Toolbar.render() always appends a new #dev-toolbar node with insertAdjacentHTML, so after Turbo restores a cached page (for example, browser Back/Forward) and fires turbo:load again, the page accumulates duplicate toolbars and duplicated toggle controls. This regression was introduced by moving rendering client-side without a guard/removal step; add an existence check (or remove before cache) so repeated loads do not duplicate UI.

Useful? React with 👍 / 👎.

Comment thread lib/dev_toolbar/middleware.rb
});
}

document.addEventListener("turbo:load", function() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always expect the turbo:load event? Should we also listen for DOMContentLoaded, in case turbo:load does not occur?

Copy link
Copy Markdown
Contributor

@bpurinton bpurinton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was curious about ways to further reduce the configuration needed for a project. What do you think of this approach that only requires one config step, import "dev_toolbar" in application.js?

#19

@jelaniwoods
Copy link
Copy Markdown
Author

jelaniwoods commented Jun 1, 2026

I was curious about ways to further reduce the configuration needed for a project. What do you think of this approach that only requires one config step, import "dev_toolbar" in application.js?

@bpurinton

I still think the main thing to optimize is minimizing the impact (lines of code added) to the source HTML page. The number of files to configure for the gem to work is less important in my mind because none of our students are going to manually install this gem. All projects and the template will have it pre-installed and configured for them. On our end, we can write a script or installer to make that process easier to make new projects and/or trigger project syncing if there are too many steps right now.

That said, we can probably condense the importmap assets into one file, but I think the CSS should be loaded through the asset pipeline of the Rails app instead of being added through the middleware. I intentionally made that change since injecting a style tag with middleware will cause it to appear in the source HTML, which we're trying to avoid. Before I make any changes, what are your thoughts on this?

@bpurinton
Copy link
Copy Markdown
Contributor

Before I make any changes, what are your thoughts on this?

I agree with your reasoning here. I was thinking it would be more lightweight to maintain and hook into projects with my update, but I don't think we need to worry much about that. I'll close my experimental PR and delete that branch.

I left one question above about turbo:load listener, but otherwise this seems to be working well in my testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants