mirror of
https://github.com/Macrame-App/Macrame
synced 2025-12-29 07:19:26 +00:00
WIP: panel management uix
This commit is contained in:
parent
0d3fb09310
commit
58de6eb976
5 changed files with 276 additions and 3 deletions
128
fe/build_panel_styles.js
Normal file
128
fe/build_panel_styles.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import chokidar from 'chokidar'
|
||||||
|
import { resolve, join, dirname, normalize, posix } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
import { spawn } from 'child_process'
|
||||||
|
import { existsSync, writeFileSync } from 'fs'
|
||||||
|
// import { posix } from 'path/posix'
|
||||||
|
|
||||||
|
// Get the directory of the current script
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
|
const panelsDir = join(__dirname, '../panels')
|
||||||
|
|
||||||
|
// Store active Tailwind processes
|
||||||
|
const tailwindProcesses = new Map()
|
||||||
|
|
||||||
|
// Function to stop an existing Tailwind process
|
||||||
|
const stopTailwind = (dir) => {
|
||||||
|
if (tailwindProcesses.has(dir)) {
|
||||||
|
// console.log(`Stopping Tailwind for ${dir}...`)
|
||||||
|
const process = tailwindProcesses.get(dir)
|
||||||
|
process.kill('SIGTERM') // Graceful stop
|
||||||
|
|
||||||
|
tailwindProcesses.delete(dir)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to start Tailwind
|
||||||
|
const startTailwind = (filePath) => {
|
||||||
|
let dir = dirname(filePath).replaceAll('\\', '/')
|
||||||
|
dir = dir.replace(/^.*\/panels/, '../panels')
|
||||||
|
const fileExt = filePath.split('.').pop()
|
||||||
|
// console.log(dir)
|
||||||
|
|
||||||
|
if (fileExt !== 'html') return
|
||||||
|
|
||||||
|
const panelName = dir
|
||||||
|
.split(/[\/\\]/)
|
||||||
|
.pop()
|
||||||
|
.replace('_', ' ')
|
||||||
|
|
||||||
|
// Create a CSS and config file for Tailwind
|
||||||
|
const cssFile = createTailwindStyling(dir)
|
||||||
|
const configFile = createTailwindConfig(dir)
|
||||||
|
|
||||||
|
const outputFile = dir + '/styles.css'
|
||||||
|
|
||||||
|
// Restart Tailwind if it's already running
|
||||||
|
if (!stopTailwind(dir)) {
|
||||||
|
console.log(`Starting Tailwind for panel: ${panelName}`)
|
||||||
|
} else {
|
||||||
|
console.log(`Restarting Tailwind for panel: ${panelName}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(configFile)
|
||||||
|
// console.log(outputFile)
|
||||||
|
|
||||||
|
// Spawn a new Tailwind process
|
||||||
|
const tailwindProcess = spawn(
|
||||||
|
'npx',
|
||||||
|
[
|
||||||
|
'tailwindcss',
|
||||||
|
'--output',
|
||||||
|
outputFile,
|
||||||
|
'--config',
|
||||||
|
`${configFile}`,
|
||||||
|
'--content',
|
||||||
|
dir + '/index.html',
|
||||||
|
'--watch',
|
||||||
|
],
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
tailwindProcesses.set(dir, tailwindProcess)
|
||||||
|
}
|
||||||
|
const createTailwindStyling = (dir) => {
|
||||||
|
const cssFile = dir + '/tailwind.css'
|
||||||
|
const cssContent = `@tailwind base; @tailwind components; @tailwind utilities;`
|
||||||
|
|
||||||
|
createFile(cssFile, cssContent)
|
||||||
|
return cssFile
|
||||||
|
}
|
||||||
|
const createTailwindConfig = (dir) => {
|
||||||
|
const configPath = dir + '/tailwind.config.js'
|
||||||
|
|
||||||
|
// Create a basic tailwind.config.js with purge setup
|
||||||
|
const configContent = `
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
export default {
|
||||||
|
content: [path.resolve(__dirname, "index.html")],
|
||||||
|
theme: { extend: {} },
|
||||||
|
plugins: [],
|
||||||
|
mode: "jit",
|
||||||
|
};
|
||||||
|
`
|
||||||
|
|
||||||
|
createFile(configPath, configContent)
|
||||||
|
return configPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const createFile = (filepath, content) => {
|
||||||
|
if (existsSync(filepath)) return
|
||||||
|
|
||||||
|
writeFileSync(filepath, content.trim())
|
||||||
|
|
||||||
|
// console.log(`Created ${filepath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for changes to `index.html` files
|
||||||
|
const watcher = chokidar.watch(`${panelsDir}/`, {
|
||||||
|
persistent: true,
|
||||||
|
ignoreInitial: false,
|
||||||
|
ignored: '*.json, *.css, *.jpg, *.jpeg, *.png, *.webp',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start or restart Tailwind when a file is added or modified
|
||||||
|
watcher.on('change', (file) => {
|
||||||
|
startTailwind(file)
|
||||||
|
})
|
||||||
59
fe/src/components/panels/PanelDetail.vue
Normal file
59
fe/src/components/panels/PanelDetail.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<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>
|
||||||
48
fe/src/components/panels/PanelsOverview.vue
Normal file
48
fe/src/components/panels/PanelsOverview.vue
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div id="panels-overview">
|
||||||
|
<!-- <AlertComp v-if="Object.keys(panels.list).length == 0" type="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="" />
|
||||||
|
<h4>{{ panel.name }}</h4>
|
||||||
|
<p>{{ panel.description }}</p>
|
||||||
|
</div>
|
||||||
|
<!-- </router-link> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { usePanelStore } from '@/stores/panel'
|
||||||
|
import { onMounted, reactive } from 'vue'
|
||||||
|
import AlertComp from '../base/AlertComp.vue'
|
||||||
|
|
||||||
|
const panel = usePanelStore()
|
||||||
|
|
||||||
|
const panels = reactive({
|
||||||
|
list: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const panelList = await panel.getPanels()
|
||||||
|
console.log(panelList)
|
||||||
|
|
||||||
|
panels.list = panelList
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@reference "@/assets/main.css";
|
||||||
|
|
||||||
|
.panel-list {
|
||||||
|
@apply grid
|
||||||
|
grid-cols-2
|
||||||
|
md:grid-cols-4
|
||||||
|
lg:grid-cols-6
|
||||||
|
gap-4
|
||||||
|
size-full;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
24
fe/src/stores/panel.js
Normal file
24
fe/src/stores/panel.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { appUrl } from '@/services/ApiService'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const usePanelStore = defineStore('panel', () => {
|
||||||
|
const list = ref([])
|
||||||
|
|
||||||
|
const getPanels = async () => {
|
||||||
|
console.log(list.value.length)
|
||||||
|
|
||||||
|
if (list.value.length > 0) return list.value
|
||||||
|
|
||||||
|
const resp = await axios.post(appUrl() + '/panel/list')
|
||||||
|
list.value = resp.data.data
|
||||||
|
|
||||||
|
return list.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
getPanels,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -1,7 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div></div>
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup></script>
|
<script setup>
|
||||||
|
import PanelsOverview from '@/components/panels/PanelsOverview.vue'
|
||||||
|
import { isLocal } from '@/services/ApiService'
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style scoped>
|
||||||
|
@reference "@/assets/main.css";
|
||||||
|
</style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue