-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge the changes to support the "fiddle" extension.
FossilOrigin-Name: 58585f01aa4747d3a09771fb462066bd037914f435ff04fa16ed9b0571e7912a
- Loading branch information
Showing
9 changed files
with
903 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# This makefile exists primarily to simplify/speed up development from | ||
# emacs. It is not part of the canonical build process. | ||
default: | ||
make -C ../.. fiddle -e emcc_opt=-O0 | ||
|
||
clean: | ||
make -C ../../ clean-fiddle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
<!doctype html> | ||
<html lang="en-us"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | ||
<title>sqlite3 fiddle</title> | ||
<!-- script src="jqterm/jqterm-bundle.min.js"></script> | ||
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/ --> | ||
<style> | ||
/* emcscript-related styling, used during the intialization phase... */ | ||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } | ||
div.emscripten { text-align: center; } | ||
div.emscripten_border { border: 1px solid black; } | ||
#spinner { overflow: visible; } | ||
#spinner > * { | ||
margin-top: 1em; | ||
} | ||
.spinner { | ||
height: 50px; | ||
width: 50px; | ||
margin: 0px auto; | ||
animation: rotation 0.8s linear infinite; | ||
border-left: 10px solid rgb(0,150,240); | ||
border-right: 10px solid rgb(0,150,240); | ||
border-bottom: 10px solid rgb(0,150,240); | ||
border-top: 10px solid rgb(100,0,200); | ||
border-radius: 100%; | ||
background-color: rgb(200,100,250); | ||
} | ||
@keyframes rotation { | ||
from {transform: rotate(0deg);} | ||
to {transform: rotate(360deg);} | ||
} | ||
|
||
/* The following styles are for app-level use. */ | ||
|
||
textarea { | ||
font-family: monospace; | ||
flex: 1 1 auto; | ||
} | ||
header { | ||
font-size: 130%; | ||
font-weight: bold; | ||
} | ||
#main-wrapper { | ||
display: flex; | ||
flex-direction: column-reverse; | ||
flex: 20 1 auto; | ||
} | ||
#main-wrapper.side-by-side { | ||
flex-direction: row-reverse; | ||
} | ||
#main-wrapper.swapio { | ||
flex-direction: column; | ||
} | ||
#main-wrapper.side-by-side.swapio { | ||
flex-direction: row; | ||
} | ||
.ta-wrapper{ | ||
display: flex; | ||
flex-direction: column; | ||
align-items: stretch; | ||
margin: 0 0.25em; | ||
flex: 1 1 auto; | ||
} | ||
.ta-wrapper.input { flex: 10 1 auto; } | ||
.ta-wrapper.output { flex: 20 1 auto; } | ||
.ta-wrapper textarea { | ||
font-size: 110%; | ||
filter: invert(100%); | ||
flex: 10 1 auto; | ||
} | ||
/*#main-wrapper:not(.side-by-side) .ta-wrapper.input { | ||
flex: 5 1 auto; | ||
}*/ | ||
.button-bar { | ||
display: flex; | ||
justify-content: center; | ||
flex: 0 1 auto; | ||
} | ||
.button-bar button { | ||
margin: 0.25em 1em; | ||
} | ||
label[for] { | ||
cursor: pointer; | ||
} | ||
fieldset { | ||
border-radius: 0.5em; | ||
} | ||
.error { | ||
color: red; | ||
background-color: yellow; | ||
} | ||
.hidden { | ||
position: absolute !important; | ||
opacity: 0 !important; | ||
pointer-events: none !important; | ||
display: none !important; | ||
} | ||
.initially-hidden { | ||
position: absolute !important; | ||
opacity: 0 !important; | ||
pointer-events: none !important; | ||
display: none !important; | ||
} | ||
fieldset.options { | ||
font-size: 75%; | ||
} | ||
fieldset > legend { | ||
padding: 0 0.5em; | ||
} | ||
span.labeled-input { | ||
padding: 0.25em; | ||
margin: 0.25em 0.5em; | ||
border-radius: 0.25em; | ||
white-space: nowrap; | ||
background: #0002; | ||
} | ||
#notes-caveats { | ||
border-top: 1px dotted; | ||
padding-top: 0.25em; | ||
margin-top: 0.5em; | ||
} | ||
.center { text-align: center; } | ||
|
||
body.terminal-mode { | ||
max-height: calc(100% - 2em); | ||
display: flex; | ||
flex-direction: column; | ||
align-items: stretch; | ||
} | ||
#jqterminal { | ||
} | ||
.app-view { | ||
flex: 20 1 auto; | ||
} | ||
#titlebar { | ||
display: flex; | ||
justify-content: space-between; | ||
margin-bottom: 0.5em; | ||
} | ||
#view-split { | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<header id='titlebar'><span>sqlite3 fiddle</span></header> | ||
<figure id="spinner"> | ||
<div class="spinner"></div> | ||
<div class='center'><strong>Initializing app...</strong></div> | ||
<div class='center'> | ||
On a slow internet connection this may take a moment. If this | ||
message displays for "a long time", intialization may have | ||
failed and the JavaScript console may contain clues as to why. | ||
</div> | ||
</figure> | ||
<div class="emscripten" id="status">Downloading...</div> | ||
<div class="emscripten"> | ||
<progress value="0" max="100" id="progress" hidden='1'></progress> | ||
</div> | ||
|
||
<div id='jqterminal' class='app-view hidden initially-hidden'> | ||
This is a placeholder for a terminal-like view. | ||
</div> | ||
|
||
<div id='view-split' class='app-view initially-hidden'> | ||
<fieldset class='options'> | ||
<legend>Options</legend> | ||
<div class=''> | ||
<span class='labeled-input'> | ||
<input type='checkbox' id='opt-cb-sbs' | ||
data-csstgt='#main-wrapper' | ||
data-cssclass='side-by-side' | ||
data-config='sideBySide'> | ||
<label for='opt-cb-sbs'>Side-by-side</label> | ||
</span> | ||
<span class='labeled-input'> | ||
<input type='checkbox' id='opt-cb-swapio' | ||
data-csstgt='#main-wrapper' | ||
data-cssclass='swapio' | ||
data-config='swapInOut'> | ||
<label for='opt-cb-swapio'>Swap in/out</label> | ||
</span> | ||
<span class='labeled-input'> | ||
<input type='checkbox' id='opt-cb-autoscroll' | ||
data-config='autoScrollOutput'> | ||
<label for='opt-cb-autoscroll'>Auto-scroll output</label> | ||
</span> | ||
<span class='labeled-input'> | ||
<input type='checkbox' id='opt-cb-autoclear' | ||
data-config='autoClearOutput'> | ||
<label for='opt-cb-autoclear'>Auto-clear output</label> | ||
</span> | ||
</div> | ||
</fieldset> | ||
<div id='main-wrapper' class=''> | ||
<div class='ta-wrapper input'> | ||
<textarea id="input" | ||
placeholder="Shell input. Ctrl-enter/shift-enter runs it."> | ||
-- Use ctrl-enter or shift-enter to execute SQL | ||
.nullvalue NULL | ||
.mode box | ||
CREATE TABLE t(a,b); | ||
INSERT INTO t(a,b) VALUES('abc',123),('def',456),(NULL,789),('ghi',012); | ||
SELECT * FROM t;</textarea> | ||
<div class='button-bar'> | ||
<button id='btn-run'>Run</button> | ||
<button id='btn-clear'>Clear</button> | ||
<button data-cmd='.help'>Help</button> | ||
</div> | ||
</div> | ||
<div class='ta-wrapper output'> | ||
<textarea id="output" readonly | ||
placeholder="Shell output."></textarea> | ||
<div class='button-bar'> | ||
<button id='btn-clear-output'>Clear</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> <!-- .app-view --> | ||
<!-- Maintenance notes: | ||
- emscripten module init goes is in fiddle-pre.js and gets | ||
prepended to the generated script code. | ||
- App-specific code is in fiddle-post.js and gets appended to | ||
the generated script code. | ||
- The following placeholder (if you're reading this in the | ||
input template file) gets replaced by a generated | ||
amalgamation of: module-pre.js, emcc-generated bootstrapping | ||
code, and module-post.js. | ||
--> | ||
{{{ SCRIPT }}} | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
This directory houses a "fiddle"-style application which embeds a | ||
[Web Assembly (WASM)](https://en.wikipedia.org/wiki/WebAssembly) | ||
build of the sqlite3 shell app into an HTML page, effectively running | ||
the shell in a client-side browser. | ||
|
||
It requires [emscripten][] and that the build environment be set up for | ||
emscripten. A mini-HOWTO for setting that up follows... | ||
|
||
First, install the Emscripten SDK, as documented | ||
[here](https://emscripten.org/docs/getting_started/downloads.html) and summarized | ||
below for Linux environments: | ||
|
||
``` | ||
# Clone the emscripten repository: | ||
$ git clone https://github.com/emscripten-core/emsdk.git | ||
$ cd emsdk | ||
# Download and install the latest SDK tools: | ||
$ ./emsdk install latest | ||
# Make the "latest" SDK "active" for the current user: | ||
$ ./emsdk activate latest | ||
``` | ||
|
||
Those parts only need to be run once. The following needs to be run for each | ||
shell instance which needs the `emcc` compiler: | ||
|
||
``` | ||
# Activate PATH and other environment variables in the current terminal: | ||
$ source ./emsdk_env.sh | ||
$ which emcc | ||
/path/to/emsdk/upstream/emscripten/emcc | ||
``` | ||
|
||
That `env` script needs to be sourced for building this application from the | ||
top of the sqlite3 build tree: | ||
|
||
``` | ||
$ make fiddle | ||
``` | ||
|
||
Or: | ||
|
||
``` | ||
$ cd ext/fiddle | ||
$ make | ||
``` | ||
|
||
That will generate the fiddle application under | ||
[ext/fiddle](/dir/ext/fiddle), as `fiddle.html`. That application | ||
cannot, due to XMLHttpRequest security limitations, run if the HTML | ||
file is opened directly in the browser (i.e. if it is opened using a | ||
`file://` URL), so it needs to be served via an HTTP server. For | ||
example, using [althttpd][]: | ||
|
||
``` | ||
$ cd ext/fiddle | ||
$ althttpd -debug 1 -jail 0 -port 9090 -root . | ||
``` | ||
|
||
Then browse to `http://localhost:9090/fiddle.html`. | ||
|
||
Note that when serving this app via [althttpd][], it must be a version | ||
from 2022-05-17 or newer so that it recognizes the `.wasm` file | ||
extension and responds with the mimetype `application/wasm`, as the | ||
WASM loader is pedantic about that detail. | ||
|
||
# Known Quirks and Limitations | ||
|
||
Some "impedence mismatch" between C and WASM/JavaScript is to be | ||
expected. | ||
|
||
## No I/O | ||
|
||
sqlite3 shell commands which require file I/O or pipes are disabled in | ||
the WASM build. | ||
|
||
## `exit()` Triggered from C | ||
|
||
When C code calls `exit()`, as happens (for example) when running an | ||
"unsafe" command when safe mode is active, WASM's connection to the | ||
sqlite3 shell environment has no sensible choice but to shut down | ||
because `exit()` leaves it in a state we can no longer recover | ||
from. The JavaScript-side application attempts to recognize this and | ||
warn the user that restarting the application is necessary. Currently | ||
the only way to restart it is to reload the page. Restructuring the | ||
shell code such that it could be "rebooted" without restarting the | ||
JS app would require some invasive changes which are not currently | ||
on any TODO list but have not been entirely ruled out long-term. | ||
|
||
|
||
[emscripten]: https://emscripten.org | ||
[althttpd]: https://sqlite.org/althttpd |
Oops, something went wrong.