@grame/faust-web-component 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,212 @@
1
+ import { icon } from "@fortawesome/fontawesome-svg-core"
2
+ import faustCSS from "@shren/faust-ui/dist/esm/index.css?inline"
3
+ import faustSvg from "./faustText.svg"
4
+ import { IFaustMonoWebAudioNode } from "@grame/faustwasm"
5
+ import { IFaustPolyWebAudioNode } from "@grame/faustwasm"
6
+ import { FaustUI } from "@shren/faust-ui"
7
+ import { faustPromise, audioCtx, mono_generator, poly_generator, compiler, getInputDevices, deviceUpdateCallbacks, accessMIDIDevice, midiInputCallback } from "./common"
8
+
9
+ const template = document.createElement("template")
10
+ template.innerHTML = `
11
+ <div id="root">
12
+ <div id="controls">
13
+ <button title="On/off" class="button" id="power" disabled>${icon({ prefix: "fas", iconName: "power-off" }).html[0]}</button>
14
+ <select id="audio-input" class="dropdown" disabled>
15
+ <option>Audio input</option>
16
+ </select>
17
+ <!-- TODO: MIDI input
18
+ <select id="midi-input" class="dropdown" disabled>
19
+ <option>MIDI input</option>
20
+ </select>
21
+ -->
22
+ <!-- TODO: volume control? <input id="volume" type="range" min="0" max="100"> -->
23
+ <a title="Faust website" id="faust" href="https://faust.grame.fr/" target="_blank"><img src="${faustSvg}" height="15px" /></a>
24
+ </div>
25
+ <div id="faust-ui"></div>
26
+ </div>
27
+ <style>
28
+ #root {
29
+ border: 1px solid black;
30
+ border-radius: 5px;
31
+ box-sizing: border-box;
32
+ display: inline-block;
33
+ background-color: #384d64;
34
+ }
35
+
36
+ *, *:before, *:after {
37
+ box-sizing: inherit;
38
+ }
39
+
40
+ #controls {
41
+ display: flex;
42
+ margin-bottom: -20px;
43
+ position: relative;
44
+ z-index: 1;
45
+ }
46
+
47
+ #faust {
48
+ margin-left: auto;
49
+ padding-left: 10px;
50
+ margin-right: 10px;
51
+ display: flex;
52
+ align-items: center;
53
+ }
54
+
55
+ a.button {
56
+ appearance: button;
57
+ }
58
+
59
+ .button {
60
+ background-color: #384d64;
61
+ border: 0;
62
+ padding: 5px;
63
+ width: 25px;
64
+ height: 25px;
65
+ color: #fff;
66
+ }
67
+
68
+ .button:hover {
69
+ background-color: #4b71a1;
70
+ }
71
+
72
+ .button:active {
73
+ background-color: #373736;
74
+ }
75
+
76
+ .button:disabled {
77
+ opacity: 0.65;
78
+ cursor: not-allowed;
79
+ pointer-events: none;
80
+ }
81
+
82
+ #controls > .button > svg {
83
+ width: 15px;
84
+ height: 15px;
85
+ vertical-align: top;
86
+ }
87
+
88
+ .dropdown {
89
+ height: 19px;
90
+ margin: 3px 0 3px 10px;
91
+ border: 0;
92
+ background: #fff;
93
+ }
94
+
95
+ ${faustCSS}
96
+ </style>
97
+ `
98
+
99
+ export default class FaustWidget extends HTMLElement {
100
+ constructor() {
101
+ super()
102
+ }
103
+
104
+ connectedCallback() {
105
+ const code = this.innerHTML.replace("<!--", "").replace("-->", "").trim()
106
+ this.attachShadow({ mode: "open" }).appendChild(template.content.cloneNode(true))
107
+
108
+ const powerButton = this.shadowRoot!.querySelector("#power") as HTMLButtonElement
109
+ const faustUIRoot = this.shadowRoot!.querySelector("#faust-ui") as HTMLDivElement
110
+
111
+ faustPromise.then(() => powerButton.disabled = false)
112
+
113
+ let on = false
114
+ //let node: IFaustMonoWebAudioNode | undefined
115
+ let node: IFaustPolyWebAudioNode | undefined
116
+ let input: MediaStreamAudioSourceNode | undefined
117
+ let faustUI: FaustUI
118
+
119
+ const setup = async () => {
120
+ await faustPromise
121
+ // Compile Faust code
122
+ //await mono_generator.compile(compiler, "main", code, "")
123
+ await poly_generator.compile(compiler, "main", code, "")
124
+ // Create controls via Faust UI
125
+ //const ui = mono_generator.getUI()
126
+ const ui = poly_generator.getUI()
127
+ faustUI = new FaustUI({ ui, root: faustUIRoot })
128
+ faustUIRoot.style.width = faustUI.minWidth * 1.25 + "px"
129
+ faustUIRoot.style.height = faustUI.minHeight * 1.25 + "px"
130
+ faustUI.resize()
131
+ }
132
+
133
+ const start = async () => {
134
+ if (audioCtx.state === "suspended") {
135
+ await audioCtx.resume()
136
+ }
137
+ // Create an audio node from compiled Faust
138
+ if (node === undefined) {
139
+ //node = (await mono_generator.createNode(audioCtx))!
140
+ node = (await poly_generator.createNode(audioCtx, 16))!
141
+ }
142
+
143
+ faustUI.paramChangeByUI = (path, value) => node?.setParamValue(path, value)
144
+ node.setOutputParamHandler((path, value) => faustUI.paramChangeByDSP(path, value))
145
+
146
+ if (node.numberOfInputs > 0) {
147
+ audioInputSelector.disabled = false
148
+ updateInputDevices(await getInputDevices())
149
+ await connectInput()
150
+ } else {
151
+ audioInputSelector.disabled = true
152
+ audioInputSelector.innerHTML = "<option>Audio input</option>"
153
+ }
154
+
155
+ accessMIDIDevice(midiInputCallback(node))
156
+ .then(() => {
157
+ console.log('Successfully connected to the MIDI device.');
158
+ })
159
+ .catch((error) => {
160
+ console.error('Error accessing MIDI device:', error.message);
161
+ });
162
+
163
+ node.connect(audioCtx.destination)
164
+ powerButton.style.color = "#ffa500"
165
+ }
166
+
167
+ const stop = () => {
168
+ node?.disconnect()
169
+ powerButton.style.color = "#fff"
170
+ }
171
+
172
+ powerButton.onclick = () => {
173
+ if (on) {
174
+ stop()
175
+ } else {
176
+ start()
177
+ }
178
+ on = !on
179
+ }
180
+
181
+ const audioInputSelector = this.shadowRoot!.querySelector("#audio-input") as HTMLSelectElement
182
+
183
+ const updateInputDevices = (devices: MediaDeviceInfo[]) => {
184
+ if (audioInputSelector.disabled) return
185
+ while (audioInputSelector.lastChild) audioInputSelector.lastChild.remove()
186
+ for (const device of devices) {
187
+ if (device.kind === "audioinput") {
188
+ audioInputSelector.appendChild(new Option(device.label || device.deviceId, device.deviceId))
189
+ }
190
+ }
191
+ }
192
+ deviceUpdateCallbacks.push(updateInputDevices)
193
+
194
+ const connectInput = async () => {
195
+ const deviceId = audioInputSelector.value
196
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId, echoCancellation: false, noiseSuppression: false, autoGainControl: false } })
197
+ if (input) {
198
+ input.disconnect()
199
+ input = undefined
200
+ }
201
+ if (node && node.numberOfInputs > 0) {
202
+ input = audioCtx.createMediaStreamSource(stream)
203
+ input.connect(node!)
204
+ }
205
+ }
206
+
207
+ audioInputSelector.onchange = connectInput
208
+
209
+ setup()
210
+ }
211
+ }
212
+
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2" xml:space="preserve" width="64.333328" height="12.414666" viewBox="0 0 64.333327 12.414666"><metadata id="metadata8"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><defs id="defs6"><clipPath clipPathUnits="userSpaceOnUse" id="clipPath18"><path d="M 0,93.507 H 93.507 V 0 H 0 Z" id="path16"/></clipPath></defs><g style="fill:#ffffff" id="g10" transform="matrix(1.3333333,0,0,-1.3333333,-31.440266,68.495732)"><g id="g12" style="fill:#ffffff"><g id="g14" clip-path="url(#clipPath18)" style="fill:#ffffff"><g id="g20" transform="translate(69.0172,49.3868)" style="fill:#ffffff"><path d="M 0,0 V -7.026 H -2.017 V 0 H -4.843 V 1.747 H 2.813 V 0 Z m -8.276,-5.615 c -0.176,-0.333 -0.415,-0.62 -0.718,-0.863 -0.302,-0.243 -0.658,-0.433 -1.067,-0.569 -0.409,-0.136 -0.849,-0.204 -1.321,-0.204 -0.435,0 -0.833,0.041 -1.193,0.124 -0.361,0.082 -0.685,0.191 -0.973,0.327 -0.289,0.135 -0.548,0.29 -0.779,0.463 -0.232,0.174 -0.431,0.352 -0.598,0.534 l 1.278,1.384 c 0.115,-0.125 0.248,-0.252 0.4,-0.381 0.152,-0.129 0.321,-0.247 0.509,-0.352 0.187,-0.106 0.392,-0.191 0.614,-0.255 0.221,-0.065 0.46,-0.097 0.716,-0.097 0.15,0 0.3,0.021 0.447,0.064 0.149,0.043 0.285,0.107 0.41,0.191 0.124,0.084 0.223,0.185 0.296,0.303 0.073,0.119 0.11,0.253 0.11,0.404 0,0.327 -0.158,0.581 -0.474,0.762 -0.316,0.181 -0.817,0.36 -1.504,0.536 -0.333,0.078 -0.64,0.192 -0.922,0.342 -0.283,0.151 -0.526,0.332 -0.729,0.543 -0.204,0.211 -0.361,0.453 -0.474,0.726 -0.112,0.274 -0.169,0.578 -0.169,0.914 0,0.368 0.065,0.714 0.195,1.038 0.13,0.325 0.328,0.611 0.595,0.859 0.266,0.247 0.597,0.443 0.993,0.589 0.397,0.145 0.865,0.218 1.406,0.218 0.446,0 0.842,-0.046 1.187,-0.137 0.344,-0.092 0.637,-0.201 0.877,-0.329 0.24,-0.127 0.437,-0.258 0.591,-0.391 0.155,-0.134 0.271,-0.239 0.348,-0.316 l -1.143,-1.266 c -0.099,0.078 -0.212,0.162 -0.338,0.255 -0.127,0.093 -0.268,0.179 -0.425,0.258 -0.156,0.08 -0.33,0.147 -0.521,0.201 -0.19,0.053 -0.391,0.08 -0.601,0.08 -0.142,0 -0.28,-0.022 -0.416,-0.067 -0.135,-0.046 -0.258,-0.105 -0.367,-0.178 -0.11,-0.073 -0.198,-0.161 -0.264,-0.265 -0.066,-0.103 -0.1,-0.211 -0.1,-0.323 0,-0.301 0.159,-0.544 0.477,-0.73 0.318,-0.185 0.753,-0.34 1.307,-0.465 0.327,-0.077 0.645,-0.184 0.955,-0.319 0.311,-0.136 0.588,-0.314 0.832,-0.536 0.244,-0.222 0.441,-0.493 0.591,-0.814 0.15,-0.321 0.225,-0.711 0.225,-1.172 0,-0.392 -0.088,-0.754 -0.263,-1.086 m -10.307,1.854 c 0,-0.527 -0.086,-1.01 -0.257,-1.449 -0.172,-0.439 -0.418,-0.813 -0.74,-1.124 -0.321,-0.31 -0.71,-0.553 -1.167,-0.729 -0.456,-0.175 -0.965,-0.263 -1.526,-0.263 -0.56,0 -1.067,0.086 -1.522,0.257 -0.455,0.171 -0.841,0.414 -1.159,0.729 -0.318,0.315 -0.563,0.691 -0.734,1.13 -0.171,0.439 -0.256,0.922 -0.256,1.449 v 5.446 h 1.985 v -5.271 c 0,-0.295 0.029,-0.56 0.086,-0.795 0.058,-0.235 0.153,-0.437 0.284,-0.606 0.131,-0.169 0.304,-0.301 0.521,-0.394 0.217,-0.094 0.481,-0.14 0.795,-0.14 0.309,0 0.57,0.046 0.785,0.14 0.214,0.093 0.388,0.225 0.521,0.394 0.133,0.169 0.227,0.371 0.283,0.606 0.056,0.235 0.084,0.5 0.084,0.795 v 5.271 h 2.017 z M -30.68,-7.026 c -0.596,0 -1.078,0.482 -1.078,1.077 0,0.595 0.482,1.078 1.078,1.078 0.595,0 1.077,-0.483 1.077,-1.078 0,-0.595 -0.482,-1.077 -1.077,-1.077 m -1.702,3.551 -1.067,3.314 -1.092,-3.314 -0.054,-0.181 h -1.899 l 2.018,5.403 h 2.196 l 1.977,-5.4 -2.023,-0.005 z m -3.708,-3.551 c -0.595,0 -1.078,0.482 -1.078,1.077 0,0.595 0.483,1.078 1.078,1.078 0.595,0 1.078,-0.483 1.078,-1.078 0,-0.595 -0.483,-1.077 -1.078,-1.077 M -39.541,0 h -3.912 v -1.991 h 2.974 v -1.696 h -2.974 v -3.339 h -1.984 v 8.773 h 5.896 z" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path22"/></g></g></g></g></svg>
package/src/main.ts ADDED
@@ -0,0 +1,5 @@
1
+ import FaustEditor from "./faust-editor"
2
+ import FaustWidget from "./faust-widget"
3
+
4
+ customElements.define("faust-editor", FaustEditor)
5
+ customElements.define("faust-widget", FaustWidget)
package/src/scope.ts ADDED
@@ -0,0 +1,205 @@
1
+ // Based on https://codepen.io/ContemporaryInsanity/pen/Mwvqpb
2
+
3
+ export class Scope {
4
+ container: HTMLElement
5
+ canvas: HTMLCanvasElement
6
+ ctx: CanvasRenderingContext2D
7
+
8
+ constructor(container: HTMLElement) {
9
+ this.container = container
10
+ this.container.classList.add("scope")
11
+
12
+ this.canvas = document.createElement("canvas")
13
+ this.canvas.style.transformOrigin = "top left"
14
+ this.ctx = this.canvas.getContext("2d")!
15
+ this.onResize = this.onResize.bind(this)
16
+
17
+ this.container.appendChild(this.canvas)
18
+ this.onResize()
19
+ window.addEventListener("resize", this.onResize)
20
+ }
21
+
22
+ get canvasWidth() { return this.canvas.width / devicePixelRatio }
23
+ set canvasWidth(canvasWidth) {
24
+ this.canvas.width = Math.floor(canvasWidth * devicePixelRatio)
25
+ this.canvas.style.width = `${canvasWidth}px`
26
+ }
27
+
28
+ get canvasHeight() { return this.canvas.height / devicePixelRatio }
29
+ set canvasHeight(canvasHeight) {
30
+ this.canvas.height = Math.floor(canvasHeight * devicePixelRatio)
31
+ this.canvas.style.height = `${canvasHeight}px`
32
+ }
33
+
34
+ renderScope(toRender: { analyser: AnalyserNode, style: string, edgeThreshold: number }[] = []) {
35
+ // grid
36
+ this.ctx.fillStyle = "white"
37
+ this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
38
+ this.ctx.lineWidth = 1
39
+ this.ctx.strokeStyle = "rgba(200, 200, 200, 0.5)"
40
+ this.ctx.fillStyle = "rgba(200, 200, 200, 0.5)"
41
+ this.ctx.beginPath()
42
+
43
+ const numHorzSteps = 8
44
+ const horzStep = this.canvasWidth / numHorzSteps
45
+ for (let i = horzStep; i < this.canvasWidth; i += horzStep) {
46
+ this.ctx.moveTo(i, 0)
47
+ this.ctx.lineTo(i, this.canvasHeight)
48
+ }
49
+
50
+ const numVertSteps = 4
51
+ const vertStep = this.canvasHeight / numVertSteps
52
+ for (let i = 0; i < this.canvasHeight; i += vertStep) {
53
+ this.ctx.moveTo(0, i)
54
+ this.ctx.lineTo(this.canvasWidth, i)
55
+ }
56
+ this.ctx.stroke()
57
+
58
+ // 0 line
59
+ this.ctx.strokeStyle = "rgba(100, 100, 100, 0.5)"
60
+ this.ctx.beginPath()
61
+ this.ctx.lineWidth = 2
62
+ this.ctx.moveTo(0, this.canvasHeight / 2)
63
+ this.ctx.lineTo(this.canvasWidth, this.canvasHeight / 2)
64
+ this.ctx.stroke()
65
+
66
+ // waveforms
67
+ toRender.forEach(({ analyser, style = "rgb(43, 156, 212)", edgeThreshold = 0 }) => {
68
+ if (analyser === undefined) { return }
69
+
70
+ const timeData = new Float32Array(analyser.frequencyBinCount)
71
+ let risingEdge = 0
72
+
73
+ analyser.getFloatTimeDomainData(timeData)
74
+
75
+ this.ctx.lineWidth = 2
76
+ this.ctx.strokeStyle = style
77
+
78
+ this.ctx.beginPath()
79
+
80
+ while (timeData[risingEdge] > 0 &&
81
+ risingEdge <= this.canvasWidth &&
82
+ risingEdge < timeData.length) {
83
+ risingEdge++
84
+ }
85
+
86
+ if (risingEdge >= this.canvasWidth) { risingEdge = 0 }
87
+
88
+
89
+ while (timeData[risingEdge] < edgeThreshold &&
90
+ risingEdge <= this.canvasWidth &&
91
+ risingEdge< timeData.length) {
92
+ risingEdge++
93
+ }
94
+
95
+ if (risingEdge >= this.canvasWidth) { risingEdge = 0 }
96
+
97
+ for (let x = risingEdge; x < timeData.length && x - risingEdge < this.canvasWidth; x++) {
98
+ const y = this.canvasHeight - (((timeData[x] + 1) / 2) * this.canvasHeight)
99
+ this.ctx.lineTo(x - risingEdge, y)
100
+ }
101
+
102
+ this.ctx.stroke()
103
+ })
104
+
105
+ // markers
106
+ this.ctx.fillStyle = "black"
107
+ this.ctx.font = "11px Courier"
108
+ this.ctx.textAlign = "left"
109
+ const numMarkers = 4
110
+ const markerStep = this.canvasHeight / numMarkers
111
+ for (let i = 0; i <= numMarkers; i++) {
112
+ this.ctx.textBaseline =
113
+ i === 0 ? "top"
114
+ : i === numMarkers ? "bottom"
115
+ : "middle"
116
+
117
+ const value = ((numMarkers - i) - (numMarkers / 2)) / numMarkers * 2
118
+ this.ctx.textAlign = "left"
119
+ this.ctx.fillText(value + "", 5, i * markerStep)
120
+ this.ctx.textAlign = "right"
121
+ this.ctx.fillText(value + "", this.canvasWidth - 5, i * markerStep)
122
+ }
123
+ }
124
+
125
+ renderSpectrum(analyser: AnalyserNode) {
126
+ const freqData = new Uint8Array(analyser.frequencyBinCount)
127
+
128
+ analyser.getByteFrequencyData(freqData)
129
+
130
+ this.ctx.fillStyle = "white"
131
+ this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
132
+
133
+ this.ctx.lineWidth = 2
134
+ this.ctx.strokeStyle = "rgb(43, 156, 212)"
135
+ this.ctx.beginPath()
136
+
137
+ for (let i = 0; i < freqData.length; i++) {
138
+ const x = (Math.log(i / 1)) / (Math.log(freqData.length / 1)) * this.canvasWidth
139
+ const height = (freqData[i] * this.canvasHeight) / 256
140
+ this.ctx.lineTo(x, this.canvasHeight - height)
141
+ }
142
+ this.ctx.stroke()
143
+
144
+ const fontSize = 12
145
+
146
+ // frequencies
147
+ function explin(value: number, inMin: number, inMax: number, outMin: number, outMax: number) {
148
+ inMin = Math.max(inMin, 1)
149
+ outMin = Math.max(outMin, 1)
150
+ return Math.log10(value / inMin) / Math.log10(inMax / inMin) * (outMax - outMin) + outMin
151
+ }
152
+
153
+ const nyquist = analyser.context.sampleRate / 2;
154
+ [0, 100, 300, 1000, 3000, 10000, 20000].forEach(freq => {
155
+ const minFreq = 20
156
+ const x = freq <= 0
157
+ ? fontSize - 5
158
+ : explin(freq, minFreq, nyquist, 0, this.canvasWidth)
159
+
160
+ this.ctx.fillStyle = "black"
161
+ this.ctx.textBaseline = "middle"
162
+ this.ctx.textAlign = "right"
163
+ this.ctx.font = `${fontSize}px Courier`
164
+ this.ctx.save()
165
+ this.ctx.translate(x, this.canvasHeight - 5)
166
+ this.ctx.rotate(Math.PI * 0.5)
167
+ this.ctx.fillText(`${freq.toFixed(0)}hz`, 0, 0)
168
+ this.ctx.restore()
169
+ });
170
+
171
+ [0, -3, -6, -12].forEach(db => {
172
+ const x = 5
173
+ const amp = Math.pow(10, db * 0.05)
174
+ const y = (1 - amp) * this.canvasHeight
175
+
176
+ this.ctx.fillStyle = "black"
177
+ this.ctx.textBaseline = "top"
178
+ this.ctx.textAlign = "left"
179
+ this.ctx.font = `${fontSize}px Courier`
180
+ this.ctx.fillText(`${db.toFixed(0)}db`, x, y)
181
+ })
182
+ }
183
+
184
+ onResize() {
185
+ this.canvasWidth = 0
186
+ this.canvasHeight = 0
187
+
188
+ const rect = this.container.getBoundingClientRect()
189
+ const style = getComputedStyle(this.container)
190
+
191
+ let borderLeft = style.getPropertyValue("border-left-width")
192
+ let left = borderLeft === "" ? 0 : parseFloat(borderLeft)
193
+ let borderRight = style.getPropertyValue("border-right-width")
194
+ let right = borderRight === "" ? 0 : parseFloat(borderRight)
195
+ this.canvasWidth = rect.width - left - right
196
+
197
+ let borderTop = style.getPropertyValue("border-top-width")
198
+ let top = borderTop === "" ? 0 : parseFloat(borderTop)
199
+ let borderBottom = style.getPropertyValue("border-bottom-width")
200
+ let bottom = borderBottom === "" ? 0 : parseFloat(borderBottom)
201
+ this.canvasHeight = rect.height - top - bottom
202
+
203
+ this.ctx.scale(devicePixelRatio, devicePixelRatio)
204
+ }
205
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noFallthroughCasesInSwitch": true
21
+ },
22
+ "include": ["src"]
23
+ }
package/vite.config.js ADDED
@@ -0,0 +1,13 @@
1
+ import { resolve } from 'path'
2
+ import { defineConfig } from 'vite'
3
+
4
+ export default defineConfig({
5
+ build: {
6
+ lib: {
7
+ entry: resolve(__dirname, "src/main.ts"),
8
+ name: "faust_web_component",
9
+ formats: ["iife"],
10
+ fileName: () => "faust-web-component.js",
11
+ },
12
+ },
13
+ })