Skip to content
Open
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# faust-web-component

> :warning: This is a fork !
>
> Features in fork: `readonly` boolean attribute for read-only editor
> and `min-height` for setting a minimum height as CSS prop to the editor element
> Additional variant \<faust-editor-basic\> without buttons for dynamic editor
> Aims to bring a lightweight variant, see [the demo](https://synthe.tiseur.fr/faust-web-component/#readonlyandbasic)
> or an usecase in [my other project](https://github.com/Simon-L/pasfa)
> `dist/faust-web-component-basic.js` contains only the basic variant, it's much smaller in size but obviously doesn't allow compiling DSP.

This package provides two [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) for embedding interactive [Faust](https://faust.grame.fr) snippets in web pages.

- `<faust-editor>` displays an editor (using [CodeMirror 6](https://codemirror.net/)) with executable, editable Faust code, along with some bells & whistles (controls, block diagram, plots) in a side pane.
This component is ideal for demonstrating some code in Faust and allowing the reader to try it out and tweak it themselves without having to leave the page. (For more extensive work, it also includes a button to open the code in the Faust IDE.)
* `readonly` : attribute disables playback and makes editor read-only.
* `min-height` : attribute to set the min-height CSS property, the value must be valid CSS value eg. "42px", "1.5em".

- `<faust-widget>` just shows the controls and does not allow editing, so it serves simply as a way to embed interactive DSP.

Expand Down
41 changes: 41 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</head>
<body>
<div id="content">
<a href="#readonlyandbasic">Readonly and basic editor examples</a>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
<faust-editor>
import("stdfaust.lib");
Expand Down Expand Up @@ -53,6 +54,46 @@
dm.zita_light; // stereo reverb
-->
</faust-editor>
<a id="readonlyandbasic"></a>
<p>Read-only editor</p>
<faust-editor readonly>
<!--
// This editor is readonly
import("stdfaust.lib");
process =
dm.cubicnl_demo : // distortion
dm.wah4_demo <: // wah pedal
dm.phaser2_demo : // stereo phaser
dm.compressor_demo : // stereo compressor
dm.zita_light; // stereo reverb
-->
</faust-editor>
<p>Basic editor without playback</p>
<faust-editor-basic>
<!--
// This editor is "basic", it doesn't offer playback
import("stdfaust.lib");
process =
dm.cubicnl_demo : // distortion
dm.wah4_demo <: // wah pedal
dm.phaser2_demo : // stereo phaser
dm.compressor_demo : // stereo compressor
dm.zita_light; // stereo reverb
-->
</faust-editor-basic>
<p>Read-only basic editor without playback</p>
<faust-editor-basic readonly>
<!--
// This editor is "basic", it doesn't offer playback
import("stdfaust.lib");
process =
dm.cubicnl_demo : // distortion
dm.wah4_demo <: // wah pedal
dm.phaser2_demo : // stereo phaser
dm.compressor_demo : // stereo compressor
dm.zita_light; // stereo reverb
-->
</faust-editor-basic>
<p>Minus, ducimus consequuntur? Corrupti animi aut magni nihil, dolor eos tenetur, autem deserunt et iure culpa, suscipit minus quia velit laudantium asperiores.</p>
<faust-widget>
<!--
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
"tsconfig.json",
"vite.config.js",
"dist/faust-web-component.js",
"dist/faust-web-component-basic.js",
"dist/02-XYLO1.mp3"
],
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build": "vite build && vite build --mode basic",
"preview": "vite preview"
},
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import jsURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.js?url"
import dataURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.data?url"
import wasmURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.wasm?url"
import { library } from "@fortawesome/fontawesome-svg-core"
import { faPlay, faStop, faUpRightFromSquare, faSquareCaretLeft, faAnglesLeft, faAnglesRight, faSliders, faDiagramProject, faWaveSquare, faChartLine, faPowerOff } from "@fortawesome/free-solid-svg-icons"
import { faPlay, faStop, faUpRightFromSquare, faSquareCaretLeft, faAnglesLeft, faAnglesRight, faSliders, faDiagramProject, faWaveSquare, faChartLine, faPowerOff, faCopy } from "@fortawesome/free-solid-svg-icons"

for (const icon of [faPlay, faStop, faUpRightFromSquare, faSquareCaretLeft, faAnglesLeft, faAnglesRight, faSliders, faDiagramProject, faWaveSquare, faChartLine, faPowerOff]) {
for (const icon of [faPlay, faStop, faUpRightFromSquare, faSquareCaretLeft, faAnglesLeft, faAnglesRight, faSliders, faDiagramProject, faWaveSquare, faChartLine, faPowerOff, faCopy]) {
library.add(icon)
}

Expand Down
3 changes: 2 additions & 1 deletion src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const faustLanguage = StreamLanguage.define(clike({
}
}))

export function createEditor(parent: HTMLElement, doc: string) {
export function createEditor(parent: HTMLElement, doc: string, readonly: boolean = false) {
return new EditorView({
parent,
doc,
Expand Down Expand Up @@ -63,6 +63,7 @@ export function createEditor(parent: HTMLElement, doc: string) {
...lintKeymap
]),
faustLanguage,
EditorState.readOnly.of(readonly)
],
})
}
Expand Down
171 changes: 171 additions & 0 deletions src/faust-editor-basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { icon } from "@fortawesome/fontawesome-svg-core"
import faustCSS from "@shren/faust-ui/dist/esm/index.css?inline"
import { createEditor, setError, clearError } from "./editor"
import faustSvg from "./faustText.svg"

function editorTemplate(minHeight: string = "") {
const editorMinHeight = minHeight != "" ? `min-height: ${minHeight};` : ""
const template = document.createElement("template")
let copyButton = `<button title="Copy" class="button" id="copy">${icon({ prefix: "fas", iconName: "copy" }).html[0]}</button>`
if (!navigator.clipboard) {
console.log("Unable to use clipboard")
copyButton = ""
}
template.innerHTML = `
<div id="root">
<div id="controls">
<a title="Open in Faust IDE" id="ide" href="https://faustide.grame.fr/" class="button" target="_blank">${icon({ prefix: "fas", iconName: "up-right-from-square" }).html[0]}</a>
${copyButton}
<a title="Faust website" id="faust" href="https://faust.grame.fr/" target="_blank"><img src="${faustSvg}" height="15px" /></a>
</div>
<div id="content">
<div id="editor"></div>
</div>
</div>
<style>
#root {
overflow:hidden;
border: 1px solid black;
border-radius: 5px;
box-sizing: border-box;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to reduce duplication between this module and faust-editor.ts. Perhaps the shared CSS could be moved out to a common module.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep that for a later task/PR


*, *:before, *:after {
box-sizing: inherit;
}

#controls {
background-color: #384d64;
border-bottom: 1px solid black;
display: flex;
}

#faust {
margin-left: auto;
margin-right: 10px;
display: flex;
align-items: center;
}

#content {
display: flex;
}

#editor {
flex-grow: 1;
overflow-y: auto;
}

#editor .cm-editor {
height: 100%;
${minHeight != "" ? `min-height: ${minHeight};` : ""}
}

.cm-diagnostic {
font-family: monospace;
}

.cm-diagnostic-error {
background-color: #fdf2f5 !important;
color: #a4000f !important;
border-color: #a4000f !important;
}

a.button {
appearance: button;
}

.button {
background-color: #384d64;
border: 0;
padding: 5px;
width: 25px;
height: 25px;
color: #fff;
}

.button:hover {
background-color: #4b71a1;
}

.button:active {
background-color: #373736;
}

.button:disabled {
opacity: 0.65;
cursor: not-allowed;
pointer-events: none;
}

#controls > .button > svg {
width: 15px;
height: 15px;
vertical-align: top;
}

${faustCSS}
</style>
`
return template
}

export default class FaustEditorBasic extends HTMLElement {
constructor() {
super()
}

readonly = false
minHeight = null
editor = null

getCodeString() {
return this.editor.state.doc.toString()
}

setCode(code) {
this.editor.dispatch({
changes: {from: 0, to: this.editor.state.doc.length, insert: code}
})
}

connectedCallback() {
const code = this.innerHTML.replace("<!--", "").replace("-->", "").trim()
this.attachShadow({ mode: "open" }).appendChild(editorTemplate(this.minHeight).content.cloneNode(true))

const ideLink = this.shadowRoot!.querySelector("#ide") as HTMLAnchorElement
ideLink.onfocus = () => {
// Open current contents of editor in IDE
const urlParams = new URLSearchParams()
urlParams.set("inline", btoa(editor.state.doc.toString()).replace("+", "-").replace("/", "_"))
ideLink.href = `https://faustide.grame.fr/?${urlParams.toString()}`
}

const editorEl = this.shadowRoot!.querySelector("#editor") as HTMLDivElement
const editor = createEditor(editorEl, code, this.readonly)

const copyButton = this.shadowRoot!.querySelector("#copy") as HTMLButtonElement

if (copyButton !== null) {
copyButton.onclick = () => {
navigator.clipboard.writeText(editor.state.doc.toString())
}
}

this.editor = editor
}

attributeChangedCallback(name, oldValue, newValue) {
if ((name === "readonly") && (newValue !== null)) {
this.readonly = true
}
if ((name === "min-height") && (newValue !== "")) {
this.minHeight = newValue
}
}

static get observedAttributes() {
return ["readonly", "min-height"];
}

}
Loading