@rozenite/sqlite-plugin 2.0.0 → 2.1.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.
- package/CHANGELOG.md +8 -0
- package/dist/devtools/assets/{panel-CsA6Ejak.js → panel-BsdCagVv.js} +3 -3
- package/dist/devtools/panel.html +1 -1
- package/dist/react-native/chunks/index.require.js +4 -16
- package/dist/react-native/chunks/sql.require.js +1 -3
- package/dist/react-native/chunks/useRozeniteSqlitePlugin.require.js +92 -110
- package/dist/react-native/index.d.ts +1 -1
- package/dist/rozenite.json +1 -1
- package/package.json +23 -23
- package/react-native.ts +7 -14
- package/src/react-native/adapters/__tests__/expo-sqlite.test.ts +4 -14
- package/src/react-native/adapters/expo-sqlite.ts +11 -40
- package/src/react-native/adapters/generic.ts +5 -13
- package/src/react-native/adapters/index.ts +1 -4
- package/src/react-native/sqlite-view.ts +2 -8
- package/src/react-native/useRozeniteSqlitePlugin.ts +110 -130
- package/src/react-native/useSqliteAgentTools.ts +6 -18
- package/src/shared/__tests__/bridge-values.test.ts +6 -11
- package/src/shared/__tests__/sql.test.ts +4 -8
- package/src/shared/bridge-values.ts +2 -5
- package/src/shared/sql.ts +3 -7
- package/src/ui/__tests__/sql-editor-utils.test.ts +1 -5
- package/src/ui/__tests__/sqlite-drop-mutations.test.ts +6 -3
- package/src/ui/__tests__/sqlite-row-mutations.test.ts +1 -3
- package/src/ui/__tests__/sqlite-table-column-order.test.ts +4 -19
- package/src/ui/cell-detail-drawer.tsx +1 -6
- package/src/ui/globals.css +14 -65
- package/src/ui/panel.tsx +256 -596
- package/src/ui/query-result-table.tsx +8 -26
- package/src/ui/sql-editor-utils.ts +24 -55
- package/src/ui/sql-editor.tsx +13 -46
- package/src/ui/sqlite-data-table.tsx +11 -28
- package/src/ui/sqlite-drop-modal.tsx +17 -43
- package/src/ui/sqlite-drop-mutations.ts +3 -10
- package/src/ui/sqlite-introspection.ts +9 -25
- package/src/ui/sqlite-row-delete-modal.tsx +5 -12
- package/src/ui/sqlite-row-edit-modal.tsx +22 -64
- package/src/ui/sqlite-row-edit-value.ts +2 -11
- package/src/ui/sqlite-row-mutations.ts +10 -32
- package/src/ui/sqlite-table-column-order.ts +8 -23
- package/src/ui/use-sqlite-requests.ts +46 -86
- package/src/ui/utils.ts +1 -5
- package/src/ui/value-utils.tsx +3 -11
|
@@ -2,11 +2,7 @@ import { useMemo, useState, type ReactNode } from 'react';
|
|
|
2
2
|
import type { CellContext, ColumnDef, OnChangeFn } from '@tanstack/react-table';
|
|
3
3
|
import type { SqliteQueryResult } from '../shared/types';
|
|
4
4
|
import { formatDuration, formatNumber } from './utils';
|
|
5
|
-
import {
|
|
6
|
-
getMetadataBadgeClassName,
|
|
7
|
-
getValueKind,
|
|
8
|
-
getValuePreview,
|
|
9
|
-
} from './value-utils';
|
|
5
|
+
import { getMetadataBadgeClassName, getValueKind, getValuePreview } from './value-utils';
|
|
10
6
|
import { CellDetailDrawer } from './cell-detail-drawer';
|
|
11
7
|
import { SqliteDataTable } from './sqlite-data-table';
|
|
12
8
|
|
|
@@ -44,9 +40,8 @@ type DrawerPayload = {
|
|
|
44
40
|
value: Record<string, unknown>;
|
|
45
41
|
} | null;
|
|
46
42
|
|
|
47
|
-
const joinClassNames = (
|
|
48
|
-
|
|
49
|
-
) => classNames.filter(Boolean).join(' ');
|
|
43
|
+
const joinClassNames = (...classNames: Array<string | false | null | undefined>) =>
|
|
44
|
+
classNames.filter(Boolean).join(' ');
|
|
50
45
|
|
|
51
46
|
const getColumnHeaderTitle = (
|
|
52
47
|
column: string,
|
|
@@ -95,9 +90,7 @@ export const QueryResultTable = ({
|
|
|
95
90
|
const handleInspectRow = (row: Record<string, unknown>, rowIndex: number) => {
|
|
96
91
|
setDrawerPayload({
|
|
97
92
|
title: `Row ${rowNumberOffset + rowIndex + 1}`,
|
|
98
|
-
value: Object.fromEntries(
|
|
99
|
-
visibleColumns.map((column) => [column, row[column]]),
|
|
100
|
-
),
|
|
93
|
+
value: Object.fromEntries(visibleColumns.map((column) => [column, row[column]])),
|
|
101
94
|
});
|
|
102
95
|
};
|
|
103
96
|
|
|
@@ -106,9 +99,7 @@ export const QueryResultTable = ({
|
|
|
106
99
|
...visibleColumns.map((column) => ({
|
|
107
100
|
id: column,
|
|
108
101
|
header: () => (
|
|
109
|
-
<span title={getColumnHeaderTitle(column, columnMeta?.[column])}>
|
|
110
|
-
{column}
|
|
111
|
-
</span>
|
|
102
|
+
<span title={getColumnHeaderTitle(column, columnMeta?.[column])}>{column}</span>
|
|
112
103
|
),
|
|
113
104
|
accessorFn: (row: Record<string, unknown>) => row[column],
|
|
114
105
|
cell: ({ row }: CellContext<Record<string, unknown>, unknown>) => {
|
|
@@ -116,9 +107,7 @@ export const QueryResultTable = ({
|
|
|
116
107
|
|
|
117
108
|
return (
|
|
118
109
|
<div className="sqlite-cell-value">
|
|
119
|
-
<span className="sqlite-cell-preview">
|
|
120
|
-
{getValuePreview(value)}
|
|
121
|
-
</span>
|
|
110
|
+
<span className="sqlite-cell-preview">{getValuePreview(value)}</span>
|
|
122
111
|
<span className="sqlite-cell-kind">{getValueKind(value)}</span>
|
|
123
112
|
</div>
|
|
124
113
|
);
|
|
@@ -145,12 +134,7 @@ export const QueryResultTable = ({
|
|
|
145
134
|
<>
|
|
146
135
|
{showMetadata && metadata ? (
|
|
147
136
|
<div className="sqlite-inline-metadata">
|
|
148
|
-
<span
|
|
149
|
-
className={joinClassNames(
|
|
150
|
-
'sqlite-badge',
|
|
151
|
-
getMetadataBadgeClassName(metadata),
|
|
152
|
-
)}
|
|
153
|
-
>
|
|
137
|
+
<span className={joinClassNames('sqlite-badge', getMetadataBadgeClassName(metadata))}>
|
|
154
138
|
{metadata.statementType}
|
|
155
139
|
</span>
|
|
156
140
|
<span className="sqlite-inline-stat sqlite-tabular">
|
|
@@ -183,9 +167,7 @@ export const QueryResultTable = ({
|
|
|
183
167
|
showRowNumbers
|
|
184
168
|
rowNumberOffset={rowNumberOffset}
|
|
185
169
|
onRowClick={handleInspectRow}
|
|
186
|
-
getRowAriaLabel={(_, rowIndex) =>
|
|
187
|
-
`Inspect row ${rowNumberOffset + rowIndex + 1}`
|
|
188
|
-
}
|
|
170
|
+
getRowAriaLabel={(_, rowIndex) => `Inspect row ${rowNumberOffset + rowIndex + 1}`}
|
|
189
171
|
/>
|
|
190
172
|
|
|
191
173
|
<CellDetailDrawer
|
|
@@ -2,11 +2,7 @@ import type { Completion } from '@codemirror/autocomplete';
|
|
|
2
2
|
import type { SQLNamespace } from '@codemirror/lang-sql';
|
|
3
3
|
import { format } from 'sql-formatter';
|
|
4
4
|
import { quoteSqlIdentifier } from '../shared/sql';
|
|
5
|
-
import type {
|
|
6
|
-
SqliteColumnInfo,
|
|
7
|
-
SqliteEntity,
|
|
8
|
-
SqliteSchema,
|
|
9
|
-
} from './sqlite-introspection';
|
|
5
|
+
import type { SqliteColumnInfo, SqliteEntity, SqliteSchema } from './sqlite-introspection';
|
|
10
6
|
|
|
11
7
|
export type SqlEditorColumnCacheState = {
|
|
12
8
|
databaseId: string | null;
|
|
@@ -28,17 +24,14 @@ type SqlEditorAliasLookup = Record<
|
|
|
28
24
|
}
|
|
29
25
|
>;
|
|
30
26
|
|
|
31
|
-
const SQL_IDENTIFIER_PATTERN =
|
|
32
|
-
'"(?:[^"]|"")+"|`(?:[^`]|``)+`|\\[[^\\]]+\\]|[A-Za-z_][\\w$]*';
|
|
27
|
+
const SQL_IDENTIFIER_PATTERN = '"(?:[^"]|"")+"|`(?:[^`]|``)+`|\\[[^\\]]+\\]|[A-Za-z_][\\w$]*';
|
|
33
28
|
|
|
34
29
|
const bareIdentifierPattern = /^[A-Za-z_][\w$]*$/;
|
|
35
30
|
const trailingIdentifierPattern = /[A-Za-z_][\w$]*$/;
|
|
36
31
|
const entityMemberPattern = new RegExp(
|
|
37
32
|
`(${SQL_IDENTIFIER_PATTERN})\\s*\\.\\s*(${SQL_IDENTIFIER_PATTERN})\\s*\\.\\s*$`,
|
|
38
33
|
);
|
|
39
|
-
const singleMemberPattern = new RegExp(
|
|
40
|
-
`(${SQL_IDENTIFIER_PATTERN})\\s*\\.\\s*$`,
|
|
41
|
-
);
|
|
34
|
+
const singleMemberPattern = new RegExp(`(${SQL_IDENTIFIER_PATTERN})\\s*\\.\\s*$`);
|
|
42
35
|
const aliasPattern = new RegExp(
|
|
43
36
|
`\\b(?:FROM|JOIN|UPDATE|INTO)\\s+(?:(?:(${SQL_IDENTIFIER_PATTERN})\\s*\\.\\s*)?(${SQL_IDENTIFIER_PATTERN}))(?:\\s+(?:AS\\s+)?(${SQL_IDENTIFIER_PATTERN}))?`,
|
|
44
37
|
'gi',
|
|
@@ -63,9 +56,7 @@ const unquoteIdentifier = (identifier: string) => {
|
|
|
63
56
|
};
|
|
64
57
|
|
|
65
58
|
const getIdentifierInsertText = (identifier: string) =>
|
|
66
|
-
bareIdentifierPattern.test(identifier)
|
|
67
|
-
? identifier
|
|
68
|
-
: quoteSqlIdentifier(identifier);
|
|
59
|
+
bareIdentifierPattern.test(identifier) ? identifier : quoteSqlIdentifier(identifier);
|
|
69
60
|
|
|
70
61
|
const getColumnDetail = (column: SqliteColumnInfo) => {
|
|
71
62
|
const parts = [column.type].filter(Boolean);
|
|
@@ -95,10 +86,7 @@ export const createSqlEditorColumnCache = (
|
|
|
95
86
|
export const syncSqlEditorColumnCacheDatabase = (
|
|
96
87
|
state: SqlEditorColumnCacheState,
|
|
97
88
|
databaseId: string | null,
|
|
98
|
-
) =>
|
|
99
|
-
state.databaseId === databaseId
|
|
100
|
-
? state
|
|
101
|
-
: createSqlEditorColumnCache(databaseId);
|
|
89
|
+
) => (state.databaseId === databaseId ? state : createSqlEditorColumnCache(databaseId));
|
|
102
90
|
|
|
103
91
|
export const getSqlEditorColumnCacheKey = (
|
|
104
92
|
databaseId: string,
|
|
@@ -111,8 +99,7 @@ export const getSqlEditorCachedColumns = (
|
|
|
111
99
|
databaseId: string,
|
|
112
100
|
schemaName: string,
|
|
113
101
|
entityName: string,
|
|
114
|
-
) =>
|
|
115
|
-
state.entries[getSqlEditorColumnCacheKey(databaseId, schemaName, entityName)];
|
|
102
|
+
) => state.entries[getSqlEditorColumnCacheKey(databaseId, schemaName, entityName)];
|
|
116
103
|
|
|
117
104
|
export const setSqlEditorCachedColumns = (
|
|
118
105
|
state: SqlEditorColumnCacheState,
|
|
@@ -176,12 +163,8 @@ export const buildSqlCompletionSchema = ({
|
|
|
176
163
|
const columns =
|
|
177
164
|
databaseId == null
|
|
178
165
|
? []
|
|
179
|
-
: (getSqlEditorCachedColumns(
|
|
180
|
-
|
|
181
|
-
databaseId,
|
|
182
|
-
entity.schemaName,
|
|
183
|
-
entity.name,
|
|
184
|
-
) ?? []);
|
|
166
|
+
: (getSqlEditorCachedColumns(columnCache, databaseId, entity.schemaName, entity.name) ??
|
|
167
|
+
[]);
|
|
185
168
|
|
|
186
169
|
schemaChildren[entity.name] = {
|
|
187
170
|
self: {
|
|
@@ -220,19 +203,15 @@ export const getDefaultSqlCompletionSchema = (schemas: SqliteSchema[]) =>
|
|
|
220
203
|
schemas.find((schema) => schema.name === 'main')?.name ?? schemas[0]?.name;
|
|
221
204
|
|
|
222
205
|
export const createSqlColumnCompletions = (columns: SqliteColumnInfo[]) =>
|
|
223
|
-
columns.map(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
export const extractSqlEditorAliases = (
|
|
234
|
-
sqlBeforeCursor: string,
|
|
235
|
-
): SqlEditorAliasLookup => {
|
|
206
|
+
columns.map((column): Completion => ({
|
|
207
|
+
label: column.name,
|
|
208
|
+
apply: getIdentifierInsertText(column.name),
|
|
209
|
+
detail: getColumnDetail(column) || undefined,
|
|
210
|
+
type: 'property',
|
|
211
|
+
boost: column.primaryKeyOrder > 0 ? 1 : 0,
|
|
212
|
+
}));
|
|
213
|
+
|
|
214
|
+
export const extractSqlEditorAliases = (sqlBeforeCursor: string): SqlEditorAliasLookup => {
|
|
236
215
|
const aliases: SqlEditorAliasLookup = {};
|
|
237
216
|
|
|
238
217
|
let match: RegExpExecArray | null;
|
|
@@ -256,10 +235,7 @@ export const getSqlEditorColumnCompletionRequest = (
|
|
|
256
235
|
const beforeCursor = sql.slice(0, cursorPosition);
|
|
257
236
|
const trailingIdentifier = beforeCursor.match(trailingIdentifierPattern)?.[0];
|
|
258
237
|
const replacementLength = trailingIdentifier?.length ?? 0;
|
|
259
|
-
const lookupPrefix = beforeCursor.slice(
|
|
260
|
-
0,
|
|
261
|
-
beforeCursor.length - replacementLength,
|
|
262
|
-
);
|
|
238
|
+
const lookupPrefix = beforeCursor.slice(0, beforeCursor.length - replacementLength);
|
|
263
239
|
|
|
264
240
|
const qualifiedMatch = lookupPrefix.match(entityMemberPattern);
|
|
265
241
|
if (qualifiedMatch) {
|
|
@@ -298,8 +274,7 @@ export const resolveSqlEditorEntityReference = ({
|
|
|
298
274
|
const findExactEntity = (schemaName: string, entityName: string) =>
|
|
299
275
|
entities.find(
|
|
300
276
|
(entity) =>
|
|
301
|
-
normalizeIdentifier(entity.schemaName) ===
|
|
302
|
-
normalizeIdentifier(schemaName) &&
|
|
277
|
+
normalizeIdentifier(entity.schemaName) === normalizeIdentifier(schemaName) &&
|
|
303
278
|
normalizeIdentifier(entity.name) === normalizeIdentifier(entityName),
|
|
304
279
|
) ?? null;
|
|
305
280
|
|
|
@@ -315,17 +290,13 @@ export const resolveSqlEditorEntityReference = ({
|
|
|
315
290
|
|
|
316
291
|
return (
|
|
317
292
|
entities.find(
|
|
318
|
-
(entity) =>
|
|
319
|
-
normalizeIdentifier(entity.name) ===
|
|
320
|
-
normalizeIdentifier(aliasMatch.entityName),
|
|
293
|
+
(entity) => normalizeIdentifier(entity.name) === normalizeIdentifier(aliasMatch.entityName),
|
|
321
294
|
) ?? null
|
|
322
295
|
);
|
|
323
296
|
}
|
|
324
297
|
|
|
325
298
|
const entityMatches = entities.filter(
|
|
326
|
-
(entity) =>
|
|
327
|
-
normalizeIdentifier(entity.name) ===
|
|
328
|
-
normalizeIdentifier(request.entityName),
|
|
299
|
+
(entity) => normalizeIdentifier(entity.name) === normalizeIdentifier(request.entityName),
|
|
329
300
|
);
|
|
330
301
|
|
|
331
302
|
if (entityMatches.length === 0) {
|
|
@@ -335,8 +306,7 @@ export const resolveSqlEditorEntityReference = ({
|
|
|
335
306
|
if (selectedSchemaName) {
|
|
336
307
|
const schemaMatch = entityMatches.find(
|
|
337
308
|
(entity) =>
|
|
338
|
-
normalizeIdentifier(entity.schemaName) ===
|
|
339
|
-
normalizeIdentifier(selectedSchemaName),
|
|
309
|
+
normalizeIdentifier(entity.schemaName) === normalizeIdentifier(selectedSchemaName),
|
|
340
310
|
);
|
|
341
311
|
|
|
342
312
|
if (schemaMatch) {
|
|
@@ -345,8 +315,7 @@ export const resolveSqlEditorEntityReference = ({
|
|
|
345
315
|
}
|
|
346
316
|
|
|
347
317
|
return (
|
|
348
|
-
entityMatches.find(
|
|
349
|
-
|
|
350
|
-
) ?? entityMatches[0]
|
|
318
|
+
entityMatches.find((entity) => normalizeIdentifier(entity.schemaName) === 'main') ??
|
|
319
|
+
entityMatches[0]
|
|
351
320
|
);
|
|
352
321
|
};
|
package/src/ui/sql-editor.tsx
CHANGED
|
@@ -5,26 +5,11 @@ import {
|
|
|
5
5
|
completionKeymap,
|
|
6
6
|
type CompletionSource,
|
|
7
7
|
} from '@codemirror/autocomplete';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
history,
|
|
11
|
-
historyKeymap,
|
|
12
|
-
indentWithTab,
|
|
13
|
-
} from '@codemirror/commands';
|
|
14
|
-
import {
|
|
15
|
-
schemaCompletionSource,
|
|
16
|
-
sql,
|
|
17
|
-
SQLite,
|
|
18
|
-
type SQLNamespace,
|
|
19
|
-
} from '@codemirror/lang-sql';
|
|
8
|
+
import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
|
|
9
|
+
import { schemaCompletionSource, sql, SQLite, type SQLNamespace } from '@codemirror/lang-sql';
|
|
20
10
|
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
21
11
|
import { tags } from '@lezer/highlight';
|
|
22
|
-
import {
|
|
23
|
-
Compartment,
|
|
24
|
-
EditorSelection,
|
|
25
|
-
EditorState,
|
|
26
|
-
type Extension,
|
|
27
|
-
} from '@codemirror/state';
|
|
12
|
+
import { Compartment, EditorSelection, EditorState, type Extension } from '@codemirror/state';
|
|
28
13
|
import { searchKeymap } from '@codemirror/search';
|
|
29
14
|
import {
|
|
30
15
|
Decoration,
|
|
@@ -36,13 +21,7 @@ import {
|
|
|
36
21
|
lineNumbers,
|
|
37
22
|
placeholder,
|
|
38
23
|
} from '@codemirror/view';
|
|
39
|
-
import {
|
|
40
|
-
forwardRef,
|
|
41
|
-
useEffect,
|
|
42
|
-
useImperativeHandle,
|
|
43
|
-
useRef,
|
|
44
|
-
type Ref,
|
|
45
|
-
} from 'react';
|
|
24
|
+
import { forwardRef, useEffect, useImperativeHandle, useRef, type Ref } from 'react';
|
|
46
25
|
|
|
47
26
|
export type SqlEditorHandle = {
|
|
48
27
|
focus: () => void;
|
|
@@ -137,8 +116,7 @@ const sqlEditorTheme = EditorView.theme(
|
|
|
137
116
|
display: 'flex',
|
|
138
117
|
flex: '1 1 auto',
|
|
139
118
|
minHeight: '0',
|
|
140
|
-
fontFamily:
|
|
141
|
-
"'IBM Plex Mono', 'JetBrains Mono', 'SFMono-Regular', ui-monospace, monospace",
|
|
119
|
+
fontFamily: "'IBM Plex Mono', 'JetBrains Mono', 'SFMono-Regular', ui-monospace, monospace",
|
|
142
120
|
},
|
|
143
121
|
'.cm-sizer': {
|
|
144
122
|
minHeight: '100%',
|
|
@@ -152,10 +130,9 @@ const sqlEditorTheme = EditorView.theme(
|
|
|
152
130
|
'.cm-cursor, .cm-dropCursor': {
|
|
153
131
|
borderLeftColor: '#f7fbff',
|
|
154
132
|
},
|
|
155
|
-
'.cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection':
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
},
|
|
133
|
+
'.cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection': {
|
|
134
|
+
backgroundColor: 'rgba(89, 163, 255, 0.2)',
|
|
135
|
+
},
|
|
159
136
|
'.cm-activeLine': {
|
|
160
137
|
backgroundColor: 'rgba(255, 255, 255, 0.032)',
|
|
161
138
|
},
|
|
@@ -367,9 +344,7 @@ const SqlEditorInner = (
|
|
|
367
344
|
history(),
|
|
368
345
|
closeBrackets(),
|
|
369
346
|
syntaxHighlighting(sqlHighlightStyle),
|
|
370
|
-
sqlSupportCompartment.of(
|
|
371
|
-
sql({ dialect: SQLite, upperCaseKeywords: true }),
|
|
372
|
-
),
|
|
347
|
+
sqlSupportCompartment.of(sql({ dialect: SQLite, upperCaseKeywords: true })),
|
|
373
348
|
autocompleteCompartment.of(
|
|
374
349
|
createAutocompleteExtension({
|
|
375
350
|
completionSchema: initialConfigRef.current.completionSchema,
|
|
@@ -378,15 +353,9 @@ const SqlEditorInner = (
|
|
|
378
353
|
defaultTable: initialConfigRef.current.defaultTable,
|
|
379
354
|
}),
|
|
380
355
|
),
|
|
381
|
-
errorLineCompartment.of(
|
|
382
|
-
|
|
383
|
-
),
|
|
384
|
-
placeholderCompartment.of(
|
|
385
|
-
placeholder(initialConfigRef.current.placeholderText),
|
|
386
|
-
),
|
|
387
|
-
editableCompartment.of(
|
|
388
|
-
createEditableExtension(initialConfigRef.current.readOnly),
|
|
389
|
-
),
|
|
356
|
+
errorLineCompartment.of(createErrorLineExtension(initialConfigRef.current.errorLine)),
|
|
357
|
+
placeholderCompartment.of(placeholder(initialConfigRef.current.placeholderText)),
|
|
358
|
+
editableCompartment.of(createEditableExtension(initialConfigRef.current.readOnly)),
|
|
390
359
|
keymap.of([
|
|
391
360
|
{
|
|
392
361
|
key: 'Mod-Enter',
|
|
@@ -447,9 +416,7 @@ const SqlEditorInner = (
|
|
|
447
416
|
|
|
448
417
|
editorView.dispatch({
|
|
449
418
|
effects: [
|
|
450
|
-
sqlSupportCompartment.reconfigure(
|
|
451
|
-
sql({ dialect: SQLite, upperCaseKeywords: true }),
|
|
452
|
-
),
|
|
419
|
+
sqlSupportCompartment.reconfigure(sql({ dialect: SQLite, upperCaseKeywords: true })),
|
|
453
420
|
autocompleteCompartment.reconfigure(
|
|
454
421
|
createAutocompleteExtension({
|
|
455
422
|
completionSchema,
|
|
@@ -19,9 +19,8 @@ import {
|
|
|
19
19
|
import { formatNumber } from './utils';
|
|
20
20
|
import { SQLITE_ROW_NUMBER_COLUMN_ID } from './sqlite-table-column-order';
|
|
21
21
|
|
|
22
|
-
const joinClassNames = (
|
|
23
|
-
|
|
24
|
-
) => classNames.filter(Boolean).join(' ');
|
|
22
|
+
const joinClassNames = (...classNames: Array<string | false | null | undefined>) =>
|
|
23
|
+
classNames.filter(Boolean).join(' ');
|
|
25
24
|
|
|
26
25
|
const LoadingState = ({ columns }: { columns: number }) => (
|
|
27
26
|
<div className="sqlite-results-loading" aria-live="polite">
|
|
@@ -34,10 +33,7 @@ const LoadingState = ({ columns }: { columns: number }) => (
|
|
|
34
33
|
}}
|
|
35
34
|
>
|
|
36
35
|
{Array.from({ length: Math.max(columns, 3) }, (_, columnIndex) => (
|
|
37
|
-
<span
|
|
38
|
-
key={`${rowIndex}-${columnIndex}`}
|
|
39
|
-
className="sqlite-results-loading-bar"
|
|
40
|
-
/>
|
|
36
|
+
<span key={`${rowIndex}-${columnIndex}`} className="sqlite-results-loading-bar" />
|
|
41
37
|
))}
|
|
42
38
|
</div>
|
|
43
39
|
))}
|
|
@@ -74,8 +70,7 @@ const SortableColumnHeader = <TData extends RowData>({
|
|
|
74
70
|
<th
|
|
75
71
|
scope="col"
|
|
76
72
|
className={joinClassNames(
|
|
77
|
-
header.column.id === SQLITE_ROW_NUMBER_COLUMN_ID &&
|
|
78
|
-
'sqlite-results-number-col',
|
|
73
|
+
header.column.id === SQLITE_ROW_NUMBER_COLUMN_ID && 'sqlite-results-number-col',
|
|
79
74
|
header.column.getIsResizing() && 'sqlite-table-column-resizing',
|
|
80
75
|
)}
|
|
81
76
|
style={{
|
|
@@ -165,8 +160,7 @@ export const SqliteDataTable = <TData extends RowData>({
|
|
|
165
160
|
getCoreRowModel: getCoreRowModel(),
|
|
166
161
|
});
|
|
167
162
|
|
|
168
|
-
const loadingColumns =
|
|
169
|
-
loadingColumnCount ?? columns.length + (showRowNumbers ? 1 : 0);
|
|
163
|
+
const loadingColumns = loadingColumnCount ?? columns.length + (showRowNumbers ? 1 : 0);
|
|
170
164
|
const tableRows = table.getRowModel().rows;
|
|
171
165
|
const rowVirtualizer = useVirtualizer({
|
|
172
166
|
count: tableRows.length,
|
|
@@ -175,8 +169,7 @@ export const SqliteDataTable = <TData extends RowData>({
|
|
|
175
169
|
overscan: 10,
|
|
176
170
|
getItemKey: (index) => tableRows[index]?.id ?? index,
|
|
177
171
|
measureElement:
|
|
178
|
-
typeof window !== 'undefined' &&
|
|
179
|
-
!window.navigator.userAgent.includes('Firefox')
|
|
172
|
+
typeof window !== 'undefined' && !window.navigator.userAgent.includes('Firefox')
|
|
180
173
|
? (element) => element?.getBoundingClientRect().height ?? 0
|
|
181
174
|
: undefined,
|
|
182
175
|
});
|
|
@@ -235,14 +228,11 @@ export const SqliteDataTable = <TData extends RowData>({
|
|
|
235
228
|
tabIndex={onRowClick ? 0 : undefined}
|
|
236
229
|
aria-label={
|
|
237
230
|
onRowClick
|
|
238
|
-
? (getRowAriaLabel?.(row.original, row.index) ??
|
|
239
|
-
`Inspect row ${row.index + 1}`)
|
|
231
|
+
? (getRowAriaLabel?.(row.original, row.index) ?? `Inspect row ${row.index + 1}`)
|
|
240
232
|
: undefined
|
|
241
233
|
}
|
|
242
234
|
onClick={() => onRowClick?.(row.original, row.index)}
|
|
243
|
-
onKeyDown={(event) =>
|
|
244
|
-
handleRowKeyDown(event, row.original, row.index)
|
|
245
|
-
}
|
|
235
|
+
onKeyDown={(event) => handleRowKeyDown(event, row.original, row.index)}
|
|
246
236
|
style={{
|
|
247
237
|
transform: `translateY(${virtualRow.start}px)`,
|
|
248
238
|
}}
|
|
@@ -251,8 +241,7 @@ export const SqliteDataTable = <TData extends RowData>({
|
|
|
251
241
|
<td
|
|
252
242
|
key={cell.id}
|
|
253
243
|
className={joinClassNames(
|
|
254
|
-
cell.column.id === SQLITE_ROW_NUMBER_COLUMN_ID &&
|
|
255
|
-
'sqlite-results-row-number',
|
|
244
|
+
cell.column.id === SQLITE_ROW_NUMBER_COLUMN_ID && 'sqlite-results-row-number',
|
|
256
245
|
)}
|
|
257
246
|
style={{ width: cell.column.getSize() }}
|
|
258
247
|
>
|
|
@@ -267,16 +256,10 @@ export const SqliteDataTable = <TData extends RowData>({
|
|
|
267
256
|
);
|
|
268
257
|
|
|
269
258
|
return (
|
|
270
|
-
<div
|
|
271
|
-
className={joinClassNames('sqlite-results-shell', shellClassName)}
|
|
272
|
-
data-table-id={tableId}
|
|
273
|
-
>
|
|
259
|
+
<div className={joinClassNames('sqlite-results-shell', shellClassName)} data-table-id={tableId}>
|
|
274
260
|
<div
|
|
275
261
|
ref={scrollElementRef}
|
|
276
|
-
className={joinClassNames(
|
|
277
|
-
'sqlite-results-scroll',
|
|
278
|
-
scrollContainerClassName,
|
|
279
|
-
)}
|
|
262
|
+
className={joinClassNames('sqlite-results-scroll', scrollContainerClassName)}
|
|
280
263
|
>
|
|
281
264
|
{loading ? (
|
|
282
265
|
<LoadingState columns={loadingColumns} />
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { Modal, useOverlayState } from '@heroui/react';
|
|
2
2
|
import { AlertTriangle, Trash2 } from 'lucide-react';
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
SqliteModalCloseButton,
|
|
6
|
-
sqliteSecondaryButtonClassName,
|
|
7
|
-
} from './sqlite-modal-controls';
|
|
4
|
+
import { SqliteModalCloseButton, sqliteSecondaryButtonClassName } from './sqlite-modal-controls';
|
|
8
5
|
|
|
9
6
|
export type SqliteDropModalTarget =
|
|
10
7
|
| {
|
|
@@ -47,12 +44,7 @@ const getConfirmationLabel = (target: SqliteDropModalTarget) =>
|
|
|
47
44
|
? `${target.entityType === 'view' ? 'view' : 'table'} name`
|
|
48
45
|
: 'database name';
|
|
49
46
|
|
|
50
|
-
export const SqliteDropModal = ({
|
|
51
|
-
isOpen,
|
|
52
|
-
target,
|
|
53
|
-
onClose,
|
|
54
|
-
onConfirm,
|
|
55
|
-
}: SqliteDropModalProps) => {
|
|
47
|
+
export const SqliteDropModal = ({ isOpen, target, onClose, onConfirm }: SqliteDropModalProps) => {
|
|
56
48
|
const overlay = useOverlayState({
|
|
57
49
|
isOpen,
|
|
58
50
|
onOpenChange: (open: boolean) => {
|
|
@@ -86,9 +78,7 @@ export const SqliteDropModal = ({
|
|
|
86
78
|
await onConfirm();
|
|
87
79
|
onClose();
|
|
88
80
|
} catch (nextError) {
|
|
89
|
-
setError(
|
|
90
|
-
nextError instanceof Error ? nextError.message : String(nextError),
|
|
91
|
-
);
|
|
81
|
+
setError(nextError instanceof Error ? nextError.message : String(nextError));
|
|
92
82
|
} finally {
|
|
93
83
|
setDropping(false);
|
|
94
84
|
}
|
|
@@ -112,12 +102,8 @@ export const SqliteDropModal = ({
|
|
|
112
102
|
<AlertTriangle aria-hidden="true" className="h-5 w-5" />
|
|
113
103
|
</div>
|
|
114
104
|
<div>
|
|
115
|
-
<h2 className="text-lg font-semibold text-white">
|
|
116
|
-
|
|
117
|
-
</h2>
|
|
118
|
-
<p className="mt-1 text-sm text-slate-400">
|
|
119
|
-
{getModalSubtitle(target)}
|
|
120
|
-
</p>
|
|
105
|
+
<h2 className="text-lg font-semibold text-white">{getModalTitle(target)}</h2>
|
|
106
|
+
<p className="mt-1 text-sm text-slate-400">{getModalSubtitle(target)}</p>
|
|
121
107
|
</div>
|
|
122
108
|
</div>
|
|
123
109
|
<SqliteModalCloseButton onClose={onClose} disabled={dropping} />
|
|
@@ -128,23 +114,18 @@ export const SqliteDropModal = ({
|
|
|
128
114
|
{target.kind === 'database' ? (
|
|
129
115
|
<p className="text-sm leading-6 text-slate-300">
|
|
130
116
|
This drops every table and view in{' '}
|
|
131
|
-
<span className="font-medium text-white">
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
their rows, indexes and triggers. The database file itself
|
|
138
|
-
is not deleted; it is left empty. This cannot be undone.
|
|
117
|
+
<span className="font-medium text-white">{target.databaseName}</span> —{' '}
|
|
118
|
+
{target.tableCount} table
|
|
119
|
+
{target.tableCount === 1 ? '' : 's'} and {target.viewCount} view
|
|
120
|
+
{target.viewCount === 1 ? '' : 's'}, along with all their rows, indexes and
|
|
121
|
+
triggers. The database file itself is not deleted; it is left empty. This cannot
|
|
122
|
+
be undone.
|
|
139
123
|
</p>
|
|
140
124
|
) : (
|
|
141
125
|
<p className="text-sm leading-6 text-slate-300">
|
|
142
126
|
This permanently drops{' '}
|
|
143
|
-
<span className="font-medium text-white">
|
|
144
|
-
|
|
145
|
-
</span>{' '}
|
|
146
|
-
and everything it owns — rows, indexes and triggers. This
|
|
147
|
-
cannot be undone.
|
|
127
|
+
<span className="font-medium text-white">{target.qualifiedName}</span> and
|
|
128
|
+
everything it owns — rows, indexes and triggers. This cannot be undone.
|
|
148
129
|
</p>
|
|
149
130
|
)}
|
|
150
131
|
|
|
@@ -156,15 +137,10 @@ export const SqliteDropModal = ({
|
|
|
156
137
|
</div>
|
|
157
138
|
|
|
158
139
|
<div className="space-y-2">
|
|
159
|
-
<label
|
|
160
|
-
htmlFor="sqlite-drop-confirmation-input"
|
|
161
|
-
className="sqlite-helper-text"
|
|
162
|
-
>
|
|
140
|
+
<label htmlFor="sqlite-drop-confirmation-input" className="sqlite-helper-text">
|
|
163
141
|
Type the {getConfirmationLabel(target)}{' '}
|
|
164
|
-
<span className="font-medium text-white">
|
|
165
|
-
|
|
166
|
-
</span>{' '}
|
|
167
|
-
to confirm
|
|
142
|
+
<span className="font-medium text-white">{target.confirmationValue}</span> to
|
|
143
|
+
confirm
|
|
168
144
|
</label>
|
|
169
145
|
<input
|
|
170
146
|
id="sqlite-drop-confirmation-input"
|
|
@@ -174,9 +150,7 @@ export const SqliteDropModal = ({
|
|
|
174
150
|
spellCheck={false}
|
|
175
151
|
className="sqlite-input"
|
|
176
152
|
value={confirmationInput}
|
|
177
|
-
onChange={(event) =>
|
|
178
|
-
setConfirmationInput(event.target.value)
|
|
179
|
-
}
|
|
153
|
+
onChange={(event) => setConfirmationInput(event.target.value)}
|
|
180
154
|
disabled={dropping}
|
|
181
155
|
/>
|
|
182
156
|
</div>
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { quoteSqlIdentifier } from '../shared/sql';
|
|
2
2
|
import type { SqliteEntity } from './sqlite-introspection';
|
|
3
3
|
|
|
4
|
-
export type SqliteDropTarget = Pick<
|
|
5
|
-
SqliteEntity,
|
|
6
|
-
'schemaName' | 'name' | 'type'
|
|
7
|
-
>;
|
|
4
|
+
export type SqliteDropTarget = Pick<SqliteEntity, 'schemaName' | 'name' | 'type'>;
|
|
8
5
|
|
|
9
6
|
const buildQualifiedEntityName = (schemaName: string, entityName: string) =>
|
|
10
7
|
`${quoteSqlIdentifier(schemaName)}.${quoteSqlIdentifier(entityName)}`;
|
|
@@ -25,9 +22,7 @@ export const buildDropEntitySql = (entity: SqliteDropTarget): string => {
|
|
|
25
22
|
* relative order within each group. This keeps a view that references a
|
|
26
23
|
* table from ever being left dangling mid-sweep.
|
|
27
24
|
*/
|
|
28
|
-
export const orderEntitiesForDrop = (
|
|
29
|
-
entities: SqliteDropTarget[],
|
|
30
|
-
): SqliteDropTarget[] => [
|
|
25
|
+
export const orderEntitiesForDrop = (entities: SqliteDropTarget[]): SqliteDropTarget[] => [
|
|
31
26
|
...entities.filter((entity) => entity.type === 'view'),
|
|
32
27
|
...entities.filter((entity) => entity.type === 'table'),
|
|
33
28
|
];
|
|
@@ -52,9 +47,7 @@ export const SQLITE_READ_FOREIGN_KEYS_SQL = 'PRAGMA foreign_keys';
|
|
|
52
47
|
export const buildSetForeignKeysSql = (enabled: boolean): string =>
|
|
53
48
|
`PRAGMA foreign_keys = ${enabled ? 'ON' : 'OFF'}`;
|
|
54
49
|
|
|
55
|
-
export const isForeignKeysEnabled = (
|
|
56
|
-
rows: Record<string, unknown>[],
|
|
57
|
-
): boolean => {
|
|
50
|
+
export const isForeignKeysEnabled = (rows: Record<string, unknown>[]): boolean => {
|
|
58
51
|
const rawValue = rows[0]?.foreign_keys;
|
|
59
52
|
return Number(rawValue) === 1;
|
|
60
53
|
};
|