From e3a1f190572708b422457e2d50eb031f85ba4f52 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 13 Jun 2026 14:19:39 -0700 Subject: [PATCH 01/23] Edit/delete icon prototype --- datasette/static/app.css | 55 +++++++++++++++++++++++++++++++++ datasette/views/table.py | 67 +++++++++++++++++++++++++++++++++++----- 2 files changed, 115 insertions(+), 7 deletions(-) diff --git a/datasette/static/app.css b/datasette/static/app.css index 6d675d9f..ec3a85fb 100644 --- a/datasette/static/app.css +++ b/datasette/static/app.css @@ -1192,6 +1192,50 @@ dialog.set-column-type-dialog::backdrop { cursor: wait; } +.row-link-with-actions { + display: inline-flex; + align-items: center; + gap: 0.4rem; + flex-wrap: wrap; +} + +.row-inline-actions { + display: inline-flex; + gap: 0.2rem; + align-items: center; +} + +.row-inline-action { + appearance: none; + border: 1px solid rgba(74, 85, 104, 0.24); + background: transparent; + color: #4a5568; + border-radius: 4px; + cursor: pointer; + display: inline-grid; + place-items: center; + min-height: 24px; + min-width: 24px; + padding: 2px; + position: relative; +} + +.row-inline-action:hover, +.row-inline-action:focus { + background: rgba(74, 85, 104, 0.07); +} + +.row-inline-action:focus { + outline: 3px solid #b3d4ff; + outline-offset: 1px; +} + +.row-inline-action-icon { + display: block; + height: 13px; + width: 13px; +} + @media (max-width: 640px) { dialog.mobile-column-actions-dialog { width: 95vw; @@ -1239,6 +1283,17 @@ dialog.set-column-type-dialog::backdrop { padding-left: 18px; padding-right: 18px; } + + .row-inline-action { + min-height: 30px; + min-width: 30px; + padding: 4px; + } + + .row-inline-action-icon { + height: 14px; + width: 14px; + } } @media only screen and (max-width: 576px) { diff --git a/datasette/views/table.py b/datasette/views/table.py index 65388c9c..49238ff4 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -194,6 +194,13 @@ async def display_columns_and_rows( pks_for_display = pks if not pks_for_display: pks_for_display = ["rowid"] + row_action_permissions = {} + if link_column and request is not None and db.is_mutable: + row_action_permissions = await datasette.allowed_many( + actions=["update-row", "delete-row"], + resource=TableResource(database=database_name, table=table_name), + actor=request.actor, + ) columns = [] for r in description: @@ -233,19 +240,65 @@ async def display_columns_and_rows( if link_column: is_special_link_column = len(pks) != 1 pk_path = path_from_row_pks(row, pks, not pks, False) + row_path = path_from_row_pks(row, pks, not pks) + row_link = '{flat_pks}'.format( + table_path=datasette.urls.table(database_name, table_name), + flat_pks=str(markupsafe.escape(pk_path)), + flat_pks_quoted=row_path, + ) + edit_icon = ( + '" + ) + delete_icon = ( + '" + ) + row_actions = [] + if row_action_permissions.get("update-row"): + row_actions.append( + '".format( + edit_icon=edit_icon, + row_label=markupsafe.escape(pk_path), + ) + ) + if row_action_permissions.get("delete-row"): + row_actions.append( + '".format( + delete_icon=delete_icon, + row_label=markupsafe.escape(pk_path), + ) + ) + if row_actions: + row_link = ( + '{row_link}' + '' + "{row_actions}" + ).format(row_link=row_link, row_actions="".join(row_actions)) cells.append( { "column": pks[0] if len(pks) == 1 else "Link", "value_type": "pk", "is_special_link_column": is_special_link_column, "raw": pk_path, - "value": markupsafe.Markup( - '{flat_pks}'.format( - table_path=datasette.urls.table(database_name, table_name), - flat_pks=str(markupsafe.escape(pk_path)), - flat_pks_quoted=path_from_row_pks(row, pks, not pks), - ) - ), + "value": markupsafe.Markup(row_link), } ) From de5f72dd8847be184f5283491a4df38c63f823bd Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 13 Jun 2026 14:38:49 -0700 Subject: [PATCH 02/23] 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. --- datasette/app.py | 1 + datasette/templates/table.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/datasette/app.py b/datasette/app.py index 9979b6c5..4931f486 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -2312,6 +2312,7 @@ class Datasette: and "ds_actor" in request.cookies and request.actor, "app_css_hash": self.app_css_hash(), + "table_js_hash": self.static_hash("table.js"), "zip": zip, "body_scripts": body_scripts, "format_bytes": format_bytes, diff --git a/datasette/templates/table.html b/datasette/templates/table.html index c841e1be..4dc908e0 100644 --- a/datasette/templates/table.html +++ b/datasette/templates/table.html @@ -5,7 +5,7 @@ {% block extra_head %} {{- super() -}} - +