mirror of
https://github.com/Macrame-App/Macrame
synced 2025-12-29 07:19:26 +00:00
Panels V1: basic panel overview, basic binding of macros to buttons, basic panel-view.
This commit is contained in:
parent
45d3135aa9
commit
887080efa6
9 changed files with 538 additions and 111 deletions
|
|
@ -1,59 +0,0 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { appUrl } from '@/services/ApiService'
|
||||
import axios from 'axios'
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue'
|
||||
|
||||
const panel = reactive({
|
||||
style: '',
|
||||
styleEl: null,
|
||||
html: '',
|
||||
})
|
||||
|
||||
const testPanel = ref()
|
||||
|
||||
onMounted(() => {
|
||||
axios.post(appUrl() + '/panel/get').then(async (data) => {
|
||||
// console.log(data.data.html)
|
||||
if (data.data) {
|
||||
setPanelStyle(data.data.css)
|
||||
panel.html = data.data.html
|
||||
|
||||
await nextTick()
|
||||
|
||||
addButtonEventListeners()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const setPanelStyle = (styleStr) => {
|
||||
const styleEl = document.createElement('style')
|
||||
styleEl.setAttribute('custom_panel_style', true)
|
||||
styleEl.innerHTML = styleStr
|
||||
document.head.appendChild(styleEl)
|
||||
|
||||
panel.styleEl = styleEl
|
||||
}
|
||||
|
||||
const addButtonEventListeners = () => {
|
||||
testPanel.value.querySelectorAll('[mcrm__button]').forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
console.log(button.id)
|
||||
if (button.id == 'button_1') {
|
||||
axios.post(appUrl() + '/macro/play', { macro: 'task_manager' })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@reference "@/assets/main.css";
|
||||
|
||||
[mcrm__button] {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
</style>
|
||||
236
fe/src/components/panels/PanelEdit.vue
Normal file
236
fe/src/components/panels/PanelEdit.vue
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
<template>
|
||||
<div id="panel-edit" class="mcrm-block block__dark !p-0 !gap-0" v-if="editPanel">
|
||||
<div class="panel-preview">
|
||||
<div class="panel-preview__content" ref="panelPreview" v-html="editPanel.html"></div>
|
||||
</div>
|
||||
<div class="panel-settings">
|
||||
<AccordionComp title="Panel info" ref="infoAccordion">
|
||||
<div class="grid grid-cols-[auto_1fr] gap-2 p-4">
|
||||
<span>Name:</span><strong class="text-right">{{ editPanel.name }}</strong>
|
||||
|
||||
<span>Aspect ratio:</span><strong class="text-right">{{ editPanel.aspectRatio }}</strong>
|
||||
|
||||
<template v-if="editPanel.macros">
|
||||
<span>Linked Macros:</span>
|
||||
<strong class="text-right">{{ Object.keys(editPanel.macros).length }}</strong>
|
||||
</template>
|
||||
</div>
|
||||
</AccordionComp>
|
||||
<div>
|
||||
<AccordionComp
|
||||
v-if="editButton.id"
|
||||
title="Button"
|
||||
ref="buttonAccordion"
|
||||
:open="editButton.id != ''"
|
||||
>
|
||||
<div class="grid gap-4 p-4">
|
||||
<div class="grid grid-cols-[auto_1fr] gap-2">
|
||||
<span>Button ID:</span>
|
||||
<strong class="text-right">{{ editButton.id }}</strong>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<FormSelect
|
||||
name="button_macro"
|
||||
label="Button macro"
|
||||
:search="true"
|
||||
:options="macroList"
|
||||
:value="editButton.macro"
|
||||
@change="checkNewMacro(editButton.id, $event)"
|
||||
/>
|
||||
<div class="grid grid-cols-2 mt-4">
|
||||
<ButtonComp
|
||||
v-if="editButton.macro != ''"
|
||||
class="col-start-1 w-fit"
|
||||
size="sm"
|
||||
variant="danger"
|
||||
@click="unlinkMacro(editButton.id)"
|
||||
ref="unlinkButton"
|
||||
>
|
||||
<IconTrash /> Unlink
|
||||
</ButtonComp>
|
||||
<ButtonComp
|
||||
v-if="editButton.changed"
|
||||
class="col-start-2 w-fit justify-self-end"
|
||||
size="sm"
|
||||
variant="primary"
|
||||
@click="linkMacro(editButton.id)"
|
||||
ref="linkButton"
|
||||
>
|
||||
<IconLink /> Link
|
||||
</ButtonComp>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionComp>
|
||||
</div>
|
||||
<footer class="flex items-end justify-end h-full p-4">
|
||||
<ButtonComp v-if="panelMacros.changed" variant="success" @click="savePanelChanges()">
|
||||
<IconDeviceFloppy /> Save changes
|
||||
</ButtonComp>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { CheckMacroListChange, GetMacroList } from '@/services/MacroService'
|
||||
import {
|
||||
PanelButtonListeners,
|
||||
RemovePanelStyle,
|
||||
SetPanelStyle,
|
||||
StripPanelHTML,
|
||||
} from '@/services/PanelService'
|
||||
import { usePanelStore } from '@/stores/panel'
|
||||
import { onMounted, onUnmounted, onUpdated, reactive, ref } from 'vue'
|
||||
import AccordionComp from '../base/AccordionComp.vue'
|
||||
import FormSelect from '../form/FormSelect.vue'
|
||||
import ButtonComp from '../base/ButtonComp.vue'
|
||||
import { IconDeviceFloppy, IconLink, IconTrash } from '@tabler/icons-vue'
|
||||
import axios from 'axios'
|
||||
import { appUrl } from '@/services/ApiService'
|
||||
|
||||
const props = defineProps({
|
||||
dirname: String,
|
||||
})
|
||||
|
||||
const panel = usePanelStore()
|
||||
|
||||
const panelPreview = ref(false)
|
||||
const editPanel = ref({})
|
||||
const panelMacros = reactive({
|
||||
old: {},
|
||||
changed: false,
|
||||
})
|
||||
|
||||
const macroList = ref({})
|
||||
|
||||
const infoAccordion = ref(false)
|
||||
const buttonAccordion = ref(false)
|
||||
|
||||
const unlinkButton = ref(null)
|
||||
const linkButton = ref(null)
|
||||
|
||||
const editButton = reactive({
|
||||
id: '',
|
||||
macro: '',
|
||||
newMacro: '',
|
||||
changed: false,
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const currentPanel = await panel.get(props.dirname)
|
||||
editPanel.value = currentPanel
|
||||
editPanel.value.dir = props.dirname
|
||||
editPanel.value.html = StripPanelHTML(editPanel.value.html, editPanel.value.aspectRatio)
|
||||
|
||||
panelMacros.old = JSON.stringify(currentPanel.macros)
|
||||
|
||||
infoAccordion.value.toggleAccordion(true)
|
||||
|
||||
const macros = await GetMacroList()
|
||||
macroList.value = Object.assign(
|
||||
{},
|
||||
...Object.keys(macros).map((key) => ({
|
||||
[key]: { value: macros[key].macroname, label: macros[key].name },
|
||||
})),
|
||||
)
|
||||
|
||||
SetPanelStyle(editPanel.value.style)
|
||||
|
||||
EditButtonListeners()
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
console.log('updated')
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
RemovePanelStyle()
|
||||
})
|
||||
|
||||
function EditButtonListeners() {
|
||||
const callback = (button) => {
|
||||
infoAccordion.value.toggleAccordion(false)
|
||||
setEditButton(button.id)
|
||||
}
|
||||
|
||||
PanelButtonListeners(panelPreview.value, callback)
|
||||
}
|
||||
|
||||
function setEditButton(id) {
|
||||
editButton.id = id
|
||||
editButton.macro = editPanel.value.macros[id] ? editPanel.value.macros[id] : ''
|
||||
}
|
||||
|
||||
function checkNewMacro(id, macro) {
|
||||
editButton.changed = editPanel.value.macros[id] != macro
|
||||
editButton.newMacro = macro
|
||||
}
|
||||
|
||||
function linkMacro(id) {
|
||||
editPanel.value.macros[id] = editButton.newMacro
|
||||
editButton.macro = editButton.newMacro
|
||||
editButton.newMacro = ''
|
||||
|
||||
panelMacros.changed = CheckMacroListChange(panelMacros.old, editPanel.value.macros)
|
||||
}
|
||||
|
||||
function unlinkMacro(id) {
|
||||
delete editPanel.value.macros[id]
|
||||
buttonAccordion.value.toggleAccordion(false)
|
||||
panelMacros.changed = CheckMacroListChange(panelMacros.old, editPanel.value.macros)
|
||||
}
|
||||
|
||||
function savePanelChanges() {
|
||||
const panelData = {
|
||||
dir: editPanel.value.dir,
|
||||
name: editPanel.value.name,
|
||||
description: editPanel.value.description,
|
||||
aspectRatio: editPanel.value.aspectRatio,
|
||||
macros: editPanel.value.macros,
|
||||
}
|
||||
|
||||
axios.post(appUrl() + '/panel/save/json', panelData).then((data) => {
|
||||
console.log(data)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@reference "@/assets/main.css";
|
||||
|
||||
[mcrm__button] {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
#panel-edit {
|
||||
@apply grid
|
||||
grid-cols-[1fr_30ch]
|
||||
size-full
|
||||
overflow-hidden;
|
||||
|
||||
.panel-preview {
|
||||
@apply border-r
|
||||
border-slate-700;
|
||||
|
||||
.panel-preview__content {
|
||||
@apply relative
|
||||
grid
|
||||
justify-center
|
||||
size-full
|
||||
p-8;
|
||||
|
||||
#panel-html__body {
|
||||
@apply size-full
|
||||
max-w-full max-h-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-settings {
|
||||
@apply grid
|
||||
grid-rows-[auto_auto_1fr]
|
||||
bg-black/30;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
74
fe/src/components/panels/PanelView.vue
Normal file
74
fe/src/components/panels/PanelView.vue
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div id="panel-view">
|
||||
<div class="panel-preview__content" ref="panelView" v-html="viewPanel.html"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { RunMacro } from '@/services/MacroService'
|
||||
import {
|
||||
PanelButtonListeners,
|
||||
RemovePanelStyle,
|
||||
SetPanelStyle,
|
||||
StripPanelHTML,
|
||||
} from '@/services/PanelService'
|
||||
import { usePanelStore } from '@/stores/panel'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const panel = usePanelStore()
|
||||
|
||||
const props = defineProps({
|
||||
dirname: String,
|
||||
})
|
||||
|
||||
const panelView = ref(null)
|
||||
|
||||
const viewPanel = ref({})
|
||||
|
||||
onMounted(async () => {
|
||||
const currentPanel = await panel.get(props.dirname)
|
||||
viewPanel.value = currentPanel
|
||||
|
||||
viewPanel.value.html = StripPanelHTML(viewPanel.value.html, viewPanel.value.aspectRatio)
|
||||
SetPanelStyle(viewPanel.value.style)
|
||||
|
||||
setTimeout(() => {
|
||||
viewPanelButtonListeners()
|
||||
}, 50)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
RemovePanelStyle()
|
||||
})
|
||||
|
||||
const viewPanelButtonListeners = () => {
|
||||
const callback = (button) => {
|
||||
RunMacro(viewPanel.value.macros[button.id])
|
||||
}
|
||||
|
||||
PanelButtonListeners(panelView.value, callback)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@reference "@/assets/main.css";
|
||||
|
||||
#panel-view {
|
||||
@apply fixed
|
||||
inset-0
|
||||
size-full
|
||||
bg-black;
|
||||
|
||||
.panel-preview__content {
|
||||
@apply relative
|
||||
grid
|
||||
justify-center
|
||||
size-full;
|
||||
|
||||
#panel-html__body {
|
||||
@apply size-full
|
||||
max-w-full max-h-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,15 +1,35 @@
|
|||
<template>
|
||||
<div id="panels-overview">
|
||||
<!-- <AlertComp v-if="Object.keys(panels.list).length == 0" type="info">No panels found</AlertComp> -->
|
||||
<AlertComp v-if="Object.keys(panels.list).length == 0" variant="info">
|
||||
No panels found
|
||||
</AlertComp>
|
||||
<div class="panel-list">
|
||||
<div class="panel-item" v-for="(panel, i) in panels.list" :key="i">
|
||||
<!-- <router-link :to="'/panel/' + panel.id"> -->
|
||||
<div class="panel-item__content">
|
||||
<img :src="panel.thumb" alt="" />
|
||||
<div class="panel-item mcrm-block block__dark" v-for="(panel, i) in panels.list" :key="i">
|
||||
<div class="panel-item__content" @click="panelItemClick(panel.dir)">
|
||||
<div class="thumb">
|
||||
<img v-if="panel.thumb" :src="`data:image/jpeg;base64,${panel.thumb}`" alt="" />
|
||||
<IconLayoutGrid v-else />
|
||||
</div>
|
||||
<h4>{{ panel.name }}</h4>
|
||||
<p>{{ panel.description }}</p>
|
||||
<div class="description" v-if="isLocal()">
|
||||
<div class="content">
|
||||
<strong class="block mb-1 text-slate-400">{{ panel.name }}</strong>
|
||||
<hr class="mb-2 border-slate-600" />
|
||||
<p v-if="panel.description != 'null'" class="text-slate-200">
|
||||
{{ panel.description }}
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<ButtonComp variant="subtle" size="sm" :href="`/panel/view/${panel.dir}`">
|
||||
<IconEye /> Preview
|
||||
</ButtonComp>
|
||||
<ButtonComp variant="primary" size="sm" :href="`/panel/edit/${panel.dir}`">
|
||||
<IconPencil /> Edit
|
||||
</ButtonComp>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </router-link> -->
|
||||
<template v-if="!isLocal()"> </template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -19,6 +39,10 @@
|
|||
import { usePanelStore } from '@/stores/panel'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import AlertComp from '../base/AlertComp.vue'
|
||||
import { IconEye, IconLayoutGrid, IconPencil } from '@tabler/icons-vue'
|
||||
import ButtonComp from '../base/ButtonComp.vue'
|
||||
import { isLocal } from '@/services/ApiService'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const panel = usePanelStore()
|
||||
|
||||
|
|
@ -26,12 +50,20 @@ const panels = reactive({
|
|||
list: {},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(async () => {
|
||||
const panelList = await panel.getPanels()
|
||||
console.log(panelList)
|
||||
const panelList = await panel.getList()
|
||||
// console.log(panelList)
|
||||
|
||||
panels.list = panelList
|
||||
})
|
||||
|
||||
function panelItemClick(dir) {
|
||||
if (isLocal()) return
|
||||
|
||||
router.push(`/panel/view/${dir}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
@ -43,6 +75,84 @@ onMounted(async () => {
|
|||
md:grid-cols-4
|
||||
lg:grid-cols-6
|
||||
gap-4
|
||||
size-full;
|
||||
w-full h-fit;
|
||||
}
|
||||
|
||||
.panel-item {
|
||||
@apply p-px
|
||||
overflow-hidden;
|
||||
|
||||
.thumb {
|
||||
@apply flex
|
||||
justify-center
|
||||
items-center
|
||||
w-full
|
||||
aspect-[4/3];
|
||||
|
||||
&:not(:has(img)) {
|
||||
@apply bg-sky-950;
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply size-12;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply px-4 py-2
|
||||
h-12
|
||||
truncate;
|
||||
}
|
||||
|
||||
&:hover .description {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.description {
|
||||
@apply absolute
|
||||
inset-0
|
||||
size-full
|
||||
pt-2
|
||||
pr-1
|
||||
pb-13
|
||||
bg-slate-900/60
|
||||
backdrop-blur-md
|
||||
text-slate-100
|
||||
opacity-0
|
||||
transition-opacity
|
||||
cursor-default
|
||||
z-10;
|
||||
|
||||
.content {
|
||||
@apply h-full
|
||||
p-4
|
||||
pt-2
|
||||
overflow-y-auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
@apply absolute
|
||||
bottom-0 left-0
|
||||
w-full
|
||||
h-12
|
||||
grid
|
||||
grid-cols-2
|
||||
bg-slate-900
|
||||
border-t
|
||||
border-slate-600;
|
||||
|
||||
.btn {
|
||||
@apply size-full
|
||||
rounded-none
|
||||
justify-center
|
||||
border-0;
|
||||
|
||||
&:last-child {
|
||||
@apply border-l
|
||||
border-slate-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
36
fe/src/services/PanelService.js
Normal file
36
fe/src/services/PanelService.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export const SetPanelStyle = (styleStr) => {
|
||||
const styleEl = document.createElement('style')
|
||||
styleEl.setAttribute('custom_panel_style', true)
|
||||
styleEl.innerHTML = styleStr
|
||||
document.head.appendChild(styleEl)
|
||||
}
|
||||
|
||||
export const RemovePanelStyle = () => {
|
||||
const styleEl = document.querySelector('style[custom_panel_style]')
|
||||
if (styleEl) {
|
||||
styleEl.remove()
|
||||
}
|
||||
}
|
||||
|
||||
export const StripPanelHTML = (html, aspectRatio) => {
|
||||
const parser = new DOMParser()
|
||||
const doc = parser.parseFromString(html, 'text/html')
|
||||
|
||||
const body = doc.body
|
||||
const bodyContents = body.innerHTML
|
||||
|
||||
const panelBody = document.createElement('div')
|
||||
panelBody.id = 'panel-html__body'
|
||||
panelBody.style = `aspect-ratio: ${aspectRatio}`
|
||||
panelBody.innerHTML = bodyContents
|
||||
|
||||
return panelBody.outerHTML
|
||||
}
|
||||
|
||||
export const PanelButtonListeners = (panelEl, callback) => {
|
||||
panelEl.querySelectorAll('[mcrm__button]').forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
callback(button)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -1,19 +1,62 @@
|
|||
<template>
|
||||
<div id="macros" class="panel">
|
||||
<h1 class="panel__title">
|
||||
Panels <span class="text-sm">{{ isLocal() ? 'remote' : 'servers' }}</span>
|
||||
</h1>
|
||||
<div class="panel__content !p-0">
|
||||
<div class="macro-panel__content">
|
||||
<PanelsOverview />
|
||||
<div id="panels" class="panel">
|
||||
<h1 class="flex items-end justify-between !w-full panel__title">
|
||||
<div>
|
||||
Panels
|
||||
<span class="text-sm">{{ isLocal() ? 'remote' : 'servers' }}</span>
|
||||
</div>
|
||||
<ButtonComp
|
||||
v-if="panel.function != 'overview'"
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
@click="router.push('/panels')"
|
||||
>
|
||||
<IconArrowLeft /> Overview
|
||||
</ButtonComp>
|
||||
</h1>
|
||||
<div :class="`panel__content !p-0 !pt-4 ${panel.function == 'overview' ?? '!pr-4'}`">
|
||||
<PanelsOverview v-if="panel.function == 'overview'" />
|
||||
<PanelEdit v-if="panel.function == 'edit'" :dirname="panel.dirname" />
|
||||
<PanelView v-if="panel.function == 'preview'" :dirname="panel.dirname" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ButtonComp from '@/components/base/ButtonComp.vue'
|
||||
import PanelEdit from '@/components/panels/PanelEdit.vue'
|
||||
import PanelView from '@/components/panels/PanelView.vue'
|
||||
import PanelsOverview from '@/components/panels/PanelsOverview.vue'
|
||||
import { isLocal } from '@/services/ApiService'
|
||||
import { IconArrowLeft } from '@tabler/icons-vue'
|
||||
import { onMounted, onUpdated, reactive } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const panel = reactive({
|
||||
function: '',
|
||||
dirname: '',
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
setVarsByRoute()
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
setVarsByRoute()
|
||||
})
|
||||
|
||||
const setVarsByRoute = () => {
|
||||
if (route.name.includes('panel-')) {
|
||||
panel.function = route.name == 'panel-edit' ? 'edit' : 'preview'
|
||||
} else {
|
||||
panel.function = 'overview'
|
||||
}
|
||||
|
||||
panel.dirname = route.params.dirname
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"name": "Almost empty panel",
|
||||
"description": "This is the third panel to be created. It is also a test panel.",
|
||||
"aspectRatio": "4/3",
|
||||
"macros": {}
|
||||
}
|
||||
|
|
@ -6,116 +6,116 @@
|
|||
<title>Document</title>
|
||||
<link rel="stylesheet" href="./output.css" />
|
||||
</head>
|
||||
<body class="bg-slate-400 w-screen h-screen m-0">
|
||||
<div class="h-full aspect-[9/20] bg-slate-500 border border-red-500">
|
||||
<div class="size-full grid grid-cols-2 grid-rows-8 gap-2">
|
||||
<body class="bg-slate-400 w-screen h-screen m-0 aspect-[9/20]">
|
||||
<div class="h-full bg-slate-500">
|
||||
<div class="grid grid-cols-2 gap-2 size-full grid-rows-8">
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_1"
|
||||
mcrm__button
|
||||
>
|
||||
button1
|
||||
Task manager
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_2"
|
||||
mcrm__button
|
||||
>
|
||||
button2
|
||||
Close window
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_3"
|
||||
mcrm__button
|
||||
>
|
||||
button3
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_4"
|
||||
mcrm__button
|
||||
>
|
||||
button4
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_5"
|
||||
mcrm__button
|
||||
>
|
||||
button5
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_6"
|
||||
mcrm__button
|
||||
>
|
||||
button6
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_7"
|
||||
mcrm__button
|
||||
>
|
||||
button7
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_8"
|
||||
mcrm__button
|
||||
>
|
||||
button8
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_9"
|
||||
mcrm__button
|
||||
>
|
||||
button9
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_10"
|
||||
mcrm__button
|
||||
>
|
||||
button10
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_11"
|
||||
mcrm__button
|
||||
>
|
||||
button11
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_12"
|
||||
mcrm__button
|
||||
>
|
||||
button12
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_13"
|
||||
mcrm__button
|
||||
>
|
||||
button13
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_14"
|
||||
mcrm__button
|
||||
>
|
||||
button14
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_15"
|
||||
mcrm__button
|
||||
>
|
||||
button15
|
||||
</div>
|
||||
<div
|
||||
class="bg-sky-400 flex justify-center items-center"
|
||||
class="flex items-center justify-center bg-sky-400"
|
||||
id="button_16"
|
||||
mcrm__button
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,8 +1 @@
|
|||
{
|
||||
"name": "Test Panel 1",
|
||||
"description": "This is the very first panel to be created. It is a test panel.",
|
||||
"aspectRatio": "9/20",
|
||||
"macros": {
|
||||
"button_1": "task_manager"
|
||||
}
|
||||
}
|
||||
{"dir":"","name":"Test Panel 1","description":"This is the very first panel to be created. It is a test panel. I'm gonna add more words to this description, not because it's an unclear description. But more so to test long descriptions in the UI. Very Nice.","aspectRatio":"10/20","macros":{"button_1":"Task_manager","button_16":"Task_manager","button_2":"ALT+F4"}}
|
||||
Loading…
Add table
Add a link
Reference in a new issue