Merge pull request #62 from agnosticeng/feat/add-editor-actions

This commit is contained in:
Yann Amsellem
2025-01-23 11:04:43 +01:00
committed by GitHub
6 changed files with 320 additions and 2 deletions

103
package-lock.json generated
View File

@@ -25,7 +25,8 @@
"d3": "^7.9.0",
"dayjs": "^1.11.13",
"normalize.css": "^8.0.1",
"p-debounce": "^4.0.0"
"p-debounce": "^4.0.0",
"sql-formatter": "^15.4.9"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.8",
@@ -1567,6 +1568,12 @@
"acorn": ">=8.9.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/aria-query": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
@@ -2080,6 +2087,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/discontinuous-range": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
"integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==",
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
@@ -2166,6 +2179,18 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-stdin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
"integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/globalyzer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
@@ -2246,6 +2271,12 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
"license": "BSD-3-Clause"
},
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -2292,6 +2323,34 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/nearley": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
"integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
"license": "MIT",
"dependencies": {
"commander": "^2.19.0",
"moo": "^0.5.0",
"railroad-diagrams": "^1.0.0",
"randexp": "0.4.6"
},
"bin": {
"nearley-railroad": "bin/nearley-railroad.js",
"nearley-test": "bin/nearley-test.js",
"nearley-unparse": "bin/nearley-unparse.js",
"nearleyc": "bin/nearleyc.js"
},
"funding": {
"type": "individual",
"url": "https://nearley.js.org/#give-to-nearley"
}
},
"node_modules/nearley/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/normalize.css": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
@@ -2370,6 +2429,25 @@
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
}
},
"node_modules/railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
"integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==",
"license": "CC0-1.0"
},
"node_modules/randexp": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
"license": "MIT",
"dependencies": {
"discontinuous-range": "1.0.0",
"ret": "~0.1.10"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/readdirp": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
@@ -2384,6 +2462,15 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"license": "MIT",
"engines": {
"node": ">=0.12"
}
},
"node_modules/robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
@@ -2485,6 +2572,20 @@
"node": ">=0.10.0"
}
},
"node_modules/sql-formatter": {
"version": "15.4.9",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.4.9.tgz",
"integrity": "sha512-5vmt2HlCAVozxsBZuXWkAki/KGawaK+b5GG5x+BtXOFVpN/8cqppblFUxHl4jxdA0cvo14lABhM+KBnrUapOlw==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"get-stdin": "=8.0.0",
"nearley": "^2.20.1"
},
"bin": {
"sql-formatter": "bin/sql-formatter-cli.cjs"
}
},
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",

View File

@@ -31,7 +31,8 @@
"d3": "^7.9.0",
"dayjs": "^1.11.13",
"normalize.css": "^8.0.1",
"p-debounce": "^4.0.0"
"p-debounce": "^4.0.0",
"sql-formatter": "^15.4.9"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.8",

View File

@@ -0,0 +1,50 @@
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
interface Props extends Omit<SvelteHTMLElements['svg'], 'width' | 'height'> {
size?: string | number | null;
}
let { size = 24, ...rest }: Props = $props();
</script>
<svg viewBox="0 0 256 256" width={size} height={size} {...rest} data-name="clipboard-text">
<line
x1="96"
y1="152"
x2="160"
y2="152"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="96"
y1="120"
x2="160"
y2="120"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<path
d="M160,40h40a8,8,0,0,1,8,8V216a8,8,0,0,1-8,8H56a8,8,0,0,1-8-8V48a8,8,0,0,1,8-8H96"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<path
d="M88,72V64a40,40,0,0,1,80,0v8Z"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
</svg>

31
src/lib/icons/Copy.svelte Normal file
View File

@@ -0,0 +1,31 @@
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
interface Props extends Omit<SvelteHTMLElements['svg'], 'width' | 'height'> {
size?: string | number | null;
}
let { size = 24, ...rest }: Props = $props();
</script>
<svg viewBox="0 0 256 256" width={size} height={size} {...rest} data-name="copy">
<polyline
points="168 168 216 168 216 40 88 40 88 88"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<rect
x="40"
y="88"
width="128"
height="128"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
</svg>

View File

@@ -0,0 +1,102 @@
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
interface Props extends Omit<SvelteHTMLElements['svg'], 'width' | 'height'> {
size?: string | number | null;
}
let { size = 24, ...rest }: Props = $props();
</script>
<svg viewBox="0 0 256 256" width={size} height={size} {...rest} data-name="magic-wand">
<line
x1="216"
y1="128"
x2="216"
y2="176"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="192"
y1="152"
x2="240"
y2="152"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="80"
y1="40"
x2="80"
y2="88"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="56"
y1="64"
x2="104"
y2="64"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="168"
y1="184"
x2="168"
y2="216"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="152"
y1="200"
x2="184"
y2="200"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<line
x1="144"
y1="80"
x2="176"
y2="112"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
<rect
x="21.49"
y="105.37"
width="213.02"
height="45.25"
rx="8"
transform="translate(-53.02 128) rotate(-45)"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
/>
</svg>

View File

@@ -11,6 +11,9 @@
import { setAppContext } from '$lib/context';
import { tablesToSQLNamespace } from '$lib/editor';
import Bars3 from '$lib/icons/Bars3.svelte';
import ClipboardText from '$lib/icons/ClipboardText.svelte';
import Copy from '$lib/icons/Copy.svelte';
import MagicWand from '$lib/icons/MagicWand.svelte';
import PanelBottom from '$lib/icons/PanelBottom.svelte';
import PanelLeft from '$lib/icons/PanelLeft.svelte';
import Play from '$lib/icons/Play.svelte';
@@ -26,6 +29,7 @@
import { ClickHouseDialect, extendsDialectKeywords } from '@agnosticeng/editor/dialect';
import { SplitPane } from '@rich_harris/svelte-split-pane';
import debounce from 'p-debounce';
import { format } from 'sql-formatter';
import { tick, type ComponentProps } from 'svelte';
import type { PageData } from './$types';
@@ -98,6 +102,7 @@
}
if (event.key === 'Enter' && event.metaKey) handleExec();
if (event.key === 'i' && event.metaKey) handleFormat();
}
async function handleCreateQuery({
@@ -227,6 +232,18 @@
bottomPanelTab = 'logs';
}
});
function handleFormat() {
if (!currentTab.contents) return;
currentTab.contents = format(currentTab.contents, {
keywordCase: 'lower',
tabWidth: 2,
useTabs: true,
expressionWidth: 80,
language: 'postgresql'
});
}
</script>
<svelte:window onkeydown={handleKeyDown} bind:innerWidth={screenWidth} />
@@ -302,6 +319,22 @@
</button>
</div>
<div class="workspace-actions">
<button
class="action"
onclick={() =>
navigator.clipboard.readText().then((t) => (currentTab.contents = t))}
>
<ClipboardText size="12" />
</button>
<button
class="action"
onclick={() => navigator.clipboard.writeText(currentTab.contents)}
>
<Copy size="12" />
</button>
<button class="action" onclick={handleFormat}>
<MagicWand size="12" />
</button>
<button class="action" onclick={handleSaveQuery} disabled={!canSave}>
<Save size="12" />
</button>