diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..5a615df
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,28 @@
+{
+ "parser": "vue-eslint-parser",
+ "parserOptions": {
+ "ecmaVersion": 2020,
+ "sourceType": "module"
+ },
+ "env": {
+ "browser": true,
+ "es2020": true,
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/vue3-recommended",
+ "eslint:recommended",
+ "plugin:prettier/recommended"
+ ],
+ "plugins": ["prettier"],
+ "rules": {
+ "prettier/prettier": "error"
+ },
+ "ignorePatterns": [
+ "node_modules/*",
+ ".next/*",
+ ".out/*",
+ "!.prettierrc.js",
+ "src/external/*"
+ ]
+}
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 96f4db1..0000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,24 +0,0 @@
-module.exports = {
- extends: [
- "eslint:recommended",
- "plugin:vue/vue3-recommended",
- "plugin:prettier/recommended",
- ],
- plugins: ["prettier"],
- rules: {
- eqeqeq: "error",
- "no-console": "warn",
- "prettier/prettier": "error",
- },
- parserOptions: {
- sourceType: "module",
- ecmaVersion: "latest",
- },
- env: {
- browser: true,
- node: true,
- es6: true,
- jest: true,
- },
- ignorePatterns: ["node_modules", "build", "dist", "public"],
-};
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..36af219
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npx lint-staged
diff --git a/.lintstagedrc b/.lintstagedrc
new file mode 100644
index 0000000..3c90fa9
--- /dev/null
+++ b/.lintstagedrc
@@ -0,0 +1,4 @@
+{
+ "**/*.{js,vue}": ["eslint --fix"],
+ "**/*.{css,scss,vue}": ["stylelint --fix"]
+}
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..1ca87ab
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,3 @@
+{
+ "singleQuote": false
+}
diff --git a/.stylelintrc b/.stylelintrc
new file mode 100644
index 0000000..e9398fb
--- /dev/null
+++ b/.stylelintrc
@@ -0,0 +1,13 @@
+{
+ "extends": ["stylelint-config-idiomatic-order"],
+ "overrides": [
+ {
+ "files": ["**/*.scss"],
+ "customSyntax": "postcss-scss"
+ },
+ {
+ "files": ["**/*.vue"],
+ "customSyntax": "postcss-html"
+ }
+ ]
+}
diff --git a/README.md b/README.md
index 64f60d7..8f6618a 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,37 @@
# hydra+
-[![Deploy to Firebase Hosting on merge](https://github.com/dahegyi/hydra-plus/actions/workflows/firebase-hosting-merge.yml/badge.svg)](https://github.com/dahegyi/hydra-plus/actions/workflows/firebase-hosting-merge.yml)
+hydra+ is a double-screen visual editing environment for [hydra synth](https://github.com/hydra-synth/hydra-synth), designed for ease of use and to project live visuals without revealing any code.
-A double-screen visual editing environment for [hydra](https://github.com/hydra-synth/hydra-synth).
+### 👉 [Live demo available here](https://hydra-plus.web.app)
-[Live demo](https://hydra-plus.web.app)
+**For now, the project is only tested on Chrome on desktop.**
+
+If you have any questions, suggestions, or just want to report a bug, please use the **Issues** / **Discussions** tabs here on Github.
---
-The project uses Vue 3 with Vite.
+### usable key combos:
+
+- hide/show GUI: `Escape`
+- undo: `Ctrl/Cmd + Z`
+- redo: `Ctrl/Cmd + Y` or `Ctrl/Cmd + Shift + Z`
-To run the application, install dependencies with
+### known issues:
-```
-npm install
-```
+- initSreen() doesn't work properly in visualizer
-and run the application with
+### planned features:
-```
-npm run dev
-```
+- audio settings modal
+- transition on updates
+- hover info boxes for sources & effects
+- UI for arrays
+- MIDI integration
+- import / export configs
+- built-in extra shaders + option to add new ones from the GUI
+- [1.0] customizable colors for GUI
+- [1.0] implementation of backend server where files and configs can be saved
-or build the application with
+---
-```
-npm run build
-```
+The project uses Vue 3 with Vuex and Vite. Commands to run and build the program are left the way they are by default (`npm i`, `npm run dev`, `npm run build`).
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..1b91fbe
Binary files /dev/null and b/favicon.ico differ
diff --git a/index.html b/index.html
index 0af337c..be718e5 100644
--- a/index.html
+++ b/index.html
@@ -1,10 +1,10 @@
-
+
synth settings
+
+
-
+
-
+
diff --git a/src/components/WelcomeModal.vue b/src/components/WelcomeModal.vue
index f9ff257..29f6614 100644
--- a/src/components/WelcomeModal.vue
+++ b/src/components/WelcomeModal.vue
@@ -29,8 +29,6 @@
@@ -347,34 +315,37 @@ $darkblue: #02042c;
.playground {
position: fixed;
+ z-index: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
- z-index: 0;
+}
+
+.hidden {
+ display: none;
}
.source {
position: absolute;
display: flex;
- flex-direction: column;
width: fit-content;
- min-width: 300px;
+ min-width: 320px;
+ flex-direction: column;
padding: 1rem;
- border-radius: 10px;
- background: #22222280;
- backdrop-filter: blur(8px);
- -webkit-backdrop-filter: blur(8px);
+ border-radius: 12px;
+ backdrop-filter: blur(6px);
+ background: #222222aa;
.output-header {
- color: $darkblue;
- padding: 6px;
display: flex;
justify-content: space-between;
- background: #fff;
+ padding: 6px;
border: 1px solid $darkblue;
border-radius: 6px;
margin-bottom: 0.5rem;
+ background: #fff;
+ color: $darkblue;
cursor: move;
$iconSize: 21px;
@@ -382,8 +353,8 @@ $darkblue: #02042c;
.activate,
.delete {
position: absolute;
- height: $iconSize;
width: $iconSize;
+ height: $iconSize;
cursor: pointer;
}
@@ -391,14 +362,14 @@ $darkblue: #02042c;
right: calc(2 * $iconSize);
&:after {
- content: "";
position: absolute;
- border: 4px solid $darkblue;
- height: calc($iconSize / 3);
- width: calc($iconSize / 3);
- border-radius: 50%;
top: 25%;
left: 25%;
+ width: calc($iconSize / 3);
+ height: calc($iconSize / 3);
+ border: 4px solid $darkblue;
+ border-radius: 50%;
+ content: "";
}
&.active {
@@ -413,12 +384,12 @@ $darkblue: #02042c;
&:before,
&:after {
- content: "";
position: absolute;
- border-top: 3px solid $darkblue;
- width: 16px;
top: 50%;
left: 10%;
+ width: 16px;
+ border-top: 3px solid $darkblue;
+ content: "";
}
&:before {
@@ -432,12 +403,12 @@ $darkblue: #02042c;
}
&:not(.external) {
- $offset-top: -200%;
- $bottom-color: #38383880;
+ $offset-top: -300%;
+ $color: #ffffff;
+ $bottom-color: #38383890;
$offset-bottom: 150%;
- &:nth-child(1) {
- $color: #fff70080;
+ @mixin block-colors {
background: linear-gradient(
180deg,
$color $offset-top,
@@ -457,74 +428,51 @@ $darkblue: #02042c;
}
}
- &:nth-child(2) {
- $color: #b8f77080;
- background: linear-gradient(
- 180deg,
- $color $offset-top,
- $bottom-color $offset-bottom
- );
-
- &.focused {
- background: linear-gradient(
- 180deg,
- $color calc($offset-top / 2),
- $bottom-color calc($offset-bottom * 2)
- );
- }
+ &:nth-child(1) {
+ $color: #fff700;
+ @include block-colors();
+ }
- .output-header {
- background: $color;
- }
+ &:nth-child(2) {
+ $color: #b8f770;
+ @include block-colors();
}
&:nth-child(3) {
- $color: #3bd5f080;
- background: linear-gradient(
- 180deg,
- $color $offset-top,
- $bottom-color $offset-bottom
- );
+ $color: #3bd5f0;
+ @include block-colors();
+ }
- &.focused {
- background: linear-gradient(
- 180deg,
- $color calc($offset-top / 2),
- $bottom-color calc($offset-bottom * 2)
- );
- }
+ &:nth-child(4) {
+ $color: #ff8fec;
+ @include block-colors();
+ }
- .output-header {
- background: $color;
- }
+ &:nth-child(5) {
+ $color: #9063f3;
+ @include block-colors();
}
- &:nth-child(4) {
- $color: #ff8fec80;
- background: linear-gradient(
- 180deg,
- $color $offset-top,
- $bottom-color $offset-bottom
- );
+ &:nth-child(6) {
+ $color: #ef8c56;
+ @include block-colors();
+ }
- &.focused {
- background: linear-gradient(
- 180deg,
- $color calc($offset-top / 2),
- $bottom-color calc($offset-bottom * 2)
- );
- }
+ &:nth-child(7) {
+ $color: #4282d6;
+ @include block-colors();
+ }
- .output-header {
- background: $color;
- }
+ &:nth-child(8) {
+ $color: #ea7979;
+ @include block-colors();
}
}
&.external {
.output-header {
- color: #000;
background: #f1a3a3;
+ color: #000;
}
}
}
diff --git a/src/pages/Visualizer.vue b/src/pages/VisualizerPage.vue
similarity index 73%
rename from src/pages/Visualizer.vue
rename to src/pages/VisualizerPage.vue
index 7421300..1a05aa2 100644
--- a/src/pages/Visualizer.vue
+++ b/src/pages/VisualizerPage.vue
@@ -8,11 +8,3 @@ watch(data, () => {
if (data.value) eval(data.value);
});
-
-
-
-
diff --git a/src/store/actions.js b/src/store/actions.js
index 69d7751..20ecf9b 100644
--- a/src/store/actions.js
+++ b/src/store/actions.js
@@ -1,4 +1,27 @@
-import { deepCopy } from '../utils/object-utils'
+import { useBroadcastChannel } from "@vueuse/core";
+const { post } = useBroadcastChannel({ name: "hydra-plus-channel" });
+
+import Toastify from "toastify-js";
+
+import store from "./";
+
+import { deepCopy, flatten, flattenExternal } from "~/utils/object-utils";
+
+import {
+ MAX_NUMBER_OF_SOURCES,
+ MAX_NUMBER_OF_EXTERNALS,
+ TYPE_SRC,
+ TYPE_EXTERNAL,
+ TYPE_THREE,
+ TYPE_COMPLEX,
+} from "~/constants";
+
+export const setFocus = ({ commit, state }, payload) => {
+ // don't call mutation if focus is the same
+ if (state.focused === payload) return;
+
+ commit("setFocus", payload);
+};
export const addBlock = ({ commit }, payload) => {
commit("addBlock", payload);
@@ -8,22 +31,159 @@ export const setBlocks = ({ commit }, payload) => {
commit("setBlocks", payload);
};
+/**
+ * Adds source to the main code block or to a child block.
+ * Deep copy is needed because we want to handle the input parameters
+ * differently for different sources.
+ *
+ * @param {Object} source - source object
+ */
+export const addSource = ({ commit, state }, source) => {
+ // commit("addSource", payload);
+ const copiedObject = deepCopy(source);
+
+ if (!state.focused) {
+ if (
+ (source.type === TYPE_SRC &&
+ state.blocks.filter((block) => block.type === TYPE_SRC).length >=
+ MAX_NUMBER_OF_SOURCES) ||
+ ((source.type === TYPE_EXTERNAL || source.type === TYPE_THREE) &&
+ state.blocks.filter(
+ (block) => block.type === TYPE_EXTERNAL || TYPE_THREE,
+ ).length >= MAX_NUMBER_OF_EXTERNALS)
+ ) {
+ return;
+ }
+
+ commit("addBlock", copiedObject);
+ commit("setOutput", state.blocks.length - 1);
+
+ commit("setFocus", state.blocks[state.blocks.length - 1]);
+ } else {
+ // this isn't right
+ state.focused.blocks.push(copiedObject);
+ }
+
+ if (source.type === TYPE_EXTERNAL) {
+ const addedExternal = flattenExternal(
+ deepCopy(source),
+ state.externalSourceBlocks.length - 1,
+ );
+
+ eval(addedExternal);
+ post(addedExternal);
+ } else {
+ store.dispatch("update");
+ }
+};
+
+/**
+ * Adds effect block to the focused block as a child.
+ */
+export const addEffect = ({ commit, state }, effect) => {
+ if (!state.focused) {
+ return;
+ }
+
+ state.focused.blocks.push(deepCopy(effect));
+
+ if (effect.type === TYPE_COMPLEX) {
+ commit("setFocus", state.focused.blocks[state.focused.blocks.length - 1]);
+ }
+
+ commit("setBlocks", {
+ blocks: [...state.blocks, ...state.externalSourceBlocks],
+ });
+
+ store.dispatch("update");
+};
+
export const setBlockPosition = ({ commit }, payload) => {
commit("setBlockPosition", payload);
};
-export const deleteBlock = ({ commit }, payload) => {
+export const deleteBlock = ({ commit, state }, payload) => {
commit("deleteBlock", payload);
+
+ const { blocks, synthSettings } = state;
+
+ if (!blocks[synthSettings.output]) {
+ commit("setOutput", blocks.length - 1);
+ }
+
+ store.dispatch("update");
+};
+
+// @todo fix this mess
+export const update = ({ commit, state }) => {
+ const { blocks, externalSourceBlocks, synthSettings } = state;
+
+ let codeString = "";
+
+ if (blocks.length === 0) {
+ codeString = "hush()";
+ } else {
+ commit("setOutput", synthSettings.output || 0);
+
+ for (let i = 0; i < externalSourceBlocks.length; i++) {
+ if (externalSourceBlocks[i].name !== "initScreen") {
+ codeString += flattenExternal(externalSourceBlocks[i], i);
+ }
+ }
+
+ for (let i = 0; i < blocks.length; i++) {
+ codeString += `${flatten(blocks[i])}.out(o${i})\n`;
+ }
+
+ codeString += `window.hydra.render(o${synthSettings.output})`;
+ }
+
+ try {
+ eval(codeString);
+ commit("setCodeString", codeString);
+ } catch (error) {
+ console.error(error);
+
+ Toastify({
+ text: error,
+ duration: 4000,
+ close: true,
+ gravity: "bottom",
+ stopOnFocus: true,
+ style: {
+ background: "#b62424",
+ },
+ }).showToast();
+ }
+};
+
+export const send = ({ state }) => {
+ if (state.codeString) {
+ post(state.codeString);
+
+ localStorage.setItem(
+ "externalSourceBlocks",
+ JSON.stringify(state.externalSourceBlocks),
+ );
+ localStorage.setItem("blocks", JSON.stringify(state.blocks));
+ localStorage.setItem("synthSettings", JSON.stringify(state.synthSettings));
+ }
};
export const setSynthSettings = ({ commit }, payload) => {
commit("setSynthSettings", payload);
};
-export const setOutput = ({ commit }, payload) => {
+export const setOutput = ({ state, commit }, payload) => {
+ if (state.synthSettings.output === payload) return;
+
commit("setOutput", payload);
+
+ store.dispatch("update");
};
+// History
+
export const setHistory = ({ commit, state }) => {
const history = state.history;
@@ -31,7 +191,9 @@ export const setHistory = ({ commit, state }) => {
commit("setHistoryIndex", state.historyIndex + 1);
};
-export const setHistoryIndex = ({ commit }, payload) => {
+export const setHistoryIndex = ({ commit, state }, payload) => {
+ if (state.historyIndex === payload) return;
+
commit("setHistoryIndex", payload);
};
@@ -39,22 +201,30 @@ export const undo = ({ commit, state }) => {
const historyIndex = deepCopy(state.historyIndex);
const history = deepCopy(state.history);
- if (historyIndex === history.length - 1) {
- return;
+ if (historyIndex < history.length - 1) {
+ commit("setHistoryIndex", historyIndex + 1);
+ commit("setBlocks", {
+ blocks: [
+ ...history[historyIndex + 1].blocks,
+ ...history[historyIndex + 1].externalSourceBlocks,
+ ],
+ isUndoRedo: true,
+ });
}
-
- commit("setHistoryIndex", historyIndex + 1);
- commit("setBlocks", { blocks: [...history[historyIndex + 1].blocks, ...history[historyIndex + 1].externalSourceBlocks], isUndoRedo: true });
};
export const redo = ({ commit, state }) => {
const historyIndex = deepCopy(state.historyIndex);
const history = deepCopy(state.history);
- if (historyIndex === 0) {
- return;
+ if (historyIndex > 0) {
+ commit("setHistoryIndex", historyIndex - 1);
+ commit("setBlocks", {
+ blocks: [
+ ...history[historyIndex - 1].blocks,
+ ...history[historyIndex - 1].externalSourceBlocks,
+ ],
+ isUndoRedo: true,
+ });
}
-
- commit("setHistoryIndex", historyIndex - 1);
- commit("setBlocks", { blocks: [...history[historyIndex - 1].blocks, ...history[historyIndex - 1].externalSourceBlocks], isUndoRedo: true });
};
diff --git a/src/store/getters.js b/src/store/getters.js
index bcfc818..7ac0e56 100644
--- a/src/store/getters.js
+++ b/src/store/getters.js
@@ -1,3 +1,7 @@
+export const focused = (state) => {
+ return state.focused;
+};
+
export const blocks = (state) => {
return state.blocks;
};
diff --git a/src/store/index.js b/src/store/index.js
index 3848d40..cbf5ee1 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -6,6 +6,7 @@ import mutations from "./mutations";
import { INITIAL_BLOCKS } from "../constants";
const state = {
+ focused: null,
blocks: JSON.parse(INITIAL_BLOCKS),
externalSourceBlocks: [],
synthSettings: {
@@ -15,6 +16,7 @@ const state = {
resolution: 100,
fps: 60,
},
+ codeString: "",
history: [],
historyIndex: 0,
};
diff --git a/src/store/mutations.js b/src/store/mutations.js
index 8df6b2a..9f16b1d 100644
--- a/src/store/mutations.js
+++ b/src/store/mutations.js
@@ -1,19 +1,23 @@
-import store from './';
+import store from "./";
-import { deepCopy } from '../utils/object-utils'
+import { deepCopy } from "~/utils/object-utils";
-import { TYPE_SRC } from "../constants";
+import { DEFAULT_POSITION, TYPE_SRC, TYPE_EXTERNAL } from "~/constants";
export default {
+ // Focus
+
+ setFocus(state, focused) {
+ state.focused = focused;
+ },
+
// Blocks
addBlock(state, block) {
- const defaultPosition = { x: 20, y: 60 };
-
if (block.type === TYPE_SRC) {
- state.blocks.push({ ...block, position: defaultPosition });
+ state.blocks.push({ ...block, position: DEFAULT_POSITION });
} else {
- state.externalSourceBlocks.push({ ...block, position: defaultPosition });
+ state.externalSourceBlocks.push({ ...block, position: DEFAULT_POSITION });
}
store.commit("setHistory");
@@ -21,9 +25,11 @@ export default {
setBlocks(state, { blocks, isUndoRedo }) {
state.blocks = blocks.filter((block) => block.type === TYPE_SRC);
- state.externalSourceBlocks = blocks.filter((block) => block.type !== TYPE_SRC);
+ state.externalSourceBlocks = blocks.filter(
+ (block) => block.type === TYPE_EXTERNAL,
+ );
- store.commit("setHistory", isUndoRedo);
+ if (!isUndoRedo) store.commit("setHistory");
},
setBlockPosition(state, { index, type, position }) {
@@ -44,6 +50,10 @@ export default {
store.commit("setHistory");
},
+ setCodeString(state, codeString) {
+ state.codeString = codeString;
+ },
+
// Synth Settings
setSynthSettings(state, synthSettings) {
@@ -56,19 +66,22 @@ export default {
// History
- setHistory(state, isUndoRedo) {
+ setHistory(state) {
if (state.history.length > 99) {
state.history.pop();
}
- if (!isUndoRedo) {
- state.history.splice(0, state.historyIndex);
-
- store.commit("setHistoryIndex", 0);
+ state.history.splice(0, state.historyIndex);
- state.history = [deepCopy({ blocks: state.blocks, externalSourceBlocks: state.externalSourceBlocks }), ...state.history]
- };
+ store.commit("setHistoryIndex", 0);
+ state.history = [
+ deepCopy({
+ blocks: state.blocks,
+ externalSourceBlocks: state.externalSourceBlocks,
+ }),
+ ...state.history,
+ ];
},
setHistoryIndex(state, index) {
diff --git a/src/style.scss b/src/style.scss
index 8c0891f..c9fe572 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -1,15 +1,15 @@
:root {
+ background-color: #282828;
+ color: rgba(255, 255, 255, 0.87);
+ color-scheme: light dark;
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
- line-height: 24px;
- font-weight: 400;
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #282828;
- font-synthesis: none;
- text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+ font-synthesis: none;
+ font-weight: 400;
+ line-height: 24px;
+ text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
@@ -19,12 +19,11 @@ body {
button {
padding: 0.5rem 1rem;
- background: #444;
border: 1px solid #111;
border-radius: 0;
+ background: #444;
color: #fff;
cursor: pointer;
- transition: all 0.1s ease-in-out;
&:hover,
&:focus,
@@ -35,16 +34,16 @@ button {
input,
select {
- appearance: none;
- color: #fff;
+ padding: 0.5rem 1rem;
border: 1px solid #111;
border-radius: 0;
- padding: 0.5rem 1rem;
+ appearance: none;
background: #00000040;
+ color: #fff;
&:focus {
- outline: 1px solid #fff;
background: #00000090;
+ outline: 1px solid #fff;
}
}
@@ -60,42 +59,6 @@ hr {
width: 100%;
}
-.modal {
- position: fixed;
- display: flex;
- align-items: center;
- flex-direction: column;
- z-index: 999;
- top: 20%;
- left: calc(50% - 180px);
- width: 360px;
- background: #222222bb;
- backdrop-filter: blur(8px);
- -webkit-backdrop-filter: blur(8px);
- padding: 20px;
- border-radius: 10px;
-
- &:before {
- content: "";
- position: absolute;
- top: -20vh;
- height: 100vh;
- width: 100vw;
- left: calc(180px - 50vw);
- background: #00000050;
- z-index: -1;
- }
-
- a {
- margin-bottom: 1rem;
- color: #fff;
-
- &:hover {
- color: #fff;
- }
- }
-}
-
.param-input-container {
display: flex;
flex-direction: row;
@@ -124,3 +87,62 @@ hr {
margin-bottom: 0;
}
}
+
+.modal {
+ position: fixed;
+ z-index: 999;
+ top: 20%;
+ left: calc(50% - 190px);
+ display: flex;
+ width: 380px;
+ min-height: 200px;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-around;
+ padding: 20px 30px;
+ border-radius: 20px;
+ background: #222;
+
+ &:before {
+ position: absolute;
+ z-index: -1;
+ top: -20vh;
+ left: calc(190px - 50vw);
+ width: 100vw;
+ height: 100vh;
+ background: #00000068;
+ content: "";
+ }
+
+ h2 {
+ margin-top: 0;
+ }
+
+ .close {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ margin: 1rem;
+ cursor: pointer;
+ font-size: 2rem;
+
+ &:before {
+ content: "×";
+ }
+ }
+
+ a {
+ margin-bottom: 1rem;
+ color: #fff;
+
+ &:hover {
+ color: #fff;
+ }
+ }
+
+ button {
+ width: 120px;
+ border: 2px solid #fff;
+ margin: 10px 0 0;
+ }
+}
diff --git a/src/utils/object-utils.js b/src/utils/object-utils.js
index fae9022..a35369c 100644
--- a/src/utils/object-utils.js
+++ b/src/utils/object-utils.js
@@ -1,11 +1,13 @@
-import { TYPE_SRC, TYPE_COMPLEX } from "../constants";
+import { TYPE_SRC, TYPE_COMPLEX } from "~/constants";
export const deepCopy = (obj) => {
return JSON.parse(JSON.stringify(obj));
};
export const flattenExternal = (obj, index) => {
- return `s${index}.${obj.name}("${obj.params && obj.params[0].value}")\n`;
+ return `s${index}.${obj.name}(${
+ obj.params && obj.params[0] ? `"${obj.params[0].value}"` : ""
+ })\n`;
};
/**
diff --git a/vite.config.js b/vite.config.js
index 6ea8547..c425bf4 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,6 +1,21 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
-export default defineConfig({
- plugins: [vue()],
+export default defineConfig(({ mode }) => {
+ const config = {
+ define: {},
+ plugins: [vue()],
+ resolve: {
+ alias: {
+ "~": "/src",
+ },
+ extensions: [".js", ".vue"],
+ },
+ };
+
+ if (mode === "development") {
+ config.define.global = {};
+ }
+
+ return config;
});