From 4bd279798cff26ada3fec39caa8a411b05bd9fc0 Mon Sep 17 00:00:00 2001 From: Peter Harpending Date: Fri, 24 Oct 2025 13:57:37 -0700 Subject: [PATCH] getting started with typescript --- Makefile | 7 ++ priv/static/index.html | 111 +------------------ priv/static/js/dist/index.d.ts | 22 ++++ priv/static/js/dist/index.js | 96 +++++++++++++++++ priv/static/js/dist/index.js.map | 1 + priv/static/js/dist/libfewd.d.ts | 6 ++ priv/static/js/dist/libfewd.js | 7 ++ priv/static/js/dist/libfewd.js.map | 1 + priv/static/js/ts/index.ts | 165 +++++++++++++++++++++++++++++ priv/static/js/ts/libfewd.ts | 10 ++ priv/static/js/tsconfig.json | 16 +++ 11 files changed, 332 insertions(+), 110 deletions(-) create mode 100644 Makefile create mode 100644 priv/static/js/dist/index.d.ts create mode 100644 priv/static/js/dist/index.js create mode 100644 priv/static/js/dist/index.js.map create mode 100644 priv/static/js/dist/libfewd.d.ts create mode 100644 priv/static/js/dist/libfewd.js create mode 100644 priv/static/js/dist/libfewd.js.map create mode 100644 priv/static/js/ts/index.ts create mode 100644 priv/static/js/ts/libfewd.ts create mode 100644 priv/static/js/tsconfig.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ef50480 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +tsc: + cd priv/static/js &&\ + tsc + +watch: + cd priv/static/js &&\ + tsc --watch diff --git a/priv/static/index.html b/priv/static/index.html index 6c1b6d7..e040762 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -24,115 +24,6 @@ - + diff --git a/priv/static/js/dist/index.d.ts b/priv/static/js/dist/index.d.ts new file mode 100644 index 0000000..e1bf407 --- /dev/null +++ b/priv/static/js/dist/index.d.ts @@ -0,0 +1,22 @@ +/** + * Home page ts/js + * + * @module + */ +declare function main(): void; +declare function on_input_key(evt: KeyboardEvent, ielt: HTMLInputElement, oelt: HTMLTextAreaElement, cb_resize: HTMLInputElement, cb_scroll: HTMLInputElement, max_height: number): Promise; +declare function auto_resize_output(checkbox_element: HTMLInputElement, target_element: HTMLTextAreaElement, max_height: number): void; +declare function auto_scroll_to_bottom(checkbox_element: HTMLInputElement, target_element: HTMLTextAreaElement): void; +type ok_err = { + ok: true; + result: t; +} | { + ok: false; + error: string; +}; +type wfcin = { + wfcin: string; +}; +type wfcout = ok_err; +declare function assert(condition: boolean, fail_msg: string): void; +declare function fetch_wfcin(user_line: string): Promise; diff --git a/priv/static/js/dist/index.js b/priv/static/js/dist/index.js new file mode 100644 index 0000000..ce687d3 --- /dev/null +++ b/priv/static/js/dist/index.js @@ -0,0 +1,96 @@ +"use strict"; +/** + * Home page ts/js + * + * @module + */ +//------------------------------------------------------------------ +// page element stuff +//------------------------------------------------------------------ +main(); +function main() { + let ielt = document.getElementById('wfc-input'); + let oelt = document.getElementById('wfc-output'); + let cb_resize = document.getElementById('auto-resize-output'); + let cb_scroll = document.getElementById('auto-scroll'); + let MAX_OELT_HEIGHT = 300; + ielt.addEventListener('keydown', function (e) { + on_input_key(e, ielt, oelt, cb_resize, cb_scroll, MAX_OELT_HEIGHT); + }); +} +// when user hits any key +async function on_input_key(evt, ielt, oelt, cb_resize, cb_scroll, max_height) { + if (evt.key === 'Enter') { + // don't do default thing + evt.preventDefault(); + // grab contents + let contents = ielt.value; + let trimmed = contents.trim(); + let nonempty = trimmed.length > 0; + // if contents are nonempty + if (nonempty) { + // clear input + ielt.value = ''; + // put in output + oelt.value += '> ' + trimmed + '\n'; + oelt.hidden = false; + // query backend for result + let result = await fetch_wfcin(trimmed); + if (result.ok) + oelt.value += result.result; + else + oelt.value += result.error; + oelt.value += '\n'; + // auto-resize + auto_resize_output(cb_resize, oelt, max_height); + auto_scroll_to_bottom(cb_scroll, oelt); + } + } +} +function auto_resize_output(checkbox_element, target_element, max_height) { + // if the user has manually resized their output, we do nothing + if (checkbox_element.checked) { + let target_height = target_element.scrollHeight; + // resize it automagically up to 500px + if (target_height < max_height) + target_element.style.height = String(target_height) + 'px'; + else + target_element.style.height = String(max_height) + 'px'; + } +} +function auto_scroll_to_bottom(checkbox_element, target_element) { + if (checkbox_element.checked) { + // scroll to bottom + target_element.scrollTop = target_element.scrollHeight; + } +} +function assert(condition, fail_msg) { + if (!condition) + throw new Error(fail_msg); +} +async function fetch_wfcin(user_line) { + let req_body_obj = { wfcin: user_line }; + let req_body_str = JSON.stringify(req_body_obj); + let req_options = { method: 'POST', + headers: { 'content-type': 'application/json' }, + body: req_body_str }; + // default result = somehow neither branch of code below was run(?) + // putting this here so ts doesn't chimp out + let result = { ok: false, + error: 'IT DO BE LIKE THAT MISTA STANCIL' }; + try { + let response = await fetch('/wfcin', req_options); + if (response.ok) + result = await response.json(); + else { + console.log('bad http response:', response); + result = { ok: false, error: 'BAD HTTP RESPONSE' }; + } + } + catch (x) { + console.log('network error:', x); + result = { ok: false, error: 'NETWORK ERROR' }; + } + return result; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/priv/static/js/dist/index.js.map b/priv/static/js/dist/index.js.map new file mode 100644 index 0000000..fa2c502 --- /dev/null +++ b/priv/static/js/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/priv/static/js/dist/libfewd.d.ts b/priv/static/js/dist/libfewd.d.ts new file mode 100644 index 0000000..1e36154 --- /dev/null +++ b/priv/static/js/dist/libfewd.d.ts @@ -0,0 +1,6 @@ +/** + * FEWD common js lib functions + * + * @module + */ +export {}; diff --git a/priv/static/js/dist/libfewd.js b/priv/static/js/dist/libfewd.js new file mode 100644 index 0000000..f609044 --- /dev/null +++ b/priv/static/js/dist/libfewd.js @@ -0,0 +1,7 @@ +/** + * FEWD common js lib functions + * + * @module + */ +export {}; +//# sourceMappingURL=libfewd.js.map \ No newline at end of file diff --git a/priv/static/js/dist/libfewd.js.map b/priv/static/js/dist/libfewd.js.map new file mode 100644 index 0000000..516a45a --- /dev/null +++ b/priv/static/js/dist/libfewd.js.map @@ -0,0 +1 @@ +{"version":3,"file":"libfewd.js","sourceRoot":"","sources":["../ts/libfewd.ts"],"names":[],"mappings":"AAAA;;;;GAIG"} \ No newline at end of file diff --git a/priv/static/js/ts/index.ts b/priv/static/js/ts/index.ts new file mode 100644 index 0000000..2b535c0 --- /dev/null +++ b/priv/static/js/ts/index.ts @@ -0,0 +1,165 @@ +/** + * Home page ts/js + * + * @module + */ + + +//------------------------------------------------------------------ +// page element stuff +//------------------------------------------------------------------ + +main(); + +function +main + () + : void +{ + let ielt : HTMLInputElement = document.getElementById('wfc-input') as HTMLInputElement ; + let oelt : HTMLTextAreaElement = document.getElementById('wfc-output') as HTMLTextAreaElement ; + let cb_resize : HTMLInputElement = document.getElementById('auto-resize-output') as HTMLInputElement ; + let cb_scroll : HTMLInputElement = document.getElementById('auto-scroll') as HTMLInputElement ; + let MAX_OELT_HEIGHT : number = 300; + + + ielt.addEventListener('keydown', + function(e: KeyboardEvent) { + on_input_key(e, ielt, oelt, cb_resize, cb_scroll, MAX_OELT_HEIGHT); + } + ); +} + + +// when user hits any key +async function +on_input_key + (evt : KeyboardEvent, + ielt : HTMLInputElement, + oelt : HTMLTextAreaElement, + cb_resize : HTMLInputElement, + cb_scroll : HTMLInputElement, + max_height : number) + : Promise +{ + if (evt.key === 'Enter') { + // don't do default thing + evt.preventDefault(); + // grab contents + let contents : string = ielt.value; + let trimmed : string = contents.trim(); + let nonempty : boolean = trimmed.length > 0; + // if contents are nonempty + if (nonempty) { + // clear input + ielt.value = ''; + + // put in output + oelt.value += '> ' + trimmed + '\n'; + oelt.hidden = false; + + // query backend for result + let result : wfcout = await fetch_wfcin(trimmed); + + if (result.ok) + oelt.value += result.result; + else + oelt.value += result.error; + oelt.value += '\n'; + + // auto-resize + auto_resize_output(cb_resize, oelt, max_height); + auto_scroll_to_bottom(cb_scroll, oelt); + } + } +} + + + +function +auto_resize_output + (checkbox_element : HTMLInputElement, + target_element : HTMLTextAreaElement, + max_height : number) + : void +{ + // if the user has manually resized their output, we do nothing + if (checkbox_element.checked) { + let target_height: number = target_element.scrollHeight; + // resize it automagically up to 500px + if (target_height < max_height) + target_element.style.height = String(target_height) + 'px'; + else + target_element.style.height = String(max_height) + 'px'; + } +} + + +function +auto_scroll_to_bottom + (checkbox_element : HTMLInputElement, + target_element : HTMLTextAreaElement) + : void +{ + if (checkbox_element.checked) { + // scroll to bottom + target_element.scrollTop = target_element.scrollHeight; + } +} + + + +//------------------------------------------------------------------ +// wfc api +//------------------------------------------------------------------ + +type ok_err = {ok: true, result: t} + | {ok: false, error: string}; + +type wfcin = {wfcin: string}; +type wfcout = ok_err; + +function +assert + (condition : boolean, + fail_msg : string) + : void +{ + if(!condition) + throw new Error(fail_msg); +} + + +async function +fetch_wfcin + (user_line : string) + : Promise +{ + let req_body_obj = {wfcin: user_line}; + let req_body_str = JSON.stringify(req_body_obj); + + let req_options = {method: 'POST', + headers: {'content-type': 'application/json'}, + body: req_body_str}; + + // default result = somehow neither branch of code below was run(?) + // putting this here so ts doesn't chimp out + let result: wfcout = {ok : false, + error : 'IT DO BE LIKE THAT MISTA STANCIL'}; + + try { + let response : Response = await fetch('/wfcin', req_options); + if (response.ok) + result = await response.json() as wfcout; + else { + console.log('bad http response:', response); + result = {ok: false, error: 'BAD HTTP RESPONSE'}; + } + } + catch (x: any) { + console.log('network error:', x); + result = {ok: false, error: 'NETWORK ERROR'}; + } + + return result; +} diff --git a/priv/static/js/ts/libfewd.ts b/priv/static/js/ts/libfewd.ts new file mode 100644 index 0000000..fedf3b7 --- /dev/null +++ b/priv/static/js/ts/libfewd.ts @@ -0,0 +1,10 @@ +/** + * FEWD common js lib functions + * + * @module + */ + +export { +}; + + diff --git a/priv/static/js/tsconfig.json b/priv/static/js/tsconfig.json new file mode 100644 index 0000000..b622d4d --- /dev/null +++ b/priv/static/js/tsconfig.json @@ -0,0 +1,16 @@ +{"compilerOptions" : {"target" : "es2022", + "strict" : true, + "esModuleInterop" : true, + "skipLibCheck" : true, + "forceConsistentCasingInFileNames" : true, + "noImplicitAny" : true, + "strictNullChecks" : true, + "strictPropertyInitialization" : true, + "sourceMap" : true, + "outDir" : "dist", + "declaration" : true}, + "$schema" : "https://json.schemastore.org/tsconfig", + "display" : "Recommended", + "include" : ["ts/**/*"], + "exclude" : [], + "composite" : true}