{"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1760552652", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1760552652, "node_id": "IC_kwDOBm6k_c5o7-bM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-10-12T23:59:21Z", "updated_at": "2023-10-12T23:59:21Z", "author_association": "OWNER", "body": "I'm landing this despite the cog failures. I'll fix them on main if I have to.", "reactions": "{\"total_count\": 1, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 1, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1632867333", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1632867333, "node_id": "IC_kwDOBm6k_c5hU5QF", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2023-07-12T16:38:27Z", "updated_at": "2023-10-12T23:52:24Z", "author_association": "NONE", "body": "## [Codecov](https://app.codecov.io/gh/simonw/datasette/pull/2052?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) Report\nAll modified lines are covered by tests :white_check_mark:\n> Comparison is base [(`3feed1f`)](https://app.codecov.io/gh/simonw/datasette/commit/3feed1f66e2b746f349ee56970a62246a18bb164?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) 92.46% compared to head [(`8ae479c`)](https://app.codecov.io/gh/simonw/datasette/pull/2052?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) 92.69%.\n> Report is 112 commits behind head on main.\n\n
Additional details and impacted files\n\n\n```diff\n@@ Coverage Diff @@\n## main #2052 +/- ##\n==========================================\n+ Coverage 92.46% 92.69% +0.22% \n==========================================\n Files 38 40 +2 \n Lines 5750 6047 +297 \n==========================================\n+ Hits 5317 5605 +288 \n- Misses 433 442 +9 \n```\n\n\n[see 19 files with indirect coverage changes](https://app.codecov.io/gh/simonw/datasette/pull/2052/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison)\n\n\n
\n\n[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/simonw/datasette/pull/2052?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). \n:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1760545012", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1760545012, "node_id": "IC_kwDOBm6k_c5o78j0", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-10-12T23:48:16Z", "updated_at": "2023-10-12T23:48:16Z", "author_association": "OWNER", "body": "Oh! I think I broke Cog on `main` and these tests are running against this branch rebased against main.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1760542865", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1760542865, "node_id": "IC_kwDOBm6k_c5o78CR", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-10-12T23:44:53Z", "updated_at": "2023-10-12T23:45:15Z", "author_association": "OWNER", "body": "Weird, the `cog` check is failing in CI.\r\n\r\n```\r\nRun cog --check docs/*.rst\r\n cog --check docs/*.rst\r\n shell: /usr/bin/bash -e {0}\r\n env:\r\n pythonLocation: /opt/hostedtoolcache/Python/3.9.18/x64\r\n PKG_CONFIG_PATH: /opt/hostedtoolcache/Python/3.9.18/x64/lib/pkgconfig\r\n Python_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.18/x64\r\n Python2_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.18/x64\r\n Python3_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.18/x64\r\n LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.9.18/x64/lib\r\nCheck failed\r\nChecking docs/authentication.rst\r\nChecking docs/binary_data.rst\r\nChecking docs/changelog.rst\r\nChecking docs/cli-reference.rst\r\nChecking docs/configuration.rst (changed)\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1722943484", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1722943484, "node_id": "IC_kwDOBm6k_c5msgf8", "user": {"value": 30934, "label": "20after4"}, "created_at": "2023-09-18T08:14:47Z", "updated_at": "2023-09-18T08:14:47Z", "author_association": "NONE", "body": "This is such a well thought out contribution. I don't think I've seen such a thoroughly considered PR on any project in recent memory.", "reactions": "{\"total_count\": 1, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 1, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1630776144", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1630776144, "node_id": "IC_kwDOBm6k_c5hM6tQ", "user": {"value": 9020979, "label": "hydrosquall"}, "created_at": "2023-07-11T12:54:03Z", "updated_at": "2023-07-11T12:54:03Z", "author_association": "CONTRIBUTOR", "body": "Thanks for the review and the code pointers @simonw - I've made the suggested edits, fixed the renamed variable, and confirmed that the panels still render on the `table` and `database` views. ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1629337927", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1629337927, "node_id": "IC_kwDOBm6k_c5hHblH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-07-10T16:43:38Z", "updated_at": "2023-07-10T16:44:23Z", "author_association": "OWNER", "body": "I tried running this locally just now. I made one edit:\r\n```diff\r\ndiff --git a/demos/plugins/example_js_manager_plugins.py b/demos/plugins/example_js_manager_plugins.py\r\nindex 7bdb9f3f..f9dfa8e6 100644\r\n--- a/demos/plugins/example_js_manager_plugins.py\r\n+++ b/demos/plugins/example_js_manager_plugins.py\r\n@@ -15,6 +15,6 @@ def extra_js_urls(view_name):\r\n if view_name in PERMITTED_VIEWS:\r\n return [\r\n {\r\n- \"url\": f\"/-/demos/plugins/static/table-example-plugins.js\",\r\n+ \"url\": f\"/static/table-example-plugins.js\",\r\n }\r\n ]\r\n```\r\nAnd then started it running like this:\r\n```bash\r\nwget https://datasette.io/content.db\r\n```\r\n```bash\r\ndatasette content.db \\\r\n --plugins-dir demos/plugins \\\r\n --static static:datasette/demos/plugins/static\r\n```\r\n\r\nIt didn't quite work for me - I got this error on a table page:\r\n\r\n\"image\"\r\n\r\nAnd this error on a query page:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1616095810", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1616095810, "node_id": "IC_kwDOBm6k_c5gU6pC", "user": {"value": 15178711, "label": "asg017"}, "created_at": "2023-07-01T20:31:31Z", "updated_at": "2023-07-01T20:31:31Z", "author_association": "CONTRIBUTOR", "body": "> Just curious, is there a query that can be used to compile this programmatically, or did you identify these through memory?\r\n\r\nI just did a github search for `user:simonw \"def extra_js_urls(\"` ! Though I'm sure other plugins made by people other than Simon also exist out there https://github.com/search?q=user%3Asimonw+%22def+extra_js_urls%28%22&type=code", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1615997736", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1615997736, "node_id": "IC_kwDOBm6k_c5gUiso", "user": {"value": 9020979, "label": "hydrosquall"}, "created_at": "2023-07-01T16:55:24Z", "updated_at": "2023-07-01T16:55:24Z", "author_association": "CONTRIBUTOR", "body": "> Ok @hydrosquall a couple things before this PR should be good to go:\r\n\r\nThank you @asg017 ! I've pushed both suggested changes onto this branch.\r\n\r\n> Not sure how difficult it'll be to inject it server-side\r\n\r\nIf we are OK with having a build system, it would free me up to do do many things! We could make datasette-manager.js a server-side rendered file as a \"template\" instead of having it as a static JS file, but I'm not sure it's worth the extra jump in complexity / loss of syntax highlighting in the JS file.\r\n\r\nIn the short-term, I could see an intermediary solution where a unit test in the preferred language was able to read both `version.py` and `datasette-manager.js`, and make sure that the strings versions are in sync. (This assumes that we want the manager and datasette's versions to be synced, and not decoupled). Since the version is not changing very often, a \"manual sync\" might be good enough. \r\n\r\n> In terms of how to integrate this into Datasette, a few options I can see working:\r\n\r\nThis sounds good to me. I'm not sure how to add a settings flag, but will be interested to see the PR that adds support for it.\r\n\r\n> I'm also curious to see how \"plugins for a plugin' would work\r\n\r\nI'm comfortable to wait until we have a realistic usecase for this. In the short term, I think we could give plugins a way to grant access to a \"public API of other plugins\", and also ask to be notified when plugins with other names have loaded, but don't picture the datasette manager getting more involved than that. \r\n\r\n> here's a list of Simon's Datasette plugins that use \"extra_js_urls()\"\r\n\r\nNeat, thanks for compiling this list! Just curious, is there a query that can be used to compile this programmatically, or did you identify these through memory?\r\n\r\n> I want to make a javascript plugin on top of the code-mirror editor to make a few things nicer (function auto-complete, table/column descriptions, etc.)\r\n\r\nI look forward to trying this out \ud83d\udc4d \r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1613778296", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1613778296, "node_id": "IC_kwDOBm6k_c5gME14", "user": {"value": 15178711, "label": "asg017"}, "created_at": "2023-06-29T20:36:09Z", "updated_at": "2023-06-29T20:36:09Z", "author_association": "CONTRIBUTOR", "body": "Ok @hydrosquall a couple things before this PR should be good to go:\r\n\r\n- Can we move `datasette/static/table-example-plugins.js` into `demos/plugins/static`?\r\n- For `datasetteManager.VERSION`, can we fill that in or just comment it out for now? Not sure how difficult it'll be to inject it server-side. I imagine we could also have a small build process with esbuild/rollup that just injects a version string into `manager.js` directly, so we don't have to worry about server-rendering (but that can be a future PR)\r\n\r\nIn terms of how to integrate this into Datasette, a few options I can see working:\r\n\r\n- Push this as-is and figure it out before the next release\r\n- Hide this feature behind a settings flag (`--setting unstable-js-plugins on`) and use that setting to hide/show `` in `base.html`\r\n\r\nI'll let @simonw decide which one to work with. I kindof like the idea of having an \"unstable\" opt-in process to enable JS plugins, to give us time to try it out with a wide variety of plugins until we feel its ready.\r\n\r\nI'm also curious to see how \"plugins for a plugin' would work, like #1542. For example, if the leaflet plugin showed default markers, but also included its own hook for other plugins to add more markers/styling. I'm imagine that the individual plugin would re-create their own plugin system compared to this, since handling \"plugins of plugins\" at the top with Datasette seems really convoluted. \r\n\r\nAlso for posterity, here's a list of Simon's Datasette plugins that use \"extra_js_urls()\", which probably means they can be ported/re-written to use this new plugin system:\r\n\r\n- [`datasette-vega`](https://github.com/simonw/datasette-vega/blob/00de059ab1ef77394ba9f9547abfacf966c479c4/datasette_vega/__init__.py#L25)\r\n- [`datasette-cluster-map`](https://github.com/simonw/datasette-cluster-map/blob/795d25ad9ff6cba0307191f44fecc8f8070bef5c/datasette_cluster_map/__init__.py#L14)\r\n- [`datasette-leaflet-geojson`](https://github.com/simonw/datasette-leaflet-geojson/blob/64713aa497750400b9ac2c12e8bb6ffab8eb77f3/datasette_leaflet_geojson/__init__.py#L47)\r\n- [`datasette-pretty-traces`](https://github.com/simonw/datasette-pretty-traces/blob/5219d65eca3d7d7a73bb9d3120df42fe046a1315/datasette_pretty_traces/__init__.py#L5)\r\n- [`datasette-youtube-embed`](https://github.com/simonw/datasette-youtube-embed/blob/4b4a0d7e58ebe15f47e9baf68beb9908c1d899da/datasette_youtube_embed/__init__.py#L55)\r\n- [`datasette-leaflet-freedraw`](https://github.com/simonw/datasette-leaflet-freedraw/blob/8f28c2c2080ec9d29f18386cc6a2573a1c8fbde7/datasette_leaflet_freedraw/__init__.py#L66)\r\n- [`datasette-hovercards`](https://github.com/simonw/datasette-hovercards/blob/9439ba46b7140fb03223faff0d21aeba5615a287/datasette_hovercards/__init__.py#L5)\r\n- [`datasette-mp3-audio`](https://github.com/simonw/datasette-mp3-audio/blob/4402168792f452a46ab7b488e40ec49cd4b12185/datasette_mp3_audio/__init__.py#L6)\r\n- [`datasette-geojson-map`](https://github.com/simonw/datasette-geojson-map/blob/32af5f1fd1a07278bbf8071fbb20a61e0f613246/datasette_geojson_map/__init__.py#L30)", "reactions": "{\"total_count\": 1, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 1}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1606352600", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1606352600, "node_id": "IC_kwDOBm6k_c5fvv7Y", "user": {"value": 15178711, "label": "asg017"}, "created_at": "2023-06-26T00:17:04Z", "updated_at": "2023-06-26T00:17:04Z", "author_association": "CONTRIBUTOR", "body": ":wave: would love to see this get merged soon! I want to make a javascript plugin on top of the code-mirror editor to make a few things nicer (function auto-complete, table/column descriptions, etc.), and this would help out a bunch", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1585149909", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1585149909, "node_id": "IC_kwDOBm6k_c5ee3fV", "user": {"value": 9020979, "label": "hydrosquall"}, "created_at": "2023-06-09T21:35:00Z", "updated_at": "2023-06-09T21:35:00Z", "author_association": "CONTRIBUTOR", "body": "Thanks @cldellow for the thoughtful comments! These are all things that I'll keep in mind as we figure out how/if this API is actually used by plugin authors once it's actually out in the world.\r\n\r\n> Yes, this would work - but it requires me to continue to communicate the column names out of band (in order to fetch the facet data per-column before registering my plugin), vs being able to re-use them from the plugin implementation.\r\n\r\nAh, I understand now! Thanks for explaining. ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1548617257", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1548617257, "node_id": "IC_kwDOBm6k_c5cTgYp", "user": {"value": 193185, "label": "cldellow"}, "created_at": "2023-05-15T21:32:20Z", "updated_at": "2023-05-15T21:32:20Z", "author_association": "CONTRIBUTOR", "body": "> Were you picturing that the whole plugin config object could be returned as a promise, or that the individual hooks (like makeColumnActions or makeAboveTablePanelConfigs supported returning a promise of arrays instead only returning plain arrays?\r\n\r\nThe latter - that you could return a promise of arrays, so it parallels the [\"await me maybe\" pattern in Datasette](https://simonwillison.net/2020/Sep/2/await-me-maybe/), where you can return either a value, a callable or an awaitable.\r\n\r\n> I have a hunch that what you're describing might be achievable without adding Promises to the API with something\r\n\r\nOops, I did a poor job explaining. Yes, this would work - but it requires me to continue to communicate the column names out of band (in order to fetch the facet data per-column before registering my plugin), vs being able to re-use them from the plugin implementation.\r\n\r\nThis isn't that big of a deal - it'd be a nice ergonomic improvement, but nowhere near as a big of an improvement as having an officially sanctioned way to add stuff to the column menus in the first place.\r\n\r\nThis could also be layered on in a future commit without breaking v1 users, too, so it's not at all urgent.\r\n\r\n> especially if those lines are encapsulated by a function we provide (maybe something that's available on the window provided by Datasette as an inline script tag\r\n\r\nAh, this is maybe the the key point. Since it's all hosted inside Datasette, Datasette can provide some arbitrary sugar to make it easier to work with.\r\n\r\nMy experience with async scripts in JS is that people sometimes don't understand the race conditions inherent to them. If they copy/paste from a tutorial, it does just work. But then they'll delete half the code, and by chance it still works on their machine/Datasette templates, and now someone's headed for an annoying debugging session -- maybe them, maybe someone else who tries to re-use their plugin.\r\n\r\nAgain, a fairly minor thing, though.", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1546362374", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1546362374, "node_id": "IC_kwDOBm6k_c5cK54G", "user": {"value": 9020979, "label": "hydrosquall"}, "created_at": "2023-05-12T22:09:03Z", "updated_at": "2023-05-12T22:09:03Z", "author_association": "CONTRIBUTOR", "body": "Hey @cldellow , thanks for the thoughtful feedback and describing the \"lazy facets\" feature! \r\n\r\nIt sounds like the [postTask](https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/postTask) API might be relevant for the types of network request scheduling you have in mind. \r\n\r\nAddressing your points inline below:\r\n\r\n> It might also be nice if the plugins could return Promises.\r\n\r\nWere you picturing that the whole plugin config object could be returned as a promise, or that the individual hooks (like `makeColumnActions` or `makeAboveTablePanelConfigs` supported returning a promise of arrays instead only returning plain arrays?\r\n\r\nI think what you're describing can be achievable, but I want to make sure I do so in a way that addresses your need / keeps the complexity of the plugin core system at a level this is approachable . \r\n\r\nI have a hunch that what you're describing might be achievable without adding Promises to the API with something like\r\n\r\n```\r\nfetch('/api/with-custom-facets').then(myFacets => {\r\n // reusing the go() idiom\r\n go(manager, myFacets);\r\n})\r\n```\r\n\r\nbut I'd like to confirm if that's the case before investigating adding support.\r\n\r\n> bulletproof plugin registration code that is robust against the order in which the script tags load\r\n\r\nYes, I think what you wrote looks right to me! While it looks a little bit verbose compared to the second example, I'm hoping we can mitigate the cost of that during this API incubation phase by making it an easy-to-copy paste code snippet.\r\n\r\nI haven't heard of the GA queing pattern before, thanks for the example. I won't have time to implement of proof of concept in the next few weeks, but I took some time to think through the pros/cons to decide whether we may want to add this in a future release:\r\n\r\nI can see that this approach brings advantages\r\n\r\n- Plugin developers don't need to know the name of the datasette initialization event to start their plugin\r\n- Pushing a function to an array probably is easier (definitely more concise) than adding a document event listener\r\n- One less event listener sitting in memory\r\n\r\nIt also has some minor costs\r\n\r\n- A malicious plugin could choose to (or accidentally) mess with the order of the queue if multiple scripts are lined up\r\n- Some risk in encouraging people to mutate global state\r\n- (not a cost, more a moot point): changing this API may not make a meaningful difference if we're discussing whether people enter 2 vs 5 lines of code, especially if those lines are encapsulated by a function we provide (maybe something that's available on the `window` provided by Datasette as an inline script tag). \r\n\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1651082214, "label": "feat: Javascript Plugin API (Custom panels, column menu items with JS actions)"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/2052#issuecomment-1530822437", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2052", "id": 1530822437, "node_id": "IC_kwDOBm6k_c5bPn8l", "user": {"value": 193185, "label": "cldellow"}, "created_at": "2023-05-02T03:35:30Z", "updated_at": "2023-05-02T16:02:38Z", "author_association": "CONTRIBUTOR", "body": "Also, just checking - is this how I'd write bulletproof plugin registration code that is robust against the order in which the script tags load (eg if both my code and the Datasette code are loaded via a `