sqlite-hub 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/changelog.md +25 -0
  2. package/docs/DESIGN_GUIDELINES.md +36 -0
  3. package/frontend/index.html +79 -0
  4. package/frontend/js/api.js +67 -0
  5. package/frontend/js/app.js +1401 -921
  6. package/frontend/js/components/connectionCard.js +3 -4
  7. package/frontend/js/components/emptyState.js +4 -4
  8. package/frontend/js/components/modal.js +239 -30
  9. package/frontend/js/components/queryEditor.js +11 -8
  10. package/frontend/js/components/queryHistoryDetail.js +27 -8
  11. package/frontend/js/components/queryHistoryPanel.js +6 -5
  12. package/frontend/js/components/rowEditorPanel.js +84 -58
  13. package/frontend/js/components/sidebar.js +76 -33
  14. package/frontend/js/components/structureGraph.js +629 -715
  15. package/frontend/js/components/tableDesignerEditor.js +9 -8
  16. package/frontend/js/components/tableDesignerSidebar.js +11 -10
  17. package/frontend/js/components/tableDesignerSqlPreview.js +61 -30
  18. package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
  19. package/frontend/js/router.js +81 -71
  20. package/frontend/js/store.js +3095 -2165
  21. package/frontend/js/views/charts.js +68 -40
  22. package/frontend/js/views/connections.js +5 -17
  23. package/frontend/js/views/data.js +40 -27
  24. package/frontend/js/views/editor.js +172 -177
  25. package/frontend/js/views/mediaTagging.js +861 -0
  26. package/frontend/js/views/overview.js +149 -10
  27. package/frontend/js/views/settings.js +2 -2
  28. package/frontend/js/views/structure.js +74 -70
  29. package/frontend/js/views/tableDesigner.js +7 -2
  30. package/frontend/styles/base.css +73 -1
  31. package/frontend/styles/components.css +105 -105
  32. package/frontend/styles/structure-graph.css +19 -82
  33. package/frontend/styles/tokens.css +2 -0
  34. package/frontend/styles/views.css +823 -30
  35. package/package.json +1 -1
  36. package/server/routes/charts.js +4 -1
  37. package/server/routes/data.js +14 -0
  38. package/server/routes/mediaTagging.js +166 -0
  39. package/server/routes/sql.js +1 -0
  40. package/server/server.js +4 -0
  41. package/server/services/sqlite/dataBrowserService.js +25 -0
  42. package/server/services/sqlite/exportService.js +31 -1
  43. package/server/services/sqlite/mediaTaggingService.js +1689 -0
  44. package/server/services/sqlite/overviewService.js +68 -0
  45. package/server/services/storage/appStateStore.js +321 -2
@@ -50,10 +50,10 @@ function renderChartsList(state) {
50
50
  <div>
51
51
  <span class="material-symbols-outlined mb-3 text-4xl text-on-surface-variant/25">query_stats</span>
52
52
  <p class="font-headline text-lg font-black uppercase tracking-tight text-on-surface">
53
- No Query History
53
+ No Chartable Queries
54
54
  </p>
55
55
  <p class="mt-2 max-w-xs text-sm leading-6 text-on-surface-variant/60">
56
- Run saved queries in the SQL Editor first. They will appear here automatically.
56
+ Run SELECT queries in the SQL Editor first. They will appear here automatically.
57
57
  </p>
58
58
  </div>
59
59
  </div>
@@ -157,7 +157,7 @@ function renderQuerySqlSection(state, rawSql) {
157
157
  return `
158
158
  <section class="mt-5 border border-outline-variant/10 bg-surface-container-lowest">
159
159
  <button
160
- class="flex w-full items-center justify-between gap-3 border-b border-outline-variant/10 px-4 py-3 text-left"
160
+ class="standard-button flex w-full justify-between border-b border-outline-variant/10 px-4 text-left"
161
161
  data-action="toggle-charts-sql-panel"
162
162
  type="button"
163
163
  >
@@ -192,10 +192,13 @@ function renderQueryResultsSection(state) {
192
192
  Results
193
193
  </span>
194
194
  <button
195
- class="toolbar-button query-chart-card__action"
195
+ class="standard-button"
196
196
  data-action="toggle-charts-results-panel"
197
197
  type="button"
198
198
  >
199
+ <span class="material-symbols-outlined text-sm">
200
+ ${isVisible ? 'table_rows_narrow' : 'table_rows'}
201
+ </span>
199
202
  ${isVisible ? 'Hide Results' : 'Show Results'}
200
203
  </button>
201
204
  </div>
@@ -244,7 +247,7 @@ function renderChartHeightPresetToggle(state) {
244
247
  .map(
245
248
  preset => `
246
249
  <button
247
- class="toolbar-button charts-height-toggle__button ${activePreset === preset.value ? 'is-active' : ''}"
250
+ class="standard-button charts-height-toggle__button ${activePreset === preset.value ? 'is-active' : ''}"
248
251
  data-action="set-charts-height-preset"
249
252
  data-preset="${preset.value}"
250
253
  type="button"
@@ -312,7 +315,7 @@ function renderChartCard(chart, state, analysis) {
312
315
  </div>
313
316
  <div class="flex flex-wrap items-center gap-2">
314
317
  <button
315
- class="toolbar-button query-chart-card__action"
318
+ class="standard-button"
316
319
  data-action="open-edit-query-chart-modal"
317
320
  data-chart-id="${escapeHtml(chart.id)}"
318
321
  type="button"
@@ -320,7 +323,7 @@ function renderChartCard(chart, state, analysis) {
320
323
  Edit
321
324
  </button>
322
325
  <button
323
- class="toolbar-button query-chart-card__action"
326
+ class="delete-button"
324
327
  data-action="open-delete-query-chart-modal"
325
328
  data-chart-id="${escapeHtml(chart.id)}"
326
329
  type="button"
@@ -328,7 +331,7 @@ function renderChartCard(chart, state, analysis) {
328
331
  Delete
329
332
  </button>
330
333
  <button
331
- class="toolbar-button query-chart-card__action"
334
+ class="standard-button"
332
335
  data-action="export-query-chart-png"
333
336
  data-chart-id="${escapeHtml(chart.id)}"
334
337
  type="button"
@@ -347,6 +350,7 @@ function renderChartCard(chart, state, analysis) {
347
350
  function renderChartsDetail(state) {
348
351
  const detail = state.charts.detail;
349
352
  const selectedHistoryId = state.charts.selectedHistoryId;
353
+ const historyVisible = state.editor.historyPanelVisible !== false;
350
354
 
351
355
  if (!selectedHistoryId) {
352
356
  return renderEmptyChartDetail();
@@ -384,32 +388,48 @@ function renderChartsDetail(state) {
384
388
  <div class="custom-scrollbar flex-1 overflow-auto">
385
389
  <div class="charts-detail-shell">
386
390
  <header class="charts-detail-shell__header">
387
- <div class="min-w-0">
388
- <div class="text-[10px] font-mono uppercase tracking-[0.18em] text-primary-container/70">
389
- Charts
390
- </div>
391
+ <div class="text-[10px] font-mono uppercase tracking-[0.18em] text-primary-container/70">
392
+ Charts
393
+ </div>
394
+ <div class="charts-detail-shell__title">
391
395
  <h1 class="mt-2 truncate font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
392
396
  ${escapeHtml(detail.item.displayTitle)}
393
397
  </h1>
394
398
  </div>
395
399
  <div class="charts-detail-shell__controls">
396
- ${renderChartHeightPresetToggle(state)}
397
- <button
398
- class="toolbar-button charts-detail-shell__button"
399
- data-action="open-query-history"
400
- data-history-id="${escapeHtml(detail.item.id)}"
401
- type="button"
402
- >
403
- Open In Editor
404
- </button>
405
- <button
406
- class="toolbar-button charts-detail-shell__button charts-detail-shell__button--primary"
407
- data-action="open-create-query-chart-modal"
408
- type="button"
409
- ${state.charts.resultError || !state.charts.result ? 'disabled' : ''}
410
- >
411
- New Chart
412
- </button>
400
+ <div class="charts-detail-shell__controls-group">
401
+ ${renderChartHeightPresetToggle(state)}
402
+ <button
403
+ class="standard-button"
404
+ data-action="toggle-query-history-panel"
405
+ data-next-value="${historyVisible ? 'false' : 'true'}"
406
+ type="button"
407
+ >
408
+ <span class="material-symbols-outlined text-sm">${
409
+ historyVisible ? 'visibility_off' : 'history'
410
+ }</span>
411
+ ${historyVisible ? 'Hide Query History' : 'Show Query History'}
412
+ </button>
413
+ </div>
414
+ <div class="charts-detail-shell__controls-group charts-detail-shell__controls-group--end">
415
+ <button
416
+ class="standard-button"
417
+ data-action="open-query-history"
418
+ data-history-id="${escapeHtml(detail.item.id)}"
419
+ type="button"
420
+ >
421
+ <span class="material-symbols-outlined text-sm">terminal</span>
422
+ Open In Editor
423
+ </button>
424
+ <button
425
+ class="signature-button"
426
+ data-action="open-create-query-chart-modal"
427
+ type="button"
428
+ ${state.charts.resultError || !state.charts.result ? 'disabled' : ''}
429
+ >
430
+ New Chart
431
+ </button>
432
+ </div>
413
433
  </div>
414
434
  </header>
415
435
 
@@ -442,6 +462,8 @@ function renderChartsDetail(state) {
442
462
  }
443
463
 
444
464
  export function renderChartsView(state) {
465
+ const historyVisible = state.editor.historyPanelVisible !== false || !state.charts.selectedHistoryId;
466
+
445
467
  if (!state.connections.active && state.charts.error?.code === 'ACTIVE_DATABASE_REQUIRED') {
446
468
  return {
447
469
  main: `<section class="view-surface flex min-h-full">${renderMissingDatabase()}</section>`,
@@ -452,17 +474,23 @@ export function renderChartsView(state) {
452
474
  return {
453
475
  main: `
454
476
  <section class="charts-view">
455
- <aside class="charts-view__sidebar">
456
- <div class="charts-view__sidebar-header">
457
- <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
458
- Query History
459
- </div>
460
- <h2 class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
461
- Charts
462
- </h2>
463
- </div>
464
- ${renderChartsList(state)}
465
- </aside>
477
+ ${
478
+ historyVisible
479
+ ? `
480
+ <aside class="charts-view__sidebar">
481
+ <div class="charts-view__sidebar-header">
482
+ <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
483
+ Query History
484
+ </div>
485
+ <h2 class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
486
+ Charts
487
+ </h2>
488
+ </div>
489
+ ${renderChartsList(state)}
490
+ </aside>
491
+ `
492
+ : ''
493
+ }
466
494
  <div class="charts-view__detail">${renderChartsDetail(state)}</div>
467
495
  </section>
468
496
  `,
@@ -8,32 +8,25 @@ function renderConnectionsActionButton({
8
8
  action = "open-modal",
9
9
  modal,
10
10
  tone = "secondary",
11
- className = "",
12
11
  disabled = false,
13
12
  }) {
14
- const clipPath = "polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%)";
15
13
  const toneClassName =
16
14
  tone === "primary"
17
- ? "clipped-corner border border-primary-container bg-primary-container text-on-primary shadow-[0_0_18px_-10px_rgba(252,227,0,0.65)] hover:bg-primary-fixed"
18
- : "clipped-corner border border-outline-variant/20 bg-surface-container-highest text-primary-container shadow-[inset_2px_0_0_0_rgba(252,227,0,0.95)] hover:bg-surface-bright";
19
- const iconClassName =
20
- tone === "primary"
21
- ? "text-base text-on-primary"
22
- : "text-base text-primary-container/90";
23
- const clipStyle = `style="--clip-path: ${clipPath};"`;
15
+ ? "signature-button"
16
+ : "standard-button";
17
+ const iconClassName = "";
24
18
  const modalAttribute = modal ? `data-modal="${modal}"` : "";
25
19
 
26
20
  return `
27
21
  <button
28
- class="flex h-11 items-center justify-between gap-6 px-5 font-headline text-xs font-bold uppercase tracking-[0.18em] transition-colors disabled:cursor-default disabled:opacity-40 ${toneClassName} ${className}"
22
+ class="${toneClassName}"
29
23
  data-action="${escapeHtml(action)}"
30
24
  ${modalAttribute}
31
- ${clipStyle}
32
25
  type="button"
33
26
  ${disabled ? "disabled" : ""}
34
27
  >
35
28
  <span>${label}</span>
36
- <span class="material-symbols-outlined ${iconClassName}">${icon}</span>
29
+ <span class="material-symbols-outlined${iconClassName ? ` ${iconClassName}` : ""}">${icon}</span>
37
30
  </button>
38
31
  `;
39
32
  }
@@ -77,13 +70,11 @@ function renderConnectionsBody(state) {
77
70
  icon: "folder_open",
78
71
  modal: "open-connection",
79
72
  tone: "primary",
80
- className: "min-w-[17rem] px-8 py-6 text-sm",
81
73
  })}
82
74
  ${renderConnectionsActionButton({
83
75
  label: "Create Database",
84
76
  icon: "note_add",
85
77
  modal: "create-connection",
86
- className: "min-w-[17rem] px-8 py-6 text-sm",
87
78
  })}
88
79
  </div>
89
80
  </div>
@@ -108,13 +99,11 @@ export function renderConnectionsView(state) {
108
99
  icon: "folder_open",
109
100
  modal: "open-connection",
110
101
  tone: "primary",
111
- className: "min-w-[13rem]",
112
102
  })}
113
103
  ${renderConnectionsActionButton({
114
104
  label: "Create Database",
115
105
  icon: "note_add",
116
106
  modal: "create-connection",
117
- className: "min-w-[13rem]",
118
107
  })}
119
108
  ${
120
109
  state.connections.active
@@ -122,7 +111,6 @@ export function renderConnectionsView(state) {
122
111
  label: state.connections.backupLoading ? "Creating Backup..." : "Create Backup",
123
112
  icon: "inventory_2",
124
113
  action: "create-backup",
125
- className: "min-w-[13rem]",
126
114
  disabled: state.connections.backupLoading,
127
115
  })
128
116
  : ""
@@ -3,6 +3,10 @@ import { renderRowEditorPanel } from '../components/rowEditorPanel.js';
3
3
  import { escapeHtml, formatCellValue, formatNumber, isBlobPreview, truncateMiddle } from '../utils/format.js';
4
4
 
5
5
  function getSelectedRow(state) {
6
+ if (state.dataBrowser.selectedRow) {
7
+ return state.dataBrowser.selectedRow;
8
+ }
9
+
6
10
  const rowIndex = state.dataBrowser.selectedRowIndex;
7
11
 
8
12
  if (typeof rowIndex !== 'number') {
@@ -93,14 +97,14 @@ function renderWorkspaceHeader(state) {
93
97
  ${
94
98
  table
95
99
  ? `<button
96
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
100
+ class="standard-button"
97
101
  data-action="toggle-data-tables"
98
102
  type="button"
99
103
  >
100
104
  ${tablesVisible ? '<span class="material-symbols-outlined text-sm">visibility_off</span> Hide Tables' : 'Show Tables'}
101
105
  </button>
102
106
  <button
103
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
107
+ class="standard-button"
104
108
  data-action="export-data-csv"
105
109
  type="button"
106
110
  >
@@ -109,7 +113,7 @@ function renderWorkspaceHeader(state) {
109
113
  : ''
110
114
  }
111
115
  <button
112
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
116
+ class="standard-button"
113
117
  data-action="refresh-view"
114
118
  type="button"
115
119
  >
@@ -119,11 +123,12 @@ function renderWorkspaceHeader(state) {
119
123
  table
120
124
  ? `
121
125
  <button
122
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
126
+ class="standard-button"
123
127
  data-action="navigate"
124
- data-to="/structure"
128
+ data-to="/structure/${encodeURIComponent(table.name)}"
125
129
  type="button"
126
130
  >
131
+ <span class="material-symbols-outlined">account_tree</span>
127
132
  Open Structure
128
133
  </button>
129
134
  `
@@ -239,10 +244,10 @@ function renderTableSearchBar(table, state, activeColumn, filteredRowCount) {
239
244
 
240
245
  return `
241
246
  <div class="flex flex-wrap items-center gap-3 border-b border-outline-variant/10 bg-surface-container-low px-6 py-4">
242
- <label class="flex min-w-[18rem] flex-1 items-center gap-3 border border-outline-variant/20 bg-surface-container-lowest px-4 py-3">
247
+ <label class="control-shell flex min-w-[18rem] flex-1 items-center gap-3 border border-outline-variant/20 bg-surface-container-lowest px-3">
243
248
  <span class="material-symbols-outlined text-base text-on-surface-variant/55">search</span>
244
249
  <input
245
- class="min-w-0 flex-1 bg-transparent text-sm text-on-surface outline-none placeholder:text-on-surface-variant/40"
250
+ class="control-input control-input--ghost min-w-0 flex-1 text-sm text-on-surface outline-none placeholder:text-on-surface-variant/40"
246
251
  data-bind="data-search-query"
247
252
  placeholder="Filter current page..."
248
253
  type="search"
@@ -250,7 +255,7 @@ function renderTableSearchBar(table, state, activeColumn, filteredRowCount) {
250
255
  />
251
256
  </label>
252
257
  <select
253
- class="min-w-[14rem] border border-outline-variant/20 bg-surface-container-lowest px-4 py-3 font-mono text-xs tracking-[0.04em] text-on-surface outline-none"
258
+ class="control-select min-w-[14rem] border border-outline-variant/20 bg-surface-container-lowest font-mono text-xs tracking-[0.04em] text-on-surface outline-none"
254
259
  data-bind="data-search-column"
255
260
  >
256
261
  ${columns
@@ -340,13 +345,9 @@ function renderTableSurface(state) {
340
345
  getRowClass: (_, filteredIndex) => {
341
346
  const rowIndex = filteredRows[filteredIndex]?.index ?? filteredIndex;
342
347
 
343
- return `${
344
- state.dataBrowser.selectedRowIndex === rowIndex
345
- ? 'bg-surface-bright'
346
- : filteredIndex % 2 === 0
347
- ? 'bg-surface-container-low'
348
- : 'bg-surface-container-lowest'
349
- } cursor-pointer transition-colors hover:bg-surface-container-high`;
348
+ return `data-browser-row ${
349
+ filteredIndex % 2 === 0 ? 'data-browser-row--even' : 'data-browser-row--odd'
350
+ } ${state.dataBrowser.selectedRowIndex === rowIndex ? 'is-selected' : ''} cursor-pointer transition-colors`;
350
351
  },
351
352
  getRowAttrs: (_, filteredIndex) => {
352
353
  const rowIndex = filteredRows[filteredIndex]?.index ?? filteredIndex;
@@ -390,10 +391,8 @@ function renderTableSurface(state) {
390
391
  .map(
391
392
  pageSize => `
392
393
  <button
393
- class="border px-3 py-2 text-[10px] font-bold uppercase tracking-[0.16em] transition-colors ${
394
- pageSize === (table.limit ?? state.dataBrowser.pageSize)
395
- ? 'border-primary-container/30 bg-surface-container-high text-primary-container'
396
- : 'border-outline-variant/20 text-on-surface hover:bg-surface-container-highest'
394
+ class="standard-button ${
395
+ pageSize === (table.limit ?? state.dataBrowser.pageSize) ? 'is-active' : ''
397
396
  }"
398
397
  data-action="set-data-page-size"
399
398
  data-page-size="${pageSize}"
@@ -408,7 +407,7 @@ function renderTableSurface(state) {
408
407
  </div>
409
408
  <div class="flex items-center gap-2">
410
409
  <button
411
- class="border border-outline-variant/20 px-3 py-2 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface transition-colors hover:bg-surface-container-highest disabled:cursor-default disabled:opacity-30"
410
+ class="standard-button"
412
411
  data-action="set-data-page"
413
412
  data-page="${page - 1}"
414
413
  type="button"
@@ -420,7 +419,7 @@ function renderTableSurface(state) {
420
419
  page ${escapeHtml(formatNumber(page))} / ${escapeHtml(formatNumber(pageCount))}
421
420
  </div>
422
421
  <button
423
- class="border border-outline-variant/20 px-3 py-2 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface transition-colors hover:bg-surface-container-highest disabled:cursor-default disabled:opacity-30"
422
+ class="standard-button"
424
423
  data-action="set-data-page"
425
424
  data-page="${page + 1}"
426
425
  type="button"
@@ -435,15 +434,23 @@ function renderTableSurface(state) {
435
434
  `;
436
435
  }
437
436
 
438
- function renderDataRowEditorPanel(state) {
437
+ export function renderDataRowEditorPanel(state) {
439
438
  const table = state.dataBrowser.table;
440
439
  const rowIndex = state.dataBrowser.selectedRowIndex;
441
440
  const row = getSelectedRow(state);
441
+ const isIndexedRow = typeof rowIndex === 'number';
442
442
 
443
- if (!table || !row || typeof rowIndex !== 'number') {
443
+ if (!table || !row) {
444
444
  return '';
445
445
  }
446
446
 
447
+ const foreignKeyColumnNames = new Set(
448
+ (table.foreignKeys ?? []).flatMap(foreignKey =>
449
+ (foreignKey.mappings ?? []).map(mapping => String(mapping.from ?? '').trim()).filter(Boolean),
450
+ ),
451
+ );
452
+ const getColumnBadges = column => (foreignKeyColumnNames.has(column.name) ? ['FK'] : []);
453
+
447
454
  const identityColumns = table.identityStrategy?.type === 'primaryKey' ? (table.identityStrategy.columns ?? []) : [];
448
455
  const editableColumns = (table.columnMeta ?? []).filter(column => {
449
456
  if (!column.visible || column.generated) {
@@ -477,10 +484,12 @@ function renderDataRowEditorPanel(state) {
477
484
  return renderRowEditorPanel({
478
485
  title: table.name,
479
486
  sectionLabel: 'Row Editor',
480
- subtitle: `row ${rowIndex + 1}`,
487
+ subtitle: isIndexedRow ? `row ${rowIndex + 1}` : 'targeted row',
481
488
  closeAction: 'clear-data-row-selection',
482
489
  formName: 'save-data-row',
483
- hiddenFields: [{ name: 'rowIndex', value: String(rowIndex) }],
490
+ hiddenFields: isIndexedRow
491
+ ? [{ name: 'rowIndex', value: String(rowIndex) }]
492
+ : [{ name: 'rowIdentity', value: JSON.stringify(row.__identity ?? null) }],
484
493
  disabledMessage: state.connections.active?.readOnly
485
494
  ? 'The active database is opened read-only, so row editing is disabled.'
486
495
  : table.notSafelyUpdatable
@@ -492,19 +501,23 @@ function renderDataRowEditorPanel(state) {
492
501
  return {
493
502
  name: column.name,
494
503
  label: column.name,
504
+ badges: getColumnBadges(column),
495
505
  value: value === null || value === undefined ? '' : String(value),
496
506
  };
497
507
  }),
498
508
  readonlyFields: readonlyColumns.map(column => ({
499
509
  name: column.name,
500
- label: column.name,
510
+ label: {
511
+ label: column.name,
512
+ badges: getColumnBadges(column),
513
+ },
501
514
  value: formatCellValue(row[column.name]),
502
515
  })),
503
516
  saveError: state.dataBrowser.saveError,
504
517
  saving: state.dataBrowser.saving,
505
518
  deleting: state.dataBrowser.deleting,
506
519
  deleteAction: 'delete-data-row',
507
- deleteRowIndex: rowIndex,
520
+ deleteRowIndex: isIndexedRow ? rowIndex : null,
508
521
  deleteEnabled: Boolean(row.__identity),
509
522
  reloadAction: 'reload-data-route',
510
523
  });