Getting Started with ArcanePad on Web
Welcome to ArcanePad! This guide will help you get started with integrating ArcanePad into your web projects.
If you haven't installed the app yet, please refer to this guide and follow the instructions to install ArcanePad desktop and mobile apps before continuing.
We are going to use Quasar.js, a framework on top of Vue.js. The reason for this is that Quasar comes with a lot of tools out of the box that are going to make our development journey a lot easier. For more information, visit quasar.dev to get started.
Things you need to have installed
- Visual Studio Code
- Node.js
- NPM
- Vue
- Quasar
Installing the ArcanePad Web SDK
bash
npm install arcanepad-web-sdk
npm install arcanepad-web-sdk
Basic Tutorial
Getting Sensors Data Tutorial
Starter Template Repo
https://github.com/imvenx/arcanepad-web-template
vue
<template>
<router-view v-if="isInitialized" />
</template>
<script setup lang="ts">
import { Arcane } from 'arcanepad-web-sdk';
import { ArcaneInitParams } from 'arcanepad-web-sdk/src/models/Models';
import { onMounted, ref } from 'vue';
const isInitialized = ref(false)
onMounted(async () => {
Arcane.init(new ArcaneInitParams({ padOrientation: 'Portrait', hideMouse: false }))
await Arcane.arcaneClientInitialized()
isInitialized.value = true
})
</script>
<template>
<router-view v-if="isInitialized" />
</template>
<script setup lang="ts">
import { Arcane } from 'arcanepad-web-sdk';
import { ArcaneInitParams } from 'arcanepad-web-sdk/src/models/Models';
import { onMounted, ref } from 'vue';
const isInitialized = ref(false)
onMounted(async () => {
Arcane.init(new ArcaneInitParams({ padOrientation: 'Portrait', hideMouse: false }))
await Arcane.arcaneClientInitialized()
isInitialized.value = true
})
</script>
vue
<template>
<h1>Viewer</h1>
<div>Is app paused: {{ isAppPaused }}</div>
<div style="display: grid; grid-template-columns: 50% 50%;">
<div v-for="pad in pads">
<Player :pad="pad" />
</div>
</div>
</template>
<script setup lang="ts">
import { AEventName, Arcane, ArcanePad, IframePadConnectEvent, IframePadDisconnectEvent } from 'arcanepad-web-sdk';
import Player from 'src/components/Player.vue';
import { Ref, onMounted, ref } from 'vue';
const pads: Ref<ArcanePad[]> = ref([])
const isAppPaused = ref(false)
onMounted(() => {
init()
})
async function init() {
let initialState = await Arcane.arcaneClientInitialized()
pads.value = initialState.pads
Arcane.msg.on(AEventName.IframePadConnect, (e: IframePadConnectEvent) => {
const padExists = pads.value.some(p => p.iframeId === e.iframeId)
if (padExists) return
const padToAdd = new ArcanePad({ deviceId: e.deviceId, internalId: e.internalId, iframeId: e.iframeId, isConnected: true, user: e.user })
pads.value.push(padToAdd)
})
Arcane.msg.on(AEventName.IframePadDisconnect, (e: IframePadDisconnectEvent) => {
const padToRemove = pads.value.find(p => p.iframeId === e.iframeId)
if (!padToRemove) return
pads.value.splice(pads.value.indexOf(padToRemove), 1)
})
Arcane.msg.on(AEventName.PauseApp, (e) => isAppPaused.value = true)
Arcane.msg.on(AEventName.ResumeApp, (e) => isAppPaused.value = false)
}
</script>
<template>
<h1>Viewer</h1>
<div>Is app paused: {{ isAppPaused }}</div>
<div style="display: grid; grid-template-columns: 50% 50%;">
<div v-for="pad in pads">
<Player :pad="pad" />
</div>
</div>
</template>
<script setup lang="ts">
import { AEventName, Arcane, ArcanePad, IframePadConnectEvent, IframePadDisconnectEvent } from 'arcanepad-web-sdk';
import Player from 'src/components/Player.vue';
import { Ref, onMounted, ref } from 'vue';
const pads: Ref<ArcanePad[]> = ref([])
const isAppPaused = ref(false)
onMounted(() => {
init()
})
async function init() {
let initialState = await Arcane.arcaneClientInitialized()
pads.value = initialState.pads
Arcane.msg.on(AEventName.IframePadConnect, (e: IframePadConnectEvent) => {
const padExists = pads.value.some(p => p.iframeId === e.iframeId)
if (padExists) return
const padToAdd = new ArcanePad({ deviceId: e.deviceId, internalId: e.internalId, iframeId: e.iframeId, isConnected: true, user: e.user })
pads.value.push(padToAdd)
})
Arcane.msg.on(AEventName.IframePadDisconnect, (e: IframePadDisconnectEvent) => {
const padToRemove = pads.value.find(p => p.iframeId === e.iframeId)
if (!padToRemove) return
pads.value.splice(pads.value.indexOf(padToRemove), 1)
})
Arcane.msg.on(AEventName.PauseApp, (e) => isAppPaused.value = true)
Arcane.msg.on(AEventName.ResumeApp, (e) => isAppPaused.value = false)
}
</script>
vue
<template>
<router-view />
</template>
<script setup lang="ts">
</script>
<template>
<router-view />
</template>
<script setup lang="ts">
</script>
ts
import { ArcaneBaseEvent } from "arcanepad-web-sdk"
export class AttackedEvent extends ArcaneBaseEvent {
damage: number
constructor(damage: number) {
super('Attacked')
this.damage = damage
}
}
import { ArcaneBaseEvent } from "arcanepad-web-sdk"
export class AttackedEvent extends ArcaneBaseEvent {
damage: number
constructor(damage: number) {
super('Attacked')
this.damage = damage
}
}
vue
<template>
<h1>Gamepad</h1>
<h5> User Name:
{{ Arcane.pad?.user?.name }}
</h5>
<q-btn @click="Arcane.msg.emitToViews(new ArcaneBaseEvent('Jump'))" size="xl" outline>Jump</q-btn>
<div>
<q-btn @click="Arcane.pad?.calibratePointer(true)" size="xl" outline>Calibrate Pointer Top Left</q-btn>
<q-btn @click="Arcane.pad?.calibratePointer(false)" size="xl" outline>Calibrate Pointer Bottom Right</q-btn>
<q-btn @click="Arcane.pad?.calibrateQuaternion()" size="xl" outline>Calibrate Quaternion</q-btn>
</div>
</template>
<script setup lang="ts">
import { Arcane, ArcaneBaseEvent } from 'arcanepad-web-sdk';
import { AttackedEvent } from 'src/models';
import { onMounted } from 'vue';
onMounted(() => {
Arcane.msg.on('Attacked', ({ damage }: AttackedEvent) => { alert('taken damage: ' + damage) })
})
</script>
<template>
<h1>Gamepad</h1>
<h5> User Name:
{{ Arcane.pad?.user?.name }}
</h5>
<q-btn @click="Arcane.msg.emitToViews(new ArcaneBaseEvent('Jump'))" size="xl" outline>Jump</q-btn>
<div>
<q-btn @click="Arcane.pad?.calibratePointer(true)" size="xl" outline>Calibrate Pointer Top Left</q-btn>
<q-btn @click="Arcane.pad?.calibratePointer(false)" size="xl" outline>Calibrate Pointer Bottom Right</q-btn>
<q-btn @click="Arcane.pad?.calibrateQuaternion()" size="xl" outline>Calibrate Quaternion</q-btn>
</div>
</template>
<script setup lang="ts">
import { Arcane, ArcaneBaseEvent } from 'arcanepad-web-sdk';
import { AttackedEvent } from 'src/models';
import { onMounted } from 'vue';
onMounted(() => {
Arcane.msg.on('Attacked', ({ damage }: AttackedEvent) => { alert('taken damage: ' + damage) })
})
</script>
vue
<template>
<div :style="`border: 2px solid #${pad.user?.color}`">
{{ pad.user?.name }}
</div>
<div>
Jump count: {{ jumpCount }}
</div>
<q-btn @click="onAttacked" size="xl" outline>Be Attacked</q-btn>
<div>
{{ pointerData.x.toFixed(0) }} |
{{ pointerData.y.toFixed(0) }}
</div>
<q-btn @click="pad.startGetPointer()" size="xl" outline>Start Get Pointer</q-btn>
<q-btn @click="pad.stopGetPointer()" size="xl" outline>Stop Get Pointer</q-btn>
<div style="width: 10px; height: 10px; border-radius: 100px; border: 2px solid red; position: absolute;"
:style="`left:${pointerData.x}%; top: ${pointerData.y}%`">
</div>
<div>
Euler Data: {{ eulerData }}
</div>
<div>
<q-btn @click="pad.startGetRotationEuler()" size="xl" outline>Start Get Euler</q-btn>
<q-btn @click="pad.stopGetRotationEuler()" size="xl" outline>Stop Get Euler</q-btn>
</div>
<div>
Quaternion Data: {{ quaternionData }}
</div>
<div>
<q-btn @click="pad.startGetQuaternion()" size="xl" outline>Start Get Quaternion</q-btn>
<q-btn @click="pad.stopGetQuaternion()" size="xl" outline>Stop Get Quaternion</q-btn>
</div>
<div>
Linear acceleration Data: {{ linearAcceleration }}
</div>
<div>
<q-btn @click="pad.startGetLinearAcceleration()" size="xl" outline>Start Get linear acceleration</q-btn>
<q-btn @click="pad.stopGetLinearAcceleration()" size="xl" outline>Stop Get linear acceleration</q-btn>
</div>
</template>
<script lang="ts" setup>
import { ArcaneBaseEvent, ArcanePad, GetLinearAccelerationEvent, GetPointerEvent, GetQuaternionEvent, GetRotationEulerEvent } from 'arcanepad-web-sdk';
import { AttackedEvent } from 'src/models';
import { onMounted, ref } from 'vue';
const { pad } = defineProps<{ pad: ArcanePad }>()
const jumpCount = ref(0)
const pointerData = ref({ x: 0, y: 0 })
const eulerData = ref({ azimuth: 0, pitch: 0, roll: 0 })
const quaternionData = ref({ x: 0, y: 0, z: 0, w: 0 })
const linearAcceleration = ref({ x: 0, y: 0, z: 0 })
onMounted(() => {
pad.on('Jump', () => jumpCount.value++)
pad.onGetPointer(({ x, y }: GetPointerEvent) => {
pointerData.value = { x, y }
})
pad.onGetRotationEuler(({ azimuth, pitch, roll }: GetRotationEulerEvent) => {
eulerData.value = { azimuth, pitch, roll }
})
pad.onGetQuaternion(({ w, x, y, z, }: GetQuaternionEvent) => {
quaternionData.value = { w, x, y, z }
})
pad.onGetLinearAcceleration(({ x, y, z, }: GetLinearAccelerationEvent) => {
linearAcceleration.value = { x, y, z }
})
})
function onAttacked() {
pad.vibrate(1000)
pad.emit(new AttackedEvent(5))
}
</script>
<template>
<div :style="`border: 2px solid #${pad.user?.color}`">
{{ pad.user?.name }}
</div>
<div>
Jump count: {{ jumpCount }}
</div>
<q-btn @click="onAttacked" size="xl" outline>Be Attacked</q-btn>
<div>
{{ pointerData.x.toFixed(0) }} |
{{ pointerData.y.toFixed(0) }}
</div>
<q-btn @click="pad.startGetPointer()" size="xl" outline>Start Get Pointer</q-btn>
<q-btn @click="pad.stopGetPointer()" size="xl" outline>Stop Get Pointer</q-btn>
<div style="width: 10px; height: 10px; border-radius: 100px; border: 2px solid red; position: absolute;"
:style="`left:${pointerData.x}%; top: ${pointerData.y}%`">
</div>
<div>
Euler Data: {{ eulerData }}
</div>
<div>
<q-btn @click="pad.startGetRotationEuler()" size="xl" outline>Start Get Euler</q-btn>
<q-btn @click="pad.stopGetRotationEuler()" size="xl" outline>Stop Get Euler</q-btn>
</div>
<div>
Quaternion Data: {{ quaternionData }}
</div>
<div>
<q-btn @click="pad.startGetQuaternion()" size="xl" outline>Start Get Quaternion</q-btn>
<q-btn @click="pad.stopGetQuaternion()" size="xl" outline>Stop Get Quaternion</q-btn>
</div>
<div>
Linear acceleration Data: {{ linearAcceleration }}
</div>
<div>
<q-btn @click="pad.startGetLinearAcceleration()" size="xl" outline>Start Get linear acceleration</q-btn>
<q-btn @click="pad.stopGetLinearAcceleration()" size="xl" outline>Stop Get linear acceleration</q-btn>
</div>
</template>
<script lang="ts" setup>
import { ArcaneBaseEvent, ArcanePad, GetLinearAccelerationEvent, GetPointerEvent, GetQuaternionEvent, GetRotationEulerEvent } from 'arcanepad-web-sdk';
import { AttackedEvent } from 'src/models';
import { onMounted, ref } from 'vue';
const { pad } = defineProps<{ pad: ArcanePad }>()
const jumpCount = ref(0)
const pointerData = ref({ x: 0, y: 0 })
const eulerData = ref({ azimuth: 0, pitch: 0, roll: 0 })
const quaternionData = ref({ x: 0, y: 0, z: 0, w: 0 })
const linearAcceleration = ref({ x: 0, y: 0, z: 0 })
onMounted(() => {
pad.on('Jump', () => jumpCount.value++)
pad.onGetPointer(({ x, y }: GetPointerEvent) => {
pointerData.value = { x, y }
})
pad.onGetRotationEuler(({ azimuth, pitch, roll }: GetRotationEulerEvent) => {
eulerData.value = { azimuth, pitch, roll }
})
pad.onGetQuaternion(({ w, x, y, z, }: GetQuaternionEvent) => {
quaternionData.value = { w, x, y, z }
})
pad.onGetLinearAcceleration(({ x, y, z, }: GetLinearAccelerationEvent) => {
linearAcceleration.value = { x, y, z }
})
})
function onAttacked() {
pad.vibrate(1000)
pad.emit(new AttackedEvent(5))
}
</script>
ts
import { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/IndexPage.vue') },
{ path: '/Pad', component: () => import('pages/PadPage.vue') },
],
},
// Always leave this as last one,
// but you can also remove it
{
path: '/:catchAll(.*)*',
component: () => import('pages/ErrorNotFound.vue'),
},
];
export default routes;
import { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/IndexPage.vue') },
{ path: '/Pad', component: () => import('pages/PadPage.vue') },
],
},
// Always leave this as last one,
// but you can also remove it
{
path: '/:catchAll(.*)*',
component: () => import('pages/ErrorNotFound.vue'),
},
];
export default routes;
js
/* eslint-env node */
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
*/
// Configuration for your app
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const { configure } = require('quasar/wrappers');
module.exports = configure(function (/* ctx */) {
return {
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
// preFetch: true,
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: [
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: [
'app.css'
],
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
// 'mdi-v5',
// 'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it
'material-icons', // optional, you are not bound to it
],
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
node: 'node16'
},
vueRouterMode: 'hash', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
// vueOptionsAPI: false,
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
// publicPath: '/',
// analyze: true,
// env: {},
// rawDefine: {}
// ignorePublicFolder: true,
// minify: false,
// polyfillModulePreload: true,
// distDir
// extendViteConf (viteConf) {},
// viteVuePluginOptions: {},
// vitePlugins: [
// [ 'package-name', { ..options.. } ]
// ]
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
devServer: {
https: true,
open: false // opens browser window automatically
},
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
framework: {
config: { dark: true },
// iconSet: 'material-icons', // Quasar icon set
// lang: 'en-US', // Quasar language pack
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),
// you can manually specify Quasar components/directives to be available everywhere:
//
// components: [],
// directives: [],
// Quasar plugins
plugins: []
},
// animations: 'all', // --- includes all animations
// https://v2.quasar.dev/options/animations
animations: [],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles
// sourceFiles: {
// rootComponent: 'src/App.vue',
// router: 'src/router/index',
// store: 'src/store/index',
// registerServiceWorker: 'src-pwa/register-service-worker',
// serviceWorker: 'src-pwa/custom-service-worker',
// pwaManifestFile: 'src-pwa/manifest.json',
// electronMain: 'src-electron/electron-main',
// electronPreload: 'src-electron/electron-preload'
// },
// https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
ssr: {
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
// will mess up SSR
// extendSSRWebserverConf (esbuildConf) {},
// extendPackageJson (json) {},
pwa: false,
// manualStoreHydration: true,
// manualPostHydrationTrigger: true,
prodPort: 3000, // The default port that the production server should use
// (gets superseded if process.env.PORT is specified at runtime)
middlewares: [
'render' // keep this as last one
]
},
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
pwa: {
workboxMode: 'generateSW', // or 'injectManifest'
injectPwaMetaTags: true,
swFilename: 'sw.js',
manifestFilename: 'manifest.json',
useCredentialsForManifestTag: false,
// useFilenameHashes: true,
// extendGenerateSWOptions (cfg) {}
// extendInjectManifestOptions (cfg) {},
// extendManifestJson (json) {}
// extendPWACustomSWConf (esbuildConf) {}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
cordova: {
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
capacitor: {
hideSplashscreen: true
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
electron: {
// extendElectronMainConf (esbuildConf)
// extendElectronPreloadConf (esbuildConf)
inspectPort: 5858,
bundler: 'packager', // 'packager' or 'builder'
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store
// appBundleId: '',
// appCategoryType: '',
// osxSign: '',
// protocol: 'myapp://path',
// Windows only
// win32metadata: { ... }
},
builder: {
// https://www.electron.build/configuration/configuration
appId: 'arcanepad-web-template'
}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
bex: {
contentScripts: [
'my-content-script'
],
// extendBexScriptsConf (esbuildConf) {}
// extendBexManifestJson (json) {}
}
}
});
/* eslint-env node */
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
*/
// Configuration for your app
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const { configure } = require('quasar/wrappers');
module.exports = configure(function (/* ctx */) {
return {
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
// preFetch: true,
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: [
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: [
'app.css'
],
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
// 'mdi-v5',
// 'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it
'material-icons', // optional, you are not bound to it
],
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
node: 'node16'
},
vueRouterMode: 'hash', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
// vueOptionsAPI: false,
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
// publicPath: '/',
// analyze: true,
// env: {},
// rawDefine: {}
// ignorePublicFolder: true,
// minify: false,
// polyfillModulePreload: true,
// distDir
// extendViteConf (viteConf) {},
// viteVuePluginOptions: {},
// vitePlugins: [
// [ 'package-name', { ..options.. } ]
// ]
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
devServer: {
https: true,
open: false // opens browser window automatically
},
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
framework: {
config: { dark: true },
// iconSet: 'material-icons', // Quasar icon set
// lang: 'en-US', // Quasar language pack
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),
// you can manually specify Quasar components/directives to be available everywhere:
//
// components: [],
// directives: [],
// Quasar plugins
plugins: []
},
// animations: 'all', // --- includes all animations
// https://v2.quasar.dev/options/animations
animations: [],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles
// sourceFiles: {
// rootComponent: 'src/App.vue',
// router: 'src/router/index',
// store: 'src/store/index',
// registerServiceWorker: 'src-pwa/register-service-worker',
// serviceWorker: 'src-pwa/custom-service-worker',
// pwaManifestFile: 'src-pwa/manifest.json',
// electronMain: 'src-electron/electron-main',
// electronPreload: 'src-electron/electron-preload'
// },
// https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
ssr: {
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
// will mess up SSR
// extendSSRWebserverConf (esbuildConf) {},
// extendPackageJson (json) {},
pwa: false,
// manualStoreHydration: true,
// manualPostHydrationTrigger: true,
prodPort: 3000, // The default port that the production server should use
// (gets superseded if process.env.PORT is specified at runtime)
middlewares: [
'render' // keep this as last one
]
},
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
pwa: {
workboxMode: 'generateSW', // or 'injectManifest'
injectPwaMetaTags: true,
swFilename: 'sw.js',
manifestFilename: 'manifest.json',
useCredentialsForManifestTag: false,
// useFilenameHashes: true,
// extendGenerateSWOptions (cfg) {}
// extendInjectManifestOptions (cfg) {},
// extendManifestJson (json) {}
// extendPWACustomSWConf (esbuildConf) {}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
cordova: {
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
capacitor: {
hideSplashscreen: true
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
electron: {
// extendElectronMainConf (esbuildConf)
// extendElectronPreloadConf (esbuildConf)
inspectPort: 5858,
bundler: 'packager', // 'packager' or 'builder'
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store
// appBundleId: '',
// appCategoryType: '',
// osxSign: '',
// protocol: 'myapp://path',
// Windows only
// win32metadata: { ... }
},
builder: {
// https://www.electron.build/configuration/configuration
appId: 'arcanepad-web-template'
}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
bex: {
contentScripts: [
'my-content-script'
],
// extendBexScriptsConf (esbuildConf) {}
// extendBexManifestJson (json) {}
}
}
});
Upload your game to Arcanepad
Go to https://dev.arcanepad.com, create an account and after you are verified you can upload your game. The app folder has to be compressed on .zip format.