diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..2d525873a0 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "jest": true + }, + "parserOptions": { + "ecmaVersion": 2019, + "sourceType": "module" + }, + "ignorePatterns": ["node_modules", "dist", "public"], + "plugins": ["prettier", "svelte3"], + "extends": ["eslint:recommended"], + "overrides": [ + { + "files": ["**/*.svelte"], + "processor": "svelte3/svelte3" + } + ], + "rules": { + "prettier/prettier": "error" + } +} \ No newline at end of file diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml new file mode 100644 index 0000000000..a9bad4639e --- /dev/null +++ b/.github/workflows/budibase_ci.yml @@ -0,0 +1,35 @@ +name: Budibase CI + +on: + # Trigger the workflow on push or pull request, + # but only for the master branch + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [10.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: yarn + - run: yarn lint + - run: yarn build + - run: yarn test + env: + CI: true + name: Budibase CI + diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..ce02997c90 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "tabWidth": 2, + "semi": false, + "singleQuote": false, + "trailingComma": "es5", + "plugins": ["prettier-plugin-svelte"] +} \ No newline at end of file diff --git a/package.json b/package.json index 6c39e60edf..57a3080fb7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,12 @@ "name": "root", "private": true, "devDependencies": { - "lerna": "^3.14.1" + "eslint": "^6.8.0", + "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-svelte3": "^2.7.3", + "lerna": "^3.14.1", + "prettier": "^1.19.1", + "prettier-plugin-svelte": "^0.7.0" }, "dependencies": {}, "scripts": { @@ -10,6 +15,9 @@ "build": "lerna run build", "initialise": "lerna run initialise", "clean": "lerna clean", - "dev": "lerna run --parallel --stream dev:builder" + "dev": "lerna run --parallel --stream dev:builder", + "test": "lerna run test", + "lint": "eslint packages", + "format": "prettier --write \"{,!(node_modules)/**/}*.{js,jsx}\"" } } diff --git a/packages/builder/package.json b/packages/builder/package.json index 5566e7e036..9595da46f6 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -6,6 +6,7 @@ "build": "rollup -c", "start": "rollup -c -w", "test": "jest", + "test:watch": "jest --watchAll", "dev:builder": "rollup -c -w" }, "jest": { diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js index b5cc54873d..67507a5cfd 100644 --- a/packages/builder/rollup.config.js +++ b/packages/builder/rollup.config.js @@ -11,34 +11,34 @@ import browsersync from "rollup-plugin-browsersync"; import proxy from "http-proxy-middleware"; const target = 'http://localhost:4001'; -const _builderProxy = proxy('/_builder', { - target:"http://localhost:3000", - pathRewrite: {'^/_builder' : ''} +const _builderProxy = proxy('/_builder', { + target: "http://localhost:3000", + pathRewrite: { '^/_builder': '' } }); -const apiProxy = proxy(['/_builder/api/**', '/_builder/**/componentlibrary', '/_builder/**/componentlibraryGenerators'] , { +const apiProxy = proxy(['/_builder/api/**', '/_builder/**/componentlibrary', '/_builder/**/componentlibraryGenerators'], { target, logLevel: "debug", changeOrigin: true, cookieDomainRewrite: true, onProxyReq(proxyReq) { - if (proxyReq.getHeader("origin")) { - proxyReq.setHeader("origin", target) - } + if (proxyReq.getHeader("origin")) { + proxyReq.setHeader("origin", target) + } } - }); +}); const production = !process.env.ROLLUP_WATCH; -const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant", -"take", "first", "intersection", "mapValues", "isNull", "has", "isInteger", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy", "isNaN", -"keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy", -"differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign", "uniq", "trimChars", "trimCharsStart", "isObjectLike", "flattenDeep", "indexOf", "isPlainObject", -"toNumber", "takeRight"]; +const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant", + "take", "first", "intersection", "mapValues", "isNull", "has", "isInteger", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy", "isNaN", + "keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy", + "differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign", "uniq", "trimChars", "trimCharsStart", "isObjectLike", "flattenDeep", "indexOf", "isPlainObject", + "toNumber", "takeRight", "toPairs"]; -const lodash_exports = ["flow", "join", "replace", "trim", "dropRight", "takeRight", "head", "reduce", -"tail", "startsWith", "findIndex", "merge", -"assign", "each", "find", "orderBy", "union"]; +const lodash_exports = ["flow", "join", "replace", "trim", "dropRight", "takeRight", "head", "reduce", + "tail", "startsWith", "findIndex", "merge", + "assign", "each", "find", "orderBy", "union"]; const outputpath = "../server/builder"; @@ -63,7 +63,7 @@ export default { { src: 'src/favicon.png', dest: outputpath }, { src: 'src/assets', dest: outputpath }, { src: 'node_modules/@budibase/client/dist/budibase-client.esm.mjs', dest: outputpath }, - ] + ] }), svelte({ @@ -80,28 +80,28 @@ export default { resolve({ browser: true, dedupe: importee => { - return importee === 'svelte' - || importee.startsWith('svelte/') - || coreExternal.includes(importee); + return importee === 'svelte' + || importee.startsWith('svelte/') + || coreExternal.includes(importee); } - + }), commonjs({ namedExports: { "lodash/fp": lodash_fp_exports, - "lodash":lodash_exports, + "lodash": lodash_exports, "shortid": ["generate"] } }), url({ - limit: 0, - include: ["**/*.woff2", "**/*.png"], + limit: 0, + include: ["**/*.woff2", "**/*.png"], fileName: "[dirname][name][extname]", emitFiles: true }), url({ - limit: 0, - include: ["**/*.css"], + limit: 0, + include: ["**/*.css"], fileName: "[name][extname]", emitFiles: true }), @@ -113,7 +113,7 @@ export default { !production && livereload(outputpath), !production && browsersync({ server: outputpath, - middleware: [apiProxy,_builderProxy] + middleware: [apiProxy, _builderProxy] }), // If we're building for production (npm run build diff --git a/packages/builder/src/builderStore/generate_css.js b/packages/builder/src/builderStore/generate_css.js new file mode 100644 index 0000000000..29ddb16245 --- /dev/null +++ b/packages/builder/src/builderStore/generate_css.js @@ -0,0 +1,118 @@ +import { filter, map, reduce, toPairs } from "lodash/fp"; +import { pipe } from "../common/core"; + +const self = n => n; +const join_with = delimiter => a => a.join(delimiter); +const empty_string_to_unset = s => s.length ? s : "0"; +const add_suffix = suffix => s => s + suffix; + +export const make_margin = (values) => pipe(values, [ + map(empty_string_to_unset), + map(add_suffix('px')), + join_with(' ') +]); + +const tap = message => x => { + console.log(x); + return x; +} + +const css_map = { + templaterows: { + name: 'grid-template-columns', + generate: self + }, + templatecolumns: { + name: 'grid-template-rows', + generate: self + }, + gridarea: { + name: 'grid-area', + generate: make_margin + }, + gap: { + name: 'grid-gap', + generate: n => `${n}px` + }, + columnstart: { + name: 'grid-column-start', + generate: self + }, + columnend: { + name: 'grid-column-end', + generate: self + }, + rowstart: { + name: 'grid-row-start', + generate: self + }, + rowend: { + name: 'grid-row-end', + generate: self + }, + padding: { + name: 'padding', + generate: make_margin + }, + margin: { + name: 'margin', + generate: make_margin + }, + zindex: { + name: 'z-index', + generate: self + } +} + +export const generate_rule = ([name, values]) => + `${css_map[name].name}: ${css_map[name].generate(values)};` + +const handle_grid = (acc, [name, value]) => { + let tmp = []; + + if (name === 'row' || name === 'column') { + if (value[0]) tmp.push([`${name}start`, value[0]]); + if (value[1]) tmp.push([`${name}end`, value[1]]); + return acc.concat(tmp) + } + + return acc.concat([[name, value]]); +} + +const object_to_css_string = [ + toPairs, + reduce(handle_grid, []), + filter(v => Array.isArray(v[1]) ? v[1].some(s => s.length) : v[1].length), + map(generate_rule), + join_with('\n'), +]; + +export const generate_css = ({ layout, position }) => { + let _layout = pipe(layout, object_to_css_string); + _layout = _layout.length ? _layout + "\ndisplay: grid;" : _layout; + + return { + layout: _layout, + position: pipe(position, object_to_css_string) + } +} + +const apply_class = (id, name, styles) => `.${name}-${id} {\n${styles}\n}`; + +export const generate_screen_css = (component_array) => { + let styles = ""; + + for (let i = 0; i < component_array.length; i += 1) { + const { _styles, _id, _children } = component_array[i]; + const { layout, position } = generate_css(_styles); + + styles += apply_class(_id, 'pos', position) + "\n"; + styles += apply_class(_id, 'lay', layout) + "\n"; + + if (_children && _children.length) { + styles += generate_screen_css(_children) + "\n"; + } + + } + return styles.trim(); +} diff --git a/packages/builder/src/builderStore/store.js b/packages/builder/src/builderStore/store.js index 0975671073..c9dc3da87b 100644 --- a/packages/builder/src/builderStore/store.js +++ b/packages/builder/src/builderStore/store.js @@ -4,7 +4,7 @@ import { import { filter, cloneDeep, sortBy, map, last, keys, concat, keyBy, - find, isEmpty, reduce, values, isEqual + find, isEmpty, values, } from "lodash/fp"; import { pipe, getNode, validate, @@ -17,11 +17,13 @@ import api from "./api"; import { isRootComponent, getExactComponent } from "../userInterface/pagesParsing/searchComponents"; import { rename } from "../userInterface/pagesParsing/renameScreen"; import { - getNewComponentInfo, getScreenInfo, getComponentInfo + getNewComponentInfo, getScreenInfo, } from "../userInterface/pagesParsing/createProps"; import { loadLibs, loadLibUrls, loadGeneratorLibs } from "./loadComponentLibraries"; +import { uuid } from './uuid'; +import { generate_screen_css } from './generate_css'; let appname = ""; @@ -710,7 +712,8 @@ const addChildComponent = store => component => { const component_definition = Object.assign( cloneDeep(newComponent.fullProps), { _component: component, - _layout: {} + _styles: { position: {}, layout: {} }, + _id: uuid() }) if (children) { @@ -729,6 +732,8 @@ const addChildComponent = store => component => { _saveScreen(store, s, s.currentFrontEndItem); + _saveScreen(store, s, s.currentFrontEndItem); + return s; }) } @@ -751,12 +756,13 @@ const setComponentProp = store => (name, value) => { }) } -const setComponentStyle = store => (name, value) => { +const setComponentStyle = store => (type, name, value) => { store.update(s => { - if (!s.currentComponentInfo._layout) { - s.currentComponentInfo._layout = {}; + if (!s.currentComponentInfo._styles) { + s.currentComponentInfo._styles = {}; } - s.currentComponentInfo._layout[name] = value; + s.currentComponentInfo._styles[type][name] = value; + s.currentFrontEndItem._css = generate_screen_css(s.currentFrontEndItem.props._children) // save without messing with the store _save(s.appname, s.currentFrontEndItem, store, s) diff --git a/packages/builder/src/builderStore/uuid.js b/packages/builder/src/builderStore/uuid.js new file mode 100644 index 0000000000..79ad68af94 --- /dev/null +++ b/packages/builder/src/builderStore/uuid.js @@ -0,0 +1,7 @@ +export function uuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0, + v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} diff --git a/packages/builder/src/common/ActionButton.svelte b/packages/builder/src/common/ActionButton.svelte new file mode 100644 index 0000000000..fca74c09ff --- /dev/null +++ b/packages/builder/src/common/ActionButton.svelte @@ -0,0 +1,53 @@ + + + + + diff --git a/packages/builder/src/common/Icons/Events.svelte b/packages/builder/src/common/Icons/Events.svelte new file mode 100644 index 0000000000..b7d92481e3 --- /dev/null +++ b/packages/builder/src/common/Icons/Events.svelte @@ -0,0 +1,9 @@ + + + + diff --git a/packages/builder/src/common/Icons/Pencil.svelte b/packages/builder/src/common/Icons/Pencil.svelte new file mode 100644 index 0000000000..266458a132 --- /dev/null +++ b/packages/builder/src/common/Icons/Pencil.svelte @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/packages/builder/src/common/Icons/index.js b/packages/builder/src/common/Icons/index.js index 2e2ffc2010..c00ebb3ce4 100644 --- a/packages/builder/src/common/Icons/index.js +++ b/packages/builder/src/common/Icons/index.js @@ -4,3 +4,5 @@ export { default as TerminalIcon } from './Terminal.svelte'; export { default as InputIcon } from './Input.svelte'; export { default as ImageIcon } from './Image.svelte'; export { default as ArrowDownIcon } from './ArrowDown.svelte'; +export { default as EventsIcon } from './Events.svelte'; +export { default as PencilIcon } from './Pencil.svelte'; diff --git a/packages/builder/src/common/Input.svelte b/packages/builder/src/common/Input.svelte new file mode 100644 index 0000000000..04938b670a --- /dev/null +++ b/packages/builder/src/common/Input.svelte @@ -0,0 +1,27 @@ + + + + + diff --git a/packages/builder/src/common/Inputs/InputGroup.svelte b/packages/builder/src/common/Inputs/InputGroup.svelte index 55bede83f2..22a272aec4 100644 --- a/packages/builder/src/common/Inputs/InputGroup.svelte +++ b/packages/builder/src/common/Inputs/InputGroup.svelte @@ -2,6 +2,7 @@ export let meta = []; export let size = ''; export let values = []; + export let type = "number"; export let onStyleChanged = () => {}; let _values = values.map(v => v); @@ -11,7 +12,7 @@
{#each meta as { placeholder }, i} - _values[i] = e.target.value} /> diff --git a/packages/builder/src/common/Modal.svelte b/packages/builder/src/common/Modal.svelte index 0f47d553a0..5d695d0fc1 100644 --- a/packages/builder/src/common/Modal.svelte +++ b/packages/builder/src/common/Modal.svelte @@ -1,41 +1,44 @@ -
-
- -
-
- - -.uk-modal-dialog { - border-radius: .3rem; -} - - \ No newline at end of file +
+
+ {#if onClosed} +
+
diff --git a/packages/builder/src/common/PlusButton.svelte b/packages/builder/src/common/PlusButton.svelte new file mode 100644 index 0000000000..7424ffa66e --- /dev/null +++ b/packages/builder/src/common/PlusButton.svelte @@ -0,0 +1,23 @@ + + + diff --git a/packages/builder/src/common/Select.svelte b/packages/builder/src/common/Select.svelte new file mode 100644 index 0000000000..bebad32c38 --- /dev/null +++ b/packages/builder/src/common/Select.svelte @@ -0,0 +1,55 @@ + + + + +
+ + + {@html getIcon('chevron-down', '24')} + +
diff --git a/packages/builder/src/userInterface/ComponentPanel.svelte b/packages/builder/src/userInterface/ComponentPanel.svelte index 5649820970..d4d982f2e2 100644 --- a/packages/builder/src/userInterface/ComponentPanel.svelte +++ b/packages/builder/src/userInterface/ComponentPanel.svelte @@ -2,9 +2,10 @@ import PropsView from "./PropsView.svelte"; import { store } from "../builderStore"; import IconButton from "../common/IconButton.svelte"; - import { LayoutIcon, PaintIcon, TerminalIcon } from '../common/Icons/'; + import { LayoutIcon, PaintIcon, TerminalIcon, EventsIcon } from '../common/Icons/'; import CodeEditor from './CodeEditor.svelte'; import LayoutEditor from './LayoutEditor.svelte'; + import EventsEditor from "./EventsEditor"; let current_view = 'props'; @@ -17,6 +18,7 @@ const onPropChanged = store.setComponentProp; const onStyleChanged = store.setComponentStyle; +
@@ -36,6 +38,11 @@ +
  • + +
  • {#if !componentInfo.component} @@ -45,6 +52,8 @@ {:else if current_view === 'layout'} + {:else if current_view === 'events'} + {:else} {/if} diff --git a/packages/builder/src/userInterface/ComponentsHierarchyChildren.svelte b/packages/builder/src/userInterface/ComponentsHierarchyChildren.svelte index 4211f05b41..433f17641e 100644 --- a/packages/builder/src/userInterface/ComponentsHierarchyChildren.svelte +++ b/packages/builder/src/userInterface/ComponentsHierarchyChildren.svelte @@ -1,13 +1,10 @@ - -
    -
    - {#each events as ev, index} - -
    - - -
    - -
    - {/each} - -
    - -
    -
    -
    - - - diff --git a/packages/builder/src/userInterface/EventSelector.svelte b/packages/builder/src/userInterface/EventSelector.svelte deleted file mode 100644 index e109d8122d..0000000000 --- a/packages/builder/src/userInterface/EventSelector.svelte +++ /dev/null @@ -1,105 +0,0 @@ - - -
    - - - - -
    - -{#if parameters} - {#each parameters as p, index} - -
    - {p.name} -
    - - - {/each} -{/if} - - diff --git a/packages/builder/src/userInterface/EventsEditor/EventEditorModal.svelte b/packages/builder/src/userInterface/EventsEditor/EventEditorModal.svelte new file mode 100644 index 0000000000..290381c5dc --- /dev/null +++ b/packages/builder/src/userInterface/EventsEditor/EventEditorModal.svelte @@ -0,0 +1,164 @@ + + + + + +

    + {eventData.name ? `${eventData.name} Event` : 'Create a New Component Event'} +

    + + Click here to learn more about component events + + +
    +
    +
    +
    Event Type
    + {@html getIcon('info', 20)} +
    + +
    +
    + +
    +
    Event Action(s)
    + {@html getIcon('info', 20)} +
    + { + createNewEventHandler(draftEventHandler); + draftEventHandler = { parameters: [] }; + }} + handler={draftEventHandler} /> + {#if eventData} + {#each eventData.handlers as handler, index} + deleteEventHandler(index)} + {handler} /> + {/each} + {/if} + +
    + + + Save + +
    +
    diff --git a/packages/builder/src/userInterface/EventsEditor/EventsEditor.svelte b/packages/builder/src/userInterface/EventsEditor/EventsEditor.svelte new file mode 100644 index 0000000000..fa9907e077 --- /dev/null +++ b/packages/builder/src/userInterface/EventsEditor/EventsEditor.svelte @@ -0,0 +1,152 @@ + + + + +
    +

    Events

    + openModal()} /> +
    + +
    +
    + {#each events as event, index} + {#if event.handlers.length > 0} +
    openModal({ ...event, index })}> + {event.name} + EDIT +
    + {/if} + {/each} +
    +
    + diff --git a/packages/builder/src/userInterface/EventsEditor/HandlerSelector.svelte b/packages/builder/src/userInterface/EventsEditor/HandlerSelector.svelte new file mode 100644 index 0000000000..e22a992fe7 --- /dev/null +++ b/packages/builder/src/userInterface/EventsEditor/HandlerSelector.svelte @@ -0,0 +1,148 @@ + + + + +
    +
    +
    + Action + +
    + {#if parameters} + {#each parameters as param, idx} +
    + {param.name} + +
    + {/each} + {/if} +
    +
    + {#if parameters.length > 0} + {#if newHandler} + + {:else} + + {/if} + {/if} +
    +
    diff --git a/packages/builder/src/userInterface/EventsEditor/index.js b/packages/builder/src/userInterface/EventsEditor/index.js new file mode 100644 index 0000000000..7782e7fccd --- /dev/null +++ b/packages/builder/src/userInterface/EventsEditor/index.js @@ -0,0 +1 @@ +export { default } from "./EventsEditor.svelte"; \ No newline at end of file diff --git a/packages/builder/src/userInterface/LayoutEditor.svelte b/packages/builder/src/userInterface/LayoutEditor.svelte index 6e58551d22..b418a7061e 100644 --- a/packages/builder/src/userInterface/LayoutEditor.svelte +++ b/packages/builder/src/userInterface/LayoutEditor.svelte @@ -19,13 +19,16 @@ const single = [{ placeholder: '' }]; - $: layout = componentInfo._layout; + $: layout = { ...componentInfo._styles.position, ...componentInfo._styles.layout }; + + $: layouts = { + templaterows: ['Grid Rows', single], + templatecolumns: ['Grid Columns', single], + }; $: positions = { - gridarea: ['Grid Area', tbrl, 'small'], column: ['Column', se], row: ['Row', se], - gap: ['Gap', single], }; $: spacing = { @@ -41,15 +44,28 @@ -

    Layout

    +

    Styles

    Positioning

    +
    + {#each Object.entries(layouts) as [key, [name, meta, size]]} +
    +
    {name}:
    + onStyleChanged('layout',key, _value)} + values={layout[key] || newValue(meta.length)} + {meta} + {size} + type="text"/> +
    + {/each} +
    +

    Positioning

    {#each Object.entries(positions) as [key, [name, meta, size]]}
    -
    Grid Area:
    - onStyleChanged(key, _value)} +
    {name}:
    + onStyleChanged('position',key, _value)} values={layout[key] || newValue(meta.length)} {meta} {size} /> @@ -61,8 +77,8 @@
    {#each Object.entries(spacing) as [key, [name, meta, size]]}
    -
    Grid Area:
    - onStyleChanged(key, _value)} +
    {name}:
    + onStyleChanged('position', key, _value)} values={layout[key] || newValue(meta.length)} {meta} {size} /> @@ -74,8 +90,8 @@
    {#each Object.entries(zindex) as [key, [name, meta, size]]}
    -
    Grid Area:
    - onStyleChanged(key, _value)} +
    {name}:
    + onStyleChanged('position', key, _value)} values={layout[key] || newValue(meta.length)} {meta} {size} /> diff --git a/packages/builder/src/userInterface/PropsView.svelte b/packages/builder/src/userInterface/PropsView.svelte index 4e2a93beea..f0ff5d2fdc 100644 --- a/packages/builder/src/userInterface/PropsView.svelte +++ b/packages/builder/src/userInterface/PropsView.svelte @@ -12,7 +12,7 @@ let errors = []; let props = {}; - const props_to_ignore = ['_component','_children', '_layout']; + const props_to_ignore = ['_component','_children', '_styles', '_id']; $: propDefs = componentInfo && Object.entries(componentInfo).filter(([name])=> !props_to_ignore.includes(name)); diff --git a/packages/builder/src/userInterface/StateBindingControl.svelte b/packages/builder/src/userInterface/StateBindingControl.svelte index 32065a111e..76f81f2bc8 100644 --- a/packages/builder/src/userInterface/StateBindingControl.svelte +++ b/packages/builder/src/userInterface/StateBindingControl.svelte @@ -1,5 +1,6 @@