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
- 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
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.
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.
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.
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.
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.