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}
{:else}
{#if component === editing}
- {component.name + (/\./.test(component.name) ? '' : `.${component.type}`)}
+ {editing.name + (/\./.test(editing.name) ? '' : `.${editing.type}`)}
-
+
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 @@
+
+
+
+
+
+ Result
+
+ JS output
+
+ CSS output
+
+
+
+
+
+
+
+
+
+
+ {#if show_props}
+ Props editor
+
+ {#if props}
+ {#if props.length > 0}
+
+ {#each props.sort() as prop (prop)}
+
{prop}
+
+
+
+ {/each}
+
+ {:else}
+
+ {/if}
+ {/if}
+ {/if}
+
+
+
+
+
+
+ {#if embedded}
+
+ {:else}
+
+
+
+
+
+
+
+ {/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}
-
- Click to run
-
- {: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 @@
-
-
-
-
-
- Result
-
- JS output
-
- CSS output
-
-
-{#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}
-
- {/if}
- {/if}
-
-
-{:elseif embedded}
-
-{:else}
-
-
-
-
-
-
-
-{/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 @@