Skip to content

Commit

Permalink
feat: wasm playground (#233)
Browse files Browse the repository at this point in the history
* chore:  update

* chore: basic work

* chore: tweak

* fix: dead loop

* feat: auto focus and rename to newly added module

* chore: tweak style

* chore: fmt
  • Loading branch information
houyunlu committed Nov 13, 2023
1 parent 4bc4ada commit 724f4b2
Show file tree
Hide file tree
Showing 13 changed files with 764 additions and 10 deletions.
3 changes: 2 additions & 1 deletion web/playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
>This page contains webassembly and javascript content, please enable
javascript in your browser.</noscript
>
<script src="./bootstrap.js" type="module"></script>
<div id="app"></div>
<script src="./src/main.ts" type="module"></script>
</body>
</html>
18 changes: 12 additions & 6 deletions web/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@
"wasm",
"rust"
],
"author": "Ashley Williams <ashley666ashley@gmail.com>",
"license": "(MIT OR Apache-2.0)",
"bugs": {
"url": "https://github.com/rustwasm/create-wasm-app/issues"
},
"homepage": "https://github.com/rustwasm/create-wasm-app#readme",
"license": "MIT",
"homepage": "https://github.com/rolldown-rs/rolldown",
"devDependencies": {
"@types/path-browserify": "^1.0.2",
"@vitejs/plugin-vue": "^4.4.1",
"vite": "^4.5.0",
"vite-plugin-top-level-await": "^1.3.1"
},
"dependencies": {
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/theme-one-dark": "^6.1.2",
"codemirror": "^6.0.1",
"path-browserify": "^1.0.1",
"vue": "^3.3.8",
"vue-codemirror": "^6.1.1"
}
}
83 changes: 83 additions & 0 deletions web/playground/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<script setup lang="ts">
import { ref, onMounted, Ref } from 'vue'
import ModuleBlock from './components/ModuleBlock.vue'
import init, { bundle } from '../../wasm'
import {
convertAssetListToModuleList,
normalizeModules,
uniqueModulePath,
} from './utils/index'
import type { ModuleInfo } from './utils/index'
const moduleList: Ref<ModuleInfo[]> = ref([
{ title: 'index.js', code: `console.log("hello world")` },
])
const outputs: Ref<ModuleInfo[]> = ref([])
const wasmLoadFinished = ref(false)
onMounted(() => {
init().then((_) => {
wasmLoadFinished.value = true
})
})
const handleBuild = () => {
const fileList = normalizeModules(moduleList.value)
let res = bundle(fileList)
outputs.value = convertAssetListToModuleList(res)
}
const handleAddModule = () => {
const title = uniqueModulePath(moduleList.value)
moduleList.value.push({
title,
code: `console.log("hello world")`,
autofocus: true,
})
}
</script>

<template>
<div class="container">
<!-- module declaration block -->
<div class="module-list column">
<ModuleBlock
v-for="item in moduleList"
:code="item.code"
:title="item.title"
@code="item.code = $event"
@title="item.title = $event.target.innerText"
:auto-focus="item.autofocus"
/>
<button @click="handleAddModule">Add module</button>
</div>
<!-- output block -->
<div class="output column">
<button @click="handleBuild" :disabled="!wasmLoadFinished">build</button>
<ModuleBlock
v-for="item in outputs"
:code="item.code"
:title="item.title"
@code="item.code = $event"
@title="item.title = $event.target.innerText"
/>
</div>
</div>
</template>

<style scoped>
.container {
display: flex;
}
.column {
flex: 1;
padding: 0 10px;
}
.module-list {
margin-top: 40px;
}
</style>
66 changes: 66 additions & 0 deletions web/playground/src/components/CodeBlock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup lang="ts">
import { ShallowRef, defineComponent, ref, shallowRef } from 'vue'
import { Codemirror } from 'vue-codemirror'
import { javascript } from '@codemirror/lang-javascript'
import { oneDark } from '@codemirror/theme-one-dark'
import { EditorState } from '@codemirror/state'
import { EditorView } from '@codemirror/view'
type Payload = {
state: EditorState
view: EditorView
container: HTMLElement
}
defineProps({
code: String,
})
const emit = defineEmits(['code'])
const extensions = [javascript(), oneDark]
// Codemirror EditorView instance ref
const view = <ShallowRef<EditorView>>shallowRef()
const handleReady = (payload: Payload) => {
view.value = payload.view
}
// Status is available at all times via Codemirror EditorView
// const getCodemirrorStates = () => {
// const state = view.value.state
// const ranges = state.selection.ranges
// const selected = ranges.reduce((r, range) => r + range.to - range.from, 0)
// const cursor = ranges[0].anchor
// const length = state.doc.length
// const lines = state.doc.lines
// return {
// state,
// ranges,
// selected,
// cursor,
// length,
// lines
// }
// }
const handleCodeChange = (e: string) => {
emit('code', e)
}
const log = console.log
</script>
<template>
<codemirror
:model-value="code"
placeholder="Code goes here..."
:style="{ height: '400px' }"
:autofocus="false"
:indent-with-tab="true"
:tab-size="2"
:extensions="extensions"
@ready="handleReady"
@change="handleCodeChange"
@focus="log('focus', $event)"
@blur="console.log('blur', $event)"
/>
</template>
58 changes: 58 additions & 0 deletions web/playground/src/components/ModuleBlock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script setup lang="ts">
import CodeBlock from './CodeBlock.vue'
import { defineProps, onMounted, ref } from 'vue'
const props = defineProps({
title: {
type: String,
required: true,
},
code: {
type: String,
required: true,
},
autoFocus: {
type: Boolean,
required: false,
},
})
const input = ref(null)
onMounted(() => {
if (props.autoFocus && input.value) {
const [basename, _] = props.title.split('.')
const target = input.value as any
var range = document.createRange()
// Select the text nodes within the div (startNode, startOffset, endNode, endOffset)
range.setStart(target.childNodes[0], 0)
range.setEnd(target.childNodes[0], basename.length)
getSelection()?.removeAllRanges()
// Add the new range to the selection
getSelection()?.addRange(range)
target.focus()
}
})
</script>

<template>
<div>
<div
class="title"
ref="input"
contenteditable
@input="$emit('title', $event)"
>
{{ title }}
</div>
<CodeBlock :code="code" @code="$emit('code', $event)" />
</div>
</template>

<style>
.title {
margin: auto;
}
</style>
18 changes: 18 additions & 0 deletions web/playground/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createApp } from 'vue'
import './style.css'
import { basicSetup } from 'codemirror'
import VueCodemirror from 'vue-codemirror'
import App from './App.vue'

let app = createApp(App)
app.use(VueCodemirror, {
// optional default global options
autofocus: true,
disabled: false,
indentWithTab: true,
tabSize: 2,
placeholder: 'Code goes here...',
extensions: [basicSetup],
// ...
})
app.mount('#app')
65 changes: 65 additions & 0 deletions web/playground/src/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}

.card {
padding: 2em;
}

@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
51 changes: 51 additions & 0 deletions web/playground/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as path from 'path-browserify'
import { AssetItem, FileItem } from '../../../wasm'

export type ModuleInfo = {
title: string
code: string
autofocus?: boolean
}

export function normalizeModules(modules: ModuleInfo[]): FileItem[] {
return modules.map(normalizeModule)
}

export function convertAssetListToModuleList(
assetList: AssetItem[],
): ModuleInfo[] {
return assetList.map((item) => {
return {
title: item.name,
code: item.content,
}
})
}

/**
* convert relative path into absolute path in memory fs
*
* */
function normalizeModule(module: ModuleInfo): FileItem {
let title = module.title
let code = module.code
let isAbsolute = path.isAbsolute(title)
if (!isAbsolute) {
title = path.join('/', title)
}
return new FileItem(title, code)
}

let moduleId = 1

export function uniqueModulePath(modules: ModuleInfo[]): string {
let curName = `module_${moduleId}.js`
while (true) {
let m = modules.find((item) => item.title === curName)
if (!m) {
break
}
curName = `module_${++moduleId}.js`
}
return curName
}
1 change: 1 addition & 0 deletions web/playground/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />

0 comments on commit 724f4b2

Please sign in to comment.