diff --git a/site/src/routes/repl/_components/CodeMirror.svelte b/site/src/components/Repl/CodeMirror.svelte similarity index 70% rename from site/src/routes/repl/_components/CodeMirror.svelte rename to site/src/components/Repl/CodeMirror.svelte index b8b3b18109..8c3f5a0660 100644 --- a/site/src/routes/repl/_components/CodeMirror.svelte +++ b/site/src/components/Repl/CodeMirror.svelte @@ -12,33 +12,60 @@ @@ -171,10 +180,10 @@
- {#each $component_store as component} + {#each $components as component}
diff --git a/site/src/components/Repl/Input/ModuleEditor.svelte b/site/src/components/Repl/Input/ModuleEditor.svelte new file mode 100644 index 0000000000..272066e84f --- /dev/null +++ b/site/src/components/Repl/Input/ModuleEditor.svelte @@ -0,0 +1,59 @@ + + + + +
+
+ +
+ +
+ {#if $bundle} + {#if $bundle.error} + + {:else if $bundle.warnings.length > 0} + {#each $bundle.warnings as warning} + + {/each} + {/if} + {/if} +
+
\ No newline at end of file diff --git a/site/src/routes/repl/_components/InputOutputToggle.svelte b/site/src/components/Repl/InputOutputToggle.svelte similarity index 100% rename from site/src/routes/repl/_components/InputOutputToggle.svelte rename to site/src/components/Repl/InputOutputToggle.svelte diff --git a/site/src/components/Repl/Message.svelte b/site/src/components/Repl/Message.svelte new file mode 100644 index 0000000000..6a9183dfb3 --- /dev/null +++ b/site/src/components/Repl/Message.svelte @@ -0,0 +1,80 @@ + + + + +
+ {#if details} +

{message(details)}

+ {:else} + + {/if} +
\ No newline at end of file diff --git a/site/src/components/Repl/Output/Compiler.js b/site/src/components/Repl/Output/Compiler.js new file mode 100644 index 0000000000..b6d13e53bd --- /dev/null +++ b/site/src/components/Repl/Output/Compiler.js @@ -0,0 +1,38 @@ +export default class Compiler { + constructor(version) { + this.worker = new Worker('/workers/compiler.js'); + this.worker.postMessage({ type: 'init', version }); + + this.uid = 1; + this.handlers = new Map(); + + this.worker.onmessage = event => { + const handler = this.handlers.get(event.data.id); + handler(event.data.result); + this.handlers.delete(event.data.id); + }; + } + + compile(component, options) { + return new Promise(fulfil => { + const id = this.uid++; + + this.handlers.set(id, fulfil); + + this.worker.postMessage({ + id, + type: 'compile', + source: component.source, + options: Object.assign({ + name: component.name, + filename: `${component.name}.svelte` + }, options), + entry: component.name === 'App' + }); + }); + } + + destroy() { + this.worker.terminate(); + } +} \ No newline at end of file diff --git a/site/src/routes/repl/_components/Output/CompilerOptions.svelte b/site/src/components/Repl/Output/CompilerOptions.svelte similarity index 100% rename from site/src/routes/repl/_components/Output/CompilerOptions.svelte rename to site/src/components/Repl/Output/CompilerOptions.svelte diff --git a/site/src/routes/repl/_components/Output/PropEditor.svelte b/site/src/components/Repl/Output/PropEditor.svelte similarity index 100% rename from site/src/routes/repl/_components/Output/PropEditor.svelte rename to site/src/components/Repl/Output/PropEditor.svelte diff --git a/site/src/components/Repl/Output/ReplProxy.js b/site/src/components/Repl/Output/ReplProxy.js new file mode 100644 index 0000000000..44b77b84dd --- /dev/null +++ b/site/src/components/Repl/Output/ReplProxy.js @@ -0,0 +1,89 @@ +export default class ReplProxy { + constructor(iframe, handlers) { + this.iframe = iframe; + this.handlers = handlers; + + this.cmdId = 1; + this.pendingCmds = new Map(); + + this.handle_event = e => this.handleReplMessage(e); + window.addEventListener('message', this.handle_event, false); + } + + destroy() { + window.removeEventListener('message', this.handle_event); + } + + iframeCommand(command, args) { + return new Promise( (resolve, reject) => { + this.cmdId += 1; + this.pendingCmds.set(this.cmdId, { resolve, reject }); + + this.iframe.contentWindow.postMessage({ + action: command, + cmdId: this.cmdId, + args + }, '*') + }); + } + + handleCommandMessage(cmdData) { + let action = cmdData.action; + let id = cmdData.cmdId; + let handler = this.pendingCmds.get(id); + + if (handler) { + this.pendingCmds.delete(id); + if (action === 'cmdError') { + let { message, stack } = cmdData; + let e = new Error(message); + e.stack = stack; + console.log('repl cmd fail'); + handler.reject(e) + } + + if (action === 'cmdOk') { + handler.resolve(cmdData.args) + } + } else { + console.error('command not found', id, cmdData, [...this.pendingCmds.keys()]); + } + } + + handleReplMessage(event) { + const { action, args } = event.data; + + if (action === 'cmdError' || action === 'cmdOk') { + this.handleCommandMessage(event.data); + } + + if (action === 'prop_update') { + const { prop, value } = args; + this.handlers.onPropUpdate(prop, value) + } + + if (action === 'fetch_progress') { + this.handlers.onFetchProgress(args.remaining) + } + } + + eval(script) { + return this.iframeCommand('eval', { script }); + } + + setProp(prop, value) { + return this.iframeCommand('set_prop', {prop, value}) + } + + bindProps(props) { + return this.iframeCommand('bind_props', { props }) + } + + handleLinks() { + return this.iframeCommand('catch_clicks', {}); + } + + fetchImports(imports, import_map) { + return this.iframeCommand('fetch_imports', { imports, import_map }) + } +} \ No newline at end of file diff --git a/site/src/components/Repl/Output/Viewer.svelte b/site/src/components/Repl/Output/Viewer.svelte new file mode 100644 index 0000000000..42f3e31326 --- /dev/null +++ b/site/src/components/Repl/Output/Viewer.svelte @@ -0,0 +1,158 @@ + + + + +
+ + +
+ {#if error} + + {:else if !$bundle} + loading Svelte compiler... + {:else if pending_imports} + loading {pending_imports} {pending_imports === 1 ? 'dependency' : 'dependencies'} from + https://bundle.run + {/if} +
+
\ No newline at end of file diff --git a/site/src/routes/repl/_utils/getLocationFromStack.js b/site/src/components/Repl/Output/getLocationFromStack.js similarity index 100% rename from site/src/routes/repl/_utils/getLocationFromStack.js rename to site/src/components/Repl/Output/getLocationFromStack.js diff --git a/site/src/components/Repl/Output/index.svelte b/site/src/components/Repl/Output/index.svelte new file mode 100644 index 0000000000..d1b2bd344e --- /dev/null +++ b/site/src/components/Repl/Output/index.svelte @@ -0,0 +1,246 @@ + + + + +
+ + + + + +
+ + +
+ +
+ +
+ +
+ {#if show_props} +

Props editor

+ + {#if props} + {#if props.length > 0} +
+ {#each props.sort() as prop (prop)} + {prop} + + + + {/each} +
+ {:else} +
+ + +

This component has no props — declare props with the export keyword

+
+ {/if} + {/if} + {/if} +
+
+
+ + +
+ {#if embedded} + + {:else} + +
+ +
+ +
+

Compiler options

+ + +
+
+ {/if} +
+ + +
+ +
\ No newline at end of file diff --git a/site/src/routes/repl/_components/SplitPane.svelte b/site/src/components/Repl/SplitPane.svelte similarity index 99% rename from site/src/routes/repl/_components/SplitPane.svelte rename to site/src/components/Repl/SplitPane.svelte index a79ef8b775..d7106553f3 100644 --- a/site/src/routes/repl/_components/SplitPane.svelte +++ b/site/src/components/Repl/SplitPane.svelte @@ -71,6 +71,7 @@ } .pane { + position: relative; float: left; width: 100%; height: 100%; diff --git a/site/src/routes/repl/_components/_codemirror.js b/site/src/components/Repl/_codemirror.js similarity index 100% rename from site/src/routes/repl/_components/_codemirror.js rename to site/src/components/Repl/_codemirror.js diff --git a/site/src/routes/repl/_components/codemirror.css b/site/src/components/Repl/codemirror.css similarity index 100% rename from site/src/routes/repl/_components/codemirror.css rename to site/src/components/Repl/codemirror.css diff --git a/site/src/components/Repl/index.svelte b/site/src/components/Repl/index.svelte new file mode 100644 index 0000000000..128e6f639f --- /dev/null +++ b/site/src/components/Repl/index.svelte @@ -0,0 +1,232 @@ + + + + +
+
+ +
+ + +
+ +
+ +
+
+
+
+ + diff --git a/site/src/routes/api/blog/_posts.js b/site/src/routes/api/blog/_posts.js index 8b70483cd1..33c033b517 100644 --- a/site/src/routes/api/blog/_posts.js +++ b/site/src/routes/api/blog/_posts.js @@ -1,19 +1,10 @@ import fs from 'fs'; import path from 'path'; -import process_markdown from '../../../utils/_process_markdown.js'; +import { extract_frontmatter, langs } from '../../../utils/markdown.js'; import marked from 'marked'; - import PrismJS from 'prismjs'; import 'prismjs/components/prism-bash'; -// map lang to prism-language-attr -const prismLang = { - bash: 'bash', - html: 'markup', - js: 'javascript', - css: 'css', -}; - export default function() { return fs .readdirSync('content/blog') @@ -22,7 +13,7 @@ export default function() { const markdown = fs.readFileSync(`content/blog/${file}`, 'utf-8'); - const { content, metadata } = process_markdown(markdown); + const { content, metadata } = extract_frontmatter(markdown); const date = new Date(`${metadata.pubdate} EDT`); // cheeky hack metadata.dateString = date.toDateString(); @@ -30,7 +21,7 @@ export default function() { const renderer = new marked.Renderer(); renderer.code = (source, lang) => { - const plang = prismLang[lang]; + const plang = langs[lang]; const highlighted = PrismJS.highlight( source, PrismJS.languages[plang], diff --git a/site/src/routes/guide/_sections.js b/site/src/routes/guide/_sections.js index 59818bc464..0fd1382652 100644 --- a/site/src/routes/guide/_sections.js +++ b/site/src/routes/guide/_sections.js @@ -1,20 +1,10 @@ import fs from 'fs'; import path from 'path'; -import * as fleece from 'golden-fleece'; -import process_markdown from '../../utils/_process_markdown.js'; +import { extract_frontmatter, extract_metadata, langs } from '../../utils/markdown.js'; import marked from 'marked'; - import PrismJS from 'prismjs'; import 'prismjs/components/prism-bash'; -// map lang to prism-language-attr -const prismLang = { - bash: 'bash', - html: 'markup', - js: 'javascript', - css: 'css', -}; - const escaped = { '"': '"', "'": ''', @@ -45,24 +35,6 @@ const blockTypes = [ 'tablecell' ]; -function extractMeta(line, lang) { - try { - if (lang === 'html' && line.startsWith('')) { - return fleece.evaluate(line.slice(4, -3).trim()); - } - - if ( - lang === 'js' || - (lang === 'json' && line.startsWith('/*') && line.endsWith('*/')) - ) { - return fleece.evaluate(line.slice(2, -2).trim()); - } - } catch (err) { - // TODO report these errors, don't just squelch them - return null; - } -} - // https://github.com/darkskyapp/string-hash/blob/master/index.js function getHash(str) { let hash = 5381; @@ -81,7 +53,7 @@ export default function() { .map(file => { const markdown = fs.readFileSync(`content/guide/${file}`, 'utf-8'); - const { content, metadata } = process_markdown(markdown); + const { content, metadata } = extract_frontmatter(markdown); const subsections = []; const groups = []; @@ -97,7 +69,7 @@ export default function() { const lines = source.split('\n'); - const meta = extractMeta(lines[0], lang); + const meta = extract_metadata(lines[0], lang); let prefix = ''; let className = 'code-block'; @@ -124,7 +96,7 @@ export default function() { if (meta && meta.hidden) return ''; - const plang = prismLang[lang]; + const plang = langs[lang]; const highlighted = PrismJS.highlight( source, PrismJS.languages[plang], diff --git a/site/src/routes/repl/_components/AppControls/index.svelte b/site/src/routes/repl/_components/AppControls/index.svelte index af4a0cf2e8..9f1b6a47a7 100644 --- a/site/src/routes/repl/_components/AppControls/index.svelte +++ b/site/src/routes/repl/_components/AppControls/index.svelte @@ -6,7 +6,7 @@ import * as doNotZip from 'do-not-zip'; import downloadBlob from '../../_utils/downloadBlob.js'; import { user } from '../../../../user.js'; - import { enter } from '../events.js'; + import { enter } from '../../../../utils/events.js'; const dispatch = createEventDispatcher(); diff --git a/site/src/routes/repl/_components/Input/ModuleEditor.svelte b/site/src/routes/repl/_components/Input/ModuleEditor.svelte deleted file mode 100644 index 1b72f3aa31..0000000000 --- a/site/src/routes/repl/_components/Input/ModuleEditor.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - - - -
- {#if component} - - {/if} -
\ No newline at end of file diff --git a/site/src/routes/repl/_components/Input/index.svelte b/site/src/routes/repl/_components/Input/index.svelte deleted file mode 100644 index 0e824dd389..0000000000 --- a/site/src/routes/repl/_components/Input/index.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/site/src/routes/repl/_components/Output/Viewer.svelte b/site/src/routes/repl/_components/Output/Viewer.svelte deleted file mode 100644 index e204b3c79d..0000000000 --- a/site/src/routes/repl/_components/Output/Viewer.svelte +++ /dev/null @@ -1,325 +0,0 @@ - - - - -
- -
- -
- {#if error} -

- {#if error.loc} - - {#if error.filename} - {error.filename} - {/if} - - ({error.loc.line}:{error.loc.column}) - - {/if} - - {error.message} -

- {:elseif pending} -
- -
- {:elseif pendingImports} -

loading {pendingImports} {pendingImports === 1 ? 'dependency' : 'dependencies'} from - https://bundle.run

- {/if} -
\ No newline at end of file diff --git a/site/src/routes/repl/_components/Output/index.svelte b/site/src/routes/repl/_components/Output/index.svelte deleted file mode 100644 index aea23e51d1..0000000000 --- a/site/src/routes/repl/_components/Output/index.svelte +++ /dev/null @@ -1,197 +0,0 @@ - - - - -
- - - - - -
- -{#if view === 'result'} - -
- {#if bundle} - - {:else} - -

loading Svelte compiler...

- {/if} -
- -
-

Props editor

- - {#if props} - {#if props.length > 0} -
- {#each props.sort() as prop (prop)} - {prop} - - - - {/each} -
- {:else} -
- - -

This component has no props — declare props with the export keyword

-
- {/if} - {/if} -
-
-{:elseif embedded} - -{:else} - -
- -
- -
-

Compiler options

- - -
-
-{/if} diff --git a/site/src/routes/repl/_components/Repl.svelte b/site/src/routes/repl/_components/Repl.svelte deleted file mode 100644 index 318ba0d5e8..0000000000 --- a/site/src/routes/repl/_components/Repl.svelte +++ /dev/null @@ -1,321 +0,0 @@ - - - - -
-
- -
- -
- -
- -
-
-
-
- - diff --git a/site/src/routes/repl/_utils/replProxy.js b/site/src/routes/repl/_utils/replProxy.js deleted file mode 100644 index 4025dffe6b..0000000000 --- a/site/src/routes/repl/_utils/replProxy.js +++ /dev/null @@ -1,90 +0,0 @@ -export default class ReplProxy { - constructor(iframe) { - this.iframe = iframe; - this.cmdId = 1; - this.pendingCmds = new Map(); - this.onPropUpdate = null; - this.onFetchProgress = null; - this.handle_event = (ev) => this.handleReplMessage(ev); - window.addEventListener("message", this.handle_event, false); - } - - destroy() { - window.removeEventListener("message", this.handle_event); - } - - iframeCommand(command, args) { - return new Promise( (resolve, reject) => { - this.cmdId = this.cmdId + 1; - this.pendingCmds.set(this.cmdId, { resolve: resolve, reject: reject }); - this.iframe.contentWindow.postMessage({ - action: command, - cmdId: this.cmdId, - args: args - }, '*') - }); - } - - handleCommandMessage(cmdData) { - let action = cmdData.action; - let id = cmdData.cmdId; - let handler = this.pendingCmds.get(id); - if (handler) { - - this.pendingCmds.delete(id); - if (action == "cmdError") { - let { message, stack } = cmdData; - let e = new Error(message); - e.stack = stack; - console.log("repl cmd fail"); - handler.reject(e) - } - - if (action == "cmdOk") { - handler.resolve(cmdData.args) - } - } else { - console.error("command not found", id, cmdData, [...this.pendingCmds.keys()]); - } - } - - handleReplMessage(ev) { - - let action = ev.data.action; - if ( action == "cmdError" || action == "cmdOk" ) { - this.handleCommandMessage(ev.data); - } - - if (action == "prop_update") { - let { prop, value } = ev.data.args; - if (this.onPropUpdate) - this.onPropUpdate(prop, value) - } - - if (action == "fetch_progress") { - if (this.onFetchProgress) - this.onFetchProgress(ev.data.args.remaining) - } - } - - eval(script) { - return this.iframeCommand("eval", { script: script }); - } - - setProp(prop, value) { - return this.iframeCommand("set_prop", {prop, value}) - } - - bindProps(props) { - return this.iframeCommand("bind_props", { props: props }) - } - - handleLinks() { - return this.iframeCommand("catch_clicks", {}); - } - - fetchImports(bundle) { - return this.iframeCommand("fetch_imports", { bundle }) - } - -} \ No newline at end of file diff --git a/site/src/routes/repl/embed.svelte b/site/src/routes/repl/embed.svelte index c9eb0a08ea..fc74d8df5f 100644 --- a/site/src/routes/repl/embed.svelte +++ b/site/src/routes/repl/embed.svelte @@ -11,15 +11,11 @@