Commit graph

3,214 commits

Author SHA1 Message Date
Simon Willison
2a887e5853 Merge branch 'main' into playwright-tests 2026-06-16 13:29:22 -07:00
Simon Willison
b5ba41c9db Fix for Safari esc-closes-confirm bug, closes #2784 2026-06-16 13:28:18 -07:00
Simon Willison
0a0491568a Fix flaky autocomplete timeout test 2026-06-16 11:59:42 -07:00
Simon Willison
dc6f207d37 Update unreleased release notes section
Refs #2780 and many more.
2026-06-14 20:29:59 -07:00
Simon Willison
49362a9b20 await request.json() method, closes #2767 2026-06-14 17:00:12 -07:00
Simon Willison
0748b561d5 Document Playwright test commands
Refs #2779
2026-06-14 16:52:07 -07:00
Simon Willison
387e309b3b Port JSON column field test to Playwright
Refs #2779
2026-06-14 16:49:30 -07:00
Simon Willison
3cfdca026a Port edit tools field API test to Playwright
Refs #2779
2026-06-14 16:48:32 -07:00
Simon Willison
b5fa485a9f Port datasette manager plugin test to Playwright
Refs #2779
2026-06-14 16:45:48 -07:00
Simon Willison
6bbd33d81d Port navigation jump sections to Playwright
Refs #2779
2026-06-14 16:44:59 -07:00
Simon Willison
047b69e87f Port navigation search recents to Playwright
Refs #2779
2026-06-14 16:44:17 -07:00
Simon Willison
6cd65cf4fb Initial Playwright setup plus first test
Refs #2779
2026-06-14 16:39:55 -07:00
Simon Willison
bba7e0b027 Better docs for makeJumpSections()
Closes #2778
2026-06-14 16:28:09 -07:00
Simon Willison
f1af216852
Insert/edit/delete UI in Datasette core, plus makeColumnField() plugin hook
PR #2781

- A new `/db/table/-/autocomplete?q=term` JSON API for fast autocomplete search against foreign key tables - it searches against their label column or their primary key and switches to just a prefix search against the first primary key (for speed) if the label column check takes more than 500ms. A new `/-/debug/autocomplete` page lets you try this out.
- A `<datasette-autocomplete>` Web Component that uses that API.
- Table pages now get an insert button above the table, and little edit and delete icons next to each row. All three trigger custom modal dialogs. The edit/insert dialog is a full form - the delete one is just confirmation.
- A new `/<database>/<table>/-/fragment?_row=` endpoint which returns a rendered fragment of HTML for the specified row. This is used by the insert/edit code to partially update the table to reflect those changes. Uses a new `data-row="{{ row.row_path }}"` attribute on the `<tr>` to enable the replacement.
- A new default column type called `textarea` which users can use to specify a multi-line textarea for a column
- A new JavaScript plugin hook, [makeColumnField()](3f7d389caf/docs/javascript_plugins.rst (makecolumnfieldcontext)), which plugins can use to add custom form fields to the edit form. Datasette [uses this itself](3f7d389caf/datasette/static/table.js (L1181-L1209)) for the JSON field to add client-side JSON validation. I iterated a *lot* on this one, including spinning up a `datasette-prosemirror` plugin and a branch of `datasette-files` to fully exercise it.

Closes #2780

Video demo: https://github.com/user-attachments/assets/2c18b8a4-975f-4c7b-9573-ec6040fe8223
2026-06-14 16:14:14 -07:00
Simon Willison
f157df7f07 Ran prettier 2026-06-14 16:06:36 -07:00
Simon Willison
82c95a1a13 Refactor edit/delete tools to work on row pages too
Refs https://github.com/simonw/datasette/pull/2781#issuecomment-4703303274

Refs #2780
2026-06-14 16:05:42 -07:00
Simon Willison
4ce2888e79 Support for <button> items in action menus
Closes #2782

Animated demo: https://github.com/simonw/datasette/pull/2781#issuecomment-4703303274
2026-06-14 15:58:37 -07:00
Simon Willison
3f7d389caf Refine column field plugin API and documentation
- Simplify JavaScript column field context:
  - expose `isPk` instead of `isPrimaryKey`
  - expose `defaultExpression` instead of separate SQLite default flags
  - remove value/default state from plugin context
- Update field helper behavior:
  - `setValue()` no longer dispatches input/change events
  - remove dispatch options and `resetValue()`
  - add `markClean()` for plugin-normalized initial values
  - track clean field state for reliable dirty detection

Also:

- Prompt before closing row insert/edit dialogs when there are unsaved changes
- Map declared SQLite types to affinities, returning `BLOB` for typeless columns and `NUMERIC` for numeric/date/boolean-like declarations
2026-06-14 15:09:24 -07:00
Simon Willison
c083e44561 Ignore .playwright-mcp
Codex Desktop likes using this folder.
2026-06-14 14:21:04 -07:00
Simon Willison
841a2536ea Tighten row edit field plugin API
Replace the value/valueType/originalValue/originalValueType fields on makeColumnField() contexts with an explicit field object API for reading, writing, resetting, comparing and validating field values.

Normalize columnType to {type, config}, rename the SQLite default metadata so it is clearly SQLite-specific, and document that plugins submit only string, number, boolean or null values. Plugins that need structured data should serialize it themselves instead of relying on Datasette to special-case JSON.

Move the built-in json column type behavior onto the same plugin API used by external plugins: validate the textarea with field.setValidity() as the value changes, but submit plain text. Harden row edit value comparison so fixing invalid JSON in an existing row is not blocked by the original invalid value.

Update the JavaScript plugin documentation and Node-based tests for the revised field contract.
2026-06-14 13:58:55 -07:00
Simon Willison
b2de8b5d2e First draft of makeColumnField() plugin hook 2026-06-14 11:57:13 -07:00
Simon Willison
c405b56223 Tweak modal color scheme to be more Datasette 2026-06-14 11:56:51 -07:00
Simon Willison
35d7e3cab8 Fixed some tests for the new autocomplete work 2026-06-14 08:03:28 -07:00
Simon Willison
574290fb23 Add foreign key autocomplete to row forms
Expose single-primary-key foreign key autocomplete URLs in table page metadata and load the autocomplete component when needed.

Enhance insert and edit dialogs to wrap foreign-key inputs with the autocomplete web component, show linked selected-row labels, reserve metadata space, and keep the dropdown as a fixed overlay above modal chrome.

Add an explicit _initial=1 autocomplete mode for empty-field starter suggestions while keeping blank q responses empty by default, with tests for the endpoint and table metadata.
2026-06-14 07:30:34 -07:00
Simon Willison
aa5fb7be3d Autocomplete widget and /-/debug/autocomplete test page 2026-06-13 22:59:37 -07:00
Simon Willison
b868f7d4c3 /db/table/-/autocomplete?q= JSON endpoint
Needed to help implement edit foreign key reference.
2026-06-13 22:40:03 -07:00
Simon Willison
5490c7b794 textarea column type
Shows as multiline edit in edit/insert dialog
2026-06-13 22:18:45 -07:00
Simon Willison
2b61c916d0 Handle hidden label columns in row actions 2026-06-13 22:01:26 -07:00
Simon Willison
e1e67e912a Docs for /<database>/<table>/-/fragment 2026-06-13 21:58:16 -07:00
Simon Willison
a7cd746613 Use label column in 'Inserted X' messages 2026-06-13 21:48:28 -07:00
Simon Willison
e91c646ee6 Use column label, if available in edit/delete dialog 2026-06-13 21:43:48 -07:00
Simon Willison
5bf4cf8860 Add insert row UI to table pages
Add a permission-gated Insert row button to mutable table pages and expose the metadata needed by the client-side UI, including the insert API path, table name, primary keys, editable columns, defaults, nullability, and column type information.

Reuse the existing row edit modal for inserts. Insert submissions now use the JSON API with return=true, derive the new row's tilde-encoded row path from the returned primary key values, fetch the matching table fragment, and insert the rendered row into the current table. Successful inserts and updates now show mutation status messages above the table.

Support SQLite defaults in insert forms by showing default expressions as non-editable values with Set value / Use default controls. Keep those controls aligned and stable so toggling between default and custom values does not shift the modal layout.

Refine the edit modal at the same time: send only changed fields on update, skip the update API entirely when nothing changed, clear stale mutation status for no-op saves, and simplify modal headings so insert/edit context is shown in the bold title instead of duplicated summary text.

Add tests for the insert button and metadata, including omitted integer primary keys, default values, table names, and compound primary keys.
2026-06-13 21:34:45 -07:00
Simon Willison
e50d176722 Add in-place table row edit and delete UI
Use a compact data-row attribute on table row fragments and derive row API URLs in JavaScript from a page-level table URL. Add a /-/fragment endpoint so edited rows can be re-rendered with the active table template and render_cell hooks, then replaced in place after a successful save.

Document the custom _table.html data-row contract and cover the fragment endpoint, base_url handling, and row markup with tests.
2026-06-13 18:41:00 -07:00
Simon Willison
ad3456dc4a Display of edit modal (no save yet) 2026-06-13 14:48:44 -07:00
Simon Willison
20824bd707 Delete icon on table page now works 2026-06-13 14:40:29 -07:00
Simon Willison
de5f72dd88 hash busting thing on table.js
Addd for Codex Desktop, which has real trouble with edits
to files like this as the in-app browser does not seem to
have a cache-busting reload option.
2026-06-13 14:38:49 -07:00
Simon Willison
e3a1f19057 Edit/delete icon prototype 2026-06-13 14:19:39 -07:00
Simon Willison
f2927a1647 Fix for gen.throw(*sys.exc_info()) warning
Closes #2776
2026-06-13 11:15:47 -07:00
Simon Willison
d473dc565f
datasette.allowed_many() method and per-request permission check cache
Merge pull request #2775
2026-06-13 11:13:08 -07:00
Simon Willison
ab19b0382b Removed note from permission_resources_sql
Refs https://github.com/simonw/datasette/pull/2775/changes#r3408385197
2026-06-13 11:12:31 -07:00
Simon Willison
86334d233d Switch to CTE to handle 600+ actions at once
GPT-5.5 xhigh in Codex spotted this problem and fixed it with a CTE:

https://gisthost.github.io/?46076499ee685acddc988ff6b47a74b0
2026-06-13 11:09:28 -07:00
Simon Willison
d4cb8b464b Fix for trace_child_tasks exception handling
I had Claude Fable 5 review our use of contextvar and
it spotted this place where exceptions were not
correctly handled.
2026-06-12 13:21:58 -07:00
Simon Willison
bb59c61c9f Request-scoped permission check cache
Adds a per-request cache for permission check results, plus wiring that
resolves action permissions in bulk before plugin hooks need them:

- New _permission_check_cache contextvar, set to a fresh dict for each
  request by DatasetteRouter and reset when the request ends. Keys
  include the full serialized actor, so actors differing in any field
  (e.g. token restrictions) never share entries. SkipPermissions mode
  bypasses the cache entirely.
- datasette.allowed_many() now consults the cache and stores its
  results there, so repeated datasette.allowed() checks within one
  request resolve without further SQL.
- Table pages resolve all registered table-level actions against the
  current table and all database-level actions against its database
  (database pages likewise) in batched queries before invoking the
  table_actions/database_actions plugin hooks - allowed() calls made
  inside those hooks are then served from the cache with no plugin
  changes required. Actions with no permission rules from any plugin
  are resolved to False without touching the database.

Benchmarks (benchmarks/) with a simulated 12-plugin ecosystem making
18 checks per table page show 34 -> 13 internal-DB queries per page;
with 2ms-per-query internal DB latency (modelling Datasette Cloud)
table page time drops from 77.9ms to 27.6ms - the caching layer
accounts for ~91% of that improvement over allowed_many() alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:11:17 -07:00
Simon Willison
88878b4184 datasette.allowed_many() method 2026-06-12 12:51:40 -07:00
Simon Willison
fa86ac7b11
Clearer examples and descriptions for JSON API extras (#2773)
Review of the generated ?_extra= documentation found several extras
with no example output or with examples that needed explanation:

- extras: now shows an abbreviated example of the toggle list and has
  a clearer description (which also improves the live API output)
- set_column_type_ui: example of the shape seen with set-column-type
  permission, plus a note that it is null otherwise
- column_types: live example generated from a table with an assigned
  column type instead of an empty {}
- metadata: live table example now demonstrates a table description
  and column descriptions; row and query examples gained explanatory
  notes
- expandable_columns, foreign_key_tables, facets_timed_out, next_url,
  renderers: notes explaining the shape of their output

Also added docs_note cross-references to the relevant documentation:
facets, pagination, render_cell and register_output_renderer plugin
hooks, column type configuration and API, metadata, custom templates,
permissions and foreign key label expansion. foreign_key_tables is
now flagged as potentially executing additional queries.

https://claude.ai/code/session_01EfjBe6E817m9XNFW7EX3Vm

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-11 19:41:24 -07:00
Simon Willison
1d4212122e Add release date for 1.0a33 2026-06-11 10:36:16 -07:00
Simon Willison
993169ae49 Release 1.0a33 1.0a33
Refs #2735, #2677, #2680, #2711, #2756, #2761, #2768, #2754
2026-06-11 08:24:37 -07:00
Simon Willison
4e9556cc24
Redesign and document extras mechanism to cover rows and queries in addition to tables
Merge PR #2769
2026-06-11 07:43:18 -07:00
Simon Willison
26f3b20e58 Fix to our pytest plugin to better support pytest-cov
Refs https://github.com/simonw/datasette/pulls#issuecomment-4681621052
2026-06-11 07:29:27 -07:00
Simon Willison
648a34ce81 Fix for test I broke in 92848c06 refs #2754 2026-06-11 07:13:07 -07:00