windmill-components 1.596.0 → 1.596.1
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/package/common.d.ts +2 -0
- package/package/components/ArgInput.svelte +4 -2
- package/package/components/ArgInput.svelte.d.ts +1 -0
- package/package/components/ArrayTypeNarrowing.svelte +8 -7
- package/package/components/DBManager.svelte +377 -41
- package/package/components/DBManager.svelte.d.ts +16 -1
- package/package/components/DBManagerContent.svelte +195 -0
- package/package/components/DBManagerContent.svelte.d.ts +24 -0
- package/package/components/DBManagerDrawer.svelte +90 -175
- package/package/components/DBManagerDrawer.svelte.d.ts +5 -1
- package/package/components/InputTransformForm.svelte +13 -1
- package/package/components/InputTransformForm.svelte.d.ts +1 -0
- package/package/components/InputTransformSchemaForm.svelte +2 -1
- package/package/components/InputTransformSchemaForm.svelte.d.ts +1 -0
- package/package/components/SchemaForm.svelte +21 -7
- package/package/components/SchemaForm.svelte.d.ts +1 -0
- package/package/components/ScriptBuilder.svelte +14 -0
- package/package/components/common/drawer/DrawerContent.svelte +4 -1
- package/package/components/common/drawer/DrawerContent.svelte.d.ts +1 -0
- package/package/components/common/modal/Modal.svelte +13 -17
- package/package/components/common/modal/Modal.svelte.d.ts +13 -23
- package/package/components/copilot/chat/AIChatDisplay.svelte +3 -1
- package/package/components/copilot/chat/AIChatManager.svelte.js +2 -0
- package/package/components/copilot/chat/DatatableCreationPolicy.svelte +63 -0
- package/package/components/copilot/chat/DatatableCreationPolicy.svelte.d.ts +3 -0
- package/package/components/copilot/chat/__tests__/app/appEvalHelpers.js +13 -0
- package/package/components/copilot/chat/app/core.d.ts +27 -0
- package/package/components/copilot/chat/app/core.js +207 -2
- package/package/components/copilot/chat/flow/core.js +1 -1
- package/package/components/copilot/chat/script/core.js +1 -1
- package/package/components/copilot/chat/sharedChatState.svelte.js +3 -1
- package/package/components/dbOps.d.ts +3 -0
- package/package/components/dbOps.js +10 -1
- package/package/components/flows/CreateActionsApp.svelte +114 -9
- package/package/components/flows/CreateActionsApp.svelte.d.ts +2 -17
- package/package/components/flows/DebounceLimit.svelte +64 -1
- package/package/components/flows/DebounceLimit.svelte.d.ts +6 -1
- package/package/components/flows/content/FlowModuleComponent.svelte +1 -0
- package/package/components/flows/content/FlowSettings.svelte +4 -0
- package/package/components/flows/conversations/FlowChatManager.svelte.js +0 -1
- package/package/components/flows/flowInfers.js +19 -1
- package/package/components/flows/flowStore.svelte.d.ts +2 -0
- package/package/components/flows/utils.svelte.js +17 -0
- package/package/components/icons/index.d.ts +101 -0
- package/package/components/icons/index.js +1 -1
- package/package/components/raw_apps/DefaultDatabaseSelector.svelte +61 -0
- package/package/components/raw_apps/DefaultDatabaseSelector.svelte.d.ts +13 -0
- package/package/components/raw_apps/RawAppBackgroundRunner.svelte +17 -1
- package/package/components/raw_apps/RawAppDataTableDrawer.svelte +205 -0
- package/package/components/raw_apps/RawAppDataTableDrawer.svelte.d.ts +14 -0
- package/package/components/raw_apps/RawAppDataTableList.svelte +160 -0
- package/package/components/raw_apps/RawAppDataTableList.svelte.d.ts +22 -0
- package/package/components/raw_apps/RawAppEditor.svelte +153 -9
- package/package/components/raw_apps/RawAppEditor.svelte.d.ts +3 -0
- package/package/components/raw_apps/RawAppEditorHeader.svelte +2 -2
- package/package/components/raw_apps/RawAppEditorHeader.svelte.d.ts +3 -0
- package/package/components/raw_apps/RawAppHistoryManager.svelte.d.ts +5 -2
- package/package/components/raw_apps/RawAppHistoryManager.svelte.js +11 -9
- package/package/components/raw_apps/RawAppSidebar.svelte +36 -4
- package/package/components/raw_apps/RawAppSidebar.svelte.d.ts +8 -0
- package/package/components/raw_apps/dataTableRefUtils.d.ts +30 -0
- package/package/components/raw_apps/dataTableRefUtils.js +40 -0
- package/package/components/raw_apps/datatableUtils.svelte.d.ts +24 -0
- package/package/components/raw_apps/datatableUtils.svelte.js +69 -0
- package/package/gen/schemas.gen.d.ts +39 -0
- package/package/gen/schemas.gen.js +39 -0
- package/package/gen/services.gen.d.ts +10 -1
- package/package/gen/services.gen.js +18 -0
- package/package/gen/types.gen.d.ts +45 -0
- package/package/system_prompts/index.d.ts +3 -0
- package/package/system_prompts/index.js +33 -0
- package/package/system_prompts/prompts.d.ts +25 -0
- package/package/system_prompts/prompts.js +2598 -0
- package/package/utils.js +3 -1
- package/package.json +19 -3
package/package/common.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export interface SchemaProperty {
|
|
|
43
43
|
};
|
|
44
44
|
required?: string[];
|
|
45
45
|
showExpr?: string;
|
|
46
|
+
hideWhenChatEnabled?: boolean;
|
|
46
47
|
password?: boolean;
|
|
47
48
|
order?: string[];
|
|
48
49
|
nullable?: boolean;
|
|
@@ -53,6 +54,7 @@ export interface SchemaProperty {
|
|
|
53
54
|
originalType?: string;
|
|
54
55
|
disabled?: boolean;
|
|
55
56
|
'x-no-s3-storage-workspace-warning'?: string;
|
|
57
|
+
'x-auto-generate'?: boolean;
|
|
56
58
|
}
|
|
57
59
|
export interface ModalSchemaProperty {
|
|
58
60
|
selectedType?: string;
|
|
@@ -32,8 +32,9 @@ import { getJsonSchemaFromResource } from './schema/jsonSchemaResource.svelte';
|
|
|
32
32
|
import AIProviderPicker from './AIProviderPicker.svelte';
|
|
33
33
|
import TextInput from './text_input/TextInput.svelte';
|
|
34
34
|
import FileInput from './common/fileInput/FileInput.svelte';
|
|
35
|
+
import { randomUUID } from './flows/conversations/FlowChatManager.svelte';
|
|
35
36
|
let { label = '', value = $bindable(), defaultValue = $bindable(undefined), description = $bindable(undefined), format = $bindable(undefined), contentEncoding = undefined, type = undefined, oneOf = $bindable(undefined), required = false, pattern = $bindable(undefined), valid = $bindable(undefined), // Note : this should not exist, valid and error should be one coherent state
|
|
36
|
-
enum_ = $bindable(undefined), disabled = false, itemsType = $bindable(undefined), displayHeader = true, properties = $bindable(undefined), nestedRequired = undefined, autofocus = null, compact = false, password = false, pickForField = $bindable(undefined), variableEditor = undefined, itemPicker = undefined, noMargin = false, extra = {}, minW = true, prettifyHeader = false, resourceTypes, disablePortal = false, showSchemaExplorer = false, simpleTooltip = undefined, customErrorMessage = undefined, onlyMaskPassword = false, nullable = false, title = $bindable(undefined), placeholder = $bindable(undefined), order = $bindable(undefined), editor = $bindable(undefined), orderEditable = false, shouldDispatchChanges = false, noDefaultOnSelectFirst = false, helperScript = undefined, otherArgs = {}, lightHeader = false, diffStatus = undefined, hideNested = false, nestedParent = undefined, nestedClasses = '', displayType = true, css = undefined, appPath = undefined, computeS3ForceViewerPolicies = undefined, workspace = undefined, s3StorageConfigured = true, actions, innerBottomSnippet, fieldHeaderActions, lightHeaderFont = false } = $props();
|
|
37
|
+
enum_ = $bindable(undefined), disabled = false, itemsType = $bindable(undefined), displayHeader = true, properties = $bindable(undefined), nestedRequired = undefined, autofocus = null, compact = false, password = false, pickForField = $bindable(undefined), variableEditor = undefined, itemPicker = undefined, noMargin = false, extra = {}, minW = true, prettifyHeader = false, resourceTypes, disablePortal = false, showSchemaExplorer = false, simpleTooltip = undefined, customErrorMessage = undefined, onlyMaskPassword = false, nullable = false, title = $bindable(undefined), placeholder = $bindable(undefined), order = $bindable(undefined), editor = $bindable(undefined), orderEditable = false, shouldDispatchChanges = false, noDefaultOnSelectFirst = false, helperScript = undefined, otherArgs = {}, lightHeader = false, diffStatus = undefined, hideNested = false, nestedParent = undefined, nestedClasses = '', displayType = true, css = undefined, appPath = undefined, computeS3ForceViewerPolicies = undefined, workspace = undefined, s3StorageConfigured = true, chatInputEnabled = false, actions, innerBottomSnippet, fieldHeaderActions, lightHeaderFont = false } = $props();
|
|
37
38
|
$effect(() => {
|
|
38
39
|
if (description == undefined) {
|
|
39
40
|
description = '';
|
|
@@ -91,7 +92,7 @@ function computeDefaultValue(inputCat, defaultValue, nnullable) {
|
|
|
91
92
|
nvalue = structuredClone($state.snapshot(defaultValue));
|
|
92
93
|
if (defaultValue === undefined || defaultValue === null) {
|
|
93
94
|
if (inputCat === 'string') {
|
|
94
|
-
nvalue = nullable ? null : '';
|
|
95
|
+
nvalue = nullable ? null : format === 'uuid' && extra?.['x-auto-generate'] ? randomUUID() : '';
|
|
95
96
|
}
|
|
96
97
|
else if (inputCat == 'enum' && required) {
|
|
97
98
|
let firstV = enum_?.[0];
|
|
@@ -965,6 +966,7 @@ onDestroy(() => {
|
|
|
965
966
|
{disablePortal}
|
|
966
967
|
{disabled}
|
|
967
968
|
{prettifyHeader}
|
|
969
|
+
{chatInputEnabled}
|
|
968
970
|
hiddenArgs={['label', 'kind']}
|
|
969
971
|
schema={{
|
|
970
972
|
properties: obj.properties,
|
|
@@ -80,6 +80,7 @@ interface Props {
|
|
|
80
80
|
} | undefined) | undefined;
|
|
81
81
|
workspace?: string | undefined;
|
|
82
82
|
s3StorageConfigured?: boolean;
|
|
83
|
+
chatInputEnabled?: boolean;
|
|
83
84
|
actions?: import('svelte').Snippet;
|
|
84
85
|
innerBottomSnippet?: import('svelte').Snippet;
|
|
85
86
|
fieldHeaderActions?: import('svelte').Snippet;
|
|
@@ -151,13 +151,14 @@ let selected = $state(itemsType?.type != 'string'
|
|
|
151
151
|
() => {
|
|
152
152
|
return itemsType?.properties != undefined
|
|
153
153
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
154
|
+
(v) => {
|
|
155
|
+
tick().then(() => {
|
|
156
|
+
if (v) {
|
|
157
|
+
itemsType = { type: 'object', properties: {} }
|
|
158
|
+
} else {
|
|
159
|
+
itemsType = { type: 'object', properties: undefined }
|
|
160
|
+
}
|
|
161
|
+
})
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
options={{ left: 'JSON', right: 'Custom Object' }}
|
|
@@ -12,7 +12,68 @@ import DbTableEditor from './DBTableEditor.svelte';
|
|
|
12
12
|
import Portal from './Portal.svelte';
|
|
13
13
|
import Select from './select/Select.svelte';
|
|
14
14
|
import { safeSelectItems } from './select/utils.svelte';
|
|
15
|
-
let { dbType, dbSchema, dbTableOpsFactory, dbSchemaOps, getColDefs, dbSupportsSchemas, refresh, initialSchemaKey, initialTableKey } = $props();
|
|
15
|
+
let { dbType, dbSchema, dbTableOpsFactory, dbSchemaOps, getColDefs, dbSupportsSchemas, refresh, initialSchemaKey, initialTableKey, selectedSchemaKey = $bindable(undefined), selectedTableKey = $bindable(undefined), dbSelector, multiSelectMode = false, selectedTables = $bindable([]), disabledTables = [] } = $props();
|
|
16
|
+
// Helper to check if a table is selected in multi-select mode
|
|
17
|
+
function isTableSelected(schema, table) {
|
|
18
|
+
return selectedTables.some((t) => t.schema === schema && t.table === table);
|
|
19
|
+
}
|
|
20
|
+
// Helper to check if a table is disabled (already added)
|
|
21
|
+
function isTableDisabled(schema, table) {
|
|
22
|
+
return disabledTables.some((t) => t.schema === schema && t.table === table);
|
|
23
|
+
}
|
|
24
|
+
// Toggle table selection in multi-select mode
|
|
25
|
+
function toggleTableSelection(schema, table) {
|
|
26
|
+
if (isTableDisabled(schema, table))
|
|
27
|
+
return;
|
|
28
|
+
const idx = selectedTables.findIndex((t) => t.schema === schema && t.table === table);
|
|
29
|
+
if (idx >= 0) {
|
|
30
|
+
selectedTables = selectedTables.filter((_, i) => i !== idx);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
selectedTables = [...selectedTables, { schema, table }];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Get tables for a schema (filtered by search)
|
|
37
|
+
function getTablesForSchema(schema) {
|
|
38
|
+
const tables = Object.keys(dbSchema.schema[schema] ?? {});
|
|
39
|
+
if (search) {
|
|
40
|
+
return tables.filter((t) => t.toLowerCase().includes(search.toLowerCase())).sort();
|
|
41
|
+
}
|
|
42
|
+
return tables.sort();
|
|
43
|
+
}
|
|
44
|
+
// Check if all selectable tables in a schema are selected
|
|
45
|
+
function isSchemaFullySelected(schema) {
|
|
46
|
+
const tables = getTablesForSchema(schema);
|
|
47
|
+
if (tables.length === 0)
|
|
48
|
+
return false;
|
|
49
|
+
const selectableTables = tables.filter((t) => !isTableDisabled(schema, t));
|
|
50
|
+
if (selectableTables.length === 0)
|
|
51
|
+
return true; // All disabled means "fully selected"
|
|
52
|
+
return selectableTables.every((t) => isTableSelected(schema, t));
|
|
53
|
+
}
|
|
54
|
+
// Check if some (but not all) tables in a schema are selected
|
|
55
|
+
function isSchemaPartiallySelected(schema) {
|
|
56
|
+
const tables = getTablesForSchema(schema);
|
|
57
|
+
const selectableTables = tables.filter((t) => !isTableDisabled(schema, t));
|
|
58
|
+
const selectedCount = selectableTables.filter((t) => isTableSelected(schema, t)).length;
|
|
59
|
+
return selectedCount > 0 && selectedCount < selectableTables.length;
|
|
60
|
+
}
|
|
61
|
+
// Toggle all tables in a schema
|
|
62
|
+
function toggleSchemaSelection(schema) {
|
|
63
|
+
const tables = getTablesForSchema(schema);
|
|
64
|
+
const selectableTables = tables.filter((t) => !isTableDisabled(schema, t));
|
|
65
|
+
if (isSchemaFullySelected(schema)) {
|
|
66
|
+
// Deselect all selectable tables in this schema
|
|
67
|
+
selectedTables = selectedTables.filter((t) => t.schema !== schema);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Select all selectable tables in this schema
|
|
71
|
+
const newSelections = selectableTables
|
|
72
|
+
.filter((t) => !isTableSelected(schema, t))
|
|
73
|
+
.map((t) => ({ schema, table: t }));
|
|
74
|
+
selectedTables = [...selectedTables, ...newSelections];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
16
77
|
let schemaKeys = $derived(Object.keys(dbSchema.schema ?? {}));
|
|
17
78
|
let search = $state('');
|
|
18
79
|
let selected = $state({});
|
|
@@ -26,6 +87,15 @@ $effect(() => {
|
|
|
26
87
|
selected = { schemaKey, tableKey };
|
|
27
88
|
}
|
|
28
89
|
});
|
|
90
|
+
// Sync selected state with bindable props
|
|
91
|
+
$effect(() => {
|
|
92
|
+
if (selected.schemaKey) {
|
|
93
|
+
selectedSchemaKey = selected.schemaKey;
|
|
94
|
+
}
|
|
95
|
+
if (selected.tableKey) {
|
|
96
|
+
selectedTableKey = selected.tableKey;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
29
99
|
let tableKeys = $derived.by(() => {
|
|
30
100
|
if (dbSchema.lang === 'graphql') {
|
|
31
101
|
sendUserToast('graphql not supported by DBExplorerTable', true);
|
|
@@ -50,12 +120,23 @@ let tableKey = $derived(dbSupportsSchemas && selected.schemaKey
|
|
|
50
120
|
: selected.tableKey);
|
|
51
121
|
let askingForConfirmation = $state();
|
|
52
122
|
let dbTableEditorState = $state({ open: false });
|
|
123
|
+
let newSchemaDialogOpen = $state(false);
|
|
124
|
+
let newSchemaName = $state('');
|
|
125
|
+
// Check if the sanitized schema name already exists
|
|
126
|
+
const sanitizedNewSchemaName = $derived(newSchemaName
|
|
127
|
+
.trim()
|
|
128
|
+
.toLowerCase()
|
|
129
|
+
.replace(/[^a-zA-Z0-9_]/g, ''));
|
|
130
|
+
const schemaAlreadyExists = $derived(sanitizedNewSchemaName !== '' && schemaKeys.includes(sanitizedNewSchemaName));
|
|
53
131
|
</script>
|
|
54
132
|
|
|
55
133
|
<Splitpanes>
|
|
56
134
|
<Pane size={24} class="relative flex flex-col">
|
|
57
135
|
<div class="mx-3 mt-3 flex flex-col gap-2">
|
|
58
|
-
{#if
|
|
136
|
+
{#if dbSelector}
|
|
137
|
+
{@render dbSelector()}
|
|
138
|
+
{/if}
|
|
139
|
+
{#if dbSupportsSchemas && !multiSelectMode}
|
|
59
140
|
<Select
|
|
60
141
|
bind:value={selected.schemaKey}
|
|
61
142
|
items={safeSelectItems(schemaKeys)}
|
|
@@ -90,28 +171,139 @@ let dbTableEditorState = $state({ open: false });
|
|
|
90
171
|
<ClearableInput bind:value={search} placeholder="Search table..." />
|
|
91
172
|
</div>
|
|
92
173
|
<div class="overflow-x-clip overflow-y-auto relative mt-3 border-y flex-1">
|
|
93
|
-
{#
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
174
|
+
{#if multiSelectMode}
|
|
175
|
+
<!-- Multi-select mode: show all schemas with their tables -->
|
|
176
|
+
{#if dbSupportsSchemas}
|
|
177
|
+
<!-- New schema button -->
|
|
178
|
+
<button
|
|
179
|
+
class="w-full text-sm font-medium flex gap-2 items-center h-9 cursor-pointer pl-3 pr-1 hover:bg-gray-500/10 border-b border-surface-secondary text-tertiary"
|
|
180
|
+
onclick={() => (newSchemaDialogOpen = true)}
|
|
181
|
+
>
|
|
182
|
+
<Plus class="shrink-0" size={14} />
|
|
183
|
+
<span class="text-xs">New schema</span>
|
|
184
|
+
</button>
|
|
185
|
+
{/if}
|
|
186
|
+
{#each schemaKeys as schemaKey}
|
|
187
|
+
{@const schemaTables = getTablesForSchema(schemaKey)}
|
|
188
|
+
{@const isFullySelected = isSchemaFullySelected(schemaKey)}
|
|
189
|
+
{@const isPartiallySelected = isSchemaPartiallySelected(schemaKey)}
|
|
190
|
+
{@const hasNoTables = schemaTables.length === 0}
|
|
191
|
+
<!-- Schema header with checkbox (or just label if empty) -->
|
|
192
|
+
<div
|
|
193
|
+
class="group w-full text-sm font-medium flex gap-2 items-center h-9 cursor-pointer pl-3 pr-1 hover:bg-gray-500/10 border-b border-surface-secondary"
|
|
194
|
+
role="button"
|
|
195
|
+
tabindex="0"
|
|
196
|
+
onclick={() => {
|
|
197
|
+
if (!hasNoTables) {
|
|
198
|
+
toggleSchemaSelection(schemaKey)
|
|
199
|
+
}
|
|
200
|
+
}}
|
|
201
|
+
onkeydown={(e) => {
|
|
202
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
203
|
+
if (!hasNoTables) {
|
|
204
|
+
toggleSchemaSelection(schemaKey)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
{#if hasNoTables}
|
|
210
|
+
<!-- Empty schema: no checkbox, just indent space -->
|
|
211
|
+
<span class="shrink-0 w-4"></span>
|
|
212
|
+
{:else}
|
|
213
|
+
<span class="shrink-0">
|
|
214
|
+
<input
|
|
215
|
+
type="checkbox"
|
|
216
|
+
checked={isFullySelected}
|
|
217
|
+
indeterminate={isPartiallySelected}
|
|
218
|
+
class="w-4 h-4 cursor-pointer"
|
|
219
|
+
onclick={(e) => e.stopPropagation()}
|
|
220
|
+
onchange={() => toggleSchemaSelection(schemaKey)}
|
|
221
|
+
/>
|
|
222
|
+
</span>
|
|
223
|
+
{/if}
|
|
224
|
+
<span class="truncate text-ellipsis grow text-left text-tertiary text-xs"
|
|
225
|
+
>{schemaKey}</span
|
|
226
|
+
>
|
|
227
|
+
<span class="text-2xs text-tertiary mr-2 group-hover:hidden">{schemaTables.length}</span>
|
|
228
|
+
<!-- Delete schema button (on hover) -->
|
|
229
|
+
<button
|
|
230
|
+
class="hidden group-hover:flex p-1 hover:bg-red-100 dark:hover:bg-red-900/30 rounded transition-colors mr-1"
|
|
231
|
+
title="Delete schema"
|
|
232
|
+
onclick={(e) => {
|
|
233
|
+
e.stopPropagation()
|
|
234
|
+
askingForConfirmation = {
|
|
235
|
+
title: `Are you sure you want to delete schema "${schemaKey}"? This will drop all tables in this schema. This action is irreversible.`,
|
|
236
|
+
confirmationText: 'Drop schema',
|
|
237
|
+
open: true,
|
|
238
|
+
onConfirm: async () => {
|
|
239
|
+
askingForConfirmation && (askingForConfirmation.loading = true)
|
|
240
|
+
try {
|
|
241
|
+
await dbSchemaOps.onDeleteSchema({ schema: schemaKey })
|
|
242
|
+
refresh?.()
|
|
243
|
+
sendUserToast(`Schema '${schemaKey}' deleted successfully`)
|
|
244
|
+
} catch (e) {
|
|
245
|
+
let msg: string | undefined = (e as Error).message
|
|
246
|
+
if (typeof msg !== 'string') msg = e ? JSON.stringify(e) : undefined
|
|
247
|
+
sendUserToast(msg ?? 'Action failed!', true)
|
|
248
|
+
}
|
|
249
|
+
askingForConfirmation = undefined
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
<Trash2Icon size={12} class="text-red-500" />
|
|
255
|
+
</button>
|
|
256
|
+
</div>
|
|
257
|
+
<!-- Tables under this schema -->
|
|
258
|
+
{#each schemaTables as tableKey}
|
|
259
|
+
{@const isDisabled = isTableDisabled(schemaKey, tableKey)}
|
|
260
|
+
{@const isChecked = isTableSelected(schemaKey, tableKey) || isDisabled}
|
|
261
|
+
{@const isCurrentPreview = selected.schemaKey === schemaKey && selected.tableKey === tableKey}
|
|
262
|
+
<div
|
|
263
|
+
class={'group w-full text-sm font-normal flex gap-2 items-center h-8 cursor-pointer pl-7 pr-1 ' +
|
|
264
|
+
(isCurrentPreview ? 'bg-gray-500/25' : 'hover:bg-gray-500/10') +
|
|
265
|
+
(isDisabled ? ' opacity-50' : '')}
|
|
266
|
+
role="button"
|
|
267
|
+
tabindex="0"
|
|
268
|
+
onclick={() => {
|
|
269
|
+
selected.schemaKey = schemaKey
|
|
270
|
+
selected.tableKey = tableKey
|
|
271
|
+
toggleTableSelection(schemaKey, tableKey)
|
|
272
|
+
}}
|
|
273
|
+
onkeydown={(e) => {
|
|
274
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
275
|
+
selected.schemaKey = schemaKey
|
|
276
|
+
selected.tableKey = tableKey
|
|
277
|
+
toggleTableSelection(schemaKey, tableKey)
|
|
278
|
+
}
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
<span class="shrink-0">
|
|
282
|
+
<input
|
|
283
|
+
type="checkbox"
|
|
284
|
+
checked={isChecked}
|
|
285
|
+
disabled={isDisabled}
|
|
286
|
+
class="w-4 h-4 cursor-pointer"
|
|
287
|
+
onclick={(e) => e.stopPropagation()}
|
|
288
|
+
onchange={() => toggleTableSelection(schemaKey, tableKey)}
|
|
289
|
+
/>
|
|
290
|
+
</span>
|
|
291
|
+
<Table2 class="text-primary shrink-0" size={14} />
|
|
292
|
+
<p class="truncate text-ellipsis grow text-left text-emphasis text-xs">{tableKey}</p>
|
|
293
|
+
<!-- Delete table button (on hover) -->
|
|
294
|
+
<button
|
|
295
|
+
class="hidden group-hover:flex p-1 hover:bg-red-100 dark:hover:bg-red-900/30 rounded transition-colors mr-1"
|
|
296
|
+
title="Delete table"
|
|
297
|
+
onclick={(e) => {
|
|
298
|
+
e.stopPropagation()
|
|
299
|
+
askingForConfirmation = {
|
|
300
|
+
title: `Are you sure you want to delete table "${tableKey}"? This action is irreversible.`,
|
|
301
|
+
confirmationText: 'Drop table',
|
|
110
302
|
open: true,
|
|
111
303
|
onConfirm: async () => {
|
|
112
304
|
askingForConfirmation && (askingForConfirmation.loading = true)
|
|
113
305
|
try {
|
|
114
|
-
await dbSchemaOps.onDelete({ tableKey, schema:
|
|
306
|
+
await dbSchemaOps.onDelete({ tableKey, schema: schemaKey })
|
|
115
307
|
refresh?.()
|
|
116
308
|
sendUserToast(`Table '${tableKey}' deleted successfully`)
|
|
117
309
|
} catch (e) {
|
|
@@ -121,29 +313,84 @@ let dbTableEditorState = $state({ open: false });
|
|
|
121
313
|
}
|
|
122
314
|
askingForConfirmation = undefined
|
|
123
315
|
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
316
|
+
}
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
<Trash2Icon size={12} class="text-red-500" />
|
|
320
|
+
</button>
|
|
321
|
+
</div>
|
|
322
|
+
{/each}
|
|
323
|
+
<!-- New table button for this schema -->
|
|
324
|
+
<button
|
|
325
|
+
class="w-full text-sm font-normal flex gap-2 items-center h-8 cursor-pointer pl-7 pr-1 hover:bg-gray-500/10 text-tertiary"
|
|
326
|
+
onclick={() => {
|
|
327
|
+
selected.schemaKey = schemaKey
|
|
328
|
+
dbTableEditorState = { open: true }
|
|
329
|
+
}}
|
|
128
330
|
>
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
331
|
+
<Plus class="shrink-0" size={14} />
|
|
332
|
+
<span class="text-xs">New table</span>
|
|
333
|
+
</button>
|
|
334
|
+
{/each}
|
|
335
|
+
{:else}
|
|
336
|
+
<!-- Normal mode: show tables for selected schema -->
|
|
337
|
+
{#each filteredTableKeys as tableKey}
|
|
338
|
+
<button
|
|
339
|
+
class={'w-full text-sm font-normal flex gap-2 items-center h-10 cursor-pointer pl-3 pr-1 ' +
|
|
340
|
+
(selected.tableKey === tableKey ? 'bg-gray-500/25' : 'hover:bg-gray-500/10')}
|
|
341
|
+
onclick={() => (selected.tableKey = tableKey)}
|
|
342
|
+
>
|
|
343
|
+
<Table2 class="text-primary shrink-0" size={16} />
|
|
344
|
+
<p class="truncate text-ellipsis grow text-left text-emphasis text-xs">{tableKey}</p>
|
|
345
|
+
<DropdownV2
|
|
346
|
+
items={() => [
|
|
347
|
+
{
|
|
348
|
+
displayName: 'Delete table',
|
|
349
|
+
icon: Trash2Icon,
|
|
350
|
+
action: () =>
|
|
351
|
+
(askingForConfirmation = {
|
|
352
|
+
title: `Are you sure you want to delete ${tableKey} ? This action is irreversible`,
|
|
353
|
+
confirmationText: 'Delete permanently',
|
|
354
|
+
open: true,
|
|
355
|
+
onConfirm: async () => {
|
|
356
|
+
askingForConfirmation && (askingForConfirmation.loading = true)
|
|
357
|
+
try {
|
|
358
|
+
await dbSchemaOps.onDelete({ tableKey, schema: selected.schemaKey })
|
|
359
|
+
refresh?.()
|
|
360
|
+
sendUserToast(`Table '${tableKey}' deleted successfully`)
|
|
361
|
+
} catch (e) {
|
|
362
|
+
let msg: string | undefined = (e as Error).message
|
|
363
|
+
if (typeof msg !== 'string') msg = e ? JSON.stringify(e) : undefined
|
|
364
|
+
sendUserToast(msg ?? 'Action failed!', true)
|
|
365
|
+
}
|
|
366
|
+
askingForConfirmation = undefined
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
]}
|
|
371
|
+
class="w-fit"
|
|
372
|
+
>
|
|
373
|
+
<svelte:fragment slot="buttonReplacement">
|
|
374
|
+
<MoreVertical
|
|
375
|
+
size={8}
|
|
376
|
+
class="w-8 h-8 p-2 hover:bg-surface-hover cursor-pointer rounded-md"
|
|
377
|
+
/>
|
|
378
|
+
</svelte:fragment>
|
|
379
|
+
</DropdownV2>
|
|
380
|
+
</button>
|
|
381
|
+
{/each}
|
|
382
|
+
{/if}
|
|
138
383
|
</div>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
384
|
+
{#if !multiSelectMode}
|
|
385
|
+
<Button
|
|
386
|
+
on:click={() => (dbTableEditorState = { open: true })}
|
|
387
|
+
wrapperClasses="mx-2 my-2 text-sm"
|
|
388
|
+
startIcon={{ icon: Plus }}
|
|
389
|
+
variant={tableKeys.length === 0 ? 'accent' : 'default'}
|
|
390
|
+
>
|
|
391
|
+
New table
|
|
392
|
+
</Button>
|
|
393
|
+
{/if}
|
|
147
394
|
</Pane>
|
|
148
395
|
<Pane class="p-3 pt-1">
|
|
149
396
|
{#if tableKey}
|
|
@@ -184,3 +431,92 @@ let dbTableEditorState = $state({ open: false });
|
|
|
184
431
|
/>
|
|
185
432
|
</DrawerContent>
|
|
186
433
|
</Drawer>
|
|
434
|
+
|
|
435
|
+
<Drawer
|
|
436
|
+
size="400px"
|
|
437
|
+
open={newSchemaDialogOpen}
|
|
438
|
+
on:close={() => {
|
|
439
|
+
newSchemaDialogOpen = false
|
|
440
|
+
newSchemaName = ''
|
|
441
|
+
}}
|
|
442
|
+
>
|
|
443
|
+
<DrawerContent
|
|
444
|
+
on:close={() => {
|
|
445
|
+
newSchemaDialogOpen = false
|
|
446
|
+
newSchemaName = ''
|
|
447
|
+
}}
|
|
448
|
+
title="Create a new schema"
|
|
449
|
+
>
|
|
450
|
+
<div class="flex flex-col gap-4">
|
|
451
|
+
<div>
|
|
452
|
+
<label for="schema-name" class="block text-sm font-medium text-primary mb-1"
|
|
453
|
+
>Schema name</label
|
|
454
|
+
>
|
|
455
|
+
<ClearableInput
|
|
456
|
+
bind:value={newSchemaName}
|
|
457
|
+
placeholder="Enter schema name..."
|
|
458
|
+
autofocus
|
|
459
|
+
on:keydown={(e) => {
|
|
460
|
+
if (e.key === 'Enter' && sanitizedNewSchemaName && !schemaAlreadyExists) {
|
|
461
|
+
askingForConfirmation = {
|
|
462
|
+
confirmationText: `Create ${sanitizedNewSchemaName}`,
|
|
463
|
+
type: 'reload',
|
|
464
|
+
title: `This will run 'CREATE SCHEMA ${sanitizedNewSchemaName}' on your database. Are you sure?`,
|
|
465
|
+
open: true,
|
|
466
|
+
onConfirm: async () => {
|
|
467
|
+
askingForConfirmation && (askingForConfirmation.loading = true)
|
|
468
|
+
try {
|
|
469
|
+
await dbSchemaOps.onCreateSchema({ schema: sanitizedNewSchemaName })
|
|
470
|
+
refresh?.()
|
|
471
|
+
selected.schemaKey = sanitizedNewSchemaName
|
|
472
|
+
newSchemaDialogOpen = false
|
|
473
|
+
newSchemaName = ''
|
|
474
|
+
} finally {
|
|
475
|
+
askingForConfirmation = undefined
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}}
|
|
481
|
+
/>
|
|
482
|
+
{#if schemaAlreadyExists}
|
|
483
|
+
<p class="text-xs text-red-500 mt-1">
|
|
484
|
+
Schema "{sanitizedNewSchemaName}" already exists
|
|
485
|
+
</p>
|
|
486
|
+
{:else}
|
|
487
|
+
<p class="text-xs text-tertiary mt-1">
|
|
488
|
+
Only letters, numbers, and underscores are allowed.
|
|
489
|
+
</p>
|
|
490
|
+
{/if}
|
|
491
|
+
</div>
|
|
492
|
+
</div>
|
|
493
|
+
{#snippet actions()}
|
|
494
|
+
<Button
|
|
495
|
+
color="blue"
|
|
496
|
+
disabled={!sanitizedNewSchemaName || schemaAlreadyExists}
|
|
497
|
+
on:click={() => {
|
|
498
|
+
askingForConfirmation = {
|
|
499
|
+
confirmationText: `Create ${sanitizedNewSchemaName}`,
|
|
500
|
+
type: 'reload',
|
|
501
|
+
title: `This will run 'CREATE SCHEMA ${sanitizedNewSchemaName}' on your database. Are you sure?`,
|
|
502
|
+
open: true,
|
|
503
|
+
onConfirm: async () => {
|
|
504
|
+
askingForConfirmation && (askingForConfirmation.loading = true)
|
|
505
|
+
try {
|
|
506
|
+
await dbSchemaOps.onCreateSchema({ schema: sanitizedNewSchemaName })
|
|
507
|
+
refresh?.()
|
|
508
|
+
selected.schemaKey = sanitizedNewSchemaName
|
|
509
|
+
newSchemaDialogOpen = false
|
|
510
|
+
newSchemaName = ''
|
|
511
|
+
} finally {
|
|
512
|
+
askingForConfirmation = undefined
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}}
|
|
517
|
+
>
|
|
518
|
+
Create schema
|
|
519
|
+
</Button>
|
|
520
|
+
{/snippet}
|
|
521
|
+
</DrawerContent>
|
|
522
|
+
</Drawer>
|
|
@@ -2,6 +2,12 @@ import { type DBSchema } from '../stores';
|
|
|
2
2
|
import { type ColumnDef } from './apps/components/display/dbtable/utils';
|
|
3
3
|
import type { IDbSchemaOps, IDbTableOps } from './dbOps';
|
|
4
4
|
import type { DbType } from './dbTypes';
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
/** Represents a selected table with its schema */
|
|
7
|
+
export interface SelectedTable {
|
|
8
|
+
schema: string;
|
|
9
|
+
table: string;
|
|
10
|
+
}
|
|
5
11
|
type Props = {
|
|
6
12
|
dbType: DbType;
|
|
7
13
|
dbSchema: DBSchema;
|
|
@@ -15,7 +21,16 @@ type Props = {
|
|
|
15
21
|
refresh?: () => void;
|
|
16
22
|
initialSchemaKey?: string;
|
|
17
23
|
initialTableKey?: string;
|
|
24
|
+
selectedSchemaKey?: string | undefined;
|
|
25
|
+
selectedTableKey?: string | undefined;
|
|
26
|
+
dbSelector?: Snippet<[]>;
|
|
27
|
+
/** Enable multi-select mode with checkboxes in sidebar */
|
|
28
|
+
multiSelectMode?: boolean;
|
|
29
|
+
/** Selected tables in multi-select mode */
|
|
30
|
+
selectedTables?: SelectedTable[];
|
|
31
|
+
/** Tables that are already added and should show as disabled */
|
|
32
|
+
disabledTables?: SelectedTable[];
|
|
18
33
|
};
|
|
19
|
-
declare const DBManager: import("svelte").Component<Props, {}, "">;
|
|
34
|
+
declare const DBManager: import("svelte").Component<Props, {}, "selectedSchemaKey" | "selectedTableKey" | "selectedTables">;
|
|
20
35
|
type DBManager = ReturnType<typeof DBManager>;
|
|
21
36
|
export default DBManager;
|